mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-07-28 05:00:41 +08:00
Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1407b58024 | |||
| 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 |
+1
-5
@@ -2,8 +2,4 @@ node_modules/
|
|||||||
unpackage/
|
unpackage/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
.idea
|
.idea
|
||||||
|
.trae
|
||||||
config/halo.config.js
|
|
||||||
config/love.config.js
|
|
||||||
config/token.config.js
|
|
||||||
config/plugins.config.js
|
|
||||||
@@ -1,17 +1,15 @@
|
|||||||
<script>
|
<script>
|
||||||
import HaloTokenConfig from '@/config/token.config.js';
|
import HaloTokenConfig from '@/config/uhalo.config.js';
|
||||||
|
|
||||||
// app升级检测(搭配:https://ext.dcloud.net.cn/plugin?id=4470 升级中心)
|
// app升级检测(搭配:https://ext.dcloud.net.cn/plugin?id=4470 升级中心)
|
||||||
import CheckAppUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
|
import CheckAppUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
|
||||||
import {
|
import {CheckWxUpdate} from '@/utils/update.js';
|
||||||
CheckWxUpdate
|
|
||||||
} from '@/utils/update.js';
|
export default {
|
||||||
export default {
|
|
||||||
globalData: {
|
globalData: {
|
||||||
baseApiUrl: HaloTokenConfig.BASE_API
|
baseApiUrl: HaloTokenConfig.BASE_API
|
||||||
},
|
},
|
||||||
onLaunch: function() {
|
onLaunch: function () {
|
||||||
console.log('App Launch');
|
|
||||||
|
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
CheckAppUpdate();
|
CheckAppUpdate();
|
||||||
@@ -22,45 +20,41 @@
|
|||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
uni.$tm.vx.actions('config/fetchConfigs').then((res) => {
|
uni.$tm.vx.actions('config/fetchConfigs').then((res) => {
|
||||||
console.log('正常:', res)
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
uni.$tm.vx.commit('setWxShare', res.shareConfig);
|
|
||||||
// #endif
|
|
||||||
})
|
})
|
||||||
// 启动检查app的配置是否已经就绪,若未就绪则设置默认的
|
// 启动检查app的配置是否已经就绪,若未就绪则设置默认的
|
||||||
uni.$tm.vx.actions('setting/checkAndSetDefaultAppSettings');
|
uni.$tm.vx.actions('setting/checkAndSetDefaultAppSettings');
|
||||||
},
|
},
|
||||||
onShow: function() {
|
onShow: function () {
|
||||||
console.log('App Show');
|
console.log('App Show');
|
||||||
},
|
},
|
||||||
onHide: function() {
|
onHide: function () {
|
||||||
console.log('App Hide');
|
console.log('App Hide');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
// 基础样式
|
// 基础样式
|
||||||
@import './common/styles/app.theme.scss';
|
@import './common/styles/app.theme.scss';
|
||||||
@import './common/styles/app.base.scss';
|
@import './common/styles/app.base.scss';
|
||||||
|
|
||||||
// 引入tmUI2.x样式
|
// 引入tmUI2.x样式
|
||||||
@import './tm-vuetify/mian.min.css';
|
@import './tm-vuetify/mian.min.css';
|
||||||
// 引入tmUI2.x主题包
|
// 引入tmUI2.x主题包
|
||||||
@import './tm-vuetify/scss/theme.css';
|
@import './tm-vuetify/scss/theme.css';
|
||||||
// 引入tmUI2.x预置图标
|
// 引入tmUI2.x预置图标
|
||||||
@import './tm-vuetify/scss/fonts/fontawesome_base64.css';
|
@import './tm-vuetify/scss/fonts/fontawesome_base64.css';
|
||||||
|
|
||||||
// 自定义图标
|
// 自定义图标
|
||||||
@import './common/icons/halocoloriconfont.css';
|
@import './common/icons/halocoloriconfont.css';
|
||||||
@import './common/icons/haloiconfont.css';
|
@import './common/icons/haloiconfont.css';
|
||||||
@import './common/icons/mphtmliconfont.css';
|
@import './common/icons/mphtmliconfont.css';
|
||||||
|
|
||||||
/* #ifndef MP-WEIXIN */
|
/* #ifndef MP-WEIXIN */
|
||||||
@import './common/markdown/markdown.scss';
|
@import './common/markdown/markdown.scss';
|
||||||
/* #endif */
|
/* #endif */
|
||||||
|
|
||||||
page {
|
page {
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://uni-halo.925i.cn" target="_blank" rel="noopener noreferrer">
|
<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>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -10,10 +10,10 @@
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://b.925i.cn">作者博客</a>
|
<a href="https://www.xiaoxiaomo.cn">作者主页</a>
|
||||||
<a href="https://uni-halo.925i.cn">文档地址(等待更新v2版本说明)</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">仓库地址</a>
|
||||||
<a href="https://github.com/ialley-workshop-open/uni-halo">Github仓库</a>
|
<a href="https://github.com/ialley-workshop-open/plugin-uni-halo">插件仓库</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -21,82 +21,70 @@
|
|||||||
如果您觉得这个项目对您有帮助,可以帮作者买杯饮料鼓励鼓励,同时为了项目能够持续发展,可以根据您的喜好支持一下本项目哦,非常感谢您的支持,作者也会更有动力持续维护和更新新的功能哦~
|
如果您觉得这个项目对您有帮助,可以帮作者买杯饮料鼓励鼓励,同时为了项目能够持续发展,可以根据您的喜好支持一下本项目哦,非常感谢您的支持,作者也会更有动力持续维护和更新新的功能哦~
|
||||||
|
|
||||||
| 支付宝 | 微信 | QQ |
|
| 支付宝 | 微信 | QQ |
|
||||||
|:----------------------------------------------:|:---------------------------------------------:|:---------------------------------------------:|
|
|:---------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------:|
|
||||||
|  |  |  |
|
|  |  |  |
|
||||||
|
|
||||||
|
## 一、项目介绍
|
||||||
|
|
||||||
## 🧐 功能介绍
|
基于 Halo 2.x 提供的 API 接口,为微信小程序提供的一套开源的博客应用。
|
||||||
|
|
||||||
#### 🍻 基础功能
|
- 完全免费开源,包括程序源码、插件源码
|
||||||
|
- 页面支持插件配置
|
||||||
|
- 使用最新流行的技术栈
|
||||||
|
- 支持特色功能,恋爱日记
|
||||||
|
- 理论支持编译为 小程序(推荐)、APP
|
||||||
|
|
||||||
`重要说明:2.0版本 暂时去掉后台管理功能。`
|
### 部分截图
|
||||||
|
|
||||||
- 用户端:文章列表、文章分类、文章详情、图库、友链、瞬间等
|
|首页|分类|我的|
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
#### 💞 特色功能
|
|
||||||
|
|
||||||
- 恋爱日记:精心为您设计了秀恩爱的模块,秀出关于你们的故事、情侣甜蜜相册、恋爱记录清单,为您的恋爱保驾护航。
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
#### 😘 插件配置
|
|
||||||
|
|
||||||
已支持通过插件配置,插件地址 [plugin-uni-halo](https://github.com/ialley-workshop-open/plugin-uni-halo)
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
## 😎 文档源码
|
|
||||||
|
|
||||||
- 作者博客:[https://b.925i.cn/](https://b.925i.cn/)
|
|
||||||
- 官方文档(待更新v2版本说明):[https://uni-halo.925i.cn/](https://uni-halo.925i.cn/)
|
|
||||||
- Gitee :[https://gitee.com/ialley-workshop-open/uni-halo](https://gitee.com/ialley-workshop-open/uni-halo)
|
|
||||||
- Github :[https://github.com/ialley-workshop-open/uni-halo](https://github.com/ialley-workshop-open/uni-halo)
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
## 😏 在线体验
|
|
||||||
|
|
||||||
说明:微信扫码、或搜索【小莫唐尼】即可在线体验已上线小程序!!!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<br/>
|
|
||||||
## 😘 界面预览
|
|
||||||
|
|
||||||
##### 📱 用户端
|
|
||||||
|
|
||||||
说明:以下仅为部分截图
|
|
||||||
|
|
||||||
|首页|分类|图库|
|
|
||||||
|:--:|:--:|:--:|
|
|:--:|:--:|:--:|
|
||||||
||||
|
||||
|
||||||
|
|
||||||
|瞬间|我的|
|
|
||||||
|:--:|:--:|
|
|
||||||
|||
|
|
||||||
|
|
||||||
|
|
||||||
##### 📱 恋爱日记
|
|
||||||
|
|
||||||
说明:以下仅为部分截图
|
### 恋爱日记
|
||||||
|
|
||||||
|
- 在 uni-halo 中,我们为您准备了一个恋爱日记的模块,您可以在其中记录您的恋爱故事,分享给您的朋友们,请扫示例小程序,在线体验恋爱日记的功能。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|主页|恋爱清单|
|
|
||||||
|:--:|:--:|
|
|
||||||
|||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
## 🙋 使用交流
|
|
||||||
|
### 功能介绍
|
||||||
|
|
||||||
|
- 基础功能:文章列表、文章分类、文章详情、个人图库、友情链接、我的瞬间
|
||||||
|
|
||||||
|
- 特色功能:恋爱日记,精心为您设计了秀恩爱的模块,秀出关于你们的故事、情侣甜蜜相册、恋爱记录清单,为您的恋爱保驾护航。
|
||||||
|
|
||||||
|
|
||||||
|
## 二、配套插件
|
||||||
|
|
||||||
|
### 下载地址
|
||||||
|
- 代码仓库:[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)
|
- 提交 issues:[Github Issues](https://github.com/ialley-workshop-open/uni-halo/issues)
|
||||||
丨 [Gitee Issues](https://gitee.com/ialley-workshop-open/uni-halo/issues)
|
|
||||||
- 微信交流群:由于微信交流群时效性太短,所以有需要的话可以先加入QQ群,再邀请进微信群
|
|
||||||
- QQ 交流群:632969367
|
- QQ 交流群:632969367
|
||||||
-
|
- 微信交流群:由于微信交流群时效性太短,所以有需要的话可以先加入QQ群,再邀请进微信群
|
||||||
快速加群:[](https://qm.qq.com/cgi-bin/qm/qr?k=cuam7lhrB_vHuvjw2mzUO1sOKdF9KRyM&jump_from=webapi&authKey=d6bJhNaqupTTfoMDWDIeYUTAs44Ax7p64SsM/5NRtFhXE09YexDO15Izuyth6XAi)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
@@ -108,6 +96,16 @@ uni-halo 还在持续更新中,本项目欢迎您的参与,共同维护,
|
|||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
## 💪 贡献者们
|
||||||
|
|
||||||
|
感谢以下所有给 uni-halo 贡献过代码的 [开发者](https://github.com/ialley-workshop-open/uni-halo/graphs/contributors)。
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
## 🔍 开源许可
|
## 🔍 开源许可
|
||||||
|
|
||||||
uni-halo 使用 AGPL-3.0 协议开源,请遵守开源协议。
|
uni-halo 使用 AGPL-3.0 协议开源,请遵守开源协议。
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ import v2Config from './v2/all.config.js'
|
|||||||
|
|
||||||
const ApiManager = {
|
const ApiManager = {
|
||||||
v2,
|
v2,
|
||||||
v2Config,
|
v2Config
|
||||||
};
|
};
|
||||||
|
|
||||||
const install = (Vue) => {
|
const install = (Vue) => {
|
||||||
|
|||||||
+98
-117
@@ -1,204 +1,185 @@
|
|||||||
/**
|
import {
|
||||||
* 所有的接口
|
getPersonalToken
|
||||||
*/
|
} from '@/utils/token.js'
|
||||||
import HaloTokenConfig from '@/config/token.config.js'
|
|
||||||
import HttpHandler from '@/common/http/request.js'
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
import qs from 'qs'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getAppConfigs
|
getAppConfigs
|
||||||
} from '@/config/index.js'
|
} from '@/config/index.js'
|
||||||
|
import { getNologinEmail, getOpenid } from "@/utils/auth";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
|
||||||
* 获取文章列表
|
|
||||||
* @param {Object} params 参数
|
|
||||||
*/
|
|
||||||
getPostList: (params) => {
|
getPostList: (params) => {
|
||||||
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/posts`, params)
|
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/posts`, params)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据名称获取文章
|
|
||||||
* @param {String} name 分类名称
|
|
||||||
*/
|
|
||||||
getPostByName: (name) => {
|
getPostByName: (name) => {
|
||||||
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/posts/${name}`, {}, {
|
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/posts/${name}`, {}, {
|
||||||
header: {
|
header: {
|
||||||
'Wechat-Session-Id': uni.getStorageSync('openid'),
|
'Wechat-Session-Id': getOpenid(),
|
||||||
|
'nologin-email': getNologinEmail()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 搜索文章
|
|
||||||
* @param {Object} params 数据
|
|
||||||
*/
|
|
||||||
getPostListByKeyword: (params) => {
|
getPostListByKeyword: (params) => {
|
||||||
return HttpHandler.Get(`/apis/api.halo.run/v1alpha1/indices/post`, params)
|
return HttpHandler.Post(`/apis/api.halo.run/v1alpha1/indices/-/search`, params)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询分类列表
|
|
||||||
* @param {Object} params 查询参数
|
|
||||||
*/
|
|
||||||
getCategoryList: (params) => {
|
getCategoryList: (params) => {
|
||||||
return HttpHandler.Get('/apis/api.content.halo.run/v1alpha1/categories', params)
|
const param = qs.stringify(params, {
|
||||||
|
allowDots: true,
|
||||||
|
encodeValuesOnly: true,
|
||||||
|
skipNulls: true,
|
||||||
|
encode: false,
|
||||||
|
arrayFormat: 'repeat'
|
||||||
|
})
|
||||||
|
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/categories?${param}`, {})
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 查询分类下的文章
|
|
||||||
* @param {String} name 分类名称
|
|
||||||
* @param {Object} params 查询参数
|
|
||||||
*/
|
|
||||||
getCategoryPostList: (name, params) => {
|
getCategoryPostList: (name, params) => {
|
||||||
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/categories/${name}/posts`, params)
|
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/categories/${name}/posts`, params)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取评论列表接口(列表数据)
|
|
||||||
* @param {Object} params 查询参数
|
|
||||||
*/
|
|
||||||
getPostCommentList: (params) => {
|
getPostCommentList: (params) => {
|
||||||
return HttpHandler.Get(`/apis/api.halo.run/v1alpha1/comments`, params)
|
return HttpHandler.Get(`/apis/api.halo.run/v1alpha1/comments`, params)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取回复列表
|
|
||||||
* @param {String} commentName 名称
|
|
||||||
* @param {Object} params 查询参数
|
|
||||||
*/
|
|
||||||
getPostCommentReplyList: (commentName, params) => {
|
getPostCommentReplyList: (commentName, params) => {
|
||||||
return HttpHandler.Get(`/apis/api.halo.run/v1alpha1/comments/${commentName}/reply`, params)
|
return HttpHandler.Get(`/apis/api.halo.run/v1alpha1/comments/${commentName}/reply`, params)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 提交评论
|
|
||||||
addPostComment: (data) => {
|
addPostComment: (data) => {
|
||||||
return HttpHandler.Post(`/apis/api.halo.run/v1alpha1/comments`, data)
|
return HttpHandler.Post(`/apis/api.halo.run/v1alpha1/comments`, data)
|
||||||
},
|
},
|
||||||
// 提交回复
|
|
||||||
addPostCommentReply: (commentName, data) => {
|
addPostCommentReply: (commentName, data) => {
|
||||||
return HttpHandler.Post(`/apis/api.halo.run/v1alpha1/comments/${commentName}/replay`, data)
|
return HttpHandler.Post(`/apis/api.halo.run/v1alpha1/comments/${commentName}/reply`, data)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取标签列表
|
|
||||||
* @param {Object} params 查询参数
|
|
||||||
*/
|
|
||||||
getTagList: (params) => {
|
getTagList: (params) => {
|
||||||
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/tags`, params)
|
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/tags`, params)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据标签获取文章列表
|
|
||||||
* @param {String} tagName 参数
|
|
||||||
* @param {Object} params 查询参数
|
|
||||||
*/
|
|
||||||
getPostByTagName: (tagName, params) => {
|
getPostByTagName: (tagName, params) => {
|
||||||
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/tags/${tagName}/posts`, params)
|
return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/tags/${tagName}/posts`, params)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取瞬间列表
|
|
||||||
*/
|
|
||||||
getMomentList: (params) => {
|
getMomentList: (params) => {
|
||||||
return HttpHandler.Get(`/apis/moment.halo.run/v1alpha1/moments`, params, {
|
return HttpHandler.Get(`/apis/api.moment.halo.run/v1alpha1/moments`, params, {
|
||||||
custom: {
|
custom: {
|
||||||
systemToken: HaloTokenConfig.systemToken
|
personalToken: getPersonalToken()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getMomentByName: (name) => {
|
||||||
|
return HttpHandler.Get(`/apis/api.moment.halo.run/v1alpha1/moments/${name}`, {}, {
|
||||||
|
custom: {
|
||||||
|
personalToken: getPersonalToken()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询站点统计信息
|
|
||||||
*/
|
|
||||||
getBlogStatistics: () => {
|
getBlogStatistics: () => {
|
||||||
return HttpHandler.Get(`/apis/api.halo.run/v1alpha1/stats/-`, {})
|
return HttpHandler.Get(`/apis/api.halo.run/v1alpha1/stats/-`, {})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取相册分组
|
|
||||||
*/
|
|
||||||
getPhotoGroupList: (params) => {
|
getPhotoGroupList: (params) => {
|
||||||
return HttpHandler.Get(`/apis/core.halo.run/v1alpha1/photogroups`, params, {
|
return HttpHandler.Get(`/apis/api.photo.halo.run/v1alpha1/photogroups`, params)
|
||||||
custom: {
|
|
||||||
systemToken: HaloTokenConfig.systemToken
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据分组获取相册
|
|
||||||
*/
|
|
||||||
getPhotoListByGroupName: (params) => {
|
getPhotoListByGroupName: (params) => {
|
||||||
return HttpHandler.Get(`/apis/console.api.photo.halo.run/v1alpha1/photos`, params, {
|
return HttpHandler.Get(`/apis/api.photo.halo.run/v1alpha1/photos`, params)
|
||||||
custom: {
|
|
||||||
systemToken: HaloTokenConfig.systemToken
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取友链分组
|
|
||||||
*/
|
|
||||||
getFriendLinkGroupList: (params) => {
|
getFriendLinkGroupList: (params) => {
|
||||||
return HttpHandler.Get(`/apis/core.halo.run/v1alpha1/linkgroups`, params,{
|
return HttpHandler.Get(`/apis/api.link.halo.run/v1alpha1/linkgroups`, params)
|
||||||
custom: {
|
|
||||||
systemToken: HaloTokenConfig.systemToken
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取友链
|
|
||||||
*/
|
|
||||||
getFriendLinkList: (params) => {
|
getFriendLinkList: (params) => {
|
||||||
return HttpHandler.Get(`/apis/api.plugin.halo.run/v1alpha1/plugins/PluginLinks/links`, params)
|
return HttpHandler.Get(`/apis/api.link.halo.run/v1alpha1/links`, params)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
requestRestrictReadCheck: (restrictType, code, keyId) => {
|
||||||
/**
|
const params = {
|
||||||
* 校验文章访问密码
|
code: code,
|
||||||
*/
|
templateType: 'post',
|
||||||
checkPostVerifyCode: (verifyCode, postId) => {
|
restrictType: restrictType,
|
||||||
return HttpHandler.Get(`/apis/tools.muyin.site/v1alpha1/verificationCode/check?code=${verifyCode}`, null, {
|
keyId: keyId
|
||||||
|
}
|
||||||
|
return HttpHandler.Post(`/apis/tools.muyin.site/v1alpha1/restrict-read/check`, params, {
|
||||||
header: {
|
header: {
|
||||||
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
||||||
'Wechat-Session-Id': uni.getStorageSync('openid'),
|
'Wechat-Session-Id': getOpenid(),
|
||||||
'Post-Id': postId
|
'nologin-email': getNologinEmail()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
createVerificationCode: () => {
|
||||||
* 校验文章访问密码
|
return HttpHandler.Get(`/apis/tools.muyin.site/v1alpha1/restrict-read/create`, null, {
|
||||||
*/
|
|
||||||
checkPostPasswordAccess: (password, postId) => {
|
|
||||||
return HttpHandler.Get(`/apis/tools.muyin.site/v1alpha1/visitPassword/checkPost?password=${password}`,
|
|
||||||
null, {
|
|
||||||
header: {
|
header: {
|
||||||
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
||||||
'Wechat-Session-Id': uni.getStorageSync('openid'),
|
'Wechat-Session-Id': getOpenid(),
|
||||||
'Post-Id': postId
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取文章验证码
|
|
||||||
*/
|
|
||||||
getPostVerifyCode: () => {
|
|
||||||
return HttpHandler.Get(`/apis/tools.muyin.site/v1alpha1/verificationCode/create`, null, {
|
|
||||||
header: {
|
|
||||||
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 提交友情链接
|
|
||||||
*/
|
|
||||||
submitLink(form) {
|
submitLink(form) {
|
||||||
return HttpHandler.Post(`/apis/linksSubmit.muyin.site/v1alpha1/submit`, form, null)
|
return HttpHandler.Post(`/apis/linkssubmit.muyin.site/v1alpha1/submit`, form, {
|
||||||
|
header: {
|
||||||
|
'Authorization': getAppConfigs().pluginConfig.linksSubmitPlugin?.Authorization,
|
||||||
|
'Wechat-Session-Id': getOpenid(),
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getQRCodeInfo: (key) => {
|
||||||
|
return HttpHandler.Get(`/apis/api.uni.uhalo.pro/v1alpha1/plugins/plugin-uni-halo/getQRCodeInfo/${key}`,
|
||||||
|
null, {})
|
||||||
|
},
|
||||||
|
|
||||||
|
getQRCodeImg: (postId) => {
|
||||||
|
return HttpHandler.Get(`/apis/api.uni.uhalo.pro/v1alpha1/plugins/plugin-uni-halo/getQRCodeImg/${postId}`,
|
||||||
|
null, {})
|
||||||
|
},
|
||||||
|
|
||||||
|
submitUpvote(data) {
|
||||||
|
return HttpHandler.Post(`/apis/api.halo.run/v1alpha1/trackers/upvote`, data, {})
|
||||||
|
},
|
||||||
|
|
||||||
|
getVoteList: (params) => {
|
||||||
|
return HttpHandler.Get(`/apis/api.vote.kunkunyu.com/v1alpha1/votes`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
getVoteDetail: (name) => {
|
||||||
|
return HttpHandler.Get(`/apis/api.vote.kunkunyu.com/v1alpha1/votes/${name}/detail`, {})
|
||||||
|
},
|
||||||
|
|
||||||
|
getVoteUserList: (name) => {
|
||||||
|
return HttpHandler.Get(`/apis/api.vote.kunkunyu.com/v1alpha1/votes/${name}/user-list`, {})
|
||||||
|
},
|
||||||
|
|
||||||
|
submitVote: (name, data, canAnonymously = true) => {
|
||||||
|
return HttpHandler.Post(`/apis/api.vote.kunkunyu.com/v1alpha1/votes/${name}/submit`, data, {
|
||||||
|
custom: {
|
||||||
|
personalToken: canAnonymously ? undefined : getPersonalToken()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
checkPluginAvailable: (name) => {
|
||||||
|
return HttpHandler.Get(`/apis/api.plugin.halo.run/v1alpha1/plugins/${name}/available`, {})
|
||||||
|
},
|
||||||
|
|
||||||
|
getDoubanDetail: (url) => {
|
||||||
|
return HttpHandler.Get(`/apis/api.douban.moony.la/v1alpha1/doubanmovies/-/getDoubanDetail`, { url })
|
||||||
|
},
|
||||||
|
|
||||||
|
postTrackersCounter: (data) => {
|
||||||
|
return HttpHandler.Post(`/apis/api.halo.run/v1alpha1/trackers/counter`, data)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Halo 数据看板插件
|
||||||
|
* @link https://www.halo.run/store/apps/app-rtnbbgfk
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
getPersonalToken
|
||||||
|
} from '@/utils/token.js'
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
import qs from 'qs'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getAppConfigs
|
||||||
|
} from '@/config/index.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取图表统计数据
|
||||||
|
* @description - 标签、分类、文章发布趋势、评论活跃用户、获取热门文章top10
|
||||||
|
*/
|
||||||
|
getChartData: () => {
|
||||||
|
return HttpHandler.Get(`/apis/api.data.statistics.xhhao.com/v1alpha1/chart/data`,{})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取Github配置信息
|
||||||
|
*/
|
||||||
|
getGithubConfig:()=>{
|
||||||
|
return HttpHandler.Get('/apis/api.data.statistics.xhhao.com/v1alpha1/github/config',{})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取 Uptime Kuma 状态页面数据
|
||||||
|
*/
|
||||||
|
getUptimeStatus:()=>{
|
||||||
|
return HttpHandler.Get('/apis/api.data.statistics.xhhao.com/v1alpha1/github/config',{})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
* 修改时间:
|
* 修改时间:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import HaloTokenConfig from '@/config/token.config.js'
|
import HaloTokenConfig from '@/config/uhalo.config.js'
|
||||||
import {
|
import {
|
||||||
setInterceptors
|
setInterceptors
|
||||||
} from "./interceptors.js";
|
} from "./interceptors.js";
|
||||||
@@ -21,7 +21,7 @@ const http = new Request()
|
|||||||
http.setConfig((config) => {
|
http.setConfig((config) => {
|
||||||
|
|
||||||
// 如果是在外部浏览器调试或者编译为h5,请注释该行代码
|
// 如果是在外部浏览器调试或者编译为h5,请注释该行代码
|
||||||
config.baseURL = HaloTokenConfig.BASE_API;
|
config.baseURL = HaloTokenConfig.BASE_API.replace(/\/+$/, "");
|
||||||
|
|
||||||
config.header = {
|
config.header = {
|
||||||
...config.header,
|
...config.header,
|
||||||
|
|||||||
+14
-30
@@ -9,13 +9,7 @@
|
|||||||
* 修改人员:
|
* 修改人员:
|
||||||
* 修改时间:
|
* 修改时间:
|
||||||
*/
|
*/
|
||||||
import {
|
import {delCache, setCache} from "@/utils/storage";
|
||||||
getAdminAccessToken
|
|
||||||
} from "@/utils/auth.js";
|
|
||||||
import {
|
|
||||||
delCache,
|
|
||||||
setCache
|
|
||||||
} from "@/utils/storage";
|
|
||||||
|
|
||||||
const getCategoryNameByUrl = (url) => {
|
const getCategoryNameByUrl = (url) => {
|
||||||
const reg = '(?<=/api/content/categories/).+(?=/posts)'
|
const reg = '(?<=/api/content/categories/).+(?=/posts)'
|
||||||
@@ -37,9 +31,7 @@ const showCategoryInputPasswordModal = (response, category) => {
|
|||||||
// TODO 这里如果没有输入密码点击确认应该阻止窗口关闭,但是没找到方法
|
// TODO 这里如果没有输入密码点击确认应该阻止窗口关闭,但是没找到方法
|
||||||
if (!res.content) {
|
if (!res.content) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '提示:请输入密码',
|
title: '提示:请输入密码', icon: 'none', success: () => {
|
||||||
icon: 'none',
|
|
||||||
success: () => {
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showCategoryInputPasswordModal(response, category);
|
showCategoryInputPasswordModal(response, category);
|
||||||
}, 800)
|
}, 800)
|
||||||
@@ -56,14 +48,14 @@ const showCategoryInputPasswordModal = (response, category) => {
|
|||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: '/pages/tabbar/category/category'
|
url: '/pages/tabbar/category/category'
|
||||||
});
|
});
|
||||||
} else if (res.cancel) {}
|
} else if (res.cancel) {
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setInterceptors = (http) => {
|
export const setInterceptors = (http) => {
|
||||||
http.interceptors.request.use(
|
http.interceptors.request.use((config) => {
|
||||||
(config) => {
|
|
||||||
console.log("config", config)
|
console.log("config", config)
|
||||||
|
|
||||||
// 可使用async await 做异步操作
|
// 可使用async await 做异步操作
|
||||||
@@ -71,19 +63,16 @@ export const setInterceptors = (http) => {
|
|||||||
...config.header
|
...config.header
|
||||||
// ... 可以直接加参数
|
// ... 可以直接加参数
|
||||||
};
|
};
|
||||||
if (config.custom.systemToken) {
|
if (config.custom.personalToken) {
|
||||||
config.header['Authorization'] = `Bearer ${config.custom.systemToken}`
|
config.header['Authorization'] = `Bearer ${config.custom.personalToken}`
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
},
|
}, (config) => {
|
||||||
(config) => {
|
|
||||||
// 可使用async await 做异步操作
|
// 可使用async await 做异步操作
|
||||||
return Promise.reject(config);
|
return Promise.reject(config);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
http.interceptors.response.use(
|
http.interceptors.response.use((response) => {
|
||||||
(response) => {
|
|
||||||
/* 对响应成功做点什么 可使用async await 做异步操作*/
|
/* 对响应成功做点什么 可使用async await 做异步操作*/
|
||||||
// if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
|
// if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
|
||||||
// return Promise.reject(response) // return Promise.reject 可使promise状态进入catch
|
// return Promise.reject(response) // return Promise.reject 可使promise状态进入catch
|
||||||
@@ -96,13 +85,11 @@ export const setInterceptors = (http) => {
|
|||||||
} else {
|
} else {
|
||||||
return Promise.reject(response);
|
return Promise.reject(response);
|
||||||
}
|
}
|
||||||
},
|
}, (response) => {
|
||||||
(response) => {
|
|
||||||
/* 对响应错误做点什么 (statusCode !== 200)*/
|
/* 对响应错误做点什么 (statusCode !== 200)*/
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
return Promise.reject({
|
return Promise.reject({
|
||||||
status: 500,
|
status: 500, message: 'API接口服务异常!'
|
||||||
message: 'API接口服务异常!'
|
|
||||||
})
|
})
|
||||||
} else if (response.data.status == 401) {
|
} else if (response.data.status == 401) {
|
||||||
uni.$tm.toast(response.data.message);
|
uni.$tm.toast(response.data.message);
|
||||||
@@ -111,9 +98,7 @@ export const setInterceptors = (http) => {
|
|||||||
const category = getCategoryNameByUrl(response.config.url)
|
const category = getCategoryNameByUrl(response.config.url)
|
||||||
delCache('APP_CATEGORY_PWD_' + category);
|
delCache('APP_CATEGORY_PWD_' + category);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '提示:密码不正确',
|
title: '提示:密码不正确', icon: 'none', success: () => {
|
||||||
icon: 'none',
|
|
||||||
success: () => {
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showCategoryInputPasswordModal(response, category);
|
showCategoryInputPasswordModal(response, category);
|
||||||
}, 800)
|
}, 800)
|
||||||
@@ -152,6 +137,5 @@ export const setInterceptors = (http) => {
|
|||||||
return Promise.reject(response.data);
|
return Promise.reject(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
});
|
||||||
);
|
|
||||||
};
|
};
|
||||||
@@ -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: "切换语言",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
* markdown配置
|
* markdown配置
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {getAppConfigs} from '@/config/index.js'
|
||||||
getAppConfigs
|
import HaloTokenConfig from '@/config/uhalo.config.js'
|
||||||
} from '@/config/index.js'
|
import utils from '@/utils/index.js'
|
||||||
import HaloTokenConfig from '@/config/token.config.js'
|
|
||||||
export default {
|
export default {
|
||||||
domain: HaloTokenConfig.BASE_API,
|
domain: HaloTokenConfig.BASE_API,
|
||||||
tagStyle: {
|
tagStyle: {
|
||||||
@@ -95,6 +95,6 @@ export default {
|
|||||||
},
|
},
|
||||||
containStyle: 'font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding:12px;font-size: 14px;color: #606266;word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 6px;background-color:#FFFFFF;',
|
containStyle: 'font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding:12px;font-size: 14px;color: #606266;word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 6px;background-color:#FFFFFF;',
|
||||||
|
|
||||||
loadingGif: getAppConfigs().imagesConfig.loadingGifUrl,
|
loadingGif: utils.checkImageUrl(getAppConfigs().imagesConfig.loadingGifUrl),
|
||||||
emptyGif: getAppConfigs().imagesConfig.loadingEmptyUrl,
|
emptyGif: utils.checkImageUrl(getAppConfigs().imagesConfig.loadingEmptyUrl),
|
||||||
}
|
}
|
||||||
+1
-13
@@ -42,18 +42,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
haloConfig: {
|
|
||||||
deep: true,
|
|
||||||
immediate: true,
|
|
||||||
handler(newVal) {
|
|
||||||
if (!newVal) return;
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
uni.$tm.vx.commit('setWxShare', newVal.shareConfig);
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.fnResetSetAniWaitIndex()
|
this.fnResetSetAniWaitIndex()
|
||||||
},
|
},
|
||||||
@@ -72,7 +60,7 @@ export default {
|
|||||||
// 设置页面标题
|
// 设置页面标题
|
||||||
fnSetPageTitle(title) {
|
fnSetPageTitle(title) {
|
||||||
uni.setNavigationBarTitle({
|
uni.setNavigationBarTitle({
|
||||||
title: title || (this.haloConfig && this.haloConfig.startConfig.title) || "uni-halo",
|
title: title || this.haloConfig?.appConfig?.startConfig?.title || "uni-halo",
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* 功能:插件检查
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
NeedPluginIds,
|
||||||
|
NeedPlugins,
|
||||||
|
checkNeedPluginAvailable
|
||||||
|
} from "@/utils/plugin.js"
|
||||||
|
|
||||||
|
const HaloPluginAvailableMixin = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
NeedPluginIds,
|
||||||
|
NeedPlugins,
|
||||||
|
uniHaloPluginAvailableError: "",
|
||||||
|
uniHaloPluginAvailable: true,
|
||||||
|
uniHaloPluginPageClass: "",
|
||||||
|
uniHaloPluginId: "", // 当前需要的插件
|
||||||
|
uniHaloPluginInfo: "" // 当前插件信息
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
uniHaloPluginAvailable: {
|
||||||
|
immediate: true,
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.uniHaloPluginPageClass = ""
|
||||||
|
} else {
|
||||||
|
this.uniHaloPluginPageClass = "box-border items-center justify-center"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 设置插件ID */
|
||||||
|
setPluginId(pluginId) {
|
||||||
|
this.uniHaloPluginId = pluginId
|
||||||
|
this.uniHaloPluginInfo = NeedPlugins.get(pluginId)
|
||||||
|
},
|
||||||
|
/** 检查插件状态 */
|
||||||
|
async checkPluginAvailable(pluginId) {
|
||||||
|
pluginId = pluginId ?? this.uniHaloPluginId
|
||||||
|
if (!pluginId) return false;
|
||||||
|
const available = await checkNeedPluginAvailable(pluginId)
|
||||||
|
this.uniHaloPluginAvailable = available
|
||||||
|
return available
|
||||||
|
},
|
||||||
|
/** 设置错误信息 */
|
||||||
|
setPluginError(text) {
|
||||||
|
this.uniHaloPluginAvailableError = text
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HaloPluginAvailableMixin;
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
// 微信分享配置
|
|
||||||
import HaloTokenConfig from '@/config/token.config.js'
|
|
||||||
import {jsonToUrlParams2} from '@/utils/url.params.js'
|
|
||||||
|
|
||||||
export const haloWxShareMixin = {
|
|
||||||
computed: {
|
|
||||||
haloWxShareData() {
|
|
||||||
const configs = this.$tm.vx.getters().getConfigs;
|
|
||||||
return configs?.shareConfig || {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//#ifdef MP-WEIXIN
|
|
||||||
onShareAppMessage(res) {
|
|
||||||
return {
|
|
||||||
...this.haloWxShareData,
|
|
||||||
success: res => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//#endif
|
|
||||||
/* 分享到微信好友 */
|
|
||||||
onShareAppMessage(res) {
|
|
||||||
const promise = new Promise(resolve => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve({
|
|
||||||
title: this.haloWxShareData.title,
|
|
||||||
path: this.haloWxShareData.path,
|
|
||||||
})
|
|
||||||
}, 2000)
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
title: this.haloWxShareData.title,
|
|
||||||
path: '',
|
|
||||||
promise
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 分享到朋友圈-这里封装不够,在页面还要声明一次,否则是拿不到参数的,被分享者在朋友圈打开链接是空的
|
|
||||||
onShareTimeline: function () {
|
|
||||||
return {
|
|
||||||
title: this.haloWxShareData.title,
|
|
||||||
query: {},
|
|
||||||
imageUrl: this.haloWxShareData.imageUrl,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 设置分享信息(需要在页面调用)
|
|
||||||
fnSetWxShareConfig(config = {}) {
|
|
||||||
let currentRoutes = getCurrentPages(); // 获取当前打开过的页面路由数组
|
|
||||||
let currentRoute = currentRoutes[currentRoutes.length - 1].route; //获取当前页面路由(分销思路,分享者点开使用的小程序将获取到分享者的id)
|
|
||||||
let sharePath = currentRoutes
|
|
||||||
if (config.params) {
|
|
||||||
const URLParams = config.params ? jsonToUrlParams2(config.params) : {};
|
|
||||||
sharePath += URLParams
|
|
||||||
}
|
|
||||||
let _config = Object.assign({}, {
|
|
||||||
path: sharePath,
|
|
||||||
copyLink: HaloTokenConfig.BASE_API,
|
|
||||||
query: {}
|
|
||||||
}, config)
|
|
||||||
|
|
||||||
uni.$tm.vx.commit('setWxShare', _config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -172,3 +172,178 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 文本省略样式 结束 */
|
/* 文本省略样式 结束 */
|
||||||
|
|
||||||
|
.box-border{
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义尺寸变量(单位:rpx)
|
||||||
|
$spacing-sizes: (
|
||||||
|
0: 0,
|
||||||
|
2: 4rpx,
|
||||||
|
4: 8rpx,
|
||||||
|
8: 16rpx,
|
||||||
|
12: 24rpx,
|
||||||
|
24: 48rpx,
|
||||||
|
48: 96rpx
|
||||||
|
);
|
||||||
|
|
||||||
|
// 内边距类
|
||||||
|
@each $name, $size in $spacing-sizes {
|
||||||
|
// 全方向内边距:p-{size}
|
||||||
|
.uh-p-#{$name} {
|
||||||
|
padding: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 水平方向内边距:px-{size}
|
||||||
|
.uh-px-#{$name} {
|
||||||
|
padding-left: $size !important;
|
||||||
|
padding-right: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 垂直方向内边距:py-{size}
|
||||||
|
.uh-py-#{$name} {
|
||||||
|
padding-top: $size !important;
|
||||||
|
padding-bottom: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上内边距:pt-{size}
|
||||||
|
.uh-pt-#{$name} {
|
||||||
|
padding-top: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右内边距:pr-{size}
|
||||||
|
.uh-pr-#{$name} {
|
||||||
|
padding-right: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下内边距:pb-{size}
|
||||||
|
.uh-pb-#{$name} {
|
||||||
|
padding-bottom: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左内边距:pl-{size}
|
||||||
|
.uh-pl-#{$name} {
|
||||||
|
padding-left: $size !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//外边距工具类
|
||||||
|
@each $name, $size in $spacing-sizes {
|
||||||
|
// 全方向外边距:m-{size}
|
||||||
|
.uh-m-#{$name} {
|
||||||
|
margin: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 水平方向外边距:mx-{size}
|
||||||
|
.uh-mx-#{$name} {
|
||||||
|
margin-left: $size !important;
|
||||||
|
margin-right: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 垂直方向外边距:my-{size}
|
||||||
|
.uh-my-#{$name} {
|
||||||
|
margin-top: $size !important;
|
||||||
|
margin-bottom: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上外边距:mt-{size}
|
||||||
|
.uh-mt-#{$name} {
|
||||||
|
margin-top: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右外边距:mr-{size}
|
||||||
|
.uh-mr-#{$name} {
|
||||||
|
margin-right: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下外边距:mb-{size}
|
||||||
|
.uh-mb-#{$name} {
|
||||||
|
margin-bottom: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左外边距:ml-{size}
|
||||||
|
.uh-ml-#{$name} {
|
||||||
|
margin-left: $size !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// gap 类
|
||||||
|
@each $name, $size in $spacing-sizes {
|
||||||
|
// 全方向gap:gap-{size}
|
||||||
|
.uh-gap-#{$name} {
|
||||||
|
gap: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 水平方向gap:gap-x-{size}
|
||||||
|
.uh-gap-x-#{$name} {
|
||||||
|
column-gap: $size !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 垂直方向gap:gap-y-{size}
|
||||||
|
.uh-gap-y-#{$name} {
|
||||||
|
row-gap: $size !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.w-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-full {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-1 {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-shrink-0 {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-start {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-end {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-around {
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-end {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-start {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-center {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-break {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0 24rpx;
|
margin: 0 24rpx;
|
||||||
padding: 24rpx;
|
padding:22rpx 24rpx;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
background-color: #ffff;
|
background-color: #ffff;
|
||||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
|
|||||||
@@ -0,0 +1,231 @@
|
|||||||
|
<template>
|
||||||
|
<view class="card" :class="[loading]">
|
||||||
|
<view v-if="loading !== 'success'" class="card-error" @click="fnGetData()">
|
||||||
|
{{ loadingText }}
|
||||||
|
</view>
|
||||||
|
<template v-else>
|
||||||
|
<view class="tag">豆瓣</view>
|
||||||
|
<view class="flex w-full">
|
||||||
|
<view v-if="posterEmpty" class="poster round-2">无封面</view>
|
||||||
|
<image v-else class="poster round-2" :src="poster" mode="aspectFill" @error="onPosterError"></image>
|
||||||
|
<view class="box">
|
||||||
|
<view class="title text-overflow">{{ detail.spec.name }}</view>
|
||||||
|
<view class="flex" style="align-items: center; margin-top: 12rpx">
|
||||||
|
<text class="text-size-s">评分:</text>
|
||||||
|
<tm-rate v-model="detail.spec.score / 2" color="orange" size="24" :margin="2" :num="5"></tm-rate>
|
||||||
|
<text class="text-size-s" style="margin-left: 4rpx">{{ detail.spec.score }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="content text-overflow-2">{{ detail.spec.cardSubtitle }}</view>
|
||||||
|
<view class="flex flex-wrap" style="margin-left: -10rpx">
|
||||||
|
<tm-tags color="orange" :shadow="0" size="s" model="fill">{{ types[detail.spec.type] }}</tm-tags>
|
||||||
|
<tm-tags v-for="(gen, genIndex) in detail.spec.genres" :key="genIndex" color="light-blue" :shadow="0" size="s" model="fill">{{ gen }}</tm-tags>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 扩展内容 -->
|
||||||
|
<view class="btn-group">
|
||||||
|
<tm-button theme="bg-gradient-orange-accent" icon="icon-copy" :shadow="0" :dense="true" size="m" @click="copy('post')">原文地址</tm-button>
|
||||||
|
<tm-button theme="bg-gradient-light-blue-accent" icon="icon-copy" :shadow="0" :dense="true" size="m" @click="copy('douban')">豆瓣地址</tm-button>
|
||||||
|
<tm-button theme="bg-gradient-light-blue-accent" icon="icon-copy" :shadow="0" :dense="true" size="m" @click="copy('info')">资源信息</tm-button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
|
import tmRate from '@/tm-vuetify/components/tm-rate/tm-rate.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ArticleDouban',
|
||||||
|
components: {
|
||||||
|
tmButton,
|
||||||
|
tmTags,
|
||||||
|
tmRate
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
article: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: 'loading',
|
||||||
|
loadingText: '加载中,请稍等...',
|
||||||
|
detail: null,
|
||||||
|
types: {
|
||||||
|
movie: '电影',
|
||||||
|
book: '图书',
|
||||||
|
music: '音乐',
|
||||||
|
game: '游戏',
|
||||||
|
drama: '舞台剧'
|
||||||
|
},
|
||||||
|
poster: '',
|
||||||
|
posterEmpty: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onPosterError() {
|
||||||
|
if (!this.article.spec.cover) {
|
||||||
|
this.poster = '';
|
||||||
|
this.posterEmpty = true;
|
||||||
|
} else {
|
||||||
|
this.poster = this.$utils.checkImageUrl(this.article.spec.cover);
|
||||||
|
this.posterEmpty = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fnGetData() {
|
||||||
|
this.loadingText = '加载中,请稍等...';
|
||||||
|
this.loading = 'loading';
|
||||||
|
this.$httpApi.v2
|
||||||
|
.getDoubanDetail(this.url)
|
||||||
|
.then((res) => {
|
||||||
|
this.detail = res;
|
||||||
|
this.poster = res.spec.poster;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loading = 'success';
|
||||||
|
}, 200);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.loading = 'error';
|
||||||
|
this.loadingText = '豆瓣内容加载失败,点击重试';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showToast(content) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: content,
|
||||||
|
mask: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
copy(type) {
|
||||||
|
if (type === 'post') {
|
||||||
|
const articleUrl = this.$baseApiUrl + (this.article?.status?.permalink ?? '');
|
||||||
|
this.$utils.copyText(articleUrl, '文章原文地址复制成功');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === 'douban') {
|
||||||
|
this.$utils.copyText(this.detail?.spec.link, '豆瓣资源地址复制成功');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === 'info') {
|
||||||
|
const content = `名称:${this.detail?.spec.name}丨其他:${this.detail?.spec.cardSubtitle}丨标签:${this.detail?.spec.genres.join('/')}丨时间:${
|
||||||
|
this.detail?.spec.pubdate
|
||||||
|
}丨评分:${this.detail?.spec.score}分丨链接:${this.detail?.spec.link}`;
|
||||||
|
this.$utils.copyText(content, '资源信息复制成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.w-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wp-50 {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #ffff;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
padding: 0;
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: #e88080;
|
||||||
|
color: #e88080;
|
||||||
|
background-color: rgba(232, 128, 128, 0.075);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
padding: 0;
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: rgba(3, 174, 252, 1);
|
||||||
|
color: rgba(3, 174, 252, 1);
|
||||||
|
background-color: rgba(3, 174, 252, 0.075);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-error {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 50rpx 24rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.poster {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 180rpx;
|
||||||
|
height: 220rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background-color: #eee;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
flex-grow: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 26rpx;
|
||||||
|
padding-left: 24rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-top: 12rpx;
|
||||||
|
line-height: 36rpx;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
}
|
||||||
|
.tag {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
font-size: 24rpx;
|
||||||
|
padding: 2rpx 12rpx;
|
||||||
|
background-color: #f5c618;
|
||||||
|
border-radius: 0 6rpx 0 12rpx;
|
||||||
|
}
|
||||||
|
.btn-group {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 22rpx;
|
||||||
|
gap: 0 22rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="article-min-card" :class="[globalAppSettings.layout.cardType]" @click="fnClickEvent('card')">
|
<view class="article-min-card" @click="fnClickEvent('card')">
|
||||||
<view class="left">
|
<view class="left">
|
||||||
<cache-image class="thumbnail" radius="6rpx" :url="$utils.checkThumbnailUrl(article.thumbnail)" :fileMd5="$utils.checkThumbnailUrl(article.thumbnail)" mode="aspectFill"></cache-image>
|
<cache-image class="thumbnail" radius="6rpx" :url="$utils.checkThumbnailUrl(article.spec.cover)" :fileMd5="article.spec.cover"
|
||||||
|
mode="aspectFill"></cache-image>
|
||||||
</view>
|
</view>
|
||||||
<view class="right">
|
<view class="right">
|
||||||
<view class="title text-overflow">{{ article.title }}</view>
|
<view class="title text-overflow">{{ article.spec.title }}</view>
|
||||||
<view class="content text-overflow">{{ article.summary }}</view>
|
<view class="content text-overflow">{{ article.status.excerpt }}</view>
|
||||||
<view class="foot">
|
<view class="foot">
|
||||||
<view class="create-time">
|
<view class="create-time">
|
||||||
<!-- <text class="icon iconfont icon-clock"></text> -->
|
|
||||||
<text class="time-label">发布时间:</text>
|
<text class="time-label">发布时间:</text>
|
||||||
{{ { d: article.createTime, f: 'yyyy-MM-dd' } | formatTime }}
|
{{ {d: article.spec.publishTime, f: 'yyyy-MM-dd'} | formatTime }}
|
||||||
</view>
|
</view>
|
||||||
<view class="visits">
|
<view class="visits">
|
||||||
<!-- <text class="icon iconfont icon-eye"></text> -->
|
|
||||||
浏览
|
浏览
|
||||||
<text class="number">{{ article.visits }}</text>
|
<text class="number">{{ article.stats.visit }}</text>
|
||||||
次
|
次
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -24,12 +23,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'article-min-card',
|
name: 'article-min-card',
|
||||||
props: {
|
props: {
|
||||||
article: {
|
article: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -37,11 +37,11 @@
|
|||||||
this.$emit('on-click', this.article);
|
this.$emit('on-click', this.article);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.article-min-card {
|
.article-min-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
@@ -52,7 +52,8 @@
|
|||||||
padding: 16rpx;
|
padding: 16rpx;
|
||||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
|
|
||||||
&.lr_image_text {}
|
&.lr_image_text {
|
||||||
|
}
|
||||||
|
|
||||||
&.lr_text_image {
|
&.lr_text_image {
|
||||||
.left {
|
.left {
|
||||||
@@ -216,5 +217,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,569 @@
|
|||||||
|
<template>
|
||||||
|
<view class="vote-card" :class="[loading]">
|
||||||
|
<view v-if="loading !=='success'" class="vote-error" @click="fnGetData()">
|
||||||
|
{{loadingText}}
|
||||||
|
</view>
|
||||||
|
<template v-else>
|
||||||
|
<view class="vote-card-head flex flex-col">
|
||||||
|
<view class="flex justify-between">
|
||||||
|
<view class="flex">
|
||||||
|
<tm-tags color="orange" style="min-width:40rpx;" :shadow="0" rounded size="s"
|
||||||
|
model="fill">{{ index + 1 }}</tm-tags>
|
||||||
|
<tm-tags color="light-blue" :shadow="0" rounded size="s"
|
||||||
|
model="fill">{{ vote.spec._uh_type }}</tm-tags>
|
||||||
|
<tm-tags :color="vote.spec._uh_state.color" size="s" rounded :shadow="0"
|
||||||
|
model="fill">{{vote.spec._uh_state.state}}</tm-tags>
|
||||||
|
</view>
|
||||||
|
<view class="flex-shrink">
|
||||||
|
<tm-button theme="light-blue" :shadow="0" dense size="s"
|
||||||
|
@click="handleToVoteDetail(vote)">查看投票详情</tm-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="title">
|
||||||
|
{{ vote.spec.title }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="vote-card-body">
|
||||||
|
|
||||||
|
<view v-if="vote.spec.remark" class="remark text-size-s">
|
||||||
|
{{vote.spec.remark}}
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 单选 -->
|
||||||
|
<view v-if="vote.spec.type==='single'" class="single">
|
||||||
|
<view class="w-full flex flex-col uh-gap-8">
|
||||||
|
<template v-if="vote.spec.isVoted || vote.spec.hasEnded">
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="is-voted-item" :class="[option.checked?'selected':'']" :style="{
|
||||||
|
'--percent': option._uh_percent + '%'
|
||||||
|
}">
|
||||||
|
<view class="is-voted-item-content flex w-full flex-between uh-gap-4">
|
||||||
|
<view class="text-align-left flex-1 text-break">
|
||||||
|
{{option.title }}
|
||||||
|
</view>
|
||||||
|
<view class="flex-shrink ">
|
||||||
|
{{option._uh_percent }}%
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="vote-select-option flex-1 w-full text-break"
|
||||||
|
:class="[option.checked?'light-blue':'grey-lighten-3']"
|
||||||
|
@click="handleSelectSingleOption(option)">
|
||||||
|
{{option.title }}
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 多选 -->
|
||||||
|
<view v-else-if="vote.spec.type==='multiple'" class="multiple">
|
||||||
|
<view class="w-full flex flex-col uh-gap-8">
|
||||||
|
<template v-if="vote.spec.isVoted || vote.spec.hasEnded">
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="is-voted-item" :class="[option.checked?'selected':'']" :style="{
|
||||||
|
'--percent': option._uh_percent + '%'
|
||||||
|
}">
|
||||||
|
<view class="is-voted-item-content flex w-full flex-between uh-gap-4">
|
||||||
|
<view class="text-align-left flex-1 text-break">
|
||||||
|
{{option.title }}
|
||||||
|
</view>
|
||||||
|
<view class="flex-shrink ">
|
||||||
|
{{option._uh_percent }}%
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="vote-select-option flex-1 w-full text-break"
|
||||||
|
:class="[option.checked?'light-blue':'grey-lighten-3']"
|
||||||
|
@click="handleSelectCheckboxOption(option)">
|
||||||
|
{{option.title }}
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- PK -->
|
||||||
|
<view v-else-if="vote.spec.type==='pk'" class="pk">
|
||||||
|
<view class="pk-container">
|
||||||
|
<view class="radio-item" v-for="(option,optionIndex) in vote.spec.options"
|
||||||
|
:key="optionIndex" :class="[optionIndex==0?'radio-left':'radio-right']"
|
||||||
|
:style="{width:option._uh_percent + '%'}">
|
||||||
|
<view class="option-item"
|
||||||
|
:class="[optionIndex==0?'option-item-left':'option-item-right']">
|
||||||
|
{{option._uh_percent }}%
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="option-foot w-full flex flex-between uh-mt-12">
|
||||||
|
<view class="w-full flex flex-col uh-gap-8">
|
||||||
|
<template v-if="vote.spec.isVoted || vote.spec.hasEnded">
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="is-voted-item" :class="[option.checked?'selected':'']" :style="{
|
||||||
|
'--percent': option._uh_percent + '%'
|
||||||
|
}">
|
||||||
|
<view class="is-voted-item-content flex w-full flex-between uh-gap-4">
|
||||||
|
<view class="text-align-left flex-1 text-break">
|
||||||
|
{{option.title }}
|
||||||
|
</view>
|
||||||
|
<view class="flex-shrink ">
|
||||||
|
{{option._uh_percent }}%
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="vote-select-option flex-1 w-full text-break"
|
||||||
|
:class="[option.checked?'light-blue':'grey-lighten-3']"
|
||||||
|
@click="handleSelectSingleOption(option)">
|
||||||
|
选项{{ optionIndex+1}}:{{option.title }}
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
<view class="vote-card-foot flex flex-between">
|
||||||
|
<view class="left flex">
|
||||||
|
<tm-tags v-if="vote.spec.timeLimit==='permanent'" color="grey-darken-2" rounded size="s"
|
||||||
|
model="text">结束:永久有效 </tm-tags>
|
||||||
|
<tm-tags v-else color="grey-darken-2" rounded size="s" model="text">
|
||||||
|
<template
|
||||||
|
v-if="vote.spec._state=='未开始'">开始:{{ {d: vote.spec.startDate, f: 'yyyy-MM-dd HH:mm'} | formatTime }}
|
||||||
|
</template>
|
||||||
|
<template v-else>结束:{{ {d: vote.spec.endDate, f: 'yyyy-MM-dd HH:mm'} | formatTime }}
|
||||||
|
</template>
|
||||||
|
</tm-tags>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="right flex flex-end">
|
||||||
|
<tm-tags color="grey-darken-2" rounded size="s" model="text">{{ vote.stats.voteCount }}
|
||||||
|
人已参与</tm-tags>
|
||||||
|
<tm-tags v-if="vote.spec.isVoted" color="blue" rounded size="s" model="text">已投票</tm-tags>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="submitForm.voteData.length!==0" class="box-border uh-mt-12 w-full uh-px-2">
|
||||||
|
<tm-button v-if="fnCalcIsVoted()" theme="white" text :block="true" class="w-full">您已参与投票</tm-button>
|
||||||
|
<tm-button v-else-if="vote.spec._uh_state.state==='未开始'" theme="orange" text class="w-full" :height="72"
|
||||||
|
:block="true" @click="handleSubmitTip('投票未开始')">投票未开始</tm-button>
|
||||||
|
<tm-button v-else-if="vote.spec._uh_state.state==='已结束'" theme="red" text class="w-full" :height="72"
|
||||||
|
:block="true" @click="handleSubmitTip('投票已结束')">投票已结束</tm-button>
|
||||||
|
<tm-button v-else-if="!vote.spec.canAnonymously" theme="red" :shadow="0" class="w-full" :height="72"
|
||||||
|
text :block="true" @click="handleSubmit()">不支持匿名投票</tm-button>
|
||||||
|
<tm-button v-else-if="submitForm.voteData.length===0" theme="white" text class="w-full" :height="72"
|
||||||
|
:block="true" @click="handleSubmitTip('请选择选项')">提交投票(请选择选项)</tm-button>
|
||||||
|
<tm-button v-else theme="light-blue" class="w-full" :height="72" :block="true" :loading="submitLoading"
|
||||||
|
:disabled="submitLoading" @click="handleSubmit()">提交投票</tm-button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
|
import {
|
||||||
|
VOTE_TYPES,
|
||||||
|
calcVoteState,
|
||||||
|
calcVotePercent,
|
||||||
|
voteCacheUtil
|
||||||
|
} from '@/utils/vote.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ArticleVote",
|
||||||
|
components: {
|
||||||
|
tmButton,
|
||||||
|
tmTags
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
voteId: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
article: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: "loading",
|
||||||
|
loadingText: "加载中,请稍等...",
|
||||||
|
detail: null,
|
||||||
|
vote: null,
|
||||||
|
submitForm: {
|
||||||
|
voteData: []
|
||||||
|
},
|
||||||
|
submitLoading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnGetData() {
|
||||||
|
this.loadingText = "加载中,请稍等...";
|
||||||
|
this.loading = "loading";
|
||||||
|
this.$httpApi.v2
|
||||||
|
.getVoteDetail(this.voteId)
|
||||||
|
.then(res => {
|
||||||
|
this.pageTitle = "投票详情" + `(${VOTE_TYPES[res.vote.spec.type]})`
|
||||||
|
|
||||||
|
const tempVoteRes = res;
|
||||||
|
|
||||||
|
tempVoteRes.vote.spec.isVoted = this.fnCalcIsVoted()
|
||||||
|
tempVoteRes.vote.spec.disabled = this.fnCalcIsVoted()
|
||||||
|
tempVoteRes.vote.spec._uh_state = calcVoteState(tempVoteRes.vote)
|
||||||
|
tempVoteRes.vote.spec._uh_type = VOTE_TYPES[tempVoteRes.vote.spec.type]
|
||||||
|
|
||||||
|
tempVoteRes.vote.spec.options.map((option, index) => {
|
||||||
|
option.value = option.id
|
||||||
|
option.label = option.title
|
||||||
|
option.isVoted = this.fnCalcIsVoted()
|
||||||
|
option.checked = this.fnCalcIsChecked(option)
|
||||||
|
option._uh_percent = calcVotePercent(tempVoteRes.vote, option);
|
||||||
|
option.dataStr = JSON.stringify(option)
|
||||||
|
|
||||||
|
return option
|
||||||
|
})
|
||||||
|
|
||||||
|
this.vote = tempVoteRes.vote
|
||||||
|
this.detail = tempVoteRes;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loading = 'success';
|
||||||
|
}, 200);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
this.loading = 'error';
|
||||||
|
this.loadingText = "投票内容加载失败,点击重试"
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
fnCalcIsVoted() {
|
||||||
|
return voteCacheUtil.has(this.voteId)
|
||||||
|
},
|
||||||
|
fnCalcIsChecked(option) {
|
||||||
|
const data = voteCacheUtil.get(this.voteId)
|
||||||
|
if (!data) return false;
|
||||||
|
const checked = data.selected.includes(option.id)
|
||||||
|
return checked
|
||||||
|
},
|
||||||
|
formatJsonStr(jsonStr) {
|
||||||
|
return jsonStr ? JSON.parse(jsonStr) : {}
|
||||||
|
},
|
||||||
|
handleSubmitTip(text) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: "none",
|
||||||
|
title: text
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSubmit() {
|
||||||
|
if (!this.vote.spec.canAnonymously) {
|
||||||
|
uni.showModal({
|
||||||
|
icon: "none",
|
||||||
|
title: "提示",
|
||||||
|
content: "该投票不支持匿名,请到博主的 网站端 进行投票!",
|
||||||
|
cancelColor: "#666666",
|
||||||
|
cancelText: "关闭",
|
||||||
|
confirmText: "复制地址",
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
console.log("this.article", this.article)
|
||||||
|
const articleUrl = this.$baseApiUrl + (this.article?.status?.permalink ?? "")
|
||||||
|
this.$utils.copyText(articleUrl, "原文地址复制成功")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showLoading({
|
||||||
|
title: "正在保存..."
|
||||||
|
})
|
||||||
|
this.submitLoading = true
|
||||||
|
this.$httpApi.v2
|
||||||
|
.submitVote(this.voteId, this.submitForm, this.vote.spec.canAnonymously)
|
||||||
|
.then(res => {
|
||||||
|
uni.showToast({
|
||||||
|
icon: "none",
|
||||||
|
title: "提交成功"
|
||||||
|
})
|
||||||
|
|
||||||
|
voteCacheUtil.set(this.voteId, {
|
||||||
|
selected: [...this.submitForm.voteData],
|
||||||
|
data: this.vote
|
||||||
|
})
|
||||||
|
|
||||||
|
this.fnGetData()
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
uni.showToast({
|
||||||
|
icon: "none",
|
||||||
|
title: "提交失败,请重试"
|
||||||
|
})
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSelectSingleOption(option) {
|
||||||
|
if (this.vote.spec._uh_state.state == '未开始') {
|
||||||
|
this.showToast(`投票未开始`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.vote.spec.hasEnded) return
|
||||||
|
if (this.vote.spec.disabled) return
|
||||||
|
this.vote.spec.options.map(item => {
|
||||||
|
if (option.id == item.id) {
|
||||||
|
item.checked = true
|
||||||
|
} else {
|
||||||
|
item.checked = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.submitForm.voteData = this.vote.spec.options.filter(x => x.checked).map(item => item.id)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSelectCheckboxOption(option) {
|
||||||
|
if (this.vote.spec._uh_state.state == '未开始') {
|
||||||
|
this.showToast(`投票未开始`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.vote.spec.hasEnded) return
|
||||||
|
if (this.vote.spec.disabled) return
|
||||||
|
|
||||||
|
const checkedList = this.vote.spec.options.filter(x => x.checked && x.id != option.id)
|
||||||
|
|
||||||
|
if (this.vote.spec.type === 'multiple' && checkedList.length >= this.vote.spec.maxVotes) {
|
||||||
|
this.showToast(`最多选择 ${this.vote.spec.maxVotes} 项`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.vote.spec.options.map(item => {
|
||||||
|
if (option.id == item.id) {
|
||||||
|
item.checked = !item.checked
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.submitForm.voteData = this.vote.spec.options.filter(x => x.checked).map(item => item.id)
|
||||||
|
},
|
||||||
|
handleToVoteDetail(vote) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pagesA/vote-detail/vote-detail?name=${vote.metadata.name}`
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showToast(content) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: "none",
|
||||||
|
title: content,
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.w-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wp-50 {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #ffff;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
padding: 0;
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: #e88080;
|
||||||
|
color: #e88080;
|
||||||
|
background-color: rgba(232, 128, 128, 0.075);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
padding: 0;
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: rgba(3, 174, 252, 1);
|
||||||
|
color: rgba(3, 174, 252, 1);
|
||||||
|
background-color: rgba(3, 174, 252, 0.075);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.vote-error {
|
||||||
|
padding: 50rpx 24rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card-head {
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding: 12rpx 0;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card-body {
|
||||||
|
|
||||||
|
.remark {
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card-foot {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-top: 6px;
|
||||||
|
margin-top: 12px;
|
||||||
|
border-top: 2rpx solid #eee;
|
||||||
|
|
||||||
|
.left {}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-voted-item {
|
||||||
|
min-height: 72rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: rgba(229, 229, 229, 0.75);
|
||||||
|
font-size: 24rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
width: var(--percent);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(208, 208, 208, 1);
|
||||||
|
z-index: 0;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: rgba(3, 169, 244, 0.35);
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background-color: rgba(3, 169, 244, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-voted-item-content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 72rpx;
|
||||||
|
padding: 12rpx 24rpx;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-select-option {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
transition: all 0.1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single {
|
||||||
|
::v-deep {}
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiple {
|
||||||
|
::v-deep {}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pk {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 12rpx;
|
||||||
|
|
||||||
|
::v-deep {
|
||||||
|
|
||||||
|
.pk-container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-item {
|
||||||
|
flex-grow: 1;
|
||||||
|
min-width: 30% !important;
|
||||||
|
max-width: 70% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-left {}
|
||||||
|
|
||||||
|
.radio-right {}
|
||||||
|
|
||||||
|
.option-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-item-left {
|
||||||
|
background: linear-gradient(90deg, #3B82F6, #60A5FA);
|
||||||
|
color: white;
|
||||||
|
clip-path: polygon(0 0, calc(100% - 40rpx) 0, 100% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-item-right {
|
||||||
|
background: linear-gradient(90deg, #F87171, #EF4444);
|
||||||
|
color: white;
|
||||||
|
clip-path: polygon(0 0, 100% 0, 100% 100%, 40rpx 100%);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-foot {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 6rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-right: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 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>
|
|
||||||
@@ -16,8 +16,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import imageCache from '@/utils/imageCache.js';
|
import imageCache from '@/utils/imageCache.js';
|
||||||
export default {
|
|
||||||
|
export default {
|
||||||
name: 'cache-image',
|
name: 'cache-image',
|
||||||
props: {
|
props: {
|
||||||
url: {
|
url: {
|
||||||
@@ -30,7 +31,7 @@
|
|||||||
},
|
},
|
||||||
loadStyle: {
|
loadStyle: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default () {
|
default() {
|
||||||
return {
|
return {
|
||||||
backgroundColor: '#ffffff',
|
backgroundColor: '#ffffff',
|
||||||
color: '#333'
|
color: '#333'
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
},
|
},
|
||||||
loadErrStyle: {
|
loadErrStyle: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default () {
|
default() {
|
||||||
return {
|
return {
|
||||||
color: 'rgba(244, 67, 54,1)'
|
color: 'rgba(244, 67, 54,1)'
|
||||||
// backgroundColor: 'rgba(244, 67, 54,0.2)'
|
// backgroundColor: 'rgba(244, 67, 54,0.2)'
|
||||||
@@ -64,7 +65,7 @@
|
|||||||
},
|
},
|
||||||
styles: {
|
styles: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default () {
|
default() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -96,10 +97,10 @@
|
|||||||
return this.$tm.vx.getters().getConfigs.imagesConfig
|
return this.$tm.vx.getters().getConfigs.imagesConfig
|
||||||
},
|
},
|
||||||
loadingImgSrc() {
|
loadingImgSrc() {
|
||||||
return this.imagesConfig.loadingGifUrl;
|
return this.$utils.checkImageUrl(this.imagesConfig.loadingGifUrl);
|
||||||
},
|
},
|
||||||
loadingErrorImageSrc() {
|
loadingErrorImageSrc() {
|
||||||
return this.imagesConfig.loadingErrUrl
|
return this.$utils.checkImageUrl(this.imagesConfig.loadingErrUrl)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -142,50 +143,50 @@
|
|||||||
this.loadStatus = 'error';
|
this.loadStatus = 'error';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.cache-image-wrap {
|
.cache-image-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-loading,
|
.img-loading,
|
||||||
.img-error {
|
.img-error {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-load-icon {
|
.img-load-icon {
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
animation: xhRote 0.8s infinite linear;
|
animation: xhRote 0.8s infinite linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-load-text {
|
.img-load-text {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
margin-top: 8rpx;
|
margin-top: 8rpx;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-error {
|
.img-error {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
|
|
||||||
&-img {
|
&-img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-err-icon {
|
.img-err-icon {
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes xhRote {
|
@keyframes xhRote {
|
||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
@@ -193,5 +194,5 @@
|
|||||||
100% {
|
100% {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
backdrop-filter: blur(3rpx);
|
backdrop-filter: blur(3rpx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
<text>评论列表</text>
|
<text>评论列表</text>
|
||||||
<text class="count">({{ (result && result.total) || 0 }}条)</text>
|
<text class="count">({{ (result && result.total) || 0 }}条)</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="refresh">
|
||||||
|
<text class="icon iconfont icon-sync-alt text-weight-s text-grey-darken-1"></text>
|
||||||
|
<text class="text-weight-s text-grey-darken-1 ml-6" style="font-size: 26rpx;" @click="fnGetData">刷新</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<view class="comment-list_content">
|
<view class="comment-list_content">
|
||||||
@@ -30,8 +34,7 @@
|
|||||||
<text class="text-red text-size-s">- 文章已开启禁止评论 -</text>
|
<text class="text-red text-size-s">- 文章已开启禁止评论 -</text>
|
||||||
</tm-empty>
|
</tm-empty>
|
||||||
<tm-empty v-else icon="icon-shiliangzhinengduixiang-" label="暂无评论">
|
<tm-empty v-else icon="icon-shiliangzhinengduixiang-" label="暂无评论">
|
||||||
<tm-button theme="light-blue" :dense="true" :shadow="0" size="m"
|
<tm-button theme="light-blue" :dense="true" :shadow="0" size="m" @click="fnToComment(null)">抢沙发
|
||||||
@click="fnToComment(null)">抢沙发
|
|
||||||
</tm-button>
|
</tm-button>
|
||||||
</tm-empty>
|
</tm-empty>
|
||||||
</view>
|
</view>
|
||||||
@@ -146,11 +149,9 @@ export default {
|
|||||||
return uni.$tm.toast('文章已禁止评论!');
|
return uni.$tm.toast('文章已禁止评论!');
|
||||||
}
|
}
|
||||||
let _comment = {};
|
let _comment = {};
|
||||||
|
console.log('data', data)
|
||||||
if (data) {
|
if (data) {
|
||||||
let {
|
let {type, comment} = data;
|
||||||
type,
|
|
||||||
comment
|
|
||||||
} = data;
|
|
||||||
// 来自用户
|
// 来自用户
|
||||||
_comment = {
|
_comment = {
|
||||||
isComment: false,
|
isComment: false,
|
||||||
@@ -165,16 +166,14 @@ export default {
|
|||||||
_comment = {
|
_comment = {
|
||||||
isComment: true,
|
isComment: true,
|
||||||
postName: this.post.metadata.name,
|
postName: this.post.metadata.name,
|
||||||
title: '评论文章:' + this.post.spec.title,
|
title: '新增评论',
|
||||||
formPage: 'comment_list',
|
formPage: 'comment_list',
|
||||||
from: 'posts',
|
from: 'posts',
|
||||||
type: 'post'
|
type: 'post'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit("on-comment", {
|
this.$emit("on-comment", _comment)
|
||||||
_comment
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
fnCopyContent(content) {
|
fnCopyContent(content) {
|
||||||
uni.$tm.u.setClipboardData(content);
|
uni.$tm.u.setClipboardData(content);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<tm-poup v-model="isShow" position="bottom" height="71vh" @change="handleOnChange">
|
<tm-poup v-model="isShow" position="bottom" height="71vh" @change="handleOnChange">
|
||||||
<view class="title text-align-center">{{ calcTitle }}</view>
|
<view class="title text-align-center">{{ calcTitle }}</view>
|
||||||
<tm-form @submit="fnOnSubmit">
|
<tm-form @submit="fnOnSubmit">
|
||||||
<tm-input :auto-focus="true" name="content" :vertical="true" required :height="220" input-type="textarea"
|
<tm-input name="content" :vertical="true" required :height="220" input-type="textarea"
|
||||||
bg-color="grey-lighten-5" :maxlength="200" :borderBottom="false" placeholder="请输入内容,不超过200字符..."
|
bg-color="grey-lighten-5" :maxlength="200" :borderBottom="false" placeholder="请输入内容,不超过200字符..."
|
||||||
v-model="form.content"></tm-input>
|
v-model="form.content"></tm-input>
|
||||||
<tm-input name="author" align="right" required title="我的昵称" placeholder="请输入您的昵称..."
|
<tm-input name="author" align="right" required title="我的昵称" placeholder="请输入您的昵称..."
|
||||||
@@ -169,7 +169,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.$httpApi.v2.addPostComment(commentForm)
|
this.$httpApi.v2.addPostComment(commentForm)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
uni.$tm.toast('评论成功!');
|
uni.$tm.toast('评论成功,可能需要审核!');
|
||||||
// 更新评论者信息
|
// 更新评论者信息
|
||||||
this.handleSetVisitor();
|
this.handleSetVisitor();
|
||||||
this.handleClose(true)
|
this.handleClose(true)
|
||||||
@@ -196,7 +196,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.$httpApi.v2.addPostCommentReply(this.form.postName, replyForm)
|
this.$httpApi.v2.addPostCommentReply(this.form.postName, replyForm)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
uni.$tm.toast('回复成功!');
|
uni.$tm.toast('回复成功,可能需要审核!');
|
||||||
// 更新评论者信息
|
// 更新评论者信息
|
||||||
this.handleSetVisitor();
|
this.handleSetVisitor();
|
||||||
this.handleClose(true)
|
this.handleClose(true)
|
||||||
@@ -208,8 +208,9 @@ export default {
|
|||||||
},
|
},
|
||||||
handleOnChange(isOpen) {
|
handleOnChange(isOpen) {
|
||||||
this.isShow = isOpen;
|
this.isShow = isOpen;
|
||||||
if (!isOpen) {
|
if(!isOpen){
|
||||||
this.$emit("on-close", {
|
this.$emit("on-close", {
|
||||||
|
isSubmit:false,
|
||||||
refresh: false
|
refresh: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -217,6 +218,7 @@ export default {
|
|||||||
handleClose(refresh = false) {
|
handleClose(refresh = false) {
|
||||||
this.isShow = false;
|
this.isShow = false;
|
||||||
this.$emit("on-close", {
|
this.$emit("on-close", {
|
||||||
|
isSubmit:true,
|
||||||
refresh: refresh
|
refresh: refresh
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,8 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 20rpx 24rpx;
|
padding: 20rpx 24rpx;
|
||||||
|
background-color: rgba(0, 0, 0, 0.085);
|
||||||
|
backdrop-filter: blur(3rpx);
|
||||||
&.no-dot {
|
&.no-dot {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="right-hot-ttf">
|
<view class="right-hot-ttf">
|
||||||
<text class="text hot-text">{{ title }}</text>
|
<text class="text hot-text text-overflow-2">{{ title }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -63,10 +63,10 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- 标题 -->
|
<!-- 标题 -->
|
||||||
<view class="Top-Title">
|
<view class="Top-Title">
|
||||||
<text class="text title-text">{{ item.title }}</text>
|
<text class="text title-text text-overflow-2">{{ item.title }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 用户信息 -->
|
<!-- 用户信息 -->
|
||||||
<view class="Bottom-UserInfo">
|
<view v-if="false" class="Bottom-UserInfo">
|
||||||
<!-- 头像 -->
|
<!-- 头像 -->
|
||||||
<view class="UserImage-box">
|
<view class="UserImage-box">
|
||||||
<image :src="item.avatar" class="Image" mode="aspectFill"></image>
|
<image :src="item.avatar" class="Image" mode="aspectFill"></image>
|
||||||
|
|||||||
@@ -0,0 +1,479 @@
|
|||||||
|
<template>
|
||||||
|
<view class="contribution-heatmap pa-24">
|
||||||
|
<view class="header">
|
||||||
|
<view class="title">
|
||||||
|
<view v-if="false" style="color: #7e7e7f">数据范围:</view>
|
||||||
|
<view>{{ dataRangeYears }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="controls">
|
||||||
|
<tm-stepper v-model="currentYear" :width="220" :height="48" :min="0" :max="2099" :shadow="0" :round="2" color="light-blue" @change="changeYear"></tm-stepper>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="heatmap-container">
|
||||||
|
<view class="weeks">
|
||||||
|
<view class="week-label" v-for="week in weeks" :key="week">{{ week }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="heatmap-content">
|
||||||
|
<view class="months" :style="[calcContentStyle]">
|
||||||
|
<view class="month-label" v-for="month in monthLabels" :key="month.index">
|
||||||
|
{{ month.name }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="days-container" :style="[calcContentStyle]">
|
||||||
|
<view
|
||||||
|
v-for="(day, index) in displayDays"
|
||||||
|
:key="index"
|
||||||
|
class="day-cell"
|
||||||
|
:style="{ backgroundColor: getDayColor(day) }"
|
||||||
|
@click="handleDayClick(day, index)"
|
||||||
|
></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="footer">
|
||||||
|
<view class="releases-count">
|
||||||
|
<text>累计 {{ calcAllYearCount }} 篇</text>
|
||||||
|
<text>丨</text>
|
||||||
|
<text>本年 {{ calcCurrentYearCount }} 篇</text>
|
||||||
|
</view>
|
||||||
|
<view class="legend">
|
||||||
|
<text class="legend-text">少</text>
|
||||||
|
<view v-for="(color, index) in intensityColors" :key="index" class="day-cell legend-day-cell" :style="{ backgroundColor: color }"></view>
|
||||||
|
<text class="legend-text">多</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmStepper from '@/tm-vuetify/components/tm-stepper/tm-stepper.vue';
|
||||||
|
export default {
|
||||||
|
name: 'Heatmap',
|
||||||
|
components: {
|
||||||
|
tmStepper
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
year: {
|
||||||
|
type: Number,
|
||||||
|
default: () => new Date().getFullYear()
|
||||||
|
},
|
||||||
|
chartData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
weeks: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||||
|
intensityColors: [
|
||||||
|
'#ebedf0', // 无贡献
|
||||||
|
'#dbeafe', // 1-2次
|
||||||
|
'#93c5fd', // 3-4次
|
||||||
|
'#3b82f6', // 5-6次
|
||||||
|
'#1e40af' // 7次以上
|
||||||
|
],
|
||||||
|
displayDays: [],
|
||||||
|
showTooltip: null,
|
||||||
|
currentYear: '1900',
|
||||||
|
currentYearData: [],
|
||||||
|
yearList:[]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dataRangeYears() {
|
||||||
|
const arr = this.chartData;
|
||||||
|
const dateField = 'name';
|
||||||
|
|
||||||
|
if (!arr || !Array.isArray(arr) || arr.length === 0) {
|
||||||
|
return { minDate: null, maxDate: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取所有有效日期
|
||||||
|
const validDates = arr
|
||||||
|
.map((item) => {
|
||||||
|
if (item && item[dateField]) {
|
||||||
|
const date = new Date(item[dateField]);
|
||||||
|
return isNaN(date.getTime()) ? null : date;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((date) => date !== null);
|
||||||
|
|
||||||
|
if (validDates.length === 0) {
|
||||||
|
return { minDate: null, maxDate: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到最小和最大日期
|
||||||
|
const minDate = new Date(Math.min(...validDates.map((date) => date.getTime())));
|
||||||
|
const maxDate = new Date(Math.max(...validDates.map((date) => date.getTime())));
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
minDate: this.formatDate(minDate),
|
||||||
|
maxDate: this.formatDate(maxDate)
|
||||||
|
};
|
||||||
|
|
||||||
|
return `${result.minDate} 至 ${result.maxDate}`;
|
||||||
|
},
|
||||||
|
// 计算内容宽度
|
||||||
|
calcContentStyle() {
|
||||||
|
const rowCount = Math.ceil(this.displayDays.length / 7);
|
||||||
|
const singleWidth = 36;
|
||||||
|
return {
|
||||||
|
width: rowCount * singleWidth + 'rpx'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
//累计的发文次数
|
||||||
|
calcAllYearCount() {
|
||||||
|
return this.chartData.reduce((acc, cur) => {
|
||||||
|
return acc + cur.total;
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
// 统计当前年累计的发文次数
|
||||||
|
calcCurrentYearCount() {
|
||||||
|
return this.currentYearData.reduce((acc, cur) => {
|
||||||
|
return acc + cur.total;
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
monthLabels() {
|
||||||
|
const months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
|
||||||
|
const labels = [];
|
||||||
|
|
||||||
|
if (this.displayDays.length === 0) return labels;
|
||||||
|
|
||||||
|
// 计算每个格子的总宽度(包括间距)
|
||||||
|
const cellTotalWidth = 15 + 1.5 * 2; // width + margin * 2
|
||||||
|
|
||||||
|
let currentMonth = -1;
|
||||||
|
let monthStartWeek = 0;
|
||||||
|
|
||||||
|
// 遍历所有周
|
||||||
|
const totalWeeks = Math.ceil(this.displayDays.length / 7);
|
||||||
|
|
||||||
|
for (let week = 0; week < totalWeeks; week++) {
|
||||||
|
// 找到这一周的第一个有效日期
|
||||||
|
let weekMonth = -1;
|
||||||
|
for (let day = 0; day < 7; day++) {
|
||||||
|
const dayIndex = week * 7 + day;
|
||||||
|
if (dayIndex < this.displayDays.length && this.displayDays[dayIndex].date) {
|
||||||
|
weekMonth = this.displayDays[dayIndex].date.getMonth();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果找到了有效月份
|
||||||
|
if (weekMonth !== -1) {
|
||||||
|
// 如果是新的月份
|
||||||
|
if (weekMonth !== currentMonth) {
|
||||||
|
// 如果不是第一个月份,先计算前一个月份的宽度
|
||||||
|
if (currentMonth !== -1) {
|
||||||
|
const weeksInMonth = week - monthStartWeek;
|
||||||
|
const width = weeksInMonth * cellTotalWidth;
|
||||||
|
labels.push({
|
||||||
|
name: months[currentMonth],
|
||||||
|
width: width,
|
||||||
|
marginLeft: monthStartWeek === 0 ? 0 : 2,
|
||||||
|
index: currentMonth
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMonth = weekMonth;
|
||||||
|
monthStartWeek = week;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理最后一个月份
|
||||||
|
if (currentMonth !== -1) {
|
||||||
|
const weeksInMonth = totalWeeks - monthStartWeek;
|
||||||
|
const width = weeksInMonth * cellTotalWidth;
|
||||||
|
labels.push({
|
||||||
|
name: months[currentMonth],
|
||||||
|
width: width,
|
||||||
|
marginLeft: monthStartWeek === 0 ? 0 : 2,
|
||||||
|
index: currentMonth
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
year: {
|
||||||
|
immediate: true,
|
||||||
|
handler(newYear) {
|
||||||
|
this.generateDisplayData(newYear);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
chartData: {
|
||||||
|
deep: true,
|
||||||
|
handler(newData) {
|
||||||
|
this.mergeContributionData(newData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initYearList()
|
||||||
|
this.filterAndSetCurrentYearData(this.year);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initYearList(){
|
||||||
|
for (var index = 1900; index < 2099; index++) {
|
||||||
|
this.yearList.push(index)
|
||||||
|
}
|
||||||
|
this.yearList = this.yearList.reverse();
|
||||||
|
},
|
||||||
|
filterAndSetCurrentYearData(year) {
|
||||||
|
this.currentYear = year;
|
||||||
|
this.currentYearData = this.chartData.filter((x) => x.name.includes(this.currentYear));
|
||||||
|
this.generateDisplayData(year);
|
||||||
|
},
|
||||||
|
generateDisplayData(year) {
|
||||||
|
const startDate = new Date(year, 0, 1);
|
||||||
|
const endDate = new Date(year, 11, 31);
|
||||||
|
const days = [];
|
||||||
|
|
||||||
|
// 计算第一周的偏移量(确保周一开始)
|
||||||
|
let firstDay = startDate.getDay();
|
||||||
|
firstDay = firstDay === 0 ? 6 : firstDay - 1; // 将周日(0)转换为6,周一(1)转换为0
|
||||||
|
|
||||||
|
// 添加空白填充,使第一周从周一开始
|
||||||
|
for (let i = 0; i < firstDay; i++) {
|
||||||
|
days.push({ date: null, count: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成一年的日期
|
||||||
|
let currentDate = new Date(startDate);
|
||||||
|
while (currentDate <= endDate) {
|
||||||
|
// 默认无贡献
|
||||||
|
days.push({
|
||||||
|
date: new Date(currentDate),
|
||||||
|
count: 0
|
||||||
|
});
|
||||||
|
currentDate.setDate(currentDate.getDate() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.displayDays = days;
|
||||||
|
this.mergeContributionData(this.currentYearData);
|
||||||
|
},
|
||||||
|
mergeContributionData(contributionData) {
|
||||||
|
if (!contributionData || contributionData.length === 0) return;
|
||||||
|
|
||||||
|
// 将传入的数据合并到显示数据中
|
||||||
|
const dataMap = new Map();
|
||||||
|
contributionData.forEach((item) => {
|
||||||
|
const dateStr = this.formatDate(item.date);
|
||||||
|
dataMap.set(dateStr, item.total);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.displayDays.forEach((day) => {
|
||||||
|
if (day.date) {
|
||||||
|
const dateStr = this.formatDate(day.date);
|
||||||
|
if (dataMap.has(dateStr)) {
|
||||||
|
day.count = dataMap.get(dateStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatDate(date) {
|
||||||
|
date = new Date(date);
|
||||||
|
// 将日期格式化为 YYYY-MM-DD
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
},
|
||||||
|
formatDisplayDate(date) {
|
||||||
|
// 将日期格式化为中文显示格式
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = date.getMonth() + 1;
|
||||||
|
const day = date.getDate();
|
||||||
|
return `${year}年${month}月${day}日`;
|
||||||
|
},
|
||||||
|
getDayColor(day) {
|
||||||
|
if (day.count === 0) return this.intensityColors[0];
|
||||||
|
if (day.count <= 2) return this.intensityColors[1];
|
||||||
|
if (day.count <= 4) return this.intensityColors[2];
|
||||||
|
if (day.count <= 6) return this.intensityColors[3];
|
||||||
|
return this.intensityColors[4];
|
||||||
|
},
|
||||||
|
getTooltipText(day) {
|
||||||
|
if (!day.date) return '无数据';
|
||||||
|
|
||||||
|
const dateStr = this.formatDisplayDate(day.date);
|
||||||
|
if (day.count === 0) {
|
||||||
|
return `${dateStr}: 发布 0 篇文章`;
|
||||||
|
} else {
|
||||||
|
return `${dateStr}: 发布 ${day.count} 篇文章`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDayClick(day, index) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: this.getTooltipText(day)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
changeYear(value) {
|
||||||
|
this.filterAndSetCurrentYearData(value);
|
||||||
|
this.$emit('year-change', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.contribution-heatmap {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.12), 0 2rpx 4rpx rgba(0, 0, 0, 0.24);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #000;
|
||||||
|
border: 2rpx solid #ebedf0;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
padding: 8rpx 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-selector {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #0969da;
|
||||||
|
font-size: 28rpx;
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-btn:hover {
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-display {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
min-width: 120rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border: 2rpx solid #ebedf0;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
padding: 8rpx 3rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-text {
|
||||||
|
margin: 0 10rpx;
|
||||||
|
}
|
||||||
|
.legend-day-cell {
|
||||||
|
width: 24rpx!important;
|
||||||
|
height: 24rpx!important;
|
||||||
|
}
|
||||||
|
.heatmap-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weeks {
|
||||||
|
margin-top: -2rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
padding-top: 48rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.week-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #7e7e7f;
|
||||||
|
height: 30rpx;
|
||||||
|
line-height: 30rpx;
|
||||||
|
margin-bottom: 6rpx;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.months {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.month-label {
|
||||||
|
flex: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #7e7e7f;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heatmap-content {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.days-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 252rpx; /* 7行 * 30rpx + 7行 * 6rpx间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-cell {
|
||||||
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
margin: 3rpx;
|
||||||
|
background-color: #ebedf0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #586069;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.releases-count{
|
||||||
|
display: flex;
|
||||||
|
gap: 0 6rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 2rpx solid #ebedf0;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
padding: 8rpx 12rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,159 +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().getConfigs.authorConfig.blogger;
|
|
||||||
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>
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<tm-poup v-model="isShow" width="80vw" height="800rpx" position="center" :round="6" @change="fnOnChange">
|
||||||
|
<view class="fulled pa-4">
|
||||||
|
<view class="mt-24 fulled text-weight-b text-size-sm text-align-center text-overflow-2 pb-24">{{ title }}</view>
|
||||||
|
<view class="fulled mt-2">
|
||||||
|
<scroll-view class="fulled" scroll-y :style="{
|
||||||
|
height: url? '600rpx': '680rpx'
|
||||||
|
}">
|
||||||
|
<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="content" :markdown="true" :showLineNumber="true" :showLanguageName="true"
|
||||||
|
:copyByLongPress="true"/>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
<view v-if="url" class="fulled mt-12 flex flex-center">
|
||||||
|
<tm-button theme="bg-gradient-light-blue-accent" size="m" @click="fnToWebview()">
|
||||||
|
点击跳转内容链接
|
||||||
|
</tm-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</tm-poup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
||||||
|
|
||||||
|
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'notify-dialog',
|
||||||
|
components: {
|
||||||
|
tmPoup,
|
||||||
|
tmButton,
|
||||||
|
mpHtml
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isShow: false,
|
||||||
|
markdownConfig: MarkdownConfig,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.isShow = this.show;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnToWebview() {
|
||||||
|
if (this.$utils.checkIsUrl(this.url)) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pagesC/website/website?data=' +
|
||||||
|
JSON.stringify({
|
||||||
|
title: this.title,
|
||||||
|
url: encodeURIComponent(this.url)
|
||||||
|
}),
|
||||||
|
success: () => {
|
||||||
|
this.fnOnChange(false)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fnOnChange(e) {
|
||||||
|
this.isShow = false;
|
||||||
|
this.$emit('on-change', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<view v-if="pluginInfo" class="plugin-unavailable flex flex-col flex-center"
|
||||||
|
:class="{border:useBorder,'decoration':useDecoration}" :style="[calcCustomStyle]">
|
||||||
|
<!-- 图标 -->
|
||||||
|
<image class="plugin-logo" :src="pluginInfo.logo" mode="scaleToFill"></image>
|
||||||
|
<!-- 名称 -->
|
||||||
|
<view class="plugin-name">
|
||||||
|
{{ pluginInfo.name }}
|
||||||
|
</view>
|
||||||
|
<view class="plugin-error">
|
||||||
|
未安装/启用插件
|
||||||
|
</view>
|
||||||
|
<!-- 描述 -->
|
||||||
|
<view class="plugin-desc">
|
||||||
|
{{ pluginInfo.desc }}
|
||||||
|
</view>
|
||||||
|
<view v-if="errorText" class="plugin-tip">
|
||||||
|
{{errorText}}
|
||||||
|
</view>
|
||||||
|
<!-- 插件地址 -->
|
||||||
|
<view class="plugin-url">
|
||||||
|
插件地址:{{ pluginInfo.url }}
|
||||||
|
</view>
|
||||||
|
<!-- 反馈按钮/复制地址 -->
|
||||||
|
<view class="plugin-btns w-full flex-center">
|
||||||
|
<!-- #ifndef MP-WEIXIN -->
|
||||||
|
<tm-button theme="light-blue" :block="true" class="w-full" :height="70" @click="copy">复制地址</tm-button>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<tm-button theme="light-blue" plan text :height="70" @click="copy">复制地址</tm-button>
|
||||||
|
<tm-button theme="light-blue" open-type="contact" :height="70">提交反馈</tm-button>
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="plugin-copyright">
|
||||||
|
提示:请确保 Halo 博客已安装相关插件
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
NeedPluginIds,
|
||||||
|
NeedPlugins,
|
||||||
|
CheckNeedPluginAvailable
|
||||||
|
} from "@/utils/plugin.js"
|
||||||
|
import utils from '@/utils/index.js'
|
||||||
|
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "plugin-unavailable",
|
||||||
|
components: {
|
||||||
|
tmButton
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
pluginId: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
errorText: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
useDecoration: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
useBorder: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
customStyle: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
NeedPluginIds,
|
||||||
|
NeedPlugins,
|
||||||
|
pluginInfo: null,
|
||||||
|
defaultStyle: {
|
||||||
|
width: "80vw",
|
||||||
|
borderRadius: "24rpx"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
calcCustomStyle() {
|
||||||
|
const style = this.customStyle ?? {}
|
||||||
|
return {
|
||||||
|
...this.defaultStyle,
|
||||||
|
...style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
pluginId: {
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
handler(newVal, oldVal) {
|
||||||
|
this.pluginInfo = NeedPlugins.get(newVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
copy() {
|
||||||
|
utils.copyText(this.pluginInfo.url, "插件地址已复制")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.plugin-unavailable {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: auto;
|
||||||
|
padding: 100rpx 36rpx;
|
||||||
|
gap: 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
|
||||||
|
&.border {
|
||||||
|
// border: 2rpx solid #E2E8F0;
|
||||||
|
border: 2rpx solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.decoration {
|
||||||
|
background-color: rgba(255, 255, 255, 0.95);
|
||||||
|
box-shadow: 0 0 12rpx rgba(226, 232, 240, 0.35);
|
||||||
|
backdrop-filter: blur(6rpx);
|
||||||
|
border-top: 12rpx solid rgba(3, 169, 244, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-logo {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-name {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
font-size: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-error {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 6rpx 16rpx;
|
||||||
|
border-radius: 36rpx;
|
||||||
|
background-color: rgba(255, 61, 49, 0.075);
|
||||||
|
color: rgba(255, 61, 49, 1);
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-tip {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10rpx 20rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
border: 2rpx dashed #f2c97d;
|
||||||
|
color: #f0a020;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-desc {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 60vw;
|
||||||
|
font-size: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-url {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #F1F5F9;
|
||||||
|
color: #666;
|
||||||
|
font-size: 24rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-btns {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-copyright {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #999;
|
||||||
|
transform: scale(0.9) translateY(20px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -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>
|
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'restrict-read-skeleton',
|
||||||
|
props: {
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
type: String,
|
||||||
|
default: '刷新'
|
||||||
|
},
|
||||||
|
buttonColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#07c160'
|
||||||
|
},
|
||||||
|
buttonSize: {
|
||||||
|
type: String,
|
||||||
|
default: 'normal', // 'small', 'normal', 'large'
|
||||||
|
validator: value => ['small', 'normal', 'large'].includes(value)
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
type: Number,
|
||||||
|
default: 4,
|
||||||
|
validator: value => value >= 1 && value <= 6
|
||||||
|
},
|
||||||
|
skeletonColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#f5f5f5'
|
||||||
|
},
|
||||||
|
skeletonHighlight: {
|
||||||
|
type: String,
|
||||||
|
default: '#e8e8e8'
|
||||||
|
},
|
||||||
|
animationDuration: {
|
||||||
|
type: Number,
|
||||||
|
default: 1.5
|
||||||
|
},
|
||||||
|
showButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
tipText: {
|
||||||
|
type: String,
|
||||||
|
default: '' // 默认不显示提示文字
|
||||||
|
},
|
||||||
|
tipColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#666666' // 提示文字颜色
|
||||||
|
},
|
||||||
|
tipSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 24 // 提示文字大小,单位rpx
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleRefresh() {
|
||||||
|
this.$emit('refresh');
|
||||||
|
},
|
||||||
|
onTouchStart() {
|
||||||
|
this.$emit('touchstart');
|
||||||
|
},
|
||||||
|
onTouchEnd() {
|
||||||
|
this.$emit('touchend');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 骨架屏容器 -->
|
||||||
|
<view class="container">
|
||||||
|
<!-- 骨架屏内容 -->
|
||||||
|
<view class="skeleton" v-if="loading">
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<view class="skeleton-body">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in Array(lines).fill(0)"
|
||||||
|
:key="index"
|
||||||
|
class="skeleton-line"
|
||||||
|
:class="{
|
||||||
|
'short': index === lines - 2,
|
||||||
|
'shorter': index === lines - 1
|
||||||
|
}"
|
||||||
|
:style="{
|
||||||
|
background: `linear-gradient(90deg, ${skeletonColor} 25%, ${skeletonHighlight} 50%, ${skeletonColor} 75%)`,
|
||||||
|
animationDuration: `${animationDuration}s`
|
||||||
|
}">
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 实际内容 -->
|
||||||
|
<view v-else>
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提示文字和按钮容器 -->
|
||||||
|
<view v-if="showButton" class="button-container">
|
||||||
|
<!-- 提示文字 -->
|
||||||
|
<text
|
||||||
|
v-if="tipText"
|
||||||
|
class="tip-text"
|
||||||
|
:style="{
|
||||||
|
color: tipColor,
|
||||||
|
fontSize: tipSize + 'rpx'
|
||||||
|
}"
|
||||||
|
>{{ tipText }}</text>
|
||||||
|
|
||||||
|
<!-- 按钮 -->
|
||||||
|
<button
|
||||||
|
class="overlay-button"
|
||||||
|
:class="[buttonSize, { 'button-hover': hover }]"
|
||||||
|
hover-class="none"
|
||||||
|
@touchstart="onTouchStart"
|
||||||
|
@touchend="onTouchEnd"
|
||||||
|
@click="handleRefresh"
|
||||||
|
:style="{ backgroundColor: buttonColor }"
|
||||||
|
>
|
||||||
|
{{ buttonText }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
/* 容器样式 */
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 200rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 骨架屏样式 */
|
||||||
|
.skeleton {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 30rpx;
|
||||||
|
|
||||||
|
&-body {
|
||||||
|
margin: 20rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-line {
|
||||||
|
height: 32rpx;
|
||||||
|
background-size: 400% 100%;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
animation: skeleton-loading 1.5s ease infinite;
|
||||||
|
|
||||||
|
&.short {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.shorter {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮容器样式 */
|
||||||
|
.button-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 提示文字样式 */
|
||||||
|
.tip-text {
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮样式 */
|
||||||
|
.overlay-button {
|
||||||
|
position: relative; // 改为相对定位
|
||||||
|
transform: none; // 移除transform
|
||||||
|
color: white;
|
||||||
|
border-radius: 50rpx;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.95); // 简化active状态的transform
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
height: 100rpx;
|
||||||
|
line-height: 100rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
padding: 0 50rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-hover {
|
||||||
|
transform: translateY(-4rpx); // 简化hover状态的transform
|
||||||
|
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 骨架屏动画 */
|
||||||
|
@keyframes skeleton-loading {
|
||||||
|
0% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</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>
|
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
<template>
|
||||||
|
<view class="vote-card" @click="$emit('on-click',vote)">
|
||||||
|
<view class="vote-card-head flex">
|
||||||
|
<view class="left flex flex-center w-full">
|
||||||
|
<view class="flex-shrink">
|
||||||
|
<tm-tags color="light-blue" :shadow="0" rounded size="s" model="fill">{{ vote.spec._uh_type}}</tm-tags>
|
||||||
|
</view>
|
||||||
|
<view class="title text-overflow">
|
||||||
|
{{ vote.spec.title }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="vote-card-body w-full">
|
||||||
|
<view v-if="vote.spec.remark" class="remark text-overflow-2 text-size-s">
|
||||||
|
{{vote.spec.remark}}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="vote-card-foot flex flex-between">
|
||||||
|
<view class="left flex">
|
||||||
|
<tm-tags :color="vote.spec._uh_state.color" size="s" rounded :shadow="0"
|
||||||
|
model="text">{{vote.spec._uh_state.state}}</tm-tags>
|
||||||
|
|
||||||
|
<tm-tags v-if="vote.spec.isVoted" color="blue" rounded size="s" model="text">已投票</tm-tags>
|
||||||
|
|
||||||
|
<tm-tags v-if="vote.spec.timeLimit==='permanent'" color="grey-darken-2" rounded size="s"
|
||||||
|
model="text">结束时间:永久有效 </tm-tags>
|
||||||
|
<tm-tags v-else color="grey-darken-2" rounded size="s" model="text">
|
||||||
|
<template
|
||||||
|
v-if="vote.spec._uh_state.state=='未开始'">开始时间:{{ {d: vote.spec.startDate, f: 'yyyy-MM-dd HH:mm'} | formatTime }}
|
||||||
|
</template>
|
||||||
|
<template v-else>结束时间:{{ {d: vote.spec.endDate, f: 'yyyy-MM-dd HH:mm'} | formatTime }}
|
||||||
|
</template>
|
||||||
|
</tm-tags>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="false" class="right flex flex-end">
|
||||||
|
<tm-tags v-if="false" color="grey-darken-2" rounded size="s" model="text">{{ vote.stats.voteCount }}
|
||||||
|
人已参与</tm-tags>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmGroupradio from '@/tm-vuetify/components/tm-groupradio/tm-groupradio.vue';
|
||||||
|
import tmRadio from '@/tm-vuetify/components/tm-radio/tm-radio.vue';
|
||||||
|
import tmGroupcheckbox from '@/tm-vuetify/components/tm-groupcheckbox/tm-groupcheckbox.vue';
|
||||||
|
import tmCheckbox from '@/tm-vuetify/components/tm-checkbox/tm-checkbox.vue';
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "VoteCard",
|
||||||
|
options: {
|
||||||
|
virtualHost: true,
|
||||||
|
styleIsolation: 'shared'
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
tmGroupradio,
|
||||||
|
tmRadio,
|
||||||
|
tmGroupcheckbox,
|
||||||
|
tmCheckbox,
|
||||||
|
tmButton,
|
||||||
|
tmTags
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
vote: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
showOptions: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.w-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wp-50 {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 24rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #ffff;
|
||||||
|
box-shadow: 0rpx 2rpx 12rpx rgba(0, 0, 0, 0.035);
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
// border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card-head {
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
.title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card-body {
|
||||||
|
|
||||||
|
.remark {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-top: 0;
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card-foot {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-top: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
border-top: 2rpx solid #F7F7F7;
|
||||||
|
|
||||||
|
.left {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.single {
|
||||||
|
::v-deep {
|
||||||
|
.tm-groupradio {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-checkbox {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
padding: 0 12rpx;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-button-label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiple {
|
||||||
|
::v-deep {
|
||||||
|
.tm-groupcheckbox {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-checkbox {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
padding: 0 12rpx;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-button-label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pk {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 12rpx;
|
||||||
|
|
||||||
|
::v-deep {
|
||||||
|
.tm-groupradio {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-checkbox {
|
||||||
|
flex-grow: 1;
|
||||||
|
min-width: 30% !important;
|
||||||
|
max-width: 70% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-item {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-left {}
|
||||||
|
|
||||||
|
.radio-right {}
|
||||||
|
|
||||||
|
.option-item {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12rpx 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-item-left {
|
||||||
|
background: linear-gradient(90deg, #3B82F6, #60A5FA);
|
||||||
|
color: white;
|
||||||
|
clip-path: polygon(0 0, calc(100% - 40rpx) 0, 100% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-item-right {
|
||||||
|
background: linear-gradient(90deg, #F87171, #EF4444);
|
||||||
|
color: white;
|
||||||
|
clip-path: polygon(0 0, 100% 0, 100% 100%, 40rpx 100%);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-foot {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 6rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-right: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+63
-3
@@ -1,14 +1,55 @@
|
|||||||
// 应用设置存储key值
|
// 应用设置存储key值
|
||||||
export const _AppConfigKey = 'APP_GLOBAL_CONFIGS';
|
export const _AppConfigKey = 'APP_GLOBAL_CONFIGS';
|
||||||
|
export const _AppMockJsonKey = 'APP_GLOBAL_MOCK_JSON';
|
||||||
|
|
||||||
// 默认的应用设置
|
// 默认的应用设置
|
||||||
export const DefaultAppConfigs = {
|
export const DefaultAppConfigs = {
|
||||||
|
basicConfig: {
|
||||||
|
tokenConfig: {
|
||||||
|
personalToken: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
loveConfig: {},
|
loveConfig: {},
|
||||||
imagesConfig: {},
|
imagesConfig: {},
|
||||||
authorConfig: {},
|
authorConfig: {},
|
||||||
appConfig: {},
|
appConfig: {},
|
||||||
pluginConfig: {},
|
pluginConfig: {
|
||||||
adConfig: {},
|
votePlugin: {},
|
||||||
|
toolsPlugin: {},
|
||||||
|
linksPlugin:{},
|
||||||
|
linksSubmitPlugin: {},
|
||||||
|
doubanPlugin: {
|
||||||
|
position: 'bottom'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pageConfig: {
|
||||||
|
homeConfig: {
|
||||||
|
pageTitle: "首页",
|
||||||
|
useCategory: true,
|
||||||
|
bannerConfig: {
|
||||||
|
enabled: true,
|
||||||
|
showTitle: true,
|
||||||
|
showIndicator: true,
|
||||||
|
height: "400rpx",
|
||||||
|
dotPosition: "right",
|
||||||
|
type: "post",
|
||||||
|
list: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
categoryConfig: {
|
||||||
|
type: "list"
|
||||||
|
},
|
||||||
|
momentConfig: {
|
||||||
|
useTagRandomColor: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
auditConfig: {
|
||||||
|
auditModeEnabled: false,
|
||||||
|
auditModeData: {
|
||||||
|
jsonUrl: "",
|
||||||
|
jsonData: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,7 +60,7 @@ export const getAppConfigs = () => {
|
|||||||
if (configs) return JSON.parse(configs)
|
if (configs) return JSON.parse(configs)
|
||||||
|
|
||||||
uni.setStorageSync(_AppConfigKey, JSON.stringify(DefaultAppConfigs))
|
uni.setStorageSync(_AppConfigKey, JSON.stringify(DefaultAppConfigs))
|
||||||
return configs;
|
return DefaultAppConfigs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,3 +69,22 @@ export const getAppConfigs = () => {
|
|||||||
export const setAppConfigs = (configs) => {
|
export const setAppConfigs = (configs) => {
|
||||||
uni.setStorageSync(_AppConfigKey, JSON.stringify(configs))
|
uni.setStorageSync(_AppConfigKey, JSON.stringify(configs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取应用模拟数据
|
||||||
|
*/
|
||||||
|
export const getAppMockJson = () => {
|
||||||
|
let json = uni.getStorageSync(_AppMockJsonKey)
|
||||||
|
if (json) return JSON.parse(json)
|
||||||
|
|
||||||
|
uni.setStorageSync(_AppMockJsonKey, JSON.stringify({}))
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存应用模拟数据
|
||||||
|
*/
|
||||||
|
export const setAppMockJson = (json) => {
|
||||||
|
uni.setStorageSync(_AppMockJsonKey, JSON.stringify(json))
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
/** 配置后台管理员token */
|
|
||||||
const HaloTokenConfig = Object.freeze({
|
|
||||||
|
|
||||||
/** 基础请求域名:你的Halo博客基础域名 */
|
|
||||||
BASE_API: "https://demo.halo.run",
|
|
||||||
// BASE_API: "https://blog.925i.cn",
|
|
||||||
|
|
||||||
|
|
||||||
/** 管理员token */
|
|
||||||
systemToken: ``,
|
|
||||||
/** 匿名用户token */
|
|
||||||
anonymousToken: ``
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
export default HaloTokenConfig;
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/** 应用基础配置 */
|
||||||
|
const UHaloBaseConfig = Object.freeze({
|
||||||
|
|
||||||
|
/** 基础请求域名:你的Halo博客基础域名,注意,域名后面不能带有斜杠【/】 */
|
||||||
|
// BASE_API: "",
|
||||||
|
// 示例:
|
||||||
|
BASE_API: "http://localhost:8090",
|
||||||
|
|
||||||
|
// todo:可能有其他配置
|
||||||
|
})
|
||||||
|
|
||||||
|
export default UHaloBaseConfig;
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
{
|
||||||
|
"home": {
|
||||||
|
"bannerList": [
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"categoryList": [
|
||||||
|
{
|
||||||
|
"title": "uni-halo",
|
||||||
|
"desc": "",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo",
|
||||||
|
"desc": "",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo",
|
||||||
|
"desc": "",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo",
|
||||||
|
"desc": "",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"postList": [
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"category": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"title": "uni-halo",
|
||||||
|
"desc": "",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo",
|
||||||
|
"desc": "",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo",
|
||||||
|
"desc": "",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo",
|
||||||
|
"desc": "",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"moments": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"content": "你好啊,这是我的一条生活记录",
|
||||||
|
"time": "2024-05-25 08:59:00",
|
||||||
|
"images": [
|
||||||
|
"/upload/uni_halo_ad_cover.png",
|
||||||
|
"/upload/uni_halo_ad_cover.png",
|
||||||
|
"/upload/uni_halo_ad_cover.png"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gallery": {
|
||||||
|
"list": [
|
||||||
|
"/upload/uni_halo_ad_cover.png",
|
||||||
|
"/upload/uni_halo_ad_cover.png",
|
||||||
|
"/upload/uni_halo_ad_cover.png",
|
||||||
|
"/upload/uni_halo_ad_cover.png"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"archives": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "uni-halo(2.0)开源啦!!!",
|
||||||
|
"desc": "uni-halo 2.0 已独立兼容 Halo2.x 应用,欢迎小伙伴们体验。",
|
||||||
|
"cover": "/upload/uni_halo_ad_cover.png",
|
||||||
|
"time": "2024-05-25 08:59:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,10 @@
|
|||||||
import App from "./App";
|
import App from "./App";
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import {
|
|
||||||
router,
|
|
||||||
RouterMount
|
|
||||||
} from './router/router.js'
|
|
||||||
Vue.use(router)
|
|
||||||
|
|
||||||
// 挂载全局工具类
|
// 挂载全局工具类
|
||||||
import utils from "./utils/index.js";
|
import utils from "./utils/index.js";
|
||||||
Vue.prototype.$utils = utils;
|
Vue.prototype.$utils = utils;
|
||||||
|
uni.$utils = utils;
|
||||||
|
|
||||||
// 全局统一样式的对话框
|
// 全局统一样式的对话框
|
||||||
import Fy from '@/js_sdk/fy-showModal/index.js'
|
import Fy from '@/js_sdk/fy-showModal/index.js'
|
||||||
@@ -30,28 +26,6 @@ for (let fKey in AppFilters) {
|
|||||||
import tmVuetify from "./tm-vuetify";
|
import tmVuetify from "./tm-vuetify";
|
||||||
Vue.use(tmVuetify);
|
Vue.use(tmVuetify);
|
||||||
|
|
||||||
// i18n部分的配置
|
|
||||||
// 引入语言包,注意路径
|
|
||||||
import Chinese from "@/common/locales/zh.js";
|
|
||||||
import English from "@/common/locales/en.js";
|
|
||||||
|
|
||||||
// 引入并使用vue-i18n
|
|
||||||
import VueI18n from "vue-i18n";
|
|
||||||
Vue.use(VueI18n);
|
|
||||||
|
|
||||||
// 构造i18n对象
|
|
||||||
const i18n = new VueI18n({
|
|
||||||
// 默认语言,这里的local属性,对应message中的zh、en属性
|
|
||||||
locale: uni.getStorageSync("Default_Lang") || "zh",
|
|
||||||
// 引入语言文件
|
|
||||||
messages: {
|
|
||||||
// 这里的属性名是任意的,您也可以把zh设置为cn等,只是后续切换语言时
|
|
||||||
// 要标识这里的语言属性,如:this.$i18n.locale = zh|en|cn|xxx
|
|
||||||
zh: Chinese,
|
|
||||||
en: English,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 引入请求库
|
// 引入请求库
|
||||||
import {
|
import {
|
||||||
http
|
http
|
||||||
@@ -62,23 +36,13 @@ Vue.prototype.$http = http;
|
|||||||
import ApiManager from '@/api/index.js'
|
import ApiManager from '@/api/index.js'
|
||||||
Vue.use(ApiManager);
|
Vue.use(ApiManager);
|
||||||
|
|
||||||
import HaloTokenConfig from '@/config/token.config.js'
|
import HaloTokenConfig from '@/config/uhalo.config.js'
|
||||||
Vue.prototype.$baseApiUrl = HaloTokenConfig.BASE_API
|
Vue.prototype.$baseApiUrl = HaloTokenConfig.BASE_API
|
||||||
|
|
||||||
// 由于微信小程序的运行机制问题,需声明如下一行,H5和APP非必填
|
|
||||||
Vue.prototype._i18n = i18n;
|
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
App.mpType = "app";
|
App.mpType = "app";
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
i18n,
|
|
||||||
...App,
|
...App,
|
||||||
});
|
});
|
||||||
|
|
||||||
// #ifdef H5
|
app.$mount();
|
||||||
RouterMount(app, router, '#app')
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifndef H5
|
|
||||||
app.$mount(); //为了兼容小程序及app端必须这样写才有效果
|
|
||||||
// #endif
|
|
||||||
|
|||||||
+35
-16
@@ -2,8 +2,8 @@
|
|||||||
"name" : "uni-halo",
|
"name" : "uni-halo",
|
||||||
"appid" : "__UNI__5748B6E",
|
"appid" : "__UNI__5748B6E",
|
||||||
"description" : "uni-halo博客:基于halo开源博客系统API开发的多端博客。",
|
"description" : "uni-halo博客:基于halo开源博客系统API开发的多端博客。",
|
||||||
"versionName" : "1.0.0",
|
"versionName" : "2.0.10",
|
||||||
"versionCode" : 100,
|
"versionCode" : 210,
|
||||||
"transformPx" : false,
|
"transformPx" : false,
|
||||||
/* 5+App特有相关 */
|
/* 5+App特有相关 */
|
||||||
"app-plus" : {
|
"app-plus" : {
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/* 快应用特有相关 */
|
/* 快应用特有相关 */
|
||||||
@@ -126,29 +126,29 @@
|
|||||||
},
|
},
|
||||||
"usingComponents" : true,
|
"usingComponents" : true,
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mp-alipay" : {
|
"mp-alipay" : {
|
||||||
"usingComponents" : true,
|
"usingComponents" : true,
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mp-baidu" : {
|
"mp-baidu" : {
|
||||||
"usingComponents" : true,
|
"usingComponents" : true,
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mp-toutiao" : {
|
"mp-toutiao" : {
|
||||||
"usingComponents" : true,
|
"usingComponents" : true,
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true,
|
"enable" : false,
|
||||||
"version" : "2"
|
"version" : "2"
|
||||||
},
|
},
|
||||||
"vueVersion" : "2",
|
"vueVersion" : "2",
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
"disableHostCheck" : true,
|
"disableHostCheck" : true,
|
||||||
"proxy" : {
|
"proxy" : {
|
||||||
"/apis" : {
|
"/apis" : {
|
||||||
"target" : "https://demo.halo.run",
|
"target" : "https://blog.xiaoxiaomo.cn",
|
||||||
"changeOrigin" : true,
|
"changeOrigin" : true,
|
||||||
"secure" : false,
|
"secure" : false,
|
||||||
"pathRewrite" : {
|
"pathRewrite" : {
|
||||||
@@ -174,7 +174,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
|
},
|
||||||
|
"router" : {
|
||||||
|
"base" : "/uni-halo/",
|
||||||
|
"mode" : "history"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"_spaceID" : "699a26a5-a6cd-4b54-b603-5155b312a5f5",
|
"_spaceID" : "699a26a5-a6cd-4b54-b603-5155b312a5f5",
|
||||||
@@ -186,32 +190,47 @@
|
|||||||
"es6" : true
|
"es6" : true
|
||||||
},
|
},
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mp-jd" : {
|
"mp-jd" : {
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mp-kuaishou" : {
|
"mp-kuaishou" : {
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mp-lark" : {
|
"mp-lark" : {
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"quickapp-webview-huawei" : {
|
"quickapp-webview-huawei" : {
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"quickapp-webview-union" : {
|
"quickapp-webview-union" : {
|
||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"enable" : true
|
"enable" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"app-harmony" : {
|
||||||
|
"uniStatistics" : {
|
||||||
|
"enable" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mp-harmony" : {
|
||||||
|
"uniStatistics" : {
|
||||||
|
"enable" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mp-xhs" : {
|
||||||
|
"uniStatistics" : {
|
||||||
|
"enable" : false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-7
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uni-halo",
|
"name": "uni-halo",
|
||||||
"version": "1.0.0",
|
"version": "2.0.10",
|
||||||
"description": "<p align=\"center\">\r <img alt=\"logo\" src=\"https://b.925i.cn/uni_halo/uni_halo_logo.png\" width=\"120\" height=\"120\" style=\"margin-bottom: 10px;\">\r </p>\r <h3 align=\"center\" style=\"margin: 30px 0 30px;font-weight: bold;font-size:40px;\">uni-halo</h3>\r <h3 align=\"center\">uni-halo 基于Halo一款现代化的开源博客/CMS系统API开发的可多端编译应用</h3>",
|
"description": "<p align=\"center\">\r <img alt=\"logo\" src=\"https://b.925i.cn/uni_halo/uni_halo_logo.png\" width=\"120\" height=\"120\" style=\"margin-bottom: 10px;\">\r </p>\r <h3 align=\"center\" style=\"margin: 30px 0 30px;font-weight: bold;font-size:40px;\">uni-halo</h3>\r <h3 align=\"center\">uni-halo 基于Halo一款现代化的开源博客/CMS系统API开发的可多端编译应用</h3>",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -17,12 +17,7 @@
|
|||||||
"url": "https://gitee.com/ialley-workshop-open/uni-halo/issues"
|
"url": "https://gitee.com/ialley-workshop-open/uni-halo/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://uni-halo.925i.cn",
|
"homepage": "https://uni-halo.925i.cn",
|
||||||
"devDependencies": {
|
|
||||||
"vue-i18n": "^9.1.10"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"qs": "^6.12.1",
|
"qs": "^6.12.1"
|
||||||
"uni-read-pages": "^1.0.5",
|
|
||||||
"uni-simple-router": "^2.0.8-beta.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-41
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"pages": [
|
"pages": [{
|
||||||
{
|
|
||||||
"path": "pages/index/index",
|
"path": "pages/index/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "uni-halo",
|
"navigationBarTitleText": "uni-halo",
|
||||||
@@ -92,11 +91,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
//此配置为小程序的分包加载机制。在5+App里始终为整包。
|
//此配置为小程序的分包加载机制。在5+App里始终为整包。
|
||||||
"subPackages": [
|
"subPackages": [{
|
||||||
{
|
|
||||||
"root": "pagesA",
|
"root": "pagesA",
|
||||||
"pages": [
|
"pages": [{
|
||||||
{
|
|
||||||
"path": "start/start",
|
"path": "start/start",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "uni-halo",
|
"navigationBarTitleText": "uni-halo",
|
||||||
@@ -107,7 +104,7 @@
|
|||||||
{
|
{
|
||||||
"path": "article-detail/article-detail",
|
"path": "article-detail/article-detail",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "文章详情",
|
"navigationBarTitleText": "内容详情",
|
||||||
"enablePullDownRefresh": true,
|
"enablePullDownRefresh": true,
|
||||||
"app-plus": {
|
"app-plus": {
|
||||||
"pullToRefresh": {
|
"pullToRefresh": {
|
||||||
@@ -163,7 +160,7 @@
|
|||||||
{
|
{
|
||||||
"path": "archives/archives",
|
"path": "archives/archives",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "文章归档",
|
"navigationBarTitleText": "归档",
|
||||||
"enablePullDownRefresh": true,
|
"enablePullDownRefresh": true,
|
||||||
"app-plus": {
|
"app-plus": {
|
||||||
"pullToRefresh": {
|
"pullToRefresh": {
|
||||||
@@ -186,23 +183,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "journal/journal",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "个人日记",
|
|
||||||
"enablePullDownRefresh": true,
|
|
||||||
"app-plus": {
|
|
||||||
"pullToRefresh": {
|
|
||||||
"color": "#03a9f4",
|
|
||||||
"style": "circle"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "articles/articles",
|
"path": "articles/articles",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "文章列表",
|
"navigationBarTitleText": "内容搜索",
|
||||||
"enablePullDownRefresh": true,
|
"enablePullDownRefresh": true,
|
||||||
"app-plus": {
|
"app-plus": {
|
||||||
"pullToRefresh": {
|
"pullToRefresh": {
|
||||||
@@ -273,39 +257,76 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "test-page/test-page",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "advertise/advertise",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "广告页面",
|
|
||||||
"enablePullDownRefresh": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "submit-link/submit-link",
|
"path": "submit-link/submit-link",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "友链提交",
|
"navigationBarTitleText": "友链提交",
|
||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "moment-detail/moment-detail",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "瞬间详情",
|
||||||
|
"enablePullDownRefresh": true,
|
||||||
|
"app-plus": {
|
||||||
|
"pullToRefresh": {
|
||||||
|
"color": "#03a9f4",
|
||||||
|
"style": "circle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "votes/votes",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "投票列表",
|
||||||
|
"enablePullDownRefresh": true,
|
||||||
|
"app-plus": {
|
||||||
|
"pullToRefresh": {
|
||||||
|
"color": "#03a9f4",
|
||||||
|
"style": "circle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "vote-detail/vote-detail",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "投票详情",
|
||||||
|
"enablePullDownRefresh": true,
|
||||||
|
"app-plus": {
|
||||||
|
"pullToRefresh": {
|
||||||
|
"color": "#03a9f4",
|
||||||
|
"style": "circle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "data-visual/data-visual",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "数据看板",
|
||||||
|
"enablePullDownRefresh": true,
|
||||||
|
"app-plus": {
|
||||||
|
"pullToRefresh": {
|
||||||
|
"color": "#03a9f4",
|
||||||
|
"style": "circle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"root": "pagesC",
|
"root": "pagesC",
|
||||||
"pages": [
|
"pages": [{
|
||||||
{
|
|
||||||
"path": "website/website",
|
"path": "website/website",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "",
|
"navigationBarTitleText": "",
|
||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
@@ -319,8 +340,7 @@
|
|||||||
"borderStyle": "white",
|
"borderStyle": "white",
|
||||||
"color": "#303133",
|
"color": "#303133",
|
||||||
"selectedColor": "#03a9f4",
|
"selectedColor": "#03a9f4",
|
||||||
"list": [
|
"list": [{
|
||||||
{
|
|
||||||
"iconPath": "static/tabbar/select_home.png",
|
"iconPath": "static/tabbar/select_home.png",
|
||||||
"selectedIconPath": "static/tabbar/select_home_active.png",
|
"selectedIconPath": "static/tabbar/select_home_active.png",
|
||||||
"pagePath": "pages/tabbar/home/home",
|
"pagePath": "pages/tabbar/home/home",
|
||||||
|
|||||||
+89
-9
@@ -1,27 +1,91 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="app-page"></view>
|
<view class="app-page flex flex-center">
|
||||||
|
<PluginUnavailable v-if="!uniHaloPluginAvailable" :pluginId="uniHaloPluginId" :error-text="uniHaloPluginAvailableError" :use-border="false" :use-decoration="false" />
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const homePagePath = '/pages/tabbar/home/home'
|
import pluginAvailableMixin from '@/common/mixins/pluginAvailable.js';
|
||||||
const startPagePath = '/pagesA/start/start'
|
import PluginUnavailable from '@/components/plugin-unavailable/plugin-unavailable.vue';
|
||||||
|
|
||||||
|
const homePagePath = '/pages/tabbar/home/home';
|
||||||
|
const startPagePath = '/pagesA/start/start';
|
||||||
|
const articleDetailPath = '/pagesA/article-detail/article-detail';
|
||||||
|
|
||||||
|
const _DEV_ = false;
|
||||||
|
const _DEV_TO_TYPE_ = 'page';
|
||||||
|
const _DEV_TO_PATH_ = '/pagesA/data-visual/data-visual';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [pluginAvailableMixin],
|
||||||
|
components: {
|
||||||
|
PluginUnavailable
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
configs() {
|
configs() {
|
||||||
return this.$tm.vx.getters().getConfigs;
|
return this.$tm.vx.getters().getConfigs;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
async onLoad(options) {
|
||||||
uni.$tm.vx.actions('config/fetchConfigs').then((res) => {
|
// 本地开发,快速跳转页面,发布请设置 _DEV_ = false
|
||||||
|
if (_DEV_ && _DEV_TO_PATH_) {
|
||||||
|
if (_DEV_TO_TYPE_ == 'tabbar') {
|
||||||
|
uni.switchTab({
|
||||||
|
url: _DEV_TO_PATH_
|
||||||
|
});
|
||||||
|
} else if (_DEV_TO_TYPE_ == 'page') {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: _DEV_TO_PATH_
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查插件
|
||||||
|
this.setPluginId(this.NeedPluginIds.PluginUniHalo);
|
||||||
|
this.setPluginError('阿偶,检测到当前插件没有安装或者启用,无法启动 uni-halo 哦,请联系管理员');
|
||||||
|
if (!(await this.checkPluginAvailable())) return;
|
||||||
|
|
||||||
|
// 获取配置
|
||||||
|
uni.$tm.vx
|
||||||
|
.actions('config/fetchConfigs')
|
||||||
|
.then(async (res) => {
|
||||||
|
if (options.scene) {
|
||||||
|
if ('' !== options.scene) {
|
||||||
|
const postId = await this.getPostIdByQRCode(options.scene);
|
||||||
|
if (postId) {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: articleDetailPath + `?name=${postId}`,
|
||||||
|
animationType: 'slide-in-right'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
uni.$tm.vx.commit('setWxShare', res.shareConfig);
|
// uni.$tm.vx.commit('setWxShare', res.shareConfig);
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
|
// 获取mockjson
|
||||||
|
if (res.auditConfig.auditModeEnabled) {
|
||||||
|
if (res.auditConfig.auditModeData.jsonUrl) {
|
||||||
|
await uni.$tm.vx.actions('config/fetchMockJson');
|
||||||
|
} else {
|
||||||
|
const mockJson = uni.$utils.checkJsonAndParse(res.auditConfig.auditModeData.jsonData);
|
||||||
|
if (mockJson.ok) {
|
||||||
|
uni.$tm.vx.commit('config/setMockJson', mockJson.jsonData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进入检查
|
||||||
this.fnCheckShowStarted();
|
this.fnCheckShowStarted();
|
||||||
}).catch((err) => {
|
})
|
||||||
|
.catch((err) => {
|
||||||
uni.switchTab({
|
uni.switchTab({
|
||||||
url: homePagePath
|
url: homePagePath
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fnCheckShowStarted() {
|
fnCheckShowStarted() {
|
||||||
@@ -34,7 +98,7 @@ export default {
|
|||||||
|
|
||||||
// 是否每次都显示启动页
|
// 是否每次都显示启动页
|
||||||
if (this.configs.appConfig.startConfig.alwaysShow) {
|
if (this.configs.appConfig.startConfig.alwaysShow) {
|
||||||
uni.removeStorageSync('APP_HAS_STARTED')
|
uni.removeStorageSync('APP_HAS_STARTED');
|
||||||
uni.redirectTo({
|
uni.redirectTo({
|
||||||
url: startPagePath
|
url: startPagePath
|
||||||
});
|
});
|
||||||
@@ -51,7 +115,23 @@ export default {
|
|||||||
url: startPagePath
|
url: startPagePath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
async getPostIdByQRCode(key) {
|
||||||
|
const response = await this.$httpApi.v2.getQRCodeInfo(key);
|
||||||
|
if (response) {
|
||||||
|
if (response && response.postId) {
|
||||||
|
return response.postId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.app-page {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<tm-flop :startVal="0" :decimals="0" :endVal="statistics.post"
|
<tm-flop :startVal="0" :decimals="0" :endVal="statistics.post"
|
||||||
:duration="3000"></tm-flop>
|
:duration="3000"></tm-flop>
|
||||||
</view>
|
</view>
|
||||||
<view class="mt-6 text-align-center text-size-s text-grey-darken-1">文章总数</view>
|
<view class="mt-6 text-align-center text-size-s text-grey-darken-1">内容数量</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="item flex-1 text-align-center">
|
<view class="item flex-1 text-align-center">
|
||||||
<view class="number text-size-xl text-bg-gradient-green-accent">
|
<view class="number text-size-xl text-bg-gradient-green-accent">
|
||||||
@@ -84,21 +84,30 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {checkHasAdminLogin} from '@/utils/auth.js';
|
import {
|
||||||
import CheckAppUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
|
checkHasAdminLogin
|
||||||
import {CheckWxUpdate} from '@/utils/update.js';
|
} from '@/utils/auth.js';
|
||||||
|
import CheckAppUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
|
||||||
|
import {
|
||||||
|
CheckWxUpdate
|
||||||
|
} from '@/utils/update.js';
|
||||||
|
import {
|
||||||
|
NeedPluginIds,
|
||||||
|
NeedPlugins,
|
||||||
|
checkNeedPluginAvailable
|
||||||
|
} from "@/utils/plugin.js"
|
||||||
|
|
||||||
import tmGrouplist from '@/tm-vuetify/components/tm-grouplist/tm-grouplist.vue';
|
import tmGrouplist from '@/tm-vuetify/components/tm-grouplist/tm-grouplist.vue';
|
||||||
import tmListitem from '@/tm-vuetify/components/tm-listitem/tm-listitem.vue';
|
import tmListitem from '@/tm-vuetify/components/tm-listitem/tm-listitem.vue';
|
||||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||||
import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
|
import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
|
||||||
import tmMore from '@/tm-vuetify/components/tm-more/tm-more.vue';
|
import tmMore from '@/tm-vuetify/components/tm-more/tm-more.vue';
|
||||||
import tmFlop from '@/tm-vuetify/components/tm-flop/tm-flop.vue';
|
import tmFlop from '@/tm-vuetify/components/tm-flop/tm-flop.vue';
|
||||||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
|
import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
|
||||||
import wave from '@/components/wave/wave.vue';
|
import wave from '@/components/wave/wave.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
tmGrouplist,
|
tmGrouplist,
|
||||||
tmListitem,
|
tmListitem,
|
||||||
@@ -132,85 +141,110 @@ export default {
|
|||||||
haloConfigs() {
|
haloConfigs() {
|
||||||
return this.$tm.vx.getters().getConfigs
|
return this.$tm.vx.getters().getConfigs
|
||||||
},
|
},
|
||||||
calcDefaultImageConfig() {
|
pageConfig() {
|
||||||
return this.haloConfigs.imagesConfig
|
return this.haloConfigs.pageConfig.aboutConfig;
|
||||||
|
},
|
||||||
|
postDetailConfig() {
|
||||||
|
return this.haloConfigs.basicConfig.postDetailConfig;
|
||||||
},
|
},
|
||||||
bloggerInfo() {
|
bloggerInfo() {
|
||||||
return this.haloConfigs.authorConfig.blogger;
|
return this.haloConfigs.authorConfig.blogger;
|
||||||
},
|
},
|
||||||
calcProfileStyle() {
|
calcProfileStyle() {
|
||||||
const _imgUrlOr = this.calcDefaultImageConfig.aboutProfileImageUrl;
|
const _imgUrlOr = this.pageConfig.bgImageUrl;
|
||||||
return {
|
return {
|
||||||
backgroundImage: `url(${this.$utils.checkImageUrl(_imgUrlOr)})`
|
backgroundImage: `url(${this.$utils.checkImageUrl(_imgUrlOr)})`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
calcWaveUrl() {
|
calcWaveUrl() {
|
||||||
return this.$utils.checkImageUrl(this.calcDefaultImageConfig.waveImageUrl);
|
return this.$utils.checkImageUrl(this.pageConfig.waveImageUrl);
|
||||||
},
|
},
|
||||||
copyrightConfig() {
|
copyrightConfig() {
|
||||||
return this.haloConfigs.basicConfig.copyrightConfig;
|
return this.haloConfigs.basicConfig.copyrightConfig;
|
||||||
}
|
},
|
||||||
|
calcAuditModeEnabled() {
|
||||||
|
return this.haloConfigs.auditConfig.auditModeEnabled
|
||||||
|
},
|
||||||
|
calcVotePluginEnabled() {
|
||||||
|
return !!this.haloConfigs?.pluginConfig?.votePlugin?.enabled;
|
||||||
|
},
|
||||||
|
calcLinksPluginEnabled() {
|
||||||
|
return !!this.haloConfigs?.pluginConfig?.linksPlugin?.enabled;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
globalAppSettings: {
|
|
||||||
deep: true,
|
|
||||||
handler(val) {
|
|
||||||
this.statisticsShowMore = val.about.showAllCount;
|
|
||||||
this.fnGetNavList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
haloConfigs: {
|
haloConfigs: {
|
||||||
|
handler(val) {
|
||||||
|
if (!val) return;
|
||||||
|
this.fnGetNavList();
|
||||||
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
immediate: true,
|
immediate: true,
|
||||||
handler(val) {
|
|
||||||
this.fnGetNavList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.statisticsShowMore = this.globalAppSettings.about.showAllCount;
|
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
},
|
},
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fnGetNavList() {
|
async fnGetNavList() {
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
let _isWx = false;
|
let _isWx = false;
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
_isWx = true;
|
_isWx = true;
|
||||||
// #endif
|
// #endif
|
||||||
this.navList = [{
|
this.navList = [{
|
||||||
|
key: 'data-visual',
|
||||||
|
title: '数据看板',
|
||||||
|
leftIcon: 'icon-chart-bar',
|
||||||
|
leftIconColor: 'light-blue',
|
||||||
|
rightText: '站点数据可视化',
|
||||||
|
path: '/pagesA/data-visual/data-visual',
|
||||||
|
isAdmin: false,
|
||||||
|
type: 'page',
|
||||||
|
show: await checkNeedPluginAvailable(NeedPluginIds.PluginDataStatistics)
|
||||||
|
},{
|
||||||
key: 'archives',
|
key: 'archives',
|
||||||
title: '文章归档',
|
title: this.calcAuditModeEnabled ? '内容归档' : '文章归档',
|
||||||
leftIcon: 'halocoloricon-classify',
|
leftIcon: 'halocoloricon-classify',
|
||||||
leftIconColor: 'red',
|
leftIconColor: 'red',
|
||||||
rightText: '已归档的文章',
|
rightText: this.calcAuditModeEnabled ? '全部已归档内容' : '全部已归档文章',
|
||||||
path: '/pagesA/archives/archives',
|
path: '/pagesA/archives/archives',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
type: 'page',
|
type: 'page',
|
||||||
show: false
|
show: true
|
||||||
}, {
|
}, {
|
||||||
key: 'love',
|
key: 'love',
|
||||||
title: '恋爱日记',
|
title: '恋爱日记',
|
||||||
leftIcon: 'halocoloricon-attent',
|
leftIcon: 'halocoloricon-attent',
|
||||||
leftIconColor: 'red',
|
leftIconColor: 'red',
|
||||||
rightText: '甜蜜恋人的专属',
|
rightText: '博主的恋爱日记',
|
||||||
path: '/pagesA/love/love',
|
path: '/pagesA/love/love',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
type: 'page',
|
type: 'page',
|
||||||
show: this.haloConfigs.loveConfig.loveEnabled
|
show: this.haloConfigs.loveConfig.loveEnabled
|
||||||
|
}, {
|
||||||
|
key: 'vote',
|
||||||
|
title: '投票中心',
|
||||||
|
leftIcon: 'icon-box',
|
||||||
|
leftIconColor: 'red',
|
||||||
|
rightText: '查看和进行投票',
|
||||||
|
path: '/pagesA/votes/votes',
|
||||||
|
isAdmin: false,
|
||||||
|
type: 'page',
|
||||||
|
show: !this.calcAuditModeEnabled && !!this.calcVotePluginEnabled,
|
||||||
}, {
|
}, {
|
||||||
key: 'disclaimers',
|
key: 'disclaimers',
|
||||||
title: '友情链接',
|
title: '友情链接',
|
||||||
leftIcon: 'icon-lianjie',
|
leftIcon: 'icon-lianjie',
|
||||||
leftIconColor: 'blue',
|
leftIconColor: 'blue',
|
||||||
rightText: '看看朋友们吧',
|
rightText: '看看博主朋友们吧',
|
||||||
path: '/pagesA/friend-links/friend-links',
|
path: '/pagesA/friend-links/friend-links',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
type: 'page',
|
type: 'page',
|
||||||
show: true
|
show: !!this.calcLinksPluginEnabled,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'disclaimers',
|
key: 'disclaimers',
|
||||||
@@ -232,7 +266,7 @@ export default {
|
|||||||
path: '/pagesA/contact/contact',
|
path: '/pagesA/contact/contact',
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
type: 'page',
|
type: 'page',
|
||||||
show: true
|
show: this.haloConfigs.authorConfig.social.enabled
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'session',
|
key: 'session',
|
||||||
@@ -291,28 +325,7 @@ export default {
|
|||||||
// type: 'poup',
|
// type: 'poup',
|
||||||
// show: true
|
// show: true
|
||||||
// },
|
// },
|
||||||
{
|
|
||||||
key: 'setting',
|
|
||||||
title: '应用设置',
|
|
||||||
leftIcon: 'icon-cog',
|
|
||||||
leftIconColor: 'indigo',
|
|
||||||
rightText: `进入系统常用设置`,
|
|
||||||
path: '/pagesA/setting/setting',
|
|
||||||
isAdmin: false,
|
|
||||||
type: 'page',
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'admin',
|
|
||||||
title: '后台管理',
|
|
||||||
leftIcon: 'icon-lock',
|
|
||||||
leftIconColor: 'gray',
|
|
||||||
rightText: '博客后台系统入口',
|
|
||||||
path: '/pagesB/admin/admin',
|
|
||||||
isAdmin: true,
|
|
||||||
type: 'page',
|
|
||||||
show: this.globalAppSettings.about.showAdmin
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
fnGetData() {
|
fnGetData() {
|
||||||
@@ -362,8 +375,7 @@ export default {
|
|||||||
url: '/pagesB/login/login'
|
url: '/pagesB/login/login'
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {});
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,8 +396,7 @@ export default {
|
|||||||
this.navList.find(x => x.key == 'cache').rightText =
|
this.navList.find(x => x.key == 'cache').rightText =
|
||||||
uni.getStorageInfoSync().currentSize + 'KB';
|
uni.getStorageInfoSync().currentSize + 'KB';
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {});
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case 'update':
|
case 'update':
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
@@ -406,9 +417,9 @@ export default {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (type == 'page') {
|
} else if (type == 'page') {
|
||||||
this.$Router.push({
|
uni.navigateTo({
|
||||||
path: path
|
url: path
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -420,7 +431,7 @@ export default {
|
|||||||
url: '/pagesC/website/website?data=' +
|
url: '/pagesC/website/website?data=' +
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
title: item.text || this.$haloConfig.title,
|
title: item.text || this.$haloConfig.title,
|
||||||
url: item.path
|
url: encodeURIComponent(item.path)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -444,16 +455,16 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.app-page {
|
.app-page {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blogger-info {
|
.blogger-info {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 600rpx;
|
height: 600rpx;
|
||||||
@@ -500,9 +511,9 @@ export default {
|
|||||||
mix-blend-mode: screen;
|
mix-blend-mode: screen;
|
||||||
height: 100rpx;
|
height: 100rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-card {
|
.profile-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -538,20 +549,20 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.photos-img + .photos-img {
|
.photos-img+.photos-img {
|
||||||
margin-left: 12rpx;
|
margin-left: 12rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.statistics-wrap {
|
.statistics-wrap {
|
||||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
border-radius: 0rpx 0rpx 24rpx 24rpx;
|
border-radius: 0rpx 0rpx 24rpx 24rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.statistics {
|
.statistics {
|
||||||
&.has-solid {
|
&.has-solid {
|
||||||
.item + .item {
|
.item+.item {
|
||||||
border-left: 2rpx solid #fafafa;
|
border-left: 2rpx solid #fafafa;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -570,9 +581,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.quick-nav {
|
.quick-nav {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
@@ -585,18 +596,18 @@ export default {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
font-size: 80rpx;
|
font-size: 80rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-wrap {
|
.nav-wrap {
|
||||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyright {
|
.copyright {
|
||||||
color: #c0c4c7;
|
color: #c0c4c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-value-btn {
|
.right-value-btn {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -611,5 +622,5 @@ export default {
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
transform: initial;
|
transform: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,52 +1,95 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="app-page">
|
<view class="app-page" :style="{
|
||||||
<view v-if="loading != 'success'" class="loading-wrap">
|
padding:calcShowType==='list-post'? 0 : '24rpx 0'
|
||||||
|
}">
|
||||||
|
<view v-if="loading !== 'success'" class="loading-wrap">
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
</view>
|
</view>
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<view v-else class="app-page-content">
|
<view v-else class="app-page-content" :class="[calcShowType==='list-post'?'list-post':'']">
|
||||||
<view v-if="dataList.length == 0" class="content-empty flex flex-center" style="height: 70vh;">
|
<view v-if="dataList.length === 0" class="content-empty flex flex-center" style="height: 70vh;">
|
||||||
<!-- 空布局 -->
|
<!-- 空布局 -->
|
||||||
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无数据"></tm-empty>
|
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无数据"></tm-empty>
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
|
<block v-if="calcAuditModeEnabled || calcShowType==='list'">
|
||||||
<tm-translate v-for="(item, index) in dataList" :key="index"
|
<tm-translate v-for="(item, index) in dataList" :key="index"
|
||||||
style="box-sizing: border-box;width: 50%;padding: 0 8rpx;" animation-name="fadeUp"
|
style="box-sizing: border-box;width: 50%;padding: 0 8rpx;" animation-name="fadeUp"
|
||||||
:wait="calcAniWait(index)">
|
:wait="calcAniWait(index)">
|
||||||
<view class="catgory-card" :style="{backgroundImage:`url(${item.spec.cover})`}">
|
<view class="catgory-card" :style="{backgroundImage:`url(${item.spec.cover})`}">
|
||||||
<view class="content" @click="handleToCategory(item)">
|
<view class="content" @click="handleToCategory(item)">
|
||||||
<view style="font-size: 32rpx;color: #ffffff;">{{ item.spec.displayName }}</view>
|
<view style="font-size: 32rpx;color: #ffffff;">{{ item.spec.displayName }}</view>
|
||||||
<view style="font-size: 24rpx;color: #ffffff;margin-top: 6rpx;">共
|
<view v-if="!calcAuditModeEnabled"
|
||||||
{{ item.postCount }} 篇文章
|
style="font-size: 24rpx;color: #ffffff;margin-top: 6rpx;">
|
||||||
|
共 {{ item.postCount }} 篇文章
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</tm-translate>
|
</tm-translate>
|
||||||
<tm-flotbutton @click="fnToTopPage" size="m" color="light-blue" icon="icon-angle-up"></tm-flotbutton>
|
|
||||||
<view class="load-text">{{ loadMoreText }}</view>
|
<view class="load-text">{{ loadMoreText }}</view>
|
||||||
</block>
|
</block>
|
||||||
|
<view v-else-if="calcShowType==='list-post'" class="fulled flex" style="min-height:100vh">
|
||||||
|
<view class="bg-white" :style="{height: '100%'}">
|
||||||
|
<tm-sliderNav :list="categoryList" bg-color="white" color="light-blue" rang-key="displayName"
|
||||||
|
@change="fnOnCategoryChange"></tm-sliderNav>
|
||||||
</view>
|
</view>
|
||||||
|
<scroll-view class="right-content pt-12 pb-12" :scroll-y="true" :scroll-top="scrollTop"
|
||||||
|
:scroll-with-animation="true" :refresher-enabled="true" :refresher-triggered="triggered"
|
||||||
|
:refresher-threshold="60" refresher-background="#fafafa"
|
||||||
|
@refresherrefresh="fnGetPostByCategory(true)" @scrolltolower="fnGetPostByCategory(false)"
|
||||||
|
@scroll="fnOnScroll" @touchmove.stop @touchstart="fnOnTouchStart" @touchend="fnOnTouchEnd"
|
||||||
|
@touchcancel="fnOnTouchEnd">
|
||||||
|
<view v-if="postList.length === 0" class="article-empty flex flex-center">
|
||||||
|
<tm-empty :size="120" icon="icon-shiliangzhinengduixiang-" label="该分类下暂无文章~"></tm-empty>
|
||||||
|
</view>
|
||||||
|
<block v-else>
|
||||||
|
<block v-for="(post, index) in postList" :key="post.spec.publishTime">
|
||||||
|
<tm-translate animation-name="fadeUp" :wait="calcAniWait(index)">
|
||||||
|
<article-min-card :article="post" @on-click="fnToArticleDetail"></article-min-card>
|
||||||
|
</tm-translate>
|
||||||
|
</block>
|
||||||
|
<view class="load-text">{{ loadMoreText }}</view>
|
||||||
|
</block>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flot-buttons">
|
||||||
|
<tm-button @click="fnScrollTop" size="m" :fab="true" theme="light-blue"
|
||||||
|
icon="icon-angle-up"></tm-button>
|
||||||
|
<tm-button v-if="!calcAuditModeEnabled" @click="fnChangeShowType" size="m" :fab="true" theme="light-blue"
|
||||||
|
:icon="calcShowType==='list'?'icon-align-left':'icon-all'"></tm-button>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
|
||||||
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
||||||
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
||||||
|
import tmFlowLayout from '@/tm-vuetify/components/tm-flowLayout/tm-flowLayout.vue';
|
||||||
|
import tmSliderNav from '@/tm-vuetify/components/tm-sliderNav/tm-sliderNav.vue';
|
||||||
|
import ArticleMinCard from '@/components/article-min-card/article-min-card.vue';
|
||||||
|
|
||||||
export default {
|
|
||||||
|
export default {
|
||||||
components: {
|
components: {
|
||||||
tmSkeleton,
|
tmSkeleton,
|
||||||
tmFlotbutton,
|
tmButton,
|
||||||
tmTranslate,
|
tmTranslate,
|
||||||
tmEmpty,
|
tmEmpty,
|
||||||
mpHtml
|
mpHtml,
|
||||||
|
tmFlowLayout,
|
||||||
|
tmSliderNav,
|
||||||
|
ArticleMinCard
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -54,27 +97,78 @@
|
|||||||
loading: 'loading',
|
loading: 'loading',
|
||||||
queryParams: {
|
queryParams: {
|
||||||
size: 20,
|
size: 20,
|
||||||
page: 1
|
page: 1,
|
||||||
|
fieldSelector: ['spec.hideFromList=false']
|
||||||
},
|
},
|
||||||
hasNext: false,
|
hasNext: false,
|
||||||
dataList: [],
|
dataList: [],
|
||||||
|
categoryList: [],
|
||||||
isLoadMore: false,
|
isLoadMore: false,
|
||||||
loadMoreText: '加载中...'
|
loadMoreText: '加载中...',
|
||||||
|
currentCategoryConfig: {
|
||||||
|
type: "list"
|
||||||
|
},
|
||||||
|
currentCategoryName: "",
|
||||||
|
postQueryParams: {
|
||||||
|
size: 10,
|
||||||
|
page: 0,
|
||||||
|
},
|
||||||
|
postList: [],
|
||||||
|
triggered: false,
|
||||||
|
scrollTop: 0,
|
||||||
|
tempScrollTop: 0,
|
||||||
|
scrollTimeout: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
onLoad() {
|
haloConfigs() {
|
||||||
this.fnGetData();
|
return this.$tm.vx.getters().getConfigs;
|
||||||
|
},
|
||||||
|
calcAuditModeEnabled() {
|
||||||
|
return this.haloConfigs.auditConfig.auditModeEnabled
|
||||||
|
},
|
||||||
|
mockJson() {
|
||||||
|
return this.$tm.vx.getters().getMockJson;
|
||||||
|
},
|
||||||
|
categoryConfig() {
|
||||||
|
return this.haloConfigs.pageConfig.categoryConfig
|
||||||
|
},
|
||||||
|
calcShowType() {
|
||||||
|
return this.currentCategoryConfig.type
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
categoryConfig: {
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
handler(newVal) {
|
||||||
|
console.log("执行了", newVal)
|
||||||
|
if (!newVal) return;
|
||||||
|
this.currentCategoryConfig = newVal
|
||||||
|
this.handleInitPage()
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
this.isLoadMore = false;
|
this.isLoadMore = false;
|
||||||
this.queryParams.page = 0;
|
this.queryParams.page = 0;
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
},
|
},
|
||||||
|
|
||||||
onReachBottom(e) {
|
onReachBottom(e) {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '没有更多数据了'
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
if (this.hasNext) {
|
if (this.hasNext) {
|
||||||
|
if (this.calcShowType === 'list') {
|
||||||
|
|
||||||
this.queryParams.page += 1;
|
this.queryParams.page += 1;
|
||||||
|
} else {
|
||||||
|
this.postQueryParams.page += 1;
|
||||||
|
}
|
||||||
this.isLoadMore = true;
|
this.isLoadMore = true;
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
} else {
|
} else {
|
||||||
@@ -85,7 +179,55 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
fnChangeShowType() {
|
||||||
|
if (this.calcShowType === 'list-post') {
|
||||||
|
this.currentCategoryConfig.type = 'list'
|
||||||
|
} else {
|
||||||
|
this.currentCategoryConfig.type = 'list-post'
|
||||||
|
}
|
||||||
|
this.handleInitPage();
|
||||||
|
},
|
||||||
|
handleResetInit() {
|
||||||
|
this.postList = []
|
||||||
|
this.dataList = []
|
||||||
|
this.categoryList = []
|
||||||
|
this.queryParams.page = 0;
|
||||||
|
this.postQueryParams.page = 0;
|
||||||
|
this.hasNext = false
|
||||||
|
this.isLoadMore = false
|
||||||
|
this.loadMoreText = '加载中...'
|
||||||
|
this.currentCategoryName = ""
|
||||||
|
this.triggered = false
|
||||||
|
this.fnResetSetAniWaitIndex()
|
||||||
|
},
|
||||||
|
handleInitPage() {
|
||||||
|
this.handleResetInit()
|
||||||
|
if (this.calcShowType === 'list-post') {
|
||||||
|
this.queryParams.size = 99999
|
||||||
|
}
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
fnGetData() {
|
fnGetData() {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
this.categoryConfig.type = "list"
|
||||||
|
this.dataList = this.mockJson.category.list.map((item) => {
|
||||||
|
return {
|
||||||
|
metadata: {
|
||||||
|
name: Date.now() * Math.random(),
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
displayName: item.title,
|
||||||
|
cover: this.$utils.checkImageUrl(item.cover)
|
||||||
|
},
|
||||||
|
postCount: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.loading = 'success';
|
||||||
|
this.loadMoreText = '呜呜,没有更多数据啦~';
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
mask: true,
|
mask: true,
|
||||||
title: '加载中...'
|
title: '加载中...'
|
||||||
@@ -98,15 +240,14 @@
|
|||||||
this.$httpApi.v2
|
this.$httpApi.v2
|
||||||
.getCategoryList(this.queryParams)
|
.getCategoryList(this.queryParams)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
console.log('请求结果:');
|
if (this.calcShowType === 'list') {
|
||||||
console.log(res);
|
|
||||||
|
|
||||||
this.loading = 'success';
|
this.loading = 'success';
|
||||||
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
||||||
// 处理数据
|
// 处理数据
|
||||||
this.hasNext = res.hasNext;
|
this.hasNext = res.hasNext;
|
||||||
|
|
||||||
const tempItems = res.items.map(item => {
|
const tempItems = res.items.map(item => {
|
||||||
|
item.postCount = item.postCount ?? 0
|
||||||
item.spec.cover = this.$utils.checkThumbnailUrl(item.spec.cover, true)
|
item.spec.cover = this.$utils.checkThumbnailUrl(item.spec.cover, true)
|
||||||
return item;
|
return item;
|
||||||
})
|
})
|
||||||
@@ -116,6 +257,23 @@
|
|||||||
} else {
|
} else {
|
||||||
this.dataList = tempItems;
|
this.dataList = tempItems;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.dataList = res.items
|
||||||
|
this.categoryList = res.items.map(item => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
displayName: item.spec.displayName,
|
||||||
|
name: item.metadata.name,
|
||||||
|
postCount: item.postCount ?? 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.triggered = false;
|
||||||
|
this.loading = 'success';
|
||||||
|
if (this.dataList.length !== 0) {
|
||||||
|
this.currentCategoryName = this.dataList[0].metadata.name;
|
||||||
|
this.fnGetPostByCategory()
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -136,34 +294,130 @@
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleToCategory(data) {
|
handleToCategory(data) {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pagesA/category-detail/category-detail?name=${data.metadata.name}&title=${data.spec.displayName}`
|
url: `/pagesA/category-detail/category-detail?name=${data.metadata.name}&title=${data.spec.displayName}`
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
fnGetPostByCategory(isPulldownRefresh = true, triggered = true) {
|
||||||
|
if (!isPulldownRefresh) {
|
||||||
|
if (this.hasNext) {
|
||||||
|
this.postQueryParams.page += 1;
|
||||||
|
} else {
|
||||||
|
return uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '没有更多数据了'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.postQueryParams.page = 0;
|
||||||
|
if (triggered) {
|
||||||
|
this.triggered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
this.$httpApi.v2
|
||||||
|
.getCategoryPostList(this.currentCategoryName, this.postQueryParams)
|
||||||
|
.then(res => {
|
||||||
|
this.hasNext = res.hasNext;
|
||||||
|
|
||||||
|
if (!isPulldownRefresh) {
|
||||||
|
this.postList = this.postList.concat(res.items);
|
||||||
|
} else {
|
||||||
|
this.postList = res.items;
|
||||||
|
}
|
||||||
|
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.loadMoreText = '加载失败!';
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.triggered = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
fnOnCategoryChange(e) {
|
||||||
|
this.fnResetSetAniWaitIndex();
|
||||||
|
this.currentCategoryName = this.dataList[e].metadata.name;
|
||||||
|
this.fnToTopScroll();
|
||||||
|
this.postList = [];
|
||||||
|
this.fnGetPostByCategory();
|
||||||
|
},
|
||||||
|
|
||||||
|
fnOnScroll(e) {
|
||||||
|
this.tempScrollTop = e.detail.scrollTop;
|
||||||
|
},
|
||||||
|
fnToTopScroll() {
|
||||||
|
uni.pageScrollTo({
|
||||||
|
scrollTop: 0,
|
||||||
|
duration: 500
|
||||||
|
});
|
||||||
|
this.scrollTop = 0;
|
||||||
|
this.tempScrollTop = 0;
|
||||||
|
},
|
||||||
|
fnOnTouchStart() {
|
||||||
|
clearTimeout(this.scrollTimeout);
|
||||||
|
},
|
||||||
|
fnOnTouchEnd() {
|
||||||
|
this.scrollTimeout = setTimeout(() => {
|
||||||
|
this.scrollTop = this.tempScrollTop;
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
|
//跳转文章详情
|
||||||
|
fnToArticleDetail(post) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pagesA/article-detail/article-detail?name=' + post.metadata.name,
|
||||||
|
animationType: 'slide-in-right'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fnScrollTop() {
|
||||||
|
if (this.calcShowType === 'list') {
|
||||||
|
this.fnToTopPage()
|
||||||
|
} else {
|
||||||
|
this.fnToTopScroll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-page {
|
.app-page {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 24rpx 0;
|
padding: 24rpx 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-wrap {
|
.auditModeEnabled {
|
||||||
|
width: 100%;
|
||||||
|
height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrap {
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-page-content {
|
.app-page-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: 0 12rpx;
|
padding: 0 12rpx;
|
||||||
gap: 20rpx 0;
|
gap: 20rpx 0;
|
||||||
}
|
|
||||||
|
|
||||||
.catgory-card {
|
&.list-post {
|
||||||
|
padding: 0;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.catgory-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 200rpx;
|
height: 200rpx;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -185,11 +439,12 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: rgba(0, 0, 0, 0.15);
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
backdrop-filter: blur(3rpx);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -198,10 +453,20 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.load-text {
|
.load-text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flot-buttons {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 100rpx;
|
||||||
|
right: 32rpx;
|
||||||
|
flex-direction: column;
|
||||||
|
display: flex;
|
||||||
|
gap: 6rpx;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,55 +1,86 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="app-page">
|
<view class="app-page" :class="[uniHaloPluginPageClass]">
|
||||||
|
<PluginUnavailable v-if="!uniHaloPluginAvailable" :pluginId="uniHaloPluginId"
|
||||||
|
:error-text="uniHaloPluginAvailableError" />
|
||||||
|
<template v-else>
|
||||||
<!-- 顶部切换 -->
|
<!-- 顶部切换 -->
|
||||||
<view class="e-fixed" v-if="category.list.length > 0">
|
<view class="e-fixed" v-if="category.list.length > 0">
|
||||||
<tm-tabs color="light-blue" v-model="category.activeIndex" range-key="displayName" :list="category.list"
|
<tm-tabs color="light-blue" :shadow="0" v-model="category.activeIndex" range-key="displayName"
|
||||||
align="left" @change="fnOnCategoryChange"></tm-tabs>
|
:list="category.list" align="left" @change="fnOnCategoryChange($event, false)"></tm-tabs>
|
||||||
</view>
|
</view>
|
||||||
<!-- 占位区域 -->
|
<!-- 占位区域 -->
|
||||||
<view v-if="category.list.length > 0" style="width: 100vw;height: 90rpx;"></view>
|
<view v-if="category.list.length > 0" style="width: 100vw;height: 90rpx;"></view>
|
||||||
<!-- 加载区域 -->
|
<!-- 加载区域 -->
|
||||||
<view v-if="loading != 'success'" class="loading-wrap">
|
<view v-if="loading == 'loading'" class="loading-wrap">
|
||||||
<tm-skeleton model="card"></tm-skeleton>
|
<tm-skeleton model="card"></tm-skeleton>
|
||||||
<tm-skeleton model="card"></tm-skeleton>
|
<tm-skeleton model="card"></tm-skeleton>
|
||||||
<tm-skeleton model="card"></tm-skeleton>
|
<tm-skeleton model="card"></tm-skeleton>
|
||||||
<tm-skeleton model="card"></tm-skeleton>
|
<tm-skeleton model="card"></tm-skeleton>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-else-if="loading == 'error'" class="flex flex-col flex-center" style="width:100%;height:60vh;">
|
||||||
|
<tm-empty icon="icon-wind-cry" label="阿偶,获取数据失败了~">
|
||||||
|
<tm-button theme="light-blue" size="m" :shadow="0" @click="fnGetData(true)">刷新试试</tm-button>
|
||||||
|
</tm-empty>
|
||||||
|
</view>
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<view class="content" v-else>
|
<view v-else class="content">
|
||||||
<view v-if="dataList.length == 0" class="content-empty">
|
<k-touch-listen class="touch-listen-content" @touchLeft="touchLeft" @touchRight="touchRight">
|
||||||
|
<view v-if="dataList.length === 0" class="content-empty">
|
||||||
<!-- 空布局 -->
|
<!-- 空布局 -->
|
||||||
<tm-empty icon="icon-shiliangzhinengduixiang-" label="博主还没有分享图片~"></tm-empty>
|
<tm-empty icon="icon-shiliangzhinengduixiang-" label="博主还没有分享图片~"></tm-empty>
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<block v-for="(item, index) in dataList" :key="index">
|
<block v-if="galleryConfig.useWaterfall">
|
||||||
<tm-translate style="box-sizing: border-box;padding: 6rpx;width: 50%;height: 250rpx;"
|
<!--瀑布流-->
|
||||||
|
<tm-flowLayout-custom ref="wafll" style="width: 100%;"
|
||||||
|
@click="fnOnFlowClick"></tm-flowLayout-custom>
|
||||||
|
</block>
|
||||||
|
<!-- 列表 -->
|
||||||
|
<block v-else>
|
||||||
|
<tm-translate v-for="(item, index) in dataList" :key="index"
|
||||||
|
style="box-sizing: border-box;padding: 6rpx;width: 50%;height: 250rpx;"
|
||||||
animation-name="fadeUp" :wait="calcAniWait(index)">
|
animation-name="fadeUp" :wait="calcAniWait(index)">
|
||||||
<view style="border-radius: 12rpx;overflow: hidden;width: 100%;height: 250rpx;">
|
<view style="border-radius: 12rpx;overflow: hidden;width: 100%;height: 250rpx;">
|
||||||
<image style="width: 100%;height: 100%;" mode="aspectFill" :src="item.spec.cover"
|
<image style="width: 100%;height: 100%;" mode="aspectFill" :src="item.spec.url"
|
||||||
@click="fnPreview(index)"/>
|
@click="fnPreview(item)" />
|
||||||
</view>
|
</view>
|
||||||
</tm-translate>
|
</tm-translate>
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
<tm-flotbutton @click="fnToTopPage" color="light-blue" size="m" icon="icon-angle-up"></tm-flotbutton>
|
|
||||||
<view class="load-text">{{ loadMoreText }}</view>
|
<view class="load-text">{{ loadMoreText }}</view>
|
||||||
</block>
|
</block>
|
||||||
|
</k-touch-listen>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view v-if="!calcAuditModeEnabled" class="flot-buttons">
|
||||||
|
<tm-button v-if="loading == 'error'" @click="fnGetCategory" size="m" :fab="true" theme="light-blue"
|
||||||
|
icon="icon-sync-alt"></tm-button>
|
||||||
|
<tm-button @click="fnToTopPage" size="m" :fab="true" theme="light-blue"
|
||||||
|
icon="icon-angle-up"></tm-button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||||
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
||||||
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
|
import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
|
||||||
import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue';
|
import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue';
|
||||||
import tmFlowLayout from '@/tm-vuetify/components/tm-flowLayout/tm-flowLayout.vue';
|
import tmFlowLayoutCustom from '@/tm-vuetify/components/tm-flowLayout-custom/tm-flowLayout-custom.vue';
|
||||||
import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
|
import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
|
||||||
export default {
|
import pluginAvailable from "@/common/mixins/pluginAvailable.js"
|
||||||
|
import PluginUnavailable from '@/components/plugin-unavailable/plugin-unavailable.vue'
|
||||||
|
export default {
|
||||||
|
options: {
|
||||||
|
multipleSlots: true
|
||||||
|
},
|
||||||
|
mixins: [pluginAvailable],
|
||||||
components: {
|
components: {
|
||||||
tmSkeleton,
|
tmSkeleton,
|
||||||
tmTranslate,
|
tmTranslate,
|
||||||
@@ -58,8 +89,10 @@ export default {
|
|||||||
tmEmpty,
|
tmEmpty,
|
||||||
tmIcons,
|
tmIcons,
|
||||||
tmImages,
|
tmImages,
|
||||||
tmFlowLayout,
|
tmFlowLayoutCustom,
|
||||||
tmTabs
|
tmTabs,
|
||||||
|
tmButton,
|
||||||
|
PluginUnavailable
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -75,36 +108,69 @@ export default {
|
|||||||
page: 1,
|
page: 1,
|
||||||
group: ""
|
group: ""
|
||||||
},
|
},
|
||||||
cache: {
|
|
||||||
dataList: [],
|
|
||||||
total: 0
|
|
||||||
},
|
|
||||||
isLoadMore: false,
|
isLoadMore: false,
|
||||||
loadMoreText: '',
|
loadMoreText: '',
|
||||||
hasNext: false,
|
hasNext: false,
|
||||||
dataList: []
|
dataList: [],
|
||||||
|
lock: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
filters: {
|
computed: {
|
||||||
filterTakeTime(val) {
|
galleryConfig() {
|
||||||
return uni.$tm.dayjs(val).format('DD/MM/YYYY');
|
return this.$tm.vx.getters().getConfigs.pageConfig.galleryConfig;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onLoad() {
|
haloConfigs() {
|
||||||
this.fnSetPageTitle('个人图库');
|
return this.$tm.vx.getters().getConfigs;
|
||||||
|
},
|
||||||
|
mockJson() {
|
||||||
|
return this.$tm.vx.getters().getMockJson;
|
||||||
|
},
|
||||||
|
calcAuditModeEnabled() {
|
||||||
|
return this.haloConfigs.auditConfig.auditModeEnabled
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
galleryConfig: {
|
||||||
|
async handler(newValue, oldValue) {
|
||||||
|
if (!newValue) return;
|
||||||
|
this.fnSetPageTitle(newValue.pageTitle);
|
||||||
this.fnGetCategory();
|
this.fnGetCategory();
|
||||||
},
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async onLoad() {
|
||||||
|
// 检查插件
|
||||||
|
this.setPluginId(this.NeedPluginIds.PluginPhotos)
|
||||||
|
this.setPluginError("阿偶,检测到当前插件没有安装或者启用,无法使用图库功能哦,请联系管理员")
|
||||||
|
await this.checkPluginAvailable()
|
||||||
|
},
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
|
if (!this.uniHaloPluginAvailable) {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return
|
||||||
|
}
|
||||||
this.dataList = []
|
this.dataList = []
|
||||||
this.isLoadMore = false;
|
this.isLoadMore = false;
|
||||||
this.queryParams.page = 1;
|
this.queryParams.page = 1;
|
||||||
this.fnGetData();
|
this.fnGetData(true);
|
||||||
},
|
},
|
||||||
onReachBottom(e) {
|
onReachBottom(e) {
|
||||||
|
if (!this.uniHaloPluginAvailable) return;
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '没有更多数据了'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.hasNext) {
|
if (this.hasNext) {
|
||||||
this.queryParams.page += 1;
|
this.queryParams.page += 1;
|
||||||
this.isLoadMore = true;
|
this.isLoadMore = true;
|
||||||
this.fnGetData();
|
this.fnGetData(false);
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
@@ -113,34 +179,88 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fnOnCategoryChange(index) {
|
fnGetDataByCategory(index) {
|
||||||
this.fnResetSetAniWaitIndex();
|
this.fnResetSetAniWaitIndex();
|
||||||
this.cache.dataList = []
|
|
||||||
this.dataList = [];
|
|
||||||
this.queryParams.group = this.category.list[index].name;
|
this.queryParams.group = this.category.list[index].name;
|
||||||
this.queryParams.page = 1;
|
this.queryParams.page = 1;
|
||||||
this.fnToTopPage();
|
this.fnToTopPage();
|
||||||
this.fnGetData();
|
this.dataList = [];
|
||||||
|
this.fnGetData(true);
|
||||||
|
},
|
||||||
|
fnOnCategoryChange(index) {
|
||||||
|
if (this.lock) {
|
||||||
|
// uni.showToast({
|
||||||
|
// title: "上一个请求进行中...",
|
||||||
|
// icon: "none"
|
||||||
|
// })
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.fnGetDataByCategory(index)
|
||||||
},
|
},
|
||||||
fnGetCategory() {
|
fnGetCategory() {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
this.fnGetData(true);
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$httpApi.v2.getPhotoGroupList({
|
this.$httpApi.v2.getPhotoGroupList({
|
||||||
page: 1,
|
page: 1,
|
||||||
size: 9999
|
size: 0
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
this.category.list = res.items.map(item => {
|
this.category.list = res.map(item => {
|
||||||
return {
|
return {
|
||||||
name: item.metadata.name,
|
name: item.metadata.name,
|
||||||
displayName: item.spec.displayName
|
displayName: item.spec.displayName,
|
||||||
|
priority: item.spec.priority
|
||||||
}
|
}
|
||||||
|
}).sort((a, b) => a.priority - b.priority)
|
||||||
|
this.category.list.unshift({
|
||||||
|
name: undefined,
|
||||||
|
displayName: '全部',
|
||||||
|
priority: 0
|
||||||
});
|
});
|
||||||
if (this.category.list.length !== 0) {
|
if (this.category.list.length !== 0) {
|
||||||
this.queryParams.group = this.category.list[0].name;
|
this.queryParams.group = this.category.list[0].name;
|
||||||
this.fnGetData();
|
this.fnGetData(true);
|
||||||
}
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
this.loading = 'error'
|
||||||
|
this.category.list = []
|
||||||
|
this.category.activeIndex = 0
|
||||||
|
this.category.activeValue = ""
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
fnGetData() {
|
fnGetData(isClearWaterfall = false) {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
this.dataList = this.mockJson.gallery.list.map(item => {
|
||||||
|
return {
|
||||||
|
metadata: {
|
||||||
|
name: Date.now() * Math.random(),
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
url: this.$utils.checkImageUrl(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.loading = 'success';
|
||||||
|
|
||||||
|
if (this.galleryConfig.useWaterfall) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (isClearWaterfall) {
|
||||||
|
this.$refs.wafll.clear()
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.wafll.pushData(this.dataList)
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.loadMoreText = '呜呜,没有更多数据啦~';
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
this.lock = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 设置状态为加载中
|
// 设置状态为加载中
|
||||||
if (!this.isLoadMore) {
|
if (!this.isLoadMore) {
|
||||||
this.loading = 'loading';
|
this.loading = 'loading';
|
||||||
@@ -149,84 +269,93 @@ export default {
|
|||||||
this.$httpApi.v2
|
this.$httpApi.v2
|
||||||
.getPhotoListByGroupName(this.queryParams)
|
.getPhotoListByGroupName(this.queryParams)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
console.log("相册 res", res)
|
|
||||||
this.hasNext = res.hasNext;
|
this.hasNext = res.hasNext;
|
||||||
this.loading = 'success';
|
this.loading = 'success';
|
||||||
if (res.items.length != 0) {
|
if (res.items.length !== 0) {
|
||||||
const _list = res.items.map((item, index) => {
|
const _list = res.items.map((item, index) => {
|
||||||
item.spec.cover = this.$utils.checkImageUrl(item.spec.cover);
|
item.spec.url = this.$utils.checkImageUrl(item.spec.url || item.spec.cover);
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
this.fnCacheDataList(_list);
|
|
||||||
if (this.isLoadMore) {
|
if (this.isLoadMore) {
|
||||||
this.dataList = this.dataList.concat(_list);
|
this.dataList = this.dataList.concat(_list);
|
||||||
} else {
|
} else {
|
||||||
this.dataList = _list;
|
this.dataList = _list;
|
||||||
}
|
}
|
||||||
|
if (this.galleryConfig.useWaterfall) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (isClearWaterfall) {
|
||||||
|
this.$refs.wafll.clear()
|
||||||
|
}
|
||||||
|
this.$refs.wafll.pushData(_list)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
this.loading = 'error';
|
this.loading = 'error';
|
||||||
this.waterfall.loading = 'finish';
|
|
||||||
this.loadMoreText = '加载失败,请下拉刷新!';
|
this.loadMoreText = '加载失败,请下拉刷新!';
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.stopPullDownRefresh();
|
uni.stopPullDownRefresh();
|
||||||
|
this.lock = false;
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 缓存数据
|
fnOnFlowClick({
|
||||||
fnCacheDataList(dataList) {
|
item
|
||||||
if (this.queryParams.page == 1) {
|
}) {
|
||||||
this.cache.dataList = dataList;
|
this.fnPreview(item)
|
||||||
} else {
|
|
||||||
this.cache.dataList = [...this.cache.dataList, ...dataList];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 瀑布流组件点击事件
|
|
||||||
fnOnClick(data) {
|
|
||||||
console.log('点击数据', data);
|
|
||||||
},
|
},
|
||||||
// 预览
|
// 预览
|
||||||
fnPreview(index) {
|
fnPreview(data) {
|
||||||
uni.previewImage({
|
uni.previewImage({
|
||||||
current: index,
|
current: this.dataList.findIndex(x => x.metadata.name === data.metadata.name),
|
||||||
urls: this.dataList.map(x => x.spec.cover),
|
urls: this.dataList.map(x => x.spec.url),
|
||||||
indicator: 'number',
|
indicator: 'number',
|
||||||
loop: true
|
loop: true
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
touchLeft() {
|
||||||
|
if (this.loading != "success") return;
|
||||||
|
this.category.activeIndex += 1
|
||||||
|
if (this.category.activeIndex >= this.category.list.length) {
|
||||||
|
this.category.activeIndex = 0
|
||||||
|
}
|
||||||
|
this.lock = true
|
||||||
|
this.fnGetDataByCategory(this.category.activeIndex)
|
||||||
|
},
|
||||||
|
touchRight() {
|
||||||
|
if (this.loading != "success") return;
|
||||||
|
this.category.activeIndex -= 1
|
||||||
|
if (this.category.activeIndex < 0) {
|
||||||
|
this.category.activeIndex = 0
|
||||||
|
}
|
||||||
|
this.lock = true
|
||||||
|
this.fnGetDataByCategory(this.category.activeIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-page {
|
.app-page {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-bottom: 24rpx;
|
padding-bottom: 24rpx;
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
|
|
||||||
&.is-balck {
|
|
||||||
background-color: #212121;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0 24rpx;
|
|
||||||
padding-top: 24rpx;
|
|
||||||
gap: 12rpx 0;
|
|
||||||
|
|
||||||
.content-empty {
|
.content-empty {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -235,19 +364,34 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-wrap {
|
.touch-listen-content {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 24rpx 24rpx 0;
|
||||||
|
gap: 12rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrap {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.load-text {
|
||||||
box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
.load-text {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flot-buttons {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 100rpx;
|
||||||
|
right: 32rpx;
|
||||||
|
flex-direction: column;
|
||||||
|
display: flex;
|
||||||
|
gap: 6rpx;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
+377
-81
@@ -2,14 +2,14 @@
|
|||||||
<view class="app-page">
|
<view class="app-page">
|
||||||
<tm-menubars iconColor="white" color="white" :flat="true" :showback="false">
|
<tm-menubars iconColor="white" color="white" :flat="true" :showback="false">
|
||||||
<view slot="left">
|
<view slot="left">
|
||||||
<image class="logo ml-24 round-24" :src="appInfo.logo" mode="scaleToFill"/>
|
<image @click="fnOnLogoToPage" class="logo ml-24 round-24" :src="appInfo.logo" mode="scaleToFill" />
|
||||||
</view>
|
</view>
|
||||||
<view class="search-input round-12 pt-12 pb-12 flex pl-24" @click="fnToSearch">
|
<view class="search-input round-12 pt-12 pb-12 flex pl-24" @click="fnToSearch">
|
||||||
<text class="search-input_icon iconfont text-size-m icon-search text-grey"></text>
|
<text class="search-input_icon iconfont text-size-m icon-search text-grey"></text>
|
||||||
<view class="search-input_text pl-12 text-size-m text-grey">搜索文章...</view>
|
<view class="search-input_text pl-12 text-size-m text-grey">搜索内容...</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- #ifdef APP-PLUS || H5 -->
|
<!-- #ifdef APP-PLUS || H5 -->
|
||||||
<view slot="right" class="mr-24 text-size-m text-grey">uni-halo</view>
|
<view slot="right" class="mr-24 text-size-m text-grey text-overflow">{{ appInfo.name }}</view>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
</tm-menubars>
|
</tm-menubars>
|
||||||
<view v-if="loading !== 'success' && articleList.length===0" class="loading-wrap">
|
<view v-if="loading !== 'success' && articleList.length===0" class="loading-wrap">
|
||||||
@@ -21,26 +21,52 @@
|
|||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view class="bg-white pb-24">
|
<view v-if="bannerConfig.enabled" class="bg-white pb-24">
|
||||||
<view class="banner bg-white ml-24 mr-24 mt-12 round-3" v-if="bannerList.length != 0">
|
<view class="banner bg-white ml-24 mr-24 mt-12 round-3" v-if="bannerList.length !== 0">
|
||||||
<e-swiper :dotPosition="globalAppSettings.banner.dotPosition" :autoplay="true"
|
<e-swiper :height="bannerConfig.height" :dotPosition="bannerConfig.dotPosition" :autoplay="true"
|
||||||
:useDot="globalAppSettings.banner.useDot" :list="bannerList"
|
:useDot="bannerConfig.showIndicator" :showTitle="bannerConfig.showTitle"
|
||||||
@on-click="fnOnBannerClick"></e-swiper>
|
:type="bannerConfig.type" :list="bannerList" @on-click="fnOnBannerClick" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 金刚区 :v-if="navList.filter(x=>x.show).length>=4" -->
|
||||||
|
<view v-if="calcIsShowQuickNavigationEnabled && !!navList.filter(x=>x.show).length" class="nav-box mt-16 mb-24 pl-24 pr-24 uh-py-12">
|
||||||
|
<view class="page-item_title text-weight-b ">快捷导航</view>
|
||||||
|
<view class="nav-list flex uh-mt-12">
|
||||||
|
<template v-for="(item,index) in navList">
|
||||||
|
<view v-if="item.show" class="nav-item" :key="index" @click="fnClickNav(item)">
|
||||||
|
<!-- :class="[item.bgClass]" -->
|
||||||
|
<view class="nav-item-icon" :style="{
|
||||||
|
'--bgColor':item.bgColor,
|
||||||
|
boxShadow: '0rpx 0rpx 6rpx ' + item.shadow,
|
||||||
|
backgroundColor: item.bgColor
|
||||||
|
}">
|
||||||
|
<tm-icons :size="48" color="white" prefx="halocoloricon" :name="item.icon"></tm-icons>
|
||||||
|
</view>
|
||||||
|
<view class="nav-item-text">
|
||||||
|
{{item.title}}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 精品分类 -->
|
||||||
|
<block v-if="calcIsShowCategory">
|
||||||
<view class="flex flex-between mt-16 mb-24 pl-24 pr-24">
|
<view class="flex flex-between mt-16 mb-24 pl-24 pr-24">
|
||||||
<view class="page-item_title text-weight-b ">精品分类</view>
|
<view class="page-item_title text-weight-b ">精选分类</view>
|
||||||
<view class="show-more flex flex-center bg-white round-3" @click="fnToCategoryPage">
|
<view class="show-more flex flex-center bg-white round-3" @click="fnToCategoryPage">
|
||||||
<text class="iconfont icon-angle-right text-size-s text-grey-darken-1"></text>
|
<text class="iconfont icon-angle-right text-size-s text-grey-darken-1"></text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="false" class="flex flex-center text-size-s text-grey-darken-1" @click="fnToCategoryPage">
|
<view v-if="false" class="flex flex-center text-size-s text-grey-darken-1"
|
||||||
|
@click="fnToCategoryPage">
|
||||||
<text class=" text-size-m">查看更多</text>
|
<text class=" text-size-m">查看更多</text>
|
||||||
<text class="iconfont icon-angle-right text-size-s "></text>
|
<text class="iconfont icon-angle-right text-size-s "></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<scroll-view class="category" scroll-x="true">
|
<scroll-view class="category" scroll-x="true">
|
||||||
<view v-if="categoryList.length == 0" class="cate-empty round-3 mr-5 flex flex-center text-grey">
|
<view v-if="categoryList.length === 0" class="cate-empty round-3 mr-5 flex flex-center text-grey">
|
||||||
还没有任何文章分类~
|
还没有任何分类~
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view class="content" v-for="(category, index) in categoryList" :key="category.metadata.name"
|
<view class="content" v-for="(category, index) in categoryList" :key="category.metadata.name"
|
||||||
@@ -49,10 +75,11 @@
|
|||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</block>
|
||||||
|
|
||||||
<!-- 最新文章 -->
|
<!-- 最新文章 -->
|
||||||
<view class="flex flex-between mt-24 mb-24 pl-24 pr-24">
|
<view class="flex flex-between mt-24 mb-24 pl-24 pr-24">
|
||||||
<view class="page-item_title text-weight-b">文章列表</view>
|
<view class="page-item_title text-weight-b">最新列表</view>
|
||||||
<view class="show-more flex flex-center bg-white round-3" @click="fnToArticlesPage">
|
<view class="show-more flex flex-center bg-white round-3" @click="fnToArticlesPage">
|
||||||
<text class="iconfont icon-angle-right text-size-s text-grey-darken-1"></text>
|
<text class="iconfont icon-angle-right text-size-s text-grey-darken-1"></text>
|
||||||
</view>
|
</view>
|
||||||
@@ -61,9 +88,8 @@
|
|||||||
<text class="iconfont icon-angle-right text-size-s "></text>
|
<text class="iconfont icon-angle-right text-size-s "></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="articleList.length == 0" class="article-empty">
|
<view v-if="articleList.length === 0" class="article-empty">
|
||||||
<tm-empty icon="icon-shiliangzhinengduixiang-"
|
<tm-empty icon="icon-shiliangzhinengduixiang-" label="博主还没有发表任何内容~"></tm-empty>
|
||||||
label="博主还没有发表任何文章~"></tm-empty>
|
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view :class="globalAppSettings.layout.home">
|
<view :class="globalAppSettings.layout.home">
|
||||||
@@ -74,25 +100,32 @@
|
|||||||
</tm-translate>
|
</tm-translate>
|
||||||
</view>
|
</view>
|
||||||
<view class="load-text mt-12">{{ loadMoreText }}</view>
|
<view class="load-text mt-12">{{ loadMoreText }}</view>
|
||||||
<tm-flotbutton v-if="articleList.length > 10" color="light-blue" @click="fnToTopPage" size="m"
|
<tm-flotbutton v-if="articleList.length > 10" :width="90" color="light-blue" @click="fnToTopPage"
|
||||||
icon="icon-angle-up"></tm-flotbutton>
|
size="s" icon="icon-angle-up"></tm-flotbutton>
|
||||||
</block>
|
</block>
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 弹窗 -->
|
||||||
|
<NotifyDialog v-if="notify.show" :show="notify.show" :title="notify.data.title" :content="notify.data.content"
|
||||||
|
:url="notify.data.url" @on-change="fnOnNotifyChange"></NotifyDialog>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import tmMenubars from '@/tm-vuetify/components/tm-menubars/tm-menubars.vue';
|
import tmMenubars from '@/tm-vuetify/components/tm-menubars/tm-menubars.vue';
|
||||||
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||||
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
||||||
import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
|
import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
|
||||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
import tmGrid from '@/tm-vuetify/components/tm-grid/tm-grid.vue';
|
||||||
|
|
||||||
import eSwiper from '@/components/e-swiper/e-swiper.vue';
|
import eSwiper from '@/components/e-swiper/e-swiper.vue';
|
||||||
import qs from 'qs'
|
import NotifyDialog from "@/components/notify-dialog/notify-dialog.vue";
|
||||||
|
import qs from 'qs'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
tmMenubars,
|
tmMenubars,
|
||||||
tmSkeleton,
|
tmSkeleton,
|
||||||
@@ -100,7 +133,9 @@ export default {
|
|||||||
tmFlotbutton,
|
tmFlotbutton,
|
||||||
tmIcons,
|
tmIcons,
|
||||||
tmEmpty,
|
tmEmpty,
|
||||||
eSwiper
|
tmGrid,
|
||||||
|
eSwiper,
|
||||||
|
NotifyDialog
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -118,6 +153,11 @@ export default {
|
|||||||
noticeList: [],
|
noticeList: [],
|
||||||
articleList: [],
|
articleList: [],
|
||||||
categoryList: [],
|
categoryList: [],
|
||||||
|
notify: {
|
||||||
|
show: false,
|
||||||
|
data: {}
|
||||||
|
},
|
||||||
|
navList: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -133,12 +173,49 @@ export default {
|
|||||||
const appInfo = this.haloConfigs.appConfig.appInfo;
|
const appInfo = this.haloConfigs.appConfig.appInfo;
|
||||||
appInfo.logo = this.$utils.checkImageUrl(appInfo.logo)
|
appInfo.logo = this.$utils.checkImageUrl(appInfo.logo)
|
||||||
return appInfo;
|
return appInfo;
|
||||||
|
},
|
||||||
|
mockJson() {
|
||||||
|
return this.$tm.vx.getters().getMockJson;
|
||||||
|
},
|
||||||
|
calcAuditModeEnabled() {
|
||||||
|
return this.haloConfigs.auditConfig.auditModeEnabled
|
||||||
|
},
|
||||||
|
calcIsShowQuickNavigationEnabled() {
|
||||||
|
console.log('this.haloConfigs?.pageConfig?.homeConfig', this.haloConfigs?.pageConfig?.homeConfig, !!this.haloConfigs?.pageConfig?.homeConfig?.useQuickNavigation)
|
||||||
|
return !!this.haloConfigs?.pageConfig?.homeConfig?.useQuickNavigation
|
||||||
|
},
|
||||||
|
calcIsShowCategory() {
|
||||||
|
if (this.calcAuditModeEnabled && this.categoryList.length !== 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return this.haloConfigs.pageConfig.homeConfig.useCategory
|
||||||
|
},
|
||||||
|
calcVotePluginEnabled() {
|
||||||
|
return !!this.haloConfigs?.pluginConfig?.votePlugin?.enabled
|
||||||
|
},
|
||||||
|
calcLinksPluginEnabled() {
|
||||||
|
return !!this.haloConfigs?.pluginConfig?.linksPlugin?.enabled;
|
||||||
|
},
|
||||||
|
bannerConfig() {
|
||||||
|
return this.haloConfigs.pageConfig.homeConfig.bannerConfig
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
haloConfigs: {
|
||||||
|
handler(val) {
|
||||||
|
if (!val) return;
|
||||||
|
this.fnGetNavList();
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.fnSetPageTitle();
|
this.fnSetPageTitle();
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.fnQuery();
|
this.fnQuery();
|
||||||
},
|
},
|
||||||
@@ -147,8 +224,14 @@ export default {
|
|||||||
this.queryParams.page = 1;
|
this.queryParams.page = 1;
|
||||||
this.fnQuery();
|
this.fnQuery();
|
||||||
},
|
},
|
||||||
|
|
||||||
onReachBottom(e) {
|
onReachBottom(e) {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '没有更多数据了'
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
if (this.result.hasNext) {
|
if (this.result.hasNext) {
|
||||||
this.queryParams.page += 1;
|
this.queryParams.page += 1;
|
||||||
this.isLoadMore = true;
|
this.isLoadMore = true;
|
||||||
@@ -166,12 +249,36 @@ export default {
|
|||||||
this.fnGetArticleList();
|
this.fnGetArticleList();
|
||||||
this.fnGetCategoryList();
|
this.fnGetCategoryList();
|
||||||
},
|
},
|
||||||
|
|
||||||
fnGetCategoryList() {
|
fnGetCategoryList() {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
this.categoryList = this.mockJson.home.categoryList.map((item) => {
|
||||||
|
return {
|
||||||
|
metadata: {
|
||||||
|
name: Date.now() * Math.random(),
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
displayName: item.title,
|
||||||
|
cover: item.cover
|
||||||
|
},
|
||||||
|
postCount: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.calcIsShowCategory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.$httpApi.v2
|
this.$httpApi.v2
|
||||||
.getCategoryList({})
|
.getCategoryList({
|
||||||
|
fieldSelector: ['spec.hideFromList=false']
|
||||||
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.categoryList = res.items.sort((a, b) => {
|
this.categoryList = res.items.map(item=>{
|
||||||
|
item.postCount = item.postCount ?? 0
|
||||||
|
return item;
|
||||||
|
}).sort((a, b) => {
|
||||||
return b.postCount - a.postCount;
|
return b.postCount - a.postCount;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,22 +299,48 @@ export default {
|
|||||||
},
|
},
|
||||||
// 获取轮播图
|
// 获取轮播图
|
||||||
fnGetBanner() {
|
fnGetBanner() {
|
||||||
const _this = this;
|
if (this.calcAuditModeEnabled) {
|
||||||
const _format = function (list) {
|
this.bannerList = this.mockJson.home.bannerList.map((item) => {
|
||||||
return list.map((item, index) => {
|
|
||||||
return {
|
return {
|
||||||
mp4: '',
|
mp4: '',
|
||||||
id: item.metadata.name,
|
id: Date.now() * Math.random(),
|
||||||
nickname: item.owner.displayName,
|
nickname: this.haloConfigs.authorConfig.blogger.nickname,
|
||||||
avatar: _this.$utils.checkImageUrl(item.owner.avatar),
|
avatar: this.$utils.checkAvatarUrl(this.haloConfigs.authorConfig.blogger.avatar),
|
||||||
address: '',
|
address: '',
|
||||||
createTime: uni.$tm.dayjs(item.spec.publishTime).fromNow(),
|
createTime: item.time,
|
||||||
title: item.spec.title,
|
title: item.title,
|
||||||
src: _this.$utils.checkImageUrl(item.spec.cover),
|
src: this.$utils.checkThumbnailUrl(item.cover),
|
||||||
image: _this.$utils.checkImageUrl(item.spec.cover)
|
image: this.$utils.checkThumbnailUrl(item.cover),
|
||||||
};
|
type: "custom",
|
||||||
|
content: "",
|
||||||
|
url: ""
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!this.bannerConfig.enabled) return;
|
||||||
|
|
||||||
|
if (this.bannerConfig.type === 'custom') {
|
||||||
|
this.bannerList = this.bannerConfig.list.map((item) => {
|
||||||
|
return {
|
||||||
|
mp4: '',
|
||||||
|
id: Date.now() * Math.random(),
|
||||||
|
nickname: this.haloConfigs.authorConfig.blogger.nickname,
|
||||||
|
avatar: this.$utils.checkAvatarUrl(this.haloConfigs.authorConfig.blogger.avatar),
|
||||||
|
address: '',
|
||||||
|
createTime: "",
|
||||||
|
title: item.title,
|
||||||
|
src: this.$utils.checkThumbnailUrl(item.cover),
|
||||||
|
image: this.$utils.checkThumbnailUrl(item.cover),
|
||||||
|
type: "custom",
|
||||||
|
content: item.content,
|
||||||
|
url: item.url
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const paramsStr = qs.stringify(this.queryParams, {
|
const paramsStr = qs.stringify(this.queryParams, {
|
||||||
allowDots: true,
|
allowDots: true,
|
||||||
@@ -219,20 +352,54 @@ export default {
|
|||||||
uni.request({
|
uni.request({
|
||||||
url: this.$baseApiUrl + '/apis/api.content.halo.run/v1alpha1/posts?' + paramsStr,
|
url: this.$baseApiUrl + '/apis/api.content.halo.run/v1alpha1/posts?' + paramsStr,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: this.queryParams,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
this.bannerList = _format(res.data.items);
|
this.bannerList = res.data.items.map((item, index) => {
|
||||||
|
return {
|
||||||
|
mp4: '',
|
||||||
|
id: item.metadata.name,
|
||||||
|
nickname: item.owner.displayName,
|
||||||
|
avatar: this.$utils.checkAvatarUrl(item.owner.avatar),
|
||||||
|
address: '',
|
||||||
|
createTime: uni.$tm.dayjs(item.spec.publishTime).fromNow(),
|
||||||
|
title: item.spec.title,
|
||||||
|
src: this.$utils.checkThumbnailUrl(item.spec.cover),
|
||||||
|
image: this.$utils.checkThumbnailUrl(item.spec.cover),
|
||||||
|
type: "post",
|
||||||
|
content: item.status.excerpt,
|
||||||
|
url: ""
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
fnOnBannerChange(e) {
|
fnOnNotifyChange(e) {
|
||||||
this.bannerCurrent = e.current;
|
this.notify.show = e;
|
||||||
},
|
},
|
||||||
fnOnBannerClick(item) {
|
fnOnBannerClick(item) {
|
||||||
if (item.id == '') return;
|
if (this.calcAuditModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (item.type === 'custom') {
|
||||||
|
if (item.content) {
|
||||||
|
this.notify.data = item
|
||||||
|
this.notify.show = true
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (uni.$utils.checkIsUrl(item.url)) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pagesC/website/website?data=' +
|
||||||
|
JSON.stringify({
|
||||||
|
title: item.title || "加载中...",
|
||||||
|
url: encodeURIComponent(item.url)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.id === '') return;
|
||||||
this.fnToArticleDetail({
|
this.fnToArticleDetail({
|
||||||
metadata: {
|
metadata: {
|
||||||
name: item.id
|
name: item.id
|
||||||
@@ -241,10 +408,32 @@ export default {
|
|||||||
},
|
},
|
||||||
// 文章列表
|
// 文章列表
|
||||||
fnGetArticleList() {
|
fnGetArticleList() {
|
||||||
// uni.showLoading({
|
if (this.calcAuditModeEnabled) {
|
||||||
// mask: true,
|
this.articleList = this.mockJson.home.postList.map((item) => {
|
||||||
// title: '加载中...'
|
return {
|
||||||
// });
|
metadata: {
|
||||||
|
name: Date.now() * Math.random(),
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
pinned: false,
|
||||||
|
cover: item.cover,
|
||||||
|
title: item.title,
|
||||||
|
publishTime: item.time
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
excerpt: item.desc
|
||||||
|
},
|
||||||
|
stats: {
|
||||||
|
visit: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.loading = 'success';
|
||||||
|
this.loadMoreText = '呜呜,没有更多数据啦~';
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 设置状态为加载中
|
// 设置状态为加载中
|
||||||
if (!this.isLoadMore) {
|
if (!this.isLoadMore) {
|
||||||
this.loading = 'loading';
|
this.loading = 'loading';
|
||||||
@@ -261,7 +450,6 @@ export default {
|
|||||||
uni.request({
|
uni.request({
|
||||||
url: this.$baseApiUrl + '/apis/api.content.halo.run/v1alpha1/posts?' + paramsStr,
|
url: this.$baseApiUrl + '/apis/api.content.halo.run/v1alpha1/posts?' + paramsStr,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: this.queryParams,
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
const data = res.data;
|
const data = res.data;
|
||||||
this.result.hasNext = data.hasNext;
|
this.result.hasNext = data.hasNext;
|
||||||
@@ -283,9 +471,11 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
//跳转文章详情
|
//跳转文章详情
|
||||||
fnToArticleDetail(article) {
|
fnToArticleDetail(article) {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pagesA/article-detail/article-detail?name=' + article.metadata.name,
|
url: '/pagesA/article-detail/article-detail?name=' + article.metadata.name,
|
||||||
animationType: 'slide-in-right'
|
animationType: 'slide-in-right'
|
||||||
@@ -321,6 +511,9 @@ export default {
|
|||||||
|
|
||||||
// 根据slug查询分类下的文章
|
// 根据slug查询分类下的文章
|
||||||
fnToCategoryBy(category) {
|
fnToCategoryBy(category) {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pagesA/category-detail/category-detail?name=${category.metadata.name}&title=${category.spec.displayName}`
|
url: `/pagesA/category-detail/category-detail?name=${category.metadata.name}&title=${category.spec.displayName}`
|
||||||
});
|
});
|
||||||
@@ -339,13 +532,82 @@ export default {
|
|||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pagesA/articles/articles'
|
url: '/pagesA/articles/articles'
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
fnOnLogoToPage() {
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/tabbar/about/about'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fnClickNav(data) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: data.path
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fnGetNavList() {
|
||||||
|
this.navList = [{
|
||||||
|
key: 'archives',
|
||||||
|
title: this.calcAuditModeEnabled ? '内容归档' : '文章归档',
|
||||||
|
bgColor: "rgba(3, 169, 244, 0.95)",
|
||||||
|
shadow: "rgba(3, 169, 244, 0.4)",
|
||||||
|
bgClass: 'bg-gradient-blue-accent',
|
||||||
|
icon: 'icon-news',
|
||||||
|
iconColor: '',
|
||||||
|
path: '/pagesA/archives/archives',
|
||||||
|
type: 'page',
|
||||||
|
show: true
|
||||||
|
}, {
|
||||||
|
key: 'vote',
|
||||||
|
title: '投票中心',
|
||||||
|
bgColor: "rgba(0, 188, 212,0.95)",
|
||||||
|
shadow: "rgba(0, 188, 212, 0.4)",
|
||||||
|
bgClass: 'bg-gradient-blue-accent',
|
||||||
|
icon: 'icon-box',
|
||||||
|
iconColor: '',
|
||||||
|
path: '/pagesA/votes/votes',
|
||||||
|
type: 'page',
|
||||||
|
show: !this.calcAuditModeEnabled && !!this.calcVotePluginEnabled,
|
||||||
|
}, {
|
||||||
|
key: 'disclaimers',
|
||||||
|
title: '友情链接',
|
||||||
|
bgColor: "rgba(0, 150, 136, 0.95)",
|
||||||
|
shadow: "rgba(0, 150, 136, 0.4)",
|
||||||
|
bgClass: 'bg-gradient-blue-accent',
|
||||||
|
icon: 'icon-lianjie',
|
||||||
|
iconColor: '',
|
||||||
|
path: '/pagesA/friend-links/friend-links',
|
||||||
|
type: 'page',
|
||||||
|
show: !!this.calcLinksPluginEnabled,
|
||||||
|
}, {
|
||||||
|
key: 'love',
|
||||||
|
title: '恋爱日记',
|
||||||
|
bgColor: "rgba(255,76,103, 0.95)",
|
||||||
|
shadow: "rgba(255,76,103, 0.4)",
|
||||||
|
bgClass: 'bg-gradient-blue-accent',
|
||||||
|
icon: 'icon-like',
|
||||||
|
iconColor: '',
|
||||||
|
path: '/pagesA/love/love',
|
||||||
|
type: 'page',
|
||||||
|
show: this.haloConfigs.loveConfig.loveEnabled
|
||||||
|
}, {
|
||||||
|
key: 'contact-blogger',
|
||||||
|
title: '联系博主',
|
||||||
|
bgColor: "rgba(255, 152, 0, 0.95)",
|
||||||
|
shadow: "rgba(255, 152, 0, 0.4)",
|
||||||
|
bgClass: 'bg-gradient-blue-accent',
|
||||||
|
icon: 'icon-paper-plane',
|
||||||
|
iconColor: '',
|
||||||
|
rightText: '博主主常用常用联系方式',
|
||||||
|
path: '/pagesA/contact/contact',
|
||||||
|
type: 'page',
|
||||||
|
show: this.haloConfigs.authorConfig.social.enabled
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-page {
|
.app-page {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -363,38 +625,36 @@ export default {
|
|||||||
min-width: initial;
|
min-width: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-wrap {
|
.loading-wrap {
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
/* #ifdef MP-WEIXIN */
|
/* #ifdef MP-WEIXIN */
|
||||||
margin-right: 24rpx;
|
margin-right: 24rpx;
|
||||||
|
|
||||||
/* #endif */
|
/* #endif */
|
||||||
&_icon {
|
&_icon {}
|
||||||
|
|
||||||
|
&_text {}
|
||||||
}
|
}
|
||||||
|
|
||||||
&_text {
|
.show-more {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-more {
|
|
||||||
width: 42rpx;
|
width: 42rpx;
|
||||||
height: 42rpx;
|
height: 42rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.03);
|
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.banner {
|
.banner {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quick-nav {
|
.quick-nav {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
@@ -402,9 +662,9 @@ export default {
|
|||||||
.name {
|
.name {
|
||||||
color: var(--main-text-color);
|
color: var(--main-text-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.category {
|
.category {
|
||||||
width: 94vw;
|
width: 94vw;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 200rpx;
|
height: 200rpx;
|
||||||
@@ -423,9 +683,9 @@ export default {
|
|||||||
.cate-empty {
|
.cate-empty {
|
||||||
height: inherit;
|
height: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-item {
|
.page-item {
|
||||||
&_title {
|
&_title {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 24rpx;
|
padding-left: 24rpx;
|
||||||
@@ -445,9 +705,9 @@ export default {
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.h_row_col2 {
|
.h_row_col2 {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -456,5 +716,41 @@ export default {
|
|||||||
.ani-item {
|
.ani-item {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-box {
|
||||||
|
background-color: #ffff;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-list {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: space-between;
|
||||||
|
//justify-content: space-around;
|
||||||
|
//justify-content: start;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
font-size: 26rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item-icon {
|
||||||
|
padding: 24rpx;
|
||||||
|
// border-radius: 24rpx 32rpx 24rpx 32rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
// border: 2rpx solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,25 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="app-page">
|
<view class="app-page" :class="[uniHaloPluginPageClass]">
|
||||||
<view v-if="loading != 'success'" class="loading-wrap">
|
<PluginUnavailable v-if="!uniHaloPluginAvailable" :pluginId="uniHaloPluginId"
|
||||||
|
:error-text="uniHaloPluginAvailableError" />
|
||||||
|
<template v-else>
|
||||||
|
<view v-if="loading !== 'success'" class="loading-wrap">
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
</view>
|
</view>
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<view v-else class="app-page-content">
|
<view v-else class="app-page-content">
|
||||||
<view v-if="dataList.length == 0" class="content-empty flex flex-center" style="min-height: 70vh;">
|
<view v-if="dataList.length === 0" class="content-empty flex flex-center" style="min-height: 70vh;">
|
||||||
<!-- 空布局 -->
|
<!-- 空布局 -->
|
||||||
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无数据"></tm-empty>
|
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无数据"></tm-empty>
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<block v-for="(moment, index) in dataList" :key="index">
|
|
||||||
<!-- 卡片 -->
|
<!-- 卡片 -->
|
||||||
<tm-translate animation-name="fadeUp" :wait="calcAniWait()">
|
<tm-translate v-for="(moment, index) in dataList" :key="moment.metadata.name"
|
||||||
|
animation-name="fadeUp" :wait="calcAniWait(index)">
|
||||||
<view class="moment-card">
|
<view class="moment-card">
|
||||||
<view class="head" style="display: flex;align-items: center;">
|
<view class="head" style="display: flex;align-items: center;">
|
||||||
<view class="avatar" style="flex-shrink: 0;">
|
<view class="avatar" style="flex-shrink: 0;">
|
||||||
<image style="width: 66rpx;height: 66rpx;border-radius: 50%;"
|
<image style="width: 66rpx;height: 66rpx;border-radius: 50%;"
|
||||||
:src="moment.spec.user.avatar"/>
|
:src="moment.spec.user.avatar" />
|
||||||
</view>
|
</view>
|
||||||
<view class="nickname" style="margin-left: 12rpx;">
|
<view class="nickname" style="margin-left: 12rpx;">
|
||||||
<view style="font-size: 30rpx;font-weight: bold;color: #333333;">
|
<view style="font-size: 30rpx;font-weight: bold;color: #333333;">
|
||||||
@@ -30,48 +33,84 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="content">
|
<view class="content" @click.stop="handleToMomentDetail(moment)">
|
||||||
<mp-html class="evan-markdown" lazy-load :domain="markdownConfig.domain"
|
<mp-html class="evan-markdown" lazy-load :domain="markdownConfig.domain"
|
||||||
:loading-img="markdownConfig.loadingGif" :scroll-table="true" :selectable="true"
|
:loading-img="markdownConfig.loadingGif" :scroll-table="true" :selectable="true"
|
||||||
:tag-style="markdownConfig.tagStyle" :container-style="markdownConfig.containStyle"
|
:tag-style="markdownConfig.tagStyle" :container-style="markdownConfig.containStyle"
|
||||||
:content="moment.spec.content.html" :markdown="true" :showLineNumber="true"
|
:content="moment.spec.newHtml" :markdown="true" :showLineNumber="true"
|
||||||
:showLanguageName="true" :copyByLongPress="true"/>
|
:showLanguageName="true" :copyByLongPress="true" />
|
||||||
</view>
|
</view>
|
||||||
<view v-if="moment.spec.content.medium.length!==0" class="images"
|
<view v-if="moment.images && moment.images.length!==0" class="images"
|
||||||
:class="['images-'+moment.spec.content.medium.length]">
|
:class="['images-'+moment.images.length]">
|
||||||
<view class="image-item" v-for="(image,mediumIndex) in moment.spec.content.medium"
|
<view class="image-item" v-for="(image,mediumIndex) in moment.images"
|
||||||
:key="mediumIndex">
|
:key="mediumIndex">
|
||||||
<image mode="aspectFill" style="width: 100%;height: 100%;border-radius: 6rpx;"
|
<image mode="aspectFill" style="width: 100%;height: 100%;border-radius: 6rpx;"
|
||||||
:src="image.url"
|
:src="image.url" @click="handlePreview(mediumIndex,moment.images)" />
|
||||||
@click="handlePreview(mediumIndex,moment.spec.content.medium)"/>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="moment.audios && moment.audios.length!==0" class="mb-12"
|
||||||
|
style="display: flex; flex-direction: column; gap: 12rpx 0;padding: 0 24rpx;padding-right:28rpx;">
|
||||||
|
<audio v-for="(audio,index) in moment.audios" :controls="true" :key="index"
|
||||||
|
:id="audio.url" :poster="bloggerInfo.avatar"
|
||||||
|
:name="'来自' + (startConfig.title||bloggerInfo.nickname) + '的声音'"
|
||||||
|
:author="bloggerInfo.nickname" :src="audio.url"></audio>
|
||||||
|
</view>
|
||||||
|
<view v-if="moment.videos && moment.videos.length!==0" class="mb-12"
|
||||||
|
style="display: flex; flex-direction: column; gap: 12rpx 0;padding: 0 24rpx; ">
|
||||||
|
<video style="width:100%;height: 400rpx;border-radius: 12rpx;"
|
||||||
|
v-for="(video,index) in moment.videos" :key="index" :src="video.url"
|
||||||
|
:id="'video_' + video.id" :show-mute-btn="true" :controls="true"
|
||||||
|
:show-center-play-btn="true" :enable-progress-gesture="true"
|
||||||
|
@play="onVideoPlay(video.id)" @pause="onVideoPause(video.id)"
|
||||||
|
@ended="onVideoEnded(video.id)"></video>
|
||||||
|
</view>
|
||||||
|
<view v-if="moment.spec.tags && moment.spec.tags.length!==0"
|
||||||
|
class="mt-12 px-16 pb-24 flex flex-wrap">
|
||||||
|
<tm-tags v-for="(tag,tagIndex) in moment.spec.tags" :key="tagIndex"
|
||||||
|
:color="randomTagColor()" size="m" model="text">
|
||||||
|
{{ tag }}
|
||||||
|
</tm-tags>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</tm-translate>
|
</tm-translate>
|
||||||
</block>
|
<tm-flotbutton @click="fnToTopPage" :width="90" size="xs" color="light-blue" :icon-size="24"
|
||||||
<tm-flotbutton @click="fnToTopPage" size="m" color="light-blue" icon="icon-angle-up"></tm-flotbutton>
|
icon="icon-angle-up"></tm-flotbutton>
|
||||||
<view class="load-text">{{ loadMoreText }}</view>
|
<view class="load-text">{{ loadMoreText }}</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
||||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
|
|
||||||
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
||||||
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
||||||
|
import {
|
||||||
|
getRandomNumberByRange
|
||||||
|
} from "@/utils/random.js";
|
||||||
|
import {
|
||||||
|
generateUUID
|
||||||
|
} from '@/utils/uuid.js';
|
||||||
|
|
||||||
export default {
|
import pluginAvailableMixin from "@/common/mixins/pluginAvailable.js"
|
||||||
|
import PluginUnavailable from '@/components/plugin-unavailable/plugin-unavailable.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [pluginAvailableMixin],
|
||||||
components: {
|
components: {
|
||||||
tmSkeleton,
|
tmSkeleton,
|
||||||
tmFlotbutton,
|
tmFlotbutton,
|
||||||
tmTranslate,
|
tmTranslate,
|
||||||
tmEmpty,
|
tmEmpty,
|
||||||
mpHtml
|
tmTags,
|
||||||
|
mpHtml,
|
||||||
|
PluginUnavailable
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -84,7 +123,10 @@ export default {
|
|||||||
hasNext: false,
|
hasNext: false,
|
||||||
dataList: [],
|
dataList: [],
|
||||||
isLoadMore: false,
|
isLoadMore: false,
|
||||||
loadMoreText: '加载中...'
|
loadMoreText: '加载中...',
|
||||||
|
tagColors: ['orange', 'green', 'red', 'blue'],
|
||||||
|
videoContexts: {},
|
||||||
|
currentVideoId: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -93,19 +135,52 @@ export default {
|
|||||||
let blogger = this.$tm.vx.getters().getConfigs.authorConfig.blogger;
|
let blogger = this.$tm.vx.getters().getConfigs.authorConfig.blogger;
|
||||||
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
|
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
|
||||||
return blogger;
|
return blogger;
|
||||||
|
},
|
||||||
|
haloConfigs() {
|
||||||
|
return this.$tm.vx.getters().getConfigs;
|
||||||
|
},
|
||||||
|
mockJson() {
|
||||||
|
return this.$tm.vx.getters().getMockJson;
|
||||||
|
},
|
||||||
|
calcAuditModeEnabled() {
|
||||||
|
return this.haloConfigs.auditConfig.auditModeEnabled
|
||||||
|
},
|
||||||
|
calcUseTagRandomColor() {
|
||||||
|
return this.haloConfigs.pageConfig.momentConfig.useTagRandomColor
|
||||||
|
},
|
||||||
|
startConfig() {
|
||||||
|
return this.haloConfigs.appConfig.startConfig;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async onLoad() {
|
||||||
|
// 检查插件
|
||||||
|
this.setPluginId(this.NeedPluginIds.PluginMoments)
|
||||||
|
this.setPluginError("阿偶,检测到当前插件没有安装或者启用,无法使用瞬间功能哦,请联系管理员")
|
||||||
|
if (!await this.checkPluginAvailable()) return
|
||||||
|
|
||||||
onLoad() {
|
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
},
|
},
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
|
if (!this.uniHaloPluginAvailable) {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return
|
||||||
|
}
|
||||||
this.isLoadMore = false;
|
this.isLoadMore = false;
|
||||||
this.queryParams.page = 0;
|
this.queryParams.page = 0;
|
||||||
|
this.videoContexts = {};
|
||||||
|
this.currentVideoId = null;
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
},
|
},
|
||||||
|
|
||||||
onReachBottom(e) {
|
onReachBottom(e) {
|
||||||
|
if (!this.uniHaloPluginAvailable) return;
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '没有更多数据了'
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
if (this.hasNext) {
|
if (this.hasNext) {
|
||||||
this.queryParams.page += 1;
|
this.queryParams.page += 1;
|
||||||
this.isLoadMore = true;
|
this.isLoadMore = true;
|
||||||
@@ -119,6 +194,38 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fnGetData() {
|
fnGetData() {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
this.dataList = this.mockJson.moments.list.map((item) => {
|
||||||
|
return {
|
||||||
|
metadata: {
|
||||||
|
name: Date.now() * Math.random(),
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
user: {
|
||||||
|
displayName: this.bloggerInfo.nickname,
|
||||||
|
avatar: this.$utils.checkAvatarUrl(this.bloggerInfo.avatar),
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
html: item.content
|
||||||
|
},
|
||||||
|
releaseTime: item.time,
|
||||||
|
visible: "PUBLIC"
|
||||||
|
},
|
||||||
|
images: item.images.map((img) => {
|
||||||
|
return {
|
||||||
|
type: "PHOTO",
|
||||||
|
url: this.$utils.checkThumbnailUrl(img),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
videos: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.loading = 'success';
|
||||||
|
this.loadMoreText = '呜呜,没有更多数据啦~';
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
mask: true,
|
mask: true,
|
||||||
title: '加载中...'
|
title: '加载中...'
|
||||||
@@ -131,23 +238,31 @@ export default {
|
|||||||
this.$httpApi.v2
|
this.$httpApi.v2
|
||||||
.getMomentList(this.queryParams)
|
.getMomentList(this.queryParams)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
console.log('请求结果:');
|
|
||||||
console.log(res);
|
|
||||||
|
|
||||||
this.loading = 'success';
|
this.loading = 'success';
|
||||||
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
||||||
this.hasNext = res.hasNext;
|
this.hasNext = res.hasNext;
|
||||||
|
|
||||||
const tempItems = res.items.map(item => {
|
const tempItems = res.items.filter(x => x.spec.visible === 'PUBLIC').map(item => {
|
||||||
item.spec.user = {
|
item.spec.user = {
|
||||||
displayName: this.bloggerInfo.nickname,
|
displayName: this.bloggerInfo.nickname,
|
||||||
avatar: this.$utils.checkAvatarUrl(this.bloggerInfo.avatar)
|
avatar: this.$utils.checkAvatarUrl(this.bloggerInfo.avatar)
|
||||||
}
|
}
|
||||||
item.spec.content.medium
|
item.spec.content.medium.map(medium => {
|
||||||
.filter(x => x.type === 'PHOTO')
|
|
||||||
.map(medium => {
|
|
||||||
medium.url = this.$utils.checkThumbnailUrl(medium.url, true)
|
medium.url = this.$utils.checkThumbnailUrl(medium.url, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
item.spec.newHtml = this.removeTagLinksCompletely(item.spec.content.html, '')
|
||||||
|
item['images'] = item.spec.content.medium
|
||||||
|
.filter(x => x.type === 'PHOTO')
|
||||||
|
|
||||||
|
item['videos'] = item.spec.content.medium
|
||||||
|
.filter(x => x.type === 'VIDEO').map(item => {
|
||||||
|
item.id = generateUUID()
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
|
||||||
|
item['audios'] = item.spec.content.medium
|
||||||
|
.filter(x => x.type === 'AUDIO')
|
||||||
return item;
|
return item;
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -156,6 +271,11 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.dataList = tempItems;
|
this.dataList = tempItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.createVideoContexts(tempItems);
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -174,24 +294,69 @@ export default {
|
|||||||
current: index,
|
current: index,
|
||||||
urls: list.map(item => item.url)
|
urls: list.map(item => item.url)
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
removeTagLinksCompletely(htmlString) {
|
||||||
|
const regex = /<a\s+(?:[^>]*?\s+)?class=(['"])[^'"]*?\btag\b[^'"]*?\1[^>]*?>.*?<\/a>/gi;
|
||||||
|
const newHtml = htmlString.replace(regex, '');
|
||||||
|
return newHtml
|
||||||
|
},
|
||||||
|
randomTagColor() {
|
||||||
|
if (!this.calcUseTagRandomColor) return "blue";
|
||||||
|
const randomIndex = getRandomNumberByRange(0, this.tagColors.length);
|
||||||
|
return this.tagColors[randomIndex];
|
||||||
|
},
|
||||||
|
createVideoContexts(list) {
|
||||||
|
this.stopAllVideos()
|
||||||
|
list.map(item => item.videos).flat().forEach(item => {
|
||||||
|
this.videoContexts[item.id] = uni.createVideoContext(`video_${item.id}`, this);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onVideoPlay(videoId) {
|
||||||
|
this.currentVideoId = videoId;
|
||||||
|
this.stopAllVideos(videoId)
|
||||||
|
},
|
||||||
|
onVideoPause(videoId) {
|
||||||
|
if (this.currentVideoId == videoId) {
|
||||||
|
this.currentVideoId = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onVideoEnded(videoId) {
|
||||||
|
this.currentVideoId = null;
|
||||||
|
},
|
||||||
|
stopAllVideos(excludesVideoId = null) {
|
||||||
|
Object.keys(this.videoContexts).forEach(videoId => {
|
||||||
|
if (!excludesVideoId || excludesVideoId != videoId) {
|
||||||
|
const videoContext = this.videoContexts[videoId]
|
||||||
|
videoContext?.pause();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleToMomentDetail(moment) {
|
||||||
|
if (this.calcAuditModeEnabled) return;
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pagesA/moment-detail/moment-detail?name=' + moment.metadata.name,
|
||||||
|
animationType: 'slide-in-right'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-page {
|
.app-page {
|
||||||
|
box-sizing: border-box;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 24rpx 0;
|
padding: 24rpx 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-wrap {
|
.loading-wrap {
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.moment-card {
|
.moment-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -201,14 +366,14 @@ export default {
|
|||||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.head {
|
.head {
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.images {
|
.images {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -224,17 +389,22 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-1 {
|
&-1 {
|
||||||
> .image-item {
|
>.image-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 350rpx
|
height: 350rpx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-2 {
|
&-2 {
|
||||||
> .image-item {
|
>.image-item {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
height: 250rpx
|
height: 250rpx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep .uni-audio-default {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="app-page">
|
|
||||||
<view v-if="loading != 'success'" class="loading-wrap">
|
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
|
||||||
</view>
|
|
||||||
<!-- 内容区域 -->
|
|
||||||
<view v-else class="app-page-content">
|
|
||||||
<view v-if="dataList.length == 0" class="content-empty flex flex-center">
|
|
||||||
<!-- 空布局 -->
|
|
||||||
<tm-empty icon="icon-shiliangzhinengduixiang-" label="该分类下暂无数据"></tm-empty>
|
|
||||||
</view>
|
|
||||||
<block v-else>
|
|
||||||
<block v-for="(item, index) in dataList" :key="index">
|
|
||||||
<!-- 卡片 -->
|
|
||||||
<tm-translate animation-name="fadeUp" :wait="(index + 1) * 50">
|
|
||||||
<!-- 列表项 -->
|
|
||||||
{{ item }}
|
|
||||||
</tm-translate>
|
|
||||||
</block>
|
|
||||||
<tm-flotbutton @click="fnToTopPage" size="m" color="light-blue" icon="icon-angle-up"></tm-flotbutton>
|
|
||||||
<view class="load-text">{{ loadMoreText }}</view>
|
|
||||||
</block>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
|
||||||
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
|
||||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
|
||||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
tmSkeleton,
|
|
||||||
tmFlotbutton,
|
|
||||||
tmTranslate,
|
|
||||||
tmEmpty
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: 'loading',
|
|
||||||
queryParams: {
|
|
||||||
size: 10,
|
|
||||||
page: 0
|
|
||||||
},
|
|
||||||
result: null,
|
|
||||||
dataList: [],
|
|
||||||
hasNext:false,
|
|
||||||
isLoadMore: false,
|
|
||||||
loadMoreText: '加载中...'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onLoad() {
|
|
||||||
this.fnSetPageTitle();
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.fnGetData();
|
|
||||||
},
|
|
||||||
onPullDownRefresh() {
|
|
||||||
this.isLoadMore = false;
|
|
||||||
this.queryParams.page = 0;
|
|
||||||
this.fnGetData();
|
|
||||||
},
|
|
||||||
|
|
||||||
onReachBottom(e) {
|
|
||||||
if (this.result.hasNext) {
|
|
||||||
this.queryParams.page += 1;
|
|
||||||
this.isLoadMore = true;
|
|
||||||
this.fnGetData();
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
icon: 'none',
|
|
||||||
title: '没有更多数据了'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fnGetData() {
|
|
||||||
return;
|
|
||||||
uni.showLoading({
|
|
||||||
mask: true,
|
|
||||||
title: '加载中...'
|
|
||||||
});
|
|
||||||
// 设置状态为加载中
|
|
||||||
if (!this.isLoadMore) {
|
|
||||||
this.loading = 'loading';
|
|
||||||
}
|
|
||||||
this.loadMoreText = '加载中...';
|
|
||||||
this.$httpApi
|
|
||||||
.getXXX(this.queryParams)
|
|
||||||
.then(res => {
|
|
||||||
console.log('请求结果:');
|
|
||||||
console.log(res);
|
|
||||||
|
|
||||||
this.loading = 'success';
|
|
||||||
this.loadMoreText = res.data.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
|
||||||
// 处理数据
|
|
||||||
this.result = res.data;
|
|
||||||
if (this.isLoadMore) {
|
|
||||||
this.dataList = this.dataList.concat(res.data.content);
|
|
||||||
} else {
|
|
||||||
this.dataList = res.data.content;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
this.loading = 'error';
|
|
||||||
this.loadMoreText = '加载失败,请下拉刷新!';
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.stopPullDownRefresh();
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.app-page {
|
|
||||||
width: 100vw;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 24rpx 0;
|
|
||||||
padding-bottom: 144rpx;
|
|
||||||
}
|
|
||||||
.loading-wrap {
|
|
||||||
padding: 24rpx;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -10,11 +10,11 @@
|
|||||||
<tm-listitem title="开源组织" :font-size="28" @click="$utils.copyText('https://www.ialley.cn', '巷子工坊官网已复制成功!')">
|
<tm-listitem title="开源组织" :font-size="28" @click="$utils.copyText('https://www.ialley.cn', '巷子工坊官网已复制成功!')">
|
||||||
<text class="text-size-s" slot="rightValue">巷子工坊</text>
|
<text class="text-size-s" slot="rightValue">巷子工坊</text>
|
||||||
</tm-listitem>
|
</tm-listitem>
|
||||||
<tm-listitem title="开源作者" :font-size="28" @click="$utils.copyText('https://b.925i.cn', '作者博客地址已复制')">
|
<tm-listitem title="开源作者" :font-size="28" @click="$utils.copyText('https://www.xiaoxiaomo.cn', '作者主页地址已复制')">
|
||||||
<text class="text-size-s" slot="rightValue">小莫唐尼</text>
|
<text class="text-size-s" slot="rightValue">小莫唐尼</text>
|
||||||
</tm-listitem>
|
</tm-listitem>
|
||||||
<tm-listitem title="作者博客" :font-size="28" @click="$utils.copyText('https://b.925i.cn', '作者博客地址已复制')">
|
<tm-listitem title="作者博客" :font-size="28" @click="$utils.copyText('https://blog.xiaoxiaomo.cn', '作者博客地址已复制')">
|
||||||
<text class="text-size-s text-overflow" slot="rightValue">https://b.925i.cn</text>
|
<text class="text-size-s text-overflow" slot="rightValue">https://blog.xiaoxiaomo.cn</text>
|
||||||
</tm-listitem>
|
</tm-listitem>
|
||||||
<tm-listitem title="文档地址" :font-size="28" @click="$utils.copyText('https://uni-halo.925i.cn', '项目码云仓库已复制')">
|
<tm-listitem title="文档地址" :font-size="28" @click="$utils.copyText('https://uni-halo.925i.cn', '项目码云仓库已复制')">
|
||||||
<text class="text-size-s text-overflow" slot="rightValue">https://uni-halo.925i.cn</text>
|
<text class="text-size-s text-overflow" slot="rightValue">https://uni-halo.925i.cn</text>
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
<template>
|
|
||||||
<tm-fullView>
|
|
||||||
<tm-sheet :shadow="24">
|
|
||||||
<tm-alerts label="观看视频即可获取注册码" close></tm-alerts>
|
|
||||||
<tm-divider color="red" model="dashed" :text="codeDataShow?'请复制下方注册码':'请点击“获取注册码”'"></tm-divider>
|
|
||||||
<view class="ma-20" v-show="!codeDataShow">
|
|
||||||
<tm-button theme="bg-gradient-orange-accent" :round="24" block @click="openVideoAd">获取注册码</tm-button>
|
|
||||||
</view>
|
|
||||||
<view class="ma-20" v-show="codeDataShow">
|
|
||||||
<tm-coupon :hdata="codeData" color="orange" @click="fnCopyCode"></tm-coupon>
|
|
||||||
</view>
|
|
||||||
</tm-sheet>
|
|
||||||
<!-- <tm-sheet :shadow="24">
|
|
||||||
<tm-images @load="loadimg" src="https://picsum.photos/300?id=7"></tm-images>
|
|
||||||
</tm-sheet> -->
|
|
||||||
</tm-fullView>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
let videoAd = null;
|
|
||||||
import tmFullView from "@/tm-vuetify/components/tm-fullView/tm-fullView.vue"
|
|
||||||
import tmMenubars from "@/tm-vuetify/components/tm-menubars/tm-menubars.vue"
|
|
||||||
import tmSheet from "@/tm-vuetify/components/tm-sheet/tm-sheet.vue"
|
|
||||||
import tmAlerts from "@/tm-vuetify/components/tm-alerts/tm-alerts.vue"
|
|
||||||
import tmDivider from "@/tm-vuetify/components/tm-divider/tm-divider.vue"
|
|
||||||
import tmCoupon from '@/tm-vuetify/components/tm-coupon/tm-coupon.vue'
|
|
||||||
import tmButton from "@/tm-vuetify/components/tm-button/tm-button.vue"
|
|
||||||
import tmImages from "@/tm-vuetify/components/tm-images/tm-images.vue"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
tmFullView,
|
|
||||||
tmMenubars,
|
|
||||||
tmSheet,
|
|
||||||
tmAlerts,
|
|
||||||
tmDivider,
|
|
||||||
tmCoupon,
|
|
||||||
tmButton,
|
|
||||||
tmImages
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
adUnitId: '',
|
|
||||||
codeDataShow: false,
|
|
||||||
codeData: {
|
|
||||||
// img: 'https://lywq.muyin.site/logo.png',
|
|
||||||
title: "请获取",
|
|
||||||
btnText: '复制',
|
|
||||||
time: '有效期5分钟',
|
|
||||||
sale: '',
|
|
||||||
saleLable: '注册码',
|
|
||||||
saleSplit: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
adConfigs() {
|
|
||||||
return this.$tm.vx.getters().getConfigs.adConfig;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onLoad(options) {
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
wx.hideShareMenu();
|
|
||||||
this.adLoad();
|
|
||||||
// #endif
|
|
||||||
uni.onCopyUrl((result) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.setClipboardData({
|
|
||||||
data: "禁止复制哦",
|
|
||||||
})
|
|
||||||
}, 1000);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
adLoad() {
|
|
||||||
if (wx.createRewardedVideoAd) {
|
|
||||||
videoAd = wx.createRewardedVideoAd({
|
|
||||||
adUnitId: this.adConfigs.rewardedVideoAdId //你的广告key
|
|
||||||
})
|
|
||||||
videoAd.onError(err => {
|
|
||||||
})
|
|
||||||
videoAd.onClose((status) => {
|
|
||||||
if (status && status.isEnded || status === undefined) {
|
|
||||||
//这里写广告播放完成后的事件
|
|
||||||
this.getRegistrationCode();
|
|
||||||
} else {
|
|
||||||
// 广告播放未完成
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openVideoAd: function () {
|
|
||||||
if (videoAd && this.adConfigs.rewardedVideoAdId != '') {
|
|
||||||
videoAd.show().catch(err => {
|
|
||||||
// 失败重试
|
|
||||||
console.log("广告拉取失败")
|
|
||||||
videoAd.load().then(() => videoAd.show())
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.getRegistrationCode();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getRegistrationCode() {
|
|
||||||
uni.showLoading({
|
|
||||||
title: '正在获取...'
|
|
||||||
});
|
|
||||||
this.$httpApi.v2.getPostVerifyCode()
|
|
||||||
.then(res => {
|
|
||||||
if (res.code === 200) {
|
|
||||||
uni.$tm.toast('获取成功!');
|
|
||||||
this.codeData.title = res.data;
|
|
||||||
this.codeDataShow = true;
|
|
||||||
} else {
|
|
||||||
uni.$tm.toast('操作失败,请重试!');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
uni.$tm.toast(err.message);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fnCopyCode() {
|
|
||||||
uni.setClipboardData({
|
|
||||||
data: this.codeData.title,
|
|
||||||
showToast: false,
|
|
||||||
success: () => {
|
|
||||||
uni.showToast({
|
|
||||||
icon: 'none',
|
|
||||||
title: '复制成功!'
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateBack()
|
|
||||||
}, 500);
|
|
||||||
},
|
|
||||||
fail: () => {
|
|
||||||
uni.showToast({
|
|
||||||
icon: 'none',
|
|
||||||
title: '复制失败!'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
+273
-52
@@ -1,13 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="app-page">
|
<view class="app-page">
|
||||||
|
|
||||||
<view class="e-fixed">
|
<view class="e-fixed">
|
||||||
<tm-tabs color="light-blue" v-model="tab.activeIndex" :list="tab.list" align="center" @change="fnOnTabChange"></tm-tabs>
|
<tm-tabs color="light-blue" v-model="tab.activeIndex" :list="tab.list" align="center"
|
||||||
|
@change="fnOnTabChange"></tm-tabs>
|
||||||
</view>
|
</view>
|
||||||
<!-- 占位区域 -->
|
<!-- 占位区域 -->
|
||||||
<view style="width: 100vw;height: 90rpx;"></view>
|
<view style="width: 100vw;height: 90rpx;"></view>
|
||||||
|
|
||||||
<!-- 骨架屏:加载区域 -->
|
<!-- 骨架屏:加载区域 -->
|
||||||
<view v-if="loading != 'success'" class="loading-wrap">
|
<view v-if="loading !== 'success'" class="loading-wrap">
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
@@ -15,46 +17,55 @@
|
|||||||
|
|
||||||
<!-- 加载完成区域 -->
|
<!-- 加载完成区域 -->
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view v-if="dataList.length == 0" class="list-empty flex flex-center">
|
<view v-if="dataList.length === 0" class="list-empty flex flex-center">
|
||||||
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无归档的文章"></tm-empty>
|
<tm-empty v-if="calcAuditModeEnabled" icon="icon-shiliangzhinengduixiang-" label="暂无归档的内容"></tm-empty>
|
||||||
|
<tm-empty v-else icon="icon-shiliangzhinengduixiang-" label="暂无归档的文章"></tm-empty>
|
||||||
</view>
|
</view>
|
||||||
<view v-else class="e-timeline tm-timeline mt-24">
|
<view v-else class="e-timeline tm-timeline mt-24">
|
||||||
<block v-for="(item, index) in dataList" :key="index">
|
<block v-for="(item, index) in dataList" :key="index">
|
||||||
<view class="tm-timeline-item tm-timeline-item--leftDir">
|
<view class="tm-timeline-item tm-timeline-item--leftDir">
|
||||||
<view style="width: 160rpx;">
|
<view style="width: 160rpx;">
|
||||||
<view :style="{ width: '24rpx', height: '24rpx' }" :class="[black_tmeme ? 'bk' : '']"
|
<view :style="{ width: '24rpx', height: '24rpx' }" :class="[black_tmeme ? 'bk' : '']"
|
||||||
class="flex-center rounded tm-timeline-jidian border-white-a-2 grey-lighten-2 light-blue shadow-primary-4"></view>
|
class="flex-center rounded tm-timeline-jidian border-white-a-2 grey-lighten-2 light-blue shadow-primary-4">
|
||||||
|
</view>
|
||||||
<view :style="{ marginTop: '-24rpx' }"
|
<view :style="{ marginTop: '-24rpx' }"
|
||||||
:class="[index !== dataList.length - 1 ? 'tm-timeline-item-boder' : '', black_tmeme ? 'bk' : '']"
|
:class="[index !== dataList.length - 1 ? 'tm-timeline-item-boder' : '', black_tmeme ? 'bk' : '']"
|
||||||
class="grey-lighten-2"></view>
|
class="grey-lighten-2"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="tm-timeline-item-content relative">
|
<view class="tm-timeline-item-content relative">
|
||||||
<view class="tm-timeline-item-left">
|
<view class="tm-timeline-item-left">
|
||||||
<view class="time text-weight-b mb-24">
|
<view class="flex time text-weight-b mb-24">
|
||||||
{{ item.year }}年
|
<text>{{ item.year }}年</text>
|
||||||
<block v-if="tab.activeIndex == 0">{{ item.month }}月</block>
|
<text v-if="tab.activeIndex === 0">{{ item.month }}月</text>
|
||||||
<text class="text-size-s text-grey-darken-1 ml-24">(共发布 {{ item.posts.length }} 篇文章)</text>
|
<view v-if="calcAuditModeEnabled" class="text-size-s text-grey-darken-1 ml-12">
|
||||||
|
(共 {{ item.posts.length }} 篇内容)
|
||||||
</view>
|
</view>
|
||||||
<block v-if="item.posts.length != 0">
|
<view v-else class="text-size-s text-grey-darken-1 ml-12">
|
||||||
<block v-for="(post, postIndex) in item.posts" :key="post.id">
|
(共 {{ item.posts.length }} 篇文章)
|
||||||
<tm-translate animation-name="fadeUp" :wait="calcAniWait(postIndex)">
|
</view>
|
||||||
<view class="flex post shadow-3 pa-24 mb-24" :class="[globalAppSettings.layout.cardType]"
|
</view>
|
||||||
|
<block v-if="item.posts.length !== 0">
|
||||||
|
<block v-for="(post, postIndex) in item.posts" :key="post.metadata.name">
|
||||||
|
<view class="flex post shadow-3 pa-24 mb-24"
|
||||||
|
:class="[globalAppSettings.layout.cardType]"
|
||||||
@click="fnToArticleDetail(post)">
|
@click="fnToArticleDetail(post)">
|
||||||
<image class="post-thumbnail" :src="$utils.checkThumbnailUrl(post.thumbnail)"
|
<image class="post-thumbnail"
|
||||||
mode="aspectFill"></image>
|
:src="$utils.checkThumbnailUrl(post.spec.cover)" mode="aspectFill">
|
||||||
|
</image>
|
||||||
<view class="post-info pl-20">
|
<view class="post-info pl-20">
|
||||||
<view class="post-info_title text-overflow">{{ post.title }}</view>
|
<view class="post-info_title text-overflow">{{ post.spec.title }}
|
||||||
<view class="post-info_summary text-overflow-2 mt-12 text-size-s text-grey-darken-1">
|
</view>
|
||||||
{{ post.summary }}
|
<view
|
||||||
|
class="post-info_summary text-overflow-2 mt-12 text-size-s text-grey-darken-1">
|
||||||
|
{{ post.status.excerpt }}
|
||||||
</view>
|
</view>
|
||||||
<view class="post-info_time mt-12 text-size-s text-grey-darken-1">
|
<view class="post-info_time mt-12 text-size-s text-grey-darken-1">
|
||||||
<text class="iconfont icon-clock text-size-s mr-6"></text>
|
<text class="iconfont icon-clock text-size-s mr-6"></text>
|
||||||
<text class="time-label">发布时间:</text>
|
<text class="time-label">发布时间:</text>
|
||||||
{{ {d: post.createTime, f: 'yyyy年MM月dd日 星期w'} | formatTime }}
|
{{ {d: post.spec.publishTime, f: 'yyyy年MM月dd日 星期w'} | formatTime }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</tm-translate>
|
|
||||||
</block>
|
</block>
|
||||||
</block>
|
</block>
|
||||||
<view v-else class="post-empty text-size-m text-grey-darken-1">该日期下暂无归档文章!</view>
|
<view v-else class="post-empty text-size-m text-grey-darken-1">该日期下暂无归档文章!</view>
|
||||||
@@ -63,8 +74,10 @@
|
|||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="load-text mt-12">{{ loadMoreText }}</view>
|
||||||
<!-- 返回顶部 -->
|
<!-- 返回顶部 -->
|
||||||
<tm-flotbutton @click="fnToTopPage" size="m" color="bg-gradient-light-blue-accent" icon="icon-angle-up"></tm-flotbutton>
|
<tm-flotbutton @click="fnToTopPage" size="m" color="bg-gradient-light-blue-accent"
|
||||||
|
icon="icon-angle-up"></tm-flotbutton>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -75,6 +88,7 @@ import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
|||||||
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
||||||
import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
|
import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
|
||||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
import qs from 'qs'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -93,11 +107,13 @@ export default {
|
|||||||
},
|
},
|
||||||
queryParams: {
|
queryParams: {
|
||||||
size: 10,
|
size: 10,
|
||||||
page: 0
|
page: 1
|
||||||
},
|
},
|
||||||
result: null,
|
result: {},
|
||||||
dataList: [],
|
cacheDataList: [], // 所有请求的缓存数据
|
||||||
api: 'getMonthArchives'
|
dataList: [], // 显示的数据
|
||||||
|
isLoadMore: false,
|
||||||
|
loadMoreText: "加载中..."
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -106,53 +122,245 @@ export default {
|
|||||||
},
|
},
|
||||||
color_tmeme: function () {
|
color_tmeme: function () {
|
||||||
return this.$tm.vx.state().tmVuetify.color;
|
return this.$tm.vx.state().tmVuetify.color;
|
||||||
}
|
},
|
||||||
|
haloConfigs() {
|
||||||
|
return this.$tm.vx.getters().getConfigs;
|
||||||
|
},
|
||||||
|
mockJson() {
|
||||||
|
return this.$tm.vx.getters().getMockJson;
|
||||||
|
},
|
||||||
|
calcAuditModeEnabled() {
|
||||||
|
return this.haloConfigs.auditConfig.auditModeEnabled
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
},
|
},
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
|
this.isLoadMore = false;
|
||||||
|
this.queryParams.page = 1;
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
this.queryParams.page = 0;
|
|
||||||
},
|
},
|
||||||
|
onReachBottom(e) {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '没有更多数据了'
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.result.hasNext) {
|
||||||
|
this.queryParams.page += 1;
|
||||||
|
this.isLoadMore = true;
|
||||||
|
this.fnGetData();
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '没有更多数据了'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fnOnTabChange(index) {
|
fnOnTabChange(index) {
|
||||||
this.fnResetSetAniWaitIndex();
|
this.fnResetSetAniWaitIndex();
|
||||||
const _api = ['getMonthArchives', 'getYearArchives'];
|
|
||||||
this.api = _api[index];
|
|
||||||
this.queryParams.page = 0;
|
this.queryParams.page = 0;
|
||||||
this.fnGetData();
|
this.dataList = this.handleGetShowDataList(this.handleGetPosts(this.cacheDataList))
|
||||||
|
this.fnToTopPage();
|
||||||
},
|
},
|
||||||
fnGetData() {
|
fnGetData() {
|
||||||
this.loading = 'loading';
|
if (this.calcAuditModeEnabled) {
|
||||||
// uni.showLoading({
|
const dataList = this.mockJson.archives.list.map(item => {
|
||||||
// mask: true,
|
const date = new Date(item.time)
|
||||||
// title: '加载中...'
|
const year = date.getFullYear()
|
||||||
// });
|
const month = date.getMonth() + 1
|
||||||
this.$httpApi[this.api](this.queryParams)
|
return {
|
||||||
.then(res => {
|
metadata: {
|
||||||
this.result = res.data;
|
name: Date.now() * Math.random(),
|
||||||
this.dataList = res.data;
|
labels: {
|
||||||
setTimeout(() => {
|
"content.halo.run/archive-year": year,
|
||||||
|
"content.halo.run/archive-month": month
|
||||||
|
}
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
pinned: false,
|
||||||
|
cover: item.cover,
|
||||||
|
title: item.title,
|
||||||
|
publishTime: item.time
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
excerpt: item.desc
|
||||||
|
},
|
||||||
|
stats: {
|
||||||
|
visit: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const posts = this.handleGetPosts(dataList)
|
||||||
|
this.dataList = []
|
||||||
|
this.cacheDataList = dataList;
|
||||||
|
this.dataList = this.handleGetShowDataList(posts)
|
||||||
this.loading = 'success';
|
this.loading = 'success';
|
||||||
}, 500);
|
this.loadMoreText = '呜呜,没有更多数据啦~';
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
this.loading = 'error';
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.stopPullDownRefresh();
|
uni.stopPullDownRefresh();
|
||||||
}, 500);
|
return;
|
||||||
|
}
|
||||||
|
if (this.isLoadMore) {
|
||||||
|
uni.showLoading({
|
||||||
|
title: "加载中..."
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.loading = 'loading';
|
||||||
|
}
|
||||||
|
this.loadMoreText = "加载中...";
|
||||||
|
const paramsStr = qs.stringify(this.queryParams, {
|
||||||
|
allowDots: true,
|
||||||
|
encodeValuesOnly: true,
|
||||||
|
skipNulls: true,
|
||||||
|
encode: true,
|
||||||
|
arrayFormat: 'repeat'
|
||||||
|
})
|
||||||
|
uni.request({
|
||||||
|
url: this.$baseApiUrl + '/apis/api.content.halo.run/v1alpha1/posts?' + paramsStr,
|
||||||
|
method: 'GET',
|
||||||
|
success: (res) => {
|
||||||
|
const data = res.data;
|
||||||
|
this.result = data;
|
||||||
|
const posts = this.handleGetPosts(data.items)
|
||||||
|
const showDataList = this.handleGetShowDataList(posts)
|
||||||
|
if (this.isLoadMore) {
|
||||||
|
this.cacheDataList = this.handleUniqueCacheDatalist([
|
||||||
|
...this.cacheDataList, ...data.items
|
||||||
|
]);
|
||||||
|
this.handleMergeDataList2(showDataList)
|
||||||
|
} else {
|
||||||
|
this.dataList = []
|
||||||
|
this.cacheDataList = data.items;
|
||||||
|
this.dataList = showDataList
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = 'success';
|
||||||
|
this.loadMoreText = data.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
this.loading = 'error';
|
||||||
|
this.loadMoreText = '加载失败,请下拉刷新!';
|
||||||
|
uni.$tm.toast(err.message || '数据加载失败!');
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 处理数据分类
|
||||||
|
handleGetPosts(dataList) {
|
||||||
|
const posts = {}
|
||||||
|
const postLabelYearKey = "content.halo.run/archive-year"
|
||||||
|
const postLabelMonthKey = "content.halo.run/archive-month"
|
||||||
|
dataList.forEach(item => {
|
||||||
|
let postItemKey = ""
|
||||||
|
if (this.tab.activeIndex === 0) {
|
||||||
|
postItemKey =
|
||||||
|
`${item.metadata.labels[postLabelYearKey]}-${item.metadata.labels[postLabelMonthKey]}`
|
||||||
|
} else {
|
||||||
|
postItemKey = `${item.metadata.labels[postLabelYearKey]}`
|
||||||
|
}
|
||||||
|
if (posts[postItemKey]) {
|
||||||
|
posts[postItemKey].push(item)
|
||||||
|
} else {
|
||||||
|
posts[postItemKey] = [item]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return posts;
|
||||||
|
},
|
||||||
|
// 根据分类的数据,处理成显示的数据
|
||||||
|
handleGetShowDataList(posts) {
|
||||||
|
const dataListResult = []
|
||||||
|
Object.keys(posts).forEach((key) => {
|
||||||
|
const postData = {
|
||||||
|
sort: 0,
|
||||||
|
key: key,
|
||||||
|
year: key,
|
||||||
|
month: "",
|
||||||
|
posts: posts[key]
|
||||||
|
}
|
||||||
|
if (this.tab.activeIndex == 0) {
|
||||||
|
const splitDate = key.split("-")
|
||||||
|
postData.year = splitDate[0]
|
||||||
|
postData.month = splitDate[1]
|
||||||
|
postData.sort = Number(key.replace("-", ""))
|
||||||
|
} else {
|
||||||
|
postData.sort = Number(key)
|
||||||
|
}
|
||||||
|
dataListResult.push(postData)
|
||||||
|
})
|
||||||
|
|
||||||
|
dataListResult.sort((a, b) => {
|
||||||
|
return Number(b.sort) - Number(a.sort)
|
||||||
|
})
|
||||||
|
|
||||||
|
return dataListResult;
|
||||||
|
},
|
||||||
|
handleMergeDataList(list1, list2) {
|
||||||
|
// 将list1转换为以key为键的对象
|
||||||
|
let merged = list1.reduce((acc, item) => {
|
||||||
|
acc[item.key] = {
|
||||||
|
...item
|
||||||
|
};
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// 遍历list2,合并posts数组或添加新对象
|
||||||
|
list2.forEach(item => {
|
||||||
|
if (merged[item.key]) {
|
||||||
|
// 如果key已存在,合并posts数组
|
||||||
|
merged[item.key].posts = [...merged[item.key].posts, ...item.posts];
|
||||||
|
} else {
|
||||||
|
// 如果key不存在,添加新对象
|
||||||
|
merged[item.key] = {
|
||||||
|
...item
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将对象转换回数组
|
||||||
|
return Object.values(merged);
|
||||||
|
},
|
||||||
|
handleMergeDataList2(list) {
|
||||||
|
list.forEach((item, index) => {
|
||||||
|
const find = this.dataList.find(x => x.key == item.key)
|
||||||
|
if (find) {
|
||||||
|
item.posts.forEach(post => {
|
||||||
|
if (!find.posts.find(x => x.metadata.name == post.metadata.name)) {
|
||||||
|
find.posts.push(post)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
list.forEach(post => {
|
||||||
|
if (!this.dataList.find(x => x.key === post.key)) {
|
||||||
|
this.dataList.push(post)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.dataList.sort((a, b) => {
|
||||||
|
return Number(b.sort) - Number(a.sort)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleUniqueCacheDatalist(dataList) {
|
||||||
|
const seen = new Set();
|
||||||
|
return dataList.filter(item => {
|
||||||
|
return seen.has(item.metadata.name) ? false : seen.add(item.metadata.name);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//跳转文章详情
|
|
||||||
fnToArticleDetail(article) {
|
fnToArticleDetail(article) {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pagesA/article-detail/article-detail?articleId=' + article.id,
|
url: '/pagesA/article-detail/article-detail?name=' + article.metadata.name,
|
||||||
animationType: 'slide-in-right'
|
animationType: 'slide-in-right'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -169,6 +377,15 @@ export default {
|
|||||||
background-color: #fafafd;
|
background-color: #fafafd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auditModeEnabled {
|
||||||
|
width: 100%;
|
||||||
|
height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.loading-wrap {
|
.loading-wrap {
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
@@ -376,4 +593,8 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+107
-76
@@ -1,7 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="app-page" :class="{ 'is-balck grey-darken-6': isBlackTheme }">
|
<view class="app-page" :class="[uniHaloPluginPageClass]">
|
||||||
|
<PluginUnavailable v-if="!uniHaloPluginAvailable" :pluginId="uniHaloPluginId"
|
||||||
|
:error-text="uniHaloPluginAvailableError" />
|
||||||
|
<template v-else>
|
||||||
|
|
||||||
<!-- 顶部切换 -->
|
<!-- 顶部切换 -->
|
||||||
<view class="e-fixed shadow-2">
|
<view class="e-fixed shadow-1">
|
||||||
<tm-search v-model="queryParams.keyword" :round="24" :shadow="0" color="light-blue"
|
<tm-search v-model="queryParams.keyword" :round="24" :shadow="0" color="light-blue"
|
||||||
insert-color="light-blue" :clear="true" @input="fnOnSearch" @confirm="fnOnSearch"></tm-search>
|
insert-color="light-blue" :clear="true" @input="fnOnSearch" @confirm="fnOnSearch"></tm-search>
|
||||||
<tm-tabs v-if="false" color="light-blue" :shadow="0" v-model="tab.activeIndex" :list="tab.list"
|
<tm-tabs v-if="false" color="light-blue" :shadow="0" v-model="tab.activeIndex" :list="tab.list"
|
||||||
@@ -10,62 +14,91 @@
|
|||||||
<!-- 占位区域 -->
|
<!-- 占位区域 -->
|
||||||
<view style="width: 100vw;height: 100rpx;"></view>
|
<view style="width: 100vw;height: 100rpx;"></view>
|
||||||
<!-- 加载区域 -->
|
<!-- 加载区域 -->
|
||||||
<view v-if="loading != 'success'" class="loading-wrap pa-24">
|
<view v-if="loading == 'loading'" class="loading-wrap pa-24">
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-else-if="loading == 'error'" class="content-empty flex flex-center">
|
||||||
|
<tm-empty icon="icon-wind-cry" label="搜索异常"></tm-empty>
|
||||||
|
</view>
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<view v-else class="content">
|
<view v-else class="content">
|
||||||
<view v-if="dataList.length == 0" class="content-empty flex flex-center">
|
<view v-if="dataList.length == 0" class="content-empty flex flex-center">
|
||||||
<!-- 空布局 -->
|
<!-- 空布局 -->
|
||||||
<tm-empty v-if="!queryParams.keyword" icon="icon-shiliangzhinengduixiang-" label="请输入关键词搜索"></tm-empty>
|
<tm-empty v-if="!queryParams.keyword" icon="icon-shiliangzhinengduixiang-"
|
||||||
|
label="请输入关键词搜索"></tm-empty>
|
||||||
<tm-empty v-else icon="icon-shiliangzhinengduixiang-"
|
<tm-empty v-else icon="icon-shiliangzhinengduixiang-"
|
||||||
:label="`未搜到 ${queryParams.keyword} 相关文章`"></tm-empty>
|
:label="`未搜到 ${queryParams.keyword} 相关内容`"></tm-empty>
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<!-- 文章卡片 -->
|
<tm-translate v-for="(item, index) in dataList" :key="item.metadataName" animation-name="fadeUp"
|
||||||
<tm-translate v-for="(article, index) in dataList" :key="article.name" animation-name="fadeUp"
|
|
||||||
:wait="calcAniWait(index)">
|
:wait="calcAniWait(index)">
|
||||||
<view class="article-card" @click="fnToArticleDetail(article)">
|
<view class="article-card" @click="fnToDetail(item)">
|
||||||
<rich-text style="font-size: 32rpx;font-weight: bold;color: #333;"
|
<view class="mb-12 flex flex-start">
|
||||||
:nodes="article.title"></rich-text>
|
<view class="flex-shrink ml--12">
|
||||||
<rich-text style="font-size: 28rpx;margin-top: 16rpx;color: #555;"
|
<tm-tags v-if="isArticle(item)" color="blue" size="n" model="text">文章</tm-tags>
|
||||||
:nodes="article.content"></rich-text>
|
<tm-tags v-else color="green" size="n" model="text">瞬间</tm-tags>
|
||||||
<text style="font-size: 24rpx;margin-top: 24rpx;color:#888">
|
</view>
|
||||||
发布日期:{{ {d: article.publishTimestamp, f: 'yyyy年MM月dd日'} | formatTime }}
|
<text class="ml-2 text-overflow text-size-n text-weight-b"
|
||||||
|
style="color: #333;">{{ item.title }}</text>
|
||||||
|
</view>
|
||||||
|
<mp-html class="evan-markdown" lazy-load :domain="markdownConfig.domain"
|
||||||
|
:loading-img="markdownConfig.loadingGif" :scroll-table="true" :selectable="true"
|
||||||
|
:tag-style="markdownConfig.tagStyle" :content="item.description || item.content"
|
||||||
|
:markdown="true" :showLineNumber="true" :showLanguageName="true"
|
||||||
|
:copyByLongPress="true" />
|
||||||
|
<view class="mt-12 flex flex-center flex-between">
|
||||||
|
<text style="font-size: 24rpx;color:#888">
|
||||||
|
最近更新时间:{{ {d: item.updateTimestamp, f: 'yyyy年MM月dd日 HH点mm分ss秒'} | formatTime }}
|
||||||
</text>
|
</text>
|
||||||
|
<!-- <tm-tags v-if="isArticle(item)" color="blue" size="n" model="text">文章</tm-tags>
|
||||||
|
<tm-tags v-else color="green" size="n" model="text">瞬间</tm-tags> -->
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</tm-translate>
|
</tm-translate>
|
||||||
|
|
||||||
<tm-flotbutton @click="fnToTopPage" size="m" color="light-blue" icon="icon-angle-up"></tm-flotbutton>
|
<tm-flotbutton @click="fnToTopPage" size="m" color="light-blue"
|
||||||
<view class="load-text">{{ loadMoreText }}</view>
|
icon="icon-angle-up"></tm-flotbutton>
|
||||||
|
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
import tmSearch from '@/tm-vuetify/components/tm-search/tm-search.vue';
|
import tmSearch from '@/tm-vuetify/components/tm-search/tm-search.vue';
|
||||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||||
import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
|
import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
|
||||||
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
||||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
|
|
||||||
export default {
|
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
||||||
|
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
||||||
|
|
||||||
|
import pluginAvailableMixin from "@/common/mixins/pluginAvailable.js"
|
||||||
|
import PluginUnavailable from '@/components/plugin-unavailable/plugin-unavailable.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [pluginAvailableMixin],
|
||||||
components: {
|
components: {
|
||||||
tmSkeleton,
|
tmSkeleton,
|
||||||
tmSearch,
|
tmSearch,
|
||||||
tmTranslate,
|
tmTranslate,
|
||||||
tmTabs,
|
tmTabs,
|
||||||
tmFlotbutton,
|
tmFlotbutton,
|
||||||
tmEmpty
|
tmEmpty,
|
||||||
|
tmTags,
|
||||||
|
mpHtml,
|
||||||
|
PluginUnavailable
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isBlackTheme: false,
|
markdownConfig: MarkdownConfig,
|
||||||
loading: 'loading',
|
loading: 'loading',
|
||||||
tab: {
|
tab: {
|
||||||
activeIndex: 0,
|
activeIndex: 0,
|
||||||
@@ -73,55 +106,56 @@ export default {
|
|||||||
},
|
},
|
||||||
queryParams: {
|
queryParams: {
|
||||||
keyword: "",
|
keyword: "",
|
||||||
limit: 5,
|
limit: 50,
|
||||||
highlightPreTag: "<text>",
|
highlightPreTag: "",
|
||||||
highlightPostTag: "</text>"
|
highlightPostTag: ""
|
||||||
},
|
},
|
||||||
cache: {
|
cache: {
|
||||||
dataList: [],
|
dataList: [],
|
||||||
total: 0
|
total: 0
|
||||||
},
|
},
|
||||||
hasNext: false,
|
|
||||||
isLoadMore: false,
|
|
||||||
loadMoreText: '加载中...',
|
|
||||||
dataList: []
|
dataList: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onLoad() {
|
computed: {
|
||||||
this.fnSetPageTitle('文章列表');
|
haloConfigs() {
|
||||||
|
return this.$tm.vx.getters().getConfigs;
|
||||||
},
|
},
|
||||||
created() {
|
calcAuditModeEnabled() {
|
||||||
|
return this.haloConfigs.auditConfig.auditModeEnabled
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async onLoad() {
|
||||||
|
this.fnSetPageTitle('内容搜索');
|
||||||
|
// 检查插件
|
||||||
|
this.setPluginId(this.NeedPluginIds.PluginSearchWidget)
|
||||||
|
this.setPluginError("阿偶,检测到当前插件没有安装或者启用,无法使用搜索功能哦,请联系管理员")
|
||||||
|
if (!await this.checkPluginAvailable()) return
|
||||||
if (!this.queryParams.keyword) {
|
if (!this.queryParams.keyword) {
|
||||||
this.loading = 'success'
|
this.loading = 'success'
|
||||||
} else {
|
} else {
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
this.fnResetSetAniWaitIndex();
|
if (!this.uniHaloPluginAvailable) {
|
||||||
this.isLoadMore = false;
|
uni.hideLoading();
|
||||||
this.fnGetData();
|
uni.stopPullDownRefresh();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.fnOnSearch()
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
fnOnTabChange(index) {
|
fnOnTabChange(index) {
|
||||||
this.fnResetSetAniWaitIndex();
|
this.fnResetSetAniWaitIndex();
|
||||||
this.dataList = [];
|
this.dataList = [];
|
||||||
const _sorts = {
|
|
||||||
0: '',
|
|
||||||
1: 'topPriority,createTime,desc',
|
|
||||||
2: 'topPriority,visits,desc',
|
|
||||||
3: 'topPriority,updateTime,desc',
|
|
||||||
4: 'topPriority,likes,desc'
|
|
||||||
};
|
|
||||||
this.queryParams.sort = _sorts[index];
|
|
||||||
this.dataList = [];
|
|
||||||
this.fnToTopPage();
|
this.fnToTopPage();
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
},
|
},
|
||||||
fnOnSearch() {
|
fnOnSearch() {
|
||||||
this.fnResetSetAniWaitIndex();
|
this.fnResetSetAniWaitIndex();
|
||||||
this.isLoadMore = false;
|
|
||||||
if (!this.queryParams.keyword) {
|
if (!this.queryParams.keyword) {
|
||||||
this.dataList = []
|
this.dataList = []
|
||||||
} else {
|
} else {
|
||||||
@@ -129,34 +163,20 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
fnGetData() {
|
fnGetData() {
|
||||||
// uni.showLoading({
|
if (this.calcAuditModeEnabled) {
|
||||||
// mask: true,
|
return;
|
||||||
// title: '加载中...'
|
|
||||||
// });
|
|
||||||
// 设置状态为加载中
|
|
||||||
if (!this.isLoadMore) {
|
|
||||||
this.loading = 'loading';
|
|
||||||
}
|
}
|
||||||
this.loadMoreText = '加载中...';
|
// 设置状态为加载中
|
||||||
|
this.loading = 'loading';
|
||||||
this.$httpApi.v2
|
this.$httpApi.v2
|
||||||
.getPostListByKeyword(this.queryParams)
|
.getPostListByKeyword(this.queryParams)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
console.log('请求结果:');
|
|
||||||
console.log(res);
|
|
||||||
|
|
||||||
this.loading = 'success';
|
this.loading = 'success';
|
||||||
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
|
||||||
this.hasNext = res.hasNext;
|
|
||||||
if (this.isLoadMore) {
|
|
||||||
this.dataList = this.dataList.concat(res.hits);
|
|
||||||
} else {
|
|
||||||
this.dataList = res.hits;
|
this.dataList = res.hits;
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
this.loading = 'error';
|
this.loading = 'error';
|
||||||
this.loadMoreText = '加载失败,请下拉刷新!';
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -165,20 +185,31 @@ export default {
|
|||||||
}, 800);
|
}, 800);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
isArticle(item) {
|
||||||
//跳转文章详情
|
return item.type == 'post.content.halo.run'
|
||||||
fnToArticleDetail(article) {
|
},
|
||||||
|
//跳转详情
|
||||||
|
fnToDetail(item) {
|
||||||
|
if (this.calcAuditModeEnabled) return;
|
||||||
|
if (this.isArticle(item)) {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pagesA/article-detail/article-detail?name=' + article.name,
|
url: '/pagesA/article-detail/article-detail?name=' + item.metadataName,
|
||||||
|
animationType: 'slide-in-right'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pagesA/moment-detail/moment-detail?name=' + item.metadataName,
|
||||||
animationType: 'slide-in-right'
|
animationType: 'slide-in-right'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-page {
|
.app-page {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -189,17 +220,17 @@ export default {
|
|||||||
&.is-balck {
|
&.is-balck {
|
||||||
background-color: #212121;
|
background-color: #212121;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding-top: 24rpx;
|
padding-top: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.content-empty {
|
.content-empty {
|
||||||
height: 60vh;
|
height: 60vh;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.article-card {
|
.article-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -210,5 +241,5 @@ export default {
|
|||||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -68,6 +68,18 @@ export default {
|
|||||||
this.queryParams.page = 0;
|
this.queryParams.page = 0;
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
},
|
},
|
||||||
|
onShareAppMessage() {
|
||||||
|
return {
|
||||||
|
title: this.pageTitle,
|
||||||
|
path: `/pagesA/category-detail/category-detail?name=${this.name}&title=${this.pageTitle}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShareTimeline() {
|
||||||
|
return {
|
||||||
|
title: this.pageTitle,
|
||||||
|
path: `/pagesA/category-detail/category-detail?name=${this.name}&title=${this.pageTitle}`
|
||||||
|
}
|
||||||
|
},
|
||||||
onReachBottom(e) {
|
onReachBottom(e) {
|
||||||
if (this.hasNext) {
|
if (this.hasNext) {
|
||||||
this.queryParams.page += 1;
|
this.queryParams.page += 1;
|
||||||
|
|||||||
+25
-15
@@ -26,7 +26,6 @@
|
|||||||
<!-- 社交联系方式列表 -->
|
<!-- 社交联系方式列表 -->
|
||||||
<view class="contact ma-50 mt-0 pt-12">
|
<view class="contact ma-50 mt-0 pt-12">
|
||||||
<block v-if="calcIsNotEmpty">
|
<block v-if="calcIsNotEmpty">
|
||||||
|
|
||||||
<block v-for="(item, index) in result" :key="index">
|
<block v-for="(item, index) in result" :key="index">
|
||||||
<view v-if="item.value" class="item mt-24 flex" @click="fnOnClick(item)">
|
<view v-if="item.value" class="item mt-24 flex" @click="fnOnClick(item)">
|
||||||
<view class="left">
|
<view class="left">
|
||||||
@@ -137,36 +136,47 @@ export default {
|
|||||||
return blogger;
|
return blogger;
|
||||||
},
|
},
|
||||||
socialConfig() {
|
socialConfig() {
|
||||||
return this.authorConfig.social
|
return this.authorConfig.social;
|
||||||
},
|
},
|
||||||
calcIsNotEmpty() {
|
calcIsNotEmpty() {
|
||||||
return this.result.some((item) => item.value !== "");
|
return this.result.some((item) => item.value !== "");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
socialConfig: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (!newVal) return;
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.fnSetPageTitle('联系博主');
|
this.fnSetPageTitle('联系博主');
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
this.fnGetData();
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
fnGetData() {
|
fnGetData() {
|
||||||
for (let key in this.socialConfig) {
|
for (let key in this.socialConfig) {
|
||||||
|
if (key === 'enabled') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
this.result.find(x => x.key === key).value = this.socialConfig[key];
|
this.result.find(x => x.key === key).value = this.socialConfig[key];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fnOnClick(item) {
|
fnOnClick(item) {
|
||||||
if (this.globalAppSettings.contact.isLinkCopy && this.$utils.checkIsUrl(item.value)) {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pagesC/website/website?data=' +
|
|
||||||
JSON.stringify({
|
|
||||||
title: item.name,
|
|
||||||
url: item.value
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$utils.copyText(item.value, item.name + ' 已复制!');
|
this.$utils.copyText(item.value, item.name + ' 已复制!');
|
||||||
}
|
// if (this.globalAppSettings.contact.isLinkCopy && this.$utils.checkIsUrl(item.value)) {
|
||||||
|
// uni.navigateTo({
|
||||||
|
// url: '/pagesC/website/website?data=' +
|
||||||
|
// JSON.stringify({
|
||||||
|
// title: item.name,
|
||||||
|
// url: item.value
|
||||||
|
// })
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// this.$utils.copyText(item.value, item.name + ' 已复制!');
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,549 @@
|
|||||||
|
<template>
|
||||||
|
<view class="app-page flex flex-col pa-24" :class="[uniHaloPluginPageClass]">
|
||||||
|
<PluginUnavailable v-if="!uniHaloPluginAvailable" :pluginId="uniHaloPluginId" :error-text="uniHaloPluginAvailableError" />
|
||||||
|
<template v-else>
|
||||||
|
<!-- 加载区域 -->
|
||||||
|
<view v-if="loading == 'loading'" class="loading-wrap pa-24">
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="loading == 'error'" class="content-empty flex flex-center">
|
||||||
|
<tm-empty icon="icon-wind-cry" label="加载异常"></tm-empty>
|
||||||
|
</view>
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<view v-else class="content flex flex-col uh-gap-y-12">
|
||||||
|
<!-- 标签统计 -->
|
||||||
|
<view class="card bg-white pa-24 round-4">
|
||||||
|
<view class="card-head flex items-center justify-between">
|
||||||
|
<view class="card-head_title flex items-end uh-gap-x-4">
|
||||||
|
<text class="card-head_text">标签统计</text>
|
||||||
|
<text class="card-head_subtext">(全部标签的文章数量占比)</text>
|
||||||
|
</view>
|
||||||
|
<view @click="tagChart.isExpand = !tagChart.isExpand">
|
||||||
|
<tm-icons v-if="tagChart.isExpand" :size="24" name="icon-angle-down" color="gray"></tm-icons>
|
||||||
|
<tm-icons v-else :size="24" name="icon-angle-up" color="gray"></tm-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-show="tagChart.isExpand" class="card-body flex">
|
||||||
|
<view class="chart-box">
|
||||||
|
<qiun-data-charts :type="tagChart.type" :opts="tagChart.opts" :chartData="tagChart.data" :tooltipFormat="tagChart.tooltipFormat" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 分类统计 -->
|
||||||
|
<view class="card bg-white pa-24 round-4">
|
||||||
|
<view class="card-head flex items-center justify-between">
|
||||||
|
<view class="card-head_title flex items-end uh-gap-x-4">
|
||||||
|
<text class="card-head_text">分类统计</text>
|
||||||
|
<text class="card-head_subtext">(全部分类的文章数量占比)</text>
|
||||||
|
</view>
|
||||||
|
<view @click="categoryChart.isExpand = !categoryChart.isExpand">
|
||||||
|
<tm-icons v-if="categoryChart.isExpand" :size="24" name="icon-angle-down" color="gray"></tm-icons>
|
||||||
|
<tm-icons v-else :size="24" name="icon-angle-up" color="gray"></tm-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-show="categoryChart.isExpand" class="card-body flex">
|
||||||
|
<view class="chart-box">
|
||||||
|
<qiun-data-charts
|
||||||
|
:canvasId="categoryChart.id"
|
||||||
|
:canvas2d="true"
|
||||||
|
:ontouch="true"
|
||||||
|
:type="categoryChart.type"
|
||||||
|
:opts="categoryChart.opts"
|
||||||
|
:chartData="categoryChart.data"
|
||||||
|
:tooltipFormat="categoryChart.tooltipFormat"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 文章发布趋势 -->
|
||||||
|
<view class="card bg-white pa-24 round-4">
|
||||||
|
<view class="card-head flex items-center justify-between">
|
||||||
|
<view class="card-head_title flex items-end uh-gap-x-4">
|
||||||
|
<text class="card-head_text">文章发布趋势</text>
|
||||||
|
<text class="card-head_subtext">(按日期统计文章发布数量)</text>
|
||||||
|
</view>
|
||||||
|
<view @click="trandArticleChart.isExpand = !trandArticleChart.isExpand">
|
||||||
|
<tm-icons v-if="trandArticleChart.isExpand" :size="24" name="icon-angle-down" color="gray"></tm-icons>
|
||||||
|
<tm-icons v-else :size="24" name="icon-angle-up" color="gray"></tm-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-show="trandArticleChart.isExpand" class="card-body flex">
|
||||||
|
<heatmap style="width: 100%" :chartData="trandArticleChart.data"></heatmap>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 评论活跃用户 -->
|
||||||
|
<view class="card bg-white pa-24 round-4">
|
||||||
|
<view class="card-head flex items-center justify-between">
|
||||||
|
<view class="card-head_title flex items-end uh-gap-x-4">
|
||||||
|
<text class="card-head_text">评论活跃用户</text>
|
||||||
|
<text class="card-head_subtext">(按评论作者统计评论数量)</text>
|
||||||
|
</view>
|
||||||
|
<view @click="userCommentsChart.isExpand = !userCommentsChart.isExpand">
|
||||||
|
<tm-icons v-if="userCommentsChart.isExpand" :size="24" name="icon-angle-down" color="gray"></tm-icons>
|
||||||
|
<tm-icons v-else :size="24" name="icon-angle-up" color="gray"></tm-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-show="userCommentsChart.isExpand" class="card-body flex">
|
||||||
|
<view class="chart-box">
|
||||||
|
<qiun-data-charts
|
||||||
|
:canvasId="userCommentsChart.id"
|
||||||
|
:canvas2d="true"
|
||||||
|
:ontouch="true"
|
||||||
|
:type="userCommentsChart.type"
|
||||||
|
:opts="userCommentsChart.opts"
|
||||||
|
:chartData="userCommentsChart.data"
|
||||||
|
:tooltipFormat="userCommentsChart.tooltipFormat"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 热门文章 Top10 -->
|
||||||
|
<view class="card bg-white pa-24 round-4">
|
||||||
|
<view class="card-head flex items-center justify-between">
|
||||||
|
<view class="card-head_title flex items-end uh-gap-x-4">
|
||||||
|
<text class="card-head_text">热门文章前10</text>
|
||||||
|
<text class="card-head_subtext">(按访问量排序的热门文章)</text>
|
||||||
|
</view>
|
||||||
|
<view @click="top10ArticlesChart.isExpand = !top10ArticlesChart.isExpand">
|
||||||
|
<tm-icons v-if="top10ArticlesChart.isExpand" :size="24" name="icon-angle-down" color="gray"></tm-icons>
|
||||||
|
<tm-icons v-else :size="24" name="icon-angle-up" color="gray"></tm-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="card-body flex">
|
||||||
|
<view class="chart-box">
|
||||||
|
<qiun-data-charts
|
||||||
|
:canvasId="top10ArticlesChart.id"
|
||||||
|
:canvas2d="true"
|
||||||
|
:ontouch="true"
|
||||||
|
:type="top10ArticlesChart.type"
|
||||||
|
:opts="top10ArticlesChart.opts"
|
||||||
|
:chartData="top10ArticlesChart.data"
|
||||||
|
:tooltipFormat="top10ArticlesChart.tooltipFormat"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import dataStatisticsApi from '@/api/v2/plugin.data-statistics.js';
|
||||||
|
import { NeedPluginIds, NeedPlugins, checkNeedPluginAvailable } from '@/utils/plugin.js';
|
||||||
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
|
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||||
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
|
||||||
|
|
||||||
|
import pluginAvailableMixin from '@/common/mixins/pluginAvailable.js';
|
||||||
|
import PluginUnavailable from '@/components/plugin-unavailable/plugin-unavailable.vue';
|
||||||
|
|
||||||
|
import heatmap from '@/components/heatmap/heatmap.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [pluginAvailableMixin],
|
||||||
|
name: 'DataVisual',
|
||||||
|
components: {
|
||||||
|
tmSkeleton,
|
||||||
|
tmTranslate,
|
||||||
|
tmEmpty,
|
||||||
|
tmButton,
|
||||||
|
tmIcons,
|
||||||
|
heatmap,
|
||||||
|
PluginUnavailable
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: 'loading',
|
||||||
|
statistics: null,
|
||||||
|
tagChart: {
|
||||||
|
id: 'tagChart',
|
||||||
|
isExpand: true,
|
||||||
|
type: 'ring',
|
||||||
|
data: {},
|
||||||
|
tooltipFormat: 'tooltipTag',
|
||||||
|
opts: {
|
||||||
|
rotate: false,
|
||||||
|
rotateLock: false,
|
||||||
|
color: ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899', '#14B8A6', '#F97316', '#ea7ccc', '#0EA5E9'],
|
||||||
|
padding: [5, 5, 5, 5],
|
||||||
|
dataLabel: false,
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
position: 'right',
|
||||||
|
lineHeight: 18,
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
name: '',
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#666666'
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
name: '',
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#eee'
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
ring: {
|
||||||
|
ringWidth: 36,
|
||||||
|
activeOpacity: 0.5,
|
||||||
|
activeRadius: 10,
|
||||||
|
offsetAngle: -90,
|
||||||
|
labelWidth: 15,
|
||||||
|
border: true,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#FFFFFF'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
legendShape: 'circle',
|
||||||
|
fontSize: 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
categoryChart: {
|
||||||
|
id: 'categoryChart',
|
||||||
|
isExpand: true,
|
||||||
|
type: 'line',
|
||||||
|
data: {},
|
||||||
|
tooltipFormat: 'tooltipCategory',
|
||||||
|
opts: {
|
||||||
|
color: ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899', '#14B8A6', '#F97316', '#ea7ccc', '#0EA5E9'],
|
||||||
|
padding: [20, 15, 10, 15],
|
||||||
|
touchMoveLimit: 24,
|
||||||
|
enableScroll: true,
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
disableGrid: true,
|
||||||
|
fontSize: 11,
|
||||||
|
scrollShow: true,
|
||||||
|
itemCount: 6,
|
||||||
|
boundaryGap: 'center',
|
||||||
|
format: 'xAxisCategory'
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
gridType: 'dash',
|
||||||
|
dashLength: 4,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
fontSize: 11
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
line: {
|
||||||
|
type: 'curve',
|
||||||
|
width: 2,
|
||||||
|
activeType: 'hollow'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
legendShape: 'circle',
|
||||||
|
fontSize: 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trandArticleChart: {
|
||||||
|
isExpand: true,
|
||||||
|
type: 'hotmap',
|
||||||
|
data: [],
|
||||||
|
opts: {}
|
||||||
|
},
|
||||||
|
userCommentsChart: {
|
||||||
|
id: 'userCommentsChart',
|
||||||
|
isExpand: true,
|
||||||
|
loading: true,
|
||||||
|
type: 'column',
|
||||||
|
data: {},
|
||||||
|
tooltipFormat: 'tooltipUserComments',
|
||||||
|
opts: {
|
||||||
|
color: ['#EF4444', '#F59E0B', '#14B8A6', '#3B82F6', '#10B981', '#8B5CF6', '#EC4899', '#F97316', '#ea7ccc', '#0EA5E9'],
|
||||||
|
padding: [20, 15, 10, 10],
|
||||||
|
touchMoveLimit: 24,
|
||||||
|
enableScroll: true,
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
disableGrid: true,
|
||||||
|
boundaryGap: 'justify',
|
||||||
|
fontSize: 10,
|
||||||
|
scrollShow: true,
|
||||||
|
itemCount: 5,
|
||||||
|
format: 'xAxisUserComments'
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
gridType: 'dash',
|
||||||
|
dashLength: 4,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
min: 0,
|
||||||
|
fontSize: 11
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
column: {
|
||||||
|
type: 'group',
|
||||||
|
width: 22,
|
||||||
|
activeBgColor: '#000000',
|
||||||
|
activeBgOpacity: 0.08,
|
||||||
|
linearType: 'custom',
|
||||||
|
seriesGap: 5,
|
||||||
|
linearOpacity: 0.7,
|
||||||
|
barBorderCircle: true,
|
||||||
|
customColor: ['#F59E0B']
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
legendShape: 'circle',
|
||||||
|
fontSize: 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
top10ArticlesChart: {
|
||||||
|
id: 'top10ArticlesChart',
|
||||||
|
isExpand: true,
|
||||||
|
type: 'column',
|
||||||
|
data: {},
|
||||||
|
tooltipFormat: 'tooltipTop10Articles',
|
||||||
|
opts: {
|
||||||
|
color: ['#EF4444', '#F59E0B', '#14B8A6', '#3B82F6', '#10B981', '#8B5CF6', '#EC4899', '#F97316', '#ea7ccc', '#0EA5E9'],
|
||||||
|
padding: [20, 15, 10, 10],
|
||||||
|
touchMoveLimit: 24,
|
||||||
|
enableScroll: true,
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
disableGrid: true,
|
||||||
|
boundaryGap: 'justify',
|
||||||
|
fontSize: 10,
|
||||||
|
scrollShow: true,
|
||||||
|
itemCount: 5,
|
||||||
|
format: 'xAxisTop10Article'
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
gridType: 'dash',
|
||||||
|
dashLength: 4,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
min: 0,
|
||||||
|
fontSize: 11
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
column: {
|
||||||
|
type: 'group',
|
||||||
|
width: 22,
|
||||||
|
activeBgColor: '#000000',
|
||||||
|
activeBgOpacity: 0.08,
|
||||||
|
linearType: 'custom',
|
||||||
|
seriesGap: 5,
|
||||||
|
linearOpacity: 0.7,
|
||||||
|
barBorderCircle: true,
|
||||||
|
customColor: ['#F59E0B']
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
legendShape: 'circle',
|
||||||
|
fontSize: 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async onReady() {
|
||||||
|
// 检查插件
|
||||||
|
this.setPluginId(this.NeedPluginIds.PluginDataStatistics);
|
||||||
|
this.setPluginError('阿偶,检测到当前插件没有安装或者启用,无法使用功能哦,请联系管理员');
|
||||||
|
if (!(await this.checkPluginAvailable())) return;
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
onPullDownRefresh() {
|
||||||
|
if (!this.uniHaloPluginAvailable) {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnGetData() {
|
||||||
|
uni.showLoading({
|
||||||
|
mask: true,
|
||||||
|
title: '加载中...'
|
||||||
|
});
|
||||||
|
// 设置状态为加载中
|
||||||
|
if (!this.isLoadMore) {
|
||||||
|
this.loading = 'loading';
|
||||||
|
}
|
||||||
|
this.loadMoreText = '加载中...';
|
||||||
|
dataStatisticsApi
|
||||||
|
.getChartData()
|
||||||
|
.then((res) => {
|
||||||
|
console.log('获取到统计数据:', res);
|
||||||
|
this.statistics = res;
|
||||||
|
this.handleTagChart();
|
||||||
|
this.handleCategoriesChart();
|
||||||
|
this.handleTrendArticlesChart();
|
||||||
|
this.handleUserCommentsChart();
|
||||||
|
this.handleTop10ArticlesChart();
|
||||||
|
this.loading = 'success';
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.loading = 'error';
|
||||||
|
this.loadMoreText = '加载失败,请下拉刷新!';
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 处理标签统计
|
||||||
|
handleTagChart() {
|
||||||
|
const data = this.statistics.tags.sort((a, b) => b.count - a.count);
|
||||||
|
this.tagChart.data = {
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: data.map((item) => {
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
value: item.count
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 处理分类统计
|
||||||
|
handleCategoriesChart() {
|
||||||
|
const data = this.statistics.categories.sort((a, b) => b.total - a.total);
|
||||||
|
const seriesItemData = data.map((item) => item.total);
|
||||||
|
if (Math.max(...seriesItemData) < 10) {
|
||||||
|
this.categoryChart.opts.yAxis.data[0].max = 10;
|
||||||
|
}
|
||||||
|
this.categoryChart.data = {
|
||||||
|
categories: data.map((item) => item.name),
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '分类',
|
||||||
|
data: seriesItemData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 处理文章趋势
|
||||||
|
handleTrendArticlesChart() {
|
||||||
|
this.trandArticleChart.data = this.statistics.articles;
|
||||||
|
},
|
||||||
|
// 处理评论活跃用户
|
||||||
|
handleUserCommentsChart() {
|
||||||
|
this.userCommentsChart.loading = true;
|
||||||
|
const data = this.statistics.comments.sort((a, b) => b.count - a.count).slice(0, 10);
|
||||||
|
const seriesItemData = data.map((item) => item.count);
|
||||||
|
if (Math.max(...seriesItemData) < 10) {
|
||||||
|
this.userCommentsChart.opts.yAxis.data[0].max = 10;
|
||||||
|
}
|
||||||
|
this.userCommentsChart.data = {
|
||||||
|
categories: data.map((item) => item.username),
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '评论',
|
||||||
|
data: seriesItemData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
this.userCommentsChart.loading = false;
|
||||||
|
},
|
||||||
|
// 处理热门文章TOP10
|
||||||
|
handleTop10ArticlesChart() {
|
||||||
|
const data = this.statistics.top10Articles.sort((a, b) => b.views - a.views).slice(0, 10);
|
||||||
|
const seriesItemData = data.map((item) => item.views);
|
||||||
|
if (Math.max(...seriesItemData) < 10) {
|
||||||
|
this.top10ArticlesChart.opts.yAxis.data[0].max = 10;
|
||||||
|
}
|
||||||
|
this.top10ArticlesChart.data = {
|
||||||
|
categories: data.map((item) => item.name),
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '评论',
|
||||||
|
data: seriesItemData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.app-page {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
color: #353437;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: rgba(255, 255, 255, 0.95);
|
||||||
|
box-shadow: 0 0 12rpx rgba(226, 232, 240, 0.35);
|
||||||
|
backdrop-filter: blur(6rpx);
|
||||||
|
// border: 2rpx solid #e8edf4;
|
||||||
|
}
|
||||||
|
.card-head {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&_title {
|
||||||
|
}
|
||||||
|
&_text {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 24rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 8rpx;
|
||||||
|
height: 70%;
|
||||||
|
background-color: #03a9f4;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&_subtext {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #fcfdfe;
|
||||||
|
border: 2rpx solid #e9eef3;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
.chart-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 320rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -7,12 +7,12 @@
|
|||||||
<block v-else>
|
<block v-else>
|
||||||
<view class="text-weight-b text-size-lg text-align-center">《 本博客免责声明 》</view>
|
<view class="text-weight-b text-size-lg text-align-center">《 本博客免责声明 》</view>
|
||||||
<view v-if="false" class="text-size-s text-align-center mt-12 text-grey-darken-1">—— 博主:{{ bloggerInfo.nickname }} ——</view>
|
<view v-if="false" class="text-size-s text-align-center mt-12 text-grey-darken-1">—— 博主:{{ bloggerInfo.nickname }} ——</view>
|
||||||
<view class="mt-32">1、本博客属于个人非赢利性质的网站,所有转载的文章都以遵循原作者的版权声明注明了文章来源。</view>
|
<view class="mt-32">1、本博客属于个人非盈利性质的网站,所有转载的文章都以遵循原作者的版权声明注明了文章来源。</view>
|
||||||
<view class="mt-24">2、如果原文没有版权声明,按照目前互联网开放的原则,本博客将在不通知作者的情况下转载文章。</view>
|
<view class="mt-24">2、如果原文没有版权声明,按照目前互联网开放的原则,本博客将在不通知作者的情况下转载文章。</view>
|
||||||
<view class="mt-24">3、如果原文明确注明“禁止转载”,本博客将不会转载。</view>
|
<view class="mt-24">3、如果原文明确注明“禁止转载”,本博客将不会转载。</view>
|
||||||
<view class="mt-24">
|
<view class="mt-24">
|
||||||
4、如果本博客转载的文章不符合作者的版权声明或者作者不想让本博客转载您的文章,请邮件告知
|
4、如果本博客转载的文章不符合作者的版权声明或者作者不想让本博客转载您的文章,请邮件告知
|
||||||
<text class="text-bg-gradient-light-blue-accent ml-12 mr-12 ">
|
<text class="text-bg-gradient-light-blue-accent ml-12 mr-12 " @click="$utils.copyText(bloggerInfo.email, '电子邮箱已复制到剪贴板!')">
|
||||||
<text class="iconfont icon-mail-fill text-size-m mr-6"></text>
|
<text class="iconfont icon-mail-fill text-size-m mr-6"></text>
|
||||||
{{ bloggerInfo.email }}
|
{{ bloggerInfo.email }}
|
||||||
</text>
|
</text>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="app-page card-shadow">
|
<view class="app-page card-shadow" :class="[uniHaloPluginPageClass]">
|
||||||
|
<PluginUnavailable v-if="!uniHaloPluginAvailable" :pluginId="uniHaloPluginId"
|
||||||
|
:error-text="uniHaloPluginAvailableError" />
|
||||||
|
<template v-else>
|
||||||
<view v-if="loading != 'success'" class="loading-wrap">
|
<view v-if="loading != 'success'" class="loading-wrap">
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
@@ -65,7 +68,7 @@
|
|||||||
|
|
||||||
<!-- 返回顶部 -->
|
<!-- 返回顶部 -->
|
||||||
<tm-flotbutton color="light-blue" @click="fnToTopPage" size="m" icon="icon-angle-up"></tm-flotbutton>
|
<tm-flotbutton color="light-blue" @click="fnToTopPage" size="m" icon="icon-angle-up"></tm-flotbutton>
|
||||||
<tm-flotbutton v-if="haloPluginConfigs.submitLink.enabled" :offset="[16,80]" label="申请"
|
<tm-flotbutton v-if="haloPluginConfigs.linksSubmitPlugin.enabled" :offset="[16,80]" label="申请"
|
||||||
actions-pos="left" :show-text="true" color="bg-gradient-orange-accent"
|
actions-pos="left" :show-text="true" color="bg-gradient-orange-accent"
|
||||||
@click="toSubmitLinkPage"></tm-flotbutton>
|
@click="toSubmitLinkPage"></tm-flotbutton>
|
||||||
<!-- 详情弹窗 -->
|
<!-- 详情弹窗 -->
|
||||||
@@ -73,10 +76,12 @@
|
|||||||
<view class="poup pa-36" v-if="detail.data">
|
<view class="poup pa-36" v-if="detail.data">
|
||||||
<view class="info flex">
|
<view class="info flex">
|
||||||
<view class="poup-logo bg-gradient-amber-accent pa-4 shadow-24">
|
<view class="poup-logo bg-gradient-amber-accent pa-4 shadow-24">
|
||||||
<image class="poup-logo_img" :src="$utils.checkImageUrl(detail.data.spec.logo)" mode="aspectFill"></image>
|
<image class="poup-logo_img" :src="$utils.checkImageUrl(detail.data.spec.logo)"
|
||||||
|
mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="pl-24 info-detail">
|
<view class="pl-24 info-detail">
|
||||||
<view class="poup-name text-size-lg text-weight-b">{{ detail.data.spec.displayName }}</view>
|
<view class="poup-name text-size-lg text-weight-b">{{ detail.data.spec.displayName }}
|
||||||
|
</view>
|
||||||
<view class="poup-tag ml--10">
|
<view class="poup-tag ml--10">
|
||||||
<tm-tags color="bg-gradient-light-blue-lighten" size="n" model="fill">
|
<tm-tags color="bg-gradient-light-blue-lighten" size="n" model="fill">
|
||||||
{{ detail.data.spec.groupName }}
|
{{ detail.data.spec.groupName }}
|
||||||
@@ -103,19 +108,24 @@
|
|||||||
|
|
||||||
<view class="load-text">{{ loadMoreText }}</view>
|
<view class="load-text">{{ loadMoreText }}</view>
|
||||||
</view>
|
</view>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||||
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
||||||
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue';
|
import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue';
|
||||||
import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
|
import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
|
||||||
|
|
||||||
export default {
|
import pluginAvailableMixin from "@/common/mixins/pluginAvailable.js"
|
||||||
|
import PluginUnavailable from '@/components/plugin-unavailable/plugin-unavailable.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [pluginAvailableMixin],
|
||||||
components: {
|
components: {
|
||||||
tmSkeleton,
|
tmSkeleton,
|
||||||
tmTranslate,
|
tmTranslate,
|
||||||
@@ -123,7 +133,8 @@ export default {
|
|||||||
tmTags,
|
tmTags,
|
||||||
tmEmpty,
|
tmEmpty,
|
||||||
tmImages,
|
tmImages,
|
||||||
tmPoup
|
tmPoup,
|
||||||
|
PluginUnavailable
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -152,6 +163,9 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
haloConfigs() {
|
||||||
|
return this.$tm.vx.getters().getConfigs;
|
||||||
|
},
|
||||||
haloPluginConfigs() {
|
haloPluginConfigs() {
|
||||||
return this.$tm.vx.getters().getConfigs.pluginConfig;
|
return this.$tm.vx.getters().getConfigs.pluginConfig;
|
||||||
},
|
},
|
||||||
@@ -163,19 +177,32 @@ export default {
|
|||||||
}
|
}
|
||||||
return 'https://image.thum.io/get/width/1000/crop/800/' + val;
|
return 'https://image.thum.io/get/width/1000/crop/800/' + val;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onLoad() {
|
calcAuditModeEnabled() {
|
||||||
|
return this.haloConfigs.auditConfig.auditModeEnabled
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async onLoad() {
|
||||||
this.fnSetPageTitle('友情链接');
|
this.fnSetPageTitle('友情链接');
|
||||||
|
// 检查插件
|
||||||
|
this.setPluginId(this.NeedPluginIds.PluginLinks)
|
||||||
|
this.setPluginError("阿偶,检测到当前插件没有安装或者启用,无法使用友情链接功能哦,请联系管理员")
|
||||||
|
if (!await this.checkPluginAvailable()) return
|
||||||
this.fnGetLinkGroupData();
|
this.fnGetLinkGroupData();
|
||||||
},
|
},
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
|
if (!this.uniHaloPluginAvailable) {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return
|
||||||
|
}
|
||||||
this.isLoadMore = false;
|
this.isLoadMore = false;
|
||||||
this.queryParams.page = 1;
|
this.queryParams.page = 1;
|
||||||
this.dataList = []
|
this.dataList = []
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
},
|
},
|
||||||
onReachBottom(e) {
|
onReachBottom(e) {
|
||||||
|
if (!this.uniHaloPluginAvailable) return;
|
||||||
if (this.hasNext) {
|
if (this.hasNext) {
|
||||||
this.queryParams.page += 1;
|
this.queryParams.page += 1;
|
||||||
this.isLoadMore = true;
|
this.isLoadMore = true;
|
||||||
@@ -195,18 +222,25 @@ export default {
|
|||||||
size: 0
|
size: 0
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.linkGroupList = res.items;
|
console.log("友情链接分组数据:", res);
|
||||||
|
this.linkGroupList = res;
|
||||||
this.fnGetData()
|
this.fnGetData()
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
this.loading = 'error';
|
||||||
|
this.loadMoreText = err;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
findLinkGroupDisplayNameByGroupMetadataName(groupName) {
|
findLinkGroupDisplayNameByGroupMetadataName(groupName) {
|
||||||
if (this.linkGroupList.length === 0) return groupName || "未分组"
|
if (this.linkGroupList.length === 0) return groupName || "未分组"
|
||||||
return this.linkGroupList.find(item => item.metadata.name === groupName)?.spec?.displayName || groupName || "未分组"
|
return this.linkGroupList.find(item => item.metadata.name === groupName)?.spec?.displayName || groupName ||
|
||||||
|
"未分组"
|
||||||
},
|
},
|
||||||
fnGetData() {
|
fnGetData() {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.isLoadMore) {
|
if (!this.isLoadMore) {
|
||||||
this.loading = 'loading';
|
this.loading = 'loading';
|
||||||
}
|
}
|
||||||
@@ -219,7 +253,8 @@ export default {
|
|||||||
this.hasNext = res.hasNext;
|
this.hasNext = res.hasNext;
|
||||||
const list = res.items.map(item => {
|
const list = res.items.map(item => {
|
||||||
item.spec.logo = this.$utils.checkAvatarUrl(item.spec?.logo)
|
item.spec.logo = this.$utils.checkAvatarUrl(item.spec?.logo)
|
||||||
item.spec.groupName = this.findLinkGroupDisplayNameByGroupMetadataName(item.spec?.groupName)
|
item.spec.groupName = this.findLinkGroupDisplayNameByGroupMetadataName(item.spec
|
||||||
|
?.groupName)
|
||||||
return item;
|
return item;
|
||||||
})
|
})
|
||||||
this.dataList = this.dataList.concat(list);
|
this.dataList = this.dataList.concat(list);
|
||||||
@@ -290,29 +325,29 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
toSubmitLinkPage() {
|
toSubmitLinkPage() {
|
||||||
this.$Router.push({
|
uni.navigateTo({
|
||||||
path: '/pagesA/submit-link/submit-link'
|
url: '/pagesA/submit-link/submit-link'
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-page {
|
.app-page {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #fafafd;
|
background-color: #fafafd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-wrap {
|
.loading-wrap {
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
padding-top: 24rpx;
|
padding-top: 24rpx;
|
||||||
|
|
||||||
@@ -322,9 +357,9 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-card {
|
.link-card {
|
||||||
border-bottom: 2rpx solid #f5f5f5;
|
border-bottom: 2rpx solid #f5f5f5;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
|
|
||||||
@@ -368,20 +403,20 @@ export default {
|
|||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-card_name {
|
.link-card_name {
|
||||||
// color: #303133;
|
// color: #303133;
|
||||||
// color: #0080fe;
|
// color: #0080fe;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-card_desc {
|
.link-card_desc {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-logo {
|
.link-logo {
|
||||||
width: 140rpx;
|
width: 140rpx;
|
||||||
height: 140rpx;
|
height: 140rpx;
|
||||||
|
|
||||||
@@ -389,9 +424,9 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.poup-logo {
|
.poup-logo {
|
||||||
width: 140rpx;
|
width: 140rpx;
|
||||||
height: 140rpx;
|
height: 140rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -401,22 +436,22 @@ export default {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-detail {
|
.info-detail {
|
||||||
width: 0;
|
width: 0;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poup-desc {
|
.poup-desc {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #555 !important;
|
color: #555 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-site {
|
.preview-site {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 300rpx;
|
height: 300rpx;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="app-page">
|
|
||||||
<view v-if="loading != 'success'" class="loading-wrap pa-24">
|
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
|
||||||
<tm-skeleton model="listAvatr"></tm-skeleton>
|
|
||||||
</view>
|
|
||||||
<!-- 内容区域 -->
|
|
||||||
<view v-else class="app-page-content pa-24">
|
|
||||||
<view v-if="dataList.length == 0" class="content-empty flex flex-center">
|
|
||||||
<!-- 空布局 -->
|
|
||||||
<tm-empty icon="icon-shiliangzhinengduixiang-" label="博主还没有写任何日记"></tm-empty>
|
|
||||||
</view>
|
|
||||||
<block v-else>
|
|
||||||
<block v-for="(journal, index) in dataList" :key="index">
|
|
||||||
<tm-translate animation-name="fadeUp" :wait="calcAniWait(index)">
|
|
||||||
<!-- 日记卡片 -->
|
|
||||||
<journal-card :journal="journal" :useLike="true"></journal-card>
|
|
||||||
</tm-translate>
|
|
||||||
</block>
|
|
||||||
|
|
||||||
<tm-flotbutton @click="fnToTopPage" color="light-blue" size="m" icon="icon-angle-up"></tm-flotbutton>
|
|
||||||
<view class="load-text">{{ loadMoreText }}</view>
|
|
||||||
</block>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
|
||||||
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
|
||||||
import journalCard from '@/components/journal-card/journal-card.vue';
|
|
||||||
|
|
||||||
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
|
||||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
|
||||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
|
||||||
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {mpHtml, tmSkeleton, tmEmpty, tmTranslate, tmFlotbutton, journalCard},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: 'loading',
|
|
||||||
markdownConfig: MarkdownConfig,
|
|
||||||
queryParams: {
|
|
||||||
size: 10,
|
|
||||||
page: 0
|
|
||||||
},
|
|
||||||
|
|
||||||
dataList: [],
|
|
||||||
hasNext: false,
|
|
||||||
isLoadMore: false,
|
|
||||||
loadMoreText: '加载中...'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onLoad() {
|
|
||||||
this.fnSetPageTitle('个人日记');
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.fnGetData();
|
|
||||||
},
|
|
||||||
onPullDownRefresh() {
|
|
||||||
this.isLoadMore = false;
|
|
||||||
this.queryParams.page = 0;
|
|
||||||
this.fnGetData();
|
|
||||||
},
|
|
||||||
onReachBottom(e) {
|
|
||||||
if (this.hasNext) {
|
|
||||||
this.queryParams.page += 1;
|
|
||||||
this.isLoadMore = true;
|
|
||||||
this.fnGetData();
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
icon: 'none',
|
|
||||||
title: '没有更多数据了'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fnGetData() {
|
|
||||||
// uni.showLoading({
|
|
||||||
// mask: true,
|
|
||||||
// title: '加载中...'
|
|
||||||
// });
|
|
||||||
// 设置状态为加载中
|
|
||||||
if (!this.isLoadMore) {
|
|
||||||
this.loading = 'loading';
|
|
||||||
}
|
|
||||||
this.loadMoreText = '加载中...';
|
|
||||||
this.$httpApi
|
|
||||||
.getJournals(this.queryParams)
|
|
||||||
.then(res => {
|
|
||||||
this.loading = 'success';
|
|
||||||
this.loadMoreText = res.data.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
|
||||||
// 处理数据
|
|
||||||
this.result = res.data;
|
|
||||||
if (this.isLoadMore) {
|
|
||||||
this.dataList = this.dataList.concat(res.data.content);
|
|
||||||
} else {
|
|
||||||
this.dataList = res.data.content;
|
|
||||||
}
|
|
||||||
this.loading = 'success';
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
this.loading = 'error';
|
|
||||||
this.loadMoreText = '加载失败,请下拉刷新!';
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.stopPullDownRefresh();
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.app-page {
|
|
||||||
width: 100vw;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-wrap {
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-page-content {
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-empty {
|
|
||||||
height: 60vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -123,14 +123,13 @@ export default {
|
|||||||
fnGetData() {
|
fnGetData() {
|
||||||
this.loading = 'loading';
|
this.loading = 'loading';
|
||||||
this.queryParams.group = this.loveConfig.lovePhoto.groupName
|
this.queryParams.group = this.loveConfig.lovePhoto.groupName
|
||||||
console.log('this.loveConfig------------',this.loveConfig)
|
|
||||||
this.$httpApi.v2
|
this.$httpApi.v2
|
||||||
.getPhotoListByGroupName(this.queryParams)
|
.getPhotoListByGroupName(this.queryParams)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
console.log("相册 res", res)
|
console.log("相册 res", res)
|
||||||
this.loading = 'success';
|
this.loading = 'success';
|
||||||
this.dataList = res.items.map((item, index) => {
|
this.dataList = res.items.map((item, index) => {
|
||||||
item['image'] = this.$utils.checkImageUrl(item.spec.cover);
|
item['image'] = this.$utils.checkImageUrl(item.spec.cover || item.spec.url);
|
||||||
item['takeTime'] = this.$tm.dayjs(item.metadata.creationTimestamp).format(
|
item['takeTime'] = this.$tm.dayjs(item.metadata.creationTimestamp).format(
|
||||||
'DD/MM/YYYY');
|
'DD/MM/YYYY');
|
||||||
return item;
|
return item;
|
||||||
@@ -161,13 +160,13 @@ export default {
|
|||||||
fnChange(isNext) {
|
fnChange(isNext) {
|
||||||
throttle(() => {
|
throttle(() => {
|
||||||
if (isNext) {
|
if (isNext) {
|
||||||
if (this.swiperIndex == this.dataList.length - 1) {
|
if (this.swiperIndex === this.dataList.length - 1) {
|
||||||
this.swiperIndex = 0;
|
this.swiperIndex = 0;
|
||||||
} else {
|
} else {
|
||||||
this.swiperIndex += 1;
|
this.swiperIndex += 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.swiperIndex == 0) {
|
if (this.swiperIndex === 0) {
|
||||||
this.swiperIndex = this.dataList.length - 1;
|
this.swiperIndex = this.dataList.length - 1;
|
||||||
} else {
|
} else {
|
||||||
this.swiperIndex -= 1;
|
this.swiperIndex -= 1;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<image class="avatar" :src="$utils.checkAvatarUrl(loveConfig.loveInfo.boyAvatar)" mode="aspectFit"></image>
|
<image class="avatar" :src="$utils.checkAvatarUrl(loveConfig.loveInfo.boyAvatar)" mode="aspectFit"></image>
|
||||||
<view class="name">{{ loveConfig.loveInfo.boyNickname }}</view>
|
<view class="name">{{ loveConfig.loveInfo.boyNickname }}</view>
|
||||||
</view>
|
</view>
|
||||||
<image class="like" :src="$utils.checkImageUrl(loveConfig.loveImageUrl)" mode="scaleToFill"></image>
|
<image class="like" :src="$utils.checkImageUrl(loveConfig.pageImages.heartImageUrl)" mode="scaleToFill"></image>
|
||||||
<view class="girl">
|
<view class="girl">
|
||||||
<image class="avatar" :src="$utils.checkAvatarUrl(loveConfig.loveInfo.girlAvatar)" mode="aspectFit"></image>
|
<image class="avatar" :src="$utils.checkAvatarUrl(loveConfig.loveInfo.girlAvatar)" mode="aspectFit"></image>
|
||||||
<view class="name">{{ loveConfig.loveInfo.girlNickname }}</view>
|
<view class="name">{{ loveConfig.loveInfo.girlNickname }}</view>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<block v-for="(nav, index) in navList" :key="index">
|
<block v-for="(nav, index) in navList" :key="index">
|
||||||
<view v-if="nav.use" class="list-item" @click="fnToPage(nav.key)">
|
<view v-if="nav.use" class="list-item" @click="fnToPage(nav.key)">
|
||||||
<view class="left">
|
<view class="left">
|
||||||
<image class="icon" :src="nav.iconImageUrl" mode="aspectFit"></image>
|
<image class="icon" :src="$utils.checkImageUrl(nav.iconImageUrl)" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="right">
|
<view class="right">
|
||||||
<view class="name">{{ nav.title }}</view>
|
<view class="name">{{ nav.title }}</view>
|
||||||
@@ -100,21 +100,21 @@ export default {
|
|||||||
{
|
{
|
||||||
key: 'journey',
|
key: 'journey',
|
||||||
use: configs.ourStory.enabled,
|
use: configs.ourStory.enabled,
|
||||||
iconImageUrl: 'https://b.925i.cn/uni_halo_love/diandian.png',
|
iconImageUrl: configs.ourStory.iconUrl,
|
||||||
title: '关于我们',
|
title: '关于我们',
|
||||||
desc: '我们一起度过的那些经历'
|
desc: '我们一起度过的那些经历'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'album',
|
key: 'album',
|
||||||
use: configs.lovePhoto.enabled,
|
use: configs.lovePhoto.enabled,
|
||||||
iconImageUrl: 'https://b.925i.cn/uni_halo_love/diandian.png',
|
iconImageUrl: configs.lovePhoto.iconUrl,
|
||||||
title: '恋爱相册',
|
title: '恋爱相册',
|
||||||
desc: '定格了我们的那些小美好'
|
desc: '定格了我们的那些小美好'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'list',
|
key: 'list',
|
||||||
use: configs.loveDaily.enabled,
|
use: configs.loveDaily.enabled,
|
||||||
iconImageUrl: 'https://b.925i.cn/uni_halo_love/diandian.png',
|
iconImageUrl: configs.loveDaily.iconUrl,
|
||||||
title: '恋爱清单',
|
title: '恋爱清单',
|
||||||
desc: '你我之间的约定我们都在努力实现'
|
desc: '你我之间的约定我们都在努力实现'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,357 @@
|
|||||||
|
<template>
|
||||||
|
<view class="app-page">
|
||||||
|
<view v-if="loading !== 'success'" class="loading-wrap">
|
||||||
|
<tm-skeleton model="card"></tm-skeleton>
|
||||||
|
<tm-skeleton model="card"></tm-skeleton>
|
||||||
|
<tm-skeleton model="card"></tm-skeleton>
|
||||||
|
</view>
|
||||||
|
<block v-else>
|
||||||
|
<view class="moment-card">
|
||||||
|
<view class="card flex flex-start">
|
||||||
|
<view class="avatar" style="flex-shrink: 0;">
|
||||||
|
<image style="width: 80rpx;height: 80rpx;border-radius: 50%;" :src="moment.spec.user.avatar" />
|
||||||
|
</view>
|
||||||
|
<view class="nickname" style="margin-left: 12rpx;">
|
||||||
|
<view style="font-size: 30rpx;font-weight: bold;color: #333333;">
|
||||||
|
{{ moment.spec.user.displayName }}
|
||||||
|
</view>
|
||||||
|
<view style="margin-top: 6rpx;font-size: 24rpx;color: #666;">
|
||||||
|
{{ {d: moment.spec.releaseTime, f: 'yyyy年MM月dd日 星期w'} | formatTime }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="moment.spec.tags && moment.spec.tags.length!==0" class="card flex flex-wrap flex-start"
|
||||||
|
style="padding-top:12rpx;padding-bottom:12rpx;">
|
||||||
|
<text class="text-size-m">标签列表:</text>
|
||||||
|
<tm-tags v-for="(tag,tagIndex) in moment.spec.tags" :key="tagIndex" :color="randomTagColor()"
|
||||||
|
size="m" model="text">
|
||||||
|
{{ tag }}
|
||||||
|
</tm-tags>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="card" style="padding:0">
|
||||||
|
<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="moment.spec.newHtml" :markdown="true" :showLineNumber="true" :showLanguageName="true"
|
||||||
|
:copyByLongPress="true" />
|
||||||
|
</view>
|
||||||
|
<view v-if="moment.images && moment.images.length!==0" class="card">
|
||||||
|
<view class="card-head text-size-m">
|
||||||
|
图片附件:
|
||||||
|
</view>
|
||||||
|
<view class="images" :class="['images-'+moment.images.length]">
|
||||||
|
<view class="image-item" v-for="(image,mediumIndex) in moment.images" :key="mediumIndex">
|
||||||
|
<image mode="aspectFill" style="width: 100%;height: 100%;border-radius: 6rpx;"
|
||||||
|
:src="image.url" @click="handlePreview(mediumIndex,moment.images)" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="moment.audios && moment.audios.length!==0" class="card">
|
||||||
|
<view class="card-head text-size-m">
|
||||||
|
音频附件:
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
style="display: flex; flex-direction: column; gap: 12rpx 0;padding: 0 24rpx;padding-right:28rpx;">
|
||||||
|
<audio v-for="(audio,index) in moment.audios" :controls="true" :key="index" :id="audio.url"
|
||||||
|
:poster="bloggerInfo.avatar"
|
||||||
|
:name="'来自' + (startConfig.title||bloggerInfo.nickname) + '的声音'"
|
||||||
|
:author="bloggerInfo.nickname" :src="audio.url"></audio>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="moment.videos && moment.videos.length!==0" class="card">
|
||||||
|
<view class="card-head text-size-m">
|
||||||
|
视频附件:
|
||||||
|
</view>
|
||||||
|
<view style="margin-top:24rpx;width:100%;display: flex; flex-direction: column; gap: 12rpx 0;">
|
||||||
|
<video style="width:100%;height: 400rpx;border-radius: 12rpx;"
|
||||||
|
v-for="(video,index) in moment.videos" :key="index" :src="video.url"
|
||||||
|
:id="'video_' + video.id" :show-mute-btn="true" :controls="true"
|
||||||
|
:show-center-play-btn="true" :enable-progress-gesture="true" @play="onVideoPlay(video.id)"
|
||||||
|
@pause="onVideoPause(video.id)" @ended="onVideoEnded(video.id)"></video>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 返回顶部 -->
|
||||||
|
<tm-flotbutton :width="90" :offset="[16, 80]" icon="icon-angle-up" color="bg-gradient-light-blue-accent"
|
||||||
|
@click="fnToTopPage()"></tm-flotbutton>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
||||||
|
|
||||||
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
|
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
|
|
||||||
|
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
||||||
|
import {
|
||||||
|
getAppConfigs
|
||||||
|
} from '@/config/index.js'
|
||||||
|
import HaloTokenConfig from "@/config/uhalo.config";
|
||||||
|
import {
|
||||||
|
getRandomNumberByRange
|
||||||
|
} from "@/utils/random.js";
|
||||||
|
import {
|
||||||
|
generateUUID
|
||||||
|
} from '@/utils/uuid.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
tmSkeleton,
|
||||||
|
tmFlotbutton,
|
||||||
|
tmButton,
|
||||||
|
tmEmpty,
|
||||||
|
mpHtml,
|
||||||
|
tmTags
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: 'loading',
|
||||||
|
markdownConfig: MarkdownConfig,
|
||||||
|
queryParams: {
|
||||||
|
name: null
|
||||||
|
},
|
||||||
|
moment: null,
|
||||||
|
tagColors: ['orange', 'green', 'red', 'blue'],
|
||||||
|
videoContexts: {},
|
||||||
|
currentVideoId: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
haloConfigs() {
|
||||||
|
return this.$tm.vx.getters().getConfigs;
|
||||||
|
},
|
||||||
|
calcUrl() {
|
||||||
|
return url => {
|
||||||
|
if (this.$utils.checkIsUrl(url)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return getApp().globalData.baseApiUrl + url;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 获取博主信息
|
||||||
|
bloggerInfo() {
|
||||||
|
const blogger = this.haloConfigs.authorConfig.blogger;
|
||||||
|
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
|
||||||
|
return blogger;
|
||||||
|
},
|
||||||
|
calcUseTagRandomColor() {
|
||||||
|
return this.haloConfigs.pageConfig.momentConfig.useTagRandomColor;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onLoad(e) {
|
||||||
|
this.fnSetPageTitle('瞬间加载中...');
|
||||||
|
this.queryParams.name = e.name;
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
onPullDownRefresh() {
|
||||||
|
this.videoContexts = {};
|
||||||
|
this.currentVideoId = null;
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
onShareAppMessage() {
|
||||||
|
return {
|
||||||
|
path: '/pagesA/moment-detail/moment-detail?name=' + this.moment.metadata.name,
|
||||||
|
title: this.moment.spec.title,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShareTimeline() {
|
||||||
|
return {
|
||||||
|
title: this.moment.spec.title,
|
||||||
|
query: {
|
||||||
|
name: this.moment.metadata.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnGetData() {
|
||||||
|
this.loading = 'loading';
|
||||||
|
this.$httpApi.v2
|
||||||
|
.getMomentByName(this.queryParams.name)
|
||||||
|
.then(res => {
|
||||||
|
console.log('获取详情', res);
|
||||||
|
let _tempResult = res;
|
||||||
|
this.fnSetPageTitle('瞬间详情');
|
||||||
|
|
||||||
|
_tempResult.spec.user = {
|
||||||
|
displayName: this.bloggerInfo.nickname,
|
||||||
|
avatar: this.$utils.checkAvatarUrl(this.bloggerInfo.avatar)
|
||||||
|
}
|
||||||
|
_tempResult.spec.content.medium.map(medium => {
|
||||||
|
medium.url = this.$utils.checkThumbnailUrl(medium.url, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
_tempResult.spec.newHtml = this.removeTagLinksCompletely(_tempResult.spec.content.html, '')
|
||||||
|
_tempResult['images'] = _tempResult.spec.content.medium
|
||||||
|
.filter(x => x.type === 'PHOTO')
|
||||||
|
|
||||||
|
_tempResult['videos'] = _tempResult.spec.content.medium
|
||||||
|
.filter(x => x.type === 'VIDEO').map(_tempResult => {
|
||||||
|
_tempResult.id = generateUUID()
|
||||||
|
return _tempResult;
|
||||||
|
})
|
||||||
|
|
||||||
|
_tempResult['audios'] = _tempResult.spec.content.medium
|
||||||
|
.filter(x => x.type === 'AUDIO')
|
||||||
|
|
||||||
|
this.moment = _tempResult;
|
||||||
|
this.loading = 'success';
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.createVideoContexts(_tempResult.videos);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log("错误", err)
|
||||||
|
this.loading = 'error';
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handlePreview(index, list) {
|
||||||
|
uni.previewImage({
|
||||||
|
current: index,
|
||||||
|
urls: list.map(item => item.url)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeTagLinksCompletely(htmlString) {
|
||||||
|
const regex = /<a\s+(?:[^>]*?\s+)?class=(['"])[^'"]*?\btag\b[^'"]*?\1[^>]*?>.*?<\/a>/gi;
|
||||||
|
const newHtml = htmlString.replace(regex, '');
|
||||||
|
return newHtml
|
||||||
|
},
|
||||||
|
randomTagColor() {
|
||||||
|
if (!this.calcUseTagRandomColor) return "blue";
|
||||||
|
const randomIndex = getRandomNumberByRange(0, this.tagColors.length);
|
||||||
|
return this.tagColors[randomIndex];
|
||||||
|
},
|
||||||
|
createVideoContexts(videos) {
|
||||||
|
this.stopAllVideos()
|
||||||
|
videos.forEach(item => {
|
||||||
|
this.videoContexts[item.id] = uni.createVideoContext(`video_${item.id}`, this);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onVideoPlay(videoId) {
|
||||||
|
this.currentVideoId = videoId;
|
||||||
|
this.stopAllVideos(videoId)
|
||||||
|
},
|
||||||
|
onVideoPause(videoId) {
|
||||||
|
if (this.currentVideoId == videoId) {
|
||||||
|
this.currentVideoId = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onVideoEnded(videoId) {
|
||||||
|
this.currentVideoId = null;
|
||||||
|
},
|
||||||
|
stopAllVideos(excludesVideoId = null) {
|
||||||
|
Object.keys(this.videoContexts).forEach(videoId => {
|
||||||
|
if (!excludesVideoId || excludesVideoId != videoId) {
|
||||||
|
const videoContext = this.videoContexts[videoId]
|
||||||
|
videoContext?.pause();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.app-page {
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #fafafd;
|
||||||
|
padding-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrap {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
height: inherit;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moment-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
gap: 24rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #ffff;
|
||||||
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 24rpx;
|
||||||
|
|
||||||
|
&-head {
|
||||||
|
position: relative;
|
||||||
|
// padding-left: 24rpx;
|
||||||
|
margin-bottom: 6rpx;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
display: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
content: "";
|
||||||
|
width: 6rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
background: #2196F3;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 6rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.images {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 24rpx;
|
||||||
|
|
||||||
|
.image-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 6rpx;
|
||||||
|
width: 33%;
|
||||||
|
height: 200rpx
|
||||||
|
}
|
||||||
|
|
||||||
|
&-1 {
|
||||||
|
>.image-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 350rpx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-2 {
|
||||||
|
>.image-item {
|
||||||
|
width: 50%;
|
||||||
|
height: 250rpx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .uni-audio-default {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+222
-134
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="app-page bg-gradient-blue-lighten-b" :style="pageStyle">
|
<view class="app-page bg-gradient-blue-lighten-b" :class="calcPageClass" :style="[calcPageStyle]">
|
||||||
<view class="tn-satr">
|
<view v-if="calcBackgroundType!=='video'" class="tn-satr">
|
||||||
<view class="sky"></view>
|
<view class="sky"></view>
|
||||||
<view class="stars">
|
<view class="stars">
|
||||||
<view class="falling-stars">
|
<view class="falling-stars">
|
||||||
@@ -40,29 +40,55 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="user-info__container flex flex-col flex-center">
|
<video v-else class="video-bg" :object-fit="startConfig.bgVideoFit" :src="$utils.checkUrl(startConfig.bgVideo)" :loop="true"
|
||||||
<image class="user-info__avatar" :src="$utils.checkImageUrl(startConfig.logo)" mode="aspectFill"></image>
|
:autoplay="true" :muted="true" :controls="false" :show-fullscreen-btn="false" :show-play-btn="false"
|
||||||
<view class="user-info__nick-name">「 {{ startConfig.title }} 」</view>
|
:show-center-play-btn="false" :show-loading="false" :enable-progress-gesture="false"
|
||||||
</view>
|
:show-progress="false"></video>
|
||||||
|
|
||||||
<view class="text-align-center text-white" style="padding: 60vh 0 0 0;">
|
<cover-view v-if="startConfig.title || startConfig.logo" class="title-container">
|
||||||
<view class="" style="font-size: 44rpx;" v-if="startConfig.desc1">{{ startConfig.desc1 }}</view>
|
|
||||||
<view class="mt-30 text-size-m" v-if="startConfig.desc2">{{ startConfig.desc2 }}</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="" style="padding: 120rpx 200rpx;z-index: 999;position: relative;">
|
<cover-view v-if="startConfig.logo" class="app-logo">
|
||||||
<view class="start-btn" @click="fnStart()">{{ startConfig.btnText || '开始体验' }}</view>
|
<cover-view class="app-logo-border">
|
||||||
</view>
|
<cover-image class="app-logo-image" :src="$utils.checkImageUrl(startConfig.logo)"
|
||||||
|
mode="aspectFill"></cover-image>
|
||||||
|
</cover-view>
|
||||||
|
</cover-view>
|
||||||
|
|
||||||
|
<cover-view v-if="startConfig.title" class="app-title" :style="startConfig.titleStyle">
|
||||||
|
「 {{ startConfig.title }} 」
|
||||||
|
</cover-view>
|
||||||
|
</cover-view>
|
||||||
|
|
||||||
|
<cover-view class="bottom-container">
|
||||||
|
<cover-view class="text-align-center" :style="startConfig.descStyle"
|
||||||
|
style="color:white;padding: 60vh 0 0 0;">
|
||||||
|
<cover-view style="font-size: 44rpx;" v-show="startConfig.desc1">
|
||||||
|
{{ startConfig.desc1 }}
|
||||||
|
</cover-view>
|
||||||
|
<cover-view class="mt-30 text-size-m" v-show="startConfig.desc2">
|
||||||
|
{{ startConfig.desc2 }}
|
||||||
|
</cover-view>
|
||||||
|
</cover-view>
|
||||||
|
<!-- #ifdef APP-PLUS -->
|
||||||
|
<cover-view class="start-btn-app" :style="[startConfig.btnStyle]" @click="fnStart()">
|
||||||
|
{{ startConfig.btnText || '开始体验' }}
|
||||||
|
</cover-view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifndef APP-PLUS -->
|
||||||
|
<cover-view class="start-btn" :class="[startConfig.btnClass]" @click="fnStart()"
|
||||||
|
:style="[startConfig.btnStyle]">{{ startConfig.btnText || '开始体验' }}
|
||||||
|
</cover-view>
|
||||||
|
<!-- #endif -->
|
||||||
|
</cover-view>
|
||||||
<!-- 波浪效果 -->
|
<!-- 波浪效果 -->
|
||||||
<wave></wave>
|
<wave v-if="startConfig.useWave"></wave>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import wave from '@/components/wave/wave.vue';
|
import wave from '@/components/wave/wave.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
wave
|
wave
|
||||||
},
|
},
|
||||||
@@ -70,13 +96,29 @@ export default {
|
|||||||
startConfig() {
|
startConfig() {
|
||||||
return this.$tm.vx.getters().getConfigs.appConfig.startConfig;
|
return this.$tm.vx.getters().getConfigs.appConfig.startConfig;
|
||||||
},
|
},
|
||||||
pageStyle() {
|
calcBackgroundType() {
|
||||||
if (this.startConfig.bg) {
|
return this.startConfig.backgroundType;
|
||||||
const _bg = this.$utils.checkIsUrl(this.startConfig.bg) ? `url(${this.$utils.checkImageUrl(this.startConfig.bg)})` : this
|
},
|
||||||
.startConfig.bg;
|
calcPageClass() {
|
||||||
|
if (this.calcBackgroundType === 'color') {
|
||||||
|
return [this.startConfig.bg]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
calcPageStyle() {
|
||||||
|
if (this.calcBackgroundType === 'color') {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
if (this.calcBackgroundType === 'image') {
|
||||||
return {
|
return {
|
||||||
background: _bg + '!important'
|
backgroundImage: `url(${this.$utils.checkImageUrl(this.startConfig.bgImage)})!important`,
|
||||||
};
|
backgroundSize: this.startConfig.bgImageFit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.calcBackgroundType === 'video') {
|
||||||
|
return {
|
||||||
|
background: '#ffffff'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -91,16 +133,19 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-page {
|
.app-page {
|
||||||
|
position: relative;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
.start-btn {
|
.start-btn {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
padding: 16rpx 50rpx;
|
padding: 16rpx 50rpx;
|
||||||
@@ -109,257 +154,300 @@ export default {
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
}
|
margin-top: 60rpx;
|
||||||
|
margin-bottom: 120rpx;
|
||||||
/* 用户信息 start */
|
|
||||||
.user-info {
|
|
||||||
&__container {
|
|
||||||
position: absolute;
|
|
||||||
top: 25vh;
|
|
||||||
left: 50%;
|
|
||||||
-webkit-transform: translate(-50%, -50%);
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__avatar {
|
.start-btn-app {
|
||||||
|
border-radius: 50rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
margin: 60rpx auto 120rpx auto;
|
||||||
|
width: 40vw;
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
border: 2rpx solid #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 50rpx;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-bg {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-container {
|
||||||
|
width: 100vw;
|
||||||
|
position: absolute;
|
||||||
|
top: 20vh;
|
||||||
|
left: 0;
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-logo {
|
||||||
width: 200rpx;
|
width: 200rpx;
|
||||||
height: 200rpx;
|
height: 200rpx;
|
||||||
border: 8rpx solid rgba(255, 255, 255, 0.05);
|
|
||||||
|
&-border {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 8rpx solid rgba(255, 255, 255, 0.35);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0rpx 0rpx 80rpx 0rpx rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__nick-name {
|
&-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-title {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
margin-top: 26rpx;
|
margin-top: 26rpx;
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* 用户信息 end */
|
|
||||||
|
|
||||||
/* 流星*/
|
.tn-satr {
|
||||||
.tn-satr {
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 600px;
|
height: 600px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
z-index: 998;
|
z-index: 998;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stars {
|
.stars {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star {
|
.star {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
box-shadow: 0px 0px 6px 0px rgba(255, 255, 255, 0.8);
|
box-shadow: 0 0 6px 0 rgba(255, 255, 255, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star {
|
.small-stars .star {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 3px;
|
width: 3px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(2n) {
|
.small-stars .star:nth-child(2n) {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-animation: star-blink 1.2s linear infinite alternate;
|
-webkit-animation: star-blink 1.2s linear infinite alternate;
|
||||||
animation: star-blink 1.2s linear infinite alternate;
|
animation: star-blink 1.2s linear infinite alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(1) {
|
.small-stars .star:nth-child(1) {
|
||||||
left: 40px;
|
left: 40px;
|
||||||
bottom: 50px;
|
bottom: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(2) {
|
.small-stars .star:nth-child(2) {
|
||||||
left: 200px;
|
left: 200px;
|
||||||
bottom: 40px;
|
bottom: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(3) {
|
.small-stars .star:nth-child(3) {
|
||||||
left: 60px;
|
left: 60px;
|
||||||
bottom: 120px;
|
bottom: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(4) {
|
.small-stars .star:nth-child(4) {
|
||||||
left: 140px;
|
left: 140px;
|
||||||
bottom: 250px;
|
bottom: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(5) {
|
.small-stars .star:nth-child(5) {
|
||||||
left: 400px;
|
left: 400px;
|
||||||
bottom: 300px;
|
bottom: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(6) {
|
.small-stars .star:nth-child(6) {
|
||||||
left: 170px;
|
left: 170px;
|
||||||
bottom: 80px;
|
bottom: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(7) {
|
.small-stars .star:nth-child(7) {
|
||||||
left: 200px;
|
left: 200px;
|
||||||
bottom: 360px;
|
bottom: 360px;
|
||||||
-webkit-animation-delay: 0.2s;
|
-webkit-animation-delay: 0.2s;
|
||||||
animation-delay: 0.2s;
|
animation-delay: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(8) {
|
.small-stars .star:nth-child(8) {
|
||||||
left: 250px;
|
left: 250px;
|
||||||
bottom: 320px;
|
bottom: 320px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(9) {
|
.small-stars .star:nth-child(9) {
|
||||||
left: 300px;
|
left: 300px;
|
||||||
bottom: 340px;
|
bottom: 340px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(10) {
|
.small-stars .star:nth-child(10) {
|
||||||
left: 130px;
|
left: 130px;
|
||||||
bottom: 320px;
|
bottom: 320px;
|
||||||
-webkit-animation-delay: 0.5s;
|
-webkit-animation-delay: 0.5s;
|
||||||
animation-delay: 0.5s;
|
animation-delay: 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(11) {
|
.small-stars .star:nth-child(11) {
|
||||||
left: 230px;
|
left: 230px;
|
||||||
bottom: 330px;
|
bottom: 330px;
|
||||||
-webkit-animation-delay: 7s;
|
-webkit-animation-delay: 7s;
|
||||||
animation-delay: 7s;
|
animation-delay: 7s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-stars .star:nth-child(12) {
|
.small-stars .star:nth-child(12) {
|
||||||
left: 300px;
|
left: 300px;
|
||||||
bottom: 360px;
|
bottom: 360px;
|
||||||
-webkit-animation-delay: 0.3s;
|
-webkit-animation-delay: 0.3s;
|
||||||
animation-delay: 0.3s;
|
animation-delay: 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes star-blink {
|
@-webkit-keyframes star-blink {
|
||||||
50% {
|
50% {
|
||||||
width: 3px;
|
width: 3px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes star-blink {
|
@keyframes star-blink {
|
||||||
50% {
|
50% {
|
||||||
width: 3px;
|
width: 3px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star {
|
.medium-stars .star {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 3px;
|
width: 3px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-animation: star-blink 1.2s ease-in infinite alternate;
|
-webkit-animation: star-blink 1.2s ease-in infinite alternate;
|
||||||
animation: star-blink 1.2s ease-in infinite alternate;
|
animation: star-blink 1.2s ease-in infinite alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(1) {
|
.medium-stars .star:nth-child(1) {
|
||||||
left: 300px;
|
left: 300px;
|
||||||
bottom: 50px;
|
bottom: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(2) {
|
.medium-stars .star:nth-child(2) {
|
||||||
left: 400px;
|
left: 400px;
|
||||||
bottom: 40px;
|
bottom: 40px;
|
||||||
-webkit-animation-delay: 0.4s;
|
-webkit-animation-delay: 0.4s;
|
||||||
animation-delay: 0.4s;
|
animation-delay: 0.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(3) {
|
.medium-stars .star:nth-child(3) {
|
||||||
left: 330px;
|
left: 330px;
|
||||||
bottom: 300px;
|
bottom: 300px;
|
||||||
-webkit-animation-delay: 0.2s;
|
-webkit-animation-delay: 0.2s;
|
||||||
animation-delay: 0.2s;
|
animation-delay: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(4) {
|
.medium-stars .star:nth-child(4) {
|
||||||
left: 460px;
|
left: 460px;
|
||||||
bottom: 300px;
|
bottom: 300px;
|
||||||
-webkit-animation-delay: 0.9s;
|
-webkit-animation-delay: 0.9s;
|
||||||
animation-delay: 0.9s;
|
animation-delay: 0.9s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(5) {
|
.medium-stars .star:nth-child(5) {
|
||||||
left: 300px;
|
left: 300px;
|
||||||
bottom: 150px;
|
bottom: 150px;
|
||||||
-webkit-animation-delay: 1.2s;
|
-webkit-animation-delay: 1.2s;
|
||||||
animation-delay: 1.2s;
|
animation-delay: 1.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(6) {
|
.medium-stars .star:nth-child(6) {
|
||||||
left: 440px;
|
left: 440px;
|
||||||
bottom: 120px;
|
bottom: 120px;
|
||||||
-webkit-animation-delay: 1s;
|
-webkit-animation-delay: 1s;
|
||||||
animation-delay: 1s;
|
animation-delay: 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(7) {
|
.medium-stars .star:nth-child(7) {
|
||||||
left: 200px;
|
left: 200px;
|
||||||
bottom: 140px;
|
bottom: 140px;
|
||||||
-webkit-animation-delay: 0.8s;
|
-webkit-animation-delay: 0.8s;
|
||||||
animation-delay: 0.8s;
|
animation-delay: 0.8s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(8) {
|
.medium-stars .star:nth-child(8) {
|
||||||
left: 30px;
|
left: 30px;
|
||||||
bottom: 480px;
|
bottom: 480px;
|
||||||
-webkit-animation-delay: 0.3s;
|
-webkit-animation-delay: 0.3s;
|
||||||
animation-delay: 0.3s;
|
animation-delay: 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(9) {
|
.medium-stars .star:nth-child(9) {
|
||||||
left: 460px;
|
left: 460px;
|
||||||
bottom: 400px;
|
bottom: 400px;
|
||||||
-webkit-animation-delay: 1.2s;
|
-webkit-animation-delay: 1.2s;
|
||||||
animation-delay: 1.2s;
|
animation-delay: 1.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(10) {
|
.medium-stars .star:nth-child(10) {
|
||||||
left: 150px;
|
left: 150px;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
-webkit-animation-delay: 1s;
|
-webkit-animation-delay: 1s;
|
||||||
animation-delay: 1s;
|
animation-delay: 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(11) {
|
.medium-stars .star:nth-child(11) {
|
||||||
left: 420px;
|
left: 420px;
|
||||||
bottom: 450px;
|
bottom: 450px;
|
||||||
-webkit-animation-delay: 1.2s;
|
-webkit-animation-delay: 1.2s;
|
||||||
animation-delay: 1.2s;
|
animation-delay: 1.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium-stars .star:nth-child(12) {
|
.medium-stars .star:nth-child(12) {
|
||||||
left: 340px;
|
left: 340px;
|
||||||
bottom: 180px;
|
bottom: 180px;
|
||||||
-webkit-animation-delay: 1.1s;
|
-webkit-animation-delay: 1.1s;
|
||||||
animation-delay: 1.1s;
|
animation-delay: 1.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes star-blink {
|
@keyframes star-blink {
|
||||||
50% {
|
50% {
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall {
|
.star-fall {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
@@ -367,9 +455,9 @@ export default {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
-webkit-transform: rotate(-20deg);
|
-webkit-transform: rotate(-20deg);
|
||||||
transform: rotate(-20deg);
|
transform: rotate(-20deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall:after {
|
.star-fall:after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
@@ -379,49 +467,49 @@ export default {
|
|||||||
left: 100%;
|
left: 100%;
|
||||||
-webkit-animation: star-fall 3.6s linear infinite;
|
-webkit-animation: star-fall 3.6s linear infinite;
|
||||||
animation: star-fall 3.6s linear infinite;
|
animation: star-fall 3.6s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall:nth-child(1) {
|
.star-fall:nth-child(1) {
|
||||||
left: 80px;
|
left: 80px;
|
||||||
bottom: -100px;
|
bottom: -100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall:nth-child(1):after {
|
.star-fall:nth-child(1):after {
|
||||||
-webkit-animation-delay: 2.4s;
|
-webkit-animation-delay: 2.4s;
|
||||||
animation-delay: 2.4s;
|
animation-delay: 2.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall:nth-child(2) {
|
.star-fall:nth-child(2) {
|
||||||
left: 200px;
|
left: 200px;
|
||||||
bottom: -200px;
|
bottom: -200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall:nth-child(2):after {
|
.star-fall:nth-child(2):after {
|
||||||
-webkit-animation-delay: 2s;
|
-webkit-animation-delay: 2s;
|
||||||
animation-delay: 2s;
|
animation-delay: 2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall:nth-child(3) {
|
.star-fall:nth-child(3) {
|
||||||
left: 430px;
|
left: 430px;
|
||||||
bottom: -50px;
|
bottom: -50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall:nth-child(3):after {
|
.star-fall:nth-child(3):after {
|
||||||
-webkit-animation-delay: 3.6s;
|
-webkit-animation-delay: 3.6s;
|
||||||
animation-delay: 3.6s;
|
animation-delay: 3.6s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall:nth-child(4) {
|
.star-fall:nth-child(4) {
|
||||||
left: 400px;
|
left: 400px;
|
||||||
bottom: 100px;
|
bottom: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-fall:nth-child(4):after {
|
.star-fall:nth-child(4):after {
|
||||||
-webkit-animation-delay: 0.2s;
|
-webkit-animation-delay: 0.2s;
|
||||||
animation-delay: 0.2s;
|
animation-delay: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes star-fall {
|
@-webkit-keyframes star-fall {
|
||||||
20% {
|
20% {
|
||||||
left: -100%;
|
left: -100%;
|
||||||
}
|
}
|
||||||
@@ -429,9 +517,9 @@ export default {
|
|||||||
100% {
|
100% {
|
||||||
left: -100%;
|
left: -100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes star-fall {
|
@keyframes star-fall {
|
||||||
20% {
|
20% {
|
||||||
left: -100%;
|
left: -100%;
|
||||||
}
|
}
|
||||||
@@ -439,5 +527,5 @@ export default {
|
|||||||
100% {
|
100% {
|
||||||
left: -100%;
|
left: -100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<view class="poup pa-36" scroll-y="auto">
|
<view class="poup pa-36" scroll-y="auto">
|
||||||
<view class="info flex">
|
<view class="info flex">
|
||||||
<view class="poup-logo pa-4 shadow-24">
|
<view class="poup-logo pa-4 shadow-24">
|
||||||
<image :src="blogDetail.blogLogo" class="poup-logo_img" mode="aspectFill"></image>
|
<image :src="$utils.checkAvatarUrl(blogDetail.blogLogo)" class="poup-logo_img" mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="pl-24 info-detail">
|
<view class="pl-24 info-detail">
|
||||||
<view class="poup-name text-size-lg text-weight-b">{{ blogDetail.blogName }}</view>
|
<view class="poup-name text-size-lg text-weight-b">{{ blogDetail.blogName }}</view>
|
||||||
@@ -97,6 +97,7 @@ import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue'
|
|||||||
import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue'
|
import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue'
|
||||||
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue'
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue'
|
||||||
import tmCoupon from '@/tm-vuetify/components/tm-coupon/tm-coupon.vue'
|
import tmCoupon from '@/tm-vuetify/components/tm-coupon/tm-coupon.vue'
|
||||||
|
import $utils from "@/utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -128,11 +129,14 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
$utils() {
|
||||||
|
return $utils
|
||||||
|
},
|
||||||
haloPluginConfigs(){
|
haloPluginConfigs(){
|
||||||
return this.$tm.vx.getters().getConfigs.pluginConfig;
|
return this.$tm.vx.getters().getConfigs.pluginConfig;
|
||||||
},
|
},
|
||||||
blogDetail() {
|
blogDetail() {
|
||||||
return this.haloPluginConfigs.submitLink;
|
return this.haloPluginConfigs.linksSubmitPlugin;
|
||||||
},
|
},
|
||||||
calcSiteThumbnail(val) {
|
calcSiteThumbnail(val) {
|
||||||
return val => {
|
return val => {
|
||||||
@@ -147,13 +151,13 @@ export default {
|
|||||||
return `
|
return `
|
||||||
博客名称:${this.blogDetail.blogName}
|
博客名称:${this.blogDetail.blogName}
|
||||||
博客地址:${this.blogDetail.blogUrl}
|
博客地址:${this.blogDetail.blogUrl}
|
||||||
博客logo:${this.blogDetail.blogLogo}
|
博客logo:${$utils.checkAvatarUrl(this.blogDetail.blogLogo)}
|
||||||
博客简介:${this.blogDetail.blogDesc}
|
博客简介:${this.blogDetail.blogDesc}
|
||||||
`
|
`
|
||||||
},
|
},
|
||||||
calcBlogCoupon() {
|
calcBlogCoupon() {
|
||||||
return {
|
return {
|
||||||
img: this.blogDetail.blogLogo,
|
img: $utils.checkAvatarUrl(this.blogDetail.blogLogo),
|
||||||
title: this.blogDetail.blogName,
|
title: this.blogDetail.blogName,
|
||||||
time: this.blogDetail.blogDesc,
|
time: this.blogDetail.blogDesc,
|
||||||
btnText: '友链详情'
|
btnText: '友链详情'
|
||||||
@@ -192,7 +196,7 @@ export default {
|
|||||||
this.$httpApi.v2.submitLink(this.form)
|
this.$httpApi.v2.submitLink(this.form)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
uni.$tm.toast('友链提交成功!');
|
uni.$tm.toast(res.msg);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pagesA/friend-links/friend-links',
|
url: '/pagesA/friend-links/friend-links',
|
||||||
@@ -204,7 +208,7 @@ export default {
|
|||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
uni.$tm.toast('操作失败,请重试!');
|
uni.$tm.toast(res.msg);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|||||||
@@ -1,289 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="app-page">
|
|
||||||
|
|
||||||
<view v-if="loading != 'success' && articleList.length===0" class="loading-wrap">
|
|
||||||
加载中
|
|
||||||
</view>
|
|
||||||
<block v-else>
|
|
||||||
|
|
||||||
<view v-if="articleList.length == 0" class="article-empty"><tm-empty icon="icon-shiliangzhinengduixiang-"
|
|
||||||
label="博主还没有发表任何文章~"></tm-empty></view>
|
|
||||||
<block v-else>
|
|
||||||
|
|
||||||
<view style="width: 100%;text-align: center;display: flex;flex-direction: column;align-items: center;"
|
|
||||||
v-for="(article, index) in articleList" :key="index">
|
|
||||||
<image style="width: 100%;height: 250rpx;" :src="article.cover"></image>
|
|
||||||
<view style="margin-top: 12rpx;">{{article.title}}</view>
|
|
||||||
</view>
|
|
||||||
<view class="load-text mt-12">{{ loadMoreText }}</view>
|
|
||||||
</block>
|
|
||||||
</block>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: 'loading',
|
|
||||||
queryParams: {
|
|
||||||
size: 30,
|
|
||||||
page: 1
|
|
||||||
},
|
|
||||||
result: {},
|
|
||||||
isLoadMore: false,
|
|
||||||
loadMoreText: '加载中...',
|
|
||||||
bannerCurrent: 0,
|
|
||||||
bannerList: [],
|
|
||||||
noticeList: [],
|
|
||||||
articleList: [],
|
|
||||||
categoryList: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onLoad() {
|
|
||||||
this.fnQuery();
|
|
||||||
},
|
|
||||||
|
|
||||||
onPullDownRefresh() {
|
|
||||||
this.isLoadMore = false;
|
|
||||||
this.queryParams.page = 1;
|
|
||||||
this.fnQuery();
|
|
||||||
},
|
|
||||||
|
|
||||||
onReachBottom(e) {
|
|
||||||
if (this.result.hasNext) {
|
|
||||||
this.queryParams.page += 1;
|
|
||||||
this.isLoadMore = true;
|
|
||||||
this.fnGetArticleList();
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
icon: 'none',
|
|
||||||
title: '没有更多数据了'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fnQuery() {
|
|
||||||
this.fnGetArticleList();
|
|
||||||
},
|
|
||||||
fnOnBannerChange(e) {
|
|
||||||
this.bannerCurrent = e.current;
|
|
||||||
},
|
|
||||||
fnOnBannerClick(item) {
|
|
||||||
if (item.id == '') return;
|
|
||||||
this.fnToArticleDetail({
|
|
||||||
metadata: {
|
|
||||||
name: item.id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fnGetArticleList() {
|
|
||||||
|
|
||||||
// 设置状态为加载中
|
|
||||||
if (!this.isLoadMore) {
|
|
||||||
this.loading = 'loading';
|
|
||||||
}
|
|
||||||
this.loadMoreText = '加载中...';
|
|
||||||
const list = new Array(30).fill(10).map(item => {
|
|
||||||
return {
|
|
||||||
title: '红红火火恍恍惚惚哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈发放',
|
|
||||||
cover: 'https://blog.925i.cn/upload/房间%20少女%20蓝色眼睛%20夏天的裙子%20室内绿色植物%20动漫壁纸_彼岸壁纸.webp'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.result = {
|
|
||||||
hasNext: true
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.isLoadMore) {
|
|
||||||
this.articleList = this.articleList.concat(list);
|
|
||||||
} else {
|
|
||||||
this.articleList = list;
|
|
||||||
}
|
|
||||||
}, 200)
|
|
||||||
this.loading = 'success';
|
|
||||||
this.loadMoreText = this.result ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.stopPullDownRefresh();
|
|
||||||
}, 300)
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
//跳转文章详情
|
|
||||||
fnToArticleDetail(article) {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pagesA/article-detail/article-detail?name=' + article.metadata.name,
|
|
||||||
animationType: 'slide-in-right'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 快捷导航页面跳转
|
|
||||||
fnToNavPage(item) {
|
|
||||||
switch (item.type) {
|
|
||||||
case 'tabbar':
|
|
||||||
uni.switchTab({
|
|
||||||
url: item.path
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'page':
|
|
||||||
uni.navigateTo({
|
|
||||||
url: item.path
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 分类页面
|
|
||||||
fnToCategoryPage() {
|
|
||||||
uni.switchTab({
|
|
||||||
url: '/pages/tabbar/category/category'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 所有的文章列表页面
|
|
||||||
fnToArticlesPage() {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pagesA/articles/articles'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 根据slug查询分类下的文章
|
|
||||||
fnToCategoryBy(category) {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: `/pagesA/category-detail/category-detail?name=${category.metadata.name}&title=${category.spec.displayName}`
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
fnChangeMode() {
|
|
||||||
const isBlackTheme = this.$tm.vx.state().tmVuetify.black;
|
|
||||||
this.$tm.theme.setBlack(!isBlackTheme);
|
|
||||||
uni.setNavigationBarColor({
|
|
||||||
backgroundColor: !isBlackTheme ? '#0a0a0a' : '#ffffff',
|
|
||||||
frontColor: !isBlackTheme ? '#ffffff' : '#0a0a0a'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
fnToSearch() {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pagesA/articles/articles'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.app-page {
|
|
||||||
width: 100vw;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
// background-color: #ffffff;
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
width: 60rpx;
|
|
||||||
height: 60rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep {
|
|
||||||
.tm-menubars .body .body_wk .left {
|
|
||||||
min-width: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-wrap {
|
|
||||||
padding: 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
align-items: center;
|
|
||||||
/* #ifdef MP-WEIXIN */
|
|
||||||
margin-right: 24rpx;
|
|
||||||
|
|
||||||
/* #endif */
|
|
||||||
&_icon {}
|
|
||||||
|
|
||||||
&_text {}
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-more {
|
|
||||||
width: 42rpx;
|
|
||||||
height: 42rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-nav {
|
|
||||||
background-color: #fff;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
// box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
|
||||||
.name {
|
|
||||||
color: var(--main-text-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.category {
|
|
||||||
width: 94vw;
|
|
||||||
display: flex;
|
|
||||||
height: 200rpx;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0 24rpx;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: inline-block;
|
|
||||||
padding-left: 24rpx;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cate-empty {
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-item {
|
|
||||||
&_title {
|
|
||||||
position: relative;
|
|
||||||
padding-left: 24rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
z-index: 1;
|
|
||||||
color: var(--main-text-color);
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: 0rpx;
|
|
||||||
top: 8rpx;
|
|
||||||
width: 8rpx;
|
|
||||||
height: 30rpx;
|
|
||||||
background-color: rgba(33, 150, 243, 1);
|
|
||||||
border-radius: 6rpx;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.h_row_col2 {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0 12rpx;
|
|
||||||
|
|
||||||
.ani-item {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,729 @@
|
|||||||
|
<template>
|
||||||
|
<view class="app-page">
|
||||||
|
<view v-if="loading != 'success'" class="loading-wrap">
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
</view>
|
||||||
|
<block v-else>
|
||||||
|
<view v-if="!detail" class="empty">
|
||||||
|
<tm-empty icon="icon-shiliangzhinengduixiang-" label="未查询到数据"></tm-empty>
|
||||||
|
</view>
|
||||||
|
<block v-else>
|
||||||
|
<view class="vote-card vote-info-card">
|
||||||
|
<view class="sub-title"> 投票信息 </view>
|
||||||
|
<view class="vote-card-body flex flex-col uh-mt-8">
|
||||||
|
<view class="">
|
||||||
|
投票类型:<tm-tags color="light-blue" :shadow="0" size="xs" model="fill">
|
||||||
|
{{ vote.spec._uh_type }} </tm-tags>
|
||||||
|
</view>
|
||||||
|
<view class="">
|
||||||
|
投票状态:<tm-tags :color="vote.spec._uh_state.color" size="xs" :shadow="0"
|
||||||
|
model="fill">{{vote.spec._uh_state.state}}</tm-tags>
|
||||||
|
</view>
|
||||||
|
<view class="">
|
||||||
|
投票方式:<tm-tags v-if="vote.spec.canAnonymously" color="light-blue" size="xs" :shadow="0"
|
||||||
|
model="fill">匿名</tm-tags>
|
||||||
|
<tm-tags v-else color="red" size="xs" :shadow="0" model="fill">不匿名</tm-tags>
|
||||||
|
</view>
|
||||||
|
<view class="">
|
||||||
|
开始时间:{{ {d: vote.spec.startDate, f: 'yyyy-MM-dd HH:mm'} | formatTime }}
|
||||||
|
</view>
|
||||||
|
<view class="">
|
||||||
|
<text v-if="vote.spec.timeLimit==='permanent'">
|
||||||
|
结束时间:永久有效
|
||||||
|
</text>
|
||||||
|
<text v-else>
|
||||||
|
结束时间:{{ {d: vote.spec.endDate, f: 'yyyy-MM-dd HH:mm'} | formatTime }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="vote-card">
|
||||||
|
<view class="vote-card-head flex flex-col items-start mb-12">
|
||||||
|
<view class="sub-title"> 投票内容 </view>
|
||||||
|
<view class="sub-content">
|
||||||
|
{{ vote.spec.title }}
|
||||||
|
</view>
|
||||||
|
<view v-if="vote.spec.remark" class="sub-remark">
|
||||||
|
{{ vote.spec.remark }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="vote-card-body uh-mt-8">
|
||||||
|
<view class="sub-title"> 投票选项 <text v-if="vote.spec.type==='multiple'"
|
||||||
|
class="sub-title-count">(最多选择 {{ vote.spec.maxVotes }} 项)</text> </view>
|
||||||
|
<view v-if="vote.spec.type==='single'" class="single">
|
||||||
|
<view class="w-full flex flex-col uh-gap-8">
|
||||||
|
<template v-if="vote.spec.isVoted || vote.spec.hasEnded">
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="is-voted-item" :class="[option.checked?'selected':'']" :style="{
|
||||||
|
'--percent':option._uh_percent + '%'
|
||||||
|
}">
|
||||||
|
<view class="is-voted-item-content flex w-full flex-between uh-gap-8">
|
||||||
|
<view class="flex-1 text-align-left text-break">
|
||||||
|
{{option.title }}
|
||||||
|
</view>
|
||||||
|
<view class="flex-shrink">
|
||||||
|
{{option._uh_percent }}%
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="vote-select-option flex-1 w-full text-break"
|
||||||
|
:class="[option.checked?'light-blue':'grey-lighten-3']"
|
||||||
|
@click="handleSelectSingleOption(option)">
|
||||||
|
{{option.title }}
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else-if="vote.spec.type==='multiple'" class="multiple">
|
||||||
|
<view class="w-full flex flex-col uh-gap-8">
|
||||||
|
<template v-if="vote.spec.isVoted || vote.spec.hasEnded">
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="is-voted-item" :class="[option.checked?'selected':'']" :style="{
|
||||||
|
'--percent':option._uh_percent + '%'
|
||||||
|
}">
|
||||||
|
<view class="is-voted-item-content flex w-full flex-between uh-gap-8">
|
||||||
|
<view class="flex-1 text-align-left text-break">
|
||||||
|
{{option.title }}
|
||||||
|
</view>
|
||||||
|
<view class="flex-shrink">
|
||||||
|
{{option._uh_percent }}%
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="vote-select-option flex-1 w-full text-break"
|
||||||
|
:class="[option.checked?'light-blue':'grey-lighten-3']"
|
||||||
|
@click="handleSelectCheckboxOption(option)">
|
||||||
|
{{option.title }}
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else-if="vote.spec.type==='pk'" class="pk">
|
||||||
|
<view class="pk-container">
|
||||||
|
<view class="radio-item" v-for="(option,optionIndex) in vote.spec.options"
|
||||||
|
:key="optionIndex" :class="[optionIndex==0?'radio-left':'radio-right']"
|
||||||
|
:style="{width: option._uh_percent + '%'}">
|
||||||
|
<view class="option-item"
|
||||||
|
:class="[optionIndex==0?'option-item-left':'option-item-right']">
|
||||||
|
{{option._uh_percent }}%
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="option-foot w-full flex flex-col uh-gap-8">
|
||||||
|
<template v-if="vote.spec.isVoted || vote.spec.hasEnded">
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="is-voted-item" :class="[option.checked?'selected':'']" :style="{
|
||||||
|
'--percent': option._uh_percent + '%'
|
||||||
|
}">
|
||||||
|
<view class="is-voted-item-content flex w-full flex-between uh-gap-8">
|
||||||
|
<view class="flex-1 text-align-left text-break">
|
||||||
|
选项{{ optionIndex+1}}:{{option.title }}
|
||||||
|
</view>
|
||||||
|
<view class="flex-shrink">
|
||||||
|
{{option._uh_percent }}%
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<view v-for="(option,optionIndex) in vote.spec.options" :key="optionIndex"
|
||||||
|
class="vote-select-option flex-1 w-full text-break"
|
||||||
|
:class="[option.checked?'light-blue':'grey-lighten-3']"
|
||||||
|
@click="handleSelectSingleOption(option)">
|
||||||
|
选项{{ optionIndex+1}}:{{option.title }}
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="vote-card">
|
||||||
|
<view class="vote-card-body">
|
||||||
|
<view class="sub-title"> 投票统计 </view>
|
||||||
|
<view class="">
|
||||||
|
<tm-tags color="grey-darken-4" size="s" model="text">{{ vote.stats.voteCount }}
|
||||||
|
人已参与</tm-tags>
|
||||||
|
<!-- <tm-tags v-if="vote.spec.isVoted" color="blue" rounded size="s" model="text">已投票</tm-tags> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 暂时不做,目前没有意义 -->
|
||||||
|
<!-- <view v-if="vote.spec.canSeeVoters" class="vote-card">
|
||||||
|
<view class="vote-card-body">
|
||||||
|
<view class="sub-title"> 投票用户 </view>
|
||||||
|
<view class="">
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view> -->
|
||||||
|
|
||||||
|
<view class="vote-submit flex w-full flex-center" :style="{
|
||||||
|
paddingBottom:safeAreaBottom + 'rpx'
|
||||||
|
}">
|
||||||
|
<tm-button v-if="fnCalcIsVoted()" theme="white" text :block="true" class="w-full">您已参与投票</tm-button>
|
||||||
|
<tm-button v-else-if="vote.spec._uh_state.state==='未开始'" theme="orange" text class="w-full"
|
||||||
|
:block="true" @click="handleSubmitTip('投票未开始')">投票未开始</tm-button>
|
||||||
|
<tm-button v-else-if="vote.spec._uh_state.state==='已结束'" theme="red" text class="w-full"
|
||||||
|
:block="true" @click="handleSubmitTip('投票已结束')">投票已结束</tm-button>
|
||||||
|
<tm-button v-else-if="!vote.spec.canAnonymously" theme="red" :shadow="0" class="w-full" text
|
||||||
|
:block="true" @click="handleSubmit()">不支持匿名投票</tm-button>
|
||||||
|
<tm-button v-else-if="submitForm.voteData.length===0" theme="white" text class="w-full"
|
||||||
|
:block="true" @click="handleSubmitTip('请选择选项')">提交投票(请选择选项)</tm-button>
|
||||||
|
<tm-button v-else theme="light-blue" class="w-full" :block="true" :loading="submitLoading"
|
||||||
|
:disabled="submitLoading" @click="handleSubmit()">提交投票</tm-button>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
VOTE_TYPES,
|
||||||
|
calcVoteState,
|
||||||
|
calcVotePercent,
|
||||||
|
voteCacheUtil
|
||||||
|
} from '@/utils/vote.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
tmSkeleton,
|
||||||
|
tmEmpty,
|
||||||
|
tmButton,
|
||||||
|
tmTags,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
safeAreaBottom: 24,
|
||||||
|
loading: 'loading',
|
||||||
|
submitLoading: false,
|
||||||
|
pageTitle: '加载中...',
|
||||||
|
|
||||||
|
name: '',
|
||||||
|
detail: null,
|
||||||
|
vote: null,
|
||||||
|
submitForm: {
|
||||||
|
voteData: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad(e) {
|
||||||
|
this.name = e.name;
|
||||||
|
this.fnGetData();
|
||||||
|
// this.fnGetVoteUserList();
|
||||||
|
// #ifndef H5
|
||||||
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.safeAreaBottom = systemInfo.safeAreaInsets.bottom + 12;
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
onPullDownRefresh() {
|
||||||
|
this.fnGetData();
|
||||||
|
// this.fnGetVoteUserList();
|
||||||
|
},
|
||||||
|
onShareAppMessage() {
|
||||||
|
return {
|
||||||
|
path: '/pagesA/vote-detail/vote-detail?name=' + this.name,
|
||||||
|
title: this.vote?.spec?.title ?? "来投个票吧",
|
||||||
|
imageUrl: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShareTimeline() {
|
||||||
|
return {
|
||||||
|
title: this.vote?.spec?.title ?? "来投个票吧",
|
||||||
|
query: {
|
||||||
|
name: this.name
|
||||||
|
},
|
||||||
|
imageUrl: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnGetData() {
|
||||||
|
// 设置状态为加载中
|
||||||
|
this.loading = 'loading';
|
||||||
|
this.pageTitle = "加载中..."
|
||||||
|
this.$httpApi.v2
|
||||||
|
.getVoteDetail(this.name)
|
||||||
|
.then(res => {
|
||||||
|
this.pageTitle = "投票详情" + `(${VOTE_TYPES[res.vote.spec.type]})`
|
||||||
|
|
||||||
|
const tempVoteRes = res;
|
||||||
|
|
||||||
|
tempVoteRes.vote.spec.isVoted = this.fnCalcIsVoted()
|
||||||
|
tempVoteRes.vote.spec.disabled = this.fnCalcIsVoted()
|
||||||
|
tempVoteRes.vote.spec._uh_state = calcVoteState(tempVoteRes.vote)
|
||||||
|
tempVoteRes.vote.spec._uh_type = VOTE_TYPES[tempVoteRes.vote.spec.type]
|
||||||
|
|
||||||
|
tempVoteRes.vote.spec.options.map((option, index) => {
|
||||||
|
option.value = option.id
|
||||||
|
option.label = option.title
|
||||||
|
option.isVoted = this.fnCalcIsVoted()
|
||||||
|
option.checked = this.fnCalcIsChecked(option)
|
||||||
|
option._uh_percent = calcVotePercent(tempVoteRes.vote, option);
|
||||||
|
|
||||||
|
option.dataStr = JSON.stringify(option)
|
||||||
|
|
||||||
|
return option
|
||||||
|
})
|
||||||
|
|
||||||
|
this.vote = tempVoteRes.vote
|
||||||
|
this.detail = tempVoteRes;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loading = 'success';
|
||||||
|
}, 200);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
this.loading = 'error';
|
||||||
|
this.pageTitle = "加载失败,请重试..."
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
this.fnSetPageTitle(this.pageTitle);
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fnGetVoteUserList() {
|
||||||
|
this.$httpApi.v2
|
||||||
|
.getVoteUserList(this.name)
|
||||||
|
.then(res => {
|
||||||
|
console.log("查询投票用户列表")
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log("err")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fnCalcIsVoted() {
|
||||||
|
return voteCacheUtil.has(this.name)
|
||||||
|
},
|
||||||
|
|
||||||
|
fnCalcIsChecked(option) {
|
||||||
|
const data = voteCacheUtil.get(this.name)
|
||||||
|
if (!data) return false;
|
||||||
|
const checked = data.selected.includes(option.id)
|
||||||
|
return checked
|
||||||
|
},
|
||||||
|
onOptionRadioChange(e) {
|
||||||
|
this.submitForm.voteData = e.map(item => this.vote.spec.options[item.index]?.id);
|
||||||
|
},
|
||||||
|
onOptionCheckboxChange(e) {
|
||||||
|
this.submitForm.voteData = e.map(item => this.vote.spec.options[item.index]?.id);
|
||||||
|
},
|
||||||
|
onOptionPkChange(e) {
|
||||||
|
this.submitForm.voteData = e.map(item => this.vote.spec.options[item.index]?.id);
|
||||||
|
},
|
||||||
|
formatJsonStr(jsonStr) {
|
||||||
|
return jsonStr ? JSON.parse(jsonStr) : {}
|
||||||
|
},
|
||||||
|
handleSubmitTip(text) {
|
||||||
|
this.showToast(text)
|
||||||
|
},
|
||||||
|
handleSubmit() {
|
||||||
|
if (!this.vote.spec.canAnonymously) {
|
||||||
|
uni.showModal({
|
||||||
|
icon: "none",
|
||||||
|
title: "提示",
|
||||||
|
content: "该投票不支持匿名,请到博主的 网站端 进行投票!",
|
||||||
|
cancelColor: "#666666",
|
||||||
|
cancelText: "关闭",
|
||||||
|
confirmText: "复制地址",
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
this.$utils.copyText(this.$baseApiUrl, "复制成功")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.submitLoading = true
|
||||||
|
uni.showLoading({
|
||||||
|
title: "正在保存..."
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$httpApi.v2
|
||||||
|
.submitVote(this.name, this.submitForm, this.vote.spec.canAnonymously)
|
||||||
|
.then(res => {
|
||||||
|
|
||||||
|
this.showToast("提交成功")
|
||||||
|
voteCacheUtil.set(this.name, {
|
||||||
|
selected: [...this.submitForm.voteData],
|
||||||
|
data: this.vote
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.startPullDownRefresh()
|
||||||
|
this.submitLoading = false
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
this.showToast("提交失败,请重试")
|
||||||
|
this.submitLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSelectSingleOption(option) {
|
||||||
|
if (this.vote.spec._uh_state.state == '未开始') {
|
||||||
|
this.showToast(`投票未开始`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.vote.spec.hasEnded) return
|
||||||
|
if (this.vote.spec.disabled) return
|
||||||
|
this.vote.spec.options.map(item => {
|
||||||
|
if (option.id == item.id) {
|
||||||
|
item.checked = true
|
||||||
|
} else {
|
||||||
|
item.checked = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.submitForm.voteData = this.vote.spec.options.filter(x => x.checked).map(item => item.id)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSelectCheckboxOption(option) {
|
||||||
|
if (this.vote.spec._uh_state.state == '未开始') {
|
||||||
|
this.showToast(`投票未开始`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.vote.spec.hasEnded) return
|
||||||
|
if (this.vote.spec.disabled) return
|
||||||
|
|
||||||
|
const checkedList = this.vote.spec.options.filter(x => x.checked && x.id != option.id)
|
||||||
|
|
||||||
|
if (this.vote.spec.type === 'multiple' && checkedList.length >= this.vote.spec.maxVotes) {
|
||||||
|
this.showToast(`最多选择 ${this.vote.spec.maxVotes} 项`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.vote.spec.options.map(item => {
|
||||||
|
if (option.id == item.id) {
|
||||||
|
item.checked = !item.checked
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.submitForm.voteData = this.vote.spec.options.filter(x => x.checked).map(item => item.id)
|
||||||
|
},
|
||||||
|
showToast(content) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: "none",
|
||||||
|
title: content,
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.app-page {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
padding-bottom: 160rpx;
|
||||||
|
background-color: #fafafd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrap {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
height: 60vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wp-50 {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 24rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #ffff;
|
||||||
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-info-card {
|
||||||
|
.vote-card-body {
|
||||||
|
font-size: 28rpx;
|
||||||
|
gap: 12rpx 0;
|
||||||
|
background: #F3F4F6;
|
||||||
|
color: #3F3F3F;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card-head {
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-satrt;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
.title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card-body {
|
||||||
|
|
||||||
|
.remark {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 12rpx 6rpx;
|
||||||
|
padding-top: 0;
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-card-foot {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 6px;
|
||||||
|
border-top: 2rpx solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-voted-item {
|
||||||
|
min-height: 72rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: rgba(229, 229, 229, 0.75);
|
||||||
|
font-size: 24rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
width: var(--percent);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(208, 208, 208, 1);
|
||||||
|
z-index: 0;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: rgba(3, 169, 244, 0.35);
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background-color: rgba(3, 169, 244, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-voted-item-content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 72rpx;
|
||||||
|
padding: 12rpx 24rpx;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-select-option {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
transition: all 0.1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
::v-deep {
|
||||||
|
.tm-button-label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.single {
|
||||||
|
::v-deep {}
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiple {
|
||||||
|
::v-deep {}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pk {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
::v-deep {
|
||||||
|
|
||||||
|
.pk-container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-item {
|
||||||
|
flex-grow: 1;
|
||||||
|
min-width: 30% !important;
|
||||||
|
max-width: 70% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-left {}
|
||||||
|
|
||||||
|
.radio-right {}
|
||||||
|
|
||||||
|
.option-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-item-left {
|
||||||
|
background: linear-gradient(90deg, #3B82F6, #60A5FA);
|
||||||
|
color: white;
|
||||||
|
clip-path: polygon(0 0, calc(100% - 40rpx) 0, 100% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-item-right {
|
||||||
|
background: linear-gradient(90deg, #F87171, #EF4444);
|
||||||
|
color: white;
|
||||||
|
clip-path: polygon(0 0, 100% 0, 100% 100%, 40rpx 100%);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-foot {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
.tm-groupradio {
|
||||||
|
display: flex;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-checkbox {
|
||||||
|
// width: 100%;
|
||||||
|
max-width: initial !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.flex-start.fulled {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-content {
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
padding: 12rpx 0;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2B2F33;
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-remark {
|
||||||
|
margin-bottom: 36rpx;
|
||||||
|
padding-top: 12rpx;
|
||||||
|
color: #3F3F3F;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
padding-left: 24rpx;
|
||||||
|
// font-weight: bold;
|
||||||
|
font-size: 30rpx;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
width: 8rpx;
|
||||||
|
height: 28rpx;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 6rpx;
|
||||||
|
background: #03A9F4;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title-count {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-submit {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 24rpx 36rpx;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(255, 255, 255, 0.98);
|
||||||
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
|
border-top: 2rpx solid #eee;
|
||||||
|
z-index: 99;
|
||||||
|
|
||||||
|
::v-deep {
|
||||||
|
.tm-button {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-button-btn {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,409 @@
|
|||||||
|
<template>
|
||||||
|
<view class="app-page" :class="[uniHaloPluginPageClass]">
|
||||||
|
<PluginUnavailable v-if="!uniHaloPluginAvailable" :pluginId="uniHaloPluginId"
|
||||||
|
:error-text="uniHaloPluginAvailableError" />
|
||||||
|
<template v-else>
|
||||||
|
<!-- 顶部切换 -->
|
||||||
|
<view class="e-fixed filter-box">
|
||||||
|
<tm-search v-model="queryParams.keyword" :round="24" :shadow="0" color="light-blue"
|
||||||
|
insert-color="light-blue" :clear="true" @input="fnOnSearch" @confirm="fnOnSearch"></tm-search>
|
||||||
|
<tm-dropDownMenu :shadow="0" color="light-blue" active-color="light-blue"
|
||||||
|
:default-selected="filterOption.selected" :list="filterOption.list"
|
||||||
|
@confirm="fnOnFilterConfirm"></tm-dropDownMenu>
|
||||||
|
</view>
|
||||||
|
<!-- 占位区域 -->
|
||||||
|
<view style="width: 100vw;height: 180rpx;"></view>
|
||||||
|
<!-- 加载区域 -->
|
||||||
|
<view v-if="loading == 'loading'" class="loading-wrap pa-24">
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
<tm-skeleton model="listAvatr"></tm-skeleton>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="loading == 'error'" class="content-empty flex flex-center">
|
||||||
|
<tm-empty icon="icon-wind-cry" label="加载异常"></tm-empty>
|
||||||
|
</view>
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<view v-else class="content">
|
||||||
|
<view v-if="dataList.length == 0" class="content-empty flex flex-center">
|
||||||
|
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无数据"></tm-empty>
|
||||||
|
</view>
|
||||||
|
<block v-else>
|
||||||
|
<tm-translate v-for="(item, index) in dataList" :key="item.metadata.name" animation-name="fadeUp"
|
||||||
|
:wait="calcAniWait(index)">
|
||||||
|
<VoteCard :vote="item" :index="index" @on-click="fnToDetail"></VoteCard>
|
||||||
|
</tm-translate>
|
||||||
|
<view class="load-text">{{ loadMoreText }}</view>
|
||||||
|
<tm-flotbutton @click="fnToTopPage" size="m" color="light-blue"
|
||||||
|
icon="icon-angle-up"></tm-flotbutton>
|
||||||
|
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
|
||||||
|
import tmSearch from '@/tm-vuetify/components/tm-search/tm-search.vue';
|
||||||
|
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||||
|
import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
|
||||||
|
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
|
||||||
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
|
import tmDropDownMenu from '@/tm-vuetify/components/tm-dropDownMenu/tm-dropDownMenu.vue';
|
||||||
|
|
||||||
|
import VoteCard from '@/components/vote-card/vote-card.vue'
|
||||||
|
|
||||||
|
import {
|
||||||
|
VOTE_TYPES,
|
||||||
|
calcVoteState,
|
||||||
|
voteCacheUtil
|
||||||
|
} from '@/utils/vote.js'
|
||||||
|
import pluginAvailableMixin from "@/common/mixins/pluginAvailable.js"
|
||||||
|
import PluginUnavailable from '@/components/plugin-unavailable/plugin-unavailable.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
options: {
|
||||||
|
styleIsolation: 'shared'
|
||||||
|
},
|
||||||
|
mixins: [pluginAvailableMixin],
|
||||||
|
components: {
|
||||||
|
tmSkeleton,
|
||||||
|
tmSearch,
|
||||||
|
tmTranslate,
|
||||||
|
tmTabs,
|
||||||
|
tmFlotbutton,
|
||||||
|
tmEmpty,
|
||||||
|
tmTags,
|
||||||
|
tmDropDownMenu,
|
||||||
|
VoteCard,
|
||||||
|
PluginUnavailable
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: 'loading',
|
||||||
|
hasNext: false,
|
||||||
|
isLoadMore: false,
|
||||||
|
loadMoreText: '加载中...',
|
||||||
|
filterOption: {
|
||||||
|
selected: [],
|
||||||
|
list: [{
|
||||||
|
title: '类型',
|
||||||
|
children: [{
|
||||||
|
title: "",
|
||||||
|
model: "list",
|
||||||
|
name: "type",
|
||||||
|
children: [{
|
||||||
|
title: "全部",
|
||||||
|
id: undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "单选",
|
||||||
|
id: 'single'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "多选",
|
||||||
|
id: 'multiple'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "双选PK",
|
||||||
|
id: 'pk'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
title: '状态',
|
||||||
|
children: [{
|
||||||
|
title: "",
|
||||||
|
model: "list",
|
||||||
|
name: "hasEnded",
|
||||||
|
children: [{
|
||||||
|
title: "全部",
|
||||||
|
id: undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "进行中",
|
||||||
|
id: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "已结束",
|
||||||
|
id: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
title: '排序',
|
||||||
|
children: [{
|
||||||
|
title: "",
|
||||||
|
model: "list",
|
||||||
|
name: "sort",
|
||||||
|
children: [{
|
||||||
|
title: "默认排序",
|
||||||
|
id: undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "较近创建",
|
||||||
|
id: 'metadata.creationTimestamp,desc'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "较早创建",
|
||||||
|
id: 'metadata.creationTimestamp,asc'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
title: '是否已投',
|
||||||
|
children: [{
|
||||||
|
title: "",
|
||||||
|
model: "list",
|
||||||
|
name: "isVoted",
|
||||||
|
children: [{
|
||||||
|
title: "全部",
|
||||||
|
id: undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "未投票",
|
||||||
|
id: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "已投票",
|
||||||
|
id: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
filterIsVoted: undefined,
|
||||||
|
queryParams: {
|
||||||
|
keyword: "",
|
||||||
|
page: 1,
|
||||||
|
size: 10,
|
||||||
|
sort: undefined,
|
||||||
|
type: undefined,
|
||||||
|
hasEnded: undefined
|
||||||
|
},
|
||||||
|
dataList: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
haloConfigs() {
|
||||||
|
return this.$tm.vx.getters().getConfigs;
|
||||||
|
},
|
||||||
|
calcAuditModeEnabled() {
|
||||||
|
return this.haloConfigs.auditConfig.auditModeEnabled
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async onLoad() {
|
||||||
|
this.fnSetPageTitle('投票列表');
|
||||||
|
// 检查插件
|
||||||
|
this.setPluginId(this.NeedPluginIds.PluginVote)
|
||||||
|
this.setPluginError("阿偶,检测到当前插件没有安装或者启用,无法使用投票功能哦,请联系管理员")
|
||||||
|
if (!await this.checkPluginAvailable()) return
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
onPullDownRefresh() {
|
||||||
|
if (!this.uniHaloPluginAvailable) {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.fnResetSetAniWaitIndex();
|
||||||
|
this.isLoadMore = false;
|
||||||
|
this.queryParams.page = 0;
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
onReachBottom(e) {
|
||||||
|
if (!this.uniHaloPluginAvailable) return;
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '没有更多数据了'
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.hasNext) {
|
||||||
|
this.queryParams.page += 1;
|
||||||
|
this.isLoadMore = true;
|
||||||
|
this.fnGetData();
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '没有更多数据了'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnOnSearch() {
|
||||||
|
this.fnResetSetAniWaitIndex();
|
||||||
|
this.fnToTopPage();
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
fnGetData() {
|
||||||
|
if (this.calcAuditModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uni.showLoading({
|
||||||
|
mask: true,
|
||||||
|
title: '加载中...'
|
||||||
|
});
|
||||||
|
// 设置状态为加载中
|
||||||
|
if (!this.isLoadMore) {
|
||||||
|
this.loading = 'loading';
|
||||||
|
}
|
||||||
|
this.loadMoreText = '加载中...';
|
||||||
|
this.$httpApi.v2
|
||||||
|
.getVoteList(this.queryParams)
|
||||||
|
.then(res => {
|
||||||
|
this.loading = 'success';
|
||||||
|
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
|
||||||
|
this.hasNext = res.hasNext;
|
||||||
|
|
||||||
|
const tempItems = res.items.map(item => {
|
||||||
|
item.spec.disabled = true
|
||||||
|
item.spec.isVoted = this.fnCalcIsVoted(item.metadata.name)
|
||||||
|
item.spec._uh_state = calcVoteState(item)
|
||||||
|
item.spec._uh_type = VOTE_TYPES[item.spec.type]
|
||||||
|
item.spec.options.map((option, index) => {
|
||||||
|
|
||||||
|
option.checked = this.fnCalcIsChecked(item.metadata.name, option)
|
||||||
|
option.value = option.id
|
||||||
|
option.label = option.title
|
||||||
|
|
||||||
|
// 计算当前的选择占比
|
||||||
|
if (item.spec.type === 'single') {
|
||||||
|
option.percent = this.fnCalcPercent(option, item.stats);
|
||||||
|
} else if (item.spec.type === 'multiple') {
|
||||||
|
option.percent = this.fnCalcPercent(option, item.stats);
|
||||||
|
} else if (item.spec.type === 'pk') {
|
||||||
|
option.percent = this.fnCalcPercent(option, item.stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
return option
|
||||||
|
})
|
||||||
|
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.isLoadMore) {
|
||||||
|
this.dataList = this.dataList.concat(tempItems);
|
||||||
|
} else {
|
||||||
|
this.dataList = tempItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataList = this.dataList.sort((a, b) => {
|
||||||
|
return a.spec.isVoted - b.spec.isVoted
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.filterIsVoted != undefined) {
|
||||||
|
this.dataList = this.dataList.filter(x => x.spec.isVoted == this.filterIsVoted)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
this.loading = 'error';
|
||||||
|
this.loadMoreText = '加载失败,请下拉刷新!';
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
fnCalcPercent(voteOption, stats) {
|
||||||
|
if (!stats?.voteDataList) return 0;
|
||||||
|
const option = stats.voteDataList.find(x => x.id == voteOption.id)
|
||||||
|
if (!option) return 0;
|
||||||
|
const percent = (option.voteCount / stats.voteCount) * 100
|
||||||
|
return Math.round(percent)
|
||||||
|
},
|
||||||
|
|
||||||
|
fnCalcIsVoted(name) {
|
||||||
|
return voteCacheUtil.has(name)
|
||||||
|
},
|
||||||
|
fnCalcIsChecked(name, option) {
|
||||||
|
const data = voteCacheUtil.get(name)
|
||||||
|
if (!data) return false;
|
||||||
|
const checked = data.selected.includes(option.id)
|
||||||
|
return checked
|
||||||
|
},
|
||||||
|
|
||||||
|
//跳转详情
|
||||||
|
fnToDetail(item) {
|
||||||
|
if (this.calcAuditModeEnabled) return;
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pagesA/vote-detail/vote-detail?name=' + item.metadata.name,
|
||||||
|
animationType: 'slide-in-right'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fnOnFilterConfirm(e) {
|
||||||
|
// 类型
|
||||||
|
const type = e.find(x => x.name == 'type')
|
||||||
|
if (type.children.length == 0) {
|
||||||
|
this.queryParams.type = undefined
|
||||||
|
} else {
|
||||||
|
this.queryParams.type = type.children[0]?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态
|
||||||
|
const hasEnded = e.find(x => x.name == 'hasEnded')
|
||||||
|
if (hasEnded.children.length == 0) {
|
||||||
|
this.queryParams.hasEnded = undefined
|
||||||
|
} else {
|
||||||
|
this.queryParams.hasEnded = hasEnded.children[0]?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
const sort = e.find(x => x.name == 'sort')
|
||||||
|
if (sort.children.length == 0) {
|
||||||
|
this.queryParams.sort = undefined
|
||||||
|
} else {
|
||||||
|
this.queryParams.sort = sort.children[0]?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否已经投
|
||||||
|
const isVoted = e.find(x => x.name == 'isVoted')
|
||||||
|
if (isVoted.children.length == 0) {
|
||||||
|
this.filterIsVoted = undefined
|
||||||
|
} else {
|
||||||
|
this.filterIsVoted = isVoted.children[0]?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
this.queryParams.page = 0;
|
||||||
|
this.isLoadMore = false;
|
||||||
|
this.fnResetSetAniWaitIndex();
|
||||||
|
this.fnToTopPage();
|
||||||
|
this.fnGetData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.app-page {
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-bottom: 24rpx;
|
||||||
|
background-color: #fafafd;
|
||||||
|
|
||||||
|
&.is-balck {
|
||||||
|
background-color: #212121;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-box {
|
||||||
|
box-shadow: 0rpx 0rpx 12rpx rgba(0, 0, 0, 0.035);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-empty {
|
||||||
|
height: 60vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,10 +13,13 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
onLoad(e) {
|
onLoad(e) {
|
||||||
const {title, url} = JSON.parse(e.query.data);
|
const {title, url} = JSON.parse(e.data);
|
||||||
this.webUrl = url;
|
this.webUrl = decodeURIComponent(url);
|
||||||
|
console.log("webUrl" + this.webUrl)
|
||||||
|
if (title) {
|
||||||
this.fnSetPageTitle(title);
|
this.fnSetPageTitle(title);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
// router.js
|
|
||||||
import {
|
|
||||||
RouterMount,
|
|
||||||
createRouter
|
|
||||||
} from 'uni-simple-router';
|
|
||||||
|
|
||||||
const router = createRouter({
|
|
||||||
platform: process.env.VUE_APP_PLATFORM,
|
|
||||||
routes: [...ROUTES]
|
|
||||||
});
|
|
||||||
|
|
||||||
//全局路由前置守卫
|
|
||||||
router.beforeEach((to, from, next) => {
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
// 全局路由后置守卫
|
|
||||||
router.afterEach((to, from) => {
|
|
||||||
console.log('跳转结束')
|
|
||||||
})
|
|
||||||
|
|
||||||
export {
|
|
||||||
router,
|
|
||||||
RouterMount
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
+42
-3
@@ -5,33 +5,49 @@
|
|||||||
* 时间:2024年06月22日 12:00:44
|
* 时间:2024年06月22日 12:00:44
|
||||||
* 版本:v0.1.0
|
* 版本:v0.1.0
|
||||||
*/
|
*/
|
||||||
import {DefaultAppConfigs, getAppConfigs, setAppConfigs} from '@/config/index.js'
|
import {DefaultAppConfigs, getAppConfigs, getAppMockJson, setAppConfigs, setAppMockJson} from '@/config/index.js'
|
||||||
import v2Config from '@/api/v2/all.config.js'
|
import v2Config from '@/api/v2/all.config.js'
|
||||||
|
import utils from '@/utils/index.js'
|
||||||
|
import {setTokens} from "@/utils/token";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {
|
state: {
|
||||||
configs: getAppConfigs(),
|
configs: getAppConfigs(),
|
||||||
|
mockJson: getAppMockJson(),
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getConfigs(state) {
|
getConfigs(state) {
|
||||||
if (state.configs) return state.configs;
|
if (state.configs) return state.configs;
|
||||||
return getAppConfigs()
|
return getAppConfigs()
|
||||||
},
|
},
|
||||||
|
getMockJson(state) {
|
||||||
|
if (state.mockJson) return state.mockJson;
|
||||||
|
return getAppMockJson()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setConfigs(state, data) {
|
setConfigs(state, data) {
|
||||||
state.configs = data;
|
state.configs = data;
|
||||||
setAppConfigs(data)
|
setAppConfigs(data)
|
||||||
},
|
},
|
||||||
|
setMockJson(state, data) {
|
||||||
|
setAppMockJson(data)
|
||||||
|
state.mockJson = data;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
fetchConfigs({commit, dispatch}) {
|
fetchConfigs({state, commit, dispatch}) {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const res = await v2Config.getAppConfigs()
|
const res = await v2Config.getAppConfigs()
|
||||||
if (res) {
|
if (res) {
|
||||||
commit('setConfigs', res)
|
commit('setConfigs', utils.deepMerge(DefaultAppConfigs, res))
|
||||||
|
|
||||||
|
// 存储token
|
||||||
|
if (res?.basicConfig?.tokenConfig) {
|
||||||
|
setTokens(res.basicConfig.tokenConfig)
|
||||||
|
}
|
||||||
resolve(res)
|
resolve(res)
|
||||||
} else {
|
} else {
|
||||||
dispatch("setDefaultAppSettings");
|
dispatch("setDefaultAppSettings");
|
||||||
@@ -47,5 +63,28 @@ export default {
|
|||||||
setDefaultAppSettings({commit}) {
|
setDefaultAppSettings({commit}) {
|
||||||
commit('setConfigs', JSON.parse(JSON.stringify(DefaultAppConfigs)))
|
commit('setConfigs', JSON.parse(JSON.stringify(DefaultAppConfigs)))
|
||||||
},
|
},
|
||||||
|
// 请求模拟数据
|
||||||
|
fetchMockJson({state, commit, dispatch}) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const mockJsonUrl = utils.checkUrl(state.configs.basicConfig.auditModeMockUrl)
|
||||||
|
uni.request({
|
||||||
|
url: mockJsonUrl,
|
||||||
|
method: "GET",
|
||||||
|
success: (res) => {
|
||||||
|
commit('setMockJson', res.data)
|
||||||
|
resolve({
|
||||||
|
ok: true,
|
||||||
|
data: res.data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
resolve({
|
||||||
|
ok: false,
|
||||||
|
data: err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<view @click="onclick" class=" tm-checkbox " :class="[dense?'':'pa-20',inline?'d-inline-block':'']">
|
<view @click="onclick" class=" tm-checkbox " :class="[dense?'':'pa-20',inline?'d-inline-block':'']">
|
||||||
<view class=" flex-start">
|
<view class=" flex-start">
|
||||||
|
|
||||||
<slot name="default" :checkData="{label:label,checked:changValue}" :on="onclick">
|
<slot name="default" :checkData="{label:label,checked:changValue,extendData}" :on="onclick">
|
||||||
<view :style="{width: sizes.wk,height: sizes.wk}" class="tm-checkbox-boey relative d-inline-block"
|
<view :style="{width: sizes.wk,height: sizes.wk}" class="tm-checkbox-boey relative d-inline-block"
|
||||||
:class="[black?'bk':'','flex-shrink mr-10',
|
:class="[black?'bk':'','flex-shrink mr-10',
|
||||||
changValue?'ani':'',
|
changValue?'ani':'',
|
||||||
@@ -51,6 +51,10 @@
|
|||||||
*/
|
*/
|
||||||
import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
|
import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
|
||||||
export default {
|
export default {
|
||||||
|
options: {
|
||||||
|
virtualHost: true,
|
||||||
|
styleIsolation: 'shared'
|
||||||
|
},
|
||||||
components:{tmIcons},
|
components:{tmIcons},
|
||||||
name: 'tm-checkbox',
|
name: 'tm-checkbox',
|
||||||
model: {
|
model: {
|
||||||
@@ -117,6 +121,10 @@
|
|||||||
fllowTheme:{
|
fllowTheme:{
|
||||||
type:Boolean|String,
|
type:Boolean|String,
|
||||||
default:true
|
default:true
|
||||||
|
},
|
||||||
|
extendData:{
|
||||||
|
type:Object,
|
||||||
|
default:()=>({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
<template>
|
||||||
|
<view class="tm-flowLayout relative">
|
||||||
|
<view class="tm-flowLayout-hidden absolute fulled">
|
||||||
|
<view v-for="(item, index) in list2" :key="index" style="width: 350rpx;" :class="'tm-flowLayout-hidden-'+index">
|
||||||
|
<tm-images @error="loadimg($event,false,index)" @load="loadimg($event,true,index)" :src="item.spec.url"></tm-images>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="tm-flowLayout-cm flex-between">
|
||||||
|
<view class="tm-flowLayout-left" style="width: 48.6%;">
|
||||||
|
<block v-for="(item, index) in dataList[0]" :key="index">
|
||||||
|
<view @click.stop="onclick(index,0)" class="tm-flowLayout-item mb-20 fulled" :class="['tm-flowLayout-item_'+index]">
|
||||||
|
<tm-translate animation-name="fadeUp">
|
||||||
|
<view class="card round-3 overflow white">
|
||||||
|
<tm-images :previmage="false" :src="item.spec.url"></tm-images>
|
||||||
|
</view>
|
||||||
|
</tm-translate>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
<view class="tm-flowLayout-right" style="width: 48.6%;">
|
||||||
|
<block v-for="(item, index) in dataList[1]" :key="index">
|
||||||
|
<view @click.stop="onclick(index,1)" class="tm-flowLayout-item mb-20 fulled" :class="['tm-flowLayout-item_'+index]">
|
||||||
|
<tm-translate animation-name="fadeUp">
|
||||||
|
<view class="card round-3 overflow white">
|
||||||
|
<tm-images :previmage="false" :src="item.spec.url"></tm-images>
|
||||||
|
</view>
|
||||||
|
</tm-translate>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
<view v-if="isLoading" class="flex-shrink fulled">
|
||||||
|
<tm-loadding></tm-loadding>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 瀑布流组件
|
||||||
|
* @property {Function} click 点击项目时触发
|
||||||
|
* @property {Function} load 当前列表加载完成发出的事件。
|
||||||
|
* @property {String} model = [rank|desc] desc下正时排序,所有加载完成再进行显示,rank随机,哪个先加载哪个先排前面。
|
||||||
|
*/
|
||||||
|
import tmImages from "@/tm-vuetify/components/tm-images/tm-images.vue"
|
||||||
|
import tmLoadding from "@/tm-vuetify/components/tm-loadding/tm-loadding.vue"
|
||||||
|
import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
|
||||||
|
import tmTranslate from "@/tm-vuetify/components/tm-translate/tm-translate.vue"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {tmImages, tmLoadding, tmIcons, tmTranslate},
|
||||||
|
name: 'tm-flowLayout',
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: String,
|
||||||
|
default: 'rank' //desc下正时排序,所有加载完成再进行显示,rank随机,哪个先加载哪个先排前面。
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
list2: [],
|
||||||
|
dataList: [
|
||||||
|
[], []
|
||||||
|
],
|
||||||
|
colHeight: [0, 0],
|
||||||
|
minWidth: 0,
|
||||||
|
isLoading: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
let p = await this.$Querey('.tm-flowLayout-left').catch(e => {
|
||||||
|
});
|
||||||
|
this.minWidth = p[0].width;
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onclick(index, dirIndex) {
|
||||||
|
this.$emit('click',
|
||||||
|
{
|
||||||
|
childrenIndex: index,
|
||||||
|
dirIndex: dirIndex,
|
||||||
|
item: this.dataList[dirIndex][index]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//修改或者替换列表数据
|
||||||
|
changeItemData(dirIndex, childrenIndex, item) {
|
||||||
|
this.dataList[dirIndex].splice(childrenIndex, 1, item);
|
||||||
|
},
|
||||||
|
//删除一项列表数据
|
||||||
|
delItemData(dirIndex, childrenIndex) {
|
||||||
|
this.colHeight.splice(dirIndex, 1, this.colHeight[dirIndex] - this.dataList[dirIndex][childrenIndex].height)
|
||||||
|
this.dataList[dirIndex].splice(childrenIndex, 1);
|
||||||
|
this.list2.splice(this.dataList[dirIndex][childrenIndex].index, 1);
|
||||||
|
|
||||||
|
this.$nextTick(function () {
|
||||||
|
this.sucessRank();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
//向列表添加数据
|
||||||
|
pushData(list) {
|
||||||
|
let prIdx_i = this.list2.length;
|
||||||
|
if (!Array.isArray(list) || typeof list == 'undefined') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
this.list2.push({
|
||||||
|
index: i + prIdx_i,
|
||||||
|
isLoad: false,
|
||||||
|
image: "",
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
...list[i]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//获取内部列表数据
|
||||||
|
getDataList() {
|
||||||
|
return this.dataList;
|
||||||
|
},
|
||||||
|
async loadimg(event, isLoad, index) {
|
||||||
|
this.isLoading = true;
|
||||||
|
let ps = this.list2[index];
|
||||||
|
ps.isLoad = true;
|
||||||
|
if (isLoad == false) {
|
||||||
|
ps.width = this.minWidth;
|
||||||
|
ps.height = this.minWidth;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ps.width = this.minWidth;
|
||||||
|
ps.height = ps.height + event.height;
|
||||||
|
}
|
||||||
|
if (this.list2.length == 0 && this.dataList[0].length == 0 && this.dataList[1].length == 0) {
|
||||||
|
this.isLoading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.list2.splice(index, 1, ps);
|
||||||
|
if (this.model == 'desc') {
|
||||||
|
this.sucessRank();
|
||||||
|
} else if (this.model == 'rank') {
|
||||||
|
let indexCol = this.colHeight[0] <= this.colHeight[1] ? 0 : 1;
|
||||||
|
this.dataList[indexCol].push(this.list2[index]);
|
||||||
|
this.colHeight.splice(indexCol, 1, this.colHeight[indexCol] + this.list2[index].height);
|
||||||
|
if (this.isAllLoading() === false) return;
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isAllLoading() {
|
||||||
|
let isAllload = true;
|
||||||
|
for (let i = 0; i < this.list2.length; i++) {
|
||||||
|
if (this.list2[i].isLoad === false) {
|
||||||
|
isAllload = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isAllload === true) {
|
||||||
|
// 当前列表加载完成发出的事件。
|
||||||
|
this.$emit('load', this.dataList)
|
||||||
|
}
|
||||||
|
return isAllload;
|
||||||
|
},
|
||||||
|
sucessRank() {
|
||||||
|
if (this.isAllLoading() === false) return;
|
||||||
|
this.isLoading = false;
|
||||||
|
for (let i = 0; i < this.list2.length; i++) {
|
||||||
|
// let p = await this.getLeftRightHeight();
|
||||||
|
let index = this.colHeight[0] <= this.colHeight[1] ? 0 : 1;
|
||||||
|
this.dataList[index].push(this.list2[i]);
|
||||||
|
this.colHeight.splice(index, 1, this.colHeight[index] + this.list2[i].height)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
//清空瀑布流数据 。
|
||||||
|
clear() {
|
||||||
|
this.list2 = [];
|
||||||
|
this.dataList = [[], []];
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
getLeftRightHeight(index) {
|
||||||
|
let t = this;
|
||||||
|
return new Promise((resolve, rejecvt) => {
|
||||||
|
let q = uni.createSelectorQuery().in(t);
|
||||||
|
q.select(".tm-flowLayout-left").boundingClientRect();
|
||||||
|
q.select(".tm-flowLayout-right").boundingClientRect();
|
||||||
|
q.exec((res) => {
|
||||||
|
resolve([res[0].height, res[1].height])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tm-flowLayout {
|
||||||
|
.tm-flowLayout-hidden {
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
overflow-y: auto;
|
||||||
|
left: -300%;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-flowLayout-cm {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -17,6 +17,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
|
options: {
|
||||||
|
virtualHost: true,
|
||||||
|
styleIsolation: 'shared'
|
||||||
|
},
|
||||||
name:'tm-groupcheckbox',
|
name:'tm-groupcheckbox',
|
||||||
props:{
|
props:{
|
||||||
// 最大选择数量
|
// 最大选择数量
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<view @click="onclick" class=" tm-checkbox " :class="[dense?'':'pa-20',inline?'d-inline-block ':'fulled']">
|
<view @click="onclick" class=" tm-checkbox " :class="[dense?'':'pa-20',inline?'d-inline-block ':'fulled']">
|
||||||
<view class="flex-start fulled">
|
<view class="flex-start fulled">
|
||||||
|
|
||||||
<slot name="default" :checkData="{label:label,checked:changValue}" :on="onclick">
|
<slot name="default" :checkData="{label:label,checked:changValue,extendData}" :on="onclick">
|
||||||
<view :style="{width: sizes.wk,height: sizes.wk}" class="tm-checkbox-boey relative d-inline-block"
|
<view :style="{width: sizes.wk,height: sizes.wk}" class="tm-checkbox-boey relative d-inline-block"
|
||||||
:class="[black?'bk':'','flex-shrink mr-10 ',
|
:class="[black?'bk':'','flex-shrink mr-10 ',
|
||||||
changValue?'ani':'',
|
changValue?'ani':'',
|
||||||
@@ -119,8 +119,11 @@
|
|||||||
fllowTheme:{
|
fllowTheme:{
|
||||||
type:Boolean|String,
|
type:Boolean|String,
|
||||||
default:true
|
default:true
|
||||||
}
|
},
|
||||||
|
extendData:{
|
||||||
|
type:Object,
|
||||||
|
default:()=>({})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ class vuex {
|
|||||||
let t = this;
|
let t = this;
|
||||||
const g = this.store.getters
|
const g = this.store.getters
|
||||||
let keys = Object.keys(g);
|
let keys = Object.keys(g);
|
||||||
console.log(keys)
|
|
||||||
let k = keys.map((el,index)=>{
|
let k = keys.map((el,index)=>{
|
||||||
let f = el.split('/');
|
let f = el.split('/');
|
||||||
let tst = {}
|
let tst = {}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
## 1.0.2(2023-09-14)
|
||||||
|
更新使用文档:
|
||||||
|
引入本插件 然后使用,手动滑稽,教程简单吗?
|
||||||
|
<k-touch-listen @touchUp="touchUp" @touchDown="touchDown" @touchLeft="touchLeft" @touchRight="touchRight">
|
||||||
|
<view>
|
||||||
|
//您需要监听的区域的代码
|
||||||
|
</view>
|
||||||
|
</k-touch-listen>
|
||||||
|
## 1.0.1(2023-09-14)
|
||||||
|
更新使用文档:
|
||||||
|
引入本插件 然后使用,手动滑稽,教程简单吗?
|
||||||
|
<k-touch-listen @touchUp="touchUp" @touchDown="touchDown" @touchLeft="touchLeft" @touchRight="touchRight">
|
||||||
|
<view>
|
||||||
|
//您需要监听的区域的代码
|
||||||
|
</view>
|
||||||
|
</k-touch-listen>
|
||||||
|
## 1.0.0(2023-09-14)
|
||||||
|
首次提交
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
<template>
|
||||||
|
<view class="touch-box" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd">
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'k-touch-listen',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
touchStartX: 0, // 触摸时的原点
|
||||||
|
touchStartY: 0, // 触摸时的原点
|
||||||
|
time: 0, // 时间记录,用于滑动时且时间小于1s则执行左右滑动
|
||||||
|
interval: '', // 记录/清理时间记录
|
||||||
|
touchMoveX: 0, // x轴方向移动的距离
|
||||||
|
touchMoveY: 0, // y轴方向移动的距离
|
||||||
|
touchMoveTag: false // 定义滑动状态
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
direction: {
|
||||||
|
type: String,
|
||||||
|
default: 'all'
|
||||||
|
},
|
||||||
|
distance: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 30
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['touchUp', 'touchDown', 'touchLeft', 'touchRight'],
|
||||||
|
methods: {
|
||||||
|
// 触摸开始事件
|
||||||
|
touchStart(e) {
|
||||||
|
this.touchStartX = e.touches[0].pageX // 获取触摸时的原点
|
||||||
|
this.touchStartY = e.touches[0].pageY // 获取触摸时的原点
|
||||||
|
// 使用js计时器记录时间
|
||||||
|
this.interval = setInterval(() => {
|
||||||
|
this.time++
|
||||||
|
}, 100)
|
||||||
|
},
|
||||||
|
// 触摸移动事件
|
||||||
|
touchMove(e) {
|
||||||
|
this.touchMoveTag = true
|
||||||
|
this.touchMoveX = e.touches[0].pageX
|
||||||
|
this.touchMoveY = e.touches[0].pageY
|
||||||
|
},
|
||||||
|
ressetData() {
|
||||||
|
this.touchStartX = 0 // 触摸时的原点
|
||||||
|
this.touchStartY = 0 // 触摸时的原点
|
||||||
|
this.time = 0 // 时间记录,用于滑动时且时间小于1s则执行左右滑动
|
||||||
|
this.interval = '' // 记录/清理时间记录
|
||||||
|
this.touchMoveX = 0 // x轴方向移动的距离
|
||||||
|
this.touchMoveY = 0 // y轴方向移动的距离
|
||||||
|
this.touchMoveTag = false // 定义滑动状态
|
||||||
|
},
|
||||||
|
// 触摸结束事件
|
||||||
|
touchEnd(e) {
|
||||||
|
let moveX = this.touchMoveX - this.touchStartX
|
||||||
|
let moveY = this.touchMoveY - this.touchStartY
|
||||||
|
if (Math.sign(moveX) == -1) {
|
||||||
|
moveX *= -1
|
||||||
|
}
|
||||||
|
if (Math.sign(moveY) == -1) {
|
||||||
|
moveY *= -1
|
||||||
|
}
|
||||||
|
if (2 * moveX <= moveY && this.touchMoveTag) {
|
||||||
|
// 上下
|
||||||
|
if (this.direction != 'all' && this.direction != 'vertical') return
|
||||||
|
// 向上滑动
|
||||||
|
if (
|
||||||
|
this.touchMoveY - this.touchStartY <= -this.distance &&
|
||||||
|
this.time < 10
|
||||||
|
) {
|
||||||
|
this.$emit('touchUp')
|
||||||
|
}
|
||||||
|
// 向下滑动
|
||||||
|
if (
|
||||||
|
this.touchMoveY - this.touchStartY >= this.distance &&
|
||||||
|
this.time < 10
|
||||||
|
) {
|
||||||
|
this.$emit('touchDown')
|
||||||
|
}
|
||||||
|
} else if (moveX > 2 * moveY && this.touchMoveTag) {
|
||||||
|
// 左右
|
||||||
|
if (this.direction != 'all' && this.direction != 'horizontal') return
|
||||||
|
// 向左滑动
|
||||||
|
if (
|
||||||
|
this.touchMoveX - this.touchStartX <= -this.distance &&
|
||||||
|
this.time < 10
|
||||||
|
) {
|
||||||
|
this.$emit('touchLeft')
|
||||||
|
}
|
||||||
|
// 向右滑动
|
||||||
|
if (
|
||||||
|
this.touchMoveX - this.touchStartX >= this.distance &&
|
||||||
|
this.time < 10
|
||||||
|
) {
|
||||||
|
this.$emit('touchRight')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearInterval(this.interval) // 清除setInterval
|
||||||
|
this.ressetData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.touch-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"id": "k-touch-listen",
|
||||||
|
"displayName": "k-touch-listen 手势监听 全面兼容小程序、vue2、vue3等多端",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"description": "用于监听用户滑动手势,左滑右滑,上滑下滑等一系列手势监听",
|
||||||
|
"keywords": [
|
||||||
|
"touch",
|
||||||
|
"手势",
|
||||||
|
"手势监听",
|
||||||
|
"手势监听",
|
||||||
|
"vue3"
|
||||||
|
],
|
||||||
|
"repository": "",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": "^3.7.6"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "component-vue",
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "插件不采集任何数据",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": ""
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "y"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y",
|
||||||
|
"钉钉": "u",
|
||||||
|
"快手": "u",
|
||||||
|
"飞书": "u",
|
||||||
|
"京东": "u"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
## 1.1.0(2023-07-05)
|
||||||
|
优化APP端生成逻辑
|
||||||
|
## 1.0.9(2023-07-04)
|
||||||
|
优化
|
||||||
|
## 1.0.8(2023-07-04)
|
||||||
|
增加注意事项
|
||||||
|
## 1.0.7(2023-07-04)
|
||||||
|
修改本地图片不显示问题
|
||||||
|
## 1.0.6(2023-06-26)
|
||||||
|
优化
|
||||||
|
## 1.0.5(2023-06-09)
|
||||||
|
增加子集绘制
|
||||||
|
## 1.0.4(2023-06-09)
|
||||||
|
增加子集绘制
|
||||||
|
## 1.0.3(2023-06-08)
|
||||||
|
增加预览二维码
|
||||||
|
## 1.0.2(2023-05-31)
|
||||||
|
增加license
|
||||||
|
## 1.0.1(2023-05-30)
|
||||||
|
增加示例
|
||||||
|
## 1.0.0(2023-05-30)
|
||||||
|
初始化发布
|
||||||
@@ -0,0 +1,377 @@
|
|||||||
|
<template>
|
||||||
|
<view class="canvas-main">
|
||||||
|
<canvas :style="'width:'+width+'rpx;height:'+height+'rpx;'" class="canvas-item" disable-scroll="true"
|
||||||
|
canvas-id="canvasId" @error="error"></canvas>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
//画布宽度(rpx)
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 750
|
||||||
|
},
|
||||||
|
//画布高度(rpx)
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 750
|
||||||
|
},
|
||||||
|
//生成的图片格式(jpg或png)
|
||||||
|
fileType: {
|
||||||
|
type: String,
|
||||||
|
default: 'png'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pixelRatio: 0,
|
||||||
|
context: null,
|
||||||
|
canvasList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async init(list) {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在绘制...'
|
||||||
|
})
|
||||||
|
if (this.context) {
|
||||||
|
await this.clear()
|
||||||
|
this.canvasList = []
|
||||||
|
this.context = null
|
||||||
|
}
|
||||||
|
this.canvasList = JSON.parse(JSON.stringify(list))
|
||||||
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
|
this.pixelRatio = systemInfo.pixelRatio
|
||||||
|
this.context = uni.createCanvasContext('canvasId', this)
|
||||||
|
this.start()
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
await this.context.clearRect(0, 0, this.width, this.height)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async start() {
|
||||||
|
await Promise.all(this.canvasList.map(async res => {
|
||||||
|
if (res.type == 'color') {
|
||||||
|
await this.drawBg(res)
|
||||||
|
} else if (res.type == 'image') {
|
||||||
|
await this.drawImage(res)
|
||||||
|
} else if (res.type == 'text') {
|
||||||
|
await this.drawText(res)
|
||||||
|
} else if (res.type == 'line') {
|
||||||
|
await this.drawLine(res)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
this.save()
|
||||||
|
},
|
||||||
|
drawBg(item) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
item.width = uni.upx2px(item.width)
|
||||||
|
item.height = uni.upx2px(item.height)
|
||||||
|
item.x = uni.upx2px(item.x)
|
||||||
|
item.y = uni.upx2px(item.y)
|
||||||
|
item.radius = uni.upx2px(item.radius)
|
||||||
|
item.lineWidth = uni.upx2px(item.lineWidth)
|
||||||
|
let gradient = ''
|
||||||
|
if (item.colorObj && item.colorObj.colorList) {
|
||||||
|
if (item.colorObj.colorList.length == 1) {
|
||||||
|
this.context.fillStyle = item.colorObj.colorList[0]
|
||||||
|
} else {
|
||||||
|
if (item.colorObj.direction == 1) {
|
||||||
|
gradient = this.context.createLinearGradient(0, 0, item.height, 0)
|
||||||
|
} else if (item.colorObj.direction == 2) {
|
||||||
|
gradient = this.context.createLinearGradient(0, 0, 0, item.height)
|
||||||
|
} else if (item.colorObj.direction == 3) {
|
||||||
|
gradient = this.context.createLinearGradient(0, 0, item.width, item.height)
|
||||||
|
} else if (item.colorObj.direction == 4) {
|
||||||
|
gradient = this.context.createLinearGradient(item.width, 0, 0, item.height)
|
||||||
|
}
|
||||||
|
gradient.addColorStop(0, item.colorObj.colorList[0])
|
||||||
|
gradient.addColorStop(1, item.colorObj.colorList[1])
|
||||||
|
this.context.fillStyle = gradient
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.context.fillStyle = '#FFFFFF'
|
||||||
|
}
|
||||||
|
this.context.save()
|
||||||
|
if (item.radius > 0) {
|
||||||
|
this.context.beginPath()
|
||||||
|
this.context.moveTo(item.x + item.radius, item.y)
|
||||||
|
this.context.arcTo(item.x + item.width, item.y, item.x + item.width, item.y +
|
||||||
|
item.radius, item.radius)
|
||||||
|
this.context.lineTo(item.x + item.width, item.y + item.height - item.radius)
|
||||||
|
this.context.arcTo(item.x + item.width, item.y + item.height, item.x + item
|
||||||
|
.width - item.radius, item.y + item.height, item.radius)
|
||||||
|
this.context.lineTo(item.x + item.radius, item.y + item.height)
|
||||||
|
this.context.arcTo(item.x, item.y + item.height, item.x, item.y + item
|
||||||
|
.height - item.radius, item.radius)
|
||||||
|
this.context.lineTo(item.x, item.y + item.radius)
|
||||||
|
this.context.arcTo(item.x, item.y, item.x + item.radius, item.y, item.radius)
|
||||||
|
this.context.closePath()
|
||||||
|
this.context.clip()
|
||||||
|
}
|
||||||
|
this.context.fillRect(item.x, item.y, item.width, item.height)
|
||||||
|
if (item.lineWidth) {
|
||||||
|
this.context.setLineDash([])
|
||||||
|
this.context.lineWidth = item.lineWidth
|
||||||
|
this.context.strokeStyle = item.lineColor
|
||||||
|
this.context.beginPath()
|
||||||
|
this.context.moveTo(item.x + item.radius, item.y)
|
||||||
|
this.context.arcTo(item.x + item.width, item.y, item.x + item.width, item.y +
|
||||||
|
item.radius, item.radius)
|
||||||
|
this.context.lineTo(item.x + item.width, item.y + item.height - item.radius)
|
||||||
|
this.context.arcTo(item.x + item.width, item.y + item.height, item.x + item
|
||||||
|
.width - item.radius, item.y + item.height, item.radius)
|
||||||
|
this.context.lineTo(item.x + item.radius, item.y + item.height)
|
||||||
|
this.context.arcTo(item.x, item.y + item.height, item.x, item.y + item
|
||||||
|
.height - item.radius, item.radius)
|
||||||
|
this.context.lineTo(item.x, item.y + item.radius)
|
||||||
|
this.context.arcTo(item.x, item.y, item.x + item.radius, item.y, item.radius)
|
||||||
|
this.context.closePath()
|
||||||
|
this.context.stroke()
|
||||||
|
}
|
||||||
|
this.context.restore()
|
||||||
|
await this.context.draw(true)
|
||||||
|
if (item.childs && item.childs.length > 0) {
|
||||||
|
await Promise.all(item.childs.map(async res => {
|
||||||
|
if (res.type == 'color') {
|
||||||
|
await this.drawBg(res)
|
||||||
|
} else if (res.type == 'image') {
|
||||||
|
await this.drawImage(res)
|
||||||
|
} else if (res.type == 'text') {
|
||||||
|
await this.drawText(res)
|
||||||
|
} else if (res.type == 'line') {
|
||||||
|
await this.drawLine(res)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
drawImage(item) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
item.width = uni.upx2px(item.width)
|
||||||
|
item.height = uni.upx2px(item.height)
|
||||||
|
item.x = uni.upx2px(item.x)
|
||||||
|
item.y = uni.upx2px(item.y)
|
||||||
|
item.radius = uni.upx2px(item.radius)
|
||||||
|
item.lineWidth = uni.upx2px(item.lineWidth)
|
||||||
|
await this.getImageInfo(item.path).then(async res => {
|
||||||
|
this.context.save()
|
||||||
|
if (item.radius > 0) {
|
||||||
|
this.context.beginPath()
|
||||||
|
this.context.moveTo(item.x + item.radius, item.y)
|
||||||
|
this.context.arcTo(item.x + item.width, item.y, item.x + item.width,
|
||||||
|
item.y +
|
||||||
|
item.radius, item.radius)
|
||||||
|
this.context.lineTo(item.x + item.width, item.y + item.height - item
|
||||||
|
.radius)
|
||||||
|
this.context.arcTo(item.x + item.width, item.y + item.height, item.x +
|
||||||
|
item
|
||||||
|
.width - item.radius, item.y + item.height, item.radius)
|
||||||
|
this.context.lineTo(item.x + item.radius, item.y + item.height)
|
||||||
|
this.context.arcTo(item.x, item.y + item.height, item.x, item.y + item
|
||||||
|
.height - item.radius, item.radius)
|
||||||
|
this.context.lineTo(item.x, item.y + item.radius)
|
||||||
|
this.context.arcTo(item.x, item.y, item.x + item.radius, item.y, item
|
||||||
|
.radius)
|
||||||
|
this.context.closePath()
|
||||||
|
this.context.clip()
|
||||||
|
}
|
||||||
|
await this.context.drawImage(res, item.x, item.y, item.width, item
|
||||||
|
.height)
|
||||||
|
if (item.lineWidth) {
|
||||||
|
this.context.setLineDash([])
|
||||||
|
this.context.lineWidth = item.lineWidth
|
||||||
|
this.context.strokeStyle = item.lineColor
|
||||||
|
this.context.beginPath()
|
||||||
|
this.context.moveTo(item.x + item.radius, item.y)
|
||||||
|
this.context.arcTo(item.x + item.width, item.y, item.x + item.width,
|
||||||
|
item.y +
|
||||||
|
item.radius, item.radius)
|
||||||
|
this.context.lineTo(item.x + item.width, item.y + item.height - item
|
||||||
|
.radius)
|
||||||
|
this.context.arcTo(item.x + item.width, item.y + item.height, item.x +
|
||||||
|
item
|
||||||
|
.width - item.radius, item.y + item.height, item.radius)
|
||||||
|
this.context.lineTo(item.x + item.radius, item.y + item.height)
|
||||||
|
this.context.arcTo(item.x, item.y + item.height, item.x, item.y + item
|
||||||
|
.height - item.radius, item.radius)
|
||||||
|
this.context.lineTo(item.x, item.y + item.radius)
|
||||||
|
this.context.arcTo(item.x, item.y, item.x + item.radius, item.y, item
|
||||||
|
.radius)
|
||||||
|
this.context.closePath()
|
||||||
|
this.context.stroke()
|
||||||
|
}
|
||||||
|
this.context.restore()
|
||||||
|
await this.context.draw(true)
|
||||||
|
if (item.childs && item.childs.length > 0) {
|
||||||
|
await Promise.all(item.childs.map(async res => {
|
||||||
|
if (res.type == 'color') {
|
||||||
|
await this.drawBg(res)
|
||||||
|
} else if (res.type == 'image') {
|
||||||
|
await this.drawImage(res)
|
||||||
|
} else if (res.type == 'text') {
|
||||||
|
await this.drawText(res)
|
||||||
|
} else if (res.type == 'line') {
|
||||||
|
await this.drawLine(res)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
drawText(item) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
item.width = uni.upx2px(item.width)
|
||||||
|
item.height = uni.upx2px(item.height)
|
||||||
|
item.x = uni.upx2px(item.x)
|
||||||
|
item.y = uni.upx2px(item.y)
|
||||||
|
item.fontSize = uni.upx2px(item.fontSize)
|
||||||
|
item.lineHeight = uni.upx2px(item.lineHeight)
|
||||||
|
await this.drawTextInfo(item.content, item.x, item.y, item.fontSize, item.color, item
|
||||||
|
.width, item.height, item.lineHeight, item.bold, true)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
drawLine(item) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
item.width = uni.upx2px(item.width)
|
||||||
|
item.startX = uni.upx2px(item.startX)
|
||||||
|
item.startY = uni.upx2px(item.startY)
|
||||||
|
item.endX = uni.upx2px(item.endX)
|
||||||
|
item.endY = uni.upx2px(item.endY)
|
||||||
|
this.context.setStrokeStyle(item.color)
|
||||||
|
this.context.setLineWidth(item.width)
|
||||||
|
this.context.setLineCap('round')
|
||||||
|
if (item.lineType == 'dash') this.context.setLineDash([item.width * 5, item.width * 5], 0)
|
||||||
|
else this.context.setLineDash([])
|
||||||
|
this.context.beginPath()
|
||||||
|
this.context.moveTo(item.startX, item.startY)
|
||||||
|
this.context.lineTo(item.endX, item.endY)
|
||||||
|
this.context.stroke()
|
||||||
|
this.context.closePath()
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
drawTextInfo(text, x, y, fontSize, color, width, height, lineHeight, bold, ellipsis) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
this.context.setFillStyle(color)
|
||||||
|
if (bold) this.context.font = 'bold ' + fontSize + 'px Arial'
|
||||||
|
else this.context.font = fontSize + 'px Arial'
|
||||||
|
this.context.setTextBaseline('bottom')
|
||||||
|
let textArray = text.split('')
|
||||||
|
let line = ''
|
||||||
|
let lines = []
|
||||||
|
for (let i = 0; i < textArray.length; i++) {
|
||||||
|
let testLine = line + textArray[i]
|
||||||
|
let testWidth = this.context.measureText(testLine).width
|
||||||
|
if (testWidth > width) {
|
||||||
|
lines.push(line)
|
||||||
|
line = textArray[i]
|
||||||
|
} else {
|
||||||
|
line = testLine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines.push(line)
|
||||||
|
let firstWidth = this.context.measureText(lines[0]).width
|
||||||
|
if (height >= lineHeight * lines.length) {
|
||||||
|
await Promise.all(lines.map(async (res, i) => {
|
||||||
|
let lineText = res
|
||||||
|
let lineHeights = lineHeight * (i + 1)
|
||||||
|
await this.context.fillText(lineText, x, y + lineHeights)
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
let sNum = parseInt(height / lineHeight)
|
||||||
|
lines = lines.slice(0, sNum)
|
||||||
|
await Promise.all(lines.map(async (res, i) => {
|
||||||
|
let lineText = res
|
||||||
|
let lineHeights = lineHeight * (i + 1)
|
||||||
|
if (i == lines.length - 1) {
|
||||||
|
if (this.context.measureText('...').width < fontSize) {
|
||||||
|
lineText = lineText.substring(0, lineText.length - 1)
|
||||||
|
lineText += '...'
|
||||||
|
} else {
|
||||||
|
lineText = lineText.substring(0, lineText.length - 2)
|
||||||
|
lineText += '...'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.context.fillText(lineText, x, y + lineHeights)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getImageInfo(src) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
if (src.indexOf('http') == -1) {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(src)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
uni.getImageInfo({
|
||||||
|
src: src,
|
||||||
|
success: (res) => {
|
||||||
|
resolve(res.path)
|
||||||
|
},
|
||||||
|
fail(err) {
|
||||||
|
resolve(src)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
uni.downloadFile({
|
||||||
|
url: src,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) resolve(res.tempFilePath)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
resolve(src)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
let timer = setTimeout(async () => {
|
||||||
|
await this.context.draw(true, setTimeout(() => {
|
||||||
|
uni.canvasToTempFilePath({
|
||||||
|
canvasId: 'canvasId',
|
||||||
|
fileType: this.fileType,
|
||||||
|
quality: 1,
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
destWidth: this.width * this.pixelRatio,
|
||||||
|
destHeight: this.height * this.pixelRatio,
|
||||||
|
success: (res) => {
|
||||||
|
uni.hideLoading()
|
||||||
|
this.$emit('change', res.tempFilePath)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.log('生成图片失败:', err)
|
||||||
|
}
|
||||||
|
}, this)
|
||||||
|
}, 500))
|
||||||
|
clearTimeout(timer)
|
||||||
|
}, 500)
|
||||||
|
},
|
||||||
|
error(e) {
|
||||||
|
console.log('错误信息:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.canvas-main {
|
||||||
|
position: fixed;
|
||||||
|
z-index: -999999 !important;
|
||||||
|
opacity: 0;
|
||||||
|
top: -5000rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
### 1、本插件可免费下载使用;
|
||||||
|
### 2、未经许可,严禁复制本插件派生同类插件上传插件市场;
|
||||||
|
### 3、未经许可,严禁在插件市场恶意复制抄袭本插件进行违规获利;
|
||||||
|
### 4、对本软件的任何使用都必须遵守这些条款,违反这些条款的个人或组织将面临法律追究。
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
{
|
||||||
|
"id": "liu-poster",
|
||||||
|
"displayName": "canvas海报画板、海报生成、海报图",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"description": "canvas海报画板、海报生成、海报图组件,配置简单,支持绘制背景色、绘制图片、绘制文本、绘制线条,自由生成海报图片",
|
||||||
|
"keywords": [
|
||||||
|
"海报",
|
||||||
|
"生成海报",
|
||||||
|
"canvas",
|
||||||
|
"图片合成",
|
||||||
|
"图片处理"
|
||||||
|
],
|
||||||
|
"repository": "",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": "^3.1.0",
|
||||||
|
"uni-app": "^3.1.0",
|
||||||
|
"uni-app-x": "^3.1.0"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "component-vue",
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "无",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": "",
|
||||||
|
"darkmode": "-",
|
||||||
|
"i18n": "-",
|
||||||
|
"widescreen": "-"
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "√",
|
||||||
|
"aliyun": "√"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"uni-app": {
|
||||||
|
"vue": {
|
||||||
|
"vue2": "-",
|
||||||
|
"vue3": "-"
|
||||||
|
},
|
||||||
|
"web": {
|
||||||
|
"safari": "-",
|
||||||
|
"chrome": "-"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"vue": "-",
|
||||||
|
"nvue": "-",
|
||||||
|
"android": "-",
|
||||||
|
"ios": "-",
|
||||||
|
"harmony": "-"
|
||||||
|
},
|
||||||
|
"mp": {
|
||||||
|
"weixin": "-",
|
||||||
|
"alipay": "-",
|
||||||
|
"toutiao": "-",
|
||||||
|
"baidu": "-",
|
||||||
|
"kuaishou": "-",
|
||||||
|
"jd": "-",
|
||||||
|
"harmony": "-",
|
||||||
|
"qq": "-",
|
||||||
|
"lark": "-"
|
||||||
|
},
|
||||||
|
"quickapp": {
|
||||||
|
"huawei": "-",
|
||||||
|
"union": "-"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uni-app-x": {
|
||||||
|
"web": {
|
||||||
|
"safari": "-",
|
||||||
|
"chrome": "-"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"android": "-",
|
||||||
|
"ios": "-",
|
||||||
|
"harmony": "-"
|
||||||
|
},
|
||||||
|
"mp": {
|
||||||
|
"weixin": "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
# liu-poster适用于uni-app项目的canvas海报画板、海报生成、海报图组件
|
||||||
|
### 本组件目前兼容微信小程序、H5
|
||||||
|
### 本组件是canvas海报画板、海报生成、海报图组件,配置简单,支持绘制背景色、绘制图片、绘制文本、绘制线条,自由生成海报图片
|
||||||
|
# --- 扫码预览、关注我们 ---
|
||||||
|
|
||||||
|
## 扫码关注公众号,查看更多插件信息,预览插件效果!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 属性说明
|
||||||
|
| 名称 | 类型 | 默认值 | 描述 |
|
||||||
|
| ----------------------------|--------------- | -------------------- | ---------------|
|
||||||
|
| width | Number | 750 | 画布宽度(rpx)
|
||||||
|
| height | Number | 750 | 画布高度(rpx)
|
||||||
|
| fileType | String | png | 生成的图片格式(jpg或png)
|
||||||
|
| @change | Function | | 海报绘制成功回调事件
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
```
|
||||||
|
<template>
|
||||||
|
<view class="tab-box">
|
||||||
|
<view class="btn-complete" @click="open">一键生成海报</view>
|
||||||
|
<liu-poster ref="liuPoster" :width="750" :height="1300" @change="change"></liu-poster>
|
||||||
|
<image class="success-img" :src="url" @click="previewImg(url)"></image>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
canvasList: [{
|
||||||
|
type: 'color', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 750, //宽度(rpx)
|
||||||
|
height: 1300, //高度(rpx)
|
||||||
|
x: 0, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 0, //y轴位置(离上边的距离rpx)
|
||||||
|
radius: 100, //圆角(rpx)
|
||||||
|
lineWidth: 40, //边框宽度(rpx)
|
||||||
|
lineColor: '#000000', //边框颜色
|
||||||
|
colorObj: {
|
||||||
|
colorList: ['#6900FF', '#FFFFFF'], //传入1个值为纯色,2个值为渐变色
|
||||||
|
direction: 2 //渐变色绘制方向(1:从左到右;2:从上到下;3:左上角到右下角;4:右上角到左下角)
|
||||||
|
}, //type为color时必填
|
||||||
|
}, {
|
||||||
|
type: 'image', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 132, //宽度(rpx)
|
||||||
|
height: 132, //高度(rpx)
|
||||||
|
x: 40, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 120, //y轴位置(离上边的距离rpx)
|
||||||
|
radius: 66, //圆角(rpx)
|
||||||
|
lineWidth: 6, //边框宽度(rpx)
|
||||||
|
lineColor: '#FFFFFF', //边框颜色
|
||||||
|
path: 'https://img1.baidu.com/it/u=1471990434,2209509794&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400', //图片地址(type为image时必填)
|
||||||
|
}, {
|
||||||
|
type: 'text', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 400, //文本宽度(rpx)
|
||||||
|
height: 40, //文本高度(rpx)
|
||||||
|
x: 200, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 145, //y轴位置(离上边的距离rpx)
|
||||||
|
color: '#FFFFFF', //文本颜色
|
||||||
|
fontSize: 36, //文字大小(rpx)
|
||||||
|
lineHeight: 36, //文字行高(rpx)
|
||||||
|
bold: true, //文字是否加粗
|
||||||
|
content: '好物分享猫猫虫', //文本内容(type为text时必填)
|
||||||
|
}, {
|
||||||
|
type: 'text', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 400, //文本宽度(rpx)
|
||||||
|
height: 40, //文本高度(rpx)
|
||||||
|
x: 200, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 195, //y轴位置(离上边的距离rpx)
|
||||||
|
color: '#FFFFFF', //文本颜色
|
||||||
|
fontSize: 28, //文字大小(rpx)
|
||||||
|
lineHeight: 28, //文字行高(rpx)
|
||||||
|
bold: false, //文字是否加粗
|
||||||
|
content: '猫猫虫给你分享了一张美图', //文本内容(type为text时必填)
|
||||||
|
}, {
|
||||||
|
type: 'image', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 670, //宽度(rpx)
|
||||||
|
height: 670, //高度(rpx)
|
||||||
|
x: 40, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 300, //y轴位置(离上边的距离rpx)
|
||||||
|
radius: 20, //圆角(rpx)
|
||||||
|
lineWidth: 12, //边框宽度(rpx)
|
||||||
|
lineColor: '#FFFFFF', //边框颜色
|
||||||
|
path: 'https://img1.baidu.com/it/u=1471990434,2209509794&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400', //图片地址(type为image时必填)
|
||||||
|
childs: [{
|
||||||
|
type: 'text', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 400, //文本宽度(rpx)
|
||||||
|
height: 40, //文本高度(rpx)
|
||||||
|
x: 100, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 400, //y轴位置(离上边的距离rpx)
|
||||||
|
color: '#FFFFFF', //文本颜色
|
||||||
|
fontSize: 36, //文字大小(rpx)
|
||||||
|
lineHeight: 36, //文字行高(rpx)
|
||||||
|
bold: true, //文字是否加粗
|
||||||
|
content: '好物分享猫猫虫', //文本内容(type为text时必填)
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
type: 'line', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 4, //线条宽度(rpx)
|
||||||
|
color: '#FFFFFF', //线条颜色
|
||||||
|
startX: 20, //起点x轴位置(离左边的距离rpx)
|
||||||
|
startY: 270, //起点y轴位置(离上边的距离rpx)
|
||||||
|
endX: 730, //终点x轴位置(离左边的距离rpx)
|
||||||
|
endY: 270, //终点y轴位置(离上边的距离rpx)
|
||||||
|
lineType: 'dash', //线条类型(solid:实线;dash:虚线)
|
||||||
|
}, {
|
||||||
|
type: 'line', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 4, //线条宽度(rpx)
|
||||||
|
color: '#FFFFFF', //线条颜色
|
||||||
|
startX: 20, //起点x轴位置(离左边的距离rpx)
|
||||||
|
startY: 1000, //起点y轴位置(离上边的距离rpx)
|
||||||
|
endX: 730, //终点x轴位置(离左边的距离rpx)
|
||||||
|
endY: 1000, //终点y轴位置(离上边的距离rpx)
|
||||||
|
lineType: 'dash', //线条类型(solid:实线;dash:虚线)
|
||||||
|
}, {
|
||||||
|
type: 'text', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 500, //文本宽度(rpx)
|
||||||
|
height: 150, //文本高度(rpx)
|
||||||
|
x: 40, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 1050, //y轴位置(离上边的距离rpx)
|
||||||
|
color: '#9043FD', //文本颜色
|
||||||
|
fontSize: 32, //文字大小(rpx)
|
||||||
|
lineHeight: 45, //文字行高(rpx)
|
||||||
|
bold: true, //文字是否加粗
|
||||||
|
content: '这个是一段测试文字,这个是一段测试文字,这个是一段测试文字,这个是一段测试文字,这个是一段测试文字。', //文本内容(type为text时必填)
|
||||||
|
}, {
|
||||||
|
type: 'image', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 150, //宽度(rpx)
|
||||||
|
height: 150, //高度(rpx)
|
||||||
|
x: 550, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 1050, //y轴位置(离上边的距离rpx)
|
||||||
|
radius: 4, //圆角(rpx)
|
||||||
|
lineWidth: 6, //边框宽度(rpx)
|
||||||
|
lineColor: '#FFFFFF', //边框颜色
|
||||||
|
path: 'https://img1.baidu.com/it/u=1471990434,2209509794&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400', //图片地址(type为image时必填)
|
||||||
|
}],
|
||||||
|
url: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
//开始绘制
|
||||||
|
open() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.liuPoster.init(this.canvasList)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
//绘制成功返回生成的海报图片地址
|
||||||
|
change(e) {
|
||||||
|
this.url = e
|
||||||
|
},
|
||||||
|
//预览生成的海报图片
|
||||||
|
previewImg(url) {
|
||||||
|
if (!url) return
|
||||||
|
uni.previewImage({
|
||||||
|
urls: [url]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tab-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
padding-top: 20rpx;
|
||||||
|
|
||||||
|
.btn-reset {
|
||||||
|
width: 100%;
|
||||||
|
height: 72rpx;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
border: 2rpx solid #FD430E;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #3E3E3E;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-complete {
|
||||||
|
width: 98%;
|
||||||
|
height: 76rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background-color: #FD430E;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 1300rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 传入的canvasList参数说明
|
||||||
|
### 绘制类型有4种:color:背景色;image:图片;text:文字;line:线条
|
||||||
|
```
|
||||||
|
canvasList: [{
|
||||||
|
type: 'color', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 750, //宽度(rpx)
|
||||||
|
height: 1500, //高度(rpx)
|
||||||
|
x: 0, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 0, //y轴位置(离上边的距离rpx)
|
||||||
|
radius: 100, //圆角(rpx)
|
||||||
|
lineWidth: 40, //边框宽度(rpx)
|
||||||
|
lineColor: '#000000', //边框颜色
|
||||||
|
colorObj: {
|
||||||
|
colorList: ['#6900FF', '#FFFFFF'], //传入1个值为纯色,2个值为渐变色
|
||||||
|
direction: 2 //渐变色绘制方向(1:从左到右;2:从上到下;3:左上角到右下角;4:右上角到左下角)
|
||||||
|
}, //type为color时必填
|
||||||
|
childs:[],//在背景色上绘制的内容放在childs里面即可
|
||||||
|
}, {
|
||||||
|
type: 'image', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 132, //宽度(rpx)
|
||||||
|
height: 132, //高度(rpx)
|
||||||
|
x: 40, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 150, //y轴位置(离上边的距离rpx)
|
||||||
|
radius: 66, //圆角(rpx)
|
||||||
|
lineWidth: 2, //边框宽度(rpx)
|
||||||
|
lineColor: '#FFFFFF', //边框颜色
|
||||||
|
path: 'https://img1.baidu.com/it/u=1471990434,2209509794&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400', //图片地址(type为image时必填)
|
||||||
|
childs:[],//如果在图片上绘制其他内容则将要绘制的内容放在childs里面即可
|
||||||
|
}, {
|
||||||
|
type: 'text', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 400, //文本宽度(rpx)
|
||||||
|
height: 100, //文本高度(rpx)
|
||||||
|
x: 200, //x轴位置(离左边的距离rpx)
|
||||||
|
y: 170, //y轴位置(离上边的距离rpx)
|
||||||
|
color: '#FFFFFF', //文本颜色
|
||||||
|
fontSize: 36, //文字大小(rpx)
|
||||||
|
lineHeight: 45, //文字行高(rpx)
|
||||||
|
bold: true, //文字是否加粗
|
||||||
|
content: '好物分享猫猫虫好物分享猫猫虫好物分享猫猫虫好物分享猫猫虫好物分享猫猫虫好物分享猫猫虫好物分享猫猫虫', //文本内容(type为text时必填)
|
||||||
|
}, {
|
||||||
|
type: 'line', //绘制类型(color:背景色;image:图片;text:文字;line:线条),
|
||||||
|
width: 2, //线条宽度(rpx)
|
||||||
|
color: '#FFFFFF', //线条颜色
|
||||||
|
startX: 0, //起点x轴位置(离左边的距离rpx)
|
||||||
|
startY: 310, //起点y轴位置(离上边的距离rpx)
|
||||||
|
endX: 750, //终点x轴位置(离左边的距离rpx)
|
||||||
|
endY: 310, //终点y轴位置(离上边的距离rpx)
|
||||||
|
lineType: 'dash', //线条类型(solid:实线;dash:虚线)
|
||||||
|
}]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 注意
|
||||||
|
# 1、H5端使用网络图片需要解决跨域问题;
|
||||||
|
# 2、小程序使用网络图片需要在微信公众平台配置downloadFile合法域名。
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
## 2.1.9(2022-07-13)
|
|
||||||
[修复] app端选择文件后初始化设置的文件列表被清空问题
|
|
||||||
## 2.1.8(2022-07-13)
|
|
||||||
[新增] ref方法初始化文件列表,用于已提交后再次编辑时需带入已上传文件:setFiles(files),可传入数组或Map对象,传入格式请与组件选择返回格式保持一致,且name为必须属性。
|
|
||||||
## 2.1.7(2022-07-12)
|
|
||||||
修复ios端偶现创建webview初始化参数未生效的问题
|
|
||||||
## 2.1.6(2022-07-11)
|
|
||||||
[修复]:修复上个版本更新导致nvue窗口组件不能选择文件的问题;
|
|
||||||
[新增]:
|
|
||||||
1.应群友建议(填写禁止格式太多)格式限制formats由原来填写禁止选择的格式改为填写允许被选择的格式;
|
|
||||||
2.应群友建议(增加上传结束回调事件),上传结束回调事件@uploadEnd
|
|
||||||
3.如能帮到你请留下你的免费好评,组件使用过程中有问题可以加QQ群交流,至于Map对象怎么使用这类前端基础问题请自行百度
|
|
||||||
## 2.1.5(2022-07-01)
|
|
||||||
app端组件销毁时添加自动销毁webview功能,避免v-if销毁组件的情况控件还能被点击的问题
|
|
||||||
## 2.1.4(2022-07-01)
|
|
||||||
修复小程序端回显问题
|
|
||||||
## 2.1.3(2022-06-30)
|
|
||||||
回调事件返回参数新增path字段(文件临时地址),用于回显
|
|
||||||
## 2.1.2(2022-06-16)
|
|
||||||
修复APP端Tabbar窗口无法选择文件的问题
|
|
||||||
## 2.1.1(2022-06-16)
|
|
||||||
优化:
|
|
||||||
1.组件优化为允许在v-if中使用;
|
|
||||||
2.允许option直接在data赋值,不再强制在onRead中初始化;
|
|
||||||
## 2.1.0(2022-06-13)
|
|
||||||
h5 pc端更改为单次可多选
|
|
||||||
## 2.0.9(2022-06-10)
|
|
||||||
更新演示内容,部分同学不知道怎么获取服务端返回的数据
|
|
||||||
## 2.0.8(2022-06-09)
|
|
||||||
优化动态更新上传参数函数,具体查看下方说明:动态更新参数演示
|
|
||||||
## 2.0.7(2022-06-07)
|
|
||||||
新增wxFileType属性,用于小程序端选择附件时可选文件类型
|
|
||||||
## 2.0.6(2022-06-07)
|
|
||||||
修复小程序端真机选择文件提示失败的问题
|
|
||||||
## 2.0.5(2022-06-02)
|
|
||||||
优化小程序端调用hide()后未阻止触发文件选择问题
|
|
||||||
## 2.0.4(2022-06-01)
|
|
||||||
优化APP端选择器初始定位
|
|
||||||
## 2.0.3(2022-05-31)
|
|
||||||
修复nvue窗口选择文件报错问题
|
|
||||||
## 2.0.2(2022-05-20)
|
|
||||||
修复ios端opiton设置过早未传入webview导致不自动上传问题
|
|
||||||
## 2.0.1(2022-05-19)
|
|
||||||
修复APP端子窗口点击选择文件不响应问题
|
|
||||||
## 2.0.0(2022-05-18)
|
|
||||||
此次组件更新至2.0版本,与1.0版本使用上略有差异,已使用1.0的同学请自行斟酌是否需要升级!
|
|
||||||
部分差异:
|
|
||||||
一、 2.0新增异步触发上传功能;
|
|
||||||
二、2.0新增文件批量上传功能;
|
|
||||||
三、2.0优化option,剔除属性,只保留上传接口所需字段,且允许异步更改option的值;
|
|
||||||
四、组件增加size(文件大小限制)、count(文件个数限制)、formats(文件后缀限制)、accept(文件类型限制)、instantly(是否立即自动上传)、debug(日志打印)等属性;
|
|
||||||
五、回调事件取消input事件、callback事件,新增change事件和progress事件;
|
|
||||||
六、ref事件新增upload事件、clear事件;
|
|
||||||
七、优化组件代码,show和hide函数改为显示隐藏,不再重复开关webview;
|
|
||||||
|
|
||||||
## 1.2.3(2022-03-22)
|
|
||||||
修复Demo里传入待完善功能[手动上传属性manual=true]导致不自动上传的问题,手动提交上传待下个版本更新
|
|
||||||
## 1.2.2(2022-02-21)
|
|
||||||
修复上版本APP优化导致H5和小程序端不自动初始化的问题,此次更新仅修复此问题。异步提交功能下个版本更新~
|
|
||||||
## 1.2.1(2022-01-25)
|
|
||||||
QQ1群已满,已开放2群:469580165
|
|
||||||
## 1.2.0(2021-12-09)
|
|
||||||
优化APP端页面中DOM重排后每次需要重新定位的问题
|
|
||||||
## 1.1.1(2021-12-09)
|
|
||||||
优化,与上版本使用方式有改变,请检查后确认是否需要更新,create更名为show, close更名为hide,取消初始化时手动create, 传参方式改为props=>option
|
|
||||||
## 1.1.0(2021-12-09)
|
|
||||||
新增refresh方法,用于DOM发生重排时重新定位控件(APP端)
|
|
||||||
## 1.0.9(2021-07-15)
|
|
||||||
修复上传进度未同步渲染,直接返回100%的BUG
|
|
||||||
## 1.0.8(2021-07-12)
|
|
||||||
修复H5端传入height和width未生效的bug
|
|
||||||
## 1.0.7(2021-07-07)
|
|
||||||
修复h5和小程序端上传完成callback未返回fileName字段问题
|
|
||||||
## 1.0.6(2021-07-07)
|
|
||||||
修复h5端提示信息debug
|
|
||||||
## 1.0.5(2021-06-29)
|
|
||||||
感谢小伙伴找出bug,上传成功回调success未置为true,已修复
|
|
||||||
## 1.0.4(2021-06-28)
|
|
||||||
新增兼容APP,H5,小程序手动关闭控件,关闭后不再弹出文件选择框,需要重新create再次开启
|
|
||||||
## 1.0.3(2021-06-28)
|
|
||||||
close增加条件编译,除app端外不需要close
|
|
||||||
## 1.0.2(2021-06-28)
|
|
||||||
1.修复页面滚动位置后再create控件导致控件位置不正确的问题;
|
|
||||||
2.修复nvue无法create控件;
|
|
||||||
3.示例项目新增nvue使用案例;
|
|
||||||
## 1.0.1(2021-06-28)
|
|
||||||
因为有的朋友不清楚app端切换tab时应该怎么处理webview,现重新上传一版示例项目,需要做tab切换的朋友可以导入示例项目查看
|
|
||||||
## 1.0.0(2021-06-25)
|
|
||||||
此插件为l-file插件中上传功能改版,更新内容为:
|
|
||||||
1. 按钮内嵌入页面,不再强制固定底部,可跟随页面滚动
|
|
||||||
2.无需再单独弹框点击上传,减去中间层
|
|
||||||
3.通过slot自定义按钮样式
|
|
||||||
@@ -1,392 +0,0 @@
|
|||||||
export class LsjFile {
|
|
||||||
constructor(data) {
|
|
||||||
this.dom = null;
|
|
||||||
// files.type = waiting(等待上传)|| loading(上传中)|| success(成功) || fail(失败)
|
|
||||||
this.files = new Map();
|
|
||||||
this.debug = data.debug || false;
|
|
||||||
this.id = data.id;
|
|
||||||
this.width = data.width;
|
|
||||||
this.height = data.height;
|
|
||||||
this.option = data.option;
|
|
||||||
this.instantly = data.instantly;
|
|
||||||
this.prohibited = data.prohibited;
|
|
||||||
this.onchange = data.onchange;
|
|
||||||
this.onprogress = data.onprogress;
|
|
||||||
this.uploadHandle = this._uploadHandle;
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
this.uploadHandle = this._uploadHandleWX;
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建File节点
|
|
||||||
* @param {string}path webview地址
|
|
||||||
*/
|
|
||||||
create(path) {
|
|
||||||
if (!this.dom) {
|
|
||||||
// #ifdef H5
|
|
||||||
let dom = document.createElement('input');
|
|
||||||
dom.type = 'file'
|
|
||||||
dom.value = ''
|
|
||||||
dom.style.height = this.height
|
|
||||||
dom.style.width = this.width
|
|
||||||
dom.style.position = 'absolute'
|
|
||||||
dom.style.top = 0
|
|
||||||
dom.style.left = 0
|
|
||||||
dom.style.right = 0
|
|
||||||
dom.style.bottom = 0
|
|
||||||
dom.style.opacity = 0
|
|
||||||
dom.style.zIndex = 999
|
|
||||||
dom.accept = this.prohibited.accept;
|
|
||||||
if (this.prohibited.count > 1) {
|
|
||||||
dom.multiple = 'multiple';
|
|
||||||
}
|
|
||||||
dom.onchange = event => {
|
|
||||||
for (let file of event.target.files) {
|
|
||||||
this.addFile(file);
|
|
||||||
}
|
|
||||||
this.dom.value = '';
|
|
||||||
};
|
|
||||||
this.dom = dom;
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
let styles = {
|
|
||||||
top: '-500px',
|
|
||||||
left: 0,
|
|
||||||
width: '1px',
|
|
||||||
height: '1px',
|
|
||||||
background: 'transparent'
|
|
||||||
};
|
|
||||||
let extras = {
|
|
||||||
debug: this.debug,
|
|
||||||
instantly: this.instantly,
|
|
||||||
prohibited: this.prohibited,
|
|
||||||
}
|
|
||||||
this.dom = plus.webview.create(path, this.id, styles,extras);
|
|
||||||
this.setData(this.option);
|
|
||||||
this._overrideUrlLoading();
|
|
||||||
// #endif
|
|
||||||
return this.dom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copyObject(obj) {
|
|
||||||
if (typeof obj !== "undefined") {
|
|
||||||
return JSON.parse(JSON.stringify(obj));
|
|
||||||
} else {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自动根据字符串路径设置对象中的值 支持.和[]
|
|
||||||
* @param {Object} dataObj 数据源
|
|
||||||
* @param {String} name 支持a.b 和 a[b]
|
|
||||||
* @param {String} value 值
|
|
||||||
* setValue(dataObj, name, value);
|
|
||||||
*/
|
|
||||||
setValue(dataObj, name, value) {
|
|
||||||
// 通过正则表达式 查找路径数据
|
|
||||||
let dataValue;
|
|
||||||
if (typeof value === "object") {
|
|
||||||
dataValue = this.copyObject(value);
|
|
||||||
} else {
|
|
||||||
dataValue = value;
|
|
||||||
}
|
|
||||||
let regExp = new RegExp("([\\w$]+)|\\[(:\\d)\\]", "g");
|
|
||||||
const patten = name.match(regExp);
|
|
||||||
// 遍历路径 逐级查找 最后一级用于直接赋值
|
|
||||||
for (let i = 0; i < patten.length - 1; i++) {
|
|
||||||
let keyName = patten[i];
|
|
||||||
if (typeof dataObj[keyName] !== "object") dataObj[keyName] = {};
|
|
||||||
dataObj = dataObj[keyName];
|
|
||||||
}
|
|
||||||
// 最后一级
|
|
||||||
dataObj[patten[patten.length - 1]] = dataValue;
|
|
||||||
this.debug&&console.log('参数更新后',JSON.stringify(this.option));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置上传参数
|
|
||||||
* @param {object|string}name 上传参数,支持a.b 和 a[b]
|
|
||||||
*/
|
|
||||||
setData() {
|
|
||||||
let [name,value = ''] = arguments;
|
|
||||||
if (typeof name === 'object') {
|
|
||||||
Object.assign(this.option,name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.setValue(this.option,name,value);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.debug&&console.log(JSON.stringify(this.option));
|
|
||||||
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
this.dom.evalJS(`vm.setData('${JSON.stringify(this.option)}')`);
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传
|
|
||||||
* @param {string}name 文件名称
|
|
||||||
*/
|
|
||||||
async upload(name='') {
|
|
||||||
if (!this.option.url) {
|
|
||||||
throw Error('未设置上传地址');
|
|
||||||
}
|
|
||||||
|
|
||||||
// #ifndef APP-PLUS
|
|
||||||
if (name && this.files.has(name)) {
|
|
||||||
await this.uploadHandle(this.files.get(name));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (let item of this.files.values()) {
|
|
||||||
if (item.type === 'waiting' || item.type === 'fail') {
|
|
||||||
await this.uploadHandle(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
this.dom&&this.dom.evalJS(`vm.upload('${name}')`);
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// 选择文件change
|
|
||||||
addFile(file) {
|
|
||||||
|
|
||||||
let name = file.name;
|
|
||||||
this.debug&&console.log('文件名称',name,'大小',file.size);
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
// 限制文件格式
|
|
||||||
let path = '';
|
|
||||||
let suffix = name.substring(name.lastIndexOf(".")+1).toLowerCase();
|
|
||||||
let formats = this.prohibited.formats.toLowerCase();
|
|
||||||
if (formats&&!formats.includes(suffix)) {
|
|
||||||
this.toast(`不支持上传${suffix.toUpperCase()}格式文件`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 限制文件大小
|
|
||||||
if (file.size > 1024 * 1024 * Math.abs(this.prohibited.size)) {
|
|
||||||
this.toast(`附件大小请勿超过${this.prohibited.size}M`)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// #ifndef MP-WEIXIN
|
|
||||||
path = URL.createObjectURL(file);
|
|
||||||
// #endif
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
path = file.path;
|
|
||||||
// #endif
|
|
||||||
this.files.set(file.name,{file,path,name: file.name,size: file.size,progress: 0,type: 'waiting'});
|
|
||||||
|
|
||||||
// #ifndef MP-WEIXIN
|
|
||||||
this.onchange(this.files);
|
|
||||||
this.instantly&&this.upload();
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
return true;
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除文件
|
|
||||||
* @param {string}name 不传name默认移除所有文件,传入name移除指定name的文件
|
|
||||||
*/
|
|
||||||
clear(name='') {
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
this.dom&&this.dom.evalJS(`vm.clear('${name}')`);
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
if (!name) {
|
|
||||||
this.files.clear();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.files.delete(name);
|
|
||||||
}
|
|
||||||
return this.onchange(this.files);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 提示框
|
|
||||||
* @param {string}msg 轻提示内容
|
|
||||||
*/
|
|
||||||
toast(msg) {
|
|
||||||
uni.showToast({
|
|
||||||
title: msg,
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 微信小程序选择文件
|
|
||||||
* @param {number}count 可选择文件数量
|
|
||||||
*/
|
|
||||||
chooseMessageFile(type,count) {
|
|
||||||
wx.chooseMessageFile({
|
|
||||||
count: count,
|
|
||||||
type: type,
|
|
||||||
success: ({ tempFiles }) => {
|
|
||||||
for (let file of tempFiles) {
|
|
||||||
let next = this.addFile(file);
|
|
||||||
if (!next) {return}
|
|
||||||
}
|
|
||||||
this.onchange(this.files);
|
|
||||||
this.instantly&&this.upload();
|
|
||||||
},
|
|
||||||
fail: () => {
|
|
||||||
this.toast(`打开失败`);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_overrideUrlLoading() {
|
|
||||||
this.dom.overrideUrlLoading({ mode: 'reject' }, e => {
|
|
||||||
let {retype,item,files,end} = this._getRequest(
|
|
||||||
e.url
|
|
||||||
);
|
|
||||||
let _this = this;
|
|
||||||
switch (retype) {
|
|
||||||
case 'updateOption':
|
|
||||||
this.dom.evalJS(`vm.setData('${JSON.stringify(_this.option)}')`);
|
|
||||||
break
|
|
||||||
case 'change':
|
|
||||||
try {
|
|
||||||
_this.files = new Map([..._this.files,...JSON.parse(unescape(files))]);
|
|
||||||
} catch (e) {
|
|
||||||
return console.error('出错了,请检查代码')
|
|
||||||
}
|
|
||||||
_this.onchange(_this.files);
|
|
||||||
break
|
|
||||||
case 'progress':
|
|
||||||
try {
|
|
||||||
item = JSON.parse(unescape(item));
|
|
||||||
} catch (e) {
|
|
||||||
return console.error('出错了,请检查代码')
|
|
||||||
}
|
|
||||||
_this._changeFilesItem(item,end);
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_getRequest(url) {
|
|
||||||
let theRequest = new Object()
|
|
||||||
let index = url.indexOf('?')
|
|
||||||
if (index != -1) {
|
|
||||||
let str = url.substring(index + 1)
|
|
||||||
let strs = str.split('&')
|
|
||||||
for (let i = 0; i < strs.length; i++) {
|
|
||||||
theRequest[strs[i].split('=')[0]] = unescape(strs[i].split('=')[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return theRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
_changeFilesItem(item,end=false) {
|
|
||||||
this.debug&&console.log('onprogress',JSON.stringify(item));
|
|
||||||
this.onprogress(item,end);
|
|
||||||
this.files.set(item.name,item);
|
|
||||||
}
|
|
||||||
|
|
||||||
_uploadHandle(item) {
|
|
||||||
item.type = 'loading';
|
|
||||||
delete item.responseText;
|
|
||||||
return new Promise((resolve,reject)=>{
|
|
||||||
this.debug&&console.log('option',JSON.stringify(this.option));
|
|
||||||
let {url,name,method='POST',header,formData} = this.option;
|
|
||||||
let form = new FormData();
|
|
||||||
for (let keys in formData) {
|
|
||||||
form.append(keys, formData[keys])
|
|
||||||
}
|
|
||||||
form.append(name, item.file);
|
|
||||||
let xmlRequest = new XMLHttpRequest();
|
|
||||||
xmlRequest.open(method, url, true);
|
|
||||||
for (let keys in header) {
|
|
||||||
xmlRequest.setRequestHeader(keys, header[keys])
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlRequest.upload.addEventListener(
|
|
||||||
'progress',
|
|
||||||
event => {
|
|
||||||
if (event.lengthComputable) {
|
|
||||||
let progress = Math.ceil((event.loaded * 100) / event.total)
|
|
||||||
if (progress <= 100) {
|
|
||||||
item.progress = progress;
|
|
||||||
this._changeFilesItem(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
xmlRequest.ontimeout = () => {
|
|
||||||
console.error('请求超时')
|
|
||||||
item.type = 'fail';
|
|
||||||
this._changeFilesItem(item,true);
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlRequest.onreadystatechange = ev => {
|
|
||||||
if (xmlRequest.readyState == 4) {
|
|
||||||
if (xmlRequest.status == 200) {
|
|
||||||
this.debug&&console.log('上传完成:' + xmlRequest.responseText)
|
|
||||||
item['responseText'] = xmlRequest.responseText;
|
|
||||||
item.type = 'success';
|
|
||||||
this._changeFilesItem(item,true);
|
|
||||||
return resolve(true);
|
|
||||||
} else if (xmlRequest.status == 0) {
|
|
||||||
console.error('status = 0 :请检查请求头Content-Type与服务端是否匹配,服务端已正确开启跨域,并且nginx未拦截阻止请求')
|
|
||||||
}
|
|
||||||
console.error('--ERROR--:status = ' + xmlRequest.status)
|
|
||||||
item.type = 'fail';
|
|
||||||
this._changeFilesItem(item,true);
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xmlRequest.send(form)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_uploadHandleWX(item) {
|
|
||||||
item.type = 'loading';
|
|
||||||
delete item.responseText;
|
|
||||||
return new Promise((resolve,reject)=>{
|
|
||||||
this.debug&&console.log('option',JSON.stringify(this.option));
|
|
||||||
let form = {filePath: item.file.path,...this.option };
|
|
||||||
form['fail'] = ({ errMsg = '' }) => {
|
|
||||||
console.error('--ERROR--:' + errMsg)
|
|
||||||
item.type = 'fail';
|
|
||||||
this._changeFilesItem(item,true);
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
form['success'] = res => {
|
|
||||||
if (res.statusCode == 200) {
|
|
||||||
this.debug&&console.log('上传完成,微信端返回不一定是字符串,根据接口返回格式判断是否需要JSON.parse:' + res.data)
|
|
||||||
item['responseText'] = res.data;
|
|
||||||
item.type = 'success';
|
|
||||||
this._changeFilesItem(item,true);
|
|
||||||
return resolve(true);
|
|
||||||
}
|
|
||||||
item.type = 'fail';
|
|
||||||
this._changeFilesItem(item,true);
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
let xmlRequest = uni.uploadFile(form);
|
|
||||||
xmlRequest.onProgressUpdate(({ progress = 0 }) => {
|
|
||||||
if (progress <= 100) {
|
|
||||||
item.progress = progress;
|
|
||||||
this._changeFilesItem(item);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="lsj-file" :style="[getStyles]">
|
|
||||||
<view ref="lsj" class="hFile" :style="[getStyles]" @click="onClick">
|
|
||||||
<slot><view class="defview" :style="[getStyles]">附件上传</view></slot>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// 查看文档:https://ext.dcloud.net.cn/plugin?id=5459
|
|
||||||
import {LsjFile} from './LsjFile.js'
|
|
||||||
export default {
|
|
||||||
name: 'Lsj-upload',
|
|
||||||
props: {
|
|
||||||
// 打印日志
|
|
||||||
debug: {type: Boolean,default: false},
|
|
||||||
// 自动上传
|
|
||||||
instantly: {type: Boolean,default: false},
|
|
||||||
// 上传接口参数设置
|
|
||||||
option: {type: Object,default: ()=>{}},
|
|
||||||
// 文件大小上限
|
|
||||||
size: { type: Number, default: 10 },
|
|
||||||
// 文件选择个数上限,超出后不触发点击
|
|
||||||
count: { type: Number, default: 9 },
|
|
||||||
// 允许上传的文件格式(多个以逗号隔开)
|
|
||||||
formats: { type: String, default:''},
|
|
||||||
// input file选择限制
|
|
||||||
accept: {type: String,default: ''},
|
|
||||||
// 微信选择文件类型
|
|
||||||
//all=从所有文件选择,
|
|
||||||
//video=只能选择视频文件,
|
|
||||||
//image=只能选择图片文件,
|
|
||||||
//file=可以选择除了图片和视频之外的其它的文件
|
|
||||||
wxFileType: { type: String, default: 'all' },
|
|
||||||
// webviewID需唯一,不同窗口也不要同Id
|
|
||||||
childId: { type: String, default: 'lsjUpload' },
|
|
||||||
// 文件选择触发面宽度
|
|
||||||
width: { type: String, default: '100%' },
|
|
||||||
// 文件选择触发面高度
|
|
||||||
height: { type: String, default: '80rpx' },
|
|
||||||
|
|
||||||
// top,left,bottom,right仅position=absolute时才需要传入
|
|
||||||
top: { type: [String, Number], default: '' },
|
|
||||||
left: { type: [String, Number], default: '' },
|
|
||||||
bottom: { type: [String, Number], default: '' },
|
|
||||||
right: { type: [String, Number], default: '' },
|
|
||||||
// nvue不支持跟随窗口滚动
|
|
||||||
position: {
|
|
||||||
type: String,
|
|
||||||
// #ifdef APP-NVUE
|
|
||||||
default: 'absolute',
|
|
||||||
// #endif
|
|
||||||
// #ifndef APP-NVUE
|
|
||||||
default: 'static',
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
option(v) {
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
this.lsjFile&&this.show();
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updated() {
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
if (this.isShow) {
|
|
||||||
this.lsjFile&&this.show();
|
|
||||||
}
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
getStyles() {
|
|
||||||
let styles = {
|
|
||||||
width: this.width,
|
|
||||||
height: this.height
|
|
||||||
}
|
|
||||||
if (this.position == 'absolute') {
|
|
||||||
styles['top'] = this.top
|
|
||||||
styles['bottom'] = this.bottom
|
|
||||||
styles['left'] = this.left
|
|
||||||
styles['right'] = this.right
|
|
||||||
styles['position'] = 'fixed'
|
|
||||||
}
|
|
||||||
|
|
||||||
return styles
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this._size = 0;
|
|
||||||
this.lsjFile = new LsjFile({
|
|
||||||
debug: this.debug,
|
|
||||||
id: this.childId,
|
|
||||||
width: this.width,
|
|
||||||
height: this.height,
|
|
||||||
option: this.option,
|
|
||||||
instantly: this.instantly,
|
|
||||||
// 限制条件
|
|
||||||
prohibited: {
|
|
||||||
// 大小
|
|
||||||
size: this.size,
|
|
||||||
// 允许上传的格式
|
|
||||||
formats: this.formats,
|
|
||||||
// 限制选择的格式
|
|
||||||
accept: this.accept,
|
|
||||||
count: this.count
|
|
||||||
},
|
|
||||||
onchange: this.onchange,
|
|
||||||
onprogress: this.onprogress,
|
|
||||||
});
|
|
||||||
this.create();
|
|
||||||
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
this.lsjFile.dom.close();
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setFiles(array) {
|
|
||||||
if (array instanceof Map) {
|
|
||||||
for (let [key, item] of array) {
|
|
||||||
item['progress'] = 100;
|
|
||||||
item['type'] = 'success';
|
|
||||||
this.lsjFile.files.set(key,item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (Array.isArray(array)) {
|
|
||||||
array.forEach(item=>{
|
|
||||||
if (item.name) {
|
|
||||||
item['progress'] = 100;
|
|
||||||
item['type'] = 'success';
|
|
||||||
this.lsjFile.files.set(item.name,item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.onchange(this.lsjFile.files);
|
|
||||||
},
|
|
||||||
setData() {
|
|
||||||
this.lsjFile&&this.lsjFile.setData(...arguments);
|
|
||||||
},
|
|
||||||
getDomStyles(callback) {
|
|
||||||
// #ifndef APP-NVUE
|
|
||||||
let view = uni
|
|
||||||
.createSelectorQuery()
|
|
||||||
.in(this)
|
|
||||||
.select('.lsj-file')
|
|
||||||
view.fields(
|
|
||||||
{
|
|
||||||
size: true,
|
|
||||||
rect: true
|
|
||||||
},
|
|
||||||
({ height, width, top, left, right, bottom }) => {
|
|
||||||
uni.createSelectorQuery()
|
|
||||||
.selectViewport()
|
|
||||||
.scrollOffset(({ scrollTop }) => {
|
|
||||||
return callback({
|
|
||||||
top: parseInt(top) + parseInt(scrollTop) + 'px',
|
|
||||||
left: parseInt(left) + 'px',
|
|
||||||
width: parseInt(width) + 'px',
|
|
||||||
height: parseInt(height) + 'px'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.exec()
|
|
||||||
}
|
|
||||||
).exec()
|
|
||||||
// #endif
|
|
||||||
// #ifdef APP-NVUE
|
|
||||||
const dom = weex.requireModule('dom')
|
|
||||||
dom.getComponentRect(this.$refs.lsj, ({ size: { height, width, top, left, right, bottom } }) => {
|
|
||||||
return callback({
|
|
||||||
top: parseInt(top) + 'px',
|
|
||||||
left: parseInt(left) + 'px',
|
|
||||||
width: parseInt(width) + 'px',
|
|
||||||
height: parseInt(height) + 'px',
|
|
||||||
right: parseInt(right) + 'px',
|
|
||||||
bottom: parseInt(bottom) + 'px'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
show() {
|
|
||||||
this.isShow = true;
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
this.lsjFile&&this.getDomStyles(styles => {
|
|
||||||
this.lsjFile.dom.setStyle(styles)
|
|
||||||
});
|
|
||||||
// #endif
|
|
||||||
// #ifdef H5
|
|
||||||
this.lsjFile.dom.style.display = 'inline'
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
hide() {
|
|
||||||
this.isShow = false;
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
this.lsjFile&&this.lsjFile.dom.setStyle({
|
|
||||||
top: '-500px',
|
|
||||||
left:'0px',
|
|
||||||
width: '1px',
|
|
||||||
height: '1px',
|
|
||||||
});
|
|
||||||
// #endif
|
|
||||||
// #ifdef H5
|
|
||||||
this.lsjFile.dom.style.display = 'none'
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 手动提交上传
|
|
||||||
* @param {string}name 文件名称,不传则上传所有type等于waiting和fail的文件
|
|
||||||
*/
|
|
||||||
upload(name) {
|
|
||||||
this.lsjFile&&this.lsjFile.upload(name);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @returns {Map} 已选择的文件Map集
|
|
||||||
*/
|
|
||||||
onchange(files) {
|
|
||||||
this.$emit('change',files);
|
|
||||||
this._size = files.size;
|
|
||||||
return files.size >= this.count ? this.hide() : this.show();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @returns {object} 当前上传中的对象
|
|
||||||
*/
|
|
||||||
onprogress(item,end=false) {
|
|
||||||
this.$emit('progress',item);
|
|
||||||
if (end) {
|
|
||||||
setTimeout(()=>{
|
|
||||||
this.$emit('uploadEnd',item);
|
|
||||||
},0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 移除组件内缓存的某条数据
|
|
||||||
* @param {string}name 文件名称,不指定默认清除所有文件
|
|
||||||
*/
|
|
||||||
clear(name) {
|
|
||||||
this.lsjFile.clear(name);
|
|
||||||
},
|
|
||||||
// 创建选择器
|
|
||||||
create() {
|
|
||||||
// 若iOS端服务端处理不了跨域就将hybrid目录内的html放到服务端去,并将此处path改成服务器上的地址
|
|
||||||
let path = '/uni_modules/lsj-upload/hybrid/html/uploadFile.html';
|
|
||||||
let dom = this.lsjFile.create(path);
|
|
||||||
// #ifdef H5
|
|
||||||
this.$refs.lsj.$el.appendChild(dom);
|
|
||||||
// #endif
|
|
||||||
// #ifndef APP-PLUS
|
|
||||||
this.show();
|
|
||||||
// #endif
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
dom.setStyle({position: this.position});
|
|
||||||
dom.loadURL(path);
|
|
||||||
setTimeout(()=>{
|
|
||||||
// #ifdef APP-NVUE
|
|
||||||
plus.webview.currentWebview().append(dom);
|
|
||||||
// #endif
|
|
||||||
// #ifndef APP-NVUE
|
|
||||||
this.$root.$scope.$getAppWebview().append(dom);
|
|
||||||
// #endif
|
|
||||||
this.show();
|
|
||||||
},300)
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
// 点击选择附件
|
|
||||||
onClick() {
|
|
||||||
if (this._size >= this.count) {
|
|
||||||
this.toast(`只允许上传${this.count}个文件`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
if (!this.isShow) {return;}
|
|
||||||
let count = this.count - this._size;
|
|
||||||
this.lsjFile.chooseMessageFile(this.wxFileType,count);
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
toast(msg) {
|
|
||||||
uni.showToast({
|
|
||||||
title: msg,
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.lsj-file {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.defview {
|
|
||||||
background-color: #007aff;
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 10rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
.hFile {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,179 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-cn">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title class="title">[文件管理器]</title>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
|
|
||||||
<style type="text/css">
|
|
||||||
.content {background: transparent;}
|
|
||||||
.btn {position: relative;top: 0;left: 0;bottom: 0;right: 0;}
|
|
||||||
.btn .file {position: fixed;z-index: 93;left: 0;right: 0;top: 0;bottom: 0;width: 100%;opacity: 0;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="content" class="content">
|
|
||||||
<div class="btn">
|
|
||||||
<input @change="onChange" :accept="accept" ref="file" class="file" type="file" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="js/vue.min.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
let _this;
|
|
||||||
var vm = new Vue({
|
|
||||||
el: '#content',
|
|
||||||
data: {
|
|
||||||
accept: '',
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
console.log('加载webview');
|
|
||||||
_this = this;
|
|
||||||
this.files = new Map();
|
|
||||||
document.addEventListener('plusready', (e)=>{
|
|
||||||
let {debug,instantly,prohibited} = plus.webview.currentWebview();
|
|
||||||
this.debug = debug;
|
|
||||||
this.instantly = instantly;
|
|
||||||
this.prohibited = prohibited;
|
|
||||||
this.accept = prohibited.accept;
|
|
||||||
location.href = 'callback?retype=updateOption';
|
|
||||||
}, false);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toast(msg) {
|
|
||||||
plus.nativeUI.toast(msg);
|
|
||||||
},
|
|
||||||
clear(name) {
|
|
||||||
if (!name) {
|
|
||||||
this.files.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.files.delete(name);
|
|
||||||
},
|
|
||||||
setData(option='{}') {
|
|
||||||
this.debug&&console.log('更新参数:'+option);
|
|
||||||
try{
|
|
||||||
_this.option = JSON.parse(option);
|
|
||||||
}catch(e){
|
|
||||||
console.error('参数设置错误')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async upload(name=''){
|
|
||||||
if (name && this.files.has(name)) {
|
|
||||||
await this.createUpload(this.files.get(name));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (let item of this.files.values()) {
|
|
||||||
if (item.type === 'waiting' || item.type === 'fail') {
|
|
||||||
await this.createUpload(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onChange(e) {
|
|
||||||
let fileDom = this.$refs.file;
|
|
||||||
let file = fileDom.files[0];
|
|
||||||
let name = file.name;
|
|
||||||
fileDom.value = '';
|
|
||||||
this.debug&&console.log('文件名称',name,'大小',file.size);
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
// 限制文件格式
|
|
||||||
let suffix = name.substring(name.lastIndexOf(".")+1).toLowerCase();
|
|
||||||
let formats = this.prohibited.formats.toLowerCase();
|
|
||||||
if (formats&&!formats.includes(suffix)) {
|
|
||||||
this.toast(`不支持上传${suffix.toUpperCase()}格式文件`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 限制文件大小
|
|
||||||
if (file.size > 1024 * 1024 * Math.abs(this.prohibited.size)) {
|
|
||||||
this.toast(`附件大小请勿超过${this.prohibited.size}M`)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let path = URL.createObjectURL(file);
|
|
||||||
this.files.set(file.name,{file,path,name: file.name,size: file.size,progress: 0,type: 'waiting'});
|
|
||||||
this.callChange();
|
|
||||||
this.instantly&&this.upload();
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @returns {Map} 已选择的文件Map集
|
|
||||||
*/
|
|
||||||
callChange() {
|
|
||||||
location.href = 'callback?retype=change&files=' + escape(JSON.stringify([...this.files]));
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @returns {object} 正在处理的当前对象
|
|
||||||
*/
|
|
||||||
changeFilesItem(item,end='') {
|
|
||||||
this.files.set(item.name,item);
|
|
||||||
location.href = 'callback?retype=progress&end='+ end +'&item=' + escape(JSON.stringify(item));
|
|
||||||
},
|
|
||||||
createUpload(item) {
|
|
||||||
this.debug&&console.log('准备上传,option=:'+JSON.stringify(this.option));
|
|
||||||
item.type = 'loading';
|
|
||||||
delete item.responseText;
|
|
||||||
return new Promise((resolve,reject)=>{
|
|
||||||
let {url,name,method='POST',header={},formData={}} = this.option;
|
|
||||||
let form = new FormData();
|
|
||||||
for (let keys in formData) {
|
|
||||||
form.append(keys, formData[keys])
|
|
||||||
}
|
|
||||||
form.append(name, item.file);
|
|
||||||
let xmlRequest = new XMLHttpRequest();
|
|
||||||
xmlRequest.open(method, url, true);
|
|
||||||
for (let keys in header) {
|
|
||||||
xmlRequest.setRequestHeader(keys, header[keys])
|
|
||||||
}
|
|
||||||
xmlRequest.upload.addEventListener(
|
|
||||||
'progress',
|
|
||||||
event => {
|
|
||||||
if (event.lengthComputable) {
|
|
||||||
let progress = Math.ceil((event.loaded * 100) / event.total)
|
|
||||||
if (progress <= 100) {
|
|
||||||
item.progress = progress;
|
|
||||||
this.changeFilesItem(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
xmlRequest.ontimeout = () => {
|
|
||||||
console.error('请求超时')
|
|
||||||
item.type = 'fail';
|
|
||||||
this.changeFilesItem(item,true);
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlRequest.onreadystatechange = ev => {
|
|
||||||
if (xmlRequest.readyState == 4) {
|
|
||||||
if (xmlRequest.status == 200) {
|
|
||||||
this.debug && console.log('上传完成:' + xmlRequest.responseText)
|
|
||||||
item['responseText'] = xmlRequest.responseText;
|
|
||||||
item.type = 'success';
|
|
||||||
this.changeFilesItem(item,true);
|
|
||||||
return resolve(true);
|
|
||||||
} else if (xmlRequest.status == 0) {
|
|
||||||
console.error('status = 0 :请检查请求头Content-Type与服务端是否匹配,服务端已正确开启跨域,并且nginx未拦截阻止请求')
|
|
||||||
}
|
|
||||||
console.error('--ERROR--:status = ' + xmlRequest.status)
|
|
||||||
item.type = 'fail';
|
|
||||||
this.changeFilesItem(item,true);
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xmlRequest.send(form)
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "lsj-upload",
|
|
||||||
"displayName": "全文件上传选择非原生2.0版",
|
|
||||||
"version": "2.1.9",
|
|
||||||
"description": "文件选择上传-支持APP-H5网页-微信小程序",
|
|
||||||
"keywords": [
|
|
||||||
"附件",
|
|
||||||
"file",
|
|
||||||
"upload",
|
|
||||||
"上传",
|
|
||||||
"文件管理器"
|
|
||||||
],
|
|
||||||
"repository": "",
|
|
||||||
"engines": {
|
|
||||||
"HBuilderX": "^3.2.0"
|
|
||||||
},
|
|
||||||
"dcloudext": {
|
|
||||||
"category": [
|
|
||||||
"前端组件",
|
|
||||||
"通用组件"
|
|
||||||
],
|
|
||||||
"sale": {
|
|
||||||
"regular": {
|
|
||||||
"price": "0.00"
|
|
||||||
},
|
|
||||||
"sourcecode": {
|
|
||||||
"price": "0.00"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"contact": {
|
|
||||||
"qq": ""
|
|
||||||
},
|
|
||||||
"declaration": {
|
|
||||||
"ads": "无",
|
|
||||||
"data": "无",
|
|
||||||
"permissions": "无"
|
|
||||||
},
|
|
||||||
"npmurl": ""
|
|
||||||
},
|
|
||||||
"uni_modules": {
|
|
||||||
"platforms": {
|
|
||||||
"cloud": {
|
|
||||||
"tcb": "y",
|
|
||||||
"aliyun": "y"
|
|
||||||
},
|
|
||||||
"client": {
|
|
||||||
"App": {
|
|
||||||
"app-vue": "y",
|
|
||||||
"app-nvue": "y"
|
|
||||||
},
|
|
||||||
"H5-mobile": {
|
|
||||||
"Safari": "y",
|
|
||||||
"Android Browser": "y",
|
|
||||||
"微信浏览器(Android)": "y",
|
|
||||||
"QQ浏览器(Android)": "y"
|
|
||||||
},
|
|
||||||
"H5-pc": {
|
|
||||||
"Chrome": "y",
|
|
||||||
"IE": "y",
|
|
||||||
"Edge": "y",
|
|
||||||
"Firefox": "y",
|
|
||||||
"Safari": "y"
|
|
||||||
},
|
|
||||||
"小程序": {
|
|
||||||
"微信": "y",
|
|
||||||
"阿里": "u",
|
|
||||||
"百度": "u",
|
|
||||||
"字节跳动": "u",
|
|
||||||
"QQ": "u"
|
|
||||||
},
|
|
||||||
"快应用": {
|
|
||||||
"华为": "y",
|
|
||||||
"联盟": "y"
|
|
||||||
},
|
|
||||||
"Vue": {
|
|
||||||
"vue2": "y",
|
|
||||||
"vue3": "y"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,330 +0,0 @@
|
|||||||
# lsj-upload
|
|
||||||
|
|
||||||
### 插件地址:https://ext.dcloud.net.cn/plugin?id=5459
|
|
||||||
|
|
||||||
### 不清楚使用方式可点击右侧导入示例项目运行完整示例
|
|
||||||
### 此次更新2.0与1.0使用方式略有差异,已使用1.0的同学自行斟酌是否更新到2.0版本!!!
|
|
||||||
|
|
||||||
使用插件有任何问题欢迎加入QQ讨论群:
|
|
||||||
- 群1:701468256(已满)
|
|
||||||
- 群2:469580165
|
|
||||||
|
|
||||||
若能帮到你请高抬贵手点亮5颗星~
|
|
||||||
------
|
|
||||||
## 重要提示
|
|
||||||
### 组件是窗口级滚动,不要在scroll-view内使用!!
|
|
||||||
### 组件是窗口级滚动,不要在scroll-view内使用!!
|
|
||||||
### 组件是窗口级滚动,不要在scroll-view内使用!!
|
|
||||||
|
|
||||||
### 控件的height高度应与slot自定义内容高度保持一致
|
|
||||||
### nvue窗口只能使用固定模式position=absolute
|
|
||||||
### show() 当DOM重排后在this.$nextTick内调用show(),控件定位会更加准确
|
|
||||||
### hide() APP端webview层级比view高,如不希望触发点击时,应调用hide隐藏控件,反之调用show
|
|
||||||
### 若iOS端跨域服务端同学实在配置不好,可把hybrid下html目录放到服务器去,同源则不存在跨域问题。
|
|
||||||
### 小程序端因hybrid不能使用本地HTML,所以插件提供的是从微信消息列表拉取文件并选择,请知悉。
|
|
||||||
------
|
|
||||||
|
|
||||||
## 使用说明
|
|
||||||
| 属性 | 是否必填 | 值类型 | 默认值 | 说明 |
|
|
||||||
| --------- | -------- | -----: | --: | :------------:|
|
|
||||||
| width | 否 | String |100% | 容器宽度 |
|
|
||||||
| height | 是 | String |80rpx | 容器高度 |
|
|
||||||
| debug | 否 | Boolean |false | 打印调试日志 |
|
|
||||||
| option | 是 | Object |- | [文件上传接口相关参数](#p1)|
|
|
||||||
| instantly | 否 | Boolean |false | true=自动上传 |
|
|
||||||
| count | 否 | Number |10 | 附件选择上限(个)|
|
|
||||||
| size | 否 | Number |10 | 附件大小上限(M)|
|
|
||||||
| wxFileType | 否 | String |all | 微信小程序文件选择器格式限制(all=从所有文件选择,video=只能选择视频文件,image=只能选择图片文件,file=可以选择除了图片和视频之外的其它的文件)|
|
|
||||||
| accept | 否 | String |- | 文件选择器input file格式限制(部分机型不兼容,建议使用formats)|
|
|
||||||
| formats | 否 | String |- | 限制允许上传的格式,空串=不限制,默认为空,多个格式以逗号隔开,例如png,jpg,pdf|
|
|
||||||
| childId | 否 | String |lsjUpload| 控件的id(仅APP有效,应用内每个控件命名一个唯一Id,不同窗口也不要同名Id)|
|
|
||||||
| position | 否 | String |static | 控件的定位模式(static=控件随页面滚动;absolute=控件在页面中绝对定位,不随窗口内容滚动)|
|
|
||||||
| top,left,right,bottom | 否 | [Number,String] |0 | 设置控件绝对位置,position=absolute时有效|
|
|
||||||
| @change | 否 | Function |Map | 选择文件触发,返回所有已选择文件Map集合|
|
|
||||||
| @progress | 否 | Function |Object | 上传过程中发生状态变化的文件对象,需通过set更新至Map集合|
|
|
||||||
| @uploadEnd| 否 | Function |Object | 上传结束回调,返回参数与progress一致|
|
|
||||||
|
|
||||||
## <a id="p1">option说明</a>
|
|
||||||
|参数 | 是否必填 | 说明|
|
|
||||||
|---- | ---- | :--: |
|
|
||||||
|url | 是 | 上传接口地址|
|
|
||||||
|name| 否 |上传接口文件key,默认为file|
|
|
||||||
|header| 否 |上传接口请求头|
|
|
||||||
|formData| 否 |上传接口额外参数|
|
|
||||||
|
|
||||||
## ref调用
|
|
||||||
|作用 | 方法名| 传入参数| 说明|
|
|
||||||
|---- | --------- | -------- | :--: |
|
|
||||||
|显示控件| show|-| 控件显示状态下可触发点击|
|
|
||||||
|隐藏控件| hide|-| 控件隐藏状态下不触发点击|
|
|
||||||
|动态设置文件列表| setFiles|[Array,Map] files| 传入格式请与组件选择返回格式保持一致,且name为必须属性,可查看下方演示|
|
|
||||||
|动态更新参数| setData|[String] name,[any] value| name支持a.b 和 a[b],可查看下方演示|
|
|
||||||
|移除选择的文件| clear|[String] name| 不传参数清空所有文件,传入文件name时删除该name的文件|
|
|
||||||
|手动上传| upload|[String] name| 不传参数默认依次上传所有type=waiting的文件,传入文件name时不关心type是否为waiting,单独上传指定name的文件|
|
|
||||||
|
|
||||||
## progress返回对象字段说明
|
|
||||||
|字段 | 说明|
|
|
||||||
|---- | :--: |
|
|
||||||
|file | 文件对象|
|
|
||||||
|name |文件名称|
|
|
||||||
|size |文件大小|
|
|
||||||
|type |文件上传状态:waiting(等待上传)、loading(上传中)、success(成功) 、fail(失败)|
|
|
||||||
|responseText|上传成功后服务端返回数据(仅type为success时存在)|
|
|
||||||
|
|
||||||
## 以下演示为vue窗口使用方式,nvue使用区别是必须传入控件绝对位置如top,bottom,left,right,且position只能为absolute,如不清楚可点击右侧导入示例项目有详细演示代码。
|
|
||||||
|
|
||||||
### vue:
|
|
||||||
``` javascript
|
|
||||||
<lsj-upload
|
|
||||||
ref="lsjUpload"
|
|
||||||
childId="upload1"
|
|
||||||
:width="width"
|
|
||||||
:height="height"
|
|
||||||
:option="option"
|
|
||||||
:size="size"
|
|
||||||
:formats="formats"
|
|
||||||
:debug="debug"
|
|
||||||
:instantly="instantly"
|
|
||||||
@progress="onprogress"
|
|
||||||
@change="onChange">
|
|
||||||
<view class="btn" :style="{width: width,height: height}">选择附件</view>
|
|
||||||
</lsj-upload>
|
|
||||||
|
|
||||||
|
|
||||||
<view class="padding">
|
|
||||||
|
|
||||||
<view>已选择文件列表:</view>
|
|
||||||
|
|
||||||
<!-- #ifndef MP-WEIXIN -->
|
|
||||||
<view v-for="(item,index) in files.values()" :key="index">
|
|
||||||
<image style="width: 100rpx;height: 100rpx;" :src="item.path" mode="widthFix"></image>
|
|
||||||
<text>{{item.path}}</text>
|
|
||||||
<text>{{item.name}}</text>
|
|
||||||
<text style="margin-left: 10rpx;">大小:{{item.size}}</text>
|
|
||||||
<text style="margin-left: 10rpx;">状态:{{item.type}}</text>
|
|
||||||
<text style="margin-left: 10rpx;">进度:{{item.progress}}</text>
|
|
||||||
<!-- <text style="margin-left: 10rpx;" v-if="item.responseText">服务端返回演示:{{item.responseText.code}}</text> -->
|
|
||||||
<text @click="clear(item.name)" style="margin-left: 10rpx;padding: 0 10rpx;border: 1rpx solid #007AFF;">删除</text>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<view v-for="(item,index) in wxFiles" :key="index">
|
|
||||||
<text>{{item.name}}</text>
|
|
||||||
<text style="margin-left: 10rpx;">大小:{{item.size}}</text>
|
|
||||||
<text style="margin-left: 10rpx;">状态:{{item.type}}</text>
|
|
||||||
<text style="margin-left: 10rpx;">进度:{{item.progress}}</text>
|
|
||||||
<view>
|
|
||||||
<button>删除</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
</view>
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
* 函数说明
|
|
||||||
|
|
||||||
|
|
||||||
``` javascript
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// 上传接口参数
|
|
||||||
option: {
|
|
||||||
// 上传服务器地址,此地址需要替换为你的接口地址
|
|
||||||
url: 'http://hlapi.j56.com/dropbox/document/upload',
|
|
||||||
// 上传附件的key
|
|
||||||
name: 'file',
|
|
||||||
// 根据你接口需求自定义请求头
|
|
||||||
header: {
|
|
||||||
'Authorization': 'bearer eyJhbGciOiJSUzI1NiIsI',
|
|
||||||
'uid': '27682',
|
|
||||||
'client': 'app',
|
|
||||||
'accountid': 'DP',
|
|
||||||
},
|
|
||||||
// 根据你接口需求自定义body参数
|
|
||||||
formData: {
|
|
||||||
// 'orderId': 1000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 选择文件后是否立即自动上传,true=选择后立即上传
|
|
||||||
instantly: false,
|
|
||||||
// 必传宽高且宽高应与slot宽高保持一致
|
|
||||||
width: '180rpx',
|
|
||||||
height: '180rpx',
|
|
||||||
// 限制允许选择的格式,空串=不限制,默认为空
|
|
||||||
formats: 'png,jpg,mp4',
|
|
||||||
// 文件上传大小限制
|
|
||||||
size: 10,
|
|
||||||
// 文件回显列表
|
|
||||||
files: new Map(),
|
|
||||||
// 微信小程序Map对象for循环不显示,所以转成普通数组,不要问为什么,我也不知道
|
|
||||||
wxFiles: [],
|
|
||||||
// 是否打印日志
|
|
||||||
debug: true,
|
|
||||||
|
|
||||||
|
|
||||||
// 演示用
|
|
||||||
tabIndex: 0,
|
|
||||||
list:[],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onReady() {
|
|
||||||
setTimeout(()=>{
|
|
||||||
console.log('----演示动态更新参数-----');
|
|
||||||
this.$refs.lsjUpload.setData('formData.orderId','动态设置的参数');
|
|
||||||
|
|
||||||
console.log('以下注释内容为-动态更新参数更多演示,放开后可查看演示效果');
|
|
||||||
// 修改option对象的name属性
|
|
||||||
// this.$refs.lsjUpload.setData('name','myFile');
|
|
||||||
|
|
||||||
// 修改option对象的formData内的属性
|
|
||||||
// this.$refs.lsjUpload.setData('formData.appid','1111');
|
|
||||||
|
|
||||||
// 替换option对象的formData
|
|
||||||
// this.$refs.lsjUpload.setData('formData',{appid:'222'});
|
|
||||||
|
|
||||||
// option对象的formData新增属性
|
|
||||||
// this.$refs.lsjUpload.setData('formData.newkey','新插入到formData的属性');
|
|
||||||
|
|
||||||
// ---------演示初始化值,用于已提交后再次编辑时需带入已上传文件-------
|
|
||||||
// 方式1=传入数组
|
|
||||||
let files1 = [{
|
|
||||||
name: '1.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '2.png',
|
|
||||||
}];
|
|
||||||
|
|
||||||
// 方式2=传入Map对象
|
|
||||||
let files2 = new Map();
|
|
||||||
files2.set('1.png',{name: '1.png'})
|
|
||||||
|
|
||||||
// 设置初始files列表
|
|
||||||
this.$refs.lsjUpload.setFiles(files1);
|
|
||||||
|
|
||||||
},2000)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 某文件上传结束回调(成功失败都回调)
|
|
||||||
onuploadEnd(item) {
|
|
||||||
console.log(`${item.name}已上传结束,上传状态=${item.type}`);
|
|
||||||
|
|
||||||
// 更新当前状态变化的文件
|
|
||||||
this.files.set(item.name,item);
|
|
||||||
|
|
||||||
// 演示上传完成后取服务端数据
|
|
||||||
if (item['responseText']) {
|
|
||||||
console.log('演示服务器返回的字符串JSON转对象');
|
|
||||||
this.files.get(item.name).responseText = JSON.parse(item.responseText);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 微信小程序Map对象for循环不显示,所以转成普通数组,不要问为什么,我也不知道
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
this.wxFiles = [...this.files.values()];
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// 强制更新视图
|
|
||||||
this.$forceUpdate();
|
|
||||||
|
|
||||||
|
|
||||||
// ---可删除--演示判断是否所有文件均已上传成功
|
|
||||||
let isAll = [...this.files.values()].find(item=>item.type!=='success');
|
|
||||||
if (!isAll) {
|
|
||||||
console.log('已全部上传完毕');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log(isAll.name+'待上传');
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
// 上传进度回调
|
|
||||||
onprogress(item) {
|
|
||||||
// 更新当前状态变化的文件
|
|
||||||
this.files.set(item.name,item);
|
|
||||||
|
|
||||||
console.log('打印对象',JSON.stringify(this.files.get(item.name)));
|
|
||||||
// 微信小程序Map对象for循环不显示,所以转成普通数组,不要问为什么,我也不知道
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
this.wxFiles = [...this.files.values()];
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// 强制更新视图
|
|
||||||
this.$forceUpdate();
|
|
||||||
|
|
||||||
},
|
|
||||||
// 文件选择回调
|
|
||||||
onChange(files) {
|
|
||||||
// 更新选择的文件
|
|
||||||
this.files = files;
|
|
||||||
// 强制更新视图
|
|
||||||
this.$forceUpdate();
|
|
||||||
|
|
||||||
// 微信小程序Map对象for循环不显示,所以转成普通数组,不要问为什么,我也不知道
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
this.wxFiles = [...this.files.values()];
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
// 手动上传
|
|
||||||
upload() {
|
|
||||||
// name=指定文件名,不指定则上传所有type等于waiting和fail的文件
|
|
||||||
this.$refs.lsjUpload.upload();
|
|
||||||
},
|
|
||||||
// 移除某个文件
|
|
||||||
clear(name) {
|
|
||||||
// name=指定文件名,不传name默认移除所有文件
|
|
||||||
this.$refs.lsjUpload.clear(name);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 以下为演示
|
|
||||||
*/
|
|
||||||
// DOM重排演示,重排后组件内部updated默认会触发show方法,若特殊情况未能触发updated也可以手动调用一次show()
|
|
||||||
// 什么是DOM重排?自行百度去~
|
|
||||||
add() {
|
|
||||||
this.list.push('DOM重排测试');
|
|
||||||
},
|
|
||||||
// 切换视图演示,APP端因为是webview,层级比view高,
|
|
||||||
// 此时若不希望点击触发选择文件,需要手动调用hide()
|
|
||||||
// 手动调用hide后,需要调用show()才能恢复触发面
|
|
||||||
onTab(tabIndex) {
|
|
||||||
this.tabIndex = tabIndex;
|
|
||||||
|
|
||||||
if (tabIndex == 0 ) {
|
|
||||||
this.$nextTick(()=>{
|
|
||||||
this.$refs.lsjUpload.show();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.$refs.lsjUpload.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 打开nvue窗口
|
|
||||||
open() {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/nvue-demo/nvue-demo'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## 温馨提示
|
|
||||||
|
|
||||||
* 文件上传
|
|
||||||
0. 如说明表达还不够清楚,不清楚怎么使用可导入完整示例项目运行体验和查看
|
|
||||||
1. APP端请优先联调Android,上传成功后再运行iOS端,如iOS返回status=0则需要后端开启允许跨域;
|
|
||||||
2. header的Content-Type类型需要与服务端要求一致,否则收不到附件(服务端若没有明文规定则可不写,使用默认匹配)
|
|
||||||
3. 服务端不清楚怎么配置跨域可加群咨询,具体百度~
|
|
||||||
4. 欢迎加入QQ讨论群:701468256(已满)
|
|
||||||
5. 欢迎加入QQ讨论群:469580165
|
|
||||||
6. 欢迎加入QQ讨论群:469580165
|
|
||||||
7. 若能帮到你还请点亮5颗小星星以作鼓励哈~
|
|
||||||
8. 若能帮到你还请点亮5颗小星星以作鼓励哈~
|
|
||||||
9. 若能帮到你还请点亮5颗小星星以作鼓励哈~
|
|
||||||
@@ -0,0 +1,320 @@
|
|||||||
|
## 2.5.0-20230101(2023-01-01)
|
||||||
|
- 秋云图表组件 修改条件编译顺序,确保uniapp的cli方式的项目依赖不完整时可以正常显示
|
||||||
|
- 秋云图表组件 恢复props属性directory的使用,以修复vue3项目中,开启echarts后,echarts目录识别错误的bug
|
||||||
|
- uCharts.js 修复区域图、混合图只有一个数据时图表显示不正确的bug
|
||||||
|
- uCharts.js 修复折线图、区域图中时间轴类别图表tooltip指示点显示不正确的bug
|
||||||
|
- uCharts.js 修复x轴使用labelCount时,并且boundaryGap = 'justify' 并且关闭Y轴显示的时候,最后一个坐标值不显示的bug
|
||||||
|
- uCharts.js 修复折线图只有一组数据时 ios16 渲染颜色不正确的bug
|
||||||
|
- uCharts.js 修复玫瑰图半径显示不正确的bug
|
||||||
|
- uCharts.js 柱状图、山峰图增加正负图功能,y轴网格如果需要显示0轴则由 min max 及 splitNumber 确定,后续版本优化自动显示0轴
|
||||||
|
- uCharts.js 柱状图column增加 opts.extra.column.labelPosition,数据标签位置,有效值为 outside外部, insideTop内顶部, center内中间, bottom内底部
|
||||||
|
- uCharts.js 雷达图radar增加 opts.extra.radar.labelShow,否显示各项标识文案是,默认true
|
||||||
|
- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.boxPadding,提示窗边框填充距离,默认3px
|
||||||
|
- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.fontSize,提示窗字体大小配置,默认13px
|
||||||
|
- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.lineHeight,提示窗文字行高,默认20px
|
||||||
|
- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.legendShow,是否显示左侧图例,默认true
|
||||||
|
- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.legendShape,图例形状,图例标识样式,有效值为 auto自动跟随图例, diamond◆, circle●, triangle▲, square■, rect▬, line-
|
||||||
|
- uCharts.js 标记线markLine增加 opts.extra.markLine.labelFontSize,字体大小配置,默认13px
|
||||||
|
- uCharts.js 标记线markLine增加 opts.extra.markLine.labelPadding,标签边框内填充距离,默认6px
|
||||||
|
- uCharts.js 折线图line增加 opts.extra.line.linearType,渐变色类型,可选值 none关闭渐变色,custom 自定义渐变色。使用自定义渐变色时请赋值serie.linearColor作为颜色值
|
||||||
|
- uCharts.js 折线图line增加 serie.linearColor,渐变色数组,格式为2维数组[起始位置,颜色值],例如[[0,'#0EE2F8'],[0.3,'#2BDCA8'],[0.6,'#1890FF'],[1,'#9A60B4']]
|
||||||
|
- uCharts.js 折线图line增加 opts.extra.line.onShadow,是否开启折线阴影,开启后请赋值serie.setShadow阴影设置
|
||||||
|
- uCharts.js 折线图line增加 serie.setShadow,阴影配置,格式为4位数组:[offsetX,offsetY,blur,color]
|
||||||
|
- uCharts.js 折线图line增加 opts.extra.line.animation,动画效果方向,可选值为vertical 垂直动画效果,horizontal 水平动画效果
|
||||||
|
- uCharts.js X轴xAxis增加 opts.xAxis.lineHeight,X轴字体行高,默认20px
|
||||||
|
- uCharts.js X轴xAxis增加 opts.xAxis.marginTop,X轴文字距离轴线的距离,默认0px
|
||||||
|
- uCharts.js X轴xAxis增加 opts.xAxis.title,当前X轴标题
|
||||||
|
- uCharts.js X轴xAxis增加 opts.xAxis.titleFontSize,标题字体大小,默认13px
|
||||||
|
- uCharts.js X轴xAxis增加 opts.xAxis.titleOffsetY,标题纵向偏移距离,负数为向上偏移,正数向下偏移
|
||||||
|
- uCharts.js X轴xAxis增加 opts.xAxis.titleOffsetX,标题横向偏移距离,负数为向左偏移,正数向右偏移
|
||||||
|
- uCharts.js X轴xAxis增加 opts.xAxis.titleFontColor,标题字体颜色,默认#666666
|
||||||
|
|
||||||
|
## 报错TypeError: Cannot read properties of undefined (reading 'length')
|
||||||
|
- 如果是uni-modules版本组件,请先登录HBuilderX账号;
|
||||||
|
- 在HBuilderX中的manifest.json,点击重新获取uniapp的appid,或者删除appid重新粘贴,重新运行;
|
||||||
|
- 如果是cli项目请使用码云上的非uniCloud版本组件;
|
||||||
|
- 或者添加uniCloud的依赖;
|
||||||
|
- 或者使用原生uCharts;
|
||||||
|
## 2.4.5-20221130(2022-11-30)
|
||||||
|
- uCharts.js 优化tooltip当文字很多变为左侧显示时,如果画布仍显显示不下,提示框错位置变为以左侧0位置起画
|
||||||
|
- uCharts.js 折线图修复特殊情况下只有单点数据,并改变线宽后点变为圆形的bug
|
||||||
|
- uCharts.js 修复Y轴disabled启用后无效并报错的bug
|
||||||
|
- uCharts.js 修复仪表盘起始结束角度特殊情况下显示不正确的bug
|
||||||
|
- uCharts.js 雷达图新增参数 opts.extra.radar.radius , 自定义雷达图半径
|
||||||
|
- uCharts.js 折线图、区域图增加tooltip指示点,opts.extra.line.activeType/opts.extra.area.activeType,可选值"none"不启用激活指示点,"hollow"空心点模式,"solid"实心点模式
|
||||||
|
## 2.4.4-20221102(2022-11-02)
|
||||||
|
- 秋云图表组件 修复使用echarts时reload、reshow无法调用重新渲染的bug,[详见码云PR](https://gitee.com/uCharts/uCharts/pulls/40)
|
||||||
|
- 秋云图表组件 修复使用echarts时,初始化时宽高不正确的bug,[详见码云PR](https://gitee.com/uCharts/uCharts/pulls/42)
|
||||||
|
- 秋云图表组件 修复uniapp的h5使用history模式时,无法加载echarts的bug
|
||||||
|
- 秋云图表组件 小程序端@complete、@scrollLeft、@scrollRight、@getTouchStart、@getTouchMove、@getTouchEnd事件增加opts参数传出,方便一些特殊需求的交互获取数据。
|
||||||
|
|
||||||
|
- uCharts.js 修复calTooltipYAxisData方法内formatter格式化方法未与y轴方法同步的问题,[详见码云PR](https://gitee.com/uCharts/uCharts/pulls/43)
|
||||||
|
- uCharts.js 地图新增参数opts.series[i].fillOpacity,以透明度方式来设置颜色过度效果,[详见码云PR](https://gitee.com/uCharts/uCharts/pulls/38)
|
||||||
|
- uCharts.js 地图新增参数opts.extra.map.active,是否启用点击激活变色
|
||||||
|
- uCharts.js 地图新增参数opts.extra.map.activeTextColor,是否启用点击激活变色
|
||||||
|
- uCharts.js 地图新增渲染完成事件renderComplete
|
||||||
|
- uCharts.js 漏斗图修复当部分数据相同时tooltip提示窗点击错误的bug
|
||||||
|
- uCharts.js 漏斗图新增参数series.data[i].centerText 居中标签文案
|
||||||
|
- uCharts.js 漏斗图新增参数series.data[i].centerTextSize 居中标签文案字体大小,默认opts.fontSize
|
||||||
|
- uCharts.js 漏斗图新增参数series.data[i].centerTextColor 居中标签文案字体颜色,默认#FFFFFF
|
||||||
|
- uCharts.js 漏斗图新增参数opts.extra.funnel.minSize 最小值的最小宽度,默认0
|
||||||
|
- uCharts.js 进度条新增参数opts.extra.arcbar.direction,动画方向,可选值为cw顺时针、ccw逆时针
|
||||||
|
- uCharts.js 混合图新增参数opts.extra.mix.line.width,折线的宽度,默认2
|
||||||
|
- uCharts.js 修复tooltip开启horizentalLine水平横线标注时,图表显示错位的bug
|
||||||
|
- uCharts.js 优化tooltip当文字很多变为左侧显示时,如果画布仍显显示不下,提示框错位置变为以左侧0位置起画
|
||||||
|
- uCharts.js 修复开启滚动条后X轴文字超出绘图区域后的隐藏逻辑
|
||||||
|
- uCharts.js 柱状图、条状图修复堆叠模式不能通过{value,color}赋值单个柱子颜色的问题
|
||||||
|
- uCharts.js 气泡图修复不识别series.textSize和series.textColor的bug
|
||||||
|
|
||||||
|
## 报错TypeError: Cannot read properties of undefined (reading 'length')
|
||||||
|
1. 如果是uni-modules版本组件,请先登录HBuilderX账号;
|
||||||
|
2. 在HBuilderX中的manifest.json,点击重新获取uniapp的appid,或者删除appid重新粘贴,重新运行;
|
||||||
|
3. 如果是cli项目请使用码云上的非uniCloud版本组件;
|
||||||
|
4. 或者添加uniCloud的依赖;
|
||||||
|
5. 或者使用原生uCharts;
|
||||||
|
## 2.4.3-20220505(2022-05-05)
|
||||||
|
- 秋云图表组件 修复开启canvas2d后将series赋值为空数组显示加载图标时,再次赋值后画布闪动的bug
|
||||||
|
- 秋云图表组件 修复升级hbx最新版后ECharts的highlight方法报错的bug
|
||||||
|
- uCharts.js 雷达图新增参数opts.extra.radar.gridEval,数据点位网格抽希,默认1
|
||||||
|
- uCharts.js 雷达图新增参数opts.extra.radar.axisLabel, 是否显示刻度点值,默认false
|
||||||
|
- uCharts.js 雷达图新增参数opts.extra.radar.axisLabelTofix,刻度点值小数位数,默认0
|
||||||
|
- uCharts.js 雷达图新增参数opts.extra.radar.labelPointShow,是否显示末端刻度圆点,默认false
|
||||||
|
- uCharts.js 雷达图新增参数opts.extra.radar.labelPointRadius,刻度圆点的半径,默认3
|
||||||
|
- uCharts.js 雷达图新增参数opts.extra.radar.labelPointColor,刻度圆点的颜色,默认#cccccc
|
||||||
|
- uCharts.js 雷达图新增参数opts.extra.radar.linearType,渐变色类型,可选值"none"关闭渐变,"custom"开启渐变
|
||||||
|
- uCharts.js 雷达图新增参数opts.extra.radar.customColor,自定义渐变颜色,数组类型对应series的数组长度以匹配不同series颜色的不同配色方案,例如["#FA7D8D", "#EB88E2"]
|
||||||
|
- uCharts.js 雷达图优化支持series.textColor、series.textSize属性
|
||||||
|
- uCharts.js 柱状图中温度计式图标,优化支持全圆角类型,修复边框有缝隙的bug,详见官网【演示】中的温度计图表
|
||||||
|
- uCharts.js 柱状图新增参数opts.extra.column.activeWidth,当前点击柱状图的背景宽度,默认一个单元格单位
|
||||||
|
- uCharts.js 混合图增加opts.extra.mix.area.gradient 区域图是否开启渐变色
|
||||||
|
- uCharts.js 混合图增加opts.extra.mix.area.opacity 区域图透明度,默认0.2
|
||||||
|
- uCharts.js 饼图、圆环图、玫瑰图、漏斗图,增加opts.series[0].data[i].labelText,自定义标签文字,避免formatter格式化的繁琐,详见官网【演示】中的饼图
|
||||||
|
- uCharts.js 饼图、圆环图、玫瑰图、漏斗图,增加opts.series[0].data[i].labelShow,自定义是否显示某一个指示标签,避免因饼图类别太多导致标签重复或者居多导致图形变形的问题,详见官网【演示】中的饼图
|
||||||
|
- uCharts.js 增加opts.series[i].legendText/opts.series[0].data[i].legendText(与series.name同级)自定义图例显示文字的方法
|
||||||
|
- uCharts.js 优化X轴、Y轴formatter格式化方法增加形参,统一为fromatter:function(value,index,opts){}
|
||||||
|
- uCharts.js 修复横屏模式下无法使用双指缩放方法的bug
|
||||||
|
- uCharts.js 修复当只有一条数据或者多条数据值相等的时候Y轴自动计算的最大值错误的bug
|
||||||
|
- 【官网模板】增加外部自定义图例与图表交互的例子,[点击跳转](https://www.ucharts.cn/v2/#/layout/info?id=2)
|
||||||
|
|
||||||
|
## 注意:非unimodules 版本如因更新 hbx 至 3.4.7 导致报错如下,请到码云更新非 unimodules 版本组件,[点击跳转](https://gitee.com/uCharts/uCharts/tree/master/uni-app/uCharts-%E7%BB%84%E4%BB%B6)
|
||||||
|
> Error in callback for immediate watcher "uchartsOpts": "SyntaxError: Unexpected token u in JSON at position 0"
|
||||||
|
## 2.4.2-20220421(2022-04-21)
|
||||||
|
- 秋云图表组件 修复HBX升级3.4.6.20220420版本后echarts报错的问题
|
||||||
|
## 2.4.2-20220420(2022-04-20)
|
||||||
|
## 重要!此版本uCharts新增了很多功能,修复了诸多已知问题
|
||||||
|
- 秋云图表组件 新增onzoom开启双指缩放功能(仅uCharts),前提需要直角坐标系类图表类型,并且ontouch为true、opts.enableScroll为true,详见实例项目K线图
|
||||||
|
- 秋云图表组件 新增optsWatch是否监听opts变化,关闭optsWatch后,动态修改opts不会触发图表重绘
|
||||||
|
- 秋云图表组件 修复开启canvas2d功能后,动态更新数据后画布闪动的bug
|
||||||
|
- 秋云图表组件 去除directory属性,改为自动获取echarts.min.js路径(升级不受影响)
|
||||||
|
- 秋云图表组件 增加getImage()方法及@getImage事件,通过ref调用getImage()方法获,触发@getImage事件获取当前画布的base64图片文件流。
|
||||||
|
- 秋云图表组件 支付宝、字节跳动、飞书、快手小程序支持开启canvas2d同层渲染设置。
|
||||||
|
- 秋云图表组件 新增加【非uniCloud】版本组件,避免有些不需要uniCloud的使用组件发布至小程序需要提交隐私声明问题,请到码云[【非uniCloud版本】](https://gitee.com/uCharts/uCharts/tree/master/uni-app/uCharts-%E7%BB%84%E4%BB%B6),或npm[【非uniCloud版本】](https://www.npmjs.com/package/@qiun/uni-ucharts)下载使用。
|
||||||
|
- uCharts.js 新增dobuleZoom双指缩放功能
|
||||||
|
- uCharts.js 新增山峰图type="mount",数据格式为饼图类格式,不需要传入categories,具体详见新版官网在线演示
|
||||||
|
- uCharts.js 修复折线图当数据中存在null时tooltip报错的bug
|
||||||
|
- uCharts.js 修复饼图类当画布比较小时自动计算的半径是负数报错的bug
|
||||||
|
- uCharts.js 统一各图表类型的series.formatter格式化方法的形参为(val, index, series, opts),方便格式化时有更多参数可用
|
||||||
|
- uCharts.js 标记线功能增加labelText自定义显示文字,增加labelAlign标签显示位置(左侧或右侧),增加标签显示位置微调labelOffsetX、labelOffsetY
|
||||||
|
- uCharts.js 修复条状图当数值很小时开启圆角后样式错误的bug
|
||||||
|
- uCharts.js 修复X轴开启disabled后,X轴仍占用空间的bug
|
||||||
|
- uCharts.js 修复X轴开启滚动条并且开启rotateLabel后,X轴文字与滚动条重叠的bug
|
||||||
|
- uCharts.js 增加X轴rotateAngle文字旋转自定义角度,取值范围(-90至90)
|
||||||
|
- uCharts.js 修复地图文字标签层级显示不正确的bug
|
||||||
|
- uCharts.js 修复饼图、圆环图、玫瑰图当数据全部为0的时候不显示数据标签的bug
|
||||||
|
- uCharts.js 修复当opts.padding上边距为0时,Y轴顶部刻度标签位置不正确的bug
|
||||||
|
|
||||||
|
## 另外我们还开发了各大原生小程序组件,已发布至码云和npm
|
||||||
|
[https://gitee.com/uCharts/uCharts](https://gitee.com/uCharts/uCharts)
|
||||||
|
[https://www.npmjs.com/~qiun](https://www.npmjs.com/~qiun)
|
||||||
|
|
||||||
|
## 对于原生uCharts文档我们已上线新版官方网站,详情点击下面链接进入官网
|
||||||
|
[https://www.uCharts.cn/v2/](https://www.ucharts.cn/v2/)
|
||||||
|
## 2.3.7-20220122(2022-01-22)
|
||||||
|
## 重要!使用vue3编译,请使用cli模式并升级至最新依赖,HbuilderX编译需要使用3.3.8以上版本
|
||||||
|
- uCharts.js 修复uni-app平台组件模式使用vue3编译到小程序报错的bug。
|
||||||
|
## 2.3.7-20220118(2022-01-18)
|
||||||
|
## 注意,使用vue3的前提是需要3.3.8.20220114-alpha版本的HBuilder!
|
||||||
|
## 2.3.67-20220118(2022-01-18)
|
||||||
|
- 秋云图表组件 组件初步支持vue3,全端编译会有些问题,具体详见下面修改:
|
||||||
|
1. 小程序端运行时,在uni_modules文件夹的qiun-data-charts.js中搜索 new uni_modules_qiunDataCharts_js_sdk_uCharts_uCharts.uCharts,将.uCharts去掉。
|
||||||
|
2. 小程序端发行时,在uni_modules文件夹的qiun-data-charts.js中搜索 new e.uCharts,将.uCharts去掉,变为 new e。
|
||||||
|
3. 如果觉得上述步骤比较麻烦,如果您的项目只编译到小程序端,可以修改u-charts.js最后一行导出方式,将 export default uCharts;变更为 export default { uCharts: uCharts }; 这样变更后,H5和App端的renderjs会有问题,请开发者自行选择。(此问题非组件问题,请等待DC官方修复Vue3的小程序端)
|
||||||
|
## 2.3.6-20220111(2022-01-11)
|
||||||
|
- 秋云图表组件 修改组件 props 属性中的 background 默认值为 rgba(0,0,0,0)
|
||||||
|
## 2.3.6-20211201(2021-12-01)
|
||||||
|
- uCharts.js 修复bar条状图开启圆角模式时,值很小时圆角渲染错误的bug
|
||||||
|
## 2.3.5-20211014(2021-10-15)
|
||||||
|
- uCharts.js 增加vue3的编译支持(仅原生uCharts,qiun-data-charts组件后续会支持,请关注更新)
|
||||||
|
## 2.3.4-20211012(2021-10-12)
|
||||||
|
- 秋云图表组件 修复 mac os x 系统 mouseover 事件丢失的 bug
|
||||||
|
## 2.3.3-20210706(2021-07-06)
|
||||||
|
- uCharts.js 增加雷达图开启数据点值(opts.dataLabel)的显示
|
||||||
|
## 2.3.2-20210627(2021-06-27)
|
||||||
|
- 秋云图表组件 修复tooltipCustom个别情况下传值不正确报错TypeError: Cannot read property 'name' of undefined的bug
|
||||||
|
## 2.3.1-20210616(2021-06-16)
|
||||||
|
- uCharts.js 修复圆角柱状图使用4角圆角时,当数值过大时不正确的bug
|
||||||
|
## 2.3.0-20210612(2021-06-12)
|
||||||
|
- uCharts.js 【重要】uCharts增加nvue兼容,可在nvue项目中使用gcanvas组件渲染uCharts,[详见码云uCharts-demo-nvue](https://gitee.com/uCharts/uCharts)
|
||||||
|
- 秋云图表组件 增加tapLegend属性,是否开启图例点击交互事件
|
||||||
|
- 秋云图表组件 getIndex事件中增加返回uCharts实例中的opts参数,以便在页面中调用参数
|
||||||
|
- 示例项目 pages/other/other.vue增加app端自定义tooltip的方法,详见showOptsTooltip方法
|
||||||
|
## 2.2.1-20210603(2021-06-03)
|
||||||
|
- uCharts.js 修复饼图、圆环图、玫瑰图,当起始角度不为0时,tooltip位置不准确的bug
|
||||||
|
- uCharts.js 增加温度计式柱状图开启顶部半圆形的配置
|
||||||
|
## 2.2.0-20210529(2021-05-29)
|
||||||
|
- uCharts.js 增加条状图type="bar"
|
||||||
|
- 示例项目 pages/ucharts/ucharts.vue增加条状图的demo
|
||||||
|
## 2.1.7-20210524(2021-05-24)
|
||||||
|
- uCharts.js 修复大数据量模式下曲线图不平滑的bug
|
||||||
|
## 2.1.6-20210523(2021-05-23)
|
||||||
|
- 秋云图表组件 修复小程序端开启滚动条更新数据后滚动条位置不符合预期的bug
|
||||||
|
## 2.1.5-2021051702(2021-05-17)
|
||||||
|
- uCharts.js 修复自定义Y轴min和max值为0时不能正确显示的bug
|
||||||
|
## 2.1.5-20210517(2021-05-17)
|
||||||
|
- uCharts.js 修复Y轴自定义min和max时,未按指定的最大值最小值显示坐标轴刻度的bug
|
||||||
|
## 2.1.4-20210516(2021-05-16)
|
||||||
|
- 秋云图表组件 优化onWindowResize防抖方法
|
||||||
|
- 秋云图表组件 修复APP端uCharts更新数据时,清空series显示loading图标后再显示图表,图表抖动的bug
|
||||||
|
- uCharts.js 修复开启canvas2d后,x轴、y轴、series自定义字体大小未按比例缩放的bug
|
||||||
|
- 示例项目 修复format-e.vue拼写错误导致app端使用uCharts渲染图表
|
||||||
|
## 2.1.3-20210513(2021-05-13)
|
||||||
|
- 秋云图表组件 修改uCharts变更chartData数据为updateData方法,支持带滚动条的数据动态打点
|
||||||
|
- 秋云图表组件 增加onWindowResize防抖方法 fix by ど誓言,如尘般染指流年づ
|
||||||
|
- 秋云图表组件 H5或者APP变更chartData数据显示loading图表时,原数据闪现的bug
|
||||||
|
- 秋云图表组件 props增加errorReload禁用错误点击重新加载的方法
|
||||||
|
- uCharts.js 增加tooltip显示category(x轴对应点位)标题的功能,opts.extra.tooltip.showCategory,默认为false
|
||||||
|
- uCharts.js 修复mix混合图只有柱状图时,tooltip的分割线显示位置不正确的bug
|
||||||
|
- uCharts.js 修复开启滚动条,图表在拖动中动态打点,滚动条位置不正确的bug
|
||||||
|
- uCharts.js 修复饼图类数据格式为echarts数据格式,series为空数组报错的bug
|
||||||
|
- 示例项目 修改uCharts.js更新到v2.1.2版本后,@getIndex方法获取索引值变更为e.currentIndex.index
|
||||||
|
- 示例项目 pages/updata/updata.vue增加滚动条拖动更新(数据动态打点)的demo
|
||||||
|
- 示例项目 pages/other/other.vue增加errorReload禁用错误点击重新加载的demo
|
||||||
|
## 2.1.2-20210509(2021-05-09)
|
||||||
|
秋云图表组件 修复APP端初始化时就传入chartData或lacaldata不显示图表的bug
|
||||||
|
## 2.1.1-20210509(2021-05-09)
|
||||||
|
- 秋云图表组件 变更ECharts的eopts配置在renderjs内执行,支持在config-echarts.js配置文件内写function配置。
|
||||||
|
- 秋云图表组件 修复APP端报错Prop being mutated: "onmouse"错误的bug。
|
||||||
|
- 秋云图表组件 修复APP端报错Error: Not Found:Page[6][-1,27] at view.umd.min.js:1的bug。
|
||||||
|
## 2.1.0-20210507(2021-05-07)
|
||||||
|
- 秋云图表组件 修复初始化时就有数据或者数据更新的时候loading加载动画闪动的bug
|
||||||
|
- uCharts.js 修复x轴format方法categories为字符串类型时返回NaN的bug
|
||||||
|
- uCharts.js 修复series.textColor、legend.fontColor未执行全局默认颜色的bug
|
||||||
|
## 2.1.0-20210506(2021-05-06)
|
||||||
|
- 秋云图表组件 修复极个别情况下报错item.properties undefined的bug
|
||||||
|
- 秋云图表组件 修复极个别情况下关闭加载动画reshow不起作用,无法显示图表的bug
|
||||||
|
- 示例项目 pages/ucharts/ucharts.vue 增加时间轴折线图(type="tline")、时间轴区域图(type="tarea")、散点图(type="scatter")、气泡图demo(type="bubble")、倒三角形漏斗图(opts.extra.funnel.type="triangle")、金字塔形漏斗图(opts.extra.funnel.type="pyramid")
|
||||||
|
- 示例项目 pages/format-u/format-u.vue 增加X轴format格式化示例
|
||||||
|
- uCharts.js 升级至v2.1.0版本
|
||||||
|
- uCharts.js 修复 玫瑰图面积模式点击tooltip位置不正确的bug
|
||||||
|
- uCharts.js 修复 玫瑰图点击图例,只剩一个类别显示空白的bug
|
||||||
|
- uCharts.js 修复 饼图类图点击图例,其他图表tooltip位置某些情况下不准的bug
|
||||||
|
- uCharts.js 修复 x轴为矢量轴(时间轴)情况下,点击tooltip位置不正确的bug
|
||||||
|
- uCharts.js 修复 词云图获取点击索引偶尔不准的bug
|
||||||
|
- uCharts.js 增加 直角坐标系图表X轴format格式化方法(原生uCharts.js用法请使用formatter)
|
||||||
|
- uCharts.js 增加 漏斗图扩展配置,倒三角形(opts.extra.funnel.type="triangle"),金字塔形(opts.extra.funnel.type="pyramid")
|
||||||
|
- uCharts.js 增加 散点图(opts.type="scatter")、气泡图(opts.type="bubble")
|
||||||
|
- 后期计划 完善散点图、气泡图,增加markPoints标记点,增加横向条状图。
|
||||||
|
## 2.0.0-20210502(2021-05-02)
|
||||||
|
- uCharts.js 修复词云图获取点击索引不正确的bug
|
||||||
|
## 2.0.0-20210501(2021-05-01)
|
||||||
|
- 秋云图表组件 修复QQ小程序、百度小程序在关闭动画效果情况下,v-for循环使用图表,显示不正确的bug
|
||||||
|
## 2.0.0-20210426(2021-04-26)
|
||||||
|
- 秋云图表组件 修复QQ小程序不支持canvas2d的bug
|
||||||
|
- 秋云图表组件 修复钉钉小程序某些情况点击坐标计算错误的bug
|
||||||
|
- uCharts.js 增加 extra.column.categoryGap 参数,柱状图类每个category点位(X轴点)柱子组之间的间距
|
||||||
|
- uCharts.js 增加 yAxis.data[i].titleOffsetY 参数,标题纵向偏移距离,负数为向上偏移,正数向下偏移
|
||||||
|
- uCharts.js 增加 yAxis.data[i].titleOffsetX 参数,标题横向偏移距离,负数为向左偏移,正数向右偏移
|
||||||
|
- uCharts.js 增加 extra.gauge.labelOffset 参数,仪表盘标签文字径向便宜距离,默认13px
|
||||||
|
## 2.0.0-20210422-2(2021-04-22)
|
||||||
|
秋云图表组件 修复 formatterAssign 未判断 args[key] == null 的情况导致栈溢出的 bug
|
||||||
|
## 2.0.0-20210422(2021-04-22)
|
||||||
|
- 秋云图表组件 修复H5、APP、支付宝小程序、微信小程序canvas2d模式下横屏模式的bug
|
||||||
|
## 2.0.0-20210421(2021-04-21)
|
||||||
|
- uCharts.js 修复多行图例的情况下,图例在上方或者下方时,图例float为左侧或者右侧时,第二行及以后的图例对齐方式不正确的bug
|
||||||
|
## 2.0.0-20210420(2021-04-20)
|
||||||
|
- 秋云图表组件 修复微信小程序开启canvas2d模式后,windows版微信小程序不支持canvas2d模式的bug
|
||||||
|
- 秋云图表组件 修改非uni_modules版本为v2.0版本qiun-data-charts组件
|
||||||
|
## 2.0.0-20210419(2021-04-19)
|
||||||
|
## v1.0版本已停更,建议转uni_modules版本组件方式调用,点击右侧绿色【使用HBuilderX导入插件】即可使用,示例项目请点击右侧蓝色按钮【使用HBuilderX导入示例项目】。
|
||||||
|
## 初次使用如果提示未注册<qiun-data-charts>组件,请重启HBuilderX,如仍不好用,请重启电脑;
|
||||||
|
## 如果是cli项目,请尝试清理node_modules,重新install,还不行就删除项目,再重新install。
|
||||||
|
## 此问题已于DCloud官方确认,HBuilderX下个版本会修复。
|
||||||
|
## 其他图表不显示问题详见[常见问题选项卡](https://demo.ucharts.cn)
|
||||||
|
## <font color=#FF0000> 新手请先完整阅读帮助文档及常见问题3遍,右侧蓝色按钮示例项目请看2遍! </font>
|
||||||
|
## [DEMO演示及在线生成工具(v2.0文档)https://demo.ucharts.cn](https://demo.ucharts.cn)
|
||||||
|
## [图表组件在项目中的应用参见 UReport数据报表](https://ext.dcloud.net.cn/plugin?id=4651)
|
||||||
|
- uCharts.js 修复混合图中柱状图单独设置颜色不生效的bug
|
||||||
|
- uCharts.js 修复多Y轴单独设置fontSize时,开启canvas2d后,未对应放大字体的bug
|
||||||
|
## 2.0.0-20210418(2021-04-18)
|
||||||
|
- 秋云图表组件 增加directory配置,修复H5端history模式下如果发布到二级目录无法正确加载echarts.min.js的bug
|
||||||
|
## 2.0.0-20210416(2021-04-16)
|
||||||
|
## v1.0版本已停更,建议转uni_modules版本组件方式调用,点击右侧绿色【使用HBuilderX导入插件】即可使用,示例项目请点击右侧蓝色按钮【使用HBuilderX导入示例项目】。
|
||||||
|
## 初次使用如果提示未注册<qiun-data-charts>组件,请重启HBuilderX,如仍不好用,请重启电脑;
|
||||||
|
## 如果是cli项目,请尝试清理node_modules,重新install,还不行就删除项目,再重新install。
|
||||||
|
## 此问题已于DCloud官方确认,HBuilderX下个版本会修复。
|
||||||
|
## 其他图表不显示问题详见[常见问题选项卡](https://demo.ucharts.cn)
|
||||||
|
## <font color=#FF0000> 新手请先完整阅读帮助文档及常见问题3遍,右侧蓝色按钮示例项目请看2遍! </font>
|
||||||
|
## [DEMO演示及在线生成工具(v2.0文档)https://demo.ucharts.cn](https://demo.ucharts.cn)
|
||||||
|
## [图表组件在项目中的应用参见 UReport数据报表](https://ext.dcloud.net.cn/plugin?id=4651)
|
||||||
|
- 秋云图表组件 修复APP端某些情况下报错`Not Found Page`的bug,fix by 高级bug开发技术员
|
||||||
|
- 示例项目 修复APP端v-for循环某些情况下报错`Not Found Page`的bug,fix by 高级bug开发技术员
|
||||||
|
- uCharts.js 修复非直角坐标系tooltip提示窗右侧超出未变换方向显示的bug
|
||||||
|
## 2.0.0-20210415(2021-04-15)
|
||||||
|
- 秋云图表组件 修复H5端发布到二级目录下echarts无法加载的bug
|
||||||
|
- 秋云图表组件 修复某些情况下echarts.off('finished')移除监听事件报错的bug
|
||||||
|
## 2.0.0-20210414(2021-04-14)
|
||||||
|
## v1.0版本已停更,建议转uni_modules版本组件方式调用,点击右侧绿色【使用HBuilderX导入插件】即可使用,示例项目请点击右侧蓝色按钮【使用HBuilderX导入示例项目】。
|
||||||
|
## 初次使用如果提示未注册<qiun-data-charts>组件,请重启HBuilderX,如仍不好用,请重启电脑;
|
||||||
|
## 如果是cli项目,请尝试清理node_modules,重新install,还不行就删除项目,再重新install。
|
||||||
|
## 此问题已于DCloud官方确认,HBuilderX下个版本会修复。
|
||||||
|
## 其他图表不显示问题详见[常见问题选项卡](https://demo.ucharts.cn)
|
||||||
|
## <font color=#FF0000> 新手请先完整阅读帮助文档及常见问题3遍,右侧蓝色按钮示例项目请看2遍! </font>
|
||||||
|
## [DEMO演示及在线生成工具(v2.0文档)https://demo.ucharts.cn](https://demo.ucharts.cn)
|
||||||
|
## [图表组件在项目中的应用参见 UReport数据报表](https://ext.dcloud.net.cn/plugin?id=4651)
|
||||||
|
- 秋云图表组件 修复H5端在cli项目下ECharts引用地址错误的bug
|
||||||
|
- 示例项目 增加ECharts的formatter用法的示例(详见示例项目format-e.vue)
|
||||||
|
- uCharts.js 增加圆环图中心背景色的配置extra.ring.centerColor
|
||||||
|
- uCharts.js 修复微信小程序安卓端柱状图开启透明色后显示不正确的bug
|
||||||
|
## 2.0.0-20210413(2021-04-13)
|
||||||
|
- 秋云图表组件 修复百度小程序多个图表真机未能正确获取根元素dom尺寸的bug
|
||||||
|
- 秋云图表组件 修复百度小程序横屏模式方向不正确的bug
|
||||||
|
- 秋云图表组件 修改ontouch时,@getTouchStart@getTouchMove@getTouchEnd的触发条件
|
||||||
|
- uCharts.js 修复饼图类数据格式series属性不生效的bug
|
||||||
|
- uCharts.js 增加时序区域图 详见示例项目中ucharts.vue
|
||||||
|
## 2.0.0-20210412-2(2021-04-12)
|
||||||
|
## v1.0版本已停更,建议转uni_modules版本组件方式调用,点击右侧绿色【使用HBuilderX导入插件】即可使用,示例项目请点击右侧蓝色按钮【使用HBuilderX导入示例项目】。
|
||||||
|
## 初次使用如果提示未注册<qiun-data-charts>组件,请重启HBuilderX。如仍不好用,请重启电脑,此问题已于DCloud官方确认,HBuilderX下个版本会修复。
|
||||||
|
## [DEMO演示及在线生成工具(v2.0文档)https://demo.ucharts.cn](https://demo.ucharts.cn)
|
||||||
|
## [图表组件在uniCloudAdmin中的应用 UReport数据报表](https://ext.dcloud.net.cn/plugin?id=4651)
|
||||||
|
- 秋云图表组件 修复uCharts在APP端横屏模式下不能正确渲染的bug
|
||||||
|
- 示例项目 增加ECharts柱状图渐变色、圆角柱状图、横向柱状图(条状图)的示例
|
||||||
|
## 2.0.0-20210412(2021-04-12)
|
||||||
|
- 秋云图表组件 修复created中判断echarts导致APP端无法识别,改回mounted中判断echarts初始化
|
||||||
|
- uCharts.js 修复2d模式下series.textOffset未乘像素比的bug
|
||||||
|
## 2.0.0-20210411(2021-04-11)
|
||||||
|
## v1.0版本已停更,建议转uni_modules版本组件方式调用,点击右侧绿色【使用HBuilderX导入插件】即可使用,示例项目请点击右侧蓝色按钮【使用HBuilderX导入示例项目】。
|
||||||
|
## 初次使用如果提示未注册<qiun-data-charts>组件,请重启HBuilderX,并清空小程序开发者工具缓存。
|
||||||
|
## [DEMO演示及在线生成工具(v2.0文档)https://demo.ucharts.cn](https://demo.ucharts.cn)
|
||||||
|
## [图表组件在uniCloudAdmin中的应用 UReport数据报表](https://ext.dcloud.net.cn/plugin?id=4651)
|
||||||
|
- uCharts.js 折线图区域图增加connectNulls断点续连的功能,详见示例项目中ucharts.vue
|
||||||
|
- 秋云图表组件 变更初始化方法为created,变更type2d默认值为true,优化2d模式下组件初始化后dom获取不到的bug
|
||||||
|
- 秋云图表组件 修复左右布局时,右侧图表点击坐标错误的bug,修复tooltip柱状图自定义颜色显示object的bug
|
||||||
|
## 2.0.0-20210410(2021-04-10)
|
||||||
|
- 修复左右布局时,右侧图表点击坐标错误的bug,修复柱状图自定义颜色tooltip显示object的bug
|
||||||
|
- 增加标记线及柱状图自定义颜色的demo
|
||||||
|
## 2.0.0-20210409(2021-04-08)
|
||||||
|
## v1.0版本已停更,建议转uni_modules版本组件方式调用,点击右侧【使用HBuilderX导入插件】即可体验,DEMO演示及在线生成工具(v2.0文档)[https://demo.ucharts.cn](https://demo.ucharts.cn)
|
||||||
|
## 图表组件在uniCloudAdmin中的应用 [UReport数据报表](https://ext.dcloud.net.cn/plugin?id=4651)
|
||||||
|
- uCharts.js 修复钉钉小程序百度小程序measureText不准确的bug,修复2d模式下饼图类activeRadius为按比例放大的bug
|
||||||
|
- 修复组件在支付宝小程序端点击位置不准确的bug
|
||||||
|
## 2.0.0-20210408(2021-04-07)
|
||||||
|
- 修复组件在支付宝小程序端不能显示的bug(目前支付宝小程不能点击交互,后续修复)
|
||||||
|
- uCharts.js 修复高分屏下柱状图类,圆弧进度条 自定义宽度不能按比例放大的bug
|
||||||
|
## 2.0.0-20210407(2021-04-06)
|
||||||
|
## v1.0版本已停更,建议转uni_modules版本组件方式调用,点击右侧【使用HBuilderX导入插件】即可体验,DEMO演示及在线生成工具(v2.0文档)[https://demo.ucharts.cn](https://demo.ucharts.cn)
|
||||||
|
## 增加 通过tofix和unit快速格式化y轴的demo add by `howcode`
|
||||||
|
## 增加 图表组件在uniCloudAdmin中的应用 [UReport数据报表](https://ext.dcloud.net.cn/plugin?id=4651)
|
||||||
|
## 2.0.0-20210406(2021-04-05)
|
||||||
|
# 秋云图表组件+uCharts v2.0版本同步上线,使用方法详见https://demo.ucharts.cn帮助页
|
||||||
|
## 2.0.0(2021-04-05)
|
||||||
|
# 秋云图表组件+uCharts v2.0版本同步上线,使用方法详见https://demo.ucharts.cn帮助页
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,162 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container loading1">
|
||||||
|
<view class="shape shape1"></view>
|
||||||
|
<view class="shape shape2"></view>
|
||||||
|
<view class="shape shape3"></view>
|
||||||
|
<view class="shape shape4"></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'loading1',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped="true">
|
||||||
|
.container {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.container.loading1 {
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container .shape {
|
||||||
|
position: absolute;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
.container .shape.shape1 {
|
||||||
|
left: 0;
|
||||||
|
background-color: #1890FF;
|
||||||
|
}
|
||||||
|
.container .shape.shape2 {
|
||||||
|
right: 0;
|
||||||
|
background-color: #91CB74;
|
||||||
|
}
|
||||||
|
.container .shape.shape3 {
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #FAC858;
|
||||||
|
}
|
||||||
|
.container .shape.shape4 {
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #EE6666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading1 .shape1 {
|
||||||
|
-webkit-animation: animation1shape1 0.5s ease 0s infinite alternate;
|
||||||
|
animation: animation1shape1 0.5s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes animation1shape1 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(16px, 16px);
|
||||||
|
transform: translate(16px, 16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animation1shape1 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(16px, 16px);
|
||||||
|
transform: translate(16px, 16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading1 .shape2 {
|
||||||
|
-webkit-animation: animation1shape2 0.5s ease 0s infinite alternate;
|
||||||
|
animation: animation1shape2 0.5s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes animation1shape2 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(-16px, 16px);
|
||||||
|
transform: translate(-16px, 16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animation1shape2 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(-16px, 16px);
|
||||||
|
transform: translate(-16px, 16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading1 .shape3 {
|
||||||
|
-webkit-animation: animation1shape3 0.5s ease 0s infinite alternate;
|
||||||
|
animation: animation1shape3 0.5s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes animation1shape3 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(16px, -16px);
|
||||||
|
transform: translate(16px, -16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animation1shape3 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(16px, -16px);
|
||||||
|
transform: translate(16px, -16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading1 .shape4 {
|
||||||
|
-webkit-animation: animation1shape4 0.5s ease 0s infinite alternate;
|
||||||
|
animation: animation1shape4 0.5s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes animation1shape4 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(-16px, -16px);
|
||||||
|
transform: translate(-16px, -16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animation1shape4 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(-16px, -16px);
|
||||||
|
transform: translate(-16px, -16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container loading2">
|
||||||
|
<view class="shape shape1"></view>
|
||||||
|
<view class="shape shape2"></view>
|
||||||
|
<view class="shape shape3"></view>
|
||||||
|
<view class="shape shape4"></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'loading2',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped="true">
|
||||||
|
.container {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.loading2 {
|
||||||
|
-webkit-transform: rotate(10deg);
|
||||||
|
transform: rotate(10deg);
|
||||||
|
}
|
||||||
|
.container.loading2 .shape {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.container.loading2{
|
||||||
|
-webkit-animation: rotation 1s infinite;
|
||||||
|
animation: rotation 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container .shape {
|
||||||
|
position: absolute;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
.container .shape.shape1 {
|
||||||
|
left: 0;
|
||||||
|
background-color: #1890FF;
|
||||||
|
}
|
||||||
|
.container .shape.shape2 {
|
||||||
|
right: 0;
|
||||||
|
background-color: #91CB74;
|
||||||
|
}
|
||||||
|
.container .shape.shape3 {
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #FAC858;
|
||||||
|
}
|
||||||
|
.container .shape.shape4 {
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #EE6666;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.loading2 .shape1 {
|
||||||
|
-webkit-animation: animation2shape1 0.5s ease 0s infinite alternate;
|
||||||
|
animation: animation2shape1 0.5s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes animation2shape1 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(20px, 20px);
|
||||||
|
transform: translate(20px, 20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animation2shape1 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(20px, 20px);
|
||||||
|
transform: translate(20px, 20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading2 .shape2 {
|
||||||
|
-webkit-animation: animation2shape2 0.5s ease 0s infinite alternate;
|
||||||
|
animation: animation2shape2 0.5s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes animation2shape2 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(-20px, 20px);
|
||||||
|
transform: translate(-20px, 20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animation2shape2 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(-20px, 20px);
|
||||||
|
transform: translate(-20px, 20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading2 .shape3 {
|
||||||
|
-webkit-animation: animation2shape3 0.5s ease 0s infinite alternate;
|
||||||
|
animation: animation2shape3 0.5s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes animation2shape3 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(20px, -20px);
|
||||||
|
transform: translate(20px, -20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animation2shape3 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(20px, -20px);
|
||||||
|
transform: translate(20px, -20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading2 .shape4 {
|
||||||
|
-webkit-animation: animation2shape4 0.5s ease 0s infinite alternate;
|
||||||
|
animation: animation2shape4 0.5s ease 0s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes animation2shape4 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(-20px, -20px);
|
||||||
|
transform: translate(-20px, -20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animation2shape4 {
|
||||||
|
from {
|
||||||
|
-webkit-transform: translate(0, 0);
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: translate(-20px, -20px);
|
||||||
|
transform: translate(-20px, -20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user