mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-07-27 04:20:43 +08:00
Compare commits
185 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21eeb912fd | |||
| a60def91a0 | |||
| 759373e327 | |||
| 7d74a40f51 | |||
| 50cad268d3 | |||
| 8510b71ff6 | |||
| a012a57cf0 | |||
| d8ad0772bf | |||
| f105dedc23 | |||
| c847dfb1c4 | |||
| 862d176593 | |||
| 3945ee611d | |||
| de142c4729 | |||
| 0b4090f9e9 | |||
| 3ccd4f7ccf | |||
| 6f01501fd1 | |||
| e426abf336 | |||
| a767397e23 | |||
| 17f5b4b917 | |||
| 94ff37f77f | |||
| 73bc88bad8 | |||
| 72dc278a2b | |||
| 7ba35ffd5d | |||
| 3643bdf6f6 | |||
| 21fee2dc9a | |||
| 90f6516f45 | |||
| a15621d55e | |||
| a717beb0e7 | |||
| b9ad94ddb5 | |||
| 9ab013562d | |||
| a8bd121deb | |||
| 476a1ec48e | |||
| 31ade9908a | |||
| 5606e8c59f | |||
| 61d3b22fdb | |||
| c05aa5253c | |||
| 0b599dcced | |||
| 623e3fa59a | |||
| 1075f588fa | |||
| c0d8cb2c3b | |||
| 9c6a054b12 | |||
| 6a05f664ba | |||
| 9c4d2242ae | |||
| eac027c116 | |||
| 3a08008192 | |||
| 7daf93d51f | |||
| 409be28deb | |||
| 97e358e41f | |||
| c17e668afe | |||
| 4611932ad8 | |||
| 452d021816 | |||
| d71022c224 | |||
| d521a641de | |||
| 4246d8cdcb | |||
| 6989fd8ddc | |||
| 742b51a23e | |||
| bc541a3ef6 | |||
| 67fc682164 | |||
| b43dd7e5a7 | |||
| e29ccf65c5 | |||
| 91553fc053 | |||
| 22f57b7860 | |||
| 2a25727c0b | |||
| eb69ae4af6 | |||
| 2fd83a7814 | |||
| 8963ba1921 | |||
| 051c72b584 | |||
| 552827eaac | |||
| e8d13c674f | |||
| 256bc0e00f | |||
| 9aa0e310f2 | |||
| 6a0bb0e07f | |||
| 044f737016 | |||
| 27b3d31828 | |||
| 64c9a15d51 | |||
| df4c58d3e4 | |||
| 93f23696ef | |||
| 2ed7e1a260 | |||
| e19b107ed0 | |||
| 7c15c84dba | |||
| e8edc03526 | |||
| 2b717ad449 | |||
| 5efc877a89 | |||
| 75a133415a | |||
| bf41b08b35 | |||
| 74a5efb596 | |||
| 40dd4f4b7f | |||
| 341670adcf | |||
| 115aea2580 | |||
| 58f1e7f131 | |||
| 26ce84b950 | |||
| 32b3bbaa74 | |||
| 76c00f31f6 | |||
| e28ea6ca08 | |||
| a98254e6cc | |||
| 82062d9465 | |||
| 706cee2240 | |||
| da60eb2daf | |||
| a764c3dbfe | |||
| 88fb74a795 | |||
| 52d4b7927b | |||
| ff31e0d8e8 | |||
| a65ea2617b | |||
| bb228c6705 | |||
| 62992b2688 | |||
| da141ae10a | |||
| d22224e2fc | |||
| d0b3a42c1d | |||
| c3349ae070 | |||
| ad6719e201 | |||
| 0e342d5fa3 | |||
| daf1318a90 | |||
| d4ccc9b7d6 | |||
| ce60c334ec | |||
| ca723bae80 | |||
| c312fab4b1 | |||
| d9db03959a | |||
| add84f6e4b | |||
| 850ee73fa2 | |||
| 77da85f7d4 | |||
| 4d0901b650 | |||
| 66a39dd723 | |||
| 4cdcbb1958 | |||
| e0569e287f | |||
| b42932ddad | |||
| cc05b64dec | |||
| aec54e8c5b | |||
| 15e95a2f3e | |||
| 74e5c9c3cf | |||
| 85227a8aa4 | |||
| e7a00c5b69 | |||
| 85e558f1a0 | |||
| fade65397d | |||
| a2bd2cc8ba | |||
| bdaeb98432 | |||
| ccefbcee02 | |||
| e5590c0a22 | |||
| a85131308c | |||
| e17edade28 | |||
| 959acab9c7 | |||
| 0b59f69fa0 | |||
| ab755a8623 | |||
| a67ad88c3f | |||
| 6684f80f02 | |||
| 620354b77a | |||
| 19c4e12a5b | |||
| 3b0ef59ca7 | |||
| 0386c66b70 | |||
| 9721932c34 | |||
| f9fce7d0ed | |||
| 4747d593be | |||
| 7d8e4ad559 | |||
| 3d02b8cc44 | |||
| 595c2d7cb7 | |||
| 2154daea57 | |||
| 85f166e7d0 | |||
| 0d618e21ce | |||
| 8d610473b3 | |||
| 512a4013b9 | |||
| f6069cad95 | |||
| 90ef890766 | |||
| 5727efa820 | |||
| 057a7cf6f0 | |||
| a81c506a14 | |||
| 70dc7cd0a7 | |||
| ee28753094 | |||
| 91923c0b42 | |||
| f6feecb324 | |||
| 6fdaeed792 | |||
| f838da43d8 | |||
| 21d723d260 | |||
| d701867b83 | |||
| 66f4ccffc0 | |||
| 9720743c80 | |||
| 6ba87b1972 | |||
| a0f3d1b220 | |||
| 6e80fa14c8 | |||
| 22a8e92dc2 | |||
| 6b1e0e5cc0 | |||
| 261617fc12 | |||
| 5000da9a64 | |||
| 9573b303e2 | |||
| f12262f4df | |||
| 1305b4a555 | |||
| b7e2025ebb |
@@ -0,0 +1,8 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets).
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md).
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.1.3/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [],
|
||||
"access": "restricted",
|
||||
"baseBranch": "base",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# API 和 HTTP 请求规范
|
||||
|
||||
## HTTP 请求封装
|
||||
- 可以使用 `简单http` 或者 `alova` 或者 `@tanstack/vue-query` 进行请求管理
|
||||
- HTTP 配置在 [src/http/](mdc:src/http/) 目录下
|
||||
- `简单http` - [src/http/http.ts](mdc:src/http/http.ts)
|
||||
- `alova` - [src/http/alova.ts](mdc:src/http/alova.ts)
|
||||
- `vue-query` - [src/http/vue-query.ts](mdc:src/http/vue-query.ts)
|
||||
- 请求拦截器在 [src/http/interceptor.ts](mdc:src/http/interceptor.ts)
|
||||
- 支持请求重试、缓存、错误处理
|
||||
|
||||
## API 接口规范
|
||||
- API 接口定义在 [src/api/](mdc:src/api/) 目录下
|
||||
- 按功能模块组织 API 文件
|
||||
- 使用 TypeScript 定义请求和响应类型
|
||||
- 支持 `简单http`、`alova` 和 `vue-query` 三种请求方式
|
||||
|
||||
|
||||
## 示例代码结构
|
||||
```typescript
|
||||
// API 接口定义
|
||||
export interface LoginParams {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
token: string
|
||||
userInfo: UserInfo
|
||||
}
|
||||
|
||||
// alova 方式
|
||||
export const login = (params: LoginParams) =>
|
||||
http.Post<LoginResponse>('/api/login', params)
|
||||
|
||||
// vue-query 方式
|
||||
export const useLogin = () => {
|
||||
return useMutation({
|
||||
mutationFn: (params: LoginParams) =>
|
||||
http.post<LoginResponse>('/api/login', params)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
- 统一错误处理在拦截器中配置
|
||||
- 支持网络错误、业务错误、认证错误等
|
||||
- 自动处理 token 过期和刷新
|
||||
---
|
||||
globs: src/api/*.ts,src/http/*.ts
|
||||
---
|
||||
@@ -0,0 +1,43 @@
|
||||
# 开发工作流程
|
||||
|
||||
## 项目启动
|
||||
1. 安装依赖:`pnpm install`
|
||||
2. 开发环境:
|
||||
- H5: `pnpm dev` 或 `pnpm dev:h5`
|
||||
- 微信小程序: `pnpm dev:mp`
|
||||
- 支付宝小程序: `pnpm dev:mp-alipay`
|
||||
- APP: `pnpm dev:app`
|
||||
|
||||
## 代码规范
|
||||
- 使用 ESLint 进行代码检查:`pnpm lint`
|
||||
- 自动修复代码格式:`pnpm lint:fix`
|
||||
- 使用 eslint 格式化代码
|
||||
- 遵循 TypeScript 严格模式
|
||||
|
||||
## 构建和部署
|
||||
- H5 构建:`pnpm build:h5`
|
||||
- 微信小程序构建:`pnpm build:mp`
|
||||
- 支付宝小程序构建:`pnpm build:mp-alipay`
|
||||
- APP 构建:`pnpm build:app`
|
||||
- 类型检查:`pnpm type-check`
|
||||
|
||||
## 开发工具
|
||||
- 推荐使用 VSCode 编辑器
|
||||
- 安装 Vue 和 TypeScript 相关插件
|
||||
- 使用 uni-app 开发者工具调试小程序
|
||||
- 使用 HBuilderX 调试 APP
|
||||
|
||||
## 调试技巧
|
||||
- 使用 console.log 和 uni.showToast 调试
|
||||
- 利用 Vue DevTools 调试组件状态
|
||||
- 使用网络面板调试 API 请求
|
||||
- 平台差异测试和兼容性检查
|
||||
|
||||
## 性能优化
|
||||
- 使用懒加载和代码分割
|
||||
- 优化图片和静态资源
|
||||
- 减少不必要的重渲染
|
||||
- 合理使用缓存策略
|
||||
---
|
||||
description: 开发工作流程和最佳实践指南
|
||||
---
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
# 项目概览
|
||||
|
||||
这是一个基于 uniapp + Vue3 + TypeScript + Vite5 + UnoCSS 的跨平台开发框架。
|
||||
|
||||
## 项目特点
|
||||
- 支持 H5、小程序、APP 多平台开发
|
||||
- 使用最新的前端技术栈
|
||||
- 内置约定式路由、layout布局、请求封装、登录拦截、自定义tabbar等功能
|
||||
- 无需依赖 HBuilderX,支持命令行开发
|
||||
|
||||
## 核心配置文件
|
||||
- [package.json](mdc:package.json) - 项目依赖和脚本配置
|
||||
- [vite.config.ts](mdc:vite.config.ts) - Vite 构建配置
|
||||
- [pages.config.ts](mdc:pages.config.ts) - 页面路由配置
|
||||
- [manifest.config.ts](mdc:manifest.config.ts) - 应用清单配置
|
||||
- [uno.config.ts](mdc:uno.config.ts) - UnoCSS 配置
|
||||
|
||||
## 主要目录结构
|
||||
- `src/pages/` - 页面文件
|
||||
- `src/components/` - 组件文件
|
||||
- `src/layouts/` - 布局文件
|
||||
- `src/api/` - API 接口
|
||||
- `src/http/` - HTTP 请求封装
|
||||
- `src/store/` - 状态管理
|
||||
- `src/tabbar/` - 底部导航栏
|
||||
- `src/App.ku.vue` - 全局根组件(类似 App.vue 里面的 template作用)
|
||||
|
||||
## 开发命令
|
||||
- `pnpm dev` - 开发 H5 版本
|
||||
- `pnpm dev:mp` - 开发微信小程序
|
||||
- `pnpm dev:mp-alipay` - 开发支付宝小程序(含钉钉)
|
||||
- `pnpm dev:app` - 开发 APP 版本
|
||||
- `pnpm build` - 构建生产版本
|
||||
@@ -0,0 +1,54 @@
|
||||
# 样式和 CSS 开发规范
|
||||
|
||||
## UnoCSS 原子化 CSS
|
||||
- 项目使用 UnoCSS 作为原子化 CSS 框架
|
||||
- 配置在 [uno.config.ts](mdc:uno.config.ts)
|
||||
- 支持预设和自定义规则
|
||||
- 优先使用原子化类名,减少自定义 CSS
|
||||
|
||||
## SCSS 规范
|
||||
- 使用 SCSS 预处理器
|
||||
- 样式文件使用 `lang="scss"` 和 `scoped` 属性
|
||||
- 遵循 BEM 命名规范
|
||||
- 使用变量和混入提高复用性
|
||||
|
||||
## 样式组织
|
||||
- 全局样式在 [src/style/](mdc:src/style/) 目录下
|
||||
- 组件样式使用 scoped 作用域
|
||||
- 图标字体在 [src/style/iconfont.css](mdc:src/style/iconfont.css)
|
||||
- 主题变量在 [src/uni_modules/uni-scss/](mdc:src/uni_modules/uni-scss/) 目录下
|
||||
|
||||
## 示例代码结构
|
||||
```vue
|
||||
<template>
|
||||
<view class="container flex flex-col items-center p-4">
|
||||
<text class="title text-lg font-bold mb-2">标题</text>
|
||||
<view class="content bg-gray-100 rounded-lg p-3">
|
||||
<!-- 内容 -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
|
||||
.title {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
max-width: 600rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
## 响应式设计
|
||||
- 使用 rpx 单位适配不同屏幕
|
||||
- 支持横屏和竖屏布局
|
||||
- 使用 flexbox 和 grid 布局
|
||||
- 考虑不同平台的样式差异
|
||||
---
|
||||
globs: *.vue,*.scss,*.css
|
||||
---
|
||||
@@ -0,0 +1,63 @@
|
||||
# uni-app 开发规范
|
||||
|
||||
## 页面开发
|
||||
- 页面文件放在 [src/pages/](mdc:src/pages/) 目录下
|
||||
- 使用约定式路由,文件名即路由路径
|
||||
- 页面配置在仅需要在 宏`definePage` 中配置标题等内容即可,会自动生成到 `pages.json` 中
|
||||
- definePage的顺序在最上面
|
||||
|
||||
## 组件开发
|
||||
- 组件文件放在 [src/components/](mdc:src/components/) 或者 [src/pages/xx/components/](mdc:src/pages/xx/components/) 目录下
|
||||
- 使用 uni-app 内置组件和第三方组件库
|
||||
- 支持 wot-ui\uview-pro\uv-ui\sard-ui\uview-plus 等多种第三方组件库 和 z-paging 组件
|
||||
- 自定义组件遵循 uni-app 组件规范
|
||||
|
||||
## 平台适配
|
||||
- 使用条件编译处理平台差异
|
||||
- 支持 H5、小程序、APP 多平台
|
||||
- 注意各平台的 API 差异
|
||||
- 使用 uni.xxx API 替代原生 API
|
||||
|
||||
## 示例代码结构
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// #ifdef H5
|
||||
import { h5Api } from '@/utils/h5'
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
import { mpApi } from '@/utils/mp'
|
||||
// #endif
|
||||
|
||||
const handleClick = () => {
|
||||
// #ifdef H5
|
||||
h5Api.showToast('H5 平台')
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
mpApi.showToast('微信小程序')
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- uni-app 组件 -->
|
||||
<button @click="handleClick">点击</button>
|
||||
|
||||
<!-- 条件渲染 -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view>H5 特有内容</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 生命周期
|
||||
- 使用 uni-app 页面生命周期
|
||||
- onLoad、onShow、onReady、onHide、onUnload
|
||||
- 组件生命周期遵循 Vue3 规范
|
||||
- 注意页面栈和导航管理
|
||||
---
|
||||
globs: src/pages/*.vue,src/components/*.vue
|
||||
---
|
||||
@@ -0,0 +1,53 @@
|
||||
# Vue3 + TypeScript 开发规范
|
||||
|
||||
## Vue 组件规范
|
||||
- 使用 Composition API 和 `<script setup>` 语法
|
||||
- 组件文件使用 PascalCase 命名
|
||||
- 页面文件放在 `src/pages/` 目录下
|
||||
- 全局组件文件放在 `src/components/` 目录下
|
||||
- 局部组件文件放在页面的 `/components/` 目录下
|
||||
|
||||
## Vue SFC 组件规范
|
||||
- `<script setup lang="ts">` 标签必须是第一个子元素
|
||||
- `<template>` 标签必须是第二个子元素
|
||||
- `<style scoped>` 标签必须是最后一个子元素(因为推荐使用原子化类名,所以很可能没有)
|
||||
|
||||
## TypeScript 规范
|
||||
- 严格使用 TypeScript,避免使用 `any` 类型
|
||||
- 为 API 响应数据定义接口类型
|
||||
- 使用 `interface` 定义对象类型,`type` 定义联合类型
|
||||
- 导入类型时使用 `import type` 语法
|
||||
|
||||
## 状态管理
|
||||
- 使用 Pinia 进行状态管理
|
||||
- Store 文件放在 `src/store/` 目录下
|
||||
- 使用 `defineStore` 定义 store
|
||||
- 支持持久化存储
|
||||
|
||||
## 示例代码结构
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import type { UserInfo } from '@/types/user'
|
||||
|
||||
const userInfo = ref<UserInfo | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化逻辑
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 模板内容 -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
// 样式
|
||||
}
|
||||
</style>
|
||||
---
|
||||
globs: *.vue,*.ts,*.tsx
|
||||
---
|
||||
@@ -0,0 +1,13 @@
|
||||
root = true
|
||||
|
||||
[*] # 表示所有文件适用
|
||||
charset = utf-8 # 设置文件字符集为 utf-8
|
||||
indent_style = space # 缩进风格(tab | space)
|
||||
indent_size = 2 # 缩进大小
|
||||
end_of_line = lf # 控制换行类型(lf | cr | crlf)
|
||||
trim_trailing_whitespace = true # 去除行首的任意空白字符
|
||||
insert_final_newline = true # 始终在文件末尾插入一个新行
|
||||
|
||||
[*.md] # 表示仅 md 文件适用以下规则
|
||||
max_line_length = off # 关闭最大行长度限制
|
||||
trim_trailing_whitespace = false # 关闭末尾空格修剪
|
||||
+48
-5
@@ -1,5 +1,48 @@
|
||||
node_modules/
|
||||
unpackage/
|
||||
config/halo.config.js
|
||||
config/ad.config.js
|
||||
package-lock.json
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
.hbuilderx
|
||||
|
||||
.stylelintcache
|
||||
.eslintcache
|
||||
|
||||
docs/.vitepress/dist
|
||||
docs/.vitepress/cache
|
||||
|
||||
src/types
|
||||
# 单独把这个文件排除掉,用以解决部分电脑生成的 auto-import.d.ts 的API不完整导致类型提示报错问题
|
||||
!src/types/auto-import.d.ts
|
||||
src/manifest.json
|
||||
src/pages.json
|
||||
|
||||
# 2025-10-15 by 菲鸽: lock 文件还是需要加入版本管理,今天又遇到版本不一致导致无法运行的问题了。
|
||||
# pnpm-lock.yaml
|
||||
# package-lock.json
|
||||
|
||||
# TIPS:如果某些文件已经加入了版本管理,现在重新加入 .gitignore 是不生效的,需要执行下面的操作
|
||||
# `git rm -r --cached .` 然后提交 commit 即可。
|
||||
|
||||
# git rm -r --cached file1 file2 ## 针对某些文件
|
||||
# git rm -r --cached dir1 dir2 ## 针对某些文件夹
|
||||
# git rm -r --cached . ## 针对所有文件
|
||||
|
||||
# 更新 uni-app 官方版本
|
||||
# npx @dcloudio/uvm@latest
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
|
||||
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
|
||||
"version": "0.0",
|
||||
"configurations": [{
|
||||
"app-plus" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"default" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"h5" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"mp-weixin" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"type" : "uniCloud"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
npx --no-install commitlint --edit "$1"
|
||||
@@ -0,0 +1 @@
|
||||
npx lint-staged --allow-empty
|
||||
@@ -0,0 +1,9 @@
|
||||
# registry = https://registry.npmjs.org
|
||||
registry = https://registry.npmmirror.com
|
||||
|
||||
strict-peer-dependencies=false
|
||||
auto-install-peers=true
|
||||
shamefully-hoist=true
|
||||
ignore-workspace-root-check=true
|
||||
install-workspace-root=true
|
||||
node-options=--max-old-space-size=8192
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
scene: git_message
|
||||
---
|
||||
|
||||
在此处编写规则,自定义 AI 生成提交信息的风格。
|
||||
|
||||
使用中文编写提交信息
|
||||
@@ -0,0 +1,123 @@
|
||||
# 项目概览
|
||||
|
||||
这是一个基于 uniapp + Vue3 + TypeScript + Vite5 + UnoCSS 的跨平台开发框架。
|
||||
|
||||
## 项目特点
|
||||
- 支持 H5、小程序、APP 多平台开发
|
||||
- 使用最新的前端技术栈
|
||||
- 内置约定式路由、layout布局、请求封装等功能
|
||||
- 无需依赖 HBuilderX,支持命令行开发
|
||||
|
||||
## 核心配置文件
|
||||
- [package.json](mdc:package.json) - 项目依赖和脚本配置
|
||||
- [vite.config.ts](mdc:vite.config.ts) - Vite 构建配置
|
||||
- [pages.config.ts](mdc:pages.config.ts) - 页面路由配置
|
||||
- [manifest.config.ts](mdc:manifest.config.ts) - 应用清单配置
|
||||
- [uno.config.ts](mdc:uno.config.ts) - UnoCSS 配置
|
||||
|
||||
## 主要目录结构
|
||||
- `src/pages/` - 页面文件
|
||||
- `src/components/` - 组件文件
|
||||
- `src/layouts/` - 布局文件
|
||||
- `src/api/` - API 接口
|
||||
- `src/http/` - HTTP 请求封装
|
||||
- `src/store/` - 状态管理
|
||||
- `src/tabbar/` - 底部导航栏
|
||||
- `src/App.ku.vue` - 全局根组件(类似 App.vue 里面的 template作用)
|
||||
|
||||
## 开发命令
|
||||
- `pnpm dev` - 开发 H5 版本
|
||||
- `pnpm dev:mp` - 开发微信小程序
|
||||
- `pnpm dev:mp-alipay` - 开发支付宝小程序(含钉钉)
|
||||
- `pnpm dev:app` - 开发 APP 版本
|
||||
- `pnpm build` - 构建生产版本
|
||||
|
||||
## Vue 组件规范
|
||||
- 使用 Composition API 和 `<script setup>` 语法
|
||||
- 组件文件使用 PascalCase 命名
|
||||
- 页面文件放在 `src/pages/` 目录下
|
||||
- 全局组件文件放在 `src/components/` 目录下
|
||||
- 局部组件文件放在页面的 `/components/` 目录下
|
||||
|
||||
## TypeScript 规范
|
||||
- 严格使用 TypeScript,避免使用 `any` 类型
|
||||
- 为 API 响应数据定义接口类型
|
||||
- 使用 `interface` 定义对象类型,`type` 定义联合类型
|
||||
- 导入类型时使用 `import type` 语法
|
||||
|
||||
## 状态管理
|
||||
- 使用 Pinia 进行状态管理
|
||||
- Store 文件放在 `src/store/` 目录下
|
||||
- 使用 `defineStore` 定义 store
|
||||
- 支持持久化存储
|
||||
|
||||
## UnoCSS 原子化 CSS
|
||||
- 项目使用 UnoCSS 作为原子化 CSS 框架
|
||||
- 配置在 [uno.config.ts]
|
||||
- 支持预设和自定义规则
|
||||
- 优先使用原子化类名,减少自定义 CSS
|
||||
|
||||
## Vue SFC 组件规范
|
||||
- `<script setup lang="ts">` 标签必须是第一个子元素
|
||||
- `<template>` 标签必须是第二个子元素
|
||||
- `<style scoped>` 标签必须是最后一个子元素(因为推荐使用原子化类名,所以很可能没有)
|
||||
|
||||
## 页面开发
|
||||
- 页面文件放在 [src/pages/]目录下
|
||||
- 使用约定式路由,文件名即路由路径
|
||||
- 页面配置在仅需要在 宏`definePage` 中配置标题等内容即可,会自动生成到 `pages.json` 中
|
||||
- definePage的顺序在最上面
|
||||
|
||||
## 组件开发
|
||||
- 全局组件文件放在 `src/components/` 目录下
|
||||
- 局部组件文件放在页面的 `/components/` 目录下
|
||||
- 使用 uni-app 内置组件和第三方组件库
|
||||
- 支持 wot-ui\uview-pro\uv-ui\sard-ui\uview-plus 等多种第三方组件库 和 z-paging 组件
|
||||
- 自定义组件遵循 uni-app 组件规范
|
||||
|
||||
## 平台适配
|
||||
- 使用条件编译处理平台差异
|
||||
- 支持 H5、小程序、APP 多平台
|
||||
- 注意各平台的 API 差异
|
||||
- 使用 uni.xxx API 替代原生 API
|
||||
|
||||
## 示例代码结构
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// #ifdef H5
|
||||
import { h5Api } from '@/utils/h5'
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
import { mpApi } from '@/utils/mp'
|
||||
// #endif
|
||||
|
||||
const handleClick = () => {
|
||||
// #ifdef H5
|
||||
h5Api.showToast('H5 平台')
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
mpApi.showToast('微信小程序')
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- uni-app 组件 -->
|
||||
<button @click="handleClick">点击</button>
|
||||
|
||||
<!-- 条件渲染 -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view>H5 特有内容</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 生命周期
|
||||
- 使用 uni-app 页面生命周期
|
||||
- onLoad、onShow、onReady、onHide、onUnload
|
||||
- 组件生命周期遵循 Vue3 规范
|
||||
- 注意页面栈和导航管理
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"antfu.unocss",
|
||||
"antfu.iconify",
|
||||
"evils.uniapp-vscode",
|
||||
"uni-helper.uni-helper-vscode",
|
||||
"uni-helper.uni-app-schemas-vscode",
|
||||
"uni-helper.uni-highlight-vscode",
|
||||
"uni-helper.uni-ui-snippets-vscode",
|
||||
"uni-helper.uni-app-snippets-vscode",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
]
|
||||
}
|
||||
Vendored
+107
@@ -0,0 +1,107 @@
|
||||
{
|
||||
// 配置语言的文件关联
|
||||
"files.associations": {
|
||||
"pages.json": "jsonc", // pages.json 可以写注释
|
||||
"manifest.json": "jsonc" // manifest.json 可以写注释
|
||||
},
|
||||
|
||||
"stylelint.enable": false, // 禁用 stylelint
|
||||
"css.validate": false, // 禁用 CSS 内置验证
|
||||
"scss.validate": false, // 禁用 SCSS 内置验证
|
||||
"less.validate": false, // 禁用 LESS 内置验证
|
||||
|
||||
// 新版本 VsCode 中这个配置已失效
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
|
||||
// 配置新版本 VsCode 工作区的 TypeScript 的版本
|
||||
"js/ts.tsdk.path": "node_modules/typescript/lib",
|
||||
"js/ts.tsdk.promptToUseWorkspaceVersion": true,
|
||||
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.expand": false,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"README.md": "index.html,favicon.ico,robots.txt,CHANGELOG.md",
|
||||
"docker.md": "Dockerfile,docker*.md,nginx*,.dockerignore",
|
||||
"pages.config.ts": "manifest.config.ts,openapi-ts-request.config.ts",
|
||||
"package.json": "tsconfig.json,pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,.npmrc,.browserslistrc",
|
||||
"eslint.config.mjs": ".commitlintrc.*,.prettier*,.editorconfig,.commitlint.cjs,.eslint*"
|
||||
},
|
||||
|
||||
// Disable the default formatter, use eslint instead
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{ "rule": "style/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "format/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-indent", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spacing", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spaces", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-order", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-dangle", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-newline", "severity": "off", "fixable": true },
|
||||
{ "rule": "*quotes", "severity": "off", "fixable": true },
|
||||
{ "rule": "*semi", "severity": "off", "fixable": true }
|
||||
],
|
||||
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml",
|
||||
"toml",
|
||||
"xml",
|
||||
"gql",
|
||||
"graphql",
|
||||
"astro",
|
||||
"svelte",
|
||||
"css",
|
||||
"less",
|
||||
"scss",
|
||||
"pcss",
|
||||
"postcss"
|
||||
],
|
||||
"cSpell.words": [
|
||||
"alova",
|
||||
"Aplipay",
|
||||
"attributify",
|
||||
"chooseavatar",
|
||||
"climblee",
|
||||
"commitlint",
|
||||
"dcloudio",
|
||||
"iconfont",
|
||||
"oxlint",
|
||||
"qrcode",
|
||||
"refresherrefresh",
|
||||
"scrolltolower",
|
||||
"tabbar",
|
||||
"Toutiao",
|
||||
"uniapp",
|
||||
"unibest",
|
||||
"unocss",
|
||||
"uview",
|
||||
"uvui",
|
||||
"Wechat",
|
||||
"WechatMiniprogram",
|
||||
"Weixin"
|
||||
],
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/locale",
|
||||
"src/pages/i18n"
|
||||
],
|
||||
"i18n-ally.keystyle": "nested"
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
{
|
||||
// Place your unibest 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||
// Placeholders with the same ids are connected.
|
||||
// Example:
|
||||
// "Print to console": {
|
||||
// "scope": "javascript,typescript",
|
||||
// "prefix": "log",
|
||||
// "body": [
|
||||
// "console.log('$1');",
|
||||
// "$2"
|
||||
// ],
|
||||
// "description": "Log output to console"
|
||||
// }
|
||||
"Print unibest Vue3 SFC": {
|
||||
"scope": "vue",
|
||||
"prefix": "v3",
|
||||
"body": [
|
||||
"<script lang=\"ts\" setup>",
|
||||
"definePage({",
|
||||
" style: {",
|
||||
" navigationBarTitleText: '$1',",
|
||||
" },",
|
||||
"})",
|
||||
"defineOptions({",
|
||||
" name: '$2',",
|
||||
" options: { ",
|
||||
" virtualHost: true,",
|
||||
" },",
|
||||
"})",
|
||||
"</script>\n",
|
||||
"<template>",
|
||||
" <view class=\"\">$3</view>",
|
||||
"</template>\n",
|
||||
"<style lang=\"scss\" scoped>",
|
||||
"//$4",
|
||||
"</style>\n",
|
||||
],
|
||||
},
|
||||
"Print unibest style": {
|
||||
"scope": "vue",
|
||||
"prefix": "st",
|
||||
"body": [
|
||||
"<style lang=\"scss\" scoped>",
|
||||
"//",
|
||||
"</style>\n"
|
||||
],
|
||||
},
|
||||
"Print unibest script with definePage": {
|
||||
"scope": "vue",
|
||||
"prefix": "sc",
|
||||
"body": [
|
||||
"<script lang=\"ts\" setup>",
|
||||
"definePage({",
|
||||
" style: {",
|
||||
" navigationBarTitleText: '$1',",
|
||||
" },",
|
||||
"})",
|
||||
"defineOptions({",
|
||||
" name: '$2',",
|
||||
" options: { ",
|
||||
" virtualHost: true,",
|
||||
" },",
|
||||
"})",
|
||||
"</script>\n"
|
||||
],
|
||||
},
|
||||
"Print unibest template": {
|
||||
"scope": "vue",
|
||||
"prefix": "te",
|
||||
"body": [
|
||||
"<template>",
|
||||
" <view class=\"\">$1</view>",
|
||||
"</template>\n"
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<script>
|
||||
import HaloConfig from '@/config/halo.config.js';
|
||||
import HaloAdConfig from '@/config/ad.config.js';
|
||||
|
||||
// app升级检测(搭配:https://ext.dcloud.net.cn/plugin?id=4470 升级中心)
|
||||
import CheckAppUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
|
||||
import { CheckWxUpdate } from '@/utils/update.js';
|
||||
export default {
|
||||
globalData: {
|
||||
baseApiUrl: HaloConfig.apiUrl,
|
||||
...HaloConfig,
|
||||
haloAdConfig: HaloAdConfig
|
||||
},
|
||||
onLaunch: function() {
|
||||
console.log('App Launch');
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
CheckAppUpdate();
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
CheckWxUpdate();
|
||||
uni.$tm.vx.commit('setWxShare', HaloConfig.wxShareConfig);
|
||||
// #endif
|
||||
|
||||
// 监听中间按钮(暂时没有使用)
|
||||
uni.onTabBarMidButtonTap(() => {
|
||||
console.log('点击中间按钮');
|
||||
});
|
||||
|
||||
// 初始化博主信息
|
||||
uni.$tm.vx.actions('blogger/fnGetBlogger');
|
||||
|
||||
// 临时:检查是否有用户,没有的话添加一个默认的用户
|
||||
uni.$tm.vx.actions('user/checkAndSetDefaultUser');
|
||||
|
||||
// 启动检查app的配置是否已经就绪,若未就绪则设置默认的
|
||||
uni.$tm.vx.actions('setting/checkAndSetDefaultAppSettings');
|
||||
},
|
||||
onShow: function() {
|
||||
console.log('App Show');
|
||||
},
|
||||
onHide: function() {
|
||||
console.log('App Hide');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// 基础样式
|
||||
@import './common/styles/app.theme.scss';
|
||||
@import './common/styles/app.base.scss';
|
||||
|
||||
// 引入tmUI2.x样式
|
||||
@import './tm-vuetify/mian.min.css';
|
||||
// 引入tmUI2.x主题包
|
||||
@import './tm-vuetify/scss/theme.css';
|
||||
// 引入tmUI2.x预置图标
|
||||
@import './tm-vuetify/scss/fonts/fontawesome_base64.css';
|
||||
|
||||
// 自定义图标
|
||||
@import './common/icons/halocoloriconfont.css';
|
||||
@import './common/icons/haloiconfont.css';
|
||||
@import './common/icons/mphtmliconfont.css';
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
@import './common/markdown/markdown.scss';
|
||||
/* #endif */
|
||||
|
||||
page {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +0,0 @@
|
||||
# 更新日志
|
||||
|
||||
## 暂无更新记录
|
||||
@@ -1,19 +0,0 @@
|
||||
# Git管理同步配置
|
||||
|
||||
#### 同时推送Gitee和Github
|
||||
找到项目目录下的`.git`文件夹,打开`config`文件,修改相关代码:
|
||||
原始代码:
|
||||
```bash
|
||||
[remote "origin"]
|
||||
url = https://gitee.com/ialley-workshop-open/uni-halo.git
|
||||
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||
```
|
||||
修改后的代码:
|
||||
```bash
|
||||
[remote "github"]
|
||||
url = https://github.com/ialley-workshop-open/uni-halo.git
|
||||
fetch = +refs/heads/*:refs/remotes/github/*
|
||||
[remote "gitee"]
|
||||
url = https://gitee.com/ialley-workshop-open/uni-halo.git
|
||||
fetch = +refs/heads/*:refs/remotes/gitee/*
|
||||
```
|
||||
@@ -1,661 +1,21 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 菲鸽
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,110 +1,122 @@
|
||||
<p align="center">
|
||||
<a href="https://uni-halo.925i.cn" target="_blank" rel="noopener noreferrer">
|
||||
<img width="100" src="https://b.925i.cn/uni_halo/uni_halo_logo.png" alt="uni-halo logo" />
|
||||
<img width="100" src="https://uni-halo.925i.cn/logo.png" alt="uni-halo logo" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><b>uni-halo</b> 基于Halo一款现代化的开源博客/CMS系统API开发的多端系统,值得一试。</p>
|
||||
<p align="center"><b>【uni-halo v3.0】</b> 基于 Halo2.X API 多端项目,值得一试。</p>
|
||||
|
||||
<p align="center"><b style="font-size: 20px;color: #10B5FB">正式支持 Halo2.X 版本,免费开源</b></p>
|
||||
|
||||
<br />
|
||||
<p align="center">
|
||||
<a href="https://b.925i.cn">作者博客</a>
|
||||
<a href="https://uni-halo.925i.cn">文档地址</a>
|
||||
<a href="https://gitee.com/ialley-workshop-open/uni-halo">Gitee仓库</a>
|
||||
<a href="https://github.com/ialley-workshop-open/uni-halo">Github仓库</a>
|
||||
<a href="https://www.xiaoxiaomo.cn">作者主页</a>
|
||||
<a href="https://uni-halo.925i.cn">官方文档</a>
|
||||
<a href="https://github.com/ialley-workshop-open/uni-halo">仓库地址</a>
|
||||
<a href="https://github.com/ialley-workshop-open/plugin-uni-halo">插件仓库</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
如果您觉得这个项目对您有帮助,可以帮作者买杯饮料鼓励鼓励!
|
||||
如果您觉得这个项目对您有帮助,可以帮作者买杯饮料鼓励鼓励,同时为了项目能够持续发展,可以根据您的喜好支持一下本项目哦,非常感谢您的支持,作者也会更有动力持续维护和更新新的功能哦~
|
||||
|
||||
| 支付宝 | 微信 | QQ |
|
||||
|:---------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------:|
|
||||
|  |  |  |
|
||||
|
||||
## 一、项目介绍
|
||||
|
||||
基于 Halo 2.x 提供的 API 接口,为微信小程序提供的一套开源的博客应用。
|
||||
|
||||
- 完全免费开源,包括程序源码、插件源码
|
||||
- 页面支持插件配置
|
||||
- 使用最新流行的技术栈
|
||||
- 支持特色功能,恋爱日记
|
||||
- 理论支持编译为 小程序(推荐)、APP
|
||||
|
||||
### 部分截图
|
||||
|
||||
|首页|分类|我的|
|
||||
|:--:|:--:|:--:|
|
||||
||||
|
||||
|
||||
|
||||
|
||||
### 恋爱日记
|
||||
|
||||
- 在 uni-halo 中,我们为您准备了一个恋爱日记的模块,您可以在其中记录您的恋爱故事,分享给您的朋友们,请扫示例小程序,在线体验恋爱日记的功能。
|
||||
|
||||

|
||||
|
||||
<table rules="none" align="center" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<center>
|
||||
<img src="https://uni-halo.925i.cn/qrcode/zfb.png" width="100%" />
|
||||
<br/>
|
||||
<font color="AAAAAA">支付宝打赏</font>
|
||||
</center>
|
||||
</td>
|
||||
<td>
|
||||
<center>
|
||||
<img src="https://uni-halo.925i.cn/qrcode/wx.png" width="100%" />
|
||||
|
||||
|
||||
### 功能介绍
|
||||
|
||||
- 基础功能:文章列表、文章分类、文章详情、个人图库、友情链接、我的瞬间
|
||||
|
||||
- 特色功能:恋爱日记,精心为您设计了秀恩爱的模块,秀出关于你们的故事、情侣甜蜜相册、恋爱记录清单,为您的恋爱保驾护航。
|
||||
|
||||
|
||||
## 二、配套插件
|
||||
|
||||
### 下载地址
|
||||
- 代码仓库:[https://github.com/ialley-workshop-open/plugin-uni-halo](https://github.com/ialley-workshop-open/plugin-uni-halo)
|
||||
- 仓库版本:[https://github.com/ialley-workshop-open/plugin-uni-halo/releases](https://github.com/ialley-workshop-open/plugin-uni-halo/releases)
|
||||
- 应用市场:[https://www.halo.run/store/apps/app-ryemX](https://www.halo.run/store/apps/app-ryemX)
|
||||
|
||||
### 使用方式
|
||||
|
||||
直接下载安装插件,在插件市场安装即可,安装完成后,需要配置插件,配置完成后,即可使用插件提供的功能。
|
||||
|
||||
|
||||
## 三、相关文档
|
||||
|
||||
- 作者主页:[https://www.xiaoxiaomo.cn/](https://www.xiaoxiaomo.cn/)
|
||||
- 作者博客:[https://blog.xiaoxiaomo.cn/](https://blog.xiaoxiaomo.cn/)
|
||||
- 官方文档:[https://uni-halo.925i.cn/](https://uni-halo.925i.cn/)
|
||||
- 代码仓库:[https://github.com/ialley-workshop-open/uni-halo](https://github.com/ialley-workshop-open/uni-halo)
|
||||
- 插件地址:[https://github.com/ialley-workshop-open/plugin-uni-halo](https://github.com/ialley-workshop-open/uni-halo)
|
||||
- 应用市场:[https://www.halo.run/store/apps/app-ryemX](https://www.halo.run/store/apps/app-ryemX)
|
||||
|
||||
|
||||
## 四、使用交流
|
||||
|
||||
- 提交 issues:[Github Issues](https://github.com/ialley-workshop-open/uni-halo/issues)
|
||||
- QQ 交流群:632969367
|
||||
- 微信交流群:由于微信交流群时效性太短,所以有需要的话可以先加入QQ群,再邀请进微信群
|
||||
|
||||
<br/>
|
||||
<font color="AAAAAA">微信打赏</font>
|
||||
</center>
|
||||
</td>
|
||||
<td>
|
||||
<center>
|
||||
<img src="https://uni-halo.925i.cn/qrcode/qq.png" width="100%" />
|
||||
|
||||
## 🎉 加入我们
|
||||
|
||||
uni-halo 还在持续更新中,本项目欢迎您的参与,共同维护,逐步完善,将项目做得更强。项目采用 AGPL-3.0 开源协议,本着一切免费的原则,原则上不会收取任何费用及版权,可以放心使用。
|
||||
|
||||
如果你想加入我们,可以多提供一些好的建议或者提交 pr,我们将会非常乐意接受您的建议和意见。
|
||||
|
||||
<br/>
|
||||
<font color="AAAAAA">QQ打赏</font>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 💪 贡献者们
|
||||
|
||||
## 🌸 快速开始
|
||||
感谢以下所有给 uni-halo 贡献过代码的 [开发者](https://github.com/ialley-workshop-open/uni-halo/graphs/contributors)。
|
||||
|
||||
详细部署文档请查阅 [uni-halo-doc](https://uni-halo.925i.cn/)
|
||||
<a href="https://github.com/ialley-workshop-open/uni-halo/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=ialley-workshop-open/uni-halo" />
|
||||
</a>
|
||||
|
||||
- 1、拉取或下载项目<https://gitee.com/ialley-workshop-open/uni-halo>;
|
||||
- 2、通过hbuilderx 导入项目;
|
||||
- 3、命令行执行 npm i 安装依赖;
|
||||
- 4、配置运行信息,找到项目根目录config目录,将`halo.config.template.js`修改为 `halo.config.js` 并设置相关信息;
|
||||
- 5、点击hbuilderx 工具 右上角预览、或者点击工具栏 运行-内置浏览器运行;
|
||||
- 6、项目发行:
|
||||
- 发行小程序:点击工具栏 发行 -> 小程序-微信
|
||||
- 发行APP: 点击工具栏 发行 -> 原生App-云打包
|
||||
<br/>
|
||||
|
||||
|
||||
## ⭐️ 功能清单
|
||||
|
||||
### 用户部分
|
||||
1. - [x] 文章分类
|
||||
2. - [x] 文章详情&海报分享
|
||||
3. - [x] 文章归档
|
||||
4. - [x] 文章列表和搜索
|
||||
5. - [x] 数据统计展示
|
||||
6. - [x] 图库
|
||||
7. - [x] 友链
|
||||
8. - [x] 留言板
|
||||
9. - [x] 个人日记
|
||||
|
||||
### 管理部分
|
||||
1. - [x] 个人信息修改
|
||||
2. - [x] 个人密码修改
|
||||
3. - [x] 日记管理
|
||||
4. - [x] 文章管理
|
||||
5. - [x] 分类管理
|
||||
6. - [x] 标签管理
|
||||
7. - [x] 附件管理
|
||||
8. - [x] 友链管理
|
||||
9. - [x] 图库管理
|
||||
10. - [x] 评论管理
|
||||
11. - [x] 日志管理
|
||||
12. - [ ] 菜单设置
|
||||
13. - [ ] 系统设置
|
||||
|
||||
## 📃 TODO
|
||||
1. - [ ] 暗黑模式
|
||||
2. - [ ] 国际化
|
||||
|
||||
|
||||
## 🌈 在线体验
|
||||
|
||||
- 敬请期待...
|
||||
|
||||
|
||||
## 🔍 许可证
|
||||
## 🔍 开源许可
|
||||
|
||||
uni-halo 使用 AGPL-3.0 协议开源,请遵守开源协议。
|
||||
|
||||
## 🏭 贡献
|
||||
<br/>
|
||||
|
||||
贡献代码请查看 [代码贡献规范](https://uni-halo.925i.cn/standard/category.html)
|
||||
## 🙋 致谢
|
||||
|
||||
## 🙆♂ Halo
|
||||
- 接口文档地址(内容端):<https://api.halo.run/content-api.html>
|
||||
- 接口文档地址(管理端):<https://api.halo.run/admin-api.html>
|
||||
uni-halo 得益于以下优秀的技术支撑,我只是站在巨人的肩膀上捡了几根头发丝,但是非常感谢这些优秀的项目,带给了我们其他开发者和用户非常友好的体验!
|
||||
|
||||
- [Halo:一款好用又强大的开源建站工具](https://halo.run/)
|
||||
- [uni-app:是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码可编译到多个平台](https://uniapp.dcloud.net.cn/)
|
||||
- [tm-vuetify:是一个为 uni-app 平台定制的颜值也非常高的 UI 框架](https://www.jx2d.cn/)
|
||||
- [mp-html:一个强大的小程序富文本组件](https://jin-yufeng.gitee.io/mp-html/#/)
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/**
|
||||
* 附件管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/attachment-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 分页获取附件列表
|
||||
* {
|
||||
* "attachmentType": "ALIOSS" "BAIDUBOS" "HUAWEIOBS" "LOCAL" "MINIO" "QINIUOSS" "SMMS" "TENCENTCOS" "UPOSS",
|
||||
* "keyword": "string"
|
||||
* "mediaType": "string"
|
||||
* "page": "string"
|
||||
* "size": "string"
|
||||
* "sort": "string"
|
||||
* }
|
||||
*/
|
||||
getAttachmentsByPage: (params) => {
|
||||
return HttpHandler.Get('/api/admin/attachments', params, {})
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取所有的附件类型
|
||||
*/
|
||||
getAttachmentsMediaTypes: () => {
|
||||
return HttpHandler.Get('/api/admin/attachments/media_types')
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据附件类型获取所有的附件列表
|
||||
*/
|
||||
getAttachmentsTypes: () => {
|
||||
return HttpHandler.Get('/api/admin/attachments/types')
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据附件Id获取附件详情
|
||||
*/
|
||||
getAttachmentsById: (attachmentId) => {
|
||||
return HttpHandler.Get(`/api/admin/attachments/${attachmentId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 上传附件-单文件(file)
|
||||
* {
|
||||
* file:文件对象
|
||||
* }
|
||||
*/
|
||||
uploadAttachment: (data) => {
|
||||
return HttpHandler.Upload(`/api/admin/attachments/upload`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 上传附件-多文件(files)
|
||||
* {
|
||||
* files:文件对象集合
|
||||
* }
|
||||
*/
|
||||
uploadAttachments: (data) => {
|
||||
return HttpHandler.Upload(`/api/admin/attachments/uploads`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改一个附件信息
|
||||
*/
|
||||
updateAttachmentById: (attachmentId, name) => {
|
||||
return HttpHandler.Put(`/api/admin/attachments/${attachmentId}`, {
|
||||
name: name
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量删除附件(id集合)
|
||||
*/
|
||||
deleteAttachmentByIds: (attachmentIds = []) => {
|
||||
return HttpHandler.Delete(`/api/admin/attachments`, attachmentIds)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除单个附件
|
||||
*/
|
||||
deleteAttachmentById: (attachmentId) => {
|
||||
return HttpHandler.Delete(`/api/admin/attachments/${attachmentId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* 文章分类管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/category-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询所有的文章分类
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* "more": "Boolean" ,// 更多参数(回调)
|
||||
* }
|
||||
*/
|
||||
getCategoryList: (params) => {
|
||||
return HttpHandler.Get('/api/admin/categories', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询所有的文章分类(树形数据)
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* }
|
||||
*/
|
||||
getCategoryListTree: (params) => {
|
||||
return HttpHandler.Get('/api/admin/categories/tree_view', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询文章分类详情
|
||||
* @param {Number} categoryId 分类ID
|
||||
*/
|
||||
getCategoryDetail: (categoryId) => {
|
||||
return HttpHandler.Get(`/api/admin/categories/${categoryId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增文章分类
|
||||
* {
|
||||
* "description": "string",
|
||||
* "id": 0,
|
||||
* "name": "string",
|
||||
* "parentId": 0,
|
||||
* "password": "string",
|
||||
* "priority": 0,
|
||||
* "slug": "string",
|
||||
* "thumbnail": "string"
|
||||
* }
|
||||
*/
|
||||
createCategory: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/categories`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章分类信息
|
||||
* @param {Number} categoryId 分类id
|
||||
* @param {Object} data 同新增
|
||||
*/
|
||||
updateCategoryById: (categoryId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/categories/${categoryId}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除单个文章分类
|
||||
* @param {Number} categoryId 文章分类id
|
||||
*/
|
||||
deleteCategoryById: (categoryId) => {
|
||||
return HttpHandler.Delete(`/api/admin/categories/${categoryId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/**
|
||||
* 文章评论管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/journal-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询文章评论
|
||||
* {
|
||||
* "keyword":"", // 关键字
|
||||
* "page": 0, // 分页索引
|
||||
* "size": 10, // 分页大小
|
||||
* "sort": ["",""], // 排序
|
||||
* "status": "" , // 类型 "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
* }
|
||||
*/
|
||||
getPostsComments: (params) => {
|
||||
return HttpHandler.Get('/api/admin/posts/comments', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 回复文章评论
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postPostsComments: (data) => {
|
||||
return HttpHandler.Post('/api/admin/posts/comments', data)
|
||||
},
|
||||
/**
|
||||
* 更新文章评论状态
|
||||
* @param {Number} commentId id
|
||||
* @param {String} status "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
*/
|
||||
updatePostsCommentsStatus: (commentId, status) => {
|
||||
return HttpHandler.Put(`/api/admin/posts/comments/${commentId}/status/${status}`)
|
||||
},
|
||||
/**
|
||||
* 删除文章评论
|
||||
* @param {Number} commentId id
|
||||
*/
|
||||
deletePostsCommentsById: (commentId) => {
|
||||
return HttpHandler.Delete(`/api/admin/posts/comments/${commentId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询页面评论
|
||||
* {
|
||||
* "keyword":"", // 关键字
|
||||
* "page": 0, // 分页索引
|
||||
* "size": 10, // 分页大小
|
||||
* "sort": ["",""], // 排序
|
||||
* "status": "" , // 类型 "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
* }
|
||||
*/
|
||||
getSheetsComments: (params) => {
|
||||
return HttpHandler.Get('/api/admin/sheets/comments', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 回复页面评论
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postSheetsComments: (data) => {
|
||||
return HttpHandler.Post('/api/admin/sheets/comments', data)
|
||||
},
|
||||
/**
|
||||
* 更新页面评论状态
|
||||
* @param {Number} commentId id
|
||||
* @param {String} status "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
*/
|
||||
updateSheetsCommentsStatus: (commentId, status) => {
|
||||
return HttpHandler.Put(`/api/admin/sheets/comments/${commentId}/status/${status}`)
|
||||
},
|
||||
/**
|
||||
* 删除页面评论
|
||||
* @param {Number} commentId id
|
||||
*/
|
||||
deleteSheetsCommentsById: (commentId) => {
|
||||
return HttpHandler.Delete(`/api/admin/sheets/comments/${commentId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询日记评论
|
||||
* {
|
||||
* "keyword":"", // 关键字
|
||||
* "page": 0, // 分页索引
|
||||
* "size": 10, // 分页大小
|
||||
* "sort": ["",""], // 排序
|
||||
* "status": "" , // 类型 "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
* }
|
||||
*/
|
||||
getJournalsComments: (params) => {
|
||||
return HttpHandler.Get('/api/admin/journals/comments', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 回复日记评论
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postJournalsComments: (data) => {
|
||||
return HttpHandler.Post('/api/admin/journals/comments', data)
|
||||
},
|
||||
/**
|
||||
* 更新日记评论状态
|
||||
* @param {Number} commentId id
|
||||
* @param {String} status "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
*/
|
||||
updateJournalsCommentsStatus: (commentId, status) => {
|
||||
return HttpHandler.Put(`/api/admin/journals/comments/${commentId}/status/${status}`)
|
||||
},
|
||||
/**
|
||||
* 删除日记评论
|
||||
* @param {Number} commentId id
|
||||
*/
|
||||
deleteJournalsCommentsById: (commentId) => {
|
||||
return HttpHandler.Delete(`/api/admin/journals/comments/${commentId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* 个人日记管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/journal-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询所有的日记列表
|
||||
* {
|
||||
* "keyword":"", // 关键字
|
||||
* "page": 0, // 分页索引
|
||||
* "size": 10, // 分页大小
|
||||
* "sort": ["",""], // 排序
|
||||
* "type": "" , // 类型 "INTIMATE" "PUBLIC"
|
||||
* }
|
||||
*/
|
||||
getJournals: (params) => {
|
||||
return HttpHandler.Get('/api/admin/journals', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询最近的所有的日记列表
|
||||
* {
|
||||
* "top":number, // 数量
|
||||
* }
|
||||
*/
|
||||
getLatestJournals: (params) => {
|
||||
return HttpHandler.Get('/api/admin/journals/latest', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增个人日记
|
||||
* {
|
||||
* "content": "string",
|
||||
* "keepRaw": true,
|
||||
* "sourceContent": "string",
|
||||
* "type": "INTIMATE",
|
||||
* }
|
||||
*/
|
||||
createJournals: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/journals`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改个人日记信息
|
||||
* @param {Number} journalsId id
|
||||
* @param {Object} data 同新增
|
||||
*/
|
||||
updateJournalsById: (journalsId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/journals/${journalsId}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除个人日记
|
||||
* @param {Number} journalsId id
|
||||
*/
|
||||
deleteJournalsById: (journalsId) => {
|
||||
return HttpHandler.Delete(`/api/admin/journals/${journalsId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* 友链管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/link-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
// 获取友链列表
|
||||
getLinkList: () => {
|
||||
return HttpHandler.Get('/api/admin/links')
|
||||
},
|
||||
/**
|
||||
* 获取友链详情
|
||||
* @params { Number } linkId 友链Id
|
||||
*/
|
||||
getLinkDetail: (linkId) => {
|
||||
return HttpHandler.Get(`/api/admin/links/${linkId}`)
|
||||
},
|
||||
/**
|
||||
* 新增友链
|
||||
* {
|
||||
* "description": "string",
|
||||
* "logo": "string",
|
||||
* "name": "string",
|
||||
* "priority": 0,
|
||||
* "team": "string",
|
||||
* "url": "string"
|
||||
* }
|
||||
*/
|
||||
addLink: (data) => {
|
||||
return HttpHandler.Post('/api/admin/links', data, {})
|
||||
},
|
||||
/**
|
||||
* 修改友链
|
||||
* {
|
||||
* "description": "string",
|
||||
* "logo": "string",
|
||||
* "name": "string",
|
||||
* "priority": 0,
|
||||
* "team": "string",
|
||||
* "url": "string"
|
||||
* }
|
||||
*/
|
||||
updateLink: (linkId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/links/${linkId}`, data, {})
|
||||
},
|
||||
/**
|
||||
* 删除友链
|
||||
* @params { Number } linkId 友链Id
|
||||
*/
|
||||
deleteLink: (linkId) => {
|
||||
return HttpHandler.Delete(`/api/admin/links/${linkId}`)
|
||||
},
|
||||
|
||||
// 获取友链分组
|
||||
getLinkTeamList: (data) => {
|
||||
return HttpHandler.Get('/api/admin/links/teams')
|
||||
},
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* 日志管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/link-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取日志列表列表
|
||||
* params:{ top: Number}
|
||||
*/
|
||||
getLogsLatestList: (params) => {
|
||||
return HttpHandler.Get('/api/admin/logs/latest', params)
|
||||
},
|
||||
/**
|
||||
* 获取日志列表列表
|
||||
* params:{ page:Number,size:Number, sort:String }
|
||||
*/
|
||||
getLogsListByPage: (params) => {
|
||||
return HttpHandler.Get('/api/admin/logs', params)
|
||||
},
|
||||
/**
|
||||
* 清空日志
|
||||
*/
|
||||
deleteAllLogs: () => {
|
||||
return HttpHandler.Get(`/api/admin/logs/clear`)
|
||||
},
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/**
|
||||
* 图库管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/photo-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询图片列表
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* "more": "Boolean" ,// 更多参数(回调)
|
||||
* }
|
||||
*/
|
||||
getPhotos: (params) => {
|
||||
return HttpHandler.Get('/api/admin/photos', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询最近的图库列表(树形数据)
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* }
|
||||
*/
|
||||
getLatestPhotos: (params) => {
|
||||
return HttpHandler.Get('/api/admin/photos/latest', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询所有的图片分组
|
||||
*/
|
||||
getPhotosTeams: () => {
|
||||
return HttpHandler.Get('/api/admin/photos/teams')
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询图片详情
|
||||
* @param {Number} photoId id
|
||||
*/
|
||||
getPhotosDetail: (photoId) => {
|
||||
return HttpHandler.Get(`/api/admin/photos/${photoId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增图片(单图)
|
||||
*{
|
||||
* "description": "string",
|
||||
* "id": 0,
|
||||
* "location": "string",
|
||||
* "name": "string",
|
||||
* "takeTime": "2019-08-24T14:15:22Z",
|
||||
* "team": "string",
|
||||
* "thumbnail": "string",
|
||||
* "url": "string"
|
||||
*}
|
||||
*/
|
||||
createPhotos: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/photos`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增图片(批量)
|
||||
* {
|
||||
* "description": "string",
|
||||
* "id": 0,
|
||||
* "location": "string",
|
||||
* "name": "string",
|
||||
* "takeTime": "2019-08-24T14:15:22Z",
|
||||
* "team": "string",
|
||||
* "thumbnail": "string",
|
||||
* "url": "string"
|
||||
* }
|
||||
*/
|
||||
createPhotosBatch: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/photos/batch`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改图片信息
|
||||
* @param {Number} photoId id
|
||||
* @param {Object} data 同新增
|
||||
*/
|
||||
updatePhotosById: (photoId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/photos/${photoId}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除单张图片
|
||||
* @param {Number} photoId id
|
||||
*/
|
||||
deletePhotosById: (photoId) => {
|
||||
return HttpHandler.Delete(`/api/admin/photos/${photoId}`)
|
||||
},
|
||||
/**
|
||||
* 批量删除图片
|
||||
* @param {Number} photoIds id数组
|
||||
*/
|
||||
deletePhotosBatchById: (photoIds) => {
|
||||
return HttpHandler.Delete(`/api/admin/photos/batch`, photoIds)
|
||||
},
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/**
|
||||
* 文章管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/post-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
/**
|
||||
* 新建和编辑文章字段
|
||||
*/
|
||||
const createOrEditModel = {
|
||||
"categoryIds": [
|
||||
0
|
||||
],
|
||||
"content": "string",
|
||||
"createTime": "2019-08-24T14:15:22Z",
|
||||
"disallowComment": true,
|
||||
"editorType": "MARKDOWN",
|
||||
"keepRaw": true,
|
||||
"metaDescription": "string",
|
||||
"metaKeywords": "string",
|
||||
"metas": [{
|
||||
"key": "string",
|
||||
"postId": 0,
|
||||
"value": "string"
|
||||
}],
|
||||
"originalContent": "string",
|
||||
"formatContent": "",
|
||||
"password": "string",
|
||||
"slug": "string",
|
||||
"status": "DRAFT",
|
||||
"summary": "string",
|
||||
"tagIds": [
|
||||
0
|
||||
],
|
||||
"template": "string",
|
||||
"thumbnail": "string",
|
||||
"title": "string",
|
||||
"topPriority": 0
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询文章列表
|
||||
* @param {Object} params {
|
||||
* categoryId,keyword,page,size,sort,
|
||||
* status:"DRAFT" "INTIMATE" "PUBLISHED" "RECYCLE",statuses,more
|
||||
* }
|
||||
*/
|
||||
getPostsByPage: (params) => {
|
||||
return HttpHandler.Get('/api/admin/posts', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询最近的文章列表
|
||||
* @param {Object} params {top:Number}
|
||||
*/
|
||||
getLatestPosts: (params) => {
|
||||
return HttpHandler.Get('/api/admin/posts/latest', params)
|
||||
},
|
||||
/**
|
||||
* 根据状态查询文章列表
|
||||
* @param {String} status:"DRAFT" "INTIMATE" "PUBLISHED" "RECYCLE"
|
||||
* @param {Object} params:{ page,size,sort,more }
|
||||
*/
|
||||
getPostsPageByStatus: (status, params) => {
|
||||
return HttpHandler.Get(`/api/admin/posts/status/${status}`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据文章id获取文章
|
||||
* @param {Number} postsId 文章id
|
||||
*/
|
||||
getPostsById: (postsId) => {
|
||||
return HttpHandler.Get(`/api/admin/posts/${postsId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增文章
|
||||
* @param {Object} data 同新增
|
||||
* @param {Boolean} isAutoSave 是否来源于自动保存
|
||||
*/
|
||||
createPosts: (data, isAutoSave = false) => {
|
||||
return HttpHandler.Post(`/api/admin/posts?autoSave=${isAutoSave}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章
|
||||
* @param {Number} postsId id
|
||||
* @param {Object} data 同新增
|
||||
* @param {Boolean} isAutoSave 是否来源于自动保存
|
||||
*/
|
||||
updatePostsById: (postsId, data, isAutoSave = false) => {
|
||||
return HttpHandler.Put(`/api/admin/posts/${postsId}?autoSave=${isAutoSave}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章(草稿)
|
||||
* @param {Number} postsId id
|
||||
* @param {Object} data { content,keepRaw,originalContent }
|
||||
*/
|
||||
updatePostsDraftById: (postsId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/posts/${postsId}/status/draft/content`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章状态
|
||||
* @param {Number} postsId id
|
||||
* @param {String} status "DRAFT" "INTIMATE" "PUBLISHED" "RECYCLE"
|
||||
*/
|
||||
updatePostsDraftById: (postsId, status) => {
|
||||
return HttpHandler.Put(`/api/admin/posts/${postsId}/status/${status}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除文章(批量)
|
||||
* @param {Array} postsIds ids
|
||||
*/
|
||||
deletePostsByIds: (postsIds) => {
|
||||
return HttpHandler.Delete(`/api/admin/posts`, postsIds)
|
||||
},
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* 标签管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/tag-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询文章标签列表
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* "more": "Boolean" ,// 更多参数(回调)
|
||||
* }
|
||||
*/
|
||||
getTagsList: (params) => {
|
||||
return HttpHandler.Get('/api/admin/tags', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询文章标签详情
|
||||
* @param {Number} tagId id
|
||||
*/
|
||||
getTagsDetail: (tagId) => {
|
||||
return HttpHandler.Get(`/api/admin/tags/${tagId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增文章标签
|
||||
* {
|
||||
* "color": "#e23d66", // 颜色选择器
|
||||
* "name": "string",
|
||||
* "slug": "string",
|
||||
* "thumbnail": "string"
|
||||
* }
|
||||
*/
|
||||
createTags: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/tags`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章标签信息
|
||||
* @param {Number} tagId id
|
||||
* @param {Object} data 同新增
|
||||
*/
|
||||
updateTagsById: (tagId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/tags/${tagId}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除文章标签
|
||||
* @param {Number} tagId id
|
||||
*/
|
||||
deleteTagsById: (tagId) => {
|
||||
return HttpHandler.Delete(`/api/admin/tags/${tagId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/**
|
||||
* 登录管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/admin-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
// 登录前检查
|
||||
loginPreCheck: (data) => {
|
||||
return HttpHandler.Post('/api/admin/login/precheck', data, {})
|
||||
},
|
||||
// 登录
|
||||
login: (data) => {
|
||||
return HttpHandler.Post('/api/admin/login', data, {})
|
||||
},
|
||||
// 刷新token
|
||||
refreshToken: (refreshToken) => {
|
||||
return HttpHandler.Post($`/api/admin/refresh/${refreshToken}`, {}, {})
|
||||
},
|
||||
|
||||
// 退出登录
|
||||
logout: () => {
|
||||
return HttpHandler.Post('/api/admin/logout')
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取修改密码的验证码
|
||||
* {
|
||||
* "email": "string",
|
||||
* "username": "string"
|
||||
* }
|
||||
*/
|
||||
getResetPasswordCode: () => {
|
||||
return HttpHandler.Post('/api/admin/password/code')
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
* {
|
||||
* "code": "string",
|
||||
* "email": "string",
|
||||
* "password": "stringst",
|
||||
* "username": "string"
|
||||
* }
|
||||
*/
|
||||
resetPasswordByCode: (data) => {
|
||||
return HttpHandler.Put('/api/admin/password/reset', data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取个人信息(当前登录的管理员)
|
||||
*/
|
||||
getAdminProfile: () => {
|
||||
return HttpHandler.Get('/api/admin/users/profiles')
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改个人信息(当前登录的管理员)
|
||||
* {
|
||||
* "avatar": "string",
|
||||
* "description": "string",
|
||||
* "email": "string",
|
||||
* "nickname": "string",
|
||||
* "password": "stringst",
|
||||
* "username": "string"
|
||||
* }
|
||||
*/
|
||||
updateAdminProfile: (data) => {
|
||||
return HttpHandler.Put('/api/admin/users/profiles', data)
|
||||
},
|
||||
/**
|
||||
* 修改密码
|
||||
* {
|
||||
* "confirmPassword": "string",
|
||||
* "newPassword": "string",
|
||||
* "oldPassword": "strings"
|
||||
* }
|
||||
*/
|
||||
modifyAdminPassword: (data) => {
|
||||
return HttpHandler.Put('/api/admin/users/profiles/password', data)
|
||||
},
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* 归档接口
|
||||
* @see https://api.halo.run/content-api.html#tag/archive-controlle
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取归档列表(按月)
|
||||
*/
|
||||
getMonthArchives: () => {
|
||||
return HttpHandler.Get(`/api/content/archives/months`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取归档列表(按年)
|
||||
*/
|
||||
getYearArchives: () => {
|
||||
return HttpHandler.Get(`/api/content/archives/years`)
|
||||
},
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* 文章接口
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
export default {
|
||||
/**
|
||||
* 获取文章列表
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getArticleList: (params) => {
|
||||
return HttpHandler.Get('/api/content/posts', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取文章详情
|
||||
* @param {String} articleId 文章id
|
||||
*/
|
||||
getArticleDetail: (articleId) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${articleId}`, {
|
||||
formatDisabled: false,
|
||||
sourceDisabled: true
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* 博主信息
|
||||
* @see https://api.halo.run/content-api.html#tag/user-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取博主信息
|
||||
*/
|
||||
getBloggerInfo: () => {
|
||||
return HttpHandler.Get(`/api/content/users/profile`)
|
||||
},
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 分类接口
|
||||
* @see https://api.halo.run/content-api.html#tag/category-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
export default {
|
||||
/**
|
||||
* 查询分类列表
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getCategoryList: (params) => {
|
||||
return HttpHandler.Get('/api/content/categories', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询分类下的文章
|
||||
* @param {String} slug 分类名称
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getCategoryPostList: (slug, params) => {
|
||||
return HttpHandler.Get(`/api/content/categories/${slug}/posts`, params)
|
||||
},
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* 评论接口
|
||||
* @see https://api.halo.run/content-api.html#tag/post-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取评论列表接口(树形数据)
|
||||
* @param {String} postId 文章id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getPostCommentTree: (postId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/comments/tree_view`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取评论列表接口(列表数据)
|
||||
* @param {String} postId 文章id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getPostCommentList: (postId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/comments/list_view`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取置顶评论
|
||||
* @param {String} postId 文章id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getPostTopCommentList: (postId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/comments/top_view`, params)
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 获取评论的子评论列表
|
||||
* @param {String} postId 文章id
|
||||
* @param {String} commentParentId 要获取的评论id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getPostChildrenCommentList: (postId, commentParentId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/comments/${commentParentId}/children`, params)
|
||||
},
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/**
|
||||
* 功能:全局API管理
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 19:14:44
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
import archive from './archive.js'
|
||||
import article from './article.js'
|
||||
import blogger from './blogger.js'
|
||||
import category from './category.js'
|
||||
import tag from './tag.js'
|
||||
import comment from './comment.js'
|
||||
import journal from './journal.js'
|
||||
import link from './link.js'
|
||||
import menu from './menu.js'
|
||||
import option from './option.js'
|
||||
import photo from './photo.js'
|
||||
import post from './post.js'
|
||||
import sheet from './sheet.js'
|
||||
import statistics from './statistics.js'
|
||||
import theme from './theme.js'
|
||||
|
||||
// 管理端
|
||||
import admin_login from './admin/user.js'
|
||||
import admin_links from './admin/links.js'
|
||||
import admin_attachment from './admin/attachment.js'
|
||||
import admin_category from './admin/category.js'
|
||||
import admin_journal from './admin/journal.js'
|
||||
import admin_photos from './admin/photos.js'
|
||||
import admin_tags from './admin/tags.js'
|
||||
import admin_comments from './admin/comments.js'
|
||||
import admin_posts from './admin/posts.js'
|
||||
import admin_logs from './admin/logs.js'
|
||||
|
||||
const ApiManager = {
|
||||
...archive,
|
||||
...article,
|
||||
...blogger,
|
||||
...category,
|
||||
...tag,
|
||||
...comment,
|
||||
...journal,
|
||||
...link,
|
||||
...option,
|
||||
...photo,
|
||||
...post,
|
||||
...sheet,
|
||||
...statistics,
|
||||
...theme,
|
||||
// 管理端的api
|
||||
admin: {
|
||||
...admin_login,
|
||||
...admin_links,
|
||||
...admin_attachment,
|
||||
...admin_category,
|
||||
...admin_journal,
|
||||
...admin_photos,
|
||||
...admin_tags,
|
||||
...admin_comments,
|
||||
...admin_posts,
|
||||
...admin_logs
|
||||
}
|
||||
};
|
||||
|
||||
const install = (Vue) => {
|
||||
Vue.prototype.$httpApi = ApiManager
|
||||
}
|
||||
|
||||
export default {
|
||||
install
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* 日记接口
|
||||
* @see https://api.halo.run/content-api.html#tag/journal-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取日记列表
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournals: () => {
|
||||
return HttpHandler.Get(`/api/content/journals`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记详情
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournalDetail: (journalId) => {
|
||||
return HttpHandler.Get(`/api/content/journals/${journalId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记置顶评论列表
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournalTopComments: (journalId) => {
|
||||
return HttpHandler.Get(`/api/content/journals/${journalId}/comments/top_view`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记评论列表(列表形式)
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournalCommentList: (journalId) => {
|
||||
return HttpHandler.Get(`/api/content/journals/${journalId}/comments/list_view`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记评论列表(树形式)
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournalCommentTree: (journalId) => {
|
||||
return HttpHandler.Get(`/api/content/journals/${journalId}/comments/tree_view`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记评论列表(树形式)
|
||||
* @param {String} journalId 日记id
|
||||
* @param {String} commentParentId 评论id
|
||||
*/
|
||||
getJournalCommentChildren: (journalId, commentParentId) => {
|
||||
return HttpHandler.Get(
|
||||
`/api/content/journals/${journalId}/comments/${commentParentId}/children`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 发表日记评论
|
||||
* @param {Object} data 评论数据
|
||||
*/
|
||||
postJournalComments: (data) => {
|
||||
return HttpHandler.Post(`/api/content/journals/comments`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 给日记点赞
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
postJournalLikes: (journalId) => {
|
||||
return HttpHandler.Post(`/api/content/journals/${journalId}/likes`)
|
||||
},
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 友链接口
|
||||
* @see https://api.halo.run/content-api.html#tag/link-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取友链列表
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getLinkList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/links`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取分组的友链列表
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getLinkListByTeam: (params) => {
|
||||
return HttpHandler.Get(`/api/content/links/team_view`, params)
|
||||
},
|
||||
}
|
||||
-112
@@ -1,112 +0,0 @@
|
||||
/**
|
||||
* 普通用户登录
|
||||
*/
|
||||
|
||||
// 获取用户信息
|
||||
export function getUserInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.getUserProfile({
|
||||
lang: 'zh_CN',
|
||||
desc: '用户登录', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,
|
||||
success: (res) => {
|
||||
console.log(res, 'resss')
|
||||
resolve(res.userInfo)
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function getLogin() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.login({
|
||||
success(res) {
|
||||
console.log('----------getLogin ---------')
|
||||
console.log(res)
|
||||
resolve(res)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err, 'logoer')
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function wxLogin() {
|
||||
uni.getProvider({
|
||||
service: 'oauth',
|
||||
success: function(res) {
|
||||
//支持微信、qq和微博等
|
||||
if (~res.provider.indexOf('weixin')) {
|
||||
console.log(res, 'ress')
|
||||
let _userInfo = getUserInfo();
|
||||
let _loginRes = getLogin();
|
||||
Promise.all([_userInfo, _loginRes]).then((res) => {
|
||||
let userInfo = res[0];
|
||||
let loginRes = res[1];
|
||||
if (loginRes.errMsg == 'login:ok') {
|
||||
uni.$tm.vx.commit('user/setWxLoginInfo', {
|
||||
avatarUrl: userInfo.avatarUrl,
|
||||
nickName: userInfo.nickName,
|
||||
email: '',
|
||||
url: ''
|
||||
});
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录成功!'
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录失败,请重试!'
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录失败,请重试!'
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: function(err) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录失败,请重试!'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function appWxLogin() {
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
success: function(loginRes) {
|
||||
// 获取用户信息
|
||||
uni.getUserInfo({
|
||||
provider: 'weixin',
|
||||
success: function(infoRes) {
|
||||
uni.$tm.vx.commit('user/setWxLoginInfo', {
|
||||
avatarUrl: infoRes.userInfo.avatarUrl,
|
||||
nickName: infoRes.userInfo.nickName,
|
||||
email: '',
|
||||
url: ''
|
||||
});
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录成功!'
|
||||
})
|
||||
},
|
||||
fail: function(err) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录失败,请重试!'
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 菜单接口
|
||||
* @see https://api.halo.run/content-api.html#tag/menu-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取菜单列表(列表)
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getMenuList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/menus`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取菜单列表(树形)
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getMenuTree: (params) => {
|
||||
return HttpHandler.Get(`/api/content/menus/tree_view`, params)
|
||||
},
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* 配置接口
|
||||
* @see https://api.halo.run/content-api.html#tag/option-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 根据key获取配置
|
||||
* @param {String} key 配置的key
|
||||
*/
|
||||
getOptionByKey: (key) => {
|
||||
return HttpHandler.Get(`/api/content/options/keys/${key}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取配置列表(列表)
|
||||
*/
|
||||
getOptionList: () => {
|
||||
return HttpHandler.Get(`/api/content/options/list_view`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取配置列表(键值对)
|
||||
*/
|
||||
getOptionMap: () => {
|
||||
return HttpHandler.Get(`/api/content/options/map_view`)
|
||||
},
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* 图库接口
|
||||
* @see https://api.halo.run/content-api.html#tag/photo-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取图库列表(分页查询)
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPhotoListByPage: (params) => {
|
||||
return HttpHandler.Get(`/api/content/photos`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取图库列表(最新)
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPhotoList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/photos/latest`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取图库分组
|
||||
*/
|
||||
getPhotoTeams: () => {
|
||||
return HttpHandler.Get(`/api/content/photos/teams`)
|
||||
},
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/**
|
||||
* 文章接口
|
||||
* @see https://api.halo.run/content-api.html#tag/post-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取文章列表
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPostList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/posts`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 评论文章
|
||||
* @param {Object} data 数据
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postCommentPost: (data) => {
|
||||
return HttpHandler.Post(`/api/content/posts/comments`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 搜索文章
|
||||
* @param {Object} data 数据
|
||||
*/
|
||||
getPostListByKeyword: (data) => {
|
||||
return HttpHandler.Post(`/api/content/posts/search`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据分类获取文章
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPostDetailBySlug: (params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/slug`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据文章id获取文章
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPostDetailByPostId: (postId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 给文章点赞
|
||||
* @param {Object} postId 文章id
|
||||
*/
|
||||
postLikePost: (postId) => {
|
||||
return HttpHandler.Post(`/api/content/posts/${postId}/likes`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据当前文章id获取前一篇文章
|
||||
* @param {Object} postId 文章id
|
||||
*/
|
||||
getPrevByCurrentPostId: (postId) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/prev`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据当前文章id获取下一篇文章
|
||||
* @param {Object} postId 文章id
|
||||
*/
|
||||
getNextByCurrentPostId: (postId) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/next`)
|
||||
},
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/**
|
||||
* 自定义页面模板
|
||||
* @see https://api.halo.run/content-api.html#tag/sheet-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取页面列表
|
||||
* {
|
||||
* page:
|
||||
* size:
|
||||
* sort:
|
||||
* }
|
||||
*/
|
||||
getSheetsList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据分类获取页面数据
|
||||
*/
|
||||
getSheetsListBySlug: (params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets/slug`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取页面评论(列表)
|
||||
*/
|
||||
getSheetsCommentsListBySheetId: (sheetId, params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets/${sheetId}/comments/list_view`, params)
|
||||
},
|
||||
/**
|
||||
* 获取页面评论(树形)
|
||||
*/
|
||||
getSheetsCommentsTreeBySheetId: (sheetId, params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets/${sheetId}/comments/tree_view`, params)
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 获取评论的子评论列表
|
||||
* @param {String} sheetId 页面id
|
||||
* @param {String} commentParentId 要获取的评论id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getSheetsChildrenCommentList: (sheetId, commentParentId, params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets/${sheetId}/comments/${commentParentId}/children`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 给页面添加一个评论
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postSheetsComments: (data) => {
|
||||
return HttpHandler.Post(`/api/content/sheets/comments`, data)
|
||||
},
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* 博客统计信息
|
||||
* @see https://api.halo.run/content-api.html#tag/statistic-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取博客统计信息
|
||||
*/
|
||||
getBlogStatistics: () => {
|
||||
return HttpHandler.Get(`/api/content/statistics`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取博客统计信息和用户信息
|
||||
*/
|
||||
getBlogStatisticsWithUser: () => {
|
||||
return HttpHandler.Get(`/api/content/statistics/user`)
|
||||
},
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 标签接口
|
||||
* @see https://api.halo.run/content-api.html#tag/tag-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
export default {
|
||||
/**
|
||||
* 查询标签列表
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getTagList: (params) => {
|
||||
return HttpHandler.Get('/api/content/tags', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询标签下的文章
|
||||
* @param {String} slug 别名
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getTagPostsList: (slug, params) => {
|
||||
return HttpHandler.Get(`/api/content/tags/${slug}/posts`, params)
|
||||
},
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
* 主题设置
|
||||
* @see https://api.halo.run/content-api.html#tag/theme-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取激活主题的信息
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
geActivationThemeList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/themes/activation`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取激活的主题设置
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getActivationThemeSettings: (params) => {
|
||||
return HttpHandler.Get(`/api/content/themes/activation/settings`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据主题ID列出主题设置
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getThemeSettingsByThemeId: (themeId) => {
|
||||
return HttpHandler.Get(`/api/content/themes/${themeId}/settings`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 通过主题ID获取主题属性
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getThemePropertyByThemeId: (themeId) => {
|
||||
return HttpHandler.Get(`/api/content/themes/${themeId}`)
|
||||
},
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* 功能:全局过滤器
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 17:39:04
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 功能描述:时间格式化,将指定的时间戳(或正常的日期)转换为带格式的日期
|
||||
*
|
||||
* 参数说明:
|
||||
* 1.支持格式化 yyyy年MM月dd日 HH点mm分ss秒 星期w q季
|
||||
* 2.对象形式传入 { d:'2021-06-04',f:'yyyy年' } d是必传项,f可不传(默认yyyy-MM-dd HH:mm:ss)
|
||||
* 使用示例:
|
||||
* 1:<view>{{ dateTimeParamName | formatTime }}</view>
|
||||
* 2:<view>{{ { d: '2021-06-04', f: 'yyyy' } | formatTime }}</view>
|
||||
* 3:<view>{{ { d: dateTimeParamName, f: 'yyyy年MM月dd日 HH点mm分ss秒 星期w q季' } | formatTime }}</view>
|
||||
* 特别说明: 由于uniapp中的filter 不支持多参数,但是允许传入对象的形式,故以此方式实现!
|
||||
*/
|
||||
formatTime: function(data) {
|
||||
let _dateTime = new Date(data);
|
||||
let _fmt = 'yyyy-MM-dd HH:mm:ss';
|
||||
if (_dateTime == 'Invalid Date') {
|
||||
if (data.d == undefined || data.d == null || data.d == "") {
|
||||
console.error('日期参数不正确,传入的参数列表:', data);
|
||||
return ''
|
||||
};
|
||||
_dateTime = new Date(data.d);
|
||||
if (_dateTime == 'Invalid Date') {
|
||||
console.error('日期参数不正确,传入的参数列表:', data);
|
||||
return '111'
|
||||
}
|
||||
if (data.hasOwnProperty('f')) {
|
||||
_fmt = data.f
|
||||
}
|
||||
}
|
||||
const _weekDays = ["日", "一", "二", "三", "四", "五", "六"];
|
||||
const _seasons = ["冬", "春", "夏", "秋"];
|
||||
const o = {
|
||||
"M+": _dateTime.getMonth() + 1, //月份
|
||||
"d+": _dateTime.getDate(), //日
|
||||
"H+": _dateTime.getHours(), //小时
|
||||
"m+": _dateTime.getMinutes(), //分
|
||||
"s+": _dateTime.getSeconds(), //秒
|
||||
"w+": _weekDays[_dateTime.getDay()], // 星期几
|
||||
"q+": _seasons[Math.floor((_dateTime.getMonth() + 3) / 3)], //季度
|
||||
S: _dateTime.getMilliseconds(), //毫秒
|
||||
};
|
||||
if (/(y+)/.test(_fmt)) {
|
||||
_fmt = _fmt.replace(
|
||||
RegExp.$1,
|
||||
(_dateTime.getFullYear() + "").substr(4 - RegExp.$1.length)
|
||||
);
|
||||
}
|
||||
for (let k in o) {
|
||||
if (new RegExp("(" + k + ")").test(_fmt)) {
|
||||
_fmt = _fmt.replace(
|
||||
RegExp.$1,
|
||||
RegExp.$1.length == 1 ?
|
||||
o[k] :
|
||||
("00" + o[k]).substr(("" + o[k]).length)
|
||||
);
|
||||
}
|
||||
}
|
||||
return _fmt;
|
||||
},
|
||||
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* 功能:请求工具
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 18:58:03
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
|
||||
import HaloConfig from '@/config/halo.config.js'
|
||||
import {
|
||||
setInterceptors
|
||||
} from "./interceptors.js";
|
||||
import Request from "@/js_sdk/luch-request/luch-request";
|
||||
|
||||
const http = new Request()
|
||||
/* 设置全局配置 */
|
||||
http.setConfig((config) => {
|
||||
|
||||
// 如果是在外部浏览器调试或者编译为h5,请注释该行代码
|
||||
config.baseURL = HaloConfig.apiUrl;
|
||||
|
||||
config.header = {
|
||||
...config.header,
|
||||
'api-authorization': HaloConfig.apiAuthorization,
|
||||
ContentType: 'application/json',
|
||||
dataType: 'json'
|
||||
}
|
||||
return config
|
||||
})
|
||||
setInterceptors(http)
|
||||
|
||||
export {
|
||||
http
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* 功能:http拦截
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 19:02:14
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
import {
|
||||
getAdminAccessToken
|
||||
} from "@/utils/auth.js";
|
||||
import {
|
||||
delCache
|
||||
} from "@/utils/storage";
|
||||
export const setInterceptors = (http) => {
|
||||
http.interceptors.request.use(
|
||||
(config) => {
|
||||
// 可使用async await 做异步操作
|
||||
config.header = {
|
||||
...config.header
|
||||
// ... 可以直接加参数
|
||||
};
|
||||
if (getAdminAccessToken()) {
|
||||
config.header['admin-authorization'] = getAdminAccessToken()
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(config) => {
|
||||
// 可使用async await 做异步操作
|
||||
return Promise.reject(config);
|
||||
}
|
||||
);
|
||||
|
||||
http.interceptors.response.use(
|
||||
(response) => {
|
||||
/* 对响应成功做点什么 可使用async await 做异步操作*/
|
||||
// if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
|
||||
// return Promise.reject(response) // return Promise.reject 可使promise状态进入catch
|
||||
// if (response.config.custom.verification) { // 演示自定义参数的作用
|
||||
// return response.data
|
||||
// }
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return response.data;
|
||||
} else {
|
||||
return Promise.reject(response);
|
||||
}
|
||||
},
|
||||
(response) => {
|
||||
/* 对响应错误做点什么 (statusCode !== 200)*/
|
||||
if (!response.data) {
|
||||
return Promise.reject({
|
||||
status: 500,
|
||||
message: 'API接口服务异常!'
|
||||
})
|
||||
} else if (response.data.status == 401) {
|
||||
delCache('APP_ADMIN_LOGIN_TOKEN');
|
||||
uni.$eShowModal({
|
||||
title: '提示',
|
||||
content: '您未登录超管账号或登录已过期,是否重新登录?',
|
||||
showCancel: true,
|
||||
cancelText: '否',
|
||||
cancelColor: '#999999',
|
||||
confirmText: '是',
|
||||
confirmColor: '#03a9f4'
|
||||
}).then(res => {
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/login/login'
|
||||
})
|
||||
}).catch(err => {
|
||||
uni.switchTab({
|
||||
url: '/pages/tabbar/about/about'
|
||||
})
|
||||
})
|
||||
} else {
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* 封装各种请求方式
|
||||
*/
|
||||
|
||||
|
||||
import {
|
||||
http
|
||||
} from '@/common/http/index.js'
|
||||
|
||||
export default {
|
||||
Get: (url, params, config = {}) => {
|
||||
return http.get(url, {
|
||||
params,
|
||||
...config
|
||||
})
|
||||
},
|
||||
Post: (url, data, config = {}) => {
|
||||
return http.post(url, data, config)
|
||||
},
|
||||
Put: (url, data, config = {}) => {
|
||||
return http.put(url, data, config)
|
||||
},
|
||||
Upload: (url, config = {}) => {
|
||||
return http.upload(url, config)
|
||||
},
|
||||
Delete: (url, data, config = {}) => {
|
||||
return http.delete(url, data, config)
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* 可以以页面为单位来写,
|
||||
* 比如首页的内容,写在index字段,个人中心写在center,共同部分写在common部分
|
||||
* */
|
||||
|
||||
export default {
|
||||
app: {
|
||||
name: "alley studio",
|
||||
author: "Evan Mo",
|
||||
},
|
||||
tabbar: {
|
||||
home: "Home",
|
||||
moments: "Moments",
|
||||
publish: "Publish",
|
||||
mall: "Mall",
|
||||
mine: "Mine",
|
||||
},
|
||||
// 提示文本
|
||||
tips: {
|
||||
switchLang: "switch Lang",
|
||||
},
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* 可以以页面为单位来写,
|
||||
* 比如首页的内容,写在index字段,个人中心写在center,共同部分写在common部分
|
||||
* */
|
||||
|
||||
export default {
|
||||
app: {
|
||||
name: "巷子工坊",
|
||||
author: "小莫唐尼",
|
||||
},
|
||||
tabbar: {
|
||||
home: "首页",
|
||||
moments: "动态",
|
||||
publish: "发布",
|
||||
mall: "商店",
|
||||
mine: "我的",
|
||||
},
|
||||
// 提示文本
|
||||
tips: {
|
||||
switchLang: "切换语言",
|
||||
},
|
||||
};
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* markdown配置
|
||||
*/
|
||||
|
||||
import HaloConfig from '@/config/halo.config.js'
|
||||
export default {
|
||||
domain: HaloConfig.apiUrl,
|
||||
tagStyle: {
|
||||
table: `
|
||||
table-layout: fixed;
|
||||
border-collapse:collapse;
|
||||
margin-bottom: 18px;
|
||||
overflow: hidden;
|
||||
font-size: 13px;
|
||||
color: var(--routine);
|
||||
background: #f2f6fc;
|
||||
border: 1px solid #dcdcdc;
|
||||
border-radius: 4px;
|
||||
`,
|
||||
th: `
|
||||
padding: 8px;
|
||||
border-right: 1px solid var(--classE);
|
||||
border-bottom: 1px solid var(--classE);
|
||||
`,
|
||||
td: `
|
||||
padding: 8px;
|
||||
border-right: 1px solid var(--classE);
|
||||
border-bottom: 1px solid var(--classE);
|
||||
`,
|
||||
blockquote: `
|
||||
padding: 8px 15px;
|
||||
color: #606266;
|
||||
background: #f2f6fc;
|
||||
border-left: 5px solid #50bfff;
|
||||
border-radius: 4px;
|
||||
line-height: 26px;
|
||||
margin-bottom: 18px;
|
||||
`,
|
||||
ul: 'padding-left: 15px;line-height: 1.85;',
|
||||
ol: 'padding-left: 15px;line-height: 1.85;',
|
||||
li: 'margin-bottom: 12px;line-height: 1.85;',
|
||||
h1: `
|
||||
margin: 30px 0 20px;
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
font-size:1.2em;
|
||||
`,
|
||||
h2: `
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
margin: 22px 0 16px;
|
||||
font-size: 1.16em;
|
||||
`,
|
||||
h3: `
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
margin: 26px 0 18px;
|
||||
font-size: 1.14em;
|
||||
`,
|
||||
h4: `
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
margin-bottom: 18px;
|
||||
position: relative;
|
||||
font-size: 1.2em;
|
||||
`,
|
||||
h5: `
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
margin-bottom: 14px;
|
||||
position: relative;
|
||||
font-size: 16px;
|
||||
`,
|
||||
h6: `
|
||||
color: #303133;
|
||||
line-height: 24px;
|
||||
margin-bottom: 14px;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
`,
|
||||
p: `
|
||||
line-height: 1.65;
|
||||
margin-bottom: 14px;
|
||||
font-size: 14px;
|
||||
`,
|
||||
'code': ` `,
|
||||
strong: 'font-weight: 700;color: rgb(248, 57, 41);',
|
||||
video: 'width: 100%',
|
||||
},
|
||||
containStyle: 'font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding:12px;font-size: 16px;color: #606266;word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 6px;background-color:#FFFFFF;',
|
||||
|
||||
loadingGif: HaloConfig.loadingGifUrl,
|
||||
emptyGif: HaloConfig.loadingEmptyUrl,
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
:root {
|
||||
--main: #303133;
|
||||
--theme: #fb6c28;
|
||||
--code-background: #e8f3ff;
|
||||
--radius-inner: 4px;
|
||||
--classA: #dcdfe6;
|
||||
--classB: #e4e7ed;
|
||||
--classC: #ebeef5;
|
||||
--classD: #f2f6fc;
|
||||
--classE: #dcdcdc;
|
||||
--classF: #333;
|
||||
--classG: #dcdcdc;
|
||||
--classH: #e9f2ff;
|
||||
--classI: #5a3713;
|
||||
--classJ: #f9e5fb;
|
||||
--classK: #e4e7ed;
|
||||
--classL: #666;
|
||||
--classM: #2d2e37;
|
||||
--quote: #50bfff;
|
||||
--code: #409eff;
|
||||
}
|
||||
.evan-markdown {
|
||||
::v-deep {
|
||||
h1::before,
|
||||
h2::before,
|
||||
h3::before,
|
||||
h4::before,
|
||||
h5::before,
|
||||
h6::before {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
content: '';
|
||||
margin-right: 6px;
|
||||
background-position: center;
|
||||
}
|
||||
h1::before {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
content: '¶';
|
||||
top: -4px;
|
||||
margin-right: 12px;
|
||||
font-size: 24px;
|
||||
color: var(--theme);
|
||||
}
|
||||
h2::before {
|
||||
top: -2px;
|
||||
left: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-size: auto 100%;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAVJJREFUWEftl7FKw1AUhv9rbmf1PRw6utkHEOoi6WPoE1ifQJ8gySKCOtRFRBKsDuIiDdZAURBFF50M2sFIzpGogVK03JA2cbh3S/i55zvfvYETgRyLj5erIKOO949FyKm5eHvvHEBV2jyruq1QDQ7n2DNXALEx+D7e2vl6lBYp76scHCzErtmCEPVhqEIAfus8BZk4wM+Zd/46tskDeI0mgLUyAdoAFsoDcM0XCDFdHoDX4FGfbRF3QANoA9qANqANaAPawD82EEWId1sAOJQWz6iO+5nGch41kDw9I3bbYOaTis21wgG4G4AuAzCwXrEoGV6V1ngMJPoPjoB+PzSIa8KBr1QdwFgA6KID7t1AgFYNC5uqxZNcbgB+eASdngHM+9LmpSzF8wFEEagbgHvXoQA3s3aegmYz8P1f4NNVME+39z5e3w4lkSMc3GXtPM1/AjYDFjDGddN5AAAAAElFTkSuQmCC);
|
||||
}
|
||||
h3::before {
|
||||
top: -3px;
|
||||
left: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-size: auto 100%;
|
||||
background-repeat: none;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAH1JREFUWEft1qENgDAQheH/JANg60DAJIgmrMFCzEEYgTFgDyRFIClp0yBf9evl8pl3RsYbp7BjNLHoMptljPiMZH3WAhKQgAQkIIGXQAXUT79cniHaNMa5draliqqojIID86nRHEtvbSqlBSQgAQlIQAISkECRAA746SC5Ad6XpiGnnOGPAAAAAElFTkSuQmCC);
|
||||
}
|
||||
h4::before {
|
||||
top: -2px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
color: var(--theme);
|
||||
background-size: auto 100%;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAndJREFUWEftVkty2kAQfT1SBLtwg6AqwzbxCQIniG8QvAmwCp8kRqvgFa5KwHjlkI3NCeIbhJwgXptUgW9AdiDQdGoITqUsjcZyuYqNZ6WSXs88vel+3YQtL9ry+Xgk8KhApALNAe8x5PPbCUrgabdsD2/e+1edEpN4FsIRps7OwT9cXKKHCLw75QIL+V0XRFIUP1dptBx3ChKkxQlw8UnOG5mqLESgdsYZy5dTAE8jg5lH3Ypd5Mlxxl8ttDgGRulcq5iYgApofgn6ILzVBQsSu5/e0OXiqnMOotdatVjsOvkPl4muQIFrp5y1hJzEBA67ZavEk07WX5EexzxM5b1SYgJrFQbBOQDt3wVSuP0qTRfjowsAr3SHODa75HrqqiKX1gdMycjAYa9stU3JCPBhKue1ExNQAY3BakSgl1HBDMykI9z+Ps0WV0eXIITKdhM3c+yUS259FrVPrBM2TlclEnSmY8+S93tV+/yvH+hxxLzv5D11paFltOLmIFD3FzIbtRMzpr2K5arn+bgzJZAWl8631rjby0zAUJI3xnTfkjQTiFEAwHW3bGWNCoCv0zlvjUukgOoJgPymzQGg3itbfVMOMKiezh30ExOIqwIAvwNHZFUVzMcdbbUonGOnsomr4P1XfiFZ/tS6GOOkW7FqJh8gwomz06ol9oE7O6GhH9zLCbfeCxqDoE3AR61sm5nA/3XUZ47pmneYCSLnAeHLCQGZaAvmH72yXdjMA6oTanHpnFfQ5tDmQ+KJ6MZ+jckXY7//k4pWYB7sQVCUccxU3a9teHKcWS7ne0wI4Rhipqv7REZkku8hvhut+CEOidvjkcDWFfgD9RMzMKE7f80AAAAASUVORK5CYII=);
|
||||
}
|
||||
h5::before {
|
||||
top: -1px;
|
||||
left: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-size: 100% 100%;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAC8klEQVRYR+3WP2gTURwH8O/vKnVRRHKXP52cBO3g4p/BqYNIhy4muajUQRBFKjQV1En6ZxJBcmlRCoJDRe3FDiJVwamLS3FwqbgIgss1l2ZQF5XeT3I1Z3NJ7u5dLlAwN9699/t97vfe7/EIO/yhHe5DD9jpCv3fFVTu856+Xzi62Y/35hj9CFPNrlUwWeRJAJfBGADwBcBNI0/PRZFdAdo4xlQLjCqKjBzogat7hZCRAgPghJGBgbFnxglAOvS7b/fLb+q+qnv5BHBCyEBAWTdnANy2IxOtWSC1qsY+1jOFwAVG+gIVvXyHQbcaKuZCxmf5iMQogXFQtEsBeO5JT6BcMu+Bcb1lUhcyqfExACUAB6JEtgUqi+U5JrrmmcyFHCjwSYtQO+tSUSFbAmXdnAdwJVASdyWLPAS2kbFA8xsHNS13EzCmlx8R6KJQcBcypfFpho3cKxRna3ADsgEo6+ZjAKMhgjZ1d2KWR2gTSyD0h4jnIB2gXDIXwciFCPZvSnMl0wwshYrJGDImaMUGKovlLBPVOrDjh8APzFx8zDkjNa7FzYoGZsKT9XEarQOHmei1aJA246cqOWW6/i2l8VMGzoWIPW/k6eq2Ja6UwCz8pw2JGZ8sS8pUz8fWau/jGp+SgLchcGBgZD1Py41NUuoI+ZloV8ZU93+ogZKzfBiWfXAPCgMJU8Y42avQdMzI4ZBfmSizocqrUeJaAmsvBZGGBCtTziXeRY1rCxRAViEhXckqK93AeQJ9kYTvlmVlqmcTdhNEtefc+9X3utVmuX+CkK6oyqtu4nwrWP8bF5IZdGYjJ79wDuMCz4D+XmhFWnZbt7ab5ltBB6mbkyAaBPFCJassuwMmNb4L4EZgXwBc4AoGTZrUeA6A9x1yK6tzzvnFDlxBv0D176kCP2TCpbbjBXCRV9DZk0VeAONCE1IQ1zWg3dlF1sFQHWQIXFeBNrLAw5BwHBZWjQl6E3SbbB8X+R4Mg/Ca0wN2WtFeBTut4B84mFI4VpekyAAAAABJRU5ErkJggg==);
|
||||
}
|
||||
h6::before {
|
||||
top: -1px;
|
||||
left: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-size: auto 100%;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAEI0lEQVRYR+3Xb2wTZRwH8G/vL22RPy5GW8fK6rJpGeFFY9RO3TRRE1HfmEAammEyjZmOSEg1RrPZaTD6xjhU/Ndlf0CZgwmD+qcgZBRIETeqY0Vcnc7pGonhRY2l3m2t5upqjq693l2vcy+8N81zz/NcPvf75fd7rjos8ku3yH34HyjK0PqlDLPzD56vMNL0VHxmpgXAoUIZXKgI3sMSpK+cNTAbylaj/9IkprnLM3+mkg8XQi4EsJ4lSL+ZNbAfVd+JG/XLcSERg3M8IAtZauBtDEkeNdN6fX9NfRqXueQiSwm00wQRMDMGw94snBJkqYBrSYIIXs/ojftqGq6IXHZRFIpkKYC2JSw9SJE663W0nthlqcMawwrJYpVCag1M48pNy1YNvLWRffGNIYSCv6Cnog7rjCtVIbUEXoG7qeqaNMj51F58dWoKPZY62JeWyUJG+ctcIplcIizWCpgTl9EIyODJn9BtceDWq/6B57tO/H4R688fFaZ7AWzWAiiJEyMDx39Ed6UDdyy7Ni/w1ekxbP95VJi/F8CRYoGycGLksaEJdFU6cPdy0zxkR/RbtE6FhPvHATQUm2JFODHSfyyCLmsd7lth/hf53q/jcE8OC+NTAG7PTKiNoCqcGPnpF+PotDrwwNXl+OC3H9A8cVqYPgPgFnFo1QCLwomRg4cvoPMGBzZHTgq3zwKwZ+ddKVATXAZhf+gdnPvuojAUqmJdrspRAtQU93pXEM+8clgwhQHU5itruUBNcTt6TsP9sl8weQC0S/VFOUBNcW/2folt2z+XhZPTZh6kSKJ79aqVRuFszRxfUm8sNbdz9xlsfekz2bhCQBvDMMM8z+ubN92Mjrb71brS+97dM4wtnk8U4SSBBEGErVarzeVywePxoHVLA1pb6lUh3+8bwZMv+BTjpIAVACa9Xq+uqakJ7e3tqpGd/WfR3Jr+81awIJS0mY0A+sLhMGw2W3qfGmTXvhAef/6gapxUBDuqqqoejUQiBvFbKUH2DHyNx54bLAqXF0jT9HBjY6Pd6/XOi7oc5K7936Dp2QNF4/IBrTqdLuLxeIi2tracRSGF/PDgKB55er8muHzATQB2m81m+P1+1NbmPoVyIfsOnUOj+2PNcPmAO1iWbeE4TmexWODz+WQhayrL4No2oCkuJ5Bl2VGO49ZmcisXObdeVSuRaq7ZZ3G10KBTqRQl3pQPGY1GEQgE4HQ6heVDAO5S1cklNmUDXQRB9KZSqXkfESaTCW63G7FYDKFQKDEyMoJoNKqfe/bbAJ7QGpcrxa8xDLOV5/k0kGXZv2ZnZ5FMJtNjmqYvURR1IpFInAcwAeD7ud/pUuDmAY1G41g8Hl9DkmScoqggx3FHAIyJMMlSQfI9V5zKagDC93dsoRFKimQx2dIWOV/U/yn6bx0WyDj8vgLOAAAAAElFTkSuQmCC);
|
||||
}
|
||||
blockquote > p {
|
||||
margin-bottom: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
code[class='md-code'],
|
||||
code:not([class]) {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
color: #409eff;
|
||||
margin: 2px 5px;
|
||||
padding: 0 8px;
|
||||
white-space: normal;
|
||||
text-indent: 0;
|
||||
-webkit-user-select: auto;
|
||||
-moz-user-select: auto;
|
||||
-ms-user-select: auto;
|
||||
user-select: auto;
|
||||
vertical-align: baseline;
|
||||
word-break: break-word;
|
||||
background: #e8f3ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
code[class*='language-'] {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
// border-radius: 0 0 8px 8px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
user-select: auto;
|
||||
padding: 12px 12px 14px 18px;
|
||||
margin-bottom: 16px;
|
||||
background: #282c34;
|
||||
color: #abb2bf;
|
||||
border-radius: 4px;
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
||||
font-family: 'Fira Code', 'Fira Mono', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
line-height: 1.5;
|
||||
-moz-tab-size: 2;
|
||||
-o-tab-size: 2;
|
||||
tab-size: 2;
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
table {
|
||||
td {
|
||||
padding: 8px;
|
||||
border-right: 1px solid var(--classE);
|
||||
border-bottom: 1px solid var(--classE);
|
||||
}
|
||||
thead th {
|
||||
font-weight: 500;
|
||||
background: var(--classC);
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
transition: background 0.35s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/**
|
||||
* 功能:全局混入函数
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 17:39:32
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
import HaloConfig from '@/config/halo.config.js';
|
||||
import HaloAdConfig from '@/config/ad.config.js';
|
||||
export default {
|
||||
install(Vue) {
|
||||
Vue.mixin({
|
||||
data() {
|
||||
return {
|
||||
author: HaloConfig.author,
|
||||
_isWechat: true,
|
||||
haloAdConfig: HaloAdConfig
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 获取全局应用设置
|
||||
globalAppSettings() {
|
||||
return uni.$tm.vx.getters().getSettings;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this._isWechat = true;
|
||||
uni.$tm.vx.commit('setWxShare', HaloConfig.wxShareConfig);
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
this._isWechat = false;
|
||||
// #endif
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 设置页面标题
|
||||
* @param {Object} title 标题
|
||||
*/
|
||||
fnSetPageTitle(title) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: title || HaloConfig.title
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面返回顶部
|
||||
*/
|
||||
fnToTopPage(duration = 500) {
|
||||
duration = isNaN(duration) ? 500 : duration
|
||||
uni.pageScrollTo({
|
||||
scrollTop: 0,
|
||||
duration: duration,
|
||||
fail: (err) => {
|
||||
console.log('err:', err);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
// 全局css基础样式
|
||||
|
||||
.card-shadow {
|
||||
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.bg-white {
|
||||
background-color: #fff;
|
||||
}
|
||||
.load-text {
|
||||
padding: 0 0 20rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.e-fixed {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
/* #ifndef H5 */
|
||||
top: 0;
|
||||
/* #endif */
|
||||
/* #ifdef H5 */
|
||||
top: 88rpx;
|
||||
/* #endif */
|
||||
right: 0;
|
||||
z-index: 6;
|
||||
}
|
||||
.e-loading-icon {
|
||||
animation: eLoading 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes eLoading {
|
||||
0% {
|
||||
transform: rotateZ(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// 主题
|
||||
|
||||
:root {
|
||||
--theme: #f79ea3;
|
||||
// --theme: #ffaec3;
|
||||
--main: #303133;
|
||||
--main-text-color: rgba(12, 25, 50, 1);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,304 +0,0 @@
|
||||
<template>
|
||||
<view class="article-card " :class="cardType" @click="fnClickEvent('card')">
|
||||
<view class="left">
|
||||
<cache-image
|
||||
class="thumbnail"
|
||||
radius="12rpx"
|
||||
:url="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||
:fileMd5="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||
mode="aspectFill"
|
||||
></cache-image>
|
||||
<!-- <image class="thumbnail" lazy-load :src="$utils.checkThumbnailUrl(article.thumbnail)" mode="aspectFill"></image> -->
|
||||
</view>
|
||||
<view class="right">
|
||||
<view class="title">
|
||||
<text class="is-top" v-if="article.topped">置顶</text>
|
||||
<text class="title-text text-overflow">{{ article.title }}</text>
|
||||
</view>
|
||||
<view class="content text-overflow-2">{{ article.summary }}</view>
|
||||
<view class="foot">
|
||||
<view class="create-time">
|
||||
<text class="time-label">发布时间:</text>
|
||||
{{ { d: article.createTime, f: 'yyyy-MM-dd' } | formatTime }}
|
||||
</view>
|
||||
<view class="visits">
|
||||
<!-- <tm-icons :size="24" name="icon-filter-fill"></tm-icons> -->
|
||||
浏览
|
||||
<text class="number">{{ article.visits }}</text>
|
||||
次
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'article-card',
|
||||
props: {
|
||||
from: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
article: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cardType() {
|
||||
// tb_image_text=上图下文
|
||||
// tb_text_image=上文下图
|
||||
if (this.from == 'home' && this.globalAppSettings.layout.home == 'h_row_col2') {
|
||||
if (!['tb_image_text', 'tb_text_image', 'only_text'].some(x => x == this.globalAppSettings.layout.cardType)) {
|
||||
return [this.from, this.globalAppSettings.layout.home, 'tb_image_text'];
|
||||
}
|
||||
return [this.from, this.globalAppSettings.layout.home, this.globalAppSettings.layout.cardType];
|
||||
}
|
||||
return [this.globalAppSettings.layout.home, this.globalAppSettings.layout.cardType];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fnClickEvent() {
|
||||
this.$emit('on-click', this.article);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.article-card {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
margin: 0 24rpx;
|
||||
padding: 32rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #ffff;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||
overflow: hidden;
|
||||
margin-bottom: 24rpx;
|
||||
&.home {
|
||||
&.h_row_col2 {
|
||||
margin: 12rpx;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
.thumbnail {
|
||||
::v-deep uni-image {
|
||||
border-radius: 6rpx 6rpx 0 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
.is-top {
|
||||
height: 36rpx;
|
||||
margin-right: 10rpx;
|
||||
line-height: 36rpx;
|
||||
vertical-align: 4rpx;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
.foot {
|
||||
justify-content: space-between;
|
||||
.create-time {
|
||||
font-size: 24rpx;
|
||||
.time-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
font-size: 24rpx;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.tb_text_image {
|
||||
padding: 12rpx;
|
||||
.left .thumbnail {
|
||||
::v-deep {
|
||||
uni-image {
|
||||
border-radius: 6rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.only_text {
|
||||
padding: 24rpx;
|
||||
.right .foot {
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.lr_image_text {
|
||||
}
|
||||
|
||||
&.lr_text_image {
|
||||
.left {
|
||||
order: 2;
|
||||
padding-left: 30rpx;
|
||||
}
|
||||
.right {
|
||||
order: 1;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
&.tb_image_text {
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 340rpx;
|
||||
.thumbnail {
|
||||
::v-deep uni-image {
|
||||
border-radius: 6rpx 6rpx 0 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
padding: 24rpx;
|
||||
width: 100%;
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.tb_text_image {
|
||||
flex-direction: column;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 340rpx;
|
||||
order: 2;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
order: 1;
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.only_text {
|
||||
padding: 36rpx;
|
||||
.left {
|
||||
display: none;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
.content {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
margin-top: 24rpx;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 240rpx;
|
||||
height: 180rpx;
|
||||
.thumbnail {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 30rpx;
|
||||
box-sizing: border-box;
|
||||
.title {
|
||||
display: flex;
|
||||
font-size: 30rpx;
|
||||
color: var(--main-text-color);
|
||||
.is-top {
|
||||
height: 40rpx;
|
||||
padding: 0 12rpx;
|
||||
margin-right: 10rpx;
|
||||
line-height: 40rpx;
|
||||
font-size: 24rpx;
|
||||
white-space: nowrap;
|
||||
vertical-align: 4rpx;
|
||||
color: #fff;
|
||||
background-image: -webkit-linear-gradient(0deg, #3ca5f6 0, #a86af9 100%);
|
||||
border-radius: 4rpx 12rpx;
|
||||
}
|
||||
&-text {
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
display: -webkit-box;
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
height: 80rpx;
|
||||
margin-top: 14rpx;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
.foot {
|
||||
display: flex;
|
||||
font-size: 24rpx;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #909399;
|
||||
margin-top: 18rpx;
|
||||
|
||||
.create-time {
|
||||
font-size: 26rpx;
|
||||
.time-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
.number {
|
||||
padding: 0 6rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,197 +0,0 @@
|
||||
<template>
|
||||
<view class="article-min-card" :class="[globalAppSettings.layout.cardType]" @click="fnClickEvent('card')">
|
||||
<view class="left">
|
||||
<cache-image
|
||||
class="thumbnail"
|
||||
radius="12rpx"
|
||||
:url="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||
:fileMd5="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||
mode="aspectFill"
|
||||
></cache-image>
|
||||
</view>
|
||||
<view class="right">
|
||||
<view class="title text-overflow">{{ article.title }}</view>
|
||||
<view class="content text-overflow">{{ article.summary }}</view>
|
||||
<view class="foot">
|
||||
<view class="create-time">
|
||||
<!-- <text class="icon iconfont icon-clock"></text> -->
|
||||
<text class="time-label">发布时间:</text>
|
||||
{{ { d: article.createTime, f: 'yyyy-MM-dd' } | formatTime }}
|
||||
</view>
|
||||
<view class="visits">
|
||||
<!-- <text class="icon iconfont icon-eye"></text> -->
|
||||
浏览
|
||||
<text class="number">{{ article.visits }}</text>
|
||||
次
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'article-min-card',
|
||||
props: {
|
||||
article: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fnClickEvent() {
|
||||
this.$emit('on-click', this.article);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.article-min-card {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
border-radius: 12rpx;
|
||||
background-color: #ffff;
|
||||
overflow: hidden;
|
||||
margin: 12rpx 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 16rpx;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
&.lr_image_text {
|
||||
}
|
||||
|
||||
&.lr_text_image {
|
||||
.left {
|
||||
order: 2;
|
||||
padding-left: 30rpx;
|
||||
}
|
||||
.right {
|
||||
order: 1;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
&.tb_image_text {
|
||||
flex-direction: column;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 220rpx;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
.title {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.tb_text_image {
|
||||
flex-direction: column;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 220rpx;
|
||||
order: 2;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
order: 1;
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.only_text {
|
||||
.left {
|
||||
display: none;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 180rpx;
|
||||
height: 130rpx;
|
||||
.thumbnail {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 24rpx;
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
.content {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
.foot {
|
||||
display: flex;
|
||||
font-size: 24rpx;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #909399;
|
||||
margin-top: 14rpx;
|
||||
.create-time {
|
||||
font-size: 24rpx;
|
||||
.time-label {
|
||||
display: none;
|
||||
}
|
||||
.icon {
|
||||
font-size: 24rpx;
|
||||
padding-right: 4rpx;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
.icon {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.number {
|
||||
padding: 0 6rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,207 +0,0 @@
|
||||
<template>
|
||||
<tm-poup v-model="show" position="bottom" height="auto" @change="fnClose">
|
||||
<view class="poup-head pa-24 text-align-center text-weight-b ">{{ title }}</view>
|
||||
<view class="poup-body pa-24 pt-0 pb-0">
|
||||
<view v-if="loading != 'success'" class="loading-wrap flex flex-center">
|
||||
<view v-if="loading == 'loading'" class="loading">加载中...</view>
|
||||
<view v-else class="error" @click="fnGetData()">加载失败,点击刷新!</view>
|
||||
</view>
|
||||
<block v-else>
|
||||
<view v-if="total == 0" class="empty">无附件</view>
|
||||
<scroll-view v-else class="poup-content" :enable-flex="true" :scroll-y="true" @touchmove.stop>
|
||||
<view class="card-content">
|
||||
<view class="card pa-12" v-for="(file, index) in dataList" :key="index" @click="fnOnSelect(file, index)">
|
||||
<view class="card-inner round-3" :class="{ 'is-select': selectIndex == index }">
|
||||
<cache-image v-if="file.isImage" class="cover" height="160rpx" :url="file.thumbPath" :fileMd5="file.thumbPath" mode="aspectFill"></cache-image>
|
||||
<view v-else class="cover flex pl-46 pr-46 flex-center bg-gradient-blue-grey-accent text-align-center text-size-m">{{ file.mediaType }}</view>
|
||||
|
||||
<view class="name text-overflow text-size-m pa-12">{{ file.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
</view>
|
||||
<view class="poup-foot pa-30 pb-12 pt-0">
|
||||
<!-- 分页 -->
|
||||
<view v-if="total > queryParams.size" class="mt-36 pl-24 pr-24">
|
||||
<tm-pagination color="bg-gradient-blue-accent" :page.sync="queryParams.page" :total="total" :totalVisible="5" @change="fnGetPagingData"></tm-pagination>
|
||||
</view>
|
||||
<view class=" flex flex-center mt-12">
|
||||
<tm-button size="m" theme="bg-gradient-blue-accent" @click="fnSava()">确定选用</tm-button>
|
||||
<tm-button size="m" theme="bg-gradient-orange-accent" @click="fnUpload()">上传</tm-button>
|
||||
<tm-button size="m" theme="bg-gradient-blue-grey-accent" @click="fnClose(false)">关 闭</tm-button>
|
||||
</view>
|
||||
</view>
|
||||
</tm-poup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAdminAccessToken } from '@/utils/auth.js';
|
||||
|
||||
import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
|
||||
import tmPagination from '@/tm-vuetify/components/tm-pagination/tm-pagination.vue';
|
||||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||
export default {
|
||||
name: 'attachment-select',
|
||||
components: { tmPoup, tmPagination, tmButton },
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '附件列表'
|
||||
},
|
||||
selectType: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: true,
|
||||
loading: 'loading',
|
||||
total: 0,
|
||||
queryParams: {
|
||||
size: 6,
|
||||
page: 1
|
||||
},
|
||||
dataList: [],
|
||||
select: null,
|
||||
selectIndex: -1
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fnGetData();
|
||||
},
|
||||
methods: {
|
||||
fnGetData() {
|
||||
this.queryParams.page = 1;
|
||||
this.fnGetPagingData(1);
|
||||
},
|
||||
fnGetPagingData(page) {
|
||||
this.loading = 'loading';
|
||||
const _params = {
|
||||
...this.queryParams
|
||||
};
|
||||
_params.page = page - 1;
|
||||
this.$httpApi.admin
|
||||
.getAttachmentsByPage(_params)
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
this.total = res.data.total;
|
||||
this.dataList = res.data.content.map(file => {
|
||||
if (this.$utils.fnCheckIsFileType('image', file) && file.size / 1024 / 1024 > 2) {
|
||||
file.isImage = false;
|
||||
file.desc = '图片过大无法显示缩略图';
|
||||
} else {
|
||||
file.isImage = this.$utils.fnCheckIsFileType('image', file);
|
||||
}
|
||||
file.thumbPath = this.$utils.checkThumbnailUrl(file.thumbPath);
|
||||
return file;
|
||||
});
|
||||
this.loading = 'success';
|
||||
} else {
|
||||
uni.$tm.toast('加载失败,请重试!');
|
||||
this.loading = 'error';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
uni.$tm.toast('加载失败,请重试!');
|
||||
this.loading = 'error';
|
||||
});
|
||||
},
|
||||
fnOnSelect(file, index) {
|
||||
this.select = file;
|
||||
this.selectIndex = index;
|
||||
},
|
||||
fnSava() {
|
||||
if (this.selectType) {
|
||||
if (this.$utils.fnCheckIsFileType(this.selectType, this.select)) {
|
||||
this.$emit('on-select', this.select);
|
||||
} else {
|
||||
uni.$tm.toast('该附件类型不符合!');
|
||||
}
|
||||
} else {
|
||||
this.$emit('on-select', this.select);
|
||||
}
|
||||
},
|
||||
fnClose(e) {
|
||||
if (!e) {
|
||||
this.$emit('on-close');
|
||||
}
|
||||
},
|
||||
fnUpload() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: res => {
|
||||
uni.uploadFile({
|
||||
filePath: res.tempFilePaths[0],
|
||||
header: {
|
||||
'admin-authorization': getAdminAccessToken()
|
||||
},
|
||||
url: this.$baseApiUrl + '/api/admin/attachments/upload',
|
||||
name: 'file',
|
||||
success: upladRes => {
|
||||
const _uploadRes = JSON.parse(upladRes.data);
|
||||
if (_uploadRes.status == 200) {
|
||||
uni.$tm.toast('上传成功!');
|
||||
this.fnGetData(1);
|
||||
} else {
|
||||
uni.$tm.toast(_uploadRes.message);
|
||||
}
|
||||
},
|
||||
fail: err => {
|
||||
uni.$tm.toast(err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.poup-head {
|
||||
}
|
||||
.poup-body {
|
||||
height: 50vh;
|
||||
}
|
||||
.loading-wrap {
|
||||
height: 50vh;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.poup-content {
|
||||
height: inherit;
|
||||
box-sizing: border-box;
|
||||
.card-content {
|
||||
height: inherit;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
.card {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
&-inner {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
border: 4rpx solid transparent;
|
||||
&.is-select {
|
||||
border-color: rgb(13, 141, 242);
|
||||
}
|
||||
}
|
||||
.cover {
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
flex-wrap: wrap;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.name {
|
||||
color: #303133;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,194 +0,0 @@
|
||||
<template>
|
||||
<view class="bottom-tool-bar">
|
||||
<tm-translate :auto="true" animation-name="fadeUp">
|
||||
<view class="content flex">
|
||||
<view class="input" @click="fnToComment()">
|
||||
<text class="icon iconfont icon-edit"></text>
|
||||
<text class="text">(*^▽^*)说点啥吧~</text>
|
||||
</view>
|
||||
<view class="right flex">
|
||||
<!-- 点赞 -->
|
||||
<view class="item likes" @click="fnDoLikes()">
|
||||
<view class="iconfont icon-like"></view>
|
||||
<view class="text">{{ tempPost.likes }}</view>
|
||||
</view>
|
||||
<!-- 评论 -->
|
||||
<view class="item comment">
|
||||
<view class="iconfont icon-comment-dots"></view>
|
||||
<view class="text">{{ tempPost.commentCount }}</view>
|
||||
</view>
|
||||
<!-- 分享 -->
|
||||
<view class="item share" @click="fnOnShare()"><text class="iconfont icon-share1"></text></view>
|
||||
</view>
|
||||
</view>
|
||||
</tm-translate>
|
||||
|
||||
<tm-shareSheet @change="fnOnShareChange" :actions="share.list" title="分享文章" v-model="share.show"></tm-shareSheet>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||
import tmShareSheet from '@/tm-vuetify/components/tm-shareSheet/tm-shareSheet.vue';
|
||||
export default {
|
||||
name: 'bottom-tool-bar',
|
||||
components: {
|
||||
tmTranslate,
|
||||
tmShareSheet
|
||||
},
|
||||
props: {
|
||||
// 文章数据
|
||||
post: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
// 其他参数
|
||||
params: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
share: {
|
||||
show: false,
|
||||
list: [
|
||||
[
|
||||
{ name: '微信好友', bgcolor: '#07c160', icon: 'icon-weixin', color: 'white' },
|
||||
{ name: '朋友圈', bgcolor: '#04c887', icon: 'icon-pengyouquan', color: 'white' },
|
||||
{ name: '生成海报', bgcolor: '#1dc0fd', icon: 'icon-QQ', color: 'white' }
|
||||
]
|
||||
]
|
||||
},
|
||||
tempPost: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
post: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
console.log('watch', val);
|
||||
this.tempPost = this.$utils.deepClone(val);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
console.log(this.post);
|
||||
this.tempPost = this.$utils.deepClone(this.post);
|
||||
console.log(this.tempPost);
|
||||
},
|
||||
methods: {
|
||||
fnToComment() {
|
||||
this.$Router.push({
|
||||
path: '/pagesA/comment/comment',
|
||||
query: {
|
||||
postId: this.post.id,
|
||||
parentId: 0,
|
||||
title: this.post.title,
|
||||
formPage: 'comment_list',
|
||||
type: 'post'
|
||||
}
|
||||
});
|
||||
},
|
||||
fnDoLikes() {
|
||||
this.$httpApi
|
||||
.postLikePost(this.post.id)
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '点赞成功'
|
||||
});
|
||||
this.tempPost.likes += 1;
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: err.message
|
||||
});
|
||||
});
|
||||
},
|
||||
fnOnShare() {
|
||||
// this.$emit('on-share');
|
||||
this.share.show = true;
|
||||
},
|
||||
fnOnShareChange(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bottom-tool-bar {
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 401;
|
||||
|
||||
::v-deep {
|
||||
.tm-shareSheet-wk .uni-scroll-view-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
padding: 24rpx;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0rpx -4rpx 24rpx rgba(0, 0, 0, 0.07);
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
.input {
|
||||
width: 280rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 60rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
.icon {
|
||||
}
|
||||
.text {
|
||||
padding-left: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-left: 24rpx;
|
||||
.item {
|
||||
margin-left: 24rpx;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&.share {
|
||||
.iconfont {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
}
|
||||
.iconfont {
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
}
|
||||
.text {
|
||||
padding-left: 6rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,185 +0,0 @@
|
||||
<template>
|
||||
<view class="cache-image">
|
||||
<view v-if="loadStatus == 'loading'" class="img-loading" :style="[imgStyle, loadStyle]">
|
||||
<!-- <text class="img-load-icon iconfont icon-loading"></text>
|
||||
<text class="img-load-text">{{ loadText }}</text> -->
|
||||
<image :src="loadingImgSrc" :style="[imgStyle]" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view v-if="loadStatus == 'error'" class="img-error" :style="[imgStyle, loadErrStyle]">
|
||||
<text class="img-err-icon iconfont icon-exclamation-circle"></text>
|
||||
<text class="img-load-text">{{ loadErrText }}</text>
|
||||
</view>
|
||||
<image
|
||||
v-show="loadStatus == 'success'"
|
||||
:src="src"
|
||||
@load="fnOnLoad"
|
||||
@error="fnOnError"
|
||||
:lazy-load="lazyLoad"
|
||||
:style="[imgStyle]"
|
||||
:mode="mode"
|
||||
@click="$emit('on-click', url)"
|
||||
></image>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import imageCache from '@/utils/imageCache.js';
|
||||
export default {
|
||||
name: 'cache-image',
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
lazyLoad: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
loadStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
backgroundColor: '#ffffff',
|
||||
color: '#333'
|
||||
};
|
||||
}
|
||||
},
|
||||
loadErrStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
color: 'rgba(244, 67, 54,1)'
|
||||
// backgroundColor: 'rgba(244, 67, 54,0.2)'
|
||||
};
|
||||
}
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'aspectFill'
|
||||
},
|
||||
loadText: {
|
||||
type: String,
|
||||
default: '加载中...'
|
||||
},
|
||||
loadErrText: {
|
||||
type: String,
|
||||
default: '加载失败'
|
||||
},
|
||||
fileMd5: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
radius: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imgStyle: {},
|
||||
src: '', // 图片地址
|
||||
loadStatus: 'loading'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
loadingImgSrc() {
|
||||
return getApp().globalData.loadingGifUrl;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听图片md5值的变化
|
||||
fileMd5(val) {
|
||||
// 查找获取图片缓存
|
||||
this.fnGetImageCache();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
console.log('cache-image', this.url);
|
||||
this.imgStyle = {
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
borderRadius: this.radius,
|
||||
...this.styles
|
||||
};
|
||||
|
||||
// 查找获取图片缓存
|
||||
this.fnGetImageCache();
|
||||
},
|
||||
methods: {
|
||||
// 查找获取图片缓存
|
||||
async fnGetImageCache() {
|
||||
// #ifdef APP-PLUS
|
||||
var result = await imageCache.getImageCache(this.url, this.fileMd5);
|
||||
if (result) {
|
||||
this.src = result;
|
||||
} else {
|
||||
this.src = this.url;
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
this.src = this.url;
|
||||
// #endif
|
||||
},
|
||||
fnOnLoad() {
|
||||
this.loadStatus = 'success';
|
||||
},
|
||||
fnOnError() {
|
||||
this.loadStatus = 'error';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.cache-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.img-loading,
|
||||
.img-error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.img-load-icon {
|
||||
font-size: 36rpx;
|
||||
animation: xhRote 0.8s infinite linear;
|
||||
}
|
||||
.img-load-text {
|
||||
font-size: 28rpx;
|
||||
margin-top: 8rpx;
|
||||
color: inherit;
|
||||
}
|
||||
.img-error {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.img-err-icon {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
@keyframes xhRote {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<view class="category-mini-card">
|
||||
<!-- <image class="img" lazy-load :src="$utils.checkThumbnailUrl(category.thumbnail)" mode="aspectFill"></image> -->
|
||||
<cache-image
|
||||
class="img"
|
||||
height="120rpx"
|
||||
:url="$utils.checkThumbnailUrl(category.thumbnail)"
|
||||
:fileMd5="$utils.checkThumbnailUrl(category.thumbnail)"
|
||||
mode="aspectFill"
|
||||
></cache-image>
|
||||
|
||||
<text class="label">{{ category.postCount }} 篇</text>
|
||||
<view class="name">{{ category.name }}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'category-mini-card',
|
||||
props: {
|
||||
category: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.category-mini-card {
|
||||
display: inline-block;
|
||||
width: 260rpx;
|
||||
height: 180rpx;
|
||||
position: relative;
|
||||
border-radius: 12rpx;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
// border: 2rpx solid #f7f7f7;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
border: 6rpx 6rpx 0 0;
|
||||
}
|
||||
.label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 86rpx;
|
||||
color: #03a9f4;
|
||||
font-size: 24rpx;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-radius: 0rpx 24rpx 0 0;
|
||||
display: flex;
|
||||
padding: 2rpx 12rpx;
|
||||
padding-right: 24rpx;
|
||||
}
|
||||
.name {
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,167 +0,0 @@
|
||||
<template>
|
||||
<view class=" comment-item flex flex-col mt-30 pt-24" :class="{ 'child-comment-item': isChild, 'no-solid': !useSolid, classItem }">
|
||||
<view class="comment-item_user flex">
|
||||
<image
|
||||
v-if="comment.isAdmin"
|
||||
class="user-avatar"
|
||||
:class="{ 'is-radius': globalAppSettings.isAvatarRadius }"
|
||||
:src="bloggerInfo.avatar"
|
||||
mode="aspectFill"
|
||||
@error="fnOnImageError(comment)"
|
||||
></image>
|
||||
<image
|
||||
v-else
|
||||
class="user-avatar"
|
||||
:class="{ 'is-radius': globalAppSettings.isAvatarRadius }"
|
||||
:src="comment.avatar"
|
||||
mode="aspectFill"
|
||||
@error="fnOnImageError(comment)"
|
||||
></image>
|
||||
<view class="user-info pl-14">
|
||||
<view class="author">
|
||||
<text class="mr-6 text-grey-darken-1 text-size-m">{{ comment.author }}</text>
|
||||
<tm-tags v-if="comment.isAdmin" :dense="true" color="bg-gradient-amber-accent" size="xs" model="fill">博主</tm-tags>
|
||||
|
||||
<tm-tags v-else :dense="true" color="bg-gradient-light-blue-lighten " size="xs" model="fill">游客</tm-tags>
|
||||
</view>
|
||||
<view class="flex mt-4">
|
||||
<view v-if="false" class="text-size-s text-grey mr-12">IP属地:暂无信息</view>
|
||||
<view class="time text-size-xs text-grey">
|
||||
<text class="">{{ $tm.dayjs(comment.createTime).format('YYYY年MM月DD日') }}</text>
|
||||
<text class="ml-12">{{ $tm.dayjs(comment.createTime).fromNow(true) }}前</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="useActions" class="">
|
||||
<tm-button v-if="!disallowComment" size="s" text theme="blue" @click="$emit('on-comment', { type: 'user', comment: comment })">回复</tm-button>
|
||||
<tm-button size="s" text theme="grey" @click="$emit('on-copy', comment.content)">复制</tm-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="comment-item_content mt-12" :class="{ 'has-bg': useContentBg, 'not-ml': isChild }" @click="$emit('on-detail', comment)" v-html="comment.content"></view>
|
||||
<!-- <view v-if="useActions" class="comment-item_info text-size-s text-grey">
|
||||
<text v-if="false" @click="$emit('on-todo')">点赞</text>
|
||||
<text @click="$emit('on-comment', { type: 'user', comment: comment })">回复</text>
|
||||
<text v-if="false" class="ml-24" @click="$emit('on-todo')">举报</text>
|
||||
<text class="ml-24" @click="$emit('on-copy', comment.content)">复制内容</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||
export default {
|
||||
name: 'comment-item',
|
||||
components: { tmTags, tmButton },
|
||||
props: {
|
||||
classItem: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
disallowComment: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
useActions: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useSolid: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useContentBg: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isChild: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
postId: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
comment: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 获取博主信息
|
||||
bloggerInfo() {
|
||||
let blogger = this.$tm.vx.getters().getBlogger;
|
||||
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
|
||||
return blogger;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fnOnImageError(data) {
|
||||
if (data.isAdmin) {
|
||||
data.avatar = this.$haloConfig.author.avatar;
|
||||
} else {
|
||||
data.avatar = `${this.$haloConfig.defaultAvatarUrl}&rt=${new Date().getTime()}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.comment-item {
|
||||
box-sizing: border-box;
|
||||
border-top: 2rpx solid #f5f5f5;
|
||||
|
||||
&.child-comment-item {
|
||||
padding-top: 0;
|
||||
margin-left: 80rpx;
|
||||
border: 0;
|
||||
}
|
||||
&.no-solid {
|
||||
border: 0;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
&_user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.user-avatar {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
border: 4rpx solid #ffffff;
|
||||
border-radius: 12rpx;
|
||||
&.is-radius {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.user-info {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&_content {
|
||||
font-size: 28rpx;
|
||||
margin-left: 80rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 10rpx;
|
||||
line-height: 1.8;
|
||||
color: var(--main-text-color);
|
||||
&.has-bg {
|
||||
background-color: #fafafa;
|
||||
padding: 6rpx 24rpx;
|
||||
}
|
||||
&.not-ml {
|
||||
margin-left: 80rpx;
|
||||
}
|
||||
}
|
||||
&_info {
|
||||
margin-top: 6rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 80rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,282 +0,0 @@
|
||||
<template>
|
||||
<view class="comment-list">
|
||||
<!-- 顶部区域 -->
|
||||
<view class="comment-list_head">
|
||||
<view class="title">
|
||||
评论列表
|
||||
<text class="count">({{ result.total || 0 }}条)</text>
|
||||
</view>
|
||||
<view class="filter">
|
||||
<text class="filter-item " :class="{ active: sort == 0 }" @click="fnOnSort(0)">默认</text>
|
||||
<text class="filter-item " :class="{ active: sort == 1 }" @click="fnOnSort(1)">热评</text>
|
||||
<!-- <text class="filter-item">全部</text> -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- 内容区域 -->
|
||||
<view class="comment-list_content">
|
||||
<view v-if="loading != 'success'" class="loading-wrap flex">
|
||||
<view v-if="loading == 'loading'" class="loading flex flex-center flex-col">
|
||||
<text class="e-loading-icon iconfont icon-loading text-blue"></text>
|
||||
<view class="text-size-n text-grey-lighten-1 py-12 mt-12">加载中,请稍等...</view>
|
||||
</view>
|
||||
<view v-else-if="loading == 'error'" class="error">
|
||||
<tm-empty icon="icon-wind-cry" label="加载失败">
|
||||
<tm-button theme="bg-gradient-light-blue-accent" size="m" v-if="!disallowComment" @click="fnToComment()">刷新试试</tm-button>
|
||||
</tm-empty>
|
||||
</view>
|
||||
</view>
|
||||
<block v-else>
|
||||
<tm-alerts
|
||||
v-if="disallowComment && dataList.length !== 0"
|
||||
color="red"
|
||||
text
|
||||
:margin="[0, 0]"
|
||||
:shadow="0"
|
||||
label="Ծ‸Ծ博主已设置该文章禁止评论!"
|
||||
:round="3"
|
||||
close
|
||||
></tm-alerts>
|
||||
<view class="empty pt-50" v-if="dataList.length == 0">
|
||||
<tm-empty v-if="disallowComment" icon="icon-shiliangzhinengduixiang-" label="暂无评论">
|
||||
<text class="text-red text-size-s">- 文章已开启禁止评论 -</text>
|
||||
</tm-empty>
|
||||
<tm-empty v-else icon="icon-shiliangzhinengduixiang-" label="暂无评论">
|
||||
<tm-button theme="bg-gradient-light-blue-accent" size="m" @click="fnToComment(null)">抢沙发</tm-button>
|
||||
</tm-empty>
|
||||
</view>
|
||||
<block v-else>
|
||||
<!-- 评论内容 : 目前仅支持二级评论 -->
|
||||
<block v-for="(comment, index) in dataList" :key="comment.id">
|
||||
<comment-item
|
||||
:useContentBg="false"
|
||||
:isChild="false"
|
||||
:comment="comment"
|
||||
:postId="postId"
|
||||
:disallowComment="disallowComment"
|
||||
@on-copy="fnCopyContent"
|
||||
@on-comment="fnToComment"
|
||||
@on-todo="fnToDo"
|
||||
@on-detail="fnShowCommetnDetail"
|
||||
></comment-item>
|
||||
|
||||
<!-- 二级评论 -->
|
||||
<block v-if="comment.children && comment.children.length != 0">
|
||||
<block v-for="(childComment, childIndex) in comment.children" :key="childComment.id">
|
||||
<comment-item
|
||||
:useContentBg="false"
|
||||
:isChild="true"
|
||||
:comment="childComment"
|
||||
:postId="postId"
|
||||
:disallowComment="disallowComment"
|
||||
@on-copy="fnCopyContent"
|
||||
@on-comment="fnToComment"
|
||||
@on-todo="fnToDo"
|
||||
@on-detail="fnShowCommetnDetail"
|
||||
></comment-item>
|
||||
</block>
|
||||
</block>
|
||||
</block>
|
||||
<view v-if="false" class="to-more-comment">
|
||||
<tm-button item-class="btn" :block="true" width="90vw" theme="bg-gradient-light-blue-lighten" size="m">点击查看全部评论</tm-button>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||
import tmAlerts from '@/tm-vuetify/components/tm-alerts/tm-alerts.vue';
|
||||
|
||||
export default {
|
||||
name: 'comment-list',
|
||||
components: { tmEmpty, tmButton, tmAlerts },
|
||||
props: {
|
||||
// 是否禁用评论
|
||||
disallowComment: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
postId: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
post: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: 'loading',
|
||||
sort: 0,
|
||||
queryParams: {
|
||||
sort: '',
|
||||
more: true
|
||||
},
|
||||
api: 'getPostCommentTree',
|
||||
result: {},
|
||||
dataList: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fnGetData();
|
||||
uni.$on('comment_list_refresh', () => {
|
||||
this.fnOnSort(this.sort, true);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
fnOnSort(type, refresh = false) {
|
||||
if (this.sort == type && refresh == false) return;
|
||||
const _api = ['getPostCommentTree', 'getPostTopCommentList'];
|
||||
this.sort = type;
|
||||
this.api = _api[type];
|
||||
this.fnGetData();
|
||||
},
|
||||
fnGetData() {
|
||||
this.loading = 'loading';
|
||||
this.$httpApi[this.api](this.postId, {})
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
this.result = res.data;
|
||||
this.dataList = res.data.content;
|
||||
this.loading = 'success';
|
||||
} else {
|
||||
this.loading = 'error';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.loading = 'error';
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
},
|
||||
fnToDo() {
|
||||
uni.$tm.toast('Halo暂未支持!');
|
||||
},
|
||||
fnToComment(data) {
|
||||
if (this.disallowComment) {
|
||||
return uni.$tm.toast('文章已禁止评论!');
|
||||
}
|
||||
console.log(data);
|
||||
let _comment = {};
|
||||
if (data) {
|
||||
let { type, comment } = data;
|
||||
// 来自用户
|
||||
_comment = {
|
||||
id: this.post.id,
|
||||
parentId: comment.id,
|
||||
title: comment.author,
|
||||
from: 'posts',
|
||||
formPage: 'comment_list',
|
||||
type: 'user'
|
||||
};
|
||||
} else {
|
||||
// 来自文章
|
||||
_comment = {
|
||||
id: this.post.id,
|
||||
parentId: 0,
|
||||
title: '评论文章:' + this.post.title,
|
||||
formPage: 'comment_list',
|
||||
from: 'posts',
|
||||
type: 'post'
|
||||
};
|
||||
}
|
||||
|
||||
uni.$tm.vx.commit('comment/setCommentInfo', _comment);
|
||||
this.$Router.push({
|
||||
path: '/pagesA/comment/comment',
|
||||
query: _comment
|
||||
});
|
||||
},
|
||||
fnCopyContent(content) {
|
||||
uni.$tm.u.setClipboardData(content);
|
||||
uni.$tm.toast('内容已复制成功!');
|
||||
},
|
||||
|
||||
fnShowCommetnDetail(comment) {
|
||||
this.$emit('on-comment-detail', {
|
||||
postId: this.postId,
|
||||
comment: comment
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.comment-list {
|
||||
&_head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding-left: 24rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0rpx;
|
||||
top: 8rpx;
|
||||
width: 8rpx;
|
||||
height: 26rpx;
|
||||
background-color: rgb(3, 174, 252);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.title {
|
||||
.count {
|
||||
font-size: 28rpx;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
.filter {
|
||||
font-size: 26rpx;
|
||||
font-weight: normal;
|
||||
|
||||
&-item {
|
||||
margin-left: 20rpx;
|
||||
color: #666;
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
color: rgb(255, 152, 0);
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&_content {
|
||||
margin-top: 24rpx;
|
||||
padding-bottom: 36rpx;
|
||||
}
|
||||
}
|
||||
.loading-wrap {
|
||||
width: 100%;
|
||||
height: 506rpx;
|
||||
.loading {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.e-loading-icon {
|
||||
font-size: 100rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.to-more-comment {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 80rpx;
|
||||
::v-deep {
|
||||
.tm-button .tm-button-btn uni-button {
|
||||
height: 70rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,341 +0,0 @@
|
||||
// 轮播图
|
||||
|
||||
.Swiper-mfw-index-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
view {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.Swiper-mfw-index {
|
||||
// 轮播图
|
||||
width: 100%;
|
||||
.Swiper-mfw {
|
||||
width: inherit;
|
||||
height: 450rpx;
|
||||
border-radius: 12rpx;
|
||||
.swiper-mfw-item {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
border-radius: 12rpx;
|
||||
.Image,
|
||||
.ImageVideo {
|
||||
border-radius: 12rpx;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 指示器
|
||||
.Swiper-indicator-box {
|
||||
width: inherit;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
// Top顶部 [今日首推-盒子]
|
||||
.Top-date-hot {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
.left-date-ri {
|
||||
justify-content: center;
|
||||
.date-ri-text {
|
||||
color: #ffffff;
|
||||
font-size: 60rpx;
|
||||
font-weight: 700;
|
||||
margin-top: -4rpx;
|
||||
}
|
||||
}
|
||||
.conter-date-nianyue {
|
||||
margin: 0 14rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
.left-width-bgcolor {
|
||||
width: 8rpx;
|
||||
height: 45rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #fafafa;
|
||||
margin-right: 14rpx;
|
||||
}
|
||||
.right-date-nianyue {
|
||||
flex-direction: column;
|
||||
.Top-yue-usa,
|
||||
.Bottom-nian,
|
||||
.text {
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
.text {
|
||||
margin-top: -4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right-hot-ttf {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-left: 6rpx;
|
||||
.hot-text {
|
||||
color: #ffffff;
|
||||
font-size: 52rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 指示器 [轮播信息 -> 标题,用户,头像,所在地]
|
||||
.Swiper-indicator-Top {
|
||||
position: absolute;
|
||||
left: 0rpx;
|
||||
bottom: 110rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx 24rpx;
|
||||
&.no-dot {
|
||||
bottom: 0;
|
||||
}
|
||||
.Top-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// 如果有视频,则显示“视频预览”
|
||||
.Top-ImageVideo {
|
||||
width: 150rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 2rpx 6rpx;
|
||||
margin-bottom: 10rpx;
|
||||
background-color: #f0ad4e;
|
||||
border-radius: 20rpx;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
.Icons {
|
||||
color: #242629;
|
||||
font-size: 26rpx;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
.ImageVideo-text {
|
||||
color: #242629;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 标题
|
||||
.Top-Title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
.title-text {
|
||||
width: 100%;
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
// 用户信息盒子
|
||||
.Bottom-UserInfo {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
margin-top: 16rpx;
|
||||
align-items: center;
|
||||
.UserImage-box {
|
||||
width: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
.Image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.textbox {
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
.wo-text,
|
||||
.UserInfo {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.wo-text {
|
||||
color: #f1f2f6;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
.UserInfo {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.jiange-box {
|
||||
margin: 0 8rpx;
|
||||
.jiange-text {
|
||||
color: #f1f2f6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 指示器 [左边图片列表+右边按钮]
|
||||
.Swiper-indicator-Bottom {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-radius: 12rpx;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
box-sizing: border-box;
|
||||
padding: 14rpx;
|
||||
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.4), rgba(101, 101, 101, 0.7));
|
||||
// 左边[图片列表]
|
||||
.Bottom-left-Imagelist {
|
||||
// width: 560rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
// 指示图(小图模式)
|
||||
.Bottom-item {
|
||||
width: 98rpx;
|
||||
height: 78rpx;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.Image {
|
||||
width: 98rpx;
|
||||
height: 78rpx;
|
||||
border-radius: 8rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
&.current {
|
||||
.Image {
|
||||
// border: 4rpx solid #ffda02;
|
||||
border: 4rpx solid rgb(110, 186, 247);
|
||||
}
|
||||
}
|
||||
}
|
||||
.Bottom-item + .Bottom-item {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
// 右边 [历历在目-按钮]
|
||||
.Bottom-right-lili-btn {
|
||||
width: 145rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10rpx;
|
||||
// background-image: linear-gradient(45deg, rgb(110, 186, 247), rgb(13, 141, 242));
|
||||
.Bottom-item {
|
||||
width: 145rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
.indicator-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
font-size: 26rpx;
|
||||
.iconfont {
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
// font-weight: normal;
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.Swiper-box {
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden !important;
|
||||
transform: translateY(0) !important;
|
||||
transform: translateX(0) !important;
|
||||
&.right {
|
||||
.indicator-Top-box {
|
||||
position: absolute;
|
||||
}
|
||||
.Swiper-indicator-Top {
|
||||
bottom: 0;
|
||||
.Top-item .Top-Title .title-text {
|
||||
width: 540rpx;
|
||||
}
|
||||
}
|
||||
.Swiper-indicator-Bottom {
|
||||
width: 120rpx;
|
||||
position: absolute;
|
||||
top: 0rpx;
|
||||
left: initial;
|
||||
right: 0rpx;
|
||||
flex-direction: column;
|
||||
background-image: none;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
// border-radius: 0rpx 12rpx 12rpx 0;
|
||||
border-radius: 12rpx;
|
||||
.Bottom-left-Imagelist {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.Bottom-item {
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.Bottom-item + .Bottom-item {
|
||||
margin-left: 0rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
.Bottom-right-lili-btn {
|
||||
width: 100rpx;
|
||||
margin-left: -4rpx;
|
||||
.Bottom-item {
|
||||
width: initial;
|
||||
margin-top: 6rpx;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
.more {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.text {
|
||||
letter-spacing: 0;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Swiper-mfw {
|
||||
position: relative;
|
||||
}
|
||||
.indicator-Top-box {
|
||||
position: absolute;
|
||||
}
|
||||
.indicator-Btoom-box {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
<template>
|
||||
<!-- 轮播图 -->
|
||||
<view class="Swiper-mfw-index-box">
|
||||
<view class="Swiper-mfw-index Swiper-box" :class="[dotPosition]">
|
||||
<swiper
|
||||
class="Swiper-mfw"
|
||||
:style="{ height: height }"
|
||||
:circular="true"
|
||||
:indicator-dots="false"
|
||||
:autoplay="autoplay"
|
||||
:interval="3000"
|
||||
:duration="1000"
|
||||
:current="currentIndex"
|
||||
:disable-touch="disable_touch"
|
||||
@change="change"
|
||||
>
|
||||
<!-- 只需要前5条数据 -->
|
||||
<swiper-item class="swiper-mfw-item" v-if="index <= (dotPosition == 'right' ? 3 : 4)" v-for="(item, index) in list" :key="index">
|
||||
<!-- /*
|
||||
1. 这里不需要用api控制暂停视频
|
||||
2. 因为video标签上加了v-if="current==index"
|
||||
3. 当current == index时才会创建视频组件
|
||||
4. 否current != index则就销毁视频
|
||||
*/ -->
|
||||
<!-- 如果有视频,则显示视频-->
|
||||
<template v-if="item.mp4 && current == index">
|
||||
<video
|
||||
class="ImageVideo"
|
||||
:id="'ImageVideo' + index"
|
||||
:ref="'ImageVideo' + index"
|
||||
:src="item.mp4"
|
||||
:loop="true"
|
||||
:muted="false"
|
||||
:autoplay="current == index ? true : false"
|
||||
:controls="false"
|
||||
:show-fullscreen-btn="false"
|
||||
:show-play-btn="false"
|
||||
:enable-progress-gesture="false"
|
||||
:play-strategy="0"
|
||||
:poster="item.image || item.src"
|
||||
></video>
|
||||
</template>
|
||||
<!-- 否则显示图片 -->
|
||||
<image v-else :src="item.image || item.src" class="Image" mode="aspectFill" @click.stop="$emit('on-click', item)"></image>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<!-- 指示器 [Top] -->
|
||||
<view v-if="useTop" class="Swiper-indicator-box indicator-Top-box">
|
||||
<!-- Top顶部 [今日首推-盒子] -->
|
||||
<view class="Top-date-hot">
|
||||
<view class="left-date-ri">
|
||||
<text class="date-ri-text text">{{ date.month }}</text>
|
||||
</view>
|
||||
<view class="conter-date-nianyue">
|
||||
<view class="left-width-bgcolor"></view>
|
||||
<view class="right-date-nianyue">
|
||||
<text class="Top-yue-usa text">{{ date.monthEn }}</text>
|
||||
<text class="Bottom-nian text">{{ date.year }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="right-hot-ttf">
|
||||
<text class="text hot-text">{{ title }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 指示器 标题区域 -->
|
||||
<view v-if="useTitle" class="Swiper-indicator-Top" :class="{ 'no-dot': !useDot }">
|
||||
<block v-for="(item, index) in list" :key="index">
|
||||
<view v-if="currentIndex == index" class="Top-item" :class="current == index ? 'current' : 'no'">
|
||||
<!-- 如果存在视频,则显示“视频预览”提示 -->
|
||||
<view v-if="item.mp4" class="Top-ImageVideo">
|
||||
<!-- icon图标 -->
|
||||
<view class="Icons">
|
||||
<!-- 播放按钮图标 -->
|
||||
<text class="iconfont icon-caret-right"></text>
|
||||
</view>
|
||||
<text class="text ImageVideo-text app-ttf">视频预览</text>
|
||||
</view>
|
||||
<!-- 标题 -->
|
||||
<view class="Top-Title">
|
||||
<text class="text title-text">{{ item.title }}</text>
|
||||
</view>
|
||||
<!-- 用户信息 -->
|
||||
<view class="Bottom-UserInfo">
|
||||
<!-- 头像 -->
|
||||
<view class="UserImage-box"><image :src="item.avatar" class="Image" mode="aspectFill"></image></view>
|
||||
<!-- 用户名 -->
|
||||
<view class="textbox UserName-box">
|
||||
<text class="text UserInfo">{{ item.nickname }}</text>
|
||||
</view>
|
||||
<view v-if="item.createTime" class="jiange-box"><text class="text jiange-text"></text></view>
|
||||
<view v-if="item.createTime" class="textbox UserGPS-box">
|
||||
<text class="text UserInfo">发布于 {{ item.createTime }}</text>
|
||||
</view>
|
||||
<view v-if="item.address" class="jiange-box"><text class="text jiange-text">·</text></view>
|
||||
<view v-if="item.address" class="textbox UserGPS-box">
|
||||
<text class="text UserInfo">{{ item.address }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<!-- 指示器 [左边图片列表+右边按钮] -->
|
||||
<view v-if="useDot" class="Swiper-indicator-Bottom">
|
||||
<!-- 左边 -->
|
||||
<view class="Bottom-left-Imagelist">
|
||||
<block v-for="(item, index) in list" :key="index">
|
||||
<view
|
||||
class="Bottom-item"
|
||||
v-if="Number(index) <= (dotPosition == 'right' ? 3 : 4)"
|
||||
:class="currentIndex == index ? 'current' : 'no'"
|
||||
@click="SwiperIndTap(index)"
|
||||
>
|
||||
<image :src="item.image || item.src" class="Image" mode="aspectFill"></image>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<!-- 右边 -->
|
||||
<view class="Bottom-right-lili-btn">
|
||||
<view class="Bottom-item is-more">
|
||||
<view class="more" @click.stop="$emit('on-more')">
|
||||
MORE
|
||||
<text class="iconfont icon-caret-right"></text>
|
||||
</view>
|
||||
<text class="left text indicator-text">更多推荐</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'e-swiper',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '450rpx'
|
||||
},
|
||||
dotPosition: {
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
},
|
||||
useTop: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useDot: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useTitle: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useUser: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 轮播图 数据列表
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 当前选中的项(指示器坐标位置)
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 是否自动轮播
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 是否禁止用户 touch 操作
|
||||
currentIndex: 0,
|
||||
disable_touch: false, //touch 用户划动引起swiper变化。
|
||||
date: {
|
||||
year: '-',
|
||||
monthEn: '-',
|
||||
month: '-'
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.currentIndex = this.current;
|
||||
const date = new Date();
|
||||
//将月份名称存储在数组中
|
||||
const monthArray = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
|
||||
|
||||
this.date.year = date.getFullYear();
|
||||
let month = date.getMonth() + 1;
|
||||
this.date.month = month < 10 ? '0' + month : month;
|
||||
this.date.monthEn = monthArray[date.getMonth()].toUpperCase();
|
||||
},
|
||||
methods: {
|
||||
// current 改变时会触发 change 事件,event.detail = {current: current, source: source}
|
||||
change(e) {
|
||||
let { current, source } = e.detail;
|
||||
//只有页面自动切换,手动切换时才轮播,其他不允许
|
||||
if (source === 'autoplay' || source === 'touch') {
|
||||
let event = {
|
||||
current: current
|
||||
};
|
||||
this.currentIndex = current;
|
||||
this.$emit('change', event);
|
||||
}
|
||||
},
|
||||
// 手动点击了指示器[小图模式]
|
||||
SwiperIndTap(e) {
|
||||
let index = e;
|
||||
let event = {
|
||||
current: index
|
||||
};
|
||||
this.currentIndex = index;
|
||||
this.$emit('change', event);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './e-swiper.scss';
|
||||
</style>
|
||||
@@ -1,177 +0,0 @@
|
||||
<template>
|
||||
<view class="journal-card mb-24 round-3 bg-white ">
|
||||
<view class="head pa-24 pb-0 flex flex-between">
|
||||
<view class="left flex">
|
||||
<cache-image
|
||||
class="avatar rounded"
|
||||
radius="50%"
|
||||
width="70rpx"
|
||||
height="70rpx"
|
||||
:url="bloggerInfo.avatar"
|
||||
:fileMd5="bloggerInfo.avatar"
|
||||
mode="scaleToFill"
|
||||
></cache-image>
|
||||
<view class="info pl-16 flex flex-col">
|
||||
<view class="nickname text-weight-b text-grey-darken-4">{{ bloggerInfo.nickname }}</view>
|
||||
<view class="mt-3 time text-size-m ">{{ $tm.dayjs(journal.createTime).format('YYYY-MM-DD HH:mm:ss') }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="right">
|
||||
<tm-button v-if="useLike" :shadow="0" theme="light-blue" size="s" @click="fnLike(journal)">点赞({{ journal.likes }})</tm-button>
|
||||
<tm-button v-if="useEdit" :shadow="0" theme="light-blue" size="s" @click="$emit('on-edit', journal)">编辑</tm-button>
|
||||
<tm-button v-if="useDel" :shadow="0" theme="red" size="s" @click="fnDel(journal)">删除</tm-button>
|
||||
</view>
|
||||
</view>
|
||||
<tm-more v-if="journal.content.length > 50" :maxHeight="100" label="查看全部内容" open-label="隐藏部分内容">
|
||||
<mp-html
|
||||
class="evan-markdown"
|
||||
lazy-load
|
||||
:domain="markdownConfig.domain"
|
||||
:loading-img="markdownConfig.loadingGif"
|
||||
:scroll-table="true"
|
||||
:selectable="true"
|
||||
:tag-style="markdownConfig.tagStyle"
|
||||
:container-style="markdownConfig.containStyle"
|
||||
:content="journal.content"
|
||||
:markdown="true"
|
||||
:showLineNumber="true"
|
||||
:showLanguageName="true"
|
||||
:copyByLongPress="true"
|
||||
/>
|
||||
</tm-more>
|
||||
<mp-html
|
||||
v-else
|
||||
class="evan-markdown"
|
||||
lazy-load
|
||||
:domain="markdownConfig.domain"
|
||||
:loading-img="markdownConfig.loadingGif"
|
||||
:scroll-table="true"
|
||||
:selectable="true"
|
||||
:tag-style="markdownConfig.tagStyle"
|
||||
:container-style="markdownConfig.containStyle"
|
||||
:content="journal.content"
|
||||
:markdown="true"
|
||||
:showLineNumber="true"
|
||||
:showLanguageName="true"
|
||||
:copyByLongPress="true"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
||||
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
||||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||
import tmMore from '@/tm-vuetify/components/tm-more/tm-more.vue';
|
||||
export default {
|
||||
name: 'journal-card',
|
||||
components: { mpHtml, tmButton, tmMore },
|
||||
props: {
|
||||
isAdmin: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
journal: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
useLike: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
useEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
useDel: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
markdownConfig: MarkdownConfig
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 获取博主信息
|
||||
bloggerInfo() {
|
||||
let blogger = this.$tm.vx.getters().getBlogger;
|
||||
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
|
||||
return blogger;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fnLike(journal) {
|
||||
uni.showLoading({
|
||||
mask: true,
|
||||
title: '正在点赞中...'
|
||||
});
|
||||
this.$httpApi
|
||||
.postJournalLikes(journal.id)
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
journal.likes += 1;
|
||||
uni.$tm.toast('o( ̄▽ ̄)d点赞成功!');
|
||||
} else {
|
||||
uni.$tm.toast('Ծ‸Ծ点赞失败了~');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
uni.$tm.toast('Ծ‸Ծ点赞失败了~');
|
||||
});
|
||||
},
|
||||
|
||||
fnDel(journal) {
|
||||
uni.$eShowModal({
|
||||
title: '提示',
|
||||
content: '您确定要删除该日记吗?',
|
||||
showCancel: true,
|
||||
cancelText: '否',
|
||||
cancelColor: '#999999',
|
||||
confirmText: '是',
|
||||
confirmColor: '#03a9f4'
|
||||
})
|
||||
.then(res => {
|
||||
this.$httpApi.admin
|
||||
.deleteJournalsById(journal.id)
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
this.$emit('on-del', journal);
|
||||
uni.$tm.toast('删除成功!');
|
||||
} else {
|
||||
uni.$tm.toast('Ծ‸Ծ删除失败~');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
uni.$tm.toast('Ծ‸Ծ删除失败~');
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.journal-card {
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
.avatar {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
border: 6rpx solid #fff;
|
||||
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.info {
|
||||
justify-content: center;
|
||||
.nickname {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.time {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,250 +0,0 @@
|
||||
<template>
|
||||
<view style="overflow: hidden;position: fixed;width: 100%;height: 100%;pointer-events: none; top: 0;">
|
||||
<view class="danmu-li" v-for="(item, index) in listData" :class="item.type" :style="item.style" :key="index">
|
||||
<view class="danmu-inner">
|
||||
<view class="user-box">
|
||||
<view class="user-img">
|
||||
<view class="img-box">
|
||||
<image :src="item.avatar || 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=317894666,3379114684&fm=26&gp=0.jpg'"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-text cl1">{{ item.nickName }}</view>
|
||||
<view class="user-status cl1">{{ item.text }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
//rightToLeft leftToRight leftBottom
|
||||
type: {
|
||||
type: String,
|
||||
default: 'rightToLeft'
|
||||
},
|
||||
minTime: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
maxTime: {
|
||||
type: Number,
|
||||
default: 9
|
||||
},
|
||||
minTop: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
maxTop: {
|
||||
type: Number,
|
||||
default: 240
|
||||
},
|
||||
hrackH: {
|
||||
//轨道高度
|
||||
type: Number,
|
||||
default: 40
|
||||
},
|
||||
noStacked: {
|
||||
//不允许堆叠(暂不可用)
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
listData: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.hrackNum = Math.floor((this.maxTop - this.minTop) / this.hrackH);
|
||||
},
|
||||
methods: {
|
||||
add(obj) {
|
||||
let data = {
|
||||
item: obj.item,
|
||||
id: Date.parse(new Date()),
|
||||
time: Math.ceil(Math.floor(Math.random() * (this.maxTime - this.minTime + 1) + this.minTime)),
|
||||
type: this.type
|
||||
};
|
||||
if (this.type === 'leftBottom') {
|
||||
let objData = {
|
||||
item: data.item,
|
||||
type: 'leftBottomEnter',
|
||||
style: {
|
||||
transition: `all 0.5s`,
|
||||
animationDuration: `0.5s`,
|
||||
transform: `translateX(0%)`,
|
||||
bottom: `${this.minTop}px`
|
||||
}
|
||||
};
|
||||
let listLen = this.listData.length;
|
||||
let hrackNum = this.hrackNum;
|
||||
for (let i in this.listData) {
|
||||
if (this.listData[i].status === 'reuse') {
|
||||
//重用
|
||||
this.$set(this.listData, i, objData);
|
||||
} else if (this.listData[i].status === 'reset') {
|
||||
//重置
|
||||
this.listData[i].style.transition = 'none';
|
||||
this.listData[i].style.bottom = 0;
|
||||
this.listData[i].status = 'reuse';
|
||||
} else if (this.listData[i].status === 'recycle') {
|
||||
//回收
|
||||
this.listData[i].type = 'leftBottomExit';
|
||||
this.listData[i].status = 'reset';
|
||||
} else {
|
||||
this.listData[i].style.bottom = parseInt(this.listData[i].style.bottom) + this.hrackH + 'px';
|
||||
}
|
||||
if (parseInt(this.listData[i].style.bottom) >= this.maxTop - this.hrackH && this.listData[i].status !== 'reset') {
|
||||
//需要回收
|
||||
this.listData[i].status = 'recycle';
|
||||
}
|
||||
}
|
||||
if (listLen < hrackNum + 2) {
|
||||
this.listData.push(objData);
|
||||
}
|
||||
} else if (this.type === 'rightToLeft' || this.type === 'leftToRight') {
|
||||
let objData = this.horStacked(data);
|
||||
for (let i in this.listData) {
|
||||
if (this.listData[i].delTime <= Date.parse(new Date())) {
|
||||
this.repaint(i, objData.type);
|
||||
objData.type = '';
|
||||
this.$set(this.listData, i, objData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.listData.push(objData);
|
||||
}
|
||||
},
|
||||
horStacked(data) {
|
||||
return {
|
||||
item: data.item,
|
||||
type: this.type,
|
||||
style: {
|
||||
animationDuration: `${data.time}s`,
|
||||
top: `${Math.ceil(Math.random() * (this.maxTop - this.minTop + 1) + this.minTop)}px`
|
||||
},
|
||||
delTime: Date.parse(new Date()) + data.time * 1200
|
||||
};
|
||||
},
|
||||
repaint(index, type) {
|
||||
setTimeout(() => {
|
||||
this.listData[index].type = type;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
<style lang="scss">
|
||||
@keyframes leftBottomEnter {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes leftBottomExit {
|
||||
0% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(-200%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes leftToRight {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rightToLeft {
|
||||
0% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.danmu-li {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
transform: translateX(100%);
|
||||
animation-timing-function: linear;
|
||||
|
||||
&.leftBottomEnter {
|
||||
animation-name: leftBottomEnter;
|
||||
}
|
||||
&.leftBottomExit {
|
||||
animation-name: leftBottomExit;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.rightToLeft {
|
||||
animation-name: rightToLeft;
|
||||
}
|
||||
|
||||
&.leftToRight {
|
||||
animation-name: leftToRight;
|
||||
}
|
||||
|
||||
.danmu-inner {
|
||||
display: inline-block;
|
||||
|
||||
.user-box {
|
||||
display: flex;
|
||||
padding: 3rpx 40rpx 3rpx 10rpx;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 32rpx;
|
||||
align-items: center;
|
||||
|
||||
.user-img {
|
||||
.img-box {
|
||||
display: flex;
|
||||
|
||||
image {
|
||||
width: 58rpx;
|
||||
height: 58rpx;
|
||||
background: rgba(55, 55, 55, 1);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-status {
|
||||
margin-left: 10rpx;
|
||||
white-space: nowrap;
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.user-text {
|
||||
margin-left: 10rpx;
|
||||
// white-space: nowrap;
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
width: 80rpx;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +0,0 @@
|
||||
#如何使用
|
||||
###js
|
||||
```javascript
|
||||
import lffBarrage from '@/components/lff-barrage/lff-barrage.vue'
|
||||
components:{lffBarrage},
|
||||
methods:{
|
||||
colrdo(){ //插入一条弹幕
|
||||
this.$refs.lffBarrage.add({item:'你好呀小伙子'});
|
||||
}
|
||||
}
|
||||
```
|
||||
###HTML
|
||||
```html
|
||||
<lff-barrage ref="lffBarrage"></lff-barrage>
|
||||
```
|
||||
@@ -1,735 +0,0 @@
|
||||
export default{
|
||||
data(){
|
||||
return{
|
||||
system_info:{}, //system info
|
||||
canvas_width:0, //canvas width px
|
||||
canvas_height:0, //canvas height px
|
||||
ctx:null, //canvas object
|
||||
canvas_id:null, //canvas id
|
||||
hidden:false,//Whether to hide canvas
|
||||
scale:1,//canvas scale
|
||||
r_canvas_scale:1,
|
||||
if_ctx:true
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
/**
|
||||
* save r-canvas.vue object
|
||||
* @param {Object} that
|
||||
*/
|
||||
// saveThis(that){
|
||||
// rCanvasThis = that
|
||||
// },
|
||||
/**
|
||||
* Draw round rect text
|
||||
* @param {Object} config
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {Number} config.w 宽度
|
||||
* @param {Number} config.h 高度
|
||||
* @param {Number} config.radius 圆角弧度
|
||||
* @param {String} config.fill_color 矩形颜色
|
||||
*/
|
||||
fillRoundRect(config) {
|
||||
return new Promise((resolve,reject)=>{
|
||||
let x = this.compatibilitySize(parseFloat(config.x)*this.scale)
|
||||
let y = this.compatibilitySize(parseFloat(config.y)*this.scale)
|
||||
let w = this.compatibilitySize(parseFloat(config.w)*this.scale)
|
||||
let h = this.compatibilitySize(parseFloat(config.h)*this.scale)
|
||||
let radius = config.radius?parseFloat(config.radius)*this.scale:10*this.scale
|
||||
|
||||
let fill_color = config.fill_color || "black"
|
||||
// The diameter of the circle must be less than the width and height of the rectangle
|
||||
if (2 * radius > w || 2 * radius > h) {
|
||||
reject("The diameter of the circle must be less than the width and height of the rectangle")
|
||||
return false;
|
||||
}
|
||||
this.ctx.save();
|
||||
this.ctx.translate(x, y);
|
||||
//
|
||||
this.drawRoundRectPath({
|
||||
w: w,
|
||||
h: h,
|
||||
radius: radius
|
||||
});
|
||||
this.ctx.fillStyle = fill_color
|
||||
this.ctx.fill();
|
||||
this.ctx.restore();
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Draws the sides of a rounded rectangle
|
||||
* @param {Object} config
|
||||
* @param {Number} config.w 宽度
|
||||
* @param {Number} config.h 高度
|
||||
* @param {Number} config.radius 圆角弧度
|
||||
*/
|
||||
drawRoundRectPath(config) {
|
||||
this.ctx.beginPath(0);
|
||||
this.ctx.arc(config.w - config.radius, config.h - config.radius, config.radius, 0, Math.PI / 2);
|
||||
this.ctx.lineTo(config.radius, config.h);
|
||||
this.ctx.arc(config.radius, config.h - config.radius, config.radius, Math.PI / 2, Math.PI);
|
||||
this.ctx.lineTo(0, config.radius);
|
||||
this.ctx.arc(config.radius, config.radius, config.radius, Math.PI, Math.PI * 3 / 2);
|
||||
this.ctx.lineTo(config.w - config.radius, 0);
|
||||
this.ctx.arc(config.w - config.radius, config.radius, config.radius, Math.PI * 3 / 2, Math.PI * 2);
|
||||
this.ctx.lineTo(config.w, config.h - config.radius);
|
||||
this.ctx.closePath();
|
||||
},
|
||||
/**
|
||||
* Draw special Text,line wrapping is not supported
|
||||
* @param {Object} config
|
||||
* @param {String} config.text 文字
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {String} config.font_color 文字颜色
|
||||
* @param {String} config.font_family 文字字体
|
||||
* @param {Number} config.font_size 文字大小(px)
|
||||
*/
|
||||
drawSpecialText(params){
|
||||
let general = params.general
|
||||
let list = params.list
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(!general){
|
||||
reject("general cannot be empty:101")
|
||||
return;
|
||||
}else if(list && list.length>0){
|
||||
for(let i in list){
|
||||
if(i != 0){
|
||||
let font_size = list[i-1].font_size?parseFloat(list[i-1].font_size):20
|
||||
this.ctx.setFontSize(font_size)
|
||||
general.x = parseFloat(general.x) + this.ctx.measureText(list[i-1].text).width
|
||||
}
|
||||
list[i].x = general.x
|
||||
list[i].y = general.y + (list[i].margin_top?parseFloat(list[i].margin_top):0)
|
||||
await this.drawText(list[i])
|
||||
}
|
||||
resolve()
|
||||
}else{
|
||||
reject("The length of config arr is less than 0")
|
||||
return;
|
||||
}
|
||||
|
||||
})
|
||||
},
|
||||
/**
|
||||
* array delete empty
|
||||
* @param {Object} arr
|
||||
*/
|
||||
arrDeleteEmpty(arr){
|
||||
let newArr = []
|
||||
for(let i in arr){
|
||||
if(arr[i]){
|
||||
newArr.push(arr[i])
|
||||
}
|
||||
}
|
||||
return newArr
|
||||
},
|
||||
/**
|
||||
* Draw Text,support line
|
||||
* @param {Object} config
|
||||
* @param {String} config.text 文字
|
||||
* @param {Number} config.max_width 文字最大宽度(大于宽度自动换行)
|
||||
* @param {Number} config.line_height 文字上下行间距
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {String} config.font_color 文字颜色
|
||||
* @param {String} config.font_family 文字字体 默认值:Arial
|
||||
* @param {String} config.text_align 文字对齐方式(left/center/right)
|
||||
* @param {Number} config.font_size 文字大小(px)
|
||||
* @param {Boolean} config.line_through_height 中划线大小
|
||||
* @param {Boolean} config.line_through_color 中划线颜色
|
||||
* @param {String} config.font_style 规定文字样式
|
||||
* @param {String} config.font_variant 规定字体变体
|
||||
* @param {String} config.font_weight 规定字体粗细
|
||||
* @param {String} config.line_through_cap 线末端类型
|
||||
* @param {String} config.line_clamp 最大行数
|
||||
* @param {String} config.line_clamp_hint 超过line_clamp后,尾部显示的自定义标识 如 ...
|
||||
* @param {String} config.is_line_break 是否开启换行符换行
|
||||
*
|
||||
*/
|
||||
drawText(config,configuration = {}){
|
||||
|
||||
configuration['line_num'] = configuration.line_num?configuration.line_num:0
|
||||
configuration['text_width'] = configuration.text_width?configuration.text_width:0
|
||||
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
|
||||
if(config.text){
|
||||
|
||||
let draw_width = 0,draw_height = 0,draw_x = config.x,draw_y = config.y
|
||||
let font_size = config.font_size?(parseFloat(config.font_size)*this.scale):(20*this.scale)
|
||||
let font_color = config.font_color || "#000"
|
||||
let font_family = config.font_family || "Arial"
|
||||
let line_height = config.line_height || config.font_size || 20
|
||||
let text_align = config.text_align || "left"
|
||||
let font_weight = config.font_weight || "normal"
|
||||
let font_variant = config.font_variant || "normal"
|
||||
let font_style = config.font_style || "normal"
|
||||
let line_clamp_hint = config.line_clamp_hint || '...'
|
||||
let lineBreakJoinText = ""
|
||||
let max_width = config.max_width?parseFloat(config.max_width)*this.scale:0
|
||||
// checkout is line break
|
||||
if(config.is_line_break){
|
||||
let splitTextArr = config.text.split(/[\n]/g)
|
||||
if(splitTextArr && splitTextArr.length > 0){
|
||||
let newSplitTextArr = this.arrDeleteEmpty(splitTextArr)
|
||||
if(newSplitTextArr && newSplitTextArr.length > 0){
|
||||
lineBreakJoinText = newSplitTextArr.slice(1).join("\n")
|
||||
config.text = newSplitTextArr[0]
|
||||
}else{
|
||||
reject("Text cannot be empty:103")
|
||||
return
|
||||
}
|
||||
}else{
|
||||
reject("Text cannot be empty:102")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.ctx.setFillStyle(font_color) // color
|
||||
this.ctx.textAlign = text_align;
|
||||
this.ctx.font = `${font_style} ${font_variant} ${font_weight} ${parseInt(font_size)}px ${font_family}`
|
||||
if(configuration.text_width >= this.ctx.measureText(config.text).width){
|
||||
draw_width = configuration.text_width
|
||||
}else if(max_width > 0){
|
||||
draw_width = max_width < this.ctx.measureText(config.text).width ? this.resetCompatibilitySize(max_width) : this.resetCompatibilitySize(this.ctx.measureText(config.text).width)
|
||||
}else{
|
||||
draw_width = this.ctx.measureText(config.text).width
|
||||
}
|
||||
configuration.text_width = draw_width / this.scale
|
||||
if( max_width && this.compatibilitySize(this.ctx.measureText(config.text).width) > this.compatibilitySize(max_width)){
|
||||
let current_text = ""
|
||||
let text_arr = config.text.split("")
|
||||
for(let i in text_arr){
|
||||
if( this.compatibilitySize(this.ctx.measureText(current_text+text_arr[i]).width) > this.compatibilitySize(max_width) ){
|
||||
// Hyphenation that is greater than the drawable width continues to draw
|
||||
if(config.line_clamp && parseInt(config.line_clamp) == 1){
|
||||
// Subtracting the current_text tail width from the line_clamp_hint width
|
||||
let current_text_arr = current_text.split('')
|
||||
let json_current_text = ''
|
||||
while(true){
|
||||
current_text_arr = current_text_arr.slice(1)
|
||||
json_current_text = current_text_arr.join('')
|
||||
if(this.compatibilitySize(this.ctx.measureText(json_current_text).width) <= this.compatibilitySize(this.ctx.measureText(line_clamp_hint).width)){
|
||||
current_text = current_text.replace(json_current_text,'')
|
||||
break;
|
||||
}
|
||||
}
|
||||
configuration.line_num += 1
|
||||
this.ctx.setFontSize(parseInt(this.compatibilitySize(font_size))) // font size
|
||||
this.ctx.fillText(current_text + line_clamp_hint, this.compatibilitySize(parseFloat(config.x)*this.scale), this.compatibilitySize(parseFloat(config.y)*this.scale));
|
||||
}else{
|
||||
configuration.line_num += 1
|
||||
this.ctx.setFontSize(parseInt(this.compatibilitySize(font_size))) // font size
|
||||
this.ctx.fillText(current_text, this.compatibilitySize(parseFloat(config.x)*this.scale), this.compatibilitySize(parseFloat(config.y)*this.scale));
|
||||
config.text = text_arr.slice(i).join("")
|
||||
config.y = config.y + line_height
|
||||
if(config.line_clamp){
|
||||
config.line_clamp = parseInt(config.line_clamp) - 1
|
||||
}
|
||||
await this.drawText(config,configuration)
|
||||
}
|
||||
|
||||
break;
|
||||
}else{
|
||||
current_text = current_text+text_arr[i]
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(config.line_through_height){
|
||||
let x = parseFloat(config.x)*this.scale
|
||||
let w
|
||||
let y = parseFloat(config.y)*this.scale - (font_size / 2.6)
|
||||
if(text_align == "left"){
|
||||
w = this.ctx.measureText(config.text).width/1.1 + parseFloat(config.x)*this.scale
|
||||
}else if(text_align == "right"){
|
||||
w = parseFloat(config.x)*this.scale - this.ctx.measureText(config.text).width/1.1
|
||||
}else if(text_align == "center"){
|
||||
x = parseFloat(config.x)*this.scale - this.ctx.measureText(config.text).width / 1.1 / 2
|
||||
w = parseFloat(config.x)*this.scale + this.ctx.measureText(config.text).width / 1.1 / 2
|
||||
}
|
||||
this.drawLineTo({
|
||||
x:x,
|
||||
y:y,
|
||||
w:w,
|
||||
h:y,
|
||||
line_width:config.line_through_height,
|
||||
line_color:config.line_through_color,
|
||||
line_cap:config.line_through_cap
|
||||
})
|
||||
}
|
||||
configuration.line_num += 1
|
||||
this.ctx.setFontSize(parseInt(this.compatibilitySize(font_size))) // font size
|
||||
this.ctx.fillText(config.text, this.compatibilitySize(parseFloat(config.x)*this.scale), this.compatibilitySize(parseFloat(config.y)*this.scale));
|
||||
if(config.line_clamp){
|
||||
config.line_clamp = parseInt(config.line_clamp) - 1
|
||||
}
|
||||
}
|
||||
if(lineBreakJoinText){
|
||||
await this.drawText({...config,text:lineBreakJoinText,y:config.y + line_height},configuration)
|
||||
}
|
||||
draw_height = config.font_size * configuration.line_num
|
||||
draw_width = configuration.text_width
|
||||
resolve({draw_width,draw_height,draw_x,draw_y})
|
||||
}else{
|
||||
reject("Text cannot be empty:101")
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Draw Line
|
||||
* @param {Object} config
|
||||
* @param {Object} config.x x坐标
|
||||
* @param {Object} config.y y坐标
|
||||
* @param {Object} config.w 线的宽度
|
||||
* @param {Object} config.h 线的高度
|
||||
* @param {Object} config.line_width 线的宽度
|
||||
* @param {Object} config.line_color 线条颜色
|
||||
*/
|
||||
drawLineTo(config){
|
||||
let x = this.compatibilitySize(config.x)
|
||||
let y = this.compatibilitySize(config.y)
|
||||
let w = this.compatibilitySize(config.w)
|
||||
let h = this.compatibilitySize(config.h)
|
||||
let line_width = config.line_width?parseFloat(config.line_width)*this.scale:1*this.scale
|
||||
let line_color = config.line_color || "black"
|
||||
let line_cap = config.line_cap || "butt"
|
||||
this.ctx.beginPath()
|
||||
this.ctx.lineCap = line_cap
|
||||
this.ctx.lineWidth = line_width
|
||||
this.ctx.strokeStyle = line_color
|
||||
this.ctx.moveTo(x,y)
|
||||
this.ctx.lineTo(w,h)
|
||||
this.ctx.stroke()
|
||||
},
|
||||
/**
|
||||
* Compatibility px
|
||||
* @param {Object} size
|
||||
*/
|
||||
compatibilitySize(size) {
|
||||
let canvasSize = (parseFloat(size) / 750) * this.system_info.windowWidth
|
||||
canvasSize = parseFloat(canvasSize * 2)
|
||||
return canvasSize
|
||||
},
|
||||
/**
|
||||
* Restore compatibility px
|
||||
* @param {Object} size
|
||||
*/
|
||||
resetCompatibilitySize(size) {
|
||||
let canvasSize = (parseFloat(size/2)/this.system_info.windowWidth) * 750
|
||||
return canvasSize
|
||||
},
|
||||
/**
|
||||
* Init canvas
|
||||
*/
|
||||
init(config){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(!config.canvas_id){
|
||||
reject("Canvas ID cannot be empty, please refer to the usage example")
|
||||
return;
|
||||
}
|
||||
this.hidden = config.hidden
|
||||
this.canvas_id = config.canvas_id
|
||||
let system_info = await uni.getSystemInfoSync()
|
||||
this.system_info = system_info
|
||||
this.scale = config.scale&&parseFloat(config.scale)>0?parseInt(config.scale):1
|
||||
this.canvas_width = (config.canvas_width ? this.compatibilitySize(config.canvas_width) : system_info.windowWidth) * this.scale
|
||||
this.canvas_height = (config.canvas_height ? this.compatibilitySize(config.canvas_height) : system_info.windowHeight) * this.scale,
|
||||
this.r_canvas_scale = 1/this.scale
|
||||
this.ctx = uni.createCanvasContext(this.canvas_id,this)
|
||||
this.setCanvasConfig({
|
||||
global_alpha:config.global_alpha?parseFloat(config.global_alpha):1,
|
||||
backgroundColor:config.background_color?config.background_color:"#fff"
|
||||
})
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
/**
|
||||
* clear canvas all path
|
||||
*/
|
||||
clearCanvas(){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(!this.ctx){
|
||||
reject("canvas is not initialized:101")
|
||||
return
|
||||
}else{
|
||||
this.ctx.clearRect(0,0,parseFloat(this.canvas_width)*this.scale,parseFloat(this.canvas_height)*this.scale)
|
||||
await this.draw()
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Set canvas config
|
||||
* @param {Object} config
|
||||
*/
|
||||
setCanvasConfig(config){
|
||||
this.ctx.globalAlpha = config.global_alpha
|
||||
this.ctx.fillStyle = config.backgroundColor
|
||||
this.ctx.fillRect(0, 0, parseFloat(this.canvas_width)*this.scale, parseFloat(this.canvas_height)*this.scale)
|
||||
},
|
||||
/**
|
||||
* set canvas width
|
||||
* @param {Object} width
|
||||
*/
|
||||
setCanvasWidth(width){
|
||||
if(!width){
|
||||
uni.showToast({
|
||||
title:'setCanvasWidth:width error',
|
||||
icon:'none'
|
||||
})
|
||||
}
|
||||
this.canvas_width = this.compatibilitySize(parseFloat(width)) * this.scale
|
||||
this.ctx.width = this.canvas_width
|
||||
},
|
||||
/**
|
||||
* set canvas height
|
||||
* @param {Object} height
|
||||
*/
|
||||
setCanvasHeight(height){
|
||||
if(!height){
|
||||
uni.showToast({
|
||||
title:'setCanvasWidth:height error',
|
||||
icon:'none'
|
||||
})
|
||||
}
|
||||
this.canvas_height = this.compatibilitySize(parseFloat(height)) * this.scale
|
||||
this.ctx.height = this.canvas_height
|
||||
},
|
||||
/**
|
||||
* Draw to filepath
|
||||
*/
|
||||
draw(callback){
|
||||
return new Promise((resolve,reject)=>{
|
||||
let stop = setTimeout(()=>{
|
||||
this.ctx.draw(false,setTimeout(()=>{
|
||||
uni.canvasToTempFilePath({
|
||||
canvasId: this.canvas_id,
|
||||
quality: 1,
|
||||
success: (res)=>{
|
||||
console.log('res',res)
|
||||
resolve(res)
|
||||
callback && callback(res)
|
||||
},
|
||||
fail:(err)=>{
|
||||
reject(JSON.stringify(err)|| "Failed to generate poster:101")
|
||||
}
|
||||
},this)
|
||||
},300))
|
||||
clearTimeout(stop)
|
||||
},300)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* draw rect
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {Number} config.w 图形宽度(px)
|
||||
* @param {Number} config.h 图形高度(px)
|
||||
* @param {Number} config.color 图形颜色
|
||||
* @param {Number} config.is_radius 是否开启圆图(1.1.6及以下版本废弃,请使用border_radius)
|
||||
* @param {Number} config.border_width 边框大小
|
||||
* @param {Number} config.border_color 边框颜色
|
||||
*
|
||||
*/
|
||||
drawRect(config){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(!config.border_width || config.border_width <=0){
|
||||
config.border_width = 0
|
||||
}else{
|
||||
config.border_width = parseFloat(config.border_width)
|
||||
}
|
||||
if(parseFloat(config.border_width) > 0){
|
||||
let sub_config = JSON.parse(JSON.stringify(config))
|
||||
sub_config.border_width = 0
|
||||
sub_config.w = config.w + config.border_width
|
||||
sub_config.h = config.h + config.border_width
|
||||
sub_config.color = config.border_color || 'black'
|
||||
if(sub_config.border_radius){
|
||||
sub_config.border_radius = parseFloat(sub_config.border_radius) + parseFloat(config.border_width) / 2
|
||||
}
|
||||
await this.drawRect(sub_config)
|
||||
}
|
||||
|
||||
let color = config.color || 'white'
|
||||
config.x = (parseFloat(config.x) + config.border_width / 2)
|
||||
config.y = (parseFloat(config.y) + config.border_width / 2)
|
||||
config['color'] = color
|
||||
this.ctx.fillStyle = color;
|
||||
if(config.is_radius || config.border_radius){
|
||||
this.setNativeBorderRadius(config)
|
||||
this.ctx.fill()
|
||||
}else{
|
||||
console.log('config.border_width',config.border_width)
|
||||
this.ctx.fillRect(this.compatibilitySize(config.x*this.scale),this.compatibilitySize(config.y*this.scale),this.compatibilitySize(parseFloat(config.w)*this.scale),this.compatibilitySize(parseFloat(config.h)*this.scale))
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Draw image
|
||||
* @param {Object} config
|
||||
* @param {String} config.url 图片链接
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {Number} config.w 图片宽度(px)
|
||||
* @param {Number} config.h 图片高度(px)
|
||||
* @param {Number} config.border_width 边大小
|
||||
* @param {Number} config.border_color 边颜色
|
||||
* @param {Number} config.is_radius 是否开启圆图(1.1.6及以下版本废弃,请使用border_radius)
|
||||
* @param {Number} config.border_radius 圆角弧度
|
||||
*/
|
||||
drawImage(config){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(config.url){
|
||||
let type = 0 // 1、network image 2、native image 3、base64 image
|
||||
let image_url
|
||||
let reg = /^https?/ig;
|
||||
if(reg.test(config.url)){
|
||||
type = 1
|
||||
}else{
|
||||
if((config.url.indexOf("data:image/png;base64") != -1) || config.url.indexOf("data:image/jpeg;base64") != -1 || config.url.indexOf("data:image/gif;base64") != -1){
|
||||
type = 3
|
||||
}else{
|
||||
type = 2
|
||||
}
|
||||
}
|
||||
if(type == 1){
|
||||
// network image
|
||||
await this.downLoadNetworkFile(config.url).then(res=>{ // two function
|
||||
image_url = res
|
||||
}).catch(err=>{
|
||||
reject(err)
|
||||
return;
|
||||
})
|
||||
}else if(type == 2){
|
||||
// native image
|
||||
const imageInfoResult = await uni.getImageInfo({
|
||||
src: config.url
|
||||
});
|
||||
try{
|
||||
if(imageInfoResult.length <= 1){
|
||||
reject(imageInfoResult[0].errMsg + ':404')
|
||||
return
|
||||
}
|
||||
}catch(e){
|
||||
reject(e+':500')
|
||||
return
|
||||
}
|
||||
let base64 = await this.urlToBase64({url:imageInfoResult[1].path})
|
||||
// #ifdef MP-WEIXIN
|
||||
await this.base64ToNative({url:base64}).then(res=>{
|
||||
image_url = res
|
||||
}).catch(err=>{
|
||||
reject(JSON.stringify(err)+":501")
|
||||
return;
|
||||
})
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
image_url = base64
|
||||
// #endif
|
||||
|
||||
}else if(type == 3){
|
||||
// #ifdef MP-WEIXIN
|
||||
await this.base64ToNative({url:config.url}).then(res=>{
|
||||
image_url = res
|
||||
}).catch(err=>{
|
||||
reject(JSON.stringify(err)+":500")
|
||||
return;
|
||||
})
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
image_url = config.url
|
||||
// #endif
|
||||
}else{
|
||||
reject("Other Type Errors:101")
|
||||
return
|
||||
}
|
||||
if(config.border_width){
|
||||
let border_radius = 0
|
||||
if(config.border_radius){
|
||||
let multiple = config.w / config.border_radius
|
||||
border_radius = (parseFloat(config.w) + parseFloat(config.border_width)) / multiple
|
||||
}
|
||||
// drawRect
|
||||
await this.drawRect({
|
||||
x:parseFloat(config.x) - parseFloat(config.border_width)/2,
|
||||
y:parseFloat(config.y) - parseFloat(config.border_width)/2,
|
||||
w:parseFloat(config.w) + parseFloat(config.border_width),
|
||||
h:parseFloat(config.h) + parseFloat(config.border_width),
|
||||
color:config.border_color,
|
||||
border_radius:border_radius,
|
||||
border_width:config.border_width,
|
||||
is_radius:config.is_radius
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(config.border_radius){
|
||||
config.color = config.color?config.color:'rgba(0,0,0,0)'
|
||||
|
||||
// 圆角有白边,+0.5的误差
|
||||
config.w = config.w + 0.3
|
||||
config.h = config.h + 0.3
|
||||
|
||||
this.setNativeBorderRadius(config)
|
||||
}else if(config.is_radius){
|
||||
//已废弃 is_radius
|
||||
this.ctx.setStrokeStyle("rgba(0,0,0,0)")
|
||||
this.ctx.save()
|
||||
this.ctx.beginPath()
|
||||
this.ctx.arc(this.compatibilitySize(parseFloat(config.x)*this.scale+parseFloat(config.w)*this.scale/2), this.compatibilitySize(parseFloat(config.y)*this.scale+parseFloat(config.h)*this.scale/2), this.compatibilitySize(parseFloat(config.w)*this.scale/2), 0, 2 * Math.PI, false)
|
||||
this.ctx.stroke();
|
||||
this.ctx.clip()
|
||||
}
|
||||
|
||||
await this.ctx.drawImage(image_url,this.compatibilitySize(parseFloat(config.x)*this.scale),this.compatibilitySize(parseFloat(config.y)*this.scale),this.compatibilitySize(parseFloat(config.w)*this.scale),this.compatibilitySize(parseFloat(config.h)*this.scale))
|
||||
this.ctx.restore() //Restore previously saved drawing context
|
||||
resolve()
|
||||
}else{
|
||||
let err_msg = "Links cannot be empty:101"
|
||||
reject(err_msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* base64 to native available path
|
||||
* @param {Object} config
|
||||
*/
|
||||
base64ToNative(config){
|
||||
return new Promise((resolve,reject)=>{
|
||||
let fileName = new Date().getTime()
|
||||
var filePath = `${wx.env.USER_DATA_PATH}/${fileName}_rCanvas.png`
|
||||
wx.getFileSystemManager().writeFile({
|
||||
filePath: filePath,
|
||||
data: config.url.replace(/^data:\S+\/\S+;base64,/, ''),
|
||||
encoding: 'base64',
|
||||
success: function() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail: function(error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* native url to base64
|
||||
* @param {Object} config
|
||||
*/
|
||||
urlToBase64(config){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if (typeof window != 'undefined') {
|
||||
await this.downLoadNetworkFile(config.url).then(res=>{ // two function
|
||||
resolve(res)
|
||||
}).catch(err=>{
|
||||
reject(err)
|
||||
})
|
||||
}else if (typeof plus != 'undefined') {
|
||||
plus.io.resolveLocalFileSystemURL(config.url,(obj)=>{
|
||||
obj.file((file)=>{
|
||||
let fileReader = new plus.io.FileReader()
|
||||
fileReader.onload = (res)=>{
|
||||
resolve(res.target.result)
|
||||
}
|
||||
fileReader.onerror = (err)=>{
|
||||
reject(err)
|
||||
}
|
||||
fileReader.readAsDataURL(file)
|
||||
}, (err)=>{
|
||||
reject(err)
|
||||
})
|
||||
},(err)=>{
|
||||
reject(err)
|
||||
})
|
||||
}else if(typeof wx != 'undefined'){
|
||||
wx.getFileSystemManager().readFile({
|
||||
filePath: config.url,
|
||||
encoding: 'base64',
|
||||
success: function(res) {
|
||||
resolve('data:image/png;base64,' + res.data)
|
||||
},
|
||||
fail: function(error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
setNativeBorderRadius(config){
|
||||
let border_radius = config.border_radius?(parseFloat(config.border_radius)*this.scale):(20*this.scale)
|
||||
if ((parseFloat(config.w)*this.scale) < 2 * border_radius) border_radius = (parseFloat(config.w)*this.scale) / 2;
|
||||
if ((parseFloat(config.h)*this.scale) < 2 * border_radius) border_radius = (parseFloat(config.h)*this.scale) / 2;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(this.compatibilitySize((parseFloat(config.x)*this.scale) + border_radius), this.compatibilitySize((parseFloat(config.y)*this.scale)));
|
||||
this.ctx.arcTo(this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize(border_radius));
|
||||
this.ctx.arcTo(this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize(border_radius));
|
||||
this.ctx.arcTo((this.compatibilitySize(parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize(border_radius));
|
||||
this.ctx.arcTo(this.compatibilitySize((parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize(border_radius));
|
||||
this.ctx.closePath();
|
||||
this.ctx.strokeStyle = config.color || config.border_color || 'rgba(0,0,0,0)'; // 设置绘制边框的颜色
|
||||
this.ctx.stroke();
|
||||
this.ctx.save()
|
||||
this.ctx.clip();
|
||||
|
||||
},
|
||||
/**
|
||||
* Download network file
|
||||
* @param {Object} url : download url
|
||||
*/
|
||||
downLoadNetworkFile(url){
|
||||
return new Promise((resolve,reject)=>{
|
||||
uni.downloadFile({
|
||||
url,
|
||||
success:(res)=>{
|
||||
if(res.statusCode == 200){
|
||||
resolve(res.tempFilePath)
|
||||
}else{
|
||||
reject("Download Image Fail:102")
|
||||
}
|
||||
},
|
||||
fail:(err)=>{
|
||||
reject("Download Image Fail:101")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Save image to natice
|
||||
* @param {Object} filePath : native imageUrl
|
||||
*/
|
||||
saveImage(filePath){
|
||||
return new Promise((resolve,reject)=>{
|
||||
if(!filePath){
|
||||
reject("FilePath cannot be null:101")
|
||||
return;
|
||||
}
|
||||
|
||||
// #ifdef H5
|
||||
var createA = document.createElement("a");
|
||||
createA.download = filePath;
|
||||
createA.href = filePath;
|
||||
document.body.appendChild(createA);
|
||||
createA.click();
|
||||
createA.remove();
|
||||
resolve()
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: filePath,
|
||||
success:(res)=>{
|
||||
resolve(res)
|
||||
},
|
||||
fail:(err)=>{
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="r-canvas-component" :style="{width:canvas_width/scale+'px',height:canvas_height/scale+'px'}" :class="{'hidden':hidden}">
|
||||
<canvas class="r-canvas" v-if="canvas_id" :canvas-id="canvas_id" :id="canvas_id" :style="{width:canvas_width+'px',height:canvas_height+'px','transform': `scale(${r_canvas_scale})`}"></canvas>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import rCanvasJS from "./r-canvas.js"
|
||||
export default {
|
||||
mixins:[rCanvasJS]
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.r-canvas{
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
.r-canvas-component{
|
||||
overflow: hidden;
|
||||
}
|
||||
.r-canvas-component.hidden{
|
||||
position: fixed;
|
||||
top:-5000upx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,784 +0,0 @@
|
||||
<template>
|
||||
<view v-show="show" class="t-wrapper" @touchmove.stop.prevent="moveHandle">
|
||||
<view class="t-mask" :class="{active:active}" @click.stop="close"></view>
|
||||
<view class="t-box" :class="{active:active}">
|
||||
<view class="t-header">
|
||||
<view class="t-header-button" @click="close">取消</view>
|
||||
<view class="t-header-button" @click="confirm">确认</view>
|
||||
</view>
|
||||
<view class="t-color__box" :style="{ background: 'rgb(' + bgcolor.r + ',' + bgcolor.g + ',' + bgcolor.b + ')'}">
|
||||
<view class="t-background boxs" @touchstart="touchstart($event, 0)" @touchmove="touchmove($event, 0)" @touchend="touchend($event, 0)">
|
||||
<view class="t-color-mask"></view>
|
||||
<view class="t-pointer" :style="{ top: site[0].top - 8 + 'px', left: site[0].left - 8 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-control__box">
|
||||
<view class="t-control__color">
|
||||
<view class="t-control__color-content" :style="{ background: 'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + rgba.a + ')' }"></view>
|
||||
</view>
|
||||
<view class="t-control-box__item">
|
||||
<view class="t-controller boxs" @touchstart="touchstart($event, 1)" @touchmove="touchmove($event, 1)" @touchend="touchend($event, 1)">
|
||||
<view class="t-hue">
|
||||
<view class="t-circle" :style="{ left: site[1].left - 12 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-controller boxs" @touchstart="touchstart($event, 2)" @touchmove="touchmove($event, 2)" @touchend="touchend($event, 2)">
|
||||
<view class="t-transparency">
|
||||
<view class="t-circle" :style="{ left: site[2].left - 12 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-result__box">
|
||||
<view v-if="mode" class="t-result__item">
|
||||
<view class="t-result__box-input">{{hex}}</view>
|
||||
<view class="t-result__box-text">HEX</view>
|
||||
</view>
|
||||
<template v-else>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{rgba.r}}</view>
|
||||
<view class="t-result__box-text">R</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{rgba.g}}</view>
|
||||
<view class="t-result__box-text">G</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{rgba.b}}</view>
|
||||
<view class="t-result__box-text">B</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{rgba.a}}</view>
|
||||
<view class="t-result__box-text">A</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view class="t-result__item t-select" @click="select">
|
||||
<view class="t-result__box-input">
|
||||
<view>切换</view>
|
||||
<view>模式</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-alternative">
|
||||
<view class="t-alternative__item" v-for="(item,index) in colorList" :key="index">
|
||||
<view class="t-alternative__item-content" :style="{ background: 'rgba(' + item.r + ',' + item.g + ',' + item.b + ',' + item.a + ')' }"
|
||||
@click="selectColor(item)">
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
spareColor: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
active: false,
|
||||
// rgba 颜色
|
||||
rgba: {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
},
|
||||
// hsb 颜色
|
||||
hsb: {
|
||||
h: 0,
|
||||
s: 0,
|
||||
b: 0
|
||||
},
|
||||
site: [{
|
||||
top: 0,
|
||||
left: 0
|
||||
}, {
|
||||
left: 0
|
||||
}, {
|
||||
left: 0
|
||||
}],
|
||||
index: 0,
|
||||
bgcolor: {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
},
|
||||
hex: '#000000',
|
||||
mode: true,
|
||||
colorList: [{
|
||||
r: 244,
|
||||
g: 67,
|
||||
b: 54,
|
||||
a: 1
|
||||
}, {
|
||||
r: 233,
|
||||
g: 30,
|
||||
b: 99,
|
||||
a: 1
|
||||
}, {
|
||||
r: 156,
|
||||
g: 39,
|
||||
b: 176,
|
||||
a: 1
|
||||
}, {
|
||||
r: 103,
|
||||
g: 58,
|
||||
b: 183,
|
||||
a: 1
|
||||
}, {
|
||||
r: 63,
|
||||
g: 81,
|
||||
b: 181,
|
||||
a: 1
|
||||
}, {
|
||||
r: 33,
|
||||
g: 150,
|
||||
b: 243,
|
||||
a: 1
|
||||
}, {
|
||||
r: 3,
|
||||
g: 169,
|
||||
b: 244,
|
||||
a: 1
|
||||
}, {
|
||||
r: 0,
|
||||
g: 188,
|
||||
b: 212,
|
||||
a: 1
|
||||
}, {
|
||||
r: 0,
|
||||
g: 150,
|
||||
b: 136,
|
||||
a: 1
|
||||
}, {
|
||||
r: 76,
|
||||
g: 175,
|
||||
b: 80,
|
||||
a: 1
|
||||
}, {
|
||||
r: 139,
|
||||
g: 195,
|
||||
b: 74,
|
||||
a: 1
|
||||
}, {
|
||||
r: 205,
|
||||
g: 220,
|
||||
b: 57,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 235,
|
||||
b: 59,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 193,
|
||||
b: 7,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 152,
|
||||
b: 0,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 87,
|
||||
b: 34,
|
||||
a: 1
|
||||
}, {
|
||||
r: 121,
|
||||
g: 85,
|
||||
b: 72,
|
||||
a: 1
|
||||
}, {
|
||||
r: 158,
|
||||
g: 158,
|
||||
b: 158,
|
||||
a: 1
|
||||
}, {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0.5
|
||||
}, {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0
|
||||
}, ]
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.rgba = this.color;
|
||||
if (this.spareColor.length !== 0) {
|
||||
this.colorList = this.spareColor;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
init() {
|
||||
// hsb 颜色
|
||||
this.hsb = this.rgbToHex(this.rgba);
|
||||
// this.setColor();
|
||||
this.setValue(this.rgba);
|
||||
},
|
||||
moveHandle() {},
|
||||
open() {
|
||||
this.show = true;
|
||||
this.$nextTick(() => {
|
||||
this.init();
|
||||
setTimeout(() => {
|
||||
this.active = true;
|
||||
setTimeout(() => {
|
||||
this.getSelectorQuery();
|
||||
}, 350)
|
||||
}, 50)
|
||||
})
|
||||
|
||||
},
|
||||
close() {
|
||||
this.active = false;
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.show = false;
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
confirm() {
|
||||
this.close();
|
||||
this.$emit('confirm', {
|
||||
rgba: this.rgba,
|
||||
hex: this.hex
|
||||
})
|
||||
},
|
||||
// 选择模式
|
||||
select() {
|
||||
this.mode = !this.mode
|
||||
},
|
||||
// 常用颜色选择
|
||||
selectColor(item) {
|
||||
this.setColorBySelect(item)
|
||||
},
|
||||
touchstart(e, index) {
|
||||
const {
|
||||
pageX,
|
||||
pageY
|
||||
} = e.touches[0];
|
||||
this.pageX = pageX;
|
||||
this.pageY = pageY;
|
||||
this.setPosition(pageX, pageY, index);
|
||||
},
|
||||
touchmove(e, index) {
|
||||
const {
|
||||
pageX,
|
||||
pageY
|
||||
} = e.touches[0];
|
||||
this.moveX = pageX;
|
||||
this.moveY = pageY;
|
||||
this.setPosition(pageX, pageY, index);
|
||||
},
|
||||
touchend(e, index) {},
|
||||
/**
|
||||
* 设置位置
|
||||
*/
|
||||
setPosition(x, y, index) {
|
||||
this.index = index;
|
||||
const {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height
|
||||
} = this.position[index];
|
||||
// 设置最大最小值
|
||||
|
||||
this.site[index].left = Math.max(0, Math.min(parseInt(x - left), width));
|
||||
if (index === 0) {
|
||||
this.site[index].top = Math.max(0, Math.min(parseInt(y - top), height));
|
||||
// 设置颜色
|
||||
this.hsb.s = parseInt((100 * this.site[index].left) / width);
|
||||
this.hsb.b = parseInt(100 - (100 * this.site[index].top) / height);
|
||||
this.setColor();
|
||||
this.setValue(this.rgba);
|
||||
} else {
|
||||
this.setControl(index, this.site[index].left);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 设置 rgb 颜色
|
||||
*/
|
||||
setColor() {
|
||||
const rgb = this.HSBToRGB(this.hsb);
|
||||
this.rgba.r = rgb.r;
|
||||
this.rgba.g = rgb.g;
|
||||
this.rgba.b = rgb.b;
|
||||
},
|
||||
/**
|
||||
* 设置二进制颜色
|
||||
* @param {Object} rgb
|
||||
*/
|
||||
setValue(rgb) {
|
||||
this.hex = '#' + this.rgbToHex(rgb);
|
||||
},
|
||||
setControl(index, x) {
|
||||
const {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height
|
||||
} = this.position[index];
|
||||
|
||||
if (index === 1) {
|
||||
this.hsb.h = parseInt((360 * x) / width);
|
||||
this.bgcolor = this.HSBToRGB({
|
||||
h: this.hsb.h,
|
||||
s: 100,
|
||||
b: 100
|
||||
});
|
||||
this.setColor()
|
||||
} else {
|
||||
this.rgba.a = (x / width).toFixed(1);
|
||||
}
|
||||
this.setValue(this.rgba);
|
||||
},
|
||||
/**
|
||||
* rgb 转 二进制 hex
|
||||
* @param {Object} rgb
|
||||
*/
|
||||
rgbToHex(rgb) {
|
||||
let hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)];
|
||||
hex.map(function(str, i) {
|
||||
if (str.length == 1) {
|
||||
hex[i] = '0' + str;
|
||||
}
|
||||
});
|
||||
return hex.join('');
|
||||
},
|
||||
setColorBySelect(getrgb) {
|
||||
const {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
a
|
||||
} = getrgb;
|
||||
let rgb = {}
|
||||
rgb = {
|
||||
r: r ? parseInt(r) : 0,
|
||||
g: g ? parseInt(g) : 0,
|
||||
b: b ? parseInt(b) : 0,
|
||||
a: a ? a : 0,
|
||||
};
|
||||
this.rgba = rgb;
|
||||
this.hsb = this.rgbToHsb(rgb);
|
||||
this.changeViewByHsb();
|
||||
},
|
||||
changeViewByHsb() {
|
||||
const [a, b, c] = this.position;
|
||||
this.site[0].left = parseInt(this.hsb.s * a.width / 100);
|
||||
this.site[0].top = parseInt((100 - this.hsb.b) * a.height / 100);
|
||||
this.setColor(this.hsb.h);
|
||||
this.setValue(this.rgba);
|
||||
this.bgcolor = this.HSBToRGB({
|
||||
h: this.hsb.h,
|
||||
s: 100,
|
||||
b: 100
|
||||
});
|
||||
|
||||
this.site[1].left = this.hsb.h / 360 * b.width;
|
||||
this.site[2].left = this.rgba.a * c.width;
|
||||
|
||||
},
|
||||
/**
|
||||
* hsb 转 rgb
|
||||
* @param {Object} 颜色模式 H(hues)表示色相,S(saturation)表示饱和度,B(brightness)表示亮度
|
||||
*/
|
||||
HSBToRGB(hsb) {
|
||||
let rgb = {};
|
||||
let h = Math.round(hsb.h);
|
||||
let s = Math.round((hsb.s * 255) / 100);
|
||||
let v = Math.round((hsb.b * 255) / 100);
|
||||
if (s == 0) {
|
||||
rgb.r = rgb.g = rgb.b = v;
|
||||
} else {
|
||||
let t1 = v;
|
||||
let t2 = ((255 - s) * v) / 255;
|
||||
let t3 = ((t1 - t2) * (h % 60)) / 60;
|
||||
if (h == 360) h = 0;
|
||||
if (h < 60) {
|
||||
rgb.r = t1;
|
||||
rgb.b = t2;
|
||||
rgb.g = t2 + t3;
|
||||
} else if (h < 120) {
|
||||
rgb.g = t1;
|
||||
rgb.b = t2;
|
||||
rgb.r = t1 - t3;
|
||||
} else if (h < 180) {
|
||||
rgb.g = t1;
|
||||
rgb.r = t2;
|
||||
rgb.b = t2 + t3;
|
||||
} else if (h < 240) {
|
||||
rgb.b = t1;
|
||||
rgb.r = t2;
|
||||
rgb.g = t1 - t3;
|
||||
} else if (h < 300) {
|
||||
rgb.b = t1;
|
||||
rgb.g = t2;
|
||||
rgb.r = t2 + t3;
|
||||
} else if (h < 360) {
|
||||
rgb.r = t1;
|
||||
rgb.g = t2;
|
||||
rgb.b = t1 - t3;
|
||||
} else {
|
||||
rgb.r = 0;
|
||||
rgb.g = 0;
|
||||
rgb.b = 0;
|
||||
}
|
||||
}
|
||||
return {
|
||||
r: Math.round(rgb.r),
|
||||
g: Math.round(rgb.g),
|
||||
b: Math.round(rgb.b)
|
||||
};
|
||||
},
|
||||
rgbToHsb(rgb) {
|
||||
let hsb = {
|
||||
h: 0,
|
||||
s: 0,
|
||||
b: 0
|
||||
};
|
||||
let min = Math.min(rgb.r, rgb.g, rgb.b);
|
||||
let max = Math.max(rgb.r, rgb.g, rgb.b);
|
||||
let delta = max - min;
|
||||
hsb.b = max;
|
||||
hsb.s = max != 0 ? 255 * delta / max : 0;
|
||||
if (hsb.s != 0) {
|
||||
if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta;
|
||||
else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta;
|
||||
else hsb.h = 4 + (rgb.r - rgb.g) / delta;
|
||||
} else hsb.h = -1;
|
||||
hsb.h *= 60;
|
||||
if (hsb.h < 0) hsb.h = 0;
|
||||
hsb.s *= 100 / 255;
|
||||
hsb.b *= 100 / 255;
|
||||
return hsb;
|
||||
},
|
||||
getSelectorQuery() {
|
||||
const views = uni.createSelectorQuery().in(this);
|
||||
views
|
||||
.selectAll('.boxs')
|
||||
.boundingClientRect(data => {
|
||||
if (!data || data.length === 0) {
|
||||
setTimeout(() => this.getSelectorQuery(), 20)
|
||||
return
|
||||
}
|
||||
this.position = data;
|
||||
// this.site[0].top = data[0].height;
|
||||
// this.site[0].left = 0;
|
||||
// this.site[1].left = data[1].width;
|
||||
// this.site[2].left = data[2].width;
|
||||
this.setColorBySelect(this.rgba);
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
spareColor(newVal) {
|
||||
this.colorList = newVal;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.t-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.t-box {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding: 30upx 0;
|
||||
padding-top: 0;
|
||||
background: #fff;
|
||||
transition: all 0.3s;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.t-box.active {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
.t-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 100upx;
|
||||
border-bottom: 1px #eee solid;
|
||||
box-shadow: 1px 0 2px rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-header-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 150upx;
|
||||
height: 100upx;
|
||||
font-size: 30upx;
|
||||
color: #666;
|
||||
padding-left: 20upx;
|
||||
}
|
||||
|
||||
.t-header-button:last-child {
|
||||
justify-content: flex-end;
|
||||
padding-right: 20upx;
|
||||
}
|
||||
|
||||
.t-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
z-index: -1;
|
||||
transition: all 0.3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.t-mask.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.t-color__box {
|
||||
position: relative;
|
||||
height: 400upx;
|
||||
background: rgb(255, 0, 0);
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
margin: 0 20upx;
|
||||
margin-top: 20upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
|
||||
}
|
||||
|
||||
.t-color-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 400upx;
|
||||
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
.t-pointer {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: -8px;
|
||||
z-index: 2;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: 1px #fff solid;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.t-show-color {
|
||||
width: 100upx;
|
||||
height: 50upx;
|
||||
}
|
||||
|
||||
.t-control__box {
|
||||
margin-top: 50upx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
padding-left: 20upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-control__color {
|
||||
flex-shrink: 0;
|
||||
width: 100upx;
|
||||
height: 100upx;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 36upx 36upx;
|
||||
background-position: 0 0, 18upx 18upx;
|
||||
border: 1px #eee solid;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.t-control__color-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.t-control-box__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 0 30upx;
|
||||
}
|
||||
|
||||
.t-controller {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 32upx 32upx;
|
||||
background-position: 0 0, 16upx 16upx;
|
||||
}
|
||||
|
||||
.t-hue {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
|
||||
}
|
||||
|
||||
.t-transparency {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0));
|
||||
}
|
||||
|
||||
.t-circle {
|
||||
position: absolute;
|
||||
/* right: -10px; */
|
||||
top: -2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.t-result__box {
|
||||
margin-top: 20upx;
|
||||
padding: 10upx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-result__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10upx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-result__box-input {
|
||||
padding: 10upx 0;
|
||||
width: 100%;
|
||||
font-size: 28upx;
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
color: #999;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-result__box-text {
|
||||
margin-top: 10upx;
|
||||
font-size: 28upx;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.t-select {
|
||||
flex-shrink: 0;
|
||||
width: 150upx;
|
||||
padding: 0 30upx;
|
||||
}
|
||||
|
||||
.t-select .t-result__box-input {
|
||||
border-radius: 10upx;
|
||||
border: none;
|
||||
color: #999;
|
||||
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-select .t-result__box-input:active {
|
||||
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.t-alternative {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
/* justify-content: space-between; */
|
||||
width: 100%;
|
||||
padding-right: 10upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-alternative__item {
|
||||
margin-left: 12upx;
|
||||
margin-top: 10upx;
|
||||
width: 50upx;
|
||||
height: 50upx;
|
||||
border-radius: 10upx;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 36upx 36upx;
|
||||
background-position: 0 0, 18upx 18upx;
|
||||
border: 1px #eee solid;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.t-alternative__item-content {
|
||||
width: 50upx;
|
||||
height: 50upx;
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.t-alternative__item:active {
|
||||
transition: all 0.3s;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
||||
@@ -1,89 +0,0 @@
|
||||
<template>
|
||||
<view class="wave-wrap waveAnimation">
|
||||
<view class="waveWrapperInner bgTop"><view class="wave waveTop"></view></view>
|
||||
<view class="waveWrapperInner bgMiddle"><view class="wave waveMiddle"></view></view>
|
||||
<view class="waveWrapperInner bgBottom"><view class="wave waveBottom"></view></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'wave',
|
||||
props: {
|
||||
height: {
|
||||
type: String,
|
||||
default: '35rpx'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wave-wrap {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
margin: auto;
|
||||
}
|
||||
.waveWrapperInner {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
.wave {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 200%;
|
||||
height: 100%;
|
||||
background-repeat: repeat no-repeat;
|
||||
background-position: 0 bottom;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
.bgTop {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.waveTop {
|
||||
background-size: 50% 45px;
|
||||
background-image: url('~@/static/wave/wave-1.png');
|
||||
}
|
||||
.waveAnimation .waveTop {
|
||||
animation: move_wave 4s linear infinite;
|
||||
}
|
||||
@keyframes move_wave {
|
||||
0% {
|
||||
transform: translateX(0) translateZ(0) scaleY(1);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-25%) translateZ(0) scaleY(1);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%) translateZ(0) scaleY(1);
|
||||
}
|
||||
}
|
||||
.bgMiddle {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.waveMiddle {
|
||||
background-size: 50% 40px;
|
||||
background-image: url('~@/static/wave/wave-2.png');
|
||||
}
|
||||
.waveAnimation .waveMiddle {
|
||||
animation: move_wave 3.5s linear infinite;
|
||||
}
|
||||
|
||||
.bgBottom {
|
||||
opacity: 0.95;
|
||||
}
|
||||
.waveBottom {
|
||||
background-size: 50% 35px;
|
||||
background-image: url('~@/static/wave/wave-1.png');
|
||||
}
|
||||
.waveAnimation .waveBottom {
|
||||
animation: move_wave 2s linear infinite;
|
||||
}
|
||||
</style>
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* 广告配置
|
||||
*/
|
||||
export default {
|
||||
adpid: '', // uni-AD App广告位id,在uni-AD官网申请广告位
|
||||
unitId: '', // 广告单元id,可在小程序管理后台的流量主模块新建 (非个人资质,小程序后台广告主开通申请)
|
||||
frequency: 8, // 列表中,广告出现的频率(8=每8条数据出现一次广告)
|
||||
// 首页广告
|
||||
home: {
|
||||
use: false,
|
||||
},
|
||||
// 文章列表广告
|
||||
articles: {
|
||||
use: false,
|
||||
},
|
||||
// 文章详情广告
|
||||
articleDetail: {
|
||||
use: false, // 是否启用
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/**
|
||||
* 功能:基础配置
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年08月23日 15:19:14
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
export default {
|
||||
showCopyright: true, // 显示开源版权信息
|
||||
showAbout: true, // 显示关于项目入口
|
||||
uni_halo_logo: 'https://b.925i.cn/uni_halo/uni_halo_logo.png', // uni-halo的logo
|
||||
|
||||
apiUrl: '', // Api基础域名 [必填] :你的Halo博客基础域名
|
||||
apiAuthorization: '', // Api认证key [必填]: Halo中-系统-博客设置-切换到高级选项-API设置-Access key
|
||||
|
||||
title: '', // 博客标题 [建议必填]:在某些页面没有设置具体的页面名称时候,使用该值显示
|
||||
miniCodeImageUrl: '', // 小程序的太阳码/二维码的图片地址 [建议必填]:主要用于文章详情页面的生成海报的功能
|
||||
aboutProfileImageUrl: '', // 关于页面中的资料卡背景图 [建议必填]
|
||||
|
||||
// 启动页面的配置(页面地址`/pagesA/start/start`)
|
||||
start: {
|
||||
use: true, // 是否使用首次启动页:用户第一次使用你的应用会显示否则不显示
|
||||
title: 'uni-halo', // 启动页面中的文字标题
|
||||
bg: '', // 留空则使用默认 开屏首页背景,可以是颜色值或者图片图片地址
|
||||
logo: 'https://b.925i.cn/uni_halo/uni_halo_logo.png', // logo
|
||||
desc1: '全新UI,准备出发', // 描述信息1
|
||||
desc2: '新触动 新感受 新体验', // 描述信息2
|
||||
btnText: '全新触发' // 按钮文字
|
||||
},
|
||||
|
||||
// 博主信息
|
||||
author: {
|
||||
use: true, // 是否启用这里配置的信息,不启用则获取PC端博客设置的信息
|
||||
nickname: '', // 昵称
|
||||
avatar: '', // 头像
|
||||
email: '', // 邮箱
|
||||
description: '', // 介绍
|
||||
},
|
||||
|
||||
// 版权信息(用于文章详情)
|
||||
copyright: {
|
||||
use: true, // 是否使用版权描述
|
||||
author: '', // 版权归属说明
|
||||
description: '', // 版权描述
|
||||
violation: '', // 侵权处理说明
|
||||
},
|
||||
|
||||
// 社交信息(将会在`/pagesA/contact/contact`页面中展示)
|
||||
// 具体的某个参数值留空则不展示
|
||||
social: {
|
||||
qq: "", // qq号
|
||||
wechat: "", // 微信号
|
||||
weibo: "", // 微博地址
|
||||
email: "", // 邮箱地址
|
||||
blog: "", // 博客地址
|
||||
juejin: "", // 掘金地址
|
||||
bilibili: "", // b站地址
|
||||
gitee: "", // gitee地址
|
||||
github: "", // github地址
|
||||
csdn: "" // CSDN地址
|
||||
},
|
||||
|
||||
defaultThumbnailUrl: '', // 默认封面图地址
|
||||
defaultImageUrl: '', // 默认图片地址
|
||||
defaultAvatarUrl: '', // 默认头像地址
|
||||
|
||||
loadingGifUrl: '', // 图片加载中的地址
|
||||
loadingErrUrl: '', // 图片加载失败的地址
|
||||
loadingEmptyUrl: '', // 加载图片为空地址
|
||||
|
||||
waveImageUrl: '', // 关于页面波浪图片
|
||||
|
||||
banner: { // 轮播图配置
|
||||
type: 'article', // 轮播图数据源 list=下方配置 article=热门文章封面
|
||||
list: [],
|
||||
},
|
||||
|
||||
quickNav: { // 快捷导航配置
|
||||
use: true,
|
||||
list: [{
|
||||
icon: 'halocoloricon-classify',
|
||||
text: '文章归档',
|
||||
iconSize: 60,
|
||||
color: 'blue',
|
||||
type: 'page',
|
||||
path: '/pagesA/archives/archives'
|
||||
},
|
||||
{
|
||||
icon: 'halocoloricon-attent',
|
||||
text: '恋爱日记',
|
||||
iconSize: 60,
|
||||
color: 'blue',
|
||||
type: 'page',
|
||||
path: '/pagesA/love/love'
|
||||
},
|
||||
{
|
||||
icon: 'halocoloricon-calendar',
|
||||
text: '个人日记',
|
||||
iconSize: 60,
|
||||
color: 'blue',
|
||||
type: 'page',
|
||||
path: '/pagesA/journal/journal'
|
||||
},
|
||||
{
|
||||
icon: 'halocoloricon-message',
|
||||
text: '留言板',
|
||||
iconSize: 60,
|
||||
color: 'blue',
|
||||
type: 'page',
|
||||
path: '/pagesA/leaving/leaving'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// 微信分享信息
|
||||
wxShareConfig: {
|
||||
title: '', // 小程序分享标题[非必填]
|
||||
desc: '', // 小程序分享描述[非必填]
|
||||
imageUrl: '', // 小程序分享时候图片地址[非必填]
|
||||
path: '/pages/start/start', // 分享路径[非必填] - 基本不需要修改
|
||||
copyLink: '/pages/start/start', // 复制链接[非必填] - 基本不需要修改
|
||||
query: {}, // 分享参数[非必填] - 基本不需要填写
|
||||
},
|
||||
|
||||
colors: [
|
||||
'#39B449',
|
||||
'#E44C41',
|
||||
'#8698A2',
|
||||
'#0080FE',
|
||||
'#1CBCB4',
|
||||
'#6638B5',
|
||||
]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* 配置key
|
||||
*/
|
||||
|
||||
export default {
|
||||
SHEET_LEAVING: 'leaving', // 留言板
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* 页面配置
|
||||
*/
|
||||
import AppKeys from './keys.js'
|
||||
|
||||
export default {
|
||||
[AppKeys.SHEET_LEAVING]: 65, // 留言板页面ID
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
VITE_APP_TITLE='uni-halo'
|
||||
VITE_APP_DESC='基于halo开源博客系统API开发的多端博客。'
|
||||
VITE_APP_PORT=9000
|
||||
|
||||
VITE_UNI_APPID='__UNI__5748B6E'
|
||||
VITE_WX_APPID='wx1bb161867082c72f'
|
||||
|
||||
# 微信开发者工具 CLI 路径,仅当默认安装路径不正确时配置(就是当 pnpm dev:mp 无法自动打开微信开发者工具时,才需要配置,通常是你更改了默认的安装位置导致的,一般出现在windows系统)
|
||||
# macOS 示例:
|
||||
# WECHAT_DEVTOOLS_CLI_PATH = '/Applications/wechatwebdevtools.app/Contents/MacOS/cli'
|
||||
# Windows 示例:
|
||||
# WECHAT_DEVTOOLS_CLI_PATH = 'C:\Program Files (x86)\Tencent\微信web开发者工具\cli.bat'
|
||||
|
||||
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
|
||||
# https://uniapp.dcloud.net.cn/collocation/manifest.html#h5-router
|
||||
# 比如你要部署到 https://unibest.tech/doc/ ,则配置为 /doc/
|
||||
VITE_APP_PUBLIC_BASE=/
|
||||
|
||||
# 默认后台请求地址
|
||||
# 不同命令会按 mode 叠加读取 .env.development / .env.test / .env.production。
|
||||
# 微信小程序如果没有配置下面的专用地址,也会回退使用这个值。
|
||||
VITE_UNI_HALO_BASEURL=''
|
||||
# 备注:如果后台带统一前缀,则也要加到后面,eg: https://blog.xiaoxiaomo.cn/api
|
||||
|
||||
# 微信小程序专用后台请求地址,按微信开发者工具 envVersion 区分。
|
||||
# 不配置时会回退使用 VITE_UNI_HALO_BASEURL。
|
||||
# VITE_UNI_HALO_BASEURL__WEIXIN_DEVELOP = 'https://dev.xxx.com'
|
||||
# VITE_UNI_HALO_BASEURL__WEIXIN_TRIAL = 'https://trial.xxx.com'
|
||||
# VITE_UNI_HALO_BASEURL__WEIXIN_RELEASE = 'https://prod.xxx.com'
|
||||
|
||||
# h5是否需要配置代理
|
||||
VITE_APP_PROXY_ENABLE=false
|
||||
# 下面的不用修改,只要不跟你后台的统一前缀冲突就行。如果修改了,记得修改 `nginx` 里面的配置
|
||||
VITE_APP_PROXY_PREFIX='/dev-api'
|
||||
|
||||
# 第二个请求地址 (目前alova中可以使用)
|
||||
VITE_UNI_HALO_BASEURL_SECONDARY='https://blog.xiaoxiaomo.cn'
|
||||
|
||||
# 认证模式,'single' | 'double' ==> 单token | 双token
|
||||
VITE_AUTH_MODE='single'
|
||||
|
||||
# 原生插件资源复制开关,启用后 App 构建会把根目录 nativeplugins 复制到 dist
|
||||
VITE_COPY_NATIVE_RES_ENABLE=true
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV='development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE=false
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP=false
|
||||
|
||||
# development mode 后台请求地址
|
||||
# VITE_UNI_HALO_BASEURL='https://www.yijunzhao.cn'
|
||||
# VITE_UNI_HALO_BASEURL='https://www.xhhao.com'
|
||||
VITE_UNI_HALO_BASEURL='https://ryanc.cc'
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV='production'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE=true
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP=false
|
||||
|
||||
# production mode 后台请求地址
|
||||
# VITE_UNI_HALO_BASEURL = 'https://prod.xxx.com'
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV='development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE=false
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP=false
|
||||
|
||||
# test mode 后台请求地址
|
||||
# VITE_UNI_HALO_BASEURL = 'https://test.xxx.com'
|
||||
@@ -0,0 +1,60 @@
|
||||
import uniHelper from '@uni-helper/eslint-config'
|
||||
|
||||
export default uniHelper({
|
||||
unocss: true,
|
||||
vue: true,
|
||||
markdown: false,
|
||||
ignores: [
|
||||
// 忽略uni_modules目录
|
||||
'**/uni_modules/',
|
||||
// 忽略原生插件目录
|
||||
'**/nativeplugins/',
|
||||
'dist',
|
||||
// unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
|
||||
'auto-import.d.ts',
|
||||
// vite-plugin-uni-pages 生成的类型文件,每次切换分支都一堆不同的,所以直接 .gitignore
|
||||
'uni-pages.d.ts',
|
||||
// 插件生成的文件
|
||||
'src/pages.json',
|
||||
'src/manifest.json',
|
||||
// 忽略自动生成文件
|
||||
'src/service/**',
|
||||
],
|
||||
// https://eslint-config.antfu.me/rules
|
||||
rules: {
|
||||
'no-useless-return': 'off',
|
||||
'no-console': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'vue/no-unused-refs': 'off',
|
||||
'unused-imports/no-unused-vars': 'off',
|
||||
'eslint-comments/no-unlimited-disable': 'off',
|
||||
'jsdoc/check-param-names': 'off',
|
||||
'jsdoc/require-returns-description': 'off',
|
||||
'ts/no-empty-object-type': 'off',
|
||||
'no-extend-native': 'off',
|
||||
// uni 条件编译注释可能包裹 import,自动排序会破坏平台条件边界
|
||||
'perfectionist/sort-imports': 'off',
|
||||
'vue/singleline-html-element-content-newline': [
|
||||
'error',
|
||||
{
|
||||
externalIgnores: ['text'],
|
||||
},
|
||||
],
|
||||
// vue SFC 调换顺序改这里
|
||||
'vue/block-order': ['error', {
|
||||
order: [['script', 'template'], 'style'],
|
||||
}],
|
||||
},
|
||||
formatters: {
|
||||
/**
|
||||
* Format CSS, LESS, SCSS files, also the `<style>` blocks in Vue
|
||||
* By default uses Prettier
|
||||
*/
|
||||
css: true,
|
||||
/**
|
||||
* Format HTML files
|
||||
* By default uses Prettier
|
||||
*/
|
||||
html: true,
|
||||
},
|
||||
})
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
+13
-7
@@ -1,20 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!doctype html>
|
||||
<html build-time="%BUILD_TIME%">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
var coverSupport =
|
||||
'CSS' in window &&
|
||||
typeof CSS.supports === 'function' &&
|
||||
(CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
(coverSupport ? ', viewport-fit=cover' : '') +
|
||||
'" />',
|
||||
)
|
||||
</script>
|
||||
<title></title>
|
||||
<title>%VITE_APP_TITLE%</title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import FyShowModal from './showModal.js'
|
||||
|
||||
export default class Fy{
|
||||
/**
|
||||
* @author 大雄
|
||||
* @Date 2021年7月1日20:49:58
|
||||
* @description 二次封装showModel
|
||||
*/
|
||||
static showModal({ title = "提示", content = "提示内容", showCancel = true, backbutton = false, cancelText = "取消", cancelColor = "#000000", confirmText = "确定", confirmColor = '#3CC51F', complete = false } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef APP-PLUS
|
||||
if (this.get_app_platform() === 'android') { // android的使用自定义的模态框
|
||||
new FyShowModal({ title, content, showCancel, backbutton, cancelText, cancelColor, confirmText, confirmColor,
|
||||
success(res) {
|
||||
if (res.confirm) {resolve(res) } else { reject(res) }
|
||||
},
|
||||
fail(err){
|
||||
this.uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }).then((res)=>resolve(res)).catch(err=>reject(err))
|
||||
}
|
||||
}).show();
|
||||
} else { // ios直接用原生的
|
||||
return this.uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }).then((res)=>resolve(res)).catch(err=>reject(err))
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
return this.uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }).then((res)=>resolve(res)).catch(err=>reject(err))
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
|
||||
// 原生showModal
|
||||
static uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let appPlatform = null;
|
||||
// #ifdef APP-PLUS
|
||||
if (this.get_app_platform() === 'android' && showCancel) { // android的确认按钮在左边,需要统一到右边
|
||||
appPlatform = 'android';
|
||||
var tempConfirmText = confirmText;
|
||||
var tempConfirmColor = confirmColor;
|
||||
confirmText = cancelText;
|
||||
cancelText = tempConfirmText;
|
||||
confirmColor = cancelColor;
|
||||
cancelColor = tempConfirmColor;
|
||||
}
|
||||
// #endif
|
||||
uni.showModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor,
|
||||
success(res){
|
||||
if (complete) {
|
||||
resolve(res);
|
||||
} else if (res.confirm) {
|
||||
appPlatform === 'android' ? reject(res) : resolve(res)
|
||||
} else {
|
||||
appPlatform === 'android' ? resolve(res) : reject(res)
|
||||
}
|
||||
},
|
||||
fail(err){ reject(err) }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取app平台(android | ios)
|
||||
* */
|
||||
static get_app_platform() {
|
||||
// #ifndef APP-PLUS
|
||||
this.showModal({ content: '仅支持app' });
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
return plus.os.name.toLowerCase();
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* @author 大雄
|
||||
* @Date 2021年7月1日20:49:58
|
||||
* @description 路由导航守卫(简单版,后续需要功能再完善)
|
||||
*/
|
||||
export default function() {
|
||||
// 监听路由前进
|
||||
function routerPush({ type = 'navigateTo' } = {}) {
|
||||
routeWatchClearModal();
|
||||
}
|
||||
// 监听路由后退
|
||||
function routerBack() {
|
||||
routeWatchClearModal();
|
||||
}
|
||||
|
||||
// 页面跳转后,销毁当前页面未关闭的弹框
|
||||
function routeWatchClearModal() {
|
||||
try {
|
||||
var FyShowModalView = plus.nativeObj.View.getViewById("FyShowModalView");
|
||||
if (FyShowModalView) {
|
||||
FyShowModalView.clear();
|
||||
}
|
||||
var FyShowModalCancel = plus.nativeObj.View.getViewById("FyShowModalCancel");
|
||||
if (FyShowModalCancel) {
|
||||
FyShowModalCancel.clear();
|
||||
}
|
||||
var FyShowModalConfirm = plus.nativeObj.View.getViewById("FyShowModalConfirm");
|
||||
if (FyShowModalConfirm) {
|
||||
FyShowModalConfirm.clear();
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
uni.addInterceptor('navigateTo', {
|
||||
success(e) {
|
||||
routerPush({ type: 'navigateTo' });
|
||||
}
|
||||
})
|
||||
uni.addInterceptor('redirectTo', {
|
||||
success(e) {
|
||||
routerPush({ type: 'redirectTo' });
|
||||
}
|
||||
})
|
||||
uni.addInterceptor('reLaunch', {
|
||||
success(e) {
|
||||
routerPush({ type: 'reLaunch' });
|
||||
}
|
||||
})
|
||||
uni.addInterceptor('switchTab', {
|
||||
success(e) {
|
||||
routerPush({ type: 'switchTab' });
|
||||
}
|
||||
})
|
||||
uni.addInterceptor('navigateBack', {
|
||||
success(e) {
|
||||
routerBack();
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/**
|
||||
* @description 替换android app的uni.showModal
|
||||
*/
|
||||
|
||||
let modalIntance = null;
|
||||
export class fyShowModal {
|
||||
constructor(options = {}) {
|
||||
modalIntance = this;
|
||||
|
||||
this.modalControl = null; // 模态框句柄
|
||||
this.cancelModel = null;
|
||||
this.confirmModel = null;
|
||||
|
||||
const { screenHeight, screenWidth } = uni.getSystemInfoSync();
|
||||
|
||||
this.modalPaddingTop = 12; // modal顶部的内边距
|
||||
this.titleHeight = 34; // 标题的高度
|
||||
this.contentHeight = 60; // 内容得高度
|
||||
this.contentPaddingBottom = 10; // 内容的底部内边距
|
||||
this.footerHeight = 50; // 底部按钮的高度
|
||||
|
||||
const modalHeight = this.modalPaddingTop + this.contentPaddingBottom + this.titleHeight + this.contentHeight + this.footerHeight; // 模态框内容高度
|
||||
|
||||
this.screenHeight = screenHeight;
|
||||
this.modalWidth = options.contentWidth || screenWidth * 0.82; // 模态框内容宽度
|
||||
this.modalHeight = modalHeight; // 模态框内容高度
|
||||
this.modalLeft = (screenWidth - this.modalWidth) / 2; // 模态框距离左边距离
|
||||
this.modalTop = (screenHeight / 2) - (modalHeight / 2) - 30; // 模态框距离顶部距离
|
||||
this.titleTop = this.modalPaddingTop + this.modalTop; // title距离顶部的距离
|
||||
this.contentTop = this.modalPaddingTop + this.modalTop + this.titleHeight; // content距离顶部的距离
|
||||
this.contentLeft = this.modalLeft + (this.modalWidth * 0.1);
|
||||
this.contentWidth = this.modalWidth * 0.8; // 内容的宽度
|
||||
this.footerBorderTop = this.contentPaddingBottom + this.contentTop + this.contentHeight; // footer的边线距离顶部的距离
|
||||
this.buttonWidth = this.modalWidth/2;
|
||||
|
||||
// 物理返回键是否关闭弹框
|
||||
this.backbutton = Boolean(options.backbutton);
|
||||
|
||||
let opacity = options.opacity || 0.6; // mask透明度
|
||||
let modal_title = options.title || '提示'; // 标题
|
||||
let model_content = options.content || '提示内容'; // 提示内容
|
||||
let maskClick = typeof options.maskClick === 'undefined' ? false : options.maskClick; // 是否可以点击mask关闭模态框
|
||||
let cancelText = options.cancelText || '取消';
|
||||
let confirmText = options.confirmText || '确定';
|
||||
let cancelColor = options.cancelColor || '#000000';
|
||||
let confirmColor = options.confirmColor || '#3CC51F';
|
||||
let showCancel = typeof options.showCancel === 'undefined' ? true : options.showCancel; // 是否显示取消按钮
|
||||
let align = options.align || 'center'; // 内容对齐方向
|
||||
let successFn = () => {};
|
||||
let failFn = () => {};
|
||||
this.success = options.success || successFn; // 成功返回模态框
|
||||
this.fail = options.fail || failFn; // 失败返回模态框
|
||||
|
||||
//#ifdef APP-PLUS
|
||||
this.creatView({ height: `${this.screenHeight}px`, top: 0 }, opacity, maskClick, { 'title': modal_title, 'content': model_content, cancelText, confirmText, confirmColor, cancelColor, showCancel, align });
|
||||
//#endif
|
||||
}
|
||||
//生成提示框view
|
||||
creatView(style, opa, maskClick, modelInfo) {
|
||||
try {
|
||||
style = { left: '0px', width: '100%', ...style };
|
||||
let view = new plus.nativeObj.View('FyShowModalView', style);
|
||||
view.draw([
|
||||
{ tag: 'rect', id: 'modal', color: `rgba(0,0,0,${opa})`, position: { top: '0px', left: '0px', width: '100%', height: '100%' } },
|
||||
{ tag: 'rect', id: 'content', color: `rgb(255,255,255)`, rectStyles: { borderWidth: '0px', radius: '8px' }, position: { top: this.modalTop+'px', left: this.modalLeft+'px', width: this.modalWidth+'px', height: this.modalHeight + 'px' } },
|
||||
|
||||
{ tag: 'font', id: 'title', text: modelInfo.title, textStyles: { size: '18px', weight: 'bold', color: '#000000' }, position: { top: this.titleTop+'px', left: this.modalLeft+'px', width: this.modalWidth+'px', height: this.titleHeight+'px' } },
|
||||
|
||||
{ tag: 'font', id: 'text', text: modelInfo.content, textStyles: { size: '15px', color: '#666', whiteSpace: 'normal', align: modelInfo.align }, position: { top: this.contentTop+'px', left: this.contentLeft+'px', width: this.contentWidth+'px', height: this.contentHeight+'px' } },
|
||||
|
||||
{ tag: 'rect', id: 'line', color: '#efeff1', position: { top: this.footerBorderTop+'px', left: this.modalLeft+'px', width: this.modalWidth+'px', height: '1px' } },
|
||||
{ tag: 'rect', id: 'line2', color: '#efeff1', position: { top: this.footerBorderTop+'px', left: '50%', width: modelInfo.showCancel ? '1px' : '0px', height: modelInfo.showCancel ? this.footerHeight+'px' : '0px' } }
|
||||
]);
|
||||
|
||||
// 取消按钮
|
||||
if (modelInfo.showCancel) {
|
||||
let viewCancel = new plus.nativeObj.View('FyShowModalCancel', { width: this.buttonWidth+'px', height: this.footerHeight+'px', top: this.footerBorderTop + 'px', left: this.modalLeft+'px' });
|
||||
viewCancel.draw([
|
||||
{ tag: 'rect', id: 'cancelBackground', color: `rgba(255,255,255,0)`, rectStyles: { borderWidth: '0px', radius: '8px' }, position: { top: '0px', left: '0px', width: '100%', height: '100%' } },
|
||||
{ tag: 'font', id: 'cancel', text: modelInfo.cancelText, textStyles: { color: modelInfo.cancelColor, size: '16px' } },
|
||||
]);
|
||||
viewCancel.addEventListener('click', (e) => {
|
||||
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||
viewCancel.drawRect('#efeff1', {top:'0px',left:'0px',width:'100%',height:'100%'}, 'cancelBackground');
|
||||
this.success({ confirm: false, cancel: true, mask: false })
|
||||
this.hide();
|
||||
}, false);
|
||||
viewCancel.addEventListener('touchstart', (e)=>{
|
||||
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||
viewCancel.drawRect({ color: '#efeff1', borderWidth: '0px', radius: '8px' }, {top:'0px',left:'0px',width:'100%',height:'100%'}, 'cancelBackground');
|
||||
})
|
||||
this.cancelModel = viewCancel;
|
||||
}
|
||||
|
||||
// 确认
|
||||
let viewconfirm = new plus.nativeObj.View('FyShowModalConfirm', { width: modelInfo.showCancel ? this.buttonWidth+'px' : this.modalWidth+'px', height: this.footerHeight+'px', top: this.footerBorderTop + 'px', left: modelInfo.showCancel ? '50%' : this.modalLeft+'px' });
|
||||
// 绘制确认
|
||||
viewconfirm.draw([
|
||||
{ tag: 'rect', id: 'confirmBackground', color: `rgba(255,255,255,0)`, rectStyles: { borderWidth: '0px', radius: '8px' }, position: { top: '0px', left: '0px', width: '100%', height: '100%' } },
|
||||
{ tag: 'font', id: 'confirm', text: modelInfo.confirmText, textStyles: { color: modelInfo.confirmColor, size: '16px' } },
|
||||
]);
|
||||
// 点击确认
|
||||
viewconfirm.addEventListener('click', (e) => {
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||
}
|
||||
viewconfirm.drawRect('#efeff1', {top:'0px',left:'0px',width:'100%',height:'100%'}, 'confirmBackground');
|
||||
this.success({ confirm: true, cancel: false, mask: false })
|
||||
this.hide();
|
||||
}, false);
|
||||
viewconfirm.addEventListener('touchstart', (e)=>{
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||
}
|
||||
viewconfirm.drawRect({ color: '#efeff1', borderWidth: '0px', radius: '8px' }, {top:'0px',left:'0px',width:'100%',height:'100%'}, 'confirmBackground');
|
||||
})
|
||||
|
||||
//点击蒙布
|
||||
if (maskClick) {
|
||||
view.addEventListener('click', (e) => {
|
||||
this.success({ confirm: false, cancel: true, mask: true })
|
||||
this.hide();
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||
}
|
||||
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||
}, false);
|
||||
} else {
|
||||
view.addEventListener('click', (e) => {
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||
}
|
||||
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||
}, false);
|
||||
}
|
||||
this.modalControl = view;
|
||||
this.confirmModel = viewconfirm;
|
||||
} catch(err) {
|
||||
this.fail(err);
|
||||
}
|
||||
}
|
||||
// 显示模态框
|
||||
show() {
|
||||
this.modalControl.show();
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.show();
|
||||
}
|
||||
this.confirmModel.show();
|
||||
|
||||
if (this.backbutton) {
|
||||
plus.key.addEventListener('backbutton', this.handlerBackButton);
|
||||
}
|
||||
}
|
||||
// 关闭模态框
|
||||
hide() {
|
||||
if (this.backbutton) {
|
||||
plus.key.removeEventListener('backbutton', this.handlerBackButton);
|
||||
}
|
||||
this.modalControl.clear();
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.clear();
|
||||
}
|
||||
this.confirmModel.clear();
|
||||
}
|
||||
// 物理返回键方法
|
||||
handlerBackButton() {
|
||||
try {
|
||||
modalIntance && modalIntance.success({ confirm: false, cancel: true, mask: false })
|
||||
modalIntance && modalIntance.hide();
|
||||
} catch(err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default fyShowModal;
|
||||
@@ -1,99 +0,0 @@
|
||||
import buildURL from '../helpers/buildURL'
|
||||
import buildFullPath from '../core/buildFullPath'
|
||||
import settle from '../core/settle'
|
||||
import { isUndefined } from "../utils"
|
||||
|
||||
/**
|
||||
* 返回可选值存在的配置
|
||||
* @param {Array} keys - 可选值数组
|
||||
* @param {Object} config2 - 配置
|
||||
* @return {{}} - 存在的配置项
|
||||
*/
|
||||
const mergeKeys = (keys, config2) => {
|
||||
let config = {}
|
||||
keys.forEach(prop => {
|
||||
if (!isUndefined(config2[prop])) {
|
||||
config[prop] = config2[prop]
|
||||
}
|
||||
})
|
||||
return config
|
||||
}
|
||||
export default (config) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params)
|
||||
const _config = {
|
||||
url: fullPath,
|
||||
header: config.header,
|
||||
complete: (response) => {
|
||||
config.fullPath = fullPath
|
||||
response.config = config
|
||||
try {
|
||||
// 对可能字符串不是json 的情况容错
|
||||
if (typeof response.data === 'string') {
|
||||
response.data = JSON.parse(response.data)
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {
|
||||
}
|
||||
settle(resolve, reject, response)
|
||||
}
|
||||
}
|
||||
let requestTask
|
||||
if (config.method === 'UPLOAD') {
|
||||
delete _config.header['content-type']
|
||||
delete _config.header['Content-Type']
|
||||
let otherConfig = {
|
||||
// #ifdef MP-ALIPAY
|
||||
fileType: config.fileType,
|
||||
// #endif
|
||||
filePath: config.filePath,
|
||||
name: config.name
|
||||
}
|
||||
const optionalKeys = [
|
||||
// #ifdef APP-PLUS || H5
|
||||
'files',
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
'file',
|
||||
// #endif
|
||||
// #ifdef H5 || APP-PLUS
|
||||
'timeout',
|
||||
// #endif
|
||||
'formData'
|
||||
]
|
||||
requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
|
||||
} else if (config.method === 'DOWNLOAD') {
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (!isUndefined(config['timeout'])) {
|
||||
_config['timeout'] = config['timeout']
|
||||
}
|
||||
// #endif
|
||||
requestTask = uni.downloadFile(_config)
|
||||
} else {
|
||||
const optionalKeys = [
|
||||
'data',
|
||||
'method',
|
||||
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||
'timeout',
|
||||
// #endif
|
||||
'dataType',
|
||||
// #ifndef MP-ALIPAY
|
||||
'responseType',
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
'sslVerify',
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
'withCredentials',
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
'firstIpv4',
|
||||
// #endif
|
||||
]
|
||||
requestTask = uni.request({..._config,...mergeKeys(optionalKeys, config)})
|
||||
}
|
||||
if (config.getTask) {
|
||||
config.getTask(requestTask, config)
|
||||
}
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user