源文件
This commit is contained in:
160
uni_modules/mp-html/README.md
Normal file
160
uni_modules/mp-html/README.md
Normal file
@@ -0,0 +1,160 @@
|
||||
## 功能介绍
|
||||
- 全端支持(含 `v3、NVUE`)
|
||||
- 支持丰富的标签(包括 `table`、`video`、`svg` 等)
|
||||
- 支持丰富的事件效果(自动预览图片、链接处理等)
|
||||
- 支持设置占位图(加载中、出错时、预览时)
|
||||
- 支持锚点跳转、长按复制等丰富功能
|
||||
- 支持大部分 *html* 实体
|
||||
- 丰富的插件(关键词搜索、内容 **编辑** 等)
|
||||
- 效率高、容错性强且轻量化
|
||||
|
||||
查看 [功能介绍](https://jin-yufeng.gitee.io/mp-html/#/overview/feature) 了解更多
|
||||
|
||||
## 使用方法
|
||||
- 源码方式
|
||||
1. 点击上方的使用 `HBuilder X` 导入插件或将下载的插件包拷贝到项目根目录下
|
||||
由于插件市场的 **非 uni_modules 版本** 无法更新,若需使用请从 [github](https://github.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 或 [gitee](https://gitee.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 获取最新版本
|
||||
2. 在需要使用页面的 `(n)vue` 文件中添加
|
||||
```html
|
||||
<mp-html :content="html" />
|
||||
```
|
||||
```javascript
|
||||
import mpHtml from '@/components/mp-html/mp-html'
|
||||
export default {
|
||||
// HBuilderX 2.5.5+ 可以通过 easycom 自动引入
|
||||
components: {
|
||||
mpHtml
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
html: '<div>Hello World!</div>'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- npm 方式
|
||||
1. 在项目根目录下执行
|
||||
```bash
|
||||
npm install mp-html
|
||||
```
|
||||
2. 在需要使用页面的 `(n)vue` 文件中添加
|
||||
```html
|
||||
<mp-html :content="html" />
|
||||
```
|
||||
```javascript
|
||||
import mpHtml from 'mp-html/dist/uni-app/components/mp-html/mp-html'
|
||||
export default {
|
||||
// 不可省略
|
||||
components: {
|
||||
mpHtml
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
html: '<div>Hello World!</div>'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
使用 *cli* 方式运行的项目,通过 *npm* 方式引入时,需要在 *vue.config.js* 中配置 *transpileDependencies*,详情可见 [#330](https://github.com/jin-yufeng/mp-html/issues/330#issuecomment-913617687)
|
||||
如果在 **nvue** 中使用还要将 `dist/uni-app/static` 目录下的内容拷贝到项目的 `static` 目录下,否则无法运行
|
||||
|
||||
查看 [快速开始](https://jin-yufeng.gitee.io/mp-html/#/overview/quickstart) 了解更多
|
||||
|
||||
## 组件属性
|
||||
|
||||
| 属性 | 类型 | 默认值 | 说明 |
|
||||
|:---:|:---:|:---:|---|
|
||||
| container-style | String | | 容器的样式([2.1.0+](https://jin-yufeng.gitee.io/mp-html/#/changelog/changelog#v210)) |
|
||||
| content | String | | 用于渲染的 html 字符串 |
|
||||
| copy-link | Boolean | true | 是否允许外部链接被点击时自动复制 |
|
||||
| domain | String | | 主域名(用于链接拼接) |
|
||||
| error-img | String | | 图片出错时的占位图链接 |
|
||||
| lazy-load | Boolean | false | 是否开启图片懒加载 |
|
||||
| loading-img | String | | 图片加载过程中的占位图链接 |
|
||||
| pause-video | Boolean | true | 是否在播放一个视频时自动暂停其他视频 |
|
||||
| preview-img | Boolean | true | 是否允许图片被点击时自动预览 |
|
||||
| scroll-table | Boolean | false | 是否给每个表格添加一个滚动层使其能单独横向滚动 |
|
||||
| selectable | Boolean | false | 是否开启文本长按复制 |
|
||||
| set-title | Boolean | true | 是否将 title 标签的内容设置到页面标题 |
|
||||
| show-img-menu | Boolean | true | 是否允许图片被长按时显示菜单 |
|
||||
| tag-style | Object | | 设置标签的默认样式 |
|
||||
| use-anchor | Boolean | false | 是否使用锚点链接 |
|
||||
|
||||
查看 [属性](https://jin-yufeng.gitee.io/mp-html/#/basic/prop) 了解更多
|
||||
|
||||
## 组件事件
|
||||
|
||||
| 名称 | 触发时机 |
|
||||
|:---:|---|
|
||||
| load | dom 树加载完毕时 |
|
||||
| ready | 图片加载完毕时 |
|
||||
| error | 发生渲染错误时 |
|
||||
| imgtap | 图片被点击时 |
|
||||
| linktap | 链接被点击时 |
|
||||
|
||||
查看 [事件](https://jin-yufeng.gitee.io/mp-html/#/basic/event) 了解更多
|
||||
|
||||
## api
|
||||
组件实例上提供了一些 `api` 方法可供调用
|
||||
|
||||
| 名称 | 作用 |
|
||||
|:---:|---|
|
||||
| in | 将锚点跳转的范围限定在一个 scroll-view 内 |
|
||||
| navigateTo | 锚点跳转 |
|
||||
| getText | 获取文本内容 |
|
||||
| getRect | 获取富文本内容的位置和大小 |
|
||||
| setContent | 设置富文本内容 |
|
||||
| imgList | 获取所有图片的数组 |
|
||||
|
||||
查看 [api](https://jin-yufeng.gitee.io/mp-html/#/advanced/api) 了解更多
|
||||
|
||||
## 插件扩展
|
||||
除基本功能外,本组件还提供了丰富的扩展,可按照需要选用
|
||||
|
||||
| 名称 | 作用 |
|
||||
|:---:|---|
|
||||
| audio | 音乐播放器 |
|
||||
| editable | 富文本编辑([示例项目](https://6874-html-foe72-1259071903.tcb.qcloud.la/editable.zip?sign=cc0017be203fb3dbca62d33a0c15792e&t=1608447445)) |
|
||||
| emoji | 解析 emoji |
|
||||
| highlight | 代码块高亮显示 |
|
||||
| markdown | 渲染 markdown |
|
||||
| search | 关键词搜索 |
|
||||
| style | 匹配 style 标签中的样式 |
|
||||
| txv-video | 使用腾讯视频 |
|
||||
| img-cache | 图片缓存 by [@PentaTea](https://github.com/PentaTea) |
|
||||
|
||||
从插件市场导入的包中 **不含有** 扩展插件,需要使用插件参考以下方法:
|
||||
1. 获取完整组件包
|
||||
```bash
|
||||
npm install mp-html
|
||||
```
|
||||
2. 编辑 `tools/config.js` 中的 `plugins` 项,选择需要的插件
|
||||
3. 生成新的组件包
|
||||
在 `node_modules/mp-html` 目录下执行
|
||||
```bash
|
||||
npm install
|
||||
npm run build:uni-app
|
||||
```
|
||||
4. 拷贝 `dist/uni-app` 中的内容到项目根目录
|
||||
|
||||
查看 [插件](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin) 了解更多
|
||||
|
||||
## 示例体验
|
||||

|
||||
|
||||
## 关于 nvue
|
||||
`nvue` 使用原生渲染,不支持部分 `css` 样式,为实现和 `html` 相同的效果,组件内部通过 `web-view` 进行渲染,性能上差于原生,根据 `weex` 官方建议,`web` 标签仅应用在非常规的降级场景。因此,如果通过原生的方式(如 `richtext`)能够满足需要,则不建议使用本组件,如果有较多的富文本内容,则可以直接使用 `vue` 页面
|
||||
由于渲染方式与其他端不同,有以下限制:
|
||||
1. 不支持 `lazy-load` 属性
|
||||
2. 视频不支持全屏播放
|
||||
|
||||
纯 `nvue` 模式下,[此问题](https://ask.dcloud.net.cn/question/119678) 修复前,不支持通过 `uni_modules` 引入,需要本地引入(将 [dist/uni-app](https://github.com/jin-yufeng/mp-html/tree/master/dist/uni-app) 中的内容拷贝到项目根目录下)
|
||||
|
||||
## 问题反馈
|
||||
遇到问题时,请先查阅 [常见问题](https://jin-yufeng.gitee.io/mp-html/#/question/faq) 和 [issue](https://github.com/jin-yufeng/mp-html/issues) 中是否已有相同的问题
|
||||
可通过 [issue](https://github.com/jin-yufeng/mp-html/issues/new/choose) 、插件问答或发送邮件到 [mp_html@126.com](mailto:mp_html@126.com) 提问,不建议在评论区提问(不方便回复)
|
||||
提问请严格按照 [issue 模板](https://github.com/jin-yufeng/mp-html/issues/new/choose) ,描述清楚使用环境、`html` 内容或可复现的 `demo` 项目以及复现方式,对于 **描述不清**、**无法复现** 或重复的问题将不予回复
|
||||
|
||||
查看 [问题反馈](https://jin-yufeng.gitee.io/mp-html/#/question/feedback) 了解更多
|
||||
62
uni_modules/mp-html/changelog.md
Normal file
62
uni_modules/mp-html/changelog.md
Normal file
@@ -0,0 +1,62 @@
|
||||
## v2.2.0(2021-10-12)
|
||||
1. `A` 增加 `customElements` 配置项,便于添加自定义功能性标签 [详细](https://github.com/jin-yufeng/mp-html/issues/350)
|
||||
2. `A` `editable` 插件增加切换音视频自动播放状态的功能 [详细](https://github.com/jin-yufeng/mp-html/pull/341) by [@leeseett](https://github.com/leeseett)
|
||||
3. `A` `editable` 插件删除媒体标签时触发 `remove` 事件,便于删除已上传的文件
|
||||
4. `U` `editable` 插件 `insertImg` 方法支持同时插入多张图片 [详细](https://github.com/jin-yufeng/mp-html/issues/342)
|
||||
5. `U` `editable` 插入图片和音视频时支持拼接 `domian` 主域名
|
||||
6. `F` 修复了内部链接参数中包含 `://` 时被认为是外部链接的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/356)
|
||||
7. `F` 修复了部分 `svg` 标签名或属性名大小写不正确时不生效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/351)
|
||||
8. `F` 修复了 `nvue` 页面运行到非 `app` 平台时可能样式错误的问题
|
||||
## v2.1.5(2021-08-13)
|
||||
1. `A` 增加支持标签的 `dir` 属性
|
||||
2. `F` 修复了 `ruby` 标签文字与拼音没有居中对齐的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/325)
|
||||
3. `F` 修复了音视频标签内有 `a` 标签时可能无法播放的问题
|
||||
4. `F` 修复了 `externStyle` 中的 `class` 名包含下划线或数字时可能失效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/326)
|
||||
5. `F` 修复了 `h5` 端引入 `externStyle` 可能不生效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/326)
|
||||
## v2.1.4(2021-07-14)
|
||||
1. `F` 修复了 `rt` 标签无法设置样式的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/318)
|
||||
2. `F` 修复了表格中有单元格同时合并行和列时可能显示不正确的问题
|
||||
3. `F` 修复了 `app` 端无法关闭图片长按菜单的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/322)
|
||||
4. `F` 修复了 `editable` 插件只能添加图片链接不能修改的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/312) by [@leeseett](https://github.com/leeseett)
|
||||
## v2.1.3(2021-06-12)
|
||||
1. `A` `editable` 插件增加 `insertTable` 方法
|
||||
2. `U` `editable` 插件支持编辑表格中的空白单元格 [详细](https://github.com/jin-yufeng/mp-html/issues/310)
|
||||
3. `F` 修复了 `externStyle` 中使用伪类可能失效的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/298)
|
||||
4. `F` 修复了多个组件同时使用时 `tag-style` 属性时可能互相影响的问题 [详细](https://github.com/jin-yufeng/mp-html/pull/305) by [@woodguoyu](https://github.com/woodguoyu)
|
||||
5. `F` 修复了包含 `linearGradient` 的 `svg` 可能无法显示的问题
|
||||
6. `F` 修复了编译到头条小程序时可能报错的问题
|
||||
7. `F` 修复了 `nvue` 端不触发 `click` 事件的问题
|
||||
8. `F` 修复了 `editable` 插件尾部插入时无法撤销的问题
|
||||
9. `F` 修复了 `editable` 插件的 `insertHtml` 方法只能在末尾插入的问题
|
||||
10. `F` 修复了 `editable` 插件插入音频不显示的问题
|
||||
## v2.1.2(2021-04-24)
|
||||
1. `A` 增加了 [img-cache](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#img-cache) 插件,可以在 `app` 端缓存图片 [详细](https://github.com/jin-yufeng/mp-html/issues/292) by [@PentaTea](https://github.com/PentaTea)
|
||||
2. `U` 支持通过 `container-style` 属性设置 `white-space` 来保留连续空格和换行符 [详细](https://jin-yufeng.gitee.io/mp-html/#/question/faq#space)
|
||||
3. `U` 代码风格符合 [standard](https://standardjs.com) 标准
|
||||
4. `U` `editable` 插件编辑状态下支持预览视频 [详细](https://github.com/jin-yufeng/mp-html/issues/286)
|
||||
5. `F` 修复了 `svg` 标签内嵌 `svg` 时无法显示的问题
|
||||
6. `F` 修复了编译到支付宝和头条小程序时部分区域不可复制的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/291)
|
||||
## v2.1.1(2021-04-09)
|
||||
1. 修复了对 `p` 标签设置 `tag-style` 可能不生效的问题
|
||||
2. 修复了 `svg` 标签中的文本无法显示的问题
|
||||
3. 修复了使用 `editable` 插件编辑表格时可能报错的问题
|
||||
4. 修复了使用 `highlight` 插件运行到头条小程序时可能没有样式的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/280)
|
||||
5. 修复了使用 `editable` 插件 `editable` 属性为 `false` 时会报错的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/284)
|
||||
6. 修复了 `style` 插件连续子选择器失效的问题
|
||||
7. 修复了 `editable` 插件无法修改图片和字体大小的问题
|
||||
## v2.1.0.2(2021-03-21)
|
||||
修复了 `nvue` 端使用可能报错的问题
|
||||
## v2.1.0(2021-03-20)
|
||||
1. `A` 增加了 [container-style](https://jin-yufeng.gitee.io/mp-html/#/basic/prop#container-style) 属性 [详细](https://gitee.com/jin-yufeng/mp-html/pulls/1)
|
||||
2. `A` 增加支持 `strike` 标签
|
||||
3. `A` `editable` 插件增加 `placeholder` 属性 [详细](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#editable)
|
||||
4. `A` `editable` 插件增加 `insertHtml` 方法 [详细](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#editable)
|
||||
5. `U` 外部样式支持标签名选择器 [详细](https://jin-yufeng.gitee.io/mp-html/#/overview/quickstart#setting)
|
||||
6. `F` 修复了 `nvue` 端部分情况下可能不显示的问题
|
||||
## v2.0.5(2021-03-12)
|
||||
1. `U` [linktap](https://jin-yufeng.gitee.io/mp-html/#/basic/event#linktap) 事件增加返回内部文本内容 `innerText` [详细](https://github.com/jin-yufeng/mp-html/issues/271)
|
||||
2. `U` [selectable](https://jin-yufeng.gitee.io/mp-html/#/basic/prop#selectable) 属性设置为 `force` 时能够在微信 `iOS` 端生效(文本块会变成 `inline-block`) [详细](https://github.com/jin-yufeng/mp-html/issues/267)
|
||||
3. `F` 修复了部分情况下竖向无法滚动的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/182)
|
||||
4. `F` 修复了多次修改富文本数据时部分内容可能不显示的问题
|
||||
5. `F` 修复了 [腾讯视频](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#txv-video) 插件可能无法播放的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/265)
|
||||
6. `F` 修复了 [highlight](https://jin-yufeng.gitee.io/mp-html/#/advanced/plugin#highlight) 插件没有设置高亮语言时没有应用默认样式的问题 [详细](https://github.com/jin-yufeng/mp-html/issues/276) by [@fuzui](https://github.com/fuzui)
|
||||
432
uni_modules/mp-html/components/mp-html/mp-html.vue
Normal file
432
uni_modules/mp-html/components/mp-html/mp-html.vue
Normal file
@@ -0,0 +1,432 @@
|
||||
<template>
|
||||
<view id="_root" :class="(selectable?'_select ':'')+'_root'" :style="containerStyle">
|
||||
<slot v-if="!nodes[0]" />
|
||||
<!-- #ifndef APP-PLUS-NVUE -->
|
||||
<node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu]" name="span" />
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-PLUS-NVUE -->
|
||||
<web-view ref="web" src="/uni_modules/mp-html/static/app-plus/mp-html/local.html" :style="'margin-top:-2px;height:' + height + 'px'" @onPostMessage="_onMessage" />
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* mp-html v2.2.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} error 媒体加载出错时触发
|
||||
*/
|
||||
// #ifndef APP-PLUS-NVUE
|
||||
import node from './node/node'
|
||||
// #endif
|
||||
const plugins=[]
|
||||
const Parser = require('./parser')
|
||||
// #ifdef APP-PLUS-NVUE
|
||||
const dom = weex.requireModule('dom')
|
||||
// #endif
|
||||
export default {
|
||||
name: 'mp-html',
|
||||
data () {
|
||||
return {
|
||||
nodes: [],
|
||||
// #ifdef APP-PLUS-NVUE
|
||||
height: 3
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
props: {
|
||||
containerStyle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
content: String,
|
||||
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]
|
||||
},
|
||||
// #ifndef APP-PLUS-NVUE
|
||||
components: {
|
||||
node
|
||||
},
|
||||
// #endif
|
||||
watch: {
|
||||
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.nodes.length) {
|
||||
this.setContent(this.content)
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this._hook('onDetached')
|
||||
clearInterval(this._timer)
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @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) {
|
||||
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 设置内容
|
||||
* @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')
|
||||
})
|
||||
|
||||
// 等待图片加载完毕
|
||||
let height
|
||||
clearInterval(this._timer)
|
||||
this._timer = setInterval(() => {
|
||||
this.getRect().then(rect => {
|
||||
// 350ms 总高度无变化就触发 ready 事件
|
||||
if (rect.height === height) {
|
||||
this.$emit('ready', rect)
|
||||
clearInterval(this._timer)
|
||||
}
|
||||
height = rect.height
|
||||
}).catch(() => { })
|
||||
}, 350)
|
||||
// #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 '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 */
|
||||
</style>
|
||||
534
uni_modules/mp-html/components/mp-html/node/node.vue
Normal file
534
uni_modules/mp-html/components/mp-html/node/node.vue
Normal file
@@ -0,0 +1,534 @@
|
||||
<template>
|
||||
<view :id="attrs.id" :class="'_block _'+name+' '+(attrs.class || '')" :style="attrs.style">
|
||||
<block v-for="(n, i) in childs" v-bind:key="i">
|
||||
<!-- 图片 -->
|
||||
<!-- 占位图 -->
|
||||
<image v-if="n.name==='img'&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
|
||||
<!-- 显示图片 -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<img v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+(n.attrs.class || '')" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap"/>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef H5 || APP-PLUS -->
|
||||
<image v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+(n.attrs.class || '')" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="n.h?'':'widthFix'" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
|
||||
<!-- #endif -->
|
||||
<!-- 文本 -->
|
||||
<!-- #ifndef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
|
||||
<text v-else-if="n.text" :user-select="n.us" decode>{{n.text}}</text>
|
||||
<!-- #endif -->
|
||||
<text v-else-if="n.name==='br'">\n</text>
|
||||
<!-- 链接 -->
|
||||
<view v-else-if="n.name==='a'" :id="n.attrs.id" :class="(n.attrs.href?'_a ':'')+(n.attrs.class || '')" hover-class="_hover" :style="'display:inline;'+n.attrs.style" :data-i="i" @tap.stop="linkTap">
|
||||
<node name="span" :childs="n.children" :opts="opts" style="display:inherit" />
|
||||
</view>
|
||||
<!-- 视频 -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+(n.attrs.class || '')" :style="n.attrs.style" v-html="n.html" />
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-PLUS -->
|
||||
<video v-else-if="n.name==='video'" :id="n.attrs.id" :class="(n.attrs.class || '')" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef H5 || APP-PLUS -->
|
||||
<iframe v-else-if="n.name==='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" />
|
||||
<embed v-else-if="n.name==='embed'" :style="n.attrs.style" :src="n.attrs.src" />
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-TOUTIAO -->
|
||||
<!-- 音频 -->
|
||||
<audio v-else-if="n.name==='audio'" :id="n.attrs.id" :class="(n.attrs.class || '')" :style="n.attrs.style" :author="n.attrs.author" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
|
||||
<!-- #endif -->
|
||||
<view v-else-if="(n.name==='table'&&n.c)||n.name==='li'" :id="n.attrs.id" :class="'_'+n.name+' '+(n.attrs.class || '')" :style="n.attrs.style">
|
||||
<node v-if="n.name==='li'" :childs="n.children" :opts="opts" />
|
||||
<view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style">
|
||||
<node v-if="tbody.name==='td'||tbody.name==='th'" :childs="tbody.children" :opts="opts" />
|
||||
<block v-else v-for="(tr, y) in tbody.children" v-bind:key="y">
|
||||
<view v-if="tr.name==='td'||tr.name==='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
|
||||
<node :childs="tr.children" :opts="opts" />
|
||||
</view>
|
||||
<view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
|
||||
<view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style">
|
||||
<node :childs="td.children" :opts="opts" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 富文本 -->
|
||||
<!-- #ifdef H5 || MP-WEIXIN || MP-QQ || APP-PLUS || MP-360 -->
|
||||
<rich-text v-else-if="handler.use(n)" :id="n.attrs.id" :style="n.f" :nodes="[n]" />
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef H5 || MP-WEIXIN || MP-QQ || APP-PLUS || MP-360 -->
|
||||
<rich-text v-else-if="!n.c" :id="n.attrs.id" :style="n.f+';display:inline'" :preview="false" :nodes="[n]" />
|
||||
<!-- #endif -->
|
||||
<!-- 继续递归 -->
|
||||
<view v-else-if="n.c===2" :id="n.attrs.id" :class="'_block _'+n.name+' '+(n.attrs.class || '')" :style="n.f+';'+n.attrs.style">
|
||||
<node v-for="(n2, j) in n.children" v-bind:key="j" :style="n2.f" :name="n2.name" :attrs="n2.attrs" :childs="n2.children" :opts="opts" />
|
||||
</view>
|
||||
<node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" />
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
<script module="handler" lang="wxs">
|
||||
// 行内标签列表
|
||||
var inlineTags = {
|
||||
abbr: true,
|
||||
b: true,
|
||||
big: true,
|
||||
code: true,
|
||||
del: true,
|
||||
em: true,
|
||||
i: true,
|
||||
ins: true,
|
||||
label: true,
|
||||
q: true,
|
||||
small: true,
|
||||
span: true,
|
||||
strong: true,
|
||||
sub: true,
|
||||
sup: true
|
||||
}
|
||||
/**
|
||||
* @description 是否使用 rich-text 显示剩余内容
|
||||
*/
|
||||
module.exports = {
|
||||
use: function (item) {
|
||||
if (item.c) return false
|
||||
// 微信和 QQ 的 rich-text inline 布局无效
|
||||
return !inlineTags[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
|
||||
import node from './node'
|
||||
export default {
|
||||
name: 'node',
|
||||
options: {
|
||||
// #ifdef MP-WEIXIN
|
||||
virtualHost: true,
|
||||
// #endif
|
||||
// #ifdef MP-TOUTIAO
|
||||
addGlobalClass: false
|
||||
// #endif
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
ctrl: {}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
name: String,
|
||||
attrs: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
childs: Array,
|
||||
opts: Array
|
||||
},
|
||||
components: {
|
||||
|
||||
node
|
||||
},
|
||||
mounted () {
|
||||
this.$nextTick(() => {
|
||||
for (this.root = this.$parent; this.root.$options.name !== 'mp-html'; this.root = this.root.$parent);
|
||||
})
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (this.opts[0]) {
|
||||
let i
|
||||
for (i = this.childs.length; i--;) {
|
||||
if (this.childs[i].name === 'img') break
|
||||
}
|
||||
if (i !== -1) {
|
||||
this.observer = uni.createIntersectionObserver(this).relativeToViewport({
|
||||
top: 500,
|
||||
bottom: 500
|
||||
})
|
||||
this.observer.observe('._img', res => {
|
||||
if (res.intersectionRatio) {
|
||||
this.$set(this.ctrl, 'load', 1)
|
||||
this.observer.disconnect()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
beforeDestroy () {
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (this.observer) {
|
||||
this.observer.disconnect()
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
methods:{
|
||||
// #ifdef MP-WEIXIN
|
||||
toJSON () { },
|
||||
// #endif
|
||||
/**
|
||||
* @description 播放视频事件
|
||||
* @param {Event} e
|
||||
*/
|
||||
play (e) {
|
||||
// #ifndef APP-PLUS
|
||||
if (this.root.pauseVideo) {
|
||||
let flag = false; const id = e.target.id
|
||||
for (let i = this.root._videos.length; i--;) {
|
||||
if (this.root._videos[i].id === id) {
|
||||
flag = true
|
||||
} else {
|
||||
this.root._videos[i].pause() // 自动暂停其他视频
|
||||
}
|
||||
}
|
||||
// 将自己加入列表
|
||||
if (!flag) {
|
||||
const ctx = uni.createVideoContext(id
|
||||
// #ifndef MP-BAIDU
|
||||
, this
|
||||
// #endif
|
||||
)
|
||||
ctx.id = id
|
||||
this.root._videos.push(ctx)
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 图片点击事件
|
||||
* @param {Event} e
|
||||
*/
|
||||
imgTap (e) {
|
||||
const node = this.childs[e.currentTarget.dataset.i]
|
||||
if (node.a) {
|
||||
this.linkTap(node.a)
|
||||
return
|
||||
}
|
||||
if (node.attrs.ignore) return
|
||||
// #ifdef H5 || APP-PLUS
|
||||
node.attrs.src = node.attrs.src || node.attrs['data-src']
|
||||
// #endif
|
||||
this.root.$emit('imgtap', node.attrs)
|
||||
// 自动预览图片
|
||||
if (this.root.previewImg) {
|
||||
uni.previewImage({
|
||||
current: parseInt(node.attrs.i),
|
||||
urls: this.root.imgList
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 图片长按
|
||||
*/
|
||||
imgLongTap (e) {
|
||||
// #ifdef APP-PLUS
|
||||
const attrs = this.childs[e.currentTarget.dataset.i].attrs
|
||||
if (this.opts[3] && !attrs.ignore) {
|
||||
uni.showActionSheet({
|
||||
itemList: ['保存图片'],
|
||||
success: () => {
|
||||
const save = path => {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: path,
|
||||
success () {
|
||||
uni.showToast({
|
||||
title: '保存成功'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
if (this.root.imgList[attrs.i].startsWith('http')) {
|
||||
uni.downloadFile({
|
||||
url: this.root.imgList[attrs.i],
|
||||
success: res => save(res.tempFilePath)
|
||||
})
|
||||
} else {
|
||||
save(this.root.imgList[attrs.i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 图片加载完成事件
|
||||
* @param {Event} e
|
||||
*/
|
||||
imgLoad (e) {
|
||||
const i = e.currentTarget.dataset.i
|
||||
/* #ifndef H5 || APP-PLUS */
|
||||
if (!this.childs[i].w) {
|
||||
// 设置原宽度
|
||||
this.$set(this.ctrl, i, e.detail.width)
|
||||
} else /* #endif */ if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] === -1) {
|
||||
// 加载完毕,取消加载中占位图
|
||||
this.$set(this.ctrl, i, 1)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 链接点击事件
|
||||
* @param {Event} e
|
||||
*/
|
||||
linkTap (e) {
|
||||
const node = e.currentTarget ? this.childs[e.currentTarget.dataset.i] : {}
|
||||
const attrs = node.attrs || e
|
||||
const href = attrs.href
|
||||
this.root.$emit('linktap', Object.assign({
|
||||
innerText: this.root.getText(node.children || []) // 链接内的文本内容
|
||||
}, attrs))
|
||||
if (href) {
|
||||
if (href[0] === '#') {
|
||||
// 跳转锚点
|
||||
this.root.navigateTo(href.substring(1)).catch(() => { })
|
||||
} else if (href.substr(0, 7) == '/pages/') {
|
||||
// 跳转页面
|
||||
uni.navigateTo({
|
||||
url: href,
|
||||
fail () {
|
||||
uni.switchTab({
|
||||
url: href,
|
||||
fail () { }
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 复制外部链接
|
||||
if (this.root.copyLink) {
|
||||
if (href.split('?')[0].includes('://')) {
|
||||
// #ifdef H5
|
||||
window.open(href)
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
uni.setClipboardData({
|
||||
data: href,
|
||||
success: () =>
|
||||
uni.showToast({
|
||||
title: '复制成功'
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openWeb(href)
|
||||
// #endif
|
||||
} else {
|
||||
uni.setClipboardData({
|
||||
data: href,
|
||||
success: () =>
|
||||
uni.showToast({
|
||||
title: '复制成功'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 错误事件
|
||||
* @param {Event} e
|
||||
*/
|
||||
mediaError (e) {
|
||||
const i = e.currentTarget.dataset.i
|
||||
const node = this.childs[i]
|
||||
// 加载其他源
|
||||
if (node.name === 'video' || node.name === 'audio') {
|
||||
let index = (this.ctrl[i] || 0) + 1
|
||||
if (index > node.src.length) {
|
||||
index = 0
|
||||
}
|
||||
if (index < node.src.length) {
|
||||
this.$set(this.ctrl, i, index)
|
||||
return
|
||||
}
|
||||
} else if (node.name === 'img' && this.opts[2]) {
|
||||
// 显示错误占位图
|
||||
this.$set(this.ctrl, i, -1)
|
||||
}
|
||||
if (this.root) {
|
||||
this.root.$emit('error', {
|
||||
source: node.name,
|
||||
attrs: node.attrs,
|
||||
errMsg: e.detail.errMsg
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
/* a 标签默认效果 */
|
||||
._a {
|
||||
padding: 1.5px 0 1.5px 0;
|
||||
color: #366092;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* a 标签点击态效果 */
|
||||
._hover {
|
||||
text-decoration: underline;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 图片默认效果 */
|
||||
._img {
|
||||
max-width: 100%;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
/* 内部样式 */
|
||||
|
||||
._block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
._b,
|
||||
._strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
._code {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
._del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
._em,
|
||||
._i {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
._h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
._h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
._h3 {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
|
||||
._h5 {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
|
||||
._h6 {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
|
||||
._h1,
|
||||
._h2,
|
||||
._h3,
|
||||
._h4,
|
||||
._h5,
|
||||
._h6 {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
._image {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
._ins {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
._li {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
._ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
._ol,
|
||||
._ul {
|
||||
display: block;
|
||||
padding-left: 40px;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
._q::before {
|
||||
content: '"';
|
||||
}
|
||||
|
||||
._q::after {
|
||||
content: '"';
|
||||
}
|
||||
|
||||
._sub {
|
||||
font-size: smaller;
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
._sup {
|
||||
font-size: smaller;
|
||||
vertical-align: super;
|
||||
}
|
||||
|
||||
._thead,
|
||||
._tbody,
|
||||
._tfoot {
|
||||
display: table-row-group;
|
||||
}
|
||||
|
||||
._tr {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
._td,
|
||||
._th {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
._th {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
._ul {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
._ul ._ul {
|
||||
margin: 0;
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
._ul ._ul ._ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
._abbr,
|
||||
._b,
|
||||
._code,
|
||||
._del,
|
||||
._em,
|
||||
._i,
|
||||
._ins,
|
||||
._label,
|
||||
._q,
|
||||
._span,
|
||||
._strong,
|
||||
._sub,
|
||||
._sup {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* #ifdef APP-PLUS */
|
||||
._video {
|
||||
width: 300px;
|
||||
height: 225px;
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
1223
uni_modules/mp-html/components/mp-html/parser.js
Normal file
1223
uni_modules/mp-html/components/mp-html/parser.js
Normal file
File diff suppressed because it is too large
Load Diff
79
uni_modules/mp-html/package.json
Normal file
79
uni_modules/mp-html/package.json
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"id": "mp-html",
|
||||
"displayName": "mp-html 富文本组件【全端支持,可编辑】",
|
||||
"version": "v2.2.0",
|
||||
"description": "一个强大的富文本组件,高效轻量,功能丰富",
|
||||
"keywords": [
|
||||
"富文本",
|
||||
"编辑器",
|
||||
"html",
|
||||
"rich-text",
|
||||
"editor"
|
||||
],
|
||||
"repository": "https://github.com/jin-yufeng/mp-html",
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/mp-html"
|
||||
},
|
||||
"uni_modules": {
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "u",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "y",
|
||||
"联盟": "y"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(){o[1]&&(this.src=o[1],this.onerror=null),this.onclick=null,this.ontouchstart=null,uni.postMessage({data:{action:"onError",source:"img",attrs:t(this)}})}function n(r,i,s){for(var c=0;c<r.length;c++)!function(c){var u=r[c],l=void 0;if(u.type&&"node"!==u.type)l=document.createTextNode(u.text.replace(/&/g,"&"));else{var d=u.name;"svg"===d&&(s="http://www.w3.org/2000/svg"),"html"!==d&&"body"!==d||(d="div"),l=s?document.createElementNS(s,d):document.createElement(d);for(var g in u.attrs)l.setAttribute(g,u.attrs[g]);if(u.children&&n(u.children,l,s),"img"===d){if(!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)}})}),o[2]){var p=new Image;p.src=l.src,l.src=o[2],p.onload=function(){l.src=this.src},p.onerror=function(){l.onerror()}}l.onerror=e}else if("a"===d)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"===d||"audio"===d)a.push(l),u.attrs.autoplay||u.attrs.controls||l.setAttribute("controls","true"),o[3]&&(l.onplay=function(){for(var t=0;t<a.length;t++)a[t]!==this&&a[t].pause()}),l.onerror=function(){uni.postMessage({data:{action:"onError",source:d,attrs:t(this)}})};else if("table"===d&&o[4]&&!l.style.cssText.includes("inline")){var h=document.createElement("div");h.style.overflow="auto",h.appendChild(l),l=h}else"svg"===d&&(s=void 0)}i.appendChild(l)}(c)}document.addEventListener("UniAppJSBridgeReady",function(){document.body.onclick=function(){return uni.postMessage({data:{action:"onClick"}})},uni.postMessage({data:{action:"onJSBridgeReady"}})});var o,a=[];window.setContent=function(t,e,r){var i=document.getElementById("content");e[0]&&(document.body.style.cssText=e[0]),e[5]||(i.style.userSelect="none"),r||(i.innerHTML="",a=[]),o=e;var s=document.createDocumentFragment();n(t,s),i.appendChild(s);var c=i.scrollHeight;uni.postMessage({data:{action:"onLoad",height:c}}),clearInterval(window.timer);var u=!1;window.timer=setInterval(function(){i.scrollHeight!==c?(c=i.scrollHeight,uni.postMessage({data:{action:"onHeightChange",height:c}})):u||(u=!0,uni.postMessage({data:{action:"onReady"}}))},350)},window.onunload=function(){clearInterval(window.timer)};
|
||||
1
uni_modules/mp-html/static/app-plus/mp-html/js/uni.webview.min.js
vendored
Normal file
1
uni_modules/mp-html/static/app-plus/mp-html/js/uni.webview.min.js
vendored
Normal file
@@ -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}));
|
||||
1
uni_modules/mp-html/static/app-plus/mp-html/local.html
Normal file
1
uni_modules/mp-html/static/app-plus/mp-html/local.html
Normal file
@@ -0,0 +1 @@
|
||||
<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}@keyframes show{0%{opacity:0}100%{opacity:1}}</style></head><body><div id="content"></div><script type="text/javascript" src="./js/uni.webview.min.js"></script><script type="text/javascript" src="./js/handler.js"></script></body>
|
||||
15
uni_modules/sp-editor/changelog.md
Normal file
15
uni_modules/sp-editor/changelog.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## 1.0.3(2023-10-13)
|
||||
1. 更新readme文档
|
||||
2. 更新调整组件示例项目,添加插件代码中部分注释
|
||||
## 1.0.2(2023-10-13)
|
||||
1. 更新uni_modules规范,可一键导入组件
|
||||
2. 更新组件示例项目(包括使用uniCloud.uploadFile多选上传图片示例方法)
|
||||
## 1.0.1(2023-10-12)
|
||||
1. 修复小程序中自动聚焦滚动到富文本组件区域的bug
|
||||
2. 略微调整了富文本上方toolbar工具栏中按钮的大小尺寸
|
||||
## 1.0.0(2023-9-19)
|
||||
1. 新增字体与背景颜色板
|
||||
2. 可自定义预设内容模板
|
||||
3. 解决官方样例在小程序和app部分报错不兼容的问题
|
||||
4. 可配合云存储上传富文本中插入的图片 本质上是基于官方内置富文本editor组件改版封装,所以官方有的功能都有,官方能兼容的也都兼容
|
||||
|
||||
784
uni_modules/sp-editor/components/sp-editor/color-picker.vue
Normal file
784
uni_modules/sp-editor/components/sp-editor/color-picker.vue
Normal file
@@ -0,0 +1,784 @@
|
||||
<template>
|
||||
<view v-if="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>
|
||||
238
uni_modules/sp-editor/components/sp-editor/editor-icon.css
Normal file
238
uni_modules/sp-editor/components/sp-editor/editor-icon.css
Normal file
File diff suppressed because one or more lines are too long
BIN
uni_modules/sp-editor/components/sp-editor/iconfont.ttf
Normal file
BIN
uni_modules/sp-editor/components/sp-editor/iconfont.ttf
Normal file
Binary file not shown.
273
uni_modules/sp-editor/components/sp-editor/sp-editor.vue
Normal file
273
uni_modules/sp-editor/components/sp-editor/sp-editor.vue
Normal file
@@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<view class="wrapper">
|
||||
<view class="editor-wrapper" :class="tools.length > 8 ? 'two-row' : 'one-row'">
|
||||
<editor id="editor" class="ql-container" show-img-size show-img-toolbar show-img-resize :placeholder="placeholder" :read-only="readOnly" @statuschange="onStatusChange" @ready="onEditorReady" @input="onEditorInput"></editor>
|
||||
</view>
|
||||
<PickerColor ref="colorPicker" :color="{ r: 0, g: 0, b: 0, a: 1 }" @confirm="confirmColor"></PickerColor>
|
||||
<view class="toolbar" @tap="format">
|
||||
<!-- 字体加粗 -->
|
||||
<view v-if="tools.indexOf('bold') !== -1" :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold"></view>
|
||||
<!-- 斜体 -->
|
||||
<view v-if="tools.indexOf('italic') !== -1" :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic"></view>
|
||||
<!-- 下划线 -->
|
||||
<view v-if="tools.indexOf('underline') !== -1" :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian" data-name="underline"></view>
|
||||
<!-- 字体删除线 -->
|
||||
<view v-if="tools.indexOf('strike') !== -1" :class="formats.strike ? 'ql-active' : ''" class="iconfont icon-zitishanchuxian" data-name="strike"></view>
|
||||
<!-- #ifndef MP-BAIDU -->
|
||||
<!-- 左对齐 -->
|
||||
<view v-if="tools.indexOf('left') !== -1" :class="formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi" data-name="align" data-value="left"></view>
|
||||
<!-- #endif -->
|
||||
<!-- 居中对齐 -->
|
||||
<view v-if="tools.indexOf('center') !== -1" :class="formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi" data-name="align" data-value="center"></view>
|
||||
<!-- 右对齐 -->
|
||||
<view v-if="tools.indexOf('right') !== -1" :class="formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi" data-name="align" data-value="right"></view>
|
||||
<!-- #ifndef MP-BAIDU -->
|
||||
<view v-if="tools.indexOf('fontSize') !== -1" :class="formats.fontSize === '24px' ? 'ql-active' : ''" class="iconfont icon-fontsize" data-name="fontSize" data-value="24px"></view>
|
||||
<!-- #endif -->
|
||||
<view v-if="tools.indexOf('color') !== -1" :style="{ color: textColor }" class="iconfont icon-text_color" data-name="color" :data-value="textColor"></view>
|
||||
<view v-if="tools.indexOf('fontbgcolor') !== -1" :style="{ color: backgroundColor }" class="iconfont icon-fontbgcolor" data-name="background-color" :data-value="backgroundColor"></view>
|
||||
<view v-if="tools.indexOf('ordered') !== -1" :class="formats.list === 'ordered' ? 'ql-active' : ''" class="iconfont icon-youxupailie" data-name="list" data-value="ordered"></view>
|
||||
<view v-if="tools.indexOf('bullet') !== -1" :class="formats.list === 'bullet' ? 'ql-active' : ''" class="iconfont icon-wuxupailie" data-name="list" data-value="bullet"></view>
|
||||
|
||||
<view v-if="tools.indexOf('images') !== -1" class="iconfont icon-charutupian" @tap="insertImage"></view>
|
||||
<view v-if="tools.indexOf('undo') !== -1" class="iconfont icon-undo" @tap="undo"></view>
|
||||
<view v-if="tools.indexOf('redo') !== -1" class="iconfont icon-redo" @tap="redo"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PickerColor from './color-picker';
|
||||
export default {
|
||||
components: {
|
||||
PickerColor,
|
||||
},
|
||||
props: {
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '写点什么吧 ~',
|
||||
},
|
||||
// 是否只读
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 最大字数限制,-1不限
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
},
|
||||
// 初始模板
|
||||
templates: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
tools: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return ['bold', 'color', 'underline', 'left', 'center', 'right', 'fontSize', 'images'];
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formats: {},
|
||||
textColor: '',
|
||||
backgroundColor: '',
|
||||
curColor: 'text',
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
// #ifndef MP-BAIDU
|
||||
uni.loadFontFace({
|
||||
family: 'Pacifico',
|
||||
source: 'url("https://sungd.github.io/Pacifico.ttf")',
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
onEditorReady() {
|
||||
// #ifdef MP-BAIDU
|
||||
this.editorCtx = requireDynamicLib('editorLib').createEditorContext('editor');
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS || MP-WEIXIN || H5
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select('#editor')
|
||||
.context((res) => {
|
||||
this.editorCtx = res.context;
|
||||
this.$emit('init', this.editorCtx);
|
||||
// 启用preRender方法时会预先渲染templates内容,但是在小程序中会导致页面自动聚焦至富文本的区域
|
||||
if (this.templates) {
|
||||
this.preRender();
|
||||
}
|
||||
})
|
||||
.exec((result) => {});
|
||||
// #endif
|
||||
},
|
||||
preRender() {
|
||||
// 初始化富文本时自带的文字模板
|
||||
this.editorCtx.setContents({
|
||||
html: this.templates,
|
||||
});
|
||||
},
|
||||
undo() {
|
||||
this.editorCtx.undo();
|
||||
},
|
||||
redo() {
|
||||
this.editorCtx.redo();
|
||||
},
|
||||
format(e) {
|
||||
let { name, value } = e.target.dataset;
|
||||
if (!name) return;
|
||||
// console.log('==== name :', name);
|
||||
switch (name) {
|
||||
case 'color':
|
||||
case 'background-color':
|
||||
this.curColor = name;
|
||||
this.showPicker();
|
||||
break;
|
||||
default:
|
||||
this.editorCtx.format(name, value);
|
||||
break;
|
||||
}
|
||||
},
|
||||
showPicker() {
|
||||
this.$refs.colorPicker.open();
|
||||
},
|
||||
confirmColor(e) {
|
||||
switch (this.curColor) {
|
||||
case 'color':
|
||||
this.textColor = e.hex;
|
||||
this.editorCtx.format('color', e.hex);
|
||||
break;
|
||||
case 'background-color':
|
||||
this.backgroundColor = e.hex;
|
||||
this.editorCtx.format('background-color', e.hex);
|
||||
break;
|
||||
}
|
||||
// 建议在更改颜色时,插入一个空以重置颜色区块,否则可能会导致颜色切换失效
|
||||
this.editorCtx.insertText({ text: '' });
|
||||
},
|
||||
onStatusChange(e) {
|
||||
this.formats = e.detail;
|
||||
// console.log('==== e :', e, this.textColor)
|
||||
},
|
||||
insertImage() {
|
||||
// #ifdef APP-PLUS || H5
|
||||
uni.chooseImage({
|
||||
// count: 1, // 默认9
|
||||
success: (res) => {
|
||||
const { tempFiles } = res;
|
||||
// 将文件和编辑器示例抛出,由开发者自行上传和插入图片
|
||||
this.$emit('upinImage', tempFiles, this.editorCtx);
|
||||
},
|
||||
fail() {
|
||||
uni.showToast({
|
||||
title: '未授权访问相册权限,请授权后使用',
|
||||
icon: 'none',
|
||||
});
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
|
||||
uni.chooseMedia({
|
||||
// count: 1, // 默认9
|
||||
mediaType: ['image', 'video'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
// 同上chooseImage处理
|
||||
const { tempFiles } = res;
|
||||
this.$emit('upinImage', tempFiles, this.editorCtx);
|
||||
},
|
||||
fail() {
|
||||
uni.showToast({
|
||||
title: '未授权访问相册权限,请授权后使用',
|
||||
icon: 'none',
|
||||
});
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
onEditorInput(e) {
|
||||
let maxlength = parseInt(this.maxlength);
|
||||
|
||||
this.editorCtx.getContents({
|
||||
success: (res) => {
|
||||
let { html, text } = res;
|
||||
let textStr = text.replace(/\s/g, '');
|
||||
|
||||
if (textStr.length > maxlength && maxlength != -1) {
|
||||
uni.showModal({
|
||||
content: `超过${maxlength}字数啦~`,
|
||||
confirmText: '确定',
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
this.$emit('overMax', { html, text });
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this.$emit('input', { html, text });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import './editor-icon.css';
|
||||
|
||||
.wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
display: inline-block;
|
||||
padding: 16rpx 0;
|
||||
width: 12.5%;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid #e4e4e4;
|
||||
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
.editor-wrapper {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.editor-wrapper.one-row {
|
||||
height: calc(100% - 80rpx);
|
||||
}
|
||||
|
||||
.editor-wrapper.two-row {
|
||||
height: calc(100% - 160rpx);
|
||||
}
|
||||
|
||||
.ql-container {
|
||||
padding: 8rpx 16rpx;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
::v-deep .ql-editor.ql-blank::before {
|
||||
font-style: normal;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.ql-active {
|
||||
color: #66ccff;
|
||||
}
|
||||
</style>
|
||||
83
uni_modules/sp-editor/package.json
Normal file
83
uni_modules/sp-editor/package.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"id": "sp-editor",
|
||||
"displayName": "sp-editor",
|
||||
"version": "1.0.3",
|
||||
"description": "官方内置富文本editor组件改版",
|
||||
"keywords": [
|
||||
"富文本",
|
||||
"editor",
|
||||
"编辑器"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.5.1"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "u",
|
||||
"QQ": "y",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
uni_modules/sp-editor/readme.md
Normal file
70
uni_modules/sp-editor/readme.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# sp-editor
|
||||
|
||||
## props参数
|
||||
|
||||
```
|
||||
props: {
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '写点什么吧 ~'
|
||||
},
|
||||
// 是否只读
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 最大字数限制,-1不限
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: -1
|
||||
},
|
||||
// 初始模板
|
||||
templates: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## emit方法
|
||||
|
||||
```
|
||||
<template>
|
||||
<sp-editor @input="input" @upinImage="upinImage"></sp-editor>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
function input(e) {
|
||||
console.log('==== input :', e)
|
||||
}
|
||||
|
||||
function upinImage(tempFiles, editorCtx) {
|
||||
// 使用 uniCloud.uploadFile 上传图片的示例方法(可适用多选上传)
|
||||
tempFiles.forEach(async (item) => {
|
||||
uni.showLoading({
|
||||
title: '上传中请稍后',
|
||||
mask: true
|
||||
})
|
||||
let upfile = await uniCloud.uploadFile({
|
||||
filePath: item.path,
|
||||
// 同名会导致报错 policy_does_not_allow_file_overwrite
|
||||
// cloudPath可由 想要存储的文件夹/文件名 拼接,若不拼文件夹名则默认存储在cloudstorage文件夹中
|
||||
cloudPath: `cloudstorage/${item.name}`,
|
||||
cloudPathAsRealPath: true
|
||||
})
|
||||
editorCtx.insertImage({
|
||||
src: upfile.fileID,
|
||||
width: '80%',
|
||||
success: function () {
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
1. 该组件在使用过程中推荐在外层套上个父盒子,并给父盒子高度,组件在封装时进行了高度计算,会自动撑满父盒子
|
||||
2. 如遇到在内置浏览器中发生无法拖动调节颜色板的问题,只需调出开发者调试面板,点击重置左上角选择dom的箭头后,便能调出模拟器手势光标,便可正常拖动了
|
||||
140
uni_modules/uni-datetime-picker/changelog.md
Normal file
140
uni_modules/uni-datetime-picker/changelog.md
Normal file
@@ -0,0 +1,140 @@
|
||||
## 2.2.24(2023-06-02)
|
||||
- 修复 部分情况修改时间,开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146)
|
||||
- 优化 当前月可以选择上月、下月的日期
|
||||
## 2.2.23(2023-05-02)
|
||||
- 修复 部分情况修改时间,开始时间未更新 [详情](https://github.com/dcloudio/uni-ui/issues/737)
|
||||
- 修复 部分平台及设备第一次点击无法显示弹框
|
||||
- 修复 ios 日期格式未补零显示及使用异常 [详情](https://ask.dcloud.net.cn/question/162979)
|
||||
## 2.2.22(2023-03-30)
|
||||
- 修复 日历 picker 修改年月后,自动选中当月1日 [详情](https://ask.dcloud.net.cn/question/165937)
|
||||
- 修复 小程序端 低版本 ios NaN [详情](https://ask.dcloud.net.cn/question/162979)
|
||||
## 2.2.21(2023-02-20)
|
||||
- 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362)
|
||||
## 2.2.20(2023-02-17)
|
||||
- 优化 值为空依然选中当天问题
|
||||
- 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间
|
||||
- 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间
|
||||
- 优化 字节小程序日期时间范围选择,底部日期换行问题
|
||||
## 2.2.19(2023-02-09)
|
||||
- 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686)
|
||||
## 2.2.18(2023-02-08)
|
||||
- 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684)
|
||||
- 优化 PC端输入日期格式错误时返回当前日期时间
|
||||
- 优化 PC端输入日期时间超出 start、end 限制的Bug
|
||||
- 优化 移动端日期时间范围用法时间展示不完整问题
|
||||
## 2.2.17(2023-02-04)
|
||||
- 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679)
|
||||
- 修复 vue3 time-picker 无法显示绑定时分秒的Bug
|
||||
## 2.2.16(2023-02-02)
|
||||
- 修复 字节小程序报错的Bug
|
||||
## 2.2.15(2023-02-02)
|
||||
- 修复 某些情况切换月份错误的Bug
|
||||
## 2.2.14(2023-01-30)
|
||||
- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033)
|
||||
## 2.2.13(2023-01-10)
|
||||
- 修复 多次加载组件造成内存占用的Bug
|
||||
## 2.2.12(2022-12-01)
|
||||
- 修复 vue3 下 i18n 国际化初始值不正确的Bug
|
||||
## 2.2.11(2022-09-19)
|
||||
- 修复 支付宝小程序样式错乱的Bug [详情](https://github.com/dcloudio/uni-app/issues/3861)
|
||||
## 2.2.10(2022-09-19)
|
||||
- 修复 反向选择日期范围,日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false)
|
||||
## 2.2.9(2022-09-16)
|
||||
- 可以使用 uni-scss 控制主题色
|
||||
## 2.2.8(2022-09-08)
|
||||
- 修复 close事件无效的Bug
|
||||
## 2.2.7(2022-09-05)
|
||||
- 修复 移动端 maskClick 无效的Bug [详情](https://ask.dcloud.net.cn/question/140824)
|
||||
## 2.2.6(2022-06-30)
|
||||
- 优化 组件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致
|
||||
## 2.2.5(2022-06-24)
|
||||
- 修复 日历顶部年月及底部确认未国际化的Bug
|
||||
## 2.2.4(2022-03-31)
|
||||
- 修复 Vue3 下动态赋值,单选类型未响应的Bug
|
||||
## 2.2.3(2022-03-28)
|
||||
- 修复 Vue3 下动态赋值未响应的Bug
|
||||
## 2.2.2(2021-12-10)
|
||||
- 修复 clear-icon 属性在小程序平台不生效的Bug
|
||||
## 2.2.1(2021-12-10)
|
||||
- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的Bug
|
||||
## 2.2.0(2021-11-19)
|
||||
- 优化 组件UI,并提供设计资源 [详情](https://uniapp.dcloud.io/component/uniui/resource)
|
||||
- 文档迁移 [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
|
||||
## 2.1.5(2021-11-09)
|
||||
- 新增 提供组件设计资源,组件样式调整
|
||||
## 2.1.4(2021-09-10)
|
||||
- 修复 hide-second 在移动端的Bug
|
||||
- 修复 单选赋默认值时,赋值日期未高亮的Bug
|
||||
- 修复 赋默认值时,移动端未正确显示时间的Bug
|
||||
## 2.1.3(2021-09-09)
|
||||
- 新增 hide-second 属性,支持只使用时分,隐藏秒
|
||||
## 2.1.2(2021-09-03)
|
||||
- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
|
||||
- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
|
||||
- 优化 调整字号大小,美化日历界面
|
||||
- 修复 因国际化导致的 placeholder 失效的Bug
|
||||
## 2.1.1(2021-08-24)
|
||||
- 新增 支持国际化
|
||||
- 优化 范围选择器在 pc 端过宽的问题
|
||||
## 2.1.0(2021-08-09)
|
||||
- 新增 适配 vue3
|
||||
## 2.0.19(2021-08-09)
|
||||
- 新增 支持作为 uni-forms 子组件相关功能
|
||||
- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的Bug
|
||||
## 2.0.18(2021-08-05)
|
||||
- 修复 type 属性动态赋值无效的Bug
|
||||
- 修复 ‘确认’按钮被 tabbar 遮盖 bug
|
||||
- 修复 组件未赋值时范围选左、右日历相同的Bug
|
||||
## 2.0.17(2021-08-04)
|
||||
- 修复 范围选未正确显示当前值的Bug
|
||||
- 修复 h5 平台(移动端)报错 'cale' of undefined 的Bug
|
||||
## 2.0.16(2021-07-21)
|
||||
- 新增 return-type 属性支持返回 date 日期对象
|
||||
## 2.0.15(2021-07-14)
|
||||
- 修复 单选日期类型,初始赋值后不在当前日历的Bug
|
||||
- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
|
||||
- 优化 移动端移除显示框的清空按钮,无实际用途
|
||||
## 2.0.14(2021-07-14)
|
||||
- 修复 组件赋值为空,界面未更新的Bug
|
||||
- 修复 start 和 end 不能动态赋值的Bug
|
||||
- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的Bug
|
||||
## 2.0.13(2021-07-08)
|
||||
- 修复 范围选择不能动态赋值的Bug
|
||||
## 2.0.12(2021-07-08)
|
||||
- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug
|
||||
## 2.0.11(2021-07-08)
|
||||
- 优化 弹出层在超出视窗边缘定位不准确的问题
|
||||
## 2.0.10(2021-07-08)
|
||||
- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的Bug
|
||||
- 优化 弹出层在超出视窗边缘被遮盖的问题
|
||||
## 2.0.9(2021-07-07)
|
||||
- 新增 maskClick 事件
|
||||
- 修复 特殊情况日历 rpx 布局错误的Bug,rpx -> px
|
||||
- 修复 范围选择时清空返回值不合理的bug,['', ''] -> []
|
||||
## 2.0.8(2021-07-07)
|
||||
- 新增 日期时间显示框支持插槽
|
||||
## 2.0.7(2021-07-01)
|
||||
- 优化 添加 uni-icons 依赖
|
||||
## 2.0.6(2021-05-22)
|
||||
- 修复 图标在小程序上不显示的Bug
|
||||
- 优化 重命名引用组件,避免潜在组件命名冲突
|
||||
## 2.0.5(2021-05-20)
|
||||
- 优化 代码目录扁平化
|
||||
## 2.0.4(2021-05-12)
|
||||
- 新增 组件示例地址
|
||||
## 2.0.3(2021-05-10)
|
||||
- 修复 ios 下不识别 '-' 日期格式的Bug
|
||||
- 优化 pc 下弹出层添加边框和阴影
|
||||
## 2.0.2(2021-05-08)
|
||||
- 修复 在 admin 中获取弹出层定位错误的bug
|
||||
## 2.0.1(2021-05-08)
|
||||
- 修复 type 属性向下兼容,默认值从 date 变更为 datetime
|
||||
## 2.0.0(2021-04-30)
|
||||
- 支持日历形式的日期+时间的范围选择
|
||||
> 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)
|
||||
## 1.0.6(2021-03-18)
|
||||
- 新增 hide-second 属性,时间支持仅选择时、分
|
||||
- 修复 选择跟显示的日期不一样的Bug
|
||||
- 修复 chang事件触发2次的Bug
|
||||
- 修复 分、秒 end 范围错误的Bug
|
||||
- 优化 更好的 nvue 适配
|
||||
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<view class="uni-calendar-item__weeks-box" :class="{
|
||||
'uni-calendar-item--disable':weeks.disable,
|
||||
'uni-calendar-item--before-checked-x':weeks.beforeMultiple,
|
||||
'uni-calendar-item--multiple': weeks.multiple,
|
||||
'uni-calendar-item--after-checked-x':weeks.afterMultiple,
|
||||
}" @tap="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)">
|
||||
<view class="uni-calendar-item__weeks-box-item" :class="{
|
||||
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover),
|
||||
'uni-calendar-item--checked-range-text': checkHover,
|
||||
'uni-calendar-item--before-checked':weeks.beforeMultiple,
|
||||
'uni-calendar-item--multiple': weeks.multiple,
|
||||
'uni-calendar-item--after-checked':weeks.afterMultiple,
|
||||
'uni-calendar-item--disable':weeks.disable,
|
||||
}">
|
||||
<text v-if="selected && weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
|
||||
<text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
|
||||
</view>
|
||||
<view :class="{'uni-calendar-item--today': weeks.isToday}"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
weeks: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
calendar: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
selected: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
checkHover: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
choiceDate(weeks) {
|
||||
this.$emit('change', weeks)
|
||||
},
|
||||
handleMousemove(weeks) {
|
||||
this.$emit('handleMouse', weeks)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" >
|
||||
$uni-primary: #007aff !default;
|
||||
|
||||
.uni-calendar-item__weeks-box {
|
||||
flex: 1;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 1px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.uni-calendar-item__weeks-box-text {
|
||||
font-size: 14px;
|
||||
// font-family: Lato-Bold, Lato;
|
||||
font-weight: bold;
|
||||
color: darken($color: $uni-primary, $amount: 40%);
|
||||
}
|
||||
|
||||
.uni-calendar-item__weeks-box-item {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
|
||||
.uni-calendar-item__weeks-box-circle {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: #dd524d;
|
||||
|
||||
}
|
||||
|
||||
.uni-calendar-item__weeks-box .uni-calendar-item--disable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
|
||||
color: #D1D1D1;
|
||||
}
|
||||
|
||||
.uni-calendar-item--today {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 17%;
|
||||
background-color: #dd524d;
|
||||
width:6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.uni-calendar-item--extra {
|
||||
color: #dd524d;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.uni-calendar-item__weeks-box .uni-calendar-item--checked {
|
||||
background-color: $uni-primary;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
border: 3px solid #fff;
|
||||
}
|
||||
|
||||
.uni-calendar-item--checked .uni-calendar-item--checked-text {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.uni-calendar-item--multiple {
|
||||
background-color: #F6F7FC;
|
||||
// color: #fff;
|
||||
}
|
||||
|
||||
.uni-calendar-item--multiple .uni-calendar-item--before-checked,
|
||||
.uni-calendar-item--multiple .uni-calendar-item--after-checked {
|
||||
background-color: $uni-primary;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
border: 3px solid #F6F7FC;
|
||||
}
|
||||
|
||||
.uni-calendar-item--before-checked .uni-calendar-item--checked-text,
|
||||
.uni-calendar-item--after-checked .uni-calendar-item--checked-text {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.uni-calendar-item--before-checked-x {
|
||||
border-top-left-radius: 50px;
|
||||
border-bottom-left-radius: 50px;
|
||||
box-sizing: border-box;
|
||||
background-color: #F6F7FC;
|
||||
}
|
||||
|
||||
.uni-calendar-item--after-checked-x {
|
||||
border-top-right-radius: 50px;
|
||||
border-bottom-right-radius: 50px;
|
||||
background-color: #F6F7FC;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,929 @@
|
||||
<template>
|
||||
<view class="uni-calendar" @mouseleave="leaveCale">
|
||||
|
||||
<view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
|
||||
@tap="maskClick"></view>
|
||||
|
||||
<view v-if="insert || show" class="uni-calendar__content"
|
||||
:class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}">
|
||||
<view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}">
|
||||
|
||||
<view class="uni-calendar__header-btn-box" @click.stop="changeMonth('pre')">
|
||||
<view class="uni-calendar__header-btn uni-calendar--left"></view>
|
||||
</view>
|
||||
|
||||
<picker mode="date" :value="date" fields="month" @change="bindDateChange">
|
||||
<text
|
||||
class="uni-calendar__header-text">{{ (nowDate.year||'') + yearText + ( nowDate.month||'') + monthText}}</text>
|
||||
</picker>
|
||||
|
||||
<view class="uni-calendar__header-btn-box" @click.stop="changeMonth('next')">
|
||||
<view class="uni-calendar__header-btn uni-calendar--right"></view>
|
||||
</view>
|
||||
|
||||
<view v-if="!insert" class="dialog-close" @tap="close">
|
||||
<view class="dialog-close-plus" data-id="close"></view>
|
||||
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="uni-calendar__box">
|
||||
|
||||
<view v-if="showMonth" class="uni-calendar__box-bg">
|
||||
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
|
||||
</view>
|
||||
|
||||
<view class="uni-calendar__weeks" style="padding-bottom: 7px;">
|
||||
<view class="uni-calendar__weeks-day">
|
||||
<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
|
||||
</view>
|
||||
<view class="uni-calendar__weeks-day">
|
||||
<text class="uni-calendar__weeks-day-text">{{MONText}}</text>
|
||||
</view>
|
||||
<view class="uni-calendar__weeks-day">
|
||||
<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
|
||||
</view>
|
||||
<view class="uni-calendar__weeks-day">
|
||||
<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
|
||||
</view>
|
||||
<view class="uni-calendar__weeks-day">
|
||||
<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
|
||||
</view>
|
||||
<view class="uni-calendar__weeks-day">
|
||||
<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
|
||||
</view>
|
||||
<view class="uni-calendar__weeks-day">
|
||||
<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
|
||||
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
|
||||
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar"
|
||||
:selected="selected" :checkHover="range" @change="choiceDate"
|
||||
@handleMouse="handleMouse">
|
||||
</calendar-item>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!insert && !range && hasTime" class="uni-date-changed uni-calendar--fixed-top"
|
||||
style="padding: 0 80px;">
|
||||
<view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view>
|
||||
<time-picker type="time" :start="timepickerStartTime" :end="timepickerEndTime" v-model="time"
|
||||
:disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
|
||||
</time-picker>
|
||||
</view>
|
||||
|
||||
<view v-if="!insert && range && hasTime" class="uni-date-changed uni-calendar--fixed-top">
|
||||
<view class="uni-date-changed--time-start">
|
||||
<view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}}
|
||||
</view>
|
||||
<time-picker type="time" :start="timepickerStartTime" v-model="timeRange.startTime" :border="false"
|
||||
:hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
|
||||
</time-picker>
|
||||
</view>
|
||||
<view style="line-height: 50px;">
|
||||
<uni-icons type="arrowthinright" color="#999"></uni-icons>
|
||||
</view>
|
||||
<view class="uni-date-changed--time-end">
|
||||
<view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view>
|
||||
<time-picker type="time" :end="timepickerEndTime" v-model="timeRange.endTime" :border="false"
|
||||
:hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style">
|
||||
</time-picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!insert" class="uni-date-changed uni-date-btn--ok">
|
||||
<view class="uni-datetime-picker--btn" @tap="confirm">{{confirmText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Calendar, getDate, getTime } from './util.js';
|
||||
import calendarItem from './calendar-item.vue'
|
||||
import timePicker from './time-picker.vue'
|
||||
|
||||
import { initVueI18n } from '@dcloudio/uni-i18n'
|
||||
import i18nMessages from './i18n/index.js'
|
||||
const { t } = initVueI18n(i18nMessages)
|
||||
|
||||
/**
|
||||
* Calendar 日历
|
||||
* @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=56
|
||||
* @property {String} date 自定义当前时间,默认为今天
|
||||
* @property {String} startDate 日期选择范围-开始日期
|
||||
* @property {String} endDate 日期选择范围-结束日期
|
||||
* @property {Boolean} range 范围选择
|
||||
* @property {Boolean} insert = [true|false] 插入模式,默认为false
|
||||
* @value true 弹窗模式
|
||||
* @value false 插入模式
|
||||
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
|
||||
* @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
|
||||
* @property {Boolean} showMonth 是否选择月份为背景
|
||||
* @property {[String} defaultValue 选择器打开时默认显示的时间
|
||||
* @event {Function} change 日期改变,`insert :ture` 时生效
|
||||
* @event {Function} confirm 确认选择`insert :false` 时生效
|
||||
* @event {Function} monthSwitch 切换月份时触发
|
||||
* @example <uni-calendar :insert="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
|
||||
*/
|
||||
export default {
|
||||
components: {
|
||||
calendarItem,
|
||||
timePicker
|
||||
},
|
||||
props: {
|
||||
date: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
defTime: {
|
||||
type: [String, Object],
|
||||
default: ''
|
||||
},
|
||||
selectableTimes: {
|
||||
type: [Object],
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
selected: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
startDate: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
endDate: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
startPlaceholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
endPlaceholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
range: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hasTime: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
insert: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showMonth: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
clearDate: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
checkHover: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hideSecond: {
|
||||
type: [Boolean],
|
||||
default: false
|
||||
},
|
||||
pleStatus: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
before: '',
|
||||
after: '',
|
||||
data: [],
|
||||
fulldate: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultValue: {
|
||||
type: [String, Object, Array],
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
weeks: [],
|
||||
calendar: {},
|
||||
nowDate: {},
|
||||
aniMaskShow: false,
|
||||
firstEnter: true,
|
||||
time: '',
|
||||
timeRange: {
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
},
|
||||
tempSingleDate: '',
|
||||
tempRange: {
|
||||
before: '',
|
||||
after: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
date: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
if (!this.range) {
|
||||
this.tempSingleDate = newVal
|
||||
setTimeout(() => {
|
||||
this.init(newVal)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
},
|
||||
defTime: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
if (!this.range) {
|
||||
this.time = newVal
|
||||
} else {
|
||||
this.timeRange.startTime = newVal.start
|
||||
this.timeRange.endTime = newVal.end
|
||||
}
|
||||
}
|
||||
},
|
||||
startDate(val) {
|
||||
// 字节小程序 watch 早于 created
|
||||
if(!this.cale){
|
||||
return
|
||||
}
|
||||
this.cale.setStartDate(val)
|
||||
this.cale.setDate(this.nowDate.fullDate)
|
||||
this.weeks = this.cale.weeks
|
||||
},
|
||||
endDate(val) {
|
||||
// 字节小程序 watch 早于 created
|
||||
if(!this.cale){
|
||||
return
|
||||
}
|
||||
this.cale.setEndDate(val)
|
||||
this.cale.setDate(this.nowDate.fullDate)
|
||||
this.weeks = this.cale.weeks
|
||||
},
|
||||
selected(newVal) {
|
||||
// 字节小程序 watch 早于 created
|
||||
if(!this.cale){
|
||||
return
|
||||
}
|
||||
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
|
||||
this.weeks = this.cale.weeks
|
||||
},
|
||||
pleStatus: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
const {
|
||||
before,
|
||||
after,
|
||||
fulldate,
|
||||
which
|
||||
} = newVal
|
||||
this.tempRange.before = before
|
||||
this.tempRange.after = after
|
||||
setTimeout(() => {
|
||||
if (fulldate) {
|
||||
this.cale.setHoverMultiple(fulldate)
|
||||
if (before && after) {
|
||||
this.cale.lastHover = true
|
||||
if (this.rangeWithinMonth(after, before)) return
|
||||
this.setDate(before)
|
||||
} else {
|
||||
this.cale.setMultiple(fulldate)
|
||||
this.setDate(this.nowDate.fullDate)
|
||||
this.calendar.fullDate = ''
|
||||
this.cale.lastHover = false
|
||||
}
|
||||
} else {
|
||||
// 字节小程序 watch 早于 created
|
||||
if(!this.cale){
|
||||
return
|
||||
}
|
||||
|
||||
this.cale.setDefaultMultiple(before, after)
|
||||
if (which === 'left' && before) {
|
||||
this.setDate(before)
|
||||
this.weeks = this.cale.weeks
|
||||
} else if(after) {
|
||||
this.setDate(after)
|
||||
this.weeks = this.cale.weeks
|
||||
}
|
||||
this.cale.lastHover = true
|
||||
}
|
||||
}, 16)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
timepickerStartTime() {
|
||||
const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
|
||||
return activeDate === this.startDate ? this.selectableTimes.start : ''
|
||||
},
|
||||
timepickerEndTime() {
|
||||
const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
|
||||
return activeDate === this.endDate ? this.selectableTimes.end : ''
|
||||
},
|
||||
/**
|
||||
* for i18n
|
||||
*/
|
||||
selectDateText() {
|
||||
return t("uni-datetime-picker.selectDate")
|
||||
},
|
||||
startDateText() {
|
||||
return this.startPlaceholder || t("uni-datetime-picker.startDate")
|
||||
},
|
||||
endDateText() {
|
||||
return this.endPlaceholder || t("uni-datetime-picker.endDate")
|
||||
},
|
||||
okText() {
|
||||
return t("uni-datetime-picker.ok")
|
||||
},
|
||||
yearText() {
|
||||
return t("uni-datetime-picker.year")
|
||||
},
|
||||
monthText() {
|
||||
return t("uni-datetime-picker.month")
|
||||
},
|
||||
MONText() {
|
||||
return t("uni-calender.MON")
|
||||
},
|
||||
TUEText() {
|
||||
return t("uni-calender.TUE")
|
||||
},
|
||||
WEDText() {
|
||||
return t("uni-calender.WED")
|
||||
},
|
||||
THUText() {
|
||||
return t("uni-calender.THU")
|
||||
},
|
||||
FRIText() {
|
||||
return t("uni-calender.FRI")
|
||||
},
|
||||
SATText() {
|
||||
return t("uni-calender.SAT")
|
||||
},
|
||||
SUNText() {
|
||||
return t("uni-calender.SUN")
|
||||
},
|
||||
confirmText() {
|
||||
return t("uni-calender.confirm")
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// 获取日历方法实例
|
||||
this.cale = new Calendar({
|
||||
selected: this.selected,
|
||||
startDate: this.startDate,
|
||||
endDate: this.endDate,
|
||||
range: this.range,
|
||||
})
|
||||
// 选中某一天
|
||||
this.init(this.date)
|
||||
},
|
||||
methods: {
|
||||
leaveCale() {
|
||||
this.firstEnter = true
|
||||
},
|
||||
handleMouse(weeks) {
|
||||
if (weeks.disable) return
|
||||
if (this.cale.lastHover) return
|
||||
let {
|
||||
before,
|
||||
after
|
||||
} = this.cale.multipleStatus
|
||||
if (!before) return
|
||||
this.calendar = weeks
|
||||
// 设置范围选
|
||||
this.cale.setHoverMultiple(this.calendar.fullDate)
|
||||
this.weeks = this.cale.weeks
|
||||
// hover时,进入一个日历,更新另一个
|
||||
if (this.firstEnter) {
|
||||
this.$emit('firstEnterCale', this.cale.multipleStatus)
|
||||
this.firstEnter = false
|
||||
}
|
||||
},
|
||||
rangeWithinMonth(A, B) {
|
||||
const [yearA, monthA] = A.split('-')
|
||||
const [yearB, monthB] = B.split('-')
|
||||
return yearA === yearB && monthA === monthB
|
||||
},
|
||||
// 蒙版点击事件
|
||||
maskClick() {
|
||||
this.close()
|
||||
this.$emit('maskClose')
|
||||
},
|
||||
|
||||
clearCalender() {
|
||||
if (this.range) {
|
||||
this.timeRange.startTime = ''
|
||||
this.timeRange.endTime = ''
|
||||
this.tempRange.before = ''
|
||||
this.tempRange.after = ''
|
||||
this.cale.multipleStatus.before = ''
|
||||
this.cale.multipleStatus.after = ''
|
||||
this.cale.multipleStatus.data = []
|
||||
this.cale.lastHover = false
|
||||
} else {
|
||||
this.time = ''
|
||||
this.tempSingleDate = ''
|
||||
}
|
||||
this.calendar.fullDate = ''
|
||||
this.setDate(new Date())
|
||||
},
|
||||
|
||||
bindDateChange(e) {
|
||||
const value = e.detail.value + '-1'
|
||||
this.setDate(value)
|
||||
},
|
||||
/**
|
||||
* 初始化日期显示
|
||||
* @param {Object} date
|
||||
*/
|
||||
init(date) {
|
||||
// 字节小程序 watch 早于 created
|
||||
if(!this.cale){
|
||||
return
|
||||
}
|
||||
this.cale.setDate(date || new Date())
|
||||
this.weeks = this.cale.weeks
|
||||
this.nowDate = this.cale.getInfo(date)
|
||||
this.calendar = {...this.nowDate}
|
||||
if(!date){
|
||||
// 优化date为空默认不选中今天
|
||||
this.calendar.fullDate = ''
|
||||
if(this.defaultValue && !this.range){
|
||||
// 暂时只支持移动端非范围选择
|
||||
const defaultDate = new Date(this.defaultValue)
|
||||
const fullDate = getDate(defaultDate)
|
||||
const year = defaultDate.getFullYear()
|
||||
const month = defaultDate.getMonth()+1
|
||||
const date = defaultDate.getDate()
|
||||
const day = defaultDate.getDay()
|
||||
this.calendar = {
|
||||
fullDate,
|
||||
year,
|
||||
month,
|
||||
date,
|
||||
day
|
||||
},
|
||||
this.tempSingleDate = fullDate
|
||||
this.time = getTime(defaultDate, this.hideSecond)
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 打开日历弹窗
|
||||
*/
|
||||
open() {
|
||||
// 弹窗模式并且清理数据
|
||||
if (this.clearDate && !this.insert) {
|
||||
this.cale.cleanMultipleStatus()
|
||||
this.init(this.date)
|
||||
}
|
||||
this.show = true
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.aniMaskShow = true
|
||||
}, 50)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 关闭日历弹窗
|
||||
*/
|
||||
close() {
|
||||
this.aniMaskShow = false
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.show = false
|
||||
this.$emit('close')
|
||||
}, 300)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 确认按钮
|
||||
*/
|
||||
confirm() {
|
||||
this.setEmit('confirm')
|
||||
this.close()
|
||||
},
|
||||
/**
|
||||
* 变化触发
|
||||
*/
|
||||
change() {
|
||||
if (!this.insert) return
|
||||
this.setEmit('change')
|
||||
},
|
||||
/**
|
||||
* 选择月份触发
|
||||
*/
|
||||
monthSwitch() {
|
||||
let {
|
||||
year,
|
||||
month
|
||||
} = this.nowDate
|
||||
this.$emit('monthSwitch', {
|
||||
year,
|
||||
month: Number(month)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 派发事件
|
||||
* @param {Object} name
|
||||
*/
|
||||
setEmit(name) {
|
||||
if(!this.range){
|
||||
if(!this.calendar.fullDate){
|
||||
this.calendar = this.cale.getInfo(new Date())
|
||||
this.tempSingleDate = this.calendar.fullDate
|
||||
}
|
||||
if(this.hasTime && !this.time) {
|
||||
this.time = getTime(new Date(), this.hideSecond)
|
||||
}
|
||||
}
|
||||
let {
|
||||
year,
|
||||
month,
|
||||
date,
|
||||
fullDate,
|
||||
extraInfo
|
||||
} = this.calendar
|
||||
this.$emit(name, {
|
||||
range: this.cale.multipleStatus,
|
||||
year,
|
||||
month,
|
||||
date,
|
||||
time: this.time,
|
||||
timeRange: this.timeRange,
|
||||
fulldate: fullDate,
|
||||
extraInfo: extraInfo || {}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 选择天触发
|
||||
* @param {Object} weeks
|
||||
*/
|
||||
choiceDate(weeks) {
|
||||
if (weeks.disable) return
|
||||
this.calendar = weeks
|
||||
this.calendar.userChecked = true
|
||||
// 设置多选
|
||||
this.cale.setMultiple(this.calendar.fullDate, true)
|
||||
this.weeks = this.cale.weeks
|
||||
this.tempSingleDate = this.calendar.fullDate
|
||||
const beforeDate = new Date(this.cale.multipleStatus.before).getTime()
|
||||
const afterDate = new Date(this.cale.multipleStatus.after).getTime()
|
||||
// 如果先选择结束日期,后选择开始日期,需要交换
|
||||
if (beforeDate > afterDate && afterDate) {
|
||||
this.tempRange.before = this.cale.multipleStatus.after
|
||||
this.tempRange.after = this.cale.multipleStatus.before
|
||||
} else {
|
||||
this.tempRange.before = this.cale.multipleStatus.before
|
||||
this.tempRange.after = this.cale.multipleStatus.after
|
||||
}
|
||||
this.change()
|
||||
},
|
||||
changeMonth(type) {
|
||||
let newDate
|
||||
if(type === 'pre') {
|
||||
newDate = this.cale.getPreMonthObj(this.nowDate.fullDate).fullDate
|
||||
} else if(type === 'next') {
|
||||
newDate = this.cale.getNextMonthObj(this.nowDate.fullDate).fullDate
|
||||
}
|
||||
|
||||
this.setDate(newDate)
|
||||
this.monthSwitch()
|
||||
},
|
||||
/**
|
||||
* 设置日期
|
||||
* @param {Object} date
|
||||
*/
|
||||
setDate(date) {
|
||||
this.cale.setDate(date)
|
||||
this.weeks = this.cale.weeks
|
||||
this.nowDate = this.cale.getInfo(date)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" >
|
||||
$uni-primary: #007aff !default;
|
||||
|
||||
.uni-calendar {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.uni-calendar__mask {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
transition-property: opacity;
|
||||
transition-duration: 0.3s;
|
||||
opacity: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 99;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-calendar--mask-show {
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
.uni-calendar--fixed {
|
||||
position: fixed;
|
||||
bottom: calc(var(--window-bottom));
|
||||
left: 0;
|
||||
right: 0;
|
||||
transition-property: transform;
|
||||
transition-duration: 0.3s;
|
||||
transform: translateY(460px);
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 99;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-calendar--ani-show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.uni-calendar__content {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.uni-calendar__content-mobile {
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.uni-calendar__header {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.uni-calendar__header-mobile {
|
||||
padding: 10px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.uni-calendar--fixed-top {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
border-top-color: rgba(0, 0, 0, 0.4);
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.uni-calendar--fixed-width {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.uni-calendar__backtoday {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 25rpx;
|
||||
padding: 0 5px;
|
||||
padding-left: 10px;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
font-size: 12px;
|
||||
border-top-left-radius: 25px;
|
||||
border-bottom-left-radius: 25px;
|
||||
color: #fff;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.uni-calendar__header-text {
|
||||
text-align: center;
|
||||
width: 100px;
|
||||
font-size: 15px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.uni-calendar__button-text {
|
||||
text-align: center;
|
||||
width: 100px;
|
||||
font-size: 14px;
|
||||
color: $uni-primary;
|
||||
/* #ifndef APP-NVUE */
|
||||
letter-spacing: 3px;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-calendar__header-btn-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.uni-calendar__header-btn {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-left-color: #808080;
|
||||
border-left-style: solid;
|
||||
border-left-width: 1px;
|
||||
border-top-color: #555555;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.uni-calendar--left {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.uni-calendar--right {
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
|
||||
|
||||
.uni-calendar__weeks {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.uni-calendar__weeks-item {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.uni-calendar__weeks-day {
|
||||
flex: 1;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
border-bottom-color: #F5F5F5;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.uni-calendar__weeks-day-text {
|
||||
font-size: 12px;
|
||||
color: #B2B2B2;
|
||||
}
|
||||
|
||||
.uni-calendar__box {
|
||||
position: relative;
|
||||
// padding: 0 10px;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
|
||||
.uni-calendar__box-bg {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.uni-calendar__box-bg-text {
|
||||
font-size: 200px;
|
||||
font-weight: bold;
|
||||
color: #999;
|
||||
opacity: 0.1;
|
||||
text-align: center;
|
||||
/* #ifndef APP-NVUE */
|
||||
line-height: 1;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-date-changed {
|
||||
padding: 0 10px;
|
||||
// line-height: 50px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
border-top-color: #DCDCDC;
|
||||
;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.uni-date-btn--ok {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.uni-date-changed--time-start {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uni-date-changed--time-end {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uni-date-changed--time-date {
|
||||
color: #999;
|
||||
line-height: 50px;
|
||||
/* #ifdef MP-TOUTIAO */
|
||||
font-size: 16px;
|
||||
/* #endif */
|
||||
margin-right: 5px;
|
||||
// opacity: 0.6;
|
||||
}
|
||||
|
||||
.time-picker-style {
|
||||
// width: 62px;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
justify-content: center;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.mr-10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.dialog-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 25px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.dialog-close-plus {
|
||||
width: 16px;
|
||||
height: 2px;
|
||||
background-color: #737987;
|
||||
border-radius: 2px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.dialog-close-rotate {
|
||||
position: absolute;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.uni-datetime-picker--btn {
|
||||
border-radius: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
background-color: $uni-primary;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
.uni-datetime-picker--btn:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"uni-datetime-picker.selectDate": "select date",
|
||||
"uni-datetime-picker.selectTime": "select time",
|
||||
"uni-datetime-picker.selectDateTime": "select date and time",
|
||||
"uni-datetime-picker.startDate": "start date",
|
||||
"uni-datetime-picker.endDate": "end date",
|
||||
"uni-datetime-picker.startTime": "start time",
|
||||
"uni-datetime-picker.endTime": "end time",
|
||||
"uni-datetime-picker.ok": "ok",
|
||||
"uni-datetime-picker.clear": "clear",
|
||||
"uni-datetime-picker.cancel": "cancel",
|
||||
"uni-datetime-picker.year": "-",
|
||||
"uni-datetime-picker.month": "",
|
||||
"uni-calender.MON": "MON",
|
||||
"uni-calender.TUE": "TUE",
|
||||
"uni-calender.WED": "WED",
|
||||
"uni-calender.THU": "THU",
|
||||
"uni-calender.FRI": "FRI",
|
||||
"uni-calender.SAT": "SAT",
|
||||
"uni-calender.SUN": "SUN",
|
||||
"uni-calender.confirm": "confirm"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import en from './en.json'
|
||||
import zhHans from './zh-Hans.json'
|
||||
import zhHant from './zh-Hant.json'
|
||||
export default {
|
||||
en,
|
||||
'zh-Hans': zhHans,
|
||||
'zh-Hant': zhHant
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"uni-datetime-picker.selectDate": "选择日期",
|
||||
"uni-datetime-picker.selectTime": "选择时间",
|
||||
"uni-datetime-picker.selectDateTime": "选择日期时间",
|
||||
"uni-datetime-picker.startDate": "开始日期",
|
||||
"uni-datetime-picker.endDate": "结束日期",
|
||||
"uni-datetime-picker.startTime": "开始时间",
|
||||
"uni-datetime-picker.endTime": "结束时间",
|
||||
"uni-datetime-picker.ok": "确定",
|
||||
"uni-datetime-picker.clear": "清除",
|
||||
"uni-datetime-picker.cancel": "取消",
|
||||
"uni-datetime-picker.year": "年",
|
||||
"uni-datetime-picker.month": "月",
|
||||
"uni-calender.SUN": "日",
|
||||
"uni-calender.MON": "一",
|
||||
"uni-calender.TUE": "二",
|
||||
"uni-calender.WED": "三",
|
||||
"uni-calender.THU": "四",
|
||||
"uni-calender.FRI": "五",
|
||||
"uni-calender.SAT": "六",
|
||||
"uni-calender.confirm": "确认"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"uni-datetime-picker.selectDate": "選擇日期",
|
||||
"uni-datetime-picker.selectTime": "選擇時間",
|
||||
"uni-datetime-picker.selectDateTime": "選擇日期時間",
|
||||
"uni-datetime-picker.startDate": "開始日期",
|
||||
"uni-datetime-picker.endDate": "結束日期",
|
||||
"uni-datetime-picker.startTime": "開始时间",
|
||||
"uni-datetime-picker.endTime": "結束时间",
|
||||
"uni-datetime-picker.ok": "確定",
|
||||
"uni-datetime-picker.clear": "清除",
|
||||
"uni-datetime-picker.cancel": "取消",
|
||||
"uni-datetime-picker.year": "年",
|
||||
"uni-datetime-picker.month": "月",
|
||||
"uni-calender.SUN": "日",
|
||||
"uni-calender.MON": "一",
|
||||
"uni-calender.TUE": "二",
|
||||
"uni-calender.WED": "三",
|
||||
"uni-calender.THU": "四",
|
||||
"uni-calender.FRI": "五",
|
||||
"uni-calender.SAT": "六",
|
||||
"uni-calender.confirm": "確認"
|
||||
}
|
||||
@@ -0,0 +1,934 @@
|
||||
<template>
|
||||
<view class="uni-datetime-picker">
|
||||
<view @tap="initTimePicker">
|
||||
<slot>
|
||||
<view class="uni-datetime-picker-timebox-pointer"
|
||||
:class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}">
|
||||
<text class="uni-datetime-picker-text">{{time}}</text>
|
||||
<view v-if="!time" class="uni-datetime-picker-time">
|
||||
<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
<view v-if="visible" id="mask" class="uni-datetime-picker-mask" @tap="tiggerTimePicker"></view>
|
||||
<view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
|
||||
:style="fixNvueBug">
|
||||
<view class="uni-title">
|
||||
<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
|
||||
</view>
|
||||
<view v-if="dateShow" class="uni-datetime-picker__container-box">
|
||||
<picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd"
|
||||
@change="bindDateChange">
|
||||
<picker-view-column>
|
||||
<view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">
|
||||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
|
||||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
|
||||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
<!-- 兼容 nvue 不支持伪类 -->
|
||||
<text class="uni-datetime-picker-sign sign-left">-</text>
|
||||
<text class="uni-datetime-picker-sign sign-right">-</text>
|
||||
</view>
|
||||
<view v-if="timeShow" class="uni-datetime-picker__container-box">
|
||||
<picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']"
|
||||
:indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange">
|
||||
<picker-view-column>
|
||||
<view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
|
||||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
|
||||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column v-if="!hideSecond">
|
||||
<view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
|
||||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
<!-- 兼容 nvue 不支持伪类 -->
|
||||
<text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text>
|
||||
<text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text>
|
||||
</view>
|
||||
<view class="uni-datetime-picker-btn">
|
||||
<view @tap="clearTime">
|
||||
<text class="uni-datetime-picker-btn-text">{{clearText}}</text>
|
||||
</view>
|
||||
<view class="uni-datetime-picker-btn-group">
|
||||
<view class="uni-datetime-picker-cancel" @tap="tiggerTimePicker">
|
||||
<text class="uni-datetime-picker-btn-text">{{cancelText}}</text>
|
||||
</view>
|
||||
<view @tap="setTime">
|
||||
<text class="uni-datetime-picker-btn-text">{{okText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { initVueI18n } from '@dcloudio/uni-i18n'
|
||||
import i18nMessages from './i18n/index.js'
|
||||
const { t } = initVueI18n(i18nMessages)
|
||||
import { fixIosDateFormat } from './util'
|
||||
|
||||
/**
|
||||
* DatetimePicker 时间选择器
|
||||
* @description 可以同时选择日期和时间的选择器
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
|
||||
* @property {String} type = [datetime | date | time] 显示模式
|
||||
* @property {Boolean} multiple = [true|false] 是否多选
|
||||
* @property {String|Number} value 默认值
|
||||
* @property {String|Number} start 起始日期或时间
|
||||
* @property {String|Number} end 起始日期或时间
|
||||
* @property {String} return-type = [timestamp | string]
|
||||
* @event {Function} change 选中发生变化触发
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'UniDatetimePicker',
|
||||
data() {
|
||||
return {
|
||||
indicatorStyle: `height: 50px;`,
|
||||
visible: false,
|
||||
fixNvueBug: {},
|
||||
dateShow: true,
|
||||
timeShow: true,
|
||||
title: '日期和时间',
|
||||
// 输入框当前时间
|
||||
time: '',
|
||||
// 当前的年月日时分秒
|
||||
year: 1920,
|
||||
month: 0,
|
||||
day: 0,
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0,
|
||||
// 起始时间
|
||||
startYear: 1920,
|
||||
startMonth: 1,
|
||||
startDay: 1,
|
||||
startHour: 0,
|
||||
startMinute: 0,
|
||||
startSecond: 0,
|
||||
// 结束时间
|
||||
endYear: 2120,
|
||||
endMonth: 12,
|
||||
endDay: 31,
|
||||
endHour: 23,
|
||||
endMinute: 59,
|
||||
endSecond: 59,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'datetime'
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
start: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
end: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
returnType: {
|
||||
type: String,
|
||||
default: 'string'
|
||||
},
|
||||
disabled: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
border: {
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
hideSecond: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// #ifndef VUE3
|
||||
value: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.parseValue(fixIosDateFormat(newVal))
|
||||
this.initTime(false)
|
||||
} else {
|
||||
this.time = ''
|
||||
this.parseValue(Date.now())
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
modelValue: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.parseValue(fixIosDateFormat(newVal))
|
||||
this.initTime(false)
|
||||
} else {
|
||||
this.time = ''
|
||||
this.parseValue(Date.now())
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
// #endif
|
||||
type: {
|
||||
handler(newValue) {
|
||||
if (newValue === 'date') {
|
||||
this.dateShow = true
|
||||
this.timeShow = false
|
||||
this.title = '日期'
|
||||
} else if (newValue === 'time') {
|
||||
this.dateShow = false
|
||||
this.timeShow = true
|
||||
this.title = '时间'
|
||||
} else {
|
||||
this.dateShow = true
|
||||
this.timeShow = true
|
||||
this.title = '日期和时间'
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
start: {
|
||||
handler(newVal) {
|
||||
this.parseDatetimeRange(fixIosDateFormat(newVal), 'start')
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
end: {
|
||||
handler(newVal) {
|
||||
this.parseDatetimeRange(fixIosDateFormat(newVal), 'end')
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
|
||||
// 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项
|
||||
months(newVal) {
|
||||
this.checkValue('month', this.month, newVal)
|
||||
},
|
||||
days(newVal) {
|
||||
this.checkValue('day', this.day, newVal)
|
||||
},
|
||||
hours(newVal) {
|
||||
this.checkValue('hour', this.hour, newVal)
|
||||
},
|
||||
minutes(newVal) {
|
||||
this.checkValue('minute', this.minute, newVal)
|
||||
},
|
||||
seconds(newVal) {
|
||||
this.checkValue('second', this.second, newVal)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 当前年、月、日、时、分、秒选择范围
|
||||
years() {
|
||||
return this.getCurrentRange('year')
|
||||
},
|
||||
|
||||
months() {
|
||||
return this.getCurrentRange('month')
|
||||
},
|
||||
|
||||
days() {
|
||||
return this.getCurrentRange('day')
|
||||
},
|
||||
|
||||
hours() {
|
||||
return this.getCurrentRange('hour')
|
||||
},
|
||||
|
||||
minutes() {
|
||||
return this.getCurrentRange('minute')
|
||||
},
|
||||
|
||||
seconds() {
|
||||
return this.getCurrentRange('second')
|
||||
},
|
||||
|
||||
// picker 当前值数组
|
||||
ymd() {
|
||||
return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
|
||||
},
|
||||
hms() {
|
||||
return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
|
||||
},
|
||||
|
||||
// 当前 date 是 start
|
||||
currentDateIsStart() {
|
||||
return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
|
||||
},
|
||||
|
||||
// 当前 date 是 end
|
||||
currentDateIsEnd() {
|
||||
return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
|
||||
},
|
||||
|
||||
// 当前年、月、日、时、分、秒的最小值和最大值
|
||||
minYear() {
|
||||
return this.startYear
|
||||
},
|
||||
maxYear() {
|
||||
return this.endYear
|
||||
},
|
||||
minMonth() {
|
||||
if (this.year === this.startYear) {
|
||||
return this.startMonth
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
},
|
||||
maxMonth() {
|
||||
if (this.year === this.endYear) {
|
||||
return this.endMonth
|
||||
} else {
|
||||
return 12
|
||||
}
|
||||
},
|
||||
minDay() {
|
||||
if (this.year === this.startYear && this.month === this.startMonth) {
|
||||
return this.startDay
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
},
|
||||
maxDay() {
|
||||
if (this.year === this.endYear && this.month === this.endMonth) {
|
||||
return this.endDay
|
||||
} else {
|
||||
return this.daysInMonth(this.year, this.month)
|
||||
}
|
||||
},
|
||||
minHour() {
|
||||
if (this.type === 'datetime') {
|
||||
if (this.currentDateIsStart) {
|
||||
return this.startHour
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
if (this.type === 'time') {
|
||||
return this.startHour
|
||||
}
|
||||
},
|
||||
maxHour() {
|
||||
if (this.type === 'datetime') {
|
||||
if (this.currentDateIsEnd) {
|
||||
return this.endHour
|
||||
} else {
|
||||
return 23
|
||||
}
|
||||
}
|
||||
if (this.type === 'time') {
|
||||
return this.endHour
|
||||
}
|
||||
},
|
||||
minMinute() {
|
||||
if (this.type === 'datetime') {
|
||||
if (this.currentDateIsStart && this.hour === this.startHour) {
|
||||
return this.startMinute
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
if (this.type === 'time') {
|
||||
if (this.hour === this.startHour) {
|
||||
return this.startMinute
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
},
|
||||
maxMinute() {
|
||||
if (this.type === 'datetime') {
|
||||
if (this.currentDateIsEnd && this.hour === this.endHour) {
|
||||
return this.endMinute
|
||||
} else {
|
||||
return 59
|
||||
}
|
||||
}
|
||||
if (this.type === 'time') {
|
||||
if (this.hour === this.endHour) {
|
||||
return this.endMinute
|
||||
} else {
|
||||
return 59
|
||||
}
|
||||
}
|
||||
},
|
||||
minSecond() {
|
||||
if (this.type === 'datetime') {
|
||||
if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
|
||||
return this.startSecond
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
if (this.type === 'time') {
|
||||
if (this.hour === this.startHour && this.minute === this.startMinute) {
|
||||
return this.startSecond
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
},
|
||||
maxSecond() {
|
||||
if (this.type === 'datetime') {
|
||||
if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) {
|
||||
return this.endSecond
|
||||
} else {
|
||||
return 59
|
||||
}
|
||||
}
|
||||
if (this.type === 'time') {
|
||||
if (this.hour === this.endHour && this.minute === this.endMinute) {
|
||||
return this.endSecond
|
||||
} else {
|
||||
return 59
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* for i18n
|
||||
*/
|
||||
selectTimeText() {
|
||||
return t("uni-datetime-picker.selectTime")
|
||||
},
|
||||
okText() {
|
||||
return t("uni-datetime-picker.ok")
|
||||
},
|
||||
clearText() {
|
||||
return t("uni-datetime-picker.clear")
|
||||
},
|
||||
cancelText() {
|
||||
return t("uni-datetime-picker.cancel")
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// #ifdef APP-NVUE
|
||||
const res = uni.getSystemInfoSync();
|
||||
this.fixNvueBug = {
|
||||
top: res.windowHeight / 2,
|
||||
left: res.windowWidth / 2
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* @param {Object} item
|
||||
* 小于 10 在前面加个 0
|
||||
*/
|
||||
|
||||
lessThanTen(item) {
|
||||
return item < 10 ? '0' + item : item
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析时分秒字符串,例如:00:00:00
|
||||
* @param {String} timeString
|
||||
*/
|
||||
parseTimeType(timeString) {
|
||||
if (timeString) {
|
||||
let timeArr = timeString.split(':')
|
||||
this.hour = Number(timeArr[0])
|
||||
this.minute = Number(timeArr[1])
|
||||
this.second = Number(timeArr[2])
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000
|
||||
* @param {String | Number} datetime
|
||||
*/
|
||||
initPickerValue(datetime) {
|
||||
let defaultValue = null
|
||||
if (datetime) {
|
||||
defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
|
||||
} else {
|
||||
defaultValue = Date.now()
|
||||
defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
|
||||
}
|
||||
this.parseValue(defaultValue)
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始值规则:
|
||||
* - 用户设置初始值 value
|
||||
* - 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start
|
||||
* - 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start
|
||||
* - 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end
|
||||
* - 无起始终止时间,则初始值为 value
|
||||
* - 无初始值 value,则初始值为当前本地时间 Date.now()
|
||||
* @param {Object} value
|
||||
* @param {Object} dateBase
|
||||
*/
|
||||
compareValueWithStartAndEnd(value, start, end) {
|
||||
let winner = null
|
||||
value = this.superTimeStamp(value)
|
||||
start = this.superTimeStamp(start)
|
||||
end = this.superTimeStamp(end)
|
||||
|
||||
if (start && end) {
|
||||
if (value < start) {
|
||||
winner = new Date(start)
|
||||
} else if (value > end) {
|
||||
winner = new Date(end)
|
||||
} else {
|
||||
winner = new Date(value)
|
||||
}
|
||||
} else if (start && !end) {
|
||||
winner = start <= value ? new Date(value) : new Date(start)
|
||||
} else if (!start && end) {
|
||||
winner = value <= end ? new Date(value) : new Date(end)
|
||||
} else {
|
||||
winner = new Date(value)
|
||||
}
|
||||
|
||||
return winner
|
||||
},
|
||||
|
||||
/**
|
||||
* 转换为可比较的时间戳,接受日期、时分秒、时间戳
|
||||
* @param {Object} value
|
||||
*/
|
||||
superTimeStamp(value) {
|
||||
let dateBase = ''
|
||||
if (this.type === 'time' && value && typeof value === 'string') {
|
||||
const now = new Date()
|
||||
const year = now.getFullYear()
|
||||
const month = now.getMonth() + 1
|
||||
const day = now.getDate()
|
||||
dateBase = year + '/' + month + '/' + day + ' '
|
||||
}
|
||||
if (Number(value)) {
|
||||
value = parseInt(value)
|
||||
dateBase = 0
|
||||
}
|
||||
return this.createTimeStamp(dateBase + value)
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析默认值 value,字符串、时间戳
|
||||
* @param {Object} defaultTime
|
||||
*/
|
||||
parseValue(value) {
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
if (this.type === 'time' && typeof value === "string") {
|
||||
this.parseTimeType(value)
|
||||
} else {
|
||||
let defaultDate = null
|
||||
defaultDate = new Date(value)
|
||||
if (this.type !== 'time') {
|
||||
this.year = defaultDate.getFullYear()
|
||||
this.month = defaultDate.getMonth() + 1
|
||||
this.day = defaultDate.getDate()
|
||||
}
|
||||
if (this.type !== 'date') {
|
||||
this.hour = defaultDate.getHours()
|
||||
this.minute = defaultDate.getMinutes()
|
||||
this.second = defaultDate.getSeconds()
|
||||
}
|
||||
}
|
||||
if (this.hideSecond) {
|
||||
this.second = 0
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析可选择时间范围 start、end,年月日字符串、时间戳
|
||||
* @param {Object} defaultTime
|
||||
*/
|
||||
parseDatetimeRange(point, pointType) {
|
||||
// 时间为空,则重置为初始值
|
||||
if (!point) {
|
||||
if (pointType === 'start') {
|
||||
this.startYear = 1920
|
||||
this.startMonth = 1
|
||||
this.startDay = 1
|
||||
this.startHour = 0
|
||||
this.startMinute = 0
|
||||
this.startSecond = 0
|
||||
}
|
||||
if (pointType === 'end') {
|
||||
this.endYear = 2120
|
||||
this.endMonth = 12
|
||||
this.endDay = 31
|
||||
this.endHour = 23
|
||||
this.endMinute = 59
|
||||
this.endSecond = 59
|
||||
}
|
||||
return
|
||||
}
|
||||
if (this.type === 'time') {
|
||||
const pointArr = point.split(':')
|
||||
this[pointType + 'Hour'] = Number(pointArr[0])
|
||||
this[pointType + 'Minute'] = Number(pointArr[1])
|
||||
this[pointType + 'Second'] = Number(pointArr[2])
|
||||
} else {
|
||||
if (!point) {
|
||||
pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
|
||||
return
|
||||
}
|
||||
if (Number(point)) {
|
||||
point = parseInt(point)
|
||||
}
|
||||
// datetime 的 end 没有时分秒, 则不限制
|
||||
const hasTime = /[0-9]:[0-9]/
|
||||
if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
|
||||
point)) {
|
||||
point = point + ' 23:59:59'
|
||||
}
|
||||
const pointDate = new Date(point)
|
||||
this[pointType + 'Year'] = pointDate.getFullYear()
|
||||
this[pointType + 'Month'] = pointDate.getMonth() + 1
|
||||
this[pointType + 'Day'] = pointDate.getDate()
|
||||
if (this.type === 'datetime') {
|
||||
this[pointType + 'Hour'] = pointDate.getHours()
|
||||
this[pointType + 'Minute'] = pointDate.getMinutes()
|
||||
this[pointType + 'Second'] = pointDate.getSeconds()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 获取 年、月、日、时、分、秒 当前可选范围
|
||||
getCurrentRange(value) {
|
||||
const range = []
|
||||
for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
|
||||
range.push(i)
|
||||
}
|
||||
return range
|
||||
},
|
||||
|
||||
// 字符串首字母大写
|
||||
capitalize(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
},
|
||||
|
||||
// 检查当前值是否在范围内,不在则当前值重置为可选范围第一项
|
||||
checkValue(name, value, values) {
|
||||
if (values.indexOf(value) === -1) {
|
||||
this[name] = values[0]
|
||||
}
|
||||
},
|
||||
|
||||
// 每个月的实际天数
|
||||
daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
|
||||
return new Date(year, month, 0).getDate();
|
||||
},
|
||||
|
||||
//兼容 iOS、safari 日期格式
|
||||
fixIosDateFormat(value) {
|
||||
if (typeof value === 'string') {
|
||||
value = value.replace(/-/g, '/')
|
||||
}
|
||||
return value
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成时间戳
|
||||
* @param {Object} time
|
||||
*/
|
||||
createTimeStamp(time) {
|
||||
if (!time) return
|
||||
if (typeof time === "number") {
|
||||
return time
|
||||
} else {
|
||||
time = time.replace(/-/g, '/')
|
||||
if (this.type === 'date') {
|
||||
time = time + ' ' + '00:00:00'
|
||||
}
|
||||
return Date.parse(time)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成日期或时间的字符串
|
||||
*/
|
||||
createDomSting() {
|
||||
const yymmdd = this.year +
|
||||
'-' +
|
||||
this.lessThanTen(this.month) +
|
||||
'-' +
|
||||
this.lessThanTen(this.day)
|
||||
|
||||
let hhmmss = this.lessThanTen(this.hour) +
|
||||
':' +
|
||||
this.lessThanTen(this.minute)
|
||||
|
||||
if (!this.hideSecond) {
|
||||
hhmmss = hhmmss + ':' + this.lessThanTen(this.second)
|
||||
}
|
||||
|
||||
if (this.type === 'date') {
|
||||
return yymmdd
|
||||
} else if (this.type === 'time') {
|
||||
return hhmmss
|
||||
} else {
|
||||
return yymmdd + ' ' + hhmmss
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化返回值,并抛出 change 事件
|
||||
*/
|
||||
initTime(emit = true) {
|
||||
this.time = this.createDomSting()
|
||||
if (!emit) return
|
||||
if (this.returnType === 'timestamp' && this.type !== 'time') {
|
||||
this.$emit('change', this.createTimeStamp(this.time))
|
||||
this.$emit('input', this.createTimeStamp(this.time))
|
||||
this.$emit('update:modelValue', this.createTimeStamp(this.time))
|
||||
} else {
|
||||
this.$emit('change', this.time)
|
||||
this.$emit('input', this.time)
|
||||
this.$emit('update:modelValue', this.time)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户选择日期或时间更新 data
|
||||
* @param {Object} e
|
||||
*/
|
||||
bindDateChange(e) {
|
||||
const val = e.detail.value
|
||||
this.year = this.years[val[0]]
|
||||
this.month = this.months[val[1]]
|
||||
this.day = this.days[val[2]]
|
||||
},
|
||||
bindTimeChange(e) {
|
||||
const val = e.detail.value
|
||||
this.hour = this.hours[val[0]]
|
||||
this.minute = this.minutes[val[1]]
|
||||
this.second = this.seconds[val[2]]
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化弹出层
|
||||
*/
|
||||
initTimePicker() {
|
||||
if (this.disabled) return
|
||||
const value = fixIosDateFormat(this.time)
|
||||
this.initPickerValue(value)
|
||||
this.visible = !this.visible
|
||||
},
|
||||
|
||||
/**
|
||||
* 触发或关闭弹框
|
||||
*/
|
||||
tiggerTimePicker(e) {
|
||||
this.visible = !this.visible
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击“清空”按钮,清空当前值
|
||||
*/
|
||||
clearTime() {
|
||||
this.time = ''
|
||||
this.$emit('change', this.time)
|
||||
this.$emit('input', this.time)
|
||||
this.$emit('update:modelValue', this.time)
|
||||
this.tiggerTimePicker()
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击“确定”按钮
|
||||
*/
|
||||
setTime() {
|
||||
this.initTime()
|
||||
this.tiggerTimePicker()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$uni-primary: #007aff !default;
|
||||
|
||||
.uni-datetime-picker {
|
||||
/* #ifndef APP-NVUE */
|
||||
/* width: 100%; */
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-datetime-picker-view {
|
||||
height: 130px;
|
||||
width: 270px;
|
||||
/* #ifndef APP-NVUE */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-datetime-picker-item {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.uni-datetime-picker-btn {
|
||||
margin-top: 60px;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.uni-datetime-picker-btn-text {
|
||||
font-size: 14px;
|
||||
color: $uni-primary;
|
||||
}
|
||||
|
||||
.uni-datetime-picker-btn-group {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.uni-datetime-picker-cancel {
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.uni-datetime-picker-mask {
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
transition-duration: 0.3s;
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
.uni-datetime-picker-popup {
|
||||
border-radius: 8px;
|
||||
padding: 30px;
|
||||
width: 270px;
|
||||
/* #ifdef APP-NVUE */
|
||||
height: 500px;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
width: 330px;
|
||||
/* #endif */
|
||||
background-color: #fff;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transition-duration: 0.3s;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.fix-nvue-height {
|
||||
/* #ifdef APP-NVUE */
|
||||
height: 330px;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-datetime-picker-time {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.uni-datetime-picker-column {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.uni-datetime-picker-timebox {
|
||||
|
||||
border: 1px solid #E5E5E5;
|
||||
border-radius: 5px;
|
||||
padding: 7px 10px;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-datetime-picker-timebox-pointer {
|
||||
/* #ifndef APP-NVUE */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
|
||||
.uni-datetime-picker-disabled {
|
||||
opacity: 0.4;
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed !important;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-datetime-picker-text {
|
||||
font-size: 14px;
|
||||
line-height: 50px
|
||||
}
|
||||
|
||||
.uni-datetime-picker-sign {
|
||||
position: absolute;
|
||||
top: 53px;
|
||||
/* 减掉 10px 的元素高度,兼容nvue */
|
||||
color: #999;
|
||||
/* #ifdef APP-NVUE */
|
||||
font-size: 16px;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.sign-left {
|
||||
left: 86px;
|
||||
}
|
||||
|
||||
.sign-right {
|
||||
right: 86px;
|
||||
}
|
||||
|
||||
.sign-center {
|
||||
left: 135px;
|
||||
}
|
||||
|
||||
.uni-datetime-picker__container-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.time-hide-second {
|
||||
width: 180px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,453 @@
|
||||
class Calendar {
|
||||
constructor({
|
||||
selected,
|
||||
startDate,
|
||||
endDate,
|
||||
range,
|
||||
} = {}) {
|
||||
// 当前日期
|
||||
this.date = this.getDateObj(new Date()) // 当前初入日期
|
||||
// 打点信息
|
||||
this.selected = selected || [];
|
||||
// 起始时间
|
||||
this.startDate = startDate
|
||||
// 终止时间
|
||||
this.endDate = endDate
|
||||
// 是否范围选择
|
||||
this.range = range
|
||||
// 多选状态
|
||||
this.cleanMultipleStatus()
|
||||
// 每周日期
|
||||
this.weeks = {}
|
||||
this.lastHover = false
|
||||
}
|
||||
/**
|
||||
* 设置日期
|
||||
* @param {Object} date
|
||||
*/
|
||||
setDate(date) {
|
||||
const selectDate = this.getDateObj(date)
|
||||
this.getWeeks(selectDate.fullDate)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理多选状态
|
||||
*/
|
||||
cleanMultipleStatus() {
|
||||
this.multipleStatus = {
|
||||
before: '',
|
||||
after: '',
|
||||
data: []
|
||||
}
|
||||
}
|
||||
|
||||
setStartDate(startDate) {
|
||||
this.startDate = startDate
|
||||
}
|
||||
|
||||
setEndDate(endDate) {
|
||||
this.endDate = endDate
|
||||
}
|
||||
|
||||
getPreMonthObj(date){
|
||||
date = fixIosDateFormat(date)
|
||||
date = new Date(date)
|
||||
|
||||
const oldMonth = date.getMonth()
|
||||
date.setMonth(oldMonth - 1)
|
||||
const newMonth = date.getMonth()
|
||||
if(oldMonth !== 0 && newMonth - oldMonth === 0){
|
||||
date.setMonth(newMonth - 1)
|
||||
}
|
||||
return this.getDateObj(date)
|
||||
}
|
||||
getNextMonthObj(date){
|
||||
date = fixIosDateFormat(date)
|
||||
date = new Date(date)
|
||||
|
||||
const oldMonth = date.getMonth()
|
||||
date.setMonth(oldMonth + 1)
|
||||
const newMonth = date.getMonth()
|
||||
if(newMonth - oldMonth > 1){
|
||||
date.setMonth(newMonth - 1)
|
||||
}
|
||||
return this.getDateObj(date)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定格式Date对象
|
||||
*/
|
||||
getDateObj(date) {
|
||||
date = fixIosDateFormat(date)
|
||||
date = new Date(date)
|
||||
|
||||
return {
|
||||
fullDate: getDate(date),
|
||||
year: date.getFullYear(),
|
||||
month: addZero(date.getMonth() + 1),
|
||||
date: addZero(date.getDate()),
|
||||
day: date.getDay()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上一个月日期集合
|
||||
*/
|
||||
getPreMonthDays(amount, dateObj) {
|
||||
const result = []
|
||||
for (let i = amount - 1; i >= 0; i--) {
|
||||
const month = dateObj.month > 1 ? dateObj.month -1 : 12
|
||||
const year = month === 12 ? dateObj.year - 1 : dateObj.year
|
||||
const date = new Date(year,month,-i).getDate()
|
||||
const fullDate = `${year}-${addZero(month)}-${addZero(date)}`
|
||||
let multiples = this.multipleStatus.data
|
||||
let multiplesStatus = -1
|
||||
if (this.range && multiples) {
|
||||
multiplesStatus = multiples.findIndex((item) => {
|
||||
return this.dateEqual(item, fullDate)
|
||||
})
|
||||
}
|
||||
const checked = multiplesStatus !== -1
|
||||
// 获取打点信息
|
||||
const extraInfo = this.selected && this.selected.find((item) => {
|
||||
if (this.dateEqual(fullDate, item.date)) {
|
||||
return item
|
||||
}
|
||||
})
|
||||
result.push({
|
||||
fullDate,
|
||||
year,
|
||||
month,
|
||||
date,
|
||||
multiple: this.range ? checked : false,
|
||||
beforeMultiple: this.isLogicBefore(fullDate, this.multipleStatus.before, this.multipleStatus.after),
|
||||
afterMultiple: this.isLogicAfter(fullDate, this.multipleStatus.before, this.multipleStatus.after),
|
||||
disable: (this.startDate && !dateCompare(this.startDate, fullDate)) || (this.endDate && !dateCompare(fullDate,this.endDate)),
|
||||
isToday: fullDate === this.date.fullDate,
|
||||
userChecked: false,
|
||||
extraInfo
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
/**
|
||||
* 获取本月日期集合
|
||||
*/
|
||||
getCurrentMonthDays(amount, dateObj) {
|
||||
const result = []
|
||||
const fullDate = this.date.fullDate
|
||||
for (let i = 1; i <= amount; i++) {
|
||||
const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}`
|
||||
const isToday = fullDate === currentDate
|
||||
// 获取打点信息
|
||||
const extraInfo = this.selected && this.selected.find((item) => {
|
||||
if (this.dateEqual(currentDate, item.date)) {
|
||||
return item
|
||||
}
|
||||
})
|
||||
|
||||
// 日期禁用
|
||||
let disableBefore = true
|
||||
let disableAfter = true
|
||||
if (this.startDate) {
|
||||
disableBefore = dateCompare(this.startDate, currentDate)
|
||||
}
|
||||
|
||||
if (this.endDate) {
|
||||
disableAfter = dateCompare(currentDate, this.endDate)
|
||||
}
|
||||
|
||||
let multiples = this.multipleStatus.data
|
||||
let multiplesStatus = -1
|
||||
if (this.range && multiples) {
|
||||
multiplesStatus = multiples.findIndex((item) => {
|
||||
return this.dateEqual(item, currentDate)
|
||||
})
|
||||
}
|
||||
const checked = multiplesStatus !== -1
|
||||
|
||||
result.push({
|
||||
fullDate: currentDate,
|
||||
year: dateObj.year,
|
||||
month: dateObj.month,
|
||||
date: i,
|
||||
multiple: this.range ? checked : false,
|
||||
beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after),
|
||||
afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after),
|
||||
disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare(currentDate,this.endDate)),
|
||||
isToday,
|
||||
userChecked: false,
|
||||
extraInfo
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
/**
|
||||
* 获取下一个月日期集合
|
||||
*/
|
||||
_getNextMonthDays(amount, dateObj) {
|
||||
const result = []
|
||||
const month = dateObj.month + 1
|
||||
for (let i = 1; i <= amount; i++) {
|
||||
const month = dateObj.month === 12 ? 1 : dateObj.month*1 + 1
|
||||
const year = month === 1 ? dateObj.year + 1 : dateObj.year
|
||||
const fullDate = `${year}-${addZero(month)}-${addZero(i)}`
|
||||
let multiples = this.multipleStatus.data
|
||||
let multiplesStatus = -1
|
||||
if (this.range && multiples) {
|
||||
multiplesStatus = multiples.findIndex((item) => {
|
||||
return this.dateEqual(item, fullDate)
|
||||
})
|
||||
}
|
||||
const checked = multiplesStatus !== -1
|
||||
// 获取打点信息
|
||||
const extraInfo = this.selected && this.selected.find((item) => {
|
||||
if (this.dateEqual(fullDate, item.date)) {
|
||||
return item
|
||||
}
|
||||
})
|
||||
result.push({
|
||||
fullDate,
|
||||
year,
|
||||
date: i,
|
||||
month,
|
||||
multiple: this.range ? checked : false,
|
||||
beforeMultiple: this.isLogicBefore(fullDate, this.multipleStatus.before, this.multipleStatus.after),
|
||||
afterMultiple: this.isLogicAfter(fullDate, this.multipleStatus.before, this.multipleStatus.after),
|
||||
disable: (this.startDate && !dateCompare(this.startDate, fullDate)) || (this.endDate && !dateCompare(fullDate,this.endDate)),
|
||||
isToday: fullDate === this.date.fullDate,
|
||||
userChecked: false,
|
||||
extraInfo
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期详情
|
||||
* @param {Object} date
|
||||
*/
|
||||
getInfo(date) {
|
||||
if (!date) {
|
||||
date = new Date()
|
||||
}
|
||||
|
||||
return this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate)
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较时间是否相等
|
||||
*/
|
||||
dateEqual(before, after) {
|
||||
before = new Date(fixIosDateFormat(before))
|
||||
after = new Date(fixIosDateFormat(after))
|
||||
return before.valueOf() === after.valueOf()
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较真实起始日期
|
||||
*/
|
||||
|
||||
isLogicBefore(currentDate, before, after) {
|
||||
let logicBefore = before
|
||||
if (before && after) {
|
||||
logicBefore = dateCompare(before, after) ? before : after
|
||||
}
|
||||
return this.dateEqual(logicBefore, currentDate)
|
||||
}
|
||||
|
||||
isLogicAfter(currentDate, before, after) {
|
||||
let logicAfter = after
|
||||
if (before && after) {
|
||||
logicAfter = dateCompare(before, after) ? after : before
|
||||
}
|
||||
return this.dateEqual(logicAfter, currentDate)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日期范围内所有日期
|
||||
* @param {Object} begin
|
||||
* @param {Object} end
|
||||
*/
|
||||
geDateAll(begin, end) {
|
||||
var arr = []
|
||||
var ab = begin.split('-')
|
||||
var ae = end.split('-')
|
||||
var db = new Date()
|
||||
db.setFullYear(ab[0], ab[1] - 1, ab[2])
|
||||
var de = new Date()
|
||||
de.setFullYear(ae[0], ae[1] - 1, ae[2])
|
||||
var unixDb = db.getTime() - 24 * 60 * 60 * 1000
|
||||
var unixDe = de.getTime() - 24 * 60 * 60 * 1000
|
||||
for (var k = unixDb; k <= unixDe;) {
|
||||
k = k + 24 * 60 * 60 * 1000
|
||||
arr.push(this.getDateObj(new Date(parseInt(k))).fullDate)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多选状态
|
||||
*/
|
||||
setMultiple(fullDate) {
|
||||
if (!this.range) return
|
||||
|
||||
let {
|
||||
before,
|
||||
after
|
||||
} = this.multipleStatus
|
||||
if (before && after) {
|
||||
if (!this.lastHover) {
|
||||
this.lastHover = true
|
||||
return
|
||||
}
|
||||
this.multipleStatus.before = fullDate
|
||||
this.multipleStatus.after = ''
|
||||
this.multipleStatus.data = []
|
||||
this.multipleStatus.fulldate = ''
|
||||
this.lastHover = false
|
||||
} else {
|
||||
if (!before) {
|
||||
this.multipleStatus.before = fullDate
|
||||
this.lastHover = false
|
||||
} else {
|
||||
this.multipleStatus.after = fullDate
|
||||
if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
|
||||
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
|
||||
.after);
|
||||
} else {
|
||||
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
|
||||
.before);
|
||||
}
|
||||
this.lastHover = true
|
||||
}
|
||||
}
|
||||
this.getWeeks(fullDate)
|
||||
}
|
||||
|
||||
/**
|
||||
* 鼠标 hover 更新多选状态
|
||||
*/
|
||||
setHoverMultiple(fullDate) {
|
||||
if (!this.range || this.lastHover) return
|
||||
|
||||
const { before } = this.multipleStatus
|
||||
|
||||
if (!before) {
|
||||
this.multipleStatus.before = fullDate
|
||||
} else {
|
||||
this.multipleStatus.after = fullDate
|
||||
if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
|
||||
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
|
||||
} else {
|
||||
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
|
||||
}
|
||||
}
|
||||
this.getWeeks(fullDate)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新默认值多选状态
|
||||
*/
|
||||
setDefaultMultiple(before, after) {
|
||||
this.multipleStatus.before = before
|
||||
this.multipleStatus.after = after
|
||||
if (before && after) {
|
||||
if (dateCompare(before, after)) {
|
||||
this.multipleStatus.data = this.geDateAll(before, after);
|
||||
this.getWeeks(after)
|
||||
} else {
|
||||
this.multipleStatus.data = this.geDateAll(after, before);
|
||||
this.getWeeks(before)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取每周数据
|
||||
* @param {Object} dateData
|
||||
*/
|
||||
getWeeks(dateData) {
|
||||
const {
|
||||
year,
|
||||
month,
|
||||
} = this.getDateObj(dateData)
|
||||
|
||||
const preMonthDayAmount = new Date(year, month - 1, 1).getDay()
|
||||
const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData))
|
||||
|
||||
const currentMonthDayAmount = new Date(year, month, 0).getDate()
|
||||
const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData))
|
||||
|
||||
const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount
|
||||
const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData))
|
||||
|
||||
const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays]
|
||||
|
||||
const weeks = new Array(6)
|
||||
for (let i = 0; i < calendarDays.length; i++) {
|
||||
const index = Math.floor(i / 7)
|
||||
if(!weeks[index]){
|
||||
weeks[index] = new Array(7)
|
||||
}
|
||||
weeks[index][i % 7] = calendarDays[i]
|
||||
}
|
||||
|
||||
this.calendar = calendarDays
|
||||
this.weeks = weeks
|
||||
}
|
||||
}
|
||||
|
||||
function getDateTime(date, hideSecond){
|
||||
return `${getDate(date)} ${getTime(date, hideSecond)}`
|
||||
}
|
||||
|
||||
function getDate(date) {
|
||||
date = fixIosDateFormat(date)
|
||||
date = new Date(date)
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth()+1
|
||||
const day = date.getDate()
|
||||
return `${year}-${addZero(month)}-${addZero(day)}`
|
||||
}
|
||||
|
||||
function getTime(date, hideSecond){
|
||||
date = fixIosDateFormat(date)
|
||||
date = new Date(date)
|
||||
const hour = date.getHours()
|
||||
const minute = date.getMinutes()
|
||||
const second = date.getSeconds()
|
||||
return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}`
|
||||
}
|
||||
|
||||
function addZero(num) {
|
||||
if(num < 10){
|
||||
num = `0${num}`
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
function getDefaultSecond(hideSecond) {
|
||||
return hideSecond ? '00:00' : '00:00:00'
|
||||
}
|
||||
|
||||
function dateCompare(startDate, endDate) {
|
||||
startDate = new Date(fixIosDateFormat(startDate))
|
||||
endDate = new Date(fixIosDateFormat(endDate))
|
||||
return startDate <= endDate
|
||||
}
|
||||
|
||||
function checkDate(date){
|
||||
const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g
|
||||
return date.match(dateReg)
|
||||
}
|
||||
|
||||
const dateTimeReg = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])( [0-5]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9])?$/
|
||||
function fixIosDateFormat(value) {
|
||||
if (typeof value === 'string' && dateTimeReg.test(value)) {
|
||||
value = value.replace(/-/g, '/')
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
export {Calendar, getDateTime, getDate, getTime, addZero, getDefaultSecond, dateCompare, checkDate, fixIosDateFormat}
|
||||
87
uni_modules/uni-datetime-picker/package.json
Normal file
87
uni_modules/uni-datetime-picker/package.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"id": "uni-datetime-picker",
|
||||
"displayName": "uni-datetime-picker 日期选择器",
|
||||
"version": "2.2.24",
|
||||
"description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择",
|
||||
"keywords": [
|
||||
"uni-datetime-picker",
|
||||
"uni-ui",
|
||||
"uniui",
|
||||
"日期时间选择器",
|
||||
"日期时间"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": ""
|
||||
},
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
|
||||
"type": "component-vue"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"uni-scss",
|
||||
"uni-icons"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "n"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
uni_modules/uni-datetime-picker/readme.md
Normal file
21
uni_modules/uni-datetime-picker/readme.md
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
|
||||
> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护`
|
||||
|
||||
## DatetimePicker 时间选择器
|
||||
|
||||
> **组件名:uni-datetime-picker**
|
||||
> 代码块: `uDatetimePicker`
|
||||
|
||||
|
||||
该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。
|
||||
|
||||
若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。
|
||||
|
||||
**_点击 picker 默认值规则:_**
|
||||
|
||||
- 若设置初始值 value, 会显示在 picker 显示框中
|
||||
- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中
|
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
|
||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||
22
uni_modules/uni-icons/changelog.md
Normal file
22
uni_modules/uni-icons/changelog.md
Normal file
@@ -0,0 +1,22 @@
|
||||
## 1.3.5(2022-01-24)
|
||||
- 优化 size 属性可以传入不带单位的字符串数值
|
||||
## 1.3.4(2022-01-24)
|
||||
- 优化 size 支持其他单位
|
||||
## 1.3.3(2022-01-17)
|
||||
- 修复 nvue 有些图标不显示的bug,兼容老版本图标
|
||||
## 1.3.2(2021-12-01)
|
||||
- 优化 示例可复制图标名称
|
||||
## 1.3.1(2021-11-23)
|
||||
- 优化 兼容旧组件 type 值
|
||||
## 1.3.0(2021-11-19)
|
||||
- 新增 更多图标
|
||||
- 优化 自定义图标使用方式
|
||||
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
|
||||
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons)
|
||||
## 1.1.7(2021-11-08)
|
||||
## 1.2.0(2021-07-30)
|
||||
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||
## 1.1.5(2021-05-12)
|
||||
- 新增 组件示例地址
|
||||
## 1.1.4(2021-02-05)
|
||||
- 调整为uni_modules目录规范
|
||||
1169
uni_modules/uni-icons/components/uni-icons/icons.js
Normal file
1169
uni_modules/uni-icons/components/uni-icons/icons.js
Normal file
File diff suppressed because it is too large
Load Diff
96
uni_modules/uni-icons/components/uni-icons/uni-icons.vue
Normal file
96
uni_modules/uni-icons/components/uni-icons/uni-icons.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<text :style="{ color: color, 'font-size': iconSize }" class="uni-icons" @click="_onClick">{{unicode}}</text>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<text :style="{ color: color, 'font-size': iconSize }" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick"></text>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import icons from './icons.js';
|
||||
const getVal = (val) => {
|
||||
const reg = /^[0-9]*$/g
|
||||
return (typeof val === 'number' || reg.test(val) )? val + 'px' : val;
|
||||
}
|
||||
// #ifdef APP-NVUE
|
||||
var domModule = weex.requireModule('dom');
|
||||
import iconUrl from './uniicons.ttf'
|
||||
domModule.addRule('fontFace', {
|
||||
'fontFamily': "uniicons",
|
||||
'src': "url('"+iconUrl+"')"
|
||||
});
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* Icons 图标
|
||||
* @description 用于展示 icons 图标
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
|
||||
* @property {Number} size 图标大小
|
||||
* @property {String} type 图标图案,参考示例
|
||||
* @property {String} color 图标颜色
|
||||
* @property {String} customPrefix 自定义图标
|
||||
* @event {Function} click 点击 Icon 触发事件
|
||||
*/
|
||||
export default {
|
||||
name: 'UniIcons',
|
||||
emits:['click'],
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333333'
|
||||
},
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 16
|
||||
},
|
||||
customPrefix:{
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
icons: icons.glyphs
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
unicode(){
|
||||
let code = this.icons.find(v=>v.font_class === this.type)
|
||||
if(code){
|
||||
return unescape(`%u${code.unicode}`)
|
||||
}
|
||||
return ''
|
||||
},
|
||||
iconSize(){
|
||||
return getVal(this.size)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
_onClick() {
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/* #ifndef APP-NVUE */
|
||||
@import './uniicons.css';
|
||||
@font-face {
|
||||
font-family: uniicons;
|
||||
src: url('./uniicons.ttf') format('truetype');
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
.uni-icons {
|
||||
font-family: uniicons;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
663
uni_modules/uni-icons/components/uni-icons/uniicons.css
Normal file
663
uni_modules/uni-icons/components/uni-icons/uniicons.css
Normal file
@@ -0,0 +1,663 @@
|
||||
.uniui-color:before {
|
||||
content: "\e6cf";
|
||||
}
|
||||
|
||||
.uniui-wallet:before {
|
||||
content: "\e6b1";
|
||||
}
|
||||
|
||||
.uniui-settings-filled:before {
|
||||
content: "\e6ce";
|
||||
}
|
||||
|
||||
.uniui-auth-filled:before {
|
||||
content: "\e6cc";
|
||||
}
|
||||
|
||||
.uniui-shop-filled:before {
|
||||
content: "\e6cd";
|
||||
}
|
||||
|
||||
.uniui-staff-filled:before {
|
||||
content: "\e6cb";
|
||||
}
|
||||
|
||||
.uniui-vip-filled:before {
|
||||
content: "\e6c6";
|
||||
}
|
||||
|
||||
.uniui-plus-filled:before {
|
||||
content: "\e6c7";
|
||||
}
|
||||
|
||||
.uniui-folder-add-filled:before {
|
||||
content: "\e6c8";
|
||||
}
|
||||
|
||||
.uniui-color-filled:before {
|
||||
content: "\e6c9";
|
||||
}
|
||||
|
||||
.uniui-tune-filled:before {
|
||||
content: "\e6ca";
|
||||
}
|
||||
|
||||
.uniui-calendar-filled:before {
|
||||
content: "\e6c0";
|
||||
}
|
||||
|
||||
.uniui-notification-filled:before {
|
||||
content: "\e6c1";
|
||||
}
|
||||
|
||||
.uniui-wallet-filled:before {
|
||||
content: "\e6c2";
|
||||
}
|
||||
|
||||
.uniui-medal-filled:before {
|
||||
content: "\e6c3";
|
||||
}
|
||||
|
||||
.uniui-gift-filled:before {
|
||||
content: "\e6c4";
|
||||
}
|
||||
|
||||
.uniui-fire-filled:before {
|
||||
content: "\e6c5";
|
||||
}
|
||||
|
||||
.uniui-refreshempty:before {
|
||||
content: "\e6bf";
|
||||
}
|
||||
|
||||
.uniui-location-filled:before {
|
||||
content: "\e6af";
|
||||
}
|
||||
|
||||
.uniui-person-filled:before {
|
||||
content: "\e69d";
|
||||
}
|
||||
|
||||
.uniui-personadd-filled:before {
|
||||
content: "\e698";
|
||||
}
|
||||
|
||||
.uniui-back:before {
|
||||
content: "\e6b9";
|
||||
}
|
||||
|
||||
.uniui-forward:before {
|
||||
content: "\e6ba";
|
||||
}
|
||||
|
||||
.uniui-arrow-right:before {
|
||||
content: "\e6bb";
|
||||
}
|
||||
|
||||
.uniui-arrowthinright:before {
|
||||
content: "\e6bb";
|
||||
}
|
||||
|
||||
.uniui-arrow-left:before {
|
||||
content: "\e6bc";
|
||||
}
|
||||
|
||||
.uniui-arrowthinleft:before {
|
||||
content: "\e6bc";
|
||||
}
|
||||
|
||||
.uniui-arrow-up:before {
|
||||
content: "\e6bd";
|
||||
}
|
||||
|
||||
.uniui-arrowthinup:before {
|
||||
content: "\e6bd";
|
||||
}
|
||||
|
||||
.uniui-arrow-down:before {
|
||||
content: "\e6be";
|
||||
}
|
||||
|
||||
.uniui-arrowthindown:before {
|
||||
content: "\e6be";
|
||||
}
|
||||
|
||||
.uniui-bottom:before {
|
||||
content: "\e6b8";
|
||||
}
|
||||
|
||||
.uniui-arrowdown:before {
|
||||
content: "\e6b8";
|
||||
}
|
||||
|
||||
.uniui-right:before {
|
||||
content: "\e6b5";
|
||||
}
|
||||
|
||||
.uniui-arrowright:before {
|
||||
content: "\e6b5";
|
||||
}
|
||||
|
||||
.uniui-top:before {
|
||||
content: "\e6b6";
|
||||
}
|
||||
|
||||
.uniui-arrowup:before {
|
||||
content: "\e6b6";
|
||||
}
|
||||
|
||||
.uniui-left:before {
|
||||
content: "\e6b7";
|
||||
}
|
||||
|
||||
.uniui-arrowleft:before {
|
||||
content: "\e6b7";
|
||||
}
|
||||
|
||||
.uniui-eye:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
||||
.uniui-eye-filled:before {
|
||||
content: "\e66a";
|
||||
}
|
||||
|
||||
.uniui-eye-slash:before {
|
||||
content: "\e6b3";
|
||||
}
|
||||
|
||||
.uniui-eye-slash-filled:before {
|
||||
content: "\e6b4";
|
||||
}
|
||||
|
||||
.uniui-info-filled:before {
|
||||
content: "\e649";
|
||||
}
|
||||
|
||||
.uniui-reload:before {
|
||||
content: "\e6b2";
|
||||
}
|
||||
|
||||
.uniui-micoff-filled:before {
|
||||
content: "\e6b0";
|
||||
}
|
||||
|
||||
.uniui-map-pin-ellipse:before {
|
||||
content: "\e6ac";
|
||||
}
|
||||
|
||||
.uniui-map-pin:before {
|
||||
content: "\e6ad";
|
||||
}
|
||||
|
||||
.uniui-location:before {
|
||||
content: "\e6ae";
|
||||
}
|
||||
|
||||
.uniui-starhalf:before {
|
||||
content: "\e683";
|
||||
}
|
||||
|
||||
.uniui-star:before {
|
||||
content: "\e688";
|
||||
}
|
||||
|
||||
.uniui-star-filled:before {
|
||||
content: "\e68f";
|
||||
}
|
||||
|
||||
.uniui-calendar:before {
|
||||
content: "\e6a0";
|
||||
}
|
||||
|
||||
.uniui-fire:before {
|
||||
content: "\e6a1";
|
||||
}
|
||||
|
||||
.uniui-medal:before {
|
||||
content: "\e6a2";
|
||||
}
|
||||
|
||||
.uniui-font:before {
|
||||
content: "\e6a3";
|
||||
}
|
||||
|
||||
.uniui-gift:before {
|
||||
content: "\e6a4";
|
||||
}
|
||||
|
||||
.uniui-link:before {
|
||||
content: "\e6a5";
|
||||
}
|
||||
|
||||
.uniui-notification:before {
|
||||
content: "\e6a6";
|
||||
}
|
||||
|
||||
.uniui-staff:before {
|
||||
content: "\e6a7";
|
||||
}
|
||||
|
||||
.uniui-vip:before {
|
||||
content: "\e6a8";
|
||||
}
|
||||
|
||||
.uniui-folder-add:before {
|
||||
content: "\e6a9";
|
||||
}
|
||||
|
||||
.uniui-tune:before {
|
||||
content: "\e6aa";
|
||||
}
|
||||
|
||||
.uniui-auth:before {
|
||||
content: "\e6ab";
|
||||
}
|
||||
|
||||
.uniui-person:before {
|
||||
content: "\e699";
|
||||
}
|
||||
|
||||
.uniui-email-filled:before {
|
||||
content: "\e69a";
|
||||
}
|
||||
|
||||
.uniui-phone-filled:before {
|
||||
content: "\e69b";
|
||||
}
|
||||
|
||||
.uniui-phone:before {
|
||||
content: "\e69c";
|
||||
}
|
||||
|
||||
.uniui-email:before {
|
||||
content: "\e69e";
|
||||
}
|
||||
|
||||
.uniui-personadd:before {
|
||||
content: "\e69f";
|
||||
}
|
||||
|
||||
.uniui-chatboxes-filled:before {
|
||||
content: "\e692";
|
||||
}
|
||||
|
||||
.uniui-contact:before {
|
||||
content: "\e693";
|
||||
}
|
||||
|
||||
.uniui-chatbubble-filled:before {
|
||||
content: "\e694";
|
||||
}
|
||||
|
||||
.uniui-contact-filled:before {
|
||||
content: "\e695";
|
||||
}
|
||||
|
||||
.uniui-chatboxes:before {
|
||||
content: "\e696";
|
||||
}
|
||||
|
||||
.uniui-chatbubble:before {
|
||||
content: "\e697";
|
||||
}
|
||||
|
||||
.uniui-upload-filled:before {
|
||||
content: "\e68e";
|
||||
}
|
||||
|
||||
.uniui-upload:before {
|
||||
content: "\e690";
|
||||
}
|
||||
|
||||
.uniui-weixin:before {
|
||||
content: "\e691";
|
||||
}
|
||||
|
||||
.uniui-compose:before {
|
||||
content: "\e67f";
|
||||
}
|
||||
|
||||
.uniui-qq:before {
|
||||
content: "\e680";
|
||||
}
|
||||
|
||||
.uniui-download-filled:before {
|
||||
content: "\e681";
|
||||
}
|
||||
|
||||
.uniui-pyq:before {
|
||||
content: "\e682";
|
||||
}
|
||||
|
||||
.uniui-sound:before {
|
||||
content: "\e684";
|
||||
}
|
||||
|
||||
.uniui-trash-filled:before {
|
||||
content: "\e685";
|
||||
}
|
||||
|
||||
.uniui-sound-filled:before {
|
||||
content: "\e686";
|
||||
}
|
||||
|
||||
.uniui-trash:before {
|
||||
content: "\e687";
|
||||
}
|
||||
|
||||
.uniui-videocam-filled:before {
|
||||
content: "\e689";
|
||||
}
|
||||
|
||||
.uniui-spinner-cycle:before {
|
||||
content: "\e68a";
|
||||
}
|
||||
|
||||
.uniui-weibo:before {
|
||||
content: "\e68b";
|
||||
}
|
||||
|
||||
.uniui-videocam:before {
|
||||
content: "\e68c";
|
||||
}
|
||||
|
||||
.uniui-download:before {
|
||||
content: "\e68d";
|
||||
}
|
||||
|
||||
.uniui-help:before {
|
||||
content: "\e679";
|
||||
}
|
||||
|
||||
.uniui-navigate-filled:before {
|
||||
content: "\e67a";
|
||||
}
|
||||
|
||||
.uniui-plusempty:before {
|
||||
content: "\e67b";
|
||||
}
|
||||
|
||||
.uniui-smallcircle:before {
|
||||
content: "\e67c";
|
||||
}
|
||||
|
||||
.uniui-minus-filled:before {
|
||||
content: "\e67d";
|
||||
}
|
||||
|
||||
.uniui-micoff:before {
|
||||
content: "\e67e";
|
||||
}
|
||||
|
||||
.uniui-closeempty:before {
|
||||
content: "\e66c";
|
||||
}
|
||||
|
||||
.uniui-clear:before {
|
||||
content: "\e66d";
|
||||
}
|
||||
|
||||
.uniui-navigate:before {
|
||||
content: "\e66e";
|
||||
}
|
||||
|
||||
.uniui-minus:before {
|
||||
content: "\e66f";
|
||||
}
|
||||
|
||||
.uniui-image:before {
|
||||
content: "\e670";
|
||||
}
|
||||
|
||||
.uniui-mic:before {
|
||||
content: "\e671";
|
||||
}
|
||||
|
||||
.uniui-paperplane:before {
|
||||
content: "\e672";
|
||||
}
|
||||
|
||||
.uniui-close:before {
|
||||
content: "\e673";
|
||||
}
|
||||
|
||||
.uniui-help-filled:before {
|
||||
content: "\e674";
|
||||
}
|
||||
|
||||
.uniui-paperplane-filled:before {
|
||||
content: "\e675";
|
||||
}
|
||||
|
||||
.uniui-plus:before {
|
||||
content: "\e676";
|
||||
}
|
||||
|
||||
.uniui-mic-filled:before {
|
||||
content: "\e677";
|
||||
}
|
||||
|
||||
.uniui-image-filled:before {
|
||||
content: "\e678";
|
||||
}
|
||||
|
||||
.uniui-locked-filled:before {
|
||||
content: "\e668";
|
||||
}
|
||||
|
||||
.uniui-info:before {
|
||||
content: "\e669";
|
||||
}
|
||||
|
||||
.uniui-locked:before {
|
||||
content: "\e66b";
|
||||
}
|
||||
|
||||
.uniui-camera-filled:before {
|
||||
content: "\e658";
|
||||
}
|
||||
|
||||
.uniui-chat-filled:before {
|
||||
content: "\e659";
|
||||
}
|
||||
|
||||
.uniui-camera:before {
|
||||
content: "\e65a";
|
||||
}
|
||||
|
||||
.uniui-circle:before {
|
||||
content: "\e65b";
|
||||
}
|
||||
|
||||
.uniui-checkmarkempty:before {
|
||||
content: "\e65c";
|
||||
}
|
||||
|
||||
.uniui-chat:before {
|
||||
content: "\e65d";
|
||||
}
|
||||
|
||||
.uniui-circle-filled:before {
|
||||
content: "\e65e";
|
||||
}
|
||||
|
||||
.uniui-flag:before {
|
||||
content: "\e65f";
|
||||
}
|
||||
|
||||
.uniui-flag-filled:before {
|
||||
content: "\e660";
|
||||
}
|
||||
|
||||
.uniui-gear-filled:before {
|
||||
content: "\e661";
|
||||
}
|
||||
|
||||
.uniui-home:before {
|
||||
content: "\e662";
|
||||
}
|
||||
|
||||
.uniui-home-filled:before {
|
||||
content: "\e663";
|
||||
}
|
||||
|
||||
.uniui-gear:before {
|
||||
content: "\e664";
|
||||
}
|
||||
|
||||
.uniui-smallcircle-filled:before {
|
||||
content: "\e665";
|
||||
}
|
||||
|
||||
.uniui-map-filled:before {
|
||||
content: "\e666";
|
||||
}
|
||||
|
||||
.uniui-map:before {
|
||||
content: "\e667";
|
||||
}
|
||||
|
||||
.uniui-refresh-filled:before {
|
||||
content: "\e656";
|
||||
}
|
||||
|
||||
.uniui-refresh:before {
|
||||
content: "\e657";
|
||||
}
|
||||
|
||||
.uniui-cloud-upload:before {
|
||||
content: "\e645";
|
||||
}
|
||||
|
||||
.uniui-cloud-download-filled:before {
|
||||
content: "\e646";
|
||||
}
|
||||
|
||||
.uniui-cloud-download:before {
|
||||
content: "\e647";
|
||||
}
|
||||
|
||||
.uniui-cloud-upload-filled:before {
|
||||
content: "\e648";
|
||||
}
|
||||
|
||||
.uniui-redo:before {
|
||||
content: "\e64a";
|
||||
}
|
||||
|
||||
.uniui-images-filled:before {
|
||||
content: "\e64b";
|
||||
}
|
||||
|
||||
.uniui-undo-filled:before {
|
||||
content: "\e64c";
|
||||
}
|
||||
|
||||
.uniui-more:before {
|
||||
content: "\e64d";
|
||||
}
|
||||
|
||||
.uniui-more-filled:before {
|
||||
content: "\e64e";
|
||||
}
|
||||
|
||||
.uniui-undo:before {
|
||||
content: "\e64f";
|
||||
}
|
||||
|
||||
.uniui-images:before {
|
||||
content: "\e650";
|
||||
}
|
||||
|
||||
.uniui-paperclip:before {
|
||||
content: "\e652";
|
||||
}
|
||||
|
||||
.uniui-settings:before {
|
||||
content: "\e653";
|
||||
}
|
||||
|
||||
.uniui-search:before {
|
||||
content: "\e654";
|
||||
}
|
||||
|
||||
.uniui-redo-filled:before {
|
||||
content: "\e655";
|
||||
}
|
||||
|
||||
.uniui-list:before {
|
||||
content: "\e644";
|
||||
}
|
||||
|
||||
.uniui-mail-open-filled:before {
|
||||
content: "\e63a";
|
||||
}
|
||||
|
||||
.uniui-hand-down-filled:before {
|
||||
content: "\e63c";
|
||||
}
|
||||
|
||||
.uniui-hand-down:before {
|
||||
content: "\e63d";
|
||||
}
|
||||
|
||||
.uniui-hand-up-filled:before {
|
||||
content: "\e63e";
|
||||
}
|
||||
|
||||
.uniui-hand-up:before {
|
||||
content: "\e63f";
|
||||
}
|
||||
|
||||
.uniui-heart-filled:before {
|
||||
content: "\e641";
|
||||
}
|
||||
|
||||
.uniui-mail-open:before {
|
||||
content: "\e643";
|
||||
}
|
||||
|
||||
.uniui-heart:before {
|
||||
content: "\e639";
|
||||
}
|
||||
|
||||
.uniui-loop:before {
|
||||
content: "\e633";
|
||||
}
|
||||
|
||||
.uniui-pulldown:before {
|
||||
content: "\e632";
|
||||
}
|
||||
|
||||
.uniui-scan:before {
|
||||
content: "\e62a";
|
||||
}
|
||||
|
||||
.uniui-bars:before {
|
||||
content: "\e627";
|
||||
}
|
||||
|
||||
.uniui-cart-filled:before {
|
||||
content: "\e629";
|
||||
}
|
||||
|
||||
.uniui-checkbox:before {
|
||||
content: "\e62b";
|
||||
}
|
||||
|
||||
.uniui-checkbox-filled:before {
|
||||
content: "\e62c";
|
||||
}
|
||||
|
||||
.uniui-shop:before {
|
||||
content: "\e62f";
|
||||
}
|
||||
|
||||
.uniui-headphones:before {
|
||||
content: "\e630";
|
||||
}
|
||||
|
||||
.uniui-cart:before {
|
||||
content: "\e631";
|
||||
}
|
||||
BIN
uni_modules/uni-icons/components/uni-icons/uniicons.ttf
Normal file
BIN
uni_modules/uni-icons/components/uni-icons/uniicons.ttf
Normal file
Binary file not shown.
86
uni_modules/uni-icons/package.json
Normal file
86
uni_modules/uni-icons/package.json
Normal file
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"id": "uni-icons",
|
||||
"displayName": "uni-icons 图标",
|
||||
"version": "1.3.5",
|
||||
"description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。",
|
||||
"keywords": [
|
||||
"uni-ui",
|
||||
"uniui",
|
||||
"icon",
|
||||
"图标"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.2.14"
|
||||
},
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": ["uni-scss"],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
uni_modules/uni-icons/readme.md
Normal file
8
uni_modules/uni-icons/readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## Icons 图标
|
||||
> **组件名:uni-icons**
|
||||
> 代码块: `uIcons`
|
||||
|
||||
用于展示 icons 图标 。
|
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-icons)
|
||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||
18
uni_modules/uni-notice-bar/changelog.md
Normal file
18
uni_modules/uni-notice-bar/changelog.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## 1.2.1(2022-09-05)
|
||||
- 新增 属性 fontSize,可修改文字大小。
|
||||
## 1.2.0(2021-11-19)
|
||||
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
|
||||
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-notice-bar](https://uniapp.dcloud.io/component/uniui/uni-notice-bar)
|
||||
## 1.1.1(2021-11-09)
|
||||
- 新增 提供组件设计资源,组件样式调整
|
||||
## 1.1.0(2021-07-30)
|
||||
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||
## 1.0.9(2021-05-12)
|
||||
- 新增 组件示例地址
|
||||
## 1.0.8(2021-04-21)
|
||||
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
|
||||
## 1.0.7(2021-02-05)
|
||||
- 优化 组件引用关系,通过uni_modules引用组件
|
||||
|
||||
## 1.0.6(2021-02-05)
|
||||
- 调整为uni_modules目录规范
|
||||
@@ -0,0 +1,426 @@
|
||||
<template>
|
||||
<view v-if="show" class="uni-noticebar" :style="{ backgroundColor }" @click="onClick">
|
||||
<uni-icons v-if="showIcon === true || showIcon === 'true'" class="uni-noticebar-icon" type="sound"
|
||||
:color="color" :size="fontSize * 1.5" />
|
||||
<view ref="textBox" class="uni-noticebar__content-wrapper"
|
||||
:class="{
|
||||
'uni-noticebar__content-wrapper--scrollable': scrollable,
|
||||
'uni-noticebar__content-wrapper--single': !scrollable && (single || moreText)
|
||||
}"
|
||||
:style="{ height: scrollable ? fontSize * 1.5 + 'px' : 'auto' }"
|
||||
>
|
||||
<view :id="elIdBox" class="uni-noticebar__content"
|
||||
:class="{
|
||||
'uni-noticebar__content--scrollable': scrollable,
|
||||
'uni-noticebar__content--single': !scrollable && (single || moreText)
|
||||
}"
|
||||
>
|
||||
<text :id="elId" ref="animationEle" class="uni-noticebar__content-text"
|
||||
:class="{
|
||||
'uni-noticebar__content-text--scrollable': scrollable,
|
||||
'uni-noticebar__content-text--single': !scrollable && (single || showGetMore)
|
||||
}"
|
||||
:style="{
|
||||
color: color,
|
||||
fontSize: fontSize + 'px',
|
||||
lineHeight: fontSize * 1.5 + 'px',
|
||||
width: wrapWidth + 'px',
|
||||
'animationDuration': animationDuration,
|
||||
'-webkit-animationDuration': animationDuration,
|
||||
animationPlayState: webviewHide ? 'paused' : animationPlayState,
|
||||
'-webkit-animationPlayState': webviewHide ? 'paused' : animationPlayState,
|
||||
animationDelay: animationDelay,
|
||||
'-webkit-animationDelay': animationDelay
|
||||
}"
|
||||
>{{text}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="isShowGetMore" class="uni-noticebar__more uni-cursor-point"
|
||||
@click="clickMore">
|
||||
<text v-if="moreText.length > 0" :style="{ color: moreColor, fontSize: fontSize + 'px' }">{{ moreText }}</text>
|
||||
<uni-icons v-else type="right" :color="moreColor" :size="fontSize * 1.1" />
|
||||
</view>
|
||||
<view class="uni-noticebar-close uni-cursor-point" v-if="isShowClose">
|
||||
<uni-icons type="closeempty" :color="color" :size="fontSize * 1.1" @click="close" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef APP-NVUE
|
||||
const dom = weex.requireModule('dom');
|
||||
const animation = weex.requireModule('animation');
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* NoticeBar 自定义导航栏
|
||||
* @description 通告栏组件
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=30
|
||||
* @property {Number} speed 文字滚动的速度,默认100px/秒
|
||||
* @property {String} text 显示文字
|
||||
* @property {String} backgroundColor 背景颜色
|
||||
* @property {String} color 文字颜色
|
||||
* @property {String} moreColor 查看更多文字的颜色
|
||||
* @property {String} moreText 设置“查看更多”的文本
|
||||
* @property {Boolean} single = [true|false] 是否单行
|
||||
* @property {Boolean} scrollable = [true|false] 是否滚动,为true时,NoticeBar为单行
|
||||
* @property {Boolean} showIcon = [true|false] 是否显示左侧喇叭图标
|
||||
* @property {Boolean} showClose = [true|false] 是否显示左侧关闭按钮
|
||||
* @property {Boolean} showGetMore = [true|false] 是否显示右侧查看更多图标,为true时,NoticeBar为单行
|
||||
* @event {Function} click 点击 NoticeBar 触发事件
|
||||
* @event {Function} close 关闭 NoticeBar 触发事件
|
||||
* @event {Function} getmore 点击”查看更多“时触发事件
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'UniNoticeBar',
|
||||
emits: ['click', 'getmore', 'close'],
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
moreText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: '#FFF9EA'
|
||||
},
|
||||
speed: {
|
||||
// 默认1s滚动100px
|
||||
type: Number,
|
||||
default: 35
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#FF9A43'
|
||||
},
|
||||
fontSize: {
|
||||
type: Number,
|
||||
default: 14
|
||||
},
|
||||
moreColor: {
|
||||
type: String,
|
||||
default: '#FF9A43'
|
||||
},
|
||||
single: {
|
||||
// 是否单行
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
scrollable: {
|
||||
// 是否滚动,添加后控制单行效果取消
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
showIcon: {
|
||||
// 是否显示左侧icon
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
showGetMore: {
|
||||
// 是否显示右侧查看更多
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
showClose: {
|
||||
// 是否显示左侧关闭按钮
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
|
||||
const elIdBox = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
|
||||
return {
|
||||
textWidth: 0,
|
||||
boxWidth: 0,
|
||||
wrapWidth: '',
|
||||
webviewHide: false,
|
||||
// #ifdef APP-NVUE
|
||||
stopAnimation: false,
|
||||
// #endif
|
||||
elId: elId,
|
||||
elIdBox: elIdBox,
|
||||
show: true,
|
||||
animationDuration: 'none',
|
||||
animationPlayState: 'paused',
|
||||
animationDelay: '0s'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isShowGetMore() {
|
||||
return this.showGetMore === true || this.showGetMore === 'true'
|
||||
},
|
||||
isShowClose() {
|
||||
return (this.showClose === true || this.showClose === 'true')
|
||||
&& (this.showGetMore === false || this.showGetMore === 'false')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// #ifdef APP-PLUS
|
||||
var pages = getCurrentPages();
|
||||
var page = pages[pages.length - 1];
|
||||
var currentWebview = page.$getAppWebview();
|
||||
currentWebview.addEventListener('hide', () => {
|
||||
this.webviewHide = true
|
||||
})
|
||||
currentWebview.addEventListener('show', () => {
|
||||
this.webviewHide = false
|
||||
})
|
||||
// #endif
|
||||
this.$nextTick(() => {
|
||||
this.initSize()
|
||||
})
|
||||
},
|
||||
// #ifdef APP-NVUE
|
||||
beforeDestroy() {
|
||||
this.stopAnimation = true
|
||||
},
|
||||
// #endif
|
||||
methods: {
|
||||
initSize() {
|
||||
if (this.scrollable) {
|
||||
// #ifndef APP-NVUE
|
||||
let query = [],
|
||||
boxWidth = 0,
|
||||
textWidth = 0;
|
||||
let textQuery = new Promise((resolve, reject) => {
|
||||
uni.createSelectorQuery()
|
||||
// #ifndef MP-ALIPAY
|
||||
.in(this)
|
||||
// #endif
|
||||
.select(`#${this.elId}`)
|
||||
.boundingClientRect()
|
||||
.exec(ret => {
|
||||
this.textWidth = ret[0].width
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
let boxQuery = new Promise((resolve, reject) => {
|
||||
uni.createSelectorQuery()
|
||||
// #ifndef MP-ALIPAY
|
||||
.in(this)
|
||||
// #endif
|
||||
.select(`#${this.elIdBox}`)
|
||||
.boundingClientRect()
|
||||
.exec(ret => {
|
||||
this.boxWidth = ret[0].width
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
query.push(textQuery)
|
||||
query.push(boxQuery)
|
||||
Promise.all(query).then(() => {
|
||||
this.animationDuration = `${this.textWidth / this.speed}s`
|
||||
this.animationDelay = `-${this.boxWidth / this.speed}s`
|
||||
setTimeout(() => {
|
||||
this.animationPlayState = 'running'
|
||||
}, 1000)
|
||||
})
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
dom.getComponentRect(this.$refs['animationEle'], (res) => {
|
||||
let winWidth = uni.getSystemInfoSync().windowWidth
|
||||
this.textWidth = res.size.width
|
||||
animation.transition(this.$refs['animationEle'], {
|
||||
styles: {
|
||||
transform: `translateX(-${winWidth}px)`
|
||||
},
|
||||
duration: 0,
|
||||
timingFunction: 'linear',
|
||||
delay: 0
|
||||
}, () => {
|
||||
if (!this.stopAnimation) {
|
||||
animation.transition(this.$refs['animationEle'], {
|
||||
styles: {
|
||||
transform: `translateX(-${this.textWidth}px)`
|
||||
},
|
||||
timingFunction: 'linear',
|
||||
duration: (this.textWidth - winWidth) / this.speed * 1000,
|
||||
delay: 1000
|
||||
}, () => {
|
||||
if (!this.stopAnimation) {
|
||||
this.loopAnimation()
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
// #ifdef APP-NVUE
|
||||
if (!this.scrollable && (this.single || this.moreText)) {
|
||||
dom.getComponentRect(this.$refs['textBox'], (res) => {
|
||||
this.wrapWidth = res.size.width
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
loopAnimation() {
|
||||
// #ifdef APP-NVUE
|
||||
animation.transition(this.$refs['animationEle'], {
|
||||
styles: {
|
||||
transform: `translateX(0px)`
|
||||
},
|
||||
duration: 0
|
||||
}, () => {
|
||||
if (!this.stopAnimation) {
|
||||
animation.transition(this.$refs['animationEle'], {
|
||||
styles: {
|
||||
transform: `translateX(-${this.textWidth}px)`
|
||||
},
|
||||
duration: this.textWidth / this.speed * 1000,
|
||||
timingFunction: 'linear',
|
||||
delay: 0
|
||||
}, () => {
|
||||
if (!this.stopAnimation) {
|
||||
this.loopAnimation()
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
clickMore() {
|
||||
this.$emit('getmore')
|
||||
},
|
||||
close() {
|
||||
this.show = false;
|
||||
this.$emit('close')
|
||||
},
|
||||
onClick() {
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.uni-noticebar {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.uni-cursor-point {
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-noticebar-close {
|
||||
margin-left: 8px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.uni-noticebar-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.uni-noticebar__content-wrapper {
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.uni-noticebar__content-wrapper--single {
|
||||
/* #ifndef APP-NVUE */
|
||||
line-height: 18px;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-noticebar__content-wrapper--single,
|
||||
.uni-noticebar__content-wrapper--scrollable {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
.uni-noticebar__content-wrapper--scrollable {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.uni-noticebar__content--scrollable {
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 0;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
flex: 1;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-noticebar__content--single {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
flex: none;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-noticebar__content-text {
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
/* #ifndef APP-NVUE */
|
||||
word-break: break-all;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-noticebar__content-text--single {
|
||||
/* #ifdef APP-NVUE */
|
||||
lines: 1;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
/* #endif */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.uni-noticebar__content-text--scrollable {
|
||||
/* #ifdef APP-NVUE */
|
||||
lines: 1;
|
||||
padding-left: 750rpx;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
position: absolute;
|
||||
display: block;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
white-space: nowrap;
|
||||
padding-left: 100%;
|
||||
animation: notice 10s 0s linear infinite both;
|
||||
animation-play-state: paused;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-noticebar__more {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
@keyframes notice {
|
||||
100% {
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
87
uni_modules/uni-notice-bar/package.json
Normal file
87
uni_modules/uni-notice-bar/package.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"id": "uni-notice-bar",
|
||||
"displayName": "uni-notice-bar 通告栏",
|
||||
"version": "1.2.1",
|
||||
"description": "NoticeBar 通告栏组件,常用于展示公告信息,可设为滚动公告",
|
||||
"keywords": [
|
||||
"uni-ui",
|
||||
"uniui",
|
||||
"通告栏",
|
||||
"公告",
|
||||
"跑马灯"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": ""
|
||||
},
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
|
||||
"type": "component-vue"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"uni-scss",
|
||||
"uni-icons"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
uni_modules/uni-notice-bar/readme.md
Normal file
13
uni_modules/uni-notice-bar/readme.md
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
## NoticeBar 通告栏
|
||||
> **组件名:uni-notice-bar**
|
||||
> 代码块: `uNoticeBar`
|
||||
|
||||
|
||||
通告栏组件 。
|
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-notice-bar)
|
||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||
|
||||
|
||||
68
uni_modules/uni-popup/changelog.md
Normal file
68
uni_modules/uni-popup/changelog.md
Normal file
@@ -0,0 +1,68 @@
|
||||
## 1.8.3(2023-04-17)
|
||||
- 修复 uni-popup 重复打开时的 bug
|
||||
## 1.8.2(2023-02-02)
|
||||
- uni-popup-dialog 组件新增 inputType 属性
|
||||
## 1.8.1(2022-12-01)
|
||||
- 修复 nvue 下 v-show 报错
|
||||
## 1.8.0(2022-11-29)
|
||||
- 优化 主题样式
|
||||
## 1.7.9(2022-04-02)
|
||||
- 修复 弹出层内部无法滚动的bug
|
||||
## 1.7.8(2022-03-28)
|
||||
- 修复 小程序中高度错误的bug
|
||||
## 1.7.7(2022-03-17)
|
||||
- 修复 快速调用open出现问题的Bug
|
||||
## 1.7.6(2022-02-14)
|
||||
- 修复 safeArea 属性不能设置为false的bug
|
||||
## 1.7.5(2022-01-19)
|
||||
- 修复 isMaskClick 失效的bug
|
||||
## 1.7.4(2022-01-19)
|
||||
- 新增 cancelText \ confirmText 属性 ,可自定义文本
|
||||
- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色
|
||||
- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题
|
||||
## 1.7.3(2022-01-13)
|
||||
- 修复 设置 safeArea 属性不生效的bug
|
||||
## 1.7.2(2021-11-26)
|
||||
- 优化 组件示例
|
||||
## 1.7.1(2021-11-26)
|
||||
- 修复 vuedoc 文字错误
|
||||
## 1.7.0(2021-11-19)
|
||||
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
|
||||
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup)
|
||||
## 1.6.2(2021-08-24)
|
||||
- 新增 支持国际化
|
||||
## 1.6.1(2021-07-30)
|
||||
- 优化 vue3下事件警告的问题
|
||||
## 1.6.0(2021-07-13)
|
||||
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||
## 1.5.0(2021-06-23)
|
||||
- 新增 mask-click 遮罩层点击事件
|
||||
## 1.4.5(2021-06-22)
|
||||
- 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
|
||||
## 1.4.4(2021-06-18)
|
||||
- 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
|
||||
## 1.4.3(2021-06-08)
|
||||
- 修复 错误的 watch 字段
|
||||
- 修复 safeArea 属性不生效的问题
|
||||
- 修复 点击内容,再点击遮罩无法关闭的Bug
|
||||
## 1.4.2(2021-05-12)
|
||||
- 新增 组件示例地址
|
||||
## 1.4.1(2021-04-29)
|
||||
- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题
|
||||
## 1.4.0 (2021-04-29)
|
||||
- 新增 type 属性的 left\right 值,支持左右弹出
|
||||
- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗
|
||||
- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色
|
||||
- 新增 safeArea 属性,是否适配底部安全区
|
||||
- 修复 App\h5\微信小程序底部安全区占位不对的Bug
|
||||
- 修复 App 端弹出等待的Bug
|
||||
- 优化 提升低配设备性能,优化动画卡顿问题
|
||||
- 优化 更简单的组件自定义方式
|
||||
## 1.2.9(2021-02-05)
|
||||
- 优化 组件引用关系,通过uni_modules引用组件
|
||||
## 1.2.8(2021-02-05)
|
||||
- 调整为uni_modules目录规范
|
||||
## 1.2.7(2021-02-05)
|
||||
- 调整为uni_modules目录规范
|
||||
- 新增 支持 PC 端
|
||||
- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端
|
||||
@@ -0,0 +1,45 @@
|
||||
// #ifdef H5
|
||||
export default {
|
||||
name: 'Keypress',
|
||||
props: {
|
||||
disable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
const keyNames = {
|
||||
esc: ['Esc', 'Escape'],
|
||||
tab: 'Tab',
|
||||
enter: 'Enter',
|
||||
space: [' ', 'Spacebar'],
|
||||
up: ['Up', 'ArrowUp'],
|
||||
left: ['Left', 'ArrowLeft'],
|
||||
right: ['Right', 'ArrowRight'],
|
||||
down: ['Down', 'ArrowDown'],
|
||||
delete: ['Backspace', 'Delete', 'Del']
|
||||
}
|
||||
const listener = ($event) => {
|
||||
if (this.disable) {
|
||||
return
|
||||
}
|
||||
const keyName = Object.keys(keyNames).find(key => {
|
||||
const keyName = $event.key
|
||||
const value = keyNames[key]
|
||||
return value === keyName || (Array.isArray(value) && value.includes(keyName))
|
||||
})
|
||||
if (keyName) {
|
||||
// 避免和其他按键事件冲突
|
||||
setTimeout(() => {
|
||||
this.$emit(keyName, {})
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
document.addEventListener('keyup', listener)
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
document.removeEventListener('keyup', listener)
|
||||
})
|
||||
},
|
||||
render: () => {}
|
||||
}
|
||||
// #endif
|
||||
@@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<view class="uni-popup-dialog">
|
||||
<view class="uni-dialog-title">
|
||||
<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text>
|
||||
</view>
|
||||
<view v-if="mode === 'base'" class="uni-dialog-content">
|
||||
<slot>
|
||||
<text class="uni-dialog-content-text">{{content}}</text>
|
||||
</slot>
|
||||
</view>
|
||||
<view v-else class="uni-dialog-content">
|
||||
<slot>
|
||||
<input class="uni-dialog-input" v-model="val" :type="inputType" :placeholder="placeholderText" :focus="focus" >
|
||||
</slot>
|
||||
</view>
|
||||
<view class="uni-dialog-button-group">
|
||||
<view class="uni-dialog-button" @click="closeDialog">
|
||||
<text class="uni-dialog-button-text">{{closeText}}</text>
|
||||
</view>
|
||||
<view class="uni-dialog-button uni-border-left" @click="onOk">
|
||||
<text class="uni-dialog-button-text uni-button-color">{{okText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import popup from '../uni-popup/popup.js'
|
||||
import {
|
||||
initVueI18n
|
||||
} from '@dcloudio/uni-i18n'
|
||||
import messages from '../uni-popup/i18n/index.js'
|
||||
const { t } = initVueI18n(messages)
|
||||
/**
|
||||
* PopUp 弹出层-对话框样式
|
||||
* @description 弹出层-对话框样式
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
|
||||
* @property {String} value input 模式下的默认值
|
||||
* @property {String} placeholder input 模式下输入提示
|
||||
* @property {String} type = [success|warning|info|error] 主题样式
|
||||
* @value success 成功
|
||||
* @value warning 提示
|
||||
* @value info 消息
|
||||
* @value error 错误
|
||||
* @property {String} mode = [base|input] 模式、
|
||||
* @value base 基础对话框
|
||||
* @value input 可输入对话框
|
||||
* @property {String} content 对话框内容
|
||||
* @property {Boolean} beforeClose 是否拦截取消事件
|
||||
* @event {Function} confirm 点击确认按钮触发
|
||||
* @event {Function} close 点击取消按钮触发
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: "uniPopupDialog",
|
||||
mixins: [popup],
|
||||
emits:['confirm','close'],
|
||||
props: {
|
||||
inputType:{
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'error'
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'base'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
beforeClose: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
cancelText:{
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
confirmText:{
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogType: 'error',
|
||||
focus: false,
|
||||
val: ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
okText() {
|
||||
return this.confirmText || t("uni-popup.ok")
|
||||
},
|
||||
closeText() {
|
||||
return this.cancelText || t("uni-popup.cancel")
|
||||
},
|
||||
placeholderText() {
|
||||
return this.placeholder || t("uni-popup.placeholder")
|
||||
},
|
||||
titleText() {
|
||||
return this.title || t("uni-popup.title")
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
type(val) {
|
||||
this.dialogType = val
|
||||
},
|
||||
mode(val) {
|
||||
if (val === 'input') {
|
||||
this.dialogType = 'info'
|
||||
}
|
||||
},
|
||||
value(val) {
|
||||
this.val = val
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 对话框遮罩不可点击
|
||||
this.popup.disableMask()
|
||||
// this.popup.closeMask()
|
||||
if (this.mode === 'input') {
|
||||
this.dialogType = 'info'
|
||||
this.val = this.value
|
||||
} else {
|
||||
this.dialogType = this.type
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 点击确认按钮
|
||||
*/
|
||||
onOk() {
|
||||
if (this.mode === 'input'){
|
||||
this.$emit('confirm', this.val)
|
||||
}else{
|
||||
this.$emit('confirm')
|
||||
}
|
||||
if(this.beforeClose) return
|
||||
this.popup.close()
|
||||
},
|
||||
/**
|
||||
* 点击取消按钮
|
||||
*/
|
||||
closeDialog() {
|
||||
this.$emit('close')
|
||||
if(this.beforeClose) return
|
||||
this.popup.close()
|
||||
},
|
||||
close(){
|
||||
this.popup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" >
|
||||
.uni-popup-dialog {
|
||||
width: 300px;
|
||||
border-radius: 11px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.uni-dialog-title {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
.uni-dialog-title-text {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.uni-dialog-content {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.uni-dialog-content-text {
|
||||
font-size: 14px;
|
||||
color: #6C6C6C;
|
||||
}
|
||||
|
||||
.uni-dialog-button-group {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
border-top-color: #f5f5f5;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.uni-dialog-button {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.uni-border-left {
|
||||
border-left-color: #f0f0f0;
|
||||
border-left-style: solid;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
.uni-dialog-button-text {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.uni-button-color {
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.uni-dialog-input {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
border: 1px #eee solid;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
border-radius: 5px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.uni-popup__success {
|
||||
color: #4cd964;
|
||||
}
|
||||
|
||||
.uni-popup__warn {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
.uni-popup__error {
|
||||
color: #dd524d;
|
||||
}
|
||||
|
||||
.uni-popup__info {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<view class="uni-popup-message">
|
||||
<view class="uni-popup-message__box fixforpc-width" :class="'uni-popup__'+type">
|
||||
<slot>
|
||||
<text class="uni-popup-message-text" :class="'uni-popup__'+type+'-text'">{{message}}</text>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import popup from '../uni-popup/popup.js'
|
||||
/**
|
||||
* PopUp 弹出层-消息提示
|
||||
* @description 弹出层-消息提示
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
|
||||
* @property {String} type = [success|warning|info|error] 主题样式
|
||||
* @value success 成功
|
||||
* @value warning 提示
|
||||
* @value info 消息
|
||||
* @value error 错误
|
||||
* @property {String} message 消息提示文字
|
||||
* @property {String} duration 显示时间,设置为 0 则不会自动关闭
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'uniPopupMessage',
|
||||
mixins:[popup],
|
||||
props: {
|
||||
/**
|
||||
* 主题 success/warning/info/error 默认 success
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
default: 'success'
|
||||
},
|
||||
/**
|
||||
* 消息文字
|
||||
*/
|
||||
message: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 显示时间,设置为 0 则不会自动关闭
|
||||
*/
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 3000
|
||||
},
|
||||
maskShow:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {
|
||||
this.popup.maskShow = this.maskShow
|
||||
this.popup.messageChild = this
|
||||
},
|
||||
methods: {
|
||||
timerClose(){
|
||||
if(this.duration === 0) return
|
||||
clearTimeout(this.timer)
|
||||
this.timer = setTimeout(()=>{
|
||||
this.popup.close()
|
||||
},this.duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" >
|
||||
.uni-popup-message {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.uni-popup-message__box {
|
||||
background-color: #e1f3d8;
|
||||
padding: 10px 15px;
|
||||
border-color: #eee;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 500px) {
|
||||
.fixforpc-width {
|
||||
margin-top: 20px;
|
||||
border-radius: 4px;
|
||||
flex: none;
|
||||
min-width: 380px;
|
||||
/* #ifndef APP-NVUE */
|
||||
max-width: 50%;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
max-width: 500px;
|
||||
/* #endif */
|
||||
}
|
||||
}
|
||||
|
||||
.uni-popup-message-text {
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.uni-popup__success {
|
||||
background-color: #e1f3d8;
|
||||
}
|
||||
|
||||
.uni-popup__success-text {
|
||||
color: #67C23A;
|
||||
}
|
||||
|
||||
.uni-popup__warn {
|
||||
background-color: #faecd8;
|
||||
}
|
||||
|
||||
.uni-popup__warn-text {
|
||||
color: #E6A23C;
|
||||
}
|
||||
|
||||
.uni-popup__error {
|
||||
background-color: #fde2e2;
|
||||
}
|
||||
|
||||
.uni-popup__error-text {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
.uni-popup__info {
|
||||
background-color: #F2F6FC;
|
||||
}
|
||||
|
||||
.uni-popup__info-text {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<view class="uni-popup-share">
|
||||
<view class="uni-share-title"><text class="uni-share-title-text">{{shareTitleText}}</text></view>
|
||||
<view class="uni-share-content">
|
||||
<view class="uni-share-content-box">
|
||||
<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
|
||||
<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
|
||||
<text class="uni-share-text">{{item.text}}</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="uni-share-button-box">
|
||||
<button class="uni-share-button" @click="close">{{cancelText}}</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import popup from '../uni-popup/popup.js'
|
||||
import {
|
||||
initVueI18n
|
||||
} from '@dcloudio/uni-i18n'
|
||||
import messages from '../uni-popup/i18n/index.js'
|
||||
const { t } = initVueI18n(messages)
|
||||
export default {
|
||||
name: 'UniPopupShare',
|
||||
mixins:[popup],
|
||||
emits:['select'],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
beforeClose: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
bottomData: [{
|
||||
text: '微信',
|
||||
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png',
|
||||
name: 'wx'
|
||||
},
|
||||
{
|
||||
text: '支付宝',
|
||||
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.png',
|
||||
name: 'wx'
|
||||
},
|
||||
{
|
||||
text: 'QQ',
|
||||
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png',
|
||||
name: 'qq'
|
||||
},
|
||||
{
|
||||
text: '新浪',
|
||||
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png',
|
||||
name: 'sina'
|
||||
},
|
||||
// {
|
||||
// text: '百度',
|
||||
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/1ec6e920-50bf-11eb-8a36-ebb87efcf8c0.png',
|
||||
// name: 'copy'
|
||||
// },
|
||||
// {
|
||||
// text: '其他',
|
||||
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/2e0fdfe0-50bf-11eb-b997-9918a5dda011.png',
|
||||
// name: 'more'
|
||||
// }
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
computed: {
|
||||
cancelText() {
|
||||
return t("uni-popup.cancel")
|
||||
},
|
||||
shareTitleText() {
|
||||
return this.title || t("uni-popup.shareTitle")
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 选择内容
|
||||
*/
|
||||
select(item, index) {
|
||||
this.$emit('select', {
|
||||
item,
|
||||
index
|
||||
})
|
||||
this.close()
|
||||
|
||||
},
|
||||
/**
|
||||
* 关闭窗口
|
||||
*/
|
||||
close() {
|
||||
if(this.beforeClose) return
|
||||
this.popup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" >
|
||||
.uni-popup-share {
|
||||
background-color: #fff;
|
||||
border-top-left-radius: 11px;
|
||||
border-top-right-radius: 11px;
|
||||
}
|
||||
.uni-share-title {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
}
|
||||
.uni-share-title-text {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.uni-share-content {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.uni-share-content-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.uni-share-content-item {
|
||||
width: 90px;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 10px 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uni-share-content-item:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.uni-share-image {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.uni-share-text {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
color: #3B4144;
|
||||
}
|
||||
|
||||
.uni-share-button-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.uni-share-button {
|
||||
flex: 1;
|
||||
border-radius: 50px;
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.uni-share-button::after {
|
||||
border-radius: 50px;
|
||||
}
|
||||
</style>
|
||||
7
uni_modules/uni-popup/components/uni-popup/i18n/en.json
Normal file
7
uni_modules/uni-popup/components/uni-popup/i18n/en.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"uni-popup.cancel": "cancel",
|
||||
"uni-popup.ok": "ok",
|
||||
"uni-popup.placeholder": "pleace enter",
|
||||
"uni-popup.title": "Hint",
|
||||
"uni-popup.shareTitle": "Share to"
|
||||
}
|
||||
8
uni_modules/uni-popup/components/uni-popup/i18n/index.js
Normal file
8
uni_modules/uni-popup/components/uni-popup/i18n/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import en from './en.json'
|
||||
import zhHans from './zh-Hans.json'
|
||||
import zhHant from './zh-Hant.json'
|
||||
export default {
|
||||
en,
|
||||
'zh-Hans': zhHans,
|
||||
'zh-Hant': zhHant
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"uni-popup.cancel": "取消",
|
||||
"uni-popup.ok": "确定",
|
||||
"uni-popup.placeholder": "请输入",
|
||||
"uni-popup.title": "提示",
|
||||
"uni-popup.shareTitle": "分享到"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"uni-popup.cancel": "取消",
|
||||
"uni-popup.ok": "確定",
|
||||
"uni-popup.placeholder": "請輸入",
|
||||
"uni-popup.title": "提示",
|
||||
"uni-popup.shareTitle": "分享到"
|
||||
}
|
||||
45
uni_modules/uni-popup/components/uni-popup/keypress.js
Normal file
45
uni_modules/uni-popup/components/uni-popup/keypress.js
Normal file
@@ -0,0 +1,45 @@
|
||||
// #ifdef H5
|
||||
export default {
|
||||
name: 'Keypress',
|
||||
props: {
|
||||
disable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
const keyNames = {
|
||||
esc: ['Esc', 'Escape'],
|
||||
tab: 'Tab',
|
||||
enter: 'Enter',
|
||||
space: [' ', 'Spacebar'],
|
||||
up: ['Up', 'ArrowUp'],
|
||||
left: ['Left', 'ArrowLeft'],
|
||||
right: ['Right', 'ArrowRight'],
|
||||
down: ['Down', 'ArrowDown'],
|
||||
delete: ['Backspace', 'Delete', 'Del']
|
||||
}
|
||||
const listener = ($event) => {
|
||||
if (this.disable) {
|
||||
return
|
||||
}
|
||||
const keyName = Object.keys(keyNames).find(key => {
|
||||
const keyName = $event.key
|
||||
const value = keyNames[key]
|
||||
return value === keyName || (Array.isArray(value) && value.includes(keyName))
|
||||
})
|
||||
if (keyName) {
|
||||
// 避免和其他按键事件冲突
|
||||
setTimeout(() => {
|
||||
this.$emit(keyName, {})
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
document.addEventListener('keyup', listener)
|
||||
// this.$once('hook:beforeDestroy', () => {
|
||||
// document.removeEventListener('keyup', listener)
|
||||
// })
|
||||
},
|
||||
render: () => {}
|
||||
}
|
||||
// #endif
|
||||
26
uni_modules/uni-popup/components/uni-popup/popup.js
Normal file
26
uni_modules/uni-popup/components/uni-popup/popup.js
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.popup = this.getParent()
|
||||
},
|
||||
methods:{
|
||||
/**
|
||||
* 获取父元素实例
|
||||
*/
|
||||
getParent(name = 'uniPopup') {
|
||||
let parent = this.$parent;
|
||||
let parentName = parent.$options.name;
|
||||
while (parentName !== name) {
|
||||
parent = parent.$parent;
|
||||
if (!parent) return false
|
||||
parentName = parent.$options.name;
|
||||
}
|
||||
return parent;
|
||||
},
|
||||
}
|
||||
}
|
||||
473
uni_modules/uni-popup/components/uni-popup/uni-popup.vue
Normal file
473
uni_modules/uni-popup/components/uni-popup/uni-popup.vue
Normal file
@@ -0,0 +1,473 @@
|
||||
<template>
|
||||
<view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']">
|
||||
<view @touchstart="touchstart">
|
||||
<uni-transition key="1" v-if="maskShow" name="mask" mode-class="fade" :styles="maskClass"
|
||||
:duration="duration" :show="showTrans" @click="onTap" />
|
||||
<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration"
|
||||
:show="showTrans" @click="onTap">
|
||||
<view class="uni-popup__wrapper" :style="{ backgroundColor: bg }" :class="[popupstyle]" @click="clear">
|
||||
<slot />
|
||||
</view>
|
||||
</uni-transition>
|
||||
</view>
|
||||
<!-- #ifdef H5 -->
|
||||
<keypress v-if="maskShow" @esc="onTap" />
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef H5
|
||||
import keypress from './keypress.js'
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* PopUp 弹出层
|
||||
* @description 弹出层组件,为了解决遮罩弹层的问题
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
|
||||
* @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
|
||||
* @value top 顶部弹出
|
||||
* @value center 中间弹出
|
||||
* @value bottom 底部弹出
|
||||
* @value left 左侧弹出
|
||||
* @value right 右侧弹出
|
||||
* @value message 消息提示
|
||||
* @value dialog 对话框
|
||||
* @value share 底部分享示例
|
||||
* @property {Boolean} animation = [true|false] 是否开启动画
|
||||
* @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃)
|
||||
* @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
|
||||
* @property {String} backgroundColor 主窗口背景色
|
||||
* @property {String} maskBackgroundColor 蒙版颜色
|
||||
* @property {Boolean} safeArea 是否适配底部安全区
|
||||
* @event {Function} change 打开关闭弹窗触发,e={show: false}
|
||||
* @event {Function} maskClick 点击遮罩触发
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'uniPopup',
|
||||
components: {
|
||||
// #ifdef H5
|
||||
keypress
|
||||
// #endif
|
||||
},
|
||||
emits: ['change', 'maskClick'],
|
||||
props: {
|
||||
// 开启动画
|
||||
animation: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
|
||||
// message: 消息提示 ; dialog : 对话框
|
||||
type: {
|
||||
type: String,
|
||||
default: 'center'
|
||||
},
|
||||
// maskClick
|
||||
isMaskClick: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
},
|
||||
// TODO 2 个版本后废弃属性 ,使用 isMaskClick
|
||||
maskClick: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: 'none'
|
||||
},
|
||||
safeArea: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
maskBackgroundColor: {
|
||||
type: String,
|
||||
default: 'rgba(0, 0, 0, 0.4)'
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
/**
|
||||
* 监听type类型
|
||||
*/
|
||||
type: {
|
||||
handler: function(type) {
|
||||
if (!this.config[type]) return
|
||||
this[this.config[type]](true)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
isDesktop: {
|
||||
handler: function(newVal) {
|
||||
if (!this.config[newVal]) return
|
||||
this[this.config[this.type]](true)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
/**
|
||||
* 监听遮罩是否可点击
|
||||
* @param {Object} val
|
||||
*/
|
||||
maskClick: {
|
||||
handler: function(val) {
|
||||
this.mkclick = val
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
isMaskClick: {
|
||||
handler: function(val) {
|
||||
this.mkclick = val
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
// H5 下禁止底部滚动
|
||||
showPopup(show) {
|
||||
// #ifdef H5
|
||||
// fix by mehaotian 处理 h5 滚动穿透的问题
|
||||
document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible'
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
duration: 300,
|
||||
ani: [],
|
||||
showPopup: false,
|
||||
showTrans: false,
|
||||
popupWidth: 0,
|
||||
popupHeight: 0,
|
||||
config: {
|
||||
top: 'top',
|
||||
bottom: 'bottom',
|
||||
center: 'center',
|
||||
left: 'left',
|
||||
right: 'right',
|
||||
message: 'top',
|
||||
dialog: 'center',
|
||||
share: 'bottom'
|
||||
},
|
||||
maskClass: {
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.4)'
|
||||
},
|
||||
transClass: {
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
right: 0
|
||||
},
|
||||
maskShow: true,
|
||||
mkclick: true,
|
||||
popupstyle: this.isDesktop ? 'fixforpc-top' : 'top'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isDesktop() {
|
||||
return this.popupWidth >= 500 && this.popupHeight >= 500
|
||||
},
|
||||
bg() {
|
||||
if (this.backgroundColor === '' || this.backgroundColor === 'none') {
|
||||
return 'transparent'
|
||||
}
|
||||
return this.backgroundColor
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const fixSize = () => {
|
||||
const {
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
windowTop,
|
||||
safeArea,
|
||||
screenHeight,
|
||||
safeAreaInsets
|
||||
} = uni.getSystemInfoSync()
|
||||
this.popupWidth = windowWidth
|
||||
this.popupHeight = windowHeight + (windowTop || 0)
|
||||
// TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复
|
||||
if (safeArea && this.safeArea) {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.safeAreaInsets = screenHeight - safeArea.bottom
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
this.safeAreaInsets = safeAreaInsets.bottom
|
||||
// #endif
|
||||
} else {
|
||||
this.safeAreaInsets = 0
|
||||
}
|
||||
}
|
||||
fixSize()
|
||||
// #ifdef H5
|
||||
// window.addEventListener('resize', fixSize)
|
||||
// this.$once('hook:beforeDestroy', () => {
|
||||
// window.removeEventListener('resize', fixSize)
|
||||
// })
|
||||
// #endif
|
||||
},
|
||||
// #ifndef VUE3
|
||||
// TODO vue2
|
||||
destroyed() {
|
||||
this.setH5Visible()
|
||||
},
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
// TODO vue3
|
||||
unmounted() {
|
||||
this.setH5Visible()
|
||||
},
|
||||
// #endif
|
||||
created() {
|
||||
// this.mkclick = this.isMaskClick || this.maskClick
|
||||
if (this.isMaskClick === null && this.maskClick === null) {
|
||||
this.mkclick = true
|
||||
} else {
|
||||
this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick
|
||||
}
|
||||
if (this.animation) {
|
||||
this.duration = 300
|
||||
} else {
|
||||
this.duration = 0
|
||||
}
|
||||
// TODO 处理 message 组件生命周期异常的问题
|
||||
this.messageChild = null
|
||||
// TODO 解决头条冒泡的问题
|
||||
this.clearPropagation = false
|
||||
this.maskClass.backgroundColor = this.maskBackgroundColor
|
||||
},
|
||||
methods: {
|
||||
setH5Visible() {
|
||||
// #ifdef H5
|
||||
// fix by mehaotian 处理 h5 滚动穿透的问题
|
||||
document.getElementsByTagName('body')[0].style.overflow = 'visible'
|
||||
// #endif
|
||||
},
|
||||
/**
|
||||
* 公用方法,不显示遮罩层
|
||||
*/
|
||||
closeMask() {
|
||||
this.maskShow = false
|
||||
},
|
||||
/**
|
||||
* 公用方法,遮罩层禁止点击
|
||||
*/
|
||||
disableMask() {
|
||||
this.mkclick = false
|
||||
},
|
||||
// TODO nvue 取消冒泡
|
||||
clear(e) {
|
||||
// #ifndef APP-NVUE
|
||||
e.stopPropagation()
|
||||
// #endif
|
||||
this.clearPropagation = true
|
||||
},
|
||||
|
||||
open(direction) {
|
||||
// fix by mehaotian 处理快速打开关闭的情况
|
||||
if (this.showPopup) {
|
||||
return
|
||||
}
|
||||
let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
|
||||
if (!(direction && innerType.indexOf(direction) !== -1)) {
|
||||
direction = this.type
|
||||
}
|
||||
if (!this.config[direction]) {
|
||||
console.error('缺少类型:', direction)
|
||||
return
|
||||
}
|
||||
this[this.config[direction]]()
|
||||
this.$emit('change', {
|
||||
show: true,
|
||||
type: direction
|
||||
})
|
||||
},
|
||||
close(type) {
|
||||
this.showTrans = false
|
||||
this.$emit('change', {
|
||||
show: false,
|
||||
type: this.type
|
||||
})
|
||||
clearTimeout(this.timer)
|
||||
// // 自定义关闭事件
|
||||
// this.customOpen && this.customClose()
|
||||
this.timer = setTimeout(() => {
|
||||
this.showPopup = false
|
||||
}, 300)
|
||||
},
|
||||
// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
|
||||
touchstart() {
|
||||
this.clearPropagation = false
|
||||
},
|
||||
|
||||
onTap() {
|
||||
if (this.clearPropagation) {
|
||||
// fix by mehaotian 兼容 nvue
|
||||
this.clearPropagation = false
|
||||
return
|
||||
}
|
||||
this.$emit('maskClick')
|
||||
if (!this.mkclick) return
|
||||
this.close()
|
||||
},
|
||||
/**
|
||||
* 顶部弹出样式处理
|
||||
*/
|
||||
top(type) {
|
||||
this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top'
|
||||
this.ani = ['slide-top']
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: this.bg
|
||||
}
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return
|
||||
this.showPopup = true
|
||||
this.showTrans = true
|
||||
this.$nextTick(() => {
|
||||
if (this.messageChild && this.type === 'message') {
|
||||
this.messageChild.timerClose()
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 底部弹出样式处理
|
||||
*/
|
||||
bottom(type) {
|
||||
this.popupstyle = 'bottom'
|
||||
this.ani = ['slide-bottom']
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
paddingBottom: this.safeAreaInsets + 'px',
|
||||
backgroundColor: this.bg
|
||||
}
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return
|
||||
this.showPopup = true
|
||||
this.showTrans = true
|
||||
},
|
||||
/**
|
||||
* 中间弹出样式处理
|
||||
*/
|
||||
center(type) {
|
||||
this.popupstyle = 'center'
|
||||
this.ani = ['zoom-out', 'fade']
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
/* #ifndef APP-NVUE */
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
/* #endif */
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
}
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return
|
||||
this.showPopup = true
|
||||
this.showTrans = true
|
||||
},
|
||||
left(type) {
|
||||
this.popupstyle = 'left'
|
||||
this.ani = ['slide-left']
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
backgroundColor: this.bg,
|
||||
/* #ifndef APP-NVUE */
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
/* #endif */
|
||||
}
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return
|
||||
this.showPopup = true
|
||||
this.showTrans = true
|
||||
},
|
||||
right(type) {
|
||||
this.popupstyle = 'right'
|
||||
this.ani = ['slide-right']
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
backgroundColor: this.bg,
|
||||
/* #ifndef APP-NVUE */
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
/* #endif */
|
||||
}
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return
|
||||
this.showPopup = true
|
||||
this.showTrans = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.uni-popup {
|
||||
position: fixed;
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 99;
|
||||
|
||||
/* #endif */
|
||||
&.top,
|
||||
&.left,
|
||||
&.right {
|
||||
/* #ifdef H5 */
|
||||
top: var(--window-top);
|
||||
/* #endif */
|
||||
/* #ifndef H5 */
|
||||
top: 0;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-popup__wrapper {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
|
||||
/* iphonex 等安全区设置,底部安全区适配 */
|
||||
/* #ifndef APP-NVUE */
|
||||
// padding-bottom: constant(safe-area-inset-bottom);
|
||||
// padding-bottom: env(safe-area-inset-bottom);
|
||||
/* #endif */
|
||||
&.left,
|
||||
&.right {
|
||||
/* #ifdef H5 */
|
||||
padding-top: var(--window-top);
|
||||
/* #endif */
|
||||
/* #ifndef H5 */
|
||||
padding-top: 0;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fixforpc-z-index {
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 999;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.fixforpc-top {
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
||||
87
uni_modules/uni-popup/package.json
Normal file
87
uni_modules/uni-popup/package.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"id": "uni-popup",
|
||||
"displayName": "uni-popup 弹出层",
|
||||
"version": "1.8.3",
|
||||
"description": " Popup 组件,提供常用的弹层",
|
||||
"keywords": [
|
||||
"uni-ui",
|
||||
"弹出层",
|
||||
"弹窗",
|
||||
"popup",
|
||||
"弹框"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": ""
|
||||
},
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
|
||||
"type": "component-vue"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"uni-scss",
|
||||
"uni-transition"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
uni_modules/uni-popup/readme.md
Normal file
17
uni_modules/uni-popup/readme.md
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
## Popup 弹出层
|
||||
> **组件名:uni-popup**
|
||||
> 代码块: `uPopup`
|
||||
> 关联组件:`uni-transition`
|
||||
|
||||
|
||||
弹出层组件,在应用中弹出一个消息提示窗口、提示框等
|
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup)
|
||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
20
uni_modules/uni-rate/changelog.md
Normal file
20
uni_modules/uni-rate/changelog.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## 1.2.2(2021-09-10)
|
||||
- 优化 默认值修改为 0 颗星
|
||||
## 1.2.1(2021-07-30)
|
||||
- 优化 vue3下事件警告的问题
|
||||
## 1.2.0(2021-07-13)
|
||||
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||
## 1.1.2(2021-05-12)
|
||||
- 新增 组件示例地址
|
||||
## 1.1.1(2021-04-21)
|
||||
- 修复 布局变化后 uni-rate 星星计算不准确的 bug
|
||||
- 优化 添加依赖 uni-icons, 导入 uni-rate 自动下载依赖
|
||||
## 1.1.0(2021-04-16)
|
||||
- 修复 uni-rate 属性 margin 值为 string 组件失效的 bug
|
||||
|
||||
## 1.0.9(2021-02-05)
|
||||
- 优化 组件引用关系,通过uni_modules引用组件
|
||||
|
||||
## 1.0.8(2021-02-05)
|
||||
- 调整为uni_modules目录规范
|
||||
- 支持 pc 端
|
||||
393
uni_modules/uni-rate/components/uni-rate/uni-rate.vue
Normal file
393
uni_modules/uni-rate/components/uni-rate/uni-rate.vue
Normal file
@@ -0,0 +1,393 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
ref="uni-rate"
|
||||
class="uni-rate"
|
||||
>
|
||||
<view
|
||||
class="uni-rate__icon"
|
||||
:class="{'uni-cursor-not-allowed': disabled}"
|
||||
:style="{ 'margin-right': marginNumber + 'px' }"
|
||||
v-for="(star, index) in stars"
|
||||
:key="index"
|
||||
@touchstart.stop="touchstart"
|
||||
@touchmove.stop="touchmove"
|
||||
@mousedown.stop="mousedown"
|
||||
@mousemove.stop="mousemove"
|
||||
@mouseleave="mouseleave"
|
||||
>
|
||||
<uni-icons
|
||||
:color="color"
|
||||
:size="size"
|
||||
:type="isFill ? 'star-filled' : 'star'"
|
||||
/>
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<view
|
||||
:style="{ width: star.activeWitch.replace('%','')*size/100+'px'}"
|
||||
class="uni-rate__icon-on"
|
||||
>
|
||||
<uni-icons
|
||||
style="text-align: left;"
|
||||
:color="disabled?'#ccc':activeColor"
|
||||
:size="size"
|
||||
type="star-filled"
|
||||
/>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view
|
||||
:style="{ width: star.activeWitch}"
|
||||
class="uni-rate__icon-on"
|
||||
>
|
||||
<uni-icons
|
||||
:color="disabled?disabledColor:activeColor"
|
||||
:size="size"
|
||||
type="star-filled"
|
||||
/>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef APP-NVUE
|
||||
const dom = uni.requireNativePlugin('dom');
|
||||
// #endif
|
||||
/**
|
||||
* Rate 评分
|
||||
* @description 评分组件
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=33
|
||||
* @property {Boolean} isFill = [true|false] 星星的类型,是否为实心类型, 默认为实心
|
||||
* @property {String} color 未选中状态的星星颜色,默认为 "#ececec"
|
||||
* @property {String} activeColor 选中状态的星星颜色,默认为 "#ffca3e"
|
||||
* @property {String} disabledColor 禁用状态的星星颜色,默认为 "#c0c0c0"
|
||||
* @property {Number} size 星星的大小
|
||||
* @property {Number} value/v-model 当前评分
|
||||
* @property {Number} max 最大评分评分数量,目前一分一颗星
|
||||
* @property {Number} margin 星星的间距,单位 px
|
||||
* @property {Boolean} disabled = [true|false] 是否为禁用状态,默认为 false
|
||||
* @property {Boolean} readonly = [true|false] 是否为只读状态,默认为 false
|
||||
* @property {Boolean} allowHalf = [true|false] 是否实现半星,默认为 false
|
||||
* @property {Boolean} touchable = [true|false] 是否支持滑动手势,默认为 true
|
||||
* @event {Function} change uniRate 的 value 改变时触发事件,e={value:Number}
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: "UniRate",
|
||||
props: {
|
||||
isFill: {
|
||||
// 星星的类型,是否镂空
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
color: {
|
||||
// 星星未选中的颜色
|
||||
type: String,
|
||||
default: "#ececec"
|
||||
},
|
||||
activeColor: {
|
||||
// 星星选中状态颜色
|
||||
type: String,
|
||||
default: "#ffca3e"
|
||||
},
|
||||
disabledColor: {
|
||||
// 星星禁用状态颜色
|
||||
type: String,
|
||||
default: "#c0c0c0"
|
||||
},
|
||||
size: {
|
||||
// 星星的大小
|
||||
type: [Number, String],
|
||||
default: 24
|
||||
},
|
||||
value: {
|
||||
// 当前评分
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
modelValue: {
|
||||
// 当前评分
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
max: {
|
||||
// 最大评分
|
||||
type: [Number, String],
|
||||
default: 5
|
||||
},
|
||||
margin: {
|
||||
// 星星的间距
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
disabled: {
|
||||
// 是否可点击
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
readonly: {
|
||||
// 是否只读
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
allowHalf: {
|
||||
// 是否显示半星
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
touchable: {
|
||||
// 是否支持滑动手势
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
valueSync: "",
|
||||
userMouseFristMove: true,
|
||||
userRated: false,
|
||||
userLastRate: 1
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.valueSync = Number(newVal);
|
||||
},
|
||||
modelValue(newVal) {
|
||||
this.valueSync = Number(newVal);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
stars() {
|
||||
const value = this.valueSync ? this.valueSync : 0;
|
||||
const starList = [];
|
||||
const floorValue = Math.floor(value);
|
||||
const ceilValue = Math.ceil(value);
|
||||
for (let i = 0; i < this.max; i++) {
|
||||
if (floorValue > i) {
|
||||
starList.push({
|
||||
activeWitch: "100%"
|
||||
});
|
||||
} else if (ceilValue - 1 === i) {
|
||||
starList.push({
|
||||
activeWitch: (value - floorValue) * 100 + "%"
|
||||
});
|
||||
} else {
|
||||
starList.push({
|
||||
activeWitch: "0"
|
||||
});
|
||||
}
|
||||
}
|
||||
return starList;
|
||||
},
|
||||
|
||||
marginNumber() {
|
||||
return Number(this.margin)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.valueSync = Number(this.value||this.modelValue);
|
||||
this._rateBoxLeft = 0
|
||||
this._oldValue = null
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => {
|
||||
this._getSize()
|
||||
}, 100)
|
||||
// #ifdef H5
|
||||
this.PC = this.IsPC()
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
touchstart(e) {
|
||||
// #ifdef H5
|
||||
if( this.IsPC() ) return
|
||||
// #endif
|
||||
if (this.readonly || this.disabled) return
|
||||
const {
|
||||
clientX,
|
||||
screenX
|
||||
} = e.changedTouches[0]
|
||||
// TODO 做一下兼容,只有 Nvue 下才有 screenX,其他平台式 clientX
|
||||
this._getRateCount(clientX || screenX)
|
||||
},
|
||||
touchmove(e) {
|
||||
// #ifdef H5
|
||||
if( this.IsPC() ) return
|
||||
// #endif
|
||||
if (this.readonly || this.disabled || !this.touchable) return
|
||||
const {
|
||||
clientX,
|
||||
screenX
|
||||
} = e.changedTouches[0]
|
||||
this._getRateCount(clientX || screenX)
|
||||
},
|
||||
|
||||
/**
|
||||
* 兼容 PC @tian
|
||||
*/
|
||||
|
||||
mousedown(e) {
|
||||
// #ifdef H5
|
||||
if( !this.IsPC() ) return
|
||||
if (this.readonly || this.disabled) return
|
||||
const {
|
||||
clientX,
|
||||
} = e
|
||||
this.userLastRate = this.valueSync
|
||||
this._getRateCount(clientX)
|
||||
this.userRated = true
|
||||
// #endif
|
||||
},
|
||||
mousemove(e) {
|
||||
// #ifdef H5
|
||||
if( !this.IsPC() ) return
|
||||
if( this.userRated ) return
|
||||
if( this.userMouseFristMove ) {
|
||||
console.log('---mousemove----', this.valueSync);
|
||||
this.userLastRate = this.valueSync
|
||||
this.userMouseFristMove = false
|
||||
}
|
||||
if (this.readonly || this.disabled || !this.touchable) return
|
||||
const {
|
||||
clientX,
|
||||
} = e
|
||||
this._getRateCount(clientX)
|
||||
// #endif
|
||||
},
|
||||
mouseleave(e) {
|
||||
// #ifdef H5
|
||||
if( !this.IsPC() ) return
|
||||
if (this.readonly || this.disabled || !this.touchable) return
|
||||
if( this.userRated ) {
|
||||
this.userRated = false
|
||||
return
|
||||
}
|
||||
this.valueSync = this.userLastRate
|
||||
// #endif
|
||||
},
|
||||
// #ifdef H5
|
||||
IsPC() {
|
||||
var userAgentInfo = navigator.userAgent;
|
||||
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
|
||||
var flag = true;
|
||||
for (let v = 0; v < Agents.length - 1; v++) {
|
||||
if (userAgentInfo.indexOf(Agents[v]) > 0) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
},
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 获取星星个数
|
||||
*/
|
||||
_getRateCount(clientX) {
|
||||
this._getSize()
|
||||
const size = Number(this.size)
|
||||
if(size === NaN){
|
||||
return new Error('size 属性只能设置为数字')
|
||||
}
|
||||
const rateMoveRange = clientX - this._rateBoxLeft
|
||||
let index = parseInt(rateMoveRange / (size + this.marginNumber))
|
||||
index = index < 0 ? 0 : index;
|
||||
index = index > this.max ? this.max : index;
|
||||
const range = parseInt(rateMoveRange - (size + this.marginNumber) * index);
|
||||
let value = 0;
|
||||
if (this._oldValue === index && !this.PC) return;
|
||||
this._oldValue = index;
|
||||
if (this.allowHalf) {
|
||||
if (range > (size / 2)) {
|
||||
value = index + 1
|
||||
} else {
|
||||
value = index + 0.5
|
||||
}
|
||||
} else {
|
||||
value = index + 1
|
||||
}
|
||||
|
||||
value = Math.max(0.5, Math.min(value, this.max))
|
||||
this.valueSync = value
|
||||
this._onChange()
|
||||
},
|
||||
|
||||
/**
|
||||
* 触发动态修改
|
||||
*/
|
||||
_onChange() {
|
||||
|
||||
this.$emit("input", this.valueSync);
|
||||
this.$emit("update:modelValue", this.valueSync);
|
||||
this.$emit("change", {
|
||||
value: this.valueSync
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 获取星星距离屏幕左侧距离
|
||||
*/
|
||||
_getSize() {
|
||||
// #ifndef APP-NVUE
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select('.uni-rate')
|
||||
.boundingClientRect()
|
||||
.exec(ret => {
|
||||
if (ret) {
|
||||
this._rateBoxLeft = ret[0].left
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
dom.getComponentRect(this.$refs['uni-rate'], (ret) => {
|
||||
const size = ret.size
|
||||
if (size) {
|
||||
this._rateBoxLeft = size.left
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style
|
||||
lang="scss"
|
||||
scoped
|
||||
>
|
||||
.uni-rate {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
line-height: 1;
|
||||
font-size: 0;
|
||||
flex-direction: row;
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-rate__icon {
|
||||
position: relative;
|
||||
line-height: 1;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.uni-rate__icon-on {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
line-height: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.uni-cursor-not-allowed {
|
||||
/* #ifdef H5 */
|
||||
cursor: not-allowed !important;
|
||||
/* #endif */
|
||||
}
|
||||
</style>
|
||||
87
uni_modules/uni-rate/package.json
Normal file
87
uni_modules/uni-rate/package.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"id": "uni-rate",
|
||||
"displayName": "uni-rate 评分",
|
||||
"version": "1.2.2",
|
||||
"description": "Rate 评分组件,可自定义评分星星图标的大小、间隔、评分数。",
|
||||
"keywords": [
|
||||
"uni-ui",
|
||||
"uniui",
|
||||
"评分"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": ""
|
||||
},
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"uni-icons"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
uni_modules/uni-rate/readme.md
Normal file
107
uni_modules/uni-rate/readme.md
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
|
||||
## Rate 评分
|
||||
> **组件名:uni-rate**
|
||||
> 代码块: `uRate`
|
||||
> 关联组件:`uni-icons`
|
||||
|
||||
|
||||
评分组件,多用于购买商品后,对商品进行评价等场景
|
||||
|
||||
> **注意事项**
|
||||
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的使用说明,可以帮你避免一些错误。
|
||||
> - 暂时不支持零星选择
|
||||
> - 当前版本暂不支持修改图标,后续版本会继续优化
|
||||
> - 绑定值推荐使用 `v-model` 的方式
|
||||
> - 如需设置一个星星表示多分,如:显示5个星星,最高分10分。这种情况请在 change 事件监听中自行处理,获取到的值乘以你的基数就可以,默认组件是一星一分
|
||||
|
||||
|
||||
### 安装方式
|
||||
|
||||
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
|
||||
|
||||
如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
|
||||
|
||||
|
||||
## 基本用法
|
||||
|
||||
```html
|
||||
<!-- 基本用法 -->
|
||||
<!-- 需要在 script 中绑定 value 变量 -->
|
||||
<uni-rate v-model="value" @change="onChange"/>
|
||||
|
||||
<!-- 不支持滑动手势选择评分 -->
|
||||
<uni-rate :touchable="false" :value="5"/>
|
||||
<!-- 设置尺寸大小 -->
|
||||
<uni-rate :size="18" :value="5"/>
|
||||
|
||||
<!-- 设置评分数 -->
|
||||
<uni-rate :max="10" :value="5" />
|
||||
|
||||
<!-- 设置星星间隔 -->
|
||||
<uni-rate :value="4" :margin="20" />
|
||||
|
||||
<!-- 设置颜色 -->
|
||||
<uni-rate :value="3" color="#bbb" active-color="red" />
|
||||
|
||||
<!-- 选择半星 -->
|
||||
<uni-rate allow-half :value="3.5" />
|
||||
|
||||
<!-- 只读状态 -->
|
||||
<uni-rate :readonly="true" :value="2" />
|
||||
|
||||
<!-- 禁用状态 -->
|
||||
<uni-rate :disabled="true" disabledColor="#ccc" :value="3" />
|
||||
|
||||
<!-- 未选中的星星为镂空状态 -->
|
||||
<uni-rate :value="3" :is-fill="false" />
|
||||
|
||||
|
||||
```
|
||||
|
||||
```javascript
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
value: 2
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(e) {
|
||||
console.log('rate发生改变:' + JSON.stringify(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## API
|
||||
### Rate Props
|
||||
|
||||
属性名 | 类型 | 默认值 | 说明
|
||||
:-: | :-: | :-: | :-:
|
||||
value/v-model | Number | 0 | 当前评分
|
||||
color | String | #ececec | 未选中状态的星星颜色
|
||||
activeColor | String | #ffca3e | 选中状态的星星颜色
|
||||
disabledColor | String | #c0c0c0 | 禁用状态的星星颜色
|
||||
size | Number | 24 | 星星的大小
|
||||
max | Number | 5 | 最大评分评分数量,目前一分一颗星
|
||||
margin | Number | 0 | 星星的间距,单位 px
|
||||
isFill | Boolean | true | 星星的类型,是否为实心类型
|
||||
disabled | Boolean | false | 是否为禁用状态 (之前版本为已读状态,现更正为禁用状态)
|
||||
readonly | Boolean | false | 是否为只读状态
|
||||
allowHalf | Boolean | false | 是否展示半星
|
||||
touchable | Boolean | true | 是否支持滑动手势
|
||||
|
||||
### Rate Events
|
||||
|
||||
事件称名 | 说明 | 返回参数
|
||||
:-: | :-: | :-:
|
||||
@change | 改变 value 的值返回 | e = { value:number }
|
||||
|
||||
|
||||
## 组件示例
|
||||
|
||||
点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/rate/rate](https://hellouniapp.dcloud.net.cn/pages/extUI/rate/rate)
|
||||
8
uni_modules/uni-scss/changelog.md
Normal file
8
uni_modules/uni-scss/changelog.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## 1.0.3(2022-01-21)
|
||||
- 优化 组件示例
|
||||
## 1.0.2(2021-11-22)
|
||||
- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题
|
||||
## 1.0.1(2021-11-22)
|
||||
- 修复 vue3中scss语法兼容问题
|
||||
## 1.0.0(2021-11-18)
|
||||
- init
|
||||
1
uni_modules/uni-scss/index.scss
Normal file
1
uni_modules/uni-scss/index.scss
Normal file
@@ -0,0 +1 @@
|
||||
@import './styles/index.scss';
|
||||
82
uni_modules/uni-scss/package.json
Normal file
82
uni_modules/uni-scss/package.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"id": "uni-scss",
|
||||
"displayName": "uni-scss 辅助样式",
|
||||
"version": "1.0.3",
|
||||
"description": "uni-sass是uni-ui提供的一套全局样式 ,通过一些简单的类名和sass变量,实现简单的页面布局操作,比如颜色、边距、圆角等。",
|
||||
"keywords": [
|
||||
"uni-scss",
|
||||
"uni-ui",
|
||||
"辅助样式"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"JS SDK",
|
||||
"通用 SDK"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "n",
|
||||
"联盟": "n"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
uni_modules/uni-scss/readme.md
Normal file
4
uni_modules/uni-scss/readme.md
Normal file
@@ -0,0 +1,4 @@
|
||||
`uni-sass` 是 `uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。
|
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass)
|
||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||
7
uni_modules/uni-scss/styles/index.scss
Normal file
7
uni_modules/uni-scss/styles/index.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
@import './setting/_variables.scss';
|
||||
@import './setting/_border.scss';
|
||||
@import './setting/_color.scss';
|
||||
@import './setting/_space.scss';
|
||||
@import './setting/_radius.scss';
|
||||
@import './setting/_text.scss';
|
||||
@import './setting/_styles.scss';
|
||||
3
uni_modules/uni-scss/styles/setting/_border.scss
Normal file
3
uni_modules/uni-scss/styles/setting/_border.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.uni-border {
|
||||
border: 1px $uni-border-1 solid;
|
||||
}
|
||||
66
uni_modules/uni-scss/styles/setting/_color.scss
Normal file
66
uni_modules/uni-scss/styles/setting/_color.scss
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
// TODO 暂时不需要 class ,需要用户使用变量实现 ,如果使用类名其实并不推荐
|
||||
// @mixin get-styles($k,$c) {
|
||||
// @if $k == size or $k == weight{
|
||||
// font-#{$k}:#{$c}
|
||||
// }@else{
|
||||
// #{$k}:#{$c}
|
||||
// }
|
||||
// }
|
||||
$uni-ui-color:(
|
||||
// 主色
|
||||
primary: $uni-primary,
|
||||
primary-disable: $uni-primary-disable,
|
||||
primary-light: $uni-primary-light,
|
||||
// 辅助色
|
||||
success: $uni-success,
|
||||
success-disable: $uni-success-disable,
|
||||
success-light: $uni-success-light,
|
||||
warning: $uni-warning,
|
||||
warning-disable: $uni-warning-disable,
|
||||
warning-light: $uni-warning-light,
|
||||
error: $uni-error,
|
||||
error-disable: $uni-error-disable,
|
||||
error-light: $uni-error-light,
|
||||
info: $uni-info,
|
||||
info-disable: $uni-info-disable,
|
||||
info-light: $uni-info-light,
|
||||
// 中性色
|
||||
main-color: $uni-main-color,
|
||||
base-color: $uni-base-color,
|
||||
secondary-color: $uni-secondary-color,
|
||||
extra-color: $uni-extra-color,
|
||||
// 背景色
|
||||
bg-color: $uni-bg-color,
|
||||
// 边框颜色
|
||||
border-1: $uni-border-1,
|
||||
border-2: $uni-border-2,
|
||||
border-3: $uni-border-3,
|
||||
border-4: $uni-border-4,
|
||||
// 黑色
|
||||
black:$uni-black,
|
||||
// 白色
|
||||
white:$uni-white,
|
||||
// 透明
|
||||
transparent:$uni-transparent
|
||||
) !default;
|
||||
@each $key, $child in $uni-ui-color {
|
||||
.uni-#{"" + $key} {
|
||||
color: $child;
|
||||
}
|
||||
.uni-#{"" + $key}-bg {
|
||||
background-color: $child;
|
||||
}
|
||||
}
|
||||
.uni-shadow-sm {
|
||||
box-shadow: $uni-shadow-sm;
|
||||
}
|
||||
.uni-shadow-base {
|
||||
box-shadow: $uni-shadow-base;
|
||||
}
|
||||
.uni-shadow-lg {
|
||||
box-shadow: $uni-shadow-lg;
|
||||
}
|
||||
.uni-mask {
|
||||
background-color:$uni-mask;
|
||||
}
|
||||
55
uni_modules/uni-scss/styles/setting/_radius.scss
Normal file
55
uni_modules/uni-scss/styles/setting/_radius.scss
Normal file
@@ -0,0 +1,55 @@
|
||||
@mixin radius($r,$d:null ,$important: false){
|
||||
$radius-value:map-get($uni-radius, $r) if($important, !important, null);
|
||||
// Key exists within the $uni-radius variable
|
||||
@if (map-has-key($uni-radius, $r) and $d){
|
||||
@if $d == t {
|
||||
border-top-left-radius:$radius-value;
|
||||
border-top-right-radius:$radius-value;
|
||||
}@else if $d == r {
|
||||
border-top-right-radius:$radius-value;
|
||||
border-bottom-right-radius:$radius-value;
|
||||
}@else if $d == b {
|
||||
border-bottom-left-radius:$radius-value;
|
||||
border-bottom-right-radius:$radius-value;
|
||||
}@else if $d == l {
|
||||
border-top-left-radius:$radius-value;
|
||||
border-bottom-left-radius:$radius-value;
|
||||
}@else if $d == tl {
|
||||
border-top-left-radius:$radius-value;
|
||||
}@else if $d == tr {
|
||||
border-top-right-radius:$radius-value;
|
||||
}@else if $d == br {
|
||||
border-bottom-right-radius:$radius-value;
|
||||
}@else if $d == bl {
|
||||
border-bottom-left-radius:$radius-value;
|
||||
}
|
||||
}@else{
|
||||
border-radius:$radius-value;
|
||||
}
|
||||
}
|
||||
|
||||
@each $key, $child in $uni-radius {
|
||||
@if($key){
|
||||
.uni-radius-#{"" + $key} {
|
||||
@include radius($key)
|
||||
}
|
||||
}@else{
|
||||
.uni-radius {
|
||||
@include radius($key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@each $direction in t, r, b, l,tl, tr, br, bl {
|
||||
@each $key, $child in $uni-radius {
|
||||
@if($key){
|
||||
.uni-radius-#{"" + $direction}-#{"" + $key} {
|
||||
@include radius($key,$direction,false)
|
||||
}
|
||||
}@else{
|
||||
.uni-radius-#{$direction} {
|
||||
@include radius($key,$direction,false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
uni_modules/uni-scss/styles/setting/_space.scss
Normal file
56
uni_modules/uni-scss/styles/setting/_space.scss
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
@mixin fn($space,$direction,$size,$n) {
|
||||
@if $n {
|
||||
#{$space}-#{$direction}: #{$size*$uni-space-root}px
|
||||
} @else {
|
||||
#{$space}-#{$direction}: #{-$size*$uni-space-root}px
|
||||
}
|
||||
}
|
||||
@mixin get-styles($direction,$i,$space,$n){
|
||||
@if $direction == t {
|
||||
@include fn($space, top,$i,$n);
|
||||
}
|
||||
@if $direction == r {
|
||||
@include fn($space, right,$i,$n);
|
||||
}
|
||||
@if $direction == b {
|
||||
@include fn($space, bottom,$i,$n);
|
||||
}
|
||||
@if $direction == l {
|
||||
@include fn($space, left,$i,$n);
|
||||
}
|
||||
@if $direction == x {
|
||||
@include fn($space, left,$i,$n);
|
||||
@include fn($space, right,$i,$n);
|
||||
}
|
||||
@if $direction == y {
|
||||
@include fn($space, top,$i,$n);
|
||||
@include fn($space, bottom,$i,$n);
|
||||
}
|
||||
@if $direction == a {
|
||||
@if $n {
|
||||
#{$space}:#{$i*$uni-space-root}px;
|
||||
} @else {
|
||||
#{$space}:#{-$i*$uni-space-root}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@each $orientation in m,p {
|
||||
$space: margin;
|
||||
@if $orientation == m {
|
||||
$space: margin;
|
||||
} @else {
|
||||
$space: padding;
|
||||
}
|
||||
@for $i from 0 through 16 {
|
||||
@each $direction in t, r, b, l, x, y, a {
|
||||
.uni-#{$orientation}#{$direction}-#{$i} {
|
||||
@include get-styles($direction,$i,$space,true);
|
||||
}
|
||||
.uni-#{$orientation}#{$direction}-n#{$i} {
|
||||
@include get-styles($direction,$i,$space,false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
167
uni_modules/uni-scss/styles/setting/_styles.scss
Normal file
167
uni_modules/uni-scss/styles/setting/_styles.scss
Normal file
@@ -0,0 +1,167 @@
|
||||
/* #ifndef APP-NVUE */
|
||||
|
||||
$-color-white:#fff;
|
||||
$-color-black:#000;
|
||||
@mixin base-style($color) {
|
||||
color: #fff;
|
||||
background-color: $color;
|
||||
border-color: mix($-color-black, $color, 8%);
|
||||
&:not([hover-class]):active {
|
||||
background: mix($-color-black, $color, 10%);
|
||||
border-color: mix($-color-black, $color, 20%);
|
||||
color: $-color-white;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
@mixin is-color($color) {
|
||||
@include base-style($color);
|
||||
&[loading] {
|
||||
@include base-style($color);
|
||||
&::before {
|
||||
margin-right:5px;
|
||||
}
|
||||
}
|
||||
&[disabled] {
|
||||
&,
|
||||
&[loading],
|
||||
&:not([hover-class]):active {
|
||||
color: $-color-white;
|
||||
border-color: mix(darken($color,10%), $-color-white);
|
||||
background-color: mix($color, $-color-white);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@mixin base-plain-style($color) {
|
||||
color:$color;
|
||||
background-color: mix($-color-white, $color, 90%);
|
||||
border-color: mix($-color-white, $color, 70%);
|
||||
&:not([hover-class]):active {
|
||||
background: mix($-color-white, $color, 80%);
|
||||
color: $color;
|
||||
outline: none;
|
||||
border-color: mix($-color-white, $color, 50%);
|
||||
}
|
||||
}
|
||||
@mixin is-plain($color){
|
||||
&[plain] {
|
||||
@include base-plain-style($color);
|
||||
&[loading] {
|
||||
@include base-plain-style($color);
|
||||
&::before {
|
||||
margin-right:5px;
|
||||
}
|
||||
}
|
||||
&[disabled] {
|
||||
&,
|
||||
&:active {
|
||||
color: mix($-color-white, $color, 40%);
|
||||
background-color: mix($-color-white, $color, 90%);
|
||||
border-color: mix($-color-white, $color, 80%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.uni-btn {
|
||||
margin: 5px;
|
||||
color: #393939;
|
||||
border:1px solid #ccc;
|
||||
font-size: 16px;
|
||||
font-weight: 200;
|
||||
background-color: #F9F9F9;
|
||||
// TODO 暂时处理边框隐藏一边的问题
|
||||
overflow: visible;
|
||||
&::after{
|
||||
border: none;
|
||||
}
|
||||
|
||||
&:not([type]),&[type=default] {
|
||||
color: #999;
|
||||
&[loading] {
|
||||
background: none;
|
||||
&::before {
|
||||
margin-right:5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
&[disabled]{
|
||||
color: mix($-color-white, #999, 60%);
|
||||
&,
|
||||
&[loading],
|
||||
&:active {
|
||||
color: mix($-color-white, #999, 60%);
|
||||
background-color: mix($-color-white,$-color-black , 98%);
|
||||
border-color: mix($-color-white, #999, 85%);
|
||||
}
|
||||
}
|
||||
|
||||
&[plain] {
|
||||
color: #999;
|
||||
background: none;
|
||||
border-color: $uni-border-1;
|
||||
&:not([hover-class]):active {
|
||||
background: none;
|
||||
color: mix($-color-white, $-color-black, 80%);
|
||||
border-color: mix($-color-white, $-color-black, 90%);
|
||||
outline: none;
|
||||
}
|
||||
&[disabled]{
|
||||
&,
|
||||
&[loading],
|
||||
&:active {
|
||||
background: none;
|
||||
color: mix($-color-white, #999, 60%);
|
||||
border-color: mix($-color-white, #999, 85%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not([hover-class]):active {
|
||||
color: mix($-color-white, $-color-black, 50%);
|
||||
}
|
||||
|
||||
&[size=mini] {
|
||||
font-size: 16px;
|
||||
font-weight: 200;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
&.uni-btn-small {
|
||||
font-size: 14px;
|
||||
}
|
||||
&.uni-btn-mini {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&.uni-btn-radius {
|
||||
border-radius: 999px;
|
||||
}
|
||||
&[type=primary] {
|
||||
@include is-color($uni-primary);
|
||||
@include is-plain($uni-primary)
|
||||
}
|
||||
&[type=success] {
|
||||
@include is-color($uni-success);
|
||||
@include is-plain($uni-success)
|
||||
}
|
||||
&[type=error] {
|
||||
@include is-color($uni-error);
|
||||
@include is-plain($uni-error)
|
||||
}
|
||||
&[type=warning] {
|
||||
@include is-color($uni-warning);
|
||||
@include is-plain($uni-warning)
|
||||
}
|
||||
&[type=info] {
|
||||
@include is-color($uni-info);
|
||||
@include is-plain($uni-info)
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
24
uni_modules/uni-scss/styles/setting/_text.scss
Normal file
24
uni_modules/uni-scss/styles/setting/_text.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
@mixin get-styles($k,$c) {
|
||||
@if $k == size or $k == weight{
|
||||
font-#{$k}:#{$c}
|
||||
}@else{
|
||||
#{$k}:#{$c}
|
||||
}
|
||||
}
|
||||
|
||||
@each $key, $child in $uni-headings {
|
||||
/* #ifndef APP-NVUE */
|
||||
.uni-#{$key} {
|
||||
@each $k, $c in $child {
|
||||
@include get-styles($k,$c)
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
.container .uni-#{$key} {
|
||||
@each $k, $c in $child {
|
||||
@include get-styles($k,$c)
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
}
|
||||
146
uni_modules/uni-scss/styles/setting/_variables.scss
Normal file
146
uni_modules/uni-scss/styles/setting/_variables.scss
Normal file
@@ -0,0 +1,146 @@
|
||||
// @use "sass:math";
|
||||
@import '../tools/functions.scss';
|
||||
// 间距基础倍数
|
||||
$uni-space-root: 2 !default;
|
||||
// 边框半径默认值
|
||||
$uni-radius-root:5px !default;
|
||||
$uni-radius: () !default;
|
||||
// 边框半径断点
|
||||
$uni-radius: map-deep-merge(
|
||||
(
|
||||
0: 0,
|
||||
// TODO 当前版本暂时不支持 sm 属性
|
||||
// 'sm': math.div($uni-radius-root, 2),
|
||||
null: $uni-radius-root,
|
||||
'lg': $uni-radius-root * 2,
|
||||
'xl': $uni-radius-root * 6,
|
||||
'pill': 9999px,
|
||||
'circle': 50%
|
||||
),
|
||||
$uni-radius
|
||||
);
|
||||
// 字体家族
|
||||
$body-font-family: 'Roboto', sans-serif !default;
|
||||
// 文本
|
||||
$heading-font-family: $body-font-family !default;
|
||||
$uni-headings: () !default;
|
||||
$letterSpacing: -0.01562em;
|
||||
$uni-headings: map-deep-merge(
|
||||
(
|
||||
'h1': (
|
||||
size: 32px,
|
||||
weight: 300,
|
||||
line-height: 50px,
|
||||
// letter-spacing:-0.01562em
|
||||
),
|
||||
'h2': (
|
||||
size: 28px,
|
||||
weight: 300,
|
||||
line-height: 40px,
|
||||
// letter-spacing: -0.00833em
|
||||
),
|
||||
'h3': (
|
||||
size: 24px,
|
||||
weight: 400,
|
||||
line-height: 32px,
|
||||
// letter-spacing: normal
|
||||
),
|
||||
'h4': (
|
||||
size: 20px,
|
||||
weight: 400,
|
||||
line-height: 30px,
|
||||
// letter-spacing: 0.00735em
|
||||
),
|
||||
'h5': (
|
||||
size: 16px,
|
||||
weight: 400,
|
||||
line-height: 24px,
|
||||
// letter-spacing: normal
|
||||
),
|
||||
'h6': (
|
||||
size: 14px,
|
||||
weight: 500,
|
||||
line-height: 18px,
|
||||
// letter-spacing: 0.0125em
|
||||
),
|
||||
'subtitle': (
|
||||
size: 12px,
|
||||
weight: 400,
|
||||
line-height: 20px,
|
||||
// letter-spacing: 0.00937em
|
||||
),
|
||||
'body': (
|
||||
font-size: 14px,
|
||||
font-weight: 400,
|
||||
line-height: 22px,
|
||||
// letter-spacing: 0.03125em
|
||||
),
|
||||
'caption': (
|
||||
'size': 12px,
|
||||
'weight': 400,
|
||||
'line-height': 20px,
|
||||
// 'letter-spacing': 0.03333em,
|
||||
// 'text-transform': false
|
||||
)
|
||||
),
|
||||
$uni-headings
|
||||
);
|
||||
|
||||
|
||||
|
||||
// 主色
|
||||
$uni-primary: #2979ff !default;
|
||||
$uni-primary-disable:lighten($uni-primary,20%) !default;
|
||||
$uni-primary-light: lighten($uni-primary,25%) !default;
|
||||
|
||||
// 辅助色
|
||||
// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
|
||||
$uni-success: #18bc37 !default;
|
||||
$uni-success-disable:lighten($uni-success,20%) !default;
|
||||
$uni-success-light: lighten($uni-success,25%) !default;
|
||||
|
||||
$uni-warning: #f3a73f !default;
|
||||
$uni-warning-disable:lighten($uni-warning,20%) !default;
|
||||
$uni-warning-light: lighten($uni-warning,25%) !default;
|
||||
|
||||
$uni-error: #e43d33 !default;
|
||||
$uni-error-disable:lighten($uni-error,20%) !default;
|
||||
$uni-error-light: lighten($uni-error,25%) !default;
|
||||
|
||||
$uni-info: #8f939c !default;
|
||||
$uni-info-disable:lighten($uni-info,20%) !default;
|
||||
$uni-info-light: lighten($uni-info,25%) !default;
|
||||
|
||||
// 中性色
|
||||
// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
|
||||
$uni-main-color: #3a3a3a !default; // 主要文字
|
||||
$uni-base-color: #6a6a6a !default; // 常规文字
|
||||
$uni-secondary-color: #909399 !default; // 次要文字
|
||||
$uni-extra-color: #c7c7c7 !default; // 辅助说明
|
||||
|
||||
// 边框颜色
|
||||
$uni-border-1: #F0F0F0 !default;
|
||||
$uni-border-2: #EDEDED !default;
|
||||
$uni-border-3: #DCDCDC !default;
|
||||
$uni-border-4: #B9B9B9 !default;
|
||||
|
||||
// 常规色
|
||||
$uni-black: #000000 !default;
|
||||
$uni-white: #ffffff !default;
|
||||
$uni-transparent: rgba($color: #000000, $alpha: 0) !default;
|
||||
|
||||
// 背景色
|
||||
$uni-bg-color: #f7f7f7 !default;
|
||||
|
||||
/* 水平间距 */
|
||||
$uni-spacing-sm: 8px !default;
|
||||
$uni-spacing-base: 15px !default;
|
||||
$uni-spacing-lg: 30px !default;
|
||||
|
||||
// 阴影
|
||||
$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5) !default;
|
||||
$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default;
|
||||
$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5) !default;
|
||||
|
||||
// 蒙版
|
||||
$uni-mask: rgba($color: #000000, $alpha: 0.4) !default;
|
||||
19
uni_modules/uni-scss/styles/tools/functions.scss
Normal file
19
uni_modules/uni-scss/styles/tools/functions.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
// 合并 map
|
||||
@function map-deep-merge($parent-map, $child-map){
|
||||
$result: $parent-map;
|
||||
@each $key, $child in $child-map {
|
||||
$parent-has-key: map-has-key($result, $key);
|
||||
$parent-value: map-get($result, $key);
|
||||
$parent-type: type-of($parent-value);
|
||||
$child-type: type-of($child);
|
||||
$parent-is-map: $parent-type == map;
|
||||
$child-is-map: $child-type == map;
|
||||
|
||||
@if (not $parent-has-key) or ($parent-type != $child-type) or (not ($parent-is-map and $child-is-map)){
|
||||
$result: map-merge($result, ( $key: $child ));
|
||||
}@else {
|
||||
$result: map-merge($result, ( $key: map-deep-merge($parent-value, $child) ));
|
||||
}
|
||||
}
|
||||
@return $result;
|
||||
};
|
||||
31
uni_modules/uni-scss/theme.scss
Normal file
31
uni_modules/uni-scss/theme.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
// 间距基础倍数
|
||||
$uni-space-root: 2;
|
||||
// 边框半径默认值
|
||||
$uni-radius-root:5px;
|
||||
// 主色
|
||||
$uni-primary: #2979ff;
|
||||
// 辅助色
|
||||
$uni-success: #4cd964;
|
||||
// 警告色
|
||||
$uni-warning: #f0ad4e;
|
||||
// 错误色
|
||||
$uni-error: #dd524d;
|
||||
// 描述色
|
||||
$uni-info: #909399;
|
||||
// 中性色
|
||||
$uni-main-color: #303133;
|
||||
$uni-base-color: #606266;
|
||||
$uni-secondary-color: #909399;
|
||||
$uni-extra-color: #C0C4CC;
|
||||
// 背景色
|
||||
$uni-bg-color: #f5f5f5;
|
||||
// 边框颜色
|
||||
$uni-border-1: #DCDFE6;
|
||||
$uni-border-2: #E4E7ED;
|
||||
$uni-border-3: #EBEEF5;
|
||||
$uni-border-4: #F2F6FC;
|
||||
|
||||
// 常规色
|
||||
$uni-black: #000000;
|
||||
$uni-white: #ffffff;
|
||||
$uni-transparent: rgba($color: #000000, $alpha: 0);
|
||||
62
uni_modules/uni-scss/variables.scss
Normal file
62
uni_modules/uni-scss/variables.scss
Normal file
@@ -0,0 +1,62 @@
|
||||
@import './styles/setting/_variables.scss';
|
||||
// 间距基础倍数
|
||||
$uni-space-root: 2;
|
||||
// 边框半径默认值
|
||||
$uni-radius-root:5px;
|
||||
|
||||
// 主色
|
||||
$uni-primary: #2979ff;
|
||||
$uni-primary-disable:mix(#fff,$uni-primary,50%);
|
||||
$uni-primary-light: mix(#fff,$uni-primary,80%);
|
||||
|
||||
// 辅助色
|
||||
// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
|
||||
$uni-success: #18bc37;
|
||||
$uni-success-disable:mix(#fff,$uni-success,50%);
|
||||
$uni-success-light: mix(#fff,$uni-success,80%);
|
||||
|
||||
$uni-warning: #f3a73f;
|
||||
$uni-warning-disable:mix(#fff,$uni-warning,50%);
|
||||
$uni-warning-light: mix(#fff,$uni-warning,80%);
|
||||
|
||||
$uni-error: #e43d33;
|
||||
$uni-error-disable:mix(#fff,$uni-error,50%);
|
||||
$uni-error-light: mix(#fff,$uni-error,80%);
|
||||
|
||||
$uni-info: #8f939c;
|
||||
$uni-info-disable:mix(#fff,$uni-info,50%);
|
||||
$uni-info-light: mix(#fff,$uni-info,80%);
|
||||
|
||||
// 中性色
|
||||
// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
|
||||
$uni-main-color: #3a3a3a; // 主要文字
|
||||
$uni-base-color: #6a6a6a; // 常规文字
|
||||
$uni-secondary-color: #909399; // 次要文字
|
||||
$uni-extra-color: #c7c7c7; // 辅助说明
|
||||
|
||||
// 边框颜色
|
||||
$uni-border-1: #F0F0F0;
|
||||
$uni-border-2: #EDEDED;
|
||||
$uni-border-3: #DCDCDC;
|
||||
$uni-border-4: #B9B9B9;
|
||||
|
||||
// 常规色
|
||||
$uni-black: #000000;
|
||||
$uni-white: #ffffff;
|
||||
$uni-transparent: rgba($color: #000000, $alpha: 0);
|
||||
|
||||
// 背景色
|
||||
$uni-bg-color: #f7f7f7;
|
||||
|
||||
/* 水平间距 */
|
||||
$uni-spacing-sm: 8px;
|
||||
$uni-spacing-base: 15px;
|
||||
$uni-spacing-lg: 30px;
|
||||
|
||||
// 阴影
|
||||
$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5);
|
||||
$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2);
|
||||
$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5);
|
||||
|
||||
// 蒙版
|
||||
$uni-mask: rgba($color: #000000, $alpha: 0.4);
|
||||
33
uni_modules/uni-swipe-action/changelog.md
Normal file
33
uni_modules/uni-swipe-action/changelog.md
Normal file
@@ -0,0 +1,33 @@
|
||||
## 1.3.3(2022-03-31)
|
||||
- 修复 按钮字体大小不能设置的bug
|
||||
## 1.3.2(2022-03-16)
|
||||
- 修复 h5和app端下报el错误的bug
|
||||
## 1.3.1(2022-03-07)
|
||||
- 修复 HBuilderX 1.4.X 版本中,h5和app端下报错的bug
|
||||
## 1.3.0(2021-11-19)
|
||||
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
|
||||
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-swipe-action](https://uniapp.dcloud.io/component/uniui/uni-swipe-action)
|
||||
## 1.2.4(2021-08-20)
|
||||
- 优化 close-all 方法
|
||||
## 1.2.3(2021-08-20)
|
||||
- 新增 close-all 方法,关闭所有已打开的组件
|
||||
## 1.2.2(2021-08-17)
|
||||
- 新增 resize() 方法,在非微信小程序、h5、app-vue端出现不能滑动的问题的时候,重置组件
|
||||
- 修复 app 端偶尔出现类似 Page[x][-x,xx;-x,xx,x,x-x] 的问题
|
||||
- 优化 微信小程序、h5、app-vue 滑动逻辑,避免出现动态新增组件后不能滑动的问题
|
||||
## 1.2.1(2021-07-30)
|
||||
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||
- 修复 跨页面修改组件数据 ,导致不能滑动的问题
|
||||
## 1.1.10(2021-06-17)
|
||||
- 修复 按钮点击执行两次的bug
|
||||
## 1.1.9(2021-05-12)
|
||||
- 新增 项目示例地址
|
||||
## 1.1.8(2021-03-26)
|
||||
- 修复 微信小程序 nv_navigator is not defined 报错的bug
|
||||
## 1.1.7(2021-02-05)
|
||||
- 调整为uni_modules目录规范
|
||||
- 新增 左侧滑动
|
||||
- 新增 插槽使用方式
|
||||
- 新增 threshold 属性,可以控制滑动缺省值
|
||||
- 优化 长列表滚动性能
|
||||
- 修复 滚动页面时触发组件滑动的Bug
|
||||
@@ -0,0 +1,300 @@
|
||||
let bindIngXMixins = {}
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
const BindingX = uni.requireNativePlugin('bindingx');
|
||||
const dom = uni.requireNativePlugin('dom');
|
||||
const animation = uni.requireNativePlugin('animation');
|
||||
|
||||
bindIngXMixins = {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
||||
watch: {
|
||||
show(newVal) {
|
||||
if (this.autoClose) return
|
||||
if (this.stop) return
|
||||
this.stop = true
|
||||
if (newVal) {
|
||||
this.open(newVal)
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
leftOptions() {
|
||||
this.getSelectorQuery()
|
||||
this.init()
|
||||
},
|
||||
rightOptions(newVal) {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.swipeaction = this.getSwipeAction()
|
||||
if (this.swipeaction.children !== undefined) {
|
||||
this.swipeaction.children.push(this)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.box = this.getEl(this.$refs['selector-box--hock'])
|
||||
this.selector = this.getEl(this.$refs['selector-content--hock']);
|
||||
this.leftButton = this.getEl(this.$refs['selector-left-button--hock']);
|
||||
this.rightButton = this.getEl(this.$refs['selector-right-button--hock']);
|
||||
this.init()
|
||||
},
|
||||
// beforeDestroy() {
|
||||
// this.swipeaction.children.forEach((item, index) => {
|
||||
// if (item === this) {
|
||||
// this.swipeaction.children.splice(index, 1)
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
methods: {
|
||||
init() {
|
||||
this.$nextTick(() => {
|
||||
this.x = 0
|
||||
this.button = {
|
||||
show: false
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.getSelectorQuery()
|
||||
}, 200)
|
||||
})
|
||||
},
|
||||
onClick(index, item, position) {
|
||||
this.$emit('click', {
|
||||
content: item,
|
||||
index,
|
||||
position
|
||||
})
|
||||
},
|
||||
touchstart(e) {
|
||||
// 每次只触发一次,避免多次监听造成闪烁
|
||||
if (this.stop) return
|
||||
this.stop = true
|
||||
if (this.autoClose) {
|
||||
this.swipeaction.closeOther(this)
|
||||
}
|
||||
|
||||
const leftWidth = this.button.left.width
|
||||
const rightWidth = this.button.right.width
|
||||
let expression = this.range(this.x, -rightWidth, leftWidth)
|
||||
let leftExpression = this.range(this.x - leftWidth, -leftWidth, 0)
|
||||
let rightExpression = this.range(this.x + rightWidth, 0, rightWidth)
|
||||
|
||||
this.eventpan = BindingX.bind({
|
||||
anchor: this.box,
|
||||
eventType: 'pan',
|
||||
props: [{
|
||||
element: this.selector,
|
||||
property: 'transform.translateX',
|
||||
expression
|
||||
}, {
|
||||
element: this.leftButton,
|
||||
property: 'transform.translateX',
|
||||
expression: leftExpression
|
||||
}, {
|
||||
element: this.rightButton,
|
||||
property: 'transform.translateX',
|
||||
expression: rightExpression
|
||||
}, ]
|
||||
}, (e) => {
|
||||
// nope
|
||||
if (e.state === 'end') {
|
||||
this.x = e.deltaX + this.x;
|
||||
this.isclick = true
|
||||
this.bindTiming(e.deltaX)
|
||||
}
|
||||
});
|
||||
},
|
||||
touchend(e) {
|
||||
if (this.isopen !== 'none' && !this.isclick) {
|
||||
this.open('none')
|
||||
}
|
||||
},
|
||||
bindTiming(x) {
|
||||
const left = this.x
|
||||
const leftWidth = this.button.left.width
|
||||
const rightWidth = this.button.right.width
|
||||
const threshold = this.threshold
|
||||
if (!this.isopen || this.isopen === 'none') {
|
||||
if (left > threshold) {
|
||||
this.open('left')
|
||||
} else if (left < -threshold) {
|
||||
this.open('right')
|
||||
} else {
|
||||
this.open('none')
|
||||
}
|
||||
} else {
|
||||
if ((x > -leftWidth && x < 0) || x > rightWidth) {
|
||||
if ((x > -threshold && x < 0) || (x - rightWidth > threshold)) {
|
||||
this.open('left')
|
||||
} else {
|
||||
this.open('none')
|
||||
}
|
||||
} else {
|
||||
if ((x < threshold && x > 0) || (x + leftWidth < -threshold)) {
|
||||
this.open('right')
|
||||
} else {
|
||||
this.open('none')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 移动范围
|
||||
* @param {Object} num
|
||||
* @param {Object} mix
|
||||
* @param {Object} max
|
||||
*/
|
||||
range(num, mix, max) {
|
||||
return `min(max(x+${num}, ${mix}), ${max})`
|
||||
},
|
||||
|
||||
/**
|
||||
* 开启swipe
|
||||
*/
|
||||
open(type) {
|
||||
this.animation(type)
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭swipe
|
||||
*/
|
||||
close() {
|
||||
this.animation('none')
|
||||
},
|
||||
|
||||
/**
|
||||
* 开启关闭动画
|
||||
* @param {Object} type
|
||||
*/
|
||||
animation(type) {
|
||||
const time = 300
|
||||
const leftWidth = this.button.left.width
|
||||
const rightWidth = this.button.right.width
|
||||
if (this.eventpan && this.eventpan.token) {
|
||||
BindingX.unbind({
|
||||
token: this.eventpan.token,
|
||||
eventType: 'pan'
|
||||
})
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'left':
|
||||
Promise.all([
|
||||
this.move(this.selector, leftWidth),
|
||||
this.move(this.leftButton, 0),
|
||||
this.move(this.rightButton, rightWidth * 2)
|
||||
]).then(() => {
|
||||
this.setEmit(leftWidth, type)
|
||||
})
|
||||
break
|
||||
case 'right':
|
||||
Promise.all([
|
||||
this.move(this.selector, -rightWidth),
|
||||
this.move(this.leftButton, -leftWidth * 2),
|
||||
this.move(this.rightButton, 0)
|
||||
]).then(() => {
|
||||
this.setEmit(-rightWidth, type)
|
||||
})
|
||||
break
|
||||
default:
|
||||
Promise.all([
|
||||
this.move(this.selector, 0),
|
||||
this.move(this.leftButton, -leftWidth),
|
||||
this.move(this.rightButton, rightWidth)
|
||||
]).then(() => {
|
||||
this.setEmit(0, type)
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
setEmit(x, type) {
|
||||
const leftWidth = this.button.left.width
|
||||
const rightWidth = this.button.right.width
|
||||
this.isopen = this.isopen || 'none'
|
||||
this.stop = false
|
||||
this.isclick = false
|
||||
// 只有状态不一致才会返回结果
|
||||
if (this.isopen !== type && this.x !== x) {
|
||||
if (type === 'left' && leftWidth > 0) {
|
||||
this.$emit('change', 'left')
|
||||
}
|
||||
if (type === 'right' && rightWidth > 0) {
|
||||
this.$emit('change', 'right')
|
||||
}
|
||||
if (type === 'none') {
|
||||
this.$emit('change', 'none')
|
||||
}
|
||||
}
|
||||
this.x = x
|
||||
this.isopen = type
|
||||
},
|
||||
move(ref, value) {
|
||||
return new Promise((resolve, reject) => {
|
||||
animation.transition(ref, {
|
||||
styles: {
|
||||
transform: `translateX(${value})`,
|
||||
},
|
||||
duration: 150, //ms
|
||||
timingFunction: 'linear',
|
||||
needLayout: false,
|
||||
delay: 0 //ms
|
||||
}, function(res) {
|
||||
resolve(res)
|
||||
})
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取ref
|
||||
* @param {Object} el
|
||||
*/
|
||||
getEl(el) {
|
||||
return el.ref
|
||||
},
|
||||
/**
|
||||
* 获取节点信息
|
||||
*/
|
||||
getSelectorQuery() {
|
||||
Promise.all([
|
||||
this.getDom('left'),
|
||||
this.getDom('right'),
|
||||
]).then((data) => {
|
||||
let show = 'none'
|
||||
if (this.autoClose) {
|
||||
show = 'none'
|
||||
} else {
|
||||
show = this.show
|
||||
}
|
||||
|
||||
if (show === 'none') {
|
||||
// this.close()
|
||||
} else {
|
||||
this.open(show)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
},
|
||||
getDom(str) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dom.getComponentRect(this.$refs[`selector-${str}-button--hock`], (data) => {
|
||||
if (data) {
|
||||
this.button[str] = data.size
|
||||
resolve(data)
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
export default bindIngXMixins
|
||||
@@ -0,0 +1,12 @@
|
||||
export function isPC() {
|
||||
var userAgentInfo = navigator.userAgent;
|
||||
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
|
||||
var flag = true;
|
||||
for (let v = 0; v < Agents.length - 1; v++) {
|
||||
if (userAgentInfo.indexOf(Agents[v]) > 0) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
x: 0,
|
||||
transition: false,
|
||||
width: 0,
|
||||
viewWidth: 0,
|
||||
swipeShow: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(newVal) {
|
||||
if (this.autoClose) return
|
||||
if (newVal && newVal !== 'none' ) {
|
||||
this.transition = true
|
||||
this.open(newVal)
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.swipeaction = this.getSwipeAction()
|
||||
if (this.swipeaction.children !== undefined) {
|
||||
this.swipeaction.children.push(this)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.isopen = false
|
||||
setTimeout(() => {
|
||||
this.getQuerySelect()
|
||||
}, 50)
|
||||
},
|
||||
methods: {
|
||||
appTouchStart(e) {
|
||||
const {
|
||||
clientX
|
||||
} = e.changedTouches[0]
|
||||
this.clientX = clientX
|
||||
this.timestamp = new Date().getTime()
|
||||
},
|
||||
appTouchEnd(e, index, item, position) {
|
||||
const {
|
||||
clientX
|
||||
} = e.changedTouches[0]
|
||||
// fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题
|
||||
let diff = Math.abs(this.clientX - clientX)
|
||||
let time = (new Date().getTime()) - this.timestamp
|
||||
if (diff < 40 && time < 300) {
|
||||
this.$emit('click', {
|
||||
content: item,
|
||||
index,
|
||||
position
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 移动触发
|
||||
* @param {Object} e
|
||||
*/
|
||||
onChange(e) {
|
||||
this.moveX = e.detail.x
|
||||
this.isclose = false
|
||||
},
|
||||
touchstart(e) {
|
||||
this.transition = false
|
||||
this.isclose = true
|
||||
this.autoClose && this.swipeaction.closeOther(this)
|
||||
},
|
||||
touchmove(e) {},
|
||||
touchend(e) {
|
||||
// 0的位置什么都不执行
|
||||
if (this.isclose && this.isopen === 'none') return
|
||||
if (this.isclose && this.isopen !== 'none') {
|
||||
this.transition = true
|
||||
this.close()
|
||||
} else {
|
||||
this.move(this.moveX + this.leftWidth)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 移动
|
||||
* @param {Object} moveX
|
||||
*/
|
||||
move(moveX) {
|
||||
// 打开关闭的处理逻辑不太一样
|
||||
this.transition = true
|
||||
// 未打开状态
|
||||
if (!this.isopen || this.isopen === 'none') {
|
||||
if (moveX > this.threshold) {
|
||||
this.open('left')
|
||||
} else if (moveX < -this.threshold) {
|
||||
this.open('right')
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
} else {
|
||||
if (moveX < 0 && moveX < this.rightWidth) {
|
||||
const rightX = this.rightWidth + moveX
|
||||
if (rightX < this.threshold) {
|
||||
this.open('right')
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
} else if (moveX > 0 && moveX < this.leftWidth) {
|
||||
const leftX = this.leftWidth - moveX
|
||||
if (leftX < this.threshold) {
|
||||
this.open('left')
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开
|
||||
*/
|
||||
open(type) {
|
||||
this.x = this.moveX
|
||||
this.animation(type)
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭
|
||||
*/
|
||||
close() {
|
||||
this.x = this.moveX
|
||||
// TODO 解决 x 值不更新的问题,所以会多触发一次 nextTick ,待优化
|
||||
this.$nextTick(() => {
|
||||
this.x = -this.leftWidth
|
||||
if(this.isopen!=='none'){
|
||||
this.$emit('change', 'none')
|
||||
}
|
||||
this.isopen = 'none'
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 执行结束动画
|
||||
* @param {Object} type
|
||||
*/
|
||||
animation(type) {
|
||||
this.$nextTick(() => {
|
||||
if (type === 'left') {
|
||||
this.x = 0
|
||||
} else {
|
||||
this.x = -this.rightWidth - this.leftWidth
|
||||
}
|
||||
|
||||
if(this.isopen!==type){
|
||||
this.$emit('change', type)
|
||||
}
|
||||
this.isopen = type
|
||||
})
|
||||
|
||||
},
|
||||
getSlide(x) {},
|
||||
getQuerySelect() {
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.selectAll('.movable-view--hock').boundingClientRect(data => {
|
||||
this.leftWidth = data[1].width
|
||||
this.rightWidth = data[2].width
|
||||
this.width = data[0].width
|
||||
this.viewWidth = this.width + this.rightWidth + this.leftWidth
|
||||
if (this.leftWidth === 0) {
|
||||
// TODO 疑似bug ,初始化的时候如果x 是0,会导致移动位置错误,所以让元素超出一点
|
||||
this.x = -0.1
|
||||
} else {
|
||||
this.x = -this.leftWidth
|
||||
}
|
||||
this.moveX = this.x
|
||||
this.$nextTick(() => {
|
||||
this.swipeShow = 1
|
||||
})
|
||||
|
||||
if (!this.buttonWidth) {
|
||||
this.disabledView = true
|
||||
}
|
||||
|
||||
if (this.autoClose) return
|
||||
if (this.show !== 'none') {
|
||||
this.transition = true
|
||||
this.open(this.shows)
|
||||
}
|
||||
}).exec();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
let otherMixins = {}
|
||||
|
||||
// #ifndef APP-PLUS|| MP-WEIXIN || H5
|
||||
const MIN_DISTANCE = 10;
|
||||
otherMixins = {
|
||||
data() {
|
||||
// TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug
|
||||
const elClass = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
|
||||
return {
|
||||
uniShow: false,
|
||||
left: 0,
|
||||
buttonShow: 'none',
|
||||
ani: false,
|
||||
moveLeft:'',
|
||||
elClass
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(newVal) {
|
||||
if (this.autoClose) return
|
||||
this.openState(newVal)
|
||||
},
|
||||
left(){
|
||||
this.moveLeft = `translateX(${this.left}px)`
|
||||
},
|
||||
buttonShow(newVal){
|
||||
if (this.autoClose) return
|
||||
this.openState(newVal)
|
||||
},
|
||||
leftOptions() {
|
||||
this.init()
|
||||
},
|
||||
rightOptions() {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.swipeaction = this.getSwipeAction()
|
||||
if (this.swipeaction.children !== undefined) {
|
||||
this.swipeaction.children.push(this)
|
||||
}
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init(){
|
||||
clearTimeout(this.timer)
|
||||
this.timer = setTimeout(() => {
|
||||
this.getSelectorQuery()
|
||||
}, 100)
|
||||
// 移动距离
|
||||
this.left = 0
|
||||
this.x = 0
|
||||
},
|
||||
|
||||
closeSwipe(e) {
|
||||
if (!this.autoClose) return
|
||||
this.swipeaction.closeOther(this)
|
||||
},
|
||||
appTouchStart(e) {
|
||||
const {
|
||||
clientX
|
||||
} = e.changedTouches[0]
|
||||
this.clientX = clientX
|
||||
this.timestamp = new Date().getTime()
|
||||
},
|
||||
appTouchEnd(e, index, item, position) {
|
||||
const {
|
||||
clientX
|
||||
} = e.changedTouches[0]
|
||||
// fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题
|
||||
let diff = Math.abs(this.clientX - clientX)
|
||||
let time = (new Date().getTime()) - this.timestamp
|
||||
if (diff < 40 && time < 300) {
|
||||
this.$emit('click', {
|
||||
content: item,
|
||||
index,
|
||||
position
|
||||
})
|
||||
}
|
||||
},
|
||||
touchstart(e) {
|
||||
if (this.disabled) return
|
||||
this.ani = false
|
||||
this.x = this.left || 0
|
||||
this.stopTouchStart(e)
|
||||
this.autoClose && this.closeSwipe()
|
||||
},
|
||||
touchmove(e) {
|
||||
if (this.disabled) return
|
||||
// 是否可以滑动页面
|
||||
this.stopTouchMove(e);
|
||||
if (this.direction !== 'horizontal') {
|
||||
return;
|
||||
}
|
||||
this.move(this.x + this.deltaX)
|
||||
return false
|
||||
},
|
||||
touchend() {
|
||||
if (this.disabled) return
|
||||
this.moveDirection(this.left)
|
||||
},
|
||||
/**
|
||||
* 设置移动距离
|
||||
* @param {Object} value
|
||||
*/
|
||||
move(value) {
|
||||
value = value || 0
|
||||
const leftWidth = this.leftWidth
|
||||
const rightWidth = this.rightWidth
|
||||
// 获取可滑动范围
|
||||
this.left = this.range(value, -rightWidth, leftWidth);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取范围
|
||||
* @param {Object} num
|
||||
* @param {Object} min
|
||||
* @param {Object} max
|
||||
*/
|
||||
range(num, min, max) {
|
||||
return Math.min(Math.max(num, min), max);
|
||||
},
|
||||
/**
|
||||
* 移动方向判断
|
||||
* @param {Object} left
|
||||
* @param {Object} value
|
||||
*/
|
||||
moveDirection(left) {
|
||||
const threshold = this.threshold
|
||||
const isopen = this.isopen || 'none'
|
||||
const leftWidth = this.leftWidth
|
||||
const rightWidth = this.rightWidth
|
||||
if (this.deltaX === 0) {
|
||||
this.openState('none')
|
||||
return
|
||||
}
|
||||
if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && rightWidth +
|
||||
left < threshold)) {
|
||||
// right
|
||||
this.openState('right')
|
||||
} else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 &&
|
||||
leftWidth - left < threshold)) {
|
||||
// left
|
||||
this.openState('left')
|
||||
} else {
|
||||
// default
|
||||
this.openState('none')
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 开启状态
|
||||
* @param {Boolean} type
|
||||
*/
|
||||
openState(type) {
|
||||
const leftWidth = this.leftWidth
|
||||
const rightWidth = this.rightWidth
|
||||
let left = ''
|
||||
this.isopen = this.isopen ? this.isopen : 'none'
|
||||
switch (type) {
|
||||
case "left":
|
||||
left = leftWidth
|
||||
break
|
||||
case "right":
|
||||
left = -rightWidth
|
||||
break
|
||||
default:
|
||||
left = 0
|
||||
}
|
||||
|
||||
|
||||
if (this.isopen !== type) {
|
||||
this.throttle = true
|
||||
this.$emit('change', type)
|
||||
}
|
||||
|
||||
this.isopen = type
|
||||
// 添加动画类
|
||||
this.ani = true
|
||||
this.$nextTick(() => {
|
||||
this.move(left)
|
||||
})
|
||||
// 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的
|
||||
},
|
||||
close() {
|
||||
this.openState('none')
|
||||
},
|
||||
getDirection(x, y) {
|
||||
if (x > y && x > MIN_DISTANCE) {
|
||||
return 'horizontal';
|
||||
}
|
||||
if (y > x && y > MIN_DISTANCE) {
|
||||
return 'vertical';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置滑动状态
|
||||
* @param {Object} event
|
||||
*/
|
||||
resetTouchStatus() {
|
||||
this.direction = '';
|
||||
this.deltaX = 0;
|
||||
this.deltaY = 0;
|
||||
this.offsetX = 0;
|
||||
this.offsetY = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置滑动开始位置
|
||||
* @param {Object} event
|
||||
*/
|
||||
stopTouchStart(event) {
|
||||
this.resetTouchStatus();
|
||||
const touch = event.touches[0];
|
||||
this.startX = touch.clientX;
|
||||
this.startY = touch.clientY;
|
||||
},
|
||||
|
||||
/**
|
||||
* 滑动中,是否禁止打开
|
||||
* @param {Object} event
|
||||
*/
|
||||
stopTouchMove(event) {
|
||||
const touch = event.touches[0];
|
||||
this.deltaX = touch.clientX - this.startX;
|
||||
this.deltaY = touch.clientY - this.startY;
|
||||
this.offsetX = Math.abs(this.deltaX);
|
||||
this.offsetY = Math.abs(this.deltaY);
|
||||
this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY);
|
||||
},
|
||||
|
||||
getSelectorQuery() {
|
||||
const views = uni.createSelectorQuery().in(this)
|
||||
views
|
||||
.selectAll('.'+this.elClass)
|
||||
.boundingClientRect(data => {
|
||||
if(data.length === 0) return
|
||||
let show = 'none'
|
||||
if (this.autoClose) {
|
||||
show = 'none'
|
||||
} else {
|
||||
show = this.show
|
||||
}
|
||||
this.leftWidth = data[0].width || 0
|
||||
this.rightWidth = data[1].width || 0
|
||||
this.buttonShow = show
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
export default otherMixins
|
||||
@@ -0,0 +1,82 @@
|
||||
let mpMixins = {}
|
||||
// #ifdef APP-VUE|| MP-WEIXIN || H5
|
||||
import {
|
||||
isPC
|
||||
} from "./isPC"
|
||||
mpMixins = {
|
||||
data() {
|
||||
return {
|
||||
is_show: 'none'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(newVal) {
|
||||
this.is_show = this.show
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.swipeaction = this.getSwipeAction()
|
||||
if (this.swipeaction.children !== undefined) {
|
||||
this.swipeaction.children.push(this)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.is_show = this.show
|
||||
},
|
||||
methods: {
|
||||
// wxs 中调用
|
||||
closeSwipe(e) {
|
||||
if (!this.autoClose) return
|
||||
this.swipeaction.closeOther(this)
|
||||
},
|
||||
|
||||
change(e) {
|
||||
this.$emit('change', e.open)
|
||||
if (this.is_show !== e.open) {
|
||||
this.is_show = e.open
|
||||
}
|
||||
},
|
||||
|
||||
appTouchStart(e) {
|
||||
// #ifdef H5
|
||||
if (isPC()) return
|
||||
// #endif
|
||||
const {
|
||||
clientX
|
||||
} = e.changedTouches[0]
|
||||
this.clientX = clientX
|
||||
this.timestamp = new Date().getTime()
|
||||
},
|
||||
appTouchEnd(e, index, item, position) {
|
||||
// #ifdef H5
|
||||
if (isPC()) return
|
||||
// #endif
|
||||
const {
|
||||
clientX
|
||||
} = e.changedTouches[0]
|
||||
// fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题
|
||||
let diff = Math.abs(this.clientX - clientX)
|
||||
let time = (new Date().getTime()) - this.timestamp
|
||||
if (diff < 40 && time < 300) {
|
||||
this.$emit('click', {
|
||||
content: item,
|
||||
index,
|
||||
position
|
||||
})
|
||||
}
|
||||
},
|
||||
onClickForPC(index, item, position) {
|
||||
// #ifdef H5
|
||||
if (!isPC()) return
|
||||
this.$emit('click', {
|
||||
content: item,
|
||||
index,
|
||||
position
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #endif
|
||||
export default mpMixins
|
||||
@@ -0,0 +1,270 @@
|
||||
const MIN_DISTANCE = 10;
|
||||
export default {
|
||||
showWatch(newVal, oldVal, ownerInstance, instance,self) {
|
||||
var state = self.state
|
||||
var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el
|
||||
if(!$el) return
|
||||
this.getDom(instance, ownerInstance,self)
|
||||
if (newVal && newVal !== 'none') {
|
||||
this.openState(newVal, instance, ownerInstance,self)
|
||||
return
|
||||
}
|
||||
|
||||
if (state.left) {
|
||||
this.openState('none', instance, ownerInstance,self)
|
||||
}
|
||||
this.resetTouchStatus(instance,self)
|
||||
},
|
||||
|
||||
/**
|
||||
* 开始触摸操作
|
||||
* @param {Object} e
|
||||
* @param {Object} ins
|
||||
*/
|
||||
touchstart(e, ownerInstance, self) {
|
||||
let instance = e.instance;
|
||||
let disabled = instance.getDataset().disabled
|
||||
let state = self.state;
|
||||
this.getDom(instance, ownerInstance, self)
|
||||
// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
|
||||
disabled = this.getDisabledType(disabled)
|
||||
if (disabled) return
|
||||
// 开始触摸时移除动画类
|
||||
instance.requestAnimationFrame(function() {
|
||||
instance.removeClass('ani');
|
||||
ownerInstance.callMethod('closeSwipe');
|
||||
})
|
||||
|
||||
// 记录上次的位置
|
||||
state.x = state.left || 0
|
||||
// 计算滑动开始位置
|
||||
this.stopTouchStart(e, ownerInstance, self)
|
||||
},
|
||||
|
||||
/**
|
||||
* 开始滑动操作
|
||||
* @param {Object} e
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
touchmove(e, ownerInstance, self) {
|
||||
let instance = e.instance;
|
||||
// 删除之后已经那不到实例了
|
||||
if(!instance) return;
|
||||
let disabled = instance.getDataset().disabled
|
||||
let state = self.state
|
||||
// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
|
||||
disabled = this.getDisabledType(disabled)
|
||||
if (disabled) return
|
||||
// 是否可以滑动页面
|
||||
this.stopTouchMove(e, self);
|
||||
if (state.direction !== 'horizontal') {
|
||||
return;
|
||||
}
|
||||
if (e.preventDefault) {
|
||||
// 阻止页面滚动
|
||||
e.preventDefault()
|
||||
}
|
||||
let x = state.x + state.deltaX
|
||||
this.move(x, instance, ownerInstance, self)
|
||||
},
|
||||
|
||||
/**
|
||||
* 结束触摸操作
|
||||
* @param {Object} e
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
touchend(e, ownerInstance, self) {
|
||||
let instance = e.instance;
|
||||
let disabled = instance.getDataset().disabled
|
||||
let state = self.state
|
||||
// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
|
||||
disabled = this.getDisabledType(disabled)
|
||||
|
||||
if (disabled) return
|
||||
// 滑动过程中触摸结束,通过阙值判断是开启还是关闭
|
||||
// fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13
|
||||
this.moveDirection(state.left, instance, ownerInstance, self)
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置移动距离
|
||||
* @param {Object} value
|
||||
* @param {Object} instance
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
move(value, instance, ownerInstance, self) {
|
||||
value = value || 0
|
||||
let state = self.state
|
||||
let leftWidth = state.leftWidth
|
||||
let rightWidth = state.rightWidth
|
||||
// 获取可滑动范围
|
||||
state.left = this.range(value, -rightWidth, leftWidth);
|
||||
instance.requestAnimationFrame(function() {
|
||||
instance.setStyle({
|
||||
transform: 'translateX(' + state.left + 'px)',
|
||||
'-webkit-transform': 'translateX(' + state.left + 'px)'
|
||||
})
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取元素信息
|
||||
* @param {Object} instance
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
getDom(instance, ownerInstance, self) {
|
||||
var state = self.state
|
||||
var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el
|
||||
var leftDom = $el.querySelector('.button-group--left')
|
||||
var rightDom = $el.querySelector('.button-group--right')
|
||||
|
||||
state.leftWidth = leftDom.offsetWidth || 0
|
||||
state.rightWidth = rightDom.offsetWidth || 0
|
||||
state.threshold = instance.getDataset().threshold
|
||||
},
|
||||
|
||||
getDisabledType(value) {
|
||||
return (typeof(value) === 'string' ? JSON.parse(value) : value) || false;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取范围
|
||||
* @param {Object} num
|
||||
* @param {Object} min
|
||||
* @param {Object} max
|
||||
*/
|
||||
range(num, min, max) {
|
||||
return Math.min(Math.max(num, min), max);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 移动方向判断
|
||||
* @param {Object} left
|
||||
* @param {Object} value
|
||||
* @param {Object} ownerInstance
|
||||
* @param {Object} ins
|
||||
*/
|
||||
moveDirection(left, ins, ownerInstance, self) {
|
||||
var state = self.state
|
||||
var threshold = state.threshold
|
||||
var position = state.position
|
||||
var isopen = state.isopen || 'none'
|
||||
var leftWidth = state.leftWidth
|
||||
var rightWidth = state.rightWidth
|
||||
if (state.deltaX === 0) {
|
||||
this.openState('none', ins, ownerInstance, self)
|
||||
return
|
||||
}
|
||||
if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 &&
|
||||
rightWidth +
|
||||
left < threshold)) {
|
||||
// right
|
||||
this.openState('right', ins, ownerInstance, self)
|
||||
} else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 &&
|
||||
leftWidth - left < threshold)) {
|
||||
// left
|
||||
this.openState('left', ins, ownerInstance, self)
|
||||
} else {
|
||||
// default
|
||||
this.openState('none', ins, ownerInstance, self)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 开启状态
|
||||
* @param {Boolean} type
|
||||
* @param {Object} ins
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
openState(type, ins, ownerInstance, self) {
|
||||
let state = self.state
|
||||
let leftWidth = state.leftWidth
|
||||
let rightWidth = state.rightWidth
|
||||
let left = ''
|
||||
state.isopen = state.isopen ? state.isopen : 'none'
|
||||
switch (type) {
|
||||
case "left":
|
||||
left = leftWidth
|
||||
break
|
||||
case "right":
|
||||
left = -rightWidth
|
||||
break
|
||||
default:
|
||||
left = 0
|
||||
}
|
||||
|
||||
// && !state.throttle
|
||||
|
||||
if (state.isopen !== type) {
|
||||
state.throttle = true
|
||||
ownerInstance.callMethod('change', {
|
||||
open: type
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
state.isopen = type
|
||||
// 添加动画类
|
||||
ins.requestAnimationFrame(()=> {
|
||||
ins.addClass('ani');
|
||||
this.move(left, ins, ownerInstance, self)
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
getDirection(x, y) {
|
||||
if (x > y && x > MIN_DISTANCE) {
|
||||
return 'horizontal';
|
||||
}
|
||||
if (y > x && y > MIN_DISTANCE) {
|
||||
return 'vertical';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置滑动状态
|
||||
* @param {Object} event
|
||||
*/
|
||||
resetTouchStatus(instance, self) {
|
||||
let state = self.state;
|
||||
state.direction = '';
|
||||
state.deltaX = 0;
|
||||
state.deltaY = 0;
|
||||
state.offsetX = 0;
|
||||
state.offsetY = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置滑动开始位置
|
||||
* @param {Object} event
|
||||
*/
|
||||
stopTouchStart(event, ownerInstance, self) {
|
||||
let instance = event.instance;
|
||||
let state = self.state
|
||||
this.resetTouchStatus(instance, self);
|
||||
var touch = event.touches[0];
|
||||
state.startX = touch.clientX;
|
||||
state.startY = touch.clientY;
|
||||
},
|
||||
|
||||
/**
|
||||
* 滑动中,是否禁止打开
|
||||
* @param {Object} event
|
||||
*/
|
||||
stopTouchMove(event, self) {
|
||||
let instance = event.instance;
|
||||
let state = self.state;
|
||||
let touch = event.touches[0];
|
||||
|
||||
state.deltaX = touch.clientX - state.startX;
|
||||
state.deltaY = touch.clientY - state.startY;
|
||||
state.offsetY = Math.abs(state.deltaY);
|
||||
state.offsetX = Math.abs(state.deltaX);
|
||||
state.direction = state.direction || this.getDirection(state.offsetX, state.offsetY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
<template>
|
||||
<!-- 在微信小程序 app vue端 h5 使用wxs 实现-->
|
||||
<!-- #ifdef APP-VUE || MP-WEIXIN || H5 || MP-KUAISHOU -->
|
||||
<view class="uni-swipe">
|
||||
<!-- #ifdef MP-WEIXIN || VUE3 || MP-KUAISHOU -->
|
||||
<view class="uni-swipe_box" :change:prop="wxsswipe.showWatch"
|
||||
:prop="is_show" :data-threshold="threshold" :data-disabled="disabled" @touchstart="wxsswipe.touchstart" @touchmove="wxsswipe.touchmove" @touchend="wxsswipe.touchend">
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN || VUE3 || MP-KUAISHOU -->
|
||||
<view class="uni-swipe_box" :change:prop="renderswipe.showWatch"
|
||||
:prop="is_show" :data-threshold="threshold" :data-disabled="disabled+''" @touchstart="renderswipe.touchstart" @touchmove="renderswipe.touchmove" @touchend="renderswipe.touchend">
|
||||
<!-- #endif -->
|
||||
<!-- 在微信小程序 app vue端 h5 使用wxs 实现-->
|
||||
<view class="uni-swipe_button-group button-group--left">
|
||||
<slot name="left">
|
||||
<view v-for="(item,index) in leftOptions" :key="index" :style="{
|
||||
backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD'
|
||||
}" class="uni-swipe_button button-hock" @touchstart="appTouchStart"
|
||||
@touchend="appTouchEnd($event,index,item,'left')" @click.stop="onClickForPC(index,item,'left')">
|
||||
<text class="uni-swipe_button-text"
|
||||
:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="uni-swipe_text--center">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="uni-swipe_button-group button-group--right">
|
||||
<slot name="right">
|
||||
<view v-for="(item,index) in rightOptions" :key="index" :style="{
|
||||
backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD'
|
||||
}" class="uni-swipe_button button-hock" @touchstart="appTouchStart"
|
||||
@touchend="appTouchEnd($event,index,item,'right')"
|
||||
@click.stop="onClickForPC(index,item,'right')"><text class="uni-swipe_button-text"
|
||||
:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- app nvue端 使用 bindingx -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<view ref="selector-box--hock" class="uni-swipe" @horizontalpan="touchstart" @touchend="touchend">
|
||||
<view ref='selector-left-button--hock' class="uni-swipe_button-group button-group--left">
|
||||
<slot name="left">
|
||||
<view v-for="(item,index) in leftOptions" :data-button="btn" :key="index" :style="{
|
||||
backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD'
|
||||
}" class="uni-swipe_button button-hock" @click.stop="onClick(index,item,'left')"><text
|
||||
class="uni-swipe_button-text"
|
||||
:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF', fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
<view ref='selector-right-button--hock' class="uni-swipe_button-group button-group--right">
|
||||
<slot name="right">
|
||||
<view v-for="(item,index) in rightOptions" :data-button="btn" :key="index" :style="{
|
||||
backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD'
|
||||
}" class="uni-swipe_button button-hock" @click.stop="onClick(index,item,'right')"><text
|
||||
class="uni-swipe_button-text"
|
||||
:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
<view ref='selector-content--hock' class="uni-swipe_box">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- 其他平台使用 js ,长列表性能可能会有影响-->
|
||||
<!-- #ifdef MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQ || MP-KUAISHOU -->
|
||||
<view class="uni-swipe">
|
||||
<view class="uni-swipe_box" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend"
|
||||
:style="{transform:moveLeft}" :class="{ani:ani}">
|
||||
<view class="uni-swipe_button-group button-group--left" :class="[elClass]">
|
||||
<slot name="left">
|
||||
<view v-for="(item,index) in leftOptions" :data-button="btn" :key="index" :style="{
|
||||
backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
|
||||
fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
|
||||
}" class="uni-swipe_button button-hock" @touchstart="appTouchStart"
|
||||
@touchend="appTouchEnd($event,index,item,'left')"><text class="uni-swipe_button-text"
|
||||
:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}">{{ item.text }}</text>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
<slot></slot>
|
||||
<view class="uni-swipe_button-group button-group--right" :class="[elClass]">
|
||||
<slot name="right">
|
||||
<view v-for="(item,index) in rightOptions" :data-button="btn" :key="index" :style="{
|
||||
backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
|
||||
fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
|
||||
}" @touchstart="appTouchStart" @touchend="appTouchEnd($event,index,item,'right')"
|
||||
class="uni-swipe_button button-hock"><text class="uni-swipe_button-text"
|
||||
:style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}">{{ item.text }}</text>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
</template>
|
||||
<script src="./wx.wxs" module="wxsswipe" lang="wxs"></script>
|
||||
|
||||
<script module="renderswipe" lang="renderjs">
|
||||
import render from './render.js'
|
||||
export default {
|
||||
mounted(e,ins,owner) {
|
||||
this.state = {}
|
||||
},
|
||||
methods:{
|
||||
showWatch(newVal, oldVal, ownerInstance, instance){
|
||||
render.showWatch(newVal, oldVal, ownerInstance, instance,this)
|
||||
},
|
||||
touchstart(e,ownerInstance){
|
||||
render.touchstart(e,ownerInstance,this)
|
||||
},
|
||||
touchmove(e, ownerInstance){
|
||||
render.touchmove(e,ownerInstance,this)
|
||||
},
|
||||
touchend(e,ownerInstance){
|
||||
render.touchend(e,ownerInstance,this)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
import mpwxs from './mpwxs'
|
||||
import bindingx from './bindingx.js'
|
||||
import mpother from './mpother'
|
||||
|
||||
/**
|
||||
* SwipeActionItem 滑动操作子组件
|
||||
* @description 通过滑动触发选项的容器
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=181
|
||||
* @property {Boolean} show = [left|right|none] 开启关闭组件,auto-close = false 时生效
|
||||
* @property {Boolean} disabled = [true|false] 是否禁止滑动
|
||||
* @property {Boolean} autoClose = [true|false] 滑动打开当前组件,是否关闭其他组件
|
||||
* @property {Number} threshold 滑动缺省值
|
||||
* @property {Array} leftOptions 左侧选项内容及样式
|
||||
* @property {Array} rgihtOptions 右侧选项内容及样式
|
||||
* @event {Function} click 点击选项按钮时触发事件,e = {content,index} ,content(点击内容)、index(下标)
|
||||
* @event {Function} change 组件打开或关闭时触发,left\right\none
|
||||
*/
|
||||
|
||||
export default {
|
||||
mixins: [mpwxs,bindingx,mpother],
|
||||
emits:['click','change'],
|
||||
props: {
|
||||
// 控制开关
|
||||
show: {
|
||||
type: String,
|
||||
default: 'none'
|
||||
},
|
||||
|
||||
// 禁用
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 是否自动关闭
|
||||
autoClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
// 滑动缺省距离
|
||||
threshold: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
|
||||
// 左侧按钮内容
|
||||
leftOptions: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
// 右侧按钮内容
|
||||
rightOptions: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
// #ifndef VUE3
|
||||
// TODO vue2
|
||||
destroyed() {
|
||||
if (this.__isUnmounted) return
|
||||
this.uninstall()
|
||||
},
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
// TODO vue3
|
||||
unmounted() {
|
||||
this.__isUnmounted = true
|
||||
this.uninstall()
|
||||
},
|
||||
// #endif
|
||||
|
||||
methods: {
|
||||
uninstall() {
|
||||
if (this.swipeaction) {
|
||||
this.swipeaction.children.forEach((item, index) => {
|
||||
if (item === this) {
|
||||
this.swipeaction.children.splice(index, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取父元素实例
|
||||
*/
|
||||
getSwipeAction(name = 'uniSwipeAction') {
|
||||
let parent = this.$parent;
|
||||
let parentName = parent.$options.name;
|
||||
while (parentName !== name) {
|
||||
parent = parent.$parent;
|
||||
if (!parent) return false;
|
||||
parentName = parent.$options.name;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.uni-swipe {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-swipe_box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
// touch-action: none;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.uni-swipe_content {
|
||||
// border: 1px red solid;
|
||||
}
|
||||
|
||||
.uni-swipe_text--center {
|
||||
width: 100%;
|
||||
/* #ifndef APP-NVUE */
|
||||
cursor: grab;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-swipe_button-group {
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.button-group--left {
|
||||
left: 0;
|
||||
transform: translateX(-100%)
|
||||
}
|
||||
|
||||
.button-group--right {
|
||||
right: 0;
|
||||
transform: translateX(100%)
|
||||
}
|
||||
|
||||
.uni-swipe_button {
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.uni-swipe_button-text {
|
||||
/* #ifndef APP-NVUE */
|
||||
flex-shrink: 0;
|
||||
/* #endif */
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ani {
|
||||
transition-property: transform;
|
||||
transition-duration: 0.3s;
|
||||
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
|
||||
/* #ifdef MP-ALIPAY */
|
||||
.movable-area {
|
||||
/* width: 100%; */
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.movable-view {
|
||||
display: flex;
|
||||
/* justify-content: center; */
|
||||
position: relative;
|
||||
flex: 1;
|
||||
height: 45px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.movable-view-button {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
background: #C0C0C0;
|
||||
}
|
||||
|
||||
/* .transition {
|
||||
transition: all 0.3s;
|
||||
} */
|
||||
|
||||
.movable-view-box {
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
</style>
|
||||
@@ -0,0 +1,341 @@
|
||||
var MIN_DISTANCE = 10;
|
||||
|
||||
/**
|
||||
* 判断当前是否为H5、app-vue
|
||||
*/
|
||||
var IS_HTML5 = false
|
||||
if (typeof window === 'object') IS_HTML5 = true
|
||||
|
||||
/**
|
||||
* 监听页面内值的变化,主要用于动态开关swipe-action
|
||||
* @param {Object} newValue
|
||||
* @param {Object} oldValue
|
||||
* @param {Object} ownerInstance
|
||||
* @param {Object} instance
|
||||
*/
|
||||
function showWatch(newVal, oldVal, ownerInstance, instance) {
|
||||
var state = instance.getState()
|
||||
getDom(instance, ownerInstance)
|
||||
if (newVal && newVal !== 'none') {
|
||||
openState(newVal, instance, ownerInstance)
|
||||
return
|
||||
}
|
||||
|
||||
if (state.left) {
|
||||
openState('none', instance, ownerInstance)
|
||||
}
|
||||
resetTouchStatus(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始触摸操作
|
||||
* @param {Object} e
|
||||
* @param {Object} ins
|
||||
*/
|
||||
function touchstart(e, ownerInstance) {
|
||||
var instance = e.instance;
|
||||
var disabled = instance.getDataset().disabled
|
||||
var state = instance.getState();
|
||||
getDom(instance, ownerInstance)
|
||||
// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
|
||||
disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
|
||||
if (disabled) return
|
||||
// 开始触摸时移除动画类
|
||||
instance.requestAnimationFrame(function() {
|
||||
instance.removeClass('ani');
|
||||
ownerInstance.callMethod('closeSwipe');
|
||||
})
|
||||
|
||||
// 记录上次的位置
|
||||
state.x = state.left || 0
|
||||
// 计算滑动开始位置
|
||||
stopTouchStart(e, ownerInstance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始滑动操作
|
||||
* @param {Object} e
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
function touchmove(e, ownerInstance) {
|
||||
var instance = e.instance;
|
||||
var disabled = instance.getDataset().disabled
|
||||
var state = instance.getState()
|
||||
// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
|
||||
disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
|
||||
if (disabled) return
|
||||
// 是否可以滑动页面
|
||||
stopTouchMove(e);
|
||||
if (state.direction !== 'horizontal') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.preventDefault) {
|
||||
// 阻止页面滚动
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
move(state.x + state.deltaX, instance, ownerInstance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束触摸操作
|
||||
* @param {Object} e
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
function touchend(e, ownerInstance) {
|
||||
var instance = e.instance;
|
||||
var disabled = instance.getDataset().disabled
|
||||
var state = instance.getState()
|
||||
// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
|
||||
disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
|
||||
|
||||
if (disabled) return
|
||||
// 滑动过程中触摸结束,通过阙值判断是开启还是关闭
|
||||
// fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13
|
||||
moveDirection(state.left, instance, ownerInstance)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置移动距离
|
||||
* @param {Object} value
|
||||
* @param {Object} instance
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
function move(value, instance, ownerInstance) {
|
||||
value = value || 0
|
||||
var state = instance.getState()
|
||||
var leftWidth = state.leftWidth
|
||||
var rightWidth = state.rightWidth
|
||||
// 获取可滑动范围
|
||||
state.left = range(value, -rightWidth, leftWidth);
|
||||
instance.requestAnimationFrame(function() {
|
||||
instance.setStyle({
|
||||
transform: 'translateX(' + state.left + 'px)',
|
||||
'-webkit-transform': 'translateX(' + state.left + 'px)'
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取元素信息
|
||||
* @param {Object} instance
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
function getDom(instance, ownerInstance) {
|
||||
var state = instance.getState()
|
||||
var leftDom = ownerInstance.selectComponent('.button-group--left')
|
||||
var rightDom = ownerInstance.selectComponent('.button-group--right')
|
||||
var leftStyles = {
|
||||
width: 0
|
||||
}
|
||||
var rightStyles = {
|
||||
width: 0
|
||||
}
|
||||
leftStyles = leftDom.getBoundingClientRect()
|
||||
rightStyles = rightDom.getBoundingClientRect()
|
||||
|
||||
state.leftWidth = leftStyles.width || 0
|
||||
state.rightWidth = rightStyles.width || 0
|
||||
state.threshold = instance.getDataset().threshold
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取范围
|
||||
* @param {Object} num
|
||||
* @param {Object} min
|
||||
* @param {Object} max
|
||||
*/
|
||||
function range(num, min, max) {
|
||||
return Math.min(Math.max(num, min), max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 移动方向判断
|
||||
* @param {Object} left
|
||||
* @param {Object} value
|
||||
* @param {Object} ownerInstance
|
||||
* @param {Object} ins
|
||||
*/
|
||||
function moveDirection(left, ins, ownerInstance) {
|
||||
var state = ins.getState()
|
||||
var threshold = state.threshold
|
||||
var position = state.position
|
||||
var isopen = state.isopen || 'none'
|
||||
var leftWidth = state.leftWidth
|
||||
var rightWidth = state.rightWidth
|
||||
if (state.deltaX === 0) {
|
||||
openState('none', ins, ownerInstance)
|
||||
return
|
||||
}
|
||||
if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 &&
|
||||
rightWidth +
|
||||
left < threshold)) {
|
||||
// right
|
||||
openState('right', ins, ownerInstance)
|
||||
} else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 &&
|
||||
leftWidth - left < threshold)) {
|
||||
// left
|
||||
openState('left', ins, ownerInstance)
|
||||
} else {
|
||||
// default
|
||||
openState('none', ins, ownerInstance)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 开启状态
|
||||
* @param {Boolean} type
|
||||
* @param {Object} ins
|
||||
* @param {Object} ownerInstance
|
||||
*/
|
||||
function openState(type, ins, ownerInstance) {
|
||||
var state = ins.getState()
|
||||
var leftWidth = state.leftWidth
|
||||
var rightWidth = state.rightWidth
|
||||
var left = ''
|
||||
state.isopen = state.isopen ? state.isopen : 'none'
|
||||
switch (type) {
|
||||
case "left":
|
||||
left = leftWidth
|
||||
break
|
||||
case "right":
|
||||
left = -rightWidth
|
||||
break
|
||||
default:
|
||||
left = 0
|
||||
}
|
||||
|
||||
// && !state.throttle
|
||||
|
||||
if (state.isopen !== type) {
|
||||
state.throttle = true
|
||||
ownerInstance.callMethod('change', {
|
||||
open: type
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
state.isopen = type
|
||||
// 添加动画类
|
||||
ins.requestAnimationFrame(function() {
|
||||
ins.addClass('ani');
|
||||
move(left, ins, ownerInstance)
|
||||
})
|
||||
// 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的
|
||||
}
|
||||
|
||||
|
||||
function getDirection(x, y) {
|
||||
if (x > y && x > MIN_DISTANCE) {
|
||||
return 'horizontal';
|
||||
}
|
||||
if (y > x && y > MIN_DISTANCE) {
|
||||
return 'vertical';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置滑动状态
|
||||
* @param {Object} event
|
||||
*/
|
||||
function resetTouchStatus(instance) {
|
||||
var state = instance.getState();
|
||||
state.direction = '';
|
||||
state.deltaX = 0;
|
||||
state.deltaY = 0;
|
||||
state.offsetX = 0;
|
||||
state.offsetY = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置滑动开始位置
|
||||
* @param {Object} event
|
||||
*/
|
||||
function stopTouchStart(event) {
|
||||
var instance = event.instance;
|
||||
var state = instance.getState();
|
||||
resetTouchStatus(instance);
|
||||
var touch = event.touches[0];
|
||||
if (IS_HTML5 && isPC()) {
|
||||
touch = event;
|
||||
}
|
||||
state.startX = touch.clientX;
|
||||
state.startY = touch.clientY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 滑动中,是否禁止打开
|
||||
* @param {Object} event
|
||||
*/
|
||||
function stopTouchMove(event) {
|
||||
var instance = event.instance;
|
||||
var state = instance.getState();
|
||||
var touch = event.touches[0];
|
||||
if (IS_HTML5 && isPC()) {
|
||||
touch = event;
|
||||
}
|
||||
state.deltaX = touch.clientX - state.startX;
|
||||
state.deltaY = touch.clientY - state.startY;
|
||||
state.offsetY = Math.abs(state.deltaY);
|
||||
state.offsetX = Math.abs(state.deltaX);
|
||||
state.direction = state.direction || getDirection(state.offsetX, state.offsetY);
|
||||
}
|
||||
|
||||
function isPC() {
|
||||
var userAgentInfo = navigator.userAgent;
|
||||
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
|
||||
var flag = true;
|
||||
for (var v = 0; v < Agents.length - 1; v++) {
|
||||
if (userAgentInfo.indexOf(Agents[v]) > 0) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
var movable = false
|
||||
|
||||
function mousedown(e, ins) {
|
||||
if (!IS_HTML5) return
|
||||
if (!isPC()) return
|
||||
touchstart(e, ins)
|
||||
movable = true
|
||||
}
|
||||
|
||||
function mousemove(e, ins) {
|
||||
if (!IS_HTML5) return
|
||||
if (!isPC()) return
|
||||
if (!movable) return
|
||||
touchmove(e, ins)
|
||||
}
|
||||
|
||||
function mouseup(e, ins) {
|
||||
if (!IS_HTML5) return
|
||||
if (!isPC()) return
|
||||
touchend(e, ins)
|
||||
movable = false
|
||||
}
|
||||
|
||||
function mouseleave(e, ins) {
|
||||
if (!IS_HTML5) return
|
||||
if (!isPC()) return
|
||||
movable = false
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
showWatch: showWatch,
|
||||
touchstart: touchstart,
|
||||
touchmove: touchmove,
|
||||
touchend: touchend,
|
||||
mousedown: mousedown,
|
||||
mousemove: mousemove,
|
||||
mouseup: mouseup,
|
||||
mouseleave: mouseleave
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<view>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* SwipeAction 滑动操作
|
||||
* @description 通过滑动触发选项的容器
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=181
|
||||
*/
|
||||
export default {
|
||||
name:"uniSwipeAction",
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
created() {
|
||||
this.children = [];
|
||||
},
|
||||
methods: {
|
||||
// 公开给用户使用,重制组件样式
|
||||
resize(){
|
||||
// wxs 会自己计算组件大小,所以无需执行下面代码
|
||||
// #ifndef APP-VUE || H5 || MP-WEIXIN || MP-KUAISHOU
|
||||
this.children.forEach(vm=>{
|
||||
vm.init()
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
// 公开给用户使用,关闭全部 已经打开的组件
|
||||
closeAll(){
|
||||
this.children.forEach(vm=>{
|
||||
// #ifdef APP-VUE || H5 || MP-WEIXIN || MP-KUAISHOU
|
||||
vm.is_show = 'none'
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-VUE || H5 || MP-WEIXIN || MP-KUAISHOU
|
||||
vm.close()
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
closeOther(vm) {
|
||||
if (this.openItem && this.openItem !== vm) {
|
||||
// #ifdef APP-VUE || H5 || MP-WEIXIN || MP-KUAISHOU
|
||||
this.openItem.is_show = 'none'
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-VUE || H5 || MP-WEIXIN || MP-KUAISHOU
|
||||
this.openItem.close()
|
||||
// #endif
|
||||
}
|
||||
// 记录上一个打开的 swipe-action-item ,用于 auto-close
|
||||
this.openItem = vm
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
87
uni_modules/uni-swipe-action/package.json
Normal file
87
uni_modules/uni-swipe-action/package.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"id": "uni-swipe-action",
|
||||
"displayName": "uni-swipe-action 滑动操作",
|
||||
"version": "1.3.3",
|
||||
"description": "SwipeAction 滑动操作操作组件",
|
||||
"keywords": [
|
||||
"",
|
||||
"uni-ui",
|
||||
"uniui",
|
||||
"滑动删除",
|
||||
"侧滑删除"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": ""
|
||||
},
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": ["uni-scss"],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "y",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
uni_modules/uni-swipe-action/readme.md
Normal file
11
uni_modules/uni-swipe-action/readme.md
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
## SwipeAction 滑动操作
|
||||
> **组件名:uni-swipe-action**
|
||||
> 代码块: `uSwipeAction`、`uSwipeActionItem`
|
||||
|
||||
|
||||
通过滑动触发选项的容器
|
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-swipe-action)
|
||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||
12
uni_modules/uni-swiper-dot/changelog.md
Normal file
12
uni_modules/uni-swiper-dot/changelog.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## 1.2.0(2021-11-19)
|
||||
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
|
||||
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-swiper-dot](https://uniapp.dcloud.io/component/uniui/uni-swiper-dot)
|
||||
## 1.1.0(2021-07-30)
|
||||
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||
## 1.0.6(2021-05-12)
|
||||
- 新增 示例地址
|
||||
- 修复 示例项目缺少组件的Bug
|
||||
## 1.0.5(2021-02-05)
|
||||
- 调整为uni_modules目录规范
|
||||
- 新增 clickItem 事件,支持指示点控制轮播
|
||||
- 新增 支持 pc 可用
|
||||
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<view class="uni-swiper__warp">
|
||||
<slot />
|
||||
<view v-if="mode === 'default'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box" key='default'>
|
||||
<view v-for="(item,index) in info" @tap="clickItem(index)" :style="{
|
||||
'width': (index === current? dots.width*2:dots.width ) + 'px','height':dots.width/2 +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border-radius':'0px'}"
|
||||
:key="index" class="uni-swiper__dots-item uni-swiper__dots-bar" />
|
||||
</view>
|
||||
<view v-if="mode === 'dot'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box" key='dot'>
|
||||
<view v-for="(item,index) in info" @tap="clickItem(index)" :style="{
|
||||
'width': dots.width + 'px','height':dots.height +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}"
|
||||
:key="index" class="uni-swiper__dots-item" />
|
||||
</view>
|
||||
<view v-if="mode === 'round'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box" key='round'>
|
||||
<view v-for="(item,index) in info" @tap="clickItem(index)" :class="[index === current&&'uni-swiper__dots-long']" :style="{
|
||||
'width':(index === current? dots.width*3:dots.width ) + 'px','height':dots.height +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}"
|
||||
:key="index" class="uni-swiper__dots-item " />
|
||||
</view>
|
||||
<view v-if="mode === 'nav'" key='nav' :style="{'background-color':dotsStyles.backgroundColor,'bottom':'0'}" class="uni-swiper__dots-box uni-swiper__dots-nav">
|
||||
<text :style="{'color':dotsStyles.color}" class="uni-swiper__dots-nav-item">{{ (current+1)+"/"+info.length +' ' +info[current][field] }}</text>
|
||||
</view>
|
||||
<view v-if="mode === 'indexes'" key='indexes' :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box">
|
||||
<view v-for="(item,index) in info" @tap="clickItem(index)" :style="{
|
||||
'width':dots.width + 'px','height':dots.height +'px' ,'color':index === current?dots.selectedColor:dots.color,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}"
|
||||
:key="index" class="uni-swiper__dots-item uni-swiper__dots-indexes"><text class="uni-swiper__dots-indexes-text">{{ index+1 }}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
/**
|
||||
* SwiperDod 轮播图指示点
|
||||
* @description 自定义轮播图指示点
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=284
|
||||
* @property {Number} current 当前指示点索引,必须是通过 `swiper` 的 `change` 事件获取到的 `e.detail.current`
|
||||
* @property {String} mode = [default|round|nav|indexes] 指示点的类型
|
||||
* @value defualt 默认指示点
|
||||
* @value round 圆形指示点
|
||||
* @value nav 条形指示点
|
||||
* @value indexes 索引指示点
|
||||
* @property {String} field mode 为 nav 时,显示的内容字段(mode = nav 时必填)
|
||||
* @property {String} info 轮播图的数据,通过数组长度决定指示点个数
|
||||
* @property {Object} dotsStyles 指示点样式
|
||||
* @event {Function} clickItem 组件触发点击事件时触发,e={currentIndex}
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'UniSwiperDot',
|
||||
emits:['clickItem'],
|
||||
props: {
|
||||
info: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
dotsStyles: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 类型 :default(默认) indexes long nav
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
// 只在 nav 模式下生效,变量名称
|
||||
field: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dots: {
|
||||
width: 6,
|
||||
height: 6,
|
||||
bottom: 10,
|
||||
color: '#fff',
|
||||
backgroundColor: 'rgba(0, 0, 0, .3)',
|
||||
border: '1px rgba(0, 0, 0, .3) solid',
|
||||
selectedBackgroundColor: '#333',
|
||||
selectedBorder: '1px rgba(0, 0, 0, .9) solid'
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dotsStyles(newVal) {
|
||||
this.dots = Object.assign(this.dots, this.dotsStyles)
|
||||
},
|
||||
mode(newVal) {
|
||||
if (newVal === 'indexes') {
|
||||
this.dots.width = 14
|
||||
this.dots.height = 14
|
||||
} else {
|
||||
this.dots.width = 6
|
||||
this.dots.height = 6
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
created() {
|
||||
if (this.mode === 'indexes') {
|
||||
this.dots.width = 12
|
||||
this.dots.height = 12
|
||||
}
|
||||
this.dots = Object.assign(this.dots, this.dotsStyles)
|
||||
},
|
||||
methods: {
|
||||
clickItem(index) {
|
||||
this.$emit('clickItem', index)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.uni-swiper__warp {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.uni-swiper__dots-box {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uni-swiper__dots-item {
|
||||
width: 8px;
|
||||
border-radius: 100px;
|
||||
margin-left: 6px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
/* #ifndef APP-NVUE */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
/* #ifdef H5 */
|
||||
// border-width: 5px 0;
|
||||
// border-style: solid;
|
||||
// border-color: transparent;
|
||||
// background-clip: padding-box;
|
||||
/* #endif */
|
||||
// transition: width 0.2s linear; 不要取消注释,不然会不能变色
|
||||
}
|
||||
|
||||
.uni-swiper__dots-item:first-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.uni-swiper__dots-default {
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.uni-swiper__dots-long {
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.uni-swiper__dots-bar {
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.uni-swiper__dots-nav {
|
||||
bottom: 0px;
|
||||
// height: 26px;
|
||||
padding: 8px 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.uni-swiper__dots-nav-item {
|
||||
/* overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap; */
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
.uni-swiper__dots-indexes {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
// flex: 1;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uni-swiper__dots-indexes-text {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
}
|
||||
</style>
|
||||
87
uni_modules/uni-swiper-dot/package.json
Normal file
87
uni_modules/uni-swiper-dot/package.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"id": "uni-swiper-dot",
|
||||
"displayName": "uni-swiper-dot 轮播图指示点",
|
||||
"version": "1.2.0",
|
||||
"description": "自定义轮播图指示点组件",
|
||||
"keywords": [
|
||||
"uni-ui",
|
||||
"uniui",
|
||||
"轮播图指示点",
|
||||
"dot",
|
||||
"swiper"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": ""
|
||||
},
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": ["uni-scss"],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
uni_modules/uni-swiper-dot/readme.md
Normal file
11
uni_modules/uni-swiper-dot/readme.md
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
## SwiperDot 轮播图指示点
|
||||
> **组件名:uni-swiper-dot**
|
||||
> 代码块: `uSwiperDot`
|
||||
|
||||
|
||||
自定义轮播图指示点
|
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-swiper-dot)
|
||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||
22
uni_modules/uni-transition/changelog.md
Normal file
22
uni_modules/uni-transition/changelog.md
Normal file
@@ -0,0 +1,22 @@
|
||||
## 1.3.2(2023-05-04)
|
||||
- 修复 NVUE 平台报错的问题
|
||||
## 1.3.1(2021-11-23)
|
||||
- 修复 init 方法初始化问题
|
||||
## 1.3.0(2021-11-19)
|
||||
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
|
||||
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-transition](https://uniapp.dcloud.io/component/uniui/uni-transition)
|
||||
## 1.2.1(2021-09-27)
|
||||
- 修复 init 方法不生效的 Bug
|
||||
## 1.2.0(2021-07-30)
|
||||
- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||
## 1.1.1(2021-05-12)
|
||||
- 新增 示例地址
|
||||
- 修复 示例项目缺少组件的 Bug
|
||||
## 1.1.0(2021-04-22)
|
||||
- 新增 通过方法自定义动画
|
||||
- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式
|
||||
- 优化 动画触发逻辑,使动画更流畅
|
||||
- 优化 支持单独的动画类型
|
||||
- 优化 文档示例
|
||||
## 1.0.2(2021-02-05)
|
||||
- 调整为 uni_modules 目录规范
|
||||
@@ -0,0 +1,131 @@
|
||||
// const defaultOption = {
|
||||
// duration: 300,
|
||||
// timingFunction: 'linear',
|
||||
// delay: 0,
|
||||
// transformOrigin: '50% 50% 0'
|
||||
// }
|
||||
// #ifdef APP-NVUE
|
||||
const nvueAnimation = uni.requireNativePlugin('animation')
|
||||
// #endif
|
||||
class MPAnimation {
|
||||
constructor(options, _this) {
|
||||
this.options = options
|
||||
// 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误
|
||||
this.animation = uni.createAnimation({
|
||||
...options
|
||||
})
|
||||
this.currentStepAnimates = {}
|
||||
this.next = 0
|
||||
this.$ = _this
|
||||
|
||||
}
|
||||
|
||||
_nvuePushAnimates(type, args) {
|
||||
let aniObj = this.currentStepAnimates[this.next]
|
||||
let styles = {}
|
||||
if (!aniObj) {
|
||||
styles = {
|
||||
styles: {},
|
||||
config: {}
|
||||
}
|
||||
} else {
|
||||
styles = aniObj
|
||||
}
|
||||
if (animateTypes1.includes(type)) {
|
||||
if (!styles.styles.transform) {
|
||||
styles.styles.transform = ''
|
||||
}
|
||||
let unit = ''
|
||||
if(type === 'rotate'){
|
||||
unit = 'deg'
|
||||
}
|
||||
styles.styles.transform += `${type}(${args+unit}) `
|
||||
} else {
|
||||
styles.styles[type] = `${args}`
|
||||
}
|
||||
this.currentStepAnimates[this.next] = styles
|
||||
}
|
||||
_animateRun(styles = {}, config = {}) {
|
||||
let ref = this.$.$refs['ani'].ref
|
||||
if (!ref) return
|
||||
return new Promise((resolve, reject) => {
|
||||
nvueAnimation.transition(ref, {
|
||||
styles,
|
||||
...config
|
||||
}, res => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_nvueNextAnimate(animates, step = 0, fn) {
|
||||
let obj = animates[step]
|
||||
if (obj) {
|
||||
let {
|
||||
styles,
|
||||
config
|
||||
} = obj
|
||||
this._animateRun(styles, config).then(() => {
|
||||
step += 1
|
||||
this._nvueNextAnimate(animates, step, fn)
|
||||
})
|
||||
} else {
|
||||
this.currentStepAnimates = {}
|
||||
typeof fn === 'function' && fn()
|
||||
this.isEnd = true
|
||||
}
|
||||
}
|
||||
|
||||
step(config = {}) {
|
||||
// #ifndef APP-NVUE
|
||||
this.animation.step(config)
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
|
||||
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
|
||||
this.next++
|
||||
// #endif
|
||||
return this
|
||||
}
|
||||
|
||||
run(fn) {
|
||||
// #ifndef APP-NVUE
|
||||
this.$.animationData = this.animation.export()
|
||||
this.$.timer = setTimeout(() => {
|
||||
typeof fn === 'function' && fn()
|
||||
}, this.$.durationTime)
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this.isEnd = false
|
||||
let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
|
||||
if(!ref) return
|
||||
this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
|
||||
this.next = 0
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
|
||||
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
|
||||
'translateZ'
|
||||
]
|
||||
const animateTypes2 = ['opacity', 'backgroundColor']
|
||||
const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
|
||||
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
|
||||
MPAnimation.prototype[type] = function(...args) {
|
||||
// #ifndef APP-NVUE
|
||||
this.animation[type](...args)
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this._nvuePushAnimates(type, args)
|
||||
// #endif
|
||||
return this
|
||||
}
|
||||
})
|
||||
|
||||
export function createAnimation(option, _this) {
|
||||
if(!_this) return
|
||||
clearTimeout(_this.timer)
|
||||
return new MPAnimation(option, _this)
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createAnimation } from './createAnimation'
|
||||
|
||||
/**
|
||||
* Transition 过渡动画
|
||||
* @description 简单过渡动画组件
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
|
||||
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
|
||||
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
|
||||
* @value fade 渐隐渐出过渡
|
||||
* @value slide-top 由上至下过渡
|
||||
* @value slide-right 由右至左过渡
|
||||
* @value slide-bottom 由下至上过渡
|
||||
* @value slide-left 由左至右过渡
|
||||
* @value zoom-in 由小到大过渡
|
||||
* @value zoom-out 由大到小过渡
|
||||
* @property {Number} duration 过渡动画持续时间
|
||||
* @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
|
||||
*/
|
||||
export default {
|
||||
name: 'uniTransition',
|
||||
emits:['click','change'],
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
modeClass: {
|
||||
type: [Array, String],
|
||||
default() {
|
||||
return 'fade'
|
||||
}
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 300
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
customClass:{
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
onceRender:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
transform: '',
|
||||
opacity: 1,
|
||||
animationData: {},
|
||||
durationTime: 300,
|
||||
config: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.open()
|
||||
} else {
|
||||
// 避免上来就执行 close,导致动画错乱
|
||||
if (this.isShow) {
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 生成样式数据
|
||||
stylesObject() {
|
||||
let styles = {
|
||||
...this.styles,
|
||||
'transition-duration': this.duration / 1000 + 's'
|
||||
}
|
||||
let transform = ''
|
||||
for (let i in styles) {
|
||||
let line = this.toLine(i)
|
||||
transform += line + ':' + styles[i] + ';'
|
||||
}
|
||||
return transform
|
||||
},
|
||||
// 初始化动画条件
|
||||
transformStyles() {
|
||||
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 动画默认配置
|
||||
this.config = {
|
||||
duration: this.duration,
|
||||
timingFunction: 'ease',
|
||||
transformOrigin: '50% 50%',
|
||||
delay: 0
|
||||
}
|
||||
this.durationTime = this.duration
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* ref 触发 初始化动画
|
||||
*/
|
||||
init(obj = {}) {
|
||||
if (obj.duration) {
|
||||
this.durationTime = obj.duration
|
||||
}
|
||||
this.animation = createAnimation(Object.assign(this.config, obj),this)
|
||||
},
|
||||
/**
|
||||
* 点击组件触发回调
|
||||
*/
|
||||
onClick() {
|
||||
this.$emit('click', {
|
||||
detail: this.isShow
|
||||
})
|
||||
},
|
||||
/**
|
||||
* ref 触发 动画分组
|
||||
* @param {Object} obj
|
||||
*/
|
||||
step(obj, config = {}) {
|
||||
if (!this.animation) return
|
||||
for (let i in obj) {
|
||||
try {
|
||||
if(typeof obj[i] === 'object'){
|
||||
this.animation[i](...obj[i])
|
||||
}else{
|
||||
this.animation[i](obj[i])
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`方法 ${i} 不存在`)
|
||||
}
|
||||
}
|
||||
this.animation.step(config)
|
||||
return this
|
||||
},
|
||||
/**
|
||||
* ref 触发 执行动画
|
||||
*/
|
||||
run(fn) {
|
||||
if (!this.animation) return
|
||||
this.animation.run(fn)
|
||||
},
|
||||
// 开始过度动画
|
||||
open() {
|
||||
clearTimeout(this.timer)
|
||||
this.transform = ''
|
||||
this.isShow = true
|
||||
let { opacity, transform } = this.styleInit(false)
|
||||
if (typeof opacity !== 'undefined') {
|
||||
this.opacity = opacity
|
||||
}
|
||||
this.transform = transform
|
||||
// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
|
||||
this.$nextTick(() => {
|
||||
// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
|
||||
this.timer = setTimeout(() => {
|
||||
this.animation = createAnimation(this.config, this)
|
||||
this.tranfromInit(false).step()
|
||||
this.animation.run()
|
||||
this.$emit('change', {
|
||||
detail: this.isShow
|
||||
})
|
||||
}, 20)
|
||||
})
|
||||
},
|
||||
// 关闭过度动画
|
||||
close(type) {
|
||||
if (!this.animation) return
|
||||
this.tranfromInit(true)
|
||||
.step()
|
||||
.run(() => {
|
||||
this.isShow = false
|
||||
this.animationData = null
|
||||
this.animation = null
|
||||
let { opacity, transform } = this.styleInit(false)
|
||||
this.opacity = opacity || 1
|
||||
this.transform = transform
|
||||
this.$emit('change', {
|
||||
detail: this.isShow
|
||||
})
|
||||
})
|
||||
},
|
||||
// 处理动画开始前的默认样式
|
||||
styleInit(type) {
|
||||
let styles = {
|
||||
transform: ''
|
||||
}
|
||||
let buildStyle = (type, mode) => {
|
||||
if (mode === 'fade') {
|
||||
styles.opacity = this.animationType(type)[mode]
|
||||
} else {
|
||||
styles.transform += this.animationType(type)[mode] + ' '
|
||||
}
|
||||
}
|
||||
if (typeof this.modeClass === 'string') {
|
||||
buildStyle(type, this.modeClass)
|
||||
} else {
|
||||
this.modeClass.forEach(mode => {
|
||||
buildStyle(type, mode)
|
||||
})
|
||||
}
|
||||
return styles
|
||||
},
|
||||
// 处理内置组合动画
|
||||
tranfromInit(type) {
|
||||
let buildTranfrom = (type, mode) => {
|
||||
let aniNum = null
|
||||
if (mode === 'fade') {
|
||||
aniNum = type ? 0 : 1
|
||||
} else {
|
||||
aniNum = type ? '-100%' : '0'
|
||||
if (mode === 'zoom-in') {
|
||||
aniNum = type ? 0.8 : 1
|
||||
}
|
||||
if (mode === 'zoom-out') {
|
||||
aniNum = type ? 1.2 : 1
|
||||
}
|
||||
if (mode === 'slide-right') {
|
||||
aniNum = type ? '100%' : '0'
|
||||
}
|
||||
if (mode === 'slide-bottom') {
|
||||
aniNum = type ? '100%' : '0'
|
||||
}
|
||||
}
|
||||
this.animation[this.animationMode()[mode]](aniNum)
|
||||
}
|
||||
if (typeof this.modeClass === 'string') {
|
||||
buildTranfrom(type, this.modeClass)
|
||||
} else {
|
||||
this.modeClass.forEach(mode => {
|
||||
buildTranfrom(type, mode)
|
||||
})
|
||||
}
|
||||
|
||||
return this.animation
|
||||
},
|
||||
animationType(type) {
|
||||
return {
|
||||
fade: type ? 1 : 0,
|
||||
'slide-top': `translateY(${type ? '0' : '-100%'})`,
|
||||
'slide-right': `translateX(${type ? '0' : '100%'})`,
|
||||
'slide-bottom': `translateY(${type ? '0' : '100%'})`,
|
||||
'slide-left': `translateX(${type ? '0' : '-100%'})`,
|
||||
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
|
||||
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
|
||||
}
|
||||
},
|
||||
// 内置动画类型与实际动画对应字典
|
||||
animationMode() {
|
||||
return {
|
||||
fade: 'opacity',
|
||||
'slide-top': 'translateY',
|
||||
'slide-right': 'translateX',
|
||||
'slide-bottom': 'translateY',
|
||||
'slide-left': 'translateX',
|
||||
'zoom-in': 'scale',
|
||||
'zoom-out': 'scale'
|
||||
}
|
||||
},
|
||||
// 驼峰转中横线
|
||||
toLine(name) {
|
||||
return name.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
84
uni_modules/uni-transition/package.json
Normal file
84
uni_modules/uni-transition/package.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"id": "uni-transition",
|
||||
"displayName": "uni-transition 过渡动画",
|
||||
"version": "1.3.2",
|
||||
"description": "元素的简单过渡动画",
|
||||
"keywords": [
|
||||
"uni-ui",
|
||||
"uniui",
|
||||
"动画",
|
||||
"过渡",
|
||||
"过渡动画"
|
||||
],
|
||||
"repository": "https://github.com/dcloudio/uni-ui",
|
||||
"engines": {
|
||||
"HBuilderX": ""
|
||||
},
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
|
||||
"type": "component-vue"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": ["uni-scss"],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
uni_modules/uni-transition/readme.md
Normal file
11
uni_modules/uni-transition/readme.md
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
## Transition 过渡动画
|
||||
> **组件名:uni-transition**
|
||||
> 代码块: `uTransition`
|
||||
|
||||
|
||||
元素过渡动画
|
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-transition)
|
||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||
76
uni_modules/uv-ui-tools/changelog.md
Normal file
76
uni_modules/uv-ui-tools/changelog.md
Normal file
@@ -0,0 +1,76 @@
|
||||
## 1.1.25(2024-01-20)
|
||||
1.1.20版本更新
|
||||
## 1.1.24(2023-12-21)
|
||||
1. luch-request更新
|
||||
## 1.1.23(2023-12-12)
|
||||
1. 1.1.19版本
|
||||
## 1.1.22(2023-11-28)
|
||||
1. 优化
|
||||
## 1.1.21(2023-11-10)
|
||||
1. 1.1.17版本
|
||||
## 1.1.20(2023-10-30)
|
||||
1. 1.1.16版本
|
||||
## 1.1.19(2023-10-13)
|
||||
1. 兼容vue3
|
||||
## 1.1.18(2023-10-12)
|
||||
1. 1.1.15版本
|
||||
## 1.1.17(2023-09-27)
|
||||
1. 1.1.14版本发布
|
||||
## 1.1.16(2023-09-15)
|
||||
1. 1.1.13版本发布
|
||||
## 1.1.15(2023-09-15)
|
||||
1. 更新button.js相关按钮支持open-type="agreePrivacyAuthorization"
|
||||
## 1.1.14(2023-09-14)
|
||||
1. 优化dayjs
|
||||
## 1.1.13(2023-09-13)
|
||||
1. 优化,$uv中增加unit参数,方便组件中使用
|
||||
## 1.1.12(2023-09-10)
|
||||
1. 升级版本
|
||||
## 1.1.11(2023-09-04)
|
||||
1. 1.1.11版本
|
||||
## 1.1.10(2023-08-31)
|
||||
1. 修复customStyle和customClass存在冲突的问题
|
||||
## 1.1.9(2023-08-27)
|
||||
1. 版本升级
|
||||
2. 优化
|
||||
## 1.1.8(2023-08-24)
|
||||
1. 版本升级
|
||||
## 1.1.7(2023-08-22)
|
||||
1. 版本升级
|
||||
## 1.1.6(2023-08-18)
|
||||
uvui版本:1.1.6
|
||||
## 1.0.15(2023-08-14)
|
||||
1. 更新uvui版本号
|
||||
## 1.0.13(2023-08-06)
|
||||
1. 优化
|
||||
## 1.0.12(2023-08-06)
|
||||
1. 修改版本号
|
||||
## 1.0.11(2023-08-06)
|
||||
1. 路由增加events参数
|
||||
2. 路由拦截修复
|
||||
## 1.0.10(2023-08-01)
|
||||
1. 优化
|
||||
## 1.0.9(2023-06-28)
|
||||
优化openType.js
|
||||
## 1.0.8(2023-06-15)
|
||||
1. 修改支付宝报错的BUG
|
||||
## 1.0.7(2023-06-07)
|
||||
1. 解决微信小程序使用uvui提示 Some selectors are not allowed in component wxss, including tag name selectors, ID selectors, and attribute selectors
|
||||
2. 解决上述提示,需要在uni.scss配置$uvui-nvue-style: false; 然后在APP.vue下面引入uvui内置的基础样式:@import '@/uni_modules/uv-ui-tools/index.scss';
|
||||
## 1.0.6(2023-06-04)
|
||||
1. uv-ui-tools 优化工具组件,兼容更多功能
|
||||
2. 小程序分享功能优化等
|
||||
## 1.0.5(2023-06-02)
|
||||
1. 修改扩展使用mixin中方法的问题
|
||||
## 1.0.4(2023-05-23)
|
||||
1. 兼容百度小程序修改bem函数
|
||||
## 1.0.3(2023-05-16)
|
||||
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
|
||||
2. 优化部分功能
|
||||
## 1.0.2(2023-05-10)
|
||||
1. 增加Http请求封装
|
||||
2. 优化
|
||||
## 1.0.1(2023-05-04)
|
||||
1. 修改名称及备注
|
||||
## 1.0.0(2023-05-04)
|
||||
1. uv-ui工具集首次发布
|
||||
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
</template>
|
||||
<script>
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
79
uni_modules/uv-ui-tools/index.js
Normal file
79
uni_modules/uv-ui-tools/index.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// 全局挂载引入http相关请求拦截插件
|
||||
import Request from './libs/luch-request'
|
||||
|
||||
// 引入全局mixin
|
||||
import mixin from './libs/mixin/mixin.js'
|
||||
// 小程序特有的mixin
|
||||
import mpMixin from './libs/mixin/mpMixin.js'
|
||||
// #ifdef MP
|
||||
import mpShare from './libs/mixin/mpShare.js'
|
||||
// #endif
|
||||
|
||||
// 路由封装
|
||||
import route from './libs/util/route.js'
|
||||
// 公共工具函数
|
||||
import * as index from './libs/function/index.js'
|
||||
// 防抖方法
|
||||
import debounce from './libs/function/debounce.js'
|
||||
// 节流方法
|
||||
import throttle from './libs/function/throttle.js'
|
||||
// 规则检验
|
||||
import * as test from './libs/function/test.js'
|
||||
|
||||
// 颜色渐变相关,colorGradient-颜色渐变,hexToRgb-十六进制颜色转rgb颜色,rgbToHex-rgb转十六进制
|
||||
import * as colorGradient from './libs/function/colorGradient.js'
|
||||
|
||||
// 配置信息
|
||||
import config from './libs/config/config.js'
|
||||
// 平台
|
||||
import platform from './libs/function/platform'
|
||||
|
||||
const $uv = {
|
||||
route,
|
||||
config,
|
||||
test,
|
||||
date: index.timeFormat, // 另名date
|
||||
...index,
|
||||
colorGradient: colorGradient.colorGradient,
|
||||
hexToRgb: colorGradient.hexToRgb,
|
||||
rgbToHex: colorGradient.rgbToHex,
|
||||
colorToRgba: colorGradient.colorToRgba,
|
||||
http: new Request(),
|
||||
debounce,
|
||||
throttle,
|
||||
platform,
|
||||
mixin,
|
||||
mpMixin
|
||||
}
|
||||
uni.$uv = $uv;
|
||||
const install = (Vue,options={}) => {
|
||||
// #ifndef APP-NVUE
|
||||
const cloneMixin = index.deepClone(mixin);
|
||||
delete cloneMixin?.props?.customClass;
|
||||
delete cloneMixin?.props?.customStyle;
|
||||
Vue.mixin(cloneMixin);
|
||||
// #ifdef MP
|
||||
if(options.mpShare){
|
||||
Vue.mixin(mpShare);
|
||||
}
|
||||
// #endif
|
||||
// #endif
|
||||
// #ifdef VUE2
|
||||
// 时间格式化,同时两个名称,date和timeFormat
|
||||
Vue.filter('timeFormat', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
|
||||
Vue.filter('date', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
|
||||
// 将多久以前的方法,注入到全局过滤器
|
||||
Vue.filter('timeFrom', (timestamp, format) => uni.$uv.timeFrom(timestamp, format));
|
||||
// 同时挂载到uni和Vue.prototype中
|
||||
// #ifndef APP-NVUE
|
||||
// 只有vue,挂载到Vue.prototype才有意义,因为nvue中全局Vue.prototype和Vue.mixin是无效的
|
||||
Vue.prototype.$uv = $uv;
|
||||
// #endif
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
Vue.config.globalProperties.$uv = $uv;
|
||||
// #endif
|
||||
}
|
||||
export default {
|
||||
install
|
||||
}
|
||||
7
uni_modules/uv-ui-tools/index.scss
Normal file
7
uni_modules/uv-ui-tools/index.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
// 引入公共基础类
|
||||
@import "./libs/css/common.scss";
|
||||
|
||||
// 非nvue的样式
|
||||
/* #ifndef APP-NVUE */
|
||||
@import "./libs/css/vue.scss";
|
||||
/* #endif */
|
||||
34
uni_modules/uv-ui-tools/libs/config/config.js
Normal file
34
uni_modules/uv-ui-tools/libs/config/config.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// 此版本发布于2024-01-20
|
||||
const version = '1.1.20'
|
||||
|
||||
// 开发环境才提示,生产环境不会提示
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(`\n %c uvui V${version} https://www.uvui.cn/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0; border-radius: 5px;');
|
||||
}
|
||||
|
||||
export default {
|
||||
v: version,
|
||||
version,
|
||||
// 主题名称
|
||||
type: [
|
||||
'primary',
|
||||
'success',
|
||||
'info',
|
||||
'error',
|
||||
'warning'
|
||||
],
|
||||
// 颜色部分,本来可以通过scss的:export导出供js使用,但是奈何nvue不支持
|
||||
color: {
|
||||
'uv-primary': '#2979ff',
|
||||
'uv-warning': '#ff9900',
|
||||
'uv-success': '#19be6b',
|
||||
'uv-error': '#fa3534',
|
||||
'uv-info': '#909399',
|
||||
'uv-main-color': '#303133',
|
||||
'uv-content-color': '#606266',
|
||||
'uv-tips-color': '#909399',
|
||||
'uv-light-color': '#c0c4cc'
|
||||
},
|
||||
// 默认单位,可以通过配置为rpx,那么在用于传入组件大小参数为数值时,就默认为rpx
|
||||
unit: 'px'
|
||||
}
|
||||
32
uni_modules/uv-ui-tools/libs/css/color.scss
Normal file
32
uni_modules/uv-ui-tools/libs/css/color.scss
Normal file
@@ -0,0 +1,32 @@
|
||||
$uv-main-color: #303133 !default;
|
||||
$uv-content-color: #606266 !default;
|
||||
$uv-tips-color: #909193 !default;
|
||||
$uv-light-color: #c0c4cc !default;
|
||||
$uv-border-color: #dadbde !default;
|
||||
$uv-bg-color: #f3f4f6 !default;
|
||||
$uv-disabled-color: #c8c9cc !default;
|
||||
|
||||
$uv-primary: #3c9cff !default;
|
||||
$uv-primary-dark: #398ade !default;
|
||||
$uv-primary-disabled: #9acafc !default;
|
||||
$uv-primary-light: #ecf5ff !default;
|
||||
|
||||
$uv-warning: #f9ae3d !default;
|
||||
$uv-warning-dark: #f1a532 !default;
|
||||
$uv-warning-disabled: #f9d39b !default;
|
||||
$uv-warning-light: #fdf6ec !default;
|
||||
|
||||
$uv-success: #5ac725 !default;
|
||||
$uv-success-dark: #53c21d !default;
|
||||
$uv-success-disabled: #a9e08f !default;
|
||||
$uv-success-light: #f5fff0;
|
||||
|
||||
$uv-error: #f56c6c !default;
|
||||
$uv-error-dark: #e45656 !default;
|
||||
$uv-error-disabled: #f7b2b2 !default;
|
||||
$uv-error-light: #fef0f0 !default;
|
||||
|
||||
$uv-info: #909399 !default;
|
||||
$uv-info-dark: #767a82 !default;
|
||||
$uv-info-disabled: #c4c6c9 !default;
|
||||
$uv-info-light: #f4f4f5 !default;
|
||||
100
uni_modules/uv-ui-tools/libs/css/common.scss
Normal file
100
uni_modules/uv-ui-tools/libs/css/common.scss
Normal file
@@ -0,0 +1,100 @@
|
||||
// 超出行数,自动显示行尾省略号,最多5行
|
||||
// 来自uvui的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】
|
||||
@for $i from 1 through 5 {
|
||||
.uv-line-#{$i} {
|
||||
/* #ifdef APP-NVUE */
|
||||
// nvue下,可以直接使用lines属性,这是weex特有样式
|
||||
lines: $i;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
// vue下,单行和多行显示省略号需要单独处理
|
||||
@if $i == '1' {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
} @else {
|
||||
display: -webkit-box!important;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
-webkit-line-clamp: $i;
|
||||
-webkit-box-orient: vertical!important;
|
||||
}
|
||||
/* #endif */
|
||||
}
|
||||
}
|
||||
$uv-bordercolor: #dadbde;
|
||||
@if variable-exists(uv-border-color) {
|
||||
$uv-bordercolor: $uv-border-color;
|
||||
}
|
||||
|
||||
// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时,
|
||||
// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效
|
||||
// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important
|
||||
// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现
|
||||
.uv-border {
|
||||
border-width: 0.5px!important;
|
||||
border-color: $uv-bordercolor!important;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.uv-border-top {
|
||||
border-top-width: 0.5px!important;
|
||||
border-color: $uv-bordercolor!important;
|
||||
border-top-style: solid;
|
||||
}
|
||||
|
||||
.uv-border-left {
|
||||
border-left-width: 0.5px!important;
|
||||
border-color: $uv-bordercolor!important;
|
||||
border-left-style: solid;
|
||||
}
|
||||
|
||||
.uv-border-right {
|
||||
border-right-width: 0.5px!important;
|
||||
border-color: $uv-bordercolor!important;
|
||||
border-right-style: solid;
|
||||
}
|
||||
|
||||
.uv-border-bottom {
|
||||
border-bottom-width: 0.5px!important;
|
||||
border-color: $uv-bordercolor!important;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.uv-border-top-bottom {
|
||||
border-top-width: 0.5px!important;
|
||||
border-bottom-width: 0.5px!important;
|
||||
border-color: $uv-bordercolor!important;
|
||||
border-top-style: solid;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
// 去除button的所有默认样式,让其表现跟普通的view、text元素一样
|
||||
.uv-reset-button {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
/* #ifndef APP-PLUS */
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
border-width: 0;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
.uv-reset-button::after {
|
||||
border: none;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.uv-hover-class {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user