@ -0,0 +1,3 @@ |
||||
unpackage |
||||
node_modules |
||||
uview-ui |
||||
@ -0,0 +1,16 @@ |
||||
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/ |
||||
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数 |
||||
"version": "0.0", |
||||
"configurations": [{ |
||||
"default" : |
||||
{ |
||||
"launchtype" : "local" |
||||
}, |
||||
"h5" : |
||||
{ |
||||
"launchtype" : "local" |
||||
}, |
||||
"type" : "uniCloud" |
||||
} |
||||
] |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
<script> |
||||
export default { |
||||
onLaunch: function() { |
||||
console.log('App Launch') |
||||
}, |
||||
onShow: function() { |
||||
console.log('App Show') |
||||
}, |
||||
onHide: function() { |
||||
console.log('App Hide') |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
@import "uview-ui/index.scss"; |
||||
/*每个页面公共css */ |
||||
</style> |
||||
@ -0,0 +1,21 @@ |
||||
MIT License |
||||
|
||||
Copyright (c) 2020 www.uviewui.com |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
||||
@ -0,0 +1,15 @@ |
||||
import Vue from 'vue' |
||||
import App from './App' |
||||
|
||||
Vue.config.productionTip = false |
||||
|
||||
App.mpType = 'app' |
||||
|
||||
// 引入全局uView
|
||||
import uView from 'uview-ui' |
||||
Vue.use(uView); |
||||
|
||||
const app = new Vue({ |
||||
...App |
||||
}) |
||||
app.$mount() |
||||
@ -0,0 +1,91 @@ |
||||
{ |
||||
"name" : "DaFoSiH5", |
||||
"appid" : "__UNI__EC580B3", |
||||
"description" : "", |
||||
"versionName" : "1.5.0", |
||||
"versionCode" : "100", |
||||
"transformPx" : false, |
||||
/* 5+App特有相关 */ |
||||
"app-plus" : { |
||||
"safearea" : { |
||||
"bottom" : { |
||||
"offset" : "none" |
||||
} |
||||
}, |
||||
"usingComponents" : true, |
||||
"nvueCompiler" : "uni-app", |
||||
"compilerVersion" : 3, |
||||
"splashscreen" : { |
||||
"alwaysShowBeforeRender" : true, |
||||
"waiting" : true, |
||||
"autoclose" : true, |
||||
"delay" : 0 |
||||
}, |
||||
/* 模块配置 */ |
||||
"modules" : {}, |
||||
/* 应用发布信息 */ |
||||
"distribute" : { |
||||
/* android打包配置 */ |
||||
"android" : { |
||||
"permissions" : [ |
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
||||
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>", |
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
||||
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>", |
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>", |
||||
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>", |
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
||||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>", |
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
||||
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>", |
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>", |
||||
"<uses-feature android:name=\"android.hardware.camera\"/>", |
||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>", |
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
||||
] |
||||
}, |
||||
/* ios打包配置 */ |
||||
"ios" : {}, |
||||
/* SDK配置 */ |
||||
"sdkConfigs" : {} |
||||
} |
||||
}, |
||||
/* 快应用特有相关 */ |
||||
"quickapp" : {}, |
||||
/* 小程序特有相关 */ |
||||
"mp-weixin" : { |
||||
"appid" : "wxc256e348c4032ebd", |
||||
"setting" : { |
||||
"urlCheck" : false |
||||
}, |
||||
"usingComponents" : true |
||||
}, |
||||
"mp-alipay" : { |
||||
"usingComponents" : true |
||||
}, |
||||
"mp-baidu" : { |
||||
"usingComponents" : true |
||||
}, |
||||
"mp-toutiao" : { |
||||
"usingComponents" : true |
||||
}, |
||||
"h5" : { |
||||
"template" : "template.h5.html", |
||||
"router" : { |
||||
"mode" : "hash", |
||||
"base" : "/h5/" |
||||
}, |
||||
"title" : "dafosi", |
||||
"devServer" : { |
||||
"https" : false |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,49 @@ |
||||
{ |
||||
"easycom": { |
||||
"^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue" |
||||
}, |
||||
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages |
||||
{ |
||||
"path" : "pages/home/home", |
||||
"style" : |
||||
{ |
||||
"navigationBarTitleText": "", |
||||
"enablePullDownRefresh": false |
||||
} |
||||
|
||||
} |
||||
,{ |
||||
"path" : "pages/error/error", |
||||
"style" : |
||||
{ |
||||
"navigationBarTitleText": "", |
||||
"enablePullDownRefresh": false |
||||
} |
||||
|
||||
} |
||||
], |
||||
"globalStyle": { |
||||
"navigationStyle":"custom", |
||||
// "navigationBarTextStyle": "black", |
||||
// "navigationBarTitleText": "uView", |
||||
// "navigationBarBackgroundColor": "#F8F8F8", |
||||
"backgroundColor": "#F8F8F8" |
||||
} |
||||
// "tabBar": { |
||||
// "color": "#909399", |
||||
// "selectedColor": "#303133", |
||||
// "borderStyle": "black", |
||||
// "backgroundColor": "#ffffff", |
||||
// "list": [{ |
||||
// "pagePath": "pages/index/index", |
||||
// "iconPath": "static/index.png", |
||||
// "selectedIconPath": "static/index-selected.png", |
||||
// "text": "首页" |
||||
// }, { |
||||
// "pagePath": "pages/center/index", |
||||
// "iconPath": "static/center.png", |
||||
// "selectedIconPath": "static/center-selected.png", |
||||
// "text": "我" |
||||
// }] |
||||
// } |
||||
} |
||||
@ -0,0 +1,30 @@ |
||||
<template> |
||||
<view class="u-flex-col u-col-center"> |
||||
<view class="title u-m-t-50">404</view> |
||||
<view class="content u-m-30">请在微信或支付宝中打开此链接!</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
return {}; |
||||
}, |
||||
onLoad() { |
||||
console.log(uni.$u.config.v); |
||||
// 判断当前处于什么浏览器 |
||||
}, |
||||
methods: {} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.title { |
||||
color: #ff0000; |
||||
font-size: 200rpx; |
||||
} |
||||
.content { |
||||
color: #ff0000; |
||||
font-size: 80rpx; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,378 @@ |
||||
<template> |
||||
<view class="content u-flex-col u-col-center"> |
||||
<view v-if="temple=='大佛寺'" class="u-flex u-row-center" style="width: 750rpx;height: 136rpx;"> |
||||
<u-image width="357rpx" height="82rpx" src="../../static/home/title.png"></u-image> |
||||
</view> |
||||
<u-image v-if="temple=='能仁寺'" width="750rpx" height="136rpx" src="../../static/home/nr.jpg"></u-image> |
||||
<u-image v-if="temple=='百花寺'" width="750rpx" height="136rpx" src="../../static/home/bh.jpg"></u-image> |
||||
<u-image width="750rpx" height="356rpx" src="../../static/home/top.jpg"></u-image> |
||||
<u-form class="form u-m-t-30" ref="uForm"> |
||||
<u-form-item label="功德主" labelWidth="120rpx" :border-bottom="false" :label-style="labelStyle" prop="master"> |
||||
<u-input class="ipt" v-model="form.master" :border="true" :clearable="false" placeholder="" borderColor="#562511"></u-input> |
||||
</u-form-item> |
||||
<u-form-item label="付款人" labelWidth="120rpx" :border-bottom="false" :label-style="labelStyle" prop="name"> |
||||
<u-input class="ipt" v-model="form.name" :border="true" :clearable="false" placeholder="" borderColor="#562511"></u-input> |
||||
</u-form-item> |
||||
<u-form-item label="手机" labelWidth="120rpx" :border-bottom="false" :label-style="labelStyle" prop="phone"> |
||||
<u-input class="ipt" type="number" v-model="form.phone" :border="true" :clearable="false" placeholder="" borderColor="#562511"></u-input> |
||||
</u-form-item> |
||||
<u-form-item label="祝福语" labelWidth="120rpx" :border-bottom="false" :label-style="labelStyle" prop="wish"> |
||||
<u-input |
||||
class="ipt" |
||||
v-model="form.wish" |
||||
type="textarea" |
||||
:autoHeight="true" |
||||
:border="true" |
||||
:clearable="false" |
||||
placeholder="请填写祝福语,最多20个字!" |
||||
borderColor="#562511" |
||||
></u-input> |
||||
</u-form-item> |
||||
</u-form> |
||||
|
||||
<view class="u-flex u-m-t-30 u-m-l-50 category-title"> |
||||
<view class="title">乐捐项目</view> |
||||
<view class="title">功德金额</view> |
||||
</view> |
||||
|
||||
<view class="u-m-t-20 u-m-l-50"> |
||||
<view class="a u-m-t-15 u-flex-col" v-for="(item, index) in categories" :key="index"> |
||||
<view class="b u-flex"> |
||||
<view class="category u-flex"> |
||||
<view class="item" @click="showSelect(index)"> |
||||
|
||||
{{item.category}} |
||||
<!-- <u-input |
||||
class="ipt" |
||||
v-model="item.category" |
||||
type="select" |
||||
:border="true" |
||||
:clearable="false" |
||||
placeholder="" |
||||
borderColor="#562511" |
||||
@click="showSelect(index)" |
||||
></u-input> --> |
||||
</view> |
||||
|
||||
<view class="item u-m-l-10" @click="showKeyboardFunc(index)">{{item.amount}}</view> |
||||
|
||||
<!-- <u-input |
||||
class="ipt u-m-l-10" |
||||
v-model="item.amount" |
||||
type="number" |
||||
:border="true" |
||||
:clearable="false" |
||||
:disabled="true" |
||||
placeholder="" |
||||
borderColor="#562511" |
||||
@click="showKeyboardFunc(index)" |
||||
></u-input> --> |
||||
</view> |
||||
<u-icon v-if="index > 0" name="minus" color="#562511" class="u-m-l-10" @click="reduce(index)"></u-icon> |
||||
</view> |
||||
|
||||
<view class="line u-m-t-15"></view> |
||||
</view> |
||||
</view> |
||||
|
||||
<view class="bottom u-flex u-col-top"> |
||||
<view class="add u-flex u-row-center" @click="add">+ 新增捐赠</view> |
||||
<view class="sure u-flex u-row-center" @click="sure">确认支付</view> |
||||
</view> |
||||
|
||||
<u-gap height="150"></u-gap> |
||||
|
||||
<u-select v-model="show" :list="list" @confirm="selectComleted"></u-select> |
||||
<u-keyboard mode="number" :tips="tips" @change="valChange" @backspace="backspace" v-model="showKeyboard"></u-keyboard> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
return { |
||||
browser: '', |
||||
temple: '', |
||||
machineNo: '', |
||||
form: { |
||||
master: '', |
||||
name: '', |
||||
phone: '', |
||||
wish: '' |
||||
}, |
||||
list: [], |
||||
show: false, |
||||
showKeyboard: false, |
||||
tips: '', |
||||
index: 0, |
||||
categories: [ |
||||
{ |
||||
category: '', |
||||
amount: '' |
||||
} |
||||
], |
||||
labelStyle: { |
||||
fontWeight: 'bold', |
||||
fontSize: '36rpx', |
||||
color: '#562511' |
||||
} |
||||
}; |
||||
}, |
||||
onLoad() { |
||||
this.browser = this.browserJudgment(); |
||||
|
||||
if (this.browser == 'Other') { |
||||
uni.redirectTo({ |
||||
url:'../error/error' |
||||
}) |
||||
} |
||||
|
||||
this.getTempleAndMachineNo(); |
||||
|
||||
this.getCategories(); |
||||
}, |
||||
methods: { |
||||
valChange(val) { |
||||
if ('.' == val) { |
||||
if ('' == this.categories[this.index].amount || this.categories[this.index].amount.indexOf('.') > -1) { |
||||
return; |
||||
} |
||||
} |
||||
if (this.categories[this.index].amount.indexOf('.') > -1) { |
||||
if (this.categories[this.index].amount.split('.')[1].length >=2) { |
||||
return; |
||||
} |
||||
} |
||||
this.categories[this.index].amount += val; |
||||
this.tips = this.categories[this.index].amount; |
||||
console.log(this.categories[this.index].amount); |
||||
|
||||
}, |
||||
backspace() { |
||||
if (this.categories[this.index].amount.length) this.categories[this.index].amount = this.categories[this.index].amount.substr(0, this.categories[this.index].amount.length - 1); |
||||
this.tips = this.categories[this.index].amount; |
||||
console.log(this.categories[this.index].amount); |
||||
}, |
||||
browserJudgment() { |
||||
var userAgent = navigator.userAgent.toLowerCase(); |
||||
if (userAgent.match(/Alipay/i) == 'alipay') { |
||||
return 'Ali'; |
||||
} else if (userAgent.match(/MicroMessenger/i) == 'micromessenger') { |
||||
return 'Wx'; |
||||
} else { |
||||
return 'Other'; |
||||
} |
||||
}, |
||||
getTempleAndMachineNo() { |
||||
var url = window.location.href; |
||||
console.log(url); |
||||
console.log(url.indexOf('temple')); |
||||
if (url.indexOf('temple') > -1) { |
||||
var obj = this.getAllUrlParams(url); |
||||
this.machineNo = obj.machineNo; |
||||
switch (obj.temple) { |
||||
case '1': |
||||
this.temple = '大佛寺'; |
||||
break; |
||||
case '2': |
||||
this.temple = '能仁寺'; |
||||
break; |
||||
case '3': |
||||
this.temple = '百花寺'; |
||||
break; |
||||
} |
||||
} |
||||
}, |
||||
getAllUrlParams(url) { |
||||
var obj = {}; |
||||
if (!url) { |
||||
return obj; |
||||
} |
||||
url = url.split('?')[1]; |
||||
var arr = url.split('&'); |
||||
for (var i = 0; i < arr.length; i++) { |
||||
// 分离成key:value的形式 |
||||
var a = arr[i].split('='); |
||||
var paramName = a[0]; |
||||
var paramValue = a[1]; |
||||
obj[paramName] = paramValue; |
||||
} |
||||
return obj; |
||||
}, |
||||
getCategories() { |
||||
this.$u |
||||
.post('http://dafosi.zhihuizongjiao.cn:7878/api/app/getcategories', { |
||||
temple: this.temple |
||||
}) |
||||
.then(res => { |
||||
if (1 == res.code) { |
||||
this.list = res.data; |
||||
} |
||||
}); |
||||
}, |
||||
donation() { |
||||
var register = JSON.stringify(this.categories); |
||||
var amount = 0; |
||||
for (var i = 0; i < this.categories.length; i++) { |
||||
amount += parseFloat(this.categories[i].amount); |
||||
} |
||||
this.$u |
||||
.post('http://dafosi.zhihuizongjiao.cn:7878/api/app/donation', { |
||||
browser: this.browser, |
||||
temple: this.temple, |
||||
master: this.form.master, |
||||
name: this.form.name, |
||||
phone: this.form.phone, |
||||
wish: this.form.wish, |
||||
register, |
||||
amount, |
||||
machineNo: this.machineNo |
||||
}) |
||||
.then(res => { |
||||
if (1 == res.code) { |
||||
// 登记成功 |
||||
// 跳转支付 |
||||
window.location.href = res.data.url; |
||||
} else { |
||||
// 登记失败 |
||||
uni.showModal({ |
||||
title: '登记出错', |
||||
content: res.msg |
||||
}); |
||||
} |
||||
}); |
||||
}, |
||||
add() { |
||||
var obj = new Object(); |
||||
obj.category = ''; |
||||
obj.amount = ''; |
||||
this.categories.push(obj); |
||||
}, |
||||
reduce(index) { |
||||
this.categories.splice(index, 1); |
||||
}, |
||||
showSelect(index) { |
||||
this.index = index; |
||||
this.show = true; |
||||
}, |
||||
showKeyboardFunc(index) { |
||||
this.tips = ''; |
||||
this.index = index; |
||||
this.showKeyboard = true; |
||||
}, |
||||
selectComleted(e) { |
||||
this.categories[this.index].category = e[0].label; |
||||
}, |
||||
sure() { |
||||
var flag = true; |
||||
for (var i = 0; i < this.categories.length; i++) { |
||||
var obj = this.categories[i]; |
||||
if (obj.category == '' || obj.amount == '') { |
||||
flag = false; |
||||
} |
||||
} |
||||
if (flag) { |
||||
if (this.form.wish.length > 20) { |
||||
uni.showModal({ |
||||
title: '温馨提示', |
||||
content: '祝福语最多20个字!', |
||||
showCancel: false |
||||
}); |
||||
return; |
||||
} |
||||
this.donation(); |
||||
} else { |
||||
uni.showModal({ |
||||
title: '温馨提示', |
||||
content: '乐捐项目或功德金额为空!', |
||||
showCancel: false |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.content { |
||||
width: 100vw; |
||||
min-height: 100vh; |
||||
background: url(../../static/home/background.png); |
||||
background-size: cover; |
||||
|
||||
.form { |
||||
width: 500rpx; |
||||
.ipt { |
||||
background: #ffffff; |
||||
color: '#562511'; |
||||
} |
||||
} |
||||
|
||||
.category-title { |
||||
width: 600rpx; |
||||
.title { |
||||
width: 50%; |
||||
height: 100%; |
||||
color: #562511; |
||||
font-size: 36rpx; |
||||
font-weight: bold; |
||||
text-shadow: 2rpx 3rpx 1rpx #ffffff; |
||||
} |
||||
} |
||||
|
||||
.a { |
||||
width: 600rpx; |
||||
.b { |
||||
width: 600rpx; |
||||
.category { |
||||
width: 550rpx; |
||||
.item { |
||||
width: 270rpx; |
||||
height: 60rpx; |
||||
background: #ffffff; |
||||
color: '#562511'; |
||||
border-radius: 5rpx; |
||||
border: solid #562511 0.25rpx; |
||||
line-height: 60rpx; |
||||
padding-left: 20rpx; |
||||
} |
||||
// .ipt { |
||||
// background: #ffffff; |
||||
// color: '#562511'; |
||||
// background: #ff0000; |
||||
// } |
||||
} |
||||
} |
||||
|
||||
.line { |
||||
width: 550rpx; |
||||
height: 2rpx; |
||||
background: #562511; |
||||
} |
||||
} |
||||
|
||||
.bottom { |
||||
position: fixed; |
||||
bottom: 0rpx; |
||||
width: 750rpx; |
||||
height: 100rpx; |
||||
color: #562511; |
||||
font-size: 36rpx; |
||||
font-weight: bold; |
||||
.add { |
||||
width: 50%; |
||||
height: 100%; |
||||
background: #e8d19d; |
||||
border: solid #562511 0.25rpx; |
||||
border-radius: 10rpx; |
||||
} |
||||
.sure { |
||||
width: 50%; |
||||
height: 100%; |
||||
background: #e8d19d; |
||||
border: solid #562511 0.25rpx; |
||||
border-radius: 10rpx; |
||||
} |
||||
} |
||||
} |
||||
</style> |
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 651 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 361 KiB |
@ -0,0 +1,24 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="zh-CN"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
||||
<link rel="shortcut icon" type="image/x-icon" href="static/favicon.ico"> |
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> |
||||
<title> |
||||
<%= htmlWebpackPlugin.options.title %> |
||||
</title> |
||||
<script> |
||||
document.addEventListener('DOMContentLoaded', function() { |
||||
document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px' |
||||
}) |
||||
</script> |
||||
<link rel="stylesheet" href="<%= BASE_URL %>static/index.css" /> |
||||
</head> |
||||
<body> |
||||
<noscript> |
||||
<strong>本站点必须要开启JavaScript才能运行</strong> |
||||
</noscript> |
||||
<div id="app"></div> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,5 @@ |
||||
/** |
||||
* 下方引入的为uView UI的集成样式文件,为scss预处理器,其中包含了一些"u-"开头的自定义变量 |
||||
* uView自定义的css类名和scss变量,均以"u-"开头,不会造成冲突,请放心使用 |
||||
*/ |
||||
@import 'uview-ui/theme.scss'; |
||||
@ -0,0 +1,3 @@ |
||||
<!DOCTYPE html><html lang=zh-CN><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><link rel="shortcut icon" type=image/x-icon href=static/favicon.ico><meta name=viewport content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><title>dafosi</title><script>document.addEventListener('DOMContentLoaded', function() { |
||||
document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px' |
||||
})</script><link rel=stylesheet href=/h5/static/index.css></head><body><noscript><strong>本站点必须要开启JavaScript才能运行</strong></noscript><div id=app></div><script src=/h5/static/js/chunk-vendors.93366f94.js></script><script src=/h5/static/js/index.ca931f29.js></script></body></html> |
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 651 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 651 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 173 KiB |
@ -0,0 +1 @@ |
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["pages-error-error"],{"0a94":function(t,e,n){var a=n("66d2");a.__esModule&&(a=a.default),"string"===typeof a&&(a=[[t.i,a,""]]),a.locals&&(t.exports=a.locals);var r=n("4f06").default;r("8bc840be",a,!0,{sourceMap:!1,shadowMode:!1})},"66d2":function(t,e,n){var a=n("24fb");e=a(!1),e.push([t.i,'@charset "UTF-8";\r\n/**\r\n * 下方引入的为uView UI的集成样式文件,为scss预处理器,其中包含了一些"u-"开头的自定义变量\r\n * uView自定义的css类名和scss变量,均以"u-"开头,不会造成冲突,请放心使用 \r\n */.title[data-v-a8466e20]{color:red;font-size:%?200?%}.content[data-v-a8466e20]{color:red;font-size:%?80?%}',""]),t.exports=e},"77ad":function(t,e,n){"use strict";var a=n("0a94"),r=n.n(a);r.a},9291:function(t,e,n){"use strict";n.r(e);var a=n("d9fc"),r=n.n(a);for(var u in a)["default"].indexOf(u)<0&&function(t){n.d(e,t,(function(){return a[t]}))}(u);e["default"]=r.a},a137:function(t,e,n){"use strict";n.d(e,"b",(function(){return a})),n.d(e,"c",(function(){return r})),n.d(e,"a",(function(){}));var a=function(){var t=this.$createElement,e=this._self._c||t;return e("v-uni-view",{staticClass:"u-flex-col u-col-center"},[e("v-uni-view",{staticClass:"title u-m-t-50"},[this._v("404")]),e("v-uni-view",{staticClass:"content u-m-30"},[this._v("请在微信或支付宝中打开此链接!")])],1)},r=[]},be2a:function(t,e,n){"use strict";n.r(e);var a=n("a137"),r=n("9291");for(var u in r)["default"].indexOf(u)<0&&function(t){n.d(e,t,(function(){return r[t]}))}(u);n("77ad");var o=n("f0c5"),i=Object(o["a"])(r["default"],a["b"],a["c"],!1,null,"a8466e20",null,!1,a["a"],void 0);e["default"]=i.exports},d9fc:function(t,e,n){"use strict";(function(t){n("7a82"),Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var a={data:function(){return{}},onLoad:function(){t("log",uni.$u.config.v," at pages/error/error.vue:14")},methods:{}};e.default=a}).call(this,n("0de9")["log"])}}]); |
||||
|
After Width: | Height: | Size: 361 KiB |
@ -0,0 +1,21 @@ |
||||
MIT License |
||||
|
||||
Copyright (c) 2023 www.uviewui.com |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
||||
@ -0,0 +1,106 @@ |
||||
<p align="center"> |
||||
<img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;"> |
||||
</p> |
||||
<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView</h3> |
||||
<h3 align="center">多平台快速开发的UI框架</h3> |
||||
|
||||
|
||||
## 说明 |
||||
|
||||
uView UI,是[uni-app](https://uniapp.dcloud.io/)生态优秀的UI框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水 |
||||
|
||||
## 特性 |
||||
|
||||
- 兼容安卓,iOS,微信小程序,H5,QQ小程序,百度小程序,支付宝小程序,头条小程序 |
||||
- 60+精选组件,功能丰富,多端兼容,让您快速集成,开箱即用 |
||||
- 众多贴心的JS利器,让您飞镖在手,召之即来,百步穿杨 |
||||
- 众多的常用页面和布局,让您专注逻辑,事半功倍 |
||||
- 详尽的文档支持,现代化的演示效果 |
||||
- 按需引入,精简打包体积 |
||||
|
||||
|
||||
## 安装 |
||||
|
||||
```bash |
||||
# npm方式安装 |
||||
npm i uview-ui |
||||
``` |
||||
|
||||
## 快速上手 |
||||
|
||||
1. `main.js`引入uView库 |
||||
```js |
||||
// main.js |
||||
import uView from 'uview-ui'; |
||||
Vue.use(uView); |
||||
``` |
||||
|
||||
2. `App.vue`引入基础样式(注意style标签需声明scss属性支持) |
||||
```css |
||||
/* App.vue */ |
||||
<style lang="scss"> |
||||
@import "uview-ui/index.scss"; |
||||
</style> |
||||
``` |
||||
|
||||
3. `uni.scss`引入全局scss变量文件 |
||||
```css |
||||
/* uni.scss */ |
||||
@import "uview-ui/theme.scss"; |
||||
``` |
||||
|
||||
4. `pages.json`配置easycom规则(按需引入) |
||||
|
||||
```js |
||||
// pages.json |
||||
{ |
||||
"easycom": { |
||||
// npm安装的方式不需要前面的"@/",下载安装的方式需要"@/" |
||||
// npm安装方式 |
||||
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue" |
||||
// 下载安装方式 |
||||
// "^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue" |
||||
}, |
||||
// 此为本身已有的内容 |
||||
"pages": [ |
||||
// ...... |
||||
] |
||||
} |
||||
``` |
||||
|
||||
请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容 |
||||
|
||||
## 使用方法 |
||||
配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。 |
||||
|
||||
```html |
||||
<template> |
||||
<u-button>按钮</u-button> |
||||
</template> |
||||
``` |
||||
|
||||
请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容 |
||||
|
||||
## 链接 |
||||
|
||||
- [官方文档](https://uviewui.com/) |
||||
- [更新日志](https://uviewui.com/components/changelog.html) |
||||
- [升级指南](https://uviewui.com/components/changelog.html) |
||||
- [关于我们](https://uviewui.com/cooperation/about.html) |
||||
|
||||
## 预览 |
||||
|
||||
您可以通过**微信**扫码,查看最佳的演示效果。 |
||||
<br> |
||||
<br> |
||||
<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" > |
||||
|
||||
<!-- ## 捐赠uView的研发 |
||||
|
||||
uView文档和源码全部开源免费,如果您认为uView帮到了您的开发工作,您可以捐赠uView的研发工作,捐赠无门槛,哪怕是一杯可乐也好(相信这比打赏主播更有意义)。 |
||||
|
||||
<img src="https://uviewui.com/common/wechat.png" width="220" > |
||||
<img style="margin-left: 100px;" src="https://uviewui.com/common/alipay.png" width="220" > |
||||
--> |
||||
## 版权信息 |
||||
uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uView应用到您的产品中。 |
||||
@ -0,0 +1,190 @@ |
||||
<template> |
||||
<u-popup mode="bottom" :border-radius="borderRadius" :popup="false" v-model="value" :maskCloseAble="maskCloseAble" |
||||
length="auto" :safeAreaInsetBottom="safeAreaInsetBottom" @close="popupClose" :z-index="uZIndex"> |
||||
<view class="u-tips u-border-bottom" v-if="tips.text" :style="[tipsStyle]"> |
||||
{{tips.text}} |
||||
</view> |
||||
<block v-for="(item, index) in list" :key="index"> |
||||
<view |
||||
@touchmove.stop.prevent |
||||
@tap="itemClick(index)" |
||||
:style="[itemStyle(index)]" |
||||
class="u-action-sheet-item u-line-1" |
||||
:class="[index < list.length - 1 ? 'u-border-bottom' : '']" |
||||
:hover-stay-time="150" |
||||
> |
||||
<text>{{item.text}}</text> |
||||
<text class="u-action-sheet-item__subtext u-line-1" v-if="item.subText">{{item.subText}}</text> |
||||
</view> |
||||
</block> |
||||
<view class="u-gab" v-if="cancelBtn"> |
||||
</view> |
||||
<view @touchmove.stop.prevent class="u-actionsheet-cancel u-action-sheet-item" hover-class="u-hover-class" |
||||
:hover-stay-time="150" v-if="cancelBtn" @tap="close">{{cancelText}}</view> |
||||
</u-popup> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* actionSheet 操作菜单 |
||||
* @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。 |
||||
* @tutorial https://www.uviewui.com/components/actionSheet.html |
||||
* @property {Array<Object>} list 按钮的文字数组,见官方文档示例 |
||||
* @property {Object} tips 顶部的提示文字,见官方文档示例 |
||||
* @property {String} cancel-text 取消按钮的提示文字 |
||||
* @property {Boolean} cancel-btn 是否显示底部的取消按钮(默认true) |
||||
* @property {Number String} border-radius 弹出部分顶部左右的圆角值,单位rpx(默认0) |
||||
* @property {Boolean} mask-close-able 点击遮罩是否可以关闭(默认true) |
||||
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false) |
||||
* @property {Number String} z-index z-index值(默认1075) |
||||
* @property {String} cancel-text 取消按钮的提示文字 |
||||
* @event {Function} click 点击ActionSheet列表项时触发 |
||||
* @event {Function} close 点击取消按钮时触发 |
||||
* @example <u-action-sheet :list="list" @click="click" v-model="show"></u-action-sheet> |
||||
*/ |
||||
export default { |
||||
name: "u-action-sheet", |
||||
props: { |
||||
// 点击遮罩是否可以关闭actionsheet |
||||
maskCloseAble: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 按钮的文字数组,可以自定义颜色和字体大小,字体单位为rpx |
||||
list: { |
||||
type: Array, |
||||
default () { |
||||
// 如下 |
||||
// return [{ |
||||
// text: '确定', |
||||
// color: '', |
||||
// fontSize: '' |
||||
// }] |
||||
return []; |
||||
} |
||||
}, |
||||
// 顶部的提示文字 |
||||
tips: { |
||||
type: Object, |
||||
default () { |
||||
return { |
||||
text: '', |
||||
color: '', |
||||
fontSize: '26' |
||||
} |
||||
} |
||||
}, |
||||
// 底部的取消按钮 |
||||
cancelBtn: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距 |
||||
safeAreaInsetBottom: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 通过双向绑定控制组件的弹出与收起 |
||||
value: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 弹出的顶部圆角值 |
||||
borderRadius: { |
||||
type: [String, Number], |
||||
default: 0 |
||||
}, |
||||
// 弹出的z-index值 |
||||
zIndex: { |
||||
type: [String, Number], |
||||
default: 0 |
||||
}, |
||||
// 取消按钮的文字提示 |
||||
cancelText: { |
||||
type: String, |
||||
default: '取消' |
||||
} |
||||
}, |
||||
computed: { |
||||
// 顶部提示的样式 |
||||
tipsStyle() { |
||||
let style = {}; |
||||
if (this.tips.color) style.color = this.tips.color; |
||||
if (this.tips.fontSize) style.fontSize = this.tips.fontSize + 'rpx'; |
||||
return style; |
||||
}, |
||||
// 操作项目的样式 |
||||
itemStyle() { |
||||
return (index) => { |
||||
let style = {}; |
||||
if (this.list[index].color) style.color = this.list[index].color; |
||||
if (this.list[index].fontSize) style.fontSize = this.list[index].fontSize + 'rpx'; |
||||
// 选项被禁用的样式 |
||||
if (this.list[index].disabled) style.color = '#c0c4cc'; |
||||
return style; |
||||
} |
||||
}, |
||||
uZIndex() { |
||||
// 如果用户有传递z-index值,优先使用 |
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup; |
||||
} |
||||
}, |
||||
methods: { |
||||
// 点击取消按钮 |
||||
close() { |
||||
// 发送input事件,并不会作用于父组件,而是要设置组件内部通过props传递的value参数 |
||||
// 这是一个vue发送事件的特殊用法 |
||||
this.popupClose(); |
||||
this.$emit('close'); |
||||
}, |
||||
// 弹窗关闭 |
||||
popupClose() { |
||||
this.$emit('input', false); |
||||
}, |
||||
// 点击某一个item |
||||
itemClick(index) { |
||||
// disabled的项禁止点击 |
||||
if(this.list[index].disabled) return; |
||||
this.$emit('click', index); |
||||
this.$emit('input', false); |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-tips { |
||||
font-size: 26rpx; |
||||
text-align: center; |
||||
padding: 34rpx 0; |
||||
line-height: 1; |
||||
color: $u-tips-color; |
||||
} |
||||
|
||||
.u-action-sheet-item { |
||||
@include vue-flex;; |
||||
line-height: 1; |
||||
justify-content: center; |
||||
align-items: center; |
||||
font-size: 32rpx; |
||||
padding: 34rpx 0; |
||||
flex-direction: column; |
||||
} |
||||
|
||||
.u-action-sheet-item__subtext { |
||||
font-size: 24rpx; |
||||
color: $u-tips-color; |
||||
margin-top: 20rpx; |
||||
} |
||||
|
||||
.u-gab { |
||||
height: 12rpx; |
||||
background-color: rgb(234, 234, 236); |
||||
} |
||||
|
||||
.u-actionsheet-cancel { |
||||
color: $u-main-color; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,256 @@ |
||||
<template> |
||||
<view class="u-alert-tips" v-if="show" :class="[ |
||||
!show ? 'u-close-alert-tips': '', |
||||
type ? 'u-alert-tips--bg--' + type + '-light' : '', |
||||
type ? 'u-alert-tips--border--' + type + '-disabled' : '', |
||||
]" :style="{ |
||||
backgroundColor: bgColor, |
||||
borderColor: borderColor |
||||
}"> |
||||
<view class="u-icon-wrap"> |
||||
<u-icon v-if="showIcon" :name="uIcon" :size="description ? 40 : 32" class="u-icon" :color="uIconType" :custom-style="iconStyle"></u-icon> |
||||
</view> |
||||
<view class="u-alert-content" @tap.stop="click"> |
||||
<view class="u-alert-title" :style="[uTitleStyle]"> |
||||
{{title}} |
||||
</view> |
||||
<view v-if="description" class="u-alert-desc" :style="[descStyle]"> |
||||
{{description}} |
||||
</view> |
||||
</view> |
||||
<view class="u-icon-wrap"> |
||||
<u-icon @click="close" v-if="closeAble && !closeText" hoverClass="u-type-error-hover-color" name="close" color="#c0c4cc" |
||||
:size="22" class="u-close-icon" :style="{ |
||||
top: description ? '18rpx' : '24rpx' |
||||
}"></u-icon> |
||||
</view> |
||||
<text v-if="closeAble && closeText" class="u-close-text" :style="{ |
||||
top: description ? '18rpx' : '24rpx' |
||||
}">{{closeText}}</text> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* alertTips 警告提示 |
||||
* @description 警告提示,展现需要关注的信息 |
||||
* @tutorial https://uviewui.com/components/alertTips.html |
||||
* @property {String} title 显示的标题文字 |
||||
* @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选 |
||||
* @property {String} type 关闭按钮(默认为叉号icon图标) |
||||
* @property {String} icon 图标名称 |
||||
* @property {Object} icon-style 图标的样式,对象形式 |
||||
* @property {Object} title-style 标题的样式,对象形式 |
||||
* @property {Object} desc-style 描述的样式,对象形式 |
||||
* @property {String} close-able 用文字替代关闭图标,close-able为true时有效 |
||||
* @property {Boolean} show-icon 是否显示左边的辅助图标 |
||||
* @property {Boolean} show 显示或隐藏组件 |
||||
* @event {Function} click 点击组件时触发 |
||||
* @event {Function} close 点击关闭按钮时触发 |
||||
*/ |
||||
export default { |
||||
name: 'u-alert-tips', |
||||
props: { |
||||
// 显示文字 |
||||
title: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 主题,success/warning/info/error |
||||
type: { |
||||
type: String, |
||||
default: 'warning' |
||||
}, |
||||
// 辅助性文字 |
||||
description: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 是否可关闭 |
||||
closeAble: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 关闭按钮自定义文本 |
||||
closeText: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 是否显示图标 |
||||
showIcon: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 文字颜色,如果定义了color值,icon会失效 |
||||
color: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 背景颜色 |
||||
bgColor: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 边框颜色 |
||||
borderColor: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 是否显示 |
||||
show: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 左边显示的icon |
||||
icon: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// icon的样式 |
||||
iconStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {} |
||||
} |
||||
}, |
||||
// 标题的样式 |
||||
titleStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {} |
||||
} |
||||
}, |
||||
// 描述文字的样式 |
||||
descStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {} |
||||
} |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
} |
||||
}, |
||||
computed: { |
||||
uTitleStyle() { |
||||
let style = {}; |
||||
// 如果有描述文字的话,标题进行加粗 |
||||
style.fontWeight = this.description ? 500 : 'normal'; |
||||
// 将用户传入样式对象和style合并,传入的优先级比style高,同属性会被覆盖 |
||||
return this.$u.deepMerge(style, this.titleStyle); |
||||
}, |
||||
uIcon() { |
||||
// 如果有设置icon名称就使用,否则根据type主题,推定一个默认的图标 |
||||
return this.icon ? this.icon : this.$u.type2icon(this.type); |
||||
}, |
||||
uIconType() { |
||||
// 如果有设置图标的样式,优先使用,没有的话,则用type的样式 |
||||
return Object.keys(this.iconStyle).length ? '' : this.type; |
||||
} |
||||
}, |
||||
methods: { |
||||
// 点击内容 |
||||
click() { |
||||
this.$emit('click'); |
||||
}, |
||||
// 点击关闭按钮 |
||||
close() { |
||||
this.$emit('close'); |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-alert-tips { |
||||
@include vue-flex; |
||||
align-items: center; |
||||
padding: 16rpx 30rpx; |
||||
border-radius: 8rpx; |
||||
position: relative; |
||||
transition: all 0.3s linear; |
||||
border: 1px solid #fff; |
||||
|
||||
&--bg--primary-light { |
||||
background-color: $u-type-primary-light; |
||||
} |
||||
|
||||
&--bg--info-light { |
||||
background-color: $u-type-info-light; |
||||
} |
||||
|
||||
&--bg--success-light { |
||||
background-color: $u-type-success-light; |
||||
} |
||||
|
||||
&--bg--warning-light { |
||||
background-color: $u-type-warning-light; |
||||
} |
||||
|
||||
&--bg--error-light { |
||||
background-color: $u-type-error-light; |
||||
} |
||||
|
||||
&--border--primary-disabled { |
||||
border-color: $u-type-primary-disabled; |
||||
} |
||||
|
||||
&--border--success-disabled { |
||||
border-color: $u-type-success-disabled; |
||||
} |
||||
|
||||
&--border--error-disabled { |
||||
border-color: $u-type-error-disabled; |
||||
} |
||||
|
||||
&--border--warning-disabled { |
||||
border-color: $u-type-warning-disabled; |
||||
} |
||||
|
||||
&--border--info-disabled { |
||||
border-color: $u-type-info-disabled; |
||||
} |
||||
} |
||||
|
||||
.u-close-alert-tips { |
||||
opacity: 0; |
||||
visibility: hidden; |
||||
} |
||||
|
||||
.u-icon { |
||||
margin-right: 16rpx; |
||||
} |
||||
|
||||
.u-alert-title { |
||||
font-size: 28rpx; |
||||
color: $u-main-color; |
||||
} |
||||
|
||||
.u-alert-desc { |
||||
font-size: 26rpx; |
||||
text-align: left; |
||||
color: $u-content-color; |
||||
} |
||||
|
||||
.u-close-icon { |
||||
position: absolute; |
||||
top: 20rpx; |
||||
right: 20rpx; |
||||
} |
||||
|
||||
.u-close-hover { |
||||
color: red; |
||||
} |
||||
|
||||
.u-close-text { |
||||
font-size: 24rpx; |
||||
color: $u-tips-color; |
||||
position: absolute; |
||||
top: 20rpx; |
||||
right: 20rpx; |
||||
line-height: 1; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,290 @@ |
||||
<template> |
||||
<view class="content"> |
||||
<view class="cropper-wrapper" :style="{ height: cropperOpt.height + 'px' }"> |
||||
<canvas |
||||
class="cropper" |
||||
:disable-scroll="true" |
||||
@touchstart="touchStart" |
||||
@touchmove="touchMove" |
||||
@touchend="touchEnd" |
||||
:style="{ width: cropperOpt.width, height: cropperOpt.height, backgroundColor: 'rgba(0, 0, 0, 0.8)' }" |
||||
canvas-id="cropper" |
||||
id="cropper" |
||||
></canvas> |
||||
<canvas |
||||
class="cropper" |
||||
:disable-scroll="true" |
||||
:style="{ |
||||
position: 'fixed', |
||||
top: `-${cropperOpt.width * cropperOpt.pixelRatio}px`, |
||||
left: `-${cropperOpt.height * cropperOpt.pixelRatio}px`, |
||||
width: `${cropperOpt.width * cropperOpt.pixelRatio}px`, |
||||
height: `${cropperOpt.height * cropperOpt.pixelRatio}` |
||||
}" |
||||
canvas-id="targetId" |
||||
id="targetId" |
||||
></canvas> |
||||
</view> |
||||
<view class="cropper-buttons safe-area-padding" :style="{ height: bottomNavHeight + 'px' }"> |
||||
<!-- #ifdef H5 --> |
||||
<view class="upload" @tap="uploadTap">选择图片</view> |
||||
<!-- #endif --> |
||||
<!-- #ifndef H5 --> |
||||
<view class="upload" @tap="uploadTap">重新选择</view> |
||||
<!-- #endif --> |
||||
<view class="getCropperImage" @tap="getCropperImage(false)">确定</view> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import WeCropper from './weCropper.js'; |
||||
export default { |
||||
props: { |
||||
// 裁剪矩形框的样式,其中可包含的属性为lineWidth-边框宽度(单位rpx),color: 边框颜色, |
||||
// mask-遮罩颜色,一般设置为一个rgba的透明度,如"rgba(0, 0, 0, 0.35)" |
||||
boundStyle: { |
||||
type: Object, |
||||
default() { |
||||
return { |
||||
lineWidth: 4, |
||||
borderColor: 'rgb(245, 245, 245)', |
||||
mask: 'rgba(0, 0, 0, 0.35)' |
||||
}; |
||||
} |
||||
} |
||||
// // 裁剪框宽度,单位rpx |
||||
// rectWidth: { |
||||
// type: [String, Number], |
||||
// default: 400 |
||||
// }, |
||||
// // 裁剪框高度,单位rpx |
||||
// rectHeight: { |
||||
// type: [String, Number], |
||||
// default: 400 |
||||
// }, |
||||
// // 输出图片宽度,单位rpx |
||||
// destWidth: { |
||||
// type: [String, Number], |
||||
// default: 400 |
||||
// }, |
||||
// // 输出图片高度,单位rpx |
||||
// destHeight: { |
||||
// type: [String, Number], |
||||
// default: 400 |
||||
// }, |
||||
// // 输出的图片类型,如果发现裁剪的图片很大,可能是因为设置为了"png",改成"jpg"即可 |
||||
// fileType: { |
||||
// type: String, |
||||
// default: 'jpg', |
||||
// }, |
||||
// // 生成的图片质量 |
||||
// // H5上无效,目前不考虑使用此参数 |
||||
// quality: { |
||||
// type: [Number, String], |
||||
// default: 1 |
||||
// } |
||||
}, |
||||
data() { |
||||
return { |
||||
// 底部导航的高度 |
||||
bottomNavHeight: 50, |
||||
originWidth: 200, |
||||
width: 0, |
||||
height: 0, |
||||
cropperOpt: { |
||||
id: 'cropper', |
||||
targetId: 'targetCropper', |
||||
pixelRatio: 1, |
||||
width: 0, |
||||
height: 0, |
||||
scale: 2.5, |
||||
zoom: 8, |
||||
cut: { |
||||
x: (this.width - this.originWidth) / 2, |
||||
y: (this.height - this.originWidth) / 2, |
||||
width: this.originWidth, |
||||
height: this.originWidth |
||||
}, |
||||
boundStyle: { |
||||
lineWidth: uni.upx2px(this.boundStyle.lineWidth), |
||||
mask: this.boundStyle.mask, |
||||
color: this.boundStyle.borderColor |
||||
} |
||||
}, |
||||
// 裁剪框和输出图片的尺寸,高度默认等于宽度 |
||||
// 输出图片宽度,单位px |
||||
destWidth: 200, |
||||
// 裁剪框宽度,单位px |
||||
rectWidth: 200, |
||||
// 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可 |
||||
fileType: 'jpg', |
||||
src: '', // 选择的图片路径,用于在点击确定时,判断是否选择了图片 |
||||
}; |
||||
}, |
||||
onLoad(option) { |
||||
let rectInfo = uni.getSystemInfoSync(); |
||||
this.width = rectInfo.windowWidth; |
||||
this.height = rectInfo.windowHeight - this.bottomNavHeight; |
||||
this.cropperOpt.width = this.width; |
||||
this.cropperOpt.height = this.height; |
||||
this.cropperOpt.pixelRatio = rectInfo.pixelRatio; |
||||
|
||||
if (option.destWidth) this.destWidth = option.destWidth; |
||||
if (option.rectWidth) { |
||||
let rectWidth = Number(option.rectWidth); |
||||
this.cropperOpt.cut = { |
||||
x: (this.width - rectWidth) / 2, |
||||
y: (this.height - rectWidth) / 2, |
||||
width: rectWidth, |
||||
height: rectWidth |
||||
}; |
||||
} |
||||
this.rectWidth = option.rectWidth; |
||||
if (option.fileType) this.fileType = option.fileType; |
||||
// 初始化 |
||||
this.cropper = new WeCropper(this.cropperOpt) |
||||
.on('ready', ctx => { |
||||
// wecropper is ready for work! |
||||
}) |
||||
.on('beforeImageLoad', ctx => { |
||||
// before picture loaded, i can do something |
||||
}) |
||||
.on('imageLoad', ctx => { |
||||
// picture loaded |
||||
}) |
||||
.on('beforeDraw', (ctx, instance) => { |
||||
// before canvas draw,i can do something |
||||
}); |
||||
// 设置导航栏样式,以免用户在page.json中没有设置为黑色背景 |
||||
uni.setNavigationBarColor({ |
||||
frontColor: '#ffffff', |
||||
backgroundColor: '#000000' |
||||
}); |
||||
uni.chooseImage({ |
||||
count: 1, // 默认9 |
||||
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有 |
||||
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 |
||||
success: res => { |
||||
this.src = res.tempFilePaths[0]; |
||||
// 获取裁剪图片资源后,给data添加src属性及其值 |
||||
this.cropper.pushOrign(this.src); |
||||
} |
||||
}); |
||||
}, |
||||
methods: { |
||||
touchStart(e) { |
||||
this.cropper.touchStart(e); |
||||
}, |
||||
touchMove(e) { |
||||
this.cropper.touchMove(e); |
||||
}, |
||||
touchEnd(e) { |
||||
this.cropper.touchEnd(e); |
||||
}, |
||||
getCropperImage(isPre = false) { |
||||
if(!this.src) return this.$u.toast('请先选择图片再裁剪'); |
||||
|
||||
let cropper_opt = { |
||||
destHeight: Number(this.destWidth), // uni.canvasToTempFilePath要求这些参数为数值 |
||||
destWidth: Number(this.destWidth), |
||||
fileType: this.fileType |
||||
}; |
||||
this.cropper.getCropperImage(cropper_opt, (path, err) => { |
||||
if (err) { |
||||
uni.showModal({ |
||||
title: '温馨提示', |
||||
content: err.message |
||||
}); |
||||
} else { |
||||
if (isPre) { |
||||
uni.previewImage({ |
||||
current: '', // 当前显示图片的 http 链接 |
||||
urls: [path] // 需要预览的图片 http 链接列表 |
||||
}); |
||||
} else { |
||||
uni.$emit('uAvatarCropper', path); |
||||
this.$u.route({ |
||||
type: 'back' |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
}, |
||||
uploadTap() { |
||||
const self = this; |
||||
uni.chooseImage({ |
||||
count: 1, // 默认9 |
||||
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 |
||||
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 |
||||
success: (res) => { |
||||
self.src = res.tempFilePaths[0]; |
||||
// 获取裁剪图片资源后,给data添加src属性及其值 |
||||
|
||||
self.cropper.pushOrign(this.src); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
@import '../../libs/css/style.components.scss'; |
||||
|
||||
.content { |
||||
background: rgba(255, 255, 255, 1); |
||||
} |
||||
|
||||
.cropper { |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
width: 100%; |
||||
height: 100%; |
||||
z-index: 11; |
||||
} |
||||
|
||||
.cropper-buttons { |
||||
background-color: #000000; |
||||
color: #eee; |
||||
} |
||||
|
||||
.cropper-wrapper { |
||||
position: relative; |
||||
@include vue-flex; |
||||
flex-direction: row; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
width: 100%; |
||||
background-color: #000; |
||||
} |
||||
|
||||
.cropper-buttons { |
||||
width: 100vw; |
||||
@include vue-flex; |
||||
flex-direction: row; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
position: fixed; |
||||
bottom: 0; |
||||
left: 0; |
||||
font-size: 28rpx; |
||||
} |
||||
|
||||
.cropper-buttons .upload, |
||||
.cropper-buttons .getCropperImage { |
||||
width: 50%; |
||||
text-align: center; |
||||
} |
||||
|
||||
.cropper-buttons .upload { |
||||
text-align: left; |
||||
padding-left: 50rpx; |
||||
} |
||||
|
||||
.cropper-buttons .getCropperImage { |
||||
text-align: right; |
||||
padding-right: 50rpx; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,244 @@ |
||||
<template> |
||||
<view class="u-avatar" :style="[wrapStyle]" @tap="click"> |
||||
<image |
||||
@error="loadError" |
||||
:style="[imgStyle]" |
||||
class="u-avatar__img" |
||||
v-if="!uText && avatar" |
||||
:src="avatar" |
||||
:mode="imgMode" |
||||
></image> |
||||
<text class="u-line-1" v-else-if="uText" :style="{ |
||||
fontSize: '38rpx' |
||||
}">{{uText}}</text> |
||||
<slot v-else></slot> |
||||
<view class="u-avatar__sex" v-if="showSex" :class="['u-avatar__sex--' + sexIcon]" :style="[uSexStyle]"> |
||||
<u-icon :name="sexIcon" size="20"></u-icon> |
||||
</view> |
||||
<view class="u-avatar__level" v-if="showLevel" :style="[uLevelStyle]"> |
||||
<u-icon :name="levelIcon" size="20"></u-icon> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
let base64Avatar = "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"; |
||||
/** |
||||
* avatar 头像 |
||||
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。 |
||||
* @tutorial https://www.uviewui.com/components/avatar.html |
||||
* @property {String} bg-color 背景颜色,一般显示文字时用(默认#ffffff) |
||||
* @property {String} src 头像路径,如加载失败,将会显示默认头像 |
||||
* @property {String Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值,单位rpx(默认default) |
||||
* @property {String} mode 显示类型,见上方说明(默认circle) |
||||
* @property {String} sex-icon 性别图标,man-男,woman-女(默认man) |
||||
* @property {String} level-icon 等级图标(默认level) |
||||
* @property {String} sex-bg-color 性别图标背景颜色 |
||||
* @property {String} level-bg-color 等级图标背景颜色 |
||||
* @property {String} show-sex 是否显示性别图标(默认false) |
||||
* @property {String} show-level 是否显示等级图标(默认false) |
||||
* @property {String} img-mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值(默认aspectFill) |
||||
* @property {String} index 用户传递的标识符值,如果是列表循环,可穿v-for的index值 |
||||
* @event {Function} click 头像被点击 |
||||
* @example <u-avatar :src="src"></u-avatar> |
||||
*/ |
||||
export default { |
||||
name: 'u-avatar', |
||||
props: { |
||||
// 背景颜色 |
||||
bgColor: { |
||||
type: String, |
||||
default: 'transparent' |
||||
}, |
||||
// 头像路径 |
||||
src: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 尺寸,large-大,default-中等,mini-小,如果为数值,则单位为rpx |
||||
// 宽度等于高度 |
||||
size: { |
||||
type: [String, Number], |
||||
default: 'default' |
||||
}, |
||||
// 头像模型,square-带圆角方形,circle-圆形 |
||||
mode: { |
||||
type: String, |
||||
default: 'circle' |
||||
}, |
||||
// 文字内容 |
||||
text: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 图片的裁剪模型 |
||||
imgMode: { |
||||
type: String, |
||||
default: 'aspectFill' |
||||
}, |
||||
// 标识符 |
||||
index: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// 右上角性别角标,man-男,woman-女 |
||||
sexIcon: { |
||||
type: String, |
||||
default: 'man' |
||||
}, |
||||
// 右下角的等级图标 |
||||
levelIcon: { |
||||
type: String, |
||||
default: 'level' |
||||
}, |
||||
// 右下角等级图标背景颜色 |
||||
levelBgColor: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 右上角性别图标的背景颜色 |
||||
sexBgColor: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 是否显示性别图标 |
||||
showSex: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 是否显示等级图标 |
||||
showLevel: { |
||||
type: Boolean, |
||||
default: false |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
error: false, |
||||
// 头像的地址,因为如果加载错误,需要赋值为默认图片,props值无法修改,所以需要一个中间值 |
||||
avatar: this.src ? this.src : base64Avatar, |
||||
} |
||||
}, |
||||
watch: { |
||||
src(n) { |
||||
// 用户可能会在头像加载失败时,再次修改头像值,所以需要重新赋值 |
||||
if(!n) { |
||||
// 如果传入null或者'',或者undefined,显示默认头像 |
||||
this.avatar = base64Avatar; |
||||
this.error = true; |
||||
} else { |
||||
this.avatar = n; |
||||
this.error = false; |
||||
} |
||||
} |
||||
}, |
||||
computed: { |
||||
wrapStyle() { |
||||
let style = {}; |
||||
style.height = this.size == 'large' ? '120rpx' : this.size == 'default' ? |
||||
'90rpx' : this.size == 'mini' ? '70rpx' : this.size + 'rpx'; |
||||
style.width = style.height; |
||||
style.flex = `0 0 ${style.height}`; |
||||
style.backgroundColor = this.bgColor; |
||||
style.borderRadius = this.mode == 'circle' ? '500px' : '5px'; |
||||
if(this.text) style.padding = `0 6rpx`; |
||||
return style; |
||||
}, |
||||
imgStyle() { |
||||
let style = {}; |
||||
style.borderRadius = this.mode == 'circle' ? '500px' : '5px'; |
||||
return style; |
||||
}, |
||||
// 取字符串的第一个字符 |
||||
uText() { |
||||
return String(this.text)[0]; |
||||
}, |
||||
// 性别图标的自定义样式 |
||||
uSexStyle() { |
||||
let style = {}; |
||||
if(this.sexBgColor) style.backgroundColor = this.sexBgColor; |
||||
return style; |
||||
}, |
||||
// 等级图标的自定义样式 |
||||
uLevelStyle() { |
||||
let style = {}; |
||||
if(this.levelBgColor) style.backgroundColor = this.levelBgColor; |
||||
return style; |
||||
} |
||||
}, |
||||
methods: { |
||||
// 图片加载错误时,显示默认头像 |
||||
loadError() { |
||||
this.error = true; |
||||
this.avatar = base64Avatar; |
||||
}, |
||||
click() { |
||||
this.$emit('click', this.index); |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-avatar { |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
align-items: center; |
||||
justify-content: center; |
||||
font-size: 28rpx; |
||||
color: $u-content-color; |
||||
border-radius: 10px; |
||||
position: relative; |
||||
|
||||
&__img { |
||||
width: 100%; |
||||
height: 100%; |
||||
} |
||||
|
||||
&__sex { |
||||
position: absolute; |
||||
width: 32rpx; |
||||
color: #ffffff; |
||||
height: 32rpx; |
||||
@include vue-flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
border-radius: 100rpx; |
||||
top: 5%; |
||||
z-index: 1; |
||||
right: -7%; |
||||
border: 1px #ffffff solid; |
||||
|
||||
&--man { |
||||
background-color: $u-type-primary; |
||||
} |
||||
|
||||
&--woman { |
||||
background-color: $u-type-error; |
||||
} |
||||
|
||||
&--none { |
||||
background-color: $u-type-warning; |
||||
} |
||||
} |
||||
|
||||
&__level { |
||||
position: absolute; |
||||
width: 32rpx; |
||||
color: #ffffff; |
||||
height: 32rpx; |
||||
@include vue-flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
border-radius: 100rpx; |
||||
bottom: 5%; |
||||
z-index: 1; |
||||
right: -7%; |
||||
border: 1px #ffffff solid; |
||||
background-color: $u-type-warning; |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,153 @@ |
||||
<template> |
||||
<view @tap="backToTop" class="u-back-top" :class="['u-back-top--mode--' + mode]" :style="[{ |
||||
bottom: bottom + 'rpx', |
||||
right: right + 'rpx', |
||||
borderRadius: mode == 'circle' ? '10000rpx' : '8rpx', |
||||
zIndex: uZIndex, |
||||
opacity: opacity |
||||
}, customStyle]"> |
||||
<view class="u-back-top__content" v-if="!$slots.default && !$slots.$default"> |
||||
<u-icon @click="backToTop" :name="icon" :custom-style="iconStyle"></u-icon> |
||||
<view class="u-back-top__content__tips"> |
||||
{{tips}} |
||||
</view> |
||||
</view> |
||||
<slot v-else /> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'u-back-top', |
||||
props: { |
||||
// 返回顶部的形状,circle-圆形,square-方形 |
||||
mode: { |
||||
type: String, |
||||
default: 'circle' |
||||
}, |
||||
// 自定义图标 |
||||
icon: { |
||||
type: String, |
||||
default: 'arrow-upward' |
||||
}, |
||||
// 提示文字 |
||||
tips: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 返回顶部滚动时间 |
||||
duration: { |
||||
type: [Number, String], |
||||
default: 100 |
||||
}, |
||||
// 滚动距离 |
||||
scrollTop: { |
||||
type: [Number, String], |
||||
default: 0 |
||||
}, |
||||
// 距离顶部多少距离显示,单位rpx |
||||
top: { |
||||
type: [Number, String], |
||||
default: 400 |
||||
}, |
||||
// 返回顶部按钮到底部的距离,单位rpx |
||||
bottom: { |
||||
type: [Number, String], |
||||
default: 200 |
||||
}, |
||||
// 返回顶部按钮到右边的距离,单位rpx |
||||
right: { |
||||
type: [Number, String], |
||||
default: 40 |
||||
}, |
||||
// 层级 |
||||
zIndex: { |
||||
type: [Number, String], |
||||
default: '9' |
||||
}, |
||||
// 图标的样式,对象形式 |
||||
iconStyle: { |
||||
type: Object, |
||||
default() { |
||||
return { |
||||
color: '#909399', |
||||
fontSize: '38rpx' |
||||
} |
||||
} |
||||
}, |
||||
// 整个组件的样式 |
||||
customStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {} |
||||
} |
||||
} |
||||
}, |
||||
watch: { |
||||
showBackTop(nVal, oVal) { |
||||
// 当组件的显示与隐藏状态发生跳变时,修改组件的层级和不透明度 |
||||
// 让组件有显示和消失的动画效果,如果用v-if控制组件状态,将无设置动画效果 |
||||
if(nVal) { |
||||
this.uZIndex = this.zIndex; |
||||
this.opacity = 1; |
||||
} else { |
||||
this.uZIndex = -1; |
||||
this.opacity = 0; |
||||
} |
||||
} |
||||
}, |
||||
computed: { |
||||
showBackTop() { |
||||
// 由于scrollTop为页面的滚动距离,默认为px单位,这里将用于传入的top(rpx)值 |
||||
// 转为px用于比较,如果滚动条到顶的距离大于设定的距离,就显示返回顶部的按钮 |
||||
return this.scrollTop > uni.upx2px(this.top); |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
// 不透明度,为了让组件有一个显示和隐藏的过渡动画 |
||||
opacity: 0, |
||||
// 组件的z-index值,隐藏时设置为-1,就会看不到 |
||||
uZIndex: -1 |
||||
} |
||||
}, |
||||
methods: { |
||||
backToTop() { |
||||
uni.pageScrollTo({ |
||||
scrollTop: 0, |
||||
duration: this.duration |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-back-top { |
||||
width: 80rpx; |
||||
height: 80rpx; |
||||
position: fixed; |
||||
z-index: 9; |
||||
@include vue-flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
background-color: #E1E1E1; |
||||
color: $u-content-color; |
||||
align-items: center; |
||||
transition: opacity 0.4s; |
||||
|
||||
&__content { |
||||
@include vue-flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
|
||||
&__tips { |
||||
font-size: 24rpx; |
||||
transform: scale(0.8); |
||||
line-height: 1; |
||||
} |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,216 @@ |
||||
<template> |
||||
<view v-if="show" class="u-badge" :class="[ |
||||
isDot ? 'u-badge-dot' : '', |
||||
size == 'mini' ? 'u-badge-mini' : '', |
||||
type ? 'u-badge--bg--' + type : '' |
||||
]" :style="[{ |
||||
top: offset[0] + 'rpx', |
||||
right: offset[1] + 'rpx', |
||||
fontSize: fontSize + 'rpx', |
||||
position: absolute ? 'absolute' : 'static', |
||||
color: color, |
||||
backgroundColor: bgColor |
||||
}, boxStyle]" |
||||
> |
||||
{{showText}} |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* badge 角标 |
||||
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。 |
||||
* @tutorial https://www.uviewui.com/components/badge.html |
||||
* @property {String Number} count 展示的数字,大于 overflowCount 时显示为 ${overflowCount}+,为0且show-zero为false时隐藏 |
||||
* @property {Boolean} is-dot 不展示数字,只有一个小点(默认false) |
||||
* @property {Boolean} absolute 组件是否绝对定位,为true时,offset参数才有效(默认true) |
||||
* @property {String Number} overflow-count 展示封顶的数字值(默认99) |
||||
* @property {String} type 使用预设的背景颜色(默认error) |
||||
* @property {Boolean} show-zero 当数值为 0 时,是否展示 Badge(默认false) |
||||
* @property {String} size Badge的尺寸,设为mini会得到小一号的Badge(默认default) |
||||
* @property {Array} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,单位rpx。absolute为true时有效(默认[20, 20]) |
||||
* @property {String} color 字体颜色(默认#ffffff) |
||||
* @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效 |
||||
* @property {Boolean} is-center 组件中心点是否和父组件右上角重合,优先级比offset高,如设置,offset参数会失效(默认false) |
||||
* @example <u-badge type="error" count="7"></u-badge> |
||||
*/ |
||||
export default { |
||||
name: 'u-badge', |
||||
props: { |
||||
// primary,warning,success,error,info |
||||
type: { |
||||
type: String, |
||||
default: 'error' |
||||
}, |
||||
// default, mini |
||||
size: { |
||||
type: String, |
||||
default: 'default' |
||||
}, |
||||
//是否是圆点 |
||||
isDot: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 显示的数值内容 |
||||
count: { |
||||
type: [Number, String], |
||||
}, |
||||
// 展示封顶的数字值 |
||||
overflowCount: { |
||||
type: Number, |
||||
default: 99 |
||||
}, |
||||
// 当数值为 0 时,是否展示 Badge |
||||
showZero: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 位置偏移 |
||||
offset: { |
||||
type: Array, |
||||
default: () => { |
||||
return [20, 20] |
||||
} |
||||
}, |
||||
// 是否开启绝对定位,开启了offset才会起作用 |
||||
absolute: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 字体大小 |
||||
fontSize: { |
||||
type: [String, Number], |
||||
default: '24' |
||||
}, |
||||
// 字体演示 |
||||
color: { |
||||
type: String, |
||||
default: '#ffffff' |
||||
}, |
||||
// badge的背景颜色 |
||||
bgColor: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 是否让badge组件的中心点和父组件右上角重合,配置的话,offset将会失效 |
||||
isCenter: { |
||||
type: Boolean, |
||||
default: false |
||||
} |
||||
}, |
||||
computed: { |
||||
// 是否将badge中心与父组件右上角重合 |
||||
boxStyle() { |
||||
let style = {}; |
||||
if(this.isCenter) { |
||||
style.top = 0; |
||||
style.right = 0; |
||||
// Y轴-50%,意味着badge向上移动了badge自身高度一半,X轴50%,意味着向右移动了自身宽度一半 |
||||
style.transform = "translateY(-50%) translateX(50%)"; |
||||
} else { |
||||
style.top = this.offset[0] + 'rpx'; |
||||
style.right = this.offset[1] + 'rpx'; |
||||
style.transform = "translateY(0) translateX(0)"; |
||||
} |
||||
// 如果尺寸为mini,后接上scale() |
||||
if(this.size == 'mini') { |
||||
style.transform = style.transform + " scale(0.8)"; |
||||
} |
||||
return style; |
||||
}, |
||||
// isDot类型时,不显示文字 |
||||
showText() { |
||||
if(this.isDot) return ''; |
||||
else { |
||||
if(this.count > this.overflowCount) return `${this.overflowCount}+`; |
||||
else return this.count; |
||||
} |
||||
}, |
||||
// 是否显示组件 |
||||
show() { |
||||
// 如果count的值为0,并且showZero设置为false,不显示组件 |
||||
if(this.count == 0 && this.showZero == false) return false; |
||||
else return true; |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-badge { |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
justify-content: center; |
||||
align-items: center; |
||||
line-height: 24rpx; |
||||
padding: 4rpx 8rpx; |
||||
border-radius: 100rpx; |
||||
z-index: 9; |
||||
|
||||
&--bg--primary { |
||||
background-color: $u-type-primary; |
||||
} |
||||
|
||||
&--bg--error { |
||||
background-color: $u-type-error; |
||||
} |
||||
|
||||
&--bg--success { |
||||
background-color: $u-type-success; |
||||
} |
||||
|
||||
&--bg--info { |
||||
background-color: $u-type-info; |
||||
} |
||||
|
||||
&--bg--warning { |
||||
background-color: $u-type-warning; |
||||
} |
||||
} |
||||
|
||||
.u-badge-dot { |
||||
height: 16rpx; |
||||
width: 16rpx; |
||||
border-radius: 100rpx; |
||||
line-height: 1; |
||||
} |
||||
|
||||
.u-badge-mini { |
||||
transform: scale(0.8); |
||||
transform-origin: center center; |
||||
} |
||||
|
||||
// .u-primary { |
||||
// background: $u-type-primary; |
||||
// color: #fff; |
||||
// } |
||||
|
||||
// .u-error { |
||||
// background: $u-type-error; |
||||
// color: #fff; |
||||
// } |
||||
|
||||
// .u-warning { |
||||
// background: $u-type-warning; |
||||
// color: #fff; |
||||
// } |
||||
|
||||
// .u-success { |
||||
// background: $u-type-success; |
||||
// color: #fff; |
||||
// } |
||||
|
||||
// .u-black { |
||||
// background: #585858; |
||||
// color: #fff; |
||||
// } |
||||
|
||||
.u-info { |
||||
background-color: $u-type-info; |
||||
color: #fff; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,596 @@ |
||||
<template> |
||||
<button |
||||
id="u-wave-btn" |
||||
class="u-btn u-line-1 u-fix-ios-appearance" |
||||
:class="[ |
||||
'u-size-' + size, |
||||
plain ? 'u-btn--' + type + '--plain' : '', |
||||
loading ? 'u-loading' : '', |
||||
shape == 'circle' ? 'u-round-circle' : '', |
||||
hairLine ? showHairLineBorder : 'u-btn--bold-border', |
||||
'u-btn--' + type, |
||||
disabled ? `u-btn--${type}--disabled` : '', |
||||
]" |
||||
:hover-start-time="Number(hoverStartTime)" |
||||
:hover-stay-time="Number(hoverStayTime)" |
||||
:disabled="disabled" |
||||
:form-type="formType" |
||||
:open-type="openType" |
||||
:app-parameter="appParameter" |
||||
:hover-stop-propagation="hoverStopPropagation" |
||||
:send-message-title="sendMessageTitle" |
||||
send-message-path="sendMessagePath" |
||||
:lang="lang" |
||||
:data-name="dataName" |
||||
:session-from="sessionFrom" |
||||
:send-message-img="sendMessageImg" |
||||
:show-message-card="showMessageCard" |
||||
@getphonenumber="getphonenumber" |
||||
@getuserinfo="getuserinfo" |
||||
@error="error" |
||||
@opensetting="opensetting" |
||||
@launchapp="launchapp" |
||||
:style="[customStyle, { |
||||
overflow: ripple ? 'hidden' : 'visible' |
||||
}]" |
||||
@tap.stop="click($event)" |
||||
:hover-class="getHoverClass" |
||||
:loading="loading" |
||||
> |
||||
<slot></slot> |
||||
<view |
||||
v-if="ripple" |
||||
class="u-wave-ripple" |
||||
:class="[waveActive ? 'u-wave-active' : '']" |
||||
:style="{ |
||||
top: rippleTop + 'px', |
||||
left: rippleLeft + 'px', |
||||
width: fields.targetWidth + 'px', |
||||
height: fields.targetWidth + 'px', |
||||
'background-color': rippleBgColor || 'rgba(0, 0, 0, 0.15)' |
||||
}" |
||||
></view> |
||||
</button> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* button 按钮 |
||||
* @description Button 按钮 |
||||
* @tutorial https://www.uviewui.com/components/button.html |
||||
* @property {String} size 按钮的大小 |
||||
* @property {Boolean} ripple 是否开启点击水波纹效果 |
||||
* @property {String} ripple-bg-color 水波纹的背景色,ripple为true时有效 |
||||
* @property {String} type 按钮的样式类型 |
||||
* @property {Boolean} plain 按钮是否镂空,背景色透明 |
||||
* @property {Boolean} disabled 是否禁用 |
||||
* @property {Boolean} hair-line 是否显示按钮的细边框(默认true) |
||||
* @property {Boolean} shape 按钮外观形状,见文档说明 |
||||
* @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) |
||||
* @property {String} form-type 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 |
||||
* @property {String} open-type 开放能力 |
||||
* @property {String} data-name 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取 |
||||
* @property {String} hover-class 指定按钮按下去的样式类。当 hover-class="none" 时,没有点击态效果(App-nvue 平台暂不支持) |
||||
* @property {Number} hover-start-time 按住后多久出现点击态,单位毫秒 |
||||
* @property {Number} hover-stay-time 手指松开后点击态保留时间,单位毫秒 |
||||
* @property {Object} custom-style 对按钮的自定义样式,对象形式,见文档说明 |
||||
* @event {Function} click 按钮点击 |
||||
* @event {Function} getphonenumber open-type="getPhoneNumber"时有效 |
||||
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo |
||||
* @event {Function} error 当使用开放能力时,发生错误的回调 |
||||
* @event {Function} opensetting 在打开授权设置页并关闭后回调 |
||||
* @event {Function} launchapp 打开 APP 成功的回调 |
||||
* @example <u-button>月落</u-button> |
||||
*/ |
||||
export default { |
||||
name: 'u-button', |
||||
props: { |
||||
// 是否细边框 |
||||
hairLine: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 按钮的预置样式,default,primary,error,warning,success |
||||
type: { |
||||
type: String, |
||||
default: 'default' |
||||
}, |
||||
// 按钮尺寸,default,medium,mini |
||||
size: { |
||||
type: String, |
||||
default: 'default' |
||||
}, |
||||
// 按钮形状,circle(两边为半圆),square(带圆角) |
||||
shape: { |
||||
type: String, |
||||
default: 'square' |
||||
}, |
||||
// 按钮是否镂空 |
||||
plain: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 是否禁止状态 |
||||
disabled: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 是否加载中 |
||||
loading: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 开放能力,具体请看uniapp稳定关于button组件部分说明 |
||||
// https://uniapp.dcloud.io/component/button |
||||
openType: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 |
||||
// 取值为submit(提交表单),reset(重置表单) |
||||
formType: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 |
||||
// 只微信小程序、QQ小程序有效 |
||||
appParameter: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效 |
||||
hoverStopPropagation: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效 |
||||
lang: { |
||||
type: String, |
||||
default: 'en' |
||||
}, |
||||
// 会话来源,open-type="contact"时有效。只微信小程序有效 |
||||
sessionFrom: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 会话内消息卡片标题,open-type="contact"时有效 |
||||
// 默认当前标题,只微信小程序有效 |
||||
sendMessageTitle: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效 |
||||
// 默认当前分享路径,只微信小程序有效 |
||||
sendMessagePath: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 会话内消息卡片图片,open-type="contact"时有效 |
||||
// 默认当前页面截图,只微信小程序有效 |
||||
sendMessageImg: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示, |
||||
// 用户点击后可以快速发送小程序消息,open-type="contact"时有效 |
||||
showMessageCard: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 手指按(触摸)按钮时按钮时的背景颜色 |
||||
hoverBgColor: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 水波纹的背景颜色 |
||||
rippleBgColor: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 是否开启水波纹效果 |
||||
ripple: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 按下的类名 |
||||
hoverClass: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 自定义样式,对象形式 |
||||
customStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
} |
||||
}, |
||||
// 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取 |
||||
dataName: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 节流,一定时间内只能触发一次 |
||||
throttleTime: { |
||||
type: [String, Number], |
||||
default: 1000 |
||||
}, |
||||
// 按住后多久出现点击态,单位毫秒 |
||||
hoverStartTime: { |
||||
type: [String, Number], |
||||
default: 20 |
||||
}, |
||||
// 手指松开后点击态保留时间,单位毫秒 |
||||
hoverStayTime: { |
||||
type: [String, Number], |
||||
default: 150 |
||||
}, |
||||
}, |
||||
computed: { |
||||
// 当没有传bgColor变量时,按钮按下去的颜色类名 |
||||
getHoverClass() { |
||||
// 如果开启水波纹效果,则不启用hover-class效果 |
||||
if (this.loading || this.disabled || this.ripple || this.hoverClass) return ''; |
||||
let hoverClass = ''; |
||||
hoverClass = this.plain ? 'u-' + this.type + '-plain-hover' : 'u-' + this.type + '-hover'; |
||||
return hoverClass; |
||||
}, |
||||
// 在'primary', 'success', 'error', 'warning'类型下,不显示边框,否则会造成四角有毛刺现象 |
||||
showHairLineBorder() { |
||||
if (['primary', 'success', 'error', 'warning'].indexOf(this.type) >= 0 && !this.plain) { |
||||
return ''; |
||||
} else { |
||||
return 'u-hairline-border'; |
||||
} |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
rippleTop: 0, // 水波纹的起点Y坐标到按钮上边界的距离 |
||||
rippleLeft: 0, // 水波纹起点X坐标到按钮左边界的距离 |
||||
fields: {}, // 波纹按钮节点信息 |
||||
waveActive: false // 激活水波纹 |
||||
}; |
||||
}, |
||||
methods: { |
||||
// 按钮点击 |
||||
click(e) { |
||||
// 进行节流控制,每this.throttle毫秒内,只在开始处执行 |
||||
this.$u.throttle(() => { |
||||
// 如果按钮时disabled和loading状态,不触发水波纹效果 |
||||
if (this.loading === true || this.disabled === true) return; |
||||
// 是否开启水波纹效果 |
||||
if (this.ripple) { |
||||
// 每次点击时,移除上一次的类,再次添加,才能触发动画效果 |
||||
this.waveActive = false; |
||||
this.$nextTick(function() { |
||||
this.getWaveQuery(e); |
||||
}); |
||||
} |
||||
this.$emit('click', e); |
||||
}, this.throttleTime); |
||||
}, |
||||
// 查询按钮的节点信息 |
||||
getWaveQuery(e) { |
||||
this.getElQuery().then(res => { |
||||
// 查询返回的是一个数组节点 |
||||
let data = res[0]; |
||||
// 查询不到节点信息,不操作 |
||||
if (!data.width || !data.width) return; |
||||
// 水波纹的最终形态是一个正方形(通过border-radius让其变为一个圆形),这里要保证正方形的边长等于按钮的最长边 |
||||
// 最终的方形(变换后的圆形)才能覆盖整个按钮 |
||||
data.targetWidth = data.height > data.width ? data.height : data.width; |
||||
if (!data.targetWidth) return; |
||||
this.fields = data; |
||||
let touchesX = '', |
||||
touchesY = ''; |
||||
// #ifdef MP-BAIDU |
||||
touchesX = e.changedTouches[0].clientX; |
||||
touchesY = e.changedTouches[0].clientY; |
||||
// #endif |
||||
// #ifdef MP-ALIPAY |
||||
touchesX = e.detail.clientX; |
||||
touchesY = e.detail.clientY; |
||||
// #endif |
||||
// #ifndef MP-BAIDU || MP-ALIPAY |
||||
touchesX = e.touches[0].clientX; |
||||
touchesY = e.touches[0].clientY; |
||||
// #endif |
||||
// 获取触摸点相对于按钮上边和左边的x和y坐标,原理是通过屏幕的触摸点(touchesY),减去按钮的上边界data.top |
||||
// 但是由于`transform-origin`默认是center,所以这里再减去半径才是水波纹view应该的位置 |
||||
// 总的来说,就是把水波纹的矩形(变换后的圆形)的中心点,移动到我们的触摸点位置 |
||||
this.rippleTop = touchesY - data.top - data.targetWidth / 2; |
||||
this.rippleLeft = touchesX - data.left - data.targetWidth / 2; |
||||
this.$nextTick(() => { |
||||
this.waveActive = true; |
||||
}); |
||||
}); |
||||
}, |
||||
// 获取节点信息 |
||||
getElQuery() { |
||||
return new Promise(resolve => { |
||||
let queryInfo = ''; |
||||
// 获取元素节点信息,请查看uniapp相关文档 |
||||
// https://uniapp.dcloud.io/api/ui/nodes-info?id=nodesrefboundingclientrect |
||||
queryInfo = uni.createSelectorQuery().in(this); |
||||
//#ifdef MP-ALIPAY |
||||
queryInfo = uni.createSelectorQuery(); |
||||
//#endif |
||||
queryInfo.select('.u-btn').boundingClientRect(); |
||||
queryInfo.exec(data => { |
||||
resolve(data); |
||||
}); |
||||
}); |
||||
}, |
||||
// 下面为对接uniapp官方按钮开放能力事件回调的对接 |
||||
getphonenumber(res) { |
||||
this.$emit('getphonenumber', res); |
||||
}, |
||||
getuserinfo(res) { |
||||
this.$emit('getuserinfo', res); |
||||
}, |
||||
error(res) { |
||||
this.$emit('error', res); |
||||
}, |
||||
opensetting(res) { |
||||
this.$emit('opensetting', res); |
||||
}, |
||||
launchapp(res) { |
||||
this.$emit('launchapp', res); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
@import '../../libs/css/style.components.scss'; |
||||
.u-btn::after { |
||||
border: none; |
||||
} |
||||
|
||||
.u-btn { |
||||
position: relative; |
||||
border: 0; |
||||
//border-radius: 10rpx; |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
// 避免边框某些场景可能被“裁剪”,不能设置为hidden |
||||
overflow: visible; |
||||
line-height: 1; |
||||
@include vue-flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
cursor: pointer; |
||||
padding: 0 40rpx; |
||||
z-index: 1; |
||||
box-sizing: border-box; |
||||
transition: all 0.15s; |
||||
|
||||
&--bold-border { |
||||
border: 1px solid #ffffff; |
||||
} |
||||
|
||||
&--default { |
||||
color: $u-content-color; |
||||
border-color: #c0c4cc; |
||||
background-color: #ffffff; |
||||
} |
||||
|
||||
&--primary { |
||||
color: #ffffff; |
||||
border-color: $u-type-primary; |
||||
background-color: $u-type-primary; |
||||
} |
||||
|
||||
&--success { |
||||
color: #ffffff; |
||||
border-color: $u-type-success; |
||||
background-color: $u-type-success; |
||||
} |
||||
|
||||
&--error { |
||||
color: #ffffff; |
||||
border-color: $u-type-error; |
||||
background-color: $u-type-error; |
||||
} |
||||
|
||||
&--warning { |
||||
color: #ffffff; |
||||
border-color: $u-type-warning; |
||||
background-color: $u-type-warning; |
||||
} |
||||
|
||||
&--default--disabled { |
||||
color: #ffffff; |
||||
border-color: #e4e7ed; |
||||
background-color: #ffffff; |
||||
} |
||||
|
||||
&--primary--disabled { |
||||
color: #ffffff!important; |
||||
border-color: $u-type-primary-disabled!important; |
||||
background-color: $u-type-primary-disabled!important; |
||||
} |
||||
|
||||
&--success--disabled { |
||||
color: #ffffff!important; |
||||
border-color: $u-type-success-disabled!important; |
||||
background-color: $u-type-success-disabled!important; |
||||
} |
||||
|
||||
&--error--disabled { |
||||
color: #ffffff!important; |
||||
border-color: $u-type-error-disabled!important; |
||||
background-color: $u-type-error-disabled!important; |
||||
} |
||||
|
||||
&--warning--disabled { |
||||
color: #ffffff!important; |
||||
border-color: $u-type-warning-disabled!important; |
||||
background-color: $u-type-warning-disabled!important; |
||||
} |
||||
|
||||
&--primary--plain { |
||||
color: $u-type-primary!important; |
||||
border-color: $u-type-primary-disabled!important; |
||||
background-color: $u-type-primary-light!important; |
||||
} |
||||
|
||||
&--success--plain { |
||||
color: $u-type-success!important; |
||||
border-color: $u-type-success-disabled!important; |
||||
background-color: $u-type-success-light!important; |
||||
} |
||||
|
||||
&--error--plain { |
||||
color: $u-type-error!important; |
||||
border-color: $u-type-error-disabled!important; |
||||
background-color: $u-type-error-light!important; |
||||
} |
||||
|
||||
&--warning--plain { |
||||
color: $u-type-warning!important; |
||||
border-color: $u-type-warning-disabled!important; |
||||
background-color: $u-type-warning-light!important; |
||||
} |
||||
} |
||||
|
||||
.u-hairline-border:after { |
||||
content: ' '; |
||||
position: absolute; |
||||
pointer-events: none; |
||||
// 设置为border-box,意味着下面的scale缩小为0.5,实际上缩小的是伪元素的内容(border-box意味着内容不含border) |
||||
box-sizing: border-box; |
||||
// 中心点作为变形(scale())的原点 |
||||
-webkit-transform-origin: 0 0; |
||||
transform-origin: 0 0; |
||||
left: 0; |
||||
top: 0; |
||||
width: 199.8%; |
||||
height: 199.7%; |
||||
-webkit-transform: scale(0.5, 0.5); |
||||
transform: scale(0.5, 0.5); |
||||
border: 1px solid currentColor; |
||||
z-index: 1; |
||||
} |
||||
|
||||
.u-wave-ripple { |
||||
z-index: 0; |
||||
position: absolute; |
||||
border-radius: 100%; |
||||
background-clip: padding-box; |
||||
pointer-events: none; |
||||
user-select: none; |
||||
transform: scale(0); |
||||
opacity: 1; |
||||
transform-origin: center; |
||||
} |
||||
|
||||
.u-wave-ripple.u-wave-active { |
||||
opacity: 0; |
||||
transform: scale(2); |
||||
transition: opacity 1s linear, transform 0.4s linear; |
||||
} |
||||
|
||||
.u-round-circle { |
||||
border-radius: 100rpx; |
||||
} |
||||
|
||||
.u-round-circle::after { |
||||
border-radius: 100rpx; |
||||
} |
||||
|
||||
.u-loading::after { |
||||
background-color: hsla(0, 0%, 100%, 0.35); |
||||
} |
||||
|
||||
.u-size-default { |
||||
font-size: 30rpx; |
||||
height: 80rpx; |
||||
line-height: 80rpx; |
||||
} |
||||
|
||||
.u-size-medium { |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
width: auto; |
||||
font-size: 26rpx; |
||||
height: 70rpx; |
||||
line-height: 70rpx; |
||||
padding: 0 80rpx; |
||||
} |
||||
|
||||
.u-size-mini { |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
width: auto; |
||||
font-size: 22rpx; |
||||
padding-top: 1px; |
||||
height: 50rpx; |
||||
line-height: 50rpx; |
||||
padding: 0 20rpx; |
||||
} |
||||
|
||||
.u-primary-plain-hover { |
||||
color: #ffffff !important; |
||||
background: $u-type-primary-dark !important; |
||||
} |
||||
|
||||
.u-default-plain-hover { |
||||
color: $u-type-primary-dark !important; |
||||
background: $u-type-primary-light !important; |
||||
} |
||||
|
||||
.u-success-plain-hover { |
||||
color: #ffffff !important; |
||||
background: $u-type-success-dark !important; |
||||
} |
||||
|
||||
.u-warning-plain-hover { |
||||
color: #ffffff !important; |
||||
background: $u-type-warning-dark !important; |
||||
} |
||||
|
||||
.u-error-plain-hover { |
||||
color: #ffffff !important; |
||||
background: $u-type-error-dark !important; |
||||
} |
||||
|
||||
.u-info-plain-hover { |
||||
color: #ffffff !important; |
||||
background: $u-type-info-dark !important; |
||||
} |
||||
|
||||
.u-default-hover { |
||||
color: $u-type-primary-dark !important; |
||||
border-color: $u-type-primary-dark !important; |
||||
background-color: $u-type-primary-light !important; |
||||
} |
||||
|
||||
.u-primary-hover { |
||||
background: $u-type-primary-dark !important; |
||||
color: #fff; |
||||
} |
||||
|
||||
.u-success-hover { |
||||
background: $u-type-success-dark !important; |
||||
color: #fff; |
||||
} |
||||
|
||||
.u-info-hover { |
||||
background: $u-type-info-dark !important; |
||||
color: #fff; |
||||
} |
||||
|
||||
.u-warning-hover { |
||||
background: $u-type-warning-dark !important; |
||||
color: #fff; |
||||
} |
||||
|
||||
.u-error-hover { |
||||
background: $u-type-error-dark !important; |
||||
color: #fff; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,643 @@ |
||||
<template> |
||||
<u-popup closeable :maskCloseAble="maskCloseAble" mode="bottom" :popup="false" v-model="value" length="auto" |
||||
:safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex" :border-radius="borderRadius" :closeable="closeable"> |
||||
<view class="u-calendar"> |
||||
<view class="u-calendar__header"> |
||||
<view class="u-calendar__header__text" v-if="!$slots['tooltip']"> |
||||
{{toolTip}} |
||||
</view> |
||||
<slot v-else name="tooltip" /> |
||||
</view> |
||||
<view class="u-calendar__action u-flex u-row-center"> |
||||
<view class="u-calendar__action__icon"> |
||||
<u-icon v-if="changeYear" name="arrow-left-double" :color="yearArrowColor" @click="changeYearHandler(0)"></u-icon> |
||||
</view> |
||||
<view class="u-calendar__action__icon"> |
||||
<u-icon v-if="changeMonth" name="arrow-left" :color="monthArrowColor" @click="changeMonthHandler(0)"></u-icon> |
||||
</view> |
||||
<view class="u-calendar__action__text">{{ showTitle }}</view> |
||||
<view class="u-calendar__action__icon"> |
||||
<u-icon v-if="changeMonth" name="arrow-right" :color="monthArrowColor" @click="changeMonthHandler(1)"></u-icon> |
||||
</view> |
||||
<view class="u-calendar__action__icon"> |
||||
<u-icon v-if="changeYear" name="arrow-right-double" :color="yearArrowColor" @click="changeYearHandler(1)"></u-icon> |
||||
</view> |
||||
</view> |
||||
<view class="u-calendar__week-day"> |
||||
<view class="u-calendar__week-day__text" v-for="(item, index) in weekDayZh" :key="index">{{item}}</view> |
||||
</view> |
||||
<view class="u-calendar__content"> |
||||
<!-- 前置空白部分 --> |
||||
<block v-for="(item, index) in weekdayArr" :key="index"> |
||||
<view class="u-calendar__content__item"></view> |
||||
</block> |
||||
<view class="u-calendar__content__item" :class="{ |
||||
'u-hover-class':openDisAbled(year,month,index+1), |
||||
'u-calendar__content--start-date': (mode == 'range' && startDate==`${year}-${month}-${index+1}`) || mode== 'date', |
||||
'u-calendar__content--end-date':(mode== 'range' && endDate==`${year}-${month}-${index+1}`) || mode == 'date' |
||||
}" :style="{backgroundColor: getColor(index,1)}" v-for="(item, index) in daysArr" :key="index" |
||||
@tap="dateClick(index)"> |
||||
<view class="u-calendar__content__item__inner" :style="{color: getColor(index,2)}"> |
||||
<view>{{ index + 1 }}</view> |
||||
</view> |
||||
<view class="u-calendar__content__item__tips" :style="{color:activeColor}" v-if="mode== 'range' && startDate==`${year}-${month}-${index+1}` && startDate!=endDate">{{startText}}</view> |
||||
<view class="u-calendar__content__item__tips" :style="{color:activeColor}" v-if="mode== 'range' && endDate==`${year}-${month}-${index+1}`">{{endText}}</view> |
||||
</view> |
||||
<view class="u-calendar__content__bg-month">{{month}}</view> |
||||
</view> |
||||
<view class="u-calendar__bottom"> |
||||
<view class="u-calendar__bottom__choose"> |
||||
<text>{{mode == 'date' ? activeDate : startDate}}</text> |
||||
<text v-if="endDate">至{{endDate}}</text> |
||||
</view> |
||||
<view class="u-calendar__bottom__btn"> |
||||
<u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)">确定</u-button> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</u-popup> |
||||
</template> |
||||
<script> |
||||
/** |
||||
* calendar 日历 |
||||
* @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中。 |
||||
* @tutorial http://uviewui.com/components/calendar.html |
||||
* @property {String} mode 选择日期的模式,date-为单个日期,range-为选择日期范围 |
||||
* @property {Boolean} v-model 布尔值变量,用于控制日历的弹出与收起 |
||||
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false) |
||||
* @property {Boolean} change-year 是否显示顶部的切换年份方向的按钮(默认true) |
||||
* @property {Boolean} change-month 是否显示顶部的切换月份方向的按钮(默认true) |
||||
* @property {String Number} max-year 可切换的最大年份(默认2050) |
||||
* @property {String Number} min-year 可切换的最小年份(默认1950) |
||||
* @property {String Number} min-date 最小可选日期(默认1950-01-01) |
||||
* @property {String Number} max-date 最大可选日期(默认当前日期) |
||||
* @property {String Number} 弹窗顶部左右两边的圆角值,单位rpx(默认20) |
||||
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭日历(默认true) |
||||
* @property {String} month-arrow-color 月份切换按钮箭头颜色(默认#606266) |
||||
* @property {String} year-arrow-color 年份切换按钮箭头颜色(默认#909399) |
||||
* @property {String} color 日期字体的默认颜色(默认#303133) |
||||
* @property {String} active-bg-color 起始/结束日期按钮的背景色(默认#2979ff) |
||||
* @property {String Number} z-index 弹出时的z-index值(默认10075) |
||||
* @property {String} active-color 起始/结束日期按钮的字体颜色(默认#ffffff) |
||||
* @property {String} range-bg-color 起始/结束日期之间的区域的背景颜色(默认rgba(41,121,255,0.13)) |
||||
* @property {String} range-color 选择范围内字体颜色(默认#2979ff) |
||||
* @property {String} start-text 起始日期底部的提示文字(默认 '开始') |
||||
* @property {String} end-text 结束日期底部的提示文字(默认 '结束') |
||||
* @property {String} btn-type 底部确定按钮的主题(默认 'primary') |
||||
* @property {String} toolTip 顶部提示文字,如设置名为tooltip的slot,此参数将失效(默认 '选择日期') |
||||
* @property {Boolean} closeable 是否显示右上角的关闭图标(默认true) |
||||
* @example <u-calendar v-model="show" :mode="mode"></u-calendar> |
||||
*/ |
||||
|
||||
export default { |
||||
name: 'u-calendar', |
||||
props: { |
||||
safeAreaInsetBottom: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 是否允许通过点击遮罩关闭Picker |
||||
maskCloseAble: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 通过双向绑定控制组件的弹出与收起 |
||||
value: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 弹出的z-index值 |
||||
zIndex: { |
||||
type: [String, Number], |
||||
default: 0 |
||||
}, |
||||
// 是否允许切换年份 |
||||
changeYear: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 是否允许切换月份 |
||||
changeMonth: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// date-单个日期选择,range-开始日期+结束日期选择 |
||||
mode: { |
||||
type: String, |
||||
default: 'date' |
||||
}, |
||||
// 可切换的最大年份 |
||||
maxYear: { |
||||
type: [Number, String], |
||||
default: 2050 |
||||
}, |
||||
// 可切换的最小年份 |
||||
minYear: { |
||||
type: [Number, String], |
||||
default: 1950 |
||||
}, |
||||
// 最小可选日期(不在范围内日期禁用不可选) |
||||
minDate: { |
||||
type: [Number, String], |
||||
default: '1950-01-01' |
||||
}, |
||||
/** |
||||
* 最大可选日期 |
||||
* 默认最大值为今天,之后的日期不可选 |
||||
* 2030-12-31 |
||||
* */ |
||||
maxDate: { |
||||
type: [Number, String], |
||||
default: '' |
||||
}, |
||||
// 弹窗顶部左右两边的圆角值 |
||||
borderRadius: { |
||||
type: [String, Number], |
||||
default: 20 |
||||
}, |
||||
// 月份切换按钮箭头颜色 |
||||
monthArrowColor: { |
||||
type: String, |
||||
default: '#606266' |
||||
}, |
||||
// 年份切换按钮箭头颜色 |
||||
yearArrowColor: { |
||||
type: String, |
||||
default: '#909399' |
||||
}, |
||||
// 默认日期字体颜色 |
||||
color: { |
||||
type: String, |
||||
default: '#303133' |
||||
}, |
||||
// 选中|起始结束日期背景色 |
||||
activeBgColor: { |
||||
type: String, |
||||
default: '#2979ff' |
||||
}, |
||||
// 选中|起始结束日期字体颜色 |
||||
activeColor: { |
||||
type: String, |
||||
default: '#ffffff' |
||||
}, |
||||
// 范围内日期背景色 |
||||
rangeBgColor: { |
||||
type: String, |
||||
default: 'rgba(41,121,255,0.13)' |
||||
}, |
||||
// 范围内日期字体颜色 |
||||
rangeColor: { |
||||
type: String, |
||||
default: '#2979ff' |
||||
}, |
||||
// mode=range时生效,起始日期自定义文案 |
||||
startText: { |
||||
type: String, |
||||
default: '开始' |
||||
}, |
||||
// mode=range时生效,结束日期自定义文案 |
||||
endText: { |
||||
type: String, |
||||
default: '结束' |
||||
}, |
||||
//按钮样式类型 |
||||
btnType: { |
||||
type: String, |
||||
default: 'primary' |
||||
}, |
||||
// 当前选中日期带选中效果 |
||||
isActiveCurrent: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 切换年月是否触发事件 mode=date时生效 |
||||
isChange: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 是否显示右上角的关闭图标 |
||||
closeable: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 顶部的提示文字 |
||||
toolTip: { |
||||
type: String, |
||||
default: '选择日期' |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
// 星期几,值为1-7 |
||||
weekday: 1, |
||||
weekdayArr:[], |
||||
// 当前月有多少天 |
||||
days: 0, |
||||
daysArr:[], |
||||
showTitle: '', |
||||
year: 2020, |
||||
month: 0, |
||||
day: 0, |
||||
startYear: 0, |
||||
startMonth: 0, |
||||
startDay: 0, |
||||
endYear: 0, |
||||
endMonth: 0, |
||||
endDay: 0, |
||||
today: '', |
||||
activeDate: '', |
||||
startDate: '', |
||||
endDate: '', |
||||
isStart: true, |
||||
min: null, |
||||
max: null, |
||||
weekDayZh: ['日', '一', '二', '三', '四', '五', '六'] |
||||
}; |
||||
}, |
||||
computed: { |
||||
dataChange() { |
||||
return `${this.mode}-${this.minDate}-${this.maxDate}`; |
||||
}, |
||||
uZIndex() { |
||||
// 如果用户有传递z-index值,优先使用 |
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup; |
||||
} |
||||
}, |
||||
watch: { |
||||
dataChange(val) { |
||||
this.init() |
||||
} |
||||
}, |
||||
created() { |
||||
this.init() |
||||
}, |
||||
methods: { |
||||
getColor(index, type) { |
||||
let color = type == 1 ? '' : this.color; |
||||
let day = index + 1 |
||||
let date = `${this.year}-${this.month}-${day}` |
||||
let timestamp = new Date(date.replace(/\-/g, '/')).getTime(); |
||||
let start = this.startDate.replace(/\-/g, '/') |
||||
let end = this.endDate.replace(/\-/g, '/') |
||||
if ((this.isActiveCurrent && this.activeDate == date) || this.startDate == date || this.endDate == date) { |
||||
color = type == 1 ? this.activeBgColor : this.activeColor; |
||||
} else if (this.endDate && timestamp > new Date(start).getTime() && timestamp < new Date(end).getTime()) { |
||||
color = type == 1 ? this.rangeBgColor : this.rangeColor; |
||||
} |
||||
return color; |
||||
}, |
||||
init() { |
||||
let now = new Date(); |
||||
let minDate = new Date(this.minDate); |
||||
let maxDate = new Date(this.maxDate); |
||||
if (now < minDate) now = minDate; |
||||
if (now > maxDate) now = maxDate; |
||||
this.year = now.getFullYear(); |
||||
this.month = now.getMonth() + 1; |
||||
this.day = now.getDate(); |
||||
this.today = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`; |
||||
this.activeDate = this.today; |
||||
this.min = this.initDate(this.minDate); |
||||
this.max = this.initDate(this.maxDate || this.today); |
||||
this.startDate = ""; |
||||
this.startYear = 0; |
||||
this.startMonth = 0; |
||||
this.startDay = 0; |
||||
this.endYear = 0; |
||||
this.endMonth = 0; |
||||
this.endDay = 0; |
||||
this.endDate = ""; |
||||
this.isStart = true; |
||||
this.changeData(); |
||||
}, |
||||
//日期处理 |
||||
initDate(date) { |
||||
let fdate = date.split('-'); |
||||
return { |
||||
year: Number(fdate[0] || 1920), |
||||
month: Number(fdate[1] || 1), |
||||
day: Number(fdate[2] || 1) |
||||
} |
||||
}, |
||||
openDisAbled: function(year, month, day) { |
||||
let bool = true; |
||||
let date = `${year}/${month}/${day}`; |
||||
// let today = this.today.replace(/\-/g, '/'); |
||||
let min = `${this.min.year}/${this.min.month}/${this.min.day}`; |
||||
let max = `${this.max.year}/${this.max.month}/${this.max.day}`; |
||||
let timestamp = new Date(date).getTime(); |
||||
if (timestamp >= new Date(min).getTime() && timestamp <= new Date(max).getTime()) { |
||||
bool = false; |
||||
} |
||||
return bool; |
||||
}, |
||||
generateArray: function(start, end) { |
||||
return Array.from(new Array(end + 1).keys()).slice(start); |
||||
}, |
||||
formatNum: function(num) { |
||||
return num < 10 ? '0' + num : num + ''; |
||||
}, |
||||
//一个月有多少天 |
||||
getMonthDay(year, month) { |
||||
let days = new Date(year, month, 0).getDate(); |
||||
return days; |
||||
}, |
||||
getWeekday(year, month) { |
||||
let date = new Date(`${year}/${month}/01 00:00:00`); |
||||
return date.getDay(); |
||||
}, |
||||
checkRange(year) { |
||||
let overstep = false; |
||||
if (year < this.minYear || year > this.maxYear) { |
||||
uni.showToast({ |
||||
title: "日期超出范围啦~", |
||||
icon: 'none' |
||||
}) |
||||
overstep = true; |
||||
} |
||||
return overstep; |
||||
}, |
||||
changeMonthHandler(isAdd) { |
||||
if (isAdd) { |
||||
let month = this.month + 1; |
||||
let year = month > 12 ? this.year + 1 : this.year; |
||||
if (!this.checkRange(year)) { |
||||
this.month = month > 12 ? 1 : month; |
||||
this.year = year; |
||||
this.changeData(); |
||||
} |
||||
|
||||
} else { |
||||
let month = this.month - 1; |
||||
let year = month < 1 ? this.year - 1 : this.year; |
||||
if (!this.checkRange(year)) { |
||||
this.month = month < 1 ? 12 : month; |
||||
this.year = year; |
||||
this.changeData(); |
||||
} |
||||
} |
||||
}, |
||||
changeYearHandler(isAdd) { |
||||
let year = isAdd ? this.year + 1 : this.year - 1; |
||||
if (!this.checkRange(year)) { |
||||
this.year = year; |
||||
this.changeData(); |
||||
} |
||||
}, |
||||
changeData() { |
||||
this.days = this.getMonthDay(this.year, this.month); |
||||
this.daysArr=this.generateArray(1,this.days) |
||||
this.weekday = this.getWeekday(this.year, this.month); |
||||
this.weekdayArr=this.generateArray(1,this.weekday) |
||||
this.showTitle = `${this.year}年${this.month}月`; |
||||
if (this.isChange && this.mode == 'date') { |
||||
this.btnFix(true); |
||||
} |
||||
}, |
||||
dateClick: function(day) { |
||||
day += 1; |
||||
if (!this.openDisAbled(this.year, this.month, day)) { |
||||
this.day = day; |
||||
let date = `${this.year}-${this.month}-${day}`; |
||||
if (this.mode == 'date') { |
||||
this.activeDate = date; |
||||
} else { |
||||
let compare = new Date(date.replace(/\-/g, '/')).getTime() < new Date(this.startDate.replace(/\-/g, '/')).getTime() |
||||
if (this.isStart || compare) { |
||||
this.startDate = date; |
||||
this.startYear = this.year; |
||||
this.startMonth = this.month; |
||||
this.startDay = this.day; |
||||
this.endYear = 0; |
||||
this.endMonth = 0; |
||||
this.endDay = 0; |
||||
this.endDate = ""; |
||||
this.activeDate = ""; |
||||
this.isStart = false; |
||||
} else { |
||||
this.endDate = date; |
||||
this.endYear = this.year; |
||||
this.endMonth = this.month; |
||||
this.endDay = this.day; |
||||
this.isStart = true; |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
close() { |
||||
// 修改通过v-model绑定的父组件变量的值为false,从而隐藏日历弹窗 |
||||
this.$emit('input', false); |
||||
}, |
||||
getWeekText(date) { |
||||
date = new Date(`${date.replace(/\-/g, '/')} 00:00:00`); |
||||
let week = date.getDay(); |
||||
return '星期' + ['日', '一', '二', '三', '四', '五', '六'][week]; |
||||
}, |
||||
btnFix(show) { |
||||
if (!show) { |
||||
this.close(); |
||||
} |
||||
if (this.mode == 'date') { |
||||
let arr = this.activeDate.split('-') |
||||
let year = this.isChange ? this.year : Number(arr[0]); |
||||
let month = this.isChange ? this.month : Number(arr[1]); |
||||
let day = this.isChange ? this.day : Number(arr[2]); |
||||
//当前月有多少天 |
||||
let days = this.getMonthDay(year, month); |
||||
let result = `${year}-${this.formatNum(month)}-${this.formatNum(day)}`; |
||||
let weekText = this.getWeekText(result); |
||||
let isToday = false; |
||||
if (`${year}-${month}-${day}` == this.today) { |
||||
//今天 |
||||
isToday = true; |
||||
} |
||||
this.$emit('change', { |
||||
year: year, |
||||
month: month, |
||||
day: day, |
||||
days: days, |
||||
result: result, |
||||
week: weekText, |
||||
isToday: isToday, |
||||
// switch: show //是否是切换年月操作 |
||||
}); |
||||
} else { |
||||
if (!this.startDate || !this.endDate) return; |
||||
let startMonth = this.formatNum(this.startMonth); |
||||
let startDay = this.formatNum(this.startDay); |
||||
let startDate = `${this.startYear}-${startMonth}-${startDay}`; |
||||
let startWeek = this.getWeekText(startDate) |
||||
|
||||
let endMonth = this.formatNum(this.endMonth); |
||||
let endDay = this.formatNum(this.endDay); |
||||
let endDate = `${this.endYear}-${endMonth}-${endDay}`; |
||||
let endWeek = this.getWeekText(endDate); |
||||
this.$emit('change', { |
||||
startYear: this.startYear, |
||||
startMonth: this.startMonth, |
||||
startDay: this.startDay, |
||||
startDate: startDate, |
||||
startWeek: startWeek, |
||||
endYear: this.endYear, |
||||
endMonth: this.endMonth, |
||||
endDay: this.endDay, |
||||
endDate: endDate, |
||||
endWeek: endWeek |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-calendar { |
||||
color: $u-content-color; |
||||
|
||||
&__header { |
||||
width: 100%; |
||||
box-sizing: border-box; |
||||
font-size: 30rpx; |
||||
background-color: #fff; |
||||
color: $u-main-color; |
||||
|
||||
&__text { |
||||
margin-top: 30rpx; |
||||
padding: 0 60rpx; |
||||
@include vue-flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
} |
||||
} |
||||
|
||||
&__action { |
||||
padding: 40rpx 0 40rpx 0; |
||||
|
||||
&__icon { |
||||
margin: 0 16rpx; |
||||
} |
||||
|
||||
&__text { |
||||
padding: 0 16rpx; |
||||
color: $u-main-color; |
||||
font-size: 32rpx; |
||||
line-height: 32rpx; |
||||
font-weight: bold; |
||||
} |
||||
} |
||||
|
||||
&__week-day { |
||||
@include vue-flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
padding: 6px 0; |
||||
overflow: hidden; |
||||
|
||||
&__text { |
||||
flex: 1; |
||||
text-align: center; |
||||
} |
||||
} |
||||
|
||||
&__content { |
||||
width: 100%; |
||||
@include vue-flex; |
||||
flex-wrap: wrap; |
||||
padding: 6px 0; |
||||
box-sizing: border-box; |
||||
background-color: #fff; |
||||
position: relative; |
||||
|
||||
&--end-date { |
||||
border-top-right-radius: 8rpx; |
||||
border-bottom-right-radius: 8rpx; |
||||
} |
||||
|
||||
&--start-date { |
||||
border-top-left-radius: 8rpx; |
||||
border-bottom-left-radius: 8rpx; |
||||
} |
||||
|
||||
&__item { |
||||
width: 14.2857%; |
||||
@include vue-flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
padding: 6px 0; |
||||
overflow: hidden; |
||||
position: relative; |
||||
z-index: 2; |
||||
|
||||
&__inner { |
||||
height: 84rpx; |
||||
@include vue-flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
flex-direction: column; |
||||
font-size: 32rpx; |
||||
position: relative; |
||||
border-radius: 50%; |
||||
|
||||
&__desc { |
||||
width: 100%; |
||||
font-size: 24rpx; |
||||
line-height: 24rpx; |
||||
transform: scale(0.75); |
||||
transform-origin: center center; |
||||
position: absolute; |
||||
left: 0; |
||||
text-align: center; |
||||
bottom: 2rpx; |
||||
} |
||||
} |
||||
|
||||
&__tips { |
||||
width: 100%; |
||||
font-size: 24rpx; |
||||
line-height: 24rpx; |
||||
position: absolute; |
||||
left: 0; |
||||
transform: scale(0.8); |
||||
transform-origin: center center; |
||||
text-align: center; |
||||
bottom: 8rpx; |
||||
z-index: 2; |
||||
} |
||||
} |
||||
|
||||
&__bg-month { |
||||
position: absolute; |
||||
font-size: 130px; |
||||
line-height: 130px; |
||||
left: 50%; |
||||
top: 50%; |
||||
transform: translate(-50%, -50%); |
||||
color: #e4e7ed; |
||||
z-index: 1; |
||||
} |
||||
} |
||||
|
||||
&__bottom { |
||||
width: 100%; |
||||
@include vue-flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
flex-direction: column; |
||||
background-color: #fff; |
||||
padding: 0 40rpx 30rpx; |
||||
box-sizing: border-box; |
||||
font-size: 24rpx; |
||||
color: $u-tips-color; |
||||
|
||||
&__choose { |
||||
height: 50rpx; |
||||
} |
||||
|
||||
&__btn { |
||||
width: 100%; |
||||
} |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,257 @@ |
||||
<template> |
||||
<view class="u-keyboard" @touchmove.stop.prevent="() => {}"> |
||||
<view class="u-keyboard-grids"> |
||||
<block> |
||||
<view class="u-keyboard-grids-item" v-for="(group, i) in abc ? EngKeyBoardList : areaList" :key="i"> |
||||
<view :hover-stay-time="100" @tap="carInputClick(i, j)" hover-class="u-carinput-hover" class="u-keyboard-grids-btn" |
||||
v-for="(item, j) in group" :key="j"> |
||||
{{ item }} |
||||
</view> |
||||
</view> |
||||
<view @touchstart="backspaceClick" @touchend="clearTimer" :hover-stay-time="100" class="u-keyboard-back" |
||||
hover-class="u-hover-class"> |
||||
<u-icon :size="38" name="backspace" :bold="true"></u-icon> |
||||
</view> |
||||
<view :hover-stay-time="100" class="u-keyboard-change" hover-class="u-carinput-hover" @tap="changeCarInputMode"> |
||||
<text class="zh" :class="[!abc ? 'active' : 'inactive']">中</text> |
||||
/ |
||||
<text class="en" :class="[abc ? 'active' : 'inactive']">英</text> |
||||
</view> |
||||
</block> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: "u-keyboard", |
||||
props: { |
||||
// 是否打乱键盘按键的顺序 |
||||
random: { |
||||
type: Boolean, |
||||
default: false |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
// 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称 |
||||
abc: false |
||||
}; |
||||
}, |
||||
computed: { |
||||
areaList() { |
||||
let data = [ |
||||
'京', |
||||
'沪', |
||||
'粤', |
||||
'津', |
||||
'冀', |
||||
'豫', |
||||
'云', |
||||
'辽', |
||||
'黑', |
||||
'湘', |
||||
'皖', |
||||
'鲁', |
||||
'苏', |
||||
'浙', |
||||
'赣', |
||||
'鄂', |
||||
'桂', |
||||
'甘', |
||||
'晋', |
||||
'陕', |
||||
'蒙', |
||||
'吉', |
||||
'闽', |
||||
'贵', |
||||
'渝', |
||||
'川', |
||||
'青', |
||||
'琼', |
||||
'宁', |
||||
'挂', |
||||
'藏', |
||||
'港', |
||||
'澳', |
||||
'新', |
||||
'使', |
||||
'学' |
||||
]; |
||||
let tmp = []; |
||||
// 打乱顺序 |
||||
if (this.random) data = this.$u.randomArray(data); |
||||
// 切割成二维数组 |
||||
tmp[0] = data.slice(0, 10); |
||||
tmp[1] = data.slice(10, 20); |
||||
tmp[2] = data.slice(20, 30); |
||||
tmp[3] = data.slice(30, 36); |
||||
return tmp; |
||||
}, |
||||
EngKeyBoardList() { |
||||
let data = [ |
||||
1, |
||||
2, |
||||
3, |
||||
4, |
||||
5, |
||||
6, |
||||
7, |
||||
8, |
||||
9, |
||||
0, |
||||
'Q', |
||||
'W', |
||||
'E', |
||||
'R', |
||||
'T', |
||||
'Y', |
||||
'U', |
||||
'I', |
||||
'O', |
||||
'P', |
||||
'A', |
||||
'S', |
||||
'D', |
||||
'F', |
||||
'G', |
||||
'H', |
||||
'J', |
||||
'K', |
||||
'L', |
||||
'Z', |
||||
'X', |
||||
'C', |
||||
'V', |
||||
'B', |
||||
'N', |
||||
'M' |
||||
]; |
||||
let tmp = []; |
||||
if (this.random) data = this.$u.randomArray(data); |
||||
tmp[0] = data.slice(0, 10); |
||||
tmp[1] = data.slice(10, 20); |
||||
tmp[2] = data.slice(20, 30); |
||||
tmp[3] = data.slice(30, 36); |
||||
return tmp; |
||||
} |
||||
}, |
||||
methods: { |
||||
// 点击键盘按钮 |
||||
carInputClick(i, j) { |
||||
let value = ''; |
||||
// 不同模式,获取不同数组的值 |
||||
if (this.abc) value = this.EngKeyBoardList[i][j]; |
||||
else value = this.areaList[i][j]; |
||||
this.$emit('change', value); |
||||
}, |
||||
// 修改汽车牌键盘的输入模式,中文|英文 |
||||
changeCarInputMode() { |
||||
this.abc = !this.abc; |
||||
}, |
||||
// 点击退格键 |
||||
backspaceClick() { |
||||
this.$emit('backspace'); |
||||
clearInterval(this.timer); //再次清空定时器,防止重复注册定时器 |
||||
this.timer = null; |
||||
this.timer = setInterval(() => { |
||||
this.$emit('backspace'); |
||||
}, 250); |
||||
}, |
||||
clearTimer() { |
||||
clearInterval(this.timer); |
||||
this.timer = null; |
||||
}, |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-keyboard-grids { |
||||
background: rgb(215, 215, 217); |
||||
padding: 24rpx 0; |
||||
position: relative; |
||||
} |
||||
|
||||
.u-keyboard-grids-item { |
||||
@include vue-flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.u-keyboard-grids-btn { |
||||
text-decoration: none; |
||||
width: 62rpx; |
||||
flex: 0 0 64rpx; |
||||
height: 80rpx; |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
font-size: 36rpx; |
||||
text-align: center; |
||||
line-height: 80rpx; |
||||
background-color: #fff; |
||||
margin: 8rpx 5rpx; |
||||
border-radius: 8rpx; |
||||
box-shadow: 0 2rpx 0rpx #888992; |
||||
font-weight: 500; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.u-carinput-hover { |
||||
background-color: rgb(185, 188, 195) !important; |
||||
} |
||||
|
||||
.u-keyboard-back { |
||||
position: absolute; |
||||
width: 96rpx; |
||||
right: 22rpx; |
||||
bottom: 32rpx; |
||||
height: 80rpx; |
||||
background-color: rgb(185, 188, 195); |
||||
@include vue-flex; |
||||
align-items: center; |
||||
border-radius: 8rpx; |
||||
justify-content: center; |
||||
box-shadow: 0 2rpx 0rpx #888992; |
||||
} |
||||
|
||||
.u-keyboard-change { |
||||
font-size: 24rpx; |
||||
box-shadow: 0 2rpx 0rpx #888992; |
||||
position: absolute; |
||||
width: 96rpx; |
||||
left: 22rpx; |
||||
line-height: 1; |
||||
bottom: 32rpx; |
||||
height: 80rpx; |
||||
background-color: #ffffff; |
||||
@include vue-flex; |
||||
align-items: center; |
||||
border-radius: 8rpx; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.u-keyboard-change .inactive.zh { |
||||
transform: scale(0.85) translateY(-10rpx); |
||||
} |
||||
|
||||
.u-keyboard-change .inactive.en { |
||||
transform: scale(0.85) translateY(10rpx); |
||||
} |
||||
|
||||
.u-keyboard-change .active { |
||||
color: rgb(237, 112, 64); |
||||
font-size: 30rpx; |
||||
} |
||||
|
||||
.u-keyboard-change .zh { |
||||
transform: translateY(-10rpx); |
||||
} |
||||
|
||||
.u-keyboard-change .en { |
||||
transform: translateY(10rpx); |
||||
} |
||||
</style> |
||||
@ -0,0 +1,299 @@ |
||||
<template> |
||||
<view |
||||
class="u-card" |
||||
@tap.stop="click" |
||||
:class="{ 'u-border': border, 'u-card-full': full, 'u-card--border': borderRadius > 0 }" |
||||
:style="{ |
||||
borderRadius: borderRadius + 'rpx', |
||||
margin: margin, |
||||
boxShadow: boxShadow |
||||
}" |
||||
> |
||||
<view |
||||
v-if="showHead" |
||||
class="u-card__head" |
||||
:style="[{padding: padding + 'rpx'}, headStyle]" |
||||
:class="{ |
||||
'u-border-bottom': headBorderBottom |
||||
}" |
||||
@tap="headClick" |
||||
> |
||||
<view v-if="!$slots.head" class="u-flex u-row-between"> |
||||
<view class="u-card__head--left u-flex u-line-1" v-if="title"> |
||||
<image |
||||
:src="thumb" |
||||
class="u-card__head--left__thumb" |
||||
mode="aspectFill" |
||||
v-if="thumb" |
||||
:style="{ |
||||
height: thumbWidth + 'rpx', |
||||
width: thumbWidth + 'rpx', |
||||
borderRadius: thumbCircle ? '100rpx' : '6rpx' |
||||
}" |
||||
></image> |
||||
<text |
||||
class="u-card__head--left__title u-line-1" |
||||
:style="{ |
||||
fontSize: titleSize + 'rpx', |
||||
color: titleColor |
||||
}" |
||||
> |
||||
{{ title }} |
||||
</text> |
||||
</view> |
||||
<view class="u-card__head--right u-line-1" v-if="subTitle"> |
||||
<text |
||||
class="u-card__head__title__text" |
||||
:style="{ |
||||
fontSize: subTitleSize + 'rpx', |
||||
color: subTitleColor |
||||
}" |
||||
> |
||||
{{ subTitle }} |
||||
</text> |
||||
</view> |
||||
</view> |
||||
<slot name="head" v-else /> |
||||
</view> |
||||
<view @tap="bodyClick" class="u-card__body" :style="[{padding: padding + 'rpx'}, bodyStyle]"><slot name="body" /></view> |
||||
<view |
||||
v-if="showFoot" |
||||
class="u-card__foot" |
||||
@tap="footClick" |
||||
:style="[{padding: $slots.foot ? padding + 'rpx' : 0}, footStyle]" |
||||
:class="{ |
||||
'u-border-top': footBorderTop |
||||
}" |
||||
> |
||||
<slot name="foot" /> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* card 卡片 |
||||
* @description 卡片组件一般用于多个列表条目,且风格统一的场景 |
||||
* @tutorial https://www.uviewui.com/components/card.html |
||||
* @property {Boolean} full 卡片与屏幕两侧是否留空隙(默认false) |
||||
* @property {String} title 头部左边的标题 |
||||
* @property {String} title-color 标题颜色(默认#303133) |
||||
* @property {String | Number} title-size 标题字体大小,单位rpx(默认30) |
||||
* @property {String} sub-title 头部右边的副标题 |
||||
* @property {String} sub-title-color 副标题颜色(默认#909399) |
||||
* @property {String | Number} sub-title-size 副标题字体大小(默认26) |
||||
* @property {Boolean} border 是否显示边框(默认true) |
||||
* @property {String | Number} index 用于标识点击了第几个卡片 |
||||
* @property {String} box-shadow 卡片外围阴影,字符串形式(默认none) |
||||
* @property {String} margin 卡片与屏幕两边和上下元素的间距,需带单位,如"30rpx 20rpx"(默认30rpx) |
||||
* @property {String | Number} border-radius 卡片整体的圆角值,单位rpx(默认16) |
||||
* @property {Object} head-style 头部自定义样式,对象形式 |
||||
* @property {Object} body-style 中部自定义样式,对象形式 |
||||
* @property {Object} foot-style 底部自定义样式,对象形式 |
||||
* @property {Boolean} head-border-bottom 是否显示头部的下边框(默认true) |
||||
* @property {Boolean} foot-border-top 是否显示底部的上边框(默认true) |
||||
* @property {Boolean} show-head 是否显示头部(默认true) |
||||
* @property {Boolean} show-head 是否显示尾部(默认true) |
||||
* @property {String} thumb 缩略图路径,如设置将显示在标题的左边,不建议使用相对路径 |
||||
* @property {String | Number} thumb-width 缩略图的宽度,高等于宽,单位rpx(默认60) |
||||
* @property {Boolean} thumb-circle 缩略图是否为圆形(默认false) |
||||
* @event {Function} click 整个卡片任意位置被点击时触发 |
||||
* @event {Function} head-click 卡片头部被点击时触发 |
||||
* @event {Function} body-click 卡片主体部分被点击时触发 |
||||
* @event {Function} foot-click 卡片底部部分被点击时触发 |
||||
* @example <u-card padding="30" title="card"></u-card> |
||||
*/ |
||||
export default { |
||||
name: 'u-card', |
||||
props: { |
||||
// 与屏幕两侧是否留空隙 |
||||
full: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 标题 |
||||
title: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 标题颜色 |
||||
titleColor: { |
||||
type: String, |
||||
default: '#303133' |
||||
}, |
||||
// 标题字体大小,单位rpx |
||||
titleSize: { |
||||
type: [Number, String], |
||||
default: '30' |
||||
}, |
||||
// 副标题 |
||||
subTitle: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 副标题颜色 |
||||
subTitleColor: { |
||||
type: String, |
||||
default: '#909399' |
||||
}, |
||||
// 副标题字体大小,单位rpx |
||||
subTitleSize: { |
||||
type: [Number, String], |
||||
default: '26' |
||||
}, |
||||
// 是否显示外部边框,只对full=false时有效(卡片与边框有空隙时) |
||||
border: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 用于标识点击了第几个 |
||||
index: { |
||||
type: [Number, String, Object], |
||||
default: '' |
||||
}, |
||||
// 用于隔开上下左右的边距,带单位的写法,如:"30rpx 30rpx","20rpx 20rpx 30rpx 30rpx" |
||||
margin: { |
||||
type: String, |
||||
default: '30rpx' |
||||
}, |
||||
// card卡片的圆角 |
||||
borderRadius: { |
||||
type: [Number, String], |
||||
default: '16' |
||||
}, |
||||
// 头部自定义样式,对象形式 |
||||
headStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
} |
||||
}, |
||||
// 主体自定义样式,对象形式 |
||||
bodyStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
} |
||||
}, |
||||
// 底部自定义样式,对象形式 |
||||
footStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
} |
||||
}, |
||||
// 头部是否下边框 |
||||
headBorderBottom: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 底部是否有上边框 |
||||
footBorderTop: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 标题左边的缩略图 |
||||
thumb: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 缩略图宽高,单位rpx |
||||
thumbWidth: { |
||||
type: [String, Number], |
||||
default: '60' |
||||
}, |
||||
// 缩略图是否为圆形 |
||||
thumbCircle: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 给head,body,foot的内边距 |
||||
padding: { |
||||
type: [String, Number], |
||||
default: '30' |
||||
}, |
||||
// 是否显示头部 |
||||
showHead: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 是否显示尾部 |
||||
showFoot: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 卡片外围阴影,字符串形式 |
||||
boxShadow: { |
||||
type: String, |
||||
default: 'none' |
||||
} |
||||
}, |
||||
data() { |
||||
return {}; |
||||
}, |
||||
methods: { |
||||
click() { |
||||
this.$emit('click', this.index); |
||||
}, |
||||
headClick() { |
||||
this.$emit('head-click', this.index); |
||||
}, |
||||
bodyClick() { |
||||
this.$emit('body-click', this.index); |
||||
}, |
||||
footClick() { |
||||
this.$emit('foot-click', this.index); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-card { |
||||
position: relative; |
||||
overflow: hidden; |
||||
font-size: 28rpx; |
||||
background-color: #ffffff; |
||||
box-sizing: border-box; |
||||
|
||||
&-full { |
||||
// 如果是与屏幕之间不留空隙,应该设置左右边距为0 |
||||
margin-left: 0 !important; |
||||
margin-right: 0 !important; |
||||
width: 100%; |
||||
} |
||||
|
||||
&--border:after { |
||||
border-radius: 16rpx; |
||||
} |
||||
|
||||
&__head { |
||||
&--left { |
||||
color: $u-main-color; |
||||
|
||||
&__thumb { |
||||
margin-right: 16rpx; |
||||
} |
||||
|
||||
&__title { |
||||
max-width: 400rpx; |
||||
} |
||||
} |
||||
|
||||
&--right { |
||||
color: $u-tips-color; |
||||
margin-left: 6rpx; |
||||
} |
||||
} |
||||
|
||||
&__body { |
||||
color: $u-content-color; |
||||
} |
||||
|
||||
&__foot { |
||||
color: $u-tips-color; |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,70 @@ |
||||
<template> |
||||
<view class="u-cell-box"> |
||||
<view class="u-cell-title" v-if="title" :style="[titleStyle]"> |
||||
{{title}} |
||||
</view> |
||||
<view class="u-cell-item-box" :class="{'u-border-bottom u-border-top': border}"> |
||||
<slot /> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* cellGroup 单元格父组件Group |
||||
* @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。搭配u-cell-item |
||||
* @tutorial https://www.uviewui.com/components/cell.html |
||||
* @property {String} title 分组标题 |
||||
* @property {Boolean} border 是否显示外边框(默认true) |
||||
* @property {Object} title-style 分组标题的的样式,对象形式,如{'font-size': '24rpx'} 或 {'fontSize': '24rpx'} |
||||
* @example <u-cell-group title="设置喜好"> |
||||
*/ |
||||
export default { |
||||
name: "u-cell-group", |
||||
props: { |
||||
// 分组标题 |
||||
title: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 是否显示分组list上下边框 |
||||
border: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 分组标题的样式,对象形式,注意驼峰属性写法 |
||||
// 类似 {'font-size': '24rpx'} 和 {'fontSize': '24rpx'} |
||||
titleStyle: { |
||||
type: Object, |
||||
default () { |
||||
return {}; |
||||
} |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
index: 0, |
||||
} |
||||
}, |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-cell-box { |
||||
width: 100%; |
||||
} |
||||
|
||||
.u-cell-title { |
||||
padding: 30rpx 32rpx 10rpx 32rpx; |
||||
font-size: 30rpx; |
||||
text-align: left; |
||||
color: $u-tips-color; |
||||
} |
||||
|
||||
.u-cell-item-box { |
||||
background-color: #FFFFFF; |
||||
flex-direction: row; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,316 @@ |
||||
<template> |
||||
<view |
||||
@tap="click" |
||||
class="u-cell" |
||||
:class="{ 'u-border-bottom': borderBottom, 'u-border-top': borderTop, 'u-col-center': center, 'u-cell--required': required }" |
||||
hover-stay-time="150" |
||||
:hover-class="hoverClass" |
||||
:style="{ |
||||
backgroundColor: bgColor |
||||
}" |
||||
> |
||||
<u-icon :size="iconSize" :name="icon" v-if="icon" :custom-style="iconStyle" class="u-cell__left-icon-wrap"></u-icon> |
||||
<view class="u-flex" v-else> |
||||
<slot name="icon"></slot> |
||||
</view> |
||||
<view |
||||
class="u-cell_title" |
||||
:style="[ |
||||
{ |
||||
width: titleWidth ? titleWidth + 'rpx' : 'auto' |
||||
}, |
||||
titleStyle |
||||
]" |
||||
> |
||||
<block v-if="title !== ''">{{ title }}</block> |
||||
<slot name="title" v-else></slot> |
||||
|
||||
<view class="u-cell__label" v-if="label || $slots.label" :style="[labelStyle]"> |
||||
<block v-if="label !== ''">{{ label }}</block> |
||||
<slot name="label" v-else></slot> |
||||
</view> |
||||
</view> |
||||
|
||||
<view class="u-cell__value" :style="[valueStyle]"> |
||||
<block class="u-cell__value" v-if="value !== ''">{{ value }}</block> |
||||
<slot v-else></slot> |
||||
</view> |
||||
<view class="u-flex u-cell_right" v-if="$slots['right-icon']"> |
||||
<slot name="right-icon"></slot> |
||||
</view> |
||||
<u-icon v-if="arrow" name="arrow-right" :style="[arrowStyle]" class="u-icon-wrap u-cell__right-icon-wrap"></u-icon> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* cellItem 单元格Item |
||||
* @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。搭配u-cell-group使用 |
||||
* @tutorial https://www.uviewui.com/components/cell.html |
||||
* @property {String} title 左侧标题 |
||||
* @property {String} icon 左侧图标名,只支持uView内置图标,见Icon 图标 |
||||
* @property {Object} icon-style 左边图标的样式,对象形式 |
||||
* @property {String} value 右侧内容 |
||||
* @property {String} label 标题下方的描述信息 |
||||
* @property {Boolean} border-bottom 是否显示cell的下边框(默认true) |
||||
* @property {Boolean} border-top 是否显示cell的上边框(默认false) |
||||
* @property {Boolean} center 是否使内容垂直居中(默认false) |
||||
* @property {String} hover-class 是否开启点击反馈,none为无效果(默认true) |
||||
* // @property {Boolean} border-gap border-bottom为true时,Cell列表中间的条目的下边框是否与左边有一个间隔(默认true) |
||||
* @property {Boolean} arrow 是否显示右侧箭头(默认true) |
||||
* @property {Boolean} required 箭头方向,可选值(默认right) |
||||
* @property {Boolean} arrow-direction 是否显示左边表示必填的星号(默认false) |
||||
* @property {Object} title-style 标题样式,对象形式 |
||||
* @property {Object} value-style 右侧内容样式,对象形式 |
||||
* @property {Object} label-style 标题下方描述信息的样式,对象形式 |
||||
* @property {String} bg-color 背景颜色(默认transparent) |
||||
* @property {String Number} index 用于在click事件回调中返回,标识当前是第几个Item |
||||
* @property {String Number} title-width 标题的宽度,单位rpx |
||||
* @example <u-cell-item icon="integral-fill" title="会员等级" value="新版本"></u-cell-item> |
||||
*/ |
||||
export default { |
||||
name: 'u-cell-item', |
||||
props: { |
||||
// 左侧图标名称(只能uView内置图标),或者图标src |
||||
icon: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 左侧标题 |
||||
title: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// 右侧内容 |
||||
value: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// 标题下方的描述信息 |
||||
label: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// 是否显示下边框 |
||||
borderBottom: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 是否显示上边框 |
||||
borderTop: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 多个cell中,中间的cell显示下划线时,下划线是否给一个到左边的距离 |
||||
// 1.4.0版本废除此参数,默认边框由border-top和border-bottom提供,此参数会造成干扰 |
||||
// borderGap: { |
||||
// type: Boolean, |
||||
// default: true |
||||
// }, |
||||
// 是否开启点击反馈,即点击时cell背景为灰色,none为无效果 |
||||
hoverClass: { |
||||
type: String, |
||||
default: 'u-cell-hover' |
||||
}, |
||||
// 是否显示右侧箭头 |
||||
arrow: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 内容是否垂直居中 |
||||
center: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 是否显示左边表示必填的星号 |
||||
required: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 标题的宽度,单位rpx |
||||
titleWidth: { |
||||
type: [Number, String], |
||||
default: '' |
||||
}, |
||||
// 右侧箭头方向,可选值:right|up|down,默认为right |
||||
arrowDirection: { |
||||
type: String, |
||||
default: 'right' |
||||
}, |
||||
// 控制标题的样式 |
||||
titleStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
} |
||||
}, |
||||
// 右侧显示内容的样式 |
||||
valueStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
} |
||||
}, |
||||
// 描述信息的样式 |
||||
labelStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
} |
||||
}, |
||||
// 背景颜色 |
||||
bgColor: { |
||||
type: String, |
||||
default: 'transparent' |
||||
}, |
||||
// 用于识别被点击的是第几个cell |
||||
index: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// 是否使用lable插槽 |
||||
useLabelSlot: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 左边图标的大小,单位rpx,只对传入icon字段时有效 |
||||
iconSize: { |
||||
type: [Number, String], |
||||
default: 34 |
||||
}, |
||||
// 左边图标的样式,对象形式 |
||||
iconStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {} |
||||
} |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
|
||||
}; |
||||
}, |
||||
computed: { |
||||
arrowStyle() { |
||||
let style = {}; |
||||
if (this.arrowDirection == 'up') style.transform = 'rotate(-90deg)'; |
||||
else if (this.arrowDirection == 'down') style.transform = 'rotate(90deg)'; |
||||
else style.transform = 'rotate(0deg)'; |
||||
return style; |
||||
} |
||||
}, |
||||
methods: { |
||||
click() { |
||||
this.$emit('click', this.index); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
.u-cell { |
||||
@include vue-flex; |
||||
align-items: center; |
||||
position: relative; |
||||
/* #ifndef APP-NVUE */ |
||||
box-sizing: border-box; |
||||
/* #endif */ |
||||
width: 100%; |
||||
padding: 26rpx 32rpx; |
||||
font-size: 28rpx; |
||||
line-height: 54rpx; |
||||
color: $u-content-color; |
||||
background-color: #fff; |
||||
text-align: left; |
||||
} |
||||
|
||||
.u-cell_title { |
||||
font-size: 28rpx; |
||||
} |
||||
|
||||
.u-cell__left-icon-wrap { |
||||
margin-right: 10rpx; |
||||
font-size: 32rpx; |
||||
} |
||||
|
||||
.u-cell__right-icon-wrap { |
||||
margin-left: 10rpx; |
||||
color: #969799; |
||||
font-size: 28rpx; |
||||
} |
||||
|
||||
.u-cell__left-icon-wrap, |
||||
.u-cell__right-icon-wrap { |
||||
@include vue-flex; |
||||
align-items: center; |
||||
height: 48rpx; |
||||
} |
||||
|
||||
.u-cell-border:after { |
||||
position: absolute; |
||||
/* #ifndef APP-NVUE */ |
||||
box-sizing: border-box; |
||||
content: ' '; |
||||
pointer-events: none; |
||||
border-bottom: 1px solid $u-border-color; |
||||
/* #endif */ |
||||
right: 0; |
||||
left: 0; |
||||
top: 0; |
||||
transform: scaleY(0.5); |
||||
} |
||||
|
||||
.u-cell-border { |
||||
position: relative; |
||||
} |
||||
|
||||
.u-cell__label { |
||||
margin-top: 6rpx; |
||||
font-size: 26rpx; |
||||
line-height: 36rpx; |
||||
color: $u-tips-color; |
||||
/* #ifndef APP-NVUE */ |
||||
word-wrap: break-word; |
||||
/* #endif */ |
||||
} |
||||
|
||||
.u-cell__value { |
||||
overflow: hidden; |
||||
text-align: right; |
||||
/* #ifndef APP-NVUE */ |
||||
vertical-align: middle; |
||||
/* #endif */ |
||||
color: $u-tips-color; |
||||
font-size: 26rpx; |
||||
} |
||||
|
||||
.u-cell__title, |
||||
.u-cell__value { |
||||
flex: 1; |
||||
} |
||||
|
||||
.u-cell--required { |
||||
/* #ifndef APP-NVUE */ |
||||
overflow: visible; |
||||
/* #endif */ |
||||
@include vue-flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.u-cell--required:before { |
||||
position: absolute; |
||||
/* #ifndef APP-NVUE */ |
||||
content: '*'; |
||||
/* #endif */ |
||||
left: 8px; |
||||
margin-top: 4rpx; |
||||
font-size: 14px; |
||||
color: $u-type-error; |
||||
} |
||||
|
||||
.u-cell_right { |
||||
line-height: 1; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,123 @@ |
||||
<template> |
||||
<view class="u-checkbox-group u-clearfix"> |
||||
<slot></slot> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import Emitter from '../../libs/util/emitter.js'; |
||||
/** |
||||
* checkboxGroup 开关选择器父组件Group |
||||
* @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便 |
||||
* @tutorial https://www.uviewui.com/components/checkbox.html |
||||
* @property {String Number} max 最多能选中多少个checkbox(默认999) |
||||
* @property {String Number} size 组件整体的大小,单位rpx(默认40) |
||||
* @property {Boolean} disabled 是否禁用所有checkbox(默认false) |
||||
* @property {String Number} icon-size 图标大小,单位rpx(默认20) |
||||
* @property {Boolean} label-disabled 是否禁止点击文本操作checkbox(默认false) |
||||
* @property {String} width 宽度,需带单位 |
||||
* @property {String} width 宽度,需带单位 |
||||
* @property {String} shape 外观形状,shape-方形,circle-圆形(默认circle) |
||||
* @property {Boolean} wrap 是否每个checkbox都换行(默认false) |
||||
* @property {String} active-color 选中时的颜色,应用到所有子Checkbox组件(默认#2979ff) |
||||
* @event {Function} change 任一个checkbox状态发生变化时触发,回调为一个对象 |
||||
* @example <u-checkbox-group></u-checkbox-group> |
||||
*/ |
||||
export default { |
||||
name: 'u-checkbox-group', |
||||
mixins: [Emitter], |
||||
props: { |
||||
// 最多能选中多少个checkbox |
||||
max: { |
||||
type: [Number, String], |
||||
default: 999 |
||||
}, |
||||
// 所有选中项的 name |
||||
// value: { |
||||
// default: Array, |
||||
// default() { |
||||
// return [] |
||||
// } |
||||
// }, |
||||
// 是否禁用所有复选框 |
||||
disabled: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 在表单内提交时的标识符 |
||||
name: { |
||||
type: [Boolean, String], |
||||
default: '' |
||||
}, |
||||
// 是否禁止点击提示语选中复选框 |
||||
labelDisabled: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 形状,square为方形,circle为原型 |
||||
shape: { |
||||
type: String, |
||||
default: 'square' |
||||
}, |
||||
// 选中状态下的颜色 |
||||
activeColor: { |
||||
type: String, |
||||
default: '#2979ff' |
||||
}, |
||||
// 组件的整体大小 |
||||
size: { |
||||
type: [String, Number], |
||||
default: 34 |
||||
}, |
||||
// 每个checkbox占u-checkbox-group的宽度 |
||||
width: { |
||||
type: String, |
||||
default: 'auto' |
||||
}, |
||||
// 是否每个checkbox都换行 |
||||
wrap: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 图标的大小,单位rpx |
||||
iconSize: { |
||||
type: [String, Number], |
||||
default: 20 |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
} |
||||
}, |
||||
created() { |
||||
// 如果将children定义在data中,在微信小程序会造成循环引用而报错 |
||||
this.children = []; |
||||
}, |
||||
methods: { |
||||
emitEvent() { |
||||
let values = []; |
||||
this.children.map(val => { |
||||
if(val.value) values.push(val.name); |
||||
}) |
||||
this.$emit('change', values); |
||||
// 发出事件,用于在表单组件中嵌入checkbox的情况,进行验证 |
||||
// 由于头条小程序执行迟钝,故需要用几十毫秒的延时 |
||||
setTimeout(() => { |
||||
// 将当前的值发送到 u-form-item 进行校验 |
||||
this.dispatch('u-form-item', 'on-form-change', values); |
||||
}, 60) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-checkbox-group { |
||||
/* #ifndef MP || APP-NVUE */ |
||||
display: inline-flex; |
||||
flex-wrap: wrap; |
||||
/* #endif */ |
||||
} |
||||
</style> |
||||
@ -0,0 +1,284 @@ |
||||
<template> |
||||
<view class="u-checkbox" :style="[checkboxStyle]"> |
||||
<view class="u-checkbox__icon-wrap" @tap="toggle" :class="[iconClass]" :style="[iconStyle]"> |
||||
<u-icon class="u-checkbox__icon-wrap__icon" name="checkbox-mark" :size="checkboxIconSize" :color="iconColor"/> |
||||
</view> |
||||
<view class="u-checkbox__label" @tap="onClickLabel" :style="{ |
||||
fontSize: $u.addUnit(labelSize) |
||||
}"> |
||||
<slot /> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* checkbox 复选框 |
||||
* @description 该组件需要搭配checkboxGroup组件使用,以便用户进行操作时,获得当前复选框组的选中情况。 |
||||
* @tutorial https://www.uviewui.com/components/checkbox.html |
||||
* @property {String Number} icon-size 图标大小,单位rpx(默认20) |
||||
* @property {String Number} label-size label字体大小,单位rpx(默认28) |
||||
* @property {String Number} name checkbox组件的标示符 |
||||
* @property {String} shape 形状,见官网说明(默认circle) |
||||
* @property {Boolean} disabled 是否禁用 |
||||
* @property {Boolean} label-disabled 是否禁止点击文本操作checkbox |
||||
* @property {String} active-color 选中时的颜色,如设置CheckboxGroup的active-color将失效 |
||||
* @event {Function} change 某个checkbox状态发生变化时触发,回调为一个对象 |
||||
* @example <u-checkbox v-model="checked" :disabled="false">天涯</u-checkbox> |
||||
*/ |
||||
export default { |
||||
name: "u-checkbox", |
||||
props: { |
||||
// checkbox的名称 |
||||
name: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// 形状,square为方形,circle为原型 |
||||
shape: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 是否为选中状态 |
||||
value: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 是否禁用 |
||||
disabled: { |
||||
type: [String, Boolean], |
||||
default: '' |
||||
}, |
||||
// 是否禁止点击提示语选中复选框 |
||||
labelDisabled: { |
||||
type: [String, Boolean], |
||||
default: '' |
||||
}, |
||||
// 选中状态下的颜色,如设置此值,将会覆盖checkboxGroup的activeColor值 |
||||
activeColor: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 图标的大小,单位rpx |
||||
iconSize: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// label的字体大小,rpx单位 |
||||
labelSize: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// 组件的整体大小 |
||||
size: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
parentDisabled: false, |
||||
newParams: {}, |
||||
}; |
||||
}, |
||||
created() { |
||||
// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环应用 |
||||
this.parent = this.$u.$parent.call(this, 'u-checkbox-group'); |
||||
// 如果存在u-checkbox-group,将本组件的this塞进父组件的children中 |
||||
this.parent && this.parent.children.push(this); |
||||
}, |
||||
computed: { |
||||
// 是否禁用,如果父组件u-checkbox-group禁用的话,将会忽略子组件的配置 |
||||
isDisabled() { |
||||
return this.disabled !== '' ? this.disabled : this.parent ? this.parent.disabled : false; |
||||
}, |
||||
// 是否禁用label点击 |
||||
isLabelDisabled() { |
||||
return this.labelDisabled !== '' ? this.labelDisabled : this.parent ? this.parent.labelDisabled : false; |
||||
}, |
||||
// 组件尺寸,对应size的值,默认值为34rpx |
||||
checkboxSize() { |
||||
return this.size ? this.size : (this.parent ? this.parent.size : 34); |
||||
}, |
||||
// 组件的勾选图标的尺寸,默认20 |
||||
checkboxIconSize() { |
||||
return this.iconSize ? this.iconSize : (this.parent ? this.parent.iconSize : 20); |
||||
}, |
||||
// 组件选中激活时的颜色 |
||||
elActiveColor() { |
||||
return this.activeColor ? this.activeColor : (this.parent ? this.parent.activeColor : 'primary'); |
||||
}, |
||||
// 组件的形状 |
||||
elShape() { |
||||
return this.shape ? this.shape : (this.parent ? this.parent.shape : 'square'); |
||||
}, |
||||
iconStyle() { |
||||
let style = {}; |
||||
// 既要判断是否手动禁用,还要判断用户v-model绑定的值,如果绑定为false,那么也无法选中 |
||||
if (this.elActiveColor && this.value && !this.isDisabled) { |
||||
style.borderColor = this.elActiveColor; |
||||
style.backgroundColor = this.elActiveColor; |
||||
} |
||||
style.width = this.$u.addUnit(this.checkboxSize); |
||||
style.height = this.$u.addUnit(this.checkboxSize); |
||||
return style; |
||||
}, |
||||
// checkbox内部的勾选图标,如果选中状态,为白色,否则为透明色即可 |
||||
iconColor() { |
||||
return this.value ? '#ffffff' : 'transparent'; |
||||
}, |
||||
iconClass() { |
||||
let classes = []; |
||||
classes.push('u-checkbox__icon-wrap--' + this.elShape); |
||||
if (this.value == true) classes.push('u-checkbox__icon-wrap--checked'); |
||||
if (this.isDisabled) classes.push('u-checkbox__icon-wrap--disabled'); |
||||
if (this.value && this.isDisabled) classes.push('u-checkbox__icon-wrap--disabled--checked'); |
||||
// 支付宝小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效 |
||||
return classes.join(' '); |
||||
}, |
||||
checkboxStyle() { |
||||
let style = {}; |
||||
if(this.parent && this.parent.width) { |
||||
style.width = this.parent.width; |
||||
// #ifdef MP |
||||
// 各家小程序因为它们特殊的编译结构,使用float布局 |
||||
style.float = 'left'; |
||||
// #endif |
||||
// #ifndef MP |
||||
// H5和APP使用flex布局 |
||||
style.flex = `0 0 ${this.parent.width}`; |
||||
// #endif |
||||
} |
||||
if(this.parent && this.parent.wrap) { |
||||
style.width = '100%'; |
||||
// #ifndef MP |
||||
// H5和APP使用flex布局,将宽度设置100%,即可自动换行 |
||||
style.flex = '0 0 100%'; |
||||
// #endif |
||||
} |
||||
return style; |
||||
} |
||||
}, |
||||
methods: { |
||||
onClickLabel() { |
||||
if (!this.isLabelDisabled && !this.isDisabled) { |
||||
this.setValue(); |
||||
} |
||||
}, |
||||
toggle() { |
||||
if (!this.isDisabled) { |
||||
this.setValue(); |
||||
} |
||||
}, |
||||
emitEvent() { |
||||
this.$emit('change', { |
||||
value: !this.value, |
||||
name: this.name |
||||
}) |
||||
// 执行父组件u-checkbox-group的事件方法 |
||||
// 等待下一个周期再执行,因为this.$emit('input')作用于父组件,再反馈到子组件内部,需要时间 |
||||
setTimeout(() => { |
||||
if(this.parent && this.parent.emitEvent) this.parent.emitEvent(); |
||||
}, 80); |
||||
}, |
||||
// 设置input的值,这里通过input事件,设置通过v-model绑定的组件的值 |
||||
setValue() { |
||||
// 判断是否超过了可选的最大数量 |
||||
let checkedNum = 0; |
||||
if(this.parent && this.parent.children) { |
||||
// 只要父组件的某一个子元素的value为true,就加1(已有的选中数量) |
||||
this.parent.children.map(val => { |
||||
if (val.value) checkedNum++; |
||||
}) |
||||
} |
||||
// 如果原来为选中状态,那么可以取消 |
||||
if (this.value == true) { |
||||
this.emitEvent(); |
||||
this.$emit('input', !this.value); |
||||
} else { |
||||
// 如果超出最多可选项,提示 |
||||
if(this.parent && checkedNum >= this.parent.max) { |
||||
return this.$u.toast(`最多可选${this.parent.max}项`); |
||||
} |
||||
// 如果原来为未选中状态,需要选中的数量少于父组件中设置的max值,才可以选中 |
||||
this.emitEvent(); |
||||
this.$emit('input', !this.value); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-checkbox { |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
align-items: center; |
||||
overflow: hidden; |
||||
user-select: none; |
||||
line-height: 1.8; |
||||
|
||||
&__icon-wrap { |
||||
color: $u-content-color; |
||||
flex: none; |
||||
display: -webkit-flex; |
||||
@include vue-flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
box-sizing: border-box; |
||||
width: 42rpx; |
||||
height: 42rpx; |
||||
color: transparent; |
||||
text-align: center; |
||||
transition-property: color, border-color, background-color; |
||||
font-size: 20px; |
||||
border: 1px solid #c8c9cc; |
||||
transition-duration: 0.2s; |
||||
|
||||
/* #ifdef MP-TOUTIAO */ |
||||
// 头条小程序兼容性问题,需要设置行高为0,否则图标偏下 |
||||
&__icon { |
||||
line-height: 0; |
||||
} |
||||
/* #endif */ |
||||
|
||||
&--circle { |
||||
border-radius: 100%; |
||||
} |
||||
|
||||
&--square { |
||||
border-radius: 6rpx; |
||||
} |
||||
|
||||
&--checked { |
||||
color: #fff; |
||||
background-color: $u-type-primary; |
||||
border-color: $u-type-primary; |
||||
} |
||||
|
||||
&--disabled { |
||||
background-color: #ebedf0; |
||||
border-color: #c8c9cc; |
||||
} |
||||
|
||||
&--disabled--checked { |
||||
color: #c8c9cc !important; |
||||
} |
||||
} |
||||
|
||||
&__label { |
||||
word-wrap: break-word; |
||||
margin-left: 10rpx; |
||||
margin-right: 24rpx; |
||||
color: $u-content-color; |
||||
font-size: 30rpx; |
||||
|
||||
&--disabled { |
||||
color: #c8c9cc; |
||||
} |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,220 @@ |
||||
<template> |
||||
<view |
||||
class="u-circle-progress" |
||||
:style="{ |
||||
width: widthPx + 'px', |
||||
height: widthPx + 'px', |
||||
backgroundColor: bgColor |
||||
}" |
||||
> |
||||
<!-- 支付宝小程序不支持canvas-id属性,必须用id属性 --> |
||||
<canvas |
||||
class="u-canvas-bg" |
||||
:canvas-id="elBgId" |
||||
:id="elBgId" |
||||
:style="{ |
||||
width: widthPx + 'px', |
||||
height: widthPx + 'px' |
||||
}" |
||||
></canvas> |
||||
<canvas |
||||
class="u-canvas" |
||||
:canvas-id="elId" |
||||
:id="elId" |
||||
:style="{ |
||||
width: widthPx + 'px', |
||||
height: widthPx + 'px' |
||||
}" |
||||
></canvas> |
||||
<slot></slot> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* circleProgress 环形进度条 |
||||
* @description 展示操作或任务的当前进度,比如上传文件,是一个圆形的进度条。注意:此组件的percent值只能动态增加,不能动态减少。 |
||||
* @tutorial https://www.uviewui.com/components/circleProgress.html |
||||
* @property {String Number} percent 圆环进度百分比值,为数值类型,0-100 |
||||
* @property {String} inactive-color 圆环的底色,默认为灰色(该值无法动态变更)(默认#ececec) |
||||
* @property {String} active-color 圆环激活部分的颜色(该值无法动态变更)(默认#19be6b) |
||||
* @property {String Number} width 整个圆环组件的宽度,高度默认等于宽度值,单位rpx(默认200) |
||||
* @property {String Number} border-width 圆环的边框宽度,单位rpx(默认14) |
||||
* @property {String Number} duration 整个圆环执行一圈的时间,单位ms(默认呢1500) |
||||
* @property {String} type 如设置,active-color值将会失效 |
||||
* @property {String} bg-color 整个组件背景颜色,默认为白色 |
||||
* @example <u-circle-progress active-color="#2979ff" :percent="80"></u-circle-progress> |
||||
*/ |
||||
export default { |
||||
name: 'u-circle-progress', |
||||
props: { |
||||
// 圆环进度百分比值 |
||||
percent: { |
||||
type: Number, |
||||
default: 0, |
||||
// 限制值在0到100之间 |
||||
validator: val => { |
||||
return val >= 0 && val <= 100; |
||||
} |
||||
}, |
||||
// 底部圆环的颜色(灰色的圆环) |
||||
inactiveColor: { |
||||
type: String, |
||||
default: '#ececec' |
||||
}, |
||||
// 圆环激活部分的颜色 |
||||
activeColor: { |
||||
type: String, |
||||
default: '#19be6b' |
||||
}, |
||||
// 圆环线条的宽度,单位rpx |
||||
borderWidth: { |
||||
type: [Number, String], |
||||
default: 14 |
||||
}, |
||||
// 整个圆形的宽度,单位rpx |
||||
width: { |
||||
type: [Number, String], |
||||
default: 200 |
||||
}, |
||||
// 整个圆环执行一圈的时间,单位ms |
||||
duration: { |
||||
type: [Number, String], |
||||
default: 1500 |
||||
}, |
||||
// 主题类型 |
||||
type: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 整个圆环进度区域的背景色 |
||||
bgColor: { |
||||
type: String, |
||||
default: '#ffffff' |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
// #ifdef MP-WEIXIN |
||||
elBgId: 'uCircleProgressBgId', // 微信小程序中不能使用this.$u.guid()形式动态生成id值,否则会报错 |
||||
elId: 'uCircleProgressElId', |
||||
// #endif |
||||
// #ifndef MP-WEIXIN |
||||
elBgId: this.$u.guid(), // 非微信端的时候,需用动态的id,否则一个页面多个圆形进度条组件数据会混乱 |
||||
elId: this.$u.guid(), |
||||
// #endif |
||||
widthPx: uni.upx2px(this.width), // 转成px后的整个组件的背景宽度 |
||||
borderWidthPx: uni.upx2px(this.borderWidth), // 转成px后的圆环的宽度 |
||||
startAngle: -Math.PI / 2, // canvas画圆的起始角度,默认为3点钟方向,定位到12点钟方向 |
||||
progressContext: null, // 活动圆的canvas上下文 |
||||
newPercent: 0, // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用 |
||||
oldPercent: 0 // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用 |
||||
}; |
||||
}, |
||||
watch: { |
||||
percent(nVal, oVal = 0) { |
||||
if (nVal > 100) nVal = 100; |
||||
if (nVal < 0) oVal = 0; |
||||
// 此值其实等于this.percent,命名一个新 |
||||
this.newPercent = nVal; |
||||
this.oldPercent = oVal; |
||||
setTimeout(() => { |
||||
// 无论是百分比值增加还是减少,需要操作还是原来的旧的百分比值 |
||||
// 将此值减少或者新增到新的百分比值 |
||||
this.drawCircleByProgress(oVal); |
||||
}, 50); |
||||
} |
||||
}, |
||||
created() { |
||||
// 赋值,用于加载后第一个画圆使用 |
||||
this.newPercent = this.percent; |
||||
this.oldPercent = 0; |
||||
}, |
||||
computed: { |
||||
// 有type主题时,优先起作用 |
||||
circleColor() { |
||||
if (['success', 'error', 'info', 'primary', 'warning'].indexOf(this.type) >= 0) return this.$u.color[this.type]; |
||||
else return this.activeColor; |
||||
} |
||||
}, |
||||
mounted() { |
||||
// 在h5端,必须要做一点延时才起作用,this.$nextTick()无效(HX2.4.7) |
||||
setTimeout(() => { |
||||
this.drawProgressBg(); |
||||
this.drawCircleByProgress(this.oldPercent); |
||||
}, 50); |
||||
}, |
||||
methods: { |
||||
drawProgressBg() { |
||||
let ctx = uni.createCanvasContext(this.elBgId, this); |
||||
ctx.setLineWidth(this.borderWidthPx); // 设置圆环宽度 |
||||
ctx.setStrokeStyle(this.inactiveColor); // 线条颜色 |
||||
ctx.beginPath(); // 开始描绘路径 |
||||
// 设置一个原点(110,110),半径为100的圆的路径到当前路径 |
||||
let radius = this.widthPx / 2; |
||||
ctx.arc(radius, radius, radius - this.borderWidthPx, 0, 2 * Math.PI, false); |
||||
ctx.stroke(); // 对路径进行描绘 |
||||
ctx.draw(); |
||||
}, |
||||
drawCircleByProgress(progress) { |
||||
// 第一次操作进度环时将上下文保存到了this.data中,直接使用即可 |
||||
let ctx = this.progressContext; |
||||
if (!ctx) { |
||||
ctx = uni.createCanvasContext(this.elId, this); |
||||
this.progressContext = ctx; |
||||
} |
||||
// 表示进度的两端为圆形 |
||||
ctx.setLineCap('round'); |
||||
// 设置线条的宽度和颜色 |
||||
ctx.setLineWidth(this.borderWidthPx); |
||||
ctx.setStrokeStyle(this.circleColor); |
||||
// 将总过渡时间除以100,得出每修改百分之一进度所需的时间 |
||||
let time = Math.floor(this.duration / 100); |
||||
// 结束角的计算依据为:将2π分为100份,乘以当前的进度值,得出终止点的弧度值,加起始角,为整个圆从默认的 |
||||
// 3点钟方向开始画图,转为更好理解的12点钟方向开始作图,这需要起始角和终止角同时加上this.startAngle值 |
||||
let endAngle = ((2 * Math.PI) / 100) * progress + this.startAngle; |
||||
ctx.beginPath(); |
||||
// 半径为整个canvas宽度的一半 |
||||
let radius = this.widthPx / 2; |
||||
ctx.arc(radius, radius, radius - this.borderWidthPx, this.startAngle, endAngle, false); |
||||
ctx.stroke(); |
||||
ctx.draw(); |
||||
// 如果变更后新值大于旧值,意味着增大了百分比 |
||||
if (this.newPercent > this.oldPercent) { |
||||
// 每次递增百分之一 |
||||
progress++; |
||||
// 如果新增后的值,大于需要设置的值百分比值,停止继续增加 |
||||
if (progress > this.newPercent) return; |
||||
} else { |
||||
// 同理于上面 |
||||
progress--; |
||||
if (progress < this.newPercent) return; |
||||
} |
||||
setTimeout(() => { |
||||
// 定时器,每次操作间隔为time值,为了让进度条有动画效果 |
||||
this.drawCircleByProgress(progress); |
||||
}, time); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
.u-circle-progress { |
||||
position: relative; |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.u-canvas-bg { |
||||
position: absolute; |
||||
} |
||||
|
||||
.u-canvas { |
||||
position: absolute; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,147 @@ |
||||
<template> |
||||
<view class="u-progress" :style="{ |
||||
borderRadius: round ? '100rpx' : 0, |
||||
height: height + 'rpx', |
||||
backgroundColor: inactiveColor |
||||
}"> |
||||
<view :class="[ |
||||
type ? `u-type-${type}-bg` : '', |
||||
striped ? 'u-striped' : '', |
||||
striped && stripedActive ? 'u-striped-active' : '' |
||||
]" class="u-active" :style="[progressStyle]"> |
||||
<slot v-if="$slots.default || $slots.$default" /> |
||||
<block v-else-if="showPercent"> |
||||
{{percent + '%'}} |
||||
</block> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* lineProgress 线型进度条 |
||||
* @description 展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。 |
||||
* @tutorial https://www.uviewui.com/components/lineProgress.html |
||||
* @property {String Number} percent 进度条百分比值,为数值类型,0-100 |
||||
* @property {Boolean} round 进度条两端是否为半圆(默认true) |
||||
* @property {String} type 如设置,active-color值将会失效 |
||||
* @property {String} active-color 进度条激活部分的颜色(默认#19be6b) |
||||
* @property {String} inactive-color 进度条的底色(默认#ececec) |
||||
* @property {Boolean} show-percent 是否在进度条内部显示当前的百分比值数值(默认true) |
||||
* @property {String Number} height 进度条的高度,单位rpx(默认28) |
||||
* @property {Boolean} striped 是否显示进度条激活部分的条纹(默认false) |
||||
* @property {Boolean} striped-active 条纹是否具有动态效果(默认false) |
||||
* @example <u-line-progress :percent="70" :show-percent="true"></u-line-progress> |
||||
*/ |
||||
export default { |
||||
name: "u-line-progress", |
||||
props: { |
||||
// 两端是否显示半圆形 |
||||
round: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 主题颜色 |
||||
type: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 激活部分的颜色 |
||||
activeColor: { |
||||
type: String, |
||||
default: '#19be6b' |
||||
}, |
||||
inactiveColor: { |
||||
type: String, |
||||
default: '#ececec' |
||||
}, |
||||
// 进度百分比,数值 |
||||
percent: { |
||||
type: Number, |
||||
default: 0 |
||||
}, |
||||
// 是否在进度条内部显示百分比的值 |
||||
showPercent: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 进度条的高度,单位rpx |
||||
height: { |
||||
type: [Number, String], |
||||
default: 28 |
||||
}, |
||||
// 是否显示条纹 |
||||
striped: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 条纹是否显示活动状态 |
||||
stripedActive: { |
||||
type: Boolean, |
||||
default: false |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
|
||||
} |
||||
}, |
||||
computed: { |
||||
progressStyle() { |
||||
let style = {}; |
||||
style.width = this.percent + '%'; |
||||
if(this.activeColor) style.backgroundColor = this.activeColor; |
||||
return style; |
||||
} |
||||
}, |
||||
methods: { |
||||
|
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-progress { |
||||
overflow: hidden; |
||||
height: 15px; |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
align-items: center; |
||||
width: 100%; |
||||
border-radius: 100rpx; |
||||
} |
||||
|
||||
.u-active { |
||||
width: 0; |
||||
height: 100%; |
||||
align-items: center; |
||||
@include vue-flex; |
||||
justify-items: flex-end; |
||||
justify-content: space-around; |
||||
font-size: 20rpx; |
||||
color: #ffffff; |
||||
transition: all 0.4s ease; |
||||
} |
||||
|
||||
.u-striped { |
||||
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); |
||||
background-size: 39px 39px; |
||||
} |
||||
|
||||
.u-striped-active { |
||||
animation: progress-stripes 2s linear infinite; |
||||
} |
||||
|
||||
@keyframes progress-stripes { |
||||
0% { |
||||
background-position: 0 0; |
||||
} |
||||
|
||||
100% { |
||||
background-position: 39px 0; |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,156 @@ |
||||
<template> |
||||
<view class="u-col" :class="[ |
||||
'u-col-' + span |
||||
]" :style="{ |
||||
padding: `0 ${Number(gutter)/2 + 'rpx'}`, |
||||
marginLeft: 100 / 12 * offset + '%', |
||||
flex: `0 0 ${100 / 12 * span}%`, |
||||
alignItems: uAlignItem, |
||||
justifyContent: uJustify, |
||||
textAlign: textAlign |
||||
}" |
||||
@tap="click"> |
||||
<slot></slot> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* col 布局单元格 |
||||
* @description 通过基础的 12 分栏,迅速简便地创建布局(搭配<u-row>使用) |
||||
* @tutorial https://www.uviewui.com/components/layout.html |
||||
* @property {String Number} span 栅格占据的列数,总12等分(默认0) |
||||
* @property {String} text-align 文字水平对齐方式(默认left) |
||||
* @property {String Number} offset 分栏左边偏移,计算方式与span相同(默认0) |
||||
* @example <u-col span="3"><view class="demo-layout bg-purple"></view></u-col> |
||||
*/ |
||||
export default { |
||||
name: "u-col", |
||||
props: { |
||||
// 占父容器宽度的多少等分,总分为12份 |
||||
span: { |
||||
type: [Number, String], |
||||
default: 12 |
||||
}, |
||||
// 指定栅格左侧的间隔数(总12栏) |
||||
offset: { |
||||
type: [Number, String], |
||||
default: 0 |
||||
}, |
||||
// 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`) |
||||
justify: { |
||||
type: String, |
||||
default: 'start' |
||||
}, |
||||
// 垂直对齐方式,可选值为top、center、bottom |
||||
align: { |
||||
type: String, |
||||
default: 'center' |
||||
}, |
||||
// 文字对齐方式 |
||||
textAlign: { |
||||
type: String, |
||||
default: 'left' |
||||
}, |
||||
// 是否阻止事件传播 |
||||
stop: { |
||||
type: Boolean, |
||||
default: true |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
gutter: 20, // 给col添加间距,左右边距各占一半,从父组件u-row获取 |
||||
} |
||||
}, |
||||
created() { |
||||
this.parent = false; |
||||
}, |
||||
mounted() { |
||||
// 获取父组件实例,并赋值给对应的参数 |
||||
this.parent = this.$u.$parent.call(this, 'u-row'); |
||||
if (this.parent) { |
||||
this.gutter = this.parent.gutter; |
||||
} |
||||
}, |
||||
computed: { |
||||
uJustify() { |
||||
if (this.justify == 'end' || this.justify == 'start') return 'flex-' + this.justify; |
||||
else if (this.justify == 'around' || this.justify == 'between') return 'space-' + this.justify; |
||||
else return this.justify; |
||||
}, |
||||
uAlignItem() { |
||||
if (this.align == 'top') return 'flex-start'; |
||||
if (this.align == 'bottom') return 'flex-end'; |
||||
else return this.align; |
||||
} |
||||
}, |
||||
methods: { |
||||
click(e) { |
||||
this.$emit('click'); |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-col { |
||||
/* #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO */ |
||||
float: left; |
||||
/* #endif */ |
||||
} |
||||
|
||||
.u-col-0 { |
||||
width: 0; |
||||
} |
||||
|
||||
.u-col-1 { |
||||
width: calc(100%/12); |
||||
} |
||||
|
||||
.u-col-2 { |
||||
width: calc(100%/12 * 2); |
||||
} |
||||
|
||||
.u-col-3 { |
||||
width: calc(100%/12 * 3); |
||||
} |
||||
|
||||
.u-col-4 { |
||||
width: calc(100%/12 * 4); |
||||
} |
||||
|
||||
.u-col-5 { |
||||
width: calc(100%/12 * 5); |
||||
} |
||||
|
||||
.u-col-6 { |
||||
width: calc(100%/12 * 6); |
||||
} |
||||
|
||||
.u-col-7 { |
||||
width: calc(100%/12 * 7); |
||||
} |
||||
|
||||
.u-col-8 { |
||||
width: calc(100%/12 * 8); |
||||
} |
||||
|
||||
.u-col-9 { |
||||
width: calc(100%/12 * 9); |
||||
} |
||||
|
||||
.u-col-10 { |
||||
width: calc(100%/12 * 10); |
||||
} |
||||
|
||||
.u-col-11 { |
||||
width: calc(100%/12 * 11); |
||||
} |
||||
|
||||
.u-col-12 { |
||||
width: calc(100%/12 * 12); |
||||
} |
||||
</style> |
||||
@ -0,0 +1,205 @@ |
||||
<template> |
||||
<view class="u-collapse-item" :style="[itemStyle]"> |
||||
<view :hover-stay-time="200" class="u-collapse-head" @tap.stop="headClick" :hover-class="hoverClass" :style="[headStyle]"> |
||||
<block v-if="!$slots['title-all']"> |
||||
<view v-if="!$slots['title']" class="u-collapse-title u-line-1" :style="[{ textAlign: align ? align : 'left' }, |
||||
isShow && activeStyle && !arrow ? activeStyle : '']"> |
||||
{{ title }} |
||||
</view> |
||||
<slot v-else name="title" /> |
||||
<view class="u-icon-wrap"> |
||||
<u-icon v-if="arrow" :color="arrowColor" :class="{ 'u-arrow-down-icon-active': isShow }" |
||||
class="u-arrow-down-icon" name="arrow-down"></u-icon> |
||||
</view> |
||||
</block> |
||||
<slot v-else name="title-all" /> |
||||
</view> |
||||
<view class="u-collapse-body" :style="[{ |
||||
height: isShow ? height + 'px' : '0' |
||||
}]"> |
||||
<view class="u-collapse-content" :id="elId" :style="[bodyStyle]"> |
||||
<slot></slot> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* collapseItem 手风琴Item |
||||
* @description 通过折叠面板收纳内容区域(搭配u-collapse使用) |
||||
* @tutorial https://www.uviewui.com/components/collapse.html |
||||
* @property {String} title 面板标题 |
||||
* @property {String Number} index 主要用于事件的回调,标识那个Item被点击 |
||||
* @property {Boolean} disabled 面板是否可以打开或收起(默认false) |
||||
* @property {Boolean} open 设置某个面板的初始状态是否打开(默认false) |
||||
* @property {String Number} name 唯一标识符,如不设置,默认用当前collapse-item的索引值 |
||||
* @property {String} align 标题的对齐方式(默认left) |
||||
* @property {Object} active-style 不显示箭头时,可以添加当前选择的collapse-item活动样式,对象形式 |
||||
* @event {Function} change 某个item被打开或者收起时触发 |
||||
* @example <u-collapse-item :title="item.head" v-for="(item, index) in itemList" :key="index">{{item.body}}</u-collapse-item> |
||||
*/ |
||||
export default { |
||||
name: "u-collapse-item", |
||||
props: { |
||||
// 标题 |
||||
title: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 标题的对齐方式 |
||||
align: { |
||||
type: String, |
||||
default: 'left' |
||||
}, |
||||
// 是否可以点击收起 |
||||
disabled: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// collapse显示与否 |
||||
open: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 唯一标识符 |
||||
name: { |
||||
type: [Number, String], |
||||
default: '' |
||||
}, |
||||
//活动样式 |
||||
activeStyle: { |
||||
type: Object, |
||||
default () { |
||||
return {} |
||||
} |
||||
}, |
||||
// 标识当前为第几个 |
||||
index: { |
||||
type: [String, Number], |
||||
default: '' |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
isShow: false, |
||||
elId: this.$u.guid(), |
||||
height: 0, // body内容的高度 |
||||
headStyle: {}, // 头部样式,对象形式 |
||||
bodyStyle: {}, // 主体部分样式 |
||||
itemStyle: {}, // 每个item的整体样式 |
||||
arrowColor: '', // 箭头的颜色 |
||||
hoverClass: '', // 头部按下时的效果样式类 |
||||
arrow: true, // 是否显示右侧箭头 |
||||
|
||||
}; |
||||
}, |
||||
watch: { |
||||
open(val) { |
||||
this.isShow = val; |
||||
} |
||||
}, |
||||
created() { |
||||
this.parent = false; |
||||
// 获取u-collapse的信息,放在u-collapse是为了方便,不用每个u-collapse-item写一遍 |
||||
this.isShow = this.open; |
||||
}, |
||||
methods: { |
||||
// 异步获取内容,或者动态修改了内容时,需要重新初始化 |
||||
init() { |
||||
this.parent = this.$u.$parent.call(this, 'u-collapse'); |
||||
if(this.parent) { |
||||
this.nameSync = this.name ? this.name : this.parent.childrens.length; |
||||
// 不存在时才添加本实例 |
||||
!this.parent.childrens.includes(this) && this.parent.childrens.push(this); |
||||
this.headStyle = this.parent.headStyle; |
||||
this.bodyStyle = this.parent.bodyStyle; |
||||
this.arrowColor = this.parent.arrowColor; |
||||
this.hoverClass = this.parent.hoverClass; |
||||
this.arrow = this.parent.arrow; |
||||
this.itemStyle = this.parent.itemStyle; |
||||
} |
||||
this.$nextTick(() => { |
||||
this.queryRect(); |
||||
}); |
||||
}, |
||||
// 点击collapsehead头部 |
||||
headClick() { |
||||
if (this.disabled) return; |
||||
if (this.parent && this.parent.accordion == true) { |
||||
this.parent.childrens.map(val => { |
||||
// 自身不设置为false,因为后面有this.isShow = !this.isShow;处理了 |
||||
if (this != val) { |
||||
val.isShow = false; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
this.isShow = !this.isShow; |
||||
// 触发本组件的事件 |
||||
this.$emit('change', { |
||||
index: this.index, |
||||
show: this.isShow |
||||
}) |
||||
// 只有在打开时才发出事件 |
||||
if (this.isShow) this.parent && this.parent.onChange(); |
||||
this.$forceUpdate(); |
||||
}, |
||||
// 查询内容高度 |
||||
queryRect() { |
||||
// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html |
||||
// 组件内部一般用this.$uGetRect,对外的为this.$u.getRect,二者功能一致,名称不同 |
||||
this.$uGetRect('#' + this.elId).then(res => { |
||||
this.height = res.height; |
||||
}) |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.init(); |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-collapse-head { |
||||
position: relative; |
||||
@include vue-flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
color: $u-main-color; |
||||
font-size: 30rpx; |
||||
line-height: 1; |
||||
padding: 24rpx 0; |
||||
text-align: left; |
||||
} |
||||
|
||||
.u-collapse-title { |
||||
flex: 1; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.u-arrow-down-icon { |
||||
transition: all 0.3s; |
||||
margin-right: 20rpx; |
||||
margin-left: 14rpx; |
||||
} |
||||
|
||||
.u-arrow-down-icon-active { |
||||
transform: rotate(180deg); |
||||
transform-origin: center center; |
||||
} |
||||
|
||||
.u-collapse-body { |
||||
overflow: hidden; |
||||
transition: all 0.3s; |
||||
} |
||||
|
||||
.u-collapse-content { |
||||
overflow: hidden; |
||||
font-size: 28rpx; |
||||
color: $u-tips-color; |
||||
text-align: left; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,99 @@ |
||||
<template> |
||||
<view class="u-collapse"> |
||||
<slot /> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* collapse 手风琴 |
||||
* @description 通过折叠面板收纳内容区域 |
||||
* @tutorial https://www.uviewui.com/components/collapse.html |
||||
* @property {Boolean} accordion 是否手风琴模式(默认true) |
||||
* @property {Boolean} arrow 是否显示标题右侧的箭头(默认true) |
||||
* @property {String} arrow-color 标题右侧箭头的颜色(默认#909399) |
||||
* @property {Object} head-style 标题自定义样式,对象形式 |
||||
* @property {Object} body-style 主体自定义样式,对象形式 |
||||
* @property {String} hover-class 样式类名,按下时有效(默认u-hover-class) |
||||
* @event {Function} change 当前激活面板展开时触发(如果是手风琴模式,参数activeNames类型为String,否则为Array) |
||||
* @example <u-collapse></u-collapse> |
||||
*/ |
||||
export default { |
||||
name:"u-collapse", |
||||
props: { |
||||
// 是否手风琴模式 |
||||
accordion: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 头部的样式 |
||||
headStyle: { |
||||
type: Object, |
||||
default () { |
||||
return {} |
||||
} |
||||
}, |
||||
// 主体的样式 |
||||
bodyStyle: { |
||||
type: Object, |
||||
default () { |
||||
return {} |
||||
} |
||||
}, |
||||
// 每一个item的样式 |
||||
itemStyle: { |
||||
type: Object, |
||||
default () { |
||||
return {} |
||||
} |
||||
}, |
||||
// 是否显示右侧的箭头 |
||||
arrow: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 箭头的颜色 |
||||
arrowColor: { |
||||
type: String, |
||||
default: '#909399' |
||||
}, |
||||
// 标题部分按压时的样式类,"none"为无效果 |
||||
hoverClass: { |
||||
type: String, |
||||
default: 'u-hover-class' |
||||
} |
||||
}, |
||||
created() { |
||||
this.childrens = [] |
||||
}, |
||||
data() { |
||||
return { |
||||
|
||||
} |
||||
}, |
||||
methods: { |
||||
// 重新初始化一次内部的所有子元素的高度计算,用于异步获取数据渲染的情况 |
||||
init() { |
||||
this.childrens.forEach((vm, index) => { |
||||
vm.init(); |
||||
}) |
||||
}, |
||||
// collapse item被点击,由collapse item调用父组件方法 |
||||
onChange() { |
||||
let activeItem = []; |
||||
this.childrens.forEach((vm, index) => { |
||||
if (vm.isShow) { |
||||
activeItem.push(vm.nameSync); |
||||
} |
||||
}) |
||||
// 如果是手风琴模式,只有一个匹配结果,也即activeItem长度为1,将其转为字符串 |
||||
if (this.accordion) activeItem = activeItem.join(''); |
||||
this.$emit('change', activeItem); |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
</style> |
||||
@ -0,0 +1,237 @@ |
||||
<template> |
||||
<view |
||||
class="u-notice-bar" |
||||
:style="{ |
||||
background: computeBgColor, |
||||
padding: padding |
||||
}" |
||||
:class="[ |
||||
type ? `u-type-${type}-light-bg` : '' |
||||
]" |
||||
> |
||||
<view class="u-icon-wrap"> |
||||
<u-icon class="u-left-icon" v-if="volumeIcon" name="volume-fill" :size="volumeSize" :color="computeColor"></u-icon> |
||||
</view> |
||||
<swiper :disable-touch="disableTouch" @change="change" :autoplay="autoplay && playState == 'play'" :vertical="vertical" circular :interval="duration" class="u-swiper"> |
||||
<swiper-item v-for="(item, index) in list" :key="index" class="u-swiper-item"> |
||||
<view |
||||
class="u-news-item u-line-1" |
||||
:style="[textStyle]" |
||||
@tap="click(index)" |
||||
:class="['u-type-' + type]" |
||||
> |
||||
{{ item }} |
||||
</view> |
||||
</swiper-item> |
||||
</swiper> |
||||
<view class="u-icon-wrap"> |
||||
<u-icon @click="getMore" class="u-right-icon" v-if="moreIcon" name="arrow-right" :size="26" :color="computeColor"></u-icon> |
||||
<u-icon @click="close" class="u-right-icon" v-if="closeIcon" name="close" :size="24" :color="computeColor"></u-icon> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
props: { |
||||
// 显示的内容,数组 |
||||
list: { |
||||
type: Array, |
||||
default() { |
||||
return []; |
||||
} |
||||
}, |
||||
// 显示的主题,success|error|primary|info|warning |
||||
type: { |
||||
type: String, |
||||
default: 'warning' |
||||
}, |
||||
// 是否显示左侧的音量图标 |
||||
volumeIcon: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 是否显示右侧的右箭头图标 |
||||
moreIcon: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 是否显示右侧的关闭图标 |
||||
closeIcon: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 是否自动播放 |
||||
autoplay: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 文字颜色,各图标也会使用文字颜色 |
||||
color: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 背景颜色 |
||||
bgColor: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 滚动方向,row-水平滚动,column-垂直滚动 |
||||
direction: { |
||||
type: String, |
||||
default: 'row' |
||||
}, |
||||
// 是否显示 |
||||
show: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 字体大小,单位rpx |
||||
fontSize: { |
||||
type: [Number, String], |
||||
default: 26 |
||||
}, |
||||
// 滚动一个周期的时间长,单位ms |
||||
duration: { |
||||
type: [Number, String], |
||||
default: 2000 |
||||
}, |
||||
// 音量喇叭的大小 |
||||
volumeSize: { |
||||
type: [Number, String], |
||||
default: 34 |
||||
}, |
||||
// 水平滚动时的滚动速度,即每秒滚动多少rpx,这有利于控制文字无论多少时,都能有一个恒定的速度 |
||||
speed: { |
||||
type: Number, |
||||
default: 160 |
||||
}, |
||||
// 水平滚动时,是否采用衔接形式滚动 |
||||
isCircular: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 滚动方向,horizontal-水平滚动,vertical-垂直滚动 |
||||
mode: { |
||||
type: String, |
||||
default: 'horizontal' |
||||
}, |
||||
// 播放状态,play-播放,paused-暂停 |
||||
playState: { |
||||
type: String, |
||||
default: 'play' |
||||
}, |
||||
// 是否禁止用手滑动切换 |
||||
// 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序 |
||||
disableTouch: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 通知的边距 |
||||
padding: { |
||||
type: [Number, String], |
||||
default: '18rpx 24rpx' |
||||
} |
||||
}, |
||||
computed: { |
||||
// 计算字体颜色,如果没有自定义的,就用uview主题颜色 |
||||
computeColor() { |
||||
if (this.color) return this.color; |
||||
// 如果是无主题,就默认使用content-color |
||||
else if(this.type == 'none') return '#606266'; |
||||
else return this.type; |
||||
}, |
||||
// 文字内容的样式 |
||||
textStyle() { |
||||
let style = {}; |
||||
if (this.color) style.color = this.color; |
||||
else if(this.type == 'none') style.color = '#606266'; |
||||
style.fontSize = this.fontSize + 'rpx'; |
||||
return style; |
||||
}, |
||||
// 垂直或者水平滚动 |
||||
vertical() { |
||||
if(this.mode == 'horizontal') return false; |
||||
else return true; |
||||
}, |
||||
// 计算背景颜色 |
||||
computeBgColor() { |
||||
if (this.bgColor) return this.bgColor; |
||||
else if(this.type == 'none') return 'transparent'; |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
// animation: false |
||||
}; |
||||
}, |
||||
methods: { |
||||
// 点击通告栏 |
||||
click(index) { |
||||
this.$emit('click', index); |
||||
}, |
||||
// 点击关闭按钮 |
||||
close() { |
||||
this.$emit('close'); |
||||
}, |
||||
// 点击更多箭头按钮 |
||||
getMore() { |
||||
this.$emit('getMore'); |
||||
}, |
||||
change(e) { |
||||
let index = e.detail.current; |
||||
if(index == this.list.length - 1) { |
||||
this.$emit('end'); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-notice-bar { |
||||
width: 100%; |
||||
@include vue-flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
flex-wrap: nowrap; |
||||
padding: 18rpx 24rpx; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.u-swiper { |
||||
font-size: 26rpx; |
||||
height: 32rpx; |
||||
@include vue-flex; |
||||
align-items: center; |
||||
flex: 1; |
||||
margin-left: 12rpx; |
||||
} |
||||
|
||||
.u-swiper-item { |
||||
@include vue-flex; |
||||
align-items: center; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.u-news-item { |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.u-right-icon { |
||||
margin-left: 12rpx; |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
align-items: center; |
||||
} |
||||
|
||||
.u-left-icon { |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
align-items: center; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,318 @@ |
||||
<template> |
||||
<view class="u-countdown"> |
||||
<view class="u-countdown-item" :style="[itemStyle]" v-if="showDays && (hideZeroDay || (!hideZeroDay && d != '00'))"> |
||||
<view class="u-countdown-time" :style="[letterStyle]"> |
||||
{{ d }} |
||||
</view> |
||||
</view> |
||||
<view |
||||
class="u-countdown-colon" |
||||
:style="{fontSize: separatorSize + 'rpx', color: separatorColor, paddingBottom: separator == 'colon' ? '4rpx' : 0}" |
||||
v-if="showDays && (hideZeroDay || (!hideZeroDay && d != '00'))" |
||||
> |
||||
{{ separator == 'colon' ? ':' : '天' }} |
||||
</view> |
||||
<view class="u-countdown-item" :style="[itemStyle]" v-if="showHours"> |
||||
<view class="u-countdown-time" :style="{ fontSize: fontSize + 'rpx', color: color}"> |
||||
{{ h }} |
||||
</view> |
||||
</view> |
||||
<view |
||||
class="u-countdown-colon" |
||||
:style="{fontSize: separatorSize + 'rpx', color: separatorColor, paddingBottom: separator == 'colon' ? '4rpx' : 0}" |
||||
v-if="showHours" |
||||
> |
||||
{{ separator == 'colon' ? ':' : '时' }} |
||||
</view> |
||||
<view class="u-countdown-item" :style="[itemStyle]" v-if="showMinutes"> |
||||
<view class="u-countdown-time" :style="{ fontSize: fontSize + 'rpx', color: color}"> |
||||
{{ i }} |
||||
</view> |
||||
</view> |
||||
<view |
||||
class="u-countdown-colon" |
||||
:style="{fontSize: separatorSize + 'rpx', color: separatorColor, paddingBottom: separator == 'colon' ? '4rpx' : 0}" |
||||
v-if="showMinutes" |
||||
> |
||||
{{ separator == 'colon' ? ':' : '分' }} |
||||
</view> |
||||
<view class="u-countdown-item" :style="[itemStyle]" v-if="showSeconds"> |
||||
<view class="u-countdown-time" :style="{ fontSize: fontSize + 'rpx', color: color}"> |
||||
{{ s }} |
||||
</view> |
||||
</view> |
||||
<view |
||||
class="u-countdown-colon" |
||||
:style="{fontSize: separatorSize + 'rpx', color: separatorColor, paddingBottom: separator == 'colon' ? '4rpx' : 0}" |
||||
v-if="showSeconds && separator == 'zh'" |
||||
> |
||||
秒 |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* countDown 倒计时 |
||||
* @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。 |
||||
* @tutorial https://www.uviewui.com/components/countDown.html |
||||
* @property {String Number} timestamp 倒计时,单位为秒 |
||||
* @property {Boolean} autoplay 是否自动开始倒计时,如果为false,需手动调用开始方法。见官网说明(默认true) |
||||
* @property {String} separator 分隔符,colon为英文冒号,zh为中文(默认colon) |
||||
* @property {String Number} separator-size 分隔符的字体大小,单位rpx(默认30) |
||||
* @property {String} separator-color 分隔符的颜色(默认#303133) |
||||
* @property {String Number} font-size 倒计时字体大小,单位rpx(默认30) |
||||
* @property {Boolean} show-border 是否显示倒计时数字的边框(默认false) |
||||
* @property {Boolean} hide-zero-day 当"天"的部分为0时,隐藏该字段 (默认true) |
||||
* @property {String} border-color 数字边框的颜色(默认#303133) |
||||
* @property {String} bg-color 倒计时数字的背景颜色(默认#ffffff) |
||||
* @property {String} color 倒计时数字的颜色(默认#303133) |
||||
* @property {String} height 数字高度值(宽度等同此值),设置边框时看情况是否需要设置此值,单位rpx(默认auto) |
||||
* @property {Boolean} show-days 是否显示倒计时的"天"部分(默认true) |
||||
* @property {Boolean} show-hours 是否显示倒计时的"时"部分(默认true) |
||||
* @property {Boolean} show-minutes 是否显示倒计时的"分"部分(默认true) |
||||
* @property {Boolean} show-seconds 是否显示倒计时的"秒"部分(默认true) |
||||
* @event {Function} end 倒计时结束 |
||||
* @event {Function} change 每秒触发一次,回调为当前剩余的倒计秒数 |
||||
* @example <u-count-down ref="uCountDown" :timestamp="86400" :autoplay="false"></u-count-down> |
||||
*/ |
||||
export default { |
||||
name: 'u-count-down', |
||||
props: { |
||||
// 倒计时的时间,秒为单位 |
||||
timestamp: { |
||||
type: [Number, String], |
||||
default: 0 |
||||
}, |
||||
// 是否自动开始倒计时 |
||||
autoplay: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 用英文冒号(colon)或者中文(zh)当做分隔符,false的时候为中文,如:"11:22"或"11时22秒" |
||||
separator: { |
||||
type: String, |
||||
default: 'colon' |
||||
}, |
||||
// 分隔符的大小,单位rpx |
||||
separatorSize: { |
||||
type: [Number, String], |
||||
default: 30 |
||||
}, |
||||
// 分隔符颜色 |
||||
separatorColor: { |
||||
type: String, |
||||
default: "#303133" |
||||
}, |
||||
// 字体颜色 |
||||
color: { |
||||
type: String, |
||||
default: '#303133' |
||||
}, |
||||
// 字体大小,单位rpx |
||||
fontSize: { |
||||
type: [Number, String], |
||||
default: 30 |
||||
}, |
||||
// 背景颜色 |
||||
bgColor: { |
||||
type: String, |
||||
default: '#fff' |
||||
}, |
||||
// 数字框高度,单位rpx |
||||
height: { |
||||
type: [Number, String], |
||||
default: 'auto' |
||||
}, |
||||
// 是否显示数字框 |
||||
showBorder: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 边框颜色 |
||||
borderColor: { |
||||
type: String, |
||||
default: '#303133' |
||||
}, |
||||
// 是否显示秒 |
||||
showSeconds: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 是否显示分钟 |
||||
showMinutes: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 是否显示小时 |
||||
showHours: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 是否显示“天” |
||||
showDays: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 当"天"的部分为0时,不显示 |
||||
hideZeroDay: { |
||||
type: Boolean, |
||||
default: false |
||||
} |
||||
}, |
||||
watch: { |
||||
// 监听时间戳的变化 |
||||
timestamp(newVal, oldVal) { |
||||
// 如果倒计时间发生变化,清除定时器,重新开始倒计时 |
||||
this.clearTimer(); |
||||
this.start(); |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
d: '00', // 天的默认值 |
||||
h: '00', // 小时的默认值 |
||||
i: '00', // 分钟的默认值 |
||||
s: '00', // 秒的默认值 |
||||
timer: null ,// 定时器 |
||||
seconds: 0, // 记录不停倒计过程中变化的秒数 |
||||
}; |
||||
}, |
||||
computed: { |
||||
// 倒计时item的样式,item为分别的时分秒部分的数字 |
||||
itemStyle() { |
||||
let style = {}; |
||||
if(this.height) { |
||||
style.height = this.height + 'rpx'; |
||||
style.width = this.height + 'rpx'; |
||||
} |
||||
if(this.showBorder) { |
||||
style.borderStyle = 'solid'; |
||||
style.borderColor = this.borderColor; |
||||
style.borderWidth = '1px'; |
||||
} |
||||
if(this.bgColor) { |
||||
style.backgroundColor = this.bgColor; |
||||
} |
||||
return style; |
||||
}, |
||||
// 倒计时数字的样式 |
||||
letterStyle() { |
||||
let style = {}; |
||||
if(this.fontSize) style.fontSize = this.fontSize + 'rpx'; |
||||
if(this.color) style.color = this.color; |
||||
return style; |
||||
} |
||||
}, |
||||
mounted() { |
||||
// 如果自动倒计时 |
||||
this.autoplay && this.timestamp && this.start(); |
||||
}, |
||||
methods: { |
||||
// 倒计时 |
||||
start() { |
||||
// 避免可能出现的倒计时重叠情况 |
||||
this.clearTimer(); |
||||
if (this.timestamp <= 0) return; |
||||
this.seconds = Number(this.timestamp); |
||||
this.formatTime(this.seconds); |
||||
this.timer = setInterval(() => { |
||||
this.seconds--; |
||||
// 发出change事件 |
||||
this.$emit('change', this.seconds); |
||||
if (this.seconds < 0) { |
||||
return this.end(); |
||||
} |
||||
this.formatTime(this.seconds); |
||||
}, 1000); |
||||
}, |
||||
// 格式化时间 |
||||
formatTime(seconds) { |
||||
// 小于等于0的话,结束倒计时 |
||||
seconds <= 0 && this.end(); |
||||
let [day, hour, minute, second] = [0, 0, 0, 0]; |
||||
day = Math.floor(seconds / (60 * 60 * 24)); |
||||
// 判断是否显示“天”参数,如果不显示,将天部分的值,加入到小时中 |
||||
// hour为给后面计算秒和分等用的(基于显示天的前提下计算) |
||||
hour = Math.floor(seconds / (60 * 60)) - day * 24; |
||||
// showHour为需要显示的小时 |
||||
let showHour = null; |
||||
if(this.showDays) { |
||||
showHour = hour; |
||||
} else { |
||||
// 如果不显示天数,将“天”部分的时间折算到小时中去 |
||||
showHour = Math.floor(seconds / (60 * 60)); |
||||
} |
||||
minute = Math.floor(seconds / 60) - hour * 60 - day * 24 * 60; |
||||
second = Math.floor(seconds) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60; |
||||
// 如果小于10,在前面补上一个"0" |
||||
showHour = showHour < 10 ? '0' + showHour : showHour; |
||||
minute = minute < 10 ? '0' + minute : minute; |
||||
second = second < 10 ? '0' + second : second; |
||||
day = day < 10 ? '0' + day : day; |
||||
this.d = day; |
||||
this.h = showHour; |
||||
this.i = minute; |
||||
this.s = second; |
||||
}, |
||||
// 停止倒计时 |
||||
end() { |
||||
this.clearTimer(); |
||||
this.$emit('end', {}); |
||||
}, |
||||
// 清除定时器 |
||||
clearTimer() { |
||||
if(this.timer) { |
||||
// 清除定时器 |
||||
clearInterval(this.timer); |
||||
this.timer = null; |
||||
} |
||||
} |
||||
}, |
||||
beforeDestroy() { |
||||
clearInterval(this.timer); |
||||
this.timer = null; |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-countdown { |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
align-items: center; |
||||
} |
||||
|
||||
.u-countdown-item { |
||||
@include vue-flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
padding: 2rpx; |
||||
border-radius: 6rpx; |
||||
white-space: nowrap; |
||||
transform: translateZ(0); |
||||
} |
||||
|
||||
.u-countdown-time { |
||||
margin: 0; |
||||
padding: 0; |
||||
line-height: 1; |
||||
} |
||||
|
||||
.u-countdown-colon { |
||||
@include vue-flex; |
||||
justify-content: center; |
||||
padding: 0 5rpx; |
||||
line-height: 1; |
||||
align-items: center; |
||||
padding-bottom: 4rpx; |
||||
} |
||||
|
||||
.u-countdown-scale { |
||||
transform: scale(0.9); |
||||
transform-origin: center center; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,241 @@ |
||||
<template> |
||||
<view |
||||
class="u-count-num" |
||||
:style="{ |
||||
fontSize: fontSize + 'rpx', |
||||
fontWeight: bold ? 'bold' : 'normal', |
||||
color: color |
||||
}" |
||||
> |
||||
{{ displayValue }} |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* countTo 数字滚动 |
||||
* @description 该组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值。 |
||||
* @tutorial https://www.uviewui.com/components/countTo.html |
||||
* @property {String Number} start-val 开始值 |
||||
* @property {String Number} end-val 结束值 |
||||
* @property {String Number} duration 滚动过程所需的时间,单位ms(默认2000) |
||||
* @property {Boolean} autoplay 是否自动开始滚动(默认true) |
||||
* @property {String Number} decimals 要显示的小数位数,见官网说明(默认0) |
||||
* @property {Boolean} use-easing 滚动结束时,是否缓动结尾,见官网说明(默认true) |
||||
* @property {String} separator 千位分隔符,见官网说明 |
||||
* @property {String} color 字体颜色(默认#303133) |
||||
* @property {String Number} font-size 字体大小,单位rpx(默认50) |
||||
* @property {Boolean} bold 字体是否加粗(默认false) |
||||
* @event {Function} end 数值滚动到目标值时触发 |
||||
* @example <u-count-to ref="uCountTo" :end-val="endVal" :autoplay="autoplay"></u-count-to> |
||||
*/ |
||||
export default { |
||||
name: 'u-count-to', |
||||
props: { |
||||
// 开始的数值,默认从0增长到某一个数 |
||||
startVal: { |
||||
type: [Number, String], |
||||
default: 0 |
||||
}, |
||||
// 要滚动的目标数值,必须 |
||||
endVal: { |
||||
type: [Number, String], |
||||
default: 0, |
||||
required: true |
||||
}, |
||||
// 滚动到目标数值的动画持续时间,单位为毫秒(ms) |
||||
duration: { |
||||
type: [Number, String], |
||||
default: 2000 |
||||
}, |
||||
// 设置数值后是否自动开始滚动 |
||||
autoplay: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 要显示的小数位数 |
||||
decimals: { |
||||
type: [Number, String], |
||||
default: 0 |
||||
}, |
||||
// 是否在即将到达目标数值的时候,使用缓慢滚动的效果 |
||||
useEasing: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
// 十进制分割 |
||||
decimal: { |
||||
type: [Number, String], |
||||
default: '.' |
||||
}, |
||||
// 字体颜色 |
||||
color: { |
||||
type: String, |
||||
default: '#303133' |
||||
}, |
||||
// 字体大小 |
||||
fontSize: { |
||||
type: [Number, String], |
||||
default: 50 |
||||
}, |
||||
// 是否加粗字体 |
||||
bold: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 千位分隔符,类似金额的分割(¥23,321.05中的",") |
||||
separator: { |
||||
type: String, |
||||
default: '' |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
localStartVal: this.startVal, |
||||
displayValue: this.formatNumber(this.startVal), |
||||
printVal: null, |
||||
paused: false, // 是否暂停 |
||||
localDuration: Number(this.duration), |
||||
startTime: null, // 开始的时间 |
||||
timestamp: null, // 时间戳 |
||||
remaining: null, // 停留的时间 |
||||
rAF: null, |
||||
lastTime: 0 // 上一次的时间 |
||||
}; |
||||
}, |
||||
computed: { |
||||
countDown() { |
||||
return this.startVal > this.endVal; |
||||
} |
||||
}, |
||||
watch: { |
||||
startVal() { |
||||
this.autoplay && this.start(); |
||||
}, |
||||
endVal() { |
||||
this.autoplay && this.start(); |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.autoplay && this.start(); |
||||
}, |
||||
methods: { |
||||
easingFn(t, b, c, d) { |
||||
return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b; |
||||
}, |
||||
requestAnimationFrame(callback) { |
||||
const currTime = new Date().getTime(); |
||||
// 为了使setTimteout的尽可能的接近每秒60帧的效果 |
||||
const timeToCall = Math.max(0, 16 - (currTime - this.lastTime)); |
||||
const id = setTimeout(() => { |
||||
callback(currTime + timeToCall); |
||||
}, timeToCall); |
||||
this.lastTime = currTime + timeToCall; |
||||
return id; |
||||
}, |
||||
|
||||
cancelAnimationFrame(id) { |
||||
clearTimeout(id); |
||||
}, |
||||
// 开始滚动数字 |
||||
start() { |
||||
this.localStartVal = this.startVal; |
||||
this.startTime = null; |
||||
this.localDuration = this.duration; |
||||
this.paused = false; |
||||
this.rAF = this.requestAnimationFrame(this.count); |
||||
}, |
||||
// 暂定状态,重新再开始滚动;或者滚动状态下,暂停 |
||||
reStart() { |
||||
if (this.paused) { |
||||
this.resume(); |
||||
this.paused = false; |
||||
} else { |
||||
this.stop(); |
||||
this.paused = true; |
||||
} |
||||
}, |
||||
// 暂停 |
||||
stop() { |
||||
this.cancelAnimationFrame(this.rAF); |
||||
}, |
||||
// 重新开始(暂停的情况下) |
||||
resume() { |
||||
this.startTime = null; |
||||
this.localDuration = this.remaining; |
||||
this.localStartVal = this.printVal; |
||||
this.requestAnimationFrame(this.count); |
||||
}, |
||||
// 重置 |
||||
reset() { |
||||
this.startTime = null; |
||||
this.cancelAnimationFrame(this.rAF); |
||||
this.displayValue = this.formatNumber(this.startVal); |
||||
}, |
||||
count(timestamp) { |
||||
if (!this.startTime) this.startTime = timestamp; |
||||
this.timestamp = timestamp; |
||||
const progress = timestamp - this.startTime; |
||||
this.remaining = this.localDuration - progress; |
||||
if (this.useEasing) { |
||||
if (this.countDown) { |
||||
this.printVal = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration); |
||||
} else { |
||||
this.printVal = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration); |
||||
} |
||||
} else { |
||||
if (this.countDown) { |
||||
this.printVal = this.localStartVal - (this.localStartVal - this.endVal) * (progress / this.localDuration); |
||||
} else { |
||||
this.printVal = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration); |
||||
} |
||||
} |
||||
if (this.countDown) { |
||||
this.printVal = this.printVal < this.endVal ? this.endVal : this.printVal; |
||||
} else { |
||||
this.printVal = this.printVal > this.endVal ? this.endVal : this.printVal; |
||||
} |
||||
this.displayValue = this.formatNumber(this.printVal); |
||||
if (progress < this.localDuration) { |
||||
this.rAF = this.requestAnimationFrame(this.count); |
||||
} else { |
||||
this.$emit('end'); |
||||
} |
||||
}, |
||||
// 判断是否数字 |
||||
isNumber(val) { |
||||
return !isNaN(parseFloat(val)); |
||||
}, |
||||
formatNumber(num) { |
||||
// 将num转为Number类型,因为其值可能为字符串数值,调用toFixed会报错 |
||||
num = Number(num); |
||||
num = num.toFixed(Number(this.decimals)); |
||||
num += ''; |
||||
const x = num.split('.'); |
||||
let x1 = x[0]; |
||||
const x2 = x.length > 1 ? this.decimal + x[1] : ''; |
||||
const rgx = /(\d+)(\d{3})/; |
||||
if (this.separator && !this.isNumber(this.separator)) { |
||||
while (rgx.test(x1)) { |
||||
x1 = x1.replace(rgx, '$1' + this.separator + '$2'); |
||||
} |
||||
} |
||||
return x1 + x2; |
||||
}, |
||||
destroyed() { |
||||
this.cancelAnimationFrame(this.rAF); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
|
||||
.u-count-num { |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
text-align: center; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,153 @@ |
||||
<template> |
||||
<view class="u-divider" :style="{ |
||||
height: height == 'auto' ? 'auto' : height + 'rpx', |
||||
backgroundColor: bgColor, |
||||
marginBottom: marginBottom + 'rpx', |
||||
marginTop: marginTop + 'rpx' |
||||
}" @tap="click"> |
||||
<view class="u-divider-line" :class="[type ? 'u-divider-line--bordercolor--' + type : '']" :style="[lineStyle]"></view> |
||||
<view v-if="useSlot" class="u-divider-text" :style="{ |
||||
color: color, |
||||
fontSize: fontSize + 'rpx' |
||||
}"><slot /></view> |
||||
<view class="u-divider-line" :class="[type ? 'u-divider-line--bordercolor--' + type : '']" :style="[lineStyle]"></view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* divider 分割线 |
||||
* @description 区隔内容的分割线,一般用于页面底部"没有更多"的提示。 |
||||
* @tutorial https://www.uviewui.com/components/divider.html |
||||
* @property {String Number} half-width 文字左或右边线条宽度,数值或百分比,数值时单位为rpx |
||||
* @property {String} border-color 线条颜色,优先级高于type(默认#dcdfe6) |
||||
* @property {String} color 文字颜色(默认#909399) |
||||
* @property {String Number} fontSize 字体大小,单位rpx(默认26) |
||||
* @property {String} bg-color 整个divider的背景颜色(默认呢#ffffff) |
||||
* @property {String Number} height 整个divider的高度,单位rpx(默认40) |
||||
* @property {String} type 将线条设置主题色(默认primary) |
||||
* @property {Boolean} useSlot 是否使用slot传入内容,如果不传入,中间不会有空隙(默认true) |
||||
* @property {String Number} margin-top 与前一个组件的距离,单位rpx(默认0) |
||||
* @property {String Number} margin-bottom 与后一个组件的距离,单位rpx(0) |
||||
* @event {Function} click divider组件被点击时触发 |
||||
* @example <u-divider color="#fa3534">长河落日圆</u-divider> |
||||
*/ |
||||
export default { |
||||
name: 'u-divider', |
||||
props: { |
||||
// 单一边divider横线的宽度(数值),单位rpx。或者百分比 |
||||
halfWidth: { |
||||
type: [Number, String], |
||||
default: 150 |
||||
}, |
||||
// divider横线的颜色,如设置, |
||||
borderColor: { |
||||
type: String, |
||||
default: '#dcdfe6' |
||||
}, |
||||
// 主题色,可以是primary|info|success|warning|error之一值 |
||||
type: { |
||||
type: String, |
||||
default: 'primary' |
||||
}, |
||||
// 文字颜色 |
||||
color: { |
||||
type: String, |
||||
default: '#909399' |
||||
}, |
||||
// 文字大小,单位rpx |
||||
fontSize: { |
||||
type: [Number, String], |
||||
default: 26 |
||||
}, |
||||
// 整个divider的背景颜色 |
||||
bgColor: { |
||||
type: String, |
||||
default: '#ffffff' |
||||
}, |
||||
// 整个divider的高度单位rpx |
||||
height: { |
||||
type: [Number, String], |
||||
default: 'auto' |
||||
}, |
||||
// 上边距 |
||||
marginTop: { |
||||
type: [String, Number], |
||||
default: 0 |
||||
}, |
||||
// 下边距 |
||||
marginBottom: { |
||||
type: [String, Number], |
||||
default: 0 |
||||
}, |
||||
// 是否使用slot传入内容,如果不用slot传入内容,先的中间就不会有空隙 |
||||
useSlot: { |
||||
type: Boolean, |
||||
default: true |
||||
} |
||||
}, |
||||
computed: { |
||||
lineStyle() { |
||||
let style = {}; |
||||
if(String(this.halfWidth).indexOf('%') != -1) style.width = this.halfWidth; |
||||
else style.width = this.halfWidth + 'rpx'; |
||||
// borderColor优先级高于type值 |
||||
if(this.borderColor) style.borderColor = this.borderColor; |
||||
return style; |
||||
} |
||||
}, |
||||
methods: { |
||||
click() { |
||||
this.$emit('click'); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import "../../libs/css/style.components.scss"; |
||||
.u-divider { |
||||
width: 100%; |
||||
position: relative; |
||||
text-align: center; |
||||
@include vue-flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
overflow: hidden; |
||||
flex-direction: row; |
||||
} |
||||
|
||||
.u-divider-line { |
||||
border-bottom: 1px solid $u-border-color; |
||||
transform: scale(1, 0.5); |
||||
transform-origin: center; |
||||
|
||||
&--bordercolor--primary { |
||||
border-color: $u-type-primary; |
||||
} |
||||
|
||||
&--bordercolor--success { |
||||
border-color: $u-type-success; |
||||
} |
||||
|
||||
&--bordercolor--error { |
||||
border-color: $u-type-primary; |
||||
} |
||||
|
||||
&--bordercolor--info { |
||||
border-color: $u-type-info; |
||||
} |
||||
|
||||
&--bordercolor--warning { |
||||
border-color: $u-type-warning; |
||||
} |
||||
} |
||||
|
||||
.u-divider-text { |
||||
white-space: nowrap; |
||||
padding: 0 16rpx; |
||||
/* #ifndef APP-NVUE */ |
||||
display: inline-flex; |
||||
/* #endif */ |
||||
} |
||||
</style> |
||||
@ -0,0 +1,132 @@ |
||||
<template> |
||||
<view class="u-dropdown-item" v-if="active" @touchmove.stop.prevent="() => {}" @tap.stop.prevent="() => {}"> |
||||
<block v-if="!$slots.default && !$slots.$default"> |
||||
<scroll-view scroll-y="true" :style="{ |
||||
height: $u.addUnit(height) |
||||
}"> |
||||
<view class="u-dropdown-item__options"> |
||||
<u-cell-group> |
||||
<u-cell-item @click="cellClick(item.value)" :arrow="false" :title="item.label" v-for="(item, index) in options" |
||||
:key="index" :title-style="{ |
||||
color: value == item.value ? activeColor : inactiveColor |
||||
}"> |
||||
<u-icon v-if="value == item.value" name="checkbox-mark" :color="activeColor" size="32"></u-icon> |
||||
</u-cell-item> |
||||
</u-cell-group> |
||||
</view> |
||||
</scroll-view> |
||||
</block> |
||||
<slot v-else /> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* dropdown-item 下拉菜单 |
||||
* @description 该组件一般用于向下展开菜单,同时可切换多个选项卡的场景 |
||||
* @tutorial http://uviewui.com/components/dropdown.html |
||||
* @property {String | Number} v-model 双向绑定选项卡选择值 |
||||
* @property {String} title 菜单项标题 |
||||
* @property {Array[Object]} options 选项数据,如果传入了默认slot,此参数无效 |
||||
* @property {Boolean} disabled 是否禁用此选项卡(默认false) |
||||
* @property {String | Number} duration 选项卡展开和收起的过渡时间, | ||||