正在显示
37 个修改的文件
包含
4529 行增加
和
0 行删除
.hbuilderx/launch.json
0 → 100644
1 | +{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/ | ||
2 | + // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数 | ||
3 | + "version": "0.0", | ||
4 | + "configurations": [{ | ||
5 | + "default" : | ||
6 | + { | ||
7 | + "launchtype" : "local" | ||
8 | + }, | ||
9 | + "mp-weixin" : | ||
10 | + { | ||
11 | + "launchtype" : "local" | ||
12 | + }, | ||
13 | + "type" : "uniCloud" | ||
14 | + } | ||
15 | + ] | ||
16 | +} |
App.vue
0 → 100644
1 | +<script> | ||
2 | + export default { | ||
3 | + onLaunch: function() { | ||
4 | + console.log('App Launch') | ||
5 | + }, | ||
6 | + onShow: function() { | ||
7 | + console.log('App Show') | ||
8 | + }, | ||
9 | + onHide: function() { | ||
10 | + console.log('App Hide') | ||
11 | + } | ||
12 | + } | ||
13 | +</script> | ||
14 | + | ||
15 | +<style lang="scss"> | ||
16 | + /*每个页面公共css */ | ||
17 | + @import "node_modules/uview-ui/index.scss"; | ||
18 | +</style> |
api/index.js
0 → 100644
1 | +<template> | ||
2 | + <view class="filter-wrapper" :style="{ height: height + 'rpx', top: top,'border-top':border?'1rpx solid #f2f2f2':'none' }" @touchmove.stop.prevent="discard"> | ||
3 | + <view class="inner-wrapper"> | ||
4 | + <view class="mask" :class="showMask ? 'show' : 'hide'" @tap="tapMask"></view> | ||
5 | + <view class="navs"> | ||
6 | + <view class="c-flex-align" :class="{ 'c-flex-center': index > 0, actNav: index === actNav }" v-for="(item, index) in navData" :key="index" @click="navClick(index)"> | ||
7 | + <view v-for="(child, childx) in item" :key="childx" v-if="child.select">{{ child.text }}</view> | ||
8 | + <image style="width:30rpx;height:30rpx;" src="/static/2-2筛选-全城/ic-arrow-putaway@2x.png" mode="" class="icon-triangle" v-if="index === actNav"></image> | ||
9 | + <image style="width:30rpx;height:30rpx;" src="/static/2-1筛选-分类/ic-arrow-unfold@2x(2).png" mode="" class="icon-triangle" v-else></image> | ||
10 | + </view> | ||
11 | + | ||
12 | + <!-- <view class="date-wrapper"> | ||
13 | + <picker mode="date" @change="handleDate"> | ||
14 | + <view class="date c-flex-align" :style="{ height: height + 'rpx' }" @click="dateClick"> | ||
15 | + <view>{{ selDate }}</view> | ||
16 | + <image src="https://i.loli.net/2020/07/15/xjVSvzWcH9NO7al.png" mode="" class="icon-triangle"></image> | ||
17 | + </view> | ||
18 | + </picker> | ||
19 | + </view> --> | ||
20 | + </view> | ||
21 | + <scroll-view scroll-y="true" class="popup" :class="popupShow ? 'popupShow' : ''"> | ||
22 | + <view class="item-opt c-flex-align" :class="item.select ? 'actOpt' : ''" v-for="(item, index) in navData[actNav]" :key="index" @click="handleOpt(index)"> | ||
23 | + {{ item.text }} | ||
24 | + </view> | ||
25 | + </scroll-view> | ||
26 | + </view> | ||
27 | + </view> | ||
28 | +</template> | ||
29 | + | ||
30 | +<script> | ||
31 | +// import { getCurDateTime } from '@/libs/utils.js'; | ||
32 | +export default { | ||
33 | + props: { | ||
34 | + height: { | ||
35 | + type: Number, | ||
36 | + default: 108 | ||
37 | + }, | ||
38 | + top: { | ||
39 | + type: String, | ||
40 | + default: 'calc(var(--window-statsu-bar) + 44px)' | ||
41 | + }, | ||
42 | + border: { | ||
43 | + type: Boolean, | ||
44 | + default: false | ||
45 | + }, | ||
46 | + filterData: { | ||
47 | + //必填 | ||
48 | + type: Array, | ||
49 | + default: () => { | ||
50 | + return []; | ||
51 | + } | ||
52 | + // default: () => { | ||
53 | + // return [ | ||
54 | + // [{ text: '全部状态', value: '' }, { text: '状态1', value: 1 }, { text: '状态2', value: 2 }, { text: '状态3', value: 3 }], | ||
55 | + // [{ text: '全部类型', value: '' }, { text: '类型1', value: 1 }, { text: '类型2', value: 2 }, { text: '类型3', value: 3 }] | ||
56 | + // ]; | ||
57 | + // } | ||
58 | + }, | ||
59 | + defaultIndex: { | ||
60 | + //默认选中条件索引,超出一类时必填 | ||
61 | + type: Array, | ||
62 | + default: () => { | ||
63 | + return [0]; | ||
64 | + } | ||
65 | + } | ||
66 | + }, | ||
67 | + data() { | ||
68 | + return { | ||
69 | + navData: [], | ||
70 | + popupShow: false, | ||
71 | + showMask: false, | ||
72 | + actNav: null, | ||
73 | + selDate: '选择日期', | ||
74 | + selIndex: [] //选中条件索引 | ||
75 | + }; | ||
76 | + }, | ||
77 | + created() { | ||
78 | + this.navData = this.filterData; | ||
79 | + this.selIndex = this.defaultIndex; | ||
80 | + this.keepStatus(); | ||
81 | + }, | ||
82 | + mounted() { | ||
83 | + // this.selDate = getCurDateTime().formatDate; | ||
84 | + }, | ||
85 | + methods: { | ||
86 | + keepStatus() { | ||
87 | + this.navData.forEach(itemnavData => { | ||
88 | + itemnavData.map(child => { | ||
89 | + child.select = false; | ||
90 | + }); | ||
91 | + return itemnavData; | ||
92 | + }); | ||
93 | + for (let i = 0; i < this.selIndex.length; i++) { | ||
94 | + let selindex = this.selIndex[i]; | ||
95 | + this.navData[i][selindex].select = true; | ||
96 | + } | ||
97 | + }, | ||
98 | + navClick(index) { | ||
99 | + if (index === this.actNav) { | ||
100 | + this.tapMask(); | ||
101 | + return; | ||
102 | + } | ||
103 | + this.popupShow = true; | ||
104 | + this.showMask = true; | ||
105 | + this.actNav = index; | ||
106 | + }, | ||
107 | + handleOpt(index) { | ||
108 | + this.selIndex[this.actNav] = index; | ||
109 | + this.keepStatus(); | ||
110 | + setTimeout(() => { | ||
111 | + this.tapMask(); | ||
112 | + }, 100); | ||
113 | + let data = []; | ||
114 | + let res = this.navData.forEach(item => { | ||
115 | + let sel = item.filter(child => child.select); | ||
116 | + data.push(sel); | ||
117 | + }); | ||
118 | + console.log(data); | ||
119 | + this.$emit('onSelected', data); | ||
120 | + }, | ||
121 | + dateClick() { | ||
122 | + this.tapMask(); | ||
123 | + }, | ||
124 | + tapMask() { | ||
125 | + this.showMask = false; | ||
126 | + this.popupShow = false; | ||
127 | + this.actNav = null; | ||
128 | + }, | ||
129 | + handleDate(e) { | ||
130 | + let d = e.detail.value; | ||
131 | + this.selDate = d; | ||
132 | + this.$emit('dateChange', d); | ||
133 | + }, | ||
134 | + discard() {} | ||
135 | + } | ||
136 | +}; | ||
137 | +</script> | ||
138 | + | ||
139 | +<style lang="scss" scoped> | ||
140 | +page { | ||
141 | + font-size: 28rpx; | ||
142 | +} | ||
143 | +.c-flex-align { | ||
144 | + display: flex; | ||
145 | + align-items: center; | ||
146 | +} | ||
147 | +.c-flex-center { | ||
148 | + display: flex; | ||
149 | + align-items: center; | ||
150 | + justify-content: center; | ||
151 | + flex-direction: column; | ||
152 | +} | ||
153 | +.filter-wrapper { | ||
154 | + position: fixed; | ||
155 | + left: 0; | ||
156 | + width: 750rpx; | ||
157 | + z-index: 999; | ||
158 | + .inner-wrapper { | ||
159 | + // position: relative; | ||
160 | + .navs { | ||
161 | + position: relative; | ||
162 | + height: 110rpx; | ||
163 | + padding: 0 40rpx; | ||
164 | + display: flex; | ||
165 | + align-items: center; | ||
166 | + justify-content: space-between; | ||
167 | + background-color: #fff; | ||
168 | + border-bottom: 2rpx solid #f5f6f9; | ||
169 | + color: #8b9aae; | ||
170 | + z-index: 999; | ||
171 | + box-sizing: border-box; | ||
172 | + & > view { | ||
173 | + flex: 1; | ||
174 | + height: 100%; | ||
175 | + flex-direction: row; | ||
176 | + z-index: 999; | ||
177 | + } | ||
178 | + .date { | ||
179 | + justify-content: flex-end; | ||
180 | + } | ||
181 | + .actNav { | ||
182 | + color: #4d7df9; | ||
183 | + font-weight: bold; | ||
184 | + } | ||
185 | + } | ||
186 | + .mask { | ||
187 | + z-index: 666; | ||
188 | + position: fixed; | ||
189 | + top: calc(var(--status-bar-height) + 44px); | ||
190 | + left: 0; | ||
191 | + right: 0; | ||
192 | + bottom: 0; | ||
193 | + background-color: rgba(0, 0, 0, 0); | ||
194 | + transition: background-color 0.15s linear; | ||
195 | + &.show { | ||
196 | + background-color: rgba(0, 0, 0, 0.4); | ||
197 | + } | ||
198 | + &.hide { | ||
199 | + display: none; | ||
200 | + } | ||
201 | + } | ||
202 | + .popup { | ||
203 | + position: relative; | ||
204 | + max-height: 500rpx; | ||
205 | + background-color: #fff; | ||
206 | + border-bottom-left-radius: 20rpx; | ||
207 | + border-bottom-right-radius: 20rpx; | ||
208 | + overflow: scroll; | ||
209 | + z-index: 999; | ||
210 | + transition: all 1s linear; | ||
211 | + opacity: 0; | ||
212 | + display: none; | ||
213 | + .item-opt { | ||
214 | + height: 100rpx; | ||
215 | + padding: 0 40rpx; | ||
216 | + color: #8b9aae; | ||
217 | + border-bottom: 2rpx solid #f5f6f9; | ||
218 | + } | ||
219 | + .actOpt { | ||
220 | + color: #4d7df9; | ||
221 | + font-weight: bold; | ||
222 | + position: relative; | ||
223 | + &::after { | ||
224 | + content: '✓'; | ||
225 | + font-weight: bold; | ||
226 | + font-size: 36rpx; | ||
227 | + position: absolute; | ||
228 | + right: 40rpx; | ||
229 | + } | ||
230 | + } | ||
231 | + } | ||
232 | + .popupShow { | ||
233 | + display: block; | ||
234 | + opacity: 1; | ||
235 | + } | ||
236 | + } | ||
237 | + | ||
238 | + .icon-triangle { | ||
239 | + width: 16rpx; | ||
240 | + height: 16rpx; | ||
241 | + margin-left: 10rpx; | ||
242 | + } | ||
243 | +} | ||
244 | +</style> |
index.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="en"> | ||
3 | + <head> | ||
4 | + <meta charset="UTF-8" /> | ||
5 | + <script> | ||
6 | + var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || | ||
7 | + CSS.supports('top: constant(a)')) | ||
8 | + document.write( | ||
9 | + '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + | ||
10 | + (coverSupport ? ', viewport-fit=cover' : '') + '" />') | ||
11 | + </script> | ||
12 | + <title></title> | ||
13 | + <!--preload-links--> | ||
14 | + <!--app-context--> | ||
15 | + </head> | ||
16 | + <body> | ||
17 | + <div id="app"><!--app-html--></div> | ||
18 | + <script type="module" src="/main.js"></script> | ||
19 | + </body> | ||
20 | +</html> |
main.js
0 → 100644
1 | +import App from './App' | ||
2 | + | ||
3 | +// #ifndef VUE3 | ||
4 | +import Vue from 'vue' | ||
5 | +Vue.config.productionTip = false | ||
6 | +App.mpType = 'app' | ||
7 | + | ||
8 | +import uView from "uview-ui"; | ||
9 | +Vue.use(uView); | ||
10 | +const app = new Vue({ | ||
11 | + ...App | ||
12 | +}) | ||
13 | +app.$mount() | ||
14 | +// #endif | ||
15 | + | ||
16 | +// #ifdef VUE3 | ||
17 | +import { createSSRApp } from 'vue' | ||
18 | +export function createApp() { | ||
19 | + const app = createSSRApp(App) | ||
20 | + return { | ||
21 | + app | ||
22 | + } | ||
23 | +} | ||
24 | +// #endif |
manifest.json
0 → 100644
1 | +{ | ||
2 | + "name" : "beijingTheatreGoing", | ||
3 | + "appid" : "", | ||
4 | + "description" : "", | ||
5 | + "versionName" : "1.0.0", | ||
6 | + "versionCode" : "100", | ||
7 | + "transformPx" : false, | ||
8 | + /* 5+App特有相关 */ | ||
9 | + "app-plus" : { | ||
10 | + "usingComponents" : true, | ||
11 | + "nvueStyleCompiler" : "uni-app", | ||
12 | + "compilerVersion" : 3, | ||
13 | + "splashscreen" : { | ||
14 | + "alwaysShowBeforeRender" : true, | ||
15 | + "waiting" : true, | ||
16 | + "autoclose" : true, | ||
17 | + "delay" : 0 | ||
18 | + }, | ||
19 | + /* 快应用特有相关 */ | ||
20 | + "quickapp" : {}, | ||
21 | + /* 小程序特有相关 */ | ||
22 | + "mp-weixin" : { | ||
23 | + "appid" : "wx8a42f2d5eaa6b32e", | ||
24 | + "setting" : { | ||
25 | + "urlCheck" : false | ||
26 | + }, | ||
27 | + "usingComponents" : true | ||
28 | + }, | ||
29 | + /* 模块配置 */ | ||
30 | + "modules" : {}, | ||
31 | + /* 应用发布信息 */ | ||
32 | + "distribute" : { | ||
33 | + /* android打包配置 */ | ||
34 | + "android" : { | ||
35 | + "permissions" : [ | ||
36 | + "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", | ||
37 | + "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", | ||
38 | + "<uses-permission android:name=\"android.permission.VIBRATE\"/>", | ||
39 | + "<uses-permission android:name=\"android.permission.READ_LOGS\"/>", | ||
40 | + "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", | ||
41 | + "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", | ||
42 | + "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", | ||
43 | + "<uses-permission android:name=\"android.permission.CAMERA\"/>", | ||
44 | + "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", | ||
45 | + "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", | ||
46 | + "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", | ||
47 | + "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", | ||
48 | + "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", | ||
49 | + "<uses-feature android:name=\"android.hardware.camera\"/>", | ||
50 | + "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" | ||
51 | + ] | ||
52 | + }, | ||
53 | + /* ios打包配置 */ | ||
54 | + "ios" : {}, | ||
55 | + /* SDK配置 */ | ||
56 | + "sdkConfigs" : {} | ||
57 | + } | ||
58 | + }, | ||
59 | + /* 快应用特有相关 */ | ||
60 | + "quickapp" : {}, | ||
61 | + /* 小程序特有相关 */ | ||
62 | + "mp-alipay" : { | ||
63 | + "usingComponents" : true | ||
64 | + }, | ||
65 | + "mp-baidu" : { | ||
66 | + "usingComponents" : true | ||
67 | + }, | ||
68 | + "mp-toutiao" : { | ||
69 | + "usingComponents" : true | ||
70 | + }, | ||
71 | + "uniStatistics" : { | ||
72 | + "enable" : false | ||
73 | + }, | ||
74 | + "vueVersion" : "2", | ||
75 | + "mp-weixin" : { | ||
76 | + "appid" : "wxcfaf35f5706cc64c" | ||
77 | + } | ||
78 | +} |
node_modules/.package-lock.json
0 → 100644
1 | +{ | ||
2 | + "name": "beijingtheatregoing", | ||
3 | + "version": "1.0.0", | ||
4 | + "lockfileVersion": 2, | ||
5 | + "requires": true, | ||
6 | + "packages": { | ||
7 | + "node_modules/uview-ui": { | ||
8 | + "version": "2.0.31", | ||
9 | + "resolved": "https://registry.npmmirror.com/uview-ui/-/uview-ui-2.0.31.tgz", | ||
10 | + "integrity": "sha512-I/0fGuvtiKHH/mBb864SGYk+SJ7WaF32tsBgYgeBOsxlUp+Th+Ac2tgz2cTvsQJl6eZYWsKZ3ixiSXCAcxZ8Sw==", | ||
11 | + "engines": { | ||
12 | + "HBuilderX": "^3.1.0" | ||
13 | + } | ||
14 | + } | ||
15 | + } | ||
16 | +} |
node_modules/uview-ui/LICENSE
0 → 100644
1 | +MIT License | ||
2 | + | ||
3 | +Copyright (c) 2020 www.uviewui.com | ||
4 | + | ||
5 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
6 | +of this software and associated documentation files (the "Software"), to deal | ||
7 | +in the Software without restriction, including without limitation the rights | ||
8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | +copies of the Software, and to permit persons to whom the Software is | ||
10 | +furnished to do so, subject to the following conditions: | ||
11 | + | ||
12 | +The above copyright notice and this permission notice shall be included in all | ||
13 | +copies or substantial portions of the Software. | ||
14 | + | ||
15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
21 | +SOFTWARE. |
node_modules/uview-ui/README.md
0 → 100644
1 | +<p align="center"> | ||
2 | + <img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;"> | ||
3 | +</p> | ||
4 | +<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView</h3> | ||
5 | +<h3 align="center">多平台快速开发的UI框架</h3> | ||
6 | + | ||
7 | +## 说明 | ||
8 | + | ||
9 | +uView UI,是[uni-app](https://uniapp.dcloud.io/)生态优秀的UI框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水 | ||
10 | + | ||
11 | +## 特性 | ||
12 | + | ||
13 | +- 兼容安卓,iOS,微信小程序,H5,QQ小程序,百度小程序,支付宝小程序,头条小程序 | ||
14 | +- 60+精选组件,功能丰富,多端兼容,让您快速集成,开箱即用 | ||
15 | +- 众多贴心的JS利器,让您飞镖在手,召之即来,百步穿杨 | ||
16 | +- 众多的常用页面和布局,让您专注逻辑,事半功倍 | ||
17 | +- 详尽的文档支持,现代化的演示效果 | ||
18 | +- 按需引入,精简打包体积 | ||
19 | + | ||
20 | + | ||
21 | +## 安装 | ||
22 | + | ||
23 | +```bash | ||
24 | +# npm方式安装,插件市场导入无需执行此命令 | ||
25 | +npm i uview-ui | ||
26 | +``` | ||
27 | + | ||
28 | +## 快速上手 | ||
29 | + | ||
30 | +1. `main.js`引入uView库 | ||
31 | +```js | ||
32 | +// main.js | ||
33 | +import uView from 'uview-ui'; | ||
34 | +Vue.use(uView); | ||
35 | +``` | ||
36 | + | ||
37 | +2. `App.vue`引入基础样式(注意style标签需声明scss属性支持) | ||
38 | +```css | ||
39 | +/* App.vue */ | ||
40 | +<style lang="scss"> | ||
41 | +@import "uview-ui/index.scss"; | ||
42 | +</style> | ||
43 | +``` | ||
44 | + | ||
45 | +3. `uni.scss`引入全局scss变量文件 | ||
46 | +```css | ||
47 | +/* uni.scss */ | ||
48 | +@import "uview-ui/theme.scss"; | ||
49 | +``` | ||
50 | + | ||
51 | +4. `pages.json`配置easycom规则(按需引入) | ||
52 | + | ||
53 | +```js | ||
54 | +// pages.json | ||
55 | +{ | ||
56 | + "easycom": { | ||
57 | + // npm安装的方式不需要前面的"@/",下载安装的方式需要"@/" | ||
58 | + // npm安装方式 | ||
59 | + "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue" | ||
60 | + // 下载安装方式 | ||
61 | + // "^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue" | ||
62 | + }, | ||
63 | + // 此为本身已有的内容 | ||
64 | + "pages": [ | ||
65 | + // ...... | ||
66 | + ] | ||
67 | +} | ||
68 | +``` | ||
69 | + | ||
70 | +请通过[快速上手](https://www.uviewui.com/components/quickstart.html)了解更详细的内容 | ||
71 | + | ||
72 | +## 使用方法 | ||
73 | +配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。 | ||
74 | + | ||
75 | +```html | ||
76 | +<template> | ||
77 | + <u-button text="按钮"></u-button> | ||
78 | +</template> | ||
79 | +``` | ||
80 | + | ||
81 | +请通过[快速上手](https://www.uviewui.com/components/quickstart.html)了解更详细的内容 | ||
82 | + | ||
83 | +## 链接 | ||
84 | + | ||
85 | +- [官方文档](https://www.uviewui.com/) | ||
86 | +- [更新日志](https://www.www.uviewui.com/components/changelog.html) | ||
87 | +- [升级指南](https://www.uviewui.com/components/changelog.html) | ||
88 | +- [关于我们](https://www.uviewui.com/cooperation/about.html) | ||
89 | + | ||
90 | +## 预览 | ||
91 | + | ||
92 | +您可以通过**微信**扫码,查看最佳的演示效果。 | ||
93 | +<br> | ||
94 | +<br> | ||
95 | +<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" > | ||
96 | + | ||
97 | +## 捐赠uView的研发 | ||
98 | + | ||
99 | +uView文档和源码全部开源免费,如果您认为uView帮到了您的开发工作,您可以捐赠uView的研发工作,捐赠无门槛,哪怕是一杯可乐也好(相信这比打赏主播更有意义)。 | ||
100 | + | ||
101 | +<img src="https://uviewui.com/common/alipay.png" width="220" ><img style="margin-left: 100px;" src="https://uviewui.com/common/wechat.png" width="220" > | ||
102 | + | ||
103 | +## 版权信息 | ||
104 | +uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uView应用到您的产品中。 |
node_modules/uview-ui/changelog.md
0 → 100644
1 | +## 2.0.31(2022-04-19) | ||
2 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
3 | + | ||
4 | +1. 修复`upload`在`vue`页面上传成功后没有成功标志的问题 | ||
5 | +2. 解决演示项目中微信小程序模拟上传图片一直出于上传中问题 | ||
6 | +3. 修复`u-code-input`组件在`nvue`页面编译到`app`平台上光标异常问题(`app`去除此功能) | ||
7 | +4. 修复`actionSheet`组件标题关闭按钮点击事件名称错误的问题 | ||
8 | +5. 其他修复 | ||
9 | +## 2.0.30(2022-04-04) | ||
10 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
11 | + | ||
12 | +1. `u-rate`增加`readonly`属性 | ||
13 | +2. `tabs`滑块支持设置背景图片 | ||
14 | +3. 修复`u-subsection` `mode`为`subsection`时,滑块样式不正确的问题 | ||
15 | +4. `u-code-input`添加光标效果动画 | ||
16 | +5. 修复`popup`的`open`事件不触发 | ||
17 | +6. 修复`u-flex-column`无效的问题 | ||
18 | +7. 修复`u-datetime-picker`索引在特定场合异常问题 | ||
19 | +8. 修复`u-datetime-picker`最小时间字符串模板错误问题 | ||
20 | +9. `u-swiper`添加`m3u8`验证 | ||
21 | +10. `u-swiper`修改判断image和video逻辑 | ||
22 | +11. 修复`swiper`无法使用本地图片问题,增加`type`参数 | ||
23 | +12. 修复`u-row-notice`格式错误问题 | ||
24 | +13. 修复`u-switch`组件当`unit`为`rpx`时,`nodeStyle`消失的问题 | ||
25 | +14. 修复`datetime-picker`组件`showToolbar`与`visibleItemCount`属性无效的问题 | ||
26 | +15. 修复`upload`组件条件编译位置判断错误,导致`previewImage`属性设置为`false`时,整个组件都会被隐藏的问题 | ||
27 | +16. 修复`u-checkbox-group`设置`shape`属性无效的问题 | ||
28 | +17. 修复`u-upload`的`capture`传入字符串的时候不生效的问题 | ||
29 | +18. 修复`u-action-sheet`组件,关闭事件逻辑错误的问题 | ||
30 | +19. 修复`u-list`触顶事件的触发错误的问题 | ||
31 | +20. 修复`u-text`只有手机号可拨打的问题 | ||
32 | +21. 修复`u-textarea`不能换行的问题 | ||
33 | +22. 其他修复 | ||
34 | +## 2.0.29(2022-03-13) | ||
35 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
36 | + | ||
37 | +1. 修复`u--text`组件设置`decoration`属性未生效的问题 | ||
38 | +2. 修复`u-datetime-picker`使用`formatter`后返回值不正确 | ||
39 | +3. 修复`u-datetime-picker` `intercept` 可能为undefined | ||
40 | +4. 修复已设置单位 uni..config.unit = 'rpx'时,线型指示器 `transform` 的位置翻倍,导致指示器超出宽度 | ||
41 | +5. 修复mixin中bem方法生成的类名在支付宝和字节小程序中失效 | ||
42 | +6. 修复默认值传值为空的时候,打开`u-datetime-picker`报错,不能选中第一列时间的bug | ||
43 | +7. 修复`u-datetime-picker`使用`formatter`后返回值不正确 | ||
44 | +8. 修复`u-image`组件`loading`无效果的问题 | ||
45 | +9. 修复`config.unit`属性设为`rpx`时,导航栏占用高度不足导致塌陷的问题 | ||
46 | +10. 修复`u-datetime-picker`组件`itemHeight`无效问题 | ||
47 | +11. 其他修复 | ||
48 | +## 2.0.28(2022-02-22) | ||
49 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
50 | + | ||
51 | +1. search组件新增searchIconSize属性 | ||
52 | +2. 兼容Safari/Webkit中传入时间格式如2022-02-17 12:00:56 | ||
53 | +3. 修复text value.js 判断日期出format错误问题 | ||
54 | +4. priceFormat格式化金额出现精度错误 | ||
55 | +5. priceFormat在部分情况下出现精度损失问题 | ||
56 | +6. 优化表单rules提示 | ||
57 | +7. 修复avatar组件src为空时,展示状态不对 | ||
58 | +8. 其他修复 | ||
59 | +## 2.0.27(2022-01-28) | ||
60 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
61 | + | ||
62 | +1.样式修复 | ||
63 | +## 2.0.26(2022-01-28) | ||
64 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
65 | + | ||
66 | +1.样式修复 | ||
67 | +## 2.0.25(2022-01-27) | ||
68 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
69 | + | ||
70 | +1. 修复text组件mode=price时,可能会导致精度错误的问题 | ||
71 | +2. 添加$u.setConfig()方法,可设置uView内置的config, props, zIndex, color属性,详见:[修改uView内置配置方案](https://uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE) | ||
72 | +3. 优化form组件在errorType=toast时,如果输入错误页面会有抖动的问题 | ||
73 | +4. 修复$u.addUnit()对配置默认单位可能无效的问题 | ||
74 | +## 2.0.24(2022-01-25) | ||
75 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
76 | + | ||
77 | +1. 修复swiper在current指定非0时缩放有误 | ||
78 | +2. 修复u-icon添加stop属性的时候报错 | ||
79 | +3. 优化遗留的通过正则判断rpx单位的问题 | ||
80 | +4. 优化Layout布局 vue使用gutter时,会超出固定区域 | ||
81 | +5. 优化search组件高度单位问题(rpx -> px) | ||
82 | +6. 修复u-image slot 加载和错误的图片失去了高度 | ||
83 | +7. 修复u-index-list中footer插槽与header插槽存在性判断错误 | ||
84 | +8. 修复部分机型下u-popup关闭时会闪烁 | ||
85 | +9. 修复u-image在nvue-app下失去宽高 | ||
86 | +10. 修复u-popup运行报错 | ||
87 | +11. 修复u-tooltip报错 | ||
88 | +12. 修复box-sizing在app下的警告 | ||
89 | +13. 修复u-navbar在小程序中报运行时错误 | ||
90 | +14. 其他修复 | ||
91 | +## 2.0.23(2022-01-24) | ||
92 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
93 | + | ||
94 | +1. 修复image组件在hx3.3.9的nvue下可能会显示异常的问题 | ||
95 | +2. 修复col组件gutter参数带rpx单位处理不正确的问题 | ||
96 | +3. 修复text组件单行时无法显示省略号的问题 | ||
97 | +4. navbar添加titleStyle参数 | ||
98 | +5. 升级到hx3.3.9可消除nvue下控制台样式警告的问题 | ||
99 | +## 2.0.22(2022-01-19) | ||
100 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
101 | + | ||
102 | +1. $u.page()方法优化,避免在特殊场景可能报错的问题 | ||
103 | +2. picker组件添加immediateChange参数 | ||
104 | +3. 新增$u.pages()方法 | ||
105 | +## 2.0.21(2022-01-19) | ||
106 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
107 | + | ||
108 | +1. 优化:form组件在用户设置rules的时候提示用户model必传 | ||
109 | +2. 优化遗留的通过正则判断rpx单位的问题 | ||
110 | +3. 修复微信小程序环境中tabbar组件开启safeAreaInsetBottom属性后,placeholder高度填充不正确 | ||
111 | +4. 修复swiper在current指定非0时缩放有误 | ||
112 | +5. 修复u-icon添加stop属性的时候报错 | ||
113 | +6. 修复upload组件在accept=all的时候没有作用 | ||
114 | +7. 修复在text组件mode为phone时call属性无效的问题 | ||
115 | +8. 处理u-form clearValidate方法 | ||
116 | +9. 其他修复 | ||
117 | +## 2.0.20(2022-01-14) | ||
118 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
119 | + | ||
120 | +1. 修复calendar默认会选择一个日期,如果直接点确定的话,无法取到值的问题 | ||
121 | +2. 修复Slider缺少disabled props 还有注释 | ||
122 | +3. 修复u-notice-bar点击事件无法拿到index索引值的问题 | ||
123 | +4. 修复u-collapse-item在vue文件下,app端自定义插槽不生效的问题 | ||
124 | +5. 优化头像为空时显示默认头像 | ||
125 | +6. 修复图片地址赋值后判断加载状态为完成问题 | ||
126 | +7. 修复日历滚动到默认日期月份区域 | ||
127 | +8. search组件暴露点击左边icon事件 | ||
128 | +9. 修复u-form clearValidate方法不生效 | ||
129 | +10. upload h5端增加返回文件参数(文件的name参数) | ||
130 | +11. 处理upload选择文件后url为blob类型无法预览的问题 | ||
131 | +12. u-code-input 修复输入框没有往左移出一半屏幕 | ||
132 | +13. 修复Upload上传 disabled为true时,控制台报hoverClass类型错误 | ||
133 | +14. 临时处理ios app下grid点击坍塌问题 | ||
134 | +15. 其他修复 | ||
135 | +## 2.0.19(2021-12-29) | ||
136 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
137 | + | ||
138 | +1. 优化微信小程序包体积可在微信中预览,请升级HbuilderX3.3.4,同时在“运行->运行到小程序模拟器”中勾选“运行时是否压缩代码” | ||
139 | +2. 优化微信小程序setData性能,处理某些方法如$u.route()无法在模板中使用的问题 | ||
140 | +3. navbar添加autoBack参数 | ||
141 | +4. 允许avatar组件的事件冒泡 | ||
142 | +5. 修复cell组件报错问题 | ||
143 | +6. 其他修复 | ||
144 | +## 2.0.18(2021-12-28) | ||
145 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
146 | + | ||
147 | +1. 修复app端编译报错问题 | ||
148 | +2. 重新处理微信小程序端setData过大的性能问题 | ||
149 | +3. 修复边框问题 | ||
150 | +4. 修复最大最小月份不大于0则没有数据出现的问题 | ||
151 | +5. 修复SwipeAction微信小程序端无法上下滑动问题 | ||
152 | +6. 修复input的placeholder在小程序端默认显示为true问题 | ||
153 | +7. 修复divider组件click事件无效问题 | ||
154 | +8. 修复u-code-input maxlength 属性值为 String 类型时显示异常 | ||
155 | +9. 修复当 grid只有 1到2时 在小程序端algin设置无效的问题 | ||
156 | +10. 处理form-item的label为top时,取消错误提示的左边距 | ||
157 | +11. 其他修复 | ||
158 | +## 2.0.17(2021-12-26) | ||
159 | +## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583) | ||
160 | + | ||
161 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
162 | + | ||
163 | +1. 解决HBuilderX3.3.3.20211225版本导致的样式问题 | ||
164 | +2. calendar日历添加monthNum参数 | ||
165 | +3. navbar添加center slot | ||
166 | +## 2.0.16(2021-12-25) | ||
167 | +## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583) | ||
168 | + | ||
169 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
170 | + | ||
171 | +1. 解决微信小程序setData性能问题 | ||
172 | +2. 修复count-down组件change事件不触发问题 | ||
173 | +## 2.0.15(2021-12-21) | ||
174 | +## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583) | ||
175 | + | ||
176 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
177 | + | ||
178 | +1. 修复Cell单元格titleWidth无效 | ||
179 | +2. 修复cheakbox组件ischecked不更新 | ||
180 | +3. 修复keyboard是否显示"."按键默认值问题 | ||
181 | +4. 修复number-keyboard是否显示键盘的"."符号问题 | ||
182 | +5. 修复Input输入框 readonly无效 | ||
183 | +6. 修复u-avatar 导致打包app、H5时候报错问题 | ||
184 | +7. 修复Upload上传deletable无效 | ||
185 | +8. 修复upload当设置maxSize时无效的问题 | ||
186 | +9. 修复tabs lineWidth传入带单位的字符串的时候偏移量计算错误问题 | ||
187 | +10. 修复rate组件在有padding的view内,显示的星星位置和可触摸区域不匹配,无法正常选中星星 | ||
188 | +## 2.0.13(2021-12-14) | ||
189 | +## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY) | ||
190 | + | ||
191 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
192 | + | ||
193 | +1. 修复配置默认单位为rpx可能会导致自定义导航栏高度异常的问题 | ||
194 | +## 2.0.12(2021-12-14) | ||
195 | +## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY) | ||
196 | + | ||
197 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
198 | + | ||
199 | +1. 修复tabs组件在vue环境下划线消失的问题 | ||
200 | +2. 修复upload组件在安卓小程序无法选择视频的问题 | ||
201 | +3. 添加uni.$u.config.unit配置,用于配置参数默认单位,详见:[默认单位配置](https://www.uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE) | ||
202 | +4. 修复textarea组件在没绑定v-model时,字符统计不生效问题 | ||
203 | +5. 修复nvue下控制是否出现滚动条失效问题 | ||
204 | +## 2.0.11(2021-12-13) | ||
205 | +## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY) | ||
206 | + | ||
207 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
208 | + | ||
209 | +1. text组件align参数无效的问题 | ||
210 | +2. subsection组件添加keyName参数 | ||
211 | +3. upload组件无法判断[Object file]类型的问题 | ||
212 | +4. 处理notify层级过低问题 | ||
213 | +5. codeInput组件添加disabledDot参数 | ||
214 | +6. 处理actionSheet组件round参数无效的问题 | ||
215 | +7. calendar组件添加round参数用于控制圆角值 | ||
216 | +8. 处理swipeAction组件在vue环境下默认被打开的问题 | ||
217 | +9. button组件的throttleTime节流参数无效的问题 | ||
218 | +10. 解决u-notify手动关闭方法close()无效的问题 | ||
219 | +11. input组件readonly不生效问题 | ||
220 | +12. tag组件type参数为info不生效问题 | ||
221 | +## 2.0.10(2021-12-08) | ||
222 | +## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY) | ||
223 | + | ||
224 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
225 | + | ||
226 | +1. 修复button sendMessagePath属性不生效 | ||
227 | +2. 修复DatetimePicker选择器title无效 | ||
228 | +3. 修复u-toast设置loading=true不生效 | ||
229 | +4. 修复u-text金额模式传0报错 | ||
230 | +5. 修复u-toast组件的icon属性配置不生效 | ||
231 | +6. button的icon在特殊场景下的颜色优化 | ||
232 | +7. IndexList优化,增加# | ||
233 | +## 2.0.9(2021-12-01) | ||
234 | +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) | ||
235 | + | ||
236 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
237 | + | ||
238 | +1. 优化swiper的height支持100%值(仅vue有效),修复嵌入视频时click事件无法触发的问题 | ||
239 | +2. 优化tabs组件对list值为空的判断,或者动态变化list时重新计算相关尺寸的问题 | ||
240 | +3. 优化datetime-picker组件逻辑,让其后续打开的默认值为上一次的选中值,需要通过v-model绑定值才有效 | ||
241 | +4. 修复upload内嵌在其他组件中,选择图片可能不会换行的问题 | ||
242 | +## 2.0.8(2021-12-01) | ||
243 | +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) | ||
244 | + | ||
245 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
246 | + | ||
247 | +1. 修复toast的position参数无效问题 | ||
248 | +2. 处理input在ios nvue上无法获得焦点的问题 | ||
249 | +3. avatar-group组件添加extraValue参数,让剩余展示数量可手动控制 | ||
250 | +4. tabs组件添加keyName参数用于配置从对象中读取的键名 | ||
251 | +5. 处理text组件名字脱敏默认配置无效的问题 | ||
252 | +6. 处理picker组件item文本太长换行问题 | ||
253 | +## 2.0.7(2021-11-30) | ||
254 | +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) | ||
255 | + | ||
256 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
257 | + | ||
258 | +1. 修复radio和checkbox动态改变v-model无效的问题。 | ||
259 | +2. 优化form规则validator在微信小程序用法 | ||
260 | +3. 修复backtop组件mode参数在微信小程序无效的问题 | ||
261 | +4. 处理Album的previewFullImage属性无效的问题 | ||
262 | +5. 处理u-datetime-picker组件mode='time'在选择改变时间时,控制台报错的问题 | ||
263 | +## 2.0.6(2021-11-27) | ||
264 | +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) | ||
265 | + | ||
266 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
267 | + | ||
268 | +1. 处理tag组件在vue下边框无效的问题。 | ||
269 | +2. 处理popup组件圆角参数可能无效的问题。 | ||
270 | +3. 处理tabs组件lineColor参数可能无效的问题。 | ||
271 | +4. propgress组件在值很小时,显示异常的问题。 | ||
272 | +## 2.0.5(2021-11-25) | ||
273 | +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) | ||
274 | + | ||
275 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
276 | + | ||
277 | +1. calendar在vue下显示异常问题。 | ||
278 | +2. form组件labelPosition和errorType参数无效的问题 | ||
279 | +3. input组件inputAlign无效的问题 | ||
280 | +4. 其他一些修复 | ||
281 | +## 2.0.4(2021-11-23) | ||
282 | +## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU) | ||
283 | + | ||
284 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
285 | + | ||
286 | +0. input组件缺失@confirm事件,以及subfix和prefix无效问题 | ||
287 | +1. component.scss文件样式在vue下干扰全局布局问题 | ||
288 | +2. 修复subsection在vue环境下表现异常的问题 | ||
289 | +3. tag组件的bgColor等参数无效的问题 | ||
290 | +4. upload组件不换行的问题 | ||
291 | +5. 其他的一些修复处理 | ||
292 | +## 2.0.3(2021-11-16) | ||
293 | +## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU) | ||
294 | + | ||
295 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
296 | + | ||
297 | +1. uView2.0已实现全面兼容nvue | ||
298 | +2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升 | ||
299 | +3. 目前uView2.0为公测阶段,相关细节可能会有变动 | ||
300 | +4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html) | ||
301 | +5. 处理modal的confirm回调事件拼写错误问题 | ||
302 | +6. 处理input组件@input事件参数错误问题 | ||
303 | +7. 其他一些修复 | ||
304 | +## 2.0.2(2021-11-16) | ||
305 | +## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU) | ||
306 | + | ||
307 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
308 | + | ||
309 | +1. uView2.0已实现全面兼容nvue | ||
310 | +2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升 | ||
311 | +3. 目前uView2.0为公测阶段,相关细节可能会有变动 | ||
312 | +4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html) | ||
313 | +5. 修复input组件formatter参数缺失问题 | ||
314 | +6. 优化loading-icon组件的scss写法问题,防止不兼容新版本scss | ||
315 | +## 2.0.0(2020-11-15) | ||
316 | +## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU) | ||
317 | + | ||
318 | +# uView2.0重磅发布,利剑出鞘,一统江湖 | ||
319 | + | ||
320 | +1. uView2.0已实现全面兼容nvue | ||
321 | +2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升 | ||
322 | +3. 目前uView2.0为公测阶段,相关细节可能会有变动 | ||
323 | +4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html) | ||
324 | +5. 修复input组件formatter参数缺失问题 | ||
325 | + | ||
326 | + |
1 | +<template> | ||
2 | + <uvForm | ||
3 | + ref="uForm" | ||
4 | + :model="model" | ||
5 | + :rules="rules" | ||
6 | + :errorType="errorType" | ||
7 | + :borderBottom="borderBottom" | ||
8 | + :labelPosition="labelPosition" | ||
9 | + :labelWidth="labelWidth" | ||
10 | + :labelAlign="labelAlign" | ||
11 | + :labelStyle="labelStyle" | ||
12 | + :customStyle="customStyle" | ||
13 | + > | ||
14 | + <slot /> | ||
15 | + </uvForm> | ||
16 | +</template> | ||
17 | + | ||
18 | +<script> | ||
19 | + /** | ||
20 | + * 此组件存在的理由是,在nvue下,u-form被uni-app官方占用了,u-form在nvue中相当于form组件 | ||
21 | + * 所以在nvue下,取名为u--form,内部其实还是u-form.vue,只不过做一层中转 | ||
22 | + */ | ||
23 | + import uvForm from '../u-form/u-form.vue'; | ||
24 | + import props from '../u-form/props.js' | ||
25 | + export default { | ||
26 | + // #ifdef MP-WEIXIN | ||
27 | + name: 'u-form', | ||
28 | + // #endif | ||
29 | + // #ifndef MP-WEIXIN | ||
30 | + name: 'u--form', | ||
31 | + // #endif | ||
32 | + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], | ||
33 | + components: { | ||
34 | + uvForm | ||
35 | + }, | ||
36 | + created() { | ||
37 | + this.children = [] | ||
38 | + }, | ||
39 | + methods: { | ||
40 | + // 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则 | ||
41 | + setRules(rules) { | ||
42 | + this.$refs.uForm.setRules(rules) | ||
43 | + }, | ||
44 | + validate() { | ||
45 | + /** | ||
46 | + * 在微信小程序中,通过this.$parent拿到的父组件是u--form,而不是其内嵌的u-form | ||
47 | + * 导致在u-form组件中,拿不到对应的children数组,从而校验无效,所以这里每次调用u-form组件中的 | ||
48 | + * 对应方法的时候,在小程序中都先将u--form的children赋值给u-form中的children | ||
49 | + */ | ||
50 | + // #ifdef MP-WEIXIN | ||
51 | + this.setMpData() | ||
52 | + // #endif | ||
53 | + return this.$refs.uForm.validate() | ||
54 | + }, | ||
55 | + validateField(value, callback) { | ||
56 | + // #ifdef MP-WEIXIN | ||
57 | + this.setMpData() | ||
58 | + // #endif | ||
59 | + return this.$refs.uForm.validateField(value, callback) | ||
60 | + }, | ||
61 | + resetFields() { | ||
62 | + // #ifdef MP-WEIXIN | ||
63 | + this.setMpData() | ||
64 | + // #endif | ||
65 | + return this.$refs.uForm.resetFields() | ||
66 | + }, | ||
67 | + clearValidate(props) { | ||
68 | + // #ifdef MP-WEIXIN | ||
69 | + this.setMpData() | ||
70 | + // #endif | ||
71 | + return this.$refs.uForm.clearValidate(props) | ||
72 | + }, | ||
73 | + setMpData() { | ||
74 | + this.$refs.uForm.children = this.children | ||
75 | + } | ||
76 | + }, | ||
77 | + } | ||
78 | +</script> |
1 | +<template> | ||
2 | + <uvImage | ||
3 | + :src="src" | ||
4 | + :mode="mode" | ||
5 | + :width="width" | ||
6 | + :height="height" | ||
7 | + :shape="shape" | ||
8 | + :radius="radius" | ||
9 | + :lazyLoad="lazyLoad" | ||
10 | + :showMenuByLongpress="showMenuByLongpress" | ||
11 | + :loadingIcon="loadingIcon" | ||
12 | + :errorIcon="errorIcon" | ||
13 | + :showLoading="showLoading" | ||
14 | + :showError="showError" | ||
15 | + :fade="fade" | ||
16 | + :webp="webp" | ||
17 | + :duration="duration" | ||
18 | + :bgColor="bgColor" | ||
19 | + :customStyle="customStyle" | ||
20 | + @click="$emit('click')" | ||
21 | + @error="$emit('error')" | ||
22 | + @load="$emit('load')" | ||
23 | + > | ||
24 | + <template v-slot:loading> | ||
25 | + <slot name="loading"></slot> | ||
26 | + </template> | ||
27 | + <template v-slot:error> | ||
28 | + <slot name="error"></slot> | ||
29 | + </template> | ||
30 | + </uvImage> | ||
31 | +</template> | ||
32 | + | ||
33 | +<script> | ||
34 | + /** | ||
35 | + * 此组件存在的理由是,在nvue下,u-image被uni-app官方占用了,u-image在nvue中相当于image组件 | ||
36 | + * 所以在nvue下,取名为u--image,内部其实还是u-iamge.vue,只不过做一层中转 | ||
37 | + */ | ||
38 | + import uvImage from '../u-image/u-image.vue'; | ||
39 | + import props from '../u-image/props.js'; | ||
40 | + export default { | ||
41 | + name: 'u--image', | ||
42 | + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], | ||
43 | + components: { | ||
44 | + uvImage | ||
45 | + }, | ||
46 | + } | ||
47 | +</script> |
1 | +<template> | ||
2 | + <uvInput | ||
3 | + :value="value" | ||
4 | + :type="type" | ||
5 | + :fixed="fixed" | ||
6 | + :disabled="disabled" | ||
7 | + :disabledColor="disabledColor" | ||
8 | + :clearable="clearable" | ||
9 | + :password="password" | ||
10 | + :maxlength="maxlength" | ||
11 | + :placeholder="placeholder" | ||
12 | + :placeholderClass="placeholderClass" | ||
13 | + :placeholderStyle="placeholderStyle" | ||
14 | + :showWordLimit="showWordLimit" | ||
15 | + :confirmType="confirmType" | ||
16 | + :confirmHold="confirmHold" | ||
17 | + :holdKeyboard="holdKeyboard" | ||
18 | + :focus="focus" | ||
19 | + :autoBlur="autoBlur" | ||
20 | + :disableDefaultPadding="disableDefaultPadding" | ||
21 | + :cursor="cursor" | ||
22 | + :cursorSpacing="cursorSpacing" | ||
23 | + :selectionStart="selectionStart" | ||
24 | + :selectionEnd="selectionEnd" | ||
25 | + :adjustPosition="adjustPosition" | ||
26 | + :inputAlign="inputAlign" | ||
27 | + :fontSize="fontSize" | ||
28 | + :color="color" | ||
29 | + :prefixIcon="prefixIcon" | ||
30 | + :suffixIcon="suffixIcon" | ||
31 | + :suffixIconStyle="suffixIconStyle" | ||
32 | + :prefixIconStyle="prefixIconStyle" | ||
33 | + :border="border" | ||
34 | + :readonly="readonly" | ||
35 | + :shape="shape" | ||
36 | + :customStyle="customStyle" | ||
37 | + :formatter="formatter" | ||
38 | + @focus="$emit('focus')" | ||
39 | + @blur="$emit('blur')" | ||
40 | + @keyboardheightchange="$emit('keyboardheightchange')" | ||
41 | + @change="e => $emit('change', e)" | ||
42 | + @input="e => $emit('input', e)" | ||
43 | + @confirm="e => $emit('confirm', e)" | ||
44 | + @clear="$emit('clear')" | ||
45 | + @click="$emit('click')" | ||
46 | + > | ||
47 | + <!-- #ifdef MP --> | ||
48 | + <slot name="prefix"></slot> | ||
49 | + <slot name="suffix"></slot> | ||
50 | + <!-- #endif --> | ||
51 | + <!-- #ifndef MP --> | ||
52 | + <slot name="prefix" slot="prefix"></slot> | ||
53 | + <slot name="suffix" slot="suffix"></slot> | ||
54 | + <!-- #endif --> | ||
55 | + </uvInput> | ||
56 | +</template> | ||
57 | + | ||
58 | +<script> | ||
59 | + /** | ||
60 | + * 此组件存在的理由是,在nvue下,u-input被uni-app官方占用了,u-input在nvue中相当于input组件 | ||
61 | + * 所以在nvue下,取名为u--input,内部其实还是u-input.vue,只不过做一层中转 | ||
62 | + */ | ||
63 | + import uvInput from '../u-input/u-input.vue'; | ||
64 | + import props from '../u-input/props.js' | ||
65 | + export default { | ||
66 | + name: 'u--input', | ||
67 | + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], | ||
68 | + components: { | ||
69 | + uvInput | ||
70 | + }, | ||
71 | + } | ||
72 | +</script> |
1 | +<template> | ||
2 | + <uvText | ||
3 | + :type="type" | ||
4 | + :show="show" | ||
5 | + :text="text" | ||
6 | + :prefixIcon="prefixIcon" | ||
7 | + :suffixIcon="suffixIcon" | ||
8 | + :mode="mode" | ||
9 | + :href="href" | ||
10 | + :format="format" | ||
11 | + :call="call" | ||
12 | + :openType="openType" | ||
13 | + :bold="bold" | ||
14 | + :block="block" | ||
15 | + :lines="lines" | ||
16 | + :color="color" | ||
17 | + :decoration="decoration" | ||
18 | + :size="size" | ||
19 | + :iconStyle="iconStyle" | ||
20 | + :margin="margin" | ||
21 | + :lineHeight="lineHeight" | ||
22 | + :align="align" | ||
23 | + :wordWrap="wordWrap" | ||
24 | + :customStyle="customStyle" | ||
25 | + @click="$emit('click')" | ||
26 | + ></uvText> | ||
27 | +</template> | ||
28 | + | ||
29 | +<script> | ||
30 | +/** | ||
31 | + * 此组件存在的理由是,在nvue下,u-text被uni-app官方占用了,u-text在nvue中相当于input组件 | ||
32 | + * 所以在nvue下,取名为u--input,内部其实还是u-text.vue,只不过做一层中转 | ||
33 | + * 不使用v-bind="$attrs",而是分开独立写传参,是因为微信小程序不支持此写法 | ||
34 | + */ | ||
35 | +import uvText from "../u-text/u-text.vue"; | ||
36 | +import props from "../u-text/props.js"; | ||
37 | +export default { | ||
38 | + name: "u--text", | ||
39 | + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], | ||
40 | + components: { | ||
41 | + uvText, | ||
42 | + }, | ||
43 | +}; | ||
44 | +</script> |
1 | +<template> | ||
2 | + <uvTextarea | ||
3 | + :value="value" | ||
4 | + :placeholder="placeholder" | ||
5 | + :height="height" | ||
6 | + :confirmType="confirmType" | ||
7 | + :disabled="disabled" | ||
8 | + :count="count" | ||
9 | + :focus="focus" | ||
10 | + :autoHeight="autoHeight" | ||
11 | + :fixed="fixed" | ||
12 | + :cursorSpacing="cursorSpacing" | ||
13 | + :cursor="cursor" | ||
14 | + :showConfirmBar="showConfirmBar" | ||
15 | + :selectionStart="selectionStart" | ||
16 | + :selectionEnd="selectionEnd" | ||
17 | + :adjustPosition="adjustPosition" | ||
18 | + :disableDefaultPadding="disableDefaultPadding" | ||
19 | + :holdKeyboard="holdKeyboard" | ||
20 | + :maxlength="maxlength" | ||
21 | + :border="border" | ||
22 | + :customStyle="customStyle" | ||
23 | + :formatter="formatter" | ||
24 | + @focus="e => $emit('focus')" | ||
25 | + @blur="e => $emit('blur')" | ||
26 | + @linechange="e => $emit('linechange', e)" | ||
27 | + @confirm="e => $emit('confirm')" | ||
28 | + @input="e => $emit('input', e)" | ||
29 | + @keyboardheightchange="e => $emit('keyboardheightchange')" | ||
30 | + ></uvTextarea> | ||
31 | +</template> | ||
32 | + | ||
33 | +<script> | ||
34 | + /** | ||
35 | + * 此组件存在的理由是,在nvue下,u--textarea被uni-app官方占用了,u-textarea在nvue中相当于textarea组件 | ||
36 | + * 所以在nvue下,取名为u--textarea,内部其实还是u-textarea.vue,只不过做一层中转 | ||
37 | + */ | ||
38 | + import uvTextarea from '../u-textarea/u-textarea.vue'; | ||
39 | + import props from '../u-textarea/props.js' | ||
40 | + export default { | ||
41 | + name: 'u--textarea', | ||
42 | + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], | ||
43 | + components: { | ||
44 | + uvTextarea | ||
45 | + }, | ||
46 | + } | ||
47 | +</script> |
1 | +export default { | ||
2 | + props: { | ||
3 | + // 操作菜单是否展示 (默认false) | ||
4 | + show: { | ||
5 | + type: Boolean, | ||
6 | + default: uni.$u.props.actionSheet.show | ||
7 | + }, | ||
8 | + // 标题 | ||
9 | + title: { | ||
10 | + type: String, | ||
11 | + default: uni.$u.props.actionSheet.title | ||
12 | + }, | ||
13 | + // 选项上方的描述信息 | ||
14 | + description: { | ||
15 | + type: String, | ||
16 | + default: uni.$u.props.actionSheet.description | ||
17 | + }, | ||
18 | + // 数据 | ||
19 | + actions: { | ||
20 | + type: Array, | ||
21 | + default: uni.$u.props.actionSheet.actions | ||
22 | + }, | ||
23 | + // 取消按钮的文字,不为空时显示按钮 | ||
24 | + cancelText: { | ||
25 | + type: String, | ||
26 | + default: uni.$u.props.actionSheet.cancelText | ||
27 | + }, | ||
28 | + // 点击某个菜单项时是否关闭弹窗 | ||
29 | + closeOnClickAction: { | ||
30 | + type: Boolean, | ||
31 | + default: uni.$u.props.actionSheet.closeOnClickAction | ||
32 | + }, | ||
33 | + // 处理底部安全区(默认true) | ||
34 | + safeAreaInsetBottom: { | ||
35 | + type: Boolean, | ||
36 | + default: uni.$u.props.actionSheet.safeAreaInsetBottom | ||
37 | + }, | ||
38 | + // 小程序的打开方式 | ||
39 | + openType: { | ||
40 | + type: String, | ||
41 | + default: uni.$u.props.actionSheet.openType | ||
42 | + }, | ||
43 | + // 点击遮罩是否允许关闭 (默认true) | ||
44 | + closeOnClickOverlay: { | ||
45 | + type: Boolean, | ||
46 | + default: uni.$u.props.actionSheet.closeOnClickOverlay | ||
47 | + }, | ||
48 | + // 圆角值 | ||
49 | + round: { | ||
50 | + type: [Boolean, String, Number], | ||
51 | + default: uni.$u.props.actionSheet.round | ||
52 | + } | ||
53 | + } | ||
54 | +} |
1 | + | ||
2 | +<template> | ||
3 | + <u-popup | ||
4 | + :show="show" | ||
5 | + mode="bottom" | ||
6 | + @close="closeHandler" | ||
7 | + :safeAreaInsetBottom="safeAreaInsetBottom" | ||
8 | + :round="round" | ||
9 | + > | ||
10 | + <view class="u-action-sheet"> | ||
11 | + <view | ||
12 | + class="u-action-sheet__header" | ||
13 | + v-if="title" | ||
14 | + > | ||
15 | + <text class="u-action-sheet__header__title u-line-1">{{title}}</text> | ||
16 | + <view | ||
17 | + class="u-action-sheet__header__icon-wrap" | ||
18 | + @tap.stop="cancel" | ||
19 | + > | ||
20 | + <u-icon | ||
21 | + name="close" | ||
22 | + size="17" | ||
23 | + color="#c8c9cc" | ||
24 | + bold | ||
25 | + ></u-icon> | ||
26 | + </view> | ||
27 | + </view> | ||
28 | + <text | ||
29 | + class="u-action-sheet__description" | ||
30 | + :style="[{ | ||
31 | + marginTop: `${title && description ? 0 : '18px'}` | ||
32 | + }]" | ||
33 | + v-if="description" | ||
34 | + >{{description}}</text> | ||
35 | + <slot> | ||
36 | + <u-line v-if="description"></u-line> | ||
37 | + <view class="u-action-sheet__item-wrap"> | ||
38 | + <template v-for="(item, index) in actions"> | ||
39 | + <!-- #ifdef MP --> | ||
40 | + <button | ||
41 | + :key="index" | ||
42 | + class="u-reset-button" | ||
43 | + :openType="item.openType" | ||
44 | + @getuserinfo="onGetUserInfo" | ||
45 | + @contact="onContact" | ||
46 | + @getphonenumber="onGetPhoneNumber" | ||
47 | + @error="onError" | ||
48 | + @launchapp="onLaunchApp" | ||
49 | + @opensetting="onOpenSetting" | ||
50 | + :lang="lang" | ||
51 | + :session-from="sessionFrom" | ||
52 | + :send-message-title="sendMessageTitle" | ||
53 | + :send-message-path="sendMessagePath" | ||
54 | + :send-message-img="sendMessageImg" | ||
55 | + :show-message-card="showMessageCard" | ||
56 | + :app-parameter="appParameter" | ||
57 | + @tap="selectHandler(index)" | ||
58 | + :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''" | ||
59 | + > | ||
60 | + <!-- #endif --> | ||
61 | + <view | ||
62 | + class="u-action-sheet__item-wrap__item" | ||
63 | + @tap.stop="selectHandler(index)" | ||
64 | + :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''" | ||
65 | + :hover-stay-time="150" | ||
66 | + > | ||
67 | + <template v-if="!item.loading"> | ||
68 | + <text | ||
69 | + class="u-action-sheet__item-wrap__item__name" | ||
70 | + :style="[itemStyle(index)]" | ||
71 | + >{{ item.name }}</text> | ||
72 | + <text | ||
73 | + v-if="item.subname" | ||
74 | + class="u-action-sheet__item-wrap__item__subname" | ||
75 | + >{{ item.subname }}</text> | ||
76 | + </template> | ||
77 | + <u-loading-icon | ||
78 | + v-else | ||
79 | + custom-class="van-action-sheet__loading" | ||
80 | + size="18" | ||
81 | + mode="circle" | ||
82 | + /> | ||
83 | + </view> | ||
84 | + <!-- #ifdef MP --> | ||
85 | + </button> | ||
86 | + <!-- #endif --> | ||
87 | + <u-line v-if="index !== actions.length - 1"></u-line> | ||
88 | + </template> | ||
89 | + </view> | ||
90 | + </slot> | ||
91 | + <u-gap | ||
92 | + bgColor="#eaeaec" | ||
93 | + height="6" | ||
94 | + v-if="cancelText" | ||
95 | + ></u-gap> | ||
96 | + <view hover-class="u-action-sheet--hover"> | ||
97 | + <text | ||
98 | + @touchmove.stop.prevent | ||
99 | + :hover-stay-time="150" | ||
100 | + v-if="cancelText" | ||
101 | + class="u-action-sheet__cancel-text" | ||
102 | + @tap="cancel" | ||
103 | + >{{cancelText}}</text> | ||
104 | + </view> | ||
105 | + </view> | ||
106 | + </u-popup> | ||
107 | +</template> | ||
108 | + | ||
109 | +<script> | ||
110 | + import openType from '../../libs/mixin/openType' | ||
111 | + import button from '../../libs/mixin/button' | ||
112 | + import props from './props.js'; | ||
113 | + /** | ||
114 | + * ActionSheet 操作菜单 | ||
115 | + * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。 | ||
116 | + * @tutorial https://www.uviewui.com/components/actionSheet.html | ||
117 | + * | ||
118 | + * @property {Boolean} show 操作菜单是否展示 (默认 false ) | ||
119 | + * @property {String} title 操作菜单标题 | ||
120 | + * @property {String} description 选项上方的描述信息 | ||
121 | + * @property {Array<Object>} actions 按钮的文字数组,见官方文档示例 | ||
122 | + * @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮 | ||
123 | + * @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 (默认 true ) | ||
124 | + * @property {Boolean} safeAreaInsetBottom 处理底部安全区 (默认 true ) | ||
125 | + * @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error ) | ||
126 | + * @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true ) | ||
127 | + * @property {Number|String} round 圆角值,默认无圆角 (默认 0 ) | ||
128 | + * @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文 | ||
129 | + * @property {String} sessionFrom 会话来源,openType="contact"时有效 | ||
130 | + * @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效 | ||
131 | + * @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效 | ||
132 | + * @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效 | ||
133 | + * @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false ) | ||
134 | + * @property {String} appParameter 打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效 | ||
135 | + * | ||
136 | + * @event {Function} select 点击ActionSheet列表项时触发 | ||
137 | + * @event {Function} close 点击取消按钮时触发 | ||
138 | + * @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效 | ||
139 | + * @event {Function} contact 客服消息回调,openType="contact"时有效 | ||
140 | + * @event {Function} getphonenumber 获取用户手机号回调,openType="getPhoneNumber"时有效 | ||
141 | + * @event {Function} error 当使用开放能力时,发生错误的回调,openType="error"时有效 | ||
142 | + * @event {Function} launchapp 打开 APP 成功的回调,openType="launchApp"时有效 | ||
143 | + * @event {Function} opensetting 在打开授权设置页后回调,openType="openSetting"时有效 | ||
144 | + * @example <u-action-sheet :actions="list" :title="title" :show="show"></u-action-sheet> | ||
145 | + */ | ||
146 | + export default { | ||
147 | + name: "u-action-sheet", | ||
148 | + // 一些props参数和methods方法,通过mixin混入,因为其他文件也会用到 | ||
149 | + mixins: [openType, button, uni.$u.mixin, props], | ||
150 | + data() { | ||
151 | + return { | ||
152 | + | ||
153 | + } | ||
154 | + }, | ||
155 | + computed: { | ||
156 | + // 操作项目的样式 | ||
157 | + itemStyle() { | ||
158 | + return (index) => { | ||
159 | + let style = {}; | ||
160 | + if (this.actions[index].color) style.color = this.actions[index].color | ||
161 | + if (this.actions[index].fontSize) style.fontSize = uni.$u.addUnit(this.actions[index].fontSize) | ||
162 | + // 选项被禁用的样式 | ||
163 | + if (this.actions[index].disabled) style.color = '#c0c4cc' | ||
164 | + return style; | ||
165 | + } | ||
166 | + }, | ||
167 | + }, | ||
168 | + methods: { | ||
169 | + closeHandler() { | ||
170 | + // 允许点击遮罩关闭时,才发出close事件 | ||
171 | + if(this.closeOnClickOverlay) { | ||
172 | + this.$emit('close') | ||
173 | + } | ||
174 | + }, | ||
175 | + // 点击取消按钮 | ||
176 | + cancel() { | ||
177 | + this.$emit('close') | ||
178 | + }, | ||
179 | + selectHandler(index) { | ||
180 | + const item = this.actions[index] | ||
181 | + if (item && !item.disabled && !item.loading) { | ||
182 | + this.$emit('select', item) | ||
183 | + if (this.closeOnClickAction) { | ||
184 | + this.$emit('close') | ||
185 | + } | ||
186 | + } | ||
187 | + }, | ||
188 | + } | ||
189 | + } | ||
190 | +</script> | ||
191 | + | ||
192 | +<style lang="scss" scoped> | ||
193 | + @import "../../libs/css/components.scss"; | ||
194 | + $u-action-sheet-reset-button-width:100% !default; | ||
195 | + $u-action-sheet-title-font-size: 16px !default; | ||
196 | + $u-action-sheet-title-padding: 12px 30px !default; | ||
197 | + $u-action-sheet-title-color: $u-main-color !default; | ||
198 | + $u-action-sheet-header-icon-wrap-right:15px !default; | ||
199 | + $u-action-sheet-header-icon-wrap-top:15px !default; | ||
200 | + $u-action-sheet-description-font-size:13px !default; | ||
201 | + $u-action-sheet-description-color:14px !default; | ||
202 | + $u-action-sheet-description-margin: 18px 15px !default; | ||
203 | + $u-action-sheet-item-wrap-item-padding:15px !default; | ||
204 | + $u-action-sheet-item-wrap-name-font-size:16px !default; | ||
205 | + $u-action-sheet-item-wrap-subname-font-size:13px !default; | ||
206 | + $u-action-sheet-item-wrap-subname-color: #c0c4cc !default; | ||
207 | + $u-action-sheet-item-wrap-subname-margin-top:10px !default; | ||
208 | + $u-action-sheet-cancel-text-font-size:16px !default; | ||
209 | + $u-action-sheet-cancel-text-color:$u-content-color !default; | ||
210 | + $u-action-sheet-cancel-text-font-size:15px !default; | ||
211 | + $u-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default; | ||
212 | + | ||
213 | + .u-reset-button { | ||
214 | + width: $u-action-sheet-reset-button-width; | ||
215 | + } | ||
216 | + | ||
217 | + .u-action-sheet { | ||
218 | + text-align: center; | ||
219 | + &__header { | ||
220 | + position: relative; | ||
221 | + padding: $u-action-sheet-title-padding; | ||
222 | + &__title { | ||
223 | + font-size: $u-action-sheet-title-font-size; | ||
224 | + color: $u-action-sheet-title-color; | ||
225 | + font-weight: bold; | ||
226 | + text-align: center; | ||
227 | + } | ||
228 | + | ||
229 | + &__icon-wrap { | ||
230 | + position: absolute; | ||
231 | + right: $u-action-sheet-header-icon-wrap-right; | ||
232 | + top: $u-action-sheet-header-icon-wrap-top; | ||
233 | + } | ||
234 | + } | ||
235 | + | ||
236 | + &__description { | ||
237 | + font-size: $u-action-sheet-description-font-size; | ||
238 | + color: $u-tips-color; | ||
239 | + margin: $u-action-sheet-description-margin; | ||
240 | + text-align: center; | ||
241 | + } | ||
242 | + | ||
243 | + &__item-wrap { | ||
244 | + | ||
245 | + &__item { | ||
246 | + padding: $u-action-sheet-item-wrap-item-padding; | ||
247 | + @include flex; | ||
248 | + align-items: center; | ||
249 | + justify-content: center; | ||
250 | + flex-direction: column; | ||
251 | + | ||
252 | + &__name { | ||
253 | + font-size: $u-action-sheet-item-wrap-name-font-size; | ||
254 | + color: $u-main-color; | ||
255 | + text-align: center; | ||
256 | + } | ||
257 | + | ||
258 | + &__subname { | ||
259 | + font-size: $u-action-sheet-item-wrap-subname-font-size; | ||
260 | + color: $u-action-sheet-item-wrap-subname-color; | ||
261 | + margin-top: $u-action-sheet-item-wrap-subname-margin-top; | ||
262 | + text-align: center; | ||
263 | + } | ||
264 | + } | ||
265 | + } | ||
266 | + | ||
267 | + &__cancel-text { | ||
268 | + font-size: $u-action-sheet-cancel-text-font-size; | ||
269 | + color: $u-action-sheet-cancel-text-color; | ||
270 | + text-align: center; | ||
271 | + padding: $u-action-sheet-cancel-text-font-size; | ||
272 | + } | ||
273 | + | ||
274 | + &--hover { | ||
275 | + background-color: $u-action-sheet-cancel-text-hover-background-color; | ||
276 | + } | ||
277 | + } | ||
278 | +</style> |
1 | +export default { | ||
2 | + props: { | ||
3 | + // 图片地址,Array<String>|Array<Object>形式 | ||
4 | + urls: { | ||
5 | + type: Array, | ||
6 | + default: uni.$u.props.album.urls | ||
7 | + }, | ||
8 | + // 指定从数组的对象元素中读取哪个属性作为图片地址 | ||
9 | + keyName: { | ||
10 | + type: String, | ||
11 | + default: uni.$u.props.album.keyName | ||
12 | + }, | ||
13 | + // 单图时,图片长边的长度 | ||
14 | + singleSize: { | ||
15 | + type: [String, Number], | ||
16 | + default: uni.$u.props.album.singleSize | ||
17 | + }, | ||
18 | + // 多图时,图片边长 | ||
19 | + multipleSize: { | ||
20 | + type: [String, Number], | ||
21 | + default: uni.$u.props.album.multipleSize | ||
22 | + }, | ||
23 | + // 多图时,图片水平和垂直之间的间隔 | ||
24 | + space: { | ||
25 | + type: [String, Number], | ||
26 | + default: uni.$u.props.album.space | ||
27 | + }, | ||
28 | + // 单图时,图片缩放裁剪的模式 | ||
29 | + singleMode: { | ||
30 | + type: String, | ||
31 | + default: uni.$u.props.album.singleMode | ||
32 | + }, | ||
33 | + // 多图时,图片缩放裁剪的模式 | ||
34 | + multipleMode: { | ||
35 | + type: String, | ||
36 | + default: uni.$u.props.album.multipleMode | ||
37 | + }, | ||
38 | + // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量 | ||
39 | + maxCount: { | ||
40 | + type: [String, Number], | ||
41 | + default: uni.$u.props.album.maxCount | ||
42 | + }, | ||
43 | + // 是否可以预览图片 | ||
44 | + previewFullImage: { | ||
45 | + type: Boolean, | ||
46 | + default: uni.$u.props.album.previewFullImage | ||
47 | + }, | ||
48 | + // 每行展示图片数量,如设置,singleSize和multipleSize将会无效 | ||
49 | + rowCount: { | ||
50 | + type: [String, Number], | ||
51 | + default: uni.$u.props.album.rowCount | ||
52 | + }, | ||
53 | + // 超出maxCount时是否显示查看更多的提示 | ||
54 | + showMore: { | ||
55 | + type: Boolean, | ||
56 | + default: uni.$u.props.album.showMore | ||
57 | + } | ||
58 | + } | ||
59 | +} |
1 | +<template> | ||
2 | + <view class="u-album"> | ||
3 | + <view | ||
4 | + class="u-album__row" | ||
5 | + ref="u-album__row" | ||
6 | + v-for="(arr, index) in showUrls" | ||
7 | + :forComputedUse="albumWidth" | ||
8 | + :key="index" | ||
9 | + > | ||
10 | + <view | ||
11 | + class="u-album__row__wrapper" | ||
12 | + v-for="(item, index1) in arr" | ||
13 | + :key="index1" | ||
14 | + :style="[imageStyle(index + 1, index1 + 1)]" | ||
15 | + @tap="previewFullImage ? onPreviewTap(getSrc(item)) : ''" | ||
16 | + > | ||
17 | + <image | ||
18 | + :src="getSrc(item)" | ||
19 | + :mode=" | ||
20 | + urls.length === 1 | ||
21 | + ? imageHeight > 0 | ||
22 | + ? singleMode | ||
23 | + : 'widthFix' | ||
24 | + : multipleMode | ||
25 | + " | ||
26 | + :style="[ | ||
27 | + { | ||
28 | + width: imageWidth, | ||
29 | + height: imageHeight | ||
30 | + } | ||
31 | + ]" | ||
32 | + ></image> | ||
33 | + <view | ||
34 | + v-if=" | ||
35 | + showMore && | ||
36 | + urls.length > rowCount * showUrls.length && | ||
37 | + index === showUrls.length - 1 && | ||
38 | + index1 === showUrls[showUrls.length - 1].length - 1 | ||
39 | + " | ||
40 | + class="u-album__row__wrapper__text" | ||
41 | + > | ||
42 | + <u--text | ||
43 | + :text="`+${urls.length - maxCount}`" | ||
44 | + color="#fff" | ||
45 | + :size="multipleSize * 0.3" | ||
46 | + align="center" | ||
47 | + customStyle="justify-content: center" | ||
48 | + ></u--text> | ||
49 | + </view> | ||
50 | + </view> | ||
51 | + </view> | ||
52 | + </view> | ||
53 | +</template> | ||
54 | + | ||
55 | +<script> | ||
56 | +import props from './props.js' | ||
57 | +// #ifdef APP-NVUE | ||
58 | +// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度 | ||
59 | +const dom = uni.requireNativePlugin('dom') | ||
60 | +// #endif | ||
61 | + | ||
62 | +/** | ||
63 | + * Album 相册 | ||
64 | + * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码 | ||
65 | + * @tutorial https://www.uviewui.com/components/album.html | ||
66 | + * | ||
67 | + * @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式 | ||
68 | + * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址 | ||
69 | + * @property {String | Number} singleSize 单图时,图片长边的长度 (默认 180 ) | ||
70 | + * @property {String | Number} multipleSize 多图时,图片边长 (默认 70 ) | ||
71 | + * @property {String | Number} space 多图时,图片水平和垂直之间的间隔 (默认 6 ) | ||
72 | + * @property {String} singleMode 单图时,图片缩放裁剪的模式 (默认 'scaleToFill' ) | ||
73 | + * @property {String} multipleMode 多图时,图片缩放裁剪的模式 (默认 'aspectFill' ) | ||
74 | + * @property {String | Number} maxCount 取消按钮的提示文字 (默认 9 ) | ||
75 | + * @property {Boolean} previewFullImage 是否可以预览图片 (默认 true ) | ||
76 | + * @property {String | Number} rowCount 每行展示图片数量,如设置,singleSize和multipleSize将会无效 (默认 3 ) | ||
77 | + * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true ) | ||
78 | + * | ||
79 | + * @event {Function} albumWidth 某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送 (回调参数 width ) | ||
80 | + * @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album> | ||
81 | + */ | ||
82 | +export default { | ||
83 | + name: 'u-album', | ||
84 | + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], | ||
85 | + data() { | ||
86 | + return { | ||
87 | + // 单图的宽度 | ||
88 | + singleWidth: 0, | ||
89 | + // 单图的高度 | ||
90 | + singleHeight: 0, | ||
91 | + // 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比 | ||
92 | + singlePercent: 0.6 | ||
93 | + } | ||
94 | + }, | ||
95 | + watch: { | ||
96 | + urls: { | ||
97 | + immediate: true, | ||
98 | + handler(newVal) { | ||
99 | + if (newVal.length === 1) { | ||
100 | + this.getImageRect() | ||
101 | + } | ||
102 | + } | ||
103 | + } | ||
104 | + }, | ||
105 | + computed: { | ||
106 | + imageStyle() { | ||
107 | + return (index1, index2) => { | ||
108 | + const { space, rowCount, multipleSize, urls } = this, | ||
109 | + { addUnit, addStyle } = uni.$u, | ||
110 | + rowLen = this.showUrls.length, | ||
111 | + allLen = this.urls.length | ||
112 | + const style = { | ||
113 | + marginRight: addUnit(space), | ||
114 | + marginBottom: addUnit(space) | ||
115 | + } | ||
116 | + // 如果为最后一行,则每个图片都无需下边框 | ||
117 | + if (index1 === rowLen) style.marginBottom = 0 | ||
118 | + // 每行的最右边一张和总长度的最后一张无需右边框 | ||
119 | + if ( | ||
120 | + index2 === rowCount || | ||
121 | + (index1 === rowLen && | ||
122 | + index2 === this.showUrls[index1 - 1].length) | ||
123 | + ) | ||
124 | + style.marginRight = 0 | ||
125 | + return style | ||
126 | + } | ||
127 | + }, | ||
128 | + // 将数组划分为二维数组 | ||
129 | + showUrls() { | ||
130 | + const arr = [] | ||
131 | + this.urls.map((item, index) => { | ||
132 | + // 限制最大展示数量 | ||
133 | + if (index + 1 <= this.maxCount) { | ||
134 | + // 计算该元素为第几个素组内 | ||
135 | + const itemIndex = Math.floor(index / this.rowCount) | ||
136 | + // 判断对应的索引是否存在 | ||
137 | + if (!arr[itemIndex]) { | ||
138 | + arr[itemIndex] = [] | ||
139 | + } | ||
140 | + arr[itemIndex].push(item) | ||
141 | + } | ||
142 | + }) | ||
143 | + return arr | ||
144 | + }, | ||
145 | + imageWidth() { | ||
146 | + return uni.$u.addUnit( | ||
147 | + this.urls.length === 1 ? this.singleWidth : this.multipleSize | ||
148 | + ) | ||
149 | + }, | ||
150 | + imageHeight() { | ||
151 | + return uni.$u.addUnit( | ||
152 | + this.urls.length === 1 ? this.singleHeight : this.multipleSize | ||
153 | + ) | ||
154 | + }, | ||
155 | + // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度 | ||
156 | + // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送 | ||
157 | + albumWidth() { | ||
158 | + let width = 0 | ||
159 | + if (this.urls.length === 1) { | ||
160 | + width = this.singleWidth | ||
161 | + } else { | ||
162 | + width = | ||
163 | + this.showUrls[0].length * this.multipleSize + | ||
164 | + this.space * (this.showUrls[0].length - 1) | ||
165 | + } | ||
166 | + this.$emit('albumWidth', width) | ||
167 | + return width | ||
168 | + } | ||
169 | + }, | ||
170 | + methods: { | ||
171 | + // 预览图片 | ||
172 | + onPreviewTap(url) { | ||
173 | + const urls = this.urls.map((item) => { | ||
174 | + return this.getSrc(item) | ||
175 | + }) | ||
176 | + uni.previewImage({ | ||
177 | + current: url, | ||
178 | + urls | ||
179 | + }) | ||
180 | + }, | ||
181 | + // 获取图片的路径 | ||
182 | + getSrc(item) { | ||
183 | + return uni.$u.test.object(item) | ||
184 | + ? (this.keyName && item[this.keyName]) || item.src | ||
185 | + : item | ||
186 | + }, | ||
187 | + // 单图时,获取图片的尺寸 | ||
188 | + // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸 | ||
189 | + // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent) | ||
190 | + getImageRect() { | ||
191 | + const src = this.getSrc(this.urls[0]) | ||
192 | + uni.getImageInfo({ | ||
193 | + src, | ||
194 | + success: (res) => { | ||
195 | + // 判断图片横向还是竖向展示方式 | ||
196 | + const isHorizotal = res.width >= res.height | ||
197 | + this.singleWidth = isHorizotal | ||
198 | + ? this.singleSize | ||
199 | + : (res.width / res.height) * this.singleSize | ||
200 | + this.singleHeight = !isHorizotal | ||
201 | + ? this.singleSize | ||
202 | + : (res.height / res.width) * this.singleWidth | ||
203 | + }, | ||
204 | + fail: () => { | ||
205 | + this.getComponentWidth() | ||
206 | + } | ||
207 | + }) | ||
208 | + }, | ||
209 | + // 获取组件的宽度 | ||
210 | + async getComponentWidth() { | ||
211 | + // 延时一定时间,以获取dom尺寸 | ||
212 | + await uni.$u.sleep(30) | ||
213 | + // #ifndef APP-NVUE | ||
214 | + this.$uGetRect('.u-album__row').then((size) => { | ||
215 | + this.singleWidth = size.width * this.singlePercent | ||
216 | + }) | ||
217 | + // #endif | ||
218 | + | ||
219 | + // #ifdef APP-NVUE | ||
220 | + // 这里ref="u-album__row"所在的标签为通过for循环出来,导致this.$refs['u-album__row']是一个数组 | ||
221 | + const ref = this.$refs['u-album__row'][0] | ||
222 | + ref && | ||
223 | + dom.getComponentRect(ref, (res) => { | ||
224 | + this.singleWidth = res.size.width * this.singlePercent | ||
225 | + }) | ||
226 | + // #endif | ||
227 | + } | ||
228 | + } | ||
229 | +} | ||
230 | +</script> | ||
231 | + | ||
232 | +<style lang="scss" scoped> | ||
233 | +@import '../../libs/css/components.scss'; | ||
234 | + | ||
235 | +.u-album { | ||
236 | + @include flex(column); | ||
237 | + | ||
238 | + &__row { | ||
239 | + @include flex(row); | ||
240 | + flex-wrap: wrap; | ||
241 | + | ||
242 | + &__wrapper { | ||
243 | + position: relative; | ||
244 | + | ||
245 | + &__text { | ||
246 | + position: absolute; | ||
247 | + top: 0; | ||
248 | + left: 0; | ||
249 | + right: 0; | ||
250 | + bottom: 0; | ||
251 | + background-color: rgba(0, 0, 0, 0.3); | ||
252 | + @include flex(row); | ||
253 | + justify-content: center; | ||
254 | + align-items: center; | ||
255 | + } | ||
256 | + } | ||
257 | + } | ||
258 | +} | ||
259 | +</style> |
1 | +export default { | ||
2 | + props: { | ||
3 | + // 显示文字 | ||
4 | + title: { | ||
5 | + type: String, | ||
6 | + default: uni.$u.props.alert.title | ||
7 | + }, | ||
8 | + // 主题,success/warning/info/error | ||
9 | + type: { | ||
10 | + type: String, | ||
11 | + default: uni.$u.props.alert.type | ||
12 | + }, | ||
13 | + // 辅助性文字 | ||
14 | + description: { | ||
15 | + type: String, | ||
16 | + default: uni.$u.props.alert.description | ||
17 | + }, | ||
18 | + // 是否可关闭 | ||
19 | + closable: { | ||
20 | + type: Boolean, | ||
21 | + default: uni.$u.props.alert.closable | ||
22 | + }, | ||
23 | + // 是否显示图标 | ||
24 | + showIcon: { | ||
25 | + type: Boolean, | ||
26 | + default: uni.$u.props.alert.showIcon | ||
27 | + }, | ||
28 | + // 浅或深色调,light-浅色,dark-深色 | ||
29 | + effect: { | ||
30 | + type: String, | ||
31 | + default: uni.$u.props.alert.effect | ||
32 | + }, | ||
33 | + // 文字是否居中 | ||
34 | + center: { | ||
35 | + type: Boolean, | ||
36 | + default: uni.$u.props.alert.center | ||
37 | + }, | ||
38 | + // 字体大小 | ||
39 | + fontSize: { | ||
40 | + type: [String, Number], | ||
41 | + default: uni.$u.props.alert.fontSize | ||
42 | + } | ||
43 | + } | ||
44 | +} |
1 | +<template> | ||
2 | + <u-transition | ||
3 | + mode="fade" | ||
4 | + :show="show" | ||
5 | + > | ||
6 | + <view | ||
7 | + class="u-alert" | ||
8 | + :class="[`u-alert--${type}--${effect}`]" | ||
9 | + @tap.stop="clickHandler" | ||
10 | + :style="[$u.addStyle(customStyle)]" | ||
11 | + > | ||
12 | + <view | ||
13 | + class="u-alert__icon" | ||
14 | + v-if="showIcon" | ||
15 | + > | ||
16 | + <u-icon | ||
17 | + :name="iconName" | ||
18 | + size="18" | ||
19 | + :color="iconColor" | ||
20 | + ></u-icon> | ||
21 | + </view> | ||
22 | + <view | ||
23 | + class="u-alert__content" | ||
24 | + :style="[{ | ||
25 | + paddingRight: closable ? '20px' : 0 | ||
26 | + }]" | ||
27 | + > | ||
28 | + <text | ||
29 | + class="u-alert__content__title" | ||
30 | + v-if="title" | ||
31 | + :style="[{ | ||
32 | + fontSize: $u.addUnit(fontSize), | ||
33 | + textAlign: center ? 'center' : 'left' | ||
34 | + }]" | ||
35 | + :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]" | ||
36 | + >{{ title }}</text> | ||
37 | + <text | ||
38 | + class="u-alert__content__desc" | ||
39 | + v-if="description" | ||
40 | + :style="[{ | ||
41 | + fontSize: $u.addUnit(fontSize), | ||
42 | + textAlign: center ? 'center' : 'left' | ||
43 | + }]" | ||
44 | + :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]" | ||
45 | + >{{ description }}</text> | ||
46 | + </view> | ||
47 | + <view | ||
48 | + class="u-alert__close" | ||
49 | + v-if="closable" | ||
50 | + @tap.stop="closeHandler" | ||
51 | + > | ||
52 | + <u-icon | ||
53 | + name="close" | ||
54 | + :color="iconColor" | ||
55 | + size="15" | ||
56 | + ></u-icon> | ||
57 | + </view> | ||
58 | + </view> | ||
59 | + </u-transition> | ||
60 | +</template> | ||
61 | + | ||
62 | +<script> | ||
63 | + import props from './props.js'; | ||
64 | + /** | ||
65 | + * Alert 警告提示 | ||
66 | + * @description 警告提示,展现需要关注的信息。 | ||
67 | + * @tutorial https://www.uviewui.com/components/alertTips.html | ||
68 | + * | ||
69 | + * @property {String} title 显示的文字 | ||
70 | + * @property {String} type 使用预设的颜色 (默认 'warning' ) | ||
71 | + * @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选 | ||
72 | + * @property {Boolean} closable 关闭按钮(默认为叉号icon图标) (默认 false ) | ||
73 | + * @property {Boolean} showIcon 是否显示左边的辅助图标 ( 默认 false ) | ||
74 | + * @property {String} effect 多图时,图片缩放裁剪的模式 (默认 'light' ) | ||
75 | + * @property {Boolean} center 文字是否居中 (默认 false ) | ||
76 | + * @property {String | Number} fontSize 字体大小 (默认 14 ) | ||
77 | + * @property {Object} customStyle 定义需要用到的外部样式 | ||
78 | + * @event {Function} click 点击组件时触发 | ||
79 | + * @example <u-alert :title="title" type = "warning" :closable="closable" :description = "description"></u-alert> | ||
80 | + */ | ||
81 | + export default { | ||
82 | + name: 'u-alert', | ||
83 | + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], | ||
84 | + data() { | ||
85 | + return { | ||
86 | + show: true | ||
87 | + } | ||
88 | + }, | ||
89 | + computed: { | ||
90 | + iconColor() { | ||
91 | + return this.effect === 'light' ? this.type : '#fff' | ||
92 | + }, | ||
93 | + // 不同主题对应不同的图标 | ||
94 | + iconName() { | ||
95 | + switch (this.type) { | ||
96 | + case 'success': | ||
97 | + return 'checkmark-circle-fill'; | ||
98 | + break; | ||
99 | + case 'error': | ||
100 | + return 'close-circle-fill'; | ||
101 | + break; | ||
102 | + case 'warning': | ||
103 | + return 'error-circle-fill'; | ||
104 | + break; | ||
105 | + case 'info': | ||
106 | + return 'info-circle-fill'; | ||
107 | + break; | ||
108 | + case 'primary': | ||
109 | + return 'more-circle-fill'; | ||
110 | + break; | ||
111 | + default: | ||
112 | + return 'error-circle-fill'; | ||
113 | + } | ||
114 | + } | ||
115 | + }, | ||
116 | + methods: { | ||
117 | + // 点击内容 | ||
118 | + clickHandler() { | ||
119 | + this.$emit('click') | ||
120 | + }, | ||
121 | + // 点击关闭按钮 | ||
122 | + closeHandler() { | ||
123 | + this.show = false | ||
124 | + } | ||
125 | + } | ||
126 | + } | ||
127 | +</script> | ||
128 | + | ||
129 | +<style lang="scss" scoped> | ||
130 | + @import "../../libs/css/components.scss"; | ||
131 | + | ||
132 | + .u-alert { | ||
133 | + position: relative; | ||
134 | + background-color: $u-primary; | ||
135 | + padding: 8px 10px; | ||
136 | + @include flex(row); | ||
137 | + align-items: center; | ||
138 | + border-top-left-radius: 4px; | ||
139 | + border-top-right-radius: 4px; | ||
140 | + border-bottom-left-radius: 4px; | ||
141 | + border-bottom-right-radius: 4px; | ||
142 | + | ||
143 | + &--primary--dark { | ||
144 | + background-color: $u-primary; | ||
145 | + } | ||
146 | + | ||
147 | + &--primary--light { | ||
148 | + background-color: #ecf5ff; | ||
149 | + } | ||
150 | + | ||
151 | + &--error--dark { | ||
152 | + background-color: $u-error; | ||
153 | + } | ||
154 | + | ||
155 | + &--error--light { | ||
156 | + background-color: #FEF0F0; | ||
157 | + } | ||
158 | + | ||
159 | + &--success--dark { | ||
160 | + background-color: $u-success; | ||
161 | + } | ||
162 | + | ||
163 | + &--success--light { | ||
164 | + background-color: #f5fff0; | ||
165 | + } | ||
166 | + | ||
167 | + &--warning--dark { | ||
168 | + background-color: $u-warning; | ||
169 | + } | ||
170 | + | ||
171 | + &--warning--light { | ||
172 | + background-color: #FDF6EC; | ||
173 | + } | ||
174 | + | ||
175 | + &--info--dark { | ||
176 | + background-color: $u-info; | ||
177 | + } | ||
178 | + | ||
179 | + &--info--light { | ||
180 | + background-color: #f4f4f5; | ||
181 | + } | ||
182 | + | ||
183 | + &__icon { | ||
184 | + margin-right: 5px; | ||
185 | + } | ||
186 | + | ||
187 | + &__content { | ||
188 | + @include flex(column); | ||
189 | + flex: 1; | ||
190 | + | ||
191 | + &__title { | ||
192 | + color: $u-main-color; | ||
193 | + font-size: 14px; | ||
194 | + font-weight: bold; | ||
195 | + color: #fff; | ||
196 | + margin-bottom: 2px; | ||
197 | + } | ||
198 | + | ||
199 | + &__desc { | ||
200 | + color: $u-main-color; | ||
201 | + font-size: 14px; | ||
202 | + flex-wrap: wrap; | ||
203 | + color: #fff; | ||
204 | + } | ||
205 | + } | ||
206 | + | ||
207 | + &__title--dark, | ||
208 | + &__desc--dark { | ||
209 | + color: #FFFFFF; | ||
210 | + } | ||
211 | + | ||
212 | + &__text--primary--light, | ||
213 | + &__text--primary--light { | ||
214 | + color: $u-primary; | ||
215 | + } | ||
216 | + | ||
217 | + &__text--success--light, | ||
218 | + &__text--success--light { | ||
219 | + color: $u-success; | ||
220 | + } | ||
221 | + | ||
222 | + &__text--warning--light, | ||
223 | + &__text--warning--light { | ||
224 | + color: $u-warning; | ||
225 | + } | ||
226 | + | ||
227 | + &__text--error--light, | ||
228 | + &__text--error--light { | ||
229 | + color: $u-error; | ||
230 | + } | ||
231 | + | ||
232 | + &__text--info--light, | ||
233 | + &__text--info--light { | ||
234 | + color: $u-info; | ||
235 | + } | ||
236 | + | ||
237 | + &__close { | ||
238 | + position: absolute; | ||
239 | + top: 11px; | ||
240 | + right: 10px; | ||
241 | + } | ||
242 | + } | ||
243 | +</style> |
1 | +export default { | ||
2 | + props: { | ||
3 | + // 头像图片组 | ||
4 | + urls: { | ||
5 | + type: Array, | ||
6 | + default: uni.$u.props.avatarGroup.urls | ||
7 | + }, | ||
8 | + // 最多展示的头像数量 | ||
9 | + maxCount: { | ||
10 | + type: [String, Number], | ||
11 | + default: uni.$u.props.avatarGroup.maxCount | ||
12 | + }, | ||
13 | + // 头像形状 | ||
14 | + shape: { | ||
15 | + type: String, | ||
16 | + default: uni.$u.props.avatarGroup.shape | ||
17 | + }, | ||
18 | + // 图片裁剪模式 | ||
19 | + mode: { | ||
20 | + type: String, | ||
21 | + default: uni.$u.props.avatarGroup.mode | ||
22 | + }, | ||
23 | + // 超出maxCount时是否显示查看更多的提示 | ||
24 | + showMore: { | ||
25 | + type: Boolean, | ||
26 | + default: uni.$u.props.avatarGroup.showMore | ||
27 | + }, | ||
28 | + // 头像大小 | ||
29 | + size: { | ||
30 | + type: [String, Number], | ||
31 | + default: uni.$u.props.avatarGroup.size | ||
32 | + }, | ||
33 | + // 指定从数组的对象元素中读取哪个属性作为图片地址 | ||
34 | + keyName: { | ||
35 | + type: String, | ||
36 | + default: uni.$u.props.avatarGroup.keyName | ||
37 | + }, | ||
38 | + // 头像之间的遮挡比例 | ||
39 | + gap: { | ||
40 | + type: [String, Number], | ||
41 | + validator(value) { | ||
42 | + return value >= 0 && value <= 1 | ||
43 | + }, | ||
44 | + default: uni.$u.props.avatarGroup.gap | ||
45 | + }, | ||
46 | + // 需额外显示的值 | ||
47 | + extraValue: { | ||
48 | + type: [Number, String], | ||
49 | + default: uni.$u.props.avatarGroup.extraValue | ||
50 | + } | ||
51 | + } | ||
52 | +} |
1 | +<template> | ||
2 | + <view class="u-avatar-group"> | ||
3 | + <view | ||
4 | + class="u-avatar-group__item" | ||
5 | + v-for="(item, index) in showUrl" | ||
6 | + :key="index" | ||
7 | + :style="{ | ||
8 | + marginLeft: index === 0 ? 0 : $u.addUnit(-size * gap) | ||
9 | + }" | ||
10 | + > | ||
11 | + <u-avatar | ||
12 | + :size="size" | ||
13 | + :shape="shape" | ||
14 | + :mode="mode" | ||
15 | + :src="$u.test.object(item) ? keyName && item[keyName] || item.url : item" | ||
16 | + ></u-avatar> | ||
17 | + <view | ||
18 | + class="u-avatar-group__item__show-more" | ||
19 | + v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)" | ||
20 | + @tap="clickHandler" | ||
21 | + > | ||
22 | + <u--text | ||
23 | + color="#ffffff" | ||
24 | + :size="size * 0.4" | ||
25 | + :text="`+${extraValue || urls.length - showUrl.length}`" | ||
26 | + align="center" | ||
27 | + customStyle="justify-content: center" | ||
28 | + ></u--text> | ||
29 | + </view> | ||
30 | + </view> | ||
31 | + </view> | ||
32 | +</template> | ||
33 | + | ||
34 | +<script> | ||
35 | + import props from './props.js'; | ||
36 | + /** | ||
37 | + * AvatarGroup 头像组 | ||
38 | + * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。 | ||
39 | + * @tutorial https://www.uviewui.com/components/avatar.html | ||
40 | + * | ||
41 | + * @property {Array} urls 头像图片组 (默认 [] ) | ||
42 | + * @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 ) | ||
43 | + * @property {String} shape 头像形状( 'circle' (默认) | 'square' ) | ||
44 | + * @property {String} mode 图片裁剪模式(默认 'scaleToFill' ) | ||
45 | + * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true ) | ||
46 | + * @property {String | Number} size 头像大小 (默认 40 ) | ||
47 | + * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址 | ||
48 | + * @property {String | Number} gap 头像之间的遮挡比例(0.4代表遮挡40%) (默认 0.5 ) | ||
49 | + * @property {String | Number} extraValue 需额外显示的值 | ||
50 | + * @event {Function} showMore 头像组更多点击 | ||
51 | + * @example <u-avatar-group:urls="urls" size="35" gap="0.4" ></u-avatar-group:urls=> | ||
52 | + */ | ||
53 | + export default { | ||
54 | + name: 'u-avatar-group', | ||
55 | + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], | ||
56 | + data() { | ||
57 | + return { | ||
58 | + | ||
59 | + } | ||
60 | + }, | ||
61 | + computed: { | ||
62 | + showUrl() { | ||
63 | + return this.urls.slice(0, this.maxCount) | ||
64 | + } | ||
65 | + }, | ||
66 | + methods: { | ||
67 | + clickHandler() { | ||
68 | + this.$emit('showMore') | ||
69 | + } | ||
70 | + }, | ||
71 | + } | ||
72 | +</script> | ||
73 | + | ||
74 | +<style lang="scss" scoped> | ||
75 | + @import "../../libs/css/components.scss"; | ||
76 | + | ||
77 | + .u-avatar-group { | ||
78 | + @include flex; | ||
79 | + | ||
80 | + &__item { | ||
81 | + margin-left: -10px; | ||
82 | + position: relative; | ||
83 | + | ||
84 | + &--no-indent { | ||
85 | + // 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持 | ||
86 | + margin-left: 0; | ||
87 | + } | ||
88 | + | ||
89 | + &__show-more { | ||
90 | + position: absolute; | ||
91 | + top: 0; | ||
92 | + bottom: 0; | ||
93 | + left: 0; | ||
94 | + right: 0; | ||
95 | + background-color: rgba(0, 0, 0, 0.3); | ||
96 | + @include flex; | ||
97 | + align-items: center; | ||
98 | + justify-content: center; | ||
99 | + border-radius: 100px; | ||
100 | + } | ||
101 | + } | ||
102 | + } | ||
103 | +</style> |
1 | +export default { | ||
2 | + props: { | ||
3 | + // 头像图片路径(不能为相对路径) | ||
4 | + src: { | ||
5 | + type: String, | ||
6 | + default: uni.$u.props.avatar.src | ||
7 | + }, | ||
8 | + // 头像形状,circle-圆形,square-方形 | ||
9 | + shape: { | ||
10 | + type: String, | ||
11 | + default: uni.$u.props.avatar.shape | ||
12 | + }, | ||
13 | + // 头像尺寸 | ||
14 | + size: { | ||
15 | + type: [String, Number], | ||
16 | + default: uni.$u.props.avatar.size | ||
17 | + }, | ||
18 | + // 裁剪模式 | ||
19 | + mode: { | ||
20 | + type: String, | ||
21 | + default: uni.$u.props.avatar.mode | ||
22 | + }, | ||
23 | + // 显示的文字 | ||
24 | + text: { | ||
25 | + type: String, | ||
26 | + default: uni.$u.props.avatar.text | ||
27 | + }, | ||
28 | + // 背景色 | ||
29 | + bgColor: { | ||
30 | + type: String, | ||
31 | + default: uni.$u.props.avatar.bgColor | ||
32 | + }, | ||
33 | + // 文字颜色 | ||
34 | + color: { | ||
35 | + type: String, | ||
36 | + default: uni.$u.props.avatar.color | ||
37 | + }, | ||
38 | + // 文字大小 | ||
39 | + fontSize: { | ||
40 | + type: [String, Number], | ||
41 | + default: uni.$u.props.avatar.fontSize | ||
42 | + }, | ||
43 | + // 显示的图标 | ||
44 | + icon: { | ||
45 | + type: String, | ||
46 | + default: uni.$u.props.avatar.icon | ||
47 | + }, | ||
48 | + // 显示小程序头像,只对百度,微信,QQ小程序有效 | ||
49 | + mpAvatar: { | ||
50 | + type: Boolean, | ||
51 | + default: uni.$u.props.avatar.mpAvatar | ||
52 | + }, | ||
53 | + // 是否使用随机背景色 | ||
54 | + randomBgColor: { | ||
55 | + type: Boolean, | ||
56 | + default: uni.$u.props.avatar.randomBgColor | ||
57 | + }, | ||
58 | + // 加载失败的默认头像(组件有内置默认图片) | ||
59 | + defaultUrl: { | ||
60 | + type: String, | ||
61 | + default: uni.$u.props.avatar.defaultUrl | ||
62 | + }, | ||
63 | + // 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间 | ||
64 | + colorIndex: { | ||
65 | + type: [String, Number], | ||
66 | + // 校验参数规则,索引在0-19之间 | ||
67 | + validator(n) { | ||
68 | + return uni.$u.test.range(n, [0, 19]) || n === '' | ||
69 | + }, | ||
70 | + default: uni.$u.props.avatar.colorIndex | ||
71 | + }, | ||
72 | + // 组件标识符 | ||
73 | + name: { | ||
74 | + type: String, | ||
75 | + default: uni.$u.props.avatar.name | ||
76 | + } | ||
77 | + } | ||
78 | +} |
1 | +<template> | ||
2 | + <view | ||
3 | + class="u-avatar" | ||
4 | + :class="[`u-avatar--${shape}`]" | ||
5 | + :style="[{ | ||
6 | + backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : $u.random(0, 19)] : bgColor) : 'transparent', | ||
7 | + width: $u.addUnit(size), | ||
8 | + height: $u.addUnit(size), | ||
9 | + }, $u.addStyle(customStyle)]" | ||
10 | + @tap="clickHandler" | ||
11 | + > | ||
12 | + <slot> | ||
13 | + <!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU --> | ||
14 | + <open-data | ||
15 | + v-if="mpAvatar && allowMp" | ||
16 | + type="userAvatarUrl" | ||
17 | + :style="[{ | ||
18 | + width: $u.addUnit(size), | ||
19 | + height: $u.addUnit(size) | ||
20 | + }]" | ||
21 | + /> | ||
22 | + <!-- #endif --> | ||
23 | + <!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU --> | ||
24 | + <template v-if="mpAvatar && allowMp"></template> | ||
25 | + <!-- #endif --> | ||
26 | + <u-icon | ||
27 | + v-else-if="icon" | ||
28 | + :name="icon" | ||
29 | + :size="fontSize" | ||
30 | + :color="color" | ||
31 | + ></u-icon> | ||
32 | + <u--text | ||
33 | + v-else-if="text" | ||
34 | + :text="text" | ||
35 | + :size="fontSize" | ||
36 | + :color="color" | ||
37 | + align="center" | ||
38 | + customStyle="justify-content: center" | ||
39 | + ></u--text> | ||
40 | + <image | ||
41 | + class="u-avatar__image" | ||
42 | + v-else | ||
43 | + :class="[`u-avatar__image--${shape}`]" | ||
44 | + :src="avatarUrl || defaultUrl" | ||
45 | + :mode="mode" | ||
46 | + @error="errorHandler" | ||
47 | + :style="[{ | ||
48 | + width: $u.addUnit(size), | ||
49 | + height: $u.addUnit(size) | ||
50 | + }]" | ||
51 | + ></image> | ||
52 | + </slot> | ||
53 | + </view> | ||
54 | +</template> | ||
55 | + | ||
56 | +<script> | ||
57 | + import props from './props.js'; | ||
58 | + const base64Avatar = | ||
59 | + "data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z"; | ||
60 | + /** | ||
61 | + * Avatar 头像 | ||
62 | + * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。 | ||
63 | + * @tutorial https://www.uviewui.com/components/avatar.html | ||
64 | + * | ||
65 | + * @property {String} src 头像路径,如加载失败,将会显示默认头像(不能为相对路径) | ||
66 | + * @property {String} shape 头像形状 ( circle (默认) | square) | ||
67 | + * @property {String | Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值 (默认 40 ) | ||
68 | + * @property {String} mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值 (默认 'scaleToFill' ) | ||
69 | + * @property {String} text 用文字替代图片,级别优先于src | ||
70 | + * @property {String} bgColor 背景颜色,一般显示文字时用 (默认 '#c0c4cc' ) | ||
71 | + * @property {String} color 文字颜色 (默认 '#ffffff' ) | ||
72 | + * @property {String | Number} fontSize 文字大小 (默认 18 ) | ||
73 | + * @property {String} icon 显示的图标 | ||
74 | + * @property {Boolean} mpAvatar 显示小程序头像,只对百度,微信,QQ小程序有效 (默认 false ) | ||
75 | + * @property {Boolean} randomBgColor 是否使用随机背景色 (默认 false ) | ||
76 | + * @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片) | ||
77 | + * @property {String | Number} colorIndex 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间 | ||
78 | + * @property {String} name 组件标识符 (默认 'level' ) | ||
79 | + * @property {Object} customStyle 定义需要用到的外部样式 | ||
80 | + * | ||
81 | + * @event {Function} click 点击组件时触发 index: 用户传递的标识符 | ||
82 | + * @example <u-avatar :src="src" mode="square"></u-avatar> | ||
83 | + */ | ||
84 | + export default { | ||
85 | + name: 'u-avatar', | ||
86 | + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], | ||
87 | + data() { | ||
88 | + return { | ||
89 | + // 如果配置randomBgColor参数为true,在图标或者文字的模式下,会随机从中取出一个颜色值当做背景色 | ||
90 | + colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2', | ||
91 | + '#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee', | ||
92 | + '#73d1f1', | ||
93 | + '#80a7dc' | ||
94 | + ], | ||
95 | + avatarUrl: this.src, | ||
96 | + allowMp: false | ||
97 | + } | ||
98 | + }, | ||
99 | + watch: { | ||
100 | + // 监听头像src的变化,赋值给内部的avatarUrl变量,因为图片加载失败时,需要修改图片的src为默认值 | ||
101 | + // 而组件内部不能直接修改props的值,所以需要一个中间变量 | ||
102 | + src: { | ||
103 | + immediate: true, | ||
104 | + handler(newVal) { | ||
105 | + this.avatarUrl = newVal | ||
106 | + // 如果没有传src,则主动触发error事件,用于显示默认的头像,否则src为''空字符等的时候,会无内容展示 | ||
107 | + if(!newVal) { | ||
108 | + this.errorHandler() | ||
109 | + } | ||
110 | + } | ||
111 | + } | ||
112 | + }, | ||
113 | + computed: { | ||
114 | + imageStyle() { | ||
115 | + const style = {} | ||
116 | + return style | ||
117 | + } | ||
118 | + }, | ||
119 | + created() { | ||
120 | + this.init() | ||
121 | + }, | ||
122 | + methods: { | ||
123 | + init() { | ||
124 | + // 目前只有这几个小程序平台具有open-data标签 | ||
125 | + // 其他平台可以通过uni.getUserInfo类似接口获取信息,但是需要弹窗授权(首次),不合符组件逻辑 | ||
126 | + // 故目前自动获取小程序头像只支持这几个平台 | ||
127 | + // #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU | ||
128 | + this.allowMp = true | ||
129 | + // #endif | ||
130 | + }, | ||
131 | + // 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式 | ||
132 | + isImg() { | ||
133 | + return this.src.indexOf('/') !== -1 | ||
134 | + }, | ||
135 | + // 图片加载时失败时触发 | ||
136 | + errorHandler() { | ||
137 | + this.avatarUrl = this.defaultUrl || base64Avatar | ||
138 | + }, | ||
139 | + clickHandler() { | ||
140 | + this.$emit('click', this.name) | ||
141 | + } | ||
142 | + } | ||
143 | + } | ||
144 | +</script> | ||
145 | + | ||
146 | +<style lang="scss" scoped> | ||
147 | + @import "../../libs/css/components.scss"; | ||
148 | + | ||
149 | + .u-avatar { | ||
150 | + @include flex; | ||
151 | + align-items: center; | ||
152 | + justify-content: center; | ||
153 | + | ||
154 | + &--circle { | ||
155 | + border-radius: 100px; | ||
156 | + } | ||
157 | + | ||
158 | + &--square { | ||
159 | + border-radius: 4px; | ||
160 | + } | ||
161 | + | ||
162 | + &__image { | ||
163 | + &--circle { | ||
164 | + border-radius: 100px; | ||
165 | + } | ||
166 | + | ||
167 | + &--square { | ||
168 | + border-radius: 4px; | ||
169 | + } | ||
170 | + } | ||
171 | + } | ||
172 | +</style> |
1 | +export default { | ||
2 | + props: { | ||
3 | + // 返回顶部的形状,circle-圆形,square-方形 | ||
4 | + mode: { | ||
5 | + type: String, | ||
6 | + default: uni.$u.props.backtop.mode | ||
7 | + }, | ||
8 | + // 自定义图标 | ||
9 | + icon: { | ||
10 | + type: String, | ||
11 | + default: uni.$u.props.backtop.icon | ||
12 | + }, | ||
13 | + // 提示文字 | ||
14 | + text: { | ||
15 | + type: String, | ||
16 | + default: uni.$u.props.backtop.text | ||
17 | + }, | ||
18 | + // 返回顶部滚动时间 | ||
19 | + duration: { | ||
20 | + type: [String, Number], | ||
21 | + default: uni.$u.props.backtop.duration | ||
22 | + }, | ||
23 | + // 滚动距离 | ||
24 | + scrollTop: { | ||
25 | + type: [String, Number], | ||
26 | + default: uni.$u.props.backtop.scrollTop | ||
27 | + }, | ||
28 | + // 距离顶部多少距离显示,单位px | ||
29 | + top: { | ||
30 | + type: [String, Number], | ||
31 | + default: uni.$u.props.backtop.top | ||
32 | + }, | ||
33 | + // 返回顶部按钮到底部的距离,单位px | ||
34 | + bottom: { | ||
35 | + type: [String, Number], | ||
36 | + default: uni.$u.props.backtop.bottom | ||
37 | + }, | ||
38 | + // 返回顶部按钮到右边的距离,单位px | ||
39 | + right: { | ||
40 | + type: [String, Number], | ||
41 | + default: uni.$u.props.backtop.right | ||
42 | + }, | ||
43 | + // 层级 | ||
44 | + zIndex: { | ||
45 | + type: [String, Number], | ||
46 | + default: uni.$u.props.backtop.zIndex | ||
47 | + }, | ||
48 | + // 图标的样式,对象形式 | ||
49 | + iconStyle: { | ||
50 | + type: Object, | ||
51 | + default: uni.$u.props.backtop.iconStyle | ||
52 | + } | ||
53 | + } | ||
54 | +} |
1 | +<template> | ||
2 | + <u-transition | ||
3 | + mode="fade" | ||
4 | + :customStyle="backTopStyle" | ||
5 | + :show="show" | ||
6 | + > | ||
7 | + <view | ||
8 | + class="u-back-top" | ||
9 | + :style="[contentStyle]" | ||
10 | + v-if="!$slots.default && !$slots.$default" | ||
11 | + @click="backToTop" | ||
12 | + > | ||
13 | + <u-icon | ||
14 | + :name="icon" | ||
15 | + :custom-style="iconStyle" | ||
16 | + ></u-icon> | ||
17 | + <text | ||
18 | + v-if="text" | ||
19 | + class="u-back-top__text" | ||
20 | + >{{text}}</text> | ||
21 | + </view> | ||
22 | + <slot v-else /> | ||
23 | + </u-transition> | ||
24 | +</template> | ||
25 | + | ||
26 | +<script> | ||
27 | + import props from './props.js'; | ||
28 | + // #ifdef APP-NVUE | ||
29 | + const dom = weex.requireModule('dom') | ||
30 | + // #endif | ||
31 | + /** | ||
32 | + * backTop 返回顶部 | ||
33 | + * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。 | ||
34 | + * @tutorial https://uviewui.com/components/backTop.html | ||
35 | + * | ||
36 | + * @property {String} mode 返回顶部的形状,circle-圆形,square-方形 (默认 'circle' ) | ||
37 | + * @property {String} icon 自定义图标 (默认 'arrow-upward' ) 见官方文档示例 | ||
38 | + * @property {String} text 提示文字 | ||
39 | + * @property {String | Number} duration 返回顶部滚动时间 (默认 100) | ||
40 | + * @property {String | Number} scrollTop 滚动距离 (默认 0 ) | ||
41 | + * @property {String | Number} top 距离顶部多少距离显示,单位px (默认 400 ) | ||
42 | + * @property {String | Number} bottom 返回顶部按钮到底部的距离,单位px (默认 100 ) | ||
43 | + * @property {String | Number} right 返回顶部按钮到右边的距离,单位px (默认 20 ) | ||
44 | + * @property {String | Number} zIndex 层级 (默认 9 ) | ||
45 | + * @property {Object<Object>} iconStyle 图标的样式,对象形式 (默认 {color: '#909399',fontSize: '19px'}) | ||
46 | + * @property {Object} customStyle 定义需要用到的外部样式 | ||
47 | + * | ||
48 | + * @example <u-back-top :scrollTop="scrollTop"></u-back-top> | ||
49 | + */ | ||
50 | + export default { | ||
51 | + name: 'u-back-top', | ||
52 | + mixins: [uni.$u.mpMixin, uni.$u.mixin,props], | ||
53 | + computed: { | ||
54 | + backTopStyle() { | ||
55 | + // 动画组件样式 | ||
56 | + const style = { | ||
57 | + bottom: uni.$u.addUnit(this.bottom), | ||
58 | + right: uni.$u.addUnit(this.right), | ||
59 | + width: '40px', | ||
60 | + height: '40px', | ||
61 | + position: 'fixed', | ||
62 | + zIndex: 10, | ||
63 | + } | ||
64 | + return style | ||
65 | + }, | ||
66 | + show() { | ||
67 | + return uni.$u.getPx(this.scrollTop) > uni.$u.getPx(this.top) | ||
68 | + }, | ||
69 | + contentStyle() { | ||
70 | + const style = {} | ||
71 | + let radius = 0 | ||
72 | + // 是否圆形 | ||
73 | + if(this.mode === 'circle') { | ||
74 | + radius = '100px' | ||
75 | + } else { | ||
76 | + radius = '4px' | ||
77 | + } | ||
78 | + // 为了兼容安卓nvue,只能这么分开写 | ||
79 | + style.borderTopLeftRadius = radius | ||
80 | + style.borderTopRightRadius = radius | ||
81 | + style.borderBottomLeftRadius = radius | ||
82 | + style.borderBottomRightRadius = radius | ||
83 | + return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle)) | ||
84 | + } | ||
85 | + }, | ||
86 | + methods: { | ||
87 | + backToTop() { | ||
88 | + // #ifdef APP-NVUE | ||
89 | + if (!this.$parent.$refs['u-back-top']) { | ||
90 | + uni.$u.error(`nvue页面需要给页面最外层元素设置"ref='u-back-top'`) | ||
91 | + } | ||
92 | + dom.scrollToElement(this.$parent.$refs['u-back-top'], { | ||
93 | + offset: 0 | ||
94 | + }) | ||
95 | + // #endif | ||
96 | + | ||
97 | + // #ifndef APP-NVUE | ||
98 | + uni.pageScrollTo({ | ||
99 | + scrollTop: 0, | ||
100 | + duration: this.duration | ||
101 | + }); | ||
102 | + // #endif | ||
103 | + this.$emit('click') | ||
104 | + } | ||
105 | + } | ||
106 | + } | ||
107 | +</script> | ||
108 | + | ||
109 | +<style lang="scss" scoped> | ||
110 | + @import '../../libs/css/components.scss'; | ||
111 | + $u-back-top-flex:1 !default; | ||
112 | + $u-back-top-height:100% !default; | ||
113 | + $u-back-top-background-color:#E1E1E1 !default; | ||
114 | + $u-back-top-tips-font-size:12px !default; | ||
115 | + .u-back-top { | ||
116 | + @include flex; | ||
117 | + flex-direction: column; | ||
118 | + align-items: center; | ||
119 | + flex:$u-back-top-flex; | ||
120 | + height: $u-back-top-height; | ||
121 | + justify-content: center; | ||
122 | + background-color: $u-back-top-background-color; | ||
123 | + | ||
124 | + &__tips { | ||
125 | + font-size:$u-back-top-tips-font-size; | ||
126 | + transform: scale(0.8); | ||
127 | + } | ||
128 | + } | ||
129 | +</style> |
1 | +export default { | ||
2 | + props: { | ||
3 | + // 是否显示圆点 | ||
4 | + isDot: { | ||
5 | + type: Boolean, | ||
6 | + default: uni.$u.props.badge.isDot | ||
7 | + }, | ||
8 | + // 显示的内容 | ||
9 | + value: { | ||
10 | + type: [Number, String], | ||
11 | + default: uni.$u.props.badge.value | ||
12 | + }, | ||
13 | + // 是否显示 | ||
14 | + show: { | ||
15 | + type: Boolean, | ||
16 | + default: uni.$u.props.badge.show | ||
17 | + }, | ||
18 | + // 最大值,超过最大值会显示 '{max}+' | ||
19 | + max: { | ||
20 | + type: [Number, String], | ||
21 | + default: uni.$u.props.badge.max | ||
22 | + }, | ||
23 | + // 主题类型,error|warning|success|primary | ||
24 | + type: { | ||
25 | + type: String, | ||
26 | + default: uni.$u.props.badge.type | ||
27 | + }, | ||
28 | + // 当数值为 0 时,是否展示 Badge | ||
29 | + showZero: { | ||
30 | + type: Boolean, | ||
31 | + default: uni.$u.props.badge.showZero | ||
32 | + }, | ||
33 | + // 背景颜色,优先级比type高,如设置,type参数会失效 | ||
34 | + bgColor: { | ||
35 | + type: [String, null], | ||
36 | + default: uni.$u.props.badge.bgColor | ||
37 | + }, | ||
38 | + // 字体颜色 | ||
39 | + color: { | ||
40 | + type: [String, null], | ||
41 | + default: uni.$u.props.badge.color | ||
42 | + }, | ||
43 | + // 徽标形状,circle-四角均为圆角,horn-左下角为直角 | ||
44 | + shape: { | ||
45 | + type: String, | ||
46 | + default: uni.$u.props.badge.shape | ||
47 | + }, | ||
48 | + // 设置数字的显示方式,overflow|ellipsis|limit | ||
49 | + // overflow会根据max字段判断,超出显示`${max}+` | ||
50 | + // ellipsis会根据max判断,超出显示`${max}...` | ||
51 | + // limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数 | ||
52 | + numberType: { | ||
53 | + type: String, | ||
54 | + default: uni.$u.props.badge.numberType | ||
55 | + }, | ||
56 | + // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效 | ||
57 | + offset: { | ||
58 | + type: Array, | ||
59 | + default: uni.$u.props.badge.offset | ||
60 | + }, | ||
61 | + // 是否反转背景和字体颜色 | ||
62 | + inverted: { | ||
63 | + type: Boolean, | ||
64 | + default: uni.$u.props.badge.inverted | ||
65 | + }, | ||
66 | + // 是否绝对定位 | ||
67 | + absolute: { | ||
68 | + type: Boolean, | ||
69 | + default: uni.$u.props.badge.absolute | ||
70 | + } | ||
71 | + } | ||
72 | +} |
1 | +<template> | ||
2 | + <text | ||
3 | + v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)" | ||
4 | + :class="[isDot ? 'u-badge--dot' : 'u-badge--not-dot', inverted && 'u-badge--inverted', shape === 'horn' && 'u-badge--horn', `u-badge--${type}${inverted ? '--inverted' : ''}`]" | ||
5 | + :style="[$u.addStyle(customStyle), badgeStyle]" | ||
6 | + class="u-badge" | ||
7 | + >{{ isDot ? '' :showValue }}</text> | ||
8 | +</template> | ||
9 | + | ||
10 | +<script> | ||
11 | + import props from './props.js'; | ||
12 | + /** | ||
13 | + * badge 徽标数 | ||
14 | + * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。 | ||
15 | + * @tutorial https://uviewui.com/components/badge.html | ||
16 | + * | ||
17 | + * @property {Boolean} isDot 是否显示圆点 (默认 false ) | ||
18 | + * @property {String | Number} value 显示的内容 | ||
19 | + * @property {Boolean} show 是否显示 (默认 true ) | ||
20 | + * @property {String | Number} max 最大值,超过最大值会显示 '{max}+' (默认999) | ||
21 | + * @property {String} type 主题类型,error|warning|success|primary (默认 'error' ) | ||
22 | + * @property {Boolean} showZero 当数值为 0 时,是否展示 Badge (默认 false ) | ||
23 | + * @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效 | ||
24 | + * @property {String} color 字体颜色 (默认 '#ffffff' ) | ||
25 | + * @property {String} shape 徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' ) | ||
26 | + * @property {String} numberType 设置数字的显示方式,overflow|ellipsis|limit (默认 'overflow' ) | ||
27 | + * @property {Array}} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效 | ||
28 | + * @property {Boolean} inverted 是否反转背景和字体颜色(默认 false ) | ||
29 | + * @property {Boolean} absolute 是否绝对定位(默认 false ) | ||
30 | + * @property {Object} customStyle 定义需要用到的外部样式 | ||
31 | + * @example <u-badge :type="type" :count="count"></u-badge> | ||
32 | + */ | ||
33 | + export default { | ||
34 | + name: 'u-badge', | ||
35 | + mixins: [uni.$u.mpMixin, props, uni.$u.mixin], | ||
36 | + computed: { | ||
37 | + // 是否将badge中心与父组件右上角重合 | ||
38 | + boxStyle() { | ||
39 | + let style = {}; | ||
40 | + return style; | ||
41 | + }, | ||
42 | + // 整个组件的样式 | ||
43 | + badgeStyle() { | ||
44 | + const style = {} | ||
45 | + if(this.color) { | ||
46 | + style.color = this.color | ||
47 | + } | ||
48 | + if (this.bgColor && !this.inverted) { | ||
49 | + style.backgroundColor = this.bgColor | ||
50 | + } | ||
51 | + if (this.absolute) { | ||
52 | + style.position = 'absolute' | ||
53 | + // 如果有设置offset参数 | ||
54 | + if(this.offset.length) { | ||
55 | + // top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top | ||
56 | + const top = this.offset[0] | ||
57 | + const right = this.offset[1] || top | ||
58 | + style.top = uni.$u.addUnit(top) | ||
59 | + style.right = uni.$u.addUnit(right) | ||
60 | + } | ||
61 | + } | ||
62 | + return style | ||
63 | + }, | ||
64 | + showValue() { | ||
65 | + switch (this.numberType) { | ||
66 | + case "overflow": | ||
67 | + return Number(this.value) > Number(this.max) ? this.max + "+" : this.value | ||
68 | + break; | ||
69 | + case "ellipsis": | ||
70 | + return Number(this.value) > Number(this.max) ? "..." : this.value | ||
71 | + break; | ||
72 | + case "limit": | ||
73 | + return Number(this.value) > 999 ? Number(this.value) >= 9999 ? | ||
74 | + Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value / | ||
75 | + 1e3 * 100) / 100 + "k" : this.value | ||
76 | + break; | ||
77 | + default: | ||
78 | + return Number(this.value) | ||
79 | + } | ||
80 | + }, | ||
81 | + } | ||
82 | + } | ||
83 | +</script> | ||
84 | + | ||
85 | +<style lang="scss" scoped> | ||
86 | + @import "../../libs/css/components.scss"; | ||
87 | + | ||
88 | + $u-badge-primary: $u-primary !default; | ||
89 | + $u-badge-error: $u-error !default; | ||
90 | + $u-badge-success: $u-success !default; | ||
91 | + $u-badge-info: $u-info !default; | ||
92 | + $u-badge-warning: $u-warning !default; | ||
93 | + $u-badge-dot-radius: 100px !default; | ||
94 | + $u-badge-dot-size: 8px !default; | ||
95 | + $u-badge-dot-right: 4px !default; | ||
96 | + $u-badge-dot-top: 0 !default; | ||
97 | + $u-badge-text-font-size: 11px !default; | ||
98 | + $u-badge-text-right: 10px !default; | ||
99 | + $u-badge-text-padding: 2px 5px !default; | ||
100 | + $u-badge-text-align: center !default; | ||
101 | + $u-badge-text-color: #FFFFFF !default; | ||
102 | + | ||
103 | + .u-badge { | ||
104 | + border-top-right-radius: $u-badge-dot-radius; | ||
105 | + border-top-left-radius: $u-badge-dot-radius; | ||
106 | + border-bottom-left-radius: $u-badge-dot-radius; | ||
107 | + border-bottom-right-radius: $u-badge-dot-radius; | ||
108 | + @include flex; | ||
109 | + line-height: $u-badge-text-font-size; | ||
110 | + text-align: $u-badge-text-align; | ||
111 | + font-size: $u-badge-text-font-size; | ||
112 | + color: $u-badge-text-color; | ||
113 | + | ||
114 | + &--dot { | ||
115 | + height: $u-badge-dot-size; | ||
116 | + width: $u-badge-dot-size; | ||
117 | + } | ||
118 | + | ||
119 | + &--inverted { | ||
120 | + font-size: 13px; | ||
121 | + } | ||
122 | + | ||
123 | + &--not-dot { | ||
124 | + padding: $u-badge-text-padding; | ||
125 | + } | ||
126 | + | ||
127 | + &--horn { | ||
128 | + border-bottom-left-radius: 0; | ||
129 | + } | ||
130 | + | ||
131 | + &--primary { | ||
132 | + background-color: $u-badge-primary; | ||
133 | + } | ||
134 | + | ||
135 | + &--primary--inverted { | ||
136 | + color: $u-badge-primary; | ||
137 | + } | ||
138 | + | ||
139 | + &--error { | ||
140 | + background-color: $u-badge-error; | ||
141 | + } | ||
142 | + | ||
143 | + &--error--inverted { | ||
144 | + color: $u-badge-error; | ||
145 | + } | ||
146 | + | ||
147 | + &--success { | ||
148 | + background-color: $u-badge-success; | ||
149 | + } | ||
150 | + | ||
151 | + &--success--inverted { | ||
152 | + color: $u-badge-success; | ||
153 | + } | ||
154 | + | ||
155 | + &--info { | ||
156 | + background-color: $u-badge-info; | ||
157 | + } | ||
158 | + | ||
159 | + &--info--inverted { | ||
160 | + color: $u-badge-info; | ||
161 | + } | ||
162 | + | ||
163 | + &--warning { | ||
164 | + background-color: $u-badge-warning; | ||
165 | + } | ||
166 | + | ||
167 | + &--warning--inverted { | ||
168 | + color: $u-badge-warning; | ||
169 | + } | ||
170 | + } | ||
171 | +</style> |
1 | +$u-button-active-opacity:0.75 !default; | ||
2 | +$u-button-loading-text-margin-left:4px !default; | ||
3 | +$u-button-text-color: #FFFFFF !default; | ||
4 | +$u-button-text-plain-error-color:$u-error !default; | ||
5 | +$u-button-text-plain-warning-color:$u-warning !default; | ||
6 | +$u-button-text-plain-success-color:$u-success !default; | ||
7 | +$u-button-text-plain-info-color:$u-info !default; | ||
8 | +$u-button-text-plain-primary-color:$u-primary !default; | ||
9 | +.u-button { | ||
10 | + &--active { | ||
11 | + opacity: $u-button-active-opacity; | ||
12 | + } | ||
13 | + | ||
14 | + &--active--plain { | ||
15 | + background-color: rgb(217, 217, 217); | ||
16 | + } | ||
17 | + | ||
18 | + &__loading-text { | ||
19 | + margin-left:$u-button-loading-text-margin-left; | ||
20 | + } | ||
21 | + | ||
22 | + &__text, | ||
23 | + &__loading-text { | ||
24 | + color:$u-button-text-color; | ||
25 | + } | ||
26 | + | ||
27 | + &__text--plain--error { | ||
28 | + color:$u-button-text-plain-error-color; | ||
29 | + } | ||
30 | + | ||
31 | + &__text--plain--warning { | ||
32 | + color:$u-button-text-plain-warning-color; | ||
33 | + } | ||
34 | + | ||
35 | + &__text--plain--success{ | ||
36 | + color:$u-button-text-plain-success-color; | ||
37 | + } | ||
38 | + | ||
39 | + &__text--plain--info { | ||
40 | + color:$u-button-text-plain-info-color; | ||
41 | + } | ||
42 | + | ||
43 | + &__text--plain--primary { | ||
44 | + color:$u-button-text-plain-primary-color; | ||
45 | + } | ||
46 | +} |
1 | +/* | ||
2 | + * @Author : LQ | ||
3 | + * @Description : | ||
4 | + * @version : 1.0 | ||
5 | + * @Date : 2021-08-16 10:04:04 | ||
6 | + * @LastAuthor : LQ | ||
7 | + * @lastTime : 2021-08-16 10:04:24 | ||
8 | + * @FilePath : /u-view2.0/uview-ui/components/u-button/props.js | ||
9 | + */ | ||
10 | +export default { | ||
11 | + props: { | ||
12 | + // 是否细边框 | ||
13 | + hairline: { | ||
14 | + type: Boolean, | ||
15 | + default: uni.$u.props.button.hairline | ||
16 | + }, | ||
17 | + // 按钮的预置样式,info,primary,error,warning,success | ||
18 | + type: { | ||
19 | + type: String, | ||
20 | + default: uni.$u.props.button.type | ||
21 | + }, | ||
22 | + // 按钮尺寸,large,normal,small,mini | ||
23 | + size: { | ||
24 | + type: String, | ||
25 | + default: uni.$u.props.button.size | ||
26 | + }, | ||
27 | + // 按钮形状,circle(两边为半圆),square(带圆角) | ||
28 | + shape: { | ||
29 | + type: String, | ||
30 | + default: uni.$u.props.button.shape | ||
31 | + }, | ||
32 | + // 按钮是否镂空 | ||
33 | + plain: { | ||
34 | + type: Boolean, | ||
35 | + default: uni.$u.props.button.plain | ||
36 | + }, | ||
37 | + // 是否禁止状态 | ||
38 | + disabled: { | ||
39 | + type: Boolean, | ||
40 | + default: uni.$u.props.button.disabled | ||
41 | + }, | ||
42 | + // 是否加载中 | ||
43 | + loading: { | ||
44 | + type: Boolean, | ||
45 | + default: uni.$u.props.button.loading | ||
46 | + }, | ||
47 | + // 加载中提示文字 | ||
48 | + loadingText: { | ||
49 | + type: [String, Number], | ||
50 | + default: uni.$u.props.button.loadingText | ||
51 | + }, | ||
52 | + // 加载状态图标类型 | ||
53 | + loadingMode: { | ||
54 | + type: String, | ||
55 | + default: uni.$u.props.button.loadingMode | ||
56 | + }, | ||
57 | + // 加载图标大小 | ||
58 | + loadingSize: { | ||
59 | + type: [String, Number], | ||
60 | + default: uni.$u.props.button.loadingSize | ||
61 | + }, | ||
62 | + // 开放能力,具体请看uniapp稳定关于button组件部分说明 | ||
63 | + // https://uniapp.dcloud.io/component/button | ||
64 | + openType: { | ||
65 | + type: String, | ||
66 | + default: uni.$u.props.button.openType | ||
67 | + }, | ||
68 | + // 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 | ||
69 | + // 取值为submit(提交表单),reset(重置表单) | ||
70 | + formType: { | ||
71 | + type: String, | ||
72 | + default: uni.$u.props.button.formType | ||
73 | + }, | ||
74 | + // 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 | ||
75 | + // 只微信小程序、QQ小程序有效 | ||
76 | + appParameter: { | ||
77 | + type: String, | ||
78 | + default: uni.$u.props.button.appParameter | ||
79 | + }, | ||
80 | + // 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效 | ||
81 | + hoverStopPropagation: { | ||
82 | + type: Boolean, | ||
83 | + default: uni.$u.props.button.hoverStopPropagation | ||
84 | + }, | ||
85 | + // 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效 | ||
86 | + lang: { | ||
87 | + type: String, | ||
88 | + default: uni.$u.props.button.lang | ||
89 | + }, | ||
90 | + // 会话来源,open-type="contact"时有效。只微信小程序有效 | ||
91 | + sessionFrom: { | ||
92 | + type: String, | ||
93 | + default: uni.$u.props.button.sessionFrom | ||
94 | + }, | ||
95 | + // 会话内消息卡片标题,open-type="contact"时有效 | ||
96 | + // 默认当前标题,只微信小程序有效 | ||
97 | + sendMessageTitle: { | ||
98 | + type: String, | ||
99 | + default: uni.$u.props.button.sendMessageTitle | ||
100 | + }, | ||
101 | + // 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效 | ||
102 | + // 默认当前分享路径,只微信小程序有效 | ||
103 | + sendMessagePath: { | ||
104 | + type: String, | ||
105 | + default: uni.$u.props.button.sendMessagePath | ||
106 | + }, | ||
107 | + // 会话内消息卡片图片,open-type="contact"时有效 | ||
108 | + // 默认当前页面截图,只微信小程序有效 | ||
109 | + sendMessageImg: { | ||
110 | + type: String, | ||
111 | + default: uni.$u.props.button.sendMessageImg | ||
112 | + }, | ||
113 | + // 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示, | ||
114 | + // 用户点击后可以快速发送小程序消息,open-type="contact"时有效 | ||
115 | + showMessageCard: { | ||
116 | + type: Boolean, | ||
117 | + default: uni.$u.props.button.showMessageCard | ||
118 | + }, | ||
119 | + // 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取 | ||
120 | + dataName: { | ||
121 | + type: String, | ||
122 | + default: uni.$u.props.button.dataName | ||
123 | + }, | ||
124 | + // 节流,一定时间内只能触发一次 | ||
125 | + throttleTime: { | ||
126 | + type: [String, Number], | ||
127 | + default: uni.$u.props.button.throttleTime | ||
128 | + }, | ||
129 | + // 按住后多久出现点击态,单位毫秒 | ||
130 | + hoverStartTime: { | ||
131 | + type: [String, Number], | ||
132 | + default: uni.$u.props.button.hoverStartTime | ||
133 | + }, | ||
134 | + // 手指松开后点击态保留时间,单位毫秒 | ||
135 | + hoverStayTime: { | ||
136 | + type: [String, Number], | ||
137 | + default: uni.$u.props.button.hoverStayTime | ||
138 | + }, | ||
139 | + // 按钮文字,之所以通过props传入,是因为slot传入的话 | ||
140 | + // nvue中无法控制文字的样式 | ||
141 | + text: { | ||
142 | + type: [String, Number], | ||
143 | + default: uni.$u.props.button.text | ||
144 | + }, | ||
145 | + // 按钮图标 | ||
146 | + icon: { | ||
147 | + type: String, | ||
148 | + default: uni.$u.props.button.icon | ||
149 | + }, | ||
150 | + // 按钮图标 | ||
151 | + iconColor: { | ||
152 | + type: String, | ||
153 | + default: uni.$u.props.button.icon | ||
154 | + }, | ||
155 | + // 按钮颜色,支持传入linear-gradient渐变色 | ||
156 | + color: { | ||
157 | + type: String, | ||
158 | + default: uni.$u.props.button.color | ||
159 | + } | ||
160 | + } | ||
161 | +} |
1 | +<template> | ||
2 | + <!-- #ifndef APP-NVUE --> | ||
3 | + <button | ||
4 | + :hover-start-time="Number(hoverStartTime)" | ||
5 | + :hover-stay-time="Number(hoverStayTime)" | ||
6 | + :form-type="formType" | ||
7 | + :open-type="openType" | ||
8 | + :app-parameter="appParameter" | ||
9 | + :hover-stop-propagation="hoverStopPropagation" | ||
10 | + :send-message-title="sendMessageTitle" | ||
11 | + :send-message-path="sendMessagePath" | ||
12 | + :lang="lang" | ||
13 | + :data-name="dataName" | ||
14 | + :session-from="sessionFrom" | ||
15 | + :send-message-img="sendMessageImg" | ||
16 | + :show-message-card="showMessageCard" | ||
17 | + @getphonenumber="getphonenumber" | ||
18 | + @getuserinfo="getuserinfo" | ||
19 | + @error="error" | ||
20 | + @opensetting="opensetting" | ||
21 | + @launchapp="launchapp" | ||
22 | + :hover-class="!disabled && !loading ? 'u-button--active' : ''" | ||
23 | + class="u-button u-reset-button" | ||
24 | + :style="[baseColor, $u.addStyle(customStyle)]" | ||
25 | + @tap="clickHandler" | ||
26 | + :class="bemClass" | ||
27 | + > | ||
28 | + <template v-if="loading"> | ||
29 | + <u-loading-icon | ||
30 | + :mode="loadingMode" | ||
31 | + :size="textSize * 1.15" | ||
32 | + :color="loadingColor" | ||
33 | + ></u-loading-icon> | ||
34 | + <text | ||
35 | + class="u-button__loading-text" | ||
36 | + :style="[{ fontSize: textSize + 'px' }]" | ||
37 | + >{{ loadingText || text }}</text | ||
38 | + > | ||
39 | + </template> | ||
40 | + <template v-else> | ||
41 | + <u-icon | ||
42 | + v-if="icon" | ||
43 | + :name="icon" | ||
44 | + :color="iconColorCom" | ||
45 | + :size="textSize * 1.35" | ||
46 | + :customStyle="{ marginRight: '2px' }" | ||
47 | + ></u-icon> | ||
48 | + <slot> | ||
49 | + <text | ||
50 | + class="u-button__text" | ||
51 | + :style="[{ fontSize: textSize + 'px' }]" | ||
52 | + >{{ text }}</text | ||
53 | + > | ||
54 | + </slot> | ||
55 | + </template> | ||
56 | + </button> | ||
57 | + <!-- #endif --> | ||
58 | + | ||
59 | + <!-- #ifdef APP-NVUE --> | ||
60 | + <view | ||
61 | + :hover-start-time="Number(hoverStartTime)" | ||
62 | + :hover-stay-time="Number(hoverStayTime)" | ||
63 | + class="u-button" | ||
64 | + :hover-class=" | ||
65 | + !disabled && !loading && !color && (plain || type === 'info') | ||
66 | + ? 'u-button--active--plain' | ||
67 | + : !disabled && !loading && !plain | ||
68 | + ? 'u-button--active' | ||
69 | + : '' | ||
70 | + " | ||
71 | + @tap="clickHandler" | ||
72 | + :class="bemClass" | ||
73 | + :style="[baseColor, $u.addStyle(customStyle)]" | ||
74 | + > | ||
75 | + <template v-if="loading"> | ||
76 | + <u-loading-icon | ||
77 | + :mode="loadingMode" | ||
78 | + :size="textSize * 1.15" | ||
79 | + :color="loadingColor" | ||
80 | + ></u-loading-icon> | ||
81 | + <text | ||
82 | + class="u-button__loading-text" | ||
83 | + :style="[nvueTextStyle]" | ||
84 | + :class="[plain && `u-button__text--plain--${type}`]" | ||
85 | + >{{ loadingText || text }}</text | ||
86 | + > | ||
87 | + </template> | ||
88 | + <template v-else> | ||
89 | + <u-icon | ||
90 | + v-if="icon" | ||
91 | + :name="icon" | ||
92 | + :color="iconColorCom" | ||
93 | + :size="textSize * 1.35" | ||
94 | + ></u-icon> | ||
95 | + <text | ||
96 | + class="u-button__text" | ||
97 | + :style="[ | ||
98 | + { | ||
99 | + marginLeft: icon ? '2px' : 0, | ||
100 | + }, | ||
101 | + nvueTextStyle, | ||
102 | + ]" | ||
103 | + :class="[plain && `u-button__text--plain--${type}`]" | ||
104 | + >{{ text }}</text | ||
105 | + > | ||
106 | + </template> | ||
107 | + </view> | ||
108 | + <!-- #endif --> | ||
109 | +</template> | ||
110 | + | ||
111 | +<script> | ||
112 | +import button from "../../libs/mixin/button.js"; | ||
113 | +import openType from "../../libs/mixin/openType.js"; | ||
114 | +import props from "./props.js"; | ||
115 | +/** | ||
116 | + * button 按钮 | ||
117 | + * @description Button 按钮 | ||
118 | + * @tutorial https://www.uviewui.com/components/button.html | ||
119 | + * | ||
120 | + * @property {Boolean} hairline 是否显示按钮的细边框 (默认 true ) | ||
121 | + * @property {String} type 按钮的预置样式,info,primary,error,warning,success (默认 'info' ) | ||
122 | + * @property {String} size 按钮尺寸,large,normal,mini (默认 normal) | ||
123 | + * @property {String} shape 按钮形状,circle(两边为半圆),square(带圆角) (默认 'square' ) | ||
124 | + * @property {Boolean} plain 按钮是否镂空,背景色透明 (默认 false) | ||
125 | + * @property {Boolean} disabled 是否禁用 (默认 false) | ||
126 | + * @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) (默认 false) | ||
127 | + * @property {String | Number} loadingText 加载中提示文字 | ||
128 | + * @property {String} loadingMode 加载状态图标类型 (默认 'spinner' ) | ||
129 | + * @property {String | Number} loadingSize 加载图标大小 (默认 15 ) | ||
130 | + * @property {String} openType 开放能力,具体请看uniapp稳定关于button组件部分说明 | ||
131 | + * @property {String} formType 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 | ||
132 | + * @property {String} appParameter 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 (注:只微信小程序、QQ小程序有效) | ||
133 | + * @property {Boolean} hoverStopPropagation 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true ) | ||
134 | + * @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文(默认 en ) | ||
135 | + * @property {String} sessionFrom 会话来源,openType="contact"时有效 | ||
136 | + * @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效 | ||
137 | + * @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效 | ||
138 | + * @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效 | ||
139 | + * @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效(默认false) | ||
140 | + * @property {String} dataName 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取 | ||
141 | + * @property {String | Number} throttleTime 节流,一定时间内只能触发一次 (默认 0 ) | ||
142 | + * @property {String | Number} hoverStartTime 按住后多久出现点击态,单位毫秒 (默认 0 ) | ||
143 | + * @property {String | Number} hoverStayTime 手指松开后点击态保留时间,单位毫秒 (默认 200 ) | ||
144 | + * @property {String | Number} text 按钮文字,之所以通过props传入,是因为slot传入的话(注:nvue中无法控制文字的样式) | ||
145 | + * @property {String} icon 按钮图标 | ||
146 | + * @property {String} iconColor 按钮图标颜色 | ||
147 | + * @property {String} color 按钮颜色,支持传入linear-gradient渐变色 | ||
148 | + * @property {Object} customStyle 定义需要用到的外部样式 | ||
149 | + * | ||
150 | + * @event {Function} click 非禁止并且非加载中,才能点击 | ||
151 | + * @event {Function} getphonenumber open-type="getPhoneNumber"时有效 | ||
152 | + * @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo | ||
153 | + * @event {Function} error 当使用开放能力时,发生错误的回调 | ||
154 | + * @event {Function} opensetting 在打开授权设置页并关闭后回调 | ||
155 | + * @event {Function} launchapp 打开 APP 成功的回调 | ||
156 | + * @example <u-button>月落</u-button> | ||
157 | + */ | ||
158 | +export default { | ||
159 | + name: "u-button", | ||
160 | + // #ifdef MP | ||
161 | + mixins: [uni.$u.mpMixin, uni.$u.mixin, button, openType, props], | ||
162 | + // #endif | ||
163 | + // #ifndef MP | ||
164 | + mixins: [uni.$u.mpMixin, uni.$u.mixin, props], | ||
165 | + // #endif | ||
166 | + data() { | ||
167 | + return {}; | ||
168 | + }, | ||
169 | + computed: { | ||
170 | + // 生成bem风格的类名 | ||
171 | + bemClass() { | ||
172 | + // this.bem为一个computed变量,在mixin中 | ||
173 | + if (!this.color) { | ||
174 | + return this.bem( | ||
175 | + "button", | ||
176 | + ["type", "shape", "size"], | ||
177 | + ["disabled", "plain", "hairline"] | ||
178 | + ); | ||
179 | + } else { | ||
180 | + // 由于nvue的原因,在有color参数时,不需要传入type,否则会生成type相关的类型,影响最终的样式 | ||
181 | + return this.bem( | ||
182 | + "button", | ||
183 | + ["shape", "size"], | ||
184 | + ["disabled", "plain", "hairline"] | ||
185 | + ); | ||
186 | + } | ||
187 | + }, | ||
188 | + loadingColor() { | ||
189 | + if (this.plain) { | ||
190 | + // 如果有设置color值,则用color值,否则使用type主题颜色 | ||
191 | + return this.color | ||
192 | + ? this.color | ||
193 | + : uni.$u.config.color[`u-${this.type}`]; | ||
194 | + } | ||
195 | + if (this.type === "info") { | ||
196 | + return "#c9c9c9"; | ||
197 | + } | ||
198 | + return "rgb(200, 200, 200)"; | ||
199 | + }, | ||
200 | + iconColorCom() { | ||
201 | + // 如果是镂空状态,设置了color就用color值,否则使用主题颜色, | ||
202 | + // u-icon的color能接受一个主题颜色的值 | ||
203 | + if (this.iconColor) return this.iconColor; | ||
204 | + if (this.plain) { | ||
205 | + return this.color ? this.color : this.type; | ||
206 | + } else { | ||
207 | + return this.type === "info" ? "#000000" : "#ffffff"; | ||
208 | + } | ||
209 | + }, | ||
210 | + baseColor() { | ||
211 | + let style = {}; | ||
212 | + if (this.color) { | ||
213 | + // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色 | ||
214 | + style.color = this.plain ? this.color : "white"; | ||
215 | + if (!this.plain) { | ||
216 | + // 非镂空,背景色使用自定义的颜色 | ||
217 | + style["background-color"] = this.color; | ||
218 | + } | ||
219 | + if (this.color.indexOf("gradient") !== -1) { | ||
220 | + // 如果自定义的颜色为渐变色,不显示边框,以及通过backgroundImage设置渐变色 | ||
221 | + // weex文档说明可以写borderWidth的形式,为什么这里需要分开写? | ||
222 | + // 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西,所以需要这么写才有效 | ||
223 | + style.borderTopWidth = 0; | ||
224 | + style.borderRightWidth = 0; | ||
225 | + style.borderBottomWidth = 0; | ||
226 | + style.borderLeftWidth = 0; | ||
227 | + if (!this.plain) { | ||
228 | + style.backgroundImage = this.color; | ||
229 | + } | ||
230 | + } else { | ||
231 | + // 非渐变色,则设置边框相关的属性 | ||
232 | + style.borderColor = this.color; | ||
233 | + style.borderWidth = "1px"; | ||
234 | + style.borderStyle = "solid"; | ||
235 | + } | ||
236 | + } | ||
237 | + return style; | ||
238 | + }, | ||
239 | + // nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置 | ||
240 | + nvueTextStyle() { | ||
241 | + let style = {}; | ||
242 | + // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色 | ||
243 | + if (this.type === "info") { | ||
244 | + style.color = "#323233"; | ||
245 | + } | ||
246 | + if (this.color) { | ||
247 | + style.color = this.plain ? this.color : "white"; | ||
248 | + } | ||
249 | + style.fontSize = this.textSize + "px"; | ||
250 | + return style; | ||
251 | + }, | ||
252 | + // 字体大小 | ||
253 | + textSize() { | ||
254 | + let fontSize = 14, | ||
255 | + { size } = this; | ||
256 | + if (size === "large") fontSize = 16; | ||
257 | + if (size === "normal") fontSize = 14; | ||
258 | + if (size === "small") fontSize = 12; | ||
259 | + if (size === "mini") fontSize = 10; | ||
260 | + return fontSize; | ||
261 | + }, | ||
262 | + }, | ||
263 | + methods: { | ||
264 | + clickHandler() { | ||
265 | + // 非禁止并且非加载中,才能点击 | ||
266 | + if (!this.disabled && !this.loading) { | ||
267 | + // 进行节流控制,每this.throttle毫秒内,只在开始处执行 | ||
268 | + uni.$u.throttle(() => { | ||
269 | + this.$emit("click"); | ||
270 | + }, this.throttleTime); | ||
271 | + } | ||
272 | + }, | ||
273 | + // 下面为对接uniapp官方按钮开放能力事件回调的对接 | ||
274 | + getphonenumber(res) { | ||
275 | + this.$emit("getphonenumber", res); | ||
276 | + }, | ||
277 | + getuserinfo(res) { | ||
278 | + this.$emit("getuserinfo", res); | ||
279 | + }, | ||
280 | + error(res) { | ||
281 | + this.$emit("error", res); | ||
282 | + }, | ||
283 | + opensetting(res) { | ||
284 | + this.$emit("opensetting", res); | ||
285 | + }, | ||
286 | + launchapp(res) { | ||
287 | + this.$emit("launchapp", res); | ||
288 | + }, | ||
289 | + }, | ||
290 | +}; | ||
291 | +</script> | ||
292 | + | ||
293 | +<style lang="scss" scoped> | ||
294 | +@import "../../libs/css/components.scss"; | ||
295 | + | ||
296 | +/* #ifndef APP-NVUE */ | ||
297 | +@import "./vue.scss"; | ||
298 | +/* #endif */ | ||
299 | + | ||
300 | +/* #ifdef APP-NVUE */ | ||
301 | +@import "./nvue.scss"; | ||
302 | +/* #endif */ | ||
303 | + | ||
304 | +$u-button-u-button-height: 40px !default; | ||
305 | +$u-button-text-font-size: 15px !default; | ||
306 | +$u-button-loading-text-font-size: 15px !default; | ||
307 | +$u-button-loading-text-margin-left: 4px !default; | ||
308 | +$u-button-large-width: 100% !default; | ||
309 | +$u-button-large-height: 50px !default; | ||
310 | +$u-button-normal-padding: 0 12px !default; | ||
311 | +$u-button-large-padding: 0 15px !default; | ||
312 | +$u-button-normal-font-size: 14px !default; | ||
313 | +$u-button-small-min-width: 60px !default; | ||
314 | +$u-button-small-height: 30px !default; | ||
315 | +$u-button-small-padding: 0px 8px !default; | ||
316 | +$u-button-mini-padding: 0px 8px !default; | ||
317 | +$u-button-small-font-size: 12px !default; | ||
318 | +$u-button-mini-height: 22px !default; | ||
319 | +$u-button-mini-font-size: 10px !default; | ||
320 | +$u-button-mini-min-width: 50px !default; | ||
321 | +$u-button-disabled-opacity: 0.5 !default; | ||
322 | +$u-button-info-color: #323233 !default; | ||
323 | +$u-button-info-background-color: #fff !default; | ||
324 | +$u-button-info-border-color: #ebedf0 !default; | ||
325 | +$u-button-info-border-width: 1px !default; | ||
326 | +$u-button-info-border-style: solid !default; | ||
327 | +$u-button-success-color: #fff !default; | ||
328 | +$u-button-success-background-color: $u-success !default; | ||
329 | +$u-button-success-border-color: $u-button-success-background-color !default; | ||
330 | +$u-button-success-border-width: 1px !default; | ||
331 | +$u-button-success-border-style: solid !default; | ||
332 | +$u-button-primary-color: #fff !default; | ||
333 | +$u-button-primary-background-color: $u-primary !default; | ||
334 | +$u-button-primary-border-color: $u-button-primary-background-color !default; | ||
335 | +$u-button-primary-border-width: 1px !default; | ||
336 | +$u-button-primary-border-style: solid !default; | ||
337 | +$u-button-error-color: #fff !default; | ||
338 | +$u-button-error-background-color: $u-error !default; | ||
339 | +$u-button-error-border-color: $u-button-error-background-color !default; | ||
340 | +$u-button-error-border-width: 1px !default; | ||
341 | +$u-button-error-border-style: solid !default; | ||
342 | +$u-button-warning-color: #fff !default; | ||
343 | +$u-button-warning-background-color: $u-warning !default; | ||
344 | +$u-button-warning-border-color: $u-button-warning-background-color !default; | ||
345 | +$u-button-warning-border-width: 1px !default; | ||
346 | +$u-button-warning-border-style: solid !default; | ||
347 | +$u-button-block-width: 100% !default; | ||
348 | +$u-button-circle-border-top-right-radius: 100px !default; | ||
349 | +$u-button-circle-border-top-left-radius: 100px !default; | ||
350 | +$u-button-circle-border-bottom-left-radius: 100px !default; | ||
351 | +$u-button-circle-border-bottom-right-radius: 100px !default; | ||
352 | +$u-button-square-border-top-right-radius: 3px !default; | ||
353 | +$u-button-square-border-top-left-radius: 3px !default; | ||
354 | +$u-button-square-border-bottom-left-radius: 3px !default; | ||
355 | +$u-button-square-border-bottom-right-radius: 3px !default; | ||
356 | +$u-button-icon-min-width: 1em !default; | ||
357 | +$u-button-plain-background-color: #fff !default; | ||
358 | +$u-button-hairline-border-width: 0.5px !default; | ||
359 | + | ||
360 | +.u-button { | ||
361 | + height: $u-button-u-button-height; | ||
362 | + position: relative; | ||
363 | + align-items: center; | ||
364 | + justify-content: center; | ||
365 | + @include flex; | ||
366 | + /* #ifndef APP-NVUE */ | ||
367 | + box-sizing: border-box; | ||
368 | + /* #endif */ | ||
369 | + flex-direction: row; | ||
370 | + | ||
371 | + &__text { | ||
372 | + font-size: $u-button-text-font-size; | ||
373 | + } | ||
374 | + | ||
375 | + &__loading-text { | ||
376 | + font-size: $u-button-loading-text-font-size; | ||
377 | + margin-left: $u-button-loading-text-margin-left; | ||
378 | + } | ||
379 | + | ||
380 | + &--large { | ||
381 | + /* #ifndef APP-NVUE */ | ||
382 | + width: $u-button-large-width; | ||
383 | + /* #endif */ | ||
384 | + height: $u-button-large-height; | ||
385 | + padding: $u-button-large-padding; | ||
386 | + } | ||
387 | + | ||
388 | + &--normal { | ||
389 | + padding: $u-button-normal-padding; | ||
390 | + font-size: $u-button-normal-font-size; | ||
391 | + } | ||
392 | + | ||
393 | + &--small { | ||
394 | + /* #ifndef APP-NVUE */ | ||
395 | + min-width: $u-button-small-min-width; | ||
396 | + /* #endif */ | ||
397 | + height: $u-button-small-height; | ||
398 | + padding: $u-button-small-padding; | ||
399 | + font-size: $u-button-small-font-size; | ||
400 | + } | ||
401 | + | ||
402 | + &--mini { | ||
403 | + height: $u-button-mini-height; | ||
404 | + font-size: $u-button-mini-font-size; | ||
405 | + /* #ifndef APP-NVUE */ | ||
406 | + min-width: $u-button-mini-min-width; | ||
407 | + /* #endif */ | ||
408 | + padding: $u-button-mini-padding; | ||
409 | + } | ||
410 | + | ||
411 | + &--disabled { | ||
412 | + opacity: $u-button-disabled-opacity; | ||
413 | + } | ||
414 | + | ||
415 | + &--info { | ||
416 | + color: $u-button-info-color; | ||
417 | + background-color: $u-button-info-background-color; | ||
418 | + border-color: $u-button-info-border-color; | ||
419 | + border-width: $u-button-info-border-width; | ||
420 | + border-style: $u-button-info-border-style; | ||
421 | + } | ||
422 | + | ||
423 | + &--success { | ||
424 | + color: $u-button-success-color; | ||
425 | + background-color: $u-button-success-background-color; | ||
426 | + border-color: $u-button-success-border-color; | ||
427 | + border-width: $u-button-success-border-width; | ||
428 | + border-style: $u-button-success-border-style; | ||
429 | + } | ||
430 | + | ||
431 | + &--primary { | ||
432 | + color: $u-button-primary-color; | ||
433 | + background-color: $u-button-primary-background-color; | ||
434 | + border-color: $u-button-primary-border-color; | ||
435 | + border-width: $u-button-primary-border-width; | ||
436 | + border-style: $u-button-primary-border-style; | ||
437 | + } | ||
438 | + | ||
439 | + &--error { | ||
440 | + color: $u-button-error-color; | ||
441 | + background-color: $u-button-error-background-color; | ||
442 | + border-color: $u-button-error-border-color; | ||
443 | + border-width: $u-button-error-border-width; | ||
444 | + border-style: $u-button-error-border-style; | ||
445 | + } | ||
446 | + | ||
447 | + &--warning { | ||
448 | + color: $u-button-warning-color; | ||
449 | + background-color: $u-button-warning-background-color; | ||
450 | + border-color: $u-button-warning-border-color; | ||
451 | + border-width: $u-button-warning-border-width; | ||
452 | + border-style: $u-button-warning-border-style; | ||
453 | + } | ||
454 | + | ||
455 | + &--block { | ||
456 | + @include flex; | ||
457 | + width: $u-button-block-width; | ||
458 | + } | ||
459 | + | ||
460 | + &--circle { | ||
461 | + border-top-right-radius: $u-button-circle-border-top-right-radius; | ||
462 | + border-top-left-radius: $u-button-circle-border-top-left-radius; | ||
463 | + border-bottom-left-radius: $u-button-circle-border-bottom-left-radius; | ||
464 | + border-bottom-right-radius: $u-button-circle-border-bottom-right-radius; | ||
465 | + } | ||
466 | + | ||
467 | + &--square { | ||
468 | + border-bottom-left-radius: $u-button-square-border-top-right-radius; | ||
469 | + border-bottom-right-radius: $u-button-square-border-top-left-radius; | ||
470 | + border-top-left-radius: $u-button-square-border-bottom-left-radius; | ||
471 | + border-top-right-radius: $u-button-square-border-bottom-right-radius; | ||
472 | + } | ||
473 | + | ||
474 | + &__icon { | ||
475 | + /* #ifndef APP-NVUE */ | ||
476 | + min-width: $u-button-icon-min-width; | ||
477 | + line-height: inherit !important; | ||
478 | + vertical-align: top; | ||
479 | + /* #endif */ | ||
480 | + } | ||
481 | + | ||
482 | + &--plain { | ||
483 | + background-color: $u-button-plain-background-color; | ||
484 | + } | ||
485 | + | ||
486 | + &--hairline { | ||
487 | + border-width: $u-button-hairline-border-width !important; | ||
488 | + } | ||
489 | +} | ||
490 | +</style> |
1 | +// nvue下hover-class无效 | ||
2 | +$u-button-before-top:50% !default; | ||
3 | +$u-button-before-left:50% !default; | ||
4 | +$u-button-before-width:100% !default; | ||
5 | +$u-button-before-height:100% !default; | ||
6 | +$u-button-before-transform:translate(-50%, -50%) !default; | ||
7 | +$u-button-before-opacity:0 !default; | ||
8 | +$u-button-before-background-color:#000 !default; | ||
9 | +$u-button-before-border-color:#000 !default; | ||
10 | +$u-button-active-before-opacity:.15 !default; | ||
11 | +$u-button-icon-margin-left:4px !default; | ||
12 | +$u-button-plain-u-button-info-color:$u-info; | ||
13 | +$u-button-plain-u-button-success-color:$u-success; | ||
14 | +$u-button-plain-u-button-error-color:$u-error; | ||
15 | +$u-button-plain-u-button-warning-color:$u-error; | ||
16 | + | ||
17 | +.u-button { | ||
18 | + width: 100%; | ||
19 | + | ||
20 | + &__text { | ||
21 | + white-space: nowrap; | ||
22 | + line-height: 1; | ||
23 | + } | ||
24 | + | ||
25 | + &:before { | ||
26 | + position: absolute; | ||
27 | + top:$u-button-before-top; | ||
28 | + left:$u-button-before-left; | ||
29 | + width:$u-button-before-width; | ||
30 | + height:$u-button-before-height; | ||
31 | + border: inherit; | ||
32 | + border-radius: inherit; | ||
33 | + transform:$u-button-before-transform; | ||
34 | + opacity:$u-button-before-opacity; | ||
35 | + content: " "; | ||
36 | + background-color:$u-button-before-background-color; | ||
37 | + border-color:$u-button-before-border-color; | ||
38 | + } | ||
39 | + | ||
40 | + &--active { | ||
41 | + &:before { | ||
42 | + opacity: .15 | ||
43 | + } | ||
44 | + } | ||
45 | + | ||
46 | + &__icon+&__text:not(:empty), | ||
47 | + &__loading-text { | ||
48 | + margin-left:$u-button-icon-margin-left; | ||
49 | + } | ||
50 | + | ||
51 | + &--plain { | ||
52 | + &.u-button--primary { | ||
53 | + color: $u-primary; | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + &--plain { | ||
58 | + &.u-button--info { | ||
59 | + color:$u-button-plain-u-button-info-color; | ||
60 | + } | ||
61 | + } | ||
62 | + | ||
63 | + &--plain { | ||
64 | + &.u-button--success { | ||
65 | + color:$u-button-plain-u-button-success-color; | ||
66 | + } | ||
67 | + } | ||
68 | + | ||
69 | + &--plain { | ||
70 | + &.u-button--error { | ||
71 | + color:$u-button-plain-u-button-error-color; | ||
72 | + } | ||
73 | + } | ||
74 | + | ||
75 | + &--plain { | ||
76 | + &.u-button--warning { | ||
77 | + color:$u-button-plain-u-button-warning-color; | ||
78 | + } | ||
79 | + } | ||
80 | +} |
1 | +<template> | ||
2 | + <view class="u-calendar-header u-border-bottom"> | ||
3 | + <text | ||
4 | + class="u-calendar-header__title" | ||
5 | + v-if="showTitle" | ||
6 | + >{{ title }}</text> | ||
7 | + <text | ||
8 | + class="u-calendar-header__subtitle" | ||
9 | + v-if="showSubtitle" | ||
10 | + >{{ subtitle }}</text> | ||
11 | + <view class="u-calendar-header__weekdays"> | ||
12 | + <text class="u-calendar-header__weekdays__weekday">一</text> | ||
13 | + <text class="u-calendar-header__weekdays__weekday">二</text> | ||
14 | + <text class="u-calendar-header__weekdays__weekday">三</text> | ||
15 | + <text class="u-calendar-header__weekdays__weekday">四</text> | ||
16 | + <text class="u-calendar-header__weekdays__weekday">五</text> | ||
17 | + <text class="u-calendar-header__weekdays__weekday">六</text> | ||
18 | + <text class="u-calendar-header__weekdays__weekday">日</text> | ||
19 | + </view> | ||
20 | + </view> | ||
21 | +</template> | ||
22 | + | ||
23 | +<script> | ||
24 | + export default { | ||
25 | + name: 'u-calendar-header', | ||
26 | + mixins: [uni.$u.mpMixin, uni.$u.mixin], | ||
27 | + props: { | ||
28 | + // 标题 | ||
29 | + title: { | ||
30 | + type: String, | ||
31 | + default: '' | ||
32 | + }, | ||
33 | + // 副标题 | ||
34 | + subtitle: { | ||
35 | + type: String, | ||
36 | + default: '' | ||
37 | + }, | ||
38 | + // 是否显示标题 | ||
39 | + showTitle: { | ||
40 | + type: Boolean, | ||
41 | + default: true | ||
42 | + }, | ||
43 | + // 是否显示副标题 | ||
44 | + showSubtitle: { | ||
45 | + type: Boolean, | ||
46 | + default: true | ||
47 | + }, | ||
48 | + }, | ||
49 | + data() { | ||
50 | + return { | ||
51 | + | ||
52 | + } | ||
53 | + }, | ||
54 | + methods: { | ||
55 | + name() { | ||
56 | + | ||
57 | + } | ||
58 | + }, | ||
59 | + } | ||
60 | +</script> | ||
61 | + | ||
62 | +<style lang="scss" scoped> | ||
63 | + @import "../../libs/css/components.scss"; | ||
64 | + | ||
65 | + .u-calendar-header { | ||
66 | + padding-bottom: 4px; | ||
67 | + | ||
68 | + &__title { | ||
69 | + font-size: 16px; | ||
70 | + color: $u-main-color; | ||
71 | + text-align: center; | ||
72 | + height: 42px; | ||
73 | + line-height: 42px; | ||
74 | + font-weight: bold; | ||
75 | + } | ||
76 | + | ||
77 | + &__subtitle { | ||
78 | + font-size: 14px; | ||
79 | + color: $u-main-color; | ||
80 | + height: 40px; | ||
81 | + text-align: center; | ||
82 | + line-height: 40px; | ||
83 | + font-weight: bold; | ||
84 | + } | ||
85 | + | ||
86 | + &__weekdays { | ||
87 | + @include flex; | ||
88 | + justify-content: space-between; | ||
89 | + | ||
90 | + &__weekday { | ||
91 | + font-size: 13px; | ||
92 | + color: $u-main-color; | ||
93 | + line-height: 30px; | ||
94 | + flex: 1; | ||
95 | + text-align: center; | ||
96 | + } | ||
97 | + } | ||
98 | + } | ||
99 | +</style> |
1 | +<template> | ||
2 | + <view class="u-calendar-month-wrapper" ref="u-calendar-month-wrapper"> | ||
3 | + <view v-for="(item, index) in months" :key="index" :class="[`u-calendar-month-${index}`]" | ||
4 | + :ref="`u-calendar-month-${index}`" :id="`month-${index}`"> | ||
5 | + <text v-if="index !== 0" class="u-calendar-month__title">{{ item.year }}年{{ item.month }}月</text> | ||
6 | + <view class="u-calendar-month__days"> | ||
7 | + <view v-if="showMark" class="u-calendar-month__days__month-mark-wrapper"> | ||
8 | + <text class="u-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text> | ||
9 | + </view> | ||
10 | + <view class="u-calendar-month__days__day" v-for="(item1, index1) in item.date" :key="index1" | ||
11 | + :style="[dayStyle(index, index1, item1)]" @tap="clickHandler(index, index1, item1)" | ||
12 | + :class="[item1.selected && 'u-calendar-month__days__day__select--selected']"> | ||
13 | + <view class="u-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]"> | ||
14 | + <text class="u-calendar-month__days__day__select__info" | ||
15 | + :class="[item1.disabled && 'u-calendar-month__days__day__select__info--disabled']" | ||
16 | + :style="[textStyle(item1)]">{{ item1.day }}</text> | ||
17 | + <text v-if="getBottomInfo(index, index1, item1)" | ||
18 | + class="u-calendar-month__days__day__select__buttom-info" | ||
19 | + :class="[item1.disabled && 'u-calendar-month__days__day__select__buttom-info--disabled']" | ||
20 | + :style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text> | ||
21 | + <text v-if="item1.dot" class="u-calendar-month__days__day__select__dot"></text> | ||
22 | + </view> | ||
23 | + </view> | ||
24 | + </view> | ||
25 | + </view> | ||
26 | + </view> | ||
27 | +</template> | ||
28 | + | ||
29 | +<script> | ||
30 | + // #ifdef APP-NVUE | ||
31 | + // 由于nvue不支持百分比单位,需要查询宽度来计算每个日期的宽度 | ||
32 | + const dom = uni.requireNativePlugin('dom') | ||
33 | + // #endif | ||
34 | + import dayjs from '../../libs/util/dayjs.js'; | ||
35 | + export default { | ||
36 | + name: 'u-calendar-month', | ||
37 | + mixins: [uni.$u.mpMixin, uni.$u.mixin], | ||
38 | + props: { | ||
39 | + // 是否显示月份背景色 | ||
40 | + showMark: { | ||
41 | + type: Boolean, | ||
42 | + default: true | ||
43 | + }, | ||
44 | + // 主题色,对底部按钮和选中日期有效 | ||
45 | + color: { | ||
46 | + type: String, | ||
47 | + default: '#3c9cff' | ||
48 | + }, | ||
49 | + // 月份数据 | ||
50 | + months: { | ||
51 | + type: Array, | ||
52 | + default: () => [] | ||
53 | + }, | ||
54 | + // 日期选择类型 | ||
55 | + mode: { | ||
56 | + type: String, | ||
57 | + default: 'single' | ||
58 | + }, | ||
59 | + // 日期行高 | ||
60 | + rowHeight: { | ||
61 | + type: [String, Number], | ||
62 | + default: 58 | ||
63 | + }, | ||
64 | + // mode=multiple时,最多可选多少个日期 | ||
65 | + maxCount: { | ||
66 | + type: [String, Number], | ||
67 | + default: Infinity | ||
68 | + }, | ||
69 | + // mode=range时,第一个日期底部的提示文字 | ||
70 | + startText: { | ||
71 | + type: String, | ||
72 | + default: '开始' | ||
73 | + }, | ||
74 | + // mode=range时,最后一个日期底部的提示文字 | ||
75 | + endText: { | ||
76 | + type: String, | ||
77 | + default: '结束' | ||
78 | + }, | ||
79 | + // 默认选中的日期,mode为multiple或range是必须为数组格式 | ||
80 | + defaultDate: { | ||
81 | + type: [Array, String, Date], | ||
82 | + default: null | ||
83 | + }, | ||
84 | + // 最小的可选日期 | ||
85 | + minDate: { | ||
86 | + type: [String, Number], | ||
87 | + default: 0 | ||
88 | + }, | ||
89 | + // 最大可选日期 | ||
90 | + maxDate: { | ||
91 | + type: [String, Number], | ||
92 | + default: 0 | ||
93 | + }, | ||
94 | + // 如果没有设置maxDate,则往后推多少个月 | ||
95 | + maxMonth: { | ||
96 | + type: [String, Number], | ||
97 | + default: 2 | ||
98 | + }, | ||
99 | + // 是否为只读状态,只读状态下禁止选择日期 | ||
100 | + readonly: { | ||
101 | + type: Boolean, | ||
102 | + default: uni.$u.props.calendar.readonly | ||
103 | + }, | ||
104 | + // 日期区间最多可选天数,默认无限制,mode = range时有效 | ||
105 | + maxRange: { | ||
106 | + type: [Number, String], | ||
107 | + default: Infinity | ||
108 | + }, | ||
109 | + // 范围选择超过最多可选天数时的提示文案,mode = range时有效 | ||
110 | + rangePrompt: { | ||
111 | + type: String, | ||
112 | + default: '' | ||
113 | + }, | ||
114 | + // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 | ||
115 | + showRangePrompt: { | ||
116 | + type: Boolean, | ||
117 | + default: true | ||
118 | + }, | ||
119 | + // 是否允许日期范围的起止时间为同一天,mode = range时有效 | ||
120 | + allowSameDay: { | ||
121 | + type: Boolean, | ||
122 | + default: false | ||
123 | + } | ||
124 | + }, | ||
125 | + data() { | ||
126 | + return { | ||
127 | + // 每个日期的宽度 | ||
128 | + width: 0, | ||
129 | + // 当前选中的日期item | ||
130 | + item: {}, | ||
131 | + selected: [] | ||
132 | + } | ||
133 | + }, | ||
134 | + watch: { | ||
135 | + selectedChange: { | ||
136 | + immediate: true, | ||
137 | + handler(n) { | ||
138 | + this.setDefaultDate() | ||
139 | + } | ||
140 | + } | ||
141 | + }, | ||
142 | + computed: { | ||
143 | + // 多个条件的变化,会引起选中日期的变化,这里统一管理监听 | ||
144 | + selectedChange() { | ||
145 | + return [this.minDate, this.maxDate, this.defaultDate] | ||
146 | + }, | ||
147 | + dayStyle(index1, index2, item) { | ||
148 | + return (index1, index2, item) => { | ||
149 | + const style = {} | ||
150 | + let week = item.week | ||
151 | + // 不进行四舍五入的形式保留2位小数 | ||
152 | + const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1)) | ||
153 | + // 得出每个日期的宽度 | ||
154 | + // #ifdef APP-NVUE | ||
155 | + style.width = uni.$u.addUnit(dayWidth) | ||
156 | + // #endif | ||
157 | + style.height = uni.$u.addUnit(this.rowHeight) | ||
158 | + if (index2 === 0) { | ||
159 | + // 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数 | ||
160 | + week = (week === 0 ? 7 : week) - 1 | ||
161 | + style.marginLeft = uni.$u.addUnit(week * dayWidth) | ||
162 | + } | ||
163 | + if (this.mode === 'range') { | ||
164 | + // 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug | ||
165 | + style.paddingLeft = 0 | ||
166 | + style.paddingRight = 0 | ||
167 | + style.paddingBottom = 0 | ||
168 | + style.paddingTop = 0 | ||
169 | + } | ||
170 | + return style | ||
171 | + } | ||
172 | + }, | ||
173 | + daySelectStyle() { | ||
174 | + return (index1, index2, item) => { | ||
175 | + let date = dayjs(item.date).format("YYYY-MM-DD"), | ||
176 | + style = {} | ||
177 | + // 判断date是否在selected数组中,因为月份可能会需要补0,所以使用dateSame判断,而不用数组的includes判断 | ||
178 | + if (this.selected.some(item => this.dateSame(item, date))) { | ||
179 | + style.backgroundColor = this.color | ||
180 | + } | ||
181 | + if (this.mode === 'single') { | ||
182 | + if (date === this.selected[0]) { | ||
183 | + // 因为需要对nvue的兼容,只能这么写,无法缩写,也无法通过类名控制等等 | ||
184 | + style.borderTopLeftRadius = '3px' | ||
185 | + style.borderBottomLeftRadius = '3px' | ||
186 | + style.borderTopRightRadius = '3px' | ||
187 | + style.borderBottomRightRadius = '3px' | ||
188 | + } | ||
189 | + } else if (this.mode === 'range') { | ||
190 | + if (this.selected.length >= 2) { | ||
191 | + const len = this.selected.length - 1 | ||
192 | + // 第一个日期设置左上角和左下角的圆角 | ||
193 | + if (this.dateSame(date, this.selected[0])) { | ||
194 | + style.borderTopLeftRadius = '3px' | ||
195 | + style.borderBottomLeftRadius = '3px' | ||
196 | + } | ||
197 | + // 最后一个日期设置右上角和右下角的圆角 | ||
198 | + if (this.dateSame(date, this.selected[len])) { | ||
199 | + style.borderTopRightRadius = '3px' | ||
200 | + style.borderBottomRightRadius = '3px' | ||
201 | + } | ||
202 | + // 处于第一和最后一个之间的日期,背景色设置为浅色,通过将对应颜色进行等分,再取其尾部的颜色值 | ||
203 | + if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this | ||
204 | + .selected[len]))) { | ||
205 | + style.backgroundColor = uni.$u.colorGradient(this.color, '#ffffff', 100)[90] | ||
206 | + // 增加一个透明度,让范围区间的背景色也能看到底部的mark水印字符 | ||
207 | + style.opacity = 0.7 | ||
208 | + } | ||
209 | + } else if (this.selected.length === 1) { | ||
210 | + // 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug | ||
211 | + // 进行还原操作,否则在nvue的iOS,uni-app有bug,会导致诡异的表现 | ||
212 | + style.borderTopLeftRadius = '3px' | ||
213 | + style.borderBottomLeftRadius = '3px' | ||
214 | + } | ||
215 | + } else { | ||
216 | + if (this.selected.some(item => this.dateSame(item, date))) { | ||
217 | + style.borderTopLeftRadius = '3px' | ||
218 | + style.borderBottomLeftRadius = '3px' | ||
219 | + style.borderTopRightRadius = '3px' | ||
220 | + style.borderBottomRightRadius = '3px' | ||
221 | + } | ||
222 | + } | ||
223 | + return style | ||
224 | + } | ||
225 | + }, | ||
226 | + // 某个日期是否被选中 | ||
227 | + textStyle() { | ||
228 | + return (item) => { | ||
229 | + const date = dayjs(item.date).format("YYYY-MM-DD"), | ||
230 | + style = {} | ||
231 | + // 选中的日期,提示文字设置白色 | ||
232 | + if (this.selected.some(item => this.dateSame(item, date))) { | ||
233 | + style.color = '#ffffff' | ||
234 | + } | ||
235 | + if (this.mode === 'range') { | ||
236 | + const len = this.selected.length - 1 | ||
237 | + // 如果是范围选择模式,第一个和最后一个之间的日期,文字颜色设置为高亮的主题色 | ||
238 | + if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this | ||
239 | + .selected[len]))) { | ||
240 | + style.color = this.color | ||
241 | + } | ||
242 | + } | ||
243 | + return style | ||
244 | + } | ||
245 | + }, | ||
246 | + // 获取底部的提示文字 | ||
247 | + getBottomInfo() { | ||
248 | + return (index1, index2, item) => { | ||
249 | + const date = dayjs(item.date).format("YYYY-MM-DD") | ||
250 | + const bottomInfo = item.bottomInfo | ||
251 | + // 当为日期范围模式时,且选择的日期个数大于0时 | ||
252 | + if (this.mode === 'range' && this.selected.length > 0) { | ||
253 | + if (this.selected.length === 1) { | ||
254 | + // 选择了一个日期时,如果当前日期为数组中的第一个日期,则显示底部文字为“开始” | ||
255 | + if (this.dateSame(date, this.selected[0])) return this.startText | ||
256 | + else return bottomInfo | ||
257 | + } else { | ||
258 | + const len = this.selected.length - 1 | ||
259 | + // 如果数组中的日期大于2个时,第一个和最后一个显示为开始和结束日期 | ||
260 | + if (this.dateSame(date, this.selected[0]) && this.dateSame(date, this.selected[1]) && | ||
261 | + len === 1) { | ||
262 | + // 如果长度为2,且第一个等于第二个日期,则提示语放在同一个item中 | ||
263 | + return `${this.startText}/${this.endText}` | ||
264 | + } else if (this.dateSame(date, this.selected[0])) { | ||
265 | + return this.startText | ||
266 | + } else if (this.dateSame(date, this.selected[len])) { | ||
267 | + return this.endText | ||
268 | + } else { | ||
269 | + return bottomInfo | ||
270 | + } | ||
271 | + } | ||
272 | + } else { | ||
273 | + return bottomInfo | ||
274 | + } | ||
275 | + } | ||
276 | + } | ||
277 | + }, | ||
278 | + mounted() { | ||
279 | + this.init() | ||
280 | + }, | ||
281 | + methods: { | ||
282 | + init() { | ||
283 | + // 初始化默认选中 | ||
284 | + this.$emit('monthSelected', this.selected) | ||
285 | + this.$nextTick(() => { | ||
286 | + // 这里需要另一个延时,因为获取宽度后,会进行月份数据渲染,只有渲染完成之后,才有真正的高度 | ||
287 | + // 因为nvue下,$nextTick并不是100%可靠的 | ||
288 | + uni.$u.sleep(10).then(() => { | ||
289 | + this.getWrapperWidth() | ||
290 | + this.getMonthRect() | ||
291 | + }) | ||
292 | + }) | ||
293 | + }, | ||
294 | + // 判断两个日期是否相等 | ||
295 | + dateSame(date1, date2) { | ||
296 | + return dayjs(date1).isSame(dayjs(date2)) | ||
297 | + }, | ||
298 | + // 获取月份数据区域的宽度,因为nvue不支持百分比,所以无法通过css设置每个日期item的宽度 | ||
299 | + getWrapperWidth() { | ||
300 | + // #ifdef APP-NVUE | ||
301 | + dom.getComponentRect(this.$refs['u-calendar-month-wrapper'], res => { | ||
302 | + this.width = res.size.width | ||
303 | + }) | ||
304 | + // #endif | ||
305 | + // #ifndef APP-NVUE | ||
306 | + this.$uGetRect('.u-calendar-month-wrapper').then(size => { | ||
307 | + this.width = size.width | ||
308 | + }) | ||
309 | + // #endif | ||
310 | + }, | ||
311 | + getMonthRect() { | ||
312 | + // 获取每个月份数据的尺寸,用于父组件在scroll-view滚动事件中,监听当前滚动到了第几个月份 | ||
313 | + const promiseAllArr = this.months.map((item, index) => this.getMonthRectByPromise( | ||
314 | + `u-calendar-month-${index}`)) | ||
315 | + // 一次性返回 | ||
316 | + Promise.all(promiseAllArr).then( | ||
317 | + sizes => { | ||
318 | + let height = 1 | ||
319 | + const topArr = [] | ||
320 | + for (let i = 0; i < this.months.length; i++) { | ||
321 | + // 添加到months数组中,供scroll-view滚动事件中,判断当前滚动到哪个月份 | ||
322 | + topArr[i] = height | ||
323 | + height += sizes[i].height | ||
324 | + } | ||
325 | + // 由于微信下,无法通过this.months[i].top的形式(引用类型)去修改父组件的month的top值,所以使用事件形式对外发出 | ||
326 | + this.$emit('updateMonthTop', topArr) | ||
327 | + }) | ||
328 | + }, | ||
329 | + // 获取每个月份区域的尺寸 | ||
330 | + getMonthRectByPromise(el) { | ||
331 | + // #ifndef APP-NVUE | ||
332 | + // $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html | ||
333 | + // 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同 | ||
334 | + return new Promise(resolve => { | ||
335 | + this.$uGetRect(`.${el}`).then(size => { | ||
336 | + resolve(size) | ||
337 | + }) | ||
338 | + }) | ||
339 | + // #endif | ||
340 | + | ||
341 | + // #ifdef APP-NVUE | ||
342 | + // nvue下,使用dom模块查询元素高度 | ||
343 | + // 返回一个promise,让调用此方法的主体能使用then回调 | ||
344 | + return new Promise(resolve => { | ||
345 | + dom.getComponentRect(this.$refs[el][0], res => { | ||
346 | + resolve(res.size) | ||
347 | + }) | ||
348 | + }) | ||
349 | + // #endif | ||
350 | + }, | ||
351 | + // 点击某一个日期 | ||
352 | + clickHandler(index1, index2, item) { | ||
353 | + if (this.readonly) { | ||
354 | + return; | ||
355 | + } | ||
356 | + this.item = item | ||
357 | + const date = dayjs(item.date).format("YYYY-MM-DD") | ||
358 | + if (item.disabled) return | ||
359 | + // 对上一次选择的日期数组进行深度克隆 | ||
360 | + let selected = uni.$u.deepClone(this.selected) | ||
361 | + if (this.mode === 'single') { | ||
362 | + // 单选情况下,让数组中的元素为当前点击的日期 | ||
363 | + selected = [date] | ||
364 | + } else if (this.mode === 'multiple') { | ||
365 | + if (selected.some(item => this.dateSame(item, date))) { | ||
366 | + // 如果点击的日期已在数组中,则进行移除操作,也就是达到反选的效果 | ||
367 | + const itemIndex = selected.findIndex(item => item === date) | ||
368 | + selected.splice(itemIndex, 1) | ||
369 | + } else { | ||
370 | + // 如果点击的日期不在数组中,且已有的长度小于总可选长度时,则添加到数组中去 | ||
371 | + if (selected.length < this.maxCount) selected.push(date) | ||
372 | + } | ||
373 | + } else { | ||
374 | + // 选择区间形式 | ||
375 | + if (selected.length === 0 || selected.length >= 2) { | ||
376 | + // 如果原来就为0或者大于2的长度,则当前点击的日期,就是开始日期 | ||
377 | + selected = [date] | ||
378 | + } else if (selected.length === 1) { | ||
379 | + // 如果已经选择了开始日期 | ||
380 | + const existsDate = selected[0] | ||
381 | + // 如果当前选择的日期小于上一次选择的日期,则当前的日期定为开始日期 | ||
382 | + if (dayjs(date).isBefore(existsDate)) { | ||
383 | + selected = [date] | ||
384 | + } else if (dayjs(date).isAfter(existsDate)) { | ||
385 | + // 当前日期减去最大可选的日期天数,如果大于起始时间,则进行提示 | ||
386 | + if(dayjs(dayjs(date).subtract(this.maxRange, 'day')).isAfter(dayjs(selected[0])) && this.showRangePrompt) { | ||
387 | + if(this.rangePrompt) { | ||
388 | + uni.$u.toast(this.rangePrompt) | ||
389 | + } else { | ||
390 | + uni.$u.toast(`选择天数不能超过 ${this.maxRange} 天`) | ||
391 | + } | ||
392 | + return | ||
393 | + } | ||
394 | + // 如果当前日期大于已有日期,将当前的添加到数组尾部 | ||
395 | + selected.push(date) | ||
396 | + const startDate = selected[0] | ||
397 | + const endDate = selected[1] | ||
398 | + const arr = [] | ||
399 | + let i = 0 | ||
400 | + do { | ||
401 | + // 将开始和结束日期之间的日期添加到数组中 | ||
402 | + arr.push(dayjs(startDate).add(i, 'day').format("YYYY-MM-DD")) | ||
403 | + i++ | ||
404 | + // 累加的日期小于结束日期时,继续下一次的循环 | ||
405 | + } while (dayjs(startDate).add(i, 'day').isBefore(dayjs(endDate))) | ||
406 | + // 为了一次性修改数组,避免computed中多次触发,这里才用arr变量一次性赋值的方式,同时将最后一个日期添加近来 | ||
407 | + arr.push(endDate) | ||
408 | + selected = arr | ||
409 | + } else { | ||
410 | + // 选择区间时,只有一个日期的情况下,且不允许选择起止为同一天的话,不允许选择自己 | ||
411 | + if (selected[0] === date && !this.allowSameDay) return | ||
412 | + selected.push(date) | ||
413 | + } | ||
414 | + } | ||
415 | + } | ||
416 | + this.setSelected(selected) | ||
417 | + }, | ||
418 | + // 设置默认日期 | ||
419 | + setDefaultDate() { | ||
420 | + if (!this.defaultDate) { | ||
421 | + // 如果没有设置默认日期,则将当天日期设置为默认选中的日期 | ||
422 | + const selected = [dayjs().format("YYYY-MM-DD")] | ||
423 | + return this.setSelected(selected, false) | ||
424 | + } | ||
425 | + let defaultDate = [] | ||
426 | + const minDate = this.minDate || dayjs().format("YYYY-MM-DD") | ||
427 | + const maxDate = this.maxDate || dayjs(minDate).add(this.maxMonth - 1, 'month').format("YYYY-MM-DD") | ||
428 | + if (this.mode === 'single') { | ||
429 | + // 单选模式,可以是字符串或数组,Date对象等 | ||
430 | + if (!uni.$u.test.array(this.defaultDate)) { | ||
431 | + defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")] | ||
432 | + } else { | ||
433 | + defaultDate = [this.defaultDate[0]] | ||
434 | + } | ||
435 | + } else { | ||
436 | + // 如果为非数组,则不执行 | ||
437 | + if (!uni.$u.test.array(this.defaultDate)) return | ||
438 | + defaultDate = this.defaultDate | ||
439 | + } | ||
440 | + // 过滤用户传递的默认数组,取出只在可允许最大值与最小值之间的元素 | ||
441 | + defaultDate = defaultDate.filter(item => { | ||
442 | + return dayjs(item).isAfter(dayjs(minDate).subtract(1, 'day')) && dayjs(item).isBefore(dayjs( | ||
443 | + maxDate).add(1, 'day')) | ||
444 | + }) | ||
445 | + this.setSelected(defaultDate, false) | ||
446 | + }, | ||
447 | + setSelected(selected, event = true) { | ||
448 | + this.selected = selected | ||
449 | + event && this.$emit('monthSelected', this.selected) | ||
450 | + } | ||
451 | + } | ||
452 | + } | ||
453 | +</script> | ||
454 | + | ||
455 | +<style lang="scss" scoped> | ||
456 | + @import "../../libs/css/components.scss"; | ||
457 | + | ||
458 | + .u-calendar-month-wrapper { | ||
459 | + margin-top: 4px; | ||
460 | + } | ||
461 | + | ||
462 | + .u-calendar-month { | ||
463 | + | ||
464 | + &__title { | ||
465 | + font-size: 14px; | ||
466 | + line-height: 42px; | ||
467 | + height: 42px; | ||
468 | + color: $u-main-color; | ||
469 | + text-align: center; | ||
470 | + font-weight: bold; | ||
471 | + } | ||
472 | + | ||
473 | + &__days { | ||
474 | + position: relative; | ||
475 | + @include flex; | ||
476 | + flex-wrap: wrap; | ||
477 | + | ||
478 | + &__month-mark-wrapper { | ||
479 | + position: absolute; | ||
480 | + top: 0; | ||
481 | + bottom: 0; | ||
482 | + left: 0; | ||
483 | + right: 0; | ||
484 | + @include flex; | ||
485 | + justify-content: center; | ||
486 | + align-items: center; | ||
487 | + | ||
488 | + &__text { | ||
489 | + font-size: 155px; | ||
490 | + color: rgba(231, 232, 234, 0.83); | ||
491 | + } | ||
492 | + } | ||
493 | + | ||
494 | + &__day { | ||
495 | + @include flex; | ||
496 | + padding: 2px; | ||
497 | + /* #ifndef APP-NVUE */ | ||
498 | + // vue下使用css进行宽度计算,因为某些安卓机会无法进行js获取父元素宽度进行计算得出,会有偏移 | ||
499 | + width: calc(100% / 7); | ||
500 | + box-sizing: border-box; | ||
501 | + /* #endif */ | ||
502 | + | ||
503 | + &__select { | ||
504 | + flex: 1; | ||
505 | + @include flex; | ||
506 | + align-items: center; | ||
507 | + justify-content: center; | ||
508 | + position: relative; | ||
509 | + | ||
510 | + &__dot { | ||
511 | + width: 7px; | ||
512 | + height: 7px; | ||
513 | + border-radius: 100px; | ||
514 | + background-color: $u-error; | ||
515 | + position: absolute; | ||
516 | + top: 12px; | ||
517 | + right: 7px; | ||
518 | + } | ||
519 | + | ||
520 | + &__buttom-info { | ||
521 | + color: $u-content-color; | ||
522 | + text-align: center; | ||
523 | + position: absolute; | ||
524 | + bottom: 5px; | ||
525 | + font-size: 10px; | ||
526 | + text-align: center; | ||
527 | + left: 0; | ||
528 | + right: 0; | ||
529 | + | ||
530 | + &--selected { | ||
531 | + color: #ffffff; | ||
532 | + } | ||
533 | + | ||
534 | + &--disabled { | ||
535 | + color: #cacbcd; | ||
536 | + } | ||
537 | + } | ||
538 | + | ||
539 | + &__info { | ||
540 | + text-align: center; | ||
541 | + font-size: 16px; | ||
542 | + | ||
543 | + &--selected { | ||
544 | + color: #ffffff; | ||
545 | + } | ||
546 | + | ||
547 | + &--disabled { | ||
548 | + color: #cacbcd; | ||
549 | + } | ||
550 | + } | ||
551 | + | ||
552 | + &--selected { | ||
553 | + background-color: $u-primary; | ||
554 | + @include flex; | ||
555 | + justify-content: center; | ||
556 | + align-items: center; | ||
557 | + flex: 1; | ||
558 | + border-radius: 3px; | ||
559 | + } | ||
560 | + | ||
561 | + &--range-selected { | ||
562 | + opacity: 0.3; | ||
563 | + border-radius: 0; | ||
564 | + } | ||
565 | + | ||
566 | + &--range-start-selected { | ||
567 | + border-top-right-radius: 0; | ||
568 | + border-bottom-right-radius: 0; | ||
569 | + } | ||
570 | + | ||
571 | + &--range-end-selected { | ||
572 | + border-top-left-radius: 0; | ||
573 | + border-bottom-left-radius: 0; | ||
574 | + } | ||
575 | + } | ||
576 | + } | ||
577 | + } | ||
578 | + } | ||
579 | +</style> |
1 | +export default { | ||
2 | + props: { | ||
3 | + // 日历顶部标题 | ||
4 | + title: { | ||
5 | + type: String, | ||
6 | + default: uni.$u.props.calendar.title | ||
7 | + }, | ||
8 | + // 是否显示标题 | ||
9 | + showTitle: { | ||
10 | + type: Boolean, | ||
11 | + default: uni.$u.props.calendar.showTitle | ||
12 | + }, | ||
13 | + // 是否显示副标题 | ||
14 | + showSubtitle: { | ||
15 | + type: Boolean, | ||
16 | + default: uni.$u.props.calendar.showSubtitle | ||
17 | + }, | ||
18 | + // 日期类型选择,single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围 | ||
19 | + mode: { | ||
20 | + type: String, | ||
21 | + default: uni.$u.props.calendar.mode | ||
22 | + }, | ||
23 | + // mode=range时,第一个日期底部的提示文字 | ||
24 | + startText: { | ||
25 | + type: String, | ||
26 | + default: uni.$u.props.calendar.startText | ||
27 | + }, | ||
28 | + // mode=range时,最后一个日期底部的提示文字 | ||
29 | + endText: { | ||
30 | + type: String, | ||
31 | + default: uni.$u.props.calendar.endText | ||
32 | + }, | ||
33 | + // 自定义列表 | ||
34 | + customList: { | ||
35 | + type: Array, | ||
36 | + default: uni.$u.props.calendar.customList | ||
37 | + }, | ||
38 | + // 主题色,对底部按钮和选中日期有效 | ||
39 | + color: { | ||
40 | + type: String, | ||
41 | + default: uni.$u.props.calendar.color | ||
42 | + }, | ||
43 | + // 最小的可选日期 | ||
44 | + minDate: { | ||
45 | + type: [String, Number], | ||
46 | + default: uni.$u.props.calendar.minDate | ||
47 | + }, | ||
48 | + // 最大可选日期 | ||
49 | + maxDate: { | ||
50 | + type: [String, Number], | ||
51 | + default: uni.$u.props.calendar.maxDate | ||
52 | + }, | ||
53 | + // 默认选中的日期,mode为multiple或range是必须为数组格式 | ||
54 | + defaultDate: { | ||
55 | + type: [Array, String, Date, null], | ||
56 | + default: uni.$u.props.calendar.defaultDate | ||
57 | + }, | ||
58 | + // mode=multiple时,最多可选多少个日期 | ||
59 | + maxCount: { | ||
60 | + type: [String, Number], | ||
61 | + default: uni.$u.props.calendar.maxCount | ||
62 | + }, | ||
63 | + // 日期行高 | ||
64 | + rowHeight: { | ||
65 | + type: [String, Number], | ||
66 | + default: uni.$u.props.calendar.rowHeight | ||
67 | + }, | ||
68 | + // 日期格式化函数 | ||
69 | + formatter: { | ||
70 | + type: [Function, null], | ||
71 | + default: uni.$u.props.calendar.formatter | ||
72 | + }, | ||
73 | + // 是否显示农历 | ||
74 | + showLunar: { | ||
75 | + type: Boolean, | ||
76 | + default: uni.$u.props.calendar.showLunar | ||
77 | + }, | ||
78 | + // 是否显示月份背景色 | ||
79 | + showMark: { | ||
80 | + type: Boolean, | ||
81 | + default: uni.$u.props.calendar.showMark | ||
82 | + }, | ||
83 | + // 确定按钮的文字 | ||
84 | + confirmText: { | ||
85 | + type: String, | ||
86 | + default: uni.$u.props.calendar.confirmText | ||
87 | + }, | ||
88 | + // 确认按钮处于禁用状态时的文字 | ||
89 | + confirmDisabledText: { | ||
90 | + type: String, | ||
91 | + default: uni.$u.props.calendar.confirmDisabledText | ||
92 | + }, | ||
93 | + // 是否显示日历弹窗 | ||
94 | + show: { | ||
95 | + type: Boolean, | ||
96 | + default: uni.$u.props.calendar.show | ||
97 | + }, | ||
98 | + // 是否允许点击遮罩关闭日历 | ||
99 | + closeOnClickOverlay: { | ||
100 | + type: Boolean, | ||
101 | + default: uni.$u.props.calendar.closeOnClickOverlay | ||
102 | + }, | ||
103 | + // 是否为只读状态,只读状态下禁止选择日期 | ||
104 | + readonly: { | ||
105 | + type: Boolean, | ||
106 | + default: uni.$u.props.calendar.readonly | ||
107 | + }, | ||
108 | + // 是否展示确认按钮 | ||
109 | + showConfirm: { | ||
110 | + type: Boolean, | ||
111 | + default: uni.$u.props.calendar.showConfirm | ||
112 | + }, | ||
113 | + // 日期区间最多可选天数,默认无限制,mode = range时有效 | ||
114 | + maxRange: { | ||
115 | + type: [Number, String], | ||
116 | + default: uni.$u.props.calendar.maxRange | ||
117 | + }, | ||
118 | + // 范围选择超过最多可选天数时的提示文案,mode = range时有效 | ||
119 | + rangePrompt: { | ||
120 | + type: String, | ||
121 | + default: uni.$u.props.calendar.rangePrompt | ||
122 | + }, | ||
123 | + // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 | ||
124 | + showRangePrompt: { | ||
125 | + type: Boolean, | ||
126 | + default: uni.$u.props.calendar.showRangePrompt | ||
127 | + }, | ||
128 | + // 是否允许日期范围的起止时间为同一天,mode = range时有效 | ||
129 | + allowSameDay: { | ||
130 | + type: Boolean, | ||
131 | + default: uni.$u.props.calendar.allowSameDay | ||
132 | + }, | ||
133 | + // 圆角值 | ||
134 | + round: { | ||
135 | + type: [Boolean, String, Number], | ||
136 | + default: uni.$u.props.calendar.round | ||
137 | + }, | ||
138 | + // 最多展示月份数量 | ||
139 | + monthNum: { | ||
140 | + type: [Number, String], | ||
141 | + default: 3 | ||
142 | + } | ||
143 | + } | ||
144 | +} |
-
请 注册 或 登录 后发表评论