mirror da
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-06-10 11:59:28 +08:00
v1.0.0-beta 源码正式开源
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules/
|
||||||
|
unpackage/
|
||||||
|
config/halo.config.js
|
||||||
|
config/ad.config.js
|
||||||
|
package-lock.json
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
|
||||||
|
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
|
||||||
|
"version": "0.0",
|
||||||
|
"configurations": [{
|
||||||
|
"app-plus" :
|
||||||
|
{
|
||||||
|
"launchtype" : "local"
|
||||||
|
},
|
||||||
|
"default" :
|
||||||
|
{
|
||||||
|
"launchtype" : "local"
|
||||||
|
},
|
||||||
|
"mp-weixin" :
|
||||||
|
{
|
||||||
|
"launchtype" : "local"
|
||||||
|
},
|
||||||
|
"type" : "uniCloud"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<script>
|
||||||
|
import HaloConfig from '@/config/halo.config.js';
|
||||||
|
import HaloAdConfig from '@/config/ad.config.js';
|
||||||
|
|
||||||
|
// app升级检测(搭配:https://ext.dcloud.net.cn/plugin?id=4470 升级中心)
|
||||||
|
import CheckAppUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
|
||||||
|
import { CheckWxUpdate } from '@/utils/update.js';
|
||||||
|
export default {
|
||||||
|
globalData: {
|
||||||
|
baseApiUrl: HaloConfig.apiUrl,
|
||||||
|
...HaloConfig,
|
||||||
|
haloAdConfig: HaloAdConfig
|
||||||
|
},
|
||||||
|
onLaunch: function() {
|
||||||
|
console.log('App Launch');
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
CheckAppUpdate();
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
CheckWxUpdate();
|
||||||
|
uni.$tm.vx.commit('setWxShare', HaloConfig.wxShareConfig);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// 监听中间按钮(暂时没有使用)
|
||||||
|
uni.onTabBarMidButtonTap(() => {
|
||||||
|
console.log('点击中间按钮');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化博主信息
|
||||||
|
uni.$tm.vx.actions('blogger/fnGetBlogger');
|
||||||
|
|
||||||
|
// 临时:检查是否有用户,没有的话添加一个默认的用户
|
||||||
|
uni.$tm.vx.actions('user/checkAndSetDefaultUser');
|
||||||
|
|
||||||
|
// 启动检查app的配置是否已经就绪,若未就绪则设置默认的
|
||||||
|
uni.$tm.vx.actions('setting/checkAndSetDefaultAppSettings');
|
||||||
|
},
|
||||||
|
onShow: function() {
|
||||||
|
console.log('App Show');
|
||||||
|
},
|
||||||
|
onHide: function() {
|
||||||
|
console.log('App Hide');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
// 基础样式
|
||||||
|
@import './common/styles/app.theme.scss';
|
||||||
|
@import './common/styles/app.base.scss';
|
||||||
|
|
||||||
|
// 引入tmUI2.x样式
|
||||||
|
@import './tm-vuetify/mian.min.css';
|
||||||
|
// 引入tmUI2.x主题包
|
||||||
|
@import './tm-vuetify/scss/theme.css';
|
||||||
|
// 引入tmUI2.x预置图标
|
||||||
|
@import './tm-vuetify/scss/fonts/fontawesome_base64.css';
|
||||||
|
|
||||||
|
// 自定义图标
|
||||||
|
@import './common/icons/halocoloriconfont.css';
|
||||||
|
@import './common/icons/haloiconfont.css';
|
||||||
|
@import './common/icons/mphtmliconfont.css';
|
||||||
|
|
||||||
|
/* #ifndef MP-WEIXIN */
|
||||||
|
@import './common/markdown/markdown.scss'; //引入markdown呈现
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
page {
|
||||||
|
// background-color: #f3f5f7;
|
||||||
|
// background-color: #f7f7f7;
|
||||||
|
// background-color: #ffffff;
|
||||||
|
background-color: #fafafa;
|
||||||
|
// background-color: #f4f5f5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# 更新日志
|
||||||
|
|
||||||
|
## 暂无更新记录
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Git管理同步配置
|
||||||
|
|
||||||
|
#### 同时推送Gitee和Github
|
||||||
|
找到项目目录下的`.git`文件夹,打开`config`文件,修改相关代码:
|
||||||
|
原始代码:
|
||||||
|
```bash
|
||||||
|
[remote "origin"]
|
||||||
|
url = https://gitee.com/ialley-workshop-open/uni-halo.git
|
||||||
|
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||||
|
```
|
||||||
|
修改后的代码:
|
||||||
|
```bash
|
||||||
|
[remote "github"]
|
||||||
|
url = https://github.com/ialley-workshop-open/uni-halo.git
|
||||||
|
fetch = +refs/heads/*:refs/remotes/github/*
|
||||||
|
[remote "gitee"]
|
||||||
|
url = https://gitee.com/ialley-workshop-open/uni-halo.git
|
||||||
|
fetch = +refs/heads/*:refs/remotes/gitee/*
|
||||||
|
```
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 RuoYi
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://uni-halo.925i.cn" target="_blank" rel="noopener noreferrer">
|
||||||
|
<img width="100" src="https://b.925i.cn/uni_halo/uni_halo_logo.png" alt="uni-halo logo" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center"><b>uni-halo</b> 基于Halo一款现代化的开源博客/CMS系统API开发的多端系统,值得一试。</p>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://b.925i.cn">作者博客</a>
|
||||||
|
<a href="https://uni-halo.925i.cn">文档地址</a>
|
||||||
|
<a href="https://gitee.com/ialley-workshop-open/uni-halo">Gitee仓库</a>
|
||||||
|
<a href="https://github.com/ialley-workshop-open/uni-halo">Github仓库</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
如果您觉得这个项目对您有帮助,可以帮作者买杯饮料鼓励鼓励!
|
||||||
|
|
||||||
|
<table rules="none" align="center" border="0">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<center>
|
||||||
|
<img src="https://uni-halo.925i.cn/qrcode/zfb.png" width="100%" />
|
||||||
|
<br/>
|
||||||
|
<font color="AAAAAA">支付宝打赏</font>
|
||||||
|
</center>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<center>
|
||||||
|
<img src="https://uni-halo.925i.cn/qrcode/wx.png" width="100%" />
|
||||||
|
<br/>
|
||||||
|
<font color="AAAAAA">微信打赏</font>
|
||||||
|
</center>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<center>
|
||||||
|
<img src="https://uni-halo.925i.cn/qrcode/qq.png" width="100%" />
|
||||||
|
<br/>
|
||||||
|
<font color="AAAAAA">QQ打赏</font>
|
||||||
|
</center>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
详细部署文档请查阅 [uni-halo-doc](https://uni-halo.925i.cn/)
|
||||||
|
|
||||||
|
- 1、拉取或下载项目<https://gitee.com/ialley-workshop-open/uni-halo>;
|
||||||
|
- 2、通过hbuilderx 导入项目;
|
||||||
|
- 3、命令行执行 npm i 安装依赖;
|
||||||
|
- 4、配置运行信息,找到项目根目录config目录,将`halo.config.template.js`修改为 `halo.config.js` 并设置相关信息;
|
||||||
|
- 5、点击hbuilderx 工具 右上角预览、或者点击工具栏 运行-内置浏览器运行;
|
||||||
|
- 6、项目发行:
|
||||||
|
- 发行小程序:点击工具栏 发行 -> 小程序-微信
|
||||||
|
- 发行APP: 点击工具栏 发行 -> 原生App-云打包
|
||||||
|
|
||||||
|
## 在线体验
|
||||||
|
|
||||||
|
- 敬请期待...
|
||||||
|
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
<a href="https://gitee.com/ialley-workshop-open/uni-halo/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||||
|
uni-halo 使用 MIT 协议开源,请遵守开源协议。
|
||||||
|
|
||||||
|
## 贡献
|
||||||
|
|
||||||
|
贡献代码请查看 [代码贡献规范](https://uni-halo.925i.cn/standard/category.html)
|
||||||
|
|
||||||
|
## Halo Api地址
|
||||||
|
- 接口文档地址(内容端):<https://api.halo.run/content-api.html>
|
||||||
|
- 接口文档地址(管理端):<https://api.halo.run/admin-api.html>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* 附件管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/attachment-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 分页获取附件列表
|
||||||
|
* {
|
||||||
|
* "attachmentType": "ALIOSS" "BAIDUBOS" "HUAWEIOBS" "LOCAL" "MINIO" "QINIUOSS" "SMMS" "TENCENTCOS" "UPOSS",
|
||||||
|
* "keyword": "string"
|
||||||
|
* "mediaType": "string"
|
||||||
|
* "page": "string"
|
||||||
|
* "size": "string"
|
||||||
|
* "sort": "string"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getAttachmentsByPage: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/attachments', params, {})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有的附件类型
|
||||||
|
*/
|
||||||
|
getAttachmentsMediaTypes: () => {
|
||||||
|
return HttpHandler.Get('/api/admin/attachments/media_types')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据附件类型获取所有的附件列表
|
||||||
|
*/
|
||||||
|
getAttachmentsTypes: () => {
|
||||||
|
return HttpHandler.Get('/api/admin/attachments/types')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据附件Id获取附件详情
|
||||||
|
*/
|
||||||
|
getAttachmentsById: (attachmentId) => {
|
||||||
|
return HttpHandler.Get(`/api/admin/attachments/${attachmentId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传附件-单文件(file)
|
||||||
|
* {
|
||||||
|
* file:文件对象
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
uploadAttachment: (data) => {
|
||||||
|
return HttpHandler.Upload(`/api/admin/attachments/upload`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传附件-多文件(files)
|
||||||
|
* {
|
||||||
|
* files:文件对象集合
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
uploadAttachments: (data) => {
|
||||||
|
return HttpHandler.Upload(`/api/admin/attachments/uploads`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改一个附件信息
|
||||||
|
*/
|
||||||
|
updateAttachmentById: (attachmentId, name) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/attachments/${attachmentId}`, {
|
||||||
|
name: name
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除附件(id集合)
|
||||||
|
*/
|
||||||
|
deleteAttachmentByIds: (attachmentIds = []) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/attachments`, attachmentIds)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除单个附件
|
||||||
|
*/
|
||||||
|
deleteAttachmentById: (attachmentId) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/attachments/${attachmentId}`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* 文章分类管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/category-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 查询所有的文章分类
|
||||||
|
* {
|
||||||
|
* "sort": ["",""], // 排序
|
||||||
|
* "more": "Boolean" ,// 更多参数(回调)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getCategoryList: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/categories', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有的文章分类(树形数据)
|
||||||
|
* {
|
||||||
|
* "sort": ["",""], // 排序
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getCategoryListTree: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/categories/tree_view', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询文章分类详情
|
||||||
|
* @param {Number} categoryId 分类ID
|
||||||
|
*/
|
||||||
|
getCategoryDetail: (categoryId) => {
|
||||||
|
return HttpHandler.Get(`/api/admin/categories/${categoryId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增文章分类
|
||||||
|
* {
|
||||||
|
* "description": "string",
|
||||||
|
* "id": 0,
|
||||||
|
* "name": "string",
|
||||||
|
* "parentId": 0,
|
||||||
|
* "password": "string",
|
||||||
|
* "priority": 0,
|
||||||
|
* "slug": "string",
|
||||||
|
* "thumbnail": "string"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
createCategory: (data) => {
|
||||||
|
return HttpHandler.Post(`/api/admin/categories`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改文章分类信息
|
||||||
|
* @param {Number} categoryId 分类id
|
||||||
|
* @param {Object} data 同新增
|
||||||
|
*/
|
||||||
|
updateCategoryById: (categoryId, data) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/categories/${categoryId}`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除单个文章分类
|
||||||
|
* @param {Number} categoryId 文章分类id
|
||||||
|
*/
|
||||||
|
deleteCategoryById: (categoryId) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/categories/${categoryId}`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
/**
|
||||||
|
* 文章评论管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/journal-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 查询文章评论
|
||||||
|
* {
|
||||||
|
* "keyword":"", // 关键字
|
||||||
|
* "page": 0, // 分页索引
|
||||||
|
* "size": 10, // 分页大小
|
||||||
|
* "sort": ["",""], // 排序
|
||||||
|
* "status": "" , // 类型 "AUDITING" "PUBLISHED" "RECYCLE"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getPostsComments: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/posts/comments', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回复文章评论
|
||||||
|
* {
|
||||||
|
* "allowNotification": true,
|
||||||
|
* "author": "string",
|
||||||
|
* "authorUrl": "string",
|
||||||
|
* "content": "string",
|
||||||
|
* "email": "string",
|
||||||
|
* "parentId": 0,
|
||||||
|
* "postId": 0
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
postPostsComments: (data) => {
|
||||||
|
return HttpHandler.Post('/api/admin/posts/comments', data)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 更新文章评论状态
|
||||||
|
* @param {Number} commentId id
|
||||||
|
* @param {String} status "AUDITING" "PUBLISHED" "RECYCLE"
|
||||||
|
*/
|
||||||
|
updatePostsCommentsStatus: (commentId, status) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/posts/comments/${commentId}/status/${status}`)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 删除文章评论
|
||||||
|
* @param {Number} commentId id
|
||||||
|
*/
|
||||||
|
deletePostsCommentsById: (commentId) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/posts/comments/${commentId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询页面评论
|
||||||
|
* {
|
||||||
|
* "keyword":"", // 关键字
|
||||||
|
* "page": 0, // 分页索引
|
||||||
|
* "size": 10, // 分页大小
|
||||||
|
* "sort": ["",""], // 排序
|
||||||
|
* "status": "" , // 类型 "AUDITING" "PUBLISHED" "RECYCLE"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getSheetsComments: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/sheets/comments', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回复页面评论
|
||||||
|
* {
|
||||||
|
* "allowNotification": true,
|
||||||
|
* "author": "string",
|
||||||
|
* "authorUrl": "string",
|
||||||
|
* "content": "string",
|
||||||
|
* "email": "string",
|
||||||
|
* "parentId": 0,
|
||||||
|
* "postId": 0
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
postSheetsComments: (data) => {
|
||||||
|
return HttpHandler.Post('/api/admin/sheets/comments', data)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 更新页面评论状态
|
||||||
|
* @param {Number} commentId id
|
||||||
|
* @param {String} status "AUDITING" "PUBLISHED" "RECYCLE"
|
||||||
|
*/
|
||||||
|
updateSheetsCommentsStatus: (commentId, status) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/sheets/comments/${commentId}/status/${status}`)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 删除页面评论
|
||||||
|
* @param {Number} commentId id
|
||||||
|
*/
|
||||||
|
deleteSheetsCommentsById: (commentId) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/sheets/comments/${commentId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询日记评论
|
||||||
|
* {
|
||||||
|
* "keyword":"", // 关键字
|
||||||
|
* "page": 0, // 分页索引
|
||||||
|
* "size": 10, // 分页大小
|
||||||
|
* "sort": ["",""], // 排序
|
||||||
|
* "status": "" , // 类型 "AUDITING" "PUBLISHED" "RECYCLE"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getJournalsComments: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/journals/comments', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回复日记评论
|
||||||
|
* {
|
||||||
|
* "allowNotification": true,
|
||||||
|
* "author": "string",
|
||||||
|
* "authorUrl": "string",
|
||||||
|
* "content": "string",
|
||||||
|
* "email": "string",
|
||||||
|
* "parentId": 0,
|
||||||
|
* "postId": 0
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
postJournalsComments: (data) => {
|
||||||
|
return HttpHandler.Post('/api/admin/journals/comments', data)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 更新日记评论状态
|
||||||
|
* @param {Number} commentId id
|
||||||
|
* @param {String} status "AUDITING" "PUBLISHED" "RECYCLE"
|
||||||
|
*/
|
||||||
|
updateJournalsCommentsStatus: (commentId, status) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/journals/comments/${commentId}/status/${status}`)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 删除日记评论
|
||||||
|
* @param {Number} commentId id
|
||||||
|
*/
|
||||||
|
deleteJournalsCommentsById: (commentId) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/journals/comments/${commentId}`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* 个人日记管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/journal-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 查询所有的日记列表
|
||||||
|
* {
|
||||||
|
* "keyword":"", // 关键字
|
||||||
|
* "page": 0, // 分页索引
|
||||||
|
* "size": 10, // 分页大小
|
||||||
|
* "sort": ["",""], // 排序
|
||||||
|
* "type": "" , // 类型 "INTIMATE" "PUBLIC"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getJournals: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/journals', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询最近的所有的日记列表
|
||||||
|
* {
|
||||||
|
* "top":number, // 数量
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getLatestJournals: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/journals/latest', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增个人日记
|
||||||
|
* {
|
||||||
|
* "content": "string",
|
||||||
|
* "keepRaw": true,
|
||||||
|
* "sourceContent": "string",
|
||||||
|
* "type": "INTIMATE",
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
createJournals: (data) => {
|
||||||
|
return HttpHandler.Post(`/api/admin/journals`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改个人日记信息
|
||||||
|
* @param {Number} journalsId id
|
||||||
|
* @param {Object} data 同新增
|
||||||
|
*/
|
||||||
|
updateJournalsById: (journalsId, data) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/journals/${journalsId}`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除个人日记
|
||||||
|
* @param {Number} journalsId id
|
||||||
|
*/
|
||||||
|
deleteJournalsById: (journalsId) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/journals/${journalsId}`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* 友链管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/link-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// 获取友链列表
|
||||||
|
getLinkList: () => {
|
||||||
|
return HttpHandler.Get('/api/admin/links')
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取友链详情
|
||||||
|
* @params { Number } linkId 友链Id
|
||||||
|
*/
|
||||||
|
getLinkDetail: (linkId) => {
|
||||||
|
return HttpHandler.Get(`/api/admin/links/${linkId}`)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 新增友链
|
||||||
|
* {
|
||||||
|
* "description": "string",
|
||||||
|
* "logo": "string",
|
||||||
|
* "name": "string",
|
||||||
|
* "priority": 0,
|
||||||
|
* "team": "string",
|
||||||
|
* "url": "string"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
addLink: (data) => {
|
||||||
|
return HttpHandler.Post('/api/admin/links', data, {})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 修改友链
|
||||||
|
* {
|
||||||
|
* "description": "string",
|
||||||
|
* "logo": "string",
|
||||||
|
* "name": "string",
|
||||||
|
* "priority": 0,
|
||||||
|
* "team": "string",
|
||||||
|
* "url": "string"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
updateLink: (linkId, data) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/links/${linkId}`, data, {})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 删除友链
|
||||||
|
* @params { Number } linkId 友链Id
|
||||||
|
*/
|
||||||
|
deleteLink: (linkId) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/links/${linkId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取友链分组
|
||||||
|
getLinkTeamList: (data) => {
|
||||||
|
return HttpHandler.Get('/api/admin/links/teams')
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 日志管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/link-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取日志列表列表
|
||||||
|
* params:{ top: Number}
|
||||||
|
*/
|
||||||
|
getLogsLatestList: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/logs/latest', params)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取日志列表列表
|
||||||
|
* params:{ page:Number,size:Number, sort:String }
|
||||||
|
*/
|
||||||
|
getLogsListByPage: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/logs', params)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 清空日志
|
||||||
|
*/
|
||||||
|
deleteAllLogs: () => {
|
||||||
|
return HttpHandler.Get(`/api/admin/logs/clear`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* 图库管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/photo-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 查询图片列表
|
||||||
|
* {
|
||||||
|
* "sort": ["",""], // 排序
|
||||||
|
* "more": "Boolean" ,// 更多参数(回调)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getPhotos: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/photos', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询最近的图库列表(树形数据)
|
||||||
|
* {
|
||||||
|
* "sort": ["",""], // 排序
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getLatestPhotos: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/photos/latest', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有的图片分组
|
||||||
|
*/
|
||||||
|
getPhotosTeams: () => {
|
||||||
|
return HttpHandler.Get('/api/admin/photos/teams')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询图片详情
|
||||||
|
* @param {Number} photoId id
|
||||||
|
*/
|
||||||
|
getPhotosDetail: (photoId) => {
|
||||||
|
return HttpHandler.Get(`/api/admin/photos/${photoId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增图片(单图)
|
||||||
|
*{
|
||||||
|
* "description": "string",
|
||||||
|
* "id": 0,
|
||||||
|
* "location": "string",
|
||||||
|
* "name": "string",
|
||||||
|
* "takeTime": "2019-08-24T14:15:22Z",
|
||||||
|
* "team": "string",
|
||||||
|
* "thumbnail": "string",
|
||||||
|
* "url": "string"
|
||||||
|
*}
|
||||||
|
*/
|
||||||
|
createPhotos: (data) => {
|
||||||
|
return HttpHandler.Post(`/api/admin/photos`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增图片(批量)
|
||||||
|
* {
|
||||||
|
* "description": "string",
|
||||||
|
* "id": 0,
|
||||||
|
* "location": "string",
|
||||||
|
* "name": "string",
|
||||||
|
* "takeTime": "2019-08-24T14:15:22Z",
|
||||||
|
* "team": "string",
|
||||||
|
* "thumbnail": "string",
|
||||||
|
* "url": "string"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
createPhotosBatch: (data) => {
|
||||||
|
return HttpHandler.Post(`/api/admin/photos/batch`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改图片信息
|
||||||
|
* @param {Number} photoId id
|
||||||
|
* @param {Object} data 同新增
|
||||||
|
*/
|
||||||
|
updatePhotosById: (photoId, data) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/photos/${photoId}`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除单张图片
|
||||||
|
* @param {Number} photoId id
|
||||||
|
*/
|
||||||
|
deletePhotosById: (photoId) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/photos/${photoId}`)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 批量删除图片
|
||||||
|
* @param {Number} photoIds id数组
|
||||||
|
*/
|
||||||
|
deletePhotosBatchById: (photoIds) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/photos/batch`, photoIds)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* 文章管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/post-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新建和编辑文章字段
|
||||||
|
*/
|
||||||
|
const createOrEditModel = {
|
||||||
|
"categoryIds": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"content": "string",
|
||||||
|
"createTime": "2019-08-24T14:15:22Z",
|
||||||
|
"disallowComment": true,
|
||||||
|
"editorType": "MARKDOWN",
|
||||||
|
"keepRaw": true,
|
||||||
|
"metaDescription": "string",
|
||||||
|
"metaKeywords": "string",
|
||||||
|
"metas": [{
|
||||||
|
"key": "string",
|
||||||
|
"postId": 0,
|
||||||
|
"value": "string"
|
||||||
|
}],
|
||||||
|
"originalContent": "string",
|
||||||
|
"password": "string",
|
||||||
|
"slug": "string",
|
||||||
|
"status": "DRAFT",
|
||||||
|
"summary": "string",
|
||||||
|
"tagIds": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"template": "string",
|
||||||
|
"thumbnail": "string",
|
||||||
|
"title": "string",
|
||||||
|
"topPriority": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 查询文章列表
|
||||||
|
* @param {Object} params {
|
||||||
|
* categoryId,keyword,page,size,sort,
|
||||||
|
* status:"DRAFT" "INTIMATE" "PUBLISHED" "RECYCLE",statuses,more
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getPostsByPage: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/posts', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询最近的文章列表
|
||||||
|
* @param {Object} params {top:Number}
|
||||||
|
*/
|
||||||
|
getLatestPosts: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/posts/latest', params)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 根据状态查询文章列表
|
||||||
|
* @param {String} status:"DRAFT" "INTIMATE" "PUBLISHED" "RECYCLE"
|
||||||
|
* @param {Object} params:{ page,size,sort,more }
|
||||||
|
*/
|
||||||
|
getPostsPageByStatus: (status, params) => {
|
||||||
|
return HttpHandler.Get(`/api/admin/posts/status/${status}`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文章id获取文章
|
||||||
|
* @param {Number} postsId 文章id
|
||||||
|
*/
|
||||||
|
getPostsById: (postsId) => {
|
||||||
|
return HttpHandler.Get(`/api/admin/posts/${postsId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增文章
|
||||||
|
* @param {Object} data 同新增
|
||||||
|
* @param {Boolean} isAutoSave 是否来源于自动保存
|
||||||
|
*/
|
||||||
|
createPosts: (data, isAutoSave = false) => {
|
||||||
|
return HttpHandler.Post(`/api/admin/posts?autoSave=${isAutoSave}`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改文章
|
||||||
|
* @param {Number} postsId id
|
||||||
|
* @param {Object} data 同新增
|
||||||
|
* @param {Boolean} isAutoSave 是否来源于自动保存
|
||||||
|
*/
|
||||||
|
updatePostsById: (postsId, data, isAutoSave = false) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/posts/${postsId}?autoSave=${isAutoSave}`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改文章(草稿)
|
||||||
|
* @param {Number} postsId id
|
||||||
|
* @param {Object} data { content,keepRaw,originalContent }
|
||||||
|
*/
|
||||||
|
updatePostsDraftById: (postsId, data) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/posts/${postsId}/status/draft/content`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改文章状态
|
||||||
|
* @param {Number} postsId id
|
||||||
|
* @param {String} status "DRAFT" "INTIMATE" "PUBLISHED" "RECYCLE"
|
||||||
|
*/
|
||||||
|
updatePostsDraftById: (postsId, status) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/posts/${postsId}/status/${status}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文章(批量)
|
||||||
|
* @param {Array} postsIds ids
|
||||||
|
*/
|
||||||
|
deletePostsByIds: (postsIds) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/posts`, postsIds)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* 标签管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/tag-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 查询文章标签列表
|
||||||
|
* {
|
||||||
|
* "sort": ["",""], // 排序
|
||||||
|
* "more": "Boolean" ,// 更多参数(回调)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getTagsList: (params) => {
|
||||||
|
return HttpHandler.Get('/api/admin/tags', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询文章标签详情
|
||||||
|
* @param {Number} tagId id
|
||||||
|
*/
|
||||||
|
getTagsDetail: (tagId) => {
|
||||||
|
return HttpHandler.Get(`/api/admin/tags/${tagId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增文章标签
|
||||||
|
* {
|
||||||
|
* "color": "#e23d66", // 颜色选择器
|
||||||
|
* "name": "string",
|
||||||
|
* "slug": "string",
|
||||||
|
* "thumbnail": "string"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
createTags: (data) => {
|
||||||
|
return HttpHandler.Post(`/api/admin/tags`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改文章标签信息
|
||||||
|
* @param {Number} tagId id
|
||||||
|
* @param {Object} data 同新增
|
||||||
|
*/
|
||||||
|
updateTagsById: (tagId, data) => {
|
||||||
|
return HttpHandler.Put(`/api/admin/tags/${tagId}`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文章标签
|
||||||
|
* @param {Number} tagId id
|
||||||
|
*/
|
||||||
|
deleteTagsById: (tagId) => {
|
||||||
|
return HttpHandler.Delete(`/api/admin/tags/${tagId}`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* 登录管理
|
||||||
|
* @see https://api.halo.run/admin-api.html#tag/admin-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// 登录前检查
|
||||||
|
loginPreCheck: (data) => {
|
||||||
|
return HttpHandler.Post('/api/admin/login/precheck', data, {})
|
||||||
|
},
|
||||||
|
// 登录
|
||||||
|
login: (data) => {
|
||||||
|
return HttpHandler.Post('/api/admin/login', data, {})
|
||||||
|
},
|
||||||
|
// 刷新token
|
||||||
|
refreshToken: (refreshToken) => {
|
||||||
|
return HttpHandler.Post($`/api/admin/refresh/${refreshToken}`, {}, {})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
logout: () => {
|
||||||
|
return HttpHandler.Post('/api/admin/logout')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取修改密码的验证码
|
||||||
|
* {
|
||||||
|
* "email": "string",
|
||||||
|
* "username": "string"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getResetPasswordCode: () => {
|
||||||
|
return HttpHandler.Post('/api/admin/password/code')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置密码
|
||||||
|
* {
|
||||||
|
* "code": "string",
|
||||||
|
* "email": "string",
|
||||||
|
* "password": "stringst",
|
||||||
|
* "username": "string"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
resetPasswordByCode: (data) => {
|
||||||
|
return HttpHandler.Put('/api/admin/password/reset', data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取个人信息(当前登录的管理员)
|
||||||
|
*/
|
||||||
|
getAdminProfile: () => {
|
||||||
|
return HttpHandler.Get('/api/admin/users/profiles')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改个人信息(当前登录的管理员)
|
||||||
|
* {
|
||||||
|
* "avatar": "string",
|
||||||
|
* "description": "string",
|
||||||
|
* "email": "string",
|
||||||
|
* "nickname": "string",
|
||||||
|
* "password": "stringst",
|
||||||
|
* "username": "string"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
updateAdminProfile: (data) => {
|
||||||
|
return HttpHandler.Put('/api/admin/users/profiles', data)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 修改密码
|
||||||
|
* {
|
||||||
|
* "confirmPassword": "string",
|
||||||
|
* "newPassword": "string",
|
||||||
|
* "oldPassword": "strings"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
modifyAdminPassword: (data) => {
|
||||||
|
return HttpHandler.Put('/api/admin/users/profiles/password', data)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 归档接口
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/archive-controlle
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取归档列表(按月)
|
||||||
|
*/
|
||||||
|
getMonthArchives: () => {
|
||||||
|
return HttpHandler.Get(`/api/content/archives/months`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取归档列表(按年)
|
||||||
|
*/
|
||||||
|
getYearArchives: () => {
|
||||||
|
return HttpHandler.Get(`/api/content/archives/years`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 文章接口
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取文章列表
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
*/
|
||||||
|
getArticleList: (params) => {
|
||||||
|
return HttpHandler.Get('/api/content/posts', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文章详情
|
||||||
|
* @param {String} articleId 文章id
|
||||||
|
*/
|
||||||
|
getArticleDetail: (articleId) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts/${articleId}`, {
|
||||||
|
formatDisabled: false,
|
||||||
|
sourceDisabled: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* 博主信息
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/user-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取博主信息
|
||||||
|
*/
|
||||||
|
getBloggerInfo: () => {
|
||||||
|
return HttpHandler.Get(`/api/content/users/profile`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 分类接口
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/category-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 查询分类列表
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
*/
|
||||||
|
getCategoryList: (params) => {
|
||||||
|
return HttpHandler.Get('/api/content/categories', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询分类下的文章
|
||||||
|
* @param {String} slug 分类名称
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
*/
|
||||||
|
getCategoryPostList: (slug, params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/categories/${slug}/posts`, params)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* 评论接口
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/post-controller
|
||||||
|
*/
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取评论列表接口(树形数据)
|
||||||
|
* @param {String} postId 文章id
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
*/
|
||||||
|
getPostCommentTree: (postId, params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts/${postId}/comments/tree_view`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评论列表接口(列表数据)
|
||||||
|
* @param {String} postId 文章id
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
*/
|
||||||
|
getPostCommentList: (postId, params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts/${postId}/comments/list_view`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取置顶评论
|
||||||
|
* @param {String} postId 文章id
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
*/
|
||||||
|
getPostTopCommentList: (postId, params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts/${postId}/comments/top_view`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评论的子评论列表
|
||||||
|
* @param {String} postId 文章id
|
||||||
|
* @param {String} commentParentId 要获取的评论id
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
*/
|
||||||
|
getPostChildrenCommentList: (postId, commentParentId, params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts/${postId}/comments/${commentParentId}/children`, params)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* 功能:全局API管理
|
||||||
|
* 作者:小莫唐尼
|
||||||
|
* 邮箱:studio@925i.cn
|
||||||
|
* 时间:2022年07月21日 19:14:44
|
||||||
|
* 版本:v0.1.0
|
||||||
|
* 修改记录:
|
||||||
|
* 修改内容:
|
||||||
|
* 修改人员:
|
||||||
|
* 修改时间:
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
import archive from './archive.js'
|
||||||
|
import article from './article.js'
|
||||||
|
import blogger from './blogger.js'
|
||||||
|
import category from './category.js'
|
||||||
|
import comment from './comment.js'
|
||||||
|
import journal from './journal.js'
|
||||||
|
import link from './link.js'
|
||||||
|
import menu from './menu.js'
|
||||||
|
import option from './option.js'
|
||||||
|
import photo from './photo.js'
|
||||||
|
import post from './post.js'
|
||||||
|
import sheet from './sheet.js'
|
||||||
|
import statistics from './statistics.js'
|
||||||
|
import theme from './theme.js'
|
||||||
|
|
||||||
|
// 管理端
|
||||||
|
import admin_login from './admin/user.js'
|
||||||
|
import admin_links from './admin/links.js'
|
||||||
|
import admin_attachment from './admin/attachment.js'
|
||||||
|
import admin_category from './admin/category.js'
|
||||||
|
import admin_journal from './admin/journal.js'
|
||||||
|
import admin_photos from './admin/photos.js'
|
||||||
|
import admin_tags from './admin/tags.js'
|
||||||
|
import admin_comments from './admin/comments.js'
|
||||||
|
import admin_posts from './admin/posts.js'
|
||||||
|
import admin_logs from './admin/logs.js'
|
||||||
|
|
||||||
|
const ApiManager = {
|
||||||
|
...archive,
|
||||||
|
...article,
|
||||||
|
...blogger,
|
||||||
|
...category,
|
||||||
|
...comment,
|
||||||
|
...journal,
|
||||||
|
...link,
|
||||||
|
...option,
|
||||||
|
...photo,
|
||||||
|
...post,
|
||||||
|
...sheet,
|
||||||
|
...statistics,
|
||||||
|
...theme,
|
||||||
|
// 管理端的api
|
||||||
|
admin: {
|
||||||
|
...admin_login,
|
||||||
|
...admin_links,
|
||||||
|
...admin_attachment,
|
||||||
|
...admin_category,
|
||||||
|
...admin_journal,
|
||||||
|
...admin_photos,
|
||||||
|
...admin_tags,
|
||||||
|
...admin_comments,
|
||||||
|
...admin_posts,
|
||||||
|
...admin_logs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const install = (Vue) => {
|
||||||
|
Vue.prototype.$httpApi = ApiManager
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* 日记接口
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/journal-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取日记列表
|
||||||
|
* @param {String} journalId 日记id
|
||||||
|
*/
|
||||||
|
getJournals: () => {
|
||||||
|
return HttpHandler.Get(`/api/content/journals`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日记详情
|
||||||
|
* @param {String} journalId 日记id
|
||||||
|
*/
|
||||||
|
getJournalDetail: (journalId) => {
|
||||||
|
return HttpHandler.Get(`/api/content/journals/${journalId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日记置顶评论列表
|
||||||
|
* @param {String} journalId 日记id
|
||||||
|
*/
|
||||||
|
getJournalTopComments: (journalId) => {
|
||||||
|
return HttpHandler.Get(`/api/content/journals/${journalId}/comments/top_view`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日记评论列表(列表形式)
|
||||||
|
* @param {String} journalId 日记id
|
||||||
|
*/
|
||||||
|
getJournalCommentList: (journalId) => {
|
||||||
|
return HttpHandler.Get(`/api/content/journals/${journalId}/comments/list_view`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日记评论列表(树形式)
|
||||||
|
* @param {String} journalId 日记id
|
||||||
|
*/
|
||||||
|
getJournalCommentTree: (journalId) => {
|
||||||
|
return HttpHandler.Get(`/api/content/journals/${journalId}/comments/tree_view`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日记评论列表(树形式)
|
||||||
|
* @param {String} journalId 日记id
|
||||||
|
* @param {String} commentParentId 评论id
|
||||||
|
*/
|
||||||
|
getJournalCommentChildren: (journalId, commentParentId) => {
|
||||||
|
return HttpHandler.Get(
|
||||||
|
`/api/content/journals/${journalId}/comments/${commentParentId}/children`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发表日记评论
|
||||||
|
* @param {Object} data 评论数据
|
||||||
|
*/
|
||||||
|
postJournalComments: (data) => {
|
||||||
|
return HttpHandler.Post(`/api/content/journals/comments`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给日记点赞
|
||||||
|
* @param {String} journalId 日记id
|
||||||
|
*/
|
||||||
|
postJournalLikes: (journalId) => {
|
||||||
|
return HttpHandler.Post(`/api/content/journals/${journalId}/likes`)
|
||||||
|
},
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 友链接口
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/link-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取友链列表
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getLinkList: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/links`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分组的友链列表
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getLinkListByTeam: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/links/team_view`, params)
|
||||||
|
},
|
||||||
|
}
|
||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
/**
|
||||||
|
* 普通用户登录
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
export function getUserInfo() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.getUserProfile({
|
||||||
|
lang: 'zh_CN',
|
||||||
|
desc: '用户登录', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,
|
||||||
|
success: (res) => {
|
||||||
|
console.log(res, 'resss')
|
||||||
|
resolve(res.userInfo)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLogin() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.login({
|
||||||
|
success(res) {
|
||||||
|
console.log('----------getLogin ---------')
|
||||||
|
console.log(res)
|
||||||
|
resolve(res)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.log(err, 'logoer')
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wxLogin() {
|
||||||
|
uni.getProvider({
|
||||||
|
service: 'oauth',
|
||||||
|
success: function(res) {
|
||||||
|
//支持微信、qq和微博等
|
||||||
|
if (~res.provider.indexOf('weixin')) {
|
||||||
|
console.log(res, 'ress')
|
||||||
|
let _userInfo = getUserInfo();
|
||||||
|
let _loginRes = getLogin();
|
||||||
|
Promise.all([_userInfo, _loginRes]).then((res) => {
|
||||||
|
let userInfo = res[0];
|
||||||
|
let loginRes = res[1];
|
||||||
|
if (loginRes.errMsg == 'login:ok') {
|
||||||
|
uni.$tm.vx.commit('user/setWxLoginInfo', {
|
||||||
|
avatarUrl: userInfo.avatarUrl,
|
||||||
|
nickName: userInfo.nickName,
|
||||||
|
email: '',
|
||||||
|
url: ''
|
||||||
|
});
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '登录成功!'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '登录失败,请重试!'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '登录失败,请重试!'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: function(err) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '登录失败,请重试!'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appWxLogin() {
|
||||||
|
uni.login({
|
||||||
|
provider: 'weixin',
|
||||||
|
success: function(loginRes) {
|
||||||
|
// 获取用户信息
|
||||||
|
uni.getUserInfo({
|
||||||
|
provider: 'weixin',
|
||||||
|
success: function(infoRes) {
|
||||||
|
uni.$tm.vx.commit('user/setWxLoginInfo', {
|
||||||
|
avatarUrl: infoRes.userInfo.avatarUrl,
|
||||||
|
nickName: infoRes.userInfo.nickName,
|
||||||
|
email: '',
|
||||||
|
url: ''
|
||||||
|
});
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '登录成功!'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: function(err) {
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '登录失败,请重试!'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 菜单接口
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/menu-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取菜单列表(列表)
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getMenuList: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/menus`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取菜单列表(树形)
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getMenuTree: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/menus/tree_view`, params)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 配置接口
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/option-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 根据key获取配置
|
||||||
|
* @param {String} key 配置的key
|
||||||
|
*/
|
||||||
|
getOptionByKey: (key) => {
|
||||||
|
return HttpHandler.Get(`/api/content/options/keys/${key}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配置列表(列表)
|
||||||
|
*/
|
||||||
|
getOptionList: () => {
|
||||||
|
return HttpHandler.Get(`/api/content/options/list_view`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配置列表(键值对)
|
||||||
|
*/
|
||||||
|
getOptionMap: () => {
|
||||||
|
return HttpHandler.Get(`/api/content/options/map_view`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 图库接口
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/photo-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取图库列表(分页查询)
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getPhotoListByPage: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/photos`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图库列表(最新)
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getPhotoList: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/photos/latest`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图库分组
|
||||||
|
*/
|
||||||
|
getPhotoTeams: () => {
|
||||||
|
return HttpHandler.Get(`/api/content/photos/teams`)
|
||||||
|
},
|
||||||
|
}
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* 文章接口
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/post-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取文章列表
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getPostList: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评论文章
|
||||||
|
* @param {Object} data 数据
|
||||||
|
* {
|
||||||
|
* "allowNotification": true,
|
||||||
|
* "author": "string",
|
||||||
|
* "authorUrl": "string",
|
||||||
|
* "content": "string",
|
||||||
|
* "email": "string",
|
||||||
|
* "parentId": 0,
|
||||||
|
* "postId": 0
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
postCommentPost: (data) => {
|
||||||
|
return HttpHandler.Post(`/api/content/posts/comments`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索文章
|
||||||
|
* @param {Object} data 数据
|
||||||
|
*/
|
||||||
|
getPostListByKeyword: (data) => {
|
||||||
|
return HttpHandler.Post(`/api/content/posts/search`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类获取文章
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getPostDetailBySlug: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts/slug`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文章id获取文章
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getPostDetailByPostId: (postId, params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts/${postId}`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给文章点赞
|
||||||
|
* @param {Object} postId 文章id
|
||||||
|
*/
|
||||||
|
postLikePost: (postId) => {
|
||||||
|
return HttpHandler.Post(`/api/content/posts/${postId}/likes`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据当前文章id获取前一篇文章
|
||||||
|
* @param {Object} postId 文章id
|
||||||
|
*/
|
||||||
|
getPrevByCurrentPostId: (postId) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts/${postId}/prev`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据当前文章id获取下一篇文章
|
||||||
|
* @param {Object} postId 文章id
|
||||||
|
*/
|
||||||
|
getNextByCurrentPostId: (postId) => {
|
||||||
|
return HttpHandler.Get(`/api/content/posts/${postId}/next`)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* 自定义页面模板
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/sheet-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取页面列表
|
||||||
|
* {
|
||||||
|
* page:
|
||||||
|
* size:
|
||||||
|
* sort:
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
getSheetsList: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/sheets`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类获取页面数据
|
||||||
|
*/
|
||||||
|
getSheetsListBySlug: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/sheets/slug`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取页面评论(列表)
|
||||||
|
*/
|
||||||
|
getSheetsCommentsListBySheetId: (sheetId, params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/sheets/${sheetId}/comments/list_view`, params)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取页面评论(树形)
|
||||||
|
*/
|
||||||
|
getSheetsCommentsTreeBySheetId: (sheetId, params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/sheets/${sheetId}/comments/tree_view`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评论的子评论列表
|
||||||
|
* @param {String} sheetId 页面id
|
||||||
|
* @param {String} commentParentId 要获取的评论id
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
*/
|
||||||
|
getSheetsChildrenCommentList: (sheetId, commentParentId, params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/sheets/${sheetId}/comments/${commentParentId}/children`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给页面添加一个评论
|
||||||
|
* {
|
||||||
|
* "allowNotification": true,
|
||||||
|
* "author": "string",
|
||||||
|
* "authorUrl": "string",
|
||||||
|
* "content": "string",
|
||||||
|
* "email": "string",
|
||||||
|
* "parentId": 0,
|
||||||
|
* "postId": 0
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
postSheetsComments: (data) => {
|
||||||
|
return HttpHandler.Post(`/api/content/sheets/comments`, data)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 博客统计信息
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/statistic-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取博客统计信息
|
||||||
|
*/
|
||||||
|
getBlogStatistics: () => {
|
||||||
|
return HttpHandler.Get(`/api/content/statistics`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取博客统计信息和用户信息
|
||||||
|
*/
|
||||||
|
getBlogStatisticsWithUser: () => {
|
||||||
|
return HttpHandler.Get(`/api/content/statistics/user`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 主题设置
|
||||||
|
* @see https://api.halo.run/content-api.html#tag/theme-controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HttpHandler from '@/common/http/request.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取激活主题的信息
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
geActivationThemeList: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/themes/activation`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取激活的主题设置
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getActivationThemeSettings: (params) => {
|
||||||
|
return HttpHandler.Get(`/api/content/themes/activation/settings`, params)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主题ID列出主题设置
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getThemeSettingsByThemeId: (themeId) => {
|
||||||
|
return HttpHandler.Get(`/api/content/themes/${themeId}/settings`)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过主题ID获取主题属性
|
||||||
|
* @param {Object} params 参数
|
||||||
|
*/
|
||||||
|
getThemePropertyByThemeId: (themeId) => {
|
||||||
|
return HttpHandler.Get(`/api/content/themes/${themeId}`)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* 功能:全局过滤器
|
||||||
|
* 作者:小莫唐尼
|
||||||
|
* 邮箱:studio@925i.cn
|
||||||
|
* 时间:2022年07月21日 17:39:04
|
||||||
|
* 版本:v0.1.0
|
||||||
|
* 修改记录:
|
||||||
|
* 修改内容:
|
||||||
|
* 修改人员:
|
||||||
|
* 修改时间:
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 功能描述:时间格式化,将指定的时间戳(或正常的日期)转换为带格式的日期
|
||||||
|
*
|
||||||
|
* 参数说明:
|
||||||
|
* 1.支持格式化 yyyy年MM月dd日 HH点mm分ss秒 星期w q季
|
||||||
|
* 2.对象形式传入 { d:'2021-06-04',f:'yyyy年' } d是必传项,f可不传(默认yyyy-MM-dd HH:mm:ss)
|
||||||
|
* 使用示例:
|
||||||
|
* 1:<view>{{ dateTimeParamName | formatTime }}</view>
|
||||||
|
* 2:<view>{{ { d: '2021-06-04', f: 'yyyy' } | formatTime }}</view>
|
||||||
|
* 3:<view>{{ { d: dateTimeParamName, f: 'yyyy年MM月dd日 HH点mm分ss秒 星期w q季' } | formatTime }}</view>
|
||||||
|
* 特别说明: 由于uniapp中的filter 不支持多参数,但是允许传入对象的形式,故以此方式实现!
|
||||||
|
*/
|
||||||
|
formatTime: function(data) {
|
||||||
|
let _dateTime = new Date(data);
|
||||||
|
let _fmt = 'yyyy-MM-dd HH:mm:ss';
|
||||||
|
if (_dateTime == 'Invalid Date') {
|
||||||
|
if (data.d == undefined || data.d == null || data.d == "") {
|
||||||
|
console.error('日期参数不正确,传入的参数列表:', data);
|
||||||
|
return ''
|
||||||
|
};
|
||||||
|
_dateTime = new Date(data.d);
|
||||||
|
if (_dateTime == 'Invalid Date') {
|
||||||
|
console.error('日期参数不正确,传入的参数列表:', data);
|
||||||
|
return '111'
|
||||||
|
}
|
||||||
|
if (data.hasOwnProperty('f')) {
|
||||||
|
_fmt = data.f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const _weekDays = ["日", "一", "二", "三", "四", "五", "六"];
|
||||||
|
const _seasons = ["冬", "春", "夏", "秋"];
|
||||||
|
const o = {
|
||||||
|
"M+": _dateTime.getMonth() + 1, //月份
|
||||||
|
"d+": _dateTime.getDate(), //日
|
||||||
|
"H+": _dateTime.getHours(), //小时
|
||||||
|
"m+": _dateTime.getMinutes(), //分
|
||||||
|
"s+": _dateTime.getSeconds(), //秒
|
||||||
|
"w+": _weekDays[_dateTime.getDay()], // 星期几
|
||||||
|
"q+": _seasons[Math.floor((_dateTime.getMonth() + 3) / 3)], //季度
|
||||||
|
S: _dateTime.getMilliseconds(), //毫秒
|
||||||
|
};
|
||||||
|
if (/(y+)/.test(_fmt)) {
|
||||||
|
_fmt = _fmt.replace(
|
||||||
|
RegExp.$1,
|
||||||
|
(_dateTime.getFullYear() + "").substr(4 - RegExp.$1.length)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (let k in o) {
|
||||||
|
if (new RegExp("(" + k + ")").test(_fmt)) {
|
||||||
|
_fmt = _fmt.replace(
|
||||||
|
RegExp.$1,
|
||||||
|
RegExp.$1.length == 1 ?
|
||||||
|
o[k] :
|
||||||
|
("00" + o[k]).substr(("" + o[k]).length)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _fmt;
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* 功能:请求工具
|
||||||
|
* 作者:小莫唐尼
|
||||||
|
* 邮箱:studio@925i.cn
|
||||||
|
* 时间:2022年07月21日 18:58:03
|
||||||
|
* 版本:v0.1.0
|
||||||
|
* 修改记录:
|
||||||
|
* 修改内容:
|
||||||
|
* 修改人员:
|
||||||
|
* 修改时间:
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HaloConfig from '@/config/halo.config.js'
|
||||||
|
import {
|
||||||
|
setInterceptors
|
||||||
|
} from "./interceptors.js";
|
||||||
|
import Request from "@/js_sdk/luch-request/luch-request";
|
||||||
|
|
||||||
|
const http = new Request()
|
||||||
|
/* 设置全局配置 */
|
||||||
|
http.setConfig((config) => {
|
||||||
|
|
||||||
|
// 如果是在外部浏览器调试或者编译为h5,请注释该行代码
|
||||||
|
config.baseURL = HaloConfig.apiUrl;
|
||||||
|
|
||||||
|
config.header = {
|
||||||
|
...config.header,
|
||||||
|
'api-authorization': HaloConfig.apiAuthorization,
|
||||||
|
ContentType: 'application/json',
|
||||||
|
dataType: 'json'
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
})
|
||||||
|
setInterceptors(http)
|
||||||
|
|
||||||
|
export {
|
||||||
|
http
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* 功能:http拦截
|
||||||
|
* 作者:小莫唐尼
|
||||||
|
* 邮箱:studio@925i.cn
|
||||||
|
* 时间:2022年07月21日 19:02:14
|
||||||
|
* 版本:v0.1.0
|
||||||
|
* 修改记录:
|
||||||
|
* 修改内容:
|
||||||
|
* 修改人员:
|
||||||
|
* 修改时间:
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
getAdminAccessToken
|
||||||
|
} from "@/utils/auth.js";
|
||||||
|
import {
|
||||||
|
delCache
|
||||||
|
} from "@/utils/storage";
|
||||||
|
export const setInterceptors = (http) => {
|
||||||
|
http.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
// 可使用async await 做异步操作
|
||||||
|
config.header = {
|
||||||
|
...config.header
|
||||||
|
// ... 可以直接加参数
|
||||||
|
};
|
||||||
|
if (getAdminAccessToken()) {
|
||||||
|
config.header['admin-authorization'] = getAdminAccessToken()
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(config) => {
|
||||||
|
// 可使用async await 做异步操作
|
||||||
|
return Promise.reject(config);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
http.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
/* 对响应成功做点什么 可使用async await 做异步操作*/
|
||||||
|
// if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
|
||||||
|
// return Promise.reject(response) // return Promise.reject 可使promise状态进入catch
|
||||||
|
// if (response.config.custom.verification) { // 演示自定义参数的作用
|
||||||
|
// return response.data
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return response.data;
|
||||||
|
} else {
|
||||||
|
return Promise.reject(response);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(response) => {
|
||||||
|
/* 对响应错误做点什么 (statusCode !== 200)*/
|
||||||
|
if (!response.data) {
|
||||||
|
return Promise.reject({
|
||||||
|
status: 500,
|
||||||
|
message: 'API接口服务异常!'
|
||||||
|
})
|
||||||
|
} else if (response.data.status == 401) {
|
||||||
|
delCache('APP_ADMIN_LOGIN_TOKEN');
|
||||||
|
uni.$eShowModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '您未登录超管账号或登录已过期,是否重新登录?',
|
||||||
|
showCancel: true,
|
||||||
|
cancelText: '否',
|
||||||
|
cancelColor: '#999999',
|
||||||
|
confirmText: '是',
|
||||||
|
confirmColor: '#03a9f4'
|
||||||
|
}).then(res => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pagesB/login/login'
|
||||||
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/tabbar/about/about'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return Promise.reject(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 封装各种请求方式
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
http
|
||||||
|
} from '@/common/http/index.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Get: (url, params, config = {}) => {
|
||||||
|
return http.get(url, {
|
||||||
|
params,
|
||||||
|
...config
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Post: (url, data, config = {}) => {
|
||||||
|
return http.post(url, data, config)
|
||||||
|
},
|
||||||
|
Put: (url, data, config = {}) => {
|
||||||
|
return http.put(url, data, config)
|
||||||
|
},
|
||||||
|
Upload: (url, config = {}) => {
|
||||||
|
return http.upload(url, config)
|
||||||
|
},
|
||||||
|
Delete: (url, data, config = {}) => {
|
||||||
|
return http.delete(url, data, config)
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff soppresso perché una o più righe sono troppo lunghe
File diff soppresso perché una o più righe sono troppo lunghe
File diff soppresso perché una o più righe sono troppo lunghe
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* 可以以页面为单位来写,
|
||||||
|
* 比如首页的内容,写在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",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* 可以以页面为单位来写,
|
||||||
|
* 比如首页的内容,写在index字段,个人中心写在center,共同部分写在common部分
|
||||||
|
* */
|
||||||
|
|
||||||
|
export default {
|
||||||
|
app: {
|
||||||
|
name: "巷子工坊",
|
||||||
|
author: "小莫唐尼",
|
||||||
|
},
|
||||||
|
tabbar: {
|
||||||
|
home: "首页",
|
||||||
|
moments: "动态",
|
||||||
|
publish: "发布",
|
||||||
|
mall: "商店",
|
||||||
|
mine: "我的",
|
||||||
|
},
|
||||||
|
// 提示文本
|
||||||
|
tips: {
|
||||||
|
switchLang: "切换语言",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* markdown配置
|
||||||
|
*/
|
||||||
|
|
||||||
|
import HaloConfig from '@/config/halo.config.js'
|
||||||
|
export default {
|
||||||
|
domain: HaloConfig.apiUrl,
|
||||||
|
tagStyle: {
|
||||||
|
table: `
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse:collapse;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--routine);
|
||||||
|
background: #f2f6fc;
|
||||||
|
border: 1px solid #dcdcdc;
|
||||||
|
border-radius: 4px;
|
||||||
|
`,
|
||||||
|
th: `
|
||||||
|
padding: 8px;
|
||||||
|
border-right: 1px solid var(--classE);
|
||||||
|
border-bottom: 1px solid var(--classE);
|
||||||
|
`,
|
||||||
|
td: `
|
||||||
|
padding: 8px;
|
||||||
|
border-right: 1px solid var(--classE);
|
||||||
|
border-bottom: 1px solid var(--classE);
|
||||||
|
`,
|
||||||
|
blockquote: `
|
||||||
|
padding: 8px 15px;
|
||||||
|
color: #606266;
|
||||||
|
background: #f2f6fc;
|
||||||
|
border-left: 5px solid #50bfff;
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 26px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
`,
|
||||||
|
ul: 'padding-left: 15px;line-height: 1.85;',
|
||||||
|
ol: 'padding-left: 15px;line-height: 1.85;',
|
||||||
|
li: 'margin-bottom: 12px;line-height: 1.85;',
|
||||||
|
h1: `
|
||||||
|
margin: 30px 0 20px;
|
||||||
|
color: var(--main);
|
||||||
|
line-height: 24px;
|
||||||
|
position: relative;
|
||||||
|
font-size:1.25em;
|
||||||
|
`,
|
||||||
|
h2: `
|
||||||
|
color: var(--main);
|
||||||
|
line-height: 24px;
|
||||||
|
position: relative;
|
||||||
|
margin: 22px 0 16px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
`,
|
||||||
|
h3: `
|
||||||
|
color: var(--main);
|
||||||
|
line-height: 24px;
|
||||||
|
position: relative;
|
||||||
|
margin: 26px 0 18px;
|
||||||
|
font-size: 1.3em;
|
||||||
|
`,
|
||||||
|
h4: `
|
||||||
|
color: var(--main);
|
||||||
|
line-height: 24px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
position: relative;
|
||||||
|
font-size: 1.18em;
|
||||||
|
`,
|
||||||
|
h5: `
|
||||||
|
color: var(--main);
|
||||||
|
line-height: 24px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
position: relative;
|
||||||
|
font-size: 1em;
|
||||||
|
`,
|
||||||
|
h6: `
|
||||||
|
color: #303133;
|
||||||
|
line-height: 24px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
position: relative;
|
||||||
|
font-size: 1em;
|
||||||
|
`,
|
||||||
|
p: `
|
||||||
|
line-height: 1.65;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
font-size: 15px;
|
||||||
|
`,
|
||||||
|
'code': ` `,
|
||||||
|
strong: 'font-weight: 700;color: rgb(248, 57, 41);',
|
||||||
|
video: 'width: 100%',
|
||||||
|
},
|
||||||
|
containStyle: 'font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding:12px;font-size: 16px;color: #606266;word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 6px;background-color:#FFFFFF;',
|
||||||
|
|
||||||
|
loadingGif: HaloConfig.loadingGifUrl,
|
||||||
|
emptyGif: HaloConfig.loadingEmptyUrl,
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
:root {
|
||||||
|
--main: #303133;
|
||||||
|
--theme: #fb6c28;
|
||||||
|
--code-background: #e8f3ff;
|
||||||
|
--radius-inner: 4px;
|
||||||
|
--classA: #dcdfe6;
|
||||||
|
--classB: #e4e7ed;
|
||||||
|
--classC: #ebeef5;
|
||||||
|
--classD: #f2f6fc;
|
||||||
|
--classE: #dcdcdc;
|
||||||
|
--classF: #333;
|
||||||
|
--classG: #dcdcdc;
|
||||||
|
--classH: #e9f2ff;
|
||||||
|
--classI: #5a3713;
|
||||||
|
--classJ: #f9e5fb;
|
||||||
|
--classK: #e4e7ed;
|
||||||
|
--classL: #666;
|
||||||
|
--classM: #2d2e37;
|
||||||
|
--quote: #50bfff;
|
||||||
|
--code: #409eff;
|
||||||
|
}
|
||||||
|
.evan-markdown {
|
||||||
|
::v-deep {
|
||||||
|
h1::before,
|
||||||
|
h2::before,
|
||||||
|
h3::before,
|
||||||
|
h4::before,
|
||||||
|
h5::before,
|
||||||
|
h6::before {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
content: '';
|
||||||
|
margin-right: 6px;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
h1::before {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
content: '¶';
|
||||||
|
top: -4px;
|
||||||
|
margin-right: 12px;
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--theme);
|
||||||
|
}
|
||||||
|
h2::before {
|
||||||
|
top: -2px;
|
||||||
|
left: 0;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-size: auto 100%;
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAVJJREFUWEftl7FKw1AUhv9rbmf1PRw6utkHEOoi6WPoE1ifQJ8gySKCOtRFRBKsDuIiDdZAURBFF50M2sFIzpGogVK03JA2cbh3S/i55zvfvYETgRyLj5erIKOO949FyKm5eHvvHEBV2jyruq1QDQ7n2DNXALEx+D7e2vl6lBYp76scHCzErtmCEPVhqEIAfus8BZk4wM+Zd/46tskDeI0mgLUyAdoAFsoDcM0XCDFdHoDX4FGfbRF3QANoA9qANqANaAPawD82EEWId1sAOJQWz6iO+5nGch41kDw9I3bbYOaTis21wgG4G4AuAzCwXrEoGV6V1ngMJPoPjoB+PzSIa8KBr1QdwFgA6KID7t1AgFYNC5uqxZNcbgB+eASdngHM+9LmpSzF8wFEEagbgHvXoQA3s3aegmYz8P1f4NNVME+39z5e3w4lkSMc3GXtPM1/AjYDFjDGddN5AAAAAElFTkSuQmCC);
|
||||||
|
}
|
||||||
|
h3::before {
|
||||||
|
top: -3px;
|
||||||
|
left: 0;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-size: auto 100%;
|
||||||
|
background-repeat: none;
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAH1JREFUWEft1qENgDAQheH/JANg60DAJIgmrMFCzEEYgTFgDyRFIClp0yBf9evl8pl3RsYbp7BjNLHoMptljPiMZH3WAhKQgAQkIIGXQAXUT79cniHaNMa5draliqqojIID86nRHEtvbSqlBSQgAQlIQAISkECRAA746SC5Ad6XpiGnnOGPAAAAAElFTkSuQmCC);
|
||||||
|
}
|
||||||
|
h4::before {
|
||||||
|
top: -2px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: var(--theme);
|
||||||
|
background-size: auto 100%;
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAndJREFUWEftVkty2kAQfT1SBLtwg6AqwzbxCQIniG8QvAmwCp8kRqvgFa5KwHjlkI3NCeIbhJwgXptUgW9AdiDQdGoITqUsjcZyuYqNZ6WSXs88vel+3YQtL9ry+Xgk8KhApALNAe8x5PPbCUrgabdsD2/e+1edEpN4FsIRps7OwT9cXKKHCLw75QIL+V0XRFIUP1dptBx3ChKkxQlw8UnOG5mqLESgdsYZy5dTAE8jg5lH3Ypd5Mlxxl8ttDgGRulcq5iYgApofgn6ILzVBQsSu5/e0OXiqnMOotdatVjsOvkPl4muQIFrp5y1hJzEBA67ZavEk07WX5EexzxM5b1SYgJrFQbBOQDt3wVSuP0qTRfjowsAr3SHODa75HrqqiKX1gdMycjAYa9stU3JCPBhKue1ExNQAY3BakSgl1HBDMykI9z+Ps0WV0eXIITKdhM3c+yUS259FrVPrBM2TlclEnSmY8+S93tV+/yvH+hxxLzv5D11paFltOLmIFD3FzIbtRMzpr2K5arn+bgzJZAWl8631rjby0zAUJI3xnTfkjQTiFEAwHW3bGWNCoCv0zlvjUukgOoJgPymzQGg3itbfVMOMKiezh30ExOIqwIAvwNHZFUVzMcdbbUonGOnsomr4P1XfiFZ/tS6GOOkW7FqJh8gwomz06ol9oE7O6GhH9zLCbfeCxqDoE3AR61sm5nA/3XUZ47pmneYCSLnAeHLCQGZaAvmH72yXdjMA6oTanHpnFfQ5tDmQ+KJ6MZ+jckXY7//k4pWYB7sQVCUccxU3a9teHKcWS7ne0wI4Rhipqv7REZkku8hvhut+CEOidvjkcDWFfgD9RMzMKE7f80AAAAASUVORK5CYII=);
|
||||||
|
}
|
||||||
|
h5::before {
|
||||||
|
top: -1px;
|
||||||
|
left: 0;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAC8klEQVRYR+3WP2gTURwH8O/vKnVRRHKXP52cBO3g4p/BqYNIhy4muajUQRBFKjQV1En6ZxJBcmlRCoJDRe3FDiJVwamLS3FwqbgIgss1l2ZQF5XeT3I1Z3NJ7u5dLlAwN9699/t97vfe7/EIO/yhHe5DD9jpCv3fFVTu856+Xzi62Y/35hj9CFPNrlUwWeRJAJfBGADwBcBNI0/PRZFdAdo4xlQLjCqKjBzogat7hZCRAgPghJGBgbFnxglAOvS7b/fLb+q+qnv5BHBCyEBAWTdnANy2IxOtWSC1qsY+1jOFwAVG+gIVvXyHQbcaKuZCxmf5iMQogXFQtEsBeO5JT6BcMu+Bcb1lUhcyqfExACUAB6JEtgUqi+U5JrrmmcyFHCjwSYtQO+tSUSFbAmXdnAdwJVASdyWLPAS2kbFA8xsHNS13EzCmlx8R6KJQcBcypfFpho3cKxRna3ADsgEo6+ZjAKMhgjZ1d2KWR2gTSyD0h4jnIB2gXDIXwciFCPZvSnMl0wwshYrJGDImaMUGKovlLBPVOrDjh8APzFx8zDkjNa7FzYoGZsKT9XEarQOHmei1aJA246cqOWW6/i2l8VMGzoWIPW/k6eq2Ja6UwCz8pw2JGZ8sS8pUz8fWau/jGp+SgLchcGBgZD1Py41NUuoI+ZloV8ZU93+ogZKzfBiWfXAPCgMJU8Y42avQdMzI4ZBfmSizocqrUeJaAmsvBZGGBCtTziXeRY1rCxRAViEhXckqK93AeQJ9kYTvlmVlqmcTdhNEtefc+9X3utVmuX+CkK6oyqtu4nwrWP8bF5IZdGYjJ79wDuMCz4D+XmhFWnZbt7ab5ltBB6mbkyAaBPFCJassuwMmNb4L4EZgXwBc4AoGTZrUeA6A9x1yK6tzzvnFDlxBv0D176kCP2TCpbbjBXCRV9DZk0VeAONCE1IQ1zWg3dlF1sFQHWQIXFeBNrLAw5BwHBZWjQl6E3SbbB8X+R4Mg/Ca0wN2WtFeBTut4B84mFI4VpekyAAAAABJRU5ErkJggg==);
|
||||||
|
}
|
||||||
|
h6::before {
|
||||||
|
top: -1px;
|
||||||
|
left: 0;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background-size: auto 100%;
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAEI0lEQVRYR+3Xb2wTZRwH8G/vL22RPy5GW8fK6rJpGeFFY9RO3TRRE1HfmEAammEyjZmOSEg1RrPZaTD6xjhU/Ndlf0CZgwmD+qcgZBRIETeqY0Vcnc7pGonhRY2l3m2t5upqjq693l2vcy+8N81zz/NcPvf75fd7rjos8ku3yH34HyjK0PqlDLPzD56vMNL0VHxmpgXAoUIZXKgI3sMSpK+cNTAbylaj/9IkprnLM3+mkg8XQi4EsJ4lSL+ZNbAfVd+JG/XLcSERg3M8IAtZauBtDEkeNdN6fX9NfRqXueQiSwm00wQRMDMGw94snBJkqYBrSYIIXs/ojftqGq6IXHZRFIpkKYC2JSw9SJE663W0nthlqcMawwrJYpVCag1M48pNy1YNvLWRffGNIYSCv6Cnog7rjCtVIbUEXoG7qeqaNMj51F58dWoKPZY62JeWyUJG+ctcIplcIizWCpgTl9EIyODJn9BtceDWq/6B57tO/H4R688fFaZ7AWzWAiiJEyMDx39Ed6UDdyy7Ni/w1ekxbP95VJi/F8CRYoGycGLksaEJdFU6cPdy0zxkR/RbtE6FhPvHATQUm2JFODHSfyyCLmsd7lth/hf53q/jcE8OC+NTAG7PTKiNoCqcGPnpF+PotDrwwNXl+OC3H9A8cVqYPgPgFnFo1QCLwomRg4cvoPMGBzZHTgq3zwKwZ+ddKVATXAZhf+gdnPvuojAUqmJdrspRAtQU93pXEM+8clgwhQHU5itruUBNcTt6TsP9sl8weQC0S/VFOUBNcW/2folt2z+XhZPTZh6kSKJ79aqVRuFszRxfUm8sNbdz9xlsfekz2bhCQBvDMMM8z+ubN92Mjrb71brS+97dM4wtnk8U4SSBBEGErVarzeVywePxoHVLA1pb6lUh3+8bwZMv+BTjpIAVACa9Xq+uqakJ7e3tqpGd/WfR3Jr+81awIJS0mY0A+sLhMGw2W3qfGmTXvhAef/6gapxUBDuqqqoejUQiBvFbKUH2DHyNx54bLAqXF0jT9HBjY6Pd6/XOi7oc5K7936Dp2QNF4/IBrTqdLuLxeIi2tracRSGF/PDgKB55er8muHzATQB2m81m+P1+1NbmPoVyIfsOnUOj+2PNcPmAO1iWbeE4TmexWODz+WQhayrL4No2oCkuJ5Bl2VGO49ZmcisXObdeVSuRaq7ZZ3G10KBTqRQl3pQPGY1GEQgE4HQ6heVDAO5S1cklNmUDXQRB9KZSqXkfESaTCW63G7FYDKFQKDEyMoJoNKqfe/bbAJ7QGpcrxa8xDLOV5/k0kGXZv2ZnZ5FMJtNjmqYvURR1IpFInAcwAeD7ud/pUuDmAY1G41g8Hl9DkmScoqggx3FHAIyJMMlSQfI9V5zKagDC93dsoRFKimQx2dIWOV/U/yn6bx0WyDj8vgLOAAAAAElFTkSuQmCC);
|
||||||
|
}
|
||||||
|
blockquote > p {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
font-size: 0.9em !important;
|
||||||
|
}
|
||||||
|
code[class='md-code'],
|
||||||
|
code:not([class]) {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #409eff;
|
||||||
|
margin: 2px 5px;
|
||||||
|
padding: 0 8px;
|
||||||
|
white-space: normal;
|
||||||
|
text-indent: 0;
|
||||||
|
-webkit-user-select: auto;
|
||||||
|
-moz-user-select: auto;
|
||||||
|
-ms-user-select: auto;
|
||||||
|
user-select: auto;
|
||||||
|
vertical-align: baseline;
|
||||||
|
word-break: break-word;
|
||||||
|
background: #e8f3ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
code[class*='language-'] {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
// border-radius: 0 0 8px 8px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
user-select: auto;
|
||||||
|
padding: 12px 12px 14px 18px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
background: #282c34;
|
||||||
|
color: #abb2bf;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
||||||
|
font-family: 'Fira Code', 'Fira Mono', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
|
||||||
|
direction: ltr;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
-moz-tab-size: 2;
|
||||||
|
-o-tab-size: 2;
|
||||||
|
tab-size: 2;
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
td {
|
||||||
|
padding: 8px;
|
||||||
|
border-right: 1px solid var(--classE);
|
||||||
|
border-bottom: 1px solid var(--classE);
|
||||||
|
}
|
||||||
|
thead th {
|
||||||
|
font-weight: 500;
|
||||||
|
background: var(--classC);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr {
|
||||||
|
transition: background 0.35s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* 功能:全局混入函数
|
||||||
|
* 作者:小莫唐尼
|
||||||
|
* 邮箱:studio@925i.cn
|
||||||
|
* 时间:2022年07月21日 17:39:32
|
||||||
|
* 版本:v0.1.0
|
||||||
|
* 修改记录:
|
||||||
|
* 修改内容:
|
||||||
|
* 修改人员:
|
||||||
|
* 修改时间:
|
||||||
|
*/
|
||||||
|
import HaloConfig from '@/config/halo.config.js';
|
||||||
|
import HaloAdConfig from '@/config/ad.config.js';
|
||||||
|
export default {
|
||||||
|
install(Vue) {
|
||||||
|
Vue.mixin({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
author: HaloConfig.author,
|
||||||
|
_isWechat: true,
|
||||||
|
haloAdConfig: HaloAdConfig
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 获取全局应用设置
|
||||||
|
globalAppSettings() {
|
||||||
|
return uni.$tm.vx.getters().setting.getSettings;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
this._isWechat = true;
|
||||||
|
uni.$tm.vx.commit('setWxShare', HaloConfig.wxShareConfig);
|
||||||
|
// #endif
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
this._isWechat = false;
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 设置页面标题
|
||||||
|
* @param {Object} title 标题
|
||||||
|
*/
|
||||||
|
fnSetPageTitle(title) {
|
||||||
|
uni.setNavigationBarTitle({
|
||||||
|
title: title || HaloConfig.title
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面返回顶部
|
||||||
|
*/
|
||||||
|
fnToTopPage(duration = 500) {
|
||||||
|
duration = isNaN(duration) ? 500 : duration
|
||||||
|
uni.pageScrollTo({
|
||||||
|
scrollTop: 0,
|
||||||
|
duration: duration,
|
||||||
|
fail: (err) => {
|
||||||
|
console.log('err:', err);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// 全局css基础样式
|
||||||
|
|
||||||
|
.card-shadow {
|
||||||
|
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
.bg-white {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
.load-text {
|
||||||
|
padding: 0 0 20rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
.e-fixed {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
/* #ifndef H5 */
|
||||||
|
top: 0;
|
||||||
|
/* #endif */
|
||||||
|
/* #ifdef H5 */
|
||||||
|
top: 88rpx;
|
||||||
|
/* #endif */
|
||||||
|
right: 0;
|
||||||
|
z-index: 6;
|
||||||
|
}
|
||||||
|
.e-loading-icon {
|
||||||
|
animation: eLoading 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes eLoading {
|
||||||
|
0% {
|
||||||
|
transform: rotateZ(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotateZ(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// 主题
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--theme: #f79ea3;
|
||||||
|
// --theme: #ffaec3;
|
||||||
|
--main: #303133;
|
||||||
|
--main-text-color: rgba(12, 25, 50, 1);
|
||||||
|
}
|
||||||
File diff soppresso perché una o più righe sono troppo lunghe
@@ -0,0 +1,304 @@
|
|||||||
|
<template>
|
||||||
|
<view class="article-card " :class="cardType" @click="fnClickEvent('card')">
|
||||||
|
<view class="left">
|
||||||
|
<cache-image
|
||||||
|
class="thumbnail"
|
||||||
|
radius="12rpx"
|
||||||
|
:url="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||||
|
:fileMd5="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||||
|
mode="aspectFill"
|
||||||
|
></cache-image>
|
||||||
|
<!-- <image class="thumbnail" lazy-load :src="$utils.checkThumbnailUrl(article.thumbnail)" mode="aspectFill"></image> -->
|
||||||
|
</view>
|
||||||
|
<view class="right">
|
||||||
|
<view class="title">
|
||||||
|
<text class="is-top" v-if="article.topped">置顶</text>
|
||||||
|
<text class="title-text text-overflow">{{ article.title }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="content text-overflow-2">{{ article.summary }}</view>
|
||||||
|
<view class="foot">
|
||||||
|
<view class="create-time">
|
||||||
|
<text class="time-label">发布时间:</text>
|
||||||
|
{{ { d: article.createTime, f: 'yyyy-MM-dd' } | formatTime }}
|
||||||
|
</view>
|
||||||
|
<view class="visits">
|
||||||
|
<!-- <tm-icons :size="24" name="icon-filter-fill"></tm-icons> -->
|
||||||
|
浏览
|
||||||
|
<text class="number">{{ article.visits }}</text>
|
||||||
|
次
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'article-card',
|
||||||
|
props: {
|
||||||
|
from: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
article: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
cardType() {
|
||||||
|
// tb_image_text=上图下文
|
||||||
|
// tb_text_image=上文下图
|
||||||
|
if (this.from == 'home' && this.globalAppSettings.layout.home == 'h_row_col2') {
|
||||||
|
if (!['tb_image_text', 'tb_text_image', 'only_text'].some(x => x == this.globalAppSettings.layout.cardType)) {
|
||||||
|
return [this.from, this.globalAppSettings.layout.home, 'tb_image_text'];
|
||||||
|
}
|
||||||
|
return [this.from, this.globalAppSettings.layout.home, this.globalAppSettings.layout.cardType];
|
||||||
|
}
|
||||||
|
return [this.globalAppSettings.layout.home, this.globalAppSettings.layout.cardType];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnClickEvent() {
|
||||||
|
this.$emit('on-click', this.article);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.article-card {
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 24rpx;
|
||||||
|
padding: 32rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #ffff;
|
||||||
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
&.home {
|
||||||
|
&.h_row_col2 {
|
||||||
|
margin: 12rpx;
|
||||||
|
.left {
|
||||||
|
width: 100%;
|
||||||
|
height: 200rpx;
|
||||||
|
.thumbnail {
|
||||||
|
::v-deep uni-image {
|
||||||
|
border-radius: 6rpx 6rpx 0 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
.is-top {
|
||||||
|
height: 36rpx;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
line-height: 36rpx;
|
||||||
|
vertical-align: 4rpx;
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foot {
|
||||||
|
justify-content: space-between;
|
||||||
|
.create-time {
|
||||||
|
font-size: 24rpx;
|
||||||
|
.time-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.tb_text_image {
|
||||||
|
padding: 12rpx;
|
||||||
|
.left .thumbnail {
|
||||||
|
::v-deep {
|
||||||
|
uni-image {
|
||||||
|
border-radius: 6rpx !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.only_text {
|
||||||
|
padding: 24rpx;
|
||||||
|
.right .foot {
|
||||||
|
.create-time {
|
||||||
|
.time-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.lr_image_text {
|
||||||
|
}
|
||||||
|
|
||||||
|
&.lr_text_image {
|
||||||
|
.left {
|
||||||
|
order: 2;
|
||||||
|
padding-left: 30rpx;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
order: 1;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.tb_image_text {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
.left {
|
||||||
|
width: 100%;
|
||||||
|
height: 300rpx;
|
||||||
|
.thumbnail {
|
||||||
|
::v-deep uni-image {
|
||||||
|
border-radius: 6rpx 6rpx 0 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
padding-left: 0;
|
||||||
|
padding: 24rpx;
|
||||||
|
width: 100%;
|
||||||
|
.foot {
|
||||||
|
justify-content: flex-start;
|
||||||
|
.create-time {
|
||||||
|
.time-label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.tb_text_image {
|
||||||
|
flex-direction: column;
|
||||||
|
.left {
|
||||||
|
width: 100%;
|
||||||
|
height: 260rpx;
|
||||||
|
order: 2;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
padding-left: 0;
|
||||||
|
width: 100%;
|
||||||
|
order: 1;
|
||||||
|
.foot {
|
||||||
|
justify-content: flex-start;
|
||||||
|
.create-time {
|
||||||
|
.time-label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.only_text {
|
||||||
|
padding: 36rpx;
|
||||||
|
.left {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
padding-left: 0;
|
||||||
|
.content {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
}
|
||||||
|
.foot {
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
.create-time {
|
||||||
|
.time-label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
width: 240rpx;
|
||||||
|
height: 180rpx;
|
||||||
|
.thumbnail {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: 30rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: var(--main-text-color);
|
||||||
|
.is-top {
|
||||||
|
height: 40rpx;
|
||||||
|
padding: 0 12rpx;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: 4rpx;
|
||||||
|
color: #fff;
|
||||||
|
background-image: -webkit-linear-gradient(0deg, #3ca5f6 0, #a86af9 100%);
|
||||||
|
border-radius: 4rpx 12rpx;
|
||||||
|
}
|
||||||
|
&-text {
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
display: -webkit-box;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #909399;
|
||||||
|
height: 80rpx;
|
||||||
|
margin-top: 14rpx;
|
||||||
|
line-height: 42rpx;
|
||||||
|
}
|
||||||
|
.foot {
|
||||||
|
display: flex;
|
||||||
|
font-size: 24rpx;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
color: #909399;
|
||||||
|
margin-top: 18rpx;
|
||||||
|
|
||||||
|
.create-time {
|
||||||
|
font-size: 26rpx;
|
||||||
|
.time-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
.number {
|
||||||
|
padding: 0 6rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
<template>
|
||||||
|
<view class="article-min-card" :class="[globalAppSettings.layout.cardType]" @click="fnClickEvent('card')">
|
||||||
|
<view class="left">
|
||||||
|
<cache-image
|
||||||
|
class="thumbnail"
|
||||||
|
radius="12rpx"
|
||||||
|
:url="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||||
|
:fileMd5="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||||
|
mode="aspectFill"
|
||||||
|
></cache-image>
|
||||||
|
</view>
|
||||||
|
<view class="right">
|
||||||
|
<view class="title text-overflow">{{ article.title }}</view>
|
||||||
|
<view class="content text-overflow">{{ article.summary }}</view>
|
||||||
|
<view class="foot">
|
||||||
|
<view class="create-time">
|
||||||
|
<!-- <text class="icon iconfont icon-clock"></text> -->
|
||||||
|
<text class="time-label">发布时间:</text>
|
||||||
|
{{ { d: article.createTime, f: 'yyyy-MM-dd' } | formatTime }}
|
||||||
|
</view>
|
||||||
|
<view class="visits">
|
||||||
|
<!-- <text class="icon iconfont icon-eye"></text> -->
|
||||||
|
浏览
|
||||||
|
<text class="number">{{ article.visits }}</text>
|
||||||
|
次
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'article-min-card',
|
||||||
|
props: {
|
||||||
|
article: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnClickEvent() {
|
||||||
|
this.$emit('on-click', this.article);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.article-min-card {
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #ffff;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 12rpx 24rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
padding: 16rpx;
|
||||||
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||||
|
&.lr_image_text {
|
||||||
|
}
|
||||||
|
|
||||||
|
&.lr_text_image {
|
||||||
|
.left {
|
||||||
|
order: 2;
|
||||||
|
padding-left: 30rpx;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
order: 1;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.tb_image_text {
|
||||||
|
flex-direction: column;
|
||||||
|
.left {
|
||||||
|
width: 100%;
|
||||||
|
height: 220rpx;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
padding-left: 0;
|
||||||
|
width: 100%;
|
||||||
|
.title {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
}
|
||||||
|
.foot {
|
||||||
|
justify-content: flex-start;
|
||||||
|
.create-time {
|
||||||
|
.time-label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.tb_text_image {
|
||||||
|
flex-direction: column;
|
||||||
|
.left {
|
||||||
|
width: 100%;
|
||||||
|
height: 220rpx;
|
||||||
|
order: 2;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
padding-left: 0;
|
||||||
|
width: 100%;
|
||||||
|
order: 1;
|
||||||
|
.foot {
|
||||||
|
justify-content: flex-start;
|
||||||
|
.create-time {
|
||||||
|
.time-label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.only_text {
|
||||||
|
.left {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
padding-left: 0;
|
||||||
|
.foot {
|
||||||
|
justify-content: flex-start;
|
||||||
|
.create-time {
|
||||||
|
.time-label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
width: 180rpx;
|
||||||
|
height: 130rpx;
|
||||||
|
.thumbnail {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: 24rpx;
|
||||||
|
.title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #909399;
|
||||||
|
margin-top: 14rpx;
|
||||||
|
}
|
||||||
|
.foot {
|
||||||
|
display: flex;
|
||||||
|
font-size: 24rpx;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
color: #909399;
|
||||||
|
margin-top: 14rpx;
|
||||||
|
.create-time {
|
||||||
|
font-size: 24rpx;
|
||||||
|
.time-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
font-size: 24rpx;
|
||||||
|
padding-right: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.visits {
|
||||||
|
.icon {
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.number {
|
||||||
|
padding: 0 6rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
<template>
|
||||||
|
<view class="">
|
||||||
|
<view v-if="loadStatus == 'loading'" class="img-loading" :style="[imgStyle, loadStyle]">
|
||||||
|
<!-- <text class="img-load-icon iconfont icon-loading"></text>
|
||||||
|
<text class="img-load-text">{{ loadText }}</text> -->
|
||||||
|
<image :src="loadingImgSrc" :style="[imgStyle]" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<view v-if="loadStatus == 'error'" class="img-error" :style="[imgStyle, loadErrStyle]">
|
||||||
|
<text class="img-err-icon iconfont icon-exclamation-circle"></text>
|
||||||
|
<text class="img-load-text">{{ loadErrText }}</text>
|
||||||
|
</view>
|
||||||
|
<image
|
||||||
|
v-show="loadStatus == 'success'"
|
||||||
|
:src="src"
|
||||||
|
@load="fnOnLoad"
|
||||||
|
@error="fnOnError"
|
||||||
|
:lazy-load="lazyLoad"
|
||||||
|
:style="[imgStyle]"
|
||||||
|
:mode="mode"
|
||||||
|
@click="$emit('on-click', url)"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import imageCache from '@/utils/imageCache.js';
|
||||||
|
export default {
|
||||||
|
name: 'cache-image',
|
||||||
|
props: {
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
lazyLoad: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
loadStyle: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
color: '#333'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadErrStyle: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
color: 'rgba(244, 67, 54,1)'
|
||||||
|
// backgroundColor: 'rgba(244, 67, 54,0.2)'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'aspectFill'
|
||||||
|
},
|
||||||
|
loadText: {
|
||||||
|
type: String,
|
||||||
|
default: '加载中...'
|
||||||
|
},
|
||||||
|
loadErrText: {
|
||||||
|
type: String,
|
||||||
|
default: '加载失败'
|
||||||
|
},
|
||||||
|
fileMd5: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '100%'
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '100%'
|
||||||
|
},
|
||||||
|
radius: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
imgStyle: {},
|
||||||
|
src: '', // 图片地址
|
||||||
|
loadStatus: 'loading'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
loadingImgSrc() {
|
||||||
|
return getApp().globalData.loadingGifUrl;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// 监听图片md5值的变化
|
||||||
|
fileMd5(val) {
|
||||||
|
// 查找获取图片缓存
|
||||||
|
this.fnGetImageCache();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.imgStyle = {
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
borderRadius: this.radius,
|
||||||
|
...this.styles
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查找获取图片缓存
|
||||||
|
this.fnGetImageCache();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 查找获取图片缓存
|
||||||
|
async fnGetImageCache() {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
var result = await imageCache.getImageCache(this.url, this.fileMd5);
|
||||||
|
if (result) {
|
||||||
|
this.src = result;
|
||||||
|
} else {
|
||||||
|
this.src = this.url;
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
this.src = this.url;
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
fnOnLoad() {
|
||||||
|
this.loadStatus = 'success';
|
||||||
|
},
|
||||||
|
fnOnError() {
|
||||||
|
this.loadStatus = 'error';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.img-loading,
|
||||||
|
.img-error {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
.img-load-icon {
|
||||||
|
font-size: 36rpx;
|
||||||
|
animation: xhRote 0.8s infinite linear;
|
||||||
|
}
|
||||||
|
.img-load-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
.img-error {
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.img-err-icon {
|
||||||
|
font-size: 36rpx;
|
||||||
|
}
|
||||||
|
@keyframes xhRote {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<view class="category-mini-card">
|
||||||
|
<!-- <image class="img" lazy-load :src="$utils.checkThumbnailUrl(category.thumbnail)" mode="aspectFill"></image> -->
|
||||||
|
<cache-image
|
||||||
|
class="img"
|
||||||
|
height="120rpx"
|
||||||
|
:url="$utils.checkThumbnailUrl(category.thumbnail)"
|
||||||
|
:fileMd5="$utils.checkThumbnailUrl(category.thumbnail)"
|
||||||
|
mode="aspectFill"
|
||||||
|
></cache-image>
|
||||||
|
|
||||||
|
<text class="label">{{ category.postCount }} 篇</text>
|
||||||
|
<view class="name">{{ category.name }}</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'category-mini-card',
|
||||||
|
props: {
|
||||||
|
category: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.category-mini-card {
|
||||||
|
display: inline-block;
|
||||||
|
width: 260rpx;
|
||||||
|
height: 180rpx;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
// border: 2rpx solid #f7f7f7;
|
||||||
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||||
|
.img {
|
||||||
|
width: 100%;
|
||||||
|
height: 120rpx;
|
||||||
|
border: 6rpx 6rpx 0 0;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 86rpx;
|
||||||
|
color: #03a9f4;
|
||||||
|
font-size: 24rpx;
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
border-radius: 0rpx 24rpx 0 0;
|
||||||
|
display: flex;
|
||||||
|
padding: 2rpx 12rpx;
|
||||||
|
padding-right: 24rpx;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
font-size: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<view class=" comment-item flex flex-col mt-30 pt-24" :class="{ 'child-comment-item': isChild, 'no-solid': !useSolid, classItem }">
|
||||||
|
<view class="comment-item_user flex">
|
||||||
|
<image v-if="comment.isAdmin" class="user-avatar" :src="bloggerInfo.avatar" mode="aspectFill" @error="fnOnImageError(comment)"></image>
|
||||||
|
<image v-else class="user-avatar" :src="comment.avatar" mode="aspectFill" @error="fnOnImageError(comment)"></image>
|
||||||
|
<view class="user-info pl-14">
|
||||||
|
<view class="author">
|
||||||
|
<text class="mr-6 text-grey-darken-1 text-size-m">{{ comment.author }}</text>
|
||||||
|
<tm-tags v-if="comment.isAdmin" :dense="true" color="bg-gradient-amber-accent" size="xs" model="fill">博主</tm-tags>
|
||||||
|
|
||||||
|
<tm-tags v-else :dense="true" color="bg-gradient-light-blue-lighten " size="xs" model="fill">游客</tm-tags>
|
||||||
|
</view>
|
||||||
|
<view class="flex mt-4">
|
||||||
|
<view v-if="false" class="text-size-s text-grey mr-12">IP属地:浙江省杭州市</view>
|
||||||
|
<view class="time text-size-xs text-grey">
|
||||||
|
<text class="">{{ $tm.dayjs(comment.createTime).format('YYYY年MM月DD日') }}</text>
|
||||||
|
<text class="ml-12">{{ $tm.dayjs(comment.createTime).fromNow(true) }}前</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="useActions" class="">
|
||||||
|
<tm-button size="s" text theme="blue" @click="$emit('on-comment', { type: 'user', comment: comment })">回复</tm-button>
|
||||||
|
<tm-button size="s" text theme="grey" @click="$emit('on-copy', comment.content)">复制</tm-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="comment-item_content mt-12" :class="{ 'has-bg': useContentBg, 'not-ml': isChild }" @click="$emit('on-detail', comment)" v-html="comment.content"></view>
|
||||||
|
|
||||||
|
<!-- <view v-if="useActions" class="comment-item_info text-size-s text-grey">
|
||||||
|
<text v-if="false" @click="$emit('on-todo')">点赞</text>
|
||||||
|
<text @click="$emit('on-comment', { type: 'user', comment: comment })">回复</text>
|
||||||
|
<text v-if="false" class="ml-24" @click="$emit('on-todo')">举报</text>
|
||||||
|
<text class="ml-24" @click="$emit('on-copy', comment.content)">复制内容</text>
|
||||||
|
</view> -->
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
export default {
|
||||||
|
name: 'comment-item',
|
||||||
|
components: { tmTags, tmButton },
|
||||||
|
props: {
|
||||||
|
classItem: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
useActions: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
useSolid: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
useContentBg: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
isChild: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
postId: {
|
||||||
|
type: Number,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
comment: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 获取博主信息
|
||||||
|
bloggerInfo() {
|
||||||
|
let blogger = this.$tm.vx.getters().blogger.getBlogger;
|
||||||
|
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
|
||||||
|
return blogger;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnOnImageError(data) {
|
||||||
|
if (data.isAdmin) {
|
||||||
|
data.avatar = this.$haloConfig.author.avatar;
|
||||||
|
} else {
|
||||||
|
data.avatar = `${this.$haloConfig.defaultAvatarUrl}&rt=${new Date().getTime()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.comment-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-top: 2rpx solid #f5f5f5;
|
||||||
|
|
||||||
|
&.child-comment-item {
|
||||||
|
padding-top: 0;
|
||||||
|
margin-left: 80rpx;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
&.no-solid {
|
||||||
|
border: 0;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
&_user {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.user-avatar {
|
||||||
|
width: 70rpx;
|
||||||
|
height: 70rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 4rpx solid #ffffff;
|
||||||
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.user-info {
|
||||||
|
width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_content {
|
||||||
|
font-size: 28rpx;
|
||||||
|
margin-left: 80rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: var(--main-text-color);
|
||||||
|
&.has-bg {
|
||||||
|
background-color: #fafafa;
|
||||||
|
padding: 6rpx 24rpx;
|
||||||
|
}
|
||||||
|
&.not-ml {
|
||||||
|
margin-left: 80rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&_info {
|
||||||
|
margin-top: 6rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 80rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
<template>
|
||||||
|
<view class="comment-list">
|
||||||
|
<!-- 顶部区域 -->
|
||||||
|
<view class="comment-list_head">
|
||||||
|
<view class="title">
|
||||||
|
评论列表
|
||||||
|
<text class="count">({{ result.total || 0 }}条)</text>
|
||||||
|
</view>
|
||||||
|
<view class="filter">
|
||||||
|
<text class="filter-item " :class="{ active: sort == 0 }" @click="fnOnSort(0)">默认</text>
|
||||||
|
<text class="filter-item " :class="{ active: sort == 1 }" @click="fnOnSort(1)">热评</text>
|
||||||
|
<!-- <text class="filter-item">全部</text> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- <view v-if="disallowComment" class="disallow-comment"><tm-empty icon="icon-shiliangzhinengduixiang-" label="文章已开启禁止评论"></tm-empty></view> -->
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<view class="comment-list_content">
|
||||||
|
<view v-if="loading != 'success'" class="loading-wrap flex">
|
||||||
|
<view v-if="loading == 'loading'" class="loading flex flex-center flex-col">
|
||||||
|
<text class="e-loading-icon iconfont icon-loading text-blue"></text>
|
||||||
|
<view class="text-size-n text-grey-lighten-1 py-12 mt-12">加载中,请稍等...</view>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="loading == 'error'" class="error">
|
||||||
|
<tm-empty icon="icon-wind-cry" label="加载失败">
|
||||||
|
<tm-button theme="bg-gradient-light-blue-accent" size="m" v-if="!disallowComment" @click="fnToComment()">刷新试试</tm-button>
|
||||||
|
</tm-empty>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<block v-else>
|
||||||
|
<view class="empty pt-50" v-if="dataList.length == 0">
|
||||||
|
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无评论">
|
||||||
|
<tm-button theme="bg-gradient-light-blue-accent" size="m" v-if="!disallowComment" @click="fnToComment(null)">抢沙发</tm-button>
|
||||||
|
</tm-empty>
|
||||||
|
</view>
|
||||||
|
<block v-else>
|
||||||
|
<!-- 评论内容 : 目前仅支持二级评论 -->
|
||||||
|
<block v-for="(comment, index) in dataList" :key="comment.id">
|
||||||
|
<comment-item
|
||||||
|
:useContentBg="false"
|
||||||
|
:isChild="false"
|
||||||
|
:comment="comment"
|
||||||
|
:postId="postId"
|
||||||
|
@on-copy="fnCopyContent"
|
||||||
|
@on-comment="fnToComment"
|
||||||
|
@on-todo="fnToDo"
|
||||||
|
@on-detail="fnShowCommetnDetail"
|
||||||
|
></comment-item>
|
||||||
|
|
||||||
|
<!-- 二级评论 -->
|
||||||
|
<block v-if="comment.children && comment.children.length != 0">
|
||||||
|
<block v-for="(childComment, childIndex) in comment.children" :key="childComment.id">
|
||||||
|
<comment-item
|
||||||
|
:useContentBg="false"
|
||||||
|
:isChild="true"
|
||||||
|
:comment="childComment"
|
||||||
|
:postId="postId"
|
||||||
|
@on-copy="fnCopyContent"
|
||||||
|
@on-comment="fnToComment"
|
||||||
|
@on-todo="fnToDo"
|
||||||
|
@on-detail="fnShowCommetnDetail"
|
||||||
|
></comment-item>
|
||||||
|
</block>
|
||||||
|
</block>
|
||||||
|
</block>
|
||||||
|
<view v-if="false" class="to-more-comment">
|
||||||
|
<tm-button item-class="btn" :block="true" width="90vw" theme="bg-gradient-light-blue-lighten" size="m">点击查看全部评论</tm-button>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||||
|
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||||
|
export default {
|
||||||
|
name: 'comment-list',
|
||||||
|
components: { tmEmpty, tmButton },
|
||||||
|
props: {
|
||||||
|
// 是否禁用评论
|
||||||
|
disallowComment: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
postId: {
|
||||||
|
type: Number,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
post: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: 'loading',
|
||||||
|
sort: 0,
|
||||||
|
queryParams: {
|
||||||
|
sort: '',
|
||||||
|
more: true
|
||||||
|
},
|
||||||
|
api: 'getPostCommentTree',
|
||||||
|
result: {},
|
||||||
|
dataList: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fnGetData();
|
||||||
|
uni.$on('comment_list_refresh', () => {
|
||||||
|
this.fnOnSort(this.sort, true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnOnSort(type, refresh = false) {
|
||||||
|
if (this.sort == type && refresh == false) return;
|
||||||
|
const _api = ['getPostCommentTree', 'getPostTopCommentList'];
|
||||||
|
this.sort = type;
|
||||||
|
this.api = _api[type];
|
||||||
|
this.fnGetData();
|
||||||
|
},
|
||||||
|
fnGetData() {
|
||||||
|
this.loading = 'loading';
|
||||||
|
this.$httpApi[this.api](this.postId, {})
|
||||||
|
.then(res => {
|
||||||
|
if (res.status == 200) {
|
||||||
|
this.result = res.data;
|
||||||
|
this.dataList = res.data.content;
|
||||||
|
this.loading = 'success';
|
||||||
|
} else {
|
||||||
|
this.loading = 'error';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.loading = 'error';
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
uni.hideLoading();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fnToDo() {
|
||||||
|
uni.$tm.toast('Halo暂未支持!');
|
||||||
|
},
|
||||||
|
fnToComment(data) {
|
||||||
|
if (this.disallowComment) {
|
||||||
|
return uni.$tm.toast('文章已禁止评论!');
|
||||||
|
}
|
||||||
|
console.log(data);
|
||||||
|
let _comment = {};
|
||||||
|
if (data) {
|
||||||
|
let { type, comment } = data;
|
||||||
|
// 来自用户
|
||||||
|
_comment = {
|
||||||
|
id: this.post.id,
|
||||||
|
parentId: comment.id,
|
||||||
|
title: comment.author,
|
||||||
|
from: 'posts',
|
||||||
|
formPage: 'comment_list',
|
||||||
|
type: 'user'
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 来自文章
|
||||||
|
_comment = {
|
||||||
|
id: this.post.id,
|
||||||
|
parentId: 0,
|
||||||
|
title: '评论文章:' + this.post.title,
|
||||||
|
formPage: 'comment_list',
|
||||||
|
from: 'posts',
|
||||||
|
type: 'post'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.$tm.vx.commit('comment/setCommentInfo', _comment);
|
||||||
|
this.$Router.push({
|
||||||
|
path: '/pagesA/comment/comment',
|
||||||
|
query: _comment
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fnCopyContent(content) {
|
||||||
|
uni.$tm.u.setClipboardData(content);
|
||||||
|
uni.$tm.toast('内容已复制成功!');
|
||||||
|
},
|
||||||
|
|
||||||
|
fnShowCommetnDetail(comment) {
|
||||||
|
this.$emit('on-comment-detail', {
|
||||||
|
postId: this.postId,
|
||||||
|
comment: comment
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.comment-list {
|
||||||
|
&_head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 24rpx;
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0rpx;
|
||||||
|
top: 8rpx;
|
||||||
|
width: 8rpx;
|
||||||
|
height: 30rpx;
|
||||||
|
background-color: rgb(3, 174, 252);
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
.count {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filter {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
margin-left: 20rpx;
|
||||||
|
color: #666;
|
||||||
|
&.active {
|
||||||
|
font-weight: bold;
|
||||||
|
color: rgb(255, 152, 0);
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&_content {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
padding-bottom: 36rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loading-wrap {
|
||||||
|
width: 100%;
|
||||||
|
height: 506rpx;
|
||||||
|
.loading {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.e-loading-icon {
|
||||||
|
font-size: 100rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.to-more-comment {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 80rpx;
|
||||||
|
::v-deep {
|
||||||
|
.tm-button .tm-button-btn uni-button {
|
||||||
|
height: 70rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,341 @@
|
|||||||
|
// 轮播图
|
||||||
|
|
||||||
|
.Swiper-mfw-index-box {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
view {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Swiper-mfw-index {
|
||||||
|
// 轮播图
|
||||||
|
width: 100%;
|
||||||
|
.Swiper-mfw {
|
||||||
|
width: inherit;
|
||||||
|
height: 450rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
.swiper-mfw-item {
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
.Image,
|
||||||
|
.ImageVideo {
|
||||||
|
border-radius: 12rpx;
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 指示器
|
||||||
|
.Swiper-indicator-box {
|
||||||
|
width: inherit;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
// Top顶部 [今日首推-盒子]
|
||||||
|
.Top-date-hot {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
.left-date-ri {
|
||||||
|
justify-content: center;
|
||||||
|
.date-ri-text {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 60rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-top: -4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.conter-date-nianyue {
|
||||||
|
margin: 0 14rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
.left-width-bgcolor {
|
||||||
|
width: 8rpx;
|
||||||
|
height: 45rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #fafafa;
|
||||||
|
margin-right: 14rpx;
|
||||||
|
}
|
||||||
|
.right-date-nianyue {
|
||||||
|
flex-direction: column;
|
||||||
|
.Top-yue-usa,
|
||||||
|
.Bottom-nian,
|
||||||
|
.text {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
margin-top: -4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right-hot-ttf {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: 6rpx;
|
||||||
|
.hot-text {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 52rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 指示器 [轮播信息 -> 标题,用户,头像,所在地]
|
||||||
|
.Swiper-indicator-Top {
|
||||||
|
position: absolute;
|
||||||
|
left: 0rpx;
|
||||||
|
bottom: 110rpx;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
&.no-dot {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.Top-item {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
// 如果有视频,则显示“视频预览”
|
||||||
|
.Top-ImageVideo {
|
||||||
|
width: 150rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 2rpx 6rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
background-color: #f0ad4e;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
.Icons {
|
||||||
|
color: #242629;
|
||||||
|
font-size: 26rpx;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ImageVideo-text {
|
||||||
|
color: #242629;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
.Top-Title {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
.title-text {
|
||||||
|
width: 100%;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 用户信息盒子
|
||||||
|
.Bottom-UserInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
align-items: center;
|
||||||
|
.UserImage-box {
|
||||||
|
width: 40rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
.Image {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.textbox {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
.wo-text,
|
||||||
|
.UserInfo {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
.wo-text {
|
||||||
|
color: #f1f2f6;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
.UserInfo {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.jiange-box {
|
||||||
|
margin: 0 8rpx;
|
||||||
|
.jiange-text {
|
||||||
|
color: #f1f2f6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 指示器 [左边图片列表+右边按钮]
|
||||||
|
.Swiper-indicator-Bottom {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 14rpx;
|
||||||
|
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.4), rgba(101, 101, 101, 0.7));
|
||||||
|
// 左边[图片列表]
|
||||||
|
.Bottom-left-Imagelist {
|
||||||
|
// width: 560rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
// 指示图(小图模式)
|
||||||
|
.Bottom-item {
|
||||||
|
width: 98rpx;
|
||||||
|
height: 78rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
|
||||||
|
.Image {
|
||||||
|
width: 98rpx;
|
||||||
|
height: 78rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
&.current {
|
||||||
|
.Image {
|
||||||
|
// border: 4rpx solid #ffda02;
|
||||||
|
border: 4rpx solid rgb(110, 186, 247);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Bottom-item + .Bottom-item {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 右边 [历历在目-按钮]
|
||||||
|
.Bottom-right-lili-btn {
|
||||||
|
width: 145rpx;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
// background-image: linear-gradient(45deg, rgb(110, 186, 247), rgb(13, 141, 242));
|
||||||
|
.Bottom-item {
|
||||||
|
width: 145rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
.indicator-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.more {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 26rpx;
|
||||||
|
.iconfont {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
// font-weight: normal;
|
||||||
|
letter-spacing: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Swiper-box {
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden !important;
|
||||||
|
transform: translateY(0) !important;
|
||||||
|
transform: translateX(0) !important;
|
||||||
|
&.right {
|
||||||
|
.indicator-Top-box {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.Swiper-indicator-Top {
|
||||||
|
bottom: 0;
|
||||||
|
.Top-item .Top-Title .title-text {
|
||||||
|
width: 540rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Swiper-indicator-Bottom {
|
||||||
|
width: 120rpx;
|
||||||
|
position: absolute;
|
||||||
|
top: 0rpx;
|
||||||
|
left: initial;
|
||||||
|
right: 0rpx;
|
||||||
|
flex-direction: column;
|
||||||
|
background-image: none;
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
// border-radius: 0rpx 12rpx 12rpx 0;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
.Bottom-left-Imagelist {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
.Bottom-item {
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
.Bottom-item + .Bottom-item {
|
||||||
|
margin-left: 0rpx;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Bottom-right-lili-btn {
|
||||||
|
width: 100rpx;
|
||||||
|
margin-left: -4rpx;
|
||||||
|
.Bottom-item {
|
||||||
|
width: initial;
|
||||||
|
margin-top: 6rpx;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.more {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
letter-spacing: 0;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Swiper-mfw {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.indicator-Top-box {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.indicator-Btoom-box {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 轮播图 -->
|
||||||
|
<view class="Swiper-mfw-index-box">
|
||||||
|
<view class="Swiper-mfw-index Swiper-box" :class="[dotPosition]">
|
||||||
|
<swiper
|
||||||
|
class="Swiper-mfw"
|
||||||
|
:style="{ height: height }"
|
||||||
|
:circular="true"
|
||||||
|
:indicator-dots="false"
|
||||||
|
:autoplay="autoplay"
|
||||||
|
:interval="3000"
|
||||||
|
:duration="1000"
|
||||||
|
:current="currentIndex"
|
||||||
|
:disable-touch="disable_touch"
|
||||||
|
@change="change"
|
||||||
|
>
|
||||||
|
<!-- 只需要前5条数据 -->
|
||||||
|
<swiper-item class="swiper-mfw-item" v-if="index <= (dotPosition == 'right' ? 3 : 4)" v-for="(item, index) in list" :key="index">
|
||||||
|
<!-- /*
|
||||||
|
1. 这里不需要用api控制暂停视频
|
||||||
|
2. 因为video标签上加了v-if="current==index"
|
||||||
|
3. 当current == index时才会创建视频组件
|
||||||
|
4. 否current != index则就销毁视频
|
||||||
|
*/ -->
|
||||||
|
<!-- 如果有视频,则显示视频-->
|
||||||
|
<template v-if="item.mp4 && current == index">
|
||||||
|
<video
|
||||||
|
class="ImageVideo"
|
||||||
|
:id="'ImageVideo' + index"
|
||||||
|
:ref="'ImageVideo' + index"
|
||||||
|
:src="item.mp4"
|
||||||
|
:loop="true"
|
||||||
|
:muted="false"
|
||||||
|
:autoplay="current == index ? true : false"
|
||||||
|
:controls="false"
|
||||||
|
:show-fullscreen-btn="false"
|
||||||
|
:show-play-btn="false"
|
||||||
|
:enable-progress-gesture="false"
|
||||||
|
:play-strategy="0"
|
||||||
|
:poster="item.image || item.src"
|
||||||
|
></video>
|
||||||
|
</template>
|
||||||
|
<!-- 否则显示图片 -->
|
||||||
|
<image v-else :src="item.image || item.src" class="Image" mode="aspectFill" @click.stop="$emit('on-click', item)"></image>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
<!-- 指示器 [Top] -->
|
||||||
|
<view v-if="useTop" class="Swiper-indicator-box indicator-Top-box">
|
||||||
|
<!-- Top顶部 [今日首推-盒子] -->
|
||||||
|
<view class="Top-date-hot">
|
||||||
|
<view class="left-date-ri">
|
||||||
|
<text class="date-ri-text text">{{ date.month }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="conter-date-nianyue">
|
||||||
|
<view class="left-width-bgcolor"></view>
|
||||||
|
<view class="right-date-nianyue">
|
||||||
|
<text class="Top-yue-usa text">{{ date.monthEn }}</text>
|
||||||
|
<text class="Bottom-nian text">{{ date.year }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="right-hot-ttf">
|
||||||
|
<text class="text hot-text">{{ title }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 指示器 标题区域 -->
|
||||||
|
<view v-if="useTitle" class="Swiper-indicator-Top" :class="{ 'no-dot': !useDot }">
|
||||||
|
<block v-for="(item, index) in list" :key="index">
|
||||||
|
<view v-if="currentIndex == index" class="Top-item" :class="current == index ? 'current' : 'no'">
|
||||||
|
<!-- 如果存在视频,则显示“视频预览”提示 -->
|
||||||
|
<view v-if="item.mp4" class="Top-ImageVideo">
|
||||||
|
<!-- icon图标 -->
|
||||||
|
<view class="Icons">
|
||||||
|
<!-- 播放按钮图标 -->
|
||||||
|
<text class="iconfont icon-caret-right"></text>
|
||||||
|
</view>
|
||||||
|
<text class="text ImageVideo-text app-ttf">视频预览</text>
|
||||||
|
</view>
|
||||||
|
<!-- 标题 -->
|
||||||
|
<view class="Top-Title">
|
||||||
|
<text class="text title-text">{{ item.title }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 用户信息 -->
|
||||||
|
<view class="Bottom-UserInfo">
|
||||||
|
<!-- 头像 -->
|
||||||
|
<view class="UserImage-box"><image :src="item.avatar" class="Image" mode="aspectFill"></image></view>
|
||||||
|
<!-- 用户名 -->
|
||||||
|
<view class="textbox UserName-box">
|
||||||
|
<text class="text UserInfo">{{ item.nickname }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="item.createTime" class="jiange-box"><text class="text jiange-text"></text></view>
|
||||||
|
<view v-if="item.createTime" class="textbox UserGPS-box">
|
||||||
|
<text class="text UserInfo">发布于 {{ item.createTime }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="item.address" class="jiange-box"><text class="text jiange-text">·</text></view>
|
||||||
|
<view v-if="item.address" class="textbox UserGPS-box">
|
||||||
|
<text class="text UserInfo">{{ item.address }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
<!-- 指示器 [左边图片列表+右边按钮] -->
|
||||||
|
<view v-if="useDot" class="Swiper-indicator-Bottom">
|
||||||
|
<!-- 左边 -->
|
||||||
|
<view class="Bottom-left-Imagelist">
|
||||||
|
<block v-for="(item, index) in list" :key="index">
|
||||||
|
<view
|
||||||
|
class="Bottom-item"
|
||||||
|
v-if="Number(index) <= (dotPosition == 'right' ? 3 : 4)"
|
||||||
|
:class="currentIndex == index ? 'current' : 'no'"
|
||||||
|
@click="SwiperIndTap(index)"
|
||||||
|
>
|
||||||
|
<image :src="item.image || item.src" class="Image" mode="aspectFill"></image>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
<!-- 右边 -->
|
||||||
|
<view class="Bottom-right-lili-btn">
|
||||||
|
<view class="Bottom-item is-more">
|
||||||
|
<view class="more" @click.stop="$emit('on-more')">
|
||||||
|
MORE
|
||||||
|
<text class="iconfont icon-caret-right"></text>
|
||||||
|
</view>
|
||||||
|
<text class="left text indicator-text">更多推荐</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'e-swiper',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '450rpx'
|
||||||
|
},
|
||||||
|
dotPosition: {
|
||||||
|
type: String,
|
||||||
|
default: 'bottom'
|
||||||
|
},
|
||||||
|
useTop: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
useDot: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
useTitle: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
useUser: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 轮播图 数据列表
|
||||||
|
list: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 当前选中的项(指示器坐标位置)
|
||||||
|
current: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
// 是否自动轮播
|
||||||
|
autoplay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 是否禁止用户 touch 操作
|
||||||
|
currentIndex: 0,
|
||||||
|
disable_touch: false, //touch 用户划动引起swiper变化。
|
||||||
|
date: {
|
||||||
|
year: '-',
|
||||||
|
monthEn: '-',
|
||||||
|
month: '-'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.currentIndex = this.current;
|
||||||
|
const date = new Date();
|
||||||
|
//将月份名称存储在数组中
|
||||||
|
const monthArray = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
|
||||||
|
|
||||||
|
this.date.year = date.getFullYear();
|
||||||
|
let month = date.getMonth() + 1;
|
||||||
|
this.date.month = month < 10 ? '0' + month : month;
|
||||||
|
this.date.monthEn = monthArray[date.getMonth()].toUpperCase();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// current 改变时会触发 change 事件,event.detail = {current: current, source: source}
|
||||||
|
change(e) {
|
||||||
|
let { current, source } = e.detail;
|
||||||
|
//只有页面自动切换,手动切换时才轮播,其他不允许
|
||||||
|
if (source === 'autoplay' || source === 'touch') {
|
||||||
|
let event = {
|
||||||
|
current: current
|
||||||
|
};
|
||||||
|
this.currentIndex = current;
|
||||||
|
this.$emit('change', event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 手动点击了指示器[小图模式]
|
||||||
|
SwiperIndTap(e) {
|
||||||
|
let index = e;
|
||||||
|
let event = {
|
||||||
|
current: index
|
||||||
|
};
|
||||||
|
this.currentIndex = index;
|
||||||
|
this.$emit('change', event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './e-swiper.scss';
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
<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().blogger.getBlogger;
|
||||||
|
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
|
||||||
|
return blogger;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fnLike(journal) {
|
||||||
|
uni.showLoading({
|
||||||
|
mask: true,
|
||||||
|
title: '正在点赞中...'
|
||||||
|
});
|
||||||
|
this.$httpApi
|
||||||
|
.postJournalLikes(journal.id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.status == 200) {
|
||||||
|
journal.likes += 1;
|
||||||
|
uni.$tm.toast('o( ̄▽ ̄)d点赞成功!');
|
||||||
|
} else {
|
||||||
|
uni.$tm.toast('Ծ‸Ծ点赞失败了~');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
uni.$tm.toast('Ծ‸Ծ点赞失败了~');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
fnDel(journal) {
|
||||||
|
uni.$eShowModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '您确定要删除该日记吗?',
|
||||||
|
showCancel: true,
|
||||||
|
cancelText: '否',
|
||||||
|
cancelColor: '#999999',
|
||||||
|
confirmText: '是',
|
||||||
|
confirmColor: '#03a9f4'
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
this.$httpApi.admin
|
||||||
|
.deleteJournalsById(journal.id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.status == 200) {
|
||||||
|
this.$emit('on-del', journal);
|
||||||
|
uni.$tm.toast('删除成功!');
|
||||||
|
} else {
|
||||||
|
uni.$tm.toast('Ծ‸Ծ删除失败~');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
uni.$tm.toast('Ծ‸Ծ删除失败~');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.journal-card {
|
||||||
|
box-sizing: border-box;
|
||||||
|
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||||
|
overflow: hidden;
|
||||||
|
.avatar {
|
||||||
|
width: 70rpx;
|
||||||
|
height: 70rpx;
|
||||||
|
border: 6rpx solid #fff;
|
||||||
|
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
justify-content: center;
|
||||||
|
.nickname {
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
.time {
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
<template>
|
||||||
|
<view style="overflow: hidden;position: fixed;width: 100%;height: 100%;pointer-events: none; top: 0;">
|
||||||
|
<view class="danmu-li" v-for="(item, index) in listData" :class="item.type" :style="item.style" :key="index">
|
||||||
|
<view class="danmu-inner">
|
||||||
|
<view class="user-box">
|
||||||
|
<view class="user-img">
|
||||||
|
<view class="img-box">
|
||||||
|
<image :src="item.avatar || 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=317894666,3379114684&fm=26&gp=0.jpg'"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="user-text cl1">{{ item.nickName }}</view>
|
||||||
|
<view class="user-status cl1">{{ item.text }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
//rightToLeft leftToRight leftBottom
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'rightToLeft'
|
||||||
|
},
|
||||||
|
minTime: {
|
||||||
|
type: Number,
|
||||||
|
default: 4
|
||||||
|
},
|
||||||
|
maxTime: {
|
||||||
|
type: Number,
|
||||||
|
default: 9
|
||||||
|
},
|
||||||
|
minTop: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
maxTop: {
|
||||||
|
type: Number,
|
||||||
|
default: 240
|
||||||
|
},
|
||||||
|
hrackH: {
|
||||||
|
//轨道高度
|
||||||
|
type: Number,
|
||||||
|
default: 40
|
||||||
|
},
|
||||||
|
noStacked: {
|
||||||
|
//不允许堆叠(暂不可用)
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
listData: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.hrackNum = Math.floor((this.maxTop - this.minTop) / this.hrackH);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
add(obj) {
|
||||||
|
let data = {
|
||||||
|
item: obj.item,
|
||||||
|
id: Date.parse(new Date()),
|
||||||
|
time: Math.ceil(Math.floor(Math.random() * (this.maxTime - this.minTime + 1) + this.minTime)),
|
||||||
|
type: this.type
|
||||||
|
};
|
||||||
|
if (this.type === 'leftBottom') {
|
||||||
|
let objData = {
|
||||||
|
item: data.item,
|
||||||
|
type: 'leftBottomEnter',
|
||||||
|
style: {
|
||||||
|
transition: `all 0.5s`,
|
||||||
|
animationDuration: `0.5s`,
|
||||||
|
transform: `translateX(0%)`,
|
||||||
|
bottom: `${this.minTop}px`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let listLen = this.listData.length;
|
||||||
|
let hrackNum = this.hrackNum;
|
||||||
|
for (let i in this.listData) {
|
||||||
|
if (this.listData[i].status === 'reuse') {
|
||||||
|
//重用
|
||||||
|
this.$set(this.listData, i, objData);
|
||||||
|
} else if (this.listData[i].status === 'reset') {
|
||||||
|
//重置
|
||||||
|
this.listData[i].style.transition = 'none';
|
||||||
|
this.listData[i].style.bottom = 0;
|
||||||
|
this.listData[i].status = 'reuse';
|
||||||
|
} else if (this.listData[i].status === 'recycle') {
|
||||||
|
//回收
|
||||||
|
this.listData[i].type = 'leftBottomExit';
|
||||||
|
this.listData[i].status = 'reset';
|
||||||
|
} else {
|
||||||
|
this.listData[i].style.bottom = parseInt(this.listData[i].style.bottom) + this.hrackH + 'px';
|
||||||
|
}
|
||||||
|
if (parseInt(this.listData[i].style.bottom) >= this.maxTop - this.hrackH && this.listData[i].status !== 'reset') {
|
||||||
|
//需要回收
|
||||||
|
this.listData[i].status = 'recycle';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (listLen < hrackNum + 2) {
|
||||||
|
this.listData.push(objData);
|
||||||
|
}
|
||||||
|
} else if (this.type === 'rightToLeft' || this.type === 'leftToRight') {
|
||||||
|
let objData = this.horStacked(data);
|
||||||
|
for (let i in this.listData) {
|
||||||
|
if (this.listData[i].delTime <= Date.parse(new Date())) {
|
||||||
|
this.repaint(i, objData.type);
|
||||||
|
objData.type = '';
|
||||||
|
this.$set(this.listData, i, objData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.listData.push(objData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
horStacked(data) {
|
||||||
|
return {
|
||||||
|
item: data.item,
|
||||||
|
type: this.type,
|
||||||
|
style: {
|
||||||
|
animationDuration: `${data.time}s`,
|
||||||
|
top: `${Math.ceil(Math.random() * (this.maxTop - this.minTop + 1) + this.minTop)}px`
|
||||||
|
},
|
||||||
|
delTime: Date.parse(new Date()) + data.time * 1200
|
||||||
|
};
|
||||||
|
},
|
||||||
|
repaint(index, type) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.listData[index].type = type;
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style></style>
|
||||||
|
<style lang="scss">
|
||||||
|
@keyframes leftBottomEnter {
|
||||||
|
0% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(0%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes leftBottomExit {
|
||||||
|
0% {
|
||||||
|
transform: translateY(0%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(-200%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes leftToRight {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rightToLeft {
|
||||||
|
0% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.danmu-li {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
transform: translateX(100%);
|
||||||
|
animation-timing-function: linear;
|
||||||
|
|
||||||
|
&.leftBottomEnter {
|
||||||
|
animation-name: leftBottomEnter;
|
||||||
|
}
|
||||||
|
&.leftBottomExit {
|
||||||
|
animation-name: leftBottomExit;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.rightToLeft {
|
||||||
|
animation-name: rightToLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.leftToRight {
|
||||||
|
animation-name: leftToRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.danmu-inner {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
.user-box {
|
||||||
|
display: flex;
|
||||||
|
padding: 3rpx 40rpx 3rpx 10rpx;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
border-radius: 32rpx;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.user-img {
|
||||||
|
.img-box {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
image {
|
||||||
|
width: 58rpx;
|
||||||
|
height: 58rpx;
|
||||||
|
background: rgba(55, 55, 55, 1);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-status {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 400;
|
||||||
|
color: rgba(255, 255, 255, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-text {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
// white-space: nowrap;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 400;
|
||||||
|
width: 80rpx;
|
||||||
|
color: rgba(255, 255, 255, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#如何使用
|
||||||
|
###js
|
||||||
|
```javascript
|
||||||
|
import lffBarrage from '@/components/lff-barrage/lff-barrage.vue'
|
||||||
|
components:{lffBarrage},
|
||||||
|
methods:{
|
||||||
|
colrdo(){ //插入一条弹幕
|
||||||
|
this.$refs.lffBarrage.add({item:'你好呀小伙子'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
###HTML
|
||||||
|
```html
|
||||||
|
<lff-barrage ref="lffBarrage"></lff-barrage>
|
||||||
|
```
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// 以下项目可以删减或更换顺序,但不能添加或更改名字
|
||||||
|
export default {
|
||||||
|
// 普通标签的菜单项
|
||||||
|
node: ['大小', '斜体', '粗体', '下划线', '居中', '缩进', '上移', '下移', '删除'],
|
||||||
|
// 图片的菜单项
|
||||||
|
img: ['换图', '宽度', '超链接', '预览图', '禁用预览', '上移', '下移', '删除'],
|
||||||
|
// 链接的菜单项
|
||||||
|
link: ['更换链接', '上移', '下移', '删除'],
|
||||||
|
// 音视频的菜单项
|
||||||
|
media: ['封面', '循环', '自动播放', '上移', '下移', '删除']
|
||||||
|
}
|
||||||
@@ -0,0 +1,532 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview editable 插件
|
||||||
|
*/
|
||||||
|
import config from './config'
|
||||||
|
import Parser from '../parser'
|
||||||
|
|
||||||
|
function Editable (vm) {
|
||||||
|
this.vm = vm
|
||||||
|
this.editHistory = [] // 历史记录
|
||||||
|
this.editI = -1 // 历史记录指针
|
||||||
|
vm._mask = [] // 蒙版被点击时进行的操作
|
||||||
|
|
||||||
|
vm._setData = function (path, val) {
|
||||||
|
const paths = path.split('.')
|
||||||
|
let target = vm
|
||||||
|
for (let i = 0; i < paths.length - 1; i++) {
|
||||||
|
target = target[paths[i]]
|
||||||
|
}
|
||||||
|
vm.$set(target, paths.pop(), val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 移动历史记录指针
|
||||||
|
* @param {Number} num 移动距离
|
||||||
|
*/
|
||||||
|
const move = num => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const item = this.editHistory[this.editI + num]
|
||||||
|
if (item) {
|
||||||
|
this.editI += num
|
||||||
|
vm._setData(item.key, item.value)
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
vm.undo = () => move(-1) // 撤销
|
||||||
|
vm.redo = () => move(1) // 重做
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 更新记录
|
||||||
|
* @param {String} path 更新内容路径
|
||||||
|
* @param {*} oldVal 旧值
|
||||||
|
* @param {*} newVal 新值
|
||||||
|
* @param {Boolean} set 是否更新到视图
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
vm._editVal = (path, oldVal, newVal, set) => {
|
||||||
|
// 当前指针后的内容去除
|
||||||
|
while (this.editI < this.editHistory.length - 1) {
|
||||||
|
this.editHistory.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最多存储 30 条操作记录
|
||||||
|
while (this.editHistory.length > 30) {
|
||||||
|
this.editHistory.pop()
|
||||||
|
this.editI--
|
||||||
|
}
|
||||||
|
|
||||||
|
const last = this.editHistory[this.editHistory.length - 1]
|
||||||
|
if (!last || last.key !== path) {
|
||||||
|
if (last) {
|
||||||
|
// 去掉上一次的新值
|
||||||
|
this.editHistory.pop()
|
||||||
|
this.editI--
|
||||||
|
}
|
||||||
|
// 存入这一次的旧值
|
||||||
|
this.editHistory.push({
|
||||||
|
key: path,
|
||||||
|
value: oldVal
|
||||||
|
})
|
||||||
|
this.editI++
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存入本次的新值
|
||||||
|
this.editHistory.push({
|
||||||
|
key: path,
|
||||||
|
value: newVal
|
||||||
|
})
|
||||||
|
this.editI++
|
||||||
|
|
||||||
|
// 更新到视图
|
||||||
|
if (set) {
|
||||||
|
vm._setData(path, newVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取菜单项
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
vm._getItem = function (node, up, down) {
|
||||||
|
let items
|
||||||
|
let i
|
||||||
|
if (node.name === 'img') {
|
||||||
|
items = config.img.slice(0)
|
||||||
|
if (!vm.getSrc) {
|
||||||
|
i = items.indexOf('换图')
|
||||||
|
if (i !== -1) {
|
||||||
|
items.splice(i, 1)
|
||||||
|
}
|
||||||
|
i = items.indexOf('超链接')
|
||||||
|
if (i !== -1) {
|
||||||
|
items.splice(i, 1)
|
||||||
|
}
|
||||||
|
i = items.indexOf('预览图')
|
||||||
|
if (i !== -1) {
|
||||||
|
items.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = items.indexOf('禁用预览')
|
||||||
|
if (i !== -1 && node.attrs.ignore) {
|
||||||
|
items[i] = '启用预览'
|
||||||
|
}
|
||||||
|
} else if (node.name === 'a') {
|
||||||
|
items = config.link.slice(0)
|
||||||
|
if (!vm.getSrc) {
|
||||||
|
i = items.indexOf('更换链接')
|
||||||
|
if (i !== -1) {
|
||||||
|
items.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (node.name === 'video' || node.name === 'audio') {
|
||||||
|
items = config.media.slice(0)
|
||||||
|
i = items.indexOf('封面')
|
||||||
|
if (!vm.getSrc && i !== -1) {
|
||||||
|
items.splice(i, 1)
|
||||||
|
}
|
||||||
|
i = items.indexOf('循环')
|
||||||
|
if (node.attrs.loop && i !== -1) {
|
||||||
|
items[i] = '不循环'
|
||||||
|
}
|
||||||
|
i = items.indexOf('自动播放')
|
||||||
|
if (node.attrs.autoplay && i !== -1) {
|
||||||
|
items[i] = '不自动播放'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items = config.node.slice(0)
|
||||||
|
}
|
||||||
|
if (!up) {
|
||||||
|
i = items.indexOf('上移')
|
||||||
|
if (i !== -1) {
|
||||||
|
items.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!down) {
|
||||||
|
i = items.indexOf('下移')
|
||||||
|
if (i !== -1) {
|
||||||
|
items.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 显示 tooltip
|
||||||
|
* @param {object} obj
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
vm._tooltip = function (obj) {
|
||||||
|
vm.$set(vm, 'tooltip', {
|
||||||
|
top: obj.top,
|
||||||
|
items: obj.items
|
||||||
|
})
|
||||||
|
vm._tooltipcb = obj.success
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 显示滚动条
|
||||||
|
* @param {object} obj
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
vm._slider = function (obj) {
|
||||||
|
vm.$set(vm, 'slider', {
|
||||||
|
min: obj.min,
|
||||||
|
max: obj.max,
|
||||||
|
value: obj.value,
|
||||||
|
top: obj.top
|
||||||
|
})
|
||||||
|
vm._slideringcb = obj.changing
|
||||||
|
vm._slidercb = obj.change
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 点击蒙版
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
vm._maskTap = function () {
|
||||||
|
// 隐藏所有悬浮窗
|
||||||
|
while (vm._mask.length) {
|
||||||
|
(vm._mask.pop())()
|
||||||
|
}
|
||||||
|
if (vm.tooltip) {
|
||||||
|
vm.$set(vm, 'tooltip', null)
|
||||||
|
}
|
||||||
|
if (vm.slider) {
|
||||||
|
vm.$set(vm, 'slider', null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 插入节点
|
||||||
|
* @param {Object} node
|
||||||
|
*/
|
||||||
|
function insert (node) {
|
||||||
|
if (vm._edit) {
|
||||||
|
vm._edit.insert(node)
|
||||||
|
} else {
|
||||||
|
const nodes = vm.nodes.slice(0)
|
||||||
|
nodes.push(node)
|
||||||
|
vm._editVal('nodes', vm.nodes, nodes, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 在光标处插入指定 html 内容
|
||||||
|
* @param {String} html 内容
|
||||||
|
*/
|
||||||
|
vm.insertHtml = html => {
|
||||||
|
this.inserting = true
|
||||||
|
const arr = new Parser(vm).parse(html)
|
||||||
|
this.inserting = undefined
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
insert(arr[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 在光标处插入图片
|
||||||
|
*/
|
||||||
|
vm.insertImg = function () {
|
||||||
|
vm.getSrc && vm.getSrc('img').then(src => {
|
||||||
|
if (typeof src === 'string') {
|
||||||
|
src = [src]
|
||||||
|
}
|
||||||
|
const parser = new Parser(vm)
|
||||||
|
for (let i = 0; i < src.length; i++) {
|
||||||
|
insert({
|
||||||
|
name: 'img',
|
||||||
|
attrs: {
|
||||||
|
src: parser.getUrl(src[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(() => { })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 在光标处插入一个链接
|
||||||
|
*/
|
||||||
|
vm.insertLink = function () {
|
||||||
|
vm.getSrc && vm.getSrc('link').then(url => {
|
||||||
|
insert({
|
||||||
|
name: 'a',
|
||||||
|
attrs: {
|
||||||
|
href: url
|
||||||
|
},
|
||||||
|
children: [{
|
||||||
|
type: 'text',
|
||||||
|
text: url
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}).catch(() => { })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 在光标处插入一个表格
|
||||||
|
* @param {Number} rows 行数
|
||||||
|
* @param {Number} cols 列数
|
||||||
|
*/
|
||||||
|
vm.insertTable = function (rows, cols) {
|
||||||
|
const table = {
|
||||||
|
name: 'table',
|
||||||
|
attrs: {
|
||||||
|
style: 'display:table;width:100%;margin:10px 0;text-align:center;border-spacing:0;border-collapse:collapse;border:1px solid gray'
|
||||||
|
},
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
for (let i = 0; i < rows; i++) {
|
||||||
|
const tr = {
|
||||||
|
name: 'tr',
|
||||||
|
attrs: {},
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
for (let j = 0; j < cols; j++) {
|
||||||
|
tr.children.push({
|
||||||
|
name: 'td',
|
||||||
|
attrs: {
|
||||||
|
style: 'padding:2px;border:1px solid gray'
|
||||||
|
},
|
||||||
|
children: [{
|
||||||
|
type: 'text',
|
||||||
|
text: ''
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
table.children.push(tr)
|
||||||
|
}
|
||||||
|
insert(table)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 插入视频/音频
|
||||||
|
* @param {Object} node
|
||||||
|
*/
|
||||||
|
function insertMedia (node) {
|
||||||
|
if (typeof node.src === 'string') {
|
||||||
|
node.src = [node.src]
|
||||||
|
}
|
||||||
|
const parser = new Parser(vm)
|
||||||
|
// 拼接主域名
|
||||||
|
for (let i = 0; i < node.src.length; i++) {
|
||||||
|
node.src[i] = parser.getUrl(node.src[i])
|
||||||
|
}
|
||||||
|
insert({
|
||||||
|
name: 'div',
|
||||||
|
attrs: {
|
||||||
|
style: 'text-align:center'
|
||||||
|
},
|
||||||
|
children: [node]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 在光标处插入一个视频
|
||||||
|
*/
|
||||||
|
vm.insertVideo = function () {
|
||||||
|
vm.getSrc && vm.getSrc('video').then(src => {
|
||||||
|
insertMedia({
|
||||||
|
name: 'video',
|
||||||
|
attrs: {
|
||||||
|
controls: 'T'
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
src,
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
html: `<video src="${src}" style="width:100%;height:100%"></video>`
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}).catch(() => { })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 在光标处插入一个音频
|
||||||
|
*/
|
||||||
|
vm.insertAudio = function () {
|
||||||
|
vm.getSrc && vm.getSrc('audio').then(attrs => {
|
||||||
|
let src
|
||||||
|
if (attrs.src) {
|
||||||
|
src = attrs.src
|
||||||
|
attrs.src = undefined
|
||||||
|
} else {
|
||||||
|
src = attrs
|
||||||
|
attrs = {}
|
||||||
|
}
|
||||||
|
attrs.controls = 'T'
|
||||||
|
insertMedia({
|
||||||
|
name: 'audio',
|
||||||
|
attrs,
|
||||||
|
children: [],
|
||||||
|
src
|
||||||
|
})
|
||||||
|
}).catch(() => { })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 在光标处插入一段文本
|
||||||
|
*/
|
||||||
|
vm.insertText = function () {
|
||||||
|
insert({
|
||||||
|
name: 'p',
|
||||||
|
attrs: {},
|
||||||
|
children: [{
|
||||||
|
type: 'text',
|
||||||
|
text: ''
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 清空内容
|
||||||
|
*/
|
||||||
|
vm.clear = function () {
|
||||||
|
vm._maskTap()
|
||||||
|
vm._edit = undefined
|
||||||
|
vm.$set(vm, 'nodes', [{
|
||||||
|
name: 'p',
|
||||||
|
attrs: {},
|
||||||
|
children: [{
|
||||||
|
type: 'text',
|
||||||
|
text: ''
|
||||||
|
}]
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取编辑后的 html
|
||||||
|
*/
|
||||||
|
vm.getContent = function () {
|
||||||
|
let html = '';
|
||||||
|
// 递归遍历获取
|
||||||
|
(function traversal (nodes, table) {
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
let item = nodes[i]
|
||||||
|
if (item.type === 'text') {
|
||||||
|
html += item.text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>').replace(/\xa0/g, ' ') // 编码实体
|
||||||
|
} else {
|
||||||
|
if (item.name === 'img') {
|
||||||
|
item.attrs.i = ''
|
||||||
|
// 还原被转换的 svg
|
||||||
|
if ((item.attrs.src || '').includes('data:image/svg+xml;utf8,')) {
|
||||||
|
html += item.attrs.src.substr(24).replace(/%23/g, '#').replace('<svg', '<svg style="' + (item.attrs.style || '') + '"')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if (item.name === 'video' || item.name === 'audio') {
|
||||||
|
// 还原 video 和 audio 的 source
|
||||||
|
item = JSON.parse(JSON.stringify(item))
|
||||||
|
if (item.src.length > 1) {
|
||||||
|
item.children = []
|
||||||
|
for (let j = 0; j < item.src.length; j++) {
|
||||||
|
item.children.push({
|
||||||
|
name: 'source',
|
||||||
|
attrs: {
|
||||||
|
src: item.src[j]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item.attrs.src = item.src[0]
|
||||||
|
}
|
||||||
|
} else if (item.name === 'div' && (item.attrs.style || '').includes('overflow:auto') && (item.children[0] || {}).name === 'table') {
|
||||||
|
// 还原滚动层
|
||||||
|
item = item.children[0]
|
||||||
|
}
|
||||||
|
// 还原 table
|
||||||
|
if (item.name === 'table') {
|
||||||
|
item = JSON.parse(JSON.stringify(item))
|
||||||
|
table = item.attrs
|
||||||
|
if ((item.attrs.style || '').includes('display:grid')) {
|
||||||
|
item.attrs.style = item.attrs.style.split('display:grid')[0]
|
||||||
|
const children = [{
|
||||||
|
name: 'tr',
|
||||||
|
attrs: {},
|
||||||
|
children: []
|
||||||
|
}]
|
||||||
|
for (let j = 0; j < item.children.length; j++) {
|
||||||
|
item.children[j].attrs.style = item.children[j].attrs.style.replace(/grid-[^;]+;*/g, '')
|
||||||
|
if (item.children[j].r !== children.length) {
|
||||||
|
children.push({
|
||||||
|
name: 'tr',
|
||||||
|
attrs: {},
|
||||||
|
children: [item.children[j]]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
children[children.length - 1].children.push(item.children[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.children = children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html += '<' + item.name
|
||||||
|
for (const attr in item.attrs) {
|
||||||
|
let val = item.attrs[attr]
|
||||||
|
if (!val) continue
|
||||||
|
if (val === 'T' || val === true) {
|
||||||
|
// bool 型省略值
|
||||||
|
html += ' ' + attr
|
||||||
|
continue
|
||||||
|
} else if (item.name[0] === 't' && attr === 'style' && table) {
|
||||||
|
// 取消为了显示 table 添加的 style
|
||||||
|
val = val.replace(/;*display:table[^;]*/, '')
|
||||||
|
if (table.border) {
|
||||||
|
val = val.replace(/border[^;]+;*/g, $ => $.includes('collapse') ? $ : '')
|
||||||
|
}
|
||||||
|
if (table.cellpadding) {
|
||||||
|
val = val.replace(/padding[^;]+;*/g, '')
|
||||||
|
}
|
||||||
|
if (!val) continue
|
||||||
|
}
|
||||||
|
html += ' ' + attr + '="' + val.replace(/"/g, '"') + '"'
|
||||||
|
}
|
||||||
|
html += '>'
|
||||||
|
if (item.children) {
|
||||||
|
traversal(item.children, table)
|
||||||
|
html += '</' + item.name + '>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(vm.nodes)
|
||||||
|
|
||||||
|
// 其他插件处理
|
||||||
|
for (let i = vm.plugins.length; i--;) {
|
||||||
|
if (vm.plugins[i].onGetContent) {
|
||||||
|
html = vm.plugins[i].onGetContent(html) || html
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Editable.prototype.onUpdate = function (content, config) {
|
||||||
|
if (this.vm.editable) {
|
||||||
|
this.vm._maskTap()
|
||||||
|
config.entities.amp = '&'
|
||||||
|
if (!this.inserting) {
|
||||||
|
this.vm._edit = undefined
|
||||||
|
if (!content) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.vm.$set(this.vm, 'nodes', [{
|
||||||
|
name: 'p',
|
||||||
|
attrs: {},
|
||||||
|
children: [{
|
||||||
|
type: 'text',
|
||||||
|
text: ''
|
||||||
|
}]
|
||||||
|
}])
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Editable.prototype.onParse = function (node) {
|
||||||
|
// 空白单元格可编辑
|
||||||
|
if (this.vm.editable && (node.name === 'td' || node.name === 'th') && !this.vm.getText(node.children)) {
|
||||||
|
node.children.push({
|
||||||
|
type: 'text',
|
||||||
|
text: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Editable
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview emoji 插件
|
||||||
|
*/
|
||||||
|
const reg = /\[(\S+?)\]/g
|
||||||
|
const data = {
|
||||||
|
笑脸: '😄',
|
||||||
|
生病: '😷',
|
||||||
|
破涕为笑: '😂',
|
||||||
|
吐舌: '😝',
|
||||||
|
脸红: '😳',
|
||||||
|
恐惧: '😱',
|
||||||
|
失望: '😔',
|
||||||
|
无语: '😒',
|
||||||
|
眨眼: '😉',
|
||||||
|
酷: '😎',
|
||||||
|
哭: '😭',
|
||||||
|
痴迷: '😍',
|
||||||
|
吻: '😘',
|
||||||
|
思考: '🤔',
|
||||||
|
困惑: '😕',
|
||||||
|
颠倒: '🙃',
|
||||||
|
钱: '🤑',
|
||||||
|
惊讶: '😲',
|
||||||
|
白眼: '🙄',
|
||||||
|
叹气: '😤',
|
||||||
|
睡觉: '😴',
|
||||||
|
书呆子: '🤓',
|
||||||
|
愤怒: '😡',
|
||||||
|
面无表情: '😑',
|
||||||
|
张嘴: '😮',
|
||||||
|
量体温: '🤒',
|
||||||
|
呕吐: '🤮',
|
||||||
|
光环: '😇',
|
||||||
|
幽灵: '👻',
|
||||||
|
外星人: '👽',
|
||||||
|
机器人: '🤖',
|
||||||
|
捂眼镜: '🙈',
|
||||||
|
捂耳朵: '🙉',
|
||||||
|
捂嘴: '🙊',
|
||||||
|
婴儿: '👶',
|
||||||
|
男孩: '👦',
|
||||||
|
女孩: '👧',
|
||||||
|
男人: '👨',
|
||||||
|
女人: '👩',
|
||||||
|
老人: '👴',
|
||||||
|
老妇人: '👵',
|
||||||
|
警察: '👮',
|
||||||
|
王子: '🤴',
|
||||||
|
公主: '🤴',
|
||||||
|
举手: '🙋',
|
||||||
|
跑步: '🏃',
|
||||||
|
家庭: '👪',
|
||||||
|
眼睛: '👀',
|
||||||
|
鼻子: '👃',
|
||||||
|
耳朵: '👂',
|
||||||
|
舌头: '👅',
|
||||||
|
嘴: '👄',
|
||||||
|
心: '❤️',
|
||||||
|
心碎: '💔',
|
||||||
|
雪人: '☃️',
|
||||||
|
情书: '💌',
|
||||||
|
大便: '💩',
|
||||||
|
闹钟: '⏰',
|
||||||
|
眼镜: '👓',
|
||||||
|
雨伞: '☂️',
|
||||||
|
音乐: '🎵',
|
||||||
|
话筒: '🎤',
|
||||||
|
游戏机: '🎮',
|
||||||
|
喇叭: '📢',
|
||||||
|
耳机: '🎧',
|
||||||
|
礼物: '🎁',
|
||||||
|
电话: '📞',
|
||||||
|
电脑: '💻',
|
||||||
|
打印机: '🖨️',
|
||||||
|
手电筒: '🔦',
|
||||||
|
灯泡: '💡',
|
||||||
|
书本: '📖',
|
||||||
|
信封: '✉️',
|
||||||
|
药丸: '💊',
|
||||||
|
口红: '💄',
|
||||||
|
手机: '📱',
|
||||||
|
相机: '📷',
|
||||||
|
电视: '📺',
|
||||||
|
中: '🀄',
|
||||||
|
垃圾桶: '🚮',
|
||||||
|
厕所: '🚾',
|
||||||
|
感叹号: '❗',
|
||||||
|
禁: '🈲',
|
||||||
|
可: '🉑',
|
||||||
|
彩虹: '🌈',
|
||||||
|
旋风: '🌀',
|
||||||
|
雷电: '⚡',
|
||||||
|
雪花: '❄️',
|
||||||
|
星星: '⭐',
|
||||||
|
水滴: '💧',
|
||||||
|
玫瑰: '🌹',
|
||||||
|
加油: '💪',
|
||||||
|
左: '👈',
|
||||||
|
右: '👉',
|
||||||
|
上: '👆',
|
||||||
|
下: '👇',
|
||||||
|
手掌: '🖐️',
|
||||||
|
好的: '👌',
|
||||||
|
好: '👍',
|
||||||
|
差: '👎',
|
||||||
|
胜利: '✌',
|
||||||
|
拳头: '👊',
|
||||||
|
挥手: '👋',
|
||||||
|
鼓掌: '👏',
|
||||||
|
猴子: '🐒',
|
||||||
|
狗: '🐶',
|
||||||
|
狼: '🐺',
|
||||||
|
猫: '🐱',
|
||||||
|
老虎: '🐯',
|
||||||
|
马: '🐎',
|
||||||
|
独角兽: '🦄',
|
||||||
|
斑马: '🦓',
|
||||||
|
鹿: '🦌',
|
||||||
|
牛: '🐮',
|
||||||
|
猪: '🐷',
|
||||||
|
羊: '🐏',
|
||||||
|
长颈鹿: '🦒',
|
||||||
|
大象: '🐘',
|
||||||
|
老鼠: '🐭',
|
||||||
|
蝙蝠: '🦇',
|
||||||
|
刺猬: '🦔',
|
||||||
|
熊猫: '🐼',
|
||||||
|
鸽子: '🕊️',
|
||||||
|
鸭子: '🦆',
|
||||||
|
兔子: '🐇',
|
||||||
|
老鹰: '🦅',
|
||||||
|
青蛙: '🐸',
|
||||||
|
蛇: '🐍',
|
||||||
|
龙: '🐉',
|
||||||
|
鲸鱼: '🐳',
|
||||||
|
海豚: '🐬',
|
||||||
|
足球: '⚽',
|
||||||
|
棒球: '⚾',
|
||||||
|
篮球: '🏀',
|
||||||
|
排球: '🏐',
|
||||||
|
橄榄球: '🏉',
|
||||||
|
网球: '🎾',
|
||||||
|
骰子: '🎲',
|
||||||
|
鸡腿: '🍗',
|
||||||
|
蛋糕: '🎂',
|
||||||
|
啤酒: '🍺',
|
||||||
|
饺子: '🥟',
|
||||||
|
汉堡: '🍔',
|
||||||
|
薯条: '🍟',
|
||||||
|
意大利面: '🍝',
|
||||||
|
干杯: '🥂',
|
||||||
|
筷子: '🥢',
|
||||||
|
糖果: '🍬',
|
||||||
|
奶瓶: '🍼',
|
||||||
|
爆米花: '🍿',
|
||||||
|
邮局: '🏤',
|
||||||
|
医院: '🏥',
|
||||||
|
银行: '🏦',
|
||||||
|
酒店: '🏨',
|
||||||
|
学校: '🏫',
|
||||||
|
城堡: '🏰',
|
||||||
|
火车: '🚂',
|
||||||
|
高铁: '🚄',
|
||||||
|
地铁: '🚇',
|
||||||
|
公交: '🚌',
|
||||||
|
救护车: '🚑',
|
||||||
|
消防车: '🚒',
|
||||||
|
警车: '🚓',
|
||||||
|
出租车: '🚕',
|
||||||
|
汽车: '🚗',
|
||||||
|
货车: '🚛',
|
||||||
|
自行车: '🚲',
|
||||||
|
摩托: '🛵',
|
||||||
|
红绿灯: '🚥',
|
||||||
|
帆船: '⛵',
|
||||||
|
游轮: '🛳️',
|
||||||
|
轮船: '⛴️',
|
||||||
|
飞机: '✈️',
|
||||||
|
直升机: '🚁',
|
||||||
|
缆车: '🚠',
|
||||||
|
警告: '⚠️',
|
||||||
|
禁止: '⛔'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Emoji () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Emoji.prototype.onUpdate = function (content) {
|
||||||
|
return content.replace(reg, ($, $1) => {
|
||||||
|
if (data[$1]) return data[$1]
|
||||||
|
return $
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Emoji.prototype.onGetContent = function (content) {
|
||||||
|
for (const item in data) {
|
||||||
|
content = content.replace(new RegExp(data[item], 'g'), '[' + item + ']')
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Emoji
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export default {
|
||||||
|
copyByLongPress: true, // 是否需要长按代码块时显示复制代码内容菜单
|
||||||
|
showLanguageName: true, // 是否在代码块右上角显示语言的名称
|
||||||
|
showLineNumber: true // 是否显示行号
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview highlight 插件
|
||||||
|
* Include prismjs (https://prismjs.com)
|
||||||
|
*/
|
||||||
|
import prism from './prism.min'
|
||||||
|
import config from './config'
|
||||||
|
import Parser from '../parser'
|
||||||
|
|
||||||
|
function Highlight (vm) {
|
||||||
|
this.vm = vm
|
||||||
|
}
|
||||||
|
|
||||||
|
Highlight.prototype.onParse = function (node, vm) {
|
||||||
|
if (node.name === 'pre') {
|
||||||
|
if (vm.options.editable) {
|
||||||
|
node.attrs.class = (node.attrs.class || '') + ' hl-pre'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let i
|
||||||
|
for (i = node.children.length; i--;) {
|
||||||
|
if (node.children[i].name === 'code') break
|
||||||
|
}
|
||||||
|
if (i === -1) return
|
||||||
|
const code = node.children[i]
|
||||||
|
let className = code.attrs.class + ' ' + node.attrs.class
|
||||||
|
i = className.indexOf('language-')
|
||||||
|
if (i === -1) {
|
||||||
|
i = className.indexOf('lang-')
|
||||||
|
if (i === -1) {
|
||||||
|
className = 'language-text'
|
||||||
|
i = 9
|
||||||
|
} else {
|
||||||
|
i += 5
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i += 9
|
||||||
|
}
|
||||||
|
let j
|
||||||
|
for (j = i; j < className.length; j++) {
|
||||||
|
if (className[j] === ' ') break
|
||||||
|
}
|
||||||
|
const lang = className.substring(i, j)
|
||||||
|
if (code.children.length) {
|
||||||
|
const text = this.vm.getText(code.children).replace(/&/g, '&')
|
||||||
|
if (!text) return
|
||||||
|
if (node.c) {
|
||||||
|
node.c = undefined
|
||||||
|
}
|
||||||
|
if (prism.languages[lang]) {
|
||||||
|
code.children = (new Parser(this.vm).parse(
|
||||||
|
// 加一层 pre 保留空白符
|
||||||
|
'<pre>' + prism.highlight(text, prism.languages[lang], lang).replace(/token /g, 'hl-') + '</pre>'))[0].children
|
||||||
|
}
|
||||||
|
node.attrs.class = 'hl-pre'
|
||||||
|
code.attrs.class = 'hl-code'
|
||||||
|
if (config.showLanguageName) {
|
||||||
|
node.children.push({
|
||||||
|
name: 'div',
|
||||||
|
attrs: {
|
||||||
|
class: 'hl-language',
|
||||||
|
style: 'user-select:none'
|
||||||
|
},
|
||||||
|
children: [{
|
||||||
|
type: 'text',
|
||||||
|
text: lang
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (config.copyByLongPress) {
|
||||||
|
node.attrs.style += (node.attrs.style || '') + ';user-select:none'
|
||||||
|
node.attrs['data-content'] = text
|
||||||
|
vm.expose()
|
||||||
|
}
|
||||||
|
if (config.showLineNumber) {
|
||||||
|
const line = text.split('\n').length; const children = []
|
||||||
|
for (let k = line; k--;) {
|
||||||
|
children.push({
|
||||||
|
name: 'span',
|
||||||
|
attrs: {
|
||||||
|
class: 'span'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
node.children.push({
|
||||||
|
name: 'span',
|
||||||
|
attrs: {
|
||||||
|
class: 'line-numbers-rows'
|
||||||
|
},
|
||||||
|
children
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Highlight
|
||||||
File diff soppresso perché una o più righe sono troppo lunghe
@@ -0,0 +1,138 @@
|
|||||||
|
const data = {
|
||||||
|
name: 'imgcache',
|
||||||
|
prefix: 'imgcache_'
|
||||||
|
}
|
||||||
|
function ImgCache (vm) {
|
||||||
|
this.vm = vm // 保存实例在其他周期使用
|
||||||
|
this.i = 0 // 用于标记第几张图
|
||||||
|
vm.imgCache = {
|
||||||
|
get list () {
|
||||||
|
return uni
|
||||||
|
.getStorageInfoSync()
|
||||||
|
.keys.filter((key) => key.startsWith(data.prefix))
|
||||||
|
.map((key) => key.split(data.prefix)[1])
|
||||||
|
},
|
||||||
|
get (url) {
|
||||||
|
return uni.getStorageSync(data.prefix + url)
|
||||||
|
},
|
||||||
|
delete (url) {
|
||||||
|
const path = uni.getStorageSync(data.prefix + url)
|
||||||
|
if (!path) return false
|
||||||
|
plus.io.resolveLocalFileSystemURL(path, (entry) => {
|
||||||
|
entry.remove()
|
||||||
|
})
|
||||||
|
uni.removeStorageSync(data.prefix + url)
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
async add (url) {
|
||||||
|
const filename = await download(url)
|
||||||
|
if (filename) {
|
||||||
|
uni.setStorageSync(data.prefix + url, filename)
|
||||||
|
return 'file://' + plus.io.convertLocalFileSystemURL(filename)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
clear () {
|
||||||
|
uni
|
||||||
|
.getStorageInfoSync()
|
||||||
|
.keys.filter((key) => key.startsWith(data.prefix))
|
||||||
|
.forEach((key) => {
|
||||||
|
uni.removeStorageSync(key)
|
||||||
|
})
|
||||||
|
|
||||||
|
plus.io.resolveLocalFileSystemURL(`_doc/${data.name}/`, (entry) => {
|
||||||
|
entry.removeRecursively(
|
||||||
|
(entry) => {
|
||||||
|
console.log(`${data.name}缓存删除成功`, entry)
|
||||||
|
},
|
||||||
|
(e) => {
|
||||||
|
console.log(`${data.name}缓存删除失败`, e)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
ImgCache.prototype.onParse = function (node, parser) {
|
||||||
|
// 启用本插件 && 解析图片标签 && 拥有src属性 && 是网络图片
|
||||||
|
if (
|
||||||
|
this.vm.ImgCache &&
|
||||||
|
node.name === 'img' &&
|
||||||
|
node.attrs.src &&
|
||||||
|
/^https?:\/\//.test(node.attrs.src)
|
||||||
|
) {
|
||||||
|
const src = node.attrs.src
|
||||||
|
node.attrs.src = ''
|
||||||
|
node.attrs.i = this.vm.imgList.length + this.i++
|
||||||
|
parser.expose()
|
||||||
|
|
||||||
|
async function getUrl (path) {
|
||||||
|
if (await resolveFile(path)) return path
|
||||||
|
const filename = await download(src)
|
||||||
|
filename && uni.setStorageSync(data.prefix + src, filename)
|
||||||
|
return filename
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.getStorage({
|
||||||
|
key: data.prefix + src,
|
||||||
|
success: async (res) => {
|
||||||
|
const path = await getUrl(res.data)
|
||||||
|
const url = path
|
||||||
|
? 'file://' + plus.io.convertLocalFileSystemURL(path)
|
||||||
|
: src
|
||||||
|
node.attrs.src = url
|
||||||
|
this.vm.imgList[node.attrs.i] = path || src
|
||||||
|
},
|
||||||
|
fail: async () => {
|
||||||
|
const path = await getUrl()
|
||||||
|
const url = path
|
||||||
|
? 'file://' + plus.io.convertLocalFileSystemURL(path)
|
||||||
|
: src
|
||||||
|
node.attrs.src = url
|
||||||
|
this.vm.imgList[node.attrs.i] = path || src
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskQueue = new Set()
|
||||||
|
|
||||||
|
function download (url) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (taskQueue.has(url)) return
|
||||||
|
taskQueue.add(url)
|
||||||
|
const suffix = /.+\.(jpg|jpeg|png|bmp|gif|webp)/.exec(url)
|
||||||
|
const name = `${makeid(8)}_${Date.now()}${suffix ? '.' + suffix[1] : ''}`
|
||||||
|
const task = plus.downloader.createDownload(
|
||||||
|
url,
|
||||||
|
{ filename: `_doc/${data.name}/${name}` },
|
||||||
|
(download, status) => {
|
||||||
|
taskQueue.delete(url)
|
||||||
|
resolve(status === 200 ? download.filename : null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
task.start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断文件存在
|
||||||
|
function resolveFile (url) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
plus.io.resolveLocalFileSystemURL(url, resolve, () => resolve(null))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成uuid
|
||||||
|
function makeid (length) {
|
||||||
|
let result = ''
|
||||||
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * characters.length))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
export default ImgCache
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview markdown 插件
|
||||||
|
* Include marked (https://github.com/markedjs/marked)
|
||||||
|
* Include github-markdown-css (https://github.com/sindresorhus/github-markdown-css)
|
||||||
|
*/
|
||||||
|
import marked from './marked.min'
|
||||||
|
let index = 0
|
||||||
|
|
||||||
|
function Markdown (vm) {
|
||||||
|
this.vm = vm
|
||||||
|
vm._ids = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Markdown.prototype.onUpdate = function (content) {
|
||||||
|
if (this.vm.markdown) {
|
||||||
|
return marked(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Markdown.prototype.onParse = function (node, vm) {
|
||||||
|
if (vm.options.markdown) {
|
||||||
|
// 中文 id 需要转换,否则无法跳转
|
||||||
|
if (vm.options.useAnchor && node.attrs && /[\u4e00-\u9fa5]/.test(node.attrs.id)) {
|
||||||
|
const id = 't' + index++
|
||||||
|
this.vm._ids[node.attrs.id] = id
|
||||||
|
node.attrs.id = id
|
||||||
|
}
|
||||||
|
if (node.name === 'p' || node.name === 'table' || node.name === 'tr' || node.name === 'th' || node.name === 'td' || node.name === 'blockquote' || node.name === 'pre' || node.name === 'code') {
|
||||||
|
node.attrs.class = `md-${node.name} ${node.attrs.class || ''}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Markdown
|
||||||
File diff soppresso perché una o più righe sono troppo lunghe
@@ -0,0 +1,579 @@
|
|||||||
|
<template>
|
||||||
|
<view id="_root" :class="(selectable?'_select ':'')+'_root'" :style="(editable?'min-height:200px;':'')+containerStyle" @tap="_containTap">
|
||||||
|
<slot v-if="!nodes[0]" />
|
||||||
|
<!-- #ifndef APP-PLUS-NVUE -->
|
||||||
|
<node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu,selectable,editable,placeholder,'nodes']" name="span" />
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef APP-PLUS-NVUE -->
|
||||||
|
<web-view ref="web" src="/static/app-plus/mp-html/local.html" :style="'margin-top:-2px;height:' + height + 'px'" @onPostMessage="_onMessage" />
|
||||||
|
<!-- #endif -->
|
||||||
|
<view v-if="tooltip" class="_tooltip_contain" :style="'top:'+tooltip.top+'px'">
|
||||||
|
<view class="_tooltip">
|
||||||
|
<view v-for="(item, index) in tooltip.items" v-bind:key="index" class="_tooltip_item" :data-i="index" @tap="_tooltipTap">{{item}}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="slider" class="_slider" :style="'top:'+slider.top+'px'">
|
||||||
|
<slider :value="slider.value" :min="slider.min" :max="slider.max" handle-size="14" block-size="14" show-value activeColor="white" style="padding:3px" @changing="_sliderChanging" @change="_sliderChange" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* mp-html v2.4.0
|
||||||
|
* @description 富文本组件
|
||||||
|
* @tutorial https://github.com/jin-yufeng/mp-html
|
||||||
|
* @property {String} container-style 容器的样式
|
||||||
|
* @property {String} content 用于渲染的 html 字符串
|
||||||
|
* @property {Boolean} copy-link 是否允许外部链接被点击时自动复制
|
||||||
|
* @property {String} domain 主域名,用于拼接链接
|
||||||
|
* @property {String} error-img 图片出错时的占位图链接
|
||||||
|
* @property {Boolean} lazy-load 是否开启图片懒加载
|
||||||
|
* @property {string} loading-img 图片加载过程中的占位图链接
|
||||||
|
* @property {Boolean} pause-video 是否在播放一个视频时自动暂停其他视频
|
||||||
|
* @property {Boolean} preview-img 是否允许图片被点击时自动预览
|
||||||
|
* @property {Boolean} scroll-table 是否给每个表格添加一个滚动层使其能单独横向滚动
|
||||||
|
* @property {Boolean | String} selectable 是否开启长按复制
|
||||||
|
* @property {Boolean} set-title 是否将 title 标签的内容设置到页面标题
|
||||||
|
* @property {Boolean} show-img-menu 是否允许图片被长按时显示菜单
|
||||||
|
* @property {Object} tag-style 标签的默认样式
|
||||||
|
* @property {Boolean | Number} use-anchor 是否使用锚点链接
|
||||||
|
* @event {Function} load dom 结构加载完毕时触发
|
||||||
|
* @event {Function} ready 所有图片加载完毕时触发
|
||||||
|
* @event {Function} imgtap 图片被点击时触发
|
||||||
|
* @event {Function} linktap 链接被点击时触发
|
||||||
|
* @event {Function} play 音视频播放时触发
|
||||||
|
* @event {Function} error 媒体加载出错时触发
|
||||||
|
*/
|
||||||
|
// #ifndef APP-PLUS-NVUE
|
||||||
|
import node from './node/node'
|
||||||
|
// #endif
|
||||||
|
import Parser from './parser'
|
||||||
|
import markdown from './markdown/index.js'
|
||||||
|
import emoji from './emoji/index.js'
|
||||||
|
import highlight from './highlight/index.js'
|
||||||
|
import style from './style/index.js'
|
||||||
|
import imgCache from './img-cache/index.js'
|
||||||
|
import editable from './editable/index.js'
|
||||||
|
const plugins=[markdown,emoji,highlight,style,imgCache,editable,]
|
||||||
|
// #ifdef APP-PLUS-NVUE
|
||||||
|
const dom = weex.requireModule('dom')
|
||||||
|
// #endif
|
||||||
|
export default {
|
||||||
|
name: 'mp-html',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tooltip: null,
|
||||||
|
slider: null,
|
||||||
|
nodes: [],
|
||||||
|
// #ifdef APP-PLUS-NVUE
|
||||||
|
height: 3
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
editable: Boolean,
|
||||||
|
placeholder: String,
|
||||||
|
ImgCache: Boolean,
|
||||||
|
markdown: Boolean,
|
||||||
|
containerStyle: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
copyLink: {
|
||||||
|
type: [Boolean, String],
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
domain: String,
|
||||||
|
errorImg: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
lazyLoad: {
|
||||||
|
type: [Boolean, String],
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
loadingImg: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
pauseVideo: {
|
||||||
|
type: [Boolean, String],
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
previewImg: {
|
||||||
|
type: [Boolean, String],
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
scrollTable: [Boolean, String],
|
||||||
|
selectable: [Boolean, String],
|
||||||
|
setTitle: {
|
||||||
|
type: [Boolean, String],
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
showImgMenu: {
|
||||||
|
type: [Boolean, String],
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
tagStyle: Object,
|
||||||
|
useAnchor: [Boolean, Number]
|
||||||
|
},
|
||||||
|
// #ifdef VUE3
|
||||||
|
emits: ['load', 'ready', 'imgtap', 'linktap', 'play', 'error'],
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS-NVUE
|
||||||
|
components: {
|
||||||
|
node
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
watch: {
|
||||||
|
editable(val) {
|
||||||
|
this.setContent(val ? this.content : this.getContent())
|
||||||
|
if (!val)
|
||||||
|
this._maskTap()
|
||||||
|
},
|
||||||
|
content (content) {
|
||||||
|
this.setContent(content)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.plugins = []
|
||||||
|
for (let i = plugins.length; i--;) {
|
||||||
|
this.plugins.push(new plugins[i](this))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if ((this.content || this.editable) && !this.nodes.length) {
|
||||||
|
this.setContent(this.content)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
this._hook('onDetached')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
_containTap() {
|
||||||
|
if (!this._lock && !this.slider) {
|
||||||
|
this._edit = undefined
|
||||||
|
this._maskTap()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_tooltipTap(e) {
|
||||||
|
this._tooltipcb(e.currentTarget.dataset.i)
|
||||||
|
this.$set(this, 'tooltip', null)
|
||||||
|
},
|
||||||
|
_sliderChanging(e) {
|
||||||
|
this._slideringcb(e.detail.value)
|
||||||
|
},
|
||||||
|
_sliderChange(e) {
|
||||||
|
this._slidercb(e.detail.value)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description 将锚点跳转的范围限定在一个 scroll-view 内
|
||||||
|
* @param {Object} page scroll-view 所在页面的示例
|
||||||
|
* @param {String} selector scroll-view 的选择器
|
||||||
|
* @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名
|
||||||
|
*/
|
||||||
|
in (page, selector, scrollTop) {
|
||||||
|
// #ifndef APP-PLUS-NVUE
|
||||||
|
if (page && selector && scrollTop) {
|
||||||
|
this._in = {
|
||||||
|
page,
|
||||||
|
selector,
|
||||||
|
scrollTop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 锚点跳转
|
||||||
|
* @param {String} id 要跳转的锚点 id
|
||||||
|
* @param {Number} offset 跳转位置的偏移量
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
navigateTo (id, offset) {
|
||||||
|
id = this._ids[decodeURI(id)] || id
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!this.useAnchor) {
|
||||||
|
reject(Error('Anchor is disabled'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
offset = offset || parseInt(this.useAnchor) || 0
|
||||||
|
// #ifdef APP-PLUS-NVUE
|
||||||
|
if (!id) {
|
||||||
|
dom.scrollToElement(this.$refs.web, {
|
||||||
|
offset
|
||||||
|
})
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
this._navigateTo = {
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
this.$refs.web.evalJs('uni.postMessage({data:{action:"getOffset",offset:(document.getElementById(' + id + ')||{}).offsetTop}})')
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS-NVUE
|
||||||
|
let deep = ' '
|
||||||
|
// #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
|
||||||
|
deep = '>>>'
|
||||||
|
// #endif
|
||||||
|
const selector = uni.createSelectorQuery()
|
||||||
|
// #ifndef MP-ALIPAY
|
||||||
|
.in(this._in ? this._in.page : this)
|
||||||
|
// #endif
|
||||||
|
.select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : '')).boundingClientRect()
|
||||||
|
if (this._in) {
|
||||||
|
selector.select(this._in.selector).scrollOffset()
|
||||||
|
.select(this._in.selector).boundingClientRect()
|
||||||
|
} else {
|
||||||
|
// 获取 scroll-view 的位置和滚动距离
|
||||||
|
selector.selectViewport().scrollOffset() // 获取窗口的滚动距离
|
||||||
|
}
|
||||||
|
selector.exec(res => {
|
||||||
|
if (!res[0]) {
|
||||||
|
reject(Error('Label not found'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset
|
||||||
|
if (this._in) {
|
||||||
|
// scroll-view 跳转
|
||||||
|
this._in.page[this._in.scrollTop] = scrollTop
|
||||||
|
} else {
|
||||||
|
// 页面跳转
|
||||||
|
uni.pageScrollTo({
|
||||||
|
scrollTop,
|
||||||
|
duration: 300
|
||||||
|
})
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取文本内容
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
getText (nodes) {
|
||||||
|
let text = '';
|
||||||
|
(function traversal (nodes) {
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
const node = nodes[i]
|
||||||
|
if (node.type === 'text') {
|
||||||
|
text += node.text.replace(/&/g, '&')
|
||||||
|
} else if (node.name === 'br') {
|
||||||
|
text += '\n'
|
||||||
|
} else {
|
||||||
|
// 块级标签前后加换行
|
||||||
|
const isBlock = node.name === 'p' || node.name === 'div' || node.name === 'tr' || node.name === 'li' || (node.name[0] === 'h' && node.name[1] > '0' && node.name[1] < '7')
|
||||||
|
if (isBlock && text && text[text.length - 1] !== '\n') {
|
||||||
|
text += '\n'
|
||||||
|
}
|
||||||
|
// 递归获取子节点的文本
|
||||||
|
if (node.children) {
|
||||||
|
traversal(node.children)
|
||||||
|
}
|
||||||
|
if (isBlock && text[text.length - 1] !== '\n') {
|
||||||
|
text += '\n'
|
||||||
|
} else if (node.name === 'td' || node.name === 'th') {
|
||||||
|
text += '\t'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(nodes || this.nodes)
|
||||||
|
return text
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取内容大小和位置
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
getRect () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.createSelectorQuery()
|
||||||
|
// #ifndef MP-ALIPAY
|
||||||
|
.in(this)
|
||||||
|
// #endif
|
||||||
|
.select('#_root').boundingClientRect().exec(res => res[0] ? resolve(res[0]) : reject(Error('Root label not found')))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 暂停播放媒体
|
||||||
|
*/
|
||||||
|
pauseMedia () {
|
||||||
|
for (let i = (this._videos || []).length; i--;) {
|
||||||
|
this._videos[i].pause()
|
||||||
|
}
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].pause()'
|
||||||
|
// #ifndef APP-PLUS-NVUE
|
||||||
|
let page = this.$parent
|
||||||
|
while (!page.$scope) page = page.$parent
|
||||||
|
page.$scope.$getAppWebview().evalJS(command)
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS-NVUE
|
||||||
|
this.$refs.web.evalJs(command)
|
||||||
|
// #endif
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置媒体播放速率
|
||||||
|
* @param {Number} rate 播放速率
|
||||||
|
*/
|
||||||
|
setPlaybackRate (rate) {
|
||||||
|
this.playbackRate = rate
|
||||||
|
for (let i = (this._videos || []).length; i--;) {
|
||||||
|
this._videos[i].playbackRate(rate)
|
||||||
|
}
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].playbackRate=' + rate
|
||||||
|
// #ifndef APP-PLUS-NVUE
|
||||||
|
let page = this.$parent
|
||||||
|
while (!page.$scope) page = page.$parent
|
||||||
|
page.$scope.$getAppWebview().evalJS(command)
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS-NVUE
|
||||||
|
this.$refs.web.evalJs(command)
|
||||||
|
// #endif
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置内容
|
||||||
|
* @param {String} content html 内容
|
||||||
|
* @param {Boolean} append 是否在尾部追加
|
||||||
|
*/
|
||||||
|
setContent (content, append) {
|
||||||
|
if (!append || !this.imgList) {
|
||||||
|
this.imgList = []
|
||||||
|
}
|
||||||
|
const nodes = new Parser(this).parse(content)
|
||||||
|
// #ifdef APP-PLUS-NVUE
|
||||||
|
if (this._ready) {
|
||||||
|
this._set(nodes, append)
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
this.$set(this, 'nodes', append ? (this.nodes || []).concat(nodes) : nodes)
|
||||||
|
|
||||||
|
// #ifndef APP-PLUS-NVUE
|
||||||
|
this._videos = []
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this._hook('onLoad')
|
||||||
|
this.$emit('load')
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
|
||||||
|
// 设置懒加载,每 350ms 获取高度,不变则认为加载完毕
|
||||||
|
let height
|
||||||
|
const callback = rect => {
|
||||||
|
// 350ms 总高度无变化就触发 ready 事件
|
||||||
|
if (rect.height === height) {
|
||||||
|
this.$emit('ready', rect)
|
||||||
|
} else {
|
||||||
|
height = rect.height
|
||||||
|
setTimeout(() => {
|
||||||
|
this.getRect().then(callback)
|
||||||
|
}, 350)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.getRect().then(callback)
|
||||||
|
} else {
|
||||||
|
// 未设置懒加载,等待所有图片加载完毕
|
||||||
|
if (!this.imgList._unloadimgs) {
|
||||||
|
this.getRect(rect => {
|
||||||
|
this.$emit('ready', rect)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 调用插件钩子函数
|
||||||
|
*/
|
||||||
|
_hook (name) {
|
||||||
|
for (let i = plugins.length; i--;) {
|
||||||
|
if (this.plugins[i][name]) {
|
||||||
|
this.plugins[i][name]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS-NVUE
|
||||||
|
/**
|
||||||
|
* @description 设置内容
|
||||||
|
*/
|
||||||
|
_set (nodes, append) {
|
||||||
|
this.$refs.web.evalJs('setContent(' + JSON.stringify(nodes) + ',' + JSON.stringify([this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''), this.errorImg, this.loadingImg, this.pauseVideo, this.scrollTable, this.selectable]) + ',' + append + ')')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 接收到 web-view 消息
|
||||||
|
*/
|
||||||
|
_onMessage (e) {
|
||||||
|
const message = e.detail.data[0]
|
||||||
|
switch (message.action) {
|
||||||
|
// web-view 初始化完毕
|
||||||
|
case 'onJSBridgeReady':
|
||||||
|
this._ready = true
|
||||||
|
if (this.nodes) {
|
||||||
|
this._set(this.nodes)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
// 内容 dom 加载完毕
|
||||||
|
case 'onLoad':
|
||||||
|
this.height = message.height
|
||||||
|
this._hook('onLoad')
|
||||||
|
this.$emit('load')
|
||||||
|
break
|
||||||
|
// 所有图片加载完毕
|
||||||
|
case 'onReady':
|
||||||
|
this.getRect().then(res => {
|
||||||
|
this.$emit('ready', res)
|
||||||
|
}).catch(() => { })
|
||||||
|
break
|
||||||
|
// 总高度发生变化
|
||||||
|
case 'onHeightChange':
|
||||||
|
this.height = message.height
|
||||||
|
break
|
||||||
|
// 图片点击
|
||||||
|
case 'onImgTap':
|
||||||
|
this.$emit('imgtap', message.attrs)
|
||||||
|
if (this.previewImg) {
|
||||||
|
uni.previewImage({
|
||||||
|
current: parseInt(message.attrs.i),
|
||||||
|
urls: this.imgList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
// 链接点击
|
||||||
|
case 'onLinkTap': {
|
||||||
|
const href = message.attrs.href
|
||||||
|
this.$emit('linktap', message.attrs)
|
||||||
|
if (href) {
|
||||||
|
// 锚点跳转
|
||||||
|
if (href[0] === '#') {
|
||||||
|
if (this.useAnchor) {
|
||||||
|
dom.scrollToElement(this.$refs.web, {
|
||||||
|
offset: message.offset
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (href.includes('://')) {
|
||||||
|
// 打开外链
|
||||||
|
if (this.copyLink) {
|
||||||
|
plus.runtime.openWeb(href)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: href,
|
||||||
|
fail () {
|
||||||
|
uni.switchTab({
|
||||||
|
url: href
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'onPlay':
|
||||||
|
this.$emit('play')
|
||||||
|
break
|
||||||
|
// 获取到锚点的偏移量
|
||||||
|
case 'getOffset':
|
||||||
|
if (typeof message.offset === 'number') {
|
||||||
|
dom.scrollToElement(this.$refs.web, {
|
||||||
|
offset: message.offset + this._navigateTo.offset
|
||||||
|
})
|
||||||
|
this._navigateTo.resolve()
|
||||||
|
} else {
|
||||||
|
this._navigateTo.reject(Error('Label not found'))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
// 点击
|
||||||
|
case 'onClick':
|
||||||
|
this.$emit('tap')
|
||||||
|
this.$emit('click')
|
||||||
|
break
|
||||||
|
// 出错
|
||||||
|
case 'onError':
|
||||||
|
this.$emit('error', {
|
||||||
|
source: message.source,
|
||||||
|
attrs: message.attrs
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* #ifndef APP-PLUS-NVUE */
|
||||||
|
/* 根节点样式 */
|
||||||
|
._root {
|
||||||
|
padding: 1px 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 长按复制 */
|
||||||
|
._select {
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
/* 提示条 */
|
||||||
|
._tooltip_contain {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
left: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
._tooltip {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0 3px;
|
||||||
|
overflow: scroll;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 30px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
._tooltip_item {
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
padding: 0 2vw;
|
||||||
|
line-height: 30px;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图片宽度滚动条 */
|
||||||
|
._slider {
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
._tooltip,
|
||||||
|
._slider {
|
||||||
|
background-color: black;
|
||||||
|
border-radius: 3px;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff soppresso perché troppo grande
Carica Diff
File diff soppresso perché troppo grande
Carica Diff
@@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview style 插件
|
||||||
|
*/
|
||||||
|
// #ifndef APP-PLUS-NVUE
|
||||||
|
import Parser from './parser'
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
function Style () {
|
||||||
|
this.styles = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifndef APP-PLUS-NVUE
|
||||||
|
Style.prototype.onParse = function (node, vm) {
|
||||||
|
// 获取样式
|
||||||
|
if (node.name === 'style' && node.children.length && node.children[0].type === 'text') {
|
||||||
|
this.styles = this.styles.concat(new Parser().parse(node.children[0].text))
|
||||||
|
} else if (node.name) {
|
||||||
|
// 匹配样式(对非文本标签)
|
||||||
|
// 存储不同优先级的样式 name < class < id < 后代
|
||||||
|
let matched = ['', '', '', '']
|
||||||
|
for (let i = 0, len = this.styles.length; i < len; i++) {
|
||||||
|
const item = this.styles[i]
|
||||||
|
let res = match(node, item.key || item.list[item.list.length - 1])
|
||||||
|
let j
|
||||||
|
if (res) {
|
||||||
|
// 后代选择器
|
||||||
|
if (!item.key) {
|
||||||
|
j = item.list.length - 2
|
||||||
|
for (let k = vm.stack.length; j >= 0 && k--;) {
|
||||||
|
// 子选择器
|
||||||
|
if (item.list[j] === '>') {
|
||||||
|
// 错误情况
|
||||||
|
if (j < 1 || j > item.list.length - 2) break
|
||||||
|
if (match(vm.stack[k], item.list[j - 1])) {
|
||||||
|
j -= 2
|
||||||
|
} else {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
} else if (match(vm.stack[k], item.list[j])) {
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = 4
|
||||||
|
}
|
||||||
|
if (item.key || j < 0) {
|
||||||
|
// 添加伪类
|
||||||
|
if (item.pseudo && node.children) {
|
||||||
|
let text
|
||||||
|
item.style = item.style.replace(/content:([^;]+)/, (_, $1) => {
|
||||||
|
text = $1.replace(/['"]/g, '')
|
||||||
|
// 处理 attr 函数
|
||||||
|
.replace(/attr\((.+?)\)/, (_, $1) => node.attrs[$1.trim()] || '')
|
||||||
|
// 编码 \xxx
|
||||||
|
.replace(/\\(\w{4})/, (_, $1) => String.fromCharCode(parseInt($1, 16)))
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
const pseudo = {
|
||||||
|
name: 'span',
|
||||||
|
attrs: {
|
||||||
|
style: item.style
|
||||||
|
},
|
||||||
|
children: [{
|
||||||
|
type: 'text',
|
||||||
|
text
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
if (item.pseudo === 'before') {
|
||||||
|
node.children.unshift(pseudo)
|
||||||
|
} else {
|
||||||
|
node.children.push(pseudo)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matched[res - 1] += item.style + (item.style[item.style.length - 1] === ';' ? '' : ';')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matched = matched.join('')
|
||||||
|
if (matched.length > 2) {
|
||||||
|
node.attrs.style = matched + (node.attrs.style || '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 匹配样式
|
||||||
|
* @param {object} node 要匹配的标签
|
||||||
|
* @param {string|string[]} keys 选择器
|
||||||
|
* @returns {number} 0:不匹配;1:name 匹配;2:class 匹配;3:id 匹配
|
||||||
|
*/
|
||||||
|
function match (node, keys) {
|
||||||
|
function matchItem (key) {
|
||||||
|
if (key[0] === '#') {
|
||||||
|
// 匹配 id
|
||||||
|
if (node.attrs.id && node.attrs.id.trim() === key.substr(1)) return 3
|
||||||
|
} else if (key[0] === '.') {
|
||||||
|
// 匹配 class
|
||||||
|
key = key.substr(1)
|
||||||
|
const selectors = (node.attrs.class || '').split(' ')
|
||||||
|
for (let i = 0; i < selectors.length; i++) {
|
||||||
|
if (selectors[i].trim() === key) return 2
|
||||||
|
}
|
||||||
|
} else if (node.name === key) {
|
||||||
|
// 匹配 name
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多选择器交集
|
||||||
|
if (keys instanceof Array) {
|
||||||
|
let res = 0
|
||||||
|
for (let j = 0; j < keys.length; j++) {
|
||||||
|
const tmp = matchItem(keys[j])
|
||||||
|
// 任意一个不匹配就失败
|
||||||
|
if (!tmp) return 0
|
||||||
|
// 优先级最大的一个作为最终优先级
|
||||||
|
if (tmp > res) {
|
||||||
|
res = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchItem(keys)
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
export default Style
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
const blank = {
|
||||||
|
' ': true,
|
||||||
|
'\n': true,
|
||||||
|
'\t': true,
|
||||||
|
'\r': true,
|
||||||
|
'\f': true
|
||||||
|
}
|
||||||
|
|
||||||
|
function Parser () {
|
||||||
|
this.styles = []
|
||||||
|
this.selectors = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 解析 css 字符串
|
||||||
|
* @param {string} content css 内容
|
||||||
|
*/
|
||||||
|
Parser.prototype.parse = function (content) {
|
||||||
|
new Lexer(this).parse(content)
|
||||||
|
return this.styles
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 解析到一个选择器
|
||||||
|
* @param {string} name 名称
|
||||||
|
*/
|
||||||
|
Parser.prototype.onSelector = function (name) {
|
||||||
|
// 不支持的选择器
|
||||||
|
if (name.includes('[') || name.includes('*') || name.includes('@')) return
|
||||||
|
const selector = {}
|
||||||
|
// 伪类
|
||||||
|
if (name.includes(':')) {
|
||||||
|
const info = name.split(':')
|
||||||
|
const pseudo = info.pop()
|
||||||
|
if (pseudo === 'before' || pseudo === 'after') {
|
||||||
|
selector.pseudo = pseudo
|
||||||
|
name = info[0]
|
||||||
|
} else return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分割交集选择器
|
||||||
|
function splitItem (str) {
|
||||||
|
const arr = []
|
||||||
|
let i, start
|
||||||
|
for (i = 1, start = 0; i < str.length; i++) {
|
||||||
|
if (str[i] === '.' || str[i] === '#') {
|
||||||
|
arr.push(str.substring(start, i))
|
||||||
|
start = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!arr.length) {
|
||||||
|
return str
|
||||||
|
} else {
|
||||||
|
arr.push(str.substring(start, i))
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后代选择器
|
||||||
|
if (name.includes(' ')) {
|
||||||
|
selector.list = []
|
||||||
|
const list = name.split(' ')
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (list[i].length) {
|
||||||
|
// 拆分子选择器
|
||||||
|
const arr = list[i].split('>')
|
||||||
|
for (let j = 0; j < arr.length; j++) {
|
||||||
|
selector.list.push(splitItem(arr[j]))
|
||||||
|
if (j < arr.length - 1) {
|
||||||
|
selector.list.push('>')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selector.key = splitItem(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectors.push(selector)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 解析到选择器内容
|
||||||
|
* @param {string} content 内容
|
||||||
|
*/
|
||||||
|
Parser.prototype.onContent = function (content) {
|
||||||
|
// 并集选择器
|
||||||
|
for (let i = 0; i < this.selectors.length; i++) {
|
||||||
|
this.selectors[i].style = content
|
||||||
|
}
|
||||||
|
this.styles = this.styles.concat(this.selectors)
|
||||||
|
this.selectors = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description css 词法分析器
|
||||||
|
* @param {object} handler 高层处理器
|
||||||
|
*/
|
||||||
|
function Lexer (handler) {
|
||||||
|
this.selector = ''
|
||||||
|
this.style = ''
|
||||||
|
this.handler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer.prototype.parse = function (content) {
|
||||||
|
this.i = 0
|
||||||
|
this.content = content
|
||||||
|
this.state = this.blank
|
||||||
|
for (let len = content.length; this.i < len; this.i++) {
|
||||||
|
this.state(content[this.i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer.prototype.comment = function () {
|
||||||
|
this.i = this.content.indexOf('*/', this.i) + 1
|
||||||
|
if (!this.i) {
|
||||||
|
this.i = this.content.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer.prototype.blank = function (c) {
|
||||||
|
if (!blank[c]) {
|
||||||
|
if (c === '/' && this.content[this.i + 1] === '*') {
|
||||||
|
this.comment()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.selector += c
|
||||||
|
this.state = this.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer.prototype.name = function (c) {
|
||||||
|
if (c === '/' && this.content[this.i + 1] === '*') {
|
||||||
|
this.comment()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (c === '{' || c === ',' || c === ';') {
|
||||||
|
this.handler.onSelector(this.selector.trimEnd())
|
||||||
|
this.selector = ''
|
||||||
|
if (c !== '{') {
|
||||||
|
while (blank[this.content[++this.i]]);
|
||||||
|
}
|
||||||
|
if (this.content[this.i] === '{') {
|
||||||
|
this.floor = 1
|
||||||
|
this.state = this.val
|
||||||
|
} else {
|
||||||
|
this.selector += this.content[this.i]
|
||||||
|
}
|
||||||
|
} else if (blank[c]) {
|
||||||
|
this.selector += ' '
|
||||||
|
} else {
|
||||||
|
this.selector += c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer.prototype.val = function (c) {
|
||||||
|
if (c === '/' && this.content[this.i + 1] === '*') {
|
||||||
|
this.comment()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (c === '{') {
|
||||||
|
this.floor++
|
||||||
|
} else if (c === '}') {
|
||||||
|
this.floor--
|
||||||
|
if (!this.floor) {
|
||||||
|
this.handler.onContent(this.style)
|
||||||
|
this.style = ''
|
||||||
|
this.state = this.blank
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.style += c
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Parser
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"use strict";function t(t){for(var e=Object.create(null),n=t.attributes.length;n--;)e[t.attributes[n].name]=t.attributes[n].value;return e}function e(){a[1]&&(this.src=a[1],this.onerror=null),this.onclick=null,this.ontouchstart=null,uni.postMessage({data:{action:"onError",source:"img",attrs:t(this)}})}function n(){window.unloadimgs-=1,0===window.unloadimgs&&uni.postMessage({data:{action:"onReady"}})}function o(r,s,c){for(var d=0;d<r.length;d++)!function(d){var u=r[d],l=void 0;if(u.type&&"node"!==u.type)l=document.createTextNode(u.text.replace(/&/g,"&"));else{var g=u.name;"svg"===g&&(c="http://www.w3.org/2000/svg"),"html"!==g&&"body"!==g||(g="div"),l=c?document.createElementNS(c,g):document.createElement(g);for(var p in u.attrs)l.setAttribute(p,u.attrs[p]);if(u.children&&o(u.children,l,c),"img"===g){if(window.unloadimgs+=1,l.onload=n,l.onerror=n,!l.src&&l.getAttribute("data-src")&&(l.src=l.getAttribute("data-src")),u.attrs.ignore||(l.onclick=function(e){e.stopPropagation(),uni.postMessage({data:{action:"onImgTap",attrs:t(this)}})}),a[2]){var h=new Image;h.src=l.src,l.src=a[2],h.onload=function(){l.src=this.src},h.onerror=function(){l.onerror()}}l.onerror=e}else if("a"===g)l.addEventListener("click",function(e){e.stopPropagation(),e.preventDefault();var n,o=this.getAttribute("href");o&&"#"===o[0]&&(n=(document.getElementById(o.substr(1))||{}).offsetTop),uni.postMessage({data:{action:"onLinkTap",attrs:t(this),offset:n}})},!0);else if("video"===g||"audio"===g)i.push(l),u.attrs.autoplay||u.attrs.controls||l.setAttribute("controls","true"),l.onplay=function(){if(uni.postMessage({data:{action:"onPlay"}}),a[3])for(var t=0;t<i.length;t++)i[t]!==this&&i[t].pause()},l.onerror=function(){uni.postMessage({data:{action:"onError",source:g,attrs:t(this)}})};else if("table"===g&&a[4]&&!l.style.cssText.includes("inline")){var f=document.createElement("div");f.style.overflow="auto",f.appendChild(l),l=f}else"svg"===g&&(c=void 0)}s.appendChild(l)}(d)}document.addEventListener("UniAppJSBridgeReady",function(){document.body.onclick=function(){return uni.postMessage({data:{action:"onClick"}})},uni.postMessage({data:{action:"onJSBridgeReady"}})});var a,i=[];window.setContent=function(t,e,n){var r=document.getElementById("content");e[0]&&(document.body.style.cssText=e[0]),e[5]||(r.style.userSelect="none"),n||(r.innerHTML="",i=[]),a=e,window.unloadimgs=0;var s=document.createDocumentFragment();o(t,s),r.appendChild(s);var c=r.scrollHeight;uni.postMessage({data:{action:"onLoad",height:c}}),window.unloadimgs||uni.postMessage({data:{action:"onReady",height:c}}),clearInterval(window.timer),window.timer=setInterval(function(){r.scrollHeight!==c&&(c=r.scrollHeight,uni.postMessage({data:{action:"onHeightChange",height:c}}))},350)},window.onunload=function(){clearInterval(window.timer)};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).uni=n()}(this,(function(){"use strict";try{var e={};Object.defineProperty(e,"passive",{get:function(){!0}}),window.addEventListener("test-passive",null,e)}catch(e){}var n=Object.prototype.hasOwnProperty;function t(e,t){return n.call(e,t)}var i=[],a=function(e,n){var t={options:{timestamp:+new Date},name:e,arg:n};if(window.__dcloud_weex_postMessage||window.__dcloud_weex_){if("postMessage"===e){var a={data:[n]};return window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(a):window.__dcloud_weex_.postMessage(JSON.stringify(a))}var o={type:"WEB_INVOKE_APPSERVICE",args:{data:t,webviewIds:i}};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessageToService(o):window.__dcloud_weex_.postMessageToService(JSON.stringify(o))}if(!window.plus)return window.parent.postMessage({type:"WEB_INVOKE_APPSERVICE",data:t,pageId:""},"*");if(0===i.length){var r=plus.webview.currentWebview();if(!r)throw new Error("plus.webview.currentWebview() is undefined");var d=r.parent(),s="";s=d?d.id:r.id,i.push(s)}if(plus.webview.getWebviewById("__uniapp__service"))plus.webview.postMessageToUniNView({type:"WEB_INVOKE_APPSERVICE",args:{data:t,webviewIds:i}},"__uniapp__service");else{var w=JSON.stringify(t);plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat("WEB_INVOKE_APPSERVICE",'",').concat(w,",").concat(JSON.stringify(i),");"))}},o={navigateTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;a("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;a("redirectTo",{url:encodeURI(n)})},getEnv:function(e){window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};a("postMessage",e.data||{})}},r=/uni-app/i.test(navigator.userAgent),d=/Html5Plus/i.test(navigator.userAgent),s=/complete|loaded|interactive/;var w=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var u=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var c=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var g=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var v=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);for(var l,_=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},f=[function(e){if(r||d)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&s.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),o},function(e){if(v)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(c)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(w){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(u)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){return document.addEventListener("DOMContentLoaded",e),o}],m=0;m<f.length&&!(l=f[m](_));m++);l||(l={});var E="undefined"!=typeof uni?uni:{};if(!E.navigateTo)for(var b in l)t(l,b)&&(E[b]=l[b]);return E.webView=l,E}));
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<style>.hl-code,.hl-pre{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}.hl-pre{padding:1em;margin:.5em 0;overflow:auto}.hl-pre{background:#2d2d2d}.hl-block-comment,.hl-cdata,.hl-comment,.hl-doctype,.hl-prolog{color:#999}.hl-punctuation{color:#ccc}.hl-attr-name,.hl-deleted,.hl-namespace,.hl-tag{color:#e2777a}.hl-function-name{color:#6196cc}.hl-boolean,.hl-function,.hl-number{color:#f08d49}.hl-class-name,.hl-constant,.hl-property,.hl-symbol{color:#f8c555}.hl-atrule,.hl-builtin,.hl-important,.hl-keyword,.hl-selector{color:#cc99cd}.hl-attr-value,.hl-char,.hl-regex,.hl-string,.hl-variable{color:#7ec699}.hl-entity,.hl-operator,.hl-url{color:#67cdcc}.hl-bold,.hl-important{font-weight:700}.hl-italic{font-style:italic}.hl-entity{cursor:help}.hl-inserted{color:green}</style><style>.md-p{margin-block-start:1em;margin-block-end:1em}.md-blockquote,.md-table{margin-bottom:16px}.md-table{box-sizing:border-box;width:100%;overflow:auto;border-spacing:0;border-collapse:collapse}.md-tr{background-color:#fff;border-top:1px solid #c6cbd1}.md-table .md-tr:nth-child(2n){background-color:#f6f8fa}.md-td,.md-th{padding:6px 13px!important;border:1px solid #dfe2e5}.md-th{font-weight:600}.md-blockquote{padding:0 1em;color:#6a737d;border-left:.25em solid #dfe2e5}.md-code{padding:.2em .4em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:85%;background-color:rgba(27,31,35,.05);border-radius:3px}.md-pre .md-code{padding:0;font-size:100%;background:0 0;border:0}.hl-pre{position:relative}.hl-code{overflow:auto;display:block}.hl-language{font-size:12px;font-weight:600;position:absolute;right:8px;text-align:right;top:3px}.hl-pre{padding-top:1.5em}.hl-pre{font-size:14px;padding-left:3.8em;counter-reset:linenumber}.line-numbers-rows{position:absolute;pointer-events:none;top:1.5em;font-size:100%;left:0;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows .span{display:block;counter-increment:linenumber}.line-numbers-rows .span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}._address,._article,._aside,._body,._caption,._center,._cite,._footer,._header,._html,._nav,._pre,._section{display:block}._video{width:300px;height:225px;display:inline-block;background-color:#000}</style><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><style>body,html{width:100%;height:100%;overflow-x:scroll;overflow-y:hidden}body{margin:0}video{width:300px;height:225px}img{max-width:100%;-webkit-touch-callout:none}</style></head><body><div id="content" style="overflow:hidden"></div><script type="text/javascript" src="./js/uni.webview.min.js"></script><script type="text/javascript" src="./js/handler.js"></script></body>
|
||||||
@@ -0,0 +1,735 @@
|
|||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<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,784 @@
|
|||||||
|
<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,89 @@
|
|||||||
|
<template>
|
||||||
|
<view class="wave-wrap waveAnimation">
|
||||||
|
<view class="waveWrapperInner bgTop"><view class="wave waveTop"></view></view>
|
||||||
|
<view class="waveWrapperInner bgMiddle"><view class="wave waveMiddle"></view></view>
|
||||||
|
<view class="waveWrapperInner bgBottom"><view class="wave waveBottom"></view></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'wave',
|
||||||
|
props: {
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '35rpx'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.wave-wrap {
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
top: 0;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.waveWrapperInner {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.wave {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: 200%;
|
||||||
|
height: 100%;
|
||||||
|
background-repeat: repeat no-repeat;
|
||||||
|
background-position: 0 bottom;
|
||||||
|
transform-origin: center bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bgTop {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.waveTop {
|
||||||
|
background-size: 50% 45px;
|
||||||
|
background-image: url('~@/static/wave/wave-1.png');
|
||||||
|
}
|
||||||
|
.waveAnimation .waveTop {
|
||||||
|
animation: move_wave 4s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes move_wave {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0) translateZ(0) scaleY(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateX(-25%) translateZ(0) scaleY(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(-50%) translateZ(0) scaleY(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bgMiddle {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.waveMiddle {
|
||||||
|
background-size: 50% 40px;
|
||||||
|
background-image: url('~@/static/wave/wave-2.png');
|
||||||
|
}
|
||||||
|
.waveAnimation .waveMiddle {
|
||||||
|
animation: move_wave 3.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bgBottom {
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
|
.waveBottom {
|
||||||
|
background-size: 50% 35px;
|
||||||
|
background-image: url('~@/static/wave/wave-1.png');
|
||||||
|
}
|
||||||
|
.waveAnimation .waveBottom {
|
||||||
|
animation: move_wave 2s linear infinite;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 广告配置
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
adpid: '', // uni-AD App广告位id,在uni-AD官网申请广告位
|
||||||
|
unitId: '', // 广告单元id,可在小程序管理后台的流量主模块新建 (非个人资质,小程序后台广告主开通申请)
|
||||||
|
frequency: 8, // 列表中,广告出现的频率(8=每8条数据出现一次广告)
|
||||||
|
// 首页广告
|
||||||
|
home: {
|
||||||
|
use: false,
|
||||||
|
},
|
||||||
|
// 文章列表广告
|
||||||
|
articles: {
|
||||||
|
use: false,
|
||||||
|
},
|
||||||
|
// 文章详情广告
|
||||||
|
articleDetail: {
|
||||||
|
use: false, // 是否启用
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* 功能:基础配置
|
||||||
|
* 作者:小莫唐尼
|
||||||
|
* 邮箱:studio@925i.cn
|
||||||
|
* 时间:2022年08月23日 15:19:14
|
||||||
|
* 版本:v0.1.0
|
||||||
|
* 修改记录:
|
||||||
|
* 修改内容:
|
||||||
|
* 修改人员:
|
||||||
|
* 修改时间:
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
showCopyright: true, // 显示开源版权信息
|
||||||
|
showAbout: true, // 显示关于项目入口
|
||||||
|
uni_halo_logo: 'https://b.925i.cn/uni_halo/uni_halo_logo.png', // uni-halo的logo
|
||||||
|
|
||||||
|
apiUrl: '', // Api基础域名(您的halo博客基础域名或者是Halo后台管理系统api地址)
|
||||||
|
apiAuthorization: '', // Halo中-系统-博客设置-切换到高级选项-API设置-Access key
|
||||||
|
|
||||||
|
title: '', // 博客标题
|
||||||
|
indexImageUrl: '', // 开屏首页图片
|
||||||
|
miniCodeImageUrl: '', // 小程序码地址
|
||||||
|
|
||||||
|
author: {
|
||||||
|
name: '', // 昵称
|
||||||
|
avatar: '', // 头像地址
|
||||||
|
motto: '', // 格言
|
||||||
|
},
|
||||||
|
|
||||||
|
social: {
|
||||||
|
qq: "", // qq号
|
||||||
|
wechat: "", // 微信号
|
||||||
|
weibo: "", // 微博地址
|
||||||
|
email: "", // 邮箱地址
|
||||||
|
blog: "", // 博客地址
|
||||||
|
juejin: "", // 掘金地址
|
||||||
|
bilibili: "", // b站地址
|
||||||
|
gitee: "", // gitee地址
|
||||||
|
github: "", // github地址
|
||||||
|
csdn: "" // CSDN地址
|
||||||
|
},
|
||||||
|
|
||||||
|
defaultThumbnailUrl: '', // 默认封面图地址
|
||||||
|
defaultImageUrl: '', // 默认图片地址
|
||||||
|
defaultAvatarUrl: '', // 默认头像地址
|
||||||
|
|
||||||
|
loadingGifUrl: '', // 图片加载中的地址
|
||||||
|
loadingErrUrl: '', // 图片加载失败的地址
|
||||||
|
loadingEmptyUrl: '', // 加载图片为空地址
|
||||||
|
|
||||||
|
waveImageUrl: '', // 关于页面波浪图片
|
||||||
|
|
||||||
|
banner: { // 轮播图配置
|
||||||
|
type: 'article', // 轮播图数据源 list=下方配置 article=热门文章封面
|
||||||
|
list: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
quickNav: { // 快捷导航配置
|
||||||
|
use: true,
|
||||||
|
list: [{
|
||||||
|
icon: 'halocoloricon-classify',
|
||||||
|
text: '文章归档',
|
||||||
|
iconSize: 60,
|
||||||
|
color: 'blue',
|
||||||
|
type: 'page',
|
||||||
|
path: '/pagesA/archives/archives'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'halocoloricon-attent',
|
||||||
|
text: '恋爱日记',
|
||||||
|
iconSize: 60,
|
||||||
|
color: 'blue',
|
||||||
|
type: 'page',
|
||||||
|
path: '/pagesA/love/love'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'halocoloricon-calendar',
|
||||||
|
text: '个人日记',
|
||||||
|
iconSize: 60,
|
||||||
|
color: 'blue',
|
||||||
|
type: 'page',
|
||||||
|
path: '/pagesA/journal/journal'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'halocoloricon-message',
|
||||||
|
text: '留言板',
|
||||||
|
iconSize: 60,
|
||||||
|
color: 'blue',
|
||||||
|
type: 'page',
|
||||||
|
path: '/pagesA/leaving/leaving'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// 微信分享信息
|
||||||
|
wxShareConfig: {
|
||||||
|
title: '', // 小程序分享标题[非必填]
|
||||||
|
desc: '', // 小程序分享描述[非必填]
|
||||||
|
imageUrl: '', // 小程序分享时候图片地址[非必填]
|
||||||
|
path: '/pages/start/start', // 分享路径[非必填] - 基本不需要修改
|
||||||
|
copyLink: '/pages/start/start', // 复制链接[非必填] - 基本不需要修改
|
||||||
|
query: {}, // 分享参数[非必填] - 基本不需要填写
|
||||||
|
},
|
||||||
|
|
||||||
|
colors: [
|
||||||
|
'#39B449',
|
||||||
|
'#E44C41',
|
||||||
|
'#8698A2',
|
||||||
|
'#0080FE',
|
||||||
|
'#1CBCB4',
|
||||||
|
'#6638B5',
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* 配置key
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
SHEET_LEAVING: 'leaving', // 留言板
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* 页面配置
|
||||||
|
*/
|
||||||
|
import AppKeys from './keys.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
[AppKeys.SHEET_LEAVING]: 65, // 留言板页面ID
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<script>
|
||||||
|
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||||
|
CSS.supports('top: constant(a)'))
|
||||||
|
document.write(
|
||||||
|
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||||
|
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||||
|
</script>
|
||||||
|
<title></title>
|
||||||
|
<!--preload-links-->
|
||||||
|
<!--app-context-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"><!--app-html--></div>
|
||||||
|
<script type="module" src="/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import FyShowModal from './showModal.js'
|
||||||
|
|
||||||
|
export default class Fy{
|
||||||
|
/**
|
||||||
|
* @author 大雄
|
||||||
|
* @Date 2021年7月1日20:49:58
|
||||||
|
* @description 二次封装showModel
|
||||||
|
*/
|
||||||
|
static showModal({ title = "提示", content = "提示内容", showCancel = true, backbutton = false, cancelText = "取消", cancelColor = "#000000", confirmText = "确定", confirmColor = '#3CC51F', complete = false } = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
if (this.get_app_platform() === 'android') { // android的使用自定义的模态框
|
||||||
|
new FyShowModal({ title, content, showCancel, backbutton, cancelText, cancelColor, confirmText, confirmColor,
|
||||||
|
success(res) {
|
||||||
|
if (res.confirm) {resolve(res) } else { reject(res) }
|
||||||
|
},
|
||||||
|
fail(err){
|
||||||
|
this.uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }).then((res)=>resolve(res)).catch(err=>reject(err))
|
||||||
|
}
|
||||||
|
}).show();
|
||||||
|
} else { // ios直接用原生的
|
||||||
|
return this.uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }).then((res)=>resolve(res)).catch(err=>reject(err))
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
return this.uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }).then((res)=>resolve(res)).catch(err=>reject(err))
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原生showModal
|
||||||
|
static uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let appPlatform = null;
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
if (this.get_app_platform() === 'android' && showCancel) { // android的确认按钮在左边,需要统一到右边
|
||||||
|
appPlatform = 'android';
|
||||||
|
var tempConfirmText = confirmText;
|
||||||
|
var tempConfirmColor = confirmColor;
|
||||||
|
confirmText = cancelText;
|
||||||
|
cancelText = tempConfirmText;
|
||||||
|
confirmColor = cancelColor;
|
||||||
|
cancelColor = tempConfirmColor;
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
uni.showModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor,
|
||||||
|
success(res){
|
||||||
|
if (complete) {
|
||||||
|
resolve(res);
|
||||||
|
} else if (res.confirm) {
|
||||||
|
appPlatform === 'android' ? reject(res) : resolve(res)
|
||||||
|
} else {
|
||||||
|
appPlatform === 'android' ? resolve(res) : reject(res)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail(err){ reject(err) }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取app平台(android | ios)
|
||||||
|
* */
|
||||||
|
static get_app_platform() {
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
this.showModal({ content: '仅支持app' });
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
return plus.os.name.toLowerCase();
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* @author 大雄
|
||||||
|
* @Date 2021年7月1日20:49:58
|
||||||
|
* @description 路由导航守卫(简单版,后续需要功能再完善)
|
||||||
|
*/
|
||||||
|
export default function() {
|
||||||
|
// 监听路由前进
|
||||||
|
function routerPush({ type = 'navigateTo' } = {}) {
|
||||||
|
routeWatchClearModal();
|
||||||
|
}
|
||||||
|
// 监听路由后退
|
||||||
|
function routerBack() {
|
||||||
|
routeWatchClearModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面跳转后,销毁当前页面未关闭的弹框
|
||||||
|
function routeWatchClearModal() {
|
||||||
|
try {
|
||||||
|
var FyShowModalView = plus.nativeObj.View.getViewById("FyShowModalView");
|
||||||
|
if (FyShowModalView) {
|
||||||
|
FyShowModalView.clear();
|
||||||
|
}
|
||||||
|
var FyShowModalCancel = plus.nativeObj.View.getViewById("FyShowModalCancel");
|
||||||
|
if (FyShowModalCancel) {
|
||||||
|
FyShowModalCancel.clear();
|
||||||
|
}
|
||||||
|
var FyShowModalConfirm = plus.nativeObj.View.getViewById("FyShowModalConfirm");
|
||||||
|
if (FyShowModalConfirm) {
|
||||||
|
FyShowModalConfirm.clear();
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.addInterceptor('navigateTo', {
|
||||||
|
success(e) {
|
||||||
|
routerPush({ type: 'navigateTo' });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
uni.addInterceptor('redirectTo', {
|
||||||
|
success(e) {
|
||||||
|
routerPush({ type: 'redirectTo' });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
uni.addInterceptor('reLaunch', {
|
||||||
|
success(e) {
|
||||||
|
routerPush({ type: 'reLaunch' });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
uni.addInterceptor('switchTab', {
|
||||||
|
success(e) {
|
||||||
|
routerPush({ type: 'switchTab' });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
uni.addInterceptor('navigateBack', {
|
||||||
|
success(e) {
|
||||||
|
routerBack();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/**
|
||||||
|
* @description 替换android app的uni.showModal
|
||||||
|
*/
|
||||||
|
|
||||||
|
let modalIntance = null;
|
||||||
|
export class fyShowModal {
|
||||||
|
constructor(options = {}) {
|
||||||
|
modalIntance = this;
|
||||||
|
|
||||||
|
this.modalControl = null; // 模态框句柄
|
||||||
|
this.cancelModel = null;
|
||||||
|
this.confirmModel = null;
|
||||||
|
|
||||||
|
const { screenHeight, screenWidth } = uni.getSystemInfoSync();
|
||||||
|
|
||||||
|
this.modalPaddingTop = 12; // modal顶部的内边距
|
||||||
|
this.titleHeight = 34; // 标题的高度
|
||||||
|
this.contentHeight = 60; // 内容得高度
|
||||||
|
this.contentPaddingBottom = 10; // 内容的底部内边距
|
||||||
|
this.footerHeight = 50; // 底部按钮的高度
|
||||||
|
|
||||||
|
const modalHeight = this.modalPaddingTop + this.contentPaddingBottom + this.titleHeight + this.contentHeight + this.footerHeight; // 模态框内容高度
|
||||||
|
|
||||||
|
this.screenHeight = screenHeight;
|
||||||
|
this.modalWidth = options.contentWidth || screenWidth * 0.82; // 模态框内容宽度
|
||||||
|
this.modalHeight = modalHeight; // 模态框内容高度
|
||||||
|
this.modalLeft = (screenWidth - this.modalWidth) / 2; // 模态框距离左边距离
|
||||||
|
this.modalTop = (screenHeight / 2) - (modalHeight / 2) - 30; // 模态框距离顶部距离
|
||||||
|
this.titleTop = this.modalPaddingTop + this.modalTop; // title距离顶部的距离
|
||||||
|
this.contentTop = this.modalPaddingTop + this.modalTop + this.titleHeight; // content距离顶部的距离
|
||||||
|
this.contentLeft = this.modalLeft + (this.modalWidth * 0.1);
|
||||||
|
this.contentWidth = this.modalWidth * 0.8; // 内容的宽度
|
||||||
|
this.footerBorderTop = this.contentPaddingBottom + this.contentTop + this.contentHeight; // footer的边线距离顶部的距离
|
||||||
|
this.buttonWidth = this.modalWidth/2;
|
||||||
|
|
||||||
|
// 物理返回键是否关闭弹框
|
||||||
|
this.backbutton = Boolean(options.backbutton);
|
||||||
|
|
||||||
|
let opacity = options.opacity || 0.6; // mask透明度
|
||||||
|
let modal_title = options.title || '提示'; // 标题
|
||||||
|
let model_content = options.content || '提示内容'; // 提示内容
|
||||||
|
let maskClick = typeof options.maskClick === 'undefined' ? false : options.maskClick; // 是否可以点击mask关闭模态框
|
||||||
|
let cancelText = options.cancelText || '取消';
|
||||||
|
let confirmText = options.confirmText || '确定';
|
||||||
|
let cancelColor = options.cancelColor || '#000000';
|
||||||
|
let confirmColor = options.confirmColor || '#3CC51F';
|
||||||
|
let showCancel = typeof options.showCancel === 'undefined' ? true : options.showCancel; // 是否显示取消按钮
|
||||||
|
let align = options.align || 'center'; // 内容对齐方向
|
||||||
|
let successFn = () => {};
|
||||||
|
let failFn = () => {};
|
||||||
|
this.success = options.success || successFn; // 成功返回模态框
|
||||||
|
this.fail = options.fail || failFn; // 失败返回模态框
|
||||||
|
|
||||||
|
//#ifdef APP-PLUS
|
||||||
|
this.creatView({ height: `${this.screenHeight}px`, top: 0 }, opacity, maskClick, { 'title': modal_title, 'content': model_content, cancelText, confirmText, confirmColor, cancelColor, showCancel, align });
|
||||||
|
//#endif
|
||||||
|
}
|
||||||
|
//生成提示框view
|
||||||
|
creatView(style, opa, maskClick, modelInfo) {
|
||||||
|
try {
|
||||||
|
style = { left: '0px', width: '100%', ...style };
|
||||||
|
let view = new plus.nativeObj.View('FyShowModalView', style);
|
||||||
|
view.draw([
|
||||||
|
{ tag: 'rect', id: 'modal', color: `rgba(0,0,0,${opa})`, position: { top: '0px', left: '0px', width: '100%', height: '100%' } },
|
||||||
|
{ tag: 'rect', id: 'content', color: `rgb(255,255,255)`, rectStyles: { borderWidth: '0px', radius: '8px' }, position: { top: this.modalTop+'px', left: this.modalLeft+'px', width: this.modalWidth+'px', height: this.modalHeight + 'px' } },
|
||||||
|
|
||||||
|
{ tag: 'font', id: 'title', text: modelInfo.title, textStyles: { size: '18px', weight: 'bold', color: '#000000' }, position: { top: this.titleTop+'px', left: this.modalLeft+'px', width: this.modalWidth+'px', height: this.titleHeight+'px' } },
|
||||||
|
|
||||||
|
{ tag: 'font', id: 'text', text: modelInfo.content, textStyles: { size: '15px', color: '#666', whiteSpace: 'normal', align: modelInfo.align }, position: { top: this.contentTop+'px', left: this.contentLeft+'px', width: this.contentWidth+'px', height: this.contentHeight+'px' } },
|
||||||
|
|
||||||
|
{ tag: 'rect', id: 'line', color: '#efeff1', position: { top: this.footerBorderTop+'px', left: this.modalLeft+'px', width: this.modalWidth+'px', height: '1px' } },
|
||||||
|
{ tag: 'rect', id: 'line2', color: '#efeff1', position: { top: this.footerBorderTop+'px', left: '50%', width: modelInfo.showCancel ? '1px' : '0px', height: modelInfo.showCancel ? this.footerHeight+'px' : '0px' } }
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 取消按钮
|
||||||
|
if (modelInfo.showCancel) {
|
||||||
|
let viewCancel = new plus.nativeObj.View('FyShowModalCancel', { width: this.buttonWidth+'px', height: this.footerHeight+'px', top: this.footerBorderTop + 'px', left: this.modalLeft+'px' });
|
||||||
|
viewCancel.draw([
|
||||||
|
{ tag: 'rect', id: 'cancelBackground', color: `rgba(255,255,255,0)`, rectStyles: { borderWidth: '0px', radius: '8px' }, position: { top: '0px', left: '0px', width: '100%', height: '100%' } },
|
||||||
|
{ tag: 'font', id: 'cancel', text: modelInfo.cancelText, textStyles: { color: modelInfo.cancelColor, size: '16px' } },
|
||||||
|
]);
|
||||||
|
viewCancel.addEventListener('click', (e) => {
|
||||||
|
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||||
|
viewCancel.drawRect('#efeff1', {top:'0px',left:'0px',width:'100%',height:'100%'}, 'cancelBackground');
|
||||||
|
this.success({ confirm: false, cancel: true, mask: false })
|
||||||
|
this.hide();
|
||||||
|
}, false);
|
||||||
|
viewCancel.addEventListener('touchstart', (e)=>{
|
||||||
|
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||||
|
viewCancel.drawRect({ color: '#efeff1', borderWidth: '0px', radius: '8px' }, {top:'0px',left:'0px',width:'100%',height:'100%'}, 'cancelBackground');
|
||||||
|
})
|
||||||
|
this.cancelModel = viewCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认
|
||||||
|
let viewconfirm = new plus.nativeObj.View('FyShowModalConfirm', { width: modelInfo.showCancel ? this.buttonWidth+'px' : this.modalWidth+'px', height: this.footerHeight+'px', top: this.footerBorderTop + 'px', left: modelInfo.showCancel ? '50%' : this.modalLeft+'px' });
|
||||||
|
// 绘制确认
|
||||||
|
viewconfirm.draw([
|
||||||
|
{ tag: 'rect', id: 'confirmBackground', color: `rgba(255,255,255,0)`, rectStyles: { borderWidth: '0px', radius: '8px' }, position: { top: '0px', left: '0px', width: '100%', height: '100%' } },
|
||||||
|
{ tag: 'font', id: 'confirm', text: modelInfo.confirmText, textStyles: { color: modelInfo.confirmColor, size: '16px' } },
|
||||||
|
]);
|
||||||
|
// 点击确认
|
||||||
|
viewconfirm.addEventListener('click', (e) => {
|
||||||
|
if (this.cancelModel) {
|
||||||
|
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||||
|
}
|
||||||
|
viewconfirm.drawRect('#efeff1', {top:'0px',left:'0px',width:'100%',height:'100%'}, 'confirmBackground');
|
||||||
|
this.success({ confirm: true, cancel: false, mask: false })
|
||||||
|
this.hide();
|
||||||
|
}, false);
|
||||||
|
viewconfirm.addEventListener('touchstart', (e)=>{
|
||||||
|
if (this.cancelModel) {
|
||||||
|
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||||
|
}
|
||||||
|
viewconfirm.drawRect({ color: '#efeff1', borderWidth: '0px', radius: '8px' }, {top:'0px',left:'0px',width:'100%',height:'100%'}, 'confirmBackground');
|
||||||
|
})
|
||||||
|
|
||||||
|
//点击蒙布
|
||||||
|
if (maskClick) {
|
||||||
|
view.addEventListener('click', (e) => {
|
||||||
|
this.success({ confirm: false, cancel: true, mask: true })
|
||||||
|
this.hide();
|
||||||
|
if (this.cancelModel) {
|
||||||
|
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||||
|
}
|
||||||
|
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||||
|
}, false);
|
||||||
|
} else {
|
||||||
|
view.addEventListener('click', (e) => {
|
||||||
|
if (this.cancelModel) {
|
||||||
|
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||||
|
}
|
||||||
|
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
this.modalControl = view;
|
||||||
|
this.confirmModel = viewconfirm;
|
||||||
|
} catch(err) {
|
||||||
|
this.fail(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 显示模态框
|
||||||
|
show() {
|
||||||
|
this.modalControl.show();
|
||||||
|
if (this.cancelModel) {
|
||||||
|
this.cancelModel.show();
|
||||||
|
}
|
||||||
|
this.confirmModel.show();
|
||||||
|
|
||||||
|
if (this.backbutton) {
|
||||||
|
plus.key.addEventListener('backbutton', this.handlerBackButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 关闭模态框
|
||||||
|
hide() {
|
||||||
|
if (this.backbutton) {
|
||||||
|
plus.key.removeEventListener('backbutton', this.handlerBackButton);
|
||||||
|
}
|
||||||
|
this.modalControl.clear();
|
||||||
|
if (this.cancelModel) {
|
||||||
|
this.cancelModel.clear();
|
||||||
|
}
|
||||||
|
this.confirmModel.clear();
|
||||||
|
}
|
||||||
|
// 物理返回键方法
|
||||||
|
handlerBackButton() {
|
||||||
|
try {
|
||||||
|
modalIntance && modalIntance.success({ confirm: false, cancel: true, mask: false })
|
||||||
|
modalIntance && modalIntance.hide();
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default fyShowModal;
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import buildURL from '../helpers/buildURL'
|
||||||
|
import buildFullPath from '../core/buildFullPath'
|
||||||
|
import settle from '../core/settle'
|
||||||
|
import { isUndefined } from "../utils"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回可选值存在的配置
|
||||||
|
* @param {Array} keys - 可选值数组
|
||||||
|
* @param {Object} config2 - 配置
|
||||||
|
* @return {{}} - 存在的配置项
|
||||||
|
*/
|
||||||
|
const mergeKeys = (keys, config2) => {
|
||||||
|
let config = {}
|
||||||
|
keys.forEach(prop => {
|
||||||
|
if (!isUndefined(config2[prop])) {
|
||||||
|
config[prop] = config2[prop]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
export default (config) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params)
|
||||||
|
const _config = {
|
||||||
|
url: fullPath,
|
||||||
|
header: config.header,
|
||||||
|
complete: (response) => {
|
||||||
|
config.fullPath = fullPath
|
||||||
|
response.config = config
|
||||||
|
try {
|
||||||
|
// 对可能字符串不是json 的情况容错
|
||||||
|
if (typeof response.data === 'string') {
|
||||||
|
response.data = JSON.parse(response.data)
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-empty
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
settle(resolve, reject, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let requestTask
|
||||||
|
if (config.method === 'UPLOAD') {
|
||||||
|
delete _config.header['content-type']
|
||||||
|
delete _config.header['Content-Type']
|
||||||
|
let otherConfig = {
|
||||||
|
// #ifdef MP-ALIPAY
|
||||||
|
fileType: config.fileType,
|
||||||
|
// #endif
|
||||||
|
filePath: config.filePath,
|
||||||
|
name: config.name
|
||||||
|
}
|
||||||
|
const optionalKeys = [
|
||||||
|
// #ifdef APP-PLUS || H5
|
||||||
|
'files',
|
||||||
|
// #endif
|
||||||
|
// #ifdef H5
|
||||||
|
'file',
|
||||||
|
// #endif
|
||||||
|
// #ifdef H5 || APP-PLUS
|
||||||
|
'timeout',
|
||||||
|
// #endif
|
||||||
|
'formData'
|
||||||
|
]
|
||||||
|
requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
|
||||||
|
} else if (config.method === 'DOWNLOAD') {
|
||||||
|
// #ifdef H5 || APP-PLUS
|
||||||
|
if (!isUndefined(config['timeout'])) {
|
||||||
|
_config['timeout'] = config['timeout']
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
requestTask = uni.downloadFile(_config)
|
||||||
|
} else {
|
||||||
|
const optionalKeys = [
|
||||||
|
'data',
|
||||||
|
'method',
|
||||||
|
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||||
|
'timeout',
|
||||||
|
// #endif
|
||||||
|
'dataType',
|
||||||
|
// #ifndef MP-ALIPAY
|
||||||
|
'responseType',
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
'sslVerify',
|
||||||
|
// #endif
|
||||||
|
// #ifdef H5
|
||||||
|
'withCredentials',
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
'firstIpv4',
|
||||||
|
// #endif
|
||||||
|
]
|
||||||
|
requestTask = uni.request({..._config,...mergeKeys(optionalKeys, config)})
|
||||||
|
}
|
||||||
|
if (config.getTask) {
|
||||||
|
config.getTask(requestTask, config)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
|
||||||
|
function InterceptorManager() {
|
||||||
|
this.handlers = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new interceptor to the stack
|
||||||
|
*
|
||||||
|
* @param {Function} fulfilled The function to handle `then` for a `Promise`
|
||||||
|
* @param {Function} rejected The function to handle `reject` for a `Promise`
|
||||||
|
*
|
||||||
|
* @return {Number} An ID used to remove interceptor later
|
||||||
|
*/
|
||||||
|
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
|
||||||
|
this.handlers.push({
|
||||||
|
fulfilled: fulfilled,
|
||||||
|
rejected: rejected
|
||||||
|
})
|
||||||
|
return this.handlers.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an interceptor from the stack
|
||||||
|
*
|
||||||
|
* @param {Number} id The ID that was returned by `use`
|
||||||
|
*/
|
||||||
|
InterceptorManager.prototype.eject = function eject(id) {
|
||||||
|
if (this.handlers[id]) {
|
||||||
|
this.handlers[id] = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over all the registered interceptors
|
||||||
|
*
|
||||||
|
* This method is particularly useful for skipping over any
|
||||||
|
* interceptors that may have become `null` calling `eject`.
|
||||||
|
*
|
||||||
|
* @param {Function} fn The function to call for each interceptor
|
||||||
|
*/
|
||||||
|
InterceptorManager.prototype.forEach = function forEach(fn) {
|
||||||
|
this.handlers.forEach(h => {
|
||||||
|
if (h !== null) {
|
||||||
|
fn(h)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InterceptorManager
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
/**
|
||||||
|
* @Class Request
|
||||||
|
* @description luch-request http请求插件
|
||||||
|
* @version 3.0.7
|
||||||
|
* @Author lu-ch
|
||||||
|
* @Date 2021-09-04
|
||||||
|
* @Email webwork.s@qq.com
|
||||||
|
* 文档: https://www.quanzhan.co/luch-request/
|
||||||
|
* github: https://github.com/lei-mu/luch-request
|
||||||
|
* DCloud: http://ext.dcloud.net.cn/plugin?id=392
|
||||||
|
* HBuilderX: beat-3.0.4 alpha-3.0.4
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import dispatchRequest from './dispatchRequest'
|
||||||
|
import InterceptorManager from './InterceptorManager'
|
||||||
|
import mergeConfig from './mergeConfig'
|
||||||
|
import defaults from './defaults'
|
||||||
|
import { isPlainObject } from '../utils'
|
||||||
|
import clone from '../utils/clone'
|
||||||
|
|
||||||
|
export default class Request {
|
||||||
|
/**
|
||||||
|
* @param {Object} arg - 全局配置
|
||||||
|
* @param {String} arg.baseURL - 全局根路径
|
||||||
|
* @param {Object} arg.header - 全局header
|
||||||
|
* @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
|
||||||
|
* @param {String} arg.dataType = [json] - 全局默认的dataType
|
||||||
|
* @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。支付宝小程序不支持
|
||||||
|
* @param {Object} arg.custom - 全局默认的自定义参数
|
||||||
|
* @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认60000。H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
|
||||||
|
* @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+)
|
||||||
|
* @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+)
|
||||||
|
* @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+)
|
||||||
|
* @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300
|
||||||
|
*/
|
||||||
|
constructor(arg = {}) {
|
||||||
|
if (!isPlainObject(arg)) {
|
||||||
|
arg = {}
|
||||||
|
console.warn('设置全局参数必须接收一个Object')
|
||||||
|
}
|
||||||
|
this.config = clone({...defaults, ...arg})
|
||||||
|
this.interceptors = {
|
||||||
|
request: new InterceptorManager(),
|
||||||
|
response: new InterceptorManager()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Function
|
||||||
|
* @param {Request~setConfigCallback} f - 设置全局默认配置
|
||||||
|
*/
|
||||||
|
setConfig(f) {
|
||||||
|
this.config = f(this.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware(config) {
|
||||||
|
config = mergeConfig(this.config, config)
|
||||||
|
let chain = [dispatchRequest, undefined]
|
||||||
|
let promise = Promise.resolve(config)
|
||||||
|
|
||||||
|
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
|
||||||
|
chain.unshift(interceptor.fulfilled, interceptor.rejected)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
|
||||||
|
chain.push(interceptor.fulfilled, interceptor.rejected)
|
||||||
|
})
|
||||||
|
|
||||||
|
while (chain.length) {
|
||||||
|
promise = promise.then(chain.shift(), chain.shift())
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Function
|
||||||
|
* @param {Object} config - 请求配置项
|
||||||
|
* @prop {String} options.url - 请求路径
|
||||||
|
* @prop {Object} options.data - 请求参数
|
||||||
|
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
|
||||||
|
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
|
||||||
|
* @prop {Object} [options.header = config.header] - 请求header
|
||||||
|
* @prop {Object} [options.method = config.method] - 请求方法
|
||||||
|
* @returns {Promise<unknown>}
|
||||||
|
*/
|
||||||
|
request(config = {}) {
|
||||||
|
return this.middleware(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
get(url, options = {}) {
|
||||||
|
return this.middleware({
|
||||||
|
url,
|
||||||
|
method: 'GET',
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
post(url, data, options = {}) {
|
||||||
|
return this.middleware({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
method: 'POST',
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifndef MP-ALIPAY
|
||||||
|
put(url, data, options = {}) {
|
||||||
|
return this.middleware({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
method: 'PUT',
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
|
||||||
|
delete(url, data, options = {}) {
|
||||||
|
return this.middleware({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
method: 'DELETE',
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5 || MP-WEIXIN
|
||||||
|
connect(url, data, options = {}) {
|
||||||
|
return this.middleware({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
method: 'CONNECT',
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5 || MP-WEIXIN || MP-BAIDU
|
||||||
|
head(url, data, options = {}) {
|
||||||
|
return this.middleware({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
method: 'HEAD',
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
|
||||||
|
options(url, data, options = {}) {
|
||||||
|
return this.middleware({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
method: 'OPTIONS',
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5 || MP-WEIXIN
|
||||||
|
trace(url, data, options = {}) {
|
||||||
|
return this.middleware({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
method: 'TRACE',
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
upload(url, config = {}) {
|
||||||
|
config.url = url
|
||||||
|
config.method = 'UPLOAD'
|
||||||
|
return this.middleware(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
download(url, config = {}) {
|
||||||
|
config.url = url
|
||||||
|
config.method = 'DOWNLOAD'
|
||||||
|
return this.middleware(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setConfig回调
|
||||||
|
* @return {Object} - 返回操作后的config
|
||||||
|
* @callback Request~setConfigCallback
|
||||||
|
* @param {Object} config - 全局默认config
|
||||||
|
*/
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
import isAbsoluteURL from '../helpers/isAbsoluteURL'
|
||||||
|
import combineURLs from '../helpers/combineURLs'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new URL by combining the baseURL with the requestedURL,
|
||||||
|
* only when the requestedURL is not already an absolute URL.
|
||||||
|
* If the requestURL is absolute, this function returns the requestedURL untouched.
|
||||||
|
*
|
||||||
|
* @param {string} baseURL The base URL
|
||||||
|
* @param {string} requestedURL Absolute or relative URL to combine
|
||||||
|
* @returns {string} The combined full path
|
||||||
|
*/
|
||||||
|
export default function buildFullPath(baseURL, requestedURL) {
|
||||||
|
if (baseURL && !isAbsoluteURL(requestedURL)) {
|
||||||
|
return combineURLs(baseURL, requestedURL)
|
||||||
|
}
|
||||||
|
return requestedURL
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 默认的全局配置
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
baseURL: '',
|
||||||
|
header: {},
|
||||||
|
method: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
// #ifndef MP-ALIPAY
|
||||||
|
responseType: 'text',
|
||||||
|
// #endif
|
||||||
|
custom: {},
|
||||||
|
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||||
|
timeout: 60000,
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
sslVerify: true,
|
||||||
|
// #endif
|
||||||
|
// #ifdef H5
|
||||||
|
withCredentials: false,
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
firstIpv4: false,
|
||||||
|
// #endif
|
||||||
|
validateStatus: function validateStatus(status) {
|
||||||
|
return status >= 200 && status < 300
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import adapter from '../adapters/index'
|
||||||
|
|
||||||
|
|
||||||
|
export default (config) => {
|
||||||
|
return adapter(config)
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import {deepMerge, isUndefined} from '../utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局
|
||||||
|
* @param {Array} keys - 配置项
|
||||||
|
* @param {Object} globalsConfig - 当前的全局配置
|
||||||
|
* @param {Object} config2 - 局部配置
|
||||||
|
* @return {{}}
|
||||||
|
*/
|
||||||
|
const mergeKeys = (keys, globalsConfig, config2) => {
|
||||||
|
let config = {}
|
||||||
|
keys.forEach(prop => {
|
||||||
|
if (!isUndefined(config2[prop])) {
|
||||||
|
config[prop] = config2[prop]
|
||||||
|
} else if (!isUndefined(globalsConfig[prop])) {
|
||||||
|
config[prop] = globalsConfig[prop]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param globalsConfig - 当前实例的全局配置
|
||||||
|
* @param config2 - 当前的局部配置
|
||||||
|
* @return - 合并后的配置
|
||||||
|
*/
|
||||||
|
export default (globalsConfig, config2 = {}) => {
|
||||||
|
const method = config2.method || globalsConfig.method || 'GET'
|
||||||
|
let config = {
|
||||||
|
baseURL: globalsConfig.baseURL || '',
|
||||||
|
method: method,
|
||||||
|
url: config2.url || '',
|
||||||
|
params: config2.params || {},
|
||||||
|
custom: {...(globalsConfig.custom || {}), ...(config2.custom || {})},
|
||||||
|
header: deepMerge(globalsConfig.header || {}, config2.header || {})
|
||||||
|
}
|
||||||
|
const defaultToConfig2Keys = ['getTask', 'validateStatus']
|
||||||
|
config = {...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2)}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-empty
|
||||||
|
if (method === 'DOWNLOAD') {
|
||||||
|
// #ifdef H5 || APP-PLUS
|
||||||
|
if (!isUndefined(config2.timeout)) {
|
||||||
|
config['timeout'] = config2['timeout']
|
||||||
|
} else if (!isUndefined(globalsConfig.timeout)) {
|
||||||
|
config['timeout'] = globalsConfig['timeout']
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
} else if (method === 'UPLOAD') {
|
||||||
|
delete config.header['content-type']
|
||||||
|
delete config.header['Content-Type']
|
||||||
|
const uploadKeys = [
|
||||||
|
// #ifdef APP-PLUS || H5
|
||||||
|
'files',
|
||||||
|
// #endif
|
||||||
|
// #ifdef MP-ALIPAY
|
||||||
|
'fileType',
|
||||||
|
// #endif
|
||||||
|
// #ifdef H5
|
||||||
|
'file',
|
||||||
|
// #endif
|
||||||
|
'filePath',
|
||||||
|
'name',
|
||||||
|
// #ifdef H5 || APP-PLUS
|
||||||
|
'timeout',
|
||||||
|
// #endif
|
||||||
|
'formData',
|
||||||
|
]
|
||||||
|
uploadKeys.forEach(prop => {
|
||||||
|
if (!isUndefined(config2[prop])) {
|
||||||
|
config[prop] = config2[prop]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #ifdef H5 || APP-PLUS
|
||||||
|
if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
|
||||||
|
config['timeout'] = globalsConfig['timeout']
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
} else {
|
||||||
|
const defaultsKeys = [
|
||||||
|
'data',
|
||||||
|
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||||
|
'timeout',
|
||||||
|
// #endif
|
||||||
|
'dataType',
|
||||||
|
// #ifndef MP-ALIPAY
|
||||||
|
'responseType',
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
'sslVerify',
|
||||||
|
// #endif
|
||||||
|
// #ifdef H5
|
||||||
|
'withCredentials',
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
'firstIpv4',
|
||||||
|
// #endif
|
||||||
|
]
|
||||||
|
config = {...config, ...mergeKeys(defaultsKeys, globalsConfig, config2)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Resolve or reject a Promise based on response status.
|
||||||
|
*
|
||||||
|
* @param {Function} resolve A function that resolves the promise.
|
||||||
|
* @param {Function} reject A function that rejects the promise.
|
||||||
|
* @param {object} response The response.
|
||||||
|
*/
|
||||||
|
export default function settle(resolve, reject, response) {
|
||||||
|
const validateStatus = response.config.validateStatus
|
||||||
|
const status = response.statusCode
|
||||||
|
if (status && (!validateStatus || validateStatus(status))) {
|
||||||
|
resolve(response)
|
||||||
|
} else {
|
||||||
|
reject(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
import * as utils from './../utils'
|
||||||
|
|
||||||
|
function encode(val) {
|
||||||
|
return encodeURIComponent(val).
|
||||||
|
replace(/%40/gi, '@').
|
||||||
|
replace(/%3A/gi, ':').
|
||||||
|
replace(/%24/g, '$').
|
||||||
|
replace(/%2C/gi, ',').
|
||||||
|
replace(/%20/g, '+').
|
||||||
|
replace(/%5B/gi, '[').
|
||||||
|
replace(/%5D/gi, ']')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a URL by appending params to the end
|
||||||
|
*
|
||||||
|
* @param {string} url The base of the url (e.g., http://www.google.com)
|
||||||
|
* @param {object} [params] The params to be appended
|
||||||
|
* @returns {string} The formatted url
|
||||||
|
*/
|
||||||
|
export default function buildURL(url, params) {
|
||||||
|
/*eslint no-param-reassign:0*/
|
||||||
|
if (!params) {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
var serializedParams
|
||||||
|
if (utils.isURLSearchParams(params)) {
|
||||||
|
serializedParams = params.toString()
|
||||||
|
} else {
|
||||||
|
var parts = []
|
||||||
|
|
||||||
|
utils.forEach(params, function serialize(val, key) {
|
||||||
|
if (val === null || typeof val === 'undefined') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (utils.isArray(val)) {
|
||||||
|
key = key + '[]'
|
||||||
|
} else {
|
||||||
|
val = [val]
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.forEach(val, function parseValue(v) {
|
||||||
|
if (utils.isDate(v)) {
|
||||||
|
v = v.toISOString()
|
||||||
|
} else if (utils.isObject(v)) {
|
||||||
|
v = JSON.stringify(v)
|
||||||
|
}
|
||||||
|
parts.push(encode(key) + '=' + encode(v))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
serializedParams = parts.join('&')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serializedParams) {
|
||||||
|
var hashmarkIndex = url.indexOf('#')
|
||||||
|
if (hashmarkIndex !== -1) {
|
||||||
|
url = url.slice(0, hashmarkIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
|
||||||
|
}
|
||||||
|
|
||||||
|
return url
|
||||||
|
}
|
||||||
Alcuni file non sono stati mostrati perché troppi file sono cambiati in questo diff Mostra Altro
Fai riferimento in un nuovo problema
Block a user