Commit f21fbd7c authored by PC-20180318CEYD\Administrator's avatar PC-20180318CEYD\Administrator

Merge branch 'tonghao' into tmp

# Conflicts:
#	uview/components/u-alert-tips/u-alert-tips.vue
#	uview/components/u-badge/u-badge.vue
#	uview/components/u-count-down/u-count-down.vue
#	uview/components/u-count-to/u-count-to.vue
#	uview/components/u-divider/u-divider.vue
#	uview/components/u-gap/u-gap.vue
#	uview/components/u-grid/u-grid.vue
#	uview/components/u-lazy-load/u-lazy-load.vue
#	uview/components/u-loadmore/u-loadmore.vue
#	uview/components/u-message-input/u-message-input.vue
#	uview/components/u-navbar/u-navbar.vue
#	uview/components/u-notice-bar/u-notice-bar.vue
#	uview/components/u-search/u-search.vue
#	uview/components/u-section/u-section.vue
parents 09385673 da09bd81
<template> <template>
<u-popup mode="bottom" :border-radius="borderRadius" <u-popup mode="bottom" :border-radius="borderRadius" :popup="false" v-model="value" :maskCloseAble="maskCloseAble"
:popup="false" v-model="value" :maskCloseAble="maskCloseAble" length="auto" :safeAreaInsetBottom="safeAreaInsetBottom" @close="popupClose" :z-index="uZIndex">
length="auto" :safeAreaInsetBottom="safeAreaInsetBottom" <view class="u-tips u-border-bottom" v-if="tips.text" :style="tipsStyle">
@close="popupClose" {{tips.text}}
:z-index="uZIndex" </view>
> <block v-for="(item, index) in list" :key="index">
<view class="u-tips u-border-bottom" v-if="tips.text" :style="tipsStyle"> <view @touchmove.stop.prevent @tap="itemClick(index)" :style="[itemStyle(index)]" class="u-action-sheet-item" :class="[index < list.length - 1 ? 'u-border-bottom' : '']"
{{tips.text}} hover-class="u-hover-class" :hover-stay-time="150">
</view> {{item.text}}
<block v-for="(item, index) in list" :key="index"> </view>
<view @touchmove.stop.prevent @tap="itemClick(index)" :style="[itemStyle(index)]" class="u-action-sheet-item" :class="[index < list.length - 1 ? 'u-border-bottom' : '']" </block>
hover-class="u-hover-class" :hover-stay-time="150"> <view class="u-gab" v-if="cancelBtn">
{{item.text}} </view>
</view> <view @touchmove.stop.prevent class="u-actionsheet-cancel u-action-sheet-item" hover-class="u-hover-class"
</block> :hover-stay-time="150" v-if="cancelBtn" @tap="close">取消</view>
<view class="u-gab" v-if="cancelBtn"> </u-popup>
</view> </template>
<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">取消</view> <script>
</u-popup> /**
</template> * alertTips 提示
* @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
<script> * @tutorial https://www.uviewui.com/components/actionSheet.html
export default { * @property {Array<Object>} list 按钮的文字数组,见官方文档示例
props: { * @property {Object} tips 顶部的提示文字,见官方文档示例
// 点击遮罩是否可以关闭actionsheet * @property {Boolean} cancel-btn 是否显示底部的取消按钮(默认true)
maskCloseAble: { * @property {Number String} border-radius 弹出部分顶部左右的圆角值,单位rpx(默认0)
type: Boolean, * @property {Boolean} mask-close-able 点击遮罩是否可以关闭(默认true)
default: true * @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
}, * @property {Number String} z-index z-index值(默认1075)
// 按钮的文字数组,可以自定义颜色和字体大小,字体单位为rpx * @event {Function} click 点击ActionSheet列表项时触发
list: { * @event {Function} close 点击取消按钮时触发
type: Array, * @example <u-action-sheet :list="list" @click="click" v-model="show"></u-action-sheet>
default () { */
// 如下 export default {
// return [{ name: "u-action-sheet",
// text: '确定', props: {
// color: '', // 点击遮罩是否可以关闭actionsheet
// fontSize: '' maskCloseAble: {
// }] type: Boolean,
return []; default: true
} },
}, // 按钮的文字数组,可以自定义颜色和字体大小,字体单位为rpx
// 顶部的提示文字 list: {
tips: { type: Array,
type: Object, default () {
default () { // 如下
return { // return [{
text: '', // text: '确定',
color: '', // color: '',
fontSize: '26' // fontSize: ''
} // }]
} return [];
}, }
// 底部的取消按钮 },
cancelBtn: { // 顶部的提示文字
type: Boolean, tips: {
default: true type: Object,
}, default () {
// 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距 return {
safeAreaInsetBottom: { text: '',
type: Boolean, color: '',
default: false fontSize: '26'
}, }
// 通过双向绑定控制组件的弹出与收起 }
value: { },
type: Boolean, // 底部的取消按钮
default: false cancelBtn: {
}, type: Boolean,
// 弹出的顶部圆角值 default: true
borderRadius: { },
type: [String, Number], // 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距
default: 0 safeAreaInsetBottom: {
}, type: Boolean,
// 弹出的z-index值 default: false
zIndex: { },
type: [String, Number], // 通过双向绑定控制组件的弹出与收起
default: 0 value: {
} type: Boolean,
}, default: false
computed: { },
// 顶部提示的样式 // 弹出的顶部圆角值
tipsStyle() { borderRadius: {
let style = {}; type: [String, Number],
if (this.tips.color) style.color = this.tips.color; default: 0
if (this.tips.fontSize) style.fontSize = this.tips.fontSize + 'rpx'; },
return style; // 弹出的z-index值
}, zIndex: {
// 操作项目的样式 type: [String, Number],
itemStyle() { default: 0
return (index) => { }
let style = {}; },
if (this.list[index].color) style.color = this.list[index].color; computed: {
if (this.list[index].fontSize) style.fontSize = this.list[index].fontSize + 'rpx'; // 顶部提示的样式
return style; tipsStyle() {
} let style = {};
}, if (this.tips.color) style.color = this.tips.color;
uZIndex() { if (this.tips.fontSize) style.fontSize = this.tips.fontSize + 'rpx';
// 如果用户有传递z-index值,优先使用 return style;
return this.zIndex ? this.zIndex : this.$u.zIndex.popup; },
} // 操作项目的样式
}, itemStyle() {
methods: { return (index) => {
// 点击取消按钮 let style = {};
close() { if (this.list[index].color) style.color = this.list[index].color;
// 发送input事件,并不会作用于父组件,而是要设置组件内部通过props传递的value参数 if (this.list[index].fontSize) style.fontSize = this.list[index].fontSize + 'rpx';
// 这是一个vue发送事件的特殊用法 return style;
this.popupClose(); }
this.$emit('close'); },
}, uZIndex() {
// 弹窗关闭 // 如果用户有传递z-index值,优先使用
popupClose() { return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
this.$emit('input', false); }
}, },
// 点击某一个itemif (!this.show) return; methods: {
itemClick(index) { // 点击取消按钮
this.$emit('click', index); close() {
this.$emit('input', false); // 发送input事件,并不会作用于父组件,而是要设置组件内部通过props传递的value参数
} // 这是一个vue发送事件的特殊用法
} this.popupClose();
} this.$emit('close');
</script> },
// 弹窗关闭
<style lang="scss" scoped> popupClose() {
.u-tips { this.$emit('input', false);
font-size: 26rpx; },
text-align: center; // 点击某一个itemif (!this.show) return;
padding: 34rpx 0; itemClick(index) {
line-height: 1; this.$emit('click', index);
color: $u-tips-color; this.$emit('input', false);
} }
}
.u-action-sheet-item { }
display: flex; </script>
line-height: 1;
justify-content: center; <style lang="scss" scoped>
align-items: center; .u-tips {
font-size: 34rpx; font-size: 26rpx;
padding: 34rpx 0; text-align: center;
} padding: 34rpx 0;
line-height: 1;
.u-gab { color: $u-tips-color;
height: 12rpx; }
background-color: rgb(234, 234, 236);
} .u-action-sheet-item {
display: flex;
.u-actionsheet-cancel { line-height: 1;
color: $u-main-color; justify-content: center;
} align-items: center;
font-size: 34rpx;
padding: 34rpx 0;
}
.u-gab {
height: 12rpx;
background-color: rgb(234, 234, 236);
}
.u-actionsheet-cancel {
color: $u-main-color;
}
</style> </style>
...@@ -7,6 +7,16 @@ ...@@ -7,6 +7,16 @@
</template> </template>
<script> <script>
/**
* alertTips 提示
* @description 该组件一般的图片裁剪需求场景,尤其适合于头像裁剪方面。
* @tutorial https://www.uviewui.com/components/avatarCropper.html
* @property {String Number} dest-width 输出图片宽度,高等于宽,单位px(默认200)
* @property {String Number} rect-width 裁剪框宽度,高等于宽,单位px(默认200)
* @property {String} file-type 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可(默认jpg)
* @event {Function} uAvatarCropper 裁剪结束后的事件,通过uni.$on监听
* @example <image class="u-avatar-demo" :src="avatar" mode="aspectFill"></image>
*/
let base64Avatar = "data:image/png;base64,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"; let base64Avatar = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAeO0lEQVR4Xu1dCbQcVZn+/+q3kOSFJYRAlpek69aLGcjoROOGIIogBxdwAxUZBycMLkedGUGRgApHIgzojKM448Iw45kjKG4YDEcZBWUZRg3qUUBeuqreewkJELKQBcLLS9c/52u64lu6X1dVV3fXcv9zKv3y3q27fPd+/d/lv//PpEUjoBGoiwBrbNqHwNatW2eOjo4e7nne4UR0eKFQmC0ih+PB/6ufQkR7mHkPPsvlcuWzt7d39+joaOXngYGB0fbVOt8laYLE3P8bN240u7q6FDOrcrls4ZOILCLC54w4imPmnSLiEJHNzI7neQ4z26Ojo87xxx//eBxl6DyeR0ATpImR4LruC4noFBE5lYhOqJLAaCLLpl9l5meq5HmQme9m5nuLxeJw0xnnNANNkBAd7zjOKhF5BRGdxMyvJaJ5IV7vZNKNRHSXiDzAzP+nlML/tQRAQBNkGpBGRkbMsbGxN1YJ8Uoi6g+AaRqS/FFEfgUNs2fPnjtWrlz5dBoq3Yk6aoJMQr1UKi1i5jcahvEGEQE5Cp3omDaWuU1E7jAMY313d/f6/v7+/W0sO/FFaYJgpWvb8wqFwhs8zwMh8MSymE5870+t4GNEtN7zvDssy1rPzOUUtiHWKueaII7jvI2I3s7M0BZHxopsyjMTERdaRURuVUrdl/LmRK5+7ggCbcHM5xERnpdGRi5fL64nopuVUjfnq9k52ubFDlSVFCDGsXnr6Jja+0cQpUqWTTHlmehsMq9BhoeHzyyXy6sxlUp0T6SrcrtBEmb+qmmaf0hX1cPVNrMEsW37RGb+MBG9OxwkOnUIBJ5j5huI6AbTNEdCvJeapJkjyMjIyPEHDx78CBF9IDW9kP6KPikiNzDzl5VS0C6ZkcwQZHBwcGFXVxc0Bp6+zPRQihrCzINVbQKtkgnJBEFc171QRK4goiWZ6JX0N+Iez/PWDgwM3Jn2pqSaIDAW9DzvCmY+J+0dkcX6M/PnReTqNE+7UksQ27YvYWZojSOyOLgy1KY/MPPVpml+N41tSh1BXNd9NRFdLiKvTyPgea2ziNxoGAaIkqrdrlQRpFQqrTEMY21eB1kG2j0kImssy/p2WtqSCoKUSqUTmPlzzHxWWoDV9ZwWgS/u2rXr8lWrVj2bdJwSTxDHcf6WmdeKyHFJB1PXLzgCzPyA53mXW5Z1d/C32p8ysQQZHByc293dDWJc1H5YdIltQsDDelIpdW2bygtdTCIJsnHjxlcXCoUvEdGLQrdIv5BGBH7Q09Pzd/39/TuTVvnEEcS27b/BaSwz69PwpI2WFtZHRH7X3d19/pIlSx5pYTGhs04UQUql0pWGYXwmdCv0C1lBAHfj36GU+nlSGpQYgjiO81Uien9SgNH16CgC5yulvtXRGlQLTwRBXNe9TUTOTgIgug7JQICZLzFN8wudrk3HCeK67t0i8ppOA6HLTx4CnuddNTAwcGUna9ZRgmhydLLr01F2p0nSMYJocqRjgCahlp0kSUcIosmRhGGXujpcqZS6qt21bjtBNDna3cXZKa8TmqStBHEc56dEpM3UszNm294SEbnEsqy27W61jSCO43yOiC5rO6K6wMwh4HneawcGBn7Rjoa1hSClUul8wzD+ux0N0mXkA4Fyubxy2bJlv291a1tOEMdx3kREt7e6IVnJ3zAM6urqqjTn4MGD5HkweNVSA4G9hmG8sNXBgVpKEMdxTiKie3X3TkVgxowZFSJ0d3dP+CwUJkZbKJfLFaKMjY1N+Ny/X0cpYOZfMPNbi8Viy+KbtIwgtm2vYOYfVuPzaY4QUW9vL82aNYv6+voOaYmowIA0+/bto2eeeYZGR3Md0/M2pdRbo+LY6L2WEcRxnB9XY200qkOm/w5SzJw5s0KMnp6elrT1wIEDFaI8++yzuSSLiFxvWdYnWgFuSwjiOM6niajthzqtAChqntASRxxxREVrtFOgTXbv3l3RLjmT85RSt8Td5tgJUiqVzjAM4ydxVzQt+YEQIAYI0kkBQUCUvEy/ROSxQqFwerFYfDRO3GMlyNDQ0JGe5/2MiF4SZyXTkBcz05FHHll58HMSRETo6aefrjz4OQdyu1IqVs83sfak4zhfIaIP5aAjJjSxU9OpoDjnadolIp+1LAtT/FgkNoK4rvteEflmLLVKUSZz5sypaI00CDTJzp2J84sQO3SGYbylWCz+KI6MYyHI0NDQceVy+V5mtuKoVFrymDt3Lh1++OFpqW6lnnv27KHt27enqs4RKrth165dp8ThmC4Wgti2/SVmRtCaXAjWGMccc0zHF+JRwcYC/qmnnsr0ugSeOE3TvDwqRv57TRPEtu0zmfmOZiuSlvdx0g1y4GwjzYIzE5AEJ/VZlTiMGpsmiOM49xDRyVkFeXy7YBYCchx22GGZaO5zzz1XIQnMWDIqP1NKnd5M25oiiOM4MF+HGXvmBZpj/vz5LTsN7xSAOIXfunVrZo0iReRSy7Kui4pvZIKUSqWVzHxPXjwgQnPMnj07Ks6Jfi/jC/c9hmGcUiwWI5nGRyaI4zg3EdH7Et3zMVUOO1XYscqybNu2LbPmKcz8ddM0IzkljEQQ27ZPZebEuIds5cCF6QimVrinkWWBdTCmWvjMoojIKsuyHgzbtkgEcRznB0TUMhPjsI1oZXqQA3c38iDY/oUmyaJE1SKhCWLb9luZGQTJvKTplDyuzsAhItYkWZQoWiQ0QRzHwdTq1CwCOL5NOOc47rj8BbXCFV9MtbC7lTWJokVCEcRxHCzKsTjPvBx77LGVS055lCzvaoXVImEJ8hsiWpX1QYM1B9YeeZYtW7Zk9S7JV5VSHwzat4EJYtv2O5k5NeF7gwJQK928efNSa2fVTLvHv5thLfKcYRgrg16sCkyQvNwxhxnJggUL4hpnqc4nw1rkWqVUICeGgQhSKpVeYxhGosP1xjUSs3xiHhajDGuRJ6pa5IlGmAQiiOu6N4rI6kaZpf3vOBRcuHBh2psRa/0zrEUuCxJ+uiFBNm7c+BeFQgF2LK3xWRNrdzaXmdYeU/HLsBZ5tKpFnptu1DQkiOu6a0VkTXNDL/lv4xLU4sWLabJnw+TXvLU1xH2RTZs2ZfVy1QeVUggeW1emJchDDz3UM3PmzD+JiNnabuh87nk9GAyC/BNPPFFxSpc1YeY7TdM8IzJBSqXSuYZhfCdrwNRqTx7NSoL2a5adPTDzi0zT/EM9LKbVII7jIFb1eUGBTHM6LM7b7QUxLXjBbRAW61kUEfmUZVlXhyaI4ziLiehPRJTuy9cBehVe1rH+0FIfAaxDMmoK/2ul1MtDE8S27Y8y87/mYdDgpiB2sLTURwB31/fu3ZtJiJj5FNM04VthitSdYuXFaheIaNOSxuM+43dFPm+a5scDE8R13ZeJyK8aw5aNFEuXLs38jcFmewpm8MPDw81mk8j3mXnQNM3lgQniOM4/EtE/J7I1MVcKV2lBEC2NEQBBshoSTkROsizr/sko1Jxi2bZ9KzOf0xiy9KeAr6v+/v70N6QNLdi8eXNmfWiJyCcsy7o+KEE2M/OiNmDe8SK09W7wLsBNQziby6KIyI8sy3pLQ4K4rvsiEYnkQyiNwOHWIG4PammMwJNPPlkJ9ZZReUopNa8hQRzHQXwPxPnIheTB51VcHZllhw5VjF6mlMKt2UMyZQ3iOM7NRPTuuEBNej5HHXUU4dHSGIFdu3YRngzLxUqpCZtTtQgyQkS5OVZOY4yPTg3QDJu+VyCttQ6ZQJBNmzYtGBsby6bRTZ1RlWfvJWGJhvUH1iEZlieUUhO8dUwgyNDQ0Nme592WYQCmNC1PnhOb7VfsYGEnK8siIosty9rst3ECQWzbvpqZm47KkyYA4Rwu7cFw2oU37oTgbkiWRUTeZlnWD2sSxHGcnxLR67MMwOS2aTus4L2dgykWwLhGKXXoBu0EDeK67g4RmRMcsvSn1PfQg/chrHlh1ZtxmRCV6hBBSqWSMgzDznjjpzTv6KOPpiOOOCJvzY7U3qzvYlVB2a2UOhTX+xBBbNt+FzPfEgm5FL+kr9oG77wsX72dhMIypVQJvztEEMdxEGswkLe54JAmP6U+SQ/eRzt27KDdu3cHfyG9Kd+slPrxZIL8JxFdkN42Rau5NlYMjtvjjz9O+/fvD/5CSlOKyPsty/r6ZILkbgcLAOj7IMFH8cjISKbjqvtIeJ531cDAwJWTCQLXJ38ZHK7spMR9ENwL0VIfATiQA0HyIOMD7YxfpG9n5qPzAMDkNmpzk8a9jqkVplh5EGa+3TTNsw5pEBHpcV13NA+Nr9VGbdHbuOexOMciPSfyoFKqEiiqokGGhoaWep43lJPGT2lmT08PLVqUiwuUkbs4Lwv0CimYt5qmWXHzXyHI8PDwK8vl8v9GRi8DL4IgIIqWqQjkaf3ht14pVeFG5R/XdV8vItjFyq3oaVb9rs/JCfoEALq6uuYsWbJkV4UgpVLpbMMwcmXmPnk46POQ+gTJ+F30mg0vl8tq2bJlrr8GeafnebkI0DmditTbvbWnV3D3k1V/WPXGgx8uukIQ27YvYGacpOdatF3W1O7P4/SqisLpSqmfVQjiOM4HiOjfc80OosphIRbriDal5XkEMhyjcNouZuZzTdP8rk+Q3LgabTTwtROHPyOUY+0BBw4VeyyfILDihTVv7kUv1v88BPKqPaoIfFIp9U/+Nu8VIvLZ3LOjCoA2PSHKs/aoDoM1SqlrfA3yD0T0L5ogzyMwY8YMgreTvAp2rOC95MCBA3mFAOvQj5imeYOvQVaLyI25RaNGw/O8o7Vz507C7cE8i4i8z7Ks//IJcq6I5CKabdBOxz2RBQsW5M78JA++rwKOgXcopb7v22KdWS6X7wj4Ym6S9fX1VcKz5UnyZJQ4Xb8y8xmmad5ZIcjg4ODJXV1dNYMY5mlw1GprntwC5cykfdqhXSgUTly6dOkDFYJs3LjxrwqFwu/yToZa7cehIRbs2P7NsmBqBe0hIlluZpi2vVAp9UffWDGXPrGColUoFGjhwoWEeOpZFJizY9dqbGwsi82L1CbDMIrFYnHYt8Wax8yZdtsdCaVxL2U5lqFed0wdHb29vXMXLVq0o0KQDRs2zDzqqKMyG1urWXL47/f29lY0SZYErkThUlTLRARM0+xl5gPjHceBIDM1UNMjkCVTlBxEjIo6nLcrpY7By+O9mvyWmVdGzTFP72WBJAhjgHAGWmoicL9S6qTJBLmFmd+lAQuGQJrNUfSao2Ef36SUWj2BIKVS6UrDMD7T8FWd4BACCLyDADxpEexSbdu2jUZHc+vhKVBXicillmVdN1mD5NK7eyDEpkkETQKHD0k/J8F0CjZWeTZADNrXzHy2aZrrJmuQlYZh/DZoJjrdnxHAYSJIcuSRh8JKJAYeHPxhMZ5348MwHcLMy03THJxAEL3VGwbC2mkx5QJRsB2cBIG7UJADp+RagiPg+8SaQBD8x7btTczcHzwrnXIyArACRsQqxB3BCXwnBCfjuPAEcmgJjcCgUmq5/9YE7wSO49xJRKeHzlK/MAUBkAMkaSdRfGKAHPhZS3gERGSdZVln1ySI67pfFpEPh89Wv1EPARBl1qxZlVuKWMi3QqtgCoXplCZGLOPwOqXUpTUJMjQ0dIHnebn2jwX/vLgH4i9uY4G8mgmmXyAJyIJ1StSdr4MHD1a2akEK7E7h/3EK7M5ybLh4KPzalDVIXiPd4hseDxbZGMS+tPp+BMryiYKfxz8gKO6G+w9IgC1aPK30cujHjYdWgo0WYqO3srw4iR1HXvv375+9YsWKfTU1CH7pOM5mIsp0LABsy/qEmEyKySDnJDZ4hZw49Kyl1Xyi5MA05R6l1Cnjx8AUF4KO43yLiM6Lg41JywP3OTB9whMm1AG+RbEjlNVDNmCBm5ONtqfRfpAEeGTxNJ6ZP2Wa5tXTEqRUKn3IMIyvJG1wN1MfDIDZs2dXiBF1kYxpBkiStTDI2JLG2c34qWUQrPft21eZgmUp6q3nea8dGBj4RSMN8lIi+nUQkJKeBothnxhx1RXfnjiVTvs3KL40QAxMNZsR4AGy4DPlsmf//v3HrFixYoIzsJpemh3H2UZEFXv4NArm0fhmbLbz67UdC2iQBE/a7nD7B5nAJ6zWmG4sQJNAo4AsKZUfKqXeNrnuNQli2/ZtMNhKW0OxPemfYrej7tAimHKlZVD42LQy5HXaMPHHiYh83LKszwciiOu6l4vIhMVKOwZc1DJa9a0YtD749vTn5EHfaVc6rLn8jYlGi/A465S2qaiIvMqyrClxOmtqkDTFLIQpB74ZW/mtGHTgYJcHRMET9+Fd0Dr46fwDT6zBom5MhC1zcnpMP6FhMRVN+FnKBPur8e2oGynGtu0HmfnFzYLUqvexZQv/ufh2TJpgYOAb1N8Sbdc6BZrUP99p1forCtb44gBRkuocgpmvN03zE7XaVpcgpVJpjWEYa6MA0up3cLgHcoQ5y2h1nerlD02CKRjm5jidjvssBV8UmDphxw64JNl3F740cGkrgWYsJyul7gtFkEcfffQF3d3dDxNRZ2y264w4XEoCOdIqsLIFWfAJ8vgP/j/evGSy1sE0CYPff/DlAGKk4UtifF+hvTt27EjMtjAzP2Ca5on1xtO0wfhs2/4OYrUlYTBijQFiJGnqkARc0loHrEugTRIglymlro1EENd1ExEWAd+UMKJLwkI8AR2amSpgjQb3Q50Uz/NOGBgYeCQSQUSkMDQ09LCIvKBTjcChH4zo4jzU6lRbdLlTEcDUcmRkpFPQrFdKvWm6whvGO3Zdd62IrOlEC9Lse6oTeKW1TGwBDw8Pt736fiTbpggyPDz84nK5/GC7a4+1BoJpaskHAtjZ2rwZNy3aJrsNw1heLBanneM11CCoruM4txPRtKoozmbBiA6PlnwhgO1weH1sh4jINyzLuqhRWYEIUiqVzjEM49ZGmcXxd6054kAxvXngrOTJJ1sfiYOZX26aZkOr9UAEqWqR+4mo7n5xHF2CPf1FizJ9mTEOmDKfR6tJIiLftCzrgiBABiaI67oXicjXgmQaNU1/f7/eyo0KXsbea3Eo6ron55NhDEyQUqnUaxjG74nokFOtOPsEC3J9CBgnounOC5YECAvXgotptyilAl8pD0yQ6jTrk0R0TdzQ60V53IhmI79WTLVE5HWWZd0VFKFQBBkeHp5fLpehRWILHo51x4IFC/RBYNAey1m67du3VxzixSHM/D3TNM8Jk1cogiBj27avY+aPhylkurR5ikMeF2Z5ygfGjdj6jcMCWETOtCzrJ2HwC02QUql0AjP/npmbjomsT8rDdFV+00KDQJM0I8y8zjTN0NfIQxOkqkW+xMwfaabCeHf+/PmVewxaNALTIYAF+5YtW5q9S3OaUurnYZGORJChoaHjyuXyvcxshS3QT4+roJheadEIBEGgGTewIvIFy7IuCVLO5DSRCIJMHMd5HxHdFKVQvAMLXdyA06IRCIIADBqhRSKsRR7u6el5dX9/f6TLJ5EJUp1q3crMoXYF8B7udeBQUItGIAwCES9Zna+UgjvdSNIUQUql0kpmvoeZQ3lOgBeSo48+OlKF9Uv5RQB3R6BFQniM+ZZS6vxmEGuKINWp1mVE9LkwlcC5R9TYGGHK0Wmzh8BTTz0V1DvKzq6urpOXLFlS97ZgEHSaJkiVJPcQ0clBCtTTqyAo6TT1EAi65cvMl5im+YVmkYyFILZtn8nMdwSpjDYrCYKSTlMPAdhmYZrVQH6ulDqtUaIgf4+FICjIdd3r4N80SKF+FKMgaXUajcBkBFzXnQ6UA8x8mmma98aBXGwEqU611hPRG4JUDPc+0ubTKUi7dJrWI/DYY4/VPTSMa2rltyJWggwNDS33PA+2LkuCwFQsFgnh0LRoBMIgUG+hLiLftSwrVj9usY9Ox3HeTkTfC9JgeAtcsiQQl4Jkp9PkAAFs8cKXVg0Xrpt6enpe19/fb8cJQ+wEqU61Pk1EVwWpqDZYDIKSTuMjAMveOmHfmjoQrIdwSwiCwsK4LUUIg7lz5+pRoBGYFgE4c6gV6k1EvmxZ1kdbAV/LCFIqlRYZhoH1yAlBKo6TdZywa9EI1EKg3rqDmX/V19d32rx581oS+61lBEEjS6XSGVWSBOp1bf4eCKbcJWrgwCGSGXtQEFtKkOpU6yJmDuwNRVv5Bu26fKRDtK5t2xBTtqasVkpFtigPgmDLCVLVJFcahvGZIBVCGn0NNyhS2U6HnSqcedQSXPs2TXNK0M24EWkLQVBp13W/JiINXT36DdRrkri7On35TXNifq1SCkayLZe2EaRKknUi8uagrdJ2W0GRylY6nHVs2rSpnub4hmmagb9om0WmrQRBZR3H2UBELwlacU2SoEhlIx3iOMJhXB35vlLqHe1sadsJUtUkW0RkQdCGapIERSrd6RosyO9QSr2x3S3sCEGqmkTCNFaTJAxa6UvbwCnD3UqpUzvRqo4RpEoSTDQDX05Pe4TbTnRwGspE1FsQpI7cpJRa3al2dJQgVZKECqsATyjY4dIBPTs1ZOIrF3fM4RCulvkIShGRSy3Lui6+EsPn1HGCVNck3xaRdwatPmKFgyTaG3xQxJKXDotxaI5pvLe/Ryl1c6drngiCVDXJF4no78MAotclYdBKTlq479m1axc0RM1KeZ539sDAwLok1DgxBKlqksDXdn3woEWgTaBVtCQbAZyMgxj1plREtL1QKJy7dOnSu5PSkkQRBKDYtn01M18eBiCsR0AS7akxDGrtTQtvJCAH1h115FEieq9S6jftrdn0pSWOINXp1seIaC0RHRYGrDlz5hB2urQkBwGcioMYe/furVspEVnX09PzscWLFzvJqfnzNUkkQaqa5FXMDId0rw4DGm4ogiTaa3wY1FqTFlMpmKo38Kd7jVJqTWtq0HyuiSUImrZhw4buOXPmrA3qTmg8HLilCKLotUnzgyRsDlhrYErVIDIUYj1f3Izf3LD1ipI+0QTxG1R1BAFtsixMI+EUAiTRNxXDoBY9LaZTOPADMertUFVzv6tcLl+8bNkyhPNLtKSCINUpV79hGNAmfx0WUfgBBlH0Ij4scsHSY+Hta4xpFuGVzHB//Omnn7541apVY8Fy72yq1BDEh8m27Q8zMzymzAkLHYL2gCj6FD4scrXTQ0v4GiOAx/XtInKFZVmBb5fGU8vmckkdQdDckZGR4w8ePIiF3XvCNh/TLhClr69Pe3YMC9649L7GqOGfakquInIjM1+vlNrYRJEdeTWVBBm3NgFBcLMskOeUyQj7RNE7XsHGHrQETNLxBCTGL6vEgEvaVEqqCQLEN2/ePGd0dHQNM18ctQdAEGgUPNoV6lQUYTflEwOh0BqJiGytEgPmQ6mW1BNknDZ5XVWb4DOSYG3iaxW9PUwVkxAQYxrTkFo4/5thGNcXi8XhSJ2QsJcyQxAfV9d1L/Y872PMHPjG4uQ+MQyjok2w6wXtkietgl0oX1tMY2k7ZRgjDjkR3WCa5v8kbIw3VZ3MEQRouK57LBFdKCIXEtHSZhACWXyigCxZ1CwgAvzdYiqFJ8g0yseUmb/ned5/WJYFL5qZk0wSxO+lhx9+eM6MGTNWV4kS6pCxXk/39vZWCIMHP6dRQAAQ4tlnn60QIkJoZTT7FuxOWZZ1VxoxCFrnTBPEB+Ghhx7qmzVr1mrP86BRVgQFp1E6X7vgIBLBgLCGwTZy0gTnFdh1Gk+KqHUUkW8y841Kqfui5pGm93JBkHFE6ent7b3QMAzccX5xKzoKBPHJgs92EwfaAGQY/0TUEOPh2S0itxqGcaNpmr9uBW5JzTNXBBnfCa7rniUiZxERnmNa3UE+caB1/Ae/m+5nTIXw7Y/P6X7G+YRPiAY2UGGbuR6m6IVCYV2xWHwi7MtZSJ9bgvidVz1HOYuZfbIkb47UxpHGzA+AFJ7nrRsYGGgqxngbq92yonJPkPHI2rZtgSjQLMx8SstQT17Gg9imBTHysrYI2gWaIHWQcl33ZSJyIhG9nIhe0ex2cdAOaUc6EdnHzAiTfC8z3xdXyOR21L3dZWiCBETctm3sfr3KMIwTRQSEiWXbOGDxTSdDJCYR+aWI3I1nYGBgtOlMc5CBJkjETn7kkUfm9/b2QsNA0yxn5hcQEZ4kyKCIYNr0KDPff+DAgbuXL19e/1J4Emqc0DpogsTcMa7rVojied540uB3cUcp3U5Eg3hABsMw4BVk0DRN/E5LTAhogsQEZKNsRKRny5Yts8fGxmZ7nje7UCj04VNEKo9hGH34RD7MvNfzPKwT9uIxDGNvuVzeh8/u7u69CxcuxO8PNCpT/715BDRBmsdQ55BhBP4fPW32bt00iqMAAAAASUVORK5CYII=";
export default { export default {
props: { props: {
......
<template> <template>
<button id="u-wave-btn" class="u-btn u-line-1 u-fix-ios-appearance" :class="[ <button id="u-wave-btn" class="u-btn u-line-1 u-fix-ios-appearance" :class="[
'u-size-' + size, 'u-size-' + size,
plain ? 'u-' + type + '-plain' : '', plain ? 'u-' + type + '-plain' : '',
loading ? 'u-loading' : '', loading ? 'u-loading' : '',
shape == 'circle' ? 'u-round-circle' : '', shape == 'circle' ? 'u-round-circle' : '',
hairLine ? showHairLineBorder : 'u-bold-border', hairLine ? showHairLineBorder : 'u-bold-border',
]" ]"
:disabled="disabled" :disabled="disabled" :form-type="formType" :open-type="openType" :app-parameter="appParameter"
:form-type="formType" :hover-stop-propagation="hoverStopPropagation" :send-message-title="sendMessageTitle" send-message-path="sendMessagePath"
:open-type="openType" :lang="lang" :session-from="sessionFrom" :send-message-img="sendMessageImg" :show-message-card="showMessageCard"
:app-parameter="appParameter" @getphonenumber="getphonenumber" @getuserinfo="getuserinfo" @error="error" @opensetting="opensetting" @launchapp="launchapp"
:hover-stop-propagation="hoverStopPropagation" :style="[buttonStyle]" @tap="click($event)" :hover-class="getHoverClass" :loading="loading">
:send-message-title="sendMessageTitle" <slot></slot>
send-message-path="sendMessagePath" <view v-if="ripple" class="u-wave-ripple" :class="[waveActive ? 'u-wave-active' : '']" :style="{
:lang="lang" 'top': rippleTop + 'px',
:session-from="sessionFrom" 'left': rippleLeft + 'px',
:send-message-img="sendMessageImg" 'width': fields.targetWidth + 'px',
:show-message-card="showMessageCard" 'height': fields.targetWidth + 'px',
@getphonenumber="getphonenumber" 'background-color': (rippleBgColor || 'rgba(0, 0, 0, 0.15)')
@getuserinfo="getuserinfo" }">
@error="error" </view>
@opensetting="opensetting" </button>
@launchapp="launchapp" </template>
:style="[buttonStyle]" @tap="click($event)" :hover-class="getHoverClass" :loading="loading">
<slot></slot> <script>
<view v-if="ripple" class="u-wave-ripple" :class="[waveActive ? 'u-wave-active' : '']" :style="{ /**
'top': rippleTop + 'px', * alertTips 提示
'left': rippleLeft + 'px', * @description Button 按钮
'width': fields.targetWidth + 'px', * @tutorial https://www.uviewui.com/components/button.html
'height': fields.targetWidth + 'px', * @property {String} size 按钮的大小
'background-color': (rippleBgColor || 'rgba(0, 0, 0, 0.15)') * @property {Boolean} ripple 是否开启点击水波纹效果
}"> * @property {String} ripple-bg-color 水波纹的背景色,ripple为true时有效
</view> * @property {String} type 按钮的样式类型
</button> * @property {Boolean} plain 按钮是否镂空,背景色透明
</template> * @property {Boolean} disabled 是否禁用
* @property {Boolean} shape 按钮外观形状,见文档说明
<script> * @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈)
* @property {String} form-type 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
* @property {String} open-type 开放能力
* @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 { export default {
props: { name:"u-button",
// 是否细边框 props: {
hairLine: { // 是否细边框
type: Boolean, hairLine: {
default: true type: Boolean,
}, default: true
// 按钮的预置样式,default,primary,error,warning,success },
type: { // 按钮的预置样式,default,primary,error,warning,success
type: String, type: {
default: 'default' type: String,
}, default: 'default'
// 按钮尺寸,default,medium,mini },
size: { // 按钮尺寸,default,medium,mini
type: String, size: {
default: 'default' type: String,
}, default: 'default'
// 按钮形状,circle(两边为半圆),square(带圆角) },
shape: { // 按钮形状,circle(两边为半圆),square(带圆角)
type: String, shape: {
default: 'square' type: String,
}, default: 'square'
// 按钮是否镂空 },
plain: { // 按钮是否镂空
type: Boolean, plain: {
default: false type: Boolean,
}, default: false
// 是否禁止状态 },
disabled: { // 是否禁止状态
type: Boolean, disabled: {
default: false type: Boolean,
}, default: false
// 是否加载中 },
loading: { // 是否加载中
type: Boolean, loading: {
default: false type: Boolean,
}, default: false
// 开放能力,具体请看uniapp稳定关于button组件部分说明 },
// https://uniapp.dcloud.io/component/button // 开放能力,具体请看uniapp稳定关于button组件部分说明
openType: { // https://uniapp.dcloud.io/component/button
type: String, openType: {
default: '' type: String,
}, default: ''
// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 },
// 取值为submit(提交表单),reset(重置表单) // 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
formType: { // 取值为submit(提交表单),reset(重置表单)
type: String, formType: {
default: '' type: String,
}, default: ''
// 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 },
// 只微信小程序、QQ小程序有效 // 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
appParameter: { // 只微信小程序、QQ小程序有效
type: String, appParameter: {
default: '' type: String,
}, default: ''
// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效 },
hoverStopPropagation: { // 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
type: Boolean, hoverStopPropagation: {
default: false type: Boolean,
}, default: false
// 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效 },
lang: { // 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
type: String, lang: {
default: 'en' type: String,
}, default: 'en'
// 会话来源,open-type="contact"时有效。只微信小程序有效 },
sessionFrom: { // 会话来源,open-type="contact"时有效。只微信小程序有效
type: String, sessionFrom: {
default: '' type: String,
}, default: ''
// 会话内消息卡片标题,open-type="contact"时有效 },
// 默认当前标题,只微信小程序有效 // 会话内消息卡片标题,open-type="contact"时有效
sendMessageTitle: { // 默认当前标题,只微信小程序有效
type: String, sendMessageTitle: {
default: '' type: String,
}, default: ''
// 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效 },
// 默认当前分享路径,只微信小程序有效 // 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
sendMessagePath: { // 默认当前分享路径,只微信小程序有效
type: String, sendMessagePath: {
default: '' type: String,
}, default: ''
// 会话内消息卡片图片,open-type="contact"时有效 },
// 默认当前页面截图,只微信小程序有效 // 会话内消息卡片图片,open-type="contact"时有效
sendMessageImg: { // 默认当前页面截图,只微信小程序有效
type: String, sendMessageImg: {
default: '' type: String,
}, default: ''
// 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示, },
// 用户点击后可以快速发送小程序消息,open-type="contact"时有效 // 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
showMessageCard: { // 用户点击后可以快速发送小程序消息,open-type="contact"时有效
type: Boolean, showMessageCard: {
default: false type: Boolean,
}, default: false
// 手指按(触摸)按钮时按钮时的背景颜色 },
hoverBgColor: { // 手指按(触摸)按钮时按钮时的背景颜色
type: String, hoverBgColor: {
default: '' type: String,
}, default: ''
// 水波纹的背景颜色 },
rippleBgColor: { // 水波纹的背景颜色
type: String, rippleBgColor: {
default: '' type: String,
}, default: ''
// 是否开启水波纹效果 },
ripple: { // 是否开启水波纹效果
type: Boolean, ripple: {
default: false type: Boolean,
}, default: false
// 按下的类名 },
hoverClass: { // 按下的类名
type: String, hoverClass: {
default: '' type: String,
}, default: ''
// 自定义样式,对象形式 },
customStyle: { // 自定义样式,对象形式
type: Object, customStyle: {
default() { type: Object,
return {} default () {
} return {}
} }
}, }
computed: { },
// 当没有传bgColor变量时,按钮按下去的颜色类名 computed: {
getHoverClass() { // 当没有传bgColor变量时,按钮按下去的颜色类名
// 如果开启水波纹效果,则不启用hover-class效果 getHoverClass() {
if (this.loading || this.disabled || this.ripple || this.hoverClass) return ''; // 如果开启水波纹效果,则不启用hover-class效果
let hoverClass = ''; if (this.loading || this.disabled || this.ripple || this.hoverClass) return '';
hoverClass = this.plain ? 'u-' + this.type + '-plain-hover' : 'u-' + this.type + '-hover'; let hoverClass = '';
return hoverClass; hoverClass = this.plain ? 'u-' + this.type + '-plain-hover' : 'u-' + this.type + '-hover';
}, return hoverClass;
// 按钮主题 },
buttonStyle() { // 按钮主题
let style = {}; buttonStyle() {
if (this.type == 'default') { let style = {};
if (this.disabled) { if (this.type == 'default') {
style.color = "#c0c4cc"; if (this.disabled) {
style.backgroundColor = "#ffffff"; style.color = "#c0c4cc";
style.borderColor = "#e4e7ed"; style.backgroundColor = "#ffffff";
} else { style.borderColor = "#e4e7ed";
style.color = this.$u.color['contentColor']; } else {
style.backgroundColor = "#ffffff"; style.color = this.$u.color['contentColor'];
style.borderColor = "#c0c4cc"; style.backgroundColor = "#ffffff";
} style.borderColor = "#c0c4cc";
} else { }
if (this.disabled) { } else {
if(this.plain) { if (this.disabled) {
style.color = this.$u.color[this.type + 'Disabled']; if (this.plain) {
style.backgroundColor = this.$u.color[this.type + 'Light']; style.color = this.$u.color[this.type + 'Disabled'];
style.borderColor = this.$u.color[this.type + 'Disabled']; style.backgroundColor = this.$u.color[this.type + 'Light'];
} else { style.borderColor = this.$u.color[this.type + 'Disabled'];
style.color = "#ffffff"; } else {
style.backgroundColor = this.$u.color[this.type + 'Disabled']; style.color = "#ffffff";
style.borderColor = this.$u.color[this.type + 'Disabled']; style.backgroundColor = this.$u.color[this.type + 'Disabled'];
} style.borderColor = this.$u.color[this.type + 'Disabled'];
} else { }
if(this.plain) { } else {
style.color = this.$u.color[this.type] if (this.plain) {
style.backgroundColor = this.$u.color[this.type + 'Light']; style.color = this.$u.color[this.type]
style.borderColor = this.$u.color[this.type + 'Disabled']; style.backgroundColor = this.$u.color[this.type + 'Light'];
} else { style.borderColor = this.$u.color[this.type + 'Disabled'];
style.color = "#ffffff"; } else {
style.backgroundColor = this.$u.color[this.type]; style.color = "#ffffff";
style.borderColor = this.$u.color[this.type]; style.backgroundColor = this.$u.color[this.type];
} style.borderColor = this.$u.color[this.type];
} }
} }
}
return Object.assign(style, this.customStyle);
}, return Object.assign(style, this.customStyle);
// 在'primary', 'success', 'error', 'warning'类型下,不显示边框,否则会造成四角有毛刺现象 },
showHairLineBorder() { // 在'primary', 'success', 'error', 'warning'类型下,不显示边框,否则会造成四角有毛刺现象
if(['primary', 'success', 'error', 'warning'].indexOf(this.type) >= 0 && !this.plain) { showHairLineBorder() {
return ''; if (['primary', 'success', 'error', 'warning'].indexOf(this.type) >= 0 && !this.plain) {
} else { return '';
return 'u-hairline-border'; } else {
} return 'u-hairline-border';
} }
}, }
data() { },
return { data() {
rippleTop: 0, // 水波纹的起点Y坐标到按钮上边界的距离 return {
rippleLeft: 0, // 水波纹起点X坐标到按钮左边界的距离 rippleTop: 0, // 水波纹的起点Y坐标到按钮上边界的距离
fields: {}, // 波纹按钮节点信息 rippleLeft: 0, // 水波纹起点X坐标到按钮左边界的距离
waveActive: false, // 激活水波纹 fields: {}, // 波纹按钮节点信息
} waveActive: false, // 激活水波纹
}, }
methods: { },
// 按钮点击 methods: {
click(e) { // 按钮点击
// 如果按钮时disabled和loading状态,不触发水波纹效果 click(e) {
if (this.loading === true || this.disabled === true) return; // 如果按钮时disabled和loading状态,不触发水波纹效果
// 是否开启水波纹效果 if (this.loading === true || this.disabled === true) return;
if (this.ripple) { // 是否开启水波纹效果
// 每次点击时,移除上一次的类,再次添加,才能触发动画效果 if (this.ripple) {
this.waveActive = false; // 每次点击时,移除上一次的类,再次添加,才能触发动画效果
this.$nextTick(function() { this.waveActive = false;
this.getWaveQuery(e); this.$nextTick(function() {
}); this.getWaveQuery(e);
} });
this.$emit('click'); }
}, this.$emit('click');
// 查询按钮的节点信息 },
getWaveQuery(e) { // 查询按钮的节点信息
this.getElQuery().then(res => { getWaveQuery(e) {
// 查询返回的是一个数组节点 this.getElQuery().then(res => {
let data = res[0]; // 查询返回的是一个数组节点
// 查询不到节点信息,不操作 let data = res[0];
if (!data.width || !data.width) return; // 查询不到节点信息,不操作
// 水波纹的最终形态是一个正方形(通过border-radius让其变为一个圆形),这里要保证正方形的边长等于按钮的最长边 if (!data.width || !data.width) return;
// 最终的方形(变换后的圆形)才能覆盖整个按钮 // 水波纹的最终形态是一个正方形(通过border-radius让其变为一个圆形),这里要保证正方形的边长等于按钮的最长边
data.targetWidth = (data.height > data.width ? data.height : data.width); // 最终的方形(变换后的圆形)才能覆盖整个按钮
if (!data.targetWidth) return; data.targetWidth = (data.height > data.width ? data.height : data.width);
this.fields = data; if (!data.targetWidth) return;
let touchesX = '', this.fields = data;
touchesY = ''; let touchesX = '',
// #ifdef MP-BAIDU touchesY = '';
touchesX = e.changedTouches[0].clientX; // #ifdef MP-BAIDU
touchesY = e.changedTouches[0].clientY; touchesX = e.changedTouches[0].clientX;
// #endif touchesY = e.changedTouches[0].clientY;
// #ifdef MP-ALIPAY // #endif
touchesX = e.detail.clientX; // #ifdef MP-ALIPAY
touchesY = e.detail.clientY; touchesX = e.detail.clientX;
// #endif touchesY = e.detail.clientY;
// #ifndef MP-BAIDU || MP-ALIPAY // #endif
touchesX = e.touches[0].clientX; // #ifndef MP-BAIDU || MP-ALIPAY
touchesY = e.touches[0].clientY; touchesX = e.touches[0].clientX;
// #endif touchesY = e.touches[0].clientY;
// 获取触摸点相对于按钮上边和左边的x和y坐标,原理是通过屏幕的触摸点(touchesY),减去按钮的上边界data.top // #endif
// 但是由于`transform-origin`默认是center,所以这里再减去半径才是水波纹view应该的位置 // 获取触摸点相对于按钮上边和左边的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.rippleTop = (touchesY - data.top - (data.targetWidth / 2));
this.$nextTick(() => { this.rippleLeft = (touchesX - data.left - (data.targetWidth / 2));
this.waveActive = true; this.$nextTick(() => {
}) this.waveActive = true;
}) })
}, })
// 获取节点信息 },
getElQuery() { // 获取节点信息
return new Promise(resolve => { getElQuery() {
let queryInfo = ''; return new Promise(resolve => {
// 获取元素节点信息,请查看uniapp相关文档 let queryInfo = '';
// https://uniapp.dcloud.io/api/ui/nodes-info?id=nodesrefboundingclientrect // 获取元素节点信息,请查看uniapp相关文档
queryInfo = uni.createSelectorQuery().in(this); // https://uniapp.dcloud.io/api/ui/nodes-info?id=nodesrefboundingclientrect
queryInfo.select('.u-btn').boundingClientRect(); queryInfo = uni.createSelectorQuery().in(this);
queryInfo.exec((data) => { queryInfo.select('.u-btn').boundingClientRect();
resolve(data) queryInfo.exec((data) => {
}); resolve(data)
}) });
}, })
// 下面为对接uniapp官方按钮开放能力事件回调的对接 },
getphonenumber(res) { // 下面为对接uniapp官方按钮开放能力事件回调的对接
this.$emit('getphonenumber', res); getphonenumber(res) {
}, this.$emit('getphonenumber', res);
getuserinfo(res) { },
this.$emit('getuserinfo', res); getuserinfo(res) {
}, this.$emit('getuserinfo', res);
error(res) { },
this.$emit('error', res); error(res) {
}, this.$emit('error', res);
opensetting(res) { },
this.$emit('opensetting', res); opensetting(res) {
}, this.$emit('opensetting', res);
launchapp(res) { },
this.$emit('launchapp', res); launchapp(res) {
}, this.$emit('launchapp', res);
} },
} }
</script> }
</script>
<style scoped lang="scss">
button::after { <style scoped lang="scss">
border: none; button::after {
} border: none;
}
.u-btn {
position: relative; .u-btn {
border: 0; position: relative;
//border-radius: 10rpx; border: 0;
display: inline-block; //border-radius: 10rpx;
overflow: hidden; display: inline-block;
line-height: 1; overflow: hidden;
display: flex; line-height: 1;
align-items: center; display: flex;
justify-content: center; align-items: center;
cursor: pointer; justify-content: center;
padding: 0 40rpx; cursor: pointer;
z-index: 1; padding: 0 40rpx;
box-sizing: border-box; z-index: 1;
transition: all 0.15s; box-sizing: border-box;
} transition: all 0.15s;
}
.u-hairline-border:after {
content: ' '; .u-hairline-border:after {
position: absolute; content: ' ';
pointer-events: none; position: absolute;
// 设置为border-box,意味着下面的scale缩小为0.5,实际上缩小的是伪元素的内容(border-box意味着内容不含border) pointer-events: none;
box-sizing: border-box; // 设置为border-box,意味着下面的scale缩小为0.5,实际上缩小的是伪元素的内容(border-box意味着内容不含border)
// 中心点作为变形(scale())的原点 box-sizing: border-box;
-webkit-transform-origin: 0 0; // 中心点作为变形(scale())的原点
transform-origin: 0 0; -webkit-transform-origin: 0 0;
left: 0; transform-origin: 0 0;
top: 0; left: 0;
width: 200%; top: 0;
height: 200%; width: 200%;
-webkit-transform: scale(0.5, 0.5); height: 200%;
transform: scale(0.5, 0.5); -webkit-transform: scale(0.5, 0.5);
border: 1px solid currentColor; transform: scale(0.5, 0.5);
z-index: 0; border: 1px solid currentColor;
} z-index: 0;
}
.u-bold-border {
border: 1px solid #FFFFFF; .u-bold-border {
} border: 1px solid #FFFFFF;
}
.u-wave-ripple {
z-index: 0; .u-wave-ripple {
position: absolute; z-index: 0;
border-radius: 100%; position: absolute;
background-clip: padding-box; border-radius: 100%;
pointer-events: none; background-clip: padding-box;
user-select: none; pointer-events: none;
transform: scale(0); user-select: none;
opacity: 1; transform: scale(0);
transform-origin: center; opacity: 1;
} transform-origin: center;
}
.u-wave-ripple.u-wave-active {
opacity: 0; .u-wave-ripple.u-wave-active {
transform: scale(2); opacity: 0;
transition: opacity 1s linear, transform 0.4s linear; transform: scale(2);
} transition: opacity 1s linear, transform 0.4s linear;
}
.u-round-circle {
border-radius: 100rpx; .u-round-circle {
} border-radius: 100rpx;
}
.u-round-circle::after {
border-radius: 100rpx; .u-round-circle::after {
} border-radius: 100rpx;
}
.u-loading::after {
background-color: hsla(0, 0%, 100%, .35); .u-loading::after {
} background-color: hsla(0, 0%, 100%, .35);
}
.u-size-default {
font-size: 30rpx; .u-size-default {
height: 80rpx; font-size: 30rpx;
line-height: 80rpx; height: 80rpx;
} line-height: 80rpx;
}
.u-size-medium {
display: inline-flex; .u-size-medium {
width: auto; display: inline-flex;
font-size: 26rpx; width: auto;
height: 70rpx; font-size: 26rpx;
line-height: 70rpx; height: 70rpx;
padding: 0 80rpx; line-height: 70rpx;
} padding: 0 80rpx;
}
.u-size-mini {
display: inline-flex; .u-size-mini {
width: auto; display: inline-flex;
font-size: 22rpx; width: auto;
padding-top: 1px; font-size: 22rpx;
height: 50rpx; padding-top: 1px;
line-height: 50rpx; height: 50rpx;
padding: 0 20rpx; line-height: 50rpx;
} padding: 0 20rpx;
}
.u-primary-plain-hover {
color: #FFFFFF!important; .u-primary-plain-hover {
background: $u-type-primary-dark !important; color: #FFFFFF !important;
} background: $u-type-primary-dark !important;
}
.u-default-plain-hover {
color: $u-type-primary-dark !important; .u-default-plain-hover {
background: $u-type-primary-light !important; color: $u-type-primary-dark !important;
} background: $u-type-primary-light !important;
}
.u-success-plain-hover {
color: #FFFFFF!important; .u-success-plain-hover {
background: $u-type-success-dark!important; color: #FFFFFF !important;
} background: $u-type-success-dark !important;
}
.u-warning-plain-hover {
color: #FFFFFF!important; .u-warning-plain-hover {
background: $u-type-warning-dark !important; color: #FFFFFF !important;
} background: $u-type-warning-dark !important;
}
.u-error-plain-hover {
color: #FFFFFF!important; .u-error-plain-hover {
background: $u-type-error-dark !important; color: #FFFFFF !important;
} background: $u-type-error-dark !important;
}
.u-info-plain-hover {
color: #FFFFFF!important; .u-info-plain-hover {
background: $u-type-info-dark !important; color: #FFFFFF !important;
} background: $u-type-info-dark !important;
}
.u-default-hover {
color: $u-type-primary-dark !important; .u-default-hover {
border-color: $u-type-primary-dark !important; color: $u-type-primary-dark !important;
background-color: $u-type-primary-light !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-primary-hover {
} background: $u-type-primary-dark !important;
color: #fff;
.u-success-hover { }
background: $u-type-success-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-info-hover {
} background: $u-type-info-dark !important;
color: #fff;
.u-warning-hover { }
background: $u-type-warning-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; .u-error-hover {
} background: $u-type-error-dark !important;
color: #fff;
}
</style> </style>
<template> <template>
<view class="u-keyboard" @touchmove.stop.prevent> <view class="u-keyboard" @touchmove.stop.prevent>
<view class="u-keyboard-grids"> <view class="u-keyboard-grids">
<block> <block>
<view class="u-keyboard-grids-item" v-for="(group, i) in abc ? EngKeyBoardList : areaList" :key="i"> <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"> <view :hover-stay-time="100" @tap="carInputClick(i, j)" hover-class="u-carinput-hover" class="u-keyboard-grids-btn"
{{ item }} v-for="(item, j) in group" :key="j">
</view> {{ item }}
</view> </view>
<view @touchstart="backspaceClick" @touchend="clearTimer" :hover-stay-time="100" class="u-keyboard-back" hover-class="u-hover-class"> </view>
<u-icon :size="38" name="backspace" :bold="true"></u-icon> <view @touchstart="backspaceClick" @touchend="clearTimer" :hover-stay-time="100" class="u-keyboard-back"
</view> hover-class="u-hover-class">
<view :hover-stay-time="100" class="u-keyboard-change" hover-class="u-carinput-hover" @tap="changeCarInputMode"> <u-icon :size="38" name="backspace" :bold="true"></u-icon>
<text class="zh" :class="[!abc ? 'active' : 'inactive']"></text> </view>
/ <view :hover-stay-time="100" class="u-keyboard-change" hover-class="u-carinput-hover" @tap="changeCarInputMode">
<text class="en" :class="[abc ? 'active' : 'inactive']"></text> <text class="zh" :class="[!abc ? 'active' : 'inactive']"></text>
</view> /
</block> <text class="en" :class="[abc ? 'active' : 'inactive']"></text>
</view> </view>
</view> </block>
</template> </view>
</view>
<script> </template>
export default {
props: { <script>
// 是否打乱键盘按键的顺序 /**
random: { * alertTips 提示
type: Boolean, * @description 此为uViw自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3中模式,都有可以打乱按键顺序的选项。
default: false * @tutorial https://www.uviewui.com/components/keyboard.html
} * @property {String} mode 键盘类型,见官网基本使用的说明(默认number)
}, * @property {Boolean} dot-enabled 是否显示"."按键,只在mode=number时有效(默认true)
data() { * @property {Boolean} tooltip 是否显示键盘顶部工具条(默认true)
return { * @property {String} tips 工具条中间的提示文字,见官网基本使用的说明,如不需要,请传""空字符
// 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称 * @property {Boolean} cancel-btn 是否显示工具条左边的"取消"按钮(默认true)
abc: false * @property {Boolean} confirm-btn 是否显示工具条右边的"完成"按钮(默认true)
}; * @property {Boolean} mask 是否显示遮罩(默认true)
}, * @property {Number String} z-index 弹出键盘的z-index值(默认1075)
computed: { * @property {Boolean} random 是否打乱键盘按键的顺序(默认false)
areaList() { * @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
let data = [ * @property {Boolean} mask-close-able 是否允许点击遮罩收起键盘(默认true)
'', * @event {Function} change 按键被点击(不包含退格键被点击)
'', * @event {Function} cancel 键盘顶部工具条左边的"取消"按钮被点击
'', * @event {Function} confirm 键盘顶部工具条右边的"完成"按钮被点击
'', * @event {Function} backspace 键盘退格键被点击
'', * @example <u-keyboard ref="uKeyboard" mode="car" v-model="show"></u-keyboard>
'', */
'', 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', let tmp = [];
'R', // 打乱顺序
'T', if (this.random) data = this.$u.randomArray(data);
'Y', // 切割成二维数组
'U', tmp[0] = data.slice(0, 10);
'I', tmp[1] = data.slice(10, 20);
'O', tmp[2] = data.slice(20, 30);
'P', tmp[3] = data.slice(30, 36);
'A', return tmp;
'S', },
'D', EngKeyBoardList() {
'F', let data = [
'G', 1,
'H', 2,
'J', 3,
'K', 4,
'L', 5,
'Z', 6,
'X', 7,
'C', 8,
'V', 9,
'B', 0,
'N', 'Q',
'M' 'W',
]; 'E',
let tmp = []; 'R',
if (this.random) data = this.$u.randomArray(data); 'T',
tmp[0] = data.slice(0, 10); 'Y',
tmp[1] = data.slice(10, 20); 'U',
tmp[2] = data.slice(20, 30); 'I',
tmp[3] = data.slice(30, 36); 'O',
return tmp; 'P',
} 'A',
}, 'S',
methods: { 'D',
// 点击键盘按钮 'F',
carInputClick(i, j) { 'G',
let value = ''; 'H',
// 不同模式,获取不同数组的值 'J',
if (this.abc) value = this.EngKeyBoardList[i][j]; 'K',
else value = this.areaList[i][j]; 'L',
this.$emit('change', value); 'Z',
}, 'X',
// 修改汽车牌键盘的输入模式,中文|英文 'C',
changeCarInputMode() { 'V',
this.abc = !this.abc; 'B',
}, 'N',
// 点击退格键 'M'
backspaceClick() { ];
this.$emit('backspace'); let tmp = [];
 clearInterval(this.timer); //再次清空定时器,防止重复注册定时器 if (this.random) data = this.$u.randomArray(data);
      this.timer = setInterval(() => { tmp[0] = data.slice(0, 10);
    this.$emit('backspace'); tmp[1] = data.slice(10, 20);
      }, 250); tmp[2] = data.slice(20, 30);
}, tmp[3] = data.slice(30, 36);
clearTimer() { return tmp;
clearInterval(this.timer); }
}, },
} methods: {
}; // 点击键盘按钮
</script> carInputClick(i, j) {
let value = '';
<style lang="scss" scoped> // 不同模式,获取不同数组的值
.u-keyboard-grids { if (this.abc) value = this.EngKeyBoardList[i][j];
background: rgb(215, 215, 217); else value = this.areaList[i][j];
padding: 24rpx 0; this.$emit('change', value);
position: relative; },
} // 修改汽车牌键盘的输入模式,中文|英文
changeCarInputMode() {
.u-keyboard-grids-item { this.abc = !this.abc;
display: flex; },
align-items: center; // 点击退格键
justify-content: center; backspaceClick() {
} this.$emit('backspace');
clearInterval(this.timer); //再次清空定时器,防止重复注册定时器
.u-keyboard-grids-btn { this.timer = setInterval(() => {
text-decoration: none; this.$emit('backspace');
width: 62rpx; }, 250);
flex: 0 0 64rpx; },
height: 80rpx; clearTimer() {
display: inline-block; clearInterval(this.timer);
font-size: 36rpx; },
text-align: center; }
line-height: 80rpx; };
background-color: #fff; </script>
margin: 8rpx 5rpx;
border-radius: 8rpx; <style lang="scss" scoped>
box-shadow: 0 2rpx 0rpx #888992; .u-keyboard-grids {
font-weight: 500; background: rgb(215, 215, 217);
} padding: 24rpx 0;
position: relative;
.u-carinput-hover { }
background-color: rgb(185, 188, 195)!important;
} .u-keyboard-grids-item {
display: flex;
.u-keyboard-back { align-items: center;
position: absolute; justify-content: center;
width: 96rpx; }
right: 22rpx;
bottom: 32rpx; .u-keyboard-grids-btn {
height: 80rpx; text-decoration: none;
background-color: rgb(185, 188, 195); width: 62rpx;
display: flex; flex: 0 0 64rpx;
align-items: center; height: 80rpx;
border-radius: 8rpx; display: inline-block;
justify-content: center; font-size: 36rpx;
box-shadow: 0 2rpx 0rpx #888992; text-align: center;
} line-height: 80rpx;
background-color: #fff;
.u-keyboard-change { margin: 8rpx 5rpx;
font-size: 24rpx; border-radius: 8rpx;
box-shadow: 0 2rpx 0rpx #888992; box-shadow: 0 2rpx 0rpx #888992;
position: absolute; font-weight: 500;
width: 96rpx; }
left: 22rpx;
line-height: 1; .u-carinput-hover {
bottom: 32rpx; background-color: rgb(185, 188, 195) !important;
height: 80rpx; }
background-color: #ffffff;
display: flex; .u-keyboard-back {
align-items: center; position: absolute;
border-radius: 8rpx; width: 96rpx;
justify-content: center; right: 22rpx;
} bottom: 32rpx;
height: 80rpx;
.u-keyboard-change .inactive.zh { background-color: rgb(185, 188, 195);
transform: scale(0.85) translateY(-10rpx); display: flex;
} align-items: center;
border-radius: 8rpx;
.u-keyboard-change .inactive.en { justify-content: center;
transform: scale(0.85) translateY(10rpx); box-shadow: 0 2rpx 0rpx #888992;
} }
.u-keyboard-change .active { .u-keyboard-change {
color: rgb(237, 112, 64); font-size: 24rpx;
font-size: 30rpx; box-shadow: 0 2rpx 0rpx #888992;
} position: absolute;
width: 96rpx;
.u-keyboard-change .zh { left: 22rpx;
transform: translateY(-10rpx); line-height: 1;
} bottom: 32rpx;
height: 80rpx;
.u-keyboard-change .en { background-color: #ffffff;
transform: translateY(10rpx); display: 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> </style>
<template> <template>
<view class="u-cell-box"> <view class="u-cell-box">
<view class="u-cell-title" v-if="title" :style="[titleStyle]"> <view class="u-cell-title" v-if="title" :style="[titleStyle]">
{{title}} {{title}}
</view> </view>
<view class="u-cell-item-box" :class="{'u-border-bottom u-border-top': border}"> <view class="u-cell-item-box" :class="{'u-border-bottom u-border-top': border}">
<slot /> <slot />
</view> </view>
</view> </view>
</template> </template>
<script> <script>
export default { /**
props: { * alertTips 提示
// 分组标题 * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。搭配u-cell-item
title: { * @tutorial https://www.uviewui.com/components/cell.html
type: String, * @property {String} title 分组标题
default: '' * @property {Boolean} border 是否显示外边框(默认true)
}, * @property {Object} title-style 分组标题的的样式,对象形式,如{'font-size': '24rpx'} 或 {'fontSize': '24rpx'}
// 是否显示分组list上下边框 * @example <u-cell-group title="设置喜好">
border: { */
type: Boolean, export default {
default: true name: "u-cell-group",
}, props: {
// 分组标题的样式,对象形式,注意驼峰属性写法 // 分组标题
// 类似 {'font-size': '24rpx'} 和 {'fontSize': '24rpx'} title: {
titleStyle: { type: String,
type: Object, default: ''
default() { },
return {}; // 是否显示分组list上下边框
} border: {
} type: Boolean,
}, default: true
data() { },
return { // 分组标题的样式,对象形式,注意驼峰属性写法
index: 0, // 类似 {'font-size': '24rpx'} 和 {'fontSize': '24rpx'}
} titleStyle: {
}, type: Object,
provide() { default () {
return { return {};
uCellGroup: this }
} }
}, },
} data() {
</script> return {
index: 0,
<style lang="scss" scoped> }
.u-cell-box { },
width: 100%; provide() {
} return {
uCellGroup: this
.u-cell-title { }
padding: 30rpx 32rpx 10rpx 32rpx; },
font-size: 30rpx; }
text-align: left; </script>
color: $u-tips-color;
} <style lang="scss" scoped>
.u-cell-box {
.u-cell-item-box { width: 100%;
background-color: #FFFFFF; }
}
.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;
}
</style> </style>
<template> <template>
<view @tap="click" class="u-cell-item-box" :class="{'u-cell-border': itemIndex > 0 }" hover-stay-time="150" :hover-class="hover ? 'u-hover-class' : ''" :style="{ <view @tap="click" class="u-cell-item-box" :class="{'u-cell-border': itemIndex > 0 }" hover-stay-time="150"
backgroundColor: bgColor :hover-class="hover ? 'u-hover-class' : ''" :style="{
}"> backgroundColor: bgColor
<view class="u-cell-content"> }">
<view class="u-icon-wrap" v-if="icon"> <view class="u-cell-content">
<u-icon size="32" :name="icon" class="u-icon"></u-icon> <view class="u-icon-wrap" v-if="icon">
</view> <u-icon size="32" :name="icon" class="u-icon"></u-icon>
<view class="u-icon-wrap"> </view>
<slot name="icon"></slot> <view class="u-icon-wrap">
</view> <slot name="icon"></slot>
<view class="u-cell-title" :style="[titleStyle]"> </view>
<text class="u-title-text" v-if="title">{{title}}</text> <view class="u-cell-title" :style="[titleStyle]">
<slot name="left"></slot> <text class="u-title-text" v-if="title">{{title}}</text>
</view> <slot name="left"></slot>
<view class="u-cell-value" v-if="value"> </view>
<text class="u-value-text" :style="[valueStyle]" v-if="value">{{value}}</text> <view class="u-cell-value" v-if="value">
</view> <text class="u-value-text" :style="[valueStyle]" v-if="value">{{value}}</text>
<view v-else class="u-slot-wrap"> </view>
<slot name="right"></slot> <view v-else class="u-slot-wrap">
</view> <slot name="right"></slot>
<view :style="[arrowStyle]" class="u-icon-wrap"> </view>
<u-icon v-if="arrow" size="26" class="u-arror-right" color="#969799" name="arrow-right"></u-icon> <view :style="[arrowStyle]" class="u-icon-wrap">
</view> <u-icon v-if="arrow" size="26" class="u-arror-right" color="#969799" name="arrow-right"></u-icon>
</view> </view>
<view class="u-cell-label" v-if="label" :style="[labelStyle]"> </view>
{{label}} <view class="u-cell-label" v-if="label" :style="[labelStyle]">
</view> {{label}}
</view> </view>
</template> </view>
</template>
<script>
export default { <script>
props: { /**
// 左侧图标名称(只能uView内置图标),或者图标src * alertTips 提示
icon: { * @description 警告提示,展现需要关注的信息。
type: String, * @tutorial https://www.uviewui.com/components/cell.html
default: '' * @property {String} title 左侧标题
}, * @property {String} icon 左侧图标名,只支持uView内置图标,见Icon 图标
// 左侧标题 * @property {String} value 右侧内容
title: { * @property {String} label 标题下方的描述信息
type: String, * @property {Boolean} border 是否显示每个cell的下边框(默认true)
default: '' * @property {Boolean} hover 是否开启点击反馈,hover-class形式,如果右侧通过slot传递switch进去的话,可以将此值设置为false(默认true)
}, * @property {Boolean} arrow 是否显示右侧箭头(默认true)
// 右侧内容 * @property {Boolean} arrow-direction 箭头方向,可选值(默认right)
value: { * @property {Object} title-style 标题样式,对象形式
type: String, * @property {Object} value-style 右侧内容样式,对象形式
default: '' * @property {Object} label-style 标题下方描述信息的样式,对象形式
}, * @property {String} bg-color 背景颜色(默认#ffffff)
// 标题下方的描述信息 * @property {String Number} index 用于在click事件回调中返回,标识当前是第几个Item
label: { * @example <u-cell-item icon="integral-fill" title="会员等级" value="新版本"></u-cell-item>
type: String, */
default: '' export default {
}, name: "u-cell-item",
// 是否显示内边框 props: {
border: { // 左侧图标名称(只能uView内置图标),或者图标src
type: Boolean, icon: {
default: true type: String,
}, default: ''
// 是否开启点击反馈,即点击是cell背景为灰色 },
hover: { // 左侧标题
type: Boolean, title: {
default: true type: String,
}, default: ''
// 是否显示右侧箭头 },
arrow: { // 右侧内容
type: Boolean, value: {
default: true type: String,
}, default: ''
// 右侧箭头方向,可选值:right|up|down,默认为right },
arrowDirection: { // 标题下方的描述信息
type: String, label: {
default: 'right' type: String,
}, default: ''
// 控制标题的样式 },
titleStyle: { // 是否显示内边框
type: Object, border: {
default() { type: Boolean,
return {}; default: true
} },
}, // 是否开启点击反馈,即点击是cell背景为灰色
// 右侧显示内容的样式 hover: {
valueStyle: { type: Boolean,
type: Object, default: true
default() { },
return {}; // 是否显示右侧箭头
} arrow: {
}, type: Boolean,
// 描述信息的样式 default: true
labelStyle: { },
type: Object, // 右侧箭头方向,可选值:right|up|down,默认为right
default() { arrowDirection: {
return {}; type: String,
} default: 'right'
}, },
// 背景颜色 // 控制标题的样式
bgColor: { titleStyle: {
type: String, type: Object,
default: '#ffffff' default () {
}, return {};
// 用于识别被点击的是第几个cell }
index: { },
type: [String, Number], // 右侧显示内容的样式
default: '' valueStyle: {
} type: Object,
}, default () {
inject: ['uCellGroup'], return {};
data() { }
return { },
itemIndex: 0, // 描述信息的样式
} labelStyle: {
}, type: Object,
created() { default () {
this.itemIndex = this.uCellGroup.index++; return {};
}, }
computed: { },
arrowStyle() { // 背景颜色
let style = {}; bgColor: {
if(this.arrowDirection == 'top') style.transform = 'rotate(-90deg)'; type: String,
else if(this.arrowDirection == 'bottom') style.transform = 'rotate(90deg)'; default: '#ffffff'
else style.transform = 'rotate(0deg)'; },
return style; // 用于识别被点击的是第几个cell
} index: {
}, type: [String, Number],
methods: { default: ''
click() { }
this.$emit('click', this.index); },
} inject: ['uCellGroup'],
} data() {
} return {
</script> itemIndex: 0,
}
<style lang="scss" scoped> },
.u-cell-item-box { created() {
padding: 28rpx 32rpx; this.itemIndex = this.uCellGroup.index++;
position: relative; },
} computed: {
arrowStyle() {
.u-cell-border:after { let style = {};
left: 32rpx!important; if (this.arrowDirection == 'top') style.transform = 'rotate(-90deg)';
position: absolute; else if (this.arrowDirection == 'bottom') style.transform = 'rotate(90deg)';
box-sizing: border-box; else style.transform = 'rotate(0deg)';
content: ' '; return style;
pointer-events: none; }
right: 0; },
top: 0; methods: {
border-bottom: 1px solid $u-border-color; click() {
-webkit-transform: scaleY(0.5); this.$emit('click', this.index);
transform: scaleY(0.5); }
} }
}
.u-cell-content { </script>
display: flex;
align-items: center; <style lang="scss" scoped>
} .u-cell-item-box {
padding: 28rpx 32rpx;
.u-cell-title { position: relative;
color: #323233; }
font-size: 30rpx;
flex: 1; .u-cell-border:after {
margin-left: 6rpx; left: 32rpx !important;
text-align: left; position: absolute;
} box-sizing: border-box;
content: ' ';
.u-cell-value { pointer-events: none;
flex: 1; right: 0;
font-size: 26rpx; top: 0;
color: #969799; border-bottom: 1px solid $u-border-color;
text-align: right; -webkit-transform: scaleY(0.5);
} transform: scaleY(0.5);
}
.u-cell-label {
color: #969799; .u-cell-content {
font-size: 26rpx; display: flex;
margin-top: 10rpx; align-items: center;
text-align: left; }
}
.u-cell-title {
.u-slot-wrap { color: #323233;
display: flex; font-size: 30rpx;
align-items: center; flex: 1;
} margin-left: 6rpx;
text-align: left;
// 微信小程序需要额外处理可能通过slot传入badge和switch的问题 }
// 否则无法垂直居中
/* #ifdef MP-WEIXIN */ .u-cell-value {
.u-slot-wrap /deep/ u-badge, flex: 1;
.u-slot-wrap /deep/ u-switch { font-size: 26rpx;
display: flex; color: #969799;
align-items: center; text-align: right;
} }
/* #endif */
.u-cell-label {
.u-icon { color: #969799;
margin-right: 6rpx; font-size: 26rpx;
} margin-top: 10rpx;
text-align: left;
.u-value-text { }
margin-right: 10rpx;
} .u-slot-wrap {
display: flex;
.u-title-text { align-items: center;
margin-right: 10rpx; }
}
</style> // 微信小程序需要额外处理可能通过slot传入badge和switch的问题
\ No newline at end of file // 否则无法垂直居中
/* #ifdef MP-WEIXIN */
.u-slot-wrap /deep/ u-badge,
.u-slot-wrap /deep/ u-switch {
display: flex;
align-items: center;
}
/* #endif */
.u-icon {
margin-right: 6rpx;
}
.u-value-text {
margin-right: 10rpx;
}
.u-title-text {
margin-right: 10rpx;
}
</style>
...@@ -5,6 +5,16 @@ ...@@ -5,6 +5,16 @@
</template> </template>
<script> <script>
/**
* alertTips 提示
* @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便
* @tutorial https://www.uviewui.com/components/checkbox.html
* @property {String Number} max 最多能选中多少个checkbox(默认999)
* @property {Boolean} disabled 是否禁用所有checkbox(默认false)
* @property {String} active-color 选中时的颜色,应用到所有子Checkbox组件(默认#2979ff)
* @event {Function} change 任一个checkbox状态发生变化时触发,回调为一个对象
* @example <u-checkbox-group></u-checkbox-group>
*/
export default { export default {
props: { props: {
// 最多能选中多少个checkbox // 最多能选中多少个checkbox
......
<template> <template>
<view class="u-checkbox"> <view class="u-checkbox">
<view class="u-checkbox__icon-wrap" @tap="toggle"> <view class="u-checkbox__icon-wrap" @tap="toggle">
<u-icon :class="iconClass" name="checkbox-mark" :size="iconSize" :color="iconColor" class="u-checkbox__icon" :style="[iconStyle]" /> <u-icon :class="iconClass" name="checkbox-mark" :size="iconSize" :color="iconColor" class="u-checkbox__icon" :style="[iconStyle]" />
</view> </view>
<view class="u-label-class u-checkbox__label" @tap="onClickLabel"><slot /></view> <view class="u-label-class u-checkbox__label" @tap="onClickLabel">
</view> <slot />
</template> </view>
</view>
<script> </template>
export default {
props: { <script>
// checkbox的名称 /**
name: { * alertTips 提示
type: [String, Number], * @description 该组件需要搭配checkboxGroup组件使用,以便用户进行操作时,获得当前复选框组的选中情况。
default: '' * @tutorial https://www.uviewui.com/components/checkbox.html
}, * @property {String Number} icon-size 图标大小,单位rpx(默认24)
// 形状,square为方形,circle为原型 * @property {String Number} size 组件整体的大小,单位rpx(默认40)
shape: { * @property {String Number} name checkbox组件的标示符
type: String, * @property {String} shape 形状,见官网说明(默认circle)
default: 'square' * @property {Boolean} disabled 是否禁用(默认false)
}, * @property {Boolean} label-disabled 点击文本是否可以操作checkbox(默认true)
// 是否为选中状态 * @property {String} active-color 选中时的颜色,如设置CheckboxGroup的active-color将失效
value: { * @event {Function} change 某个checkbox状态发生变化时触发,回调为一个对象
type: Boolean, * @example <u-checkbox v-model="checked" :disabled="false">天涯</u-checkbox>
default: false */
}, export default {
// 是否禁用 name: "u-checkbox",
disabled: { props: {
type: Boolean, // checkbox的名称
default: false name: {
}, type: [String, Number],
// 是否禁止点击提示语选中复选框 default: ''
labelDisabled: { },
type: Boolean, // 形状,square为方形,circle为原型
default: false shape: {
}, type: String,
// 选中状态下的颜色,如设置此值,将会覆盖checkboxGroup的activeColor值 default: 'square'
activeColor: { },
type: String, // 是否为选中状态
default: '' value: {
}, type: Boolean,
// 图标的大小,单位rpx default: false
iconSize: { },
type: [String, Number], // 是否禁用
default: 24 disabled: {
}, type: Boolean,
}, default: false
inject: ['checkboxGroup'], },
data() { // 是否禁止点击提示语选中复选框
return { labelDisabled: {
parentDisabled: false, type: Boolean,
}; default: false
}, },
created() { // 选中状态下的颜色,如设置此值,将会覆盖checkboxGroup的activeColor值
this.parentDisabled = this.checkboxGroup.disabled; activeColor: {
this.checkboxGroup.children.push(this); type: String,
}, default: ''
computed: { },
iconStyle() { // 图标的大小,单位rpx
let style = {}; iconSize: {
if (this.checkboxActiveColor && this.value && !this.disabled && !this.parentDisabled) { type: [String, Number],
style.borderColor = this.checkboxActiveColor; default: 24
style.backgroundColor = this.checkboxActiveColor; },
} },
style.width = this.checkboxGroup.size + 'rpx'; inject: ['checkboxGroup'],
style.height = this.checkboxGroup.size + 'rpx'; data() {
return style; return {
}, parentDisabled: false,
iconColor() { };
return this.value ? '#ffffff' : 'transparent'; },
}, created() {
iconClass() { this.parentDisabled = this.checkboxGroup.disabled;
let classs = []; this.checkboxGroup.children.push(this);
classs.push('u-checkbox__icon--' + this.shape); },
if(this.value == true) classs.push('u-checkbox__icon--checked'); computed: {
if(this.disabled || this.parentDisabled) classs.push('u-checkbox__icon--disabled'); iconStyle() {
if(this.value && (this.disabled || this.parentDisabled)) classs.push('u-checkbox__icon--disabled--checked'); let style = {};
return classs; if (this.checkboxActiveColor && this.value && !this.disabled && !this.parentDisabled) {
}, style.borderColor = this.checkboxActiveColor;
// 激活的颜色,可能受checkboxGroup和本组件的activeColor影响 style.backgroundColor = this.checkboxActiveColor;
// 本组件的activeColor值优先 }
checkboxActiveColor() { style.width = this.checkboxGroup.size + 'rpx';
return this.activeColor ? this.activeColor : this.checkboxGroup.activeColor; style.height = this.checkboxGroup.size + 'rpx';
} return style;
}, },
methods: { iconColor() {
onClickLabel() { return this.value ? '#ffffff' : 'transparent';
if (!this.disabled && !this.labelDisabled && !this.parentDisabled) { },
this.setValue(); iconClass() {
} let classs = [];
}, classs.push('u-checkbox__icon--' + this.shape);
toggle() { if (this.value == true) classs.push('u-checkbox__icon--checked');
if (!this.disabled && !this.parentDisabled) { if (this.disabled || this.parentDisabled) classs.push('u-checkbox__icon--disabled');
this.setValue(); if (this.value && (this.disabled || this.parentDisabled)) classs.push('u-checkbox__icon--disabled--checked');
} return classs;
}, },
emitEvent() { // 激活的颜色,可能受checkboxGroup和本组件的activeColor影响
this.$emit('change', {value: this.value, name: this.name}) // 本组件的activeColor值优先
this.checkboxGroup.emitEvent(); checkboxActiveColor() {
}, return this.activeColor ? this.activeColor : this.checkboxGroup.activeColor;
// 设置input的值,这里通过input事件,设置通过v-model绑定的组件的值 }
setValue() { },
// 判断是否超过了可选的最大数量 methods: {
let checkedNum = 0; onClickLabel() {
this.checkboxGroup.children.map(val => { if (!this.disabled && !this.labelDisabled && !this.parentDisabled) {
if(val.value) checkedNum ++; this.setValue();
}) }
// 如果原来为选中状态,那么可以取消 },
if(this.value == true) { toggle() {
this.$emit('input', !this.value); if (!this.disabled && !this.parentDisabled) {
// 等待下一个周期再执行,因为this.$emit('input')作用于父组件,再反馈到子组件内部,需要时间 this.setValue();
this.$nextTick(function(){ }
this.emitEvent(); },
}) emitEvent() {
} else if(checkedNum < this.checkboxGroup.max && this.value == false) { this.$emit('change', {
// 如果原来为未选中状态,需要选中的数量少于父组件中设置的max值,才可以选中 value: this.value,
this.$emit('input', !this.value); name: this.name
// 等待下一个周期再执行,因为this.$emit('input')作用于父组件,再反馈到子组件内部,需要时间 })
this.$nextTick(function(){ this.checkboxGroup.emitEvent();
this.emitEvent(); },
}) // 设置input的值,这里通过input事件,设置通过v-model绑定的组件的值
} setValue() {
// 判断是否超过了可选的最大数量
} let checkedNum = 0;
} this.checkboxGroup.children.map(val => {
}; if (val.value) checkedNum++;
</script> })
// 如果原来为选中状态,那么可以取消
<style lang="scss" scoped> if (this.value == true) {
.u-checkbox { this.$emit('input', !this.value);
display: -webkit-flex; // 等待下一个周期再执行,因为this.$emit('input')作用于父组件,再反馈到子组件内部,需要时间
display: flex; this.$nextTick(function() {
-webkit-align-items: center; this.emitEvent();
align-items: center; })
overflow: hidden; } else if (checkedNum < this.checkboxGroup.max && this.value == false) {
-webkit-user-select: none; // 如果原来为未选中状态,需要选中的数量少于父组件中设置的max值,才可以选中
user-select: none; this.$emit('input', !this.value);
} // 等待下一个周期再执行,因为this.$emit('input')作用于父组件,再反馈到子组件内部,需要时间
this.$nextTick(function() {
.u-checkbox__icon-wrap, this.emitEvent();
.u-checkbox__label { })
color: $u-content-color; }
}
}
.u-checkbox__icon-wrap { }
-webkit-flex: none; };
flex: none; </script>
}
<style lang="scss" scoped>
.u-checkbox__icon { .u-checkbox {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
-webkit-align-items: center; -webkit-align-items: center;
align-items: center; align-items: center;
-webkit-justify-content: center; overflow: hidden;
justify-content: center; -webkit-user-select: none;
box-sizing: border-box; user-select: none;
width: 42rpx; }
height: 42rpx;
color: transparent; .u-checkbox__icon-wrap,
text-align: center; .u-checkbox__label {
transition-property: color, border-color, background-color; color: $u-content-color;
font-size: 20px; }
border: 1px solid #c8c9cc;
transition-duration: 0.2s; .u-checkbox__icon-wrap {
} -webkit-flex: none;
flex: none;
.u-checkbox__icon--circle { }
border-radius: 100%;
} .u-checkbox__icon {
display: -webkit-flex;
.u-checkbox__icon--square { display: flex;
border-radius: 3px; -webkit-align-items: center;
} align-items: center;
-webkit-justify-content: center;
.u-checkbox__icon--checked { justify-content: center;
color: #fff; box-sizing: border-box;
background-color: #2979ff; width: 42rpx;
border-color: #2979ff; height: 42rpx;
} color: transparent;
text-align: center;
.u-checkbox__icon--disabled { transition-property: color, border-color, background-color;
background-color: #ebedf0; font-size: 20px;
border-color: #c8c9cc; border: 1px solid #c8c9cc;
} transition-duration: 0.2s;
}
.u-checkbox__icon--disabled--checked {
color: #c8c9cc!important; .u-checkbox__icon--circle {
} border-radius: 100%;
}
.u-checkbox__label {
word-wrap: break-word; .u-checkbox__icon--square {
margin-left: 10rpx; border-radius: 3px;
margin-right: 18rpx; }
color: $u-content-color;
font-size: 30rpx; .u-checkbox__icon--checked {
} color: #fff;
background-color: #2979ff;
.u-checkbox__label--disabled { border-color: #2979ff;
color: #c8c9cc; }
}
.u-checkbox__icon--disabled {
.u-checkbox__label:empty { background-color: #ebedf0;
margin: 0; border-color: #c8c9cc;
} }
.u-checkbox__icon--disabled--checked {
color: #c8c9cc !important;
}
.u-checkbox__label {
word-wrap: break-word;
margin-left: 10rpx;
margin-right: 18rpx;
color: $u-content-color;
font-size: 30rpx;
}
.u-checkbox__label--disabled {
color: #c8c9cc;
}
.u-checkbox__label:empty {
margin: 0;
}
</style> </style>
<template> <template>
<view class="u-circle-progress" :style="{ <view class="u-circle-progress" :style="{
width: widthPx + 'px', width: widthPx + 'px',
height: widthPx + 'px', height: widthPx + 'px',
backgroundColor: bgColor backgroundColor: bgColor
}"> }">
<canvas class="u-canvas-bg" :canvas-id="elBgId" :style="{ <canvas class="u-canvas-bg" :canvas-id="elBgId" :style="{
width: widthPx + 'px', width: widthPx + 'px',
height: widthPx + 'px' height: widthPx + 'px'
}"></canvas> }"></canvas>
<canvas class="u-canvas" :canvas-id="elId" :style="{ <canvas class="u-canvas" :canvas-id="elId" :style="{
width: widthPx + 'px', width: widthPx + 'px',
height: widthPx + 'px' height: widthPx + 'px'
}"></canvas> }"></canvas>
<slot></slot> <slot></slot>
</view> </view>
</template> </template>
<script> <script>
export default { /**
props: { * alertTips 提示
// 圆环进度百分比值 * @description 展示操作或任务的当前进度,比如上传文件,是一个圆形的进度条。注意:此组件的percent值只能动态增加,不能动态减少。
percent: { * @tutorial https://www.uviewui.com/components/circleProgress.html
type: Number, * @property {String Number} percent 圆环进度百分比值,为数值类型,0-100
default: 0, * @property {String} inactive-color 圆环的底色,默认为灰色(该值无法动态变更)(默认#ececec)
// 限制值在0到100之间 * @property {String} active-color 圆环激活部分的颜色(该值无法动态变更)(默认#19be6b)
validator: val => { * @property {String Number} width 整个圆环组件的宽度,高度默认等于宽度值,单位rpx(默认200)
return val >= 0 && val <= 100; * @property {String Number} border-width 圆环的边框宽度,单位rpx(默认14)
} * @property {String Number} duration 整个圆环执行一圈的时间,单位ms(默认呢1500)
}, * @property {String} type 如设置,active-color值将会失效
// 底部圆环的颜色(灰色的圆环) * @property {String} bg-color 整个组件背景颜色,默认为白色
inactiveColor: { * @example <u-circle-progress active-color="#2979ff" :percent="80"></u-circle-progress>
type: String, */
default: "#ececec" export default {
}, name: "u-circle-progress",
// 圆环激活部分的颜色 props: {
activeColor: { // 圆环进度百分比值
type: String, percent: {
default: '#19be6b' type: Number,
}, default: 0,
// 圆环线条的宽度,单位rpx // 限制值在0到100之间
borderWidth: { validator: val => {
type: [Number, String], return val >= 0 && val <= 100;
default: 14 }
}, },
// 整个圆形的宽度,单位rpx // 底部圆环的颜色(灰色的圆环)
width: { inactiveColor: {
type: [Number, String], type: String,
default: 200 default: "#ececec"
}, },
// 整个圆环执行一圈的时间,单位ms // 圆环激活部分的颜色
duration: { activeColor: {
type: [Number, String], type: String,
default: 1500 default: '#19be6b'
}, },
// 主题类型 // 圆环线条的宽度,单位rpx
type: { borderWidth: {
type: String, type: [Number, String],
default: '' default: 14
}, },
// 整个圆环进度区域的背景色 // 整个圆形的宽度,单位rpx
bgColor: { width: {
type: String, type: [Number, String],
default: "#ffffff" default: 200
} },
}, // 整个圆环执行一圈的时间,单位ms
data() { duration: {
return { type: [Number, String],
elBgId: this.$u.guid(), default: 1500
elId: this.$u.guid(), },
ctxBg: '', // 背景canvas实例 // 主题类型
ctx: '', // 前景(激活时候)canvas的实例 type: {
count: 0, // 计数器 type: String,
timer: null, // 定时器 default: ''
times: 0, // 总共要执行的动画次数,setInterval的次数 },
time: 0, // 执行整个动画的时间 // 整个圆环进度区域的背景色
widthPx: uni.upx2px(this.width), // 转成px后的整个组件的背景宽度 bgColor: {
borderWidthPx: uni.upx2px(this.borderWidth), // 转成px后的圆环的宽度 type: String,
mode: 'more', // more-percent增加,less-percent减少 default: "#ffffff"
} }
}, },
watch: { data() {
percent: { return {
immediate: true, elBgId: this.$u.guid(),
handler(nVal, oVal = 0) { elId: this.$u.guid(),
this.mode = nVal > oVal ? 'more' : 'less'; ctxBg: '', // 背景canvas实例
this.times = Math.ceil(nVal * 3.6); ctx: '', // 前景(激活时候)canvas的实例
this.time = Math.ceil(this.duration / 360 * this.times); count: 0, // 计数器
setTimeout(() => { timer: null, // 定时器
this.countInterval(); times: 0, // 总共要执行的动画次数,setInterval的次数
}, 50) time: 0, // 执行整个动画的时间
} widthPx: uni.upx2px(this.width), // 转成px后的整个组件的背景宽度
} borderWidthPx: uni.upx2px(this.borderWidth), // 转成px后的圆环的宽度
}, mode: 'more', // more-percent增加,less-percent减少
computed: { }
// 有type主题时,优先起作用 },
circleColor() { watch: {
if(['success', 'error', 'info', 'primary', 'warning'].indexOf(this.type) >= 0) return this.$u.color[this.type]; percent: {
else return this.activeColor; immediate: true,
} handler(nVal, oVal = 0) {
}, this.mode = nVal > oVal ? 'more' : 'less';
mounted() { this.times = Math.ceil(nVal * 3.6);
this.ctxBg = uni.createCanvasContext(this.elBgId, this); this.time = Math.ceil(this.duration / 360 * this.times);
this.ctx = uni.createCanvasContext(this.elId, this); setTimeout(() => {
// 在h5端,必须要做一点延时才起作用,this.$nextTick()无效(HX2.4.7) this.countInterval();
setTimeout(() => { }, 50)
this.drawProgressBg(); }
}, 50) }
}, },
methods: { computed: {
drawProgressBg() { // 有type主题时,优先起作用
this.ctxBg.setLineWidth(this.borderWidthPx); // 设置圆环宽度 circleColor() {
this.ctxBg.setStrokeStyle(this.inactiveColor); // 线条颜色 if (['success', 'error', 'info', 'primary', 'warning'].indexOf(this.type) >= 0) return this.$u.color[this.type];
this.ctxBg.setLineCap('round'); // 圆环端点的形状为圆形 else return this.activeColor;
this.ctxBg.beginPath(); // 开始描绘路径 }
// 设置一个原点(110,110),半径为100的圆的路径到当前路径 },
this.ctxBg.arc(this.widthPx / 2, this.widthPx / 2, this.widthPx / 2 - this.borderWidthPx / 2 - 1, 0, 2 * Math.PI, false); mounted() {
this.ctxBg.stroke(); // 对路径进行描绘 this.ctxBg = uni.createCanvasContext(this.elBgId, this);
this.ctxBg.draw(); this.ctx = uni.createCanvasContext(this.elId, this);
}, // 在h5端,必须要做一点延时才起作用,this.$nextTick()无效(HX2.4.7)
drawCircle(step) { setTimeout(() => {
this.ctx.setLineWidth(this.borderWidthPx); this.drawProgressBg();
this.ctx.setStrokeStyle(this.circleColor); }, 50)
this.ctx.setLineCap('round'); },
this.ctx.beginPath(); methods: {
// 参数step 为绘制的圆环周长,从0到2为一周 。 -Math.PI / 2 将起始角设在12点钟位置 ,结束角 通过改变 step 的值确定 drawProgressBg() {
if(this.mode == 'more') { this.ctxBg.setLineWidth(this.borderWidthPx); // 设置圆环宽度
this.ctx.arc(this.widthPx / 2, this.widthPx / 2, this.widthPx / 2 - this.borderWidthPx / 2 - 1, -Math.PI / 2, step * this.ctxBg.setStrokeStyle(this.inactiveColor); // 线条颜色
Math.PI - Math.PI / 2, false); this.ctxBg.setLineCap('round'); // 圆环端点的形状为圆形
} else { this.ctxBg.beginPath(); // 开始描绘路径
this.ctx.arc(this.widthPx / 2, this.widthPx / 2, this.widthPx / 2 - this.borderWidthPx / 2 - 1, -Math.PI / 2, Math.PI / 2 - step * // 设置一个原点(110,110),半径为100的圆的路径到当前路径
Math.PI, false); this.ctxBg.arc(this.widthPx / 2, this.widthPx / 2, this.widthPx / 2 - this.borderWidthPx / 2 - 1, 0, 2 * Math.PI,
} false);
this.ctx.stroke(); this.ctxBg.stroke(); // 对路径进行描绘
this.ctx.draw() this.ctxBg.draw();
}, },
countInterval() { drawCircle(step) {
this.countTimer = setInterval(() => { this.ctx.setLineWidth(this.borderWidthPx);
if (this.count <= this.times) { this.ctx.setStrokeStyle(this.circleColor);
// 全一个圆时候,值为2,这里求出每一份的值为2/360 this.ctx.setLineCap('round');
this.drawCircle(this.count * 2 / 360); this.ctx.beginPath();
this.count++; // 参数step 为绘制的圆环周长,从0到2为一周 。 -Math.PI / 2 将起始角设在12点钟位置 ,结束角 通过改变 step 的值确定
} else { if (this.mode == 'more') {
clearInterval(this.countTimer); this.ctx.arc(this.widthPx / 2, this.widthPx / 2, this.widthPx / 2 - this.borderWidthPx / 2 - 1, -Math.PI / 2, step *
} Math.PI - Math.PI / 2, false);
}, Math.ceil(this.duration / 360)); // 总过渡时间分为360份,这里为每一份的时间 } else {
}, this.ctx.arc(this.widthPx / 2, this.widthPx / 2, this.widthPx / 2 - this.borderWidthPx / 2 - 1, -Math.PI / 2, Math
} .PI / 2 - step *
} Math.PI, false);
</script> }
this.ctx.stroke();
<style lang="scss" scoped> this.ctx.draw()
.u-circle-progress { },
position: relative; countInterval() {
display: inline-flex; this.countTimer = setInterval(() => {
align-items: center; if (this.count <= this.times) {
justify-content: center; // 全一个圆时候,值为2,这里求出每一份的值为2/360
} this.drawCircle(this.count * 2 / 360);
this.count++;
.u-canvas-bg { } else {
position: absolute; clearInterval(this.countTimer);
} }
}, Math.ceil(this.duration / 360)); // 总过渡时间分为360份,这里为每一份的时间
.u-canvas { },
position: absolute; }
} }
</script>
<style lang="scss" scoped>
.u-circle-progress {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
}
.u-canvas-bg {
position: absolute;
}
.u-canvas {
position: absolute;
}
</style> </style>
<template> <template>
<view class="u-collapse-item"> <view class="u-collapse-item">
<view <view class="u-collapse-head" @tap.stop="headClick" hover-class="u-hover-class" :style="[headStyle]">
class="u-collapse-head" <view class="u-collapse-title u-line-1" :style="[{ textAlign: align ? align : 'left' },
@tap.stop="headClick" isShow && activeStyle && !arrow ? activeStyle : '']">
hover-class="u-hover-class" {{ title }}
:style="[headStyle]" </view>
> <view class="u-icon-wrap">
<view class="u-collapse-title u-line-1" <u-icon v-if="arrow" :color="arrowColor ? arrowColor : $u.color.tipsColor" :class="{ 'u-arrow-down-icon-active': isShow }"
:style="[{ textAlign: align ? align : 'left' }, class="u-arrow-down-icon" name="arrow-down"></u-icon>
isShow && activeStyle && !arrow ? activeStyle : '']" </view>
> </view>
{{ title }} <view class="u-collapse-body" :style="[{
</view> height: isShow ? height + 'px' : '0'
<view class="u-icon-wrap"> }, bodyStyle]">
<u-icon <view class="u-collapse-content" :id="elId">
v-if="arrow" <slot></slot>
:color="arrowColor ? arrowColor : $u.color.tipsColor" </view>
:class="{ 'u-arrow-down-icon-active': isShow }" </view>
class="u-arrow-down-icon" </view>
name="arrow-down" </template>
></u-icon>
</view> <script>
</view> /**
<view * alertTips 提示
class="u-collapse-body" * @description 通过折叠面板收纳内容区域(搭配u-collapse使用)
:style="[{ * @tutorial https://www.uviewui.com/components/collapse.html
height: isShow ? height + 'px' : '0' * @property {String} title 面板标题
}, bodyStyle]" * @property {String Number} index 主要用于事件的回调,标识那个Item被点击
> * @property {Boolean} disabled 面板是否可以打开或收起(默认false)
<view class="u-collapse-content" :id="elId"><slot></slot></view> * @property {Boolean} open 设置某个面板的初始状态是否打开(默认false)
</view> * @property {String Number} name 唯一标识符,如不设置,默认用当前collapse-item的索引值
</view> * @property {String} align 标题的对齐方式(默认left)
</template> * @property {Object} active-style 不显示箭头时,可以添加当前选择的collapse-item活动样式,对象形式
* @event {Function} change 某个item被打开或者收起时触发
<script> * @example <u-collapse-item :title="item.head" v-for="(item, index) in itemList" :key="index">{{item.body}}</u-collapse-item>
export default { */
props: { export default {
// 标题 name: "u-collapse-item",
title: { props: {
type: String, // 标题
default: '' title: {
}, type: String,
// 标题的对齐方式 default: ''
align: { },
type: String, // 标题的对齐方式
default: 'left' align: {
}, type: String,
// 是否可以点击收起 default: 'left'
disabled: { },
type: Boolean, // 是否可以点击收起
default: false disabled: {
}, type: Boolean,
// collapse显示与否 default: false
open: { },
type: Boolean, // collapse显示与否
default: false open: {
}, type: Boolean,
// 唯一标识符 default: false
name: { },
type: [Number, String], // 唯一标识符
default: '' name: {
}, type: [Number, String],
//活动样式 default: ''
activeStyle: { },
type: Object, //活动样式
default() { activeStyle: {
return {} type: Object,
} default () {
}, return {}
// 标识当前为第几个 }
index: { },
type: [String, Number], // 标识当前为第几个
default: '' index: {
} type: [String, Number],
}, default: ''
inject: ['uCollapse'], }
data() { },
return { inject: ['uCollapse'],
isShow: false, data() {
elId: this.$u.guid(), return {
height: 0, // body内容的高度 isShow: false,
headStyle: {}, // 头部样式,对象形式 elId: this.$u.guid(),
bodyStyle: {}, // 主体部分样式 height: 0, // body内容的高度
arrowColor: '' headStyle: {}, // 头部样式,对象形式
}; bodyStyle: {}, // 主体部分样式
}, arrowColor: ''
mounted() { };
this.$nextTick(() => { },
this.queryRect(); mounted() {
}); this.$nextTick(() => {
}, this.queryRect();
watch: { });
open(val) { },
this.isShow = val; watch: {
} open(val) {
}, this.isShow = val;
computed: { }
arrow() { },
return this.uCollapse.arrow; computed: {
} arrow() {
}, return this.uCollapse.arrow;
created() { }
this.isShow = this.open; },
this.nameSync = this.name ? this.name : this.uCollapse.childrens.length; created() {
this.uCollapse.childrens.push(this); this.isShow = this.open;
this.headStyle = this.uCollapse.headStyle; this.nameSync = this.name ? this.name : this.uCollapse.childrens.length;
this.bodyStyle = this.uCollapse.bodyStyle; this.uCollapse.childrens.push(this);
this.arrowColor = this.uCollapse.arrowColor; this.headStyle = this.uCollapse.headStyle;
}, this.bodyStyle = this.uCollapse.bodyStyle;
methods: { this.arrowColor = this.uCollapse.arrowColor;
// 点击collapsehead头部 },
headClick() { methods: {
if (this.disabled) return; // 点击collapsehead头部
if (this.uCollapse.accordion == true) { headClick() {
this.uCollapse.childrens.map(val => { if (this.disabled) return;
// 自身不设置为false,因为后面有this.isShow = !this.isShow;处理了 if (this.uCollapse.accordion == true) {
if (this != val) { this.uCollapse.childrens.map(val => {
val.isShow = false; // 自身不设置为false,因为后面有this.isShow = !this.isShow;处理了
} if (this != val) {
}); val.isShow = false;
} }
});
this.isShow = !this.isShow; }
// 触发本组件的事件
this.$emit('change', { this.isShow = !this.isShow;
index: this.index, // 触发本组件的事件
show: this.isShow this.$emit('change', {
}) index: this.index,
show: this.isShow
// 只有在打开时才发出事件 })
if (this.isShow) this.uCollapse.onChange();
this.$forceUpdate(); // 只有在打开时才发出事件
}, if (this.isShow) this.uCollapse.onChange();
// 查询内容高度 this.$forceUpdate();
queryRect() { },
const query = uni.createSelectorQuery().in(this); // 查询内容高度
query queryRect() {
.select('#' + this.elId) const query = uni.createSelectorQuery().in(this);
.boundingClientRect(data => { query
if (!data.height) { .select('#' + this.elId)
setTimeout(() => { .boundingClientRect(data => {
this.queryRect(); if (!data.height) {
}, 10); setTimeout(() => {
return; this.queryRect();
} }, 10);
this.height = data.height; return;
}) }
.exec(); this.height = data.height;
} })
} .exec();
}; }
</script> }
};
<style lang="scss" scoped> </script>
.u-collapse-head {
position: relative; <style lang="scss" scoped>
display: flex; .u-collapse-head {
justify-content: space-between; position: relative;
align-items: center; display: flex;
color: $u-main-color; justify-content: space-between;
} align-items: center;
color: $u-main-color;
.u-collapse-title { }
flex: 1;
overflow: hidden; .u-collapse-title {
margin-right: 14rpx; flex: 1;
font-size: 30rpx; overflow: hidden;
color: $u-main-color; margin-right: 14rpx;
line-height: 1; font-size: 30rpx;
padding: 24rpx 0; color: $u-main-color;
text-align: left; line-height: 1;
} padding: 24rpx 0;
text-align: left;
.u-arrow-down-icon { }
transition: all 0.3s;
margin-right: 24rpx; .u-arrow-down-icon {
} transition: all 0.3s;
margin-right: 24rpx;
.u-arrow-down-icon-active { }
transform: rotate(180deg);
transform-origin: center center; .u-arrow-down-icon-active {
} transform: rotate(180deg);
transform-origin: center center;
.u-collapse-body { }
overflow: hidden;
transition: all 0.3s; .u-collapse-body {
} overflow: hidden;
transition: all 0.3s;
.u-collapse-content { }
overflow: hidden;
font-size: 28rpx; .u-collapse-content {
color: $u-tips-color; overflow: hidden;
text-align: left; font-size: 28rpx;
} color: $u-tips-color;
text-align: left;
}
</style> </style>
<template> <template>
<view class="u-collapse"> <view class="u-collapse">
<slot /> <slot />
</view> </view>
</template> </template>
<script> <script>
/**
* alertTips 提示
* @description 通过折叠面板收纳内容区域
* @tutorial https://www.uviewui.com/components/collapse.html
* @property {Boolean} accordion 是否手风琴模式(默认true)
* @property {Boolean} arrow 是否显示标题右侧的箭头(默认true)
* @property {String} arrow-color 标题右侧箭头的颜色(默认#909399)
* @property {String} head-bg-color 标题的背景颜色(默认#ffffff)
* @property {String} body-bg-color 主体内容的背景颜色(默认#ffffff)
* @event {Function} change 当前激活面板展开时触发(如果是手风琴模式,参数activeNames类型为String,否则为Array)
* @example <u-collapse></u-collapse>
*/
export default { export default {
props: { name:"u-collapse",
// 是否手风琴模式 props: {
accordion: { // 是否手风琴模式
type: Boolean, accordion: {
default: true type: Boolean,
}, default: true
// 头部的样式 },
headStyle: { // 头部的样式
type: Object, headStyle: {
default() { type: Object,
return {} default () {
} return {}
}, }
// 主体的样式 },
bodyStyle: { // 主体的样式
type: Object, bodyStyle: {
default() { type: Object,
return {} default () {
} return {}
}, }
// 是否显示右侧的箭头 },
arrow: { // 是否显示右侧的箭头
type: Boolean, arrow: {
default: true type: Boolean,
}, default: true
// 箭头的颜色 },
arrowColor: { // 箭头的颜色
type: String, arrowColor: {
default: '' type: String,
}, default: ''
}, },
provide() { },
return { provide() {
uCollapse: this return {
} uCollapse: this
}, }
created() { },
this.childrens = [] created() {
}, this.childrens = []
data() { },
return { data() {
return {
}
}, }
methods: { },
// collapse item被点击,由collapse item调用父组件方法 methods: {
onChange() { // collapse item被点击,由collapse item调用父组件方法
let activeItem = []; onChange() {
this.childrens.forEach((vm, index) => { let activeItem = [];
if (vm.isShow) { this.childrens.forEach((vm, index) => {
activeItem.push(vm.nameSync); if (vm.isShow) {
} activeItem.push(vm.nameSync);
}) }
// 如果是手风琴模式,只有一个匹配结果,也即activeItem长度为1,将其转为字符串 })
if(this.accordion) activeItem = activeItem.join(''); // 如果是手风琴模式,只有一个匹配结果,也即activeItem长度为1,将其转为字符串
this.$emit('change', activeItem); if (this.accordion) activeItem = activeItem.join('');
} this.$emit('change', activeItem);
} }
} }
</script> }
</script>
<style lang="scss" scoped>
<style lang="scss" scoped>
</style> </style>
<template> <template>
<view class="u-empty" v-if="show"> <view class="u-empty" v-if="show">
<image class="u-image" :src="src ? src : icons[mode].image" mode="widthFix" :style="{ <image class="u-image" :src="src ? src : icons[mode].image" mode="widthFix" :style="{
width: imgWidth + 'rpx', width: imgWidth + 'rpx',
height: imgHeight == 'auto' ? 'auto' : imgHeight + 'rpx' height: imgHeight == 'auto' ? 'auto' : imgHeight + 'rpx'
}"></image> }"></image>
<text :style="{ <text :style="{
color: color, color: color,
fontSize: fontSize + 'rpx', fontSize: fontSize + 'rpx',
}"> }">
{{text ? text : icons[mode].text}} {{text ? text : icons[mode].text}}
</text> </text>
<view class="u-slot-wrap"> <view class="u-slot-wrap">
<slot name="bottom"></slot> <slot name="bottom"></slot>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
import icon from "./icon.js"; /**
export default { * alertTips 提示
props: { * @description 该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个"没有内容"的场景, 我们精心挑选了十几个场景的图标,方便您使用。
// 图标路径 * @tutorial https://www.uviewui.com/components/empty.html
src: { * @property {String} color 文字颜色(默认#c0c4cc)
type: String, * @property {String} text 文字提示(默认“无内容”)
default: '' * @property {String} src 自定义图标路径,如定义,mode参数会失效
}, * @property {String Number} font-size 提示文字的大小,单位rpx(默认28)
// 提示文字 * @property {String} mode 内置的图标,见官网说明(默认data)
text: { * @property {String Number} img-width 图标的宽度,单位rpx(默认240)
type: String, * @property {String} img-height 图标的高度,单位rpx(默认auto)
default: '' * @property {Boolean} show 是否显示组件(默认true)
}, * @event {Function} click 点击组件时触发
// 文字颜色 * @event {Function} close 点击关闭按钮时触发
color: { * @example <u-empty text="所谓伊人,在水一方" mode="list"></u-empty>
type: String, */
default: '#c0c4cc' import icon from "./icon.js";
}, export default {
// 文字大小,单位rpx name: "u-empty",
fontSize: { props: {
type: [String, Number], // 图标路径
default: 26 src: {
}, type: String,
// 选择预置的图标类型 default: ''
mode: { },
type: String, // 提示文字
default: 'data' text: {
}, type: String,
// 图标宽度,单位rpx default: ''
imgWidth: { },
type: [String, Number], // 文字颜色
default: 240 color: {
}, type: String,
// 图标高度,单位rpx default: '#c0c4cc'
imgHeight: { },
type: [String, Number], // 文字大小,单位rpx
default: 'auto' fontSize: {
}, type: [String, Number],
// 是否显示组件 default: 26
show: { },
type: Boolean, // 选择预置的图标类型
default: true mode: {
} type: String,
}, default: 'data'
data() { },
return { // 图标宽度,单位rpx
icons: icon imgWidth: {
} type: [String, Number],
} default: 240
} },
</script> // 图标高度,单位rpx
imgHeight: {
<style lang="scss" scoped> type: [String, Number],
.u-empty { default: 'auto'
display: flex; },
flex-direction: column; // 是否显示组件
justify-content: center; show: {
align-items: center; type: Boolean,
padding-top: 30%; default: true
} }
},
.u-image { data() {
margin-bottom: 20rpx; return {
} icons: icon
}
.u-slot-wrap { }
display: flex; }
justify-content: center; </script>
align-items: center;
margin-top: 20rpx; <style lang="scss" scoped>
} .u-empty {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-top: 30%;
}
.u-image {
margin-bottom: 20rpx;
}
.u-slot-wrap {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20rpx;
}
</style> </style>
<template> <template>
<view class="u-field" :class="{'u-field-border': itemIndex > 0 }"> <view class="u-field" :class="{'u-field-border': itemIndex > 0 }">
<view class="u-field-inner" :class="[type == 'textarea' ? 'u-textarea-inner' : '', 'u-label-postion-' + labelPostion]"> <view class="u-field-inner" :class="[type == 'textarea' ? 'u-textarea-inner' : '', 'u-label-postion-' + labelPostion]">
<view class="u-label" :class="[required ? 'u-required' : '']" <view class="u-label" :class="[required ? 'u-required' : '']" :style="{
:style="{ justifyContent: justifyContent,
justifyContent: justifyContent, flex: labelPostion == 'left' ? `0 0 ${labelWidth}rpx` : '1'
flex: labelPostion == 'left' ? `0 0 ${labelWidth}rpx` : '1' }">
}"> <view class="u-icon-wrap" v-if="icon">
<view class="u-icon-wrap" v-if="icon"> <u-icon size="32" :name="icon" :color="iconColor" class="u-icon"></u-icon>
<u-icon size="32" :name="icon" :color="iconColor" class="u-icon"></u-icon> </view>
</view> <slot name="icon"></slot>
<slot name="icon"></slot> <text class="u-label-text" :class="[this.$slots.icon || icon ? 'u-label-left-gap' : '']">{{ label }}</text>
<text class="u-label-text" :class="[this.$slots.icon || icon ? 'u-label-left-gap' : '']">{{ label }}</text> </view>
</view> <view class="fild-body">
<view class="fild-body"> <textarea v-if="type == 'textarea'" class="u-input-class u-textarea-class" :style="[inputStyle]" :value="value"
<textarea :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" :maxlength="inputMaxlength"
v-if="type == 'textarea'" :focus="focus" :autoHeight="autoHeight" :fixed="fixed" @input="onInput" @blur="onBlur" @focus="onFocus" @confirm="onConfirm"
class="u-input-class u-textarea-class" @tap="fieldClick" />
:style="[inputStyle]" <input
:value="value" v-else
:placeholder="placeholder" :style="[inputStyle]"
:placeholderStyle="placeholderStyle" class="u-input-class"
:disabled="disabled" :type="type"
:maxlength="inputMaxlength" :value="value"
:focus="focus" :password="password || type === 'password'"
:autoHeight="autoHeight" :placeholder="placeholder"
:fixed="fixed" :placeholderStyle="placeholderStyle"
@input="onInput" :disabled="disabled"
@blur="onBlur" :maxlength="inputMaxlength"
@focus="onFocus" :focus="focus"
@confirm="onConfirm" :confirmType="confirmType"
@tap="fieldClick" @focus="onFocus"
/> @blur="onBlur"
<input @input="onInput"
v-else @confirm="onConfirm"
:style="[inputStyle]" @tap="fieldClick"
class="u-input-class" />
:type="type" <u-icon v-if="clearable && value && focused" name="close-circle-fill" color="#c0c4cc" class="u-clear-icon" @touchstart="onClear"/>
:value="value" <view class="u-button-wrap"><slot name="button" /></view>
:password="password || type === 'password'" <u-icon v-if="rightIcon" @click="rightIconClick" :name="rightIcon" color="#c0c4cc" :style="[rightIconStyle]" size="26" class="u-arror-right" />
:placeholder="placeholder" </view>
:placeholderStyle="placeholderStyle" </view>
:disabled="disabled" <view v-if="errorMessage !== false && errorMessage != ''" class="u-error-message" :style="{
:maxlength="inputMaxlength" paddingLeft: labelWidth + 'rpx'
:focus="focus" }">{{ errorMessage }}</view>
:confirmType="confirmType" </view>
@focus="onFocus" </template>
@blur="onBlur"
@input="onInput" <script>
@confirm="onConfirm" /**
@tap="fieldClick" * alertTips 提示
/> * @description 借助此组件,可以实现表单的输入, 有"text"和"textarea"类型的,此外,借助uView的picker和actionSheet组件可以快速实现上拉菜单,时间,地区选择等, 为表单解决方案的利器。
<u-icon v-if="clearable && value && focused" name="close-circle-fill" color="#c0c4cc" class="u-clear-icon" @touchstart="onClear"/> * @tutorial https://www.uviewui.com/components/field.html
<view class="u-button-wrap"><slot name="button" /></view> * @property {String} type 输入框的类型(默认text)
<u-icon v-if="rightIcon" @click="rightIconClick" :name="rightIcon" color="#c0c4cc" :style="[rightIconStyle]" size="26" class="u-arror-right" /> * @property {String} icon label左边的图标,限uView的图标名称
</view> * @property {Boolean} right-icon 输入框右边的(默认false)
</view> * @property {Boolean} required 是否必填,左边您显示红色"*"号(默认false)
<view v-if="errorMessage !== false && errorMessage != ''" class="u-error-message" :style="{ * @property {String} label 输入框左边的文字提示
paddingLeft: labelWidth + 'rpx' * @property {Boolean} password 是否密码输入方式(用点替换文字),type为text时有效(默认false)
}">{{ errorMessage }}</view> * @property {Boolean} clearable 是否显示右侧清空内容的图标控件(输入框有内容,且获得焦点时才显示),点击可清空输入框内容(默认true)
</view> * @property {Number String} label-width label的宽度,单位rpx(默认130)
</template> * @property {String} label-align label的文字对齐方式(默认left)
* @property {String} input-align 输入框内容对齐方式(默认left)
<script> * @property {String} icon-color 左边通过icon配置的图标的颜色(默认#606266)
export default { * @property {Boolean} auto-height 是否自动增高输入区域,type为textarea时有效(默认true)
props: { * @property {String Boolean} error-message 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息
icon: String, * @property {String} placeholder 输入框的提示文字
rightIcon: String, * @property {String} placeholder-style placeholder的样式(内联样式,字符串),如"color: #ddd"
// arrowDirection: { * @property {Boolean} focus 是否自动获得焦点(默认false)
// type: String, * @property {Boolean} fixed 如果type为textarea,且在一个"position:fixed"的区域,需要指明为true(默认false)
// default: 'right' * @property {Boolean} disabled 是否不可输入(默认false)
// }, * @property {Number String} maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认140)
required: Boolean, * @property {String} confirm-type 设置键盘右下角按钮的文字,仅在type="text"时生效(默认done)
label: String, * @event {Function} input 输入框内容发生变化时触发
password: Boolean, * @event {Function} focus 输入框获得焦点时触发
clearable: { * @event {Function} blur 输入框失去焦点时触发
type: Boolean, * @event {Function} confirm 点击完成按钮时触发
default: true * @event {Function} right-icon-click 通过right-icon生成的图标被点击时触发
}, * @event {Function} click 输入框被点击或者通过right-icon生成的图标被点击时触发,这样设计是考虑到传递右边的图标,一般都为需要弹出"picker"等操作时的场景,点击倒三角图标,理应发出此事件,见上方说明
// 左边标题的宽度单位rpx * @example <u-field v-model="mobile" label="手机号" required :error-message="errorMessage"></u-field>
labelWidth: { */
type: [Number, String], export default {
default: 130 name:"u-field",
}, props: {
// 对齐方式,left|center|right icon: String,
labelAlign: { rightIcon: String,
type: String, // arrowDirection: {
default: 'left' // type: String,
}, // default: 'right'
inputAlign: { // },
type: String, required: Boolean,
default: 'left' label: String,
}, password: Boolean,
iconColor: { clearable: {
type: String, type: Boolean,
default: '#606266' default: true
}, },
autoHeight: { // 左边标题的宽度单位rpx
type: Boolean, labelWidth: {
default: true type: [Number, String],
}, default: 130
errorMessage: { },
type: [String, Boolean], // 对齐方式,left|center|right
default: '' labelAlign: {
}, type: String,
placeholder: String, default: 'left'
placeholderStyle: String, },
focus: Boolean, inputAlign: {
fixed: Boolean, type: String,
value: [Number, String], default: 'left'
type: { },
type: String, iconColor: {
default: 'text' type: String,
}, default: '#606266'
disabled: { },
type: Boolean, autoHeight: {
default: false type: Boolean,
}, default: true
maxlength: { },
type: [Number, String], errorMessage: {
default: 140 type: [String, Boolean],
}, default: ''
confirmType: { },
type: String, placeholder: String,
default: 'done' placeholderStyle: String,
}, focus: Boolean,
// lable的位置,可选为 left-左边,top-上边 fixed: Boolean,
labelPostion: { value: [Number, String],
type: String, type: {
default: 'left' type: String,
} default: 'text'
}, },
inject: ['uCellGroup'], disabled: {
data() { type: Boolean,
return { default: false
focused: false, },
itemIndex: 0, maxlength: {
}; type: [Number, String],
}, default: 140
created() { },
if(this.uCellGroup) { confirmType: {
this.itemIndex = this.uCellGroup.index++; type: String,
} default: 'done'
}, },
computed: { // lable的位置,可选为 left-左边,top-上边
inputStyle() { labelPostion: {
let style = {}; type: String,
style.textAlign = this.inputAlign; default: 'left'
// 判断lable的位置,如果是left的话,让input左边两边有间隙 }
if(this.labelPostion == 'left') { },
style.margin = `0 8rpx`; inject: ['uCellGroup'],
} else { data() {
// 如果lable是top的,input的左边就没必要有间隙了 return {
style.marginRight = `8rpx`; focused: false,
} itemIndex: 0,
return style; };
}, },
rightIconStyle() { created() {
let style = {}; if(this.uCellGroup) {
if (this.arrowDirection == 'top') style.transform = 'roate(-90deg)'; this.itemIndex = this.uCellGroup.index++;
if (this.arrowDirection == 'bottom') style.transform = 'roate(90deg)'; }
else style.transform = 'roate(0deg)'; },
return style; computed: {
}, inputStyle() {
labelStyle() { let style = {};
let style = {}; style.textAlign = this.inputAlign;
if(this.labelAlign == 'left') style.justifyContent = 'flext-start'; // 判断lable的位置,如果是left的话,让input左边两边有间隙
if(this.labelAlign == 'center') style.justifyContent = 'center'; if(this.labelPostion == 'left') {
if(this.labelAlign == 'right') style.justifyContent = 'flext-end'; style.margin = `0 8rpx`;
return style; } else {
}, // 如果lable是top的,input的左边就没必要有间隙了
// uni不支持在computed中写style.justifyContent = 'center'的形式,故用此方法 style.marginRight = `8rpx`;
justifyContent() { }
if(this.labelAlign == 'left') return 'flex-start'; return style;
if(this.labelAlign == 'center') return 'center'; },
if(this.labelAlign == 'right') return 'flex-end'; rightIconStyle() {
}, let style = {};
// 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,给用户可以传入字符串数值 if (this.arrowDirection == 'top') style.transform = 'roate(-90deg)';
inputMaxlength() { if (this.arrowDirection == 'bottom') style.transform = 'roate(90deg)';
return Number(this.maxlength) else style.transform = 'roate(0deg)';
}, return style;
// label的位置 },
fieldInnerStyle() { labelStyle() {
let style = {}; let style = {};
if(this.labelPostion == 'left') { if(this.labelAlign == 'left') style.justifyContent = 'flext-start';
style.flexDirection = 'row'; if(this.labelAlign == 'center') style.justifyContent = 'center';
} else { if(this.labelAlign == 'right') style.justifyContent = 'flext-end';
style.flexDirection = 'column'; return style;
} },
// uni不支持在computed中写style.justifyContent = 'center'的形式,故用此方法
return style; justifyContent() {
} if(this.labelAlign == 'left') return 'flex-start';
}, if(this.labelAlign == 'center') return 'center';
methods: { if(this.labelAlign == 'right') return 'flex-end';
onInput(event) { },
this.$emit('input', event.target.value); // 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,给用户可以传入字符串数值
}, inputMaxlength() {
onFocus(event) { return Number(this.maxlength)
this.focused = true; },
this.$emit('focus', event); // label的位置
}, fieldInnerStyle() {
onBlur(event) { let style = {};
this.focused = false; if(this.labelPostion == 'left') {
this.$emit('blur', event); style.flexDirection = 'row';
}, } else {
onConfirm(e) { style.flexDirection = 'column';
this.$emit('confirm', e.detail.value); }
},
onClear(event) { return style;
this.$emit('input', ''); }
}, },
rightIconClick() { methods: {
this.$emit('right-icon-click'); onInput(event) {
this.$emit('click'); this.$emit('input', event.target.value);
}, },
fieldClick() { onFocus(event) {
this.$emit('click'); this.focused = true;
} this.$emit('focus', event);
} },
}; onBlur(event) {
</script> this.focused = false;
this.$emit('blur', event);
<style lang="scss" scoped> },
.u-field { onConfirm(e) {
font-size: 28rpx; this.$emit('confirm', e.detail.value);
padding: 20rpx 28rpx; },
text-align: left; onClear(event) {
position: relative; this.$emit('input', '');
color: $u-main-color; },
} rightIconClick() {
this.$emit('right-icon-click');
.u-field-inner { this.$emit('click');
display: flex; },
align-items: center; fieldClick() {
} this.$emit('click');
}
.u-textarea-inner { }
align-items: flex-start; };
} </script>
.u-textarea-class { <style lang="scss" scoped>
min-height: 96rpx; .u-field {
} font-size: 28rpx;
padding: 20rpx 28rpx;
.fild-body { text-align: left;
display: flex; position: relative;
flex: 1; color: $u-main-color;
align-items: center; }
}
.u-field-inner {
.u-arror-right { display: flex;
margin-left: 8rpx; align-items: center;
} }
.u-label-text { .u-textarea-inner {
display: inline-block; align-items: flex-start;
} }
.u-label-left-gap { .u-textarea-class {
margin-left: 6rpx; min-height: 96rpx;
} }
.u-label-postion-top { .fild-body {
flex-direction: column; display: flex;
align-items: flex-start; flex: 1;
} align-items: center;
}
.u-label {
width: 130rpx; .u-arror-right {
flex: 1 1 130rpx; margin-left: 8rpx;
text-align: left; }
position: relative;
display: flex; .u-label-text {
align-items: center; display: inline-block;
} }
.u-required::before { .u-label-left-gap {
content: '*'; margin-left: 6rpx;
position: absolute; }
left: -16rpx;
font-size: 14px; .u-label-postion-top {
color: $u-type-error; flex-direction: column;
height: 9px; align-items: flex-start;
line-height: 1; }
}
.u-label {
.u-input-class { width: 130rpx;
position: relative; flex: 1 1 130rpx;
overflow: hidden; text-align: left;
font-size: 28rpx; position: relative;
height: 48rpx; display: flex;
flex: 1; align-items: center;
width: auto; }
}
.u-required::before {
.u-clear-icon { content: '*';
display: flex; position: absolute;
align-items: center; left: -16rpx;
} font-size: 14px;
color: $u-type-error;
.u-field-border:after { height: 9px;
left: 32rpx!important; line-height: 1;
position: absolute; }
box-sizing: border-box;
content: ' '; .u-input-class {
pointer-events: none; position: relative;
right: 0; overflow: hidden;
top: 0; font-size: 28rpx;
border-bottom: 1px solid $u-border-color; height: 48rpx;
-webkit-transform: scaleY(0.5); flex: 1;
transform: scaleY(0.5); width: auto;
} }
.u-error-message { .u-clear-icon {
color: $u-type-error; display: flex;
font-size: 26rpx; align-items: center;
text-align: left; }
}
.u-field-border:after {
.placeholder-style { left: 32rpx!important;
color: rgb(150, 151, 153); position: absolute;
} box-sizing: border-box;
content: ' ';
pointer-events: none;
right: 0;
top: 0;
border-bottom: 1px solid $u-border-color;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
.u-error-message {
color: $u-type-error;
font-size: 26rpx;
text-align: left;
}
.placeholder-style {
color: rgb(150, 151, 153);
}
</style> </style>
<template> <template>
<view <view class="u-grid-item" :class="[showBorder ? 'u-border-right u-border-bottom' : '']" :hover-class="hoverClass ? 'u-grid-item-hover' : ''"
class="u-grid-item" :hover-stay-time="200" @tap="click" :style="{
:class="[showBorder ? 'u-border-right u-border-bottom' : '']" background: bgColor,
:hover-class="hoverClass ? 'u-grid-item-hover' : ''" width: width + 'px'
:hover-stay-time="200" }">
@tap="click" <view class="u-grid-item-box">
:style="{ <slot />
background: bgColor, </view>
width: width + 'px' </view>
}" </template>
>
<view class="u-grid-item-box"> <script>
<slot /> /**
</view> * alertTips 提示
</view> * @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。搭配<u-grid>使用
</template> * @tutorial https://www.uviewui.com/components/grid.html
* @property {String} bg-color 宫格的背景颜色(默认#ffffff)
<script> * @property {String Number} index 点击宫格时,返回的值
export default { * @event {Function} click 点击宫格触发
props: { * @example <u-grid-item></u-grid-item>
// 背景颜色 */
bgColor: { export default {
type: String, name: "u-grid-item",
default: '#ffffff' props: {
}, // 背景颜色
// 点击时返回的index bgColor: {
index: { type: String,
type: [Number, String], default: '#ffffff'
default: '' },
}, // 点击时返回的index
}, index: {
// 父组件通过provide传递过来的整个this type: [Number, String],
inject: ['uGrid'], default: ''
data() { },
return { },
hoverClass: '', // 按下去的时候,是否显示背景灰色 // 父组件通过provide传递过来的整个this
}; inject: ['uGrid'],
}, data() {
created() { return {
this.hoverClass = this.uGrid.hoverClass; hoverClass: '', // 按下去的时候,是否显示背景灰色
}, };
computed: { },
// 小于2,显示2列;大于12,显示12列 created() {
colNum() { this.hoverClass = this.uGrid.hoverClass;
return this.col < 2 ? 2 : this.col > 12 ? 12 : this.col; },
}, computed: {
// 每个grid-item的宽度 // 小于2,显示2列;大于12,显示12列
width() { colNum() {
return this.uGrid.width / this.uGrid.col; return this.col < 2 ? 2 : this.col > 12 ? 12 : this.col;
}, },
// 是否显示边框 // 每个grid-item的宽度
// 为了迎合演示的需要,从created生命周期移到此,以为演示中可能需要动态修改有无边框 width() {
showBorder() { return this.uGrid.width / this.uGrid.col;
return this.uGrid.border; },
} // 是否显示边框
}, // 为了迎合演示的需要,从created生命周期移到此,以为演示中可能需要动态修改有无边框
methods: { showBorder() {
click() { return this.uGrid.border;
this.$emit('click', this.index); }
this.uGrid.click(this.index); },
} methods: {
}, click() {
}; this.$emit('click', this.index);
</script> this.uGrid.click(this.index);
}
<style scoped lang="scss"> },
};
.u-grid-item { </script>
box-sizing: border-box;
background: #fff; <style scoped lang="scss">
display: flex; .u-grid-item {
align-items: center; box-sizing: border-box;
justify-content: center; background: #fff;
position: relative; display: flex;
flex-direction: column; align-items: center;
position: relative; justify-content: center;
} position: relative;
flex-direction: column;
.u-grid-item-hover { position: relative;
background: #f7f7f7 !important; }
}
.u-grid-item-hover {
.u-grid-marker-box { background: #f7f7f7 !important;
position: absolute; }
display: inline-block;
line-height: 0; .u-grid-marker-box {
} position: absolute;
display: inline-block;
.u-grid-marker-wrap { line-height: 0;
position: absolute; }
}
.u-grid-marker-wrap {
.u-grid-item-box { position: absolute;
padding: 30rpx 0; }
display: flex;
align-items: center; .u-grid-item-box {
justify-content: center; padding: 30rpx 0;
flex-direction: column; display: flex;
} align-items: center;
justify-content: center;
flex-direction: column;
}
</style> </style>
<template> <template>
<text class="uicon" :class="customClass" :style="{ <text class="uicon" :class="customClass" :style="{
color: color, color: color,
fontSize: size + 'rpx', fontSize: size + 'rpx',
fontWeight: bold ? 'bold' : 'normal', fontWeight: bold ? 'bold' : 'normal',
}" @tap="click" :hover-class="hoverClass" @touchstart="touchstart"> }"
@tap="click" :hover-class="hoverClass" @touchstart="touchstart">
</text>
</template> </text>
</template>
<script>
export default { <script>
props: { /**
// 图标类名 * alertTips 提示
name: { * @description 基于字体的图标集,包含了大多数常见场景的图标。
type: String, * @tutorial https://www.uviewui.com/components/icon.html
default: '' * @property {String} name 图标名称,见示例图标集
}, * @property {String} color 图标颜色(默认inherit)
// 图标颜色 * @property {String Number} size 图标字体大小,单位rpx(默认32)
color: { * @property {String} index 一个用于区分多个图标的值,点击图标时通过click事件传出
type: String, * @property {String} hover-class 图标按下去的样式类,用法同uni的view组件的hover-class参数,详情见官网
default: 'inherit' * @event {Function} click 点击图标时触发
}, * @example <u-icon name="photo" color="#2979ff" size="28"></u-icon>
// 字体大小,单位rpx */
size: { export default {
type: [Number, String], name: "u-icon",
default: 'inherit' props: {
}, // 图标类名
// 是否显示粗体 name: {
bold: { type: String,
type: Boolean, default: ''
default: false },
}, // 图标颜色
// 点击图标的时候传递事件出去的index(用于区分点击了哪一个) color: {
index: { type: String,
type: [Number, String], default: 'inherit'
default: '' },
}, // 字体大小,单位rpx
// 触摸图标时的类名 size: {
hoverClass: { type: [Number, String],
type: String, default: 'inherit'
default: '' },
}, // 是否显示粗体
// 自定义扩展前缀,方便用户扩展自己的图标库 bold: {
customPrefix: { type: Boolean,
type: String, default: false
default: 'uicon' },
} // 点击图标的时候传递事件出去的index(用于区分点击了哪一个)
}, index: {
data() { type: [Number, String],
return { default: ''
},
} // 触摸图标时的类名
}, hoverClass: {
computed: { type: String,
customClass() { default: ''
let classes = []; },
classes.push(this.customPrefix + '-' + this.name); // 自定义扩展前缀,方便用户扩展自己的图标库
// uView的自定义图标类名为u-iconfont customPrefix: {
if(this.customPrefix == 'uicon') classes.push('u-iconfont'); type: String,
else classes.push(this.customPrefix); default: 'uicon'
return classes; }
} },
}, data() {
methods: { return {
click() {
this.$emit('click', this.index); }
}, },
touchstart() { computed: {
this.$emit('touchstart', this.index); customClass() {
} let classes = [];
} classes.push(this.customPrefix + '-' + this.name);
} // uView的自定义图标类名为u-iconfont
</script> if (this.customPrefix == 'uicon') classes.push('u-iconfont');
else classes.push(this.customPrefix);
<style scoped lang="scss"> return classes;
@import '../../../uview/iconfont.css'; }
},
.uicon { methods: {
display: inline-flex; click() {
align-items: center; this.$emit('click', this.index);
} },
touchstart() {
this.$emit('touchstart', this.index);
}
}
}
</script>
<style scoped lang="scss">
@import '../../../uview/iconfont.css';
.uicon {
display: inline-flex;
align-items: center;
}
</style> </style>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment