| New file |
| | |
| | | ## 1.0.2(2023-07-02) |
| | | uv-action-sheet 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/actionSheet.html |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-action-sheet 底部操作菜单 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 标题,有值则显示,同时会显示关闭按钮 |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 选项上方的描述信息 |
| | | description: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 数据 |
| | | actions: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | // 取消按钮的文字,不为空时显示按钮 |
| | | cancelText: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 点击某个菜单项时是否关闭弹窗 |
| | | closeOnClickAction: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 处理底部安全区(默认true) |
| | | safeAreaInsetBottom: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 小程序的打开方式 |
| | | openType: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 点击遮罩是否允许关闭 (默认true) |
| | | closeOnClickOverlay: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 圆角值 |
| | | round: { |
| | | type: [Boolean, String, Number], |
| | | default: 0 |
| | | }, |
| | | ...uni.$uv?.props?.actionSheet |
| | | } |
| | | } |
| New file |
| | |
| | | |
| | | <template> |
| | | <uv-popup |
| | | ref="popup" |
| | | mode="bottom" |
| | | :safeAreaInsetBottom="safeAreaInsetBottom" |
| | | :round="round" |
| | | :close-on-click-overlay="closeOnClickOverlay" |
| | | @change="popupChange" |
| | | > |
| | | <view class="uv-action-sheet"> |
| | | <view |
| | | class="uv-action-sheet__header" |
| | | v-if="title" |
| | | > |
| | | <text class="uv-action-sheet__header__title uv-line-1">{{title}}</text> |
| | | <view |
| | | class="uv-action-sheet__header__icon-wrap" |
| | | @tap.stop="cancel" |
| | | > |
| | | <uv-icon |
| | | name="close" |
| | | size="17" |
| | | color="#c8c9cc" |
| | | bold |
| | | ></uv-icon> |
| | | </view> |
| | | </view> |
| | | <text |
| | | class="uv-action-sheet__description" |
| | | :style="[{ |
| | | marginTop: `${title && description ? 0 : '18px'}` |
| | | }]" |
| | | v-if="description" |
| | | >{{description}}</text> |
| | | <slot> |
| | | <uv-line v-if="description"></uv-line> |
| | | <view class="uv-action-sheet__item-wrap"> |
| | | <view v-for="(item, index) in actions" :key="index"> |
| | | <!-- #ifdef MP --> |
| | | <button |
| | | class="uv-reset-button" |
| | | :openType="item.openType" |
| | | @getuserinfo="onGetUserInfo" |
| | | @contact="onContact" |
| | | @getphonenumber="onGetPhoneNumber" |
| | | @error="onError" |
| | | @launchapp="onLaunchApp" |
| | | @opensetting="onOpenSetting" |
| | | :lang="lang" |
| | | :session-from="sessionFrom" |
| | | :send-message-title="sendMessageTitle" |
| | | :send-message-path="sendMessagePath" |
| | | :send-message-img="sendMessageImg" |
| | | :show-message-card="showMessageCard" |
| | | :app-parameter="appParameter" |
| | | @tap="selectHandler(index)" |
| | | :hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''" |
| | | > |
| | | <!-- #endif --> |
| | | <view |
| | | class="uv-action-sheet__item-wrap__item" |
| | | @tap.stop="selectHandler(index)" |
| | | :hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''" |
| | | :hover-stay-time="150" |
| | | > |
| | | <template v-if="!item.loading"> |
| | | <text |
| | | class="uv-action-sheet__item-wrap__item__name" |
| | | :style="[itemStyle(index)]" |
| | | >{{ item.name }}</text> |
| | | <text |
| | | v-if="item.subname" |
| | | class="uv-action-sheet__item-wrap__item__subname" |
| | | >{{ item.subname }}</text> |
| | | </template> |
| | | <uv-loading-icon |
| | | v-else |
| | | custom-class="van-action-sheet__loading" |
| | | size="18" |
| | | mode="circle" |
| | | /> |
| | | </view> |
| | | <!-- #ifdef MP --> |
| | | </button> |
| | | <!-- #endif --> |
| | | <uv-line v-if="index !== actions.length - 1"></uv-line> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | <uv-gap |
| | | bgColor="#eaeaec" |
| | | height="6" |
| | | v-if="cancelText" |
| | | ></uv-gap> |
| | | <view hover-class="uv-action-sheet--hover"> |
| | | <text |
| | | @touchmove.stop.prevent |
| | | :hover-stay-time="150" |
| | | v-if="cancelText" |
| | | class="uv-action-sheet__cancel-text" |
| | | @tap="cancel" |
| | | >{{cancelText}}</text> |
| | | </view> |
| | | </view> |
| | | </uv-popup> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js' |
| | | import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * ActionSheet 操作菜单 |
| | | * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。 |
| | | * @tutorial https://www.uvui.cn/components/actionSheet.html |
| | | * @property {Boolean} show 操作菜单是否展示 (默认 false ) |
| | | * @property {String} title 操作菜单标题 |
| | | * @property {String} description 选项上方的描述信息 |
| | | * @property {Array<Object>} actions 按钮的文字数组,见官方文档示例 |
| | | * @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮 |
| | | * @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 (默认 true ) |
| | | * @property {Boolean} safeAreaInsetBottom 处理底部安全区 (默认 true ) |
| | | * @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error ) |
| | | * @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true ) |
| | | * @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文 |
| | | * @property {String} sessionFrom 会话来源,openType="contact"时有效 |
| | | * @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效 |
| | | * @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效 |
| | | * @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效 |
| | | * @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false ) |
| | | * @property {String} appParameter 打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效 |
| | | * |
| | | * @event {Function} select 点击ActionSheet列表项时触发 |
| | | * @event {Function} close 点击取消按钮时触发 |
| | | * @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效 |
| | | * @event {Function} contact 客服消息回调,openType="contact"时有效 |
| | | * @event {Function} getphonenumber 获取用户手机号回调,openType="getPhoneNumber"时有效 |
| | | * @event {Function} error 当使用开放能力时,发生错误的回调,openType="error"时有效 |
| | | * @event {Function} launchapp 打开 APP 成功的回调,openType="launchApp"时有效 |
| | | * @event {Function} opensetting 在打开授权设置页后回调,openType="openSetting"时有效 |
| | | * @example <uv-action-sheet ref="actionSheet" :actions="list" :title="title" ></uv-action-sheet> |
| | | */ |
| | | export default { |
| | | name: "uv-action-sheet", |
| | | mixins: [openType, button, mpMixin , mixin, props], |
| | | emits: ['close', 'select'], |
| | | computed: { |
| | | // 操作项目的样式 |
| | | itemStyle() { |
| | | return (index) => { |
| | | let style = {}; |
| | | if (this.actions[index].color) style.color = this.actions[index].color |
| | | if (this.actions[index].fontSize) style.fontSize = this.$uv.addUnit(this.actions[index].fontSize) |
| | | // 选项被禁用的样式 |
| | | if (this.actions[index].disabled) style.color = '#c0c4cc' |
| | | return style; |
| | | } |
| | | }, |
| | | }, |
| | | methods: { |
| | | open() { |
| | | this.$refs.popup.open(); |
| | | }, |
| | | close() { |
| | | this.$refs.popup.close(); |
| | | }, |
| | | popupChange(e) { |
| | | if(!e.show) this.$emit('close'); |
| | | }, |
| | | // 点击取消按钮 |
| | | cancel() { |
| | | this.close(); |
| | | }, |
| | | selectHandler(index) { |
| | | const item = this.actions[index] |
| | | if (item && !item.disabled && !item.loading) { |
| | | this.$emit('select', item) |
| | | if (this.closeOnClickAction) { |
| | | this.close(); |
| | | } |
| | | } |
| | | }, |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-lines: 1; |
| | | $show-reset-button: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-action-sheet-reset-button-width:100% !default; |
| | | $uv-action-sheet-title-font-size: 16px !default; |
| | | $uv-action-sheet-title-padding: 12px 30px !default; |
| | | $uv-action-sheet-title-color: $uv-main-color !default; |
| | | $uv-action-sheet-header-icon-wrap-right:15px !default; |
| | | $uv-action-sheet-header-icon-wrap-top:15px !default; |
| | | $uv-action-sheet-description-font-size:13px !default; |
| | | $uv-action-sheet-description-color:14px !default; |
| | | $uv-action-sheet-description-margin: 18px 15px !default; |
| | | $uv-action-sheet-item-wrap-item-padding:15px !default; |
| | | $uv-action-sheet-item-wrap-name-font-size:16px !default; |
| | | $uv-action-sheet-item-wrap-subname-font-size:13px !default; |
| | | $uv-action-sheet-item-wrap-subname-color: #c0c4cc !default; |
| | | $uv-action-sheet-item-wrap-subname-margin-top:10px !default; |
| | | $uv-action-sheet-cancel-text-font-size:16px !default; |
| | | $uv-action-sheet-cancel-text-color:$uv-content-color !default; |
| | | $uv-action-sheet-cancel-text-font-size:15px !default; |
| | | $uv-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default; |
| | | |
| | | .uv-reset-button { |
| | | width: $uv-action-sheet-reset-button-width; |
| | | } |
| | | |
| | | .uv-action-sheet { |
| | | text-align: center; |
| | | &__header { |
| | | position: relative; |
| | | padding: $uv-action-sheet-title-padding; |
| | | &__title { |
| | | font-size: $uv-action-sheet-title-font-size; |
| | | color: $uv-action-sheet-title-color; |
| | | font-weight: bold; |
| | | text-align: center; |
| | | } |
| | | |
| | | &__icon-wrap { |
| | | position: absolute; |
| | | right: $uv-action-sheet-header-icon-wrap-right; |
| | | top: $uv-action-sheet-header-icon-wrap-top; |
| | | } |
| | | } |
| | | |
| | | &__description { |
| | | font-size: $uv-action-sheet-description-font-size; |
| | | color: $uv-tips-color; |
| | | margin: $uv-action-sheet-description-margin; |
| | | text-align: center; |
| | | } |
| | | |
| | | &__item-wrap { |
| | | |
| | | &__item { |
| | | padding: $uv-action-sheet-item-wrap-item-padding; |
| | | @include flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-direction: column; |
| | | |
| | | &__name { |
| | | font-size: $uv-action-sheet-item-wrap-name-font-size; |
| | | color: $uv-main-color; |
| | | text-align: center; |
| | | } |
| | | |
| | | &__subname { |
| | | font-size: $uv-action-sheet-item-wrap-subname-font-size; |
| | | color: $uv-action-sheet-item-wrap-subname-color; |
| | | margin-top: $uv-action-sheet-item-wrap-subname-margin-top; |
| | | text-align: center; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &__cancel-text { |
| | | font-size: $uv-action-sheet-cancel-text-font-size; |
| | | color: $uv-action-sheet-cancel-text-color; |
| | | text-align: center; |
| | | padding: $uv-action-sheet-cancel-text-font-size; |
| | | } |
| | | |
| | | &--hover { |
| | | background-color: $uv-action-sheet-cancel-text-hover-background-color; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-action-sheet", |
| | | "displayName": "uv-action-sheet 底部操作菜单 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.2", |
| | | "description": "该组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。", |
| | | "keywords": [ |
| | | "action-sheet", |
| | | "uvui", |
| | | "uv-ui", |
| | | "操作菜单", |
| | | "菜单选择" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-popup", |
| | | "uv-icon", |
| | | "uv-line", |
| | | "uv-loading-icon", |
| | | "uv-gap" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## ActionSheet 操作菜单 |
| | | |
| | | > **组件名:uv-action-sheet** |
| | | |
| | | 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。 |
| | | |
| | | 本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/actionSheet.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.4(2023-12-06) |
| | | 1. 阻止事件冒泡处理 |
| | | ## 1.0.3(2023-10-23) |
| | | 1. 修复报错的BUG |
| | | ## 1.0.2(2023-10-23) |
| | | 1. 修复设置singleSize、multipleSize、space等值带单位,存在不显示的BUG |
| | | ## 1.0.1(2023-09-13) |
| | | 1. 添加依赖 |
| | | ## 1.0.0(2023-08-30) |
| | | 1. 新增uv-album相册组件 |
| New file |
| | |
| | | <template> |
| | | <view class="uv-album"> |
| | | <view |
| | | class="uv-album__row" |
| | | ref="uv-album__row" |
| | | v-for="(arr, index) in showUrls" |
| | | :forComputedUse="albumWidth" |
| | | :key="index" |
| | | > |
| | | <view |
| | | class="uv-album__row__wrapper" |
| | | v-for="(item, index1) in arr" |
| | | :key="index1" |
| | | :style="[imageStyle(index + 1, index1 + 1)]" |
| | | @tap.stop="previewFullImage ? onPreviewTap(getSrc(item)) : ''" |
| | | > |
| | | <image |
| | | :src="getSrc(item)" |
| | | :mode=" |
| | | urls.length === 1 |
| | | ? imageHeight > 0 |
| | | ? singleMode |
| | | : 'widthFix' |
| | | : multipleMode |
| | | " |
| | | :style="[ |
| | | { |
| | | width: imageWidth, |
| | | height: imageHeight |
| | | } |
| | | ]" |
| | | ></image> |
| | | <view |
| | | v-if=" |
| | | showMore && |
| | | urls.length > rowCount * showUrls.length && |
| | | index === showUrls.length - 1 && |
| | | index1 === showUrls[showUrls.length - 1].length - 1 |
| | | " |
| | | class="uv-album__row__wrapper__text" |
| | | > |
| | | <uv-text |
| | | :text="`+${urls.length - maxCount}`" |
| | | color="#fff" |
| | | :size="$uv.getPx(multipleSize) * 0.3" |
| | | align="center" |
| | | customStyle="justify-content: center" |
| | | ></uv-text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | // #ifdef APP-NVUE |
| | | // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度 |
| | | const dom = uni.requireNativePlugin('dom') |
| | | // #endif |
| | | |
| | | /** |
| | | * Album 相册 |
| | | * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码 |
| | | * @tutorial https://www.uvui.cn/components/album.html |
| | | * @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式 |
| | | * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址 |
| | | * @property {String | Number} singleSize 单图时,图片长边的长度 (默认 180 ) |
| | | * @property {String | Number} multipleSize 多图时,图片边长 (默认 70 ) |
| | | * @property {String | Number} space 多图时,图片水平和垂直之间的间隔 (默认 6 ) |
| | | * @property {String} singleMode 单图时,图片缩放裁剪的模式 (默认 'scaleToFill' ) |
| | | * @property {String} multipleMode 多图时,图片缩放裁剪的模式 (默认 'aspectFill' ) |
| | | * @property {String | Number} maxCount 取消按钮的提示文字 (默认 9 ) |
| | | * @property {Boolean} previewFullImage 是否可以预览图片 (默认 true ) |
| | | * @property {String | Number} rowCount 每行展示图片数量,如设置,singleSize和multipleSize将会无效 (默认 3 ) |
| | | * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true ) |
| | | * |
| | | * @event {Function} albumWidth 某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送 (回调参数 width ) |
| | | * @example <uv-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></uv-album> |
| | | */ |
| | | export default { |
| | | name: 'uv-album', |
| | | mixins: [mpMixin, mixin], |
| | | emits: ['albumWidth'], |
| | | props: { |
| | | // 图片地址,Array<String>|Array<Object>形式 |
| | | urls: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | // 指定从数组的对象元素中读取哪个属性作为图片地址 |
| | | keyName: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 单图时,图片长边的长度 |
| | | singleSize: { |
| | | type: [String, Number], |
| | | default: 180 |
| | | }, |
| | | // 多图时,图片边长 |
| | | multipleSize: { |
| | | type: [String, Number], |
| | | default: 70 |
| | | }, |
| | | // 多图时,图片水平和垂直之间的间隔 |
| | | space: { |
| | | type: [String, Number], |
| | | default: 6 |
| | | }, |
| | | // 单图时,图片缩放裁剪的模式 |
| | | singleMode: { |
| | | type: String, |
| | | default: 'scaleToFill' |
| | | }, |
| | | // 多图时,图片缩放裁剪的模式 |
| | | multipleMode: { |
| | | type: String, |
| | | default: 'aspectFill' |
| | | }, |
| | | // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量 |
| | | maxCount: { |
| | | type: [String, Number], |
| | | default: 9 |
| | | }, |
| | | // 是否可以预览图片 |
| | | previewFullImage: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 每行展示图片数量,如设置,singleSize和multipleSize将会无效 |
| | | rowCount: { |
| | | type: [String, Number], |
| | | default: 3 |
| | | }, |
| | | // 超出maxCount时是否显示查看更多的提示 |
| | | showMore: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | ...uni.$uv?.props?.album |
| | | }, |
| | | data() { |
| | | return { |
| | | // 单图的宽度 |
| | | singleWidth: 0, |
| | | // 单图的高度 |
| | | singleHeight: 0, |
| | | // 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比 |
| | | singlePercent: 0.6 |
| | | } |
| | | }, |
| | | watch: { |
| | | urls: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (newVal.length === 1) { |
| | | this.getImageRect() |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | imageStyle() { |
| | | return (index1, index2) => { |
| | | const { space, rowCount, multipleSize, urls } = this; |
| | | const rowLen = this.showUrls.length; |
| | | const allLen = this.urls.length; |
| | | const style = { |
| | | marginRight: this.$uv.addUnit(space), |
| | | marginBottom: this.$uv.addUnit(space) |
| | | } |
| | | // 如果为最后一行,则每个图片都无需下边框 |
| | | if (index1 === rowLen) style.marginBottom = 0 |
| | | // 每行的最右边一张和总长度的最后一张无需右边框 |
| | | if ( |
| | | index2 === rowCount || |
| | | (index1 === rowLen && |
| | | index2 === this.showUrls[index1 - 1].length) |
| | | ) |
| | | style.marginRight = 0 |
| | | return style |
| | | } |
| | | }, |
| | | // 将数组划分为二维数组 |
| | | showUrls() { |
| | | const arr = [] |
| | | this.urls.map((item, index) => { |
| | | // 限制最大展示数量 |
| | | if (index + 1 <= this.maxCount) { |
| | | // 计算该元素为第几个素组内 |
| | | const itemIndex = Math.floor(index / this.rowCount) |
| | | // 判断对应的索引是否存在 |
| | | if (!arr[itemIndex]) { |
| | | arr[itemIndex] = [] |
| | | } |
| | | arr[itemIndex].push(item) |
| | | } |
| | | }) |
| | | return arr |
| | | }, |
| | | imageWidth() { |
| | | return this.$uv.addUnit( |
| | | this.urls.length === 1 ? this.singleWidth : this.multipleSize |
| | | ) |
| | | }, |
| | | imageHeight() { |
| | | return this.$uv.addUnit( |
| | | this.urls.length === 1 ? this.singleHeight : this.multipleSize |
| | | ) |
| | | }, |
| | | // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度 |
| | | // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送 |
| | | albumWidth() { |
| | | let width = 0 |
| | | if (this.urls.length === 1) { |
| | | width = this.singleWidth |
| | | } else { |
| | | width = |
| | | this.showUrls[0].length * this.$uv.getPx(this.multipleSize) + |
| | | this.$uv.getPx(this.space) * (this.showUrls[0].length - 1) |
| | | } |
| | | this.$emit('albumWidth', width) |
| | | return width |
| | | } |
| | | }, |
| | | methods: { |
| | | // 预览图片 |
| | | onPreviewTap(url) { |
| | | const urls = this.urls.map((item) => { |
| | | return this.getSrc(item) |
| | | }) |
| | | uni.previewImage({ |
| | | current: url, |
| | | urls |
| | | }) |
| | | }, |
| | | // 获取图片的路径 |
| | | getSrc(item) { |
| | | return this.$uv.test.object(item) ? |
| | | (this.keyName && item[this.keyName]) || item.src : |
| | | item |
| | | }, |
| | | // 单图时,获取图片的尺寸 |
| | | // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸 |
| | | // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent) |
| | | getImageRect() { |
| | | const src = this.getSrc(this.urls[0]) |
| | | uni.getImageInfo({ |
| | | src, |
| | | success: (res) => { |
| | | // 判断图片横向还是竖向展示方式 |
| | | const isHorizotal = res.width >= res.height |
| | | this.singleWidth = isHorizotal ? |
| | | this.singleSize : |
| | | (res.width / res.height) * this.$uv.getPx(this.singleSize) |
| | | this.singleHeight = !isHorizotal ? |
| | | this.singleSize : |
| | | (res.height / res.width) * this.singleWidth |
| | | }, |
| | | fail: () => { |
| | | this.getComponentWidth() |
| | | } |
| | | }) |
| | | }, |
| | | // 获取组件的宽度 |
| | | async getComponentWidth() { |
| | | // 延时一定时间,以获取dom尺寸 |
| | | await this.$uv.sleep(30) |
| | | // #ifndef APP-NVUE |
| | | this.$uGetRect('.uv-album__row').then((size) => { |
| | | this.singleWidth = size.width * this.singlePercent |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifdef APP-NVUE |
| | | // 这里ref="uv-album__row"所在的标签为通过for循环出来,导致this.$refs['uv-album__row']是一个数组 |
| | | const ref = this.$refs['uv-album__row'][0] |
| | | ref && |
| | | dom.getComponentRect(ref, (res) => { |
| | | this.singleWidth = res.size.width * this.singlePercent |
| | | }) |
| | | // #endif |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | .uv-album { |
| | | @include flex(column); |
| | | &__row { |
| | | @include flex(row); |
| | | flex-wrap: wrap; |
| | | &__wrapper { |
| | | position: relative; |
| | | &__text { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: rgba(0, 0, 0, 0.3); |
| | | @include flex(row); |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-album", |
| | | "displayName": "uv-album 相册 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.4", |
| | | "description": "本组件提供一个类似相册的功能,让开发者开发起来更加得心应手,功能齐全,灵活配置可以,开箱即用。减少重复的模板代码", |
| | | "keywords": [ |
| | | "album", |
| | | "uv-ui", |
| | | "uvui", |
| | | "相册", |
| | | "图片" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-text" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | # Album 相册 |
| | | |
| | | > **组件名:uv-album** |
| | | |
| | | 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。 |
| | | |
| | | 功能齐全,灵活配置可以,开箱即用。减少重复的模板代码。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/album.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.2(2023-06-01) |
| | | 1. 修复点击触发两次实践的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-alert 警告提示组件 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 显示文字 |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 主题,success/warning/info/error |
| | | type: { |
| | | type: String, |
| | | default: 'warning' |
| | | }, |
| | | // 辅助性文字 |
| | | description: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否可关闭 |
| | | closable: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示图标 |
| | | showIcon: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 浅或深色调,light-浅色,dark-深色 |
| | | effect: { |
| | | type: String, |
| | | default: 'light' |
| | | }, |
| | | // 文字是否居中 |
| | | center: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 字体大小 |
| | | fontSize: { |
| | | type: [String, Number], |
| | | default: 14 |
| | | }, |
| | | ...uni.$uv?.props?.alert |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <uv-transition |
| | | mode="fade" |
| | | :show="show" |
| | | > |
| | | <view |
| | | class="uv-alert" |
| | | :class="[`uv-alert--${type}--${effect}`]" |
| | | @tap.stop="clickHandler" |
| | | :style="[$uv.addStyle(customStyle)]" |
| | | > |
| | | <view |
| | | class="uv-alert__icon" |
| | | v-if="showIcon" |
| | | > |
| | | <uv-icon |
| | | :name="iconName" |
| | | size="18" |
| | | :color="iconColor" |
| | | ></uv-icon> |
| | | </view> |
| | | <view |
| | | class="uv-alert__content" |
| | | :style="[{ |
| | | paddingRight: closable ? '20px' : 0 |
| | | }]" |
| | | > |
| | | <text |
| | | class="uv-alert__content__title" |
| | | v-if="title" |
| | | :style="[{ |
| | | fontSize: $uv.addUnit(fontSize), |
| | | textAlign: center ? 'center' : 'left' |
| | | }]" |
| | | :class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]" |
| | | >{{ title }}</text> |
| | | <text |
| | | class="uv-alert__content__desc" |
| | | v-if="description" |
| | | :style="[{ |
| | | fontSize: $uv.addUnit(fontSize), |
| | | textAlign: center ? 'center' : 'left' |
| | | }]" |
| | | :class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]" |
| | | >{{ description }}</text> |
| | | </view> |
| | | <view |
| | | class="uv-alert__close" |
| | | v-if="closable" |
| | | @tap.stop="closeHandler" |
| | | > |
| | | <uv-icon |
| | | name="close" |
| | | :color="iconColor" |
| | | size="15" |
| | | ></uv-icon> |
| | | </view> |
| | | </view> |
| | | </uv-transition> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * Alert 警告提示 |
| | | * @description 警告提示,展现需要关注的信息。 |
| | | * @tutorial https://www.uvui.cn/components/alertTips.html |
| | | * |
| | | * @property {String} title 显示的文字 |
| | | * @property {String} type 使用预设的颜色 (默认 'warning' ) |
| | | * @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选 |
| | | * @property {Boolean} closable 关闭按钮(默认为叉号icon图标) (默认 false ) |
| | | * @property {Boolean} showIcon 是否显示左边的辅助图标 ( 默认 false ) |
| | | * @property {String} effect 多图时,图片缩放裁剪的模式 (默认 'light' ) |
| | | * @property {Boolean} center 文字是否居中 (默认 false ) |
| | | * @property {String | Number} fontSize 字体大小 (默认 14 ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * @event {Function} click 点击组件时触发 |
| | | * @example <uv-alert :title="title" type = "warning" :closable="closable" :description = "description"></uv-alert> |
| | | */ |
| | | export default { |
| | | name: 'uv-alert', |
| | | mixins: [mpMixin, mixin, props], |
| | | emits: ['click'], |
| | | data() { |
| | | return { |
| | | show: true |
| | | } |
| | | }, |
| | | computed: { |
| | | iconColor() { |
| | | return this.effect === 'light' ? this.type : '#fff' |
| | | }, |
| | | // 不同主题对应不同的图标 |
| | | iconName() { |
| | | switch (this.type) { |
| | | case 'success': |
| | | return 'checkmark-circle-fill'; |
| | | break; |
| | | case 'error': |
| | | return 'close-circle-fill'; |
| | | break; |
| | | case 'warning': |
| | | return 'error-circle-fill'; |
| | | break; |
| | | case 'info': |
| | | return 'info-circle-fill'; |
| | | break; |
| | | case 'primary': |
| | | return 'more-circle-fill'; |
| | | break; |
| | | default: |
| | | return 'error-circle-fill'; |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | // 点击内容 |
| | | clickHandler() { |
| | | this.$emit('click') |
| | | }, |
| | | // 点击关闭按钮 |
| | | closeHandler() { |
| | | this.show = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | .uv-alert { |
| | | position: relative; |
| | | background-color: $uv-primary; |
| | | padding: 8px 10px; |
| | | @include flex(row); |
| | | align-items: center; |
| | | border-top-left-radius: 4px; |
| | | border-top-right-radius: 4px; |
| | | border-bottom-left-radius: 4px; |
| | | border-bottom-right-radius: 4px; |
| | | |
| | | &--primary--dark { |
| | | background-color: $uv-primary; |
| | | } |
| | | |
| | | &--primary--light { |
| | | background-color: #ecf5ff; |
| | | } |
| | | |
| | | &--error--dark { |
| | | background-color: $uv-error; |
| | | } |
| | | |
| | | &--error--light { |
| | | background-color: #FEF0F0; |
| | | } |
| | | |
| | | &--success--dark { |
| | | background-color: $uv-success; |
| | | } |
| | | |
| | | &--success--light { |
| | | background-color: #f5fff0; |
| | | } |
| | | |
| | | &--warning--dark { |
| | | background-color: $uv-warning; |
| | | } |
| | | |
| | | &--warning--light { |
| | | background-color: #FDF6EC; |
| | | } |
| | | |
| | | &--info--dark { |
| | | background-color: $uv-info; |
| | | } |
| | | |
| | | &--info--light { |
| | | background-color: #f4f4f5; |
| | | } |
| | | |
| | | &__icon { |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | &__content { |
| | | @include flex(column); |
| | | flex: 1; |
| | | |
| | | &__title { |
| | | color: $uv-main-color; |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | | color: #fff; |
| | | margin-bottom: 2px; |
| | | } |
| | | |
| | | &__desc { |
| | | color: $uv-main-color; |
| | | font-size: 14px; |
| | | flex-wrap: wrap; |
| | | color: #fff; |
| | | } |
| | | } |
| | | |
| | | &__title--dark, |
| | | &__desc--dark { |
| | | color: #FFFFFF; |
| | | } |
| | | |
| | | &__text--primary--light, |
| | | &__text--primary--light { |
| | | color: $uv-primary; |
| | | } |
| | | |
| | | &__text--success--light, |
| | | &__text--success--light { |
| | | color: $uv-success; |
| | | } |
| | | |
| | | &__text--warning--light, |
| | | &__text--warning--light { |
| | | color: $uv-warning; |
| | | } |
| | | |
| | | &__text--error--light, |
| | | &__text--error--light { |
| | | color: $uv-error; |
| | | } |
| | | |
| | | &__text--info--light, |
| | | &__text--info--light { |
| | | color: $uv-info; |
| | | } |
| | | |
| | | &__close { |
| | | position: absolute; |
| | | top: 11px; |
| | | right: 10px; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-alert", |
| | | "displayName": "uv-alert 警告提示 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.2", |
| | | "description": "uv-alert 警告提示,展现需要关注的信息。灵活配置,功能齐全,兼容全端", |
| | | "keywords": [ |
| | | "alert", |
| | | "uvui", |
| | | "uv-ui", |
| | | "警告提示" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-transition", |
| | | "uv-icon" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Alert 警告提示 |
| | | |
| | | > **组件名:uv-alert** |
| | | |
| | | 警告提示,展现需要关注的信息。 |
| | | |
| | | 当某个页面需要向用户显示警告的信息时。 |
| | | |
| | | 非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/alert.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.5(2023-12-06) |
| | | 1. 优化 |
| | | ## 1.0.4(2023-12-06) |
| | | 1. 优化 |
| | | ## 1.0.3(2023-12-06) |
| | | 1. 阻止事件冒泡处理,单个头像模式 |
| | | ## 1.0.2(2023-12-06) |
| | | 1. 阻止事件冒泡处理 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-avatar 头像组件 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 头像图片组 |
| | | urls: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | // 最多展示的头像数量 |
| | | maxCount: { |
| | | type: [String, Number], |
| | | default: 5 |
| | | }, |
| | | // 头像形状 |
| | | shape: { |
| | | type: String, |
| | | default: 'circle' |
| | | }, |
| | | // 图片裁剪模式 |
| | | mode: { |
| | | type: String, |
| | | default: 'scaleToFill' |
| | | }, |
| | | // 超出maxCount时是否显示查看更多的提示 |
| | | showMore: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 头像大小 |
| | | size: { |
| | | type: [String, Number], |
| | | default: 40 |
| | | }, |
| | | // 指定从数组的对象元素中读取哪个属性作为图片地址 |
| | | keyName: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 头像之间的遮挡比例 |
| | | gap: { |
| | | type: [String, Number], |
| | | validator(value) { |
| | | return value >= 0 && value <= 1 |
| | | }, |
| | | default: 0.5 |
| | | }, |
| | | // 需额外显示的值 |
| | | extraValue: { |
| | | type: [Number, String], |
| | | default: 0 |
| | | }, |
| | | ...uni.$uv?.props?.avatarGroup |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-avatar-group"> |
| | | <view |
| | | class="uv-avatar-group__item" |
| | | v-for="(item, index) in showUrl" |
| | | :key="index" |
| | | :style="{ |
| | | marginLeft: index === 0 ? 0 : $uv.addUnit(-size * gap) |
| | | }" |
| | | > |
| | | <uv-avatar |
| | | :size="size" |
| | | :shape="shape" |
| | | :mode="mode" |
| | | :src="$uv.test.object(item) ? keyName && item[keyName] || item.url : item" |
| | | ></uv-avatar> |
| | | <view |
| | | class="uv-avatar-group__item__show-more" |
| | | v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)" |
| | | @tap="clickHandler" |
| | | > |
| | | <uv-text |
| | | color="#ffffff" |
| | | :size="size * 0.4" |
| | | :text="`+${extraValue || urls.length - showUrl.length}`" |
| | | align="center" |
| | | customStyle="justify-content: center" |
| | | ></uv-text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * AvatarGroup 头像组 |
| | | * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。 |
| | | * @tutorial https://www.uvui.cn/components/avatar.html |
| | | * |
| | | * @property {Array} urls 头像图片组 (默认 [] ) |
| | | * @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 ) |
| | | * @property {String} shape 头像形状( 'circle' (默认) | 'square' ) |
| | | * @property {String} mode 图片裁剪模式(默认 'scaleToFill' ) |
| | | * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true ) |
| | | * @property {String | Number} size 头像大小 (默认 40 ) |
| | | * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址 |
| | | * @property {String | Number} gap 头像之间的遮挡比例(0.4代表遮挡40%) (默认 0.5 ) |
| | | * @property {String | Number} extraValue 需额外显示的值 |
| | | * @event {Function} showMore 头像组更多点击 |
| | | * @example <uv-avatar-group:urls="urls" size="35" gap="0.4" ></uv-avatar-group:urls=> |
| | | */ |
| | | export default { |
| | | name: 'uv-avatar-group', |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | |
| | | } |
| | | }, |
| | | computed: { |
| | | showUrl() { |
| | | return this.urls.slice(0, this.maxCount) |
| | | } |
| | | }, |
| | | methods: { |
| | | clickHandler() { |
| | | this.$emit('showMore') |
| | | } |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | |
| | | .uv-avatar-group { |
| | | @include flex; |
| | | |
| | | &__item { |
| | | margin-left: -10px; |
| | | position: relative; |
| | | |
| | | &--no-indent { |
| | | // 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持 |
| | | margin-left: 0; |
| | | } |
| | | |
| | | &__show-more { |
| | | position: absolute; |
| | | top: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background-color: rgba(0, 0, 0, 0.3); |
| | | @include flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border-radius: 100px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | import { range } from '@/uni_modules/uv-ui-tools/libs/function/test.js' |
| | | export default { |
| | | props: { |
| | | // 头像图片路径(不能为相对路径) |
| | | src: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 头像形状,circle-圆形,square-方形 |
| | | shape: { |
| | | type: String, |
| | | default: 'circle' |
| | | }, |
| | | // 头像尺寸 |
| | | size: { |
| | | type: [String, Number], |
| | | default: 40 |
| | | }, |
| | | // 裁剪模式 |
| | | mode: { |
| | | type: String, |
| | | default: 'scaleToFill' |
| | | }, |
| | | // 显示的文字 |
| | | text: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 背景色 |
| | | bgColor: { |
| | | type: String, |
| | | default: '#c0c4cc' |
| | | }, |
| | | // 文字颜色 |
| | | color: { |
| | | type: String, |
| | | default: '#fff' |
| | | }, |
| | | // 文字大小 |
| | | fontSize: { |
| | | type: [String, Number], |
| | | default: 18 |
| | | }, |
| | | // 显示的图标 |
| | | icon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 显示小程序头像,只对百度,微信,QQ小程序有效 |
| | | mpAvatar: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否使用随机背景色 |
| | | randomBgColor: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 加载失败的默认头像(组件有内置默认图片) |
| | | defaultUrl: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间 |
| | | colorIndex: { |
| | | type: [String, Number], |
| | | // 校验参数规则,索引在0-19之间 |
| | | validator(n) { |
| | | return range(n, [0, 19]) || n === '' |
| | | }, |
| | | default: '' |
| | | }, |
| | | // 组件标识符 |
| | | name: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | ...uni.$uv?.props?.avatar |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-avatar" |
| | | :class="[`uv-avatar--${shape}`]" |
| | | :style="[{ |
| | | backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : $uv.random(0, 19)] : bgColor) : 'transparent', |
| | | width: $uv.addUnit(size), |
| | | height: $uv.addUnit(size), |
| | | }, $uv.addStyle(customStyle)]" |
| | | @tap="clickHandler" |
| | | > |
| | | <slot> |
| | | <!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU --> |
| | | <open-data |
| | | v-if="mpAvatar && allowMp" |
| | | type="userAvatarUrl" |
| | | :style="[{ |
| | | width: $uv.addUnit(size), |
| | | height: $uv.addUnit(size) |
| | | }]" |
| | | /> |
| | | <!-- #endif --> |
| | | <!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU --> |
| | | <template v-if="mpAvatar && allowMp"></template> |
| | | <!-- #endif --> |
| | | <uv-icon |
| | | v-else-if="icon" |
| | | :name="icon" |
| | | :size="fontSize" |
| | | :color="color" |
| | | ></uv-icon> |
| | | <uv-text |
| | | v-else-if="text" |
| | | :text="text" |
| | | :size="fontSize" |
| | | :color="color" |
| | | align="center" |
| | | customStyle="justify-content: center" |
| | | ></uv-text> |
| | | <image |
| | | class="uv-avatar__image" |
| | | v-else |
| | | :class="[`uv-avatar__image--${shape}`]" |
| | | :src="avatarUrl || defaultUrl" |
| | | :mode="mode" |
| | | @error="errorHandler" |
| | | :style="[{ |
| | | width: $uv.addUnit(size), |
| | | height: $uv.addUnit(size) |
| | | }]" |
| | | ></image> |
| | | </slot> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | const base64Avatar = |
| | | "data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z"; |
| | | /** |
| | | * Avatar 头像 |
| | | * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。 |
| | | * @tutorial https://www.uvui.cn/components/avatar.html |
| | | * |
| | | * @property {String} src 头像路径,如加载失败,将会显示默认头像(不能为相对路径) |
| | | * @property {String} shape 头像形状 ( circle (默认) | square) |
| | | * @property {String | Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值 (默认 40 ) |
| | | * @property {String} mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值 (默认 'scaleToFill' ) |
| | | * @property {String} text 用文字替代图片,级别优先于src |
| | | * @property {String} bgColor 背景颜色,一般显示文字时用 (默认 '#c0c4cc' ) |
| | | * @property {String} color 文字颜色 (默认 '#ffffff' ) |
| | | * @property {String | Number} fontSize 文字大小 (默认 18 ) |
| | | * @property {String} icon 显示的图标 |
| | | * @property {Boolean} mpAvatar 显示小程序头像,只对百度,微信,QQ小程序有效 (默认 false ) |
| | | * @property {Boolean} randomBgColor 是否使用随机背景色 (默认 false ) |
| | | * @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片) |
| | | * @property {String | Number} colorIndex 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间 |
| | | * @property {String} name 组件标识符 (默认 'level' ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * |
| | | * @event {Function} click 点击组件时触发 index: 用户传递的标识符 |
| | | * @example <uv-avatar :src="src" mode="square"></uv-avatar> |
| | | */ |
| | | export default { |
| | | name: 'uv-avatar', |
| | | emits: ['click'], |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | // 如果配置randomBgColor参数为true,在图标或者文字的模式下,会随机从中取出一个颜色值当做背景色 |
| | | colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2', |
| | | '#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee', |
| | | '#73d1f1', |
| | | '#80a7dc' |
| | | ], |
| | | avatarUrl: this.src, |
| | | allowMp: false |
| | | } |
| | | }, |
| | | watch: { |
| | | // 监听头像src的变化,赋值给内部的avatarUrl变量,因为图片加载失败时,需要修改图片的src为默认值 |
| | | // 而组件内部不能直接修改props的值,所以需要一个中间变量 |
| | | src: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | this.avatarUrl = newVal |
| | | // 如果没有传src,则主动触发error事件,用于显示默认的头像,否则src为''空字符等的时候,会无内容展示 |
| | | if(!newVal) { |
| | | this.errorHandler() |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | imageStyle() { |
| | | const style = {} |
| | | return style |
| | | } |
| | | }, |
| | | created() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 目前只有这几个小程序平台具有open-data标签 |
| | | // 其他平台可以通过uni.getUserInfo类似接口获取信息,但是需要弹窗授权(首次),不合符组件逻辑 |
| | | // 故目前自动获取小程序头像只支持这几个平台 |
| | | // #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU |
| | | this.allowMp = true |
| | | // #endif |
| | | }, |
| | | // 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式 |
| | | isImg() { |
| | | return this.src.indexOf('/') !== -1 |
| | | }, |
| | | // 图片加载时失败时触发 |
| | | errorHandler() { |
| | | this.avatarUrl = this.defaultUrl || base64Avatar |
| | | }, |
| | | clickHandler() { |
| | | this.$emit('click', this.name) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | |
| | | .uv-avatar { |
| | | @include flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | &--circle { |
| | | border-radius: 100px; |
| | | } |
| | | |
| | | &--square { |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | &__image { |
| | | &--circle { |
| | | border-radius: 100px; |
| | | } |
| | | |
| | | &--square { |
| | | border-radius: 4px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-avatar", |
| | | "displayName": "uv-avatar 头像 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.5", |
| | | "description": "uv-avatar 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。", |
| | | "keywords": [ |
| | | "uv-avatar", |
| | | "uvui", |
| | | "uv-ui", |
| | | "avatar", |
| | | "头像" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-icon", |
| | | "uv-text" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Avatar 头像 |
| | | |
| | | > **组件名:uv-avatar** |
| | | |
| | | 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/avatar.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.2(2023-07-03) |
| | | 1. 优化插槽自定义内容部分 |
| | | 2. 增加backToTop方法说明 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-back-top 返回顶部 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 返回顶部的形状,circle-圆形,square-方形 |
| | | mode: { |
| | | type: String, |
| | | default: 'circle' |
| | | }, |
| | | // 自定义图标 |
| | | icon: { |
| | | type: String, |
| | | default: 'arrow-upward' |
| | | }, |
| | | // 提示文字 |
| | | text: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 返回顶部滚动时间 |
| | | duration: { |
| | | type: [String, Number], |
| | | default: 100 |
| | | }, |
| | | // 滚动距离 |
| | | scrollTop: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 距离顶部多少距离显示,单位px |
| | | top: { |
| | | type: [String, Number], |
| | | default: 400 |
| | | }, |
| | | // 返回顶部按钮到底部的距离,单位px |
| | | bottom: { |
| | | type: [String, Number], |
| | | default: 100 |
| | | }, |
| | | // 返回顶部按钮到右边的距离,单位px |
| | | right: { |
| | | type: [String, Number], |
| | | default: 20 |
| | | }, |
| | | // 层级 |
| | | zIndex: { |
| | | type: [String, Number], |
| | | default: 9 |
| | | }, |
| | | // 图标的样式,对象形式 |
| | | iconStyle: { |
| | | type: Object, |
| | | default: () => ({ |
| | | color: '#909399', |
| | | fontSize: '19px' |
| | | }) |
| | | }, |
| | | ...uni.$uv?.props?.backtop |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <uv-transition mode="fade" :customStyle="backTopStyle" :show="show"> |
| | | <slot> |
| | | <view class="uv-back-top" :style="[contentStyle]" @click="backToTop"> |
| | | <uv-icon :name="icon" :custom-style="iconStyle"></uv-icon> |
| | | <text v-if="text" class="uv-back-top__text">{{text}}</text> |
| | | </view> |
| | | </slot> |
| | | </uv-transition> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | // #ifdef APP-NVUE |
| | | const dom = weex.requireModule('dom') |
| | | // #endif |
| | | /** |
| | | * backTop 返回顶部 |
| | | * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。 |
| | | * @tutorial https://www.uvui.cn/components/backTop.html |
| | | * @property {String} mode 返回顶部的形状,circle-圆形,square-方形 (默认 'circle' ) |
| | | * @property {String} icon 自定义图标 (默认 'arrow-upward' ) 见官方文档示例 |
| | | * @property {String} text 提示文字 |
| | | * @property {String | Number} duration 返回顶部滚动时间 (默认 100) |
| | | * @property {String | Number} scrollTop 滚动距离 (默认 0 ) |
| | | * @property {String | Number} top 距离顶部多少距离显示,单位px (默认 400 ) |
| | | * @property {String | Number} bottom 返回顶部按钮到底部的距离,单位px (默认 100 ) |
| | | * @property {String | Number} right 返回顶部按钮到右边的距离,单位px (默认 20 ) |
| | | * @property {String | Number} zIndex 层级 (默认 9 ) |
| | | * @property {Object<Object>} iconStyle 图标的样式,对象形式 (默认 {color: '#909399',fontSize: '19px'}) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * |
| | | * @example <uv-back-top :scrollTop="scrollTop"></uv-back-top> |
| | | */ |
| | | export default { |
| | | name: 'uv-back-top', |
| | | emits: ['click'], |
| | | mixins: [mpMixin, mixin, props], |
| | | computed: { |
| | | backTopStyle() { |
| | | // 动画组件样式 |
| | | const style = { |
| | | bottom: this.$uv.addUnit(this.bottom), |
| | | right: this.$uv.addUnit(this.right), |
| | | width: '40px', |
| | | height: '40px', |
| | | position: 'fixed', |
| | | zIndex: 10, |
| | | } |
| | | return style |
| | | }, |
| | | show() { |
| | | return this.$uv.getPx(this.scrollTop) > this.$uv.getPx(this.top) |
| | | }, |
| | | contentStyle() { |
| | | const style = {} |
| | | let radius = 0 |
| | | // 是否圆形 |
| | | if (this.mode === 'circle') { |
| | | radius = '100px' |
| | | } else { |
| | | radius = '4px' |
| | | } |
| | | // 为了兼容安卓nvue,只能这么分开写 |
| | | style.borderTopLeftRadius = radius |
| | | style.borderTopRightRadius = radius |
| | | style.borderBottomLeftRadius = radius |
| | | style.borderBottomRightRadius = radius |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)) |
| | | } |
| | | }, |
| | | methods: { |
| | | backToTop() { |
| | | // #ifdef APP-NVUE |
| | | if (!this.$parent.$refs['uv-back-top']) { |
| | | this.$uv.error(`nvue页面需要给页面最外层元素设置"ref='uv-back-top'`) |
| | | } |
| | | dom.scrollToElement(this.$parent.$refs['uv-back-top'], { |
| | | offset: 0 |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifndef APP-NVUE |
| | | uni.pageScrollTo({ |
| | | scrollTop: 0, |
| | | duration: this.duration |
| | | }); |
| | | // #endif |
| | | this.$emit('click') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | $uv-back-top-flex: 1 !default; |
| | | $uv-back-top-height: 100% !default; |
| | | $uv-back-top-background-color: #E1E1E1 !default; |
| | | $uv-back-top-tips-font-size: 12px !default; |
| | | .uv-back-top { |
| | | @include flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | flex: $uv-back-top-flex; |
| | | height: $uv-back-top-height; |
| | | justify-content: center; |
| | | background-color: $uv-back-top-background-color; |
| | | &__tips { |
| | | font-size: $uv-back-top-tips-font-size; |
| | | transform: scale(0.8); |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-back-top", |
| | | "displayName": "uv-back-top 返回顶部 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.2", |
| | | "description": "返回顶部 组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。", |
| | | "keywords": [ |
| | | "uv-back-top", |
| | | "uvui", |
| | | "uv-ui", |
| | | "avatar", |
| | | "返回顶部" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-icon", |
| | | "uv-transition" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## BackTop 返回顶部 |
| | | |
| | | > **组件名:uv-back-top** |
| | | |
| | | 该组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/backTop.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.2(2023-06-04) |
| | | 1. 修复type等属性为null的时候不显示徽标的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-badge 徽标数,数字角标 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 是否显示圆点 |
| | | isDot: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 显示的内容 |
| | | value: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | // 是否显示 |
| | | show: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 最大值,超过最大值会显示 '{max}+' |
| | | max: { |
| | | type: [Number, String], |
| | | default: 999 |
| | | }, |
| | | // 主题类型,error|warning|success|primary |
| | | type: { |
| | | type: [String,undefined,null], |
| | | default: 'error' |
| | | }, |
| | | // 当数值为 0 时,是否展示 Badge |
| | | showZero: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 背景颜色,优先级比type高,如设置,type参数会失效 |
| | | bgColor: { |
| | | type: [String, null], |
| | | default: null |
| | | }, |
| | | // 字体颜色 |
| | | color: { |
| | | type: [String, null], |
| | | default: null |
| | | }, |
| | | // 徽标形状,circle-四角均为圆角,horn-左下角为直角 |
| | | shape: { |
| | | type: [String,undefined,null], |
| | | default: 'circle' |
| | | }, |
| | | // 设置数字的显示方式,overflow|ellipsis|limit |
| | | // overflow会根据max字段判断,超出显示`${max}+` |
| | | // ellipsis会根据max判断,超出显示`${max}...` |
| | | // limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数 |
| | | numberType: { |
| | | type: [String,undefined,null], |
| | | default: 'overflow' |
| | | }, |
| | | // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效 |
| | | offset: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | // 是否反转背景和字体颜色 |
| | | inverted: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否绝对定位 |
| | | absolute: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ...uni.$uv?.props?.badge |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <text |
| | | v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)" |
| | | :class="[isDot ? 'uv-badge--dot' : 'uv-badge--not-dot', inverted && 'uv-badge--inverted', shape === 'horn' && 'uv-badge--horn', `uv-badge--${propsType}${inverted ? '--inverted' : ''}`]" |
| | | :style="[$uv.addStyle(customStyle), badgeStyle]" |
| | | class="uv-badge" |
| | | >{{ isDot ? '' :showValue }}</text> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * badge 徽标数 |
| | | * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。 |
| | | * @tutorial https://www.uvui.cn/components/badge.html |
| | | * |
| | | * @property {Boolean} isDot 是否显示圆点 (默认 false ) |
| | | * @property {String | Number} value 显示的内容 |
| | | * @property {Boolean} show 是否显示 (默认 true ) |
| | | * @property {String | Number} max 最大值,超过最大值会显示 '{max}+' (默认999) |
| | | * @property {String} type 主题类型,error|warning|success|primary (默认 'error' ) |
| | | * @property {Boolean} showZero 当数值为 0 时,是否展示 Badge (默认 false ) |
| | | * @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效 |
| | | * @property {String} color 字体颜色 (默认 '#ffffff' ) |
| | | * @property {String} shape 徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' ) |
| | | * @property {String} numberType 设置数字的显示方式,overflow|ellipsis|limit (默认 'overflow' ) |
| | | * @property {Array}} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效 |
| | | * @property {Boolean} inverted 是否反转背景和字体颜色(默认 false ) |
| | | * @property {Boolean} absolute 是否绝对定位(默认 false ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * @example <uv-badge :type="type" :count="count"></uv-badge> |
| | | */ |
| | | export default { |
| | | name: 'uv-badge', |
| | | mixins: [mpMixin, mixin, props], |
| | | computed: { |
| | | // 是否将badge中心与父组件右上角重合 |
| | | boxStyle() { |
| | | let style = {}; |
| | | return style; |
| | | }, |
| | | // 整个组件的样式 |
| | | badgeStyle() { |
| | | const style = {} |
| | | if(this.color) { |
| | | style.color = this.color |
| | | } |
| | | if (this.bgColor && !this.inverted) { |
| | | style.backgroundColor = this.bgColor |
| | | } |
| | | if (this.absolute) { |
| | | style.position = 'absolute' |
| | | // 如果有设置offset参数 |
| | | if(this.offset.length) { |
| | | // top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top |
| | | const top = this.offset[0] |
| | | const right = this.offset[1] || top |
| | | style.top = this.$uv.addUnit(top) |
| | | style.right = this.$uv.addUnit(right) |
| | | } |
| | | } |
| | | return style |
| | | }, |
| | | showValue() { |
| | | switch (this.numberType) { |
| | | case "overflow": |
| | | return Number(this.value) > Number(this.max) ? this.max + "+" : this.value |
| | | break; |
| | | case "ellipsis": |
| | | return Number(this.value) > Number(this.max) ? "..." : this.value |
| | | break; |
| | | case "limit": |
| | | return Number(this.value) > 999 ? Number(this.value) >= 9999 ? |
| | | Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value / |
| | | 1e3 * 100) / 100 + "k" : this.value |
| | | break; |
| | | default: |
| | | return Number(this.value) |
| | | } |
| | | }, |
| | | propsType(){ |
| | | return this.type || 'error' |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-badge-primary: $uv-primary !default; |
| | | $uv-badge-error: $uv-error !default; |
| | | $uv-badge-success: $uv-success !default; |
| | | $uv-badge-info: $uv-info !default; |
| | | $uv-badge-warning: $uv-warning !default; |
| | | $uv-badge-dot-radius: 100px !default; |
| | | $uv-badge-dot-size: 8px !default; |
| | | $uv-badge-dot-right: 4px !default; |
| | | $uv-badge-dot-top: 0 !default; |
| | | $uv-badge-text-font-size: 11px !default; |
| | | $uv-badge-text-right: 10px !default; |
| | | $uv-badge-text-padding: 2px 5px !default; |
| | | $uv-badge-text-align: center !default; |
| | | $uv-badge-text-color: #FFFFFF !default; |
| | | |
| | | .uv-badge { |
| | | border-top-right-radius: $uv-badge-dot-radius; |
| | | border-top-left-radius: $uv-badge-dot-radius; |
| | | border-bottom-left-radius: $uv-badge-dot-radius; |
| | | border-bottom-right-radius: $uv-badge-dot-radius; |
| | | @include flex; |
| | | line-height: $uv-badge-text-font-size; |
| | | text-align: $uv-badge-text-align; |
| | | font-size: $uv-badge-text-font-size; |
| | | color: $uv-badge-text-color; |
| | | |
| | | &--dot { |
| | | height: $uv-badge-dot-size; |
| | | width: $uv-badge-dot-size; |
| | | } |
| | | |
| | | &--inverted { |
| | | font-size: 13px; |
| | | } |
| | | |
| | | &--not-dot { |
| | | padding: $uv-badge-text-padding; |
| | | } |
| | | |
| | | &--horn { |
| | | border-bottom-left-radius: 0; |
| | | } |
| | | |
| | | &--primary { |
| | | background-color: $uv-badge-primary; |
| | | } |
| | | |
| | | &--primary--inverted { |
| | | color: $uv-badge-primary; |
| | | } |
| | | |
| | | &--error { |
| | | background-color: $uv-badge-error; |
| | | } |
| | | |
| | | &--error--inverted { |
| | | color: $uv-badge-error; |
| | | } |
| | | |
| | | &--success { |
| | | background-color: $uv-badge-success; |
| | | } |
| | | |
| | | &--success--inverted { |
| | | color: $uv-badge-success; |
| | | } |
| | | |
| | | &--info { |
| | | background-color: $uv-badge-info; |
| | | } |
| | | |
| | | &--info--inverted { |
| | | color: $uv-badge-info; |
| | | } |
| | | |
| | | &--warning { |
| | | background-color: $uv-badge-warning; |
| | | } |
| | | |
| | | &--warning--inverted { |
| | | color: $uv-badge-warning; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-badge", |
| | | "displayName": "uv-badge 徽标数,数字角标 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.2", |
| | | "description": "徽标数一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。", |
| | | "keywords": [ |
| | | "uv-badge", |
| | | "uvui", |
| | | "uv-ui", |
| | | "徽标数", |
| | | "数字角标" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Badge 徽标数 |
| | | |
| | | > **组件名:uv-badge** |
| | | |
| | | 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/badge.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.15(2023-12-20) |
| | | 1. 优化 |
| | | ## 1.0.14(2023-12-06) |
| | | 1. 优化 |
| | | ## 1.0.13(2023-12-06) |
| | | 1. 阻止事件冒泡处理 |
| | | ## 1.0.12(2023-10-19) |
| | | 1. 增加后置插槽 |
| | | ## 1.0.11(2023-09-21) |
| | | 1. 修复通过customStyle修改按钮宽度,组件中最外层节点不改变的问题 |
| | | ## 1.0.10(2023-09-15) |
| | | 1. 按钮支持open-type="agreePrivacyAuthorization" |
| | | ## 1.0.9(2023-09-11) |
| | | 1. 增加参数iconSize,用于控制图标的大小 |
| | | ## 1.0.8(2023-09-10) |
| | | 1. 修复多个按钮在一行宽度不正常的BUG |
| | | ## 1.0.7(2023-09-07) |
| | | 1. 修复warning颜色对应错误的BUG |
| | | ## 1.0.6(2023-07-25) |
| | | 1. 增加customTextStyle属性,方便自定义文字样式 |
| | | ## 1.0.5(2023-07-20) |
| | | 1. 解决微信小程序动态设置hover-class点击态不消失的BUG |
| | | ## 1.0.4(2023-06-29) |
| | | 1. 修改上次更新出现nvue报错异常 |
| | | ## 1.0.3(2023-06-28) |
| | | 修复:设置open-type="chooseAvatar"等值不生效的BUG |
| | | ## 1.0.2(2023-06-01) |
| | | 1. 修复按钮点击触发两次的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-button 按钮 |
| New file |
| | |
| | | $uv-button-active-opacity:0.75 !default; |
| | | $uv-button-loading-text-margin-left:4px !default; |
| | | $uv-button-text-color: #FFFFFF !default; |
| | | $uv-button-text-plain-error-color:$uv-error !default; |
| | | $uv-button-text-plain-warning-color:$uv-warning !default; |
| | | $uv-button-text-plain-success-color:$uv-success !default; |
| | | $uv-button-text-plain-info-color:$uv-info !default; |
| | | $uv-button-text-plain-primary-color:$uv-primary !default; |
| | | .uv-button { |
| | | &--active { |
| | | opacity: $uv-button-active-opacity; |
| | | } |
| | | |
| | | &--active--plain { |
| | | background-color: rgb(217, 217, 217); |
| | | } |
| | | |
| | | &__loading-text { |
| | | margin-left:$uv-button-loading-text-margin-left; |
| | | } |
| | | |
| | | &__text, |
| | | &__loading-text { |
| | | color:$uv-button-text-color; |
| | | } |
| | | |
| | | &__text--plain--error { |
| | | color:$uv-button-text-plain-error-color; |
| | | } |
| | | |
| | | &__text--plain--warning { |
| | | color:$uv-button-text-plain-warning-color; |
| | | } |
| | | |
| | | &__text--plain--success{ |
| | | color:$uv-button-text-plain-success-color; |
| | | } |
| | | |
| | | &__text--plain--info { |
| | | color:$uv-button-text-plain-info-color; |
| | | } |
| | | |
| | | &__text--plain--primary { |
| | | color:$uv-button-text-plain-primary-color; |
| | | } |
| | | } |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 是否细边框 |
| | | hairline: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 按钮的预置样式,info,primary,error,warning,success |
| | | type: { |
| | | type: String, |
| | | default: 'info' |
| | | }, |
| | | // 按钮尺寸,large,normal,small,mini |
| | | size: { |
| | | type: String, |
| | | default: 'normal' |
| | | }, |
| | | // 按钮形状,circle(两边为半圆),square(带圆角) |
| | | shape: { |
| | | type: String, |
| | | default: 'square' |
| | | }, |
| | | // 按钮是否镂空 |
| | | plain: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否禁止状态 |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否加载中 |
| | | loading: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 加载中提示文字 |
| | | loadingText: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 加载状态图标类型 |
| | | loadingMode: { |
| | | type: String, |
| | | default: 'spinner' |
| | | }, |
| | | // 加载图标大小 |
| | | loadingSize: { |
| | | type: [String, Number], |
| | | default: 14 |
| | | }, |
| | | // 开放能力,具体请看uniapp稳定关于button组件部分说明 |
| | | // https://uniapp.dcloud.io/component/button |
| | | openType: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 |
| | | // 取值为submit(提交表单),reset(重置表单) |
| | | formType: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 |
| | | // 只微信小程序、QQ小程序有效 |
| | | appParameter: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效 |
| | | hoverStopPropagation: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效 |
| | | lang: { |
| | | type: String, |
| | | default: 'en' |
| | | }, |
| | | // 会话来源,open-type="contact"时有效。只微信小程序有效 |
| | | sessionFrom: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 会话内消息卡片标题,open-type="contact"时有效 |
| | | // 默认当前标题,只微信小程序有效 |
| | | sendMessageTitle: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效 |
| | | // 默认当前分享路径,只微信小程序有效 |
| | | sendMessagePath: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 会话内消息卡片图片,open-type="contact"时有效 |
| | | // 默认当前页面截图,只微信小程序有效 |
| | | sendMessageImg: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示, |
| | | // 用户点击后可以快速发送小程序消息,open-type="contact"时有效 |
| | | showMessageCard: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取 |
| | | dataName: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 节流,一定时间内只能触发一次 |
| | | throttleTime: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 按住后多久出现点击态,单位毫秒 |
| | | hoverStartTime: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 手指松开后点击态保留时间,单位毫秒 |
| | | hoverStayTime: { |
| | | type: [String, Number], |
| | | default: 200 |
| | | }, |
| | | // 按钮文字,之所以通过props传入,是因为slot传入的话 |
| | | // nvue中无法控制文字的样式 |
| | | text: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 按钮图标 |
| | | icon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 按钮图标大小 |
| | | iconSize: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 按钮图标颜色 |
| | | iconColor: { |
| | | type: String, |
| | | default: '#000000' |
| | | }, |
| | | // 按钮颜色,支持传入linear-gradient渐变色 |
| | | color: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 自定义按钮文本样式 |
| | | customTextStyle: { |
| | | type: [Object,String], |
| | | default: '' |
| | | }, |
| | | ...uni.$uv?.props?.button |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-button-wrapper" |
| | | :style="[btnWrapperStyle]" |
| | | > |
| | | <!-- #ifndef APP-NVUE --> |
| | | <!-- #ifdef MP --> |
| | | <!-- 为了解决微信小程序动态设置hover-class点击态不消失的BUG --> |
| | | <view class="uv-button-wrapper--dis" v-if="disabled || loading"></view> |
| | | <button |
| | | :hover-start-time="Number(hoverStartTime)" |
| | | :hover-stay-time="Number(hoverStayTime)" |
| | | :form-type="formType" |
| | | :open-type="openType" |
| | | :app-parameter="appParameter" |
| | | :hover-stop-propagation="hoverStopPropagation" |
| | | :send-message-title="sendMessageTitle" |
| | | :send-message-path="sendMessagePath" |
| | | :lang="lang" |
| | | :data-name="dataName" |
| | | :session-from="sessionFrom" |
| | | :send-message-img="sendMessageImg" |
| | | :show-message-card="showMessageCard" |
| | | @getphonenumber="onGetPhoneNumber" |
| | | @getuserinfo="onGetUserInfo" |
| | | @error="onError" |
| | | @opensetting="onOpenSetting" |
| | | @launchapp="onLaunchApp" |
| | | @contact="onContact" |
| | | @chooseavatar="onChooseavatar" |
| | | @agreeprivacyauthorization="onAgreeprivacyauthorization" |
| | | @addgroupapp="onAddgroupapp" |
| | | @chooseaddress="onChooseaddress" |
| | | @subscribe="onSubscribe" |
| | | @login="onLogin" |
| | | @im="onIm" |
| | | hover-class="uv-button--active" |
| | | class="uv-button uv-reset-button" |
| | | :style="[baseColor, $uv.addStyle(customStyle)]" |
| | | @tap="clickHandler" |
| | | :class="bemClass" |
| | | > |
| | | <!-- #endif --> |
| | | <!-- #ifndef MP --> |
| | | <button |
| | | :hover-start-time="Number(hoverStartTime)" |
| | | :hover-stay-time="Number(hoverStayTime)" |
| | | :form-type="formType" |
| | | :open-type="openType" |
| | | :app-parameter="appParameter" |
| | | :hover-stop-propagation="hoverStopPropagation" |
| | | :send-message-title="sendMessageTitle" |
| | | :send-message-path="sendMessagePath" |
| | | :lang="lang" |
| | | :data-name="dataName" |
| | | :session-from="sessionFrom" |
| | | :send-message-img="sendMessageImg" |
| | | :show-message-card="showMessageCard" |
| | | :hover-class="!disabled && !loading ? 'uv-button--active' : ''" |
| | | class="uv-button uv-reset-button" |
| | | :style="[baseColor, $uv.addStyle(customStyle)]" |
| | | @tap="clickHandler" |
| | | :class="bemClass" |
| | | > |
| | | <!-- #endif --> |
| | | <template v-if="loading"> |
| | | <uv-loading-icon |
| | | :mode="loadingMode" |
| | | :size="loadingSize * 1.15" |
| | | :color="loadingColor" |
| | | ></uv-loading-icon> |
| | | <text |
| | | class="uv-button__loading-text" |
| | | :style="[ |
| | | { fontSize: textSize + 'px' }, |
| | | $uv.addStyle(customTextStyle) |
| | | ]" |
| | | >{{ loadingText || text }}</text> |
| | | </template> |
| | | <template v-else> |
| | | <uv-icon |
| | | v-if="icon" |
| | | :name="icon" |
| | | :color="iconColorCom" |
| | | :size="getIconSize" |
| | | :customStyle="{ marginRight: '2px' }" |
| | | ></uv-icon> |
| | | <slot> |
| | | <text |
| | | class="uv-button__text" |
| | | :style="[ |
| | | { fontSize: textSize + 'px' }, |
| | | $uv.addStyle(customTextStyle) |
| | | ]" |
| | | >{{ text }}</text> |
| | | </slot> |
| | | <slot name="suffix"></slot> |
| | | </template> |
| | | </button> |
| | | <!-- #endif --> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <view |
| | | :hover-start-time="Number(hoverStartTime)" |
| | | :hover-stay-time="Number(hoverStayTime)" |
| | | class="uv-button" |
| | | :hover-class=" |
| | | !disabled && !loading && !color && (plain || type === 'info') |
| | | ? 'uv-button--active--plain' |
| | | : !disabled && !loading && !plain |
| | | ? 'uv-button--active' |
| | | : '' |
| | | " |
| | | @tap="clickHandler" |
| | | :class="bemClass" |
| | | :style="[baseColor, $uv.addStyle(customStyle)]" |
| | | > |
| | | <template v-if="loading"> |
| | | <uv-loading-icon |
| | | :mode="loadingMode" |
| | | :size="loadingSize * 1.15" |
| | | :color="loadingColor" |
| | | ></uv-loading-icon> |
| | | <text |
| | | class="uv-button__loading-text" |
| | | :style="[nvueTextStyle,$uv.addStyle(customTextStyle)]" |
| | | :class="[plain && `uv-button__text--plain--${type}`]" |
| | | >{{ loadingText || text }}</text> |
| | | </template> |
| | | <template v-else> |
| | | <uv-icon |
| | | v-if="icon" |
| | | :name="icon" |
| | | :color="iconColorCom" |
| | | :size="getIconSize" |
| | | ></uv-icon> |
| | | <text |
| | | class="uv-button__text" |
| | | :style="[ |
| | | { |
| | | marginLeft: icon ? '2px' : 0, |
| | | }, |
| | | nvueTextStyle, |
| | | $uv.addStyle(customTextStyle) |
| | | ]" |
| | | :class="[plain && `uv-button__text--plain--${type}`]" |
| | | >{{ text }}</text> |
| | | <slot name="suffix"></slot> |
| | | </template> |
| | | </view> |
| | | <!-- #endif --> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import throttle from '@/uni_modules/uv-ui-tools/libs/function/throttle.js'; |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js' |
| | | import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js' |
| | | import props from "./props.js"; |
| | | /** |
| | | * button 按钮 |
| | | * @description Button 按钮 |
| | | * @tutorial https://www.uvui.cn/components/button.html |
| | | * @property {Boolean} hairline 是否显示按钮的细边框 (默认 true ) |
| | | * @property {String} type 按钮的预置样式,info,primary,error,warning,success (默认 'info' ) |
| | | * @property {String} size 按钮尺寸,large,normal,mini (默认 normal) |
| | | * @property {String} shape 按钮形状,circle(两边为半圆),square(带圆角) (默认 'square' ) |
| | | * @property {Boolean} plain 按钮是否镂空,背景色透明 (默认 false) |
| | | * @property {Boolean} disabled 是否禁用 (默认 false) |
| | | * @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) (默认 false) |
| | | * @property {String | Number} loadingText 加载中提示文字 |
| | | * @property {String} loadingMode 加载状态图标类型 (默认 'spinner' ) |
| | | * @property {String | Number} loadingSize 加载图标大小 (默认 15 ) |
| | | * @property {String} openType 开放能力,具体请看uniapp稳定关于button组件部分说明 |
| | | * @property {String} formType 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 |
| | | * @property {String} appParameter 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 (注:只微信小程序、QQ小程序有效) |
| | | * @property {Boolean} hoverStopPropagation 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true ) |
| | | * @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文(默认 en ) |
| | | * @property {String} sessionFrom 会话来源,openType="contact"时有效 |
| | | * @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效 |
| | | * @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效 |
| | | * @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效 |
| | | * @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效(默认false) |
| | | * @property {String} dataName 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取 |
| | | * @property {String | Number} throttleTime 节流,一定时间内只能触发一次 (默认 0 ) |
| | | * @property {String | Number} hoverStartTime 按住后多久出现点击态,单位毫秒 (默认 0 ) |
| | | * @property {String | Number} hoverStayTime 手指松开后点击态保留时间,单位毫秒 (默认 200 ) |
| | | * @property {String | Number} text 按钮文字,之所以通过props传入,是因为slot传入的话(注:nvue中无法控制文字的样式) |
| | | * @property {String} icon 按钮图标 |
| | | * @property {String} iconColor 按钮图标颜色 |
| | | * @property {String} color 按钮颜色,支持传入linear-gradient渐变色 |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * @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 <uv-button>月落</uv-button> |
| | | */ |
| | | export default { |
| | | name: "uv-button", |
| | | // #ifdef MP |
| | | mixins: [mpMixin, mixin, button, openType, props], |
| | | // #endif |
| | | // #ifndef MP |
| | | mixins: [mpMixin, mixin, props], |
| | | // #endif |
| | | emits: ['click'], |
| | | data() { |
| | | return {}; |
| | | }, |
| | | computed: { |
| | | // 生成bem风格的类名 |
| | | bemClass() { |
| | | // this.bem为一个computed变量,在mixin中 |
| | | if (!this.color) { |
| | | return this.bem("button", |
| | | ["type", "shape", "size"], |
| | | ["disabled", "plain", "hairline"]); |
| | | } else { |
| | | // 由于nvue的原因,在有color参数时,不需要传入type,否则会生成type相关的类型,影响最终的样式 |
| | | return this.bem("button", |
| | | ["shape", "size"], |
| | | ["disabled", "plain", "hairline"]); |
| | | } |
| | | }, |
| | | loadingColor() { |
| | | if (this.plain) { |
| | | // 如果有设置color值,则用color值,否则使用type主题颜色 |
| | | return this.color ? this.color : '#3c9cff'; |
| | | } |
| | | if (this.type === "info") { |
| | | return "#c9c9c9"; |
| | | } |
| | | return "rgb(200, 200, 200)"; |
| | | }, |
| | | iconColorCom() { |
| | | // 如果是镂空状态,设置了color就用color值,否则使用主题颜色, |
| | | // uv-icon的color能接受一个主题颜色的值 |
| | | if (this.iconColor) return this.iconColor; |
| | | if (this.plain) { |
| | | return this.color ? this.color : this.type; |
| | | } else { |
| | | return this.type === "info" ? "#000000" : "#ffffff"; |
| | | } |
| | | }, |
| | | baseColor() { |
| | | let style = {}; |
| | | if (this.color) { |
| | | // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色 |
| | | style.color = this.plain ? this.color : "white"; |
| | | if (!this.plain) { |
| | | // 非镂空,背景色使用自定义的颜色 |
| | | style["background-color"] = this.color; |
| | | } |
| | | if (this.color.indexOf("gradient") !== -1) { |
| | | // 如果自定义的颜色为渐变色,不显示边框,以及通过backgroundImage设置渐变色 |
| | | // weex文档说明可以写borderWidth的形式,为什么这里需要分开写? |
| | | // 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西,所以需要这么写才有效 |
| | | style.borderTopWidth = 0; |
| | | style.borderRightWidth = 0; |
| | | style.borderBottomWidth = 0; |
| | | style.borderLeftWidth = 0; |
| | | if (!this.plain) { |
| | | style.backgroundImage = this.color; |
| | | } |
| | | } else { |
| | | // 非渐变色,则设置边框相关的属性 |
| | | style.borderColor = this.color; |
| | | style.borderWidth = "1px"; |
| | | style.borderStyle = "solid"; |
| | | } |
| | | } |
| | | return style; |
| | | }, |
| | | // nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置 |
| | | nvueTextStyle() { |
| | | let style = {}; |
| | | // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色 |
| | | if (this.type === "info") { |
| | | style.color = "#323233"; |
| | | } |
| | | if (this.color) { |
| | | style.color = this.plain ? this.color : "white"; |
| | | } |
| | | style.fontSize = this.textSize + "px"; |
| | | return style; |
| | | }, |
| | | // 字体大小 |
| | | textSize() { |
| | | let fontSize = 14, |
| | | { size } = this; |
| | | if (size === "large") fontSize = 16; |
| | | if (size === "normal") fontSize = 14; |
| | | if (size === "small") fontSize = 12; |
| | | if (size === "mini") fontSize = 10; |
| | | return fontSize; |
| | | }, |
| | | // 设置图标大小 |
| | | getIconSize() { |
| | | const size = this.iconSize ? this.iconSize : this.textSize * 1.35; |
| | | return this.$uv.addUnit(size); |
| | | }, |
| | | // 设置外层盒子的宽度,其他样式不需要 |
| | | btnWrapperStyle() { |
| | | const style = {}; |
| | | const customStyle = this.$uv.addStyle(this.customStyle); |
| | | if(customStyle.width) style.width = customStyle.width; |
| | | return style; |
| | | } |
| | | }, |
| | | methods: { |
| | | clickHandler() { |
| | | // 非禁止并且非加载中,才能点击 |
| | | if (!this.disabled && !this.loading) { |
| | | // 进行节流控制,每this.throttle毫秒内,只在开始处执行 |
| | | throttle(() => { |
| | | this.$emit("click"); |
| | | }, this.throttleTime); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-reset-button: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | @import "./vue.scss"; |
| | | /* #endif */ |
| | | |
| | | /* #ifdef APP-NVUE */ |
| | | @import "./nvue.scss"; |
| | | /* #endif */ |
| | | |
| | | $uv-button-uv-button-height: 40px !default; |
| | | $uv-button-text-font-size: 15px !default; |
| | | $uv-button-loading-text-font-size: 15px !default; |
| | | $uv-button-loading-text-margin-left: 4px !default; |
| | | $uv-button-large-width: 100% !default; |
| | | $uv-button-large-height: 50px !default; |
| | | $uv-button-normal-padding: 0 12px !default; |
| | | $uv-button-large-padding: 0 15px !default; |
| | | $uv-button-normal-font-size: 14px !default; |
| | | $uv-button-small-min-width: 60px !default; |
| | | $uv-button-small-height: 30px !default; |
| | | $uv-button-small-padding: 0px 8px !default; |
| | | $uv-button-mini-padding: 0px 8px !default; |
| | | $uv-button-small-font-size: 12px !default; |
| | | $uv-button-mini-height: 22px !default; |
| | | $uv-button-mini-font-size: 10px !default; |
| | | $uv-button-mini-min-width: 50px !default; |
| | | $uv-button-disabled-opacity: 0.5 !default; |
| | | $uv-button-info-color: #323233 !default; |
| | | $uv-button-info-background-color: #fff !default; |
| | | $uv-button-info-border-color: #ebedf0 !default; |
| | | $uv-button-info-border-width: 1px !default; |
| | | $uv-button-info-border-style: solid !default; |
| | | $uv-button-success-color: #fff !default; |
| | | $uv-button-success-background-color: $uv-success !default; |
| | | $uv-button-success-border-color: $uv-button-success-background-color !default; |
| | | $uv-button-success-border-width: 1px !default; |
| | | $uv-button-success-border-style: solid !default; |
| | | $uv-button-primary-color: #fff !default; |
| | | $uv-button-primary-background-color: $uv-primary !default; |
| | | $uv-button-primary-border-color: $uv-button-primary-background-color !default; |
| | | $uv-button-primary-border-width: 1px !default; |
| | | $uv-button-primary-border-style: solid !default; |
| | | $uv-button-error-color: #fff !default; |
| | | $uv-button-error-background-color: $uv-error !default; |
| | | $uv-button-error-border-color: $uv-button-error-background-color !default; |
| | | $uv-button-error-border-width: 1px !default; |
| | | $uv-button-error-border-style: solid !default; |
| | | $uv-button-warning-color: #fff !default; |
| | | $uv-button-warning-background-color: $uv-warning !default; |
| | | $uv-button-warning-border-color: $uv-button-warning-background-color !default; |
| | | $uv-button-warning-border-width: 1px !default; |
| | | $uv-button-warning-border-style: solid !default; |
| | | $uv-button-block-width: 100% !default; |
| | | $uv-button-circle-border-top-right-radius: 100px !default; |
| | | $uv-button-circle-border-top-left-radius: 100px !default; |
| | | $uv-button-circle-border-bottom-left-radius: 100px !default; |
| | | $uv-button-circle-border-bottom-right-radius: 100px !default; |
| | | $uv-button-square-border-top-right-radius: 3px !default; |
| | | $uv-button-square-border-top-left-radius: 3px !default; |
| | | $uv-button-square-border-bottom-left-radius: 3px !default; |
| | | $uv-button-square-border-bottom-right-radius: 3px !default; |
| | | $uv-button-icon-min-width: 1em !default; |
| | | $uv-button-plain-background-color: #fff !default; |
| | | $uv-button-hairline-border-width: 0.5px !default; |
| | | |
| | | .uv-button { |
| | | height: $uv-button-uv-button-height; |
| | | position: relative; |
| | | align-items: center; |
| | | justify-content: center; |
| | | @include flex; |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | |
| | | &__text { |
| | | font-size: $uv-button-text-font-size; |
| | | } |
| | | |
| | | &__loading-text { |
| | | font-size: $uv-button-loading-text-font-size; |
| | | margin-left: $uv-button-loading-text-margin-left; |
| | | } |
| | | |
| | | &--large { |
| | | /* #ifndef APP-NVUE */ |
| | | width: $uv-button-large-width; |
| | | /* #endif */ |
| | | height: $uv-button-large-height; |
| | | padding: $uv-button-large-padding; |
| | | } |
| | | |
| | | &--normal { |
| | | padding: $uv-button-normal-padding; |
| | | font-size: $uv-button-normal-font-size; |
| | | } |
| | | |
| | | &--small { |
| | | /* #ifndef APP-NVUE */ |
| | | min-width: $uv-button-small-min-width; |
| | | /* #endif */ |
| | | height: $uv-button-small-height; |
| | | padding: $uv-button-small-padding; |
| | | font-size: $uv-button-small-font-size; |
| | | } |
| | | |
| | | &--mini { |
| | | height: $uv-button-mini-height; |
| | | font-size: $uv-button-mini-font-size; |
| | | /* #ifndef APP-NVUE */ |
| | | min-width: $uv-button-mini-min-width; |
| | | /* #endif */ |
| | | padding: $uv-button-mini-padding; |
| | | } |
| | | |
| | | &--disabled { |
| | | opacity: $uv-button-disabled-opacity; |
| | | } |
| | | |
| | | &--info { |
| | | color: $uv-button-info-color; |
| | | background-color: $uv-button-info-background-color; |
| | | border-color: $uv-button-info-border-color; |
| | | border-width: $uv-button-info-border-width; |
| | | border-style: $uv-button-info-border-style; |
| | | } |
| | | |
| | | &--success { |
| | | color: $uv-button-success-color; |
| | | background-color: $uv-button-success-background-color; |
| | | border-color: $uv-button-success-border-color; |
| | | border-width: $uv-button-success-border-width; |
| | | border-style: $uv-button-success-border-style; |
| | | } |
| | | |
| | | &--primary { |
| | | color: $uv-button-primary-color; |
| | | background-color: $uv-button-primary-background-color; |
| | | border-color: $uv-button-primary-border-color; |
| | | border-width: $uv-button-primary-border-width; |
| | | border-style: $uv-button-primary-border-style; |
| | | } |
| | | |
| | | &--error { |
| | | color: $uv-button-error-color; |
| | | background-color: $uv-button-error-background-color; |
| | | border-color: $uv-button-error-border-color; |
| | | border-width: $uv-button-error-border-width; |
| | | border-style: $uv-button-error-border-style; |
| | | } |
| | | |
| | | &--warning { |
| | | color: $uv-button-warning-color; |
| | | background-color: $uv-button-warning-background-color; |
| | | border-color: $uv-button-warning-border-color; |
| | | border-width: $uv-button-warning-border-width; |
| | | border-style: $uv-button-warning-border-style; |
| | | } |
| | | |
| | | &--block { |
| | | @include flex; |
| | | width: $uv-button-block-width; |
| | | } |
| | | |
| | | &--circle { |
| | | border-top-right-radius: $uv-button-circle-border-top-right-radius; |
| | | border-top-left-radius: $uv-button-circle-border-top-left-radius; |
| | | border-bottom-left-radius: $uv-button-circle-border-bottom-left-radius; |
| | | border-bottom-right-radius: $uv-button-circle-border-bottom-right-radius; |
| | | } |
| | | |
| | | &--square { |
| | | border-bottom-left-radius: $uv-button-square-border-top-right-radius; |
| | | border-bottom-right-radius: $uv-button-square-border-top-left-radius; |
| | | border-top-left-radius: $uv-button-square-border-bottom-left-radius; |
| | | border-top-right-radius: $uv-button-square-border-bottom-right-radius; |
| | | } |
| | | |
| | | &__icon { |
| | | /* #ifndef APP-NVUE */ |
| | | min-width: $uv-button-icon-min-width; |
| | | line-height: inherit !important; |
| | | vertical-align: top; |
| | | /* #endif */ |
| | | } |
| | | |
| | | &--plain { |
| | | background-color: $uv-button-plain-background-color; |
| | | } |
| | | |
| | | &--hairline { |
| | | border-width: $uv-button-hairline-border-width !important; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | // nvue下hover-class无效 |
| | | $uv-button-before-top:50% !default; |
| | | $uv-button-before-left:50% !default; |
| | | $uv-button-before-width:100% !default; |
| | | $uv-button-before-height:100% !default; |
| | | $uv-button-before-transform:translate(-50%, -50%) !default; |
| | | $uv-button-before-opacity:0 !default; |
| | | $uv-button-before-background-color:#000 !default; |
| | | $uv-button-before-border-color:#000 !default; |
| | | $uv-button-active-before-opacity:.15 !default; |
| | | $uv-button-icon-margin-left:4px !default; |
| | | $uv-button-plain-uv-button-info-color:$uv-info; |
| | | $uv-button-plain-uv-button-success-color:$uv-success; |
| | | $uv-button-plain-uv-button-error-color:$uv-error; |
| | | $uv-button-plain-uv-button-warning-color:$uv-warning; |
| | | |
| | | .uv-button-wrapper { |
| | | position: relative; |
| | | &--dis { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | z-index: 9; |
| | | } |
| | | } |
| | | |
| | | .uv-button { |
| | | width: 100%; |
| | | |
| | | &__text { |
| | | white-space: nowrap; |
| | | line-height: 1; |
| | | } |
| | | |
| | | &:before { |
| | | position: absolute; |
| | | top:$uv-button-before-top; |
| | | left:$uv-button-before-left; |
| | | width:$uv-button-before-width; |
| | | height:$uv-button-before-height; |
| | | border: inherit; |
| | | border-radius: inherit; |
| | | transform:$uv-button-before-transform; |
| | | opacity:$uv-button-before-opacity; |
| | | content: " "; |
| | | background-color:$uv-button-before-background-color; |
| | | border-color:$uv-button-before-border-color; |
| | | } |
| | | |
| | | &--active { |
| | | &:before { |
| | | opacity: .15 |
| | | } |
| | | } |
| | | |
| | | &__icon+&__text:not(:empty), |
| | | &__loading-text { |
| | | margin-left:$uv-button-icon-margin-left; |
| | | } |
| | | |
| | | &--plain { |
| | | &.uv-button--primary { |
| | | color: $uv-primary; |
| | | } |
| | | } |
| | | |
| | | &--plain { |
| | | &.uv-button--info { |
| | | color:$uv-button-plain-uv-button-info-color; |
| | | } |
| | | } |
| | | |
| | | &--plain { |
| | | &.uv-button--success { |
| | | color:$uv-button-plain-uv-button-success-color; |
| | | } |
| | | } |
| | | |
| | | &--plain { |
| | | &.uv-button--error { |
| | | color:$uv-button-plain-uv-button-error-color; |
| | | } |
| | | } |
| | | |
| | | &--plain { |
| | | &.uv-button--warning { |
| | | color:$uv-button-plain-uv-button-warning-color; |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | { |
| | | "id": "uv-button", |
| | | "displayName": "uv-button 按钮 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.15", |
| | | "description": "按钮组件内部实现以uni-app的button组件为基础,进行二次封装,灵活配置,功能齐全,兼容全端。", |
| | | "keywords": [ |
| | | "uv-button", |
| | | "uvui", |
| | | "uv-ui", |
| | | "button", |
| | | "按钮" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-loading-icon", |
| | | "uv-icon" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Button 按钮 |
| | | |
| | | > **组件名:uv-button** |
| | | |
| | | 该组件内部实现以`uni-app`的`button`组件为基础,进行二次封装,灵活配置,功能齐全,兼容全端。灵活配置,内置状态设置,开箱即用。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/button.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.6(2023-11-03) |
| | | 1. **该版本不再维护**,推荐使用新版本[https://ext.dcloud.net.cn/plugin?name=uv-calendars](https://ext.dcloud.net.cn/plugin?name=uv-calendars) |
| | | ## 1.0.5(2023-07-02) |
| | | uv-calendar 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/calendar.html |
| | | ## 1.0.4(2023-06-15) |
| | | 1. formatter格式化中增加topInfo参数 |
| | | ## 1.0.3(2023-06-08) |
| | | 1. 增加点击日期change回调 |
| | | 2. 优化 |
| | | ## 1.0.2(2023-06-05) |
| | | 1. 修改多个时间选择的时候存在反选的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-calendar 日历 |
| New file |
| | |
| | | /** |
| | | * @1900-2100区间内的公历、农历互转 |
| | | * @charset UTF-8 |
| | | * @github https://github.com/jjonline/calendar.js |
| | | * @Author Jea杨(JJonline@JJonline.Cn) |
| | | * @Time 2014-7-21 |
| | | * @Time 2016-8-13 Fixed 2033hex、Attribution Annals |
| | | * @Time 2016-9-25 Fixed lunar LeapMonth Param Bug |
| | | * @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year |
| | | * @Version 1.0.3 |
| | | * @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0] |
| | | * @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0] |
| | | */ |
| | | /* eslint-disable */ |
| | | var calendar = { |
| | | |
| | | /** |
| | | * 农历1900-2100的润大小信息表 |
| | | * @Array Of Property |
| | | * @return Hex |
| | | */ |
| | | lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909 |
| | | 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919 |
| | | 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929 |
| | | 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939 |
| | | 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949 |
| | | 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959 |
| | | 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969 |
| | | 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979 |
| | | 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989 |
| | | 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999 |
| | | 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009 |
| | | 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019 |
| | | 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029 |
| | | 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039 |
| | | 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049 |
| | | /** Add By JJonline@JJonline.Cn**/ |
| | | 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059 |
| | | 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069 |
| | | 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079 |
| | | 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089 |
| | | 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099 |
| | | 0x0d520], // 2100 |
| | | |
| | | /** |
| | | * 公历每个月份的天数普通表 |
| | | * @Array Of Property |
| | | * @return Number |
| | | */ |
| | | solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], |
| | | |
| | | /** |
| | | * 天干地支之天干速查表 |
| | | * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"] |
| | | * @return Cn string |
| | | */ |
| | | Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'], |
| | | |
| | | /** |
| | | * 天干地支之地支速查表 |
| | | * @Array Of Property |
| | | * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"] |
| | | * @return Cn string |
| | | */ |
| | | Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'], |
| | | |
| | | /** |
| | | * 天干地支之地支速查表<=>生肖 |
| | | * @Array Of Property |
| | | * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"] |
| | | * @return Cn string |
| | | */ |
| | | Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'], |
| | | |
| | | /** |
| | | * 24节气速查表 |
| | | * @Array Of Property |
| | | * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"] |
| | | * @return Cn string |
| | | */ |
| | | solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'], |
| | | |
| | | /** |
| | | * 1900-2100各年的24节气日期速查表 |
| | | * @Array Of Property |
| | | * @return 0x string For splice |
| | | */ |
| | | sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', |
| | | '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', |
| | | 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f', |
| | | '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa', |
| | | '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', |
| | | '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f', |
| | | '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', |
| | | '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f', |
| | | '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
| | | '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
| | | '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722', |
| | | '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
| | | '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
| | | '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
| | | '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
| | | '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721', |
| | | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2', |
| | | '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
| | | '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
| | | '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721', |
| | | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5', |
| | | '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722', |
| | | '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', |
| | | '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
| | | '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd', |
| | | '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35', |
| | | '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
| | | '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721', |
| | | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5', |
| | | '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35', |
| | | '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35', |
| | | '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'], |
| | | |
| | | /** |
| | | * 数字转中文速查表 |
| | | * @Array Of Property |
| | | * @trans ['日','一','二','三','四','五','六','七','八','九','十'] |
| | | * @return Cn string |
| | | */ |
| | | nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'], |
| | | |
| | | /** |
| | | * 日期转农历称呼速查表 |
| | | * @Array Of Property |
| | | * @trans ['初','十','廿','卅'] |
| | | * @return Cn string |
| | | */ |
| | | nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'], |
| | | |
| | | /** |
| | | * 月份转农历称呼速查表 |
| | | * @Array Of Property |
| | | * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊'] |
| | | * @return Cn string |
| | | */ |
| | | nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'], |
| | | |
| | | /** |
| | | * 返回农历y年一整年的总天数 |
| | | * @param lunar Year |
| | | * @return Number |
| | | * @eg:var count = calendar.lYearDays(1987) ;//count=387 |
| | | */ |
| | | lYearDays: function (y) { |
| | | var i; var sum = 348 |
| | | for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 } |
| | | return (sum + this.leapDays(y)) |
| | | }, |
| | | |
| | | /** |
| | | * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 |
| | | * @param lunar Year |
| | | * @return Number (0-12) |
| | | * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6 |
| | | */ |
| | | leapMonth: function (y) { // 闰字编码 \u95f0 |
| | | return (this.lunarInfo[y - 1900] & 0xf) |
| | | }, |
| | | |
| | | /** |
| | | * 返回农历y年闰月的天数 若该年没有闰月则返回0 |
| | | * @param lunar Year |
| | | * @return Number (0、29、30) |
| | | * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29 |
| | | */ |
| | | leapDays: function (y) { |
| | | if (this.leapMonth(y)) { |
| | | return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29) |
| | | } |
| | | return (0) |
| | | }, |
| | | |
| | | /** |
| | | * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 |
| | | * @param lunar Year |
| | | * @return Number (-1、29、30) |
| | | * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29 |
| | | */ |
| | | monthDays: function (y, m) { |
| | | if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1 |
| | | return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29) |
| | | }, |
| | | |
| | | /** |
| | | * 返回公历(!)y年m月的天数 |
| | | * @param solar Year |
| | | * @return Number (-1、28、29、30、31) |
| | | * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30 |
| | | */ |
| | | solarDays: function (y, m) { |
| | | if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 |
| | | var ms = m - 1 |
| | | if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29 |
| | | return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28) |
| | | } else { |
| | | return (this.solarMonth[ms]) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 农历年份转换为干支纪年 |
| | | * @param lYear 农历年的年份数 |
| | | * @return Cn string |
| | | */ |
| | | toGanZhiYear: function (lYear) { |
| | | var ganKey = (lYear - 3) % 10 |
| | | var zhiKey = (lYear - 3) % 12 |
| | | if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干 |
| | | if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支 |
| | | return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1] |
| | | }, |
| | | |
| | | /** |
| | | * 公历月、日判断所属星座 |
| | | * @param cMonth [description] |
| | | * @param cDay [description] |
| | | * @return Cn string |
| | | */ |
| | | toAstro: function (cMonth, cDay) { |
| | | var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf' |
| | | var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22] |
| | | return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座 |
| | | }, |
| | | |
| | | /** |
| | | * 传入offset偏移量返回干支 |
| | | * @param offset 相对甲子的偏移量 |
| | | * @return Cn string |
| | | */ |
| | | toGanZhi: function (offset) { |
| | | return this.Gan[offset % 10] + this.Zhi[offset % 12] |
| | | }, |
| | | |
| | | /** |
| | | * 传入公历(!)y年获得该年第n个节气的公历日期 |
| | | * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 |
| | | * @return day Number |
| | | * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春 |
| | | */ |
| | | getTerm: function (y, n) { |
| | | if (y < 1900 || y > 2100) { return -1 } |
| | | if (n < 1 || n > 24) { return -1 } |
| | | var _table = this.sTermInfo[y - 1900] |
| | | var _info = [ |
| | | parseInt('0x' + _table.substr(0, 5)).toString(), |
| | | parseInt('0x' + _table.substr(5, 5)).toString(), |
| | | parseInt('0x' + _table.substr(10, 5)).toString(), |
| | | parseInt('0x' + _table.substr(15, 5)).toString(), |
| | | parseInt('0x' + _table.substr(20, 5)).toString(), |
| | | parseInt('0x' + _table.substr(25, 5)).toString() |
| | | ] |
| | | var _calday = [ |
| | | _info[0].substr(0, 1), |
| | | _info[0].substr(1, 2), |
| | | _info[0].substr(3, 1), |
| | | _info[0].substr(4, 2), |
| | | |
| | | _info[1].substr(0, 1), |
| | | _info[1].substr(1, 2), |
| | | _info[1].substr(3, 1), |
| | | _info[1].substr(4, 2), |
| | | |
| | | _info[2].substr(0, 1), |
| | | _info[2].substr(1, 2), |
| | | _info[2].substr(3, 1), |
| | | _info[2].substr(4, 2), |
| | | |
| | | _info[3].substr(0, 1), |
| | | _info[3].substr(1, 2), |
| | | _info[3].substr(3, 1), |
| | | _info[3].substr(4, 2), |
| | | |
| | | _info[4].substr(0, 1), |
| | | _info[4].substr(1, 2), |
| | | _info[4].substr(3, 1), |
| | | _info[4].substr(4, 2), |
| | | |
| | | _info[5].substr(0, 1), |
| | | _info[5].substr(1, 2), |
| | | _info[5].substr(3, 1), |
| | | _info[5].substr(4, 2) |
| | | ] |
| | | return parseInt(_calday[n - 1]) |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历数字月份返回汉语通俗表示法 |
| | | * @param lunar month |
| | | * @return Cn string |
| | | * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月' |
| | | */ |
| | | toChinaMonth: function (m) { // 月 => \u6708 |
| | | if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 |
| | | var s = this.nStr3[m - 1] |
| | | s += '\u6708'// 加上月字 |
| | | return s |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历日期数字返回汉字表示法 |
| | | * @param lunar day |
| | | * @return Cn string |
| | | * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一' |
| | | */ |
| | | toChinaDay: function (d) { // 日 => \u65e5 |
| | | var s |
| | | switch (d) { |
| | | case 10: |
| | | s = '\u521d\u5341'; break |
| | | case 20: |
| | | s = '\u4e8c\u5341'; break |
| | | break |
| | | case 30: |
| | | s = '\u4e09\u5341'; break |
| | | break |
| | | default: |
| | | s = this.nStr2[Math.floor(d / 10)] |
| | | s += this.nStr1[d % 10] |
| | | } |
| | | return (s) |
| | | }, |
| | | |
| | | /** |
| | | * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” |
| | | * @param y year |
| | | * @return Cn string |
| | | * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔' |
| | | */ |
| | | getAnimal: function (y) { |
| | | return this.Animals[(y - 4) % 12] |
| | | }, |
| | | |
| | | /** |
| | | * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON |
| | | * @param y solar year |
| | | * @param m solar month |
| | | * @param d solar day |
| | | * @return JSON object |
| | | * @eg:console.log(calendar.solar2lunar(1987,11,01)); |
| | | */ |
| | | solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31 |
| | | // 年份限定、上限 |
| | | if (y < 1900 || y > 2100) { |
| | | return -1// undefined转换为数字变为NaN |
| | | } |
| | | // 公历传参最下限 |
| | | if (y == 1900 && m == 1 && d < 31) { |
| | | return -1 |
| | | } |
| | | // 未传参 获得当天 |
| | | if (!y) { |
| | | var objDate = new Date() |
| | | } else { |
| | | var objDate = new Date(y, parseInt(m) - 1, d) |
| | | } |
| | | var i; var leap = 0; var temp = 0 |
| | | // 修正ymd参数 |
| | | var y = objDate.getFullYear() |
| | | var m = objDate.getMonth() + 1 |
| | | var d = objDate.getDate() |
| | | var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000 |
| | | for (i = 1900; i < 2101 && offset > 0; i++) { |
| | | temp = this.lYearDays(i) |
| | | offset -= temp |
| | | } |
| | | if (offset < 0) { |
| | | offset += temp; i-- |
| | | } |
| | | |
| | | // 是否今天 |
| | | var isTodayObj = new Date() |
| | | var isToday = false |
| | | if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) { |
| | | isToday = true |
| | | } |
| | | // 星期几 |
| | | var nWeek = objDate.getDay() |
| | | var cWeek = this.nStr1[nWeek] |
| | | // 数字表示周几顺应天朝周一开始的惯例 |
| | | if (nWeek == 0) { |
| | | nWeek = 7 |
| | | } |
| | | // 农历年 |
| | | var year = i |
| | | var leap = this.leapMonth(i) // 闰哪个月 |
| | | var isLeap = false |
| | | |
| | | // 效验闰月 |
| | | for (i = 1; i < 13 && offset > 0; i++) { |
| | | // 闰月 |
| | | if (leap > 0 && i == (leap + 1) && isLeap == false) { |
| | | --i |
| | | isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数 |
| | | } else { |
| | | temp = this.monthDays(year, i)// 计算农历普通月天数 |
| | | } |
| | | // 解除闰月 |
| | | if (isLeap == true && i == (leap + 1)) { isLeap = false } |
| | | offset -= temp |
| | | } |
| | | // 闰月导致数组下标重叠取反 |
| | | if (offset == 0 && leap > 0 && i == leap + 1) { |
| | | if (isLeap) { |
| | | isLeap = false |
| | | } else { |
| | | isLeap = true; --i |
| | | } |
| | | } |
| | | if (offset < 0) { |
| | | offset += temp; --i |
| | | } |
| | | // 农历月 |
| | | var month = i |
| | | // 农历日 |
| | | var day = offset + 1 |
| | | // 天干地支处理 |
| | | var sm = m - 1 |
| | | var gzY = this.toGanZhiYear(year) |
| | | |
| | | // 当月的两个节气 |
| | | // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year` |
| | | var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始 |
| | | var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始 |
| | | |
| | | // 依据12节气修正干支月 |
| | | var gzM = this.toGanZhi((y - 1900) * 12 + m + 11) |
| | | if (d >= firstNode) { |
| | | gzM = this.toGanZhi((y - 1900) * 12 + m + 12) |
| | | } |
| | | |
| | | // 传入的日期的节气与否 |
| | | var isTerm = false |
| | | var Term = null |
| | | if (firstNode == d) { |
| | | isTerm = true |
| | | Term = this.solarTerm[m * 2 - 2] |
| | | } |
| | | if (secondNode == d) { |
| | | isTerm = true |
| | | Term = this.solarTerm[m * 2 - 1] |
| | | } |
| | | // 日柱 当月一日与 1900/1/1 相差天数 |
| | | var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10 |
| | | var gzD = this.toGanZhi(dayCyclical + d - 1) |
| | | // 该日期所属的星座 |
| | | var astro = this.toAstro(m, d) |
| | | |
| | | return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro } |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON |
| | | * @param y lunar year |
| | | * @param m lunar month |
| | | * @param d lunar day |
| | | * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] |
| | | * @return JSON object |
| | | * @eg:console.log(calendar.lunar2solar(1987,9,10)); |
| | | */ |
| | | lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1 |
| | | var isLeapMonth = !!isLeapMonth |
| | | var leapOffset = 0 |
| | | var leapMonth = this.leapMonth(y) |
| | | var leapDay = this.leapDays(y) |
| | | if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 |
| | | if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值 |
| | | var day = this.monthDays(y, m) |
| | | var _day = day |
| | | // bugFix 2016-9-25 |
| | | // if month is leap, _day use leapDays method |
| | | if (isLeapMonth) { |
| | | _day = this.leapDays(y, m) |
| | | } |
| | | if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验 |
| | | |
| | | // 计算农历的时间差 |
| | | var offset = 0 |
| | | for (var i = 1900; i < y; i++) { |
| | | offset += this.lYearDays(i) |
| | | } |
| | | var leap = 0; var isAdd = false |
| | | for (var i = 1; i < m; i++) { |
| | | leap = this.leapMonth(y) |
| | | if (!isAdd) { // 处理闰月 |
| | | if (leap <= i && leap > 0) { |
| | | offset += this.leapDays(y); isAdd = true |
| | | } |
| | | } |
| | | offset += this.monthDays(y, i) |
| | | } |
| | | // 转换闰月农历 需补充该年闰月的前一个月的时差 |
| | | if (isLeapMonth) { offset += day } |
| | | // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) |
| | | var stmap = Date.UTC(1900, 1, 30, 0, 0, 0) |
| | | var calObj = new Date((offset + d - 31) * 86400000 + stmap) |
| | | var cY = calObj.getUTCFullYear() |
| | | var cM = calObj.getUTCMonth() + 1 |
| | | var cD = calObj.getUTCDate() |
| | | |
| | | return this.solar2lunar(cY, cM, cD) |
| | | } |
| | | } |
| | | |
| | | export default calendar |
| New file |
| | |
| | | <template> |
| | | <view class="uv-calendar-header uv-border-bottom"> |
| | | <text |
| | | class="uv-calendar-header__title" |
| | | v-if="showTitle" |
| | | >{{ title }}</text> |
| | | <text |
| | | class="uv-calendar-header__subtitle" |
| | | v-if="showSubtitle" |
| | | >{{ subtitle }}</text> |
| | | <view class="uv-calendar-header__weekdays"> |
| | | <text class="uv-calendar-header__weekdays__weekday">一</text> |
| | | <text class="uv-calendar-header__weekdays__weekday">二</text> |
| | | <text class="uv-calendar-header__weekdays__weekday">三</text> |
| | | <text class="uv-calendar-header__weekdays__weekday">四</text> |
| | | <text class="uv-calendar-header__weekdays__weekday">五</text> |
| | | <text class="uv-calendar-header__weekdays__weekday">六</text> |
| | | <text class="uv-calendar-header__weekdays__weekday">日</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | export default { |
| | | name: 'uv-calendar-header', |
| | | mixins: [mpMixin, mixin], |
| | | props: { |
| | | // 标题 |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 副标题 |
| | | subtitle: { |
| | | type: [String,null], |
| | | default: '' |
| | | }, |
| | | // 是否显示标题 |
| | | showTitle: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否显示副标题 |
| | | showSubtitle: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | |
| | | } |
| | | }, |
| | | methods: { |
| | | name() { |
| | | |
| | | } |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-border: 1; |
| | | $show-border-bottom: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | .uv-calendar-header { |
| | | padding-bottom: 4px; |
| | | |
| | | &__title { |
| | | font-size: 16px; |
| | | color: $uv-main-color; |
| | | text-align: center; |
| | | height: 42px; |
| | | line-height: 42px; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | &__subtitle { |
| | | font-size: 14px; |
| | | color: $uv-main-color; |
| | | height: 40px; |
| | | text-align: center; |
| | | line-height: 40px; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | &__weekdays { |
| | | @include flex; |
| | | justify-content: space-between; |
| | | |
| | | &__weekday { |
| | | font-size: 13px; |
| | | color: $uv-main-color; |
| | | line-height: 30px; |
| | | flex: 1; |
| | | text-align: center; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <view class="uv-calendar-month-wrapper" ref="uv-calendar-month-wrapper"> |
| | | <view v-for="(item, index) in months" :key="index" :class="[`uv-calendar-month-${index}`]" |
| | | :ref="`uv-calendar-month-${index}`" :id="`month-${index}`"> |
| | | <text v-if="index !== 0" class="uv-calendar-month__title">{{ item.year }}年{{ item.month }}月</text> |
| | | <view class="uv-calendar-month__days"> |
| | | <view v-if="showMark" class="uv-calendar-month__days__month-mark-wrapper"> |
| | | <text class="uv-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text> |
| | | </view> |
| | | <view class="uv-calendar-month__days__day" v-for="(item1, index1) in item.date" :key="index1" |
| | | :style="[dayStyle(index, index1, item1)]" @tap="clickHandler(index, index1, item1)" |
| | | :class="[item1.selected && 'uv-calendar-month__days__day__select--selected']"> |
| | | <view class="uv-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]"> |
| | | <text v-if="getTopInfo(index, index1, item1)" |
| | | class="uv-calendar-month__days__day__select__top-info" |
| | | :class="[item1.disabled && 'uv-calendar-month__days__day__select__top-info--disabled']" |
| | | :style="[textStyle(item1)]" |
| | | >{{ getTopInfo(index, index1, item1) }}</text> |
| | | <text class="uv-calendar-month__days__day__select__info" |
| | | :class="[item1.disabled && 'uv-calendar-month__days__day__select__info--disabled']" |
| | | :style="[textStyle(item1)]">{{ item1.day }}</text> |
| | | <text v-if="getBottomInfo(index, index1, item1)" |
| | | class="uv-calendar-month__days__day__select__buttom-info" |
| | | :class="[item1.disabled && 'uv-calendar-month__days__day__select__buttom-info--disabled']" |
| | | :style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text> |
| | | <text v-if="item1.dot" class="uv-calendar-month__days__day__select__dot"></text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | // #ifdef APP-NVUE |
| | | // 由于nvue不支持百分比单位,需要查询宽度来计算每个日期的宽度 |
| | | const dom = uni.requireNativePlugin('dom') |
| | | // #endif |
| | | import { colorGradient } from '@/uni_modules/uv-ui-tools/libs/function/colorGradient.js'; |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import dayjs from '@/uni_modules/uv-ui-tools/libs/util/dayjs.js' |
| | | export default { |
| | | name: 'uv-calendar-month', |
| | | emits:['monthSelected','updateMonthTop','change'], |
| | | mixins: [mpMixin, mixin], |
| | | props: { |
| | | // 是否显示月份背景色 |
| | | showMark: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 主题色,对底部按钮和选中日期有效 |
| | | color: { |
| | | type: String, |
| | | default: '#3c9cff' |
| | | }, |
| | | // 月份数据 |
| | | months: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | // 日期选择类型 |
| | | mode: { |
| | | type: String, |
| | | default: 'single' |
| | | }, |
| | | // 日期行高 |
| | | rowHeight: { |
| | | type: [String, Number], |
| | | default: 58 |
| | | }, |
| | | // mode=multiple时,最多可选多少个日期 |
| | | maxCount: { |
| | | type: [String, Number], |
| | | default: Infinity |
| | | }, |
| | | // mode=range时,第一个日期底部的提示文字 |
| | | startText: { |
| | | type: String, |
| | | default: '开始' |
| | | }, |
| | | // mode=range时,最后一个日期底部的提示文字 |
| | | endText: { |
| | | type: String, |
| | | default: '结束' |
| | | }, |
| | | // 默认选中的日期,mode为multiple或range是必须为数组格式 |
| | | defaultDate: { |
| | | type: [Array, String, Date], |
| | | default: null |
| | | }, |
| | | // 最小的可选日期 |
| | | minDate: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 最大可选日期 |
| | | maxDate: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 如果没有设置maxDate,则往后推多少个月 |
| | | maxMonth: { |
| | | type: [String, Number], |
| | | default: 2 |
| | | }, |
| | | // 是否为只读状态,只读状态下禁止选择日期 |
| | | readonly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 日期区间最多可选天数,默认无限制,mode = range时有效 |
| | | maxRange: { |
| | | type: [Number, String], |
| | | default: Infinity |
| | | }, |
| | | // 范围选择超过最多可选天数时的提示文案,mode = range时有效 |
| | | rangePrompt: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 |
| | | showRangePrompt: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否允许日期范围的起止时间为同一天,mode = range时有效 |
| | | allowSameDay: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | // 每个日期的宽度 |
| | | width: 0, |
| | | // 当前选中的日期item |
| | | item: {}, |
| | | selected: [] |
| | | } |
| | | }, |
| | | watch: { |
| | | selectedChange: { |
| | | immediate: true, |
| | | handler(n) { |
| | | this.setDefaultDate() |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | // 多个条件的变化,会引起选中日期的变化,这里统一管理监听 |
| | | selectedChange() { |
| | | return [this.minDate, this.maxDate, this.defaultDate] |
| | | }, |
| | | dayStyle(index1, index2, item) { |
| | | return (index1, index2, item) => { |
| | | const style = {} |
| | | let week = item.week |
| | | // 不进行四舍五入的形式保留2位小数 |
| | | const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1)) |
| | | // 得出每个日期的宽度 |
| | | // #ifdef APP-NVUE |
| | | style.width = this.$uv.addUnit(dayWidth) |
| | | // #endif |
| | | style.height = this.$uv.addUnit(this.rowHeight) |
| | | if (index2 === 0) { |
| | | // 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数 |
| | | week = (week === 0 ? 7 : week) - 1 |
| | | style.marginLeft = this.$uv.addUnit(week * dayWidth) |
| | | } |
| | | if (this.mode === 'range') { |
| | | // 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug |
| | | style.paddingLeft = 0 |
| | | style.paddingRight = 0 |
| | | style.paddingBottom = 0 |
| | | style.paddingTop = 0 |
| | | } |
| | | return style |
| | | } |
| | | }, |
| | | daySelectStyle() { |
| | | return (index1, index2, item) => { |
| | | let date = dayjs(item.date).format("YYYY-MM-DD"), |
| | | style = {} |
| | | // 判断date是否在selected数组中,因为月份可能会需要补0,所以使用dateSame判断,而不用数组的includes判断 |
| | | if (this.selected.some(item => this.dateSame(item, date))) { |
| | | style.backgroundColor = this.color |
| | | } |
| | | if (this.mode === 'single') { |
| | | if (date === this.selected[0]) { |
| | | // 因为需要对nvue的兼容,只能这么写,无法缩写,也无法通过类名控制等等 |
| | | style.borderTopLeftRadius = '3px' |
| | | style.borderBottomLeftRadius = '3px' |
| | | style.borderTopRightRadius = '3px' |
| | | style.borderBottomRightRadius = '3px' |
| | | } |
| | | } else if (this.mode === 'range') { |
| | | if (this.selected.length >= 2) { |
| | | const len = this.selected.length - 1 |
| | | // 第一个日期设置左上角和左下角的圆角 |
| | | if (this.dateSame(date, this.selected[0])) { |
| | | style.borderTopLeftRadius = '3px' |
| | | style.borderBottomLeftRadius = '3px' |
| | | } |
| | | // 最后一个日期设置右上角和右下角的圆角 |
| | | if (this.dateSame(date, this.selected[len])) { |
| | | style.borderTopRightRadius = '3px' |
| | | style.borderBottomRightRadius = '3px' |
| | | } |
| | | // 处于第一和最后一个之间的日期,背景色设置为浅色,通过将对应颜色进行等分,再取其尾部的颜色值 |
| | | if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this |
| | | .selected[len]))) { |
| | | style.backgroundColor = colorGradient(this.color, '#ffffff', 100)[90] |
| | | // 增加一个透明度,让范围区间的背景色也能看到底部的mark水印字符 |
| | | style.opacity = 0.7 |
| | | } |
| | | } else if (this.selected.length === 1) { |
| | | // 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug |
| | | // 进行还原操作,否则在nvue的iOS,uni-app有bug,会导致诡异的表现 |
| | | style.borderTopLeftRadius = '3px' |
| | | style.borderBottomLeftRadius = '3px' |
| | | } |
| | | } else { |
| | | if (this.selected.some(item => this.dateSame(item, date))) { |
| | | style.borderTopLeftRadius = '3px' |
| | | style.borderBottomLeftRadius = '3px' |
| | | style.borderTopRightRadius = '3px' |
| | | style.borderBottomRightRadius = '3px' |
| | | } |
| | | } |
| | | return style |
| | | } |
| | | }, |
| | | // 某个日期是否被选中 |
| | | textStyle() { |
| | | return (item) => { |
| | | const date = dayjs(item.date).format("YYYY-MM-DD"), |
| | | style = {} |
| | | // 选中的日期,提示文字设置白色 |
| | | if (this.selected.some(item => this.dateSame(item, date))) { |
| | | style.color = '#ffffff' |
| | | } |
| | | if (this.mode === 'range') { |
| | | const len = this.selected.length - 1 |
| | | // 如果是范围选择模式,第一个和最后一个之间的日期,文字颜色设置为高亮的主题色 |
| | | if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this |
| | | .selected[len]))) { |
| | | style.color = this.color |
| | | } |
| | | } |
| | | return style |
| | | } |
| | | }, |
| | | // 获取顶部的提示文字 |
| | | getTopInfo() { |
| | | return (index1, index2, item) => { |
| | | return item.topInfo; |
| | | } |
| | | }, |
| | | // 获取底部的提示文字 |
| | | getBottomInfo() { |
| | | return (index1, index2, item) => { |
| | | const date = dayjs(item.date).format("YYYY-MM-DD") |
| | | const bottomInfo = item.bottomInfo |
| | | // 当为日期范围模式时,且选择的日期个数大于0时 |
| | | if (this.mode === 'range' && this.selected.length > 0) { |
| | | if (this.selected.length === 1) { |
| | | // 选择了一个日期时,如果当前日期为数组中的第一个日期,则显示底部文字为“开始” |
| | | if (this.dateSame(date, this.selected[0])) return this.startText |
| | | else return bottomInfo |
| | | } else { |
| | | const len = this.selected.length - 1 |
| | | // 如果数组中的日期大于2个时,第一个和最后一个显示为开始和结束日期 |
| | | if (this.dateSame(date, this.selected[0]) && this.dateSame(date, this.selected[1]) && |
| | | len === 1) { |
| | | // 如果长度为2,且第一个等于第二个日期,则提示语放在同一个item中 |
| | | return `${this.startText}/${this.endText}` |
| | | } else if (this.dateSame(date, this.selected[0])) { |
| | | return this.startText |
| | | } else if (this.dateSame(date, this.selected[len])) { |
| | | return this.endText |
| | | } else { |
| | | return bottomInfo |
| | | } |
| | | } |
| | | } else { |
| | | return bottomInfo |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 初始化默认选中 |
| | | this.$emit('monthSelected', this.selected) |
| | | this.$nextTick(() => { |
| | | // 这里需要另一个延时,因为获取宽度后,会进行月份数据渲染,只有渲染完成之后,才有真正的高度 |
| | | // 因为nvue下,$nextTick并不是100%可靠的 |
| | | this.$uv.sleep(10).then(() => { |
| | | this.getWrapperWidth() |
| | | this.getMonthRect() |
| | | }) |
| | | }) |
| | | }, |
| | | // 判断两个日期是否相等 |
| | | dateSame(date1, date2) { |
| | | return dayjs(date1).isSame(dayjs(date2)) |
| | | }, |
| | | // 获取月份数据区域的宽度,因为nvue不支持百分比,所以无法通过css设置每个日期item的宽度 |
| | | getWrapperWidth() { |
| | | // #ifdef APP-NVUE |
| | | dom.getComponentRect(this.$refs['uv-calendar-month-wrapper'], res => { |
| | | this.width = res.size.width |
| | | }) |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | this.$uvGetRect('.uv-calendar-month-wrapper').then(size => { |
| | | this.width = size.width |
| | | }) |
| | | // #endif |
| | | }, |
| | | getMonthRect() { |
| | | // 获取每个月份数据的尺寸,用于父组件在scroll-view滚动事件中,监听当前滚动到了第几个月份 |
| | | const promiseAllArr = this.months.map((item, index) => this.getMonthRectByPromise( |
| | | `uv-calendar-month-${index}`)) |
| | | // 一次性返回 |
| | | Promise.all(promiseAllArr).then( |
| | | sizes => { |
| | | let height = 1 |
| | | const topArr = [] |
| | | for (let i = 0; i < this.months.length; i++) { |
| | | // 添加到months数组中,供scroll-view滚动事件中,判断当前滚动到哪个月份 |
| | | topArr[i] = height |
| | | height += sizes[i].height |
| | | } |
| | | // 由于微信下,无法通过this.months[i].top的形式(引用类型)去修改父组件的month的top值,所以使用事件形式对外发出 |
| | | this.$emit('updateMonthTop', topArr) |
| | | }) |
| | | }, |
| | | // 获取每个月份区域的尺寸 |
| | | getMonthRectByPromise(el) { |
| | | // #ifndef APP-NVUE |
| | | // $uvGetRect为uvui自带的节点查询简化方法,详见文档介绍:https://www.uvui.cn/js/getRect.html |
| | | // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同 |
| | | return new Promise(resolve => { |
| | | this.$uvGetRect(`.${el}`).then(size => { |
| | | resolve(size) |
| | | }) |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifdef APP-NVUE |
| | | // nvue下,使用dom模块查询元素高度 |
| | | // 返回一个promise,让调用此方法的主体能使用then回调 |
| | | return new Promise(resolve => { |
| | | dom.getComponentRect(this.$refs[el][0], res => { |
| | | resolve(res.size) |
| | | }) |
| | | }) |
| | | // #endif |
| | | }, |
| | | // 点击某一个日期 |
| | | clickHandler(index1, index2, item) { |
| | | if (this.readonly) { |
| | | return; |
| | | } |
| | | this.item = item |
| | | const date = dayjs(item.date).format("YYYY-MM-DD") |
| | | if (item.disabled) return |
| | | // 对上一次选择的日期数组进行深度克隆 |
| | | let selected = this.$uv.deepClone(this.selected) |
| | | if (this.mode === 'single') { |
| | | // 单选情况下,让数组中的元素为当前点击的日期 |
| | | selected = [date] |
| | | } else if (this.mode === 'multiple') { |
| | | if (selected.some(item => this.dateSame(item, date))) { |
| | | // 如果点击的日期已在数组中,则进行移除操作,也就是达到反选的效果 |
| | | const itemIndex = selected.findIndex(item => dayjs(item).format("YYYY-MM-DD") === dayjs(date).format("YYYY-MM-DD")) |
| | | selected.splice(itemIndex, 1) |
| | | } else { |
| | | // 如果点击的日期不在数组中,且已有的长度小于总可选长度时,则添加到数组中去 |
| | | if (selected.length < this.maxCount) selected.push(date) |
| | | } |
| | | } else { |
| | | // 选择区间形式 |
| | | if (selected.length === 0 || selected.length >= 2) { |
| | | // 如果原来就为0或者大于2的长度,则当前点击的日期,就是开始日期 |
| | | selected = [date] |
| | | } else if (selected.length === 1) { |
| | | // 如果已经选择了开始日期 |
| | | const existsDate = selected[0] |
| | | // 如果当前选择的日期小于上一次选择的日期,则当前的日期定为开始日期 |
| | | if (dayjs(date).isBefore(existsDate)) { |
| | | selected = [date] |
| | | } else if (dayjs(date).isAfter(existsDate)) { |
| | | // 当前日期减去最大可选的日期天数,如果大于起始时间,则进行提示 |
| | | if(dayjs(dayjs(date).subtract(this.maxRange, 'day')).isAfter(dayjs(selected[0])) && this.showRangePrompt) { |
| | | if(this.rangePrompt) { |
| | | this.$uv.toast(this.rangePrompt) |
| | | } else { |
| | | this.$uv.toast(`选择天数不能超过 ${this.maxRange} 天`) |
| | | } |
| | | return |
| | | } |
| | | // 如果当前日期大于已有日期,将当前的添加到数组尾部 |
| | | selected.push(date) |
| | | const startDate = selected[0] |
| | | const endDate = selected[1] |
| | | const arr = [] |
| | | let i = 0 |
| | | do { |
| | | // 将开始和结束日期之间的日期添加到数组中 |
| | | arr.push(dayjs(startDate).add(i, 'day').format("YYYY-MM-DD")) |
| | | i++ |
| | | // 累加的日期小于结束日期时,继续下一次的循环 |
| | | } while (dayjs(startDate).add(i, 'day').isBefore(dayjs(endDate))) |
| | | // 为了一次性修改数组,避免computed中多次触发,这里才用arr变量一次性赋值的方式,同时将最后一个日期添加近来 |
| | | arr.push(endDate) |
| | | selected = arr |
| | | } else { |
| | | // 选择区间时,只有一个日期的情况下,且不允许选择起止为同一天的话,不允许选择自己 |
| | | if (selected[0] === date && !this.allowSameDay) return |
| | | selected.push(date) |
| | | } |
| | | } |
| | | } |
| | | this.setSelected(selected) |
| | | this.$emit('change',{ |
| | | day: date, |
| | | selected: selected |
| | | }); |
| | | }, |
| | | // 设置默认日期 |
| | | setDefaultDate() { |
| | | if (!this.defaultDate) { |
| | | // 如果没有设置默认日期,则将当天日期设置为默认选中的日期 |
| | | const selected = [dayjs().format("YYYY-MM-DD")] |
| | | return this.setSelected(selected, false) |
| | | } |
| | | let defaultDate = [] |
| | | const minDate = this.minDate || dayjs().format("YYYY-MM-DD") |
| | | const maxDate = this.maxDate || dayjs(minDate).add(this.maxMonth - 1, 'month').format("YYYY-MM-DD") |
| | | if (this.mode === 'single') { |
| | | // 单选模式,可以是字符串或数组,Date对象等 |
| | | if (!this.$uv.test.array(this.defaultDate)) { |
| | | defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")] |
| | | } else { |
| | | defaultDate = [this.defaultDate[0]] |
| | | } |
| | | } else { |
| | | // 如果为非数组,则不执行 |
| | | if (!this.$uv.test.array(this.defaultDate)) return |
| | | defaultDate = this.defaultDate |
| | | } |
| | | // 过滤用户传递的默认数组,取出只在可允许最大值与最小值之间的元素 |
| | | defaultDate = defaultDate.filter(item => { |
| | | return dayjs(item).isAfter(dayjs(minDate).subtract(1, 'day')) && dayjs(item).isBefore(dayjs( |
| | | maxDate).add(1, 'day')) |
| | | }) |
| | | this.setSelected(defaultDate, false) |
| | | }, |
| | | setSelected(selected, event = true) { |
| | | this.selected = selected |
| | | event && this.$emit('monthSelected', this.selected) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | .uv-calendar-month-wrapper { |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .uv-calendar-month { |
| | | |
| | | &__title { |
| | | font-size: 14px; |
| | | line-height: 42px; |
| | | height: 42px; |
| | | color: $uv-main-color; |
| | | text-align: center; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | &__days { |
| | | position: relative; |
| | | @include flex; |
| | | flex-wrap: wrap; |
| | | |
| | | &__month-mark-wrapper { |
| | | position: absolute; |
| | | top: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | |
| | | &__text { |
| | | font-size: 155px; |
| | | color: rgba(231, 232, 234, 0.83); |
| | | } |
| | | } |
| | | |
| | | &__day { |
| | | @include flex; |
| | | padding: 2px; |
| | | /* #ifndef APP-NVUE */ |
| | | // vue下使用css进行宽度计算,因为某些安卓机会无法进行js获取父元素宽度进行计算得出,会有偏移 |
| | | width: calc(100% / 7); |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | |
| | | &__select { |
| | | flex: 1; |
| | | @include flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: relative; |
| | | |
| | | &__dot { |
| | | width: 7px; |
| | | height: 7px; |
| | | border-radius: 100px; |
| | | background-color: $uv-error; |
| | | position: absolute; |
| | | top: 12px; |
| | | right: 7px; |
| | | } |
| | | |
| | | &__top-info { |
| | | color: $uv-content-color; |
| | | text-align: center; |
| | | position: absolute; |
| | | top: 2px; |
| | | font-size: 10px; |
| | | text-align: center; |
| | | left: 0; |
| | | right: 0; |
| | | &--selected { |
| | | color: #ffffff; |
| | | } |
| | | |
| | | &--disabled { |
| | | color: #cacbcd; |
| | | } |
| | | } |
| | | |
| | | &__buttom-info { |
| | | color: $uv-content-color; |
| | | text-align: center; |
| | | position: absolute; |
| | | bottom: 5px; |
| | | font-size: 10px; |
| | | text-align: center; |
| | | left: 0; |
| | | right: 0; |
| | | |
| | | &--selected { |
| | | color: #ffffff; |
| | | } |
| | | |
| | | &--disabled { |
| | | color: #cacbcd; |
| | | } |
| | | } |
| | | |
| | | &__info { |
| | | text-align: center; |
| | | font-size: 16px; |
| | | |
| | | &--selected { |
| | | color: #ffffff; |
| | | } |
| | | |
| | | &--disabled { |
| | | color: #cacbcd; |
| | | } |
| | | } |
| | | |
| | | &--selected { |
| | | background-color: $uv-primary; |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | flex: 1; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | &--range-selected { |
| | | opacity: 0.3; |
| | | border-radius: 0; |
| | | } |
| | | |
| | | &--range-start-selected { |
| | | border-top-right-radius: 0; |
| | | border-bottom-right-radius: 0; |
| | | } |
| | | |
| | | &--range-end-selected { |
| | | border-top-left-radius: 0; |
| | | border-bottom-left-radius: 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 日历顶部标题 |
| | | title: { |
| | | type: String, |
| | | default: '日期选择' |
| | | }, |
| | | // 是否显示标题 |
| | | showTitle: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否显示副标题 |
| | | showSubtitle: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 日期类型选择,single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围 |
| | | mode: { |
| | | type: String, |
| | | default: 'single' |
| | | }, |
| | | // mode=range时,第一个日期底部的提示文字 |
| | | startText: { |
| | | type: String, |
| | | default: '开始' |
| | | }, |
| | | // mode=range时,最后一个日期底部的提示文字 |
| | | endText: { |
| | | type: String, |
| | | default: '结束' |
| | | }, |
| | | // 自定义列表 |
| | | customList: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | // 主题色,对底部按钮和选中日期有效 |
| | | color: { |
| | | type: String, |
| | | default: '#3c9cff' |
| | | }, |
| | | // 最小的可选日期 |
| | | minDate: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 最大可选日期 |
| | | maxDate: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 默认选中的日期,mode为multiple或range是必须为数组格式 |
| | | defaultDate: { |
| | | type: [Array, String, Date, null], |
| | | default: null |
| | | }, |
| | | // mode=multiple时,最多可选多少个日期 |
| | | maxCount: { |
| | | type: [String, Number], |
| | | default: Number.MAX_SAFE_INTEGER |
| | | }, |
| | | // 日期行高 |
| | | rowHeight: { |
| | | type: [String, Number], |
| | | default: 56 |
| | | }, |
| | | // 日期格式化函数 |
| | | formatter: { |
| | | type: [Function, null], |
| | | default: null |
| | | }, |
| | | // 是否显示农历 |
| | | showLunar: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示月份背景色 |
| | | showMark: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 确定按钮的文字 |
| | | confirmText: { |
| | | type: String, |
| | | default: '确定' |
| | | }, |
| | | // 确认按钮处于禁用状态时的文字 |
| | | confirmDisabledText: { |
| | | type: String, |
| | | default: '确定' |
| | | }, |
| | | // 是否允许点击遮罩关闭日历 |
| | | closeOnClickOverlay: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否允许点击确认按钮关闭日历 |
| | | closeOnClickConfirm: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否为只读状态,只读状态下禁止选择日期 |
| | | readonly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否展示确认按钮 |
| | | showConfirm: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 日期区间最多可选天数,默认无限制,mode = range时有效 Infinity |
| | | maxRange: { |
| | | type: [Number, String], |
| | | default: Number.MAX_SAFE_INTEGER |
| | | }, |
| | | // 范围选择超过最多可选天数时的提示文案,mode = range时有效 |
| | | rangePrompt: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 |
| | | showRangePrompt: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否允许日期范围的起止时间为同一天,mode = range时有效 |
| | | allowSameDay: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 圆角值 |
| | | round: { |
| | | type: [Boolean, String, Number], |
| | | default: 0 |
| | | }, |
| | | // 最多展示月份数量 |
| | | monthNum: { |
| | | type: [Number, String], |
| | | default: 3 |
| | | }, |
| | | ...uni.$uv?.props?.calendar |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <uv-popup |
| | | ref="calendarPopup" |
| | | mode="bottom" |
| | | closeable |
| | | :round="round" |
| | | :closeOnClickOverlay="closeOnClickOverlay" |
| | | @change="popupChange" |
| | | > |
| | | <view class="uv-calendar"> |
| | | <uvHeader |
| | | :title="title" |
| | | :subtitle="subtitle" |
| | | :showSubtitle="showSubtitle" |
| | | :showTitle="showTitle" |
| | | ></uvHeader> |
| | | <scroll-view |
| | | :style="{ height: $uv.addUnit(listHeight) }" |
| | | scroll-y |
| | | @scroll="onScroll" |
| | | :scroll-top="scrollTop" |
| | | :scrollIntoView="scrollIntoView" |
| | | > |
| | | <uvMonth |
| | | :color="color" |
| | | :rowHeight="rowHeight" |
| | | :showMark="showMark" |
| | | :months="months" |
| | | :mode="mode" |
| | | :maxCount="maxCount" |
| | | :startText="startText" |
| | | :endText="endText" |
| | | :defaultDate="defaultDate" |
| | | :minDate="innerMinDate" |
| | | :maxDate="innerMaxDate" |
| | | :maxMonth="monthNum" |
| | | :readonly="readonly" |
| | | :maxRange="maxRange" |
| | | :rangePrompt="rangePrompt" |
| | | :showRangePrompt="showRangePrompt" |
| | | :allowSameDay="allowSameDay" |
| | | ref="month" |
| | | @monthSelected="monthSelected" |
| | | @updateMonthTop="updateMonthTop" |
| | | @change="changeDay" |
| | | ></uvMonth> |
| | | </scroll-view> |
| | | <slot name="footer" v-if="showConfirm"> |
| | | <view class="uv-calendar__confirm"> |
| | | <uv-button |
| | | shape="circle" |
| | | :text="buttonDisabled ? confirmDisabledText : confirmText" |
| | | :color="color" |
| | | @click="confirm" |
| | | :disabled="buttonDisabled" |
| | | ></uv-button> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | </uv-popup> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import uvHeader from './header.vue' |
| | | import uvMonth from './month.vue' |
| | | import props from './props.js' |
| | | import dayjs from '@/uni_modules/uv-ui-tools/libs/util/dayjs.js' |
| | | import Calendar from './calendar.js' |
| | | /** |
| | | * Calendar 日历 |
| | | * @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中. |
| | | * @tutorial https://www.uvui.cn/components/calendar.html |
| | | * |
| | | * @property {String} title 标题内容 (默认 日期选择 ) |
| | | * @property {Boolean} showTitle 是否显示标题 (默认 true ) |
| | | * @property {Boolean} showSubtitle 是否显示副标题 (默认 true ) |
| | | * @property {String} mode 日期类型选择 single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围 ( 默认 'single' ) |
| | | * @property {String} startText mode=range时,第一个日期底部的提示文字 (默认 '开始' ) |
| | | * @property {String} endText mode=range时,最后一个日期底部的提示文字 (默认 '结束' ) |
| | | * @property {Array} customList 自定义列表 |
| | | * @property {String} color 主题色,对底部按钮和选中日期有效 (默认 ‘#3c9cff' ) |
| | | * @property {String | Number} minDate 最小的可选日期 (默认 0 ) |
| | | * @property {String | Number} maxDate 最大可选日期 (默认 0 ) |
| | | * @property {Array | String| Date} defaultDate 默认选中的日期,mode为multiple或range是必须为数组格式 |
| | | * @property {String | Number} maxCount mode=multiple时,最多可选多少个日期 (默认 Number.MAX_SAFE_INTEGER ) |
| | | * @property {String | Number} rowHeight 日期行高 (默认 56 ) |
| | | * @property {Function} formatter 日期格式化函数 |
| | | * @property {Boolean} showLunar 是否显示农历 (默认 false ) |
| | | * @property {Boolean} showMark 是否显示月份背景色 (默认 true ) |
| | | * @property {String} confirmText 确定按钮的文字 (默认 '确定' ) |
| | | * @property {String} confirmDisabledText 确认按钮处于禁用状态时的文字 (默认 '确定' ) |
| | | * @property {Boolean} show 是否显示日历弹窗 (默认 false ) |
| | | * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭日历 (默认 false ) |
| | | * @property {Boolean} closeOnClickConfirm 是否允许点击确认按钮关闭日历,设置为false不影响confirm事件返回 (默认 true ) |
| | | * @property {Boolean} readonly 是否为只读状态,只读状态下禁止选择日期 (默认 false ) |
| | | * @property {String | Number} maxRange 日期区间最多可选天数,默认无限制,mode = range时有效 |
| | | * @property {String} rangePrompt 范围选择超过最多可选天数时的提示文案,mode = range时有效 |
| | | * @property {Boolean} showRangePrompt 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 (默认 true ) |
| | | * @property {Boolean} allowSameDay 是否允许日期范围的起止时间为同一天,mode = range时有效 (默认 false ) |
| | | * @property {Number|String} round 圆角值,默认无圆角 (默认 0 ) |
| | | * @property {Number|String} monthNum 最多展示的月份数量 (默认 3 ) |
| | | * |
| | | * @event {Function()} confirm 点击确定按钮时触发 选择日期相关的返回参数 |
| | | * @event {Function()} close 日历关闭时触发 可定义页面关闭时的回调事件 |
| | | * @example <uv-calendar ref="calendar" :defaultDate="defaultDateMultiple" mode="multiple" @confirm="confirm"> |
| | | </uv-calendar> |
| | | * */ |
| | | export default { |
| | | name: 'uv-calendar', |
| | | emits:['confirm','close','change'], |
| | | mixins: [mpMixin, mixin, props], |
| | | components: { |
| | | uvHeader, |
| | | uvMonth |
| | | }, |
| | | data() { |
| | | return { |
| | | // 需要显示的月份的数组 |
| | | months: [], |
| | | // 在月份滚动区域中,当前视图中月份的index索引 |
| | | monthIndex: 0, |
| | | // 月份滚动区域的高度 |
| | | listHeight: 0, |
| | | // month组件中选择的日期数组 |
| | | selected: [], |
| | | scrollIntoView: '', |
| | | scrollTop:0, |
| | | // 过滤处理方法 |
| | | innerFormatter: (value) => value |
| | | } |
| | | }, |
| | | watch: { |
| | | selectedChange: { |
| | | immediate: true, |
| | | handler(n) { |
| | | this.setMonth() |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | // 由于maxDate和minDate可以为字符串(2021-10-10),或者数值(时间戳),但是dayjs如果接受字符串形式的时间戳会有问题,这里进行处理 |
| | | innerMaxDate() { |
| | | return this.$uv.test.number(this.maxDate) |
| | | ? Number(this.maxDate) |
| | | : this.maxDate |
| | | }, |
| | | innerMinDate() { |
| | | return this.$uv.test.number(this.minDate) |
| | | ? Number(this.minDate) |
| | | : this.minDate |
| | | }, |
| | | // 多个条件的变化,会引起选中日期的变化,这里统一管理监听 |
| | | selectedChange() { |
| | | return [this.innerMinDate, this.innerMaxDate, this.defaultDate] |
| | | }, |
| | | subtitle() { |
| | | // 初始化时,this.months为空数组,所以需要特别判断处理 |
| | | if (this.months.length) { |
| | | return `${this.months[this.monthIndex].year}年${ |
| | | this.months[this.monthIndex].month |
| | | }月` |
| | | } else { |
| | | return '' |
| | | } |
| | | }, |
| | | buttonDisabled() { |
| | | // 如果为range类型,且选择的日期个数不足1个时,让底部的按钮出于disabled状态 |
| | | if (this.mode === 'range') { |
| | | if (this.selected.length <= 1) { |
| | | return true |
| | | } else { |
| | | return false |
| | | } |
| | | } else { |
| | | return false |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.start = Date.now() |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用 |
| | | setFormatter(e) { |
| | | this.innerFormatter = e |
| | | }, |
| | | // 点击日期框触发 |
| | | changeDay(e) { |
| | | this.$emit('change',e); |
| | | }, |
| | | // month组件内部选择日期后,通过事件通知给父组件 |
| | | monthSelected(e) { |
| | | this.selected = e |
| | | if (!this.showConfirm) { |
| | | // 在不需要确认按钮的情况下,如果为单选,或者范围多选且已选长度大于2,则直接进行返还 |
| | | if ( |
| | | this.mode === 'multiple' || |
| | | this.mode === 'single' || |
| | | (this.mode === 'range' && this.selected.length >= 2) |
| | | ) { |
| | | this.$emit('confirm', this.selected) |
| | | } |
| | | } |
| | | }, |
| | | init() { |
| | | // 校验maxDate,不能小于minDate |
| | | if ( |
| | | this.innerMaxDate && |
| | | this.innerMinDate && |
| | | new Date(this.innerMaxDate).getTime() < new Date(this.innerMinDate).getTime() |
| | | ) { |
| | | return this.$uv.error('maxDate不能小于minDate') |
| | | } |
| | | // 滚动区域的高度 |
| | | this.listHeight = this.rowHeight * 5 + 30 |
| | | this.setMonth() |
| | | }, |
| | | open() { |
| | | this.setMonth() |
| | | this.$refs.calendarPopup.open(); |
| | | }, |
| | | popupChange(e) { |
| | | if(!e.show) { |
| | | this.$emit('close'); |
| | | } |
| | | }, |
| | | // 点击确定按钮 |
| | | confirm() { |
| | | if (!this.buttonDisabled) { |
| | | this.$emit('confirm', this.selected) |
| | | } |
| | | if (this.closeOnClickConfirm) { |
| | | this.$refs.calendarPopup.close(); |
| | | } |
| | | }, |
| | | // 获得两个日期之间的月份数 |
| | | getMonths(minDate, maxDate) { |
| | | const minYear = dayjs(minDate).year() |
| | | const minMonth = dayjs(minDate).month() + 1 |
| | | const maxYear = dayjs(maxDate).year() |
| | | const maxMonth = dayjs(maxDate).month() + 1 |
| | | return (maxYear - minYear) * 12 + (maxMonth - minMonth) + 1 |
| | | }, |
| | | // 设置月份数据 |
| | | setMonth() { |
| | | // 最小日期的毫秒数 |
| | | const minDate = this.innerMinDate || dayjs().valueOf() |
| | | // 如果没有指定最大日期,则往后推3个月 |
| | | const maxDate = |
| | | this.innerMaxDate || |
| | | dayjs(minDate) |
| | | .add(this.monthNum - 1, 'month') |
| | | .valueOf() |
| | | // 最大最小月份之间的共有多少个月份, |
| | | const months = this.$uv.range( |
| | | 1, |
| | | this.monthNum, |
| | | this.getMonths(minDate, maxDate) |
| | | ) |
| | | // 先清空数组 |
| | | this.months = [] |
| | | for (let i = 0; i < months; i++) { |
| | | this.months.push({ |
| | | date: new Array( |
| | | dayjs(minDate).add(i, 'month').daysInMonth() |
| | | ) |
| | | .fill(1) |
| | | .map((item, index) => { |
| | | // 日期,取值1-31 |
| | | let day = index + 1 |
| | | // 星期,0-6,0为周日 |
| | | const week = dayjs(minDate) |
| | | .add(i, 'month') |
| | | .date(day) |
| | | .day() |
| | | const date = dayjs(minDate) |
| | | .add(i, 'month') |
| | | .date(day) |
| | | .format('YYYY-MM-DD') |
| | | let topInfo = '' |
| | | let bottomInfo = '' |
| | | if (this.showLunar) { |
| | | // 将日期转为农历格式 |
| | | const lunar = Calendar.solar2lunar( |
| | | dayjs(date).year(), |
| | | dayjs(date).month() + 1, |
| | | dayjs(date).date() |
| | | ) |
| | | bottomInfo = lunar.IDayCn |
| | | } |
| | | let config = { |
| | | day, |
| | | week, |
| | | // 小于最小允许的日期,或者大于最大的日期,则设置为disabled状态 |
| | | disabled: |
| | | dayjs(date).isBefore( |
| | | dayjs(minDate).format('YYYY-MM-DD') |
| | | ) || |
| | | dayjs(date).isAfter( |
| | | dayjs(maxDate).format('YYYY-MM-DD') |
| | | ), |
| | | // 返回一个日期对象,供外部的formatter获取当前日期的年月日等信息,进行加工处理 |
| | | date: new Date(date), |
| | | topInfo, |
| | | bottomInfo, |
| | | dot: false, |
| | | month: |
| | | dayjs(minDate).add(i, 'month').month() + 1 |
| | | } |
| | | const formatter = |
| | | this.formatter || this.innerFormatter |
| | | return formatter(config) |
| | | }), |
| | | // 当前所属的月份 |
| | | month: dayjs(minDate).add(i, 'month').month() + 1, |
| | | // 当前年份 |
| | | year: dayjs(minDate).add(i, 'month').year() |
| | | }) |
| | | } |
| | | |
| | | }, |
| | | // 滚动到默认设置的月份 |
| | | scrollIntoDefaultMonth(selected) { |
| | | // 查询默认日期在可选列表的下标 |
| | | const _index = this.months.findIndex(({ |
| | | year, |
| | | month |
| | | }) => { |
| | | month = this.$uv.padZero(month) |
| | | return `${year}-${month}` === selected |
| | | }) |
| | | if (_index !== -1) { |
| | | // #ifndef MP-WEIXIN |
| | | this.$nextTick(() => { |
| | | this.scrollIntoView = `month-${_index}` |
| | | }) |
| | | // #endif |
| | | // #ifdef MP-WEIXIN |
| | | this.scrollTop = this.months[_index].top || 0; |
| | | // #endif |
| | | } |
| | | }, |
| | | // scroll-view滚动监听 |
| | | onScroll(event) { |
| | | // 不允许小于0的滚动值,如果scroll-view到顶了,继续下拉,会出现负数值 |
| | | const scrollTop = Math.max(0, event.detail.scrollTop) |
| | | // 将当前滚动条数值,除以滚动区域的高度,可以得出当前滚动到了哪一个月份的索引 |
| | | for (let i = 0; i < this.months.length; i++) { |
| | | if (scrollTop >= (this.months[i].top || this.listHeight)) { |
| | | this.monthIndex = i |
| | | } |
| | | } |
| | | }, |
| | | // 更新月份的top值 |
| | | updateMonthTop(topArr = []) { |
| | | // 设置对应月份的top值,用于onScroll方法更新月份 |
| | | topArr.map((item, index) => { |
| | | this.months[index].top = item |
| | | }) |
| | | |
| | | // 获取默认日期的下标 |
| | | if (!this.defaultDate) { |
| | | // 如果没有设置默认日期,则将当天日期设置为默认选中的日期 |
| | | const selected = dayjs().format("YYYY-MM") |
| | | this.scrollIntoDefaultMonth(selected) |
| | | return |
| | | } |
| | | let selected = dayjs().format("YYYY-MM"); |
| | | // 单选模式,可以是字符串或数组,Date对象等 |
| | | if (!this.$uv.test.array(this.defaultDate)) { |
| | | selected = dayjs(this.defaultDate).format("YYYY-MM") |
| | | } else { |
| | | selected = dayjs(this.defaultDate[0]).format("YYYY-MM"); |
| | | } |
| | | this.scrollIntoDefaultMonth(selected) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uv-calendar { |
| | | &__confirm { |
| | | padding: 7px 18px; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-calendar", |
| | | "displayName": "uv-calendar 日历 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.6", |
| | | "description": "日历组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中,灵活配置,功能齐全,兼容全端。强烈推荐使用最新版日历组件,在下方跳入。", |
| | | "keywords": [ |
| | | "uv-calendar", |
| | | "uvui", |
| | | "uv-ui", |
| | | "calendar", |
| | | "日历" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-button", |
| | | "uv-popup" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Calendar 日历 |
| | | |
| | | > **组件名:uv-calendar** |
| | | |
| | | 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中。灵活配置,功能齐全,兼容全端。 |
| | | |
| | | ## 温馨提示:该组件不再更新,强烈推荐使用最新版日历组件:[https://www.uvui.cn/components/calendars.html](https://www.uvui.cn/components/calendars.html)。基于 `uv-ui` 插件市场首款多功能日历组件,不仅可以查看、选择日期,还可以选择任意范围内的日期、打点操作、自定义主题颜色、自定义文案、农历显示等。追求的就是完美。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/calendar.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.15(2023-11-08) |
| | | 1. 增加readonly属性,是否为只读状态,只读状态下禁止选择日期 |
| | | ## 1.0.14(2023-10-12) |
| | | 1. 修复selected没有设置了info或者info设置为空字符串后,文本则无法恢复BUG |
| | | ## 1.0.13(2023-09-19) |
| | | 1. 修复range模式下,selected设置了info后选中后,导致文本不恢复的问题 |
| | | 2. 修复multiple模式下,selected自定义信息的颜色没变,依然是白色 |
| | | ## 1.0.12(2023-09-14) |
| | | 1. 优化 |
| | | ## 1.0.11(2023-09-14) |
| | | 1. 增加allowSameDay参数,是否允许日期范围的起止时间为同一天,mode = range时有效 |
| | | 2. 修复在vue2+小程序渲染时闪烁的问题 |
| | | ## 1.0.10(2023-09-07) |
| | | 1. 修复国际化失效的BUG |
| | | ## 1.0.9(2023-09-01) |
| | | 1. 修复在pages.json中设置easycom会报错的BUG |
| | | ## 1.0.8(2023-08-29) |
| | | 1. 修复mainjs中设置setConfig修改属性不生效的问题,出自评论区:https://ext.dcloud.net.cn/plugin?id=12287 |
| | | ## 1.0.7(2023-08-26) |
| | | 1. 去除range参数,由mode="range"替换 |
| | | 2. 新增mode参数,不传 / multiple / range,分别为单日期, 多个日期,选择日期范围 |
| | | 3. 与uv-calendar选择日期的功能保持一致 |
| | | ## 1.0.6(2023-08-25) |
| | | 1. 修复点击返回今天按钮时,monthSwitch方法回调参数返回月份不是当天对应月份:https://github.com/climblee/uv-ui/issues/7 |
| | | ## 1.0.5(2023-08-13) |
| | | 1. 修复选择月份弹窗层级的问题 |
| | | ## 1.0.4(2023-08-06) |
| | | 1. 优化 |
| | | ## 1.0.3(2023-08-06) |
| | | 1. 修复高度不对的BUG |
| | | 2. 修复文案在小屏幕的BUG |
| | | ## 1.0.2(2023-08-05) |
| | | 1. 增加startText参数 |
| | | 2. 增加endText参数 |
| | | 3. 增加selected中的参数 |
| | | 4. 优化日历范围选择 |
| | | ## 1.0.1(2023-08-04) |
| | | 1. 修复 自定义主题时 颜色错误的BUG |
| | | ## 1.0.0(2023-08-03) |
| | | 1. 新增 uv-calendars 新版日历发布 |
| New file |
| | |
| | | <template> |
| | | <view class="uv-calendar-body"> |
| | | <view class="uv-calendar__header"> |
| | | <view class="uv-calendar__header-btn-box" @click.stop="pre"> |
| | | <view class="uv-calendar__header-btn uv-calendar--left"></view> |
| | | </view> |
| | | <picker mode="date" :value="getDate" fields="month" @change="bindDateChange"> |
| | | <text class="uv-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text> |
| | | </picker> |
| | | <view class="uv-calendar__header-btn-box" @click.stop="next"> |
| | | <view class="uv-calendar__header-btn uv-calendar--right"></view> |
| | | </view> |
| | | <text class="uv-calendar__backtoday" @click="backToday">{{todayText}}</text> |
| | | </view> |
| | | <view class="uv-calendar__box"> |
| | | <view v-if="showMonth" class="uv-calendar__box-bg"> |
| | | <text class="uv-calendar__box-bg-text">{{nowDate.month}}</text> |
| | | </view> |
| | | <view class="uv-calendar__weeks uv-calendar__weeks-week"> |
| | | <view class="uv-calendar__weeks-day"> |
| | | <text class="uv-calendar__weeks-day-text">{{SUNText}}</text> |
| | | </view> |
| | | <view class="uv-calendar__weeks-day"> |
| | | <text class="uv-calendar__weeks-day-text">{{monText}}</text> |
| | | </view> |
| | | <view class="uv-calendar__weeks-day"> |
| | | <text class="uv-calendar__weeks-day-text">{{TUEText}}</text> |
| | | </view> |
| | | <view class="uv-calendar__weeks-day"> |
| | | <text class="uv-calendar__weeks-day-text">{{WEDText}}</text> |
| | | </view> |
| | | <view class="uv-calendar__weeks-day"> |
| | | <text class="uv-calendar__weeks-day-text">{{THUText}}</text> |
| | | </view> |
| | | <view class="uv-calendar__weeks-day"> |
| | | <text class="uv-calendar__weeks-day-text">{{FRIText}}</text> |
| | | </view> |
| | | <view class="uv-calendar__weeks-day"> |
| | | <text class="uv-calendar__weeks-day-text">{{SATText}}</text> |
| | | </view> |
| | | </view> |
| | | <view class="uv-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex"> |
| | | <view class="uv-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex"> |
| | | <calendar-item class="uv-calendar-item--hook" :weeks="weeks" :rangeInfoText="rangeInfoText(weeks)" :multiple="multiple" :range="range" :calendar="calendar" :selected="selected" :lunar="lunar" :color="color" @change="choiceDate"></calendar-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'; |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'; |
| | | |
| | | import CalendarItem from './calendar-item.vue'; |
| | | |
| | | import { initVueI18n } from '@dcloudio/uni-i18n'; |
| | | import i18nMessages from './i18n/index.js'; |
| | | const { t } = initVueI18n(i18nMessages); |
| | | export default { |
| | | mixins: [mpMixin, mixin], |
| | | components: { |
| | | CalendarItem |
| | | }, |
| | | props: { |
| | | date: { |
| | | type: [String,Array], |
| | | default: '' |
| | | }, |
| | | nowDate: { |
| | | type: [String, Object], |
| | | default: '' |
| | | }, |
| | | weeks: { |
| | | type: [Array, Object], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | calendar: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | lunar: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | showMonth: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#3c9cff' |
| | | }, |
| | | startText: { |
| | | type: String, |
| | | default: '开始' |
| | | }, |
| | | endText: { |
| | | type: String, |
| | | default: '结束' |
| | | }, |
| | | range: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | multiple: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否允许日期范围的起止时间为同一天,mode = range时有效 |
| | | allowSameDay: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | computed: { |
| | | getDate() { |
| | | return Array.isArray(this.date) ? this.date[0] : this.date; |
| | | }, |
| | | /** |
| | | * for i18n |
| | | */ |
| | | todayText() { |
| | | return t("uv-calender.today") |
| | | }, |
| | | monText() { |
| | | return t("uv-calender.MON") |
| | | }, |
| | | TUEText() { |
| | | return t("uv-calender.TUE") |
| | | }, |
| | | WEDText() { |
| | | return t("uv-calender.WED") |
| | | }, |
| | | THUText() { |
| | | return t("uv-calender.THU") |
| | | }, |
| | | FRIText() { |
| | | return t("uv-calender.FRI") |
| | | }, |
| | | SATText() { |
| | | return t("uv-calender.SAT") |
| | | }, |
| | | SUNText() { |
| | | return t("uv-calender.SUN") |
| | | }, |
| | | rangeInfoText(weeks) { |
| | | return weeks=> { |
| | | if(this.allowSameDay && weeks.beforeRange && weeks.afterRange && weeks.dateEqual) { |
| | | return this.setInfo(weeks,`${this.startText}/${this.endText}`); |
| | | } |
| | | if(weeks.beforeRange) { |
| | | return this.setInfo(weeks,this.startText); |
| | | } |
| | | if(weeks.afterRange) { |
| | | return this.setInfo(weeks,this.endText); |
| | | } |
| | | if(weeks.extraInfo?.info_old == ' ') { |
| | | weeks.extraInfo.info = null; |
| | | }else if(weeks.extraInfo?.info_old) { |
| | | weeks.extraInfo.info = weeks.extraInfo.info_old; |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | setInfo(weeks,text) { |
| | | this.setInfoOld(weeks); |
| | | if(weeks.extraInfo) { |
| | | weeks.extraInfo.info = text; |
| | | }else { |
| | | weeks.extraInfo = { |
| | | info: text |
| | | } |
| | | } |
| | | }, |
| | | setInfoOld(weeks) { |
| | | if(weeks.extraInfo) { |
| | | weeks.extraInfo.info_old = weeks.extraInfo.info ? weeks.extraInfo.info_old || weeks.extraInfo.info : ' '; |
| | | } |
| | | }, |
| | | bindDateChange(e) { |
| | | this.$emit('bindDateChange', e); |
| | | }, |
| | | backToday() { |
| | | this.$emit('backToday'); |
| | | }, |
| | | pre() { |
| | | this.$emit('pre'); |
| | | }, |
| | | next() { |
| | | this.$emit('next'); |
| | | }, |
| | | choiceDate(e) { |
| | | this.$emit('choiceDate', e); |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @mixin flex($direction: row) { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: $direction; |
| | | } |
| | | $uv-bg-color-mask: rgba($color: #000000, $alpha: 0.4); |
| | | $uv-border-color: #EDEDED !default; |
| | | $uv-text-color: #333; |
| | | $uv-bg-color-hover: #f1f1f1; |
| | | $uv-font-size-base: 14px; |
| | | $uv-text-color-placeholder: #808080; |
| | | $uv-color-subtitle: #555555; |
| | | $uv-text-color-grey: #999; |
| | | .uv-calendar { |
| | | @include flex(column); |
| | | } |
| | | .uv-calendar__mask { |
| | | position: fixed; |
| | | bottom: 0; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background-color: $uv-bg-color-mask; |
| | | transition-property: opacity; |
| | | transition-duration: 0.3s; |
| | | opacity: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | .uv-calendar--mask-show { |
| | | opacity: 1 |
| | | } |
| | | .uv-calendar--fixed { |
| | | position: fixed; |
| | | /* #ifdef APP-NVUE */ |
| | | bottom: 0; |
| | | /* #endif */ |
| | | left: 0; |
| | | right: 0; |
| | | transition-property: transform; |
| | | transition-duration: 0.3s; |
| | | transform: translateY(460px); |
| | | /* #ifndef APP-NVUE */ |
| | | bottom: calc(var(--window-bottom)); |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | .uv-calendar--ani-show { |
| | | transform: translateY(0); |
| | | } |
| | | .uv-calendar__content { |
| | | background-color: #fff; |
| | | } |
| | | .uv-calendar__header { |
| | | position: relative; |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 50px; |
| | | border-bottom-color: $uv-border-color; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | .uv-calendar--fixed-top { |
| | | @include flex; |
| | | justify-content: space-between; |
| | | border-top-color: $uv-border-color; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | } |
| | | .uv-calendar--fixed-width { |
| | | width: 50px; |
| | | } |
| | | .uv-calendar__backtoday { |
| | | position: absolute; |
| | | right: 0; |
| | | top: 25rpx; |
| | | padding: 0 5px; |
| | | padding-left: 10px; |
| | | height: 25px; |
| | | line-height: 25px; |
| | | font-size: 12px; |
| | | border-top-left-radius: 25px; |
| | | border-bottom-left-radius: 25px; |
| | | color: $uv-text-color; |
| | | background-color: $uv-bg-color-hover; |
| | | } |
| | | .uv-calendar__header-text { |
| | | text-align: center; |
| | | width: 100px; |
| | | font-size: $uv-font-size-base; |
| | | color: $uv-text-color; |
| | | } |
| | | .uv-calendar__header-btn-box { |
| | | @include flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 50px; |
| | | height: 50px; |
| | | } |
| | | .uv-calendar__header-btn { |
| | | width: 10px; |
| | | height: 10px; |
| | | border-left-color: $uv-text-color-placeholder; |
| | | border-left-style: solid; |
| | | border-left-width: 2px; |
| | | border-top-color: $uv-color-subtitle; |
| | | border-top-style: solid; |
| | | border-top-width: 2px; |
| | | } |
| | | .uv-calendar--left { |
| | | transform: rotate(-45deg); |
| | | } |
| | | .uv-calendar--right { |
| | | transform: rotate(135deg); |
| | | } |
| | | .uv-calendar__weeks { |
| | | position: relative; |
| | | @include flex; |
| | | } |
| | | .uv-calendar__weeks-week { |
| | | padding: 0 0 2rpx; |
| | | } |
| | | .uv-calendar__weeks-item { |
| | | flex: 1; |
| | | } |
| | | .uv-calendar__weeks-day { |
| | | flex: 1; |
| | | @include flex(column); |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 45px; |
| | | border-bottom-color: #F5F5F5; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | .uv-calendar__weeks-day-text { |
| | | font-size: 14px; |
| | | } |
| | | .uv-calendar__box { |
| | | position: relative; |
| | | } |
| | | .uv-calendar__box-bg { |
| | | @include flex(column); |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | } |
| | | .uv-calendar__box-bg-text { |
| | | font-size: 200px; |
| | | font-weight: bold; |
| | | color: $uv-text-color-grey; |
| | | opacity: 0.1; |
| | | text-align: center; |
| | | /* #ifndef APP-NVUE */ |
| | | line-height: 1; |
| | | /* #endif */ |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <view class="uv-calendar-item__weeks-box" :class="{ |
| | | 'uv-calendar-item--disable':weeks.disable || (weeks.extraInfo&&weeks.extraInfo.disable), |
| | | 'uv-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay && !multiple, |
| | | 'uv-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay && !multiple) , |
| | | 'uv-calendar-item--before-checked':weeks.beforeRange, |
| | | 'uv-calendar-item--range': weeks.range, |
| | | 'uv-calendar-item--after-checked':weeks.afterRange, |
| | | 'uv-calendar-item--multiple':weeks.multiple |
| | | }" :style="[itemBoxStyle]" @click="choiceDate(weeks)"> |
| | | <view class="uv-calendar-item__weeks-box-item"> |
| | | <text v-if="selected&&weeks.extraInfo&&weeks.extraInfo.badge" class="uv-calendar-item__weeks-box-circle"></text> |
| | | <text class="uv-calendar-item__weeks-top-text" v-if="weeks.extraInfo&&weeks.extraInfo.topinfo" :style="[infoStyle('top')]">{{weeks.extraInfo&&weeks.extraInfo.topinfo}}</text> |
| | | <text class="uv-calendar-item__weeks-box-text" :class="{ |
| | | 'uv-calendar-item--isDay-text': weeks.isDay, |
| | | 'uv-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay && !multiple, |
| | | 'uv-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay && !multiple, |
| | | 'uv-calendar-item--before-checked':weeks.beforeRange, |
| | | 'uv-calendar-item--range': weeks.range, |
| | | 'uv-calendar-item--after-checked':weeks.afterRange, |
| | | 'uv-calendar-item--multiple':weeks.multiple, |
| | | 'uv-calendar-item--disable':weeks.disable || (weeks.extraInfo&&weeks.extraInfo.disable) |
| | | }" :style="[itemBoxStyle]">{{weeks.date}}</text> |
| | | <text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uv-calendar-item__weeks-lunar-text" :class="{ |
| | | 'uv-calendar-item--isDay-text':weeks.isDay, |
| | | 'uv-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay && !multiple, |
| | | 'uv-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay && !multiple, |
| | | 'uv-calendar-item--before-checked':weeks.beforeRange, |
| | | 'uv-calendar-item--range': weeks.range, |
| | | 'uv-calendar-item--after-checked':weeks.afterRange, |
| | | 'uv-calendar-item--multiple':weeks.multiple |
| | | }" :style="[itemBoxStyle]">{{todayText}}</text> |
| | | <text v-if="lunar&&!weeks.extraInfo" class="uv-calendar-item__weeks-lunar-text" :class="{ |
| | | 'uv-calendar-item--isDay-text':weeks.isDay, |
| | | 'uv-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay && !multiple, |
| | | 'uv-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay && !multiple, |
| | | 'uv-calendar-item--before-checked':weeks.beforeRange, |
| | | 'uv-calendar-item--range': weeks.range, |
| | | 'uv-calendar-item--after-checked':weeks.afterRange, |
| | | 'uv-calendar-item--multiple':weeks.multiple, |
| | | 'uv-calendar-item--disable':weeks.disable || (weeks.extraInfo&&weeks.extraInfo.disable) |
| | | }" :style="[itemBoxStyle]">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text> |
| | | <text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uv-calendar-item__weeks-lunar-text" :class="{ |
| | | 'uv-calendar-item__weeks-lunar-text--equal': weeks.dateEqual |
| | | }" :style="[infoStyle('bottom')]">{{weeks.extraInfo.info}}</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import { colorGradient } from '@/uni_modules/uv-ui-tools/libs/function/colorGradient.js'; |
| | | import { initVueI18n } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | const { t } = initVueI18n(i18nMessages) |
| | | export default { |
| | | emits: ['change'], |
| | | props: { |
| | | weeks: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | calendar: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | } |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default: () => { |
| | | return [] |
| | | } |
| | | }, |
| | | lunar: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#3c9cff' |
| | | }, |
| | | range: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | multiple: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | computed: { |
| | | todayText() { |
| | | return t("uv-calender.today") |
| | | }, |
| | | itemBoxStyle() { |
| | | const style = {}; |
| | | if (this.multiple) { // 多选状态 |
| | | if (this.weeks.multiple) { |
| | | style.backgroundColor = this.color; |
| | | style.color = '#fff'; |
| | | } else if (this.weeks.isDay) { |
| | | style.color = this.color; |
| | | } |
| | | } else if (this.range) { // 范围选择 |
| | | if (this.weeks.beforeRange || this.weeks.afterRange) { |
| | | style.backgroundColor = this.color; |
| | | } else if (this.weeks.range) { |
| | | style.backgroundColor = colorGradient(this.color, '#ffffff', 100)[90] |
| | | style.color = this.color; |
| | | style.opacity = 0.8; |
| | | style.borderRadius = 0; |
| | | } |
| | | } else { |
| | | if (this.weeks.isDay) { |
| | | style.color = this.color; |
| | | } |
| | | if (this.calendar.fullDate === this.weeks.fullDate) { |
| | | style.backgroundColor = this.color; |
| | | style.color = '#fff'; |
| | | } |
| | | } |
| | | return style; |
| | | }, |
| | | infoStyle(val) { |
| | | return val => { |
| | | const style = {}; |
| | | if (!this.weeks.multiple) { |
| | | if (val == 'top') { |
| | | style.color = this.weeks.extraInfo.topinfoColor ? this.weeks.extraInfo.topinfoColor : '#606266'; |
| | | } else if (val == 'bottom') { |
| | | style.color = this.weeks.extraInfo.infoColor ? this.weeks.extraInfo.infoColor : '#f56c6c'; |
| | | } |
| | | if (this.weeks.range) { |
| | | style.color = this.color; |
| | | } |
| | | if (this.calendar.fullDate === this.weeks.fullDate || this.weeks.beforeRange || this.weeks.afterRange) { |
| | | style.color = this.multiple ? style.color : '#fff'; |
| | | } |
| | | } else { |
| | | style.color = '#fff'; |
| | | } |
| | | return style; |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | choiceDate(weeks) { |
| | | if (this.weeks.extraInfo && this.weeks.extraInfo.disable) return; |
| | | this.$emit('change', weeks) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | @mixin flex($direction: row) { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: $direction; |
| | | } |
| | | $uv-font-size-base: 14px; |
| | | $uv-text-color: #333; |
| | | $uv-font-size-sm: 24rpx; |
| | | $uv-error: #f56c6c !default; |
| | | $uv-opacity-disabled: 0.3; |
| | | $uv-text-color-disable: #c0c0c0; |
| | | $uv-primary: #3c9cff !default; |
| | | $info-height: 32rpx; |
| | | .uv-calendar-item__weeks-box { |
| | | flex: 1; |
| | | @include flex(column); |
| | | justify-content: center; |
| | | align-items: center; |
| | | border-radius: 4px; |
| | | } |
| | | .uv-calendar-item__weeks-top-text { |
| | | height: $info-height; |
| | | line-height: $info-height; |
| | | font-size: $uv-font-size-sm; |
| | | } |
| | | .uv-calendar-item__weeks-box-text { |
| | | font-size: $uv-font-size-base; |
| | | color: $uv-text-color; |
| | | } |
| | | .uv-calendar-item__weeks-lunar-text { |
| | | height: $info-height; |
| | | line-height: $info-height; |
| | | font-size: $uv-font-size-sm; |
| | | color: $uv-text-color; |
| | | } |
| | | .uv-calendar-item__weeks-lunar-text--equal { |
| | | /* #ifdef H5 */ |
| | | white-space: nowrap; |
| | | transform: scale(.8); |
| | | /* #endif */ |
| | | /* #ifndef H5 */ |
| | | font-size: 20rpx; |
| | | /* #endif */ |
| | | } |
| | | .uv-calendar-item__weeks-box-item { |
| | | position: relative; |
| | | @include flex(column); |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: 106rpx; |
| | | height: 56px; |
| | | } |
| | | .uv-calendar-item__weeks-box-circle { |
| | | position: absolute; |
| | | top: 5px; |
| | | right: 5px; |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 8px; |
| | | background-color: $uv-error; |
| | | } |
| | | .uv-calendar-item--disable { |
| | | background-color: rgba(249, 249, 249, $uv-opacity-disabled); |
| | | color: $uv-text-color-disable; |
| | | } |
| | | .uv-calendar-item--isDay-text { |
| | | color: $uv-primary; |
| | | } |
| | | .uv-calendar-item--isDay { |
| | | background-color: $uv-primary; |
| | | color: #fff; |
| | | } |
| | | .uv-calendar-item--checked { |
| | | background-color: $uv-primary; |
| | | color: #fff; |
| | | border-radius: 4px; |
| | | } |
| | | // .uv-calendar-item--range { |
| | | // background-color: $uv-primary; |
| | | // color: #fff; |
| | | // } |
| | | .uv-calendar-item--before-checked { |
| | | color: #fff; |
| | | } |
| | | .uv-calendar-item--after-checked { |
| | | color: #fff; |
| | | } |
| | | .uv-calendar-item--multiple { |
| | | background-color: $uv-primary; |
| | | color: #fff; |
| | | } |
| | | </style> |
| New file |
| | |
| | | /** |
| | | * @1900-2100区间内的公历、农历互转 |
| | | * @charset UTF-8 |
| | | * @github https://github.com/jjonline/calendar.js |
| | | * @Author Jea杨(JJonline@JJonline.Cn) |
| | | * @Time 2014-7-21 |
| | | * @Time 2016-8-13 Fixed 2033hex、Attribution Annals |
| | | * @Time 2016-9-25 Fixed lunar LeapMonth Param Bug |
| | | * @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year |
| | | * @Version 1.0.3 |
| | | * @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0] |
| | | * @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0] |
| | | */ |
| | | /* eslint-disable */ |
| | | var calendar = { |
| | | |
| | | /** |
| | | * 农历1900-2100的润大小信息表 |
| | | * @Array Of Property |
| | | * @return Hex |
| | | */ |
| | | lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909 |
| | | 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919 |
| | | 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929 |
| | | 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939 |
| | | 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949 |
| | | 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959 |
| | | 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969 |
| | | 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979 |
| | | 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989 |
| | | 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999 |
| | | 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009 |
| | | 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019 |
| | | 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029 |
| | | 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039 |
| | | 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049 |
| | | /** Add By JJonline@JJonline.Cn**/ |
| | | 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059 |
| | | 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069 |
| | | 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079 |
| | | 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089 |
| | | 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099 |
| | | 0x0d520], // 2100 |
| | | |
| | | /** |
| | | * 公历每个月份的天数普通表 |
| | | * @Array Of Property |
| | | * @return Number |
| | | */ |
| | | solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], |
| | | |
| | | /** |
| | | * 天干地支之天干速查表 |
| | | * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"] |
| | | * @return Cn string |
| | | */ |
| | | Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'], |
| | | |
| | | /** |
| | | * 天干地支之地支速查表 |
| | | * @Array Of Property |
| | | * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"] |
| | | * @return Cn string |
| | | */ |
| | | Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'], |
| | | |
| | | /** |
| | | * 天干地支之地支速查表<=>生肖 |
| | | * @Array Of Property |
| | | * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"] |
| | | * @return Cn string |
| | | */ |
| | | Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'], |
| | | |
| | | /** |
| | | * 24节气速查表 |
| | | * @Array Of Property |
| | | * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"] |
| | | * @return Cn string |
| | | */ |
| | | solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'], |
| | | |
| | | /** |
| | | * 1900-2100各年的24节气日期速查表 |
| | | * @Array Of Property |
| | | * @return 0x string For splice |
| | | */ |
| | | sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', |
| | | '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', |
| | | 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f', |
| | | '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa', |
| | | '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', |
| | | '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f', |
| | | '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', |
| | | '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f', |
| | | '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
| | | '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
| | | '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722', |
| | | '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
| | | '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
| | | '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
| | | '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
| | | '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721', |
| | | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2', |
| | | '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
| | | '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
| | | '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721', |
| | | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5', |
| | | '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722', |
| | | '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', |
| | | '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
| | | '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd', |
| | | '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35', |
| | | '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
| | | '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721', |
| | | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5', |
| | | '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35', |
| | | '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35', |
| | | '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'], |
| | | |
| | | /** |
| | | * 数字转中文速查表 |
| | | * @Array Of Property |
| | | * @trans ['日','一','二','三','四','五','六','七','八','九','十'] |
| | | * @return Cn string |
| | | */ |
| | | nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'], |
| | | |
| | | /** |
| | | * 日期转农历称呼速查表 |
| | | * @Array Of Property |
| | | * @trans ['初','十','廿','卅'] |
| | | * @return Cn string |
| | | */ |
| | | nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'], |
| | | |
| | | /** |
| | | * 月份转农历称呼速查表 |
| | | * @Array Of Property |
| | | * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊'] |
| | | * @return Cn string |
| | | */ |
| | | nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'], |
| | | |
| | | /** |
| | | * 返回农历y年一整年的总天数 |
| | | * @param lunar Year |
| | | * @return Number |
| | | * @eg:var count = calendar.lYearDays(1987) ;//count=387 |
| | | */ |
| | | lYearDays: function (y) { |
| | | var i; var sum = 348 |
| | | for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 } |
| | | return (sum + this.leapDays(y)) |
| | | }, |
| | | |
| | | /** |
| | | * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 |
| | | * @param lunar Year |
| | | * @return Number (0-12) |
| | | * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6 |
| | | */ |
| | | leapMonth: function (y) { // 闰字编码 \u95f0 |
| | | return (this.lunarInfo[y - 1900] & 0xf) |
| | | }, |
| | | |
| | | /** |
| | | * 返回农历y年闰月的天数 若该年没有闰月则返回0 |
| | | * @param lunar Year |
| | | * @return Number (0、29、30) |
| | | * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29 |
| | | */ |
| | | leapDays: function (y) { |
| | | if (this.leapMonth(y)) { |
| | | return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29) |
| | | } |
| | | return (0) |
| | | }, |
| | | |
| | | /** |
| | | * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 |
| | | * @param lunar Year |
| | | * @return Number (-1、29、30) |
| | | * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29 |
| | | */ |
| | | monthDays: function (y, m) { |
| | | if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1 |
| | | return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29) |
| | | }, |
| | | |
| | | /** |
| | | * 返回公历(!)y年m月的天数 |
| | | * @param solar Year |
| | | * @return Number (-1、28、29、30、31) |
| | | * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30 |
| | | */ |
| | | solarDays: function (y, m) { |
| | | if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 |
| | | var ms = m - 1 |
| | | if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29 |
| | | return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28) |
| | | } else { |
| | | return (this.solarMonth[ms]) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 农历年份转换为干支纪年 |
| | | * @param lYear 农历年的年份数 |
| | | * @return Cn string |
| | | */ |
| | | toGanZhiYear: function (lYear) { |
| | | var ganKey = (lYear - 3) % 10 |
| | | var zhiKey = (lYear - 3) % 12 |
| | | if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干 |
| | | if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支 |
| | | return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1] |
| | | }, |
| | | |
| | | /** |
| | | * 公历月、日判断所属星座 |
| | | * @param cMonth [description] |
| | | * @param cDay [description] |
| | | * @return Cn string |
| | | */ |
| | | toAstro: function (cMonth, cDay) { |
| | | var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf' |
| | | var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22] |
| | | return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座 |
| | | }, |
| | | |
| | | /** |
| | | * 传入offset偏移量返回干支 |
| | | * @param offset 相对甲子的偏移量 |
| | | * @return Cn string |
| | | */ |
| | | toGanZhi: function (offset) { |
| | | return this.Gan[offset % 10] + this.Zhi[offset % 12] |
| | | }, |
| | | |
| | | /** |
| | | * 传入公历(!)y年获得该年第n个节气的公历日期 |
| | | * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 |
| | | * @return day Number |
| | | * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春 |
| | | */ |
| | | getTerm: function (y, n) { |
| | | if (y < 1900 || y > 2100) { return -1 } |
| | | if (n < 1 || n > 24) { return -1 } |
| | | var _table = this.sTermInfo[y - 1900] |
| | | var _info = [ |
| | | parseInt('0x' + _table.substr(0, 5)).toString(), |
| | | parseInt('0x' + _table.substr(5, 5)).toString(), |
| | | parseInt('0x' + _table.substr(10, 5)).toString(), |
| | | parseInt('0x' + _table.substr(15, 5)).toString(), |
| | | parseInt('0x' + _table.substr(20, 5)).toString(), |
| | | parseInt('0x' + _table.substr(25, 5)).toString() |
| | | ] |
| | | var _calday = [ |
| | | _info[0].substr(0, 1), |
| | | _info[0].substr(1, 2), |
| | | _info[0].substr(3, 1), |
| | | _info[0].substr(4, 2), |
| | | |
| | | _info[1].substr(0, 1), |
| | | _info[1].substr(1, 2), |
| | | _info[1].substr(3, 1), |
| | | _info[1].substr(4, 2), |
| | | |
| | | _info[2].substr(0, 1), |
| | | _info[2].substr(1, 2), |
| | | _info[2].substr(3, 1), |
| | | _info[2].substr(4, 2), |
| | | |
| | | _info[3].substr(0, 1), |
| | | _info[3].substr(1, 2), |
| | | _info[3].substr(3, 1), |
| | | _info[3].substr(4, 2), |
| | | |
| | | _info[4].substr(0, 1), |
| | | _info[4].substr(1, 2), |
| | | _info[4].substr(3, 1), |
| | | _info[4].substr(4, 2), |
| | | |
| | | _info[5].substr(0, 1), |
| | | _info[5].substr(1, 2), |
| | | _info[5].substr(3, 1), |
| | | _info[5].substr(4, 2) |
| | | ] |
| | | return parseInt(_calday[n - 1]) |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历数字月份返回汉语通俗表示法 |
| | | * @param lunar month |
| | | * @return Cn string |
| | | * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月' |
| | | */ |
| | | toChinaMonth: function (m) { // 月 => \u6708 |
| | | if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 |
| | | var s = this.nStr3[m - 1] |
| | | s += '\u6708'// 加上月字 |
| | | return s |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历日期数字返回汉字表示法 |
| | | * @param lunar day |
| | | * @return Cn string |
| | | * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一' |
| | | */ |
| | | toChinaDay: function (d) { // 日 => \u65e5 |
| | | var s |
| | | switch (d) { |
| | | case 10: |
| | | s = '\u521d\u5341'; break |
| | | case 20: |
| | | s = '\u4e8c\u5341'; break |
| | | break |
| | | case 30: |
| | | s = '\u4e09\u5341'; break |
| | | break |
| | | default : |
| | | s = this.nStr2[Math.floor(d / 10)] |
| | | s += this.nStr1[d % 10] |
| | | } |
| | | return (s) |
| | | }, |
| | | |
| | | /** |
| | | * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” |
| | | * @param y year |
| | | * @return Cn string |
| | | * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔' |
| | | */ |
| | | getAnimal: function (y) { |
| | | return this.Animals[(y - 4) % 12] |
| | | }, |
| | | |
| | | /** |
| | | * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON |
| | | * @param y solar year |
| | | * @param m solar month |
| | | * @param d solar day |
| | | * @return JSON object |
| | | * @eg:console.log(calendar.solar2lunar(1987,11,01)); |
| | | */ |
| | | solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31 |
| | | // 年份限定、上限 |
| | | if (y < 1900 || y > 2100) { |
| | | return -1// undefined转换为数字变为NaN |
| | | } |
| | | // 公历传参最下限 |
| | | if (y == 1900 && m == 1 && d < 31) { |
| | | return -1 |
| | | } |
| | | // 未传参 获得当天 |
| | | if (!y) { |
| | | var objDate = new Date() |
| | | } else { |
| | | var objDate = new Date(y, parseInt(m) - 1, d) |
| | | } |
| | | var i; var leap = 0; var temp = 0 |
| | | // 修正ymd参数 |
| | | var y = objDate.getFullYear() |
| | | var m = objDate.getMonth() + 1 |
| | | var d = objDate.getDate() |
| | | var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000 |
| | | for (i = 1900; i < 2101 && offset > 0; i++) { |
| | | temp = this.lYearDays(i) |
| | | offset -= temp |
| | | } |
| | | if (offset < 0) { |
| | | offset += temp; i-- |
| | | } |
| | | |
| | | // 是否今天 |
| | | var isTodayObj = new Date() |
| | | var isToday = false |
| | | if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) { |
| | | isToday = true |
| | | } |
| | | // 星期几 |
| | | var nWeek = objDate.getDay() |
| | | var cWeek = this.nStr1[nWeek] |
| | | // 数字表示周几顺应天朝周一开始的惯例 |
| | | if (nWeek == 0) { |
| | | nWeek = 7 |
| | | } |
| | | // 农历年 |
| | | var year = i |
| | | var leap = this.leapMonth(i) // 闰哪个月 |
| | | var isLeap = false |
| | | |
| | | // 效验闰月 |
| | | for (i = 1; i < 13 && offset > 0; i++) { |
| | | // 闰月 |
| | | if (leap > 0 && i == (leap + 1) && isLeap == false) { |
| | | --i |
| | | isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数 |
| | | } else { |
| | | temp = this.monthDays(year, i)// 计算农历普通月天数 |
| | | } |
| | | // 解除闰月 |
| | | if (isLeap == true && i == (leap + 1)) { isLeap = false } |
| | | offset -= temp |
| | | } |
| | | // 闰月导致数组下标重叠取反 |
| | | if (offset == 0 && leap > 0 && i == leap + 1) { |
| | | if (isLeap) { |
| | | isLeap = false |
| | | } else { |
| | | isLeap = true; --i |
| | | } |
| | | } |
| | | if (offset < 0) { |
| | | offset += temp; --i |
| | | } |
| | | // 农历月 |
| | | var month = i |
| | | // 农历日 |
| | | var day = offset + 1 |
| | | // 天干地支处理 |
| | | var sm = m - 1 |
| | | var gzY = this.toGanZhiYear(year) |
| | | |
| | | // 当月的两个节气 |
| | | // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year` |
| | | var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始 |
| | | var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始 |
| | | |
| | | // 依据12节气修正干支月 |
| | | var gzM = this.toGanZhi((y - 1900) * 12 + m + 11) |
| | | if (d >= firstNode) { |
| | | gzM = this.toGanZhi((y - 1900) * 12 + m + 12) |
| | | } |
| | | |
| | | // 传入的日期的节气与否 |
| | | var isTerm = false |
| | | var Term = null |
| | | if (firstNode == d) { |
| | | isTerm = true |
| | | Term = this.solarTerm[m * 2 - 2] |
| | | } |
| | | if (secondNode == d) { |
| | | isTerm = true |
| | | Term = this.solarTerm[m * 2 - 1] |
| | | } |
| | | // 日柱 当月一日与 1900/1/1 相差天数 |
| | | var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10 |
| | | var gzD = this.toGanZhi(dayCyclical + d - 1) |
| | | // 该日期所属的星座 |
| | | var astro = this.toAstro(m, d) |
| | | |
| | | return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro } |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON |
| | | * @param y lunar year |
| | | * @param m lunar month |
| | | * @param d lunar day |
| | | * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] |
| | | * @return JSON object |
| | | * @eg:console.log(calendar.lunar2solar(1987,9,10)); |
| | | */ |
| | | lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1 |
| | | var isLeapMonth = !!isLeapMonth |
| | | var leapOffset = 0 |
| | | var leapMonth = this.leapMonth(y) |
| | | var leapDay = this.leapDays(y) |
| | | if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 |
| | | if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值 |
| | | var day = this.monthDays(y, m) |
| | | var _day = day |
| | | // bugFix 2016-9-25 |
| | | // if month is leap, _day use leapDays method |
| | | if (isLeapMonth) { |
| | | _day = this.leapDays(y, m) |
| | | } |
| | | if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验 |
| | | |
| | | // 计算农历的时间差 |
| | | var offset = 0 |
| | | for (var i = 1900; i < y; i++) { |
| | | offset += this.lYearDays(i) |
| | | } |
| | | var leap = 0; var isAdd = false |
| | | for (var i = 1; i < m; i++) { |
| | | leap = this.leapMonth(y) |
| | | if (!isAdd) { // 处理闰月 |
| | | if (leap <= i && leap > 0) { |
| | | offset += this.leapDays(y); isAdd = true |
| | | } |
| | | } |
| | | offset += this.monthDays(y, i) |
| | | } |
| | | // 转换闰月农历 需补充该年闰月的前一个月的时差 |
| | | if (isLeapMonth) { offset += day } |
| | | // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) |
| | | var stmap = Date.UTC(1900, 1, 30, 0, 0, 0) |
| | | var calObj = new Date((offset + d - 31) * 86400000 + stmap) |
| | | var cY = calObj.getUTCFullYear() |
| | | var cM = calObj.getUTCMonth() + 1 |
| | | var cD = calObj.getUTCDate() |
| | | |
| | | return this.solar2lunar(cY, cM, cD) |
| | | } |
| | | } |
| | | |
| | | export default calendar |
| New file |
| | |
| | | { |
| | | "uv-calender.ok": "ok", |
| | | "uv-calender.cancel": "cancel", |
| | | "uv-calender.today": "today", |
| | | "uv-calender.MON": "MON", |
| | | "uv-calender.TUE": "TUE", |
| | | "uv-calender.WED": "WED", |
| | | "uv-calender.THU": "THU", |
| | | "uv-calender.FRI": "FRI", |
| | | "uv-calender.SAT": "SAT", |
| | | "uv-calender.SUN": "SUN" |
| | | } |
| New file |
| | |
| | | import en from './en.json' |
| | | import zhHans from './zh-Hans.json' |
| | | import zhHant from './zh-Hant.json' |
| | | export default { |
| | | en, |
| | | 'zh-Hans': zhHans, |
| | | 'zh-Hant': zhHant |
| | | } |
| New file |
| | |
| | | { |
| | | "uv-calender.ok": "确定", |
| | | "uv-calender.cancel": "取消", |
| | | "uv-calender.today": "今日", |
| | | "uv-calender.SUN": "日", |
| | | "uv-calender.MON": "一", |
| | | "uv-calender.TUE": "二", |
| | | "uv-calender.WED": "三", |
| | | "uv-calender.THU": "四", |
| | | "uv-calender.FRI": "五", |
| | | "uv-calender.SAT": "六" |
| | | } |
| New file |
| | |
| | | { |
| | | "uv-calender.ok": "確定", |
| | | "uv-calender.cancel": "取消", |
| | | "uv-calender.today": "今日", |
| | | "uv-calender.SUN": "日", |
| | | "uv-calender.MON": "一", |
| | | "uv-calender.TUE": "二", |
| | | "uv-calender.WED": "三", |
| | | "uv-calender.THU": "四", |
| | | "uv-calender.FRI": "五", |
| | | "uv-calender.SAT": "六" |
| | | } |
| New file |
| | |
| | | import CALENDAR from './calendar.js' |
| | | class Calendar { |
| | | constructor({ |
| | | date, |
| | | selected, |
| | | startDate, |
| | | endDate, |
| | | range, |
| | | multiple, |
| | | allowSameDay |
| | | } = {}) { |
| | | // 当前日期 |
| | | this.date = this.getDate(new Date()) // 当前初入日期 |
| | | // 打点信息 |
| | | this.selected = selected || []; |
| | | // 范围开始 |
| | | this.startDate = startDate |
| | | // 范围结束 |
| | | this.endDate = endDate |
| | | this.range = range |
| | | this.multiple = multiple |
| | | this.allowSameDay = allowSameDay |
| | | // 多选状态 |
| | | this.cleanRangeStatus() |
| | | // 范围状态 |
| | | this.cleanMultipleStatus() |
| | | // 每周日期 |
| | | this.weeks = {} |
| | | // this._getWeek(this.date.fullDate) |
| | | } |
| | | /** |
| | | * 设置日期 |
| | | * @param {Object} date |
| | | */ |
| | | setDate(date, status) { |
| | | if (this.range && status == 'init') { |
| | | this.cleanRangeStatus(); |
| | | if (Array.isArray(date)) { |
| | | this.rangeStatus.before = date[0]; |
| | | this.rangeStatus.after = date.length > 1 ? date[date.length - 1] : ''; |
| | | if (this.rangeStatus.after && this.dateCompare(this.rangeStatus.before, this.rangeStatus.after)) { |
| | | this.rangeStatus.data = this.geDateAll(this.rangeStatus.before, this.rangeStatus.after) |
| | | } |
| | | this.selectDate = this.getDate(date[0]) |
| | | this._getWeek(this.selectDate.fullDate) |
| | | } else { |
| | | this.selectDate = this.getDate(date) |
| | | this.rangeStatus.before = this.selectDate.fullDate; |
| | | this._getWeek(this.selectDate.fullDate) |
| | | } |
| | | } else if (this.multiple && status == 'init') { |
| | | this.cleanMultipleStatus(); |
| | | if (Array.isArray(date)) { |
| | | this.multipleStatus.data = date; |
| | | this.selectDate = this.getDate(date[0]) |
| | | this._getWeek(this.selectDate.fullDate) |
| | | } else { |
| | | this.selectDate = this.getDate(date) |
| | | this.multipleStatus.data = [this.selectDate.fullDate]; |
| | | this._getWeek(this.selectDate.fullDate) |
| | | } |
| | | } else { |
| | | if (Array.isArray(date)) { |
| | | this.selectDate = this.getDate(date[0]) |
| | | this._getWeek(this.selectDate.fullDate) |
| | | } else { |
| | | this.selectDate = this.getDate(date) |
| | | this._getWeek(this.selectDate.fullDate) |
| | | } |
| | | } |
| | | } |
| | | /** |
| | | * 清理多选状态 |
| | | */ |
| | | cleanRangeStatus() { |
| | | this.rangeStatus = { |
| | | before: '', |
| | | after: '', |
| | | data: [] |
| | | } |
| | | } |
| | | /** |
| | | * 清理多选状态 |
| | | */ |
| | | cleanMultipleStatus() { |
| | | this.multipleStatus = { |
| | | data: [] |
| | | } |
| | | } |
| | | /** |
| | | * 重置开始日期 |
| | | */ |
| | | resetSatrtDate(startDate) { |
| | | // 范围开始 |
| | | this.startDate = startDate |
| | | } |
| | | /** |
| | | * 重置结束日期 |
| | | */ |
| | | resetEndDate(endDate) { |
| | | // 范围结束 |
| | | this.endDate = endDate |
| | | } |
| | | /** |
| | | * 获取任意时间 |
| | | */ |
| | | getDate(date, AddDayCount = 0, str = 'day') { |
| | | if (!date) { |
| | | date = new Date() |
| | | } |
| | | if (typeof date !== 'object') { |
| | | date = date.replace(/-/g, '/') |
| | | } |
| | | const dd = new Date(date) |
| | | switch (str) { |
| | | case 'day': |
| | | dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期 |
| | | break |
| | | case 'month': |
| | | if (dd.getDate() === 31 && AddDayCount > 0) { |
| | | dd.setDate(dd.getDate() + AddDayCount) |
| | | } else { |
| | | const preMonth = dd.getMonth() |
| | | dd.setMonth(preMonth + AddDayCount) // 获取AddDayCount天后的日期 |
| | | const nextMonth = dd.getMonth() |
| | | // 处理 pre 切换月份目标月份为2月没有当前日(30 31) 切换错误问题 |
| | | if (AddDayCount < 0 && preMonth !== 0 && nextMonth - preMonth > AddDayCount) { |
| | | dd.setMonth(nextMonth + (nextMonth - preMonth + AddDayCount)) |
| | | } |
| | | // 处理 next 切换月份目标月份为2月没有当前日(30 31) 切换错误问题 |
| | | if (AddDayCount > 0 && nextMonth - preMonth > AddDayCount) { |
| | | dd.setMonth(nextMonth - (nextMonth - preMonth - AddDayCount)) |
| | | } |
| | | } |
| | | break |
| | | case 'year': |
| | | dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期 |
| | | break |
| | | } |
| | | const y = dd.getFullYear() |
| | | const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0 |
| | | const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0 |
| | | return { |
| | | fullDate: y + '-' + m + '-' + d, |
| | | year: y, |
| | | month: m, |
| | | date: d, |
| | | day: dd.getDay() |
| | | } |
| | | } |
| | | /** |
| | | * 获取上月剩余天数 |
| | | */ |
| | | _getLastMonthDays(firstDay, full) { |
| | | let dateArr = [] |
| | | for (let i = firstDay; i > 0; i--) { |
| | | const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate() |
| | | dateArr.push({ |
| | | date: beforeDate, |
| | | month: full.month - 1, |
| | | lunar: this.getlunar(full.year, full.month - 1, beforeDate), |
| | | disable: true |
| | | }) |
| | | } |
| | | return dateArr |
| | | } |
| | | /** |
| | | * 获取本月天数 |
| | | */ |
| | | _currentMonthDys(dateData, full) { |
| | | let dateArr = [] |
| | | let fullDate = this.date.fullDate |
| | | for (let i = 1; i <= dateData; i++) { |
| | | let nowDate = full.year + '-' + (full.month < 10 ? full.month : full.month) + '-' + (i < 10 ? '0' + i : i) |
| | | // 是否今天 |
| | | let isDay = fullDate === nowDate |
| | | // 获取打点信息 |
| | | let info = this.selected && this.selected.find((item) => { |
| | | if (this.dateEqual(nowDate, item.date)) { |
| | | return item |
| | | } |
| | | }) |
| | | // 日期禁用 |
| | | let disableBefore = true |
| | | let disableAfter = true |
| | | if (this.startDate) { |
| | | // let dateCompBefore = this.dateCompare(this.startDate, fullDate) |
| | | // disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate) |
| | | disableBefore = this.dateCompare(this.startDate, nowDate) |
| | | } |
| | | if (this.endDate) { |
| | | // let dateCompAfter = this.dateCompare(fullDate, this.endDate) |
| | | // disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate) |
| | | disableAfter = this.dateCompare(nowDate, this.endDate) |
| | | } |
| | | let ranges = this.rangeStatus.data |
| | | let checked = false |
| | | let rangesStatus = -1 |
| | | if (this.range) { |
| | | if (ranges) { |
| | | rangesStatus = ranges.findIndex((item) => { |
| | | return this.dateEqual(item, nowDate) |
| | | }) |
| | | } |
| | | if (rangesStatus !== -1) { |
| | | checked = true |
| | | } |
| | | } |
| | | let multiples = this.multipleStatus.data |
| | | let checked_multiple = false |
| | | let multiplesStatus = -1 |
| | | if (this.multiple) { |
| | | if (multiples) { |
| | | multiplesStatus = multiples.findIndex((item) => { |
| | | return this.dateEqual(item, nowDate) |
| | | }) |
| | | } |
| | | if (multiplesStatus !== -1) { |
| | | checked_multiple = true |
| | | } |
| | | } |
| | | let data = { |
| | | fullDate: nowDate, |
| | | year: full.year, |
| | | date: i, |
| | | range: this.range ? checked : false, |
| | | multiple: this.multiple ? checked_multiple : false, |
| | | beforeRange: this.dateEqual(this.rangeStatus.before, nowDate), |
| | | afterRange: this.dateEqual(this.rangeStatus.after, nowDate), |
| | | dateEqual: this.range && checked && this.dateEqual(this.rangeStatus.before, this.rangeStatus.after), |
| | | month: full.month, |
| | | lunar: this.getlunar(full.year, full.month, i), |
| | | disable: !(disableBefore && disableAfter), |
| | | isDay |
| | | } |
| | | if (info) { |
| | | data.extraInfo = info |
| | | } |
| | | dateArr.push(data) |
| | | } |
| | | return dateArr |
| | | } |
| | | /** |
| | | * 获取下月天数 |
| | | */ |
| | | _getNextMonthDays(surplus, full) { |
| | | let dateArr = [] |
| | | for (let i = 1; i < surplus + 1; i++) { |
| | | dateArr.push({ |
| | | date: i, |
| | | month: Number(full.month) + 1, |
| | | lunar: this.getlunar(full.year, Number(full.month) + 1, i), |
| | | disable: true |
| | | }) |
| | | } |
| | | return dateArr |
| | | } |
| | | /** |
| | | * 获取当前日期详情 |
| | | * @param {Object} date |
| | | */ |
| | | getInfo(date) { |
| | | if (!date) { |
| | | date = new Date() |
| | | } else if (Array.isArray(date)) { |
| | | date = date[0] |
| | | } |
| | | const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate) |
| | | return dateInfo |
| | | } |
| | | /** |
| | | * 比较时间大小 |
| | | */ |
| | | dateCompare(startDate, endDate) { |
| | | // 计算截止时间 |
| | | startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
| | | // 计算详细项的截止时间 |
| | | endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
| | | if (startDate <= endDate) { |
| | | return true |
| | | } else { |
| | | return false |
| | | } |
| | | } |
| | | /** |
| | | * 比较时间是否相等 |
| | | */ |
| | | dateEqual(before, after) { |
| | | // 计算截止时间 |
| | | before = new Date(before.replace('-', '/').replace('-', '/')) |
| | | // 计算详细项的截止时间 |
| | | after = new Date(after.replace('-', '/').replace('-', '/')) |
| | | if (before.getTime() - after.getTime() === 0) { |
| | | return true |
| | | } else { |
| | | return false |
| | | } |
| | | } |
| | | /** |
| | | * 比较after时间是否大于before时间 |
| | | */ |
| | | dateAfterLgBefore(before, after) { |
| | | // 计算截止时间 |
| | | before = new Date(before.replace('-', '/').replace('-', '/')) |
| | | // 计算详细项的截止时间 |
| | | after = new Date(after.replace('-', '/').replace('-', '/')) |
| | | if (after.getTime() - before.getTime() > 0) { |
| | | return true |
| | | } else { |
| | | return false |
| | | } |
| | | } |
| | | /** |
| | | * 获取日期范围内所有日期 |
| | | * @param {Object} begin |
| | | * @param {Object} end |
| | | */ |
| | | geDateAll(begin, end) { |
| | | var arr = [] |
| | | var ab = begin.split('-') |
| | | var ae = end.split('-') |
| | | var db = new Date() |
| | | db.setFullYear(ab[0], ab[1] - 1, ab[2]) |
| | | var de = new Date() |
| | | de.setFullYear(ae[0], ae[1] - 1, ae[2]) |
| | | var unixDb = db.getTime() - 24 * 60 * 60 * 1000 |
| | | var unixDe = de.getTime() - 24 * 60 * 60 * 1000 |
| | | for (var k = unixDb; k <= unixDe;) { |
| | | k = k + 24 * 60 * 60 * 1000 |
| | | arr.push(this.getDate(new Date(parseInt(k))).fullDate) |
| | | } |
| | | return arr |
| | | } |
| | | /** |
| | | * 计算阴历日期显示 |
| | | */ |
| | | getlunar(year, month, date) { |
| | | return CALENDAR.solar2lunar(year, month, date) |
| | | } |
| | | /** |
| | | * 设置打点 |
| | | */ |
| | | setSelectInfo(data, value) { |
| | | this.selected = value |
| | | this._getWeek(data) |
| | | } |
| | | /** |
| | | * 获取多选状态 |
| | | */ |
| | | setMultiple(fullDate) { |
| | | if (!this.multiple) return |
| | | let multiples = this.multipleStatus.data; |
| | | const findIndex = multiples.findIndex(item => this.dateEqual(fullDate, item)); |
| | | if (findIndex < 0) { |
| | | this.multipleStatus.data = this.multipleStatus.data.concat([fullDate]); |
| | | } else { |
| | | this.multipleStatus.data.splice(findIndex, 1); |
| | | } |
| | | this._getWeek(fullDate) |
| | | } |
| | | /** |
| | | * 获取范围状态 |
| | | */ |
| | | setRange(fullDate) { |
| | | let { |
| | | before, |
| | | after |
| | | } = this.rangeStatus |
| | | if (!this.range) return |
| | | if (before && after) { |
| | | this.cleanRangeStatus(); |
| | | this.rangeStatus.before = fullDate |
| | | } else { |
| | | if (!before) { |
| | | this.rangeStatus.before = fullDate |
| | | } else { |
| | | if (this.allowSameDay && this.dateEqual(before, fullDate)) { |
| | | this.rangeStatus.after = fullDate |
| | | } else if (!this.dateAfterLgBefore(this.rangeStatus.before, fullDate)) { |
| | | this.cleanRangeStatus(); |
| | | this.rangeStatus.before = fullDate |
| | | this._getWeek(fullDate) |
| | | return; |
| | | } |
| | | this.rangeStatus.after = fullDate |
| | | if (this.dateCompare(this.rangeStatus.before, this.rangeStatus.after)) { |
| | | this.rangeStatus.data = this.geDateAll(this.rangeStatus.before, this.rangeStatus.after); |
| | | } else { |
| | | this.rangeStatus.data = this.geDateAll(this.rangeStatus.after, this.rangeStatus.before); |
| | | } |
| | | } |
| | | } |
| | | this._getWeek(fullDate) |
| | | } |
| | | /** |
| | | * 获取每周数据 |
| | | * @param {Object} dateData |
| | | */ |
| | | _getWeek(dateData) { |
| | | const { |
| | | year, |
| | | month |
| | | } = this.getDate(dateData) |
| | | let firstDay = new Date(year, month - 1, 1).getDay() |
| | | let currentDay = new Date(year, month, 0).getDate() |
| | | let dates = { |
| | | lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天 |
| | | currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数 |
| | | nextMonthDays: [], // 下个月开始几天 |
| | | weeks: [] |
| | | } |
| | | let canlender = [] |
| | | const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length) |
| | | dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData)) |
| | | canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays) |
| | | let weeks = {} |
| | | // 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天 |
| | | for (let i = 0; i < canlender.length; i++) { |
| | | if (i % 7 === 0) { |
| | | weeks[parseInt(i / 7)] = new Array(7) |
| | | } |
| | | weeks[parseInt(i / 7)][i % 7] = canlender[i] |
| | | } |
| | | this.canlender = canlender |
| | | this.weeks = weeks |
| | | } |
| | | //静态方法 |
| | | // static init(date) { |
| | | // if (!this.instance) { |
| | | // this.instance = new Calendar(date); |
| | | // } |
| | | // return this.instance; |
| | | // } |
| | | } |
| | | export default Calendar |
| New file |
| | |
| | | <template> |
| | | <view class="uv-calendar"> |
| | | <view class="uv-calendar__content" v-if="insert"> |
| | | <calendar-body |
| | | :date="date" |
| | | :nowDate="nowDate" |
| | | :weeks="weeks" |
| | | :calendar="calendar" |
| | | :selected="selected" |
| | | :lunar="lunar" |
| | | :showMonth="showMonth" |
| | | :color="color" |
| | | :startText="startText" |
| | | :endText="endText" |
| | | :range="range" |
| | | :multiple="multiple" |
| | | :allowSameDay="allowSameDay" |
| | | @bindDateChange="bindDateChange" |
| | | @pre="pre" |
| | | @next="next" |
| | | @backToday="backToday" |
| | | @choiceDate="choiceDate" |
| | | ></calendar-body> |
| | | </view> |
| | | <uv-popup ref="popup" mode="bottom" v-else :round="round" z-index="998" :close-on-click-overlay="closeOnClickOverlay" @maskClick="maskClick"> |
| | | <view style="min-height: 100px;"> |
| | | <uv-toolbar |
| | | :show="true" |
| | | :cancelColor="cancelColor" |
| | | :confirmColor="getConfirmColor" |
| | | :cancelText="cancelText" |
| | | :confirmText="confirmText" |
| | | :title="title" |
| | | @cancel="close" |
| | | @confirm="confirm"></uv-toolbar> |
| | | <view class="line"></view> |
| | | <calendar-body |
| | | :nowDate="nowDate" |
| | | :weeks="weeks" |
| | | :calendar="calendar" |
| | | :selected="selected" |
| | | :lunar="lunar" |
| | | :showMonth="showMonth" |
| | | :color="color" |
| | | :startText="startText" |
| | | :endText="endText" |
| | | :range="range" |
| | | :multiple="multiple" |
| | | :allowSameDay="allowSameDay" |
| | | @bindDateChange="bindDateChange" |
| | | @pre="pre" |
| | | @next="next" |
| | | @backToday="backToday" |
| | | @choiceDate="choiceDate" |
| | | ></calendar-body> |
| | | </view> |
| | | </uv-popup> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | /** |
| | | * Calendar 日历 |
| | | * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-calendar |
| | | * @property {String} date 自定义当前时间,默认为今天 |
| | | * @property {Boolean} lunar 显示农历 |
| | | * @property {String} startDate 日期选择范围-开始日期 |
| | | * @property {String} endDate 日期选择范围-结束日期 |
| | | * @property {String} mode = [不传 | multiple | range ] 多个日期 | 选择日期范围 默认单日期 |
| | | * @property {Boolean} insert = [true|false] 插入模式,默认为false |
| | | * @value true 弹窗模式 |
| | | * @value false 插入模式 |
| | | * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容 |
| | | * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
| | | * @property {String} cancelColor 取消按钮颜色 |
| | | * @property {String} confirmColor 确认按钮颜色,默认#3c9cff |
| | | * @property {String} title 头部工具条中间的标题文字 |
| | | * @property {String} color 主题色,默认#3c9cff |
| | | * @property {Number} round :insert="false"时的圆角 |
| | | * @property {Boolean} closeOnClickOverlay 点击遮罩是否关闭 |
| | | * @property {String} startText range为true时,第一个日期底部的提示文字 |
| | | * @property {String} endText range为true时,最后一个日期底部的提示文字 |
| | | * @property {String} readonly 是否为只读状态,只读状态下禁止选择日期,默认false |
| | | * |
| | | * @event {Function} change 日期改变,`insert :ture` 时生效 |
| | | * @event {Function} confirm 确认选择`insert :false` 时生效 |
| | | * @event {Function} monthSwitch 切换月份时触发 |
| | | * |
| | | * @example <uv-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> |
| | | */ |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'; |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'; |
| | | import Calendar from './util.js'; |
| | | import calendarBody from './calendar-body.vue'; |
| | | import { initVueI18n } from '@dcloudio/uni-i18n'; |
| | | import i18nMessages from './i18n/index.js'; |
| | | const { t } = initVueI18n(i18nMessages); |
| | | export default { |
| | | components: { |
| | | calendarBody |
| | | }, |
| | | mixins: [mpMixin, mixin], |
| | | emits: ['close', 'confirm', 'change', 'monthSwitch'], |
| | | props: { |
| | | // 取消按钮颜色 |
| | | cancelColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 确认按钮颜色,range模式下未选全显示灰色 |
| | | confirmColor: { |
| | | type: String, |
| | | default: '#3c9cff' |
| | | }, |
| | | // 标题 |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 主题色 |
| | | color: { |
| | | type: String, |
| | | default: '#3c9cff' |
| | | }, |
| | | // 默认显示日期 |
| | | date: { |
| | | type: [String,Array], |
| | | default: '' |
| | | }, |
| | | // 打点等设置 |
| | | selected: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | // 是否显示农历 |
| | | lunar: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 可选择的起始日期 |
| | | startDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 可选择的结束日期 |
| | | endDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // multiple - 选择多日期 range - 选择日期范围 |
| | | mode: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否插入模式 |
| | | insert: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示月份为背景 |
| | | showMonth: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 弹窗模式是否清空上次选择内容 |
| | | clearDate: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 弹窗圆角 |
| | | round: { |
| | | type: [Number,String], |
| | | default: 8 |
| | | }, |
| | | // 点击遮罩是否关闭弹窗 |
| | | closeOnClickOverlay: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // range为true时,第一个日期底部的提示文字 |
| | | startText: { |
| | | type: String, |
| | | default: '开始' |
| | | }, |
| | | // range为true时,最后一个日期底部的提示文字 |
| | | endText: { |
| | | type: String, |
| | | default: '结束' |
| | | }, |
| | | // 是否允许日期范围的起止时间为同一天,mode = range时有效 |
| | | allowSameDay: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否禁用 |
| | | readonly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ...uni.$uv?.props?.calendars |
| | | }, |
| | | data(){ |
| | | return { |
| | | weeks: [], |
| | | calendar: {}, |
| | | nowDate: '', |
| | | allowConfirm: false, |
| | | multiple: false, |
| | | range: false |
| | | } |
| | | }, |
| | | computed:{ |
| | | /** |
| | | * for i18n |
| | | */ |
| | | confirmText() { |
| | | return t("uv-calender.ok") |
| | | }, |
| | | cancelText() { |
| | | return t("uv-calender.cancel") |
| | | }, |
| | | getConfirmColor() { |
| | | if(this.range || this.multiple || this.readonly) { |
| | | return this.allowConfirm? this.confirmColor: '#999' |
| | | }else { |
| | | return this.confirmColor; |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | date(newVal) { |
| | | this.init(newVal) |
| | | }, |
| | | startDate(val) { |
| | | this.cale.resetSatrtDate(val) |
| | | this.cale.setDate(this.nowDate.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | endDate(val) { |
| | | this.cale.resetEndDate(val) |
| | | this.cale.setDate(this.nowDate.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | selected(newVal) { |
| | | this.cale.setSelectInfo(this.nowDate.fullDate, newVal) |
| | | this.weeks = this.cale.weeks |
| | | } |
| | | }, |
| | | created() { |
| | | this.setMode(); |
| | | this.cale = new Calendar({ |
| | | selected: this.selected, |
| | | startDate: this.startDate, |
| | | endDate: this.endDate, |
| | | range: this.range, |
| | | multiple: this.multiple, |
| | | allowSameDay: this.allowSameDay |
| | | }) |
| | | this.init(this.date) |
| | | }, |
| | | methods: { |
| | | setMode() { |
| | | switch (this.mode){ |
| | | case 'range': |
| | | this.range = true; |
| | | break; |
| | | case 'multiple': |
| | | this.multiple = true; |
| | | default: |
| | | break; |
| | | } |
| | | }, |
| | | async open() { |
| | | if (this.clearDate && !this.insert) { |
| | | this.cale.cleanRangeStatus() |
| | | this.init(this.date) |
| | | } |
| | | if(!this.insert){ |
| | | this.$refs.popup.open(); |
| | | } |
| | | }, |
| | | close() { |
| | | this.$refs.popup.close(); |
| | | this.$emit('close'); |
| | | }, |
| | | confirm() { |
| | | if(this.readonly) { |
| | | return; |
| | | } else if(this.range && !this.cale.rangeStatus.after) { |
| | | return; |
| | | } else if(this.multiple && this.cale.multipleStatus.data.length == 0){ |
| | | return; |
| | | } |
| | | this.setEmit('confirm'); |
| | | this.close() |
| | | }, |
| | | maskClick() { |
| | | if(this.closeOnClickOverlay) { |
| | | this.$emit('close'); |
| | | } |
| | | }, |
| | | bindDateChange(e) { |
| | | const value = e.detail.value + '-1' |
| | | this.setDate(value) |
| | | |
| | | const { year, month } = this.cale.getDate(value) |
| | | this.$emit('monthSwitch', { |
| | | year, |
| | | month |
| | | }) |
| | | }, |
| | | /** |
| | | * 初始化日期显示 |
| | | * @param {Object} date |
| | | */ |
| | | init(date) { |
| | | if(this.range) { |
| | | // 重置范围选择状态 |
| | | this.cale.cleanRangeStatus(); |
| | | }else if(this.multiple){ |
| | | // 重置多选状态 |
| | | this.cale.cleanMultipleStatus(); |
| | | } |
| | | this.cale.setDate(date,'init') |
| | | this.weeks = this.cale.weeks |
| | | this.nowDate = this.calendar = this.cale.getInfo(date) |
| | | this.changeConfirmStatus(); |
| | | }, |
| | | /** |
| | | * 变化触发 |
| | | */ |
| | | change() { |
| | | this.changeConfirmStatus(); |
| | | if (!this.insert) return |
| | | this.setEmit('change') |
| | | }, |
| | | changeConfirmStatus() { |
| | | if(this.readonly) { |
| | | this.allowConfirm = false; |
| | | } else if (this.range) { |
| | | this.allowConfirm = this.cale.rangeStatus.after ? true : false; |
| | | } else if(this.multiple) { |
| | | this.allowConfirm = this.cale.multipleStatus.data.length > 0 ? true : false; |
| | | } |
| | | }, |
| | | /** |
| | | * 选择月份触发 |
| | | */ |
| | | monthSwitch() { |
| | | let { |
| | | year, |
| | | month |
| | | } = this.nowDate |
| | | this.$emit('monthSwitch', { |
| | | year, |
| | | month: Number(month) |
| | | }) |
| | | }, |
| | | /** |
| | | * 派发事件 |
| | | * @param {Object} name |
| | | */ |
| | | setEmit(name) { |
| | | let { |
| | | year, |
| | | month, |
| | | date, |
| | | fullDate, |
| | | lunar, |
| | | extraInfo |
| | | } = this.calendar |
| | | this.$emit(name, { |
| | | range: this.cale.rangeStatus, |
| | | multiple: this.cale.multipleStatus, |
| | | year, |
| | | month, |
| | | date, |
| | | fulldate: fullDate, |
| | | lunar, |
| | | extraInfo: extraInfo || {} |
| | | }) |
| | | }, |
| | | /** |
| | | * 选择天触发 |
| | | * @param {Object} weeks |
| | | */ |
| | | choiceDate(weeks) { |
| | | if (weeks.disable || this.readonly) return |
| | | this.calendar = weeks |
| | | // 设置范围选择 |
| | | this.cale.setRange(this.calendar.fullDate) |
| | | // 设置多选 |
| | | this.cale.setMultiple(this.calendar.fullDate); |
| | | this.weeks = this.cale.weeks |
| | | this.change() |
| | | }, |
| | | /** |
| | | * 回到今天 |
| | | */ |
| | | backToday() { |
| | | const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}` |
| | | const date = this.cale.getDate(new Date()) |
| | | const todayYearMonth = `${date.year}-${date.month}` |
| | | this.init(date.fullDate) |
| | | if (nowYearMonth !== todayYearMonth) { |
| | | this.monthSwitch() |
| | | } |
| | | this.change() |
| | | }, |
| | | /** |
| | | * 上个月 |
| | | */ |
| | | pre() { |
| | | const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate |
| | | this.setDate(preDate) |
| | | this.monthSwitch() |
| | | }, |
| | | /** |
| | | * 下个月 |
| | | */ |
| | | next() { |
| | | const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate |
| | | this.setDate(nextDate) |
| | | this.monthSwitch() |
| | | }, |
| | | /** |
| | | * 设置日期 |
| | | * @param {Object} date |
| | | */ |
| | | setDate(date) { |
| | | this.cale.setDate(date) |
| | | this.weeks = this.cale.weeks |
| | | this.nowDate = this.cale.getInfo(date) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | $uv-border-color: #EDEDED !default; |
| | | .uv-calendar__content { |
| | | background-color: #fff; |
| | | } |
| | | .line { |
| | | width: 750rpx; |
| | | height: 1px; |
| | | border-bottom-color: $uv-border-color; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-calendars", |
| | | "displayName": "uv-calendars 最新日历 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.15", |
| | | "description": "新版本uv-calendars,不仅拥有老版本的所有功能,还增加了更加适用的插入页面等强大功能,且更加简洁。查看日期、选择单个或多个或任意范围日期,打点操作,自定义文案,自定义主题等强大功能。", |
| | | "keywords": [ |
| | | "uv-ui", |
| | | "uvui", |
| | | "日历", |
| | | "打卡", |
| | | "日历选择" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration":{ |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-popup", |
| | | "uv-toolbar" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Calendars 全新日历 |
| | | |
| | | > **组件名:uv-calendars** |
| | | |
| | | 为了解决老版本`uv-calendar`性能问题,特别是对日期选择范围有很大限制,体验不友好等缺点。于是有了新版日历组件。 |
| | | |
| | | 新版本`uv-calendars`,不仅拥有老版本的所有功能,还增加了更加适用的插入页面等强大功能,且更加简洁。查看日期、选择单个或多个或任意范围日期,打点操作,自定义文案,自定义主题等强大功能。 |
| | | |
| | | 常用场景:酒店日期预订、火车机票选择购买日期、上下班打卡等。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/calendars.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.5(2023-12-06) |
| | | 1. 修复uv-cell right-icon插槽编译到APP端不显示的BUG,问题来源:https://gitee.com/climblee/uv-ui/issues/I8LXZI |
| | | ## 1.0.4(2023-09-19) |
| | | 1. 增加cellStyle参数,方便自定义单元格的样式 |
| | | ## 1.0.3(2023-07-03) |
| | | 去除插槽判断,避免某些平台不显示的BUG |
| | | ## 1.0.2(2023-06-21) |
| | | 1. 优化 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-cell 单元格 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 分组标题 |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否显示外边框 |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | ...uni.$uv?.props?.cellGroup |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view :style="[$uv.addStyle(customStyle)]" :class="[customClass]" class="uv-cell-group"> |
| | | <view v-if="title" class="uv-cell-group__title"> |
| | | <slot name="title"> |
| | | <text class="uv-cell-group__title__text">{{ title }}</text> |
| | | </slot> |
| | | </view> |
| | | <view class="uv-cell-group__wrapper"> |
| | | <uv-line v-if="border"></uv-line> |
| | | <slot /> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * cellGroup 单元格 |
| | | * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。 |
| | | * @tutorial https://www.uvui.cn/components/cell.html |
| | | * |
| | | * @property {String} title 分组标题 |
| | | * @property {Boolean} border 是否显示外边框 (默认 true ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * |
| | | * @event {Function} click 点击cell列表时触发 |
| | | * @example <uv-cell-group title="设置喜好"> |
| | | */ |
| | | export default { |
| | | name: 'uv-cell-group', |
| | | mixins: [mpMixin, mixin, props] |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-cell-group-title-padding: 16px 16px 8px !default; |
| | | $uv-cell-group-title-font-size: 15px !default; |
| | | $uv-cell-group-title-line-height: 16px !default; |
| | | $uv-cell-group-title-color: $uv-main-color !default; |
| | | |
| | | .uv-cell-group { |
| | | flex: 1; |
| | | |
| | | &__title { |
| | | padding: $uv-cell-group-title-padding; |
| | | |
| | | &__text { |
| | | font-size: $uv-cell-group-title-font-size; |
| | | line-height: $uv-cell-group-title-line-height; |
| | | color: $uv-cell-group-title-color; |
| | | } |
| | | } |
| | | |
| | | &__wrapper { |
| | | position: relative; |
| | | } |
| | | } |
| | | </style> |
| | | |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 标题 |
| | | title: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 标题下方的描述信息 |
| | | label: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 右侧的内容 |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 左侧图标名称,或者图片链接(本地文件建议使用绝对地址) |
| | | icon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否禁用cell |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示下边框 |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 内容是否垂直居中(主要是针对右侧的value部分) |
| | | center: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 点击后跳转的URL地址 |
| | | url: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 链接跳转的方式,内部使用的是uvui封装的route方法,可能会进行拦截操作 |
| | | linkType: { |
| | | type: String, |
| | | default: 'navigateTo' |
| | | }, |
| | | // 是否开启点击反馈(表现为点击时加上灰色背景) |
| | | clickable: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否展示右侧箭头并开启点击反馈 |
| | | isLink: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示表单状态下的必填星号(此组件可能会内嵌入input组件) |
| | | required: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 右侧的图标箭头 |
| | | rightIcon: { |
| | | type: String, |
| | | default: 'arrow-right' |
| | | }, |
| | | // 右侧箭头的方向,可选值为:left,up,down |
| | | arrowDirection: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 左侧图标样式 |
| | | iconStyle: { |
| | | type: [Object, String], |
| | | default: () => { |
| | | return {} |
| | | } |
| | | }, |
| | | // 右侧箭头图标的样式 |
| | | rightIconStyle: { |
| | | type: [Object, String], |
| | | default: () => { |
| | | return {} |
| | | } |
| | | }, |
| | | // 标题的样式 |
| | | titleStyle: { |
| | | type: [Object, String], |
| | | default: () => { |
| | | return {} |
| | | } |
| | | }, |
| | | // 单位元的大小,可选值为large |
| | | size: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 点击cell是否阻止事件传播 |
| | | stop: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 标识符,cell被点击时返回 |
| | | name: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | // 单元格自定义样式 |
| | | cellStyle: { |
| | | type: [Object, String], |
| | | default: () => {} |
| | | }, |
| | | ...uni.$uv?.props?.cell |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-cell" :class="[customClass]" :style="[$uv.addStyle(customStyle)]" |
| | | :hover-class="(!disabled && (clickable || isLink)) ? 'uv-cell--clickable' : ''" :hover-stay-time="250" |
| | | @click="clickHandler"> |
| | | <view class="uv-cell__body" |
| | | :class="[ center && 'uv-cell--center', size === 'large' && 'uv-cell__body--large']" |
| | | :style="[cellStyle]" |
| | | > |
| | | <view class="uv-cell__body__content"> |
| | | <view class="uv-cell__left-icon-wrap"> |
| | | <slot name="icon"> |
| | | <uv-icon v-if="icon" :name="icon" :custom-style="iconStyle" :size="size === 'large' ? 22 : 18"></uv-icon> |
| | | </slot> |
| | | </view> |
| | | <view class="uv-cell__title"> |
| | | <slot name="title"> |
| | | <text v-if="title" class="uv-cell__title-text" :style="[titleTextStyle]" |
| | | :class="[disabled && 'uv-cell--disabled', size === 'large' && 'uv-cell__title-text--large']">{{ title }}</text> |
| | | </slot> |
| | | <slot name="label"> |
| | | <text class="uv-cell__label" v-if="label" |
| | | :class="[disabled && 'uv-cell--disabled', size === 'large' && 'uv-cell__label--large']">{{ label }}</text> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | <slot name="value"> |
| | | <text class="uv-cell__value" |
| | | :class="[disabled && 'uv-cell--disabled', size === 'large' && 'uv-cell__value--large']" |
| | | v-if="!$uv.test.empty(value)">{{ value }}</text> |
| | | </slot> |
| | | <view class="uv-cell__right-icon-wrap" |
| | | :class="[`uv-cell__right-icon-wrap--${arrowDirection}`]"> |
| | | <slot name="right-icon"> |
| | | <uv-icon v-if="isLink" :name="rightIcon" :custom-style="rightIconStyle" :color="disabled ? '#c8c9cc' : 'info'" |
| | | :size="size === 'large' ? 18 : 16"></uv-icon> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | <uv-line v-if="border"></uv-line> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * cell 单元格 |
| | | * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。 |
| | | * @tutorial https://www.uvui.cn/components/cell.html |
| | | * @property {String | Number} title 标题 |
| | | * @property {String | Number} label 标题下方的描述信息 |
| | | * @property {String | Number} value 右侧的内容 |
| | | * @property {String} icon 左侧图标名称,或者图片链接(本地文件建议使用绝对地址) |
| | | * @property {Boolean} disabled 是否禁用cell |
| | | * @property {Boolean} border 是否显示下边框 (默认 true ) |
| | | * @property {Boolean} center 内容是否垂直居中(主要是针对右侧的value部分) (默认 false ) |
| | | * @property {String} url 点击后跳转的URL地址 |
| | | * @property {String} linkType 链接跳转的方式,内部使用的是uvui封装的route方法,可能会进行拦截操作 (默认 'navigateTo' ) |
| | | * @property {Boolean} clickable 是否开启点击反馈(表现为点击时加上灰色背景) (默认 false ) |
| | | * @property {Boolean} isLink 是否展示右侧箭头并开启点击反馈 (默认 false ) |
| | | * @property {Boolean} required 是否显示表单状态下的必填星号(此组件可能会内嵌入input组件) (默认 false ) |
| | | * @property {String} rightIcon 右侧的图标箭头 (默认 'arrow-right') |
| | | * @property {String} arrowDirection 右侧箭头的方向,可选值为:left,up,down |
| | | * @property {Object | String} rightIconStyle 右侧箭头图标的样式 |
| | | * @property {Object | String} titleStyle 标题的样式 |
| | | * @property {Object | String} iconStyle 左侧图标样式 |
| | | * @property {String} size 单位元的大小,可选值为 large,normal,mini |
| | | * @property {Boolean} stop 点击cell是否阻止事件传播 (默认 true ) |
| | | * @property {Object | String} cellStyle 单元格自定义样式 |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * |
| | | * @event {Function} click 点击cell列表时触发 |
| | | * @example 该组件需要搭配cell-group组件使用,见官方文档示例 |
| | | */ |
| | | export default { |
| | | name: 'uv-cell', |
| | | emits: ['click'], |
| | | mixins: [mpMixin, mixin, props], |
| | | computed: { |
| | | titleTextStyle() { |
| | | return this.$uv.addStyle(this.titleStyle) |
| | | } |
| | | }, |
| | | methods: { |
| | | // 点击cell |
| | | clickHandler(e) { |
| | | if (this.disabled) return |
| | | this.$emit('click', { |
| | | name: this.name |
| | | }) |
| | | // 如果配置了url(此props参数通过mixin引入)参数,跳转页面 |
| | | this.openPage() |
| | | // 是否阻止事件传播 |
| | | this.stop && this.preventEvent(e) |
| | | }, |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-cell-padding: 10px 15px !default; |
| | | $uv-cell-font-size: 15px !default; |
| | | $uv-cell-line-height: 24px !default; |
| | | $uv-cell-color: $uv-main-color !default; |
| | | $uv-cell-icon-size: 16px !default; |
| | | $uv-cell-title-font-size: 15px !default; |
| | | $uv-cell-title-line-height: 22px !default; |
| | | $uv-cell-title-color: $uv-main-color !default; |
| | | $uv-cell-label-font-size: 12px !default; |
| | | $uv-cell-label-color: $uv-tips-color !default; |
| | | $uv-cell-label-line-height: 18px !default; |
| | | $uv-cell-value-font-size: 14px !default; |
| | | $uv-cell-value-color: $uv-content-color !default; |
| | | $uv-cell-clickable-color: $uv-bg-color !default; |
| | | $uv-cell-disabled-color: #c8c9cc !default; |
| | | $uv-cell-padding-top-large: 13px !default; |
| | | $uv-cell-padding-bottom-large: 13px !default; |
| | | $uv-cell-value-font-size-large: 15px !default; |
| | | $uv-cell-label-font-size-large: 14px !default; |
| | | $uv-cell-title-font-size-large: 16px !default; |
| | | $uv-cell-left-icon-wrap-margin-right: 4px !default; |
| | | $uv-cell-right-icon-wrap-margin-left: 4px !default; |
| | | $uv-cell-title-flex: 1 !default; |
| | | $uv-cell-label-margin-top: 5px !default; |
| | | .uv-cell { |
| | | &__body { |
| | | @include flex(); |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | padding: $uv-cell-padding; |
| | | font-size: $uv-cell-font-size; |
| | | color: $uv-cell-color; |
| | | &__content { |
| | | @include flex(row); |
| | | align-items: center; |
| | | flex: 1; |
| | | } |
| | | &--large { |
| | | padding-top: $uv-cell-padding-top-large; |
| | | padding-bottom: $uv-cell-padding-bottom-large; |
| | | } |
| | | } |
| | | &__left-icon-wrap, |
| | | &__right-icon-wrap { |
| | | @include flex(); |
| | | align-items: center; |
| | | // height: $uv-cell-line-height; |
| | | font-size: $uv-cell-icon-size; |
| | | } |
| | | &__left-icon-wrap { |
| | | margin-right: $uv-cell-left-icon-wrap-margin-right; |
| | | } |
| | | &__right-icon-wrap { |
| | | margin-left: $uv-cell-right-icon-wrap-margin-left; |
| | | transition: transform 0.3s; |
| | | &--up { |
| | | transform: rotate(-90deg); |
| | | } |
| | | &--down { |
| | | transform: rotate(90deg); |
| | | } |
| | | } |
| | | &__title { |
| | | flex: $uv-cell-title-flex; |
| | | &-text { |
| | | font-size: $uv-cell-title-font-size; |
| | | line-height: $uv-cell-title-line-height; |
| | | color: $uv-cell-title-color; |
| | | &--large { |
| | | font-size: $uv-cell-title-font-size-large; |
| | | } |
| | | } |
| | | } |
| | | &__label { |
| | | margin-top: $uv-cell-label-margin-top; |
| | | font-size: $uv-cell-label-font-size; |
| | | color: $uv-cell-label-color; |
| | | line-height: $uv-cell-label-line-height; |
| | | &--large { |
| | | font-size: $uv-cell-label-font-size-large; |
| | | } |
| | | } |
| | | &__value { |
| | | text-align: right; |
| | | font-size: $uv-cell-value-font-size; |
| | | line-height: $uv-cell-line-height; |
| | | color: $uv-cell-value-color; |
| | | &--large { |
| | | font-size: $uv-cell-value-font-size-large; |
| | | } |
| | | } |
| | | &--clickable { |
| | | background-color: $uv-cell-clickable-color; |
| | | } |
| | | &--disabled { |
| | | color: $uv-cell-disabled-color; |
| | | /* #ifndef APP-NVUE */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | &--center { |
| | | align-items: center; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-cell", |
| | | "displayName": "uv-cell 单元格 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.5", |
| | | "description": "cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。", |
| | | "keywords": [ |
| | | "uv-cell", |
| | | "uvui", |
| | | "uv-ui", |
| | | "单元格", |
| | | "设置页" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-icon", |
| | | "uv-line" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Cell 单元格 |
| | | |
| | | > **组件名:uv-cell** |
| | | |
| | | cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/cell.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.14(2023-11-04) |
| | | 1. 修复label文字较多不分行的问题 |
| | | ## 1.0.13(2023-10-11) |
| | | 1. 优化同类问题:https://gitee.com/climblee/uv-ui/issues/I872VD |
| | | ## 1.0.12(2023-09-22) |
| | | 1. 修复change回调中v-model值不更新的BUG |
| | | ## 1.0.11(2023-09-01) |
| | | 1. 修复点击空隙处无效的问题 |
| | | 2. label支持插槽下可点击 |
| | | ## 1.0.10(2023-08-27) |
| | | 1. 修复label设置布尔值不生效的BUG |
| | | ## 1.0.9(2023-08-16) |
| | | 1. 解决数据多不换行的BUG |
| | | ## 1.0.8(2023-07-13) |
| | | 1. 修复 uv-checkbox设置value属性不生效的BUG |
| | | ## 1.0.7(2023-07-05) |
| | | 修复vue3模式下,动态修改v-model绑定的值无效的BUG |
| | | ## 1.0.6(2023-06-29) |
| | | 1. 增加label插槽,与radio保持一致 |
| | | 2. 优化文档 |
| | | ## 1.0.5(2023-06-12) |
| | | 1. 修复1.0.4改出的问题 |
| | | ## 1.0.4(2023-06-08) |
| | | 1. 复选框修复全局设置不生效的BUG |
| | | ## 1.0.3(2023-06-06) |
| | | 1. uv-checkbox-group 兼容自定义样式customStyle,方便通过样式调整整体位置等; |
| | | 2. .uv-checkbox-group--row增加flex-wrap: wrap;允许换行 |
| | | ## 1.0.2(2023-05-30) |
| | | 1. 修复error报错的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-checkbox 复选框 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 绑定的值 |
| | | value: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | modelValue: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | // 标识符 |
| | | name: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 形状,circle-圆形,square-方形 |
| | | shape: { |
| | | type: String, |
| | | default: 'square' |
| | | }, |
| | | // 是否禁用全部checkbox |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 |
| | | activeColor: { |
| | | type: String, |
| | | default: '#2979ff' |
| | | }, |
| | | // 未选中的颜色 |
| | | inactiveColor: { |
| | | type: String, |
| | | default: '#c8c9cc' |
| | | }, |
| | | // 整个组件的尺寸,默认px |
| | | size: { |
| | | type: [String, Number], |
| | | default: 18 |
| | | }, |
| | | // 布局方式,row-横向,column-纵向 |
| | | placement: { |
| | | type: String, |
| | | default: 'row' |
| | | }, |
| | | // label的字体大小,px单位 |
| | | labelSize: { |
| | | type: [String, Number], |
| | | default: 14 |
| | | }, |
| | | // label的字体颜色 |
| | | labelColor: { |
| | | type: [String], |
| | | default: '#303133' |
| | | }, |
| | | // 是否禁止点击文本操作 |
| | | labelDisabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 图标颜色 |
| | | iconColor: { |
| | | type: String, |
| | | default: '#fff' |
| | | }, |
| | | // 图标的大小,单位px |
| | | iconSize: { |
| | | type: [String, Number], |
| | | default: 12 |
| | | }, |
| | | // 勾选图标的对齐方式,left-左边,right-右边 |
| | | iconPlacement: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | // 竖向配列时,是否显示下划线 |
| | | borderBottom: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ...uni.$uv?.props?.checkboxGroup |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-checkbox-group" |
| | | :class="bemClass" |
| | | :style="[$uv.addStyle(this.customStyle)]" |
| | | > |
| | | <slot></slot> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * checkboxGroup 复选框组 |
| | | * @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便 |
| | | * @tutorial https://www.uvui.cn/components/checkbox.html |
| | | * @property {String} name 标识符 |
| | | * @property {Array} value 绑定的值 |
| | | * @property {String} shape 形状,circle-圆形,square-方形 (默认 'square' ) |
| | | * @property {Boolean} disabled 是否禁用全部checkbox (默认 false ) |
| | | * @property {String} activeColor 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 (默认 '#2979ff' ) |
| | | * @property {String} inactiveColor 未选中的颜色 (默认 '#c8c9cc' ) |
| | | * @property {String | Number} size 整个组件的尺寸 单位px (默认 18 ) |
| | | * @property {String} placement 布局方式,row-横向,column-纵向 (默认 'row' ) |
| | | * @property {String | Number} labelSize label的字体大小,px单位 (默认 14 ) |
| | | * @property {String} labelColor label的字体颜色 (默认 '#303133' ) |
| | | * @property {Boolean} labelDisabled 是否禁止点击文本操作 (默认 false ) |
| | | * @property {String} iconColor 图标颜色 (默认 '#ffffff' ) |
| | | * @property {String | Number} iconSize 图标的大小,单位px (默认 12 ) |
| | | * @property {String} iconPlacement 勾选图标的对齐方式,left-左边,right-右边 (默认 'left' ) |
| | | * @property {Boolean} borderBottom placement为row时,是否显示下边框 (默认 false ) |
| | | * @event {Function} change 任一个checkbox状态发生变化时触发,回调为一个对象 |
| | | * @event {Function} input 修改通过v-model绑定的值时触发,回调为一个对象 |
| | | * @example <uv-checkbox-group></uv-checkbox-group> |
| | | */ |
| | | export default { |
| | | name: 'uv-checkbox-group', |
| | | mixins: [mpMixin, mixin, props], |
| | | computed: { |
| | | // 这里computed的变量,都是子组件uv-checkbox需要用到的,由于头条小程序的兼容性差异,子组件无法实时监听父组件参数的变化 |
| | | // 所以需要手动通知子组件,这里返回一个parentData变量,供watch监听,在其中去通知每一个子组件重新从父组件(uv-checkbox-group) |
| | | // 拉取父组件新的变化后的参数 |
| | | parentData() { |
| | | let value = []; |
| | | if(this.value.length) { |
| | | value = this.value; |
| | | } else if (this.modelValue.length){ |
| | | value = this.modelValue; |
| | | } |
| | | return [value, this.disabled, this.inactiveColor, this.activeColor, this.size, this.labelDisabled, this.shape, |
| | | this.iconSize, this.borderBottom, this.placement, this.labelSize, this.labelColor] |
| | | }, |
| | | bemClass() { |
| | | // this.bem为一个computed变量,在mixin中 |
| | | return this.bem('checkbox-group', ['placement']) |
| | | }, |
| | | }, |
| | | watch: { |
| | | // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件 |
| | | parentData() { |
| | | if (this.children.length) { |
| | | this.children.map(child => { |
| | | // 判断子组件(uv-checkbox)如果有init方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) |
| | | typeof(child.init) === 'function' && child.init() |
| | | }) |
| | | } |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | |
| | | } |
| | | }, |
| | | created() { |
| | | this.children = [] |
| | | }, |
| | | methods: { |
| | | // 将其他的checkbox设置为未选中的状态 |
| | | unCheckedOther(childInstance) { |
| | | const values = [] |
| | | this.children.map(child => { |
| | | // 将被选中的checkbox,放到数组中返回 |
| | | if (child.isChecked) { |
| | | values.push(child.name) |
| | | } |
| | | }) |
| | | // 修改通过v-model绑定的值 |
| | | // #ifdef VUE2 |
| | | this.$emit('input', values) |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | this.$emit('update:modelValue',values) |
| | | // #endif |
| | | // 发出事件 |
| | | this.$emit('change', values) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | |
| | | .uv-checkbox-group { |
| | | flex: 1; |
| | | |
| | | &--row { |
| | | @include flex; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | &--column { |
| | | @include flex(column); |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // checkbox的名称 |
| | | name: { |
| | | type: [String, Number, Boolean], |
| | | default: '' |
| | | }, |
| | | // 形状,square为方形,circle为圆型 |
| | | shape: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 整体的大小 |
| | | size: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 是否默认选中 |
| | | checked: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否禁用 |
| | | disabled: { |
| | | type: [String, Boolean], |
| | | default: '' |
| | | }, |
| | | // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 |
| | | activeColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 未选中的颜色 |
| | | inactiveColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 图标的大小,单位px |
| | | iconSize: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 图标颜色 |
| | | iconColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式 |
| | | label: { |
| | | type: [String, Number, Boolean], |
| | | default: '' |
| | | }, |
| | | // label的字体大小,px单位 |
| | | labelSize: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // label的颜色 |
| | | labelColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否禁止点击提示语选中复选框 |
| | | labelDisabled: { |
| | | type: [String, Boolean], |
| | | default: '' |
| | | }, |
| | | ...uni.$uv?.props?.checkbox |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-checkbox" |
| | | :style="[checkboxStyle]" |
| | | @tap.stop="wrapperClickHandler" |
| | | :class="[`uv-checkbox-label--${parentData.iconPlacement}`, parentData.borderBottom && parentData.placement === 'column' && 'uv-border-bottom']" |
| | | > |
| | | <view |
| | | class="uv-checkbox__icon-wrap" |
| | | @tap.stop="iconClickHandler" |
| | | :class="iconClasses" |
| | | :style="[iconWrapStyle]" |
| | | > |
| | | <slot name="icon"> |
| | | <uv-icon |
| | | class="uv-checkbox__icon-wrap__icon" |
| | | name="checkbox-mark" |
| | | :size="elIconSize" |
| | | :color="elIconColor" |
| | | /> |
| | | </slot> |
| | | </view> |
| | | <view |
| | | class="uv-checkbox__label-wrap" |
| | | @tap.stop="labelClickHandler"> |
| | | <slot> |
| | | <text |
| | | :style="{ |
| | | color: elDisabled ? elInactiveColor : elLabelColor, |
| | | fontSize: elLabelSize, |
| | | lineHeight: elLabelSize |
| | | }" |
| | | >{{label}}</text> |
| | | </slot> |
| | | </view> |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * checkbox 复选框 |
| | | * @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便 |
| | | * @tutorial https://www.uvui.cn/components/checkbox.html |
| | | * @property {String | Number | Boolean} name checkbox组件的标示符 |
| | | * @property {String} shape 形状,square为方形,circle为圆型 |
| | | * @property {String | Number} size 整体的大小 |
| | | * @property {Boolean} checked 是否默认选中 |
| | | * @property {String | Boolean} disabled 是否禁用 |
| | | * @property {String} activeColor 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 |
| | | * @property {String} inactiveColor 未选中的颜色 |
| | | * @property {String | Number} iconSize 图标的大小,单位px |
| | | * @property {String} iconColor 图标颜色 |
| | | * @property {String | Number} label label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式 |
| | | * @property {String} labelColor label的颜色 |
| | | * @property {String | Number} labelSize label的字体大小,px单位 |
| | | * @property {String | Boolean} labelDisabled 是否禁止点击提示语选中复选框 |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * |
| | | * @event {Function} change 任一个checkbox状态发生变化时触发,回调为一个对象 |
| | | * @example <uv-checkbox v-model="checked" :disabled="false">天涯</uv-checkbox> |
| | | */ |
| | | export default { |
| | | name: "uv-checkbox", |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | isChecked: false, |
| | | // 父组件的默认值,因为头条小程序不支持在computed中使用this.parent.shape的形式 |
| | | // 故只能使用如此方法 |
| | | parentData: { |
| | | iconSize: 12, |
| | | labelDisabled: null, |
| | | disabled: null, |
| | | shape: 'square', |
| | | activeColor: null, |
| | | inactiveColor: null, |
| | | size: 18, |
| | | value: null, |
| | | modelValue: null, |
| | | iconColor: null, |
| | | placement: 'row', |
| | | borderBottom: false, |
| | | iconPlacement: 'left', |
| | | labelSize: 14, |
| | | labelColor: '#303133' |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | // 是否禁用,如果父组件uv-raios-group禁用的话,将会忽略子组件的配置 |
| | | elDisabled() { |
| | | return this.disabled !== '' ? this.disabled : this.parentData.disabled !== null ? this.parentData.disabled : false; |
| | | }, |
| | | // 是否禁用label点击 |
| | | elLabelDisabled() { |
| | | return this.labelDisabled !== '' ? this.labelDisabled : this.parentData.labelDisabled !== null ? this.parentData.labelDisabled : |
| | | false; |
| | | }, |
| | | // 组件尺寸,对应size的值,默认值为21px |
| | | elSize() { |
| | | return this.size ? this.size : (this.parentData.size ? this.parentData.size : 21); |
| | | }, |
| | | // 组件的勾选图标的尺寸,默认12px |
| | | elIconSize() { |
| | | return this.iconSize ? this.iconSize : (this.parentData.iconSize ? this.parentData.iconSize : 12); |
| | | }, |
| | | // 组件选中激活时的颜色 |
| | | elActiveColor() { |
| | | return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : '#2979ff'); |
| | | }, |
| | | // 组件选未中激活时的颜色 |
| | | elInactiveColor() { |
| | | return this.inactiveColor ? this.inactiveColor : (this.parentData.inactiveColor ? this.parentData.inactiveColor : |
| | | '#c8c9cc'); |
| | | }, |
| | | // label的颜色 |
| | | elLabelColor() { |
| | | return this.labelColor ? this.labelColor : (this.parentData.labelColor ? this.parentData.labelColor : '#606266') |
| | | }, |
| | | // 组件的形状 |
| | | elShape() { |
| | | return this.shape ? this.shape : (this.parentData.shape ? this.parentData.shape : 'circle'); |
| | | }, |
| | | // label大小 |
| | | elLabelSize() { |
| | | return this.$uv.addUnit(this.labelSize ? this.labelSize : (this.parentData.labelSize ? this.parentData.labelSize : |
| | | '15')) |
| | | }, |
| | | elIconColor() { |
| | | const iconColor = this.iconColor ? this.iconColor : (this.parentData.iconColor ? this.parentData.iconColor : |
| | | '#ffffff'); |
| | | // 图标的颜色 |
| | | if (this.elDisabled) { |
| | | // disabled状态下,已勾选的checkbox图标改为elInactiveColor |
| | | return this.isChecked ? this.elInactiveColor : 'transparent' |
| | | } else { |
| | | return this.isChecked ? iconColor : 'transparent' |
| | | } |
| | | }, |
| | | iconClasses() { |
| | | let classes = [] |
| | | // 组件的形状 |
| | | classes.push('uv-checkbox__icon-wrap--' + this.elShape) |
| | | if (this.elDisabled) { |
| | | classes.push('uv-checkbox__icon-wrap--disabled') |
| | | } |
| | | if (this.isChecked && this.elDisabled) { |
| | | classes.push('uv-checkbox__icon-wrap--disabled--checked') |
| | | } |
| | | // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效 |
| | | // #ifdef MP-ALIPAY || MP-TOUTIAO |
| | | classes = classes.join(' ') |
| | | // #endif |
| | | return classes |
| | | }, |
| | | iconWrapStyle() { |
| | | // checkbox的整体样式 |
| | | const style = {} |
| | | style.backgroundColor = this.isChecked && !this.elDisabled ? this.elActiveColor : '#ffffff' |
| | | style.borderColor = this.isChecked && !this.elDisabled ? this.elActiveColor : this.elInactiveColor |
| | | style.width = this.$uv.addUnit(this.elSize) |
| | | style.height = this.$uv.addUnit(this.elSize) |
| | | // 如果是图标在右边的话,移除它的右边距 |
| | | if (this.parentData.iconPlacement === 'right') { |
| | | style.marginRight = 0 |
| | | } |
| | | return style |
| | | }, |
| | | checkboxStyle() { |
| | | const style = {} |
| | | if (this.parentData.borderBottom && this.parentData.placement === 'row') { |
| | | this.$uv.error('检测到您将borderBottom设置为true,需要同时将uv-checkbox-group的placement设置为column才有效') |
| | | } |
| | | // 当父组件设置了显示下边框并且排列形式为纵向时,给内容和边框之间加上一定间隔 |
| | | if (this.parentData.borderBottom && this.parentData.placement === 'column') { |
| | | style.paddingBottom = '8px' |
| | | } |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)) |
| | | } |
| | | }, |
| | | created() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用 |
| | | this.updateParentData() |
| | | if (!this.parent) { |
| | | this.$uv.error('uv-checkbox必须搭配uv-checkbox-group组件使用') |
| | | } |
| | | this.$nextTick(()=>{ |
| | | let parentValue = []; |
| | | if(this.parentData.value.length) { |
| | | parentValue = this.parentData.value; |
| | | } else if (this.parentData.modelValue.length){ |
| | | parentValue = this.parentData.modelValue; |
| | | } |
| | | // 设置初始化时,是否默认选中的状态,父组件uv-checkbox-group的value可能是array,所以额外判断 |
| | | if (this.checked) { |
| | | this.isChecked = true |
| | | } else if (this.$uv.test.array(parentValue)) { |
| | | // 查找数组是是否存在this.name元素值 |
| | | this.isChecked = parentValue.some(item => { |
| | | return item === this.name |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | | updateParentData() { |
| | | this.getParentData('uv-checkbox-group') |
| | | }, |
| | | // 横向两端排列时,点击组件即可触发选中事件 |
| | | wrapperClickHandler(e) { |
| | | this.parentData.iconPlacement === 'right' && this.iconClickHandler(e) |
| | | }, |
| | | // 点击图标 |
| | | iconClickHandler(e) { |
| | | this.preventEvent(e) |
| | | // 如果整体被禁用,不允许被点击 |
| | | if (!this.elDisabled) { |
| | | this.setRadioCheckedStatus() |
| | | } |
| | | }, |
| | | // 点击label |
| | | labelClickHandler(e) { |
| | | this.preventEvent(e) |
| | | // 如果按钮整体被禁用或者label被禁用,则不允许点击文字修改状态 |
| | | if (!this.elLabelDisabled && !this.elDisabled) { |
| | | this.setRadioCheckedStatus() |
| | | } |
| | | }, |
| | | emitEvent() { |
| | | this.$emit('change', this.isChecked) |
| | | // 尝试调用uv-form的验证方法,进行一定延迟,否则微信小程序更新可能会不及时 |
| | | this.$nextTick(() => { |
| | | this.$uv.formValidate(this, 'change') |
| | | }) |
| | | }, |
| | | // 改变组件选中状态 |
| | | // 这里的改变的依据是,更改本组件的checked值为true,同时通过父组件遍历所有uv-checkbox实例 |
| | | // 将本组件外的其他uv-checkbox的checked都设置为false(都被取消选中状态),因而只剩下一个为选中状态 |
| | | setRadioCheckedStatus() { |
| | | // 将本组件标记为与原来相反的状态 |
| | | this.isChecked = !this.isChecked |
| | | this.emitEvent() |
| | | typeof this.parent.unCheckedOther === 'function' && this.parent.unCheckedOther(this) |
| | | } |
| | | }, |
| | | watch:{ |
| | | checked(){ |
| | | this.isChecked = this.checked |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-border: 1; |
| | | $show-border-bottom: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-checkbox-label-wrap-padding-right:6px !default; |
| | | $uv-checkbox-icon-wrap-font-size:6px !default; |
| | | $uv-checkbox-icon-wrap-border-width:1px !default; |
| | | $uv-checkbox-icon-wrap-border-color:#c8c9cc !default; |
| | | $uv-checkbox-icon-wrap-icon-line-height:0 !default; |
| | | $uv-checkbox-icon-wrap-circle-border-radius:100% !default; |
| | | $uv-checkbox-icon-wrap-square-border-radius:3px !default; |
| | | $uv-checkbox-icon-wrap-checked-color:#fff !default; |
| | | $uv-checkbox-icon-wrap-checked-background-color:red !default; |
| | | $uv-checkbox-icon-wrap-checked-border-color:#2979ff !default; |
| | | $uv-checkbox-icon-wrap-disabled-background-color:#ebedf0 !default; |
| | | $uv-checkbox-icon-wrap-disabled-checked-color:#c8c9cc !default; |
| | | $uv-checkbox-label-margin-left:5px !default; |
| | | $uv-checkbox-label-margin-right:12px !default; |
| | | $uv-checkbox-label-color:$uv-content-color !default; |
| | | $uv-checkbox-label-font-size:15px !default; |
| | | $uv-checkbox-label-disabled-color:#c8c9cc !default; |
| | | |
| | | .uv-checkbox { |
| | | /* #ifndef APP-NVUE */ |
| | | @include flex(row); |
| | | /* #endif */ |
| | | overflow: hidden; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | |
| | | &-label--left { |
| | | flex-direction: row |
| | | } |
| | | |
| | | &-label--right { |
| | | flex-direction: row-reverse; |
| | | justify-content: space-between |
| | | } |
| | | |
| | | &__icon-wrap { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | // nvue下,border-color过渡有问题 |
| | | transition-property: border-color, background-color, color; |
| | | transition-duration: 0.2s; |
| | | /* #endif */ |
| | | color: $uv-content-color; |
| | | @include flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: transparent; |
| | | text-align: center; |
| | | font-size: $uv-checkbox-icon-wrap-font-size; |
| | | border-width: $uv-checkbox-icon-wrap-border-width; |
| | | border-color: $uv-checkbox-icon-wrap-border-color; |
| | | border-style: solid; |
| | | |
| | | /* #ifdef MP-TOUTIAO */ |
| | | // 头条小程序兼容性问题,需要设置行高为0,否则图标偏下 |
| | | &__icon { |
| | | line-height: $uv-checkbox-icon-wrap-icon-line-height; |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | &--circle { |
| | | border-radius: $uv-checkbox-icon-wrap-circle-border-radius; |
| | | } |
| | | |
| | | &--square { |
| | | border-radius: $uv-checkbox-icon-wrap-square-border-radius; |
| | | } |
| | | |
| | | &--checked { |
| | | color: $uv-checkbox-icon-wrap-checked-color; |
| | | background-color: $uv-checkbox-icon-wrap-checked-background-color; |
| | | border-color: $uv-checkbox-icon-wrap-checked-border-color; |
| | | } |
| | | |
| | | &--disabled { |
| | | background-color: $uv-checkbox-icon-wrap-disabled-background-color !important; |
| | | } |
| | | |
| | | &--disabled--checked { |
| | | color: $uv-checkbox-icon-wrap-disabled-checked-color !important; |
| | | } |
| | | } |
| | | |
| | | &__label { |
| | | /* #ifndef APP-NVUE */ |
| | | word-wrap: break-word; |
| | | /* #endif */ |
| | | margin-left: $uv-checkbox-label-margin-left; |
| | | margin-right: $uv-checkbox-label-margin-right; |
| | | color: $uv-checkbox-label-color; |
| | | font-size: $uv-checkbox-label-font-size; |
| | | |
| | | &--disabled { |
| | | color: $uv-checkbox-label-disabled-color; |
| | | } |
| | | } |
| | | |
| | | &__label-wrap { |
| | | flex: 1; |
| | | padding-left: $uv-checkbox-label-wrap-padding-right; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-checkbox", |
| | | "displayName": "uv-checkbox 复选框 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.14", |
| | | "description": "复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便。", |
| | | "keywords": [ |
| | | "uv-checkbox", |
| | | "uvui", |
| | | "uv-ui", |
| | | "checkbox", |
| | | "复选框" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-icon" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Checkbox 复选框 |
| | | |
| | | > **组件名:uv-checkbox** |
| | | |
| | | 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便。可配合 `uv-form` 组件进行表单验证等场景使用。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/checkbox.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.5(2023-08-05) |
| | | 在vue2模式下,v-model设置为0时不生效的BUG |
| | | ## 1.0.4(2023-07-13) |
| | | 1. 修复value/v-model更改不生效的BUG |
| | | ## 1.0.3(2023-06-28) |
| | | 修复:使用:disabledKeyboard="true"属性,事件全部失效的BUG |
| | | ## 1.0.2(2023-06-23) |
| | | 优化下边框 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-code-input 验证码输入 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 键盘弹起时,是否自动上推页面 |
| | | adjustPosition: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 最大输入长度 |
| | | maxlength: { |
| | | type: [String, Number], |
| | | default: 6 |
| | | }, |
| | | // 是否用圆点填充 |
| | | dot: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 显示模式,box-盒子模式,line-底部横线模式 |
| | | mode: { |
| | | type: String, |
| | | default: 'box' |
| | | }, |
| | | // 是否细边框 |
| | | hairline: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 字符间的距离 |
| | | space: { |
| | | type: [String, Number], |
| | | default: 10 |
| | | }, |
| | | // 是否自动获取焦点 |
| | | focus: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 字体是否加粗 |
| | | bold: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 字体颜色 |
| | | color: { |
| | | type: String, |
| | | default: '#606266' |
| | | }, |
| | | // 字体大小 |
| | | fontSize: { |
| | | type: [String, Number], |
| | | default: 18 |
| | | }, |
| | | // 输入框的大小,宽等于高 |
| | | size: { |
| | | type: [String, Number], |
| | | default: 35 |
| | | }, |
| | | // 是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true |
| | | disabledKeyboard: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 边框和线条颜色 |
| | | borderColor: { |
| | | type: String, |
| | | default: '#c9cacc' |
| | | }, |
| | | // 是否禁止输入"."符号 |
| | | disabledDot: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | ...uni.$uv?.props?.codeInput |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-code-input"> |
| | | <view |
| | | class="uv-code-input__item" |
| | | :style="[itemStyle(index)]" |
| | | v-for="(item, index) in codeLength" |
| | | :key="index" |
| | | > |
| | | <view |
| | | class="uv-code-input__item__dot" |
| | | v-if="dot && codeArray.length > index" |
| | | ></view> |
| | | <text |
| | | v-else |
| | | :style="{ |
| | | fontSize: $uv.addUnit(fontSize), |
| | | fontWeight: bold ? 'bold' : 'normal', |
| | | color: color |
| | | }" |
| | | >{{codeArray[index]}}</text> |
| | | <view |
| | | class="uv-code-input__item__line" |
| | | v-if="mode === 'line'" |
| | | :style="[lineStyle]" |
| | | ></view> |
| | | <!-- #ifndef APP-PLUS --> |
| | | <view v-if="isFocus && codeArray.length === index" :style="{backgroundColor: color}" class="uv-code-input__item__cursor"></view> |
| | | <!-- #endif --> |
| | | </view> |
| | | <input |
| | | :disabled="disabledKeyboard" |
| | | type="number" |
| | | :focus="focus" |
| | | :value="inputValue" |
| | | :maxlength="maxlength" |
| | | :adjustPosition="adjustPosition" |
| | | class="uv-code-input__input" |
| | | @input="inputHandler" |
| | | :style="{ |
| | | height: $uv.addUnit(size) |
| | | }" |
| | | @focus="isFocus = true" |
| | | @blur="isFocus = false" |
| | | /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * CodeInput 验证码输入 |
| | | * @description 该组件一般用于验证用户短信验证码的场景,也可以结合uvui的键盘组件使用 |
| | | * @tutorial https://www.uvui.cn/components/codeInput.html |
| | | * @property {String | Number} value / v-model 预置值 |
| | | * @property {String | Number} maxlength 最大输入长度 (默认 6 ) |
| | | * @property {Boolean} dot 是否用圆点填充 (默认 false ) |
| | | * @property {String} mode 显示模式,box-盒子模式,line-底部横线模式 (默认 'box' ) |
| | | * @property {Boolean} hairline 是否细边框 (默认 false ) |
| | | * @property {String | Number} space 字符间的距离 (默认 10 ) |
| | | * @property {Boolean} focus 是否自动获取焦点 (默认 false ) |
| | | * @property {Boolean} bold 字体和输入横线是否加粗 (默认 false ) |
| | | * @property {String} color 字体颜色 (默认 '#606266' ) |
| | | * @property {String | Number} fontSize 字体大小,单位px (默认 18 ) |
| | | * @property {String | Number} size 输入框的大小,宽等于高 (默认 35 ) |
| | | * @property {Boolean} disabledKeyboard 是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true (默认 false ) |
| | | * @property {String} borderColor 边框和线条颜色 (默认 '#c9cacc' ) |
| | | * @property {Boolean} disabledDot 是否禁止输入"."符号 (默认 true ) |
| | | * |
| | | * @event {Function} change 输入内容发生改变时触发,具体见上方说明 value:当前输入的值 |
| | | * @event {Function} finish 输入字符个数达maxlength值时触发,见上方说明 value:当前输入的值 |
| | | * @example <uv-code-input v-model="value4" :focus="true"></uv-code-input> |
| | | */ |
| | | export default { |
| | | name: 'uv-code-input', |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | inputValue: '', |
| | | isFocus: this.focus |
| | | } |
| | | }, |
| | | created() { |
| | | const value = String(this.value) || String(this.modelValue); |
| | | this.inputValue = String(value).substring(0, this.maxlength); |
| | | }, |
| | | watch: { |
| | | value(newVal) { |
| | | // 转为字符串,超出部分截掉 |
| | | this.inputValue = String(newVal).substring(0, this.maxlength); |
| | | if (this.disabledKeyboard) { |
| | | this.customInput(); |
| | | } |
| | | }, |
| | | modelValue(newVal) { |
| | | // 转为字符串,超出部分截掉 |
| | | this.inputValue = String(newVal).substring(0, this.maxlength); |
| | | if (this.disabledKeyboard) { |
| | | this.customInput(); |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | // 根据长度,循环输入框的个数,因为头条小程序数值不能用于v-for |
| | | codeLength() { |
| | | return new Array(Number(this.maxlength)) |
| | | }, |
| | | // 循环item的样式 |
| | | itemStyle() { |
| | | return index => { |
| | | const addUnit = this.$uv.addUnit |
| | | const style = { |
| | | width: addUnit(this.size), |
| | | height: addUnit(this.size) |
| | | } |
| | | // 盒子模式下,需要额外进行处理 |
| | | if (this.mode === 'box') { |
| | | // 设置盒子的边框,如果是细边框,则设置为0.5px宽度 |
| | | style.border = `${this.hairline ? 0.5 : 1}px solid ${this.borderColor}` |
| | | // 如果盒子间距为0的话 |
| | | if (this.$uv.getPx(this.space) === 0) { |
| | | // 给第一和最后一个盒子设置圆角 |
| | | if (index === 0) { |
| | | style.borderTopLeftRadius = '3px' |
| | | style.borderBottomLeftRadius = '3px' |
| | | } |
| | | if (index === this.codeLength.length - 1) { |
| | | style.borderTopRightRadius = '3px' |
| | | style.borderBottomRightRadius = '3px' |
| | | } |
| | | // 最后一个盒子的右边框需要保留 |
| | | if (index !== this.codeLength.length - 1) { |
| | | style.borderRight = 'none' |
| | | } |
| | | } |
| | | } |
| | | if (index !== this.codeLength.length - 1) { |
| | | // 设置验证码字符之间的距离,通过margin-right设置,最后一个字符,无需右边框 |
| | | style.marginRight = addUnit(this.space) |
| | | } else { |
| | | // 最后一个盒子的有边框需要保留 |
| | | style.marginRight = 0 |
| | | } |
| | | return style |
| | | } |
| | | }, |
| | | // 将输入的值,转为数组,给item历遍时,根据当前的索引显示数组的元素 |
| | | codeArray() { |
| | | return String(this.inputValue).split('') |
| | | }, |
| | | // 下划线模式下,横线的样式 |
| | | lineStyle() { |
| | | const style = {} |
| | | style.height = this.hairline ? '1px' : '4px' |
| | | style.width = this.$uv.addUnit(this.size) |
| | | // 线条模式下,背景色即为边框颜色 |
| | | style.backgroundColor = this.borderColor |
| | | return style |
| | | } |
| | | }, |
| | | methods: { |
| | | // 监听输入框的值发生变化 |
| | | inputHandler(e) { |
| | | const value = e.detail.value |
| | | this.inputValue = value |
| | | // 是否允许输入“.”符号 |
| | | if (this.disabledDot) { |
| | | this.$nextTick(() => { |
| | | this.inputValue = value.replace('.', '') |
| | | }) |
| | | } |
| | | // 未达到maxlength之前,发送change事件,达到后发送finish事件 |
| | | this.$emit('change', value) |
| | | // 修改通过v-model双向绑定的值 |
| | | this.$emit('input', value) |
| | | this.$emit('update:modelValue', value) |
| | | // 达到用户指定输入长度时,发出完成事件 |
| | | if (String(value).length >= Number(this.maxlength)) { |
| | | this.$emit('finish', value) |
| | | } |
| | | }, |
| | | // 自定义键盘输入值监听 |
| | | customInput() { |
| | | const value = this.inputValue; |
| | | // 是否允许输入“.”符号 |
| | | if (this.disabledDot) { |
| | | this.$nextTick(() => { |
| | | this.inputValue = value.replace('.', '') |
| | | }) |
| | | } |
| | | // 未达到maxlength之前,发送change事件,达到后发送finish事件 |
| | | this.$emit('change', value) |
| | | // 达到用户指定输入长度时,发出完成事件 |
| | | if (String(value).length >= Number(this.maxlength)) { |
| | | this.$emit('finish', value) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-code-input-cursor-width: 1px; |
| | | $uv-code-input-cursor-height: 40%; |
| | | $uv-code-input-cursor-animation-duration: 1s; |
| | | $uv-code-input-cursor-animation-name: uv-cursor-flicker; |
| | | .uv-code-input { |
| | | @include flex; |
| | | position: relative; |
| | | overflow: hidden; |
| | | &__item { |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: relative; |
| | | &__text { |
| | | font-size: 15px; |
| | | color: $uv-content-color; |
| | | } |
| | | &__dot { |
| | | width: 7px; |
| | | height: 7px; |
| | | border-radius: 100px; |
| | | background-color: $uv-content-color; |
| | | } |
| | | &__line { |
| | | position: absolute; |
| | | bottom: 0; |
| | | height: 4px; |
| | | border-radius: 100px; |
| | | width: 40px; |
| | | background-color: $uv-content-color; |
| | | } |
| | | /* #ifndef APP-PLUS */ |
| | | &__cursor { |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%); |
| | | width: $uv-code-input-cursor-width; |
| | | height: $uv-code-input-cursor-height; |
| | | animation: $uv-code-input-cursor-animation-duration uv-cursor-flicker infinite; |
| | | } |
| | | /* #endif */ |
| | | } |
| | | &__input { |
| | | // 之所以需要input输入框,是因为有它才能唤起键盘 |
| | | // 这里将它设置为两倍的屏幕宽度,再将左边的一半移出屏幕,为了不让用户看到输入的内容 |
| | | position: absolute; |
| | | left: -750rpx; |
| | | width: 1500rpx; |
| | | top: 0; |
| | | background-color: transparent; |
| | | text-align: left; |
| | | } |
| | | } |
| | | /* #ifndef APP-PLUS */ |
| | | @keyframes uv-cursor-flicker { |
| | | 0% { |
| | | opacity: 0; |
| | | } |
| | | 50% { |
| | | opacity: 1; |
| | | } |
| | | 100% { |
| | | opacity: 0; |
| | | } |
| | | } |
| | | /* #endif */ |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-code-input", |
| | | "displayName": "uv-code-input 验证码输入 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.5", |
| | | "description": "验证码输入组件一般用于验证用户短信验证码的场景,输入框或横线多种模式可选。", |
| | | "keywords": [ |
| | | "uv-code-input", |
| | | "uvui", |
| | | "uv-ui", |
| | | "code", |
| | | "验证码输入" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## CodeInput 验证码输入框 |
| | | |
| | | > **组件名:uv-code-input** |
| | | |
| | | 该组件一般用于验证用户短信验证码的场景,输入框或横线多种模式可选。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/codeInput.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.3(2023-10-13) |
| | | 1. 优化 |
| | | ## 1.0.2(2023-10-13) |
| | | 1. unmounted兼容vue3 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-code 验证码倒计时 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 倒计时总秒数 |
| | | seconds: { |
| | | type: [String, Number], |
| | | default: 60 |
| | | }, |
| | | // 尚未开始时提示 |
| | | startText: { |
| | | type: String, |
| | | default: '获取验证码' |
| | | }, |
| | | // 正在倒计时中的提示 |
| | | changeText: { |
| | | type: String, |
| | | default: 'X秒重新获取' |
| | | }, |
| | | // 倒计时结束时的提示 |
| | | endText: { |
| | | type: String, |
| | | default: '重新获取' |
| | | }, |
| | | // 是否在H5刷新或各端返回再进入时继续倒计时 |
| | | keepRunning: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了 |
| | | uniqueKey: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | ...uni.$uv?.props?.code |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-code"> |
| | | <!-- 此组件功能由js完成,无需写html逻辑 --> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * Code 验证码倒计时 |
| | | * @description 考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件不提供界面显示,只提供倒计时文本,由用户将文本嵌入到具体的场景 |
| | | * @tutorial https://www.uvui.cn/components/code.html |
| | | * @property {String | Number} seconds 倒计时所需的秒数(默认 60 ) |
| | | * @property {String} startText 开始前的提示语,见官网说明(默认 '获取验证码' ) |
| | | * @property {String} changeText 倒计时期间的提示语,必须带有字母"x",见官网说明(默认 'X秒重新获取' ) |
| | | * @property {String} endText 倒计结束的提示语,见官网说明(默认 '重新获取' ) |
| | | * @property {Boolean} keepRunning 是否在H5刷新或各端返回再进入时继续倒计时( 默认false ) |
| | | * @property {String} uniqueKey 为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了 |
| | | * |
| | | * @event {Function} change 倒计时期间,每秒触发一次 |
| | | * @event {Function} start 开始倒计时触发 |
| | | * @event {Function} end 结束倒计时触发 |
| | | * @example <uv-code ref="uCode" @change="codeChange" seconds="20"></uv-code> |
| | | */ |
| | | export default { |
| | | name: "uv-code", |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | secNum: this.seconds, |
| | | timer: null, |
| | | canGetCode: true, // 是否可以执行验证码操作 |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.checkKeepRunning() |
| | | }, |
| | | watch: { |
| | | seconds: { |
| | | immediate: true, |
| | | handler(n) { |
| | | this.secNum = n |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | checkKeepRunning() { |
| | | // 获取上一次退出页面(H5还包括刷新)时的时间戳,如果没有上次的保存,此值可能为空 |
| | | let lastTimestamp = Number(uni.getStorageSync(this.uniqueKey + '_$uCountDownTimestamp')) |
| | | if (!lastTimestamp) return this.changeEvent(this.startText) |
| | | // 当前秒的时间戳 |
| | | let nowTimestamp = Math.floor((+new Date()) / 1000) |
| | | // 判断当前的时间戳,是否小于上一次的本该按设定结束,却提前结束的时间戳 |
| | | if (this.keepRunning && lastTimestamp && lastTimestamp > nowTimestamp) { |
| | | // 剩余尚未执行完的倒计秒数 |
| | | this.secNum = lastTimestamp - nowTimestamp |
| | | // 清除本地保存的变量 |
| | | uni.removeStorageSync(this.uniqueKey + '_$uCountDownTimestamp') |
| | | // 开始倒计时 |
| | | this.start() |
| | | } else { |
| | | // 如果不存在需要继续上一次的倒计时,执行正常的逻辑 |
| | | this.changeEvent(this.startText) |
| | | } |
| | | }, |
| | | // 开始倒计时 |
| | | start() { |
| | | // 防止快速点击获取验证码的按钮而导致内部产生多个定时器导致混乱 |
| | | if (this.timer) { |
| | | clearInterval(this.timer) |
| | | this.timer = null |
| | | } |
| | | this.$emit('start') |
| | | this.canGetCode = false |
| | | // 这里放这句,是为了一开始时就提示,否则要等setInterval的1秒后才会有提示 |
| | | this.changeEvent(this.changeText.replace(/x|X/, this.secNum)) |
| | | this.timer = setInterval(() => { |
| | | if (--this.secNum) { |
| | | // 用当前倒计时的秒数替换提示字符串中的"x"字母 |
| | | this.changeEvent(this.changeText.replace(/x|X/, this.secNum)) |
| | | } else { |
| | | clearInterval(this.timer) |
| | | this.timer = null |
| | | this.changeEvent(this.endText) |
| | | this.secNum = this.seconds |
| | | this.$emit('end') |
| | | this.canGetCode = true |
| | | } |
| | | }, 1000) |
| | | this.setTimeToStorage() |
| | | }, |
| | | // 重置,可以让用户再次获取验证码 |
| | | reset() { |
| | | this.canGetCode = true |
| | | clearInterval(this.timer) |
| | | this.secNum = this.seconds |
| | | this.changeEvent(this.endText) |
| | | }, |
| | | changeEvent(text) { |
| | | this.$emit('change', text) |
| | | }, |
| | | // 保存时间戳,为了防止倒计时尚未结束,H5刷新或者各端的右上角返回上一页再进来 |
| | | setTimeToStorage() { |
| | | if (!this.keepRunning || !this.timer) return |
| | | // 记录当前的时间戳,为了下次进入页面,如果还在倒计时内的话,继续倒计时 |
| | | // 倒计时尚未结束,结果大于0;倒计时已经开始,就会小于初始值,如果等于初始值,说明没有开始倒计时,无需处理 |
| | | if (this.secNum > 0 && this.secNum <= this.seconds) { |
| | | // 获取当前时间戳(+ new Date()为特殊写法),除以1000变成秒,再去除小数部分 |
| | | let nowTimestamp = Math.floor((+new Date()) / 1000) |
| | | // 将本该结束时候的时间戳保存起来 => 当前时间戳 + 剩余的秒数 |
| | | uni.setStorage({ |
| | | key: this.uniqueKey + '_$uCountDownTimestamp', |
| | | data: nowTimestamp + Number(this.secNum) |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | // #ifdef VUE2 |
| | | // 组件销毁的时候,清除定时器,否则定时器会继续存在,系统不会自动清除 |
| | | beforeDestroy() { |
| | | this.setTimeToStorage() |
| | | clearTimeout(this.timer) |
| | | this.timer = null |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | // 组件销毁,兼容vue3 |
| | | unmounted() { |
| | | this.setTimeToStorage() |
| | | clearTimeout(this.timer) |
| | | this.timer = null |
| | | } |
| | | // #endif |
| | | } |
| | | </script> |
| New file |
| | |
| | | { |
| | | "id": "uv-code", |
| | | "displayName": "uv-code 验证码倒计时 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.3", |
| | | "description": "考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件不提供界面显示,只提供倒计时文本,由用户将文本嵌入到具体的场景。", |
| | | "keywords": [ |
| | | "uv-code", |
| | | "uvui", |
| | | "uv-ui", |
| | | "code", |
| | | "验证码倒计时" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Code 验证码输入框 |
| | | |
| | | > **组件名:uv-code** |
| | | |
| | | 考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件不提供界面显示,只提供倒计时文本,由用户将文本嵌入到具体的场景。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/code.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-collapse 折叠面板 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 标题 |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 标题右侧内容 |
| | | value: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 标题下方的描述信息 |
| | | label: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否禁用折叠面板 |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否展示右侧箭头并开启点击反馈 |
| | | isLink: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否开启点击反馈 |
| | | clickable: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否显示内边框 |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 标题的对齐方式 |
| | | align: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | // 唯一标识符 |
| | | name: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 标题左侧图片,可为绝对路径的图片或内置图标 |
| | | icon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 面板展开收起的过渡时间,单位ms |
| | | duration: { |
| | | type: Number, |
| | | default: 300 |
| | | }, |
| | | ...uni.$uv?.props?.collapseItem |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-collapse-item"> |
| | | <uv-cell |
| | | :title="title" |
| | | :value="value" |
| | | :label="label" |
| | | :icon="icon" |
| | | :isLink="isLink" |
| | | :clickable="clickable" |
| | | :border="parentData.border && showBorder" |
| | | @click="clickHandler" |
| | | :arrowDirection="expanded ? 'up' : 'down'" |
| | | :disabled="disabled" |
| | | > |
| | | <!-- #ifndef MP-WEIXIN --> |
| | | <!-- 微信小程序不支持,因为微信中不支持 <slot name="title" slot="title" />的写法 --> |
| | | <template slot="title"> |
| | | <slot name="title"></slot> |
| | | </template> |
| | | <template slot="icon"> |
| | | <slot name="icon"></slot> |
| | | </template> |
| | | <template slot="value"> |
| | | <slot name="value"></slot> |
| | | </template> |
| | | <template slot="right-icon"> |
| | | <slot name="right-icon"></slot> |
| | | </template> |
| | | <!-- #endif --> |
| | | </uv-cell> |
| | | <view |
| | | class="uv-collapse-item__content" |
| | | :animation="animationData" |
| | | ref="animation" |
| | | > |
| | | <view |
| | | class="uv-collapse-item__content__text content-class" |
| | | :id="elId" |
| | | :ref="elId" |
| | | ><slot /></view> |
| | | </view> |
| | | <uv-line v-if="parentData.border"></uv-line> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | // #ifdef APP-NVUE |
| | | const animation = uni.requireNativePlugin('animation') |
| | | const dom = uni.requireNativePlugin('dom') |
| | | // #endif |
| | | /** |
| | | * collapseItem 折叠面板Item |
| | | * @description 通过折叠面板收纳内容区域(搭配uv-collapse使用) |
| | | * @tutorial https://www.uvui.cn/components/collapse.html |
| | | * @property {String} title 标题 |
| | | * @property {String} value 标题右侧内容 |
| | | * @property {String} label 标题下方的描述信息 |
| | | * @property {Boolean} disbled 是否禁用折叠面板 ( 默认 false ) |
| | | * @property {Boolean} isLink 是否展示右侧箭头并开启点击反馈 ( 默认 true ) |
| | | * @property {Boolean} clickable 是否开启点击反馈 ( 默认 true ) |
| | | * @property {Boolean} border 是否显示内边框 ( 默认 true ) |
| | | * @property {String} align 标题的对齐方式 ( 默认 'left' ) |
| | | * @property {String | Number} name 唯一标识符 |
| | | * @property {String} icon 标题左侧图片,可为绝对路径的图片或内置图标 |
| | | * @event {Function} change 某个item被打开或者收起时触发 |
| | | * @example <uv-collapse-item :title="item.head" v-for="(item, index) in itemList" :key="index">{{item.body}}</uv-collapse-item> |
| | | */ |
| | | export default { |
| | | name: "uv-collapse-item", |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | elId: '', |
| | | // uni.createAnimation的导出数据 |
| | | animationData: {}, |
| | | // 是否展开状态 |
| | | expanded: false, |
| | | // 根据expanded确定是否显示border,为了控制展开时,cell的下划线更好的显示效果,进行一定时间的延时 |
| | | showBorder: false, |
| | | // 是否动画中,如果是则不允许继续触发点击 |
| | | animating: false, |
| | | // 父组件uv-collapse的参数 |
| | | parentData: { |
| | | accordion: false, |
| | | border: false |
| | | } |
| | | }; |
| | | }, |
| | | watch: { |
| | | expanded(n) { |
| | | clearTimeout(this.timer) |
| | | this.timer = null |
| | | // 这里根据expanded的值来进行一定的延时,是为了cell的下划线更好的显示效果 |
| | | this.timer = setTimeout(() => { |
| | | this.showBorder = n |
| | | }, n ? 10 : 290) |
| | | } |
| | | }, |
| | | created() { |
| | | this.elId = this.$uv.guid(); |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | // 异步获取内容,或者动态修改了内容时,需要重新初始化 |
| | | init() { |
| | | // 初始化数据 |
| | | this.updateParentData() |
| | | if (!this.parent) { |
| | | return this.$uv.error('uv-collapse-item必须要搭配uv-collapse组件使用') |
| | | } |
| | | const { |
| | | value, |
| | | accordion, |
| | | children = [] |
| | | } = this.parent |
| | | |
| | | if (accordion) { |
| | | if (this.$uv.test.array(value)) { |
| | | return this.$uv.error('手风琴模式下,uv-collapse组件的value参数不能为数组') |
| | | } |
| | | this.expanded = this.name == value |
| | | } else { |
| | | if (!this.$uv.test.array(value) && value !== null) { |
| | | return this.$uv.error('非手风琴模式下,uv-collapse组件的value参数必须为数组') |
| | | } |
| | | this.expanded = (value || []).some(item => item == this.name) |
| | | } |
| | | // 设置组件的展开或收起状态 |
| | | this.$nextTick(function() { |
| | | this.setContentAnimate() |
| | | }) |
| | | }, |
| | | updateParentData() { |
| | | // 此方法在mixin中 |
| | | this.getParentData('uv-collapse') |
| | | }, |
| | | async setContentAnimate() { |
| | | // 每次面板打开或者收起时,都查询元素尺寸 |
| | | // 好处是,父组件从服务端获取内容后,变更折叠面板后可以获得最新的高度 |
| | | const rect = await this.queryRect() |
| | | const height = this.expanded ? rect.height : 0 |
| | | this.animating = true |
| | | // #ifdef APP-NVUE |
| | | const ref = this.$refs['animation'].ref |
| | | animation.transition(ref, { |
| | | styles: { |
| | | height: height + 'px' |
| | | }, |
| | | duration: this.duration, |
| | | // 必须设置为true,否则会到面板收起或展开时,页面其他元素不会随之调整它们的布局 |
| | | needLayout: true, |
| | | timingFunction: 'ease-in-out', |
| | | }, () => { |
| | | this.animating = false |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifndef APP-NVUE |
| | | const animation = uni.createAnimation({ |
| | | timingFunction: 'ease-in-out', |
| | | }); |
| | | animation |
| | | .height(height) |
| | | .step({ |
| | | duration: this.duration, |
| | | }) |
| | | .step() |
| | | // 导出动画数据给面板的animationData值 |
| | | this.animationData = animation.export() |
| | | // 标识动画结束 |
| | | this.$uv.sleep(this.duration).then(() => { |
| | | this.animating = false |
| | | }) |
| | | // #endif |
| | | }, |
| | | // 点击collapsehead头部 |
| | | clickHandler() { |
| | | if (this.disabled && this.animating) return |
| | | // 设置本组件为相反的状态 |
| | | this.parent && this.parent.onChange(this) |
| | | }, |
| | | // 查询内容高度 |
| | | queryRect() { |
| | | // #ifndef APP-NVUE |
| | | // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同 |
| | | return new Promise(resolve => { |
| | | this.$uvGetRect(`#${this.elId}`).then(size => { |
| | | resolve(size) |
| | | }) |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifdef APP-NVUE |
| | | // nvue下,使用dom模块查询元素高度 |
| | | // 返回一个promise,让调用此方法的主体能使用then回调 |
| | | return new Promise(resolve => { |
| | | dom.getComponentRect(this.$refs[this.elId], res => { |
| | | resolve(res.size) |
| | | }) |
| | | }) |
| | | // #endif |
| | | } |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | .uv-collapse-item { |
| | | |
| | | &__content { |
| | | overflow: hidden; |
| | | height: 0; |
| | | |
| | | &__text { |
| | | padding: 12px 15px; |
| | | color: $uv-content-color; |
| | | font-size: 14px; |
| | | line-height: 18px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number |
| | | value: { |
| | | type: [String, Number, Array, null], |
| | | default: null |
| | | }, |
| | | // 是否手风琴模式 |
| | | accordion: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示外边框 |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | ...uni.$uv?.props?.collapse |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-collapse"> |
| | | <uv-line v-if="border"></uv-line> |
| | | <slot /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * collapse 折叠面板 |
| | | * @description 通过折叠面板收纳内容区域 |
| | | * @tutorial https://www.uvui.cn/components/collapse.html |
| | | * @property {String | Number | Array} value 当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number |
| | | * @property {Boolean} accordion 是否手风琴模式( 默认 false ) |
| | | * @property {Boolean} border 是否显示外边框 ( 默认 true ) |
| | | * @event {Function} change 当前激活面板展开时触发(如果是手风琴模式,参数activeNames类型为String,否则为Array) |
| | | * @example <uv-collapse></uv-collapse> |
| | | */ |
| | | export default { |
| | | name: "uv-collapse", |
| | | mixins: [mpMixin, mixin, props], |
| | | watch: { |
| | | needInit() { |
| | | this.init() |
| | | }, |
| | | // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件 |
| | | parentData() { |
| | | if (this.children.length) { |
| | | this.children.map(child => { |
| | | // 判断子组件(uv-checkbox)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) |
| | | typeof(child.updateParentData) === 'function' && child.updateParentData() |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.children = [] |
| | | }, |
| | | computed: { |
| | | needInit() { |
| | | // 通过computed,同时监听accordion和value值的变化 |
| | | // 再通过watch去执行init()方法,进行再一次的初始化 |
| | | return [this.accordion, this.value] |
| | | } |
| | | }, |
| | | methods: { |
| | | // 重新初始化一次内部的所有子元素 |
| | | init() { |
| | | this.children.map(child => { |
| | | child.init() |
| | | }) |
| | | }, |
| | | /** |
| | | * collapse-item被点击时触发,由collapse统一处理各子组件的状态 |
| | | * @param {Object} target 被操作的面板的实例 |
| | | */ |
| | | onChange(target) { |
| | | let changeArr = [] |
| | | this.children.map((child, index) => { |
| | | // 如果是手风琴模式,将其他的折叠面板收起来 |
| | | if (this.accordion) { |
| | | child.expanded = child === target ? !target.expanded : false |
| | | child.setContentAnimate() |
| | | } else { |
| | | if(child === target) { |
| | | child.expanded = !child.expanded |
| | | child.setContentAnimate() |
| | | } |
| | | } |
| | | // 拼接change事件中,数组元素的状态 |
| | | changeArr.push({ |
| | | // 如果没有定义name属性,则默认返回组件的index索引 |
| | | name: child.name || index, |
| | | status: child.expanded ? 'open' : 'close' |
| | | }) |
| | | }) |
| | | |
| | | this.$emit('change', changeArr) |
| | | this.$emit(target.expanded ? 'open' : 'close', target.name) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| New file |
| | |
| | | { |
| | | "id": "uv-collapse", |
| | | "displayName": "uv-collapse 折叠面板 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.1", |
| | | "description": "折叠面板组件,通过折叠面板收纳内容区域,点击可展开收起,多功能参数可配置。", |
| | | "keywords": [ |
| | | "uv-collapse", |
| | | "uvui", |
| | | "uv-ui", |
| | | "collapse", |
| | | "折叠面板" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-line", |
| | | "uv-cell" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Collapse 折叠面板 |
| | | |
| | | > **组件名:uv-collapse** |
| | | |
| | | 通过折叠面板收纳内容区域,点击可展开收起,多功能参数可配置。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/collapse.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.3(2023-10-13) |
| | | 1. unmounted兼容vue3 |
| | | ## 1.0.2(2023-06-20) |
| | | 1. 增加外部样式customStyle参数 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-count-down 倒计时 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 倒计时时长,单位ms |
| | | time: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 |
| | | format: { |
| | | type: String, |
| | | default: 'HH:mm:ss' |
| | | }, |
| | | // 是否自动开始倒计时 |
| | | autoStart: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否展示毫秒倒计时 |
| | | millisecond: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ...uni.$uv?.props?.countDown |
| | | } |
| | | } |
| New file |
| | |
| | | // 补0,如1 -> 01 |
| | | function padZero(num, targetLength = 2) { |
| | | let str = `${num}` |
| | | while (str.length < targetLength) { |
| | | str = `0${str}` |
| | | } |
| | | return str |
| | | } |
| | | const SECOND = 1000 |
| | | const MINUTE = 60 * SECOND |
| | | const HOUR = 60 * MINUTE |
| | | const DAY = 24 * HOUR |
| | | export function parseTimeData(time) { |
| | | const days = Math.floor(time / DAY) |
| | | const hours = Math.floor((time % DAY) / HOUR) |
| | | const minutes = Math.floor((time % HOUR) / MINUTE) |
| | | const seconds = Math.floor((time % MINUTE) / SECOND) |
| | | const milliseconds = Math.floor(time % SECOND) |
| | | return { |
| | | days, |
| | | hours, |
| | | minutes, |
| | | seconds, |
| | | milliseconds |
| | | } |
| | | } |
| | | export function parseFormat(format, timeData) { |
| | | let { |
| | | days, |
| | | hours, |
| | | minutes, |
| | | seconds, |
| | | milliseconds |
| | | } = timeData |
| | | // 如果格式化字符串中不存在DD(天),则将天的时间转为小时中去 |
| | | if (format.indexOf('DD') === -1) { |
| | | hours += days * 24 |
| | | } else { |
| | | // 对天补0 |
| | | format = format.replace('DD', padZero(days)) |
| | | } |
| | | // 其他同理于DD的格式化处理方式 |
| | | if (format.indexOf('HH') === -1) { |
| | | minutes += hours * 60 |
| | | } else { |
| | | format = format.replace('HH', padZero(hours)) |
| | | } |
| | | if (format.indexOf('mm') === -1) { |
| | | seconds += minutes * 60 |
| | | } else { |
| | | format = format.replace('mm', padZero(minutes)) |
| | | } |
| | | if (format.indexOf('ss') === -1) { |
| | | milliseconds += seconds * 1000 |
| | | } else { |
| | | format = format.replace('ss', padZero(seconds)) |
| | | } |
| | | return format.replace('SSS', padZero(milliseconds, 3)) |
| | | } |
| | | export function isSameSecond(time1, time2) { |
| | | return Math.floor(time1 / 1000) === Math.floor(time2 / 1000) |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-count-down" |
| | | :style="[$uv.addStyle(customStyle)]"> |
| | | <slot> |
| | | <text class="uv-count-down__text">{{ formattedTime }}</text> |
| | | </slot> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | import { |
| | | isSameSecond, |
| | | parseFormat, |
| | | parseTimeData |
| | | } from './utils'; |
| | | /** |
| | | * uv-count-down 倒计时 |
| | | * @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。 |
| | | * @tutorial https://www.uvui.cn/components/countDown.html |
| | | * @property {String | Number} time 倒计时时长,单位ms (默认 0 ) |
| | | * @property {String} format 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 (默认 'HH:mm:ss' ) |
| | | * @property {Boolean} autoStart 是否自动开始倒计时 (默认 true ) |
| | | * @property {Boolean} millisecond 是否展示毫秒倒计时 (默认 false ) |
| | | * @event {Function} finish 倒计时结束时触发 |
| | | * @event {Function} change 倒计时变化时触发 |
| | | * @event {Function} start 开始倒计时 |
| | | * @event {Function} pause 暂停倒计时 |
| | | * @event {Function} reset 重设倒计时,若 auto-start 为 true,重设后会自动开始倒计时 |
| | | * @example <uv-count-down :time="time"></uv-count-down> |
| | | */ |
| | | export default { |
| | | name: 'uv-count-down', |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | timer: null, |
| | | // 各单位(天,时,分等)剩余时间 |
| | | timeData: parseTimeData(0), |
| | | // 格式化后的时间,如"03:23:21" |
| | | formattedTime: '0', |
| | | // 倒计时是否正在进行中 |
| | | runing: false, |
| | | endTime: 0, // 结束的毫秒时间戳 |
| | | remainTime: 0, // 剩余的毫秒时间 |
| | | } |
| | | }, |
| | | watch: { |
| | | time(n) { |
| | | this.reset() |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | this.reset() |
| | | }, |
| | | // 开始倒计时 |
| | | start() { |
| | | if (this.runing) return |
| | | // 标识为进行中 |
| | | this.runing = true |
| | | // 结束时间戳 = 此刻时间戳 + 剩余的时间 |
| | | this.endTime = Date.now() + this.remainTime |
| | | this.toTick() |
| | | }, |
| | | // 根据是否展示毫秒,执行不同操作函数 |
| | | toTick() { |
| | | if (this.millisecond) { |
| | | this.microTick() |
| | | } else { |
| | | this.macroTick() |
| | | } |
| | | }, |
| | | macroTick() { |
| | | this.clearTimeout() |
| | | // 每隔一定时间,更新一遍定时器的值 |
| | | // 同时此定时器的作用也能带来毫秒级的更新 |
| | | this.timer = setTimeout(() => { |
| | | // 获取剩余时间 |
| | | const remain = this.getRemainTime() |
| | | // 重设剩余时间 |
| | | if (!isSameSecond(remain, this.remainTime) || remain === 0) { |
| | | this.setRemainTime(remain) |
| | | } |
| | | // 如果剩余时间不为0,则继续检查更新倒计时 |
| | | if (this.remainTime !== 0) { |
| | | this.macroTick() |
| | | } |
| | | }, 30) |
| | | }, |
| | | microTick() { |
| | | this.clearTimeout() |
| | | this.timer = setTimeout(() => { |
| | | this.setRemainTime(this.getRemainTime()) |
| | | if (this.remainTime !== 0) { |
| | | this.microTick() |
| | | } |
| | | }, 50) |
| | | }, |
| | | // 获取剩余的时间 |
| | | getRemainTime() { |
| | | // 取最大值,防止出现小于0的剩余时间值 |
| | | return Math.max(this.endTime - Date.now(), 0) |
| | | }, |
| | | // 设置剩余的时间 |
| | | setRemainTime(remain) { |
| | | this.remainTime = remain |
| | | // 根据剩余的毫秒时间,得出该有天,小时,分钟等的值,返回一个对象 |
| | | const timeData = parseTimeData(remain) |
| | | this.$emit('change', timeData) |
| | | // 得出格式化后的时间 |
| | | this.formattedTime = parseFormat(this.format, timeData) |
| | | // 如果时间已到,停止倒计时 |
| | | if (remain <= 0) { |
| | | this.pause() |
| | | this.$emit('finish') |
| | | } |
| | | }, |
| | | // 重置倒计时 |
| | | reset() { |
| | | this.pause() |
| | | this.remainTime = this.time |
| | | this.setRemainTime(this.remainTime) |
| | | if (this.autoStart) { |
| | | this.start() |
| | | } |
| | | }, |
| | | // 暂停倒计时 |
| | | pause() { |
| | | this.runing = false; |
| | | this.clearTimeout() |
| | | }, |
| | | // 清空定时器 |
| | | clearTimeout() { |
| | | clearTimeout(this.timer) |
| | | this.timer = null |
| | | } |
| | | }, |
| | | // #ifdef VUE2 |
| | | beforeDestroy() { |
| | | this.clearTimeout() |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | unmounted() { |
| | | this.clearTimeout() |
| | | } |
| | | // #endif |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-count-down-text-color: $uv-content-color !default; |
| | | $uv-count-down-text-font-size: 15px !default; |
| | | $uv-count-down-text-line-height: 22px !default; |
| | | .uv-count-down { |
| | | &__text { |
| | | color: $uv-count-down-text-color; |
| | | font-size: $uv-count-down-text-font-size; |
| | | line-height: $uv-count-down-text-line-height; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-count-down", |
| | | "displayName": "uv-count-down 倒计时 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.3", |
| | | "description": "该倒计时组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。", |
| | | "keywords": [ |
| | | "uv-count-down", |
| | | "uvui", |
| | | "uv-ui", |
| | | "countDown", |
| | | "倒计时" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## CountDown 倒计时 |
| | | |
| | | > **组件名:uv-count-down** |
| | | |
| | | 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/countDown.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.4(2023-06-20) |
| | | 1. 优化 |
| | | ## 1.0.3(2023-06-20) |
| | | 1. 修复继续滚动的函数 |
| | | 2. 修复其他 |
| | | ## 1.0.2(2023-06-20) |
| | | 1. 适配px和rpx的单位 |
| | | 2. 适配customStyle参数 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-count-to 数字滚动 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 开始的数值,默认从0增长到某一个数 |
| | | startVal: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 要滚动的目标数值,必须 |
| | | endVal: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 滚动到目标数值的动画持续时间,单位为毫秒(ms) |
| | | duration: { |
| | | type: [String, Number], |
| | | default: 2000 |
| | | }, |
| | | // 设置数值后是否自动开始滚动 |
| | | autoplay: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 要显示的小数位数 |
| | | decimals: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 是否在即将到达目标数值的时候,使用缓慢滚动的效果 |
| | | useEasing: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 十进制分割 |
| | | decimal: { |
| | | type: [String, Number], |
| | | default: '.' |
| | | }, |
| | | // 字体颜色 |
| | | color: { |
| | | type: String, |
| | | default: '#606266' |
| | | }, |
| | | // 字体大小 |
| | | fontSize: { |
| | | type: [String, Number], |
| | | default: 22 |
| | | }, |
| | | // 是否加粗字体 |
| | | bold: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 千位分隔符,类似金额的分割(¥23,321.05中的",") |
| | | separator: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | ...uni.$uv?.props?.countTo |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <text |
| | | class="uv-count-num" |
| | | :style="[{ |
| | | fontSize: $uv.addUnit(fontSize), |
| | | fontWeight: bold ? 'bold' : 'normal', |
| | | color: color |
| | | },$uv.addStyle(customStyle)]" |
| | | >{{ displayValue }}</text> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * countTo 数字滚动 |
| | | * @description 该组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值。 |
| | | * @tutorial https://www.uvui.cn/components/countTo.html |
| | | * @property {String | Number} startVal 开始的数值,默认从0增长到某一个数(默认 0 ) |
| | | * @property {String | Number} endVal 要滚动的目标数值,必须 (默认 0 ) |
| | | * @property {String | Number} duration 滚动到目标数值的动画持续时间,单位为毫秒(ms) (默认 2000 ) |
| | | * @property {Boolean} autoplay 设置数值后是否自动开始滚动 (默认 true ) |
| | | * @property {String | Number} decimals 要显示的小数位数,见官网说明(默认 0 ) |
| | | * @property {Boolean} useEasing 滚动结束时,是否缓动结尾,见官网说明(默认 true ) |
| | | * @property {String} decimal 十进制分割 ( 默认 "." ) |
| | | * @property {String} color 字体颜色( 默认 '#606266' ) |
| | | * @property {String | Number} fontSize 字体大小,单位px( 默认 22 ) |
| | | * @property {Boolean} bold 字体是否加粗(默认 false ) |
| | | * @property {String} separator 千位分隔符,见官网说明 |
| | | * @event {Function} end 数值滚动到目标值时触发 |
| | | * @example <uv-count-to ref="uCountTo" :end-val="endVal" :autoplay="autoplay"></uv-count-to> |
| | | */ |
| | | export default { |
| | | name: 'uv-count-to', |
| | | data() { |
| | | return { |
| | | localStartVal: this.startVal, |
| | | displayValue: this.formatNumber(this.startVal), |
| | | printVal: null, |
| | | paused: false, // 是否暂停 |
| | | localDuration: Number(this.duration), |
| | | startTime: null, // 开始的时间 |
| | | timestamp: null, // 时间戳 |
| | | remaining: null, // 停留的时间 |
| | | rAF: null, |
| | | lastTime: 0 // 上一次的时间 |
| | | }; |
| | | }, |
| | | mixins: [mpMixin, mixin, props], |
| | | computed: { |
| | | countDown() { |
| | | return this.startVal > this.endVal; |
| | | } |
| | | }, |
| | | watch: { |
| | | startVal() { |
| | | this.autoplay && this.start(); |
| | | }, |
| | | endVal() { |
| | | this.autoplay && this.start(); |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.autoplay && this.start(); |
| | | }, |
| | | methods: { |
| | | easingFn(t, b, c, d) { |
| | | return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b; |
| | | }, |
| | | requestAnimationFrame(callback) { |
| | | const currTime = new Date().getTime(); |
| | | // 为了使setTimteout的尽可能的接近每秒60帧的效果 |
| | | const timeToCall = Math.max(0, 16 - (currTime - this.lastTime)); |
| | | const id = setTimeout(() => { |
| | | callback(currTime + timeToCall); |
| | | }, timeToCall); |
| | | this.lastTime = currTime + timeToCall; |
| | | return id; |
| | | }, |
| | | cancelAnimationFrame(id) { |
| | | clearTimeout(id); |
| | | }, |
| | | // 开始滚动数字 |
| | | start() { |
| | | this.localStartVal = this.startVal; |
| | | this.startTime = null; |
| | | this.localDuration = this.duration; |
| | | this.paused = false; |
| | | this.rAF = this.requestAnimationFrame(this.count); |
| | | }, |
| | | // 暂定状态,从暂停状态开始滚动;或者滚动状态下,暂停 |
| | | restart() { |
| | | if (this.paused) { |
| | | this.resume(); |
| | | this.paused = false; |
| | | } else { |
| | | this.stop(); |
| | | this.paused = true; |
| | | } |
| | | }, |
| | | // 暂停 |
| | | stop() { |
| | | this.cancelAnimationFrame(this.rAF); |
| | | this.paused = true; |
| | | }, |
| | | // 重新开始(暂停的情况下) |
| | | resume() { |
| | | if (!this.remaining) return; |
| | | this.startTime = 0; |
| | | this.localDuration = this.remaining; |
| | | this.localStartVal = this.printVal; |
| | | this.requestAnimationFrame(this.count); |
| | | }, |
| | | // 重置 |
| | | reset() { |
| | | this.startTime = null; |
| | | this.cancelAnimationFrame(this.rAF); |
| | | this.displayValue = this.formatNumber(this.startVal); |
| | | }, |
| | | count(timestamp) { |
| | | if (!this.startTime) this.startTime = timestamp; |
| | | this.timestamp = timestamp; |
| | | const progress = timestamp - this.startTime; |
| | | this.remaining = this.localDuration - progress; |
| | | if (this.useEasing) { |
| | | if (this.countDown) { |
| | | this.printVal = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration); |
| | | } else { |
| | | this.printVal = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration); |
| | | } |
| | | } else { |
| | | if (this.countDown) { |
| | | this.printVal = this.localStartVal - (this.localStartVal - this.endVal) * (progress / this.localDuration); |
| | | } else { |
| | | this.printVal = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration); |
| | | } |
| | | } |
| | | if (this.countDown) { |
| | | this.printVal = this.printVal < this.endVal ? this.endVal : this.printVal; |
| | | } else { |
| | | this.printVal = this.printVal > this.endVal ? this.endVal : this.printVal; |
| | | } |
| | | this.displayValue = this.formatNumber(this.printVal) || 0; |
| | | if (progress < this.localDuration) { |
| | | this.rAF = this.requestAnimationFrame(this.count); |
| | | } else { |
| | | this.$emit('end'); |
| | | } |
| | | }, |
| | | // 判断是否数字 |
| | | isNumber(val) { |
| | | return !isNaN(parseFloat(val)); |
| | | }, |
| | | formatNumber(num) { |
| | | // 将num转为Number类型,因为其值可能为字符串数值,调用toFixed会报错 |
| | | num = Number(num); |
| | | num = num.toFixed(Number(this.decimals)); |
| | | num += ''; |
| | | const x = num.split('.'); |
| | | let x1 = x[0]; |
| | | const x2 = x.length > 1 ? `${this.decimal}${x[1]}` : ''; |
| | | const rgx = /(\d+)(\d{3})/; |
| | | if (this.separator && !this.isNumber(this.separator)) { |
| | | while (rgx.test(x1)) { |
| | | x1 = x1.replace(rgx, '$1' + this.separator + '$2'); |
| | | } |
| | | } |
| | | return x1 + x2; |
| | | }, |
| | | destroyed() { |
| | | this.cancelAnimationFrame(this.rAF); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | |
| | | .uv-count-num { |
| | | /* #ifndef APP-NVUE */ |
| | | display: inline-flex; |
| | | /* #endif */ |
| | | text-align: center; |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-count-to", |
| | | "displayName": "uv-count-to 数字滚动 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.4", |
| | | "description": "该数字滚动组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值,一种数字上升的视觉冲击效果。", |
| | | "keywords": [ |
| | | "countTo", |
| | | "uvui", |
| | | "uv-ui", |
| | | "数字滚动", |
| | | "数字变化" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## CountTo 数字滚动 |
| | | |
| | | > **组件名:uv-count-to** |
| | | |
| | | 该组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/countTo.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.15(2024-06-14) |
| | | 1. 修复上次更改引出的BUG |
| | | ## 1.0.14(2024-05-31) |
| | | 1. 修复设置maxDate后存在选择不准确的BUG |
| | | ## 1.0.13(2024-03-22) |
| | | 1. 修复VUE3中出现的BUG |
| | | ## 1.0.12(2023-11-27) |
| | | 1. 增加round圆角属性 |
| | | ## 1.0.11(2023-10-11) |
| | | 1. 修复设置minDate出现选择错乱的BUG |
| | | ## 1.0.10(2023-09-01) |
| | | 1. 增加clearDate参数,是否清除上次选择,默认false |
| | | ## 1.0.9(2023-08-31) |
| | | 1. 增加mode="year",方便只选择年 |
| | | ## 1.0.8(2023-07-17) |
| | | 1. 优化文档 |
| | | 2. 优化其他 |
| | | ## 1.0.7(2023-07-13) |
| | | 1. 修复 uv-datetime-picker 设置value属性不生效的BUG |
| | | ## 1.0.6(2023-07-05) |
| | | 修复vue3模式下,动态修改v-model绑定的值无效的BUG |
| | | ## 1.0.5(2023-07-02) |
| | | uv-datetime-picker 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/datetimePicker.html |
| | | ## 1.0.4(2023-06-29) |
| | | 1. 修复抖音小程序报错的BUG |
| | | ## 1.0.3(2023-06-07) |
| | | 1. 取消defaultIndex参数,传该值没实际意义,后续更新文档 |
| | | ## 1.0.2(2023-06-02) |
| | | 1. 修复v-model重新赋值不更新的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-datetime-picker 时间选择器 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 是否打开组件 |
| | | show: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否展示顶部的操作栏 |
| | | showToolbar: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 顶部标题 |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 展示格式,mode=date为日期选择,mode=time为时间选择,mode=year-month为年月选择,mode=datetime为日期时间选择 |
| | | mode: { |
| | | type: String, |
| | | default: 'datetime' |
| | | }, |
| | | // 可选的最大时间 |
| | | maxDate: { |
| | | type: Number, |
| | | // 最大默认值为后10年 |
| | | default: new Date(new Date().getFullYear() + 10, 0, 1).getTime() |
| | | }, |
| | | // 可选的最小时间 |
| | | minDate: { |
| | | type: Number, |
| | | // 最小默认值为前10年 |
| | | default: new Date(new Date().getFullYear() - 10, 0, 1).getTime() |
| | | }, |
| | | // 可选的最小小时,仅mode=time有效 |
| | | minHour: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | // 可选的最大小时,仅mode=time有效 |
| | | maxHour: { |
| | | type: Number, |
| | | default: 23 |
| | | }, |
| | | // 可选的最小分钟,仅mode=time有效 |
| | | minMinute: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | // 可选的最大分钟,仅mode=time有效 |
| | | maxMinute: { |
| | | type: Number, |
| | | default: 59 |
| | | }, |
| | | // 选项过滤函数 |
| | | filter: { |
| | | type: [Function, null], |
| | | default: null |
| | | }, |
| | | // 选项格式化函数 |
| | | formatter: { |
| | | type: [Function, null], |
| | | default: null |
| | | }, |
| | | // 是否显示加载中状态 |
| | | loading: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 各列中,单个选项的高度 |
| | | itemHeight: { |
| | | type: [String, Number], |
| | | default: 44 |
| | | }, |
| | | // 取消按钮的文字 |
| | | cancelText: { |
| | | type: String, |
| | | default: '取消' |
| | | }, |
| | | // 确认按钮的文字 |
| | | confirmText: { |
| | | type: String, |
| | | default: '确认' |
| | | }, |
| | | // 取消按钮的颜色 |
| | | cancelColor: { |
| | | type: String, |
| | | default: '#909193' |
| | | }, |
| | | // 确认按钮的颜色 |
| | | confirmColor: { |
| | | type: String, |
| | | default: '#3c9cff' |
| | | }, |
| | | // 每列中可见选项的数量 |
| | | visibleItemCount: { |
| | | type: [String, Number], |
| | | default: 5 |
| | | }, |
| | | // 是否允许点击遮罩关闭选择器 |
| | | closeOnClickOverlay: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否允许点击确认关闭选择器 |
| | | closeOnClickConfirm: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否清空上次选择内容 |
| | | clearDate: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 圆角 |
| | | round: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | ...uni.$uv?.props?.datetimePicker |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <uv-picker |
| | | ref="picker" |
| | | :closeOnClickOverlay="closeOnClickOverlay" |
| | | :closeOnClickConfirm="closeOnClickConfirm" |
| | | :columns="columns" |
| | | :title="title" |
| | | :itemHeight="itemHeight" |
| | | :showToolbar="showToolbar" |
| | | :visibleItemCount="visibleItemCount" |
| | | :defaultIndex="innerDefaultIndex" |
| | | :cancelText="cancelText" |
| | | :confirmText="confirmText" |
| | | :cancelColor="cancelColor" |
| | | :confirmColor="confirmColor" |
| | | :round="round" |
| | | @close="close" |
| | | @cancel="cancel" |
| | | @confirm="confirm" |
| | | @change="change" |
| | | > |
| | | </uv-picker> |
| | | </template> |
| | | <script> |
| | | function times(n, iteratee) { |
| | | let index = -1 |
| | | const result = Array(n < 0 ? 0 : n) |
| | | while (++index < n) { |
| | | result[index] = iteratee(index) |
| | | } |
| | | return result |
| | | } |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | import dayjs from '@/uni_modules/uv-ui-tools/libs/util/dayjs.js' |
| | | /** |
| | | * DatetimePicker 时间日期选择器 |
| | | * @description 此选择器用于时间日期 |
| | | * @tutorial https://www.uvui.cn/components/datetimePicker.html |
| | | * @property {Boolean} showToolbar 是否显示顶部的操作栏 ( 默认 true ) |
| | | * @property {String | Number} value 绑定值 |
| | | * @property {String} title 顶部标题 |
| | | * @property {String} mode 展示格式 mode=date为日期选择,mode=time为时间选择,mode=year-month为年月选择,mode=datetime为日期时间选择 ( 默认 ‘datetime ) |
| | | * @property {Number} maxDate 可选的最大时间 默认值为后10年 |
| | | * @property {Number} minDate 可选的最小时间 默认值为前10年 |
| | | * @property {Number} minHour 可选的最小小时,仅mode=time有效 ( 默认 0 ) |
| | | * @property {Number} maxHour 可选的最大小时,仅mode=time有效 ( 默认 23 ) |
| | | * @property {Number} minMinute 可选的最小分钟,仅mode=time有效 ( 默认 0 ) |
| | | * @property {Number} maxMinute 可选的最大分钟,仅mode=time有效 ( 默认 59 ) |
| | | * @property {Function} filter 选项过滤函数 |
| | | * @property {Function} formatter 选项格式化函数 |
| | | * @property {Boolean} loading 是否显示加载中状态 ( 默认 false ) |
| | | * @property {String | Number} itemHeight 各列中,单个选项的高度 ( 默认 44 ) |
| | | * @property {String} cancelText 取消按钮的文字 ( 默认 '取消' ) |
| | | * @property {String} confirmText 确认按钮的文字 ( 默认 '确认' ) |
| | | * @property {String} cancelColor 取消按钮的颜色 ( 默认 '#909193' ) |
| | | * @property {String} confirmColor 确认按钮的颜色 ( 默认 '#3c9cff' ) |
| | | * @property {String | Number} visibleItemCount 每列中可见选项的数量 ( 默认 5 ) |
| | | * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭选择器 ( 默认 true ) |
| | | * @property {String | Number} round 圆角 ( 默认 0 ) |
| | | * @event {Function} close 关闭选择器时触发 |
| | | * @event {Function} confirm 点击确定按钮,返回当前选择的值 |
| | | * @event {Function} change 当选择值变化时触发 |
| | | * @event {Function} cancel 点击取消按钮 |
| | | * @example <uv-datetime-picker ref="datetimepicker" :value="value1" mode="datetime" ></uv-datetime-picker> |
| | | */ |
| | | export default { |
| | | name: 'uv-datetime-picker', |
| | | emits: ['close', 'cancel', 'confirm', 'input', 'change', 'update:modelValue'], |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | columns: [], |
| | | innerDefaultIndex: [], |
| | | innerFormatter: (type, value) => value |
| | | } |
| | | }, |
| | | watch: { |
| | | propsChange() { |
| | | this.$uv.sleep(100).then(res=>{ |
| | | this.init() |
| | | }) |
| | | } |
| | | }, |
| | | computed: { |
| | | // 如果以下这些变量发生了变化,意味着需要重新初始化各列的值 |
| | | propsChange() { |
| | | const propsValue = this.value || this.modelValue; |
| | | return [this.mode, this.maxDate, this.minDate, this.minHour, this.maxHour, this.minMinute, this.maxMinute, this.filter, propsValue] |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | this.getValue(); |
| | | this.updateColumnValue(this.innerValue) |
| | | }, |
| | | getValue() { |
| | | const propsValue = this.value || this.modelValue; |
| | | this.innerValue = this.correctValue(propsValue) |
| | | }, |
| | | // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用 |
| | | setFormatter(e) { |
| | | this.innerFormatter = e |
| | | }, |
| | | open() { |
| | | this.$refs.picker.open(); |
| | | // 解决打开的前一次和当前日期错乱的BUG |
| | | setTimeout(()=>{ |
| | | this.getValue(); |
| | | this.updateColumnValue(this.innerValue); |
| | | },10) |
| | | }, |
| | | close() { |
| | | this.$emit('close'); |
| | | }, |
| | | // 点击工具栏的取消按钮 |
| | | cancel() { |
| | | this.$emit('cancel') |
| | | }, |
| | | // 点击工具栏的确定按钮 |
| | | confirm() { |
| | | this.$emit('confirm', { |
| | | value: this.innerValue, |
| | | mode: this.mode |
| | | }) |
| | | if(!this.clearDate) { |
| | | this.$emit('input', this.innerValue) |
| | | this.$emit('update:modelValue', this.innerValue) |
| | | } |
| | | }, |
| | | //用正则截取输出值,当出现多组数字时,抛出错误 |
| | | intercept(e, type) { |
| | | let judge = e.match(/\d+/g) |
| | | //判断是否掺杂数字 |
| | | if (judge.length > 1) { |
| | | this.$uv.error("请勿在过滤或格式化函数时添加数字") |
| | | return 0 |
| | | } else if (type && judge[0].length == 4) { //判断是否是年份 |
| | | return judge[0] |
| | | } else if (judge[0].length > 2) { |
| | | this.$uv.error("请勿在过滤或格式化函数时添加数字") |
| | | return 0 |
| | | } else { |
| | | return judge[0] |
| | | } |
| | | }, |
| | | // 列发生变化时触发 |
| | | change(e) { |
| | | const { indexs, values } = e |
| | | let selectValue = '' |
| | | if (this.mode === 'time') { |
| | | // 根据value各列索引,从各列数组中,取出当前时间的选中值 |
| | | selectValue = `${this.intercept(values[0][indexs[0]])}:${this.intercept(values[1][indexs[1]])}` |
| | | } else if (this.mode === 'year') { |
| | | const year = parseInt(this.intercept(values[0][indexs[0]], 'year')); |
| | | selectValue = Number(new Date(year, 0)); |
| | | } else { |
| | | // 将选择的值转为数值,比如'03'转为数值的3,'2019'转为数值的2019 |
| | | const year = parseInt(this.intercept(values[0][indexs[0]], 'year')) |
| | | const month = parseInt(this.intercept(values[1][indexs[1]])) |
| | | let date = parseInt(values[2] ? this.intercept(values[2][indexs[2]]) : 1) |
| | | let hour = 0, |
| | | minute = 0 |
| | | // 此月份的最大天数 |
| | | const maxDate = dayjs(`${year}-${month}`).daysInMonth() |
| | | // year-month模式下,date不会出现在列中,设置为1,为了符合后边需要减1的需求 |
| | | if (this.mode === 'year-month') { |
| | | date = 1 |
| | | } |
| | | // 不允许超过maxDate值 |
| | | date = Math.min(maxDate, date) |
| | | if (this.mode === 'datetime') { |
| | | hour = parseInt(this.intercept(values[3][indexs[3]])) |
| | | minute = parseInt(this.intercept(values[4][indexs[4]])) |
| | | } |
| | | // 转为时间模式 |
| | | selectValue = Number(new Date(year, month - 1, date, hour, minute)) |
| | | } |
| | | // 取出准确的合法值,防止超越边界的情况 |
| | | selectValue = this.correctValue(selectValue) |
| | | this.innerValue = selectValue |
| | | this.updateColumnValue(selectValue) |
| | | // 发出change时间,value为当前选中的时间戳 |
| | | this.$emit('change', { |
| | | value: selectValue, |
| | | mode: this.mode |
| | | }) |
| | | }, |
| | | // 更新各列的值,进行补0、格式化等操作 |
| | | updateColumnValue(value) { |
| | | this.innerValue = value |
| | | this.updateColumns() |
| | | this.updateIndexs(value) |
| | | }, |
| | | // 更新索引 |
| | | updateIndexs(value) { |
| | | let values = [] |
| | | const formatter = this.formatter || this.innerFormatter; |
| | | if (this.mode === 'time') { |
| | | // 将time模式的时间用:分隔成数组 |
| | | const timeArr = value.split(':') |
| | | // 使用formatter格式化方法进行管道处理 |
| | | values = [formatter('hour', timeArr[0]), formatter('minute', timeArr[1])] |
| | | } else { |
| | | const date = new Date(value) |
| | | values = [ |
| | | formatter('year', `${dayjs(value).year()}`), |
| | | // 月份补0 |
| | | formatter('month', this.$uv.padZero(dayjs(value).month() + 1)) |
| | | ] |
| | | if (this.mode === 'date') { |
| | | // date模式,需要添加天列 |
| | | values.push(formatter('day', this.$uv.padZero(dayjs(value).date()))) |
| | | } |
| | | if (this.mode === 'datetime') { |
| | | // 数组的push方法,可以写入多个参数 |
| | | values.push(formatter('day', this.$uv.padZero(dayjs(value).date())), formatter('hour', this.$uv.padZero(dayjs(value).hour())), formatter('minute', this.$uv.padZero(dayjs(value).minute()))) |
| | | } |
| | | } |
| | | // 根据当前各列的所有值,从各列默认值中找到默认值在各列中的索引 |
| | | const indexs = this.columns.map((column, index) => { |
| | | // 通过取大值,可以保证不会出现找不到索引的-1情况 |
| | | return Math.max(0, column.findIndex(item => item === values[index])) |
| | | }) |
| | | this.$nextTick(()=>{ |
| | | this.$uv.sleep(100).then(res=>{ |
| | | this.$refs.picker.setIndexs(indexs,true); |
| | | }) |
| | | }) |
| | | }, |
| | | // 更新各列的值 |
| | | updateColumns() { |
| | | const formatter = this.formatter || this.innerFormatter |
| | | // 获取各列的值,并且map后,对各列的具体值进行补0操作 |
| | | const results = this.getOriginColumns().map((column) => column.values.map((value) => formatter(column.type, value))) |
| | | this.columns = results |
| | | }, |
| | | getOriginColumns() { |
| | | // 生成各列的值 |
| | | const results = this.getRanges().map(({ type, range }) => { |
| | | let values = times(range[1] - range[0] + 1, (index) => { |
| | | let value = range[0] + index |
| | | value = type === 'year' ? `${value}` : this.$uv.padZero(value) |
| | | return value |
| | | }) |
| | | // 进行过滤 |
| | | if (this.filter) { |
| | | values = this.filter(type, values) |
| | | } |
| | | return { type, values } |
| | | }) |
| | | return results |
| | | }, |
| | | // 通过最大值和最小值生成数组 |
| | | generateArray(start, end) { |
| | | return Array.from(new Array(end + 1).keys()).slice(start) |
| | | }, |
| | | // 得出合法的时间 |
| | | correctValue(value) { |
| | | const isDateMode = this.mode !== 'time' |
| | | if (isDateMode && !this.$uv.test.date(value)) { |
| | | // 如果是日期类型,但是又没有设置合法的当前时间的话,使用最小时间为当前时间 |
| | | value = this.minDate |
| | | } else if (!isDateMode && !value) { |
| | | // 如果是时间类型,而又没有默认值的话,就用最小时间 |
| | | value = `${this.$uv.padZero(this.minHour)}:${this.$uv.padZero(this.minMinute)}` |
| | | } |
| | | // 时间类型 |
| | | if (!isDateMode) { |
| | | if (String(value).indexOf(':') === -1) return this.$uv.error('时间错误,请传递如12:24的格式') |
| | | let [hour, minute] = value.split(':') |
| | | // 对时间补零,同时控制在最小值和最大值之间 |
| | | hour = this.$uv.padZero(this.$uv.range(this.minHour, this.maxHour, Number(hour))) |
| | | minute = this.$uv.padZero(this.$uv.range(this.minMinute, this.maxMinute, Number(minute))) |
| | | return `${ hour }:${ minute }` |
| | | } else { |
| | | // 如果是日期格式,控制在最小日期和最大日期之间 |
| | | value = dayjs(value).isBefore(dayjs(this.minDate)) ? this.minDate : value |
| | | value = dayjs(value).isAfter(dayjs(this.maxDate)) ? this.maxDate : value |
| | | return value |
| | | } |
| | | }, |
| | | // 获取每列的最大和最小值 |
| | | getRanges() { |
| | | if (this.mode === 'time') { |
| | | return [{ |
| | | type: 'hour', |
| | | range: [this.minHour, this.maxHour], |
| | | }, { |
| | | type: 'minute', |
| | | range: [this.minMinute, this.maxMinute], |
| | | }, ]; |
| | | } |
| | | const { maxYear, maxDate, maxMonth, maxHour, maxMinute, } = this.getBoundary('max', this.innerValue); |
| | | const { minYear, minDate, minMonth, minHour, minMinute, } = this.getBoundary('min', this.innerValue); |
| | | const result = [{ |
| | | type: 'year', |
| | | range: [minYear, maxYear], |
| | | }, { |
| | | type: 'month', |
| | | range: [minMonth, maxMonth], |
| | | }, { |
| | | type: 'day', |
| | | range: [minDate, maxDate], |
| | | }, { |
| | | type: 'hour', |
| | | range: [minHour, maxHour], |
| | | }, { |
| | | type: 'minute', |
| | | range: [minMinute, maxMinute], |
| | | }, ]; |
| | | if (this.mode === 'date') result.splice(3, 2); |
| | | if (this.mode === 'year-month') result.splice(2, 3); |
| | | if (this.mode === 'year') result.splice(1, 4); |
| | | return result; |
| | | }, |
| | | // 根据minDate、maxDate、minHour、maxHour等边界值,判断各列的开始和结束边界值 |
| | | getBoundary(type, innerValue) { |
| | | const value = new Date(innerValue) |
| | | const boundary = new Date(this[`${type}Date`]) |
| | | const year = dayjs(boundary).year() |
| | | let month = 1 |
| | | let date = 1 |
| | | let hour = 0 |
| | | let minute = 0 |
| | | if (type === 'max') { |
| | | month = 12 |
| | | // 月份的天数 |
| | | date = dayjs(value).daysInMonth() |
| | | hour = 23 |
| | | minute = 59 |
| | | } |
| | | // 获取边界值,逻辑是:当年达到了边界值(最大或最小年),就检查月允许的最大和最小值,以此类推 |
| | | if (dayjs(value).year() === year) { |
| | | month = dayjs(boundary).month() + 1 |
| | | if (dayjs(value).month() + 1 === month) { |
| | | date = dayjs(boundary).date() |
| | | if (dayjs(value).date() === date) { |
| | | hour = dayjs(boundary).hour() |
| | | if (dayjs(value).hour() === hour) { |
| | | minute = dayjs(boundary).minute() |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return { |
| | | [`${type}Year`]: year, |
| | | [`${type}Month`]: month, |
| | | [`${type}Date`]: date, |
| | | [`${type}Hour`]: hour, |
| | | [`${type}Minute`]: minute |
| | | } |
| | | }, |
| | | }, |
| | | } |
| | | </script> |
| New file |
| | |
| | | { |
| | | "id": "uv-datetime-picker", |
| | | "displayName": "uv-datetime-picker 时间选择器 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.15", |
| | | "description": "时间选择器组件用于时间日期,主要用于年月日时分的选择,具体选择的精确度由参数控制。", |
| | | "keywords": [ |
| | | "datetime-picker", |
| | | "uvui", |
| | | "uv-ui", |
| | | "datetime", |
| | | "时间选择" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-picker" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y", |
| | | "alipay": "n" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## DatetimePicker 时间选择器 |
| | | |
| | | > **组件名:uv-datetime-picker** |
| | | |
| | | 此选择器用于时间日期,主要用于年月日时分的选择,具体选择的精确度由参数控制。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/datetimePicker.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.4(2023-12-06) |
| | | 1. 优化 |
| | | ## 1.0.3(2023-12-06) |
| | | 1. 阻止事件冒泡问题 |
| | | ## 1.0.2(2023-06-01) |
| | | 1. 修复点击触发两次事件的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-divider 分割线 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 是否虚线 |
| | | dashed: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否细线 |
| | | hairline: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否以点替代文字,优先于text字段起作用 |
| | | dot: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 内容文本的位置,left-左边,center-中间,right-右边 |
| | | textPosition: { |
| | | type: String, |
| | | default: 'center' |
| | | }, |
| | | // 文本内容 |
| | | text: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 文本大小 |
| | | textSize: { |
| | | type: [String, Number], |
| | | default: 14 |
| | | }, |
| | | // 文本颜色 |
| | | textColor: { |
| | | type: String, |
| | | default: '#909399' |
| | | }, |
| | | // 线条颜色 |
| | | lineColor: { |
| | | type: String, |
| | | default: '#dcdfe6' |
| | | }, |
| | | ...uni.$uv?.props?.divider |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-divider" |
| | | :style="[$uv.addStyle(customStyle)]" |
| | | @tap="click" |
| | | > |
| | | <uv-line |
| | | :color="lineColor" |
| | | :customStyle="leftLineStyle" |
| | | :hairline="hairline" |
| | | :dashed="dashed"></uv-line> |
| | | <text |
| | | v-if="dot" |
| | | class="uv-divider__dot" |
| | | >●</text> |
| | | <text |
| | | v-else-if="text" |
| | | class="uv-divider__text" |
| | | :style="[textStyle]" |
| | | >{{text}}</text> |
| | | <uv-line |
| | | :color="lineColor" |
| | | :customStyle="rightLineStyle" |
| | | :hairline="hairline" |
| | | :dashed="dashed" |
| | | ></uv-line> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * divider 分割线 |
| | | * @description 区隔内容的分割线,一般用于页面底部"没有更多"的提示。 |
| | | * @tutorial https://www.uvui.cn/components/divider.html |
| | | * @property {Boolean} dashed 是否虚线 (默认 false ) |
| | | * @property {Boolean} hairline 是否细线 (默认 true ) |
| | | * @property {Boolean} dot 是否以点替代文字,优先于text字段起作用 (默认 false ) |
| | | * @property {String} textPosition 内容文本的位置,left-左边,center-中间,right-右边 (默认 'center' ) |
| | | * @property {String | Number} text 文本内容 |
| | | * @property {String | Number} textSize 文本大小 (默认 14) |
| | | * @property {String} textColor 文本颜色 (默认 '#909399' ) |
| | | * @property {String} lineColor 线条颜色 (默认 '#dcdfe6' ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * |
| | | * @event {Function} click divider组件被点击时触发 |
| | | * @example <uv-divider :color="color">锦瑟无端五十弦</uv-divider> |
| | | */ |
| | | export default { |
| | | name: 'uv-divider', |
| | | mixins: [mpMixin, mixin, props], |
| | | emits: ['click'], |
| | | computed: { |
| | | textStyle() { |
| | | const style = {} |
| | | style.fontSize = this.$uv.addUnit(this.textSize) |
| | | style.color = this.textColor |
| | | return style |
| | | }, |
| | | // 左边线条的的样式 |
| | | leftLineStyle() { |
| | | const style = {} |
| | | // 如果是在左边,设置左边的宽度为固定值 |
| | | if (this.textPosition === 'left') { |
| | | style.width = '80rpx' |
| | | } else { |
| | | style.flex = 1 |
| | | } |
| | | return style |
| | | }, |
| | | // 右边线条的的样式 |
| | | rightLineStyle() { |
| | | const style = {} |
| | | // 如果是在右边,设置右边的宽度为固定值 |
| | | if (this.textPosition === 'right') { |
| | | style.width = '80rpx' |
| | | } else { |
| | | style.flex = 1 |
| | | } |
| | | return style |
| | | } |
| | | }, |
| | | methods: { |
| | | // divider组件被点击时触发 |
| | | click() { |
| | | this.$emit('click'); |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | $uv-divider-margin: 15px 0 !default; |
| | | $uv-divider-text-margin: 0 15px !default; |
| | | $uv-divider-dot-font-size: 12px !default; |
| | | $uv-divider-dot-margin: 0 12px !default; |
| | | $uv-divider-dot-color: #c0c4cc !default; |
| | | .uv-divider { |
| | | @include flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | margin: $uv-divider-margin; |
| | | &__text { |
| | | margin: $uv-divider-text-margin; |
| | | } |
| | | &__dot { |
| | | font-size: $uv-divider-dot-font-size; |
| | | margin: $uv-divider-dot-margin; |
| | | color: $uv-divider-dot-color; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-divider", |
| | | "displayName": "uv-divider 分割线 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.4", |
| | | "description": "区隔内容的分割线,一般用于页面底部没有更多的提示。", |
| | | "keywords": [ |
| | | "divider", |
| | | "uvui", |
| | | "uv-ui", |
| | | "分割线", |
| | | "没有更多" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-line" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Divider 分割线 |
| | | |
| | | > **组件名:uv-divider** |
| | | |
| | | 区隔内容的分割线,一般用于页面底部"没有更多"的提示。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/divider.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.5(2024-01-02) |
| | | 1. 修复parentData不变的BUG |
| | | ## 1.0.4(2023-09-28) |
| | | 1. 增加uv-sticky依赖 |
| | | ## 1.0.3(2023-08-29) |
| | | 1. 修复自定义内容,点击自定义内容时会自动关闭弹窗的问题 |
| | | ## 1.0.2(2023-08-22) |
| | | 1. 优化 |
| | | ## 1.0.1(2023-08-22) |
| | | 1. 增加@change回调,返回弹窗关闭状态 |
| | | 2. 增加init方法,方便位置改变进行调整 |
| | | ## 1.0.0(2023-07-30) |
| | | 新增uv-drop-down 下拉筛选组件 |
| New file |
| | |
| | | <template> |
| | | <view class="uv-drop-down-item" @click="clickHandler"> |
| | | <uv-text :text="label" :size="getTextStyle.size" :color="getTextStyle.color" lines="1" :custom-style="{marginRight: '10rpx',maxWidth:'200rpx'}"></uv-text> |
| | | <uv-icon :name="getDownIcon.name" :size="getDownIcon.size" :color="getDownIcon.color" v-if="[1,'1'].indexOf(type)==-1"></uv-icon> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'; |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'; |
| | | /** |
| | | * DropDown 下拉框 |
| | | * @description 下拉筛选 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-drop-down |
| | | * @property {String | Number} name 字段标识 |
| | | * @property {String | Number} type 类型 1 没有筛选项,直接进行选中和不选中 2 有多个选项 |
| | | * @property {String | Number} label 筛选项的文本 |
| | | * @property {Boolean} isDrop 该项是否打开 |
| | | */ |
| | | export default { |
| | | name: 'uv-drop-down-item', |
| | | mixins: [mpMixin, mixin], |
| | | emits: ['click'], |
| | | props: { |
| | | name: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 类型 1 没有筛选项,直接进行选中和不选中 2 有多个选项 |
| | | type: { |
| | | type: [String, Number], |
| | | default: '2' |
| | | }, |
| | | // 筛选的文本 |
| | | label: { |
| | | type: [String], |
| | | default: '' |
| | | }, |
| | | // 筛选值 |
| | | value: { |
| | | type: [String, Number, null], |
| | | default: '' |
| | | }, |
| | | // 是否下拉菜单打开 |
| | | isDrop: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | parentData: { |
| | | defaultValue: [0, '0', 'all'], |
| | | textSize: '30rpx', |
| | | textColor: '#333', |
| | | textActiveSize: '30rpx', |
| | | textActiveColor: '#3c9cff', |
| | | extraIcon: {}, |
| | | extraActiveIcon: {}, |
| | | sign: '', |
| | | clickHandler: Function |
| | | }, |
| | | active: false, |
| | | isDroped: false, |
| | | elId: '' |
| | | } |
| | | }, |
| | | watch: { |
| | | isDrop: { |
| | | handler(newVal) { |
| | | this.isDroped = newVal; |
| | | }, |
| | | immediate: true |
| | | }, |
| | | value: { |
| | | handler(newVal) { |
| | | this.$nextTick(()=>{ |
| | | this.active = this.parentData.defaultValue.indexOf(newVal) == -1; |
| | | }) |
| | | }, |
| | | immediate: true |
| | | } |
| | | }, |
| | | computed: { |
| | | getDownIcon() { |
| | | const style = { |
| | | size: '30rpx', |
| | | color: '#333', |
| | | ...this.parentData.extraIcon |
| | | } |
| | | if (this.active || this.isDroped) { |
| | | style.color = this.parentData.extraActiveIcon?.color ? this.parentData.extraActiveIcon?.color : '#3c9cff'; |
| | | style.size = this.parentData.extraActiveIcon?.size ? this.parentData.extraActiveIcon?.size : '30rpx'; |
| | | } |
| | | if (this.isDroped) { |
| | | style.name = this.parentData.extraActiveIcon?.name; |
| | | } |
| | | return style; |
| | | }, |
| | | getTextStyle() { |
| | | const style = { |
| | | size: this.parentData.textSize, |
| | | color: this.parentData.textColor |
| | | }; |
| | | if (this.active || this.isDroped) { |
| | | style.size = this.parentData.textActiveSize; |
| | | style.color = this.parentData.textActiveColor; |
| | | } |
| | | return style; |
| | | } |
| | | }, |
| | | created() { |
| | | this.init(); |
| | | }, |
| | | methods: { |
| | | init() { |
| | | this.elId = this.$uv.guid(); |
| | | this.getParentData('uv-drop-down'); |
| | | if (!this.parent) { |
| | | this.$uv.error('uv-drop-down必须搭配uv-drop-down-item组件使用'); |
| | | } |
| | | uni.$on('HANDLE_DROPDOWN_ONE', id => { |
| | | if (this.isDroped && this.elId != id) { |
| | | this.isDroped = false; |
| | | } |
| | | }) |
| | | uni.$on(`${this.parentData.sign}_CLOSEPOPUP`, async () => { |
| | | if (this.isDroped) { |
| | | this.isDroped = false; |
| | | } |
| | | }) |
| | | }, |
| | | async clickHandler() { |
| | | let data = {}; |
| | | uni.$emit('HANDLE_DROPDOWN_ONE', this.elId); |
| | | switch (+this.type) { |
| | | case 1: |
| | | this.active = !this.active; |
| | | data = { |
| | | name: this.name, |
| | | active: this.active, |
| | | type: this.type |
| | | }; |
| | | break; |
| | | case 2: |
| | | this.isDroped = !this.isDroped; |
| | | data = { |
| | | name: this.name, |
| | | active: this.isDroped, |
| | | type: this.type |
| | | }; |
| | | break; |
| | | } |
| | | this.parentData.clickHandler(data); |
| | | this.$emit('click', data); |
| | | uni.$emit(`${this.parentData.sign}_CLICKMENU`, { |
| | | show: +this.type > 1 && this.isDroped |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | .uv-drop-down-item { |
| | | @include flex; |
| | | align-items: center; |
| | | padding: 20rpx; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <view class="uv-drop-down-popup"> |
| | | <uv-transition :show="show" mode="fade" :duration="300" :custom-style="overlayStyle" @click="clickOverlay"> |
| | | <view class="uv-dp__container" ref="uvDPContainer" :style="{height: `${height}px`}" @click.stop="blockClick"> |
| | | <view class="uv-dp__container__list" ref="uvDPList"> |
| | | <slot> |
| | | <view class="uv-dp__container__list--item" v-for="(item,index) in list" :key="index" @click="clickHandler(item,index)" :style="[itemCustomStyle(index)]"> |
| | | <uv-text :text="item[keyName]" :size="getTextSize(index)" :color="getTextColor(index)"></uv-text> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </uv-transition> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'; |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'; |
| | | // #ifdef APP-NVUE |
| | | const animation = uni.requireNativePlugin('animation'); |
| | | const dom = uni.requireNativePlugin('dom'); |
| | | // #endif |
| | | /** |
| | | * DropDownPopup 下拉框 |
| | | * @description 下拉筛选框 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-drop-down |
| | | * @property {String | Number} name 字段标识 |
| | | * @property {String | Number} zIndex 弹出层的层级 |
| | | * @property {String | Number} opacity 遮罩层的透明度 |
| | | * @property {Boolean} clickOverlayOnClose 是否允许点击遮罩层关闭弹窗 |
| | | * @property {Object} currentDropItem 当前下拉筛选菜单对象 |
| | | * @property {String} keyName 指定从当前下拉筛选菜单对象元素中读取哪个属性作为文本展示 |
| | | */ |
| | | export default { |
| | | name: 'uv-drop-down-popup', |
| | | mixins: [mpMixin, mixin], |
| | | props: { |
| | | sign: { |
| | | type: [String, Number], |
| | | default: 'UVDROPDOWN' |
| | | }, |
| | | zIndex: { |
| | | type: [Number, String], |
| | | default: 999 |
| | | }, |
| | | opacity: { |
| | | type: [Number, String], |
| | | default: 0.5 |
| | | }, |
| | | clickOverlayOnClose: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 当前下拉选项对象 |
| | | currentDropItem: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | activeIndex: 0, |
| | | child: [] |
| | | } |
| | | } |
| | | }, |
| | | keyName: { |
| | | type: String, |
| | | default: 'label' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | show: false, |
| | | rect: {}, |
| | | height: 0 |
| | | } |
| | | }, |
| | | computed: { |
| | | overlayStyle() { |
| | | let { height = 0, top = 0 } = this.rect; |
| | | // #ifdef H5 |
| | | top += this.$uv.sys().windowTop; |
| | | // #endif |
| | | const style = { |
| | | position: 'fixed', |
| | | top: `${top+height}px`, |
| | | left: 0, |
| | | right: 0, |
| | | zIndex: this.zIndex, |
| | | bottom: 0, |
| | | 'background-color': `rgba(0, 0, 0, ${this.opacity})` |
| | | } |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)) |
| | | }, |
| | | list() { |
| | | try { |
| | | return Array.isArray(this.currentDropItem.child) ? this.currentDropItem.child : []; |
| | | } catch (e) { |
| | | return []; |
| | | } |
| | | }, |
| | | getTextColor(index) { |
| | | return index => { |
| | | const active = this.currentDropItem.activeIndex == index; |
| | | const color = this.currentDropItem.color; |
| | | const activeColor = this.currentDropItem.activeColor; |
| | | if (active) { |
| | | return activeColor ? activeColor : '#3c9cff'; |
| | | } |
| | | return color ? color : '#333'; |
| | | } |
| | | }, |
| | | getTextSize(index) { |
| | | return index => { |
| | | const active = this.currentDropItem.activeIndex == index; |
| | | const size = this.currentDropItem.size; |
| | | const activeSize = this.currentDropItem.activeSize; |
| | | if (active) { |
| | | return activeSize ? activeSize : '30rpx'; |
| | | } |
| | | return size ? size : '30rpx'; |
| | | } |
| | | }, |
| | | itemCustomStyle() { |
| | | return index => { |
| | | const active = this.currentDropItem.activeIndex == index; |
| | | const style = {}; |
| | | if (active && this.currentDropItem.itemActiveCustomStyle) { |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.currentDropItem.itemActiveCustomStyle)); |
| | | } |
| | | if (this.currentDropItem.itemCustomStyle) { |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.currentDropItem.itemCustomStyle)) |
| | | } |
| | | return style; |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.init(); |
| | | }, |
| | | methods: { |
| | | blockClick() {}, |
| | | clickHandler(item, index) { |
| | | this.currentDropItem.activeIndex = index; |
| | | this.$emit('clickItem', item); |
| | | this.close(); |
| | | }, |
| | | init() { |
| | | uni.$off(`${this.sign}_GETRECT`); |
| | | uni.$on(`${this.sign}_GETRECT`, rect => { |
| | | this.rect = rect; |
| | | }) |
| | | uni.$off(`${this.sign}_CLICKMENU`); |
| | | uni.$on(`${this.sign}_CLICKMENU`, async res => { |
| | | if (res.show) { |
| | | this.open(); |
| | | } else { |
| | | this.close(); |
| | | } |
| | | }) |
| | | }, |
| | | open() { |
| | | this.show = true; |
| | | this.$nextTick(async () => { |
| | | // #ifndef H5 || MP-WEIXIN |
| | | await this.$uv.sleep(60); |
| | | // #endif |
| | | const res = await this.queryRect(); |
| | | // #ifndef APP-NVUE |
| | | this.height = res.height; |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | this.animation(res.height); |
| | | // #endif |
| | | this.$emit('popupChange', { show: true }); |
| | | }) |
| | | }, |
| | | close() { |
| | | if(!this.show) return; |
| | | this.height = 0; |
| | | // #ifndef APP-NVUE |
| | | this.height = 0; |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | this.animation(0); |
| | | // #endif |
| | | this.show = false; |
| | | uni.$emit(`${this.sign}_CLOSEPOPUP`); |
| | | this.$emit('popupChange', { show: false }); |
| | | }, |
| | | clickOverlay() { |
| | | if (this.clickOverlayOnClose) { |
| | | this.close(); |
| | | } |
| | | }, |
| | | // 查询内容高度 |
| | | queryRect() { |
| | | // #ifndef APP-NVUE |
| | | // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同 |
| | | return new Promise(resolve => { |
| | | this.$uvGetRect(`.uv-dp__container__list`).then(size => { |
| | | resolve(size) |
| | | }) |
| | | }) |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | // nvue下,使用dom模块查询元素高度 |
| | | // 返回一个promise,让调用此方法的主体能使用then回调 |
| | | return new Promise(resolve => { |
| | | dom.getComponentRect(this.$refs.uvDPList, res => { |
| | | resolve(res.size) |
| | | }) |
| | | }) |
| | | // #endif |
| | | }, |
| | | // nvue下设置高度 |
| | | animation(height, duration = 200) { |
| | | // #ifdef APP-NVUE |
| | | const ref = this.$refs['uvDPContainer']; |
| | | animation.transition(ref, { |
| | | styles: { |
| | | height: `${height}px` |
| | | }, |
| | | duration |
| | | }) |
| | | // #endif |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | .uv-dp__container { |
| | | /* #ifndef APP-NVUE */ |
| | | overflow: hidden; |
| | | transition: all .15s; |
| | | /* #endif */ |
| | | background-color: #fff; |
| | | } |
| | | .uv-dp__container__list { |
| | | &--item { |
| | | padding: 20rpx 60rpx; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <uv-sticky :disabled="!isSticky"> |
| | | <view ref="dropDownRef" class="uv-drop-down" :style="[$uv.addStyle(customStyle)]"> |
| | | <slot></slot> |
| | | </view> |
| | | </uv-sticky> |
| | | </template> |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'; |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'; |
| | | // #ifdef APP-NVUE |
| | | const dom = uni.requireNativePlugin('dom'); |
| | | // #endif |
| | | /** |
| | | * DropDown 下拉框 |
| | | * @description 下拉筛选 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-drop-down |
| | | * @property {String | Number} sign 组件唯一标识,需要手动传 |
| | | * @property {Boolean} is-sticky = [true|false] 是否吸顶 |
| | | * @property {Array} default-value 默认值,表示参数value属于这里面的值,就说明是未选中即是默认展示的值。eg:上面示例中的{label: '全部',value: 'all'} 即是默认值。后续处理逻辑也可以根据是否是其中值进行过滤。 |
| | | * @property {String} textSize 每项字体大小 |
| | | * @property {String} textColor 每项文本颜色 |
| | | * @property {String} textActiveSize 每项选中状态字体大小 |
| | | * @property {String} textActiveColor 每项选中状态文本颜色 |
| | | * @property {Object} extraIcon 每项右侧图标 |
| | | * @property {Object} extraActiveIcon 每项选中后右侧图标 |
| | | */ |
| | | export default { |
| | | name: 'uv-drop-down', |
| | | mixins: [mpMixin, mixin], |
| | | emits: ['click'], |
| | | props: { |
| | | isSticky: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | sign: { |
| | | type: [String, Number], |
| | | default: 'UVDROPDOWN' |
| | | }, |
| | | defaultValue: { |
| | | type: Array, |
| | | default: () => [0, '0', 'all'] |
| | | }, |
| | | textSize: { |
| | | type: String, |
| | | default: '30rpx' |
| | | }, |
| | | textColor: { |
| | | type: String, |
| | | default: '#333' |
| | | }, |
| | | textActiveSize: { |
| | | type: String, |
| | | default: '30rpx' |
| | | }, |
| | | textActiveColor: { |
| | | type: String, |
| | | default: '#3c9cff' |
| | | }, |
| | | extraIcon: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | name: 'arrow-down', |
| | | size: '30rpx', |
| | | color: '#333' |
| | | } |
| | | } |
| | | }, |
| | | extraActiveIcon: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | name: 'arrow-up', |
| | | size: '30rpx', |
| | | color: '#3c9cff' |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | parentData() { |
| | | return [this.defaultValue, this.textSize, this.textColor, this.textActiveColor, this.textActiveSize, this.extraIcon, this.extraActiveIcon, this.sign, this.clickHandler]; |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.init(); |
| | | }, |
| | | methods: { |
| | | init() { |
| | | uni.$emit(`${this.sign}_CLICKMENU`, { |
| | | show: false |
| | | }); |
| | | this.$nextTick(async () => { |
| | | const rect = await this.queryRect(); |
| | | uni.$emit(`${this.sign}_GETRECT`, rect); |
| | | }) |
| | | }, |
| | | // 查询内容高度 |
| | | queryRect() { |
| | | // #ifndef APP-NVUE |
| | | // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同 |
| | | return new Promise(resolve => { |
| | | this.$uvGetRect(`.uv-drop-down`).then(size => { |
| | | resolve(size) |
| | | }) |
| | | }) |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | // nvue下,使用dom模块查询元素高度 |
| | | // 返回一个promise,让调用此方法的主体能使用then回调 |
| | | return new Promise(resolve => { |
| | | dom.getComponentRect(this.$refs.dropDownRef, res => { |
| | | res.size.top = res.size.top <= 0 ? 0 : res.size.top; |
| | | resolve(res.size) |
| | | }) |
| | | }) |
| | | // #endif |
| | | }, |
| | | clickHandler(data) { |
| | | this.$emit('click', data); |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | .uv-drop-down { |
| | | @include flex; |
| | | justify-content: space-between; |
| | | background-color: #fff; |
| | | border-bottom: 1px solid #dadbde; |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-drop-down", |
| | | "displayName": "uv-drop-down 下拉筛选 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.5", |
| | | "description": "该组件主要提供筛选下拉筛选框,内置基础筛选功能,可以根据自己的需求自定义筛选项", |
| | | "keywords": [ |
| | | "uv-drop-down", |
| | | "uvui", |
| | | "uv-ui", |
| | | "下拉筛选", |
| | | "筛选" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-icon", |
| | | "uv-text", |
| | | "uv-transition", |
| | | "uv-sticky" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## DropDown 下拉筛选 |
| | | |
| | | > **组件名:uv-drop-down** |
| | | |
| | | 该组件主要提供筛选下拉筛选框,内置基础筛选功能,可以根据自己的需求自定义筛选项。 |
| | | |
| | | 为了兼容app-nvue,需要内置三个组件进行配合使用,uv-drop-down属于菜单项(其实还包括子组件uv-drop-down-item),uv-drop-down-popup属于筛选框。 |
| | | |
| | | 只需要做简单的约定式配置,即可使用该功能,兼容性良好,已经在多端进行了多次测试。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/dropDown.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.5(2023-12-20) |
| | | 1. 优化 |
| | | ## 1.0.4(2023-08-04) |
| | | 1. icon支持base64图片 |
| | | ## 1.0.3(2023-07-17) |
| | | 1. 修复 uv-empty 恢复设置mode属性的内置图标 |
| | | ## 1.0.2(2023-07-03) |
| | | 去除插槽判断,避免某些平台不显示的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-empty 内容为空 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 内置图标名称,或图片路径,建议绝对路径 |
| | | icon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 提示文字 |
| | | text: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 文字颜色 |
| | | textColor: { |
| | | type: String, |
| | | default: '#c0c4cc' |
| | | }, |
| | | // 文字大小 |
| | | textSize: { |
| | | type: [String, Number], |
| | | default: 14 |
| | | }, |
| | | // 图标的颜色 |
| | | iconColor: { |
| | | type: String, |
| | | default: '#c0c4cc' |
| | | }, |
| | | // 图标的大小 |
| | | iconSize: { |
| | | type: [String, Number], |
| | | default: 90 |
| | | }, |
| | | // 选择预置的图标类型 |
| | | mode: { |
| | | type: String, |
| | | default: 'data' |
| | | }, |
| | | // 图标宽度,单位px |
| | | width: { |
| | | type: [String, Number], |
| | | default: 160 |
| | | }, |
| | | // 图标高度,单位px |
| | | height: { |
| | | type: [String, Number], |
| | | default: 160 |
| | | }, |
| | | // 是否显示组件 |
| | | show: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 组件距离上一个元素之间的距离,默认px单位 |
| | | marginTop: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | ...uni.$uv?.props?.empty |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-empty" |
| | | :style="[emptyStyle]" |
| | | v-if="show" |
| | | > |
| | | <uv-icon |
| | | v-if="!isImg" |
| | | :name="mode === 'message' ? 'chat' : `empty-${mode}`" |
| | | :size="iconSize" |
| | | :color="iconColor" |
| | | margin-top="14" |
| | | ></uv-icon> |
| | | <image |
| | | v-else |
| | | :style="{ |
| | | width: $uv.addUnit(width), |
| | | height: $uv.addUnit(height) |
| | | }" |
| | | :src="icon" |
| | | mode="widthFix" |
| | | ></image> |
| | | <text |
| | | class="uv-empty__text" |
| | | :style="[textStyle]" |
| | | >{{text ? text : icons[mode]}}</text> |
| | | <view class="uv-empty__wrap"> |
| | | <slot /> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * empty 内容为空 |
| | | * @description 该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个"没有内容"的场景, 我们精心挑选了十几个场景的图标,方便您使用。 |
| | | * @tutorial https://www.uvui.cn/components/empty.html |
| | | * @property {String} icon 内置图标名称,或图片路径,建议绝对路径 |
| | | * @property {String} text 提示文字 |
| | | * @property {String} textColor 文字颜色 (默认 '#c0c4cc' ) |
| | | * @property {String | Number} textSize 文字大小 (默认 14 ) |
| | | * @property {String} iconColor 图标的颜色 (默认 '#c0c4cc' ) |
| | | * @property {String | Number} iconSize 图标的大小 (默认 90 ) |
| | | * @property {String} mode 选择预置的图标类型 (默认 'data' ) |
| | | * @property {String | Number} width 图标宽度,单位px (默认 160 ) |
| | | * @property {String | Number} height 图标高度,单位px (默认 160 ) |
| | | * @property {Boolean} show 是否显示组件 (默认 true ) |
| | | * @property {String | Number} marginTop 组件距离上一个元素之间的距离,默认px单位 (默认 0 ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * |
| | | * @event {Function} click 点击组件时触发 |
| | | * @event {Function} close 点击关闭按钮时触发 |
| | | * @example <uv-empty text="所谓伊人,在水一方" mode="list"></uv-empty> |
| | | */ |
| | | export default { |
| | | name: "uv-empty", |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | icons: { |
| | | car: '购物车为空', |
| | | page: '页面不存在', |
| | | search: '没有搜索结果', |
| | | address: '没有收货地址', |
| | | 'wifi-off': '没有WiFi', |
| | | order: '订单为空', |
| | | coupon: '没有优惠券', |
| | | favor: '暂无收藏', |
| | | permission: '无权限', |
| | | history: '无历史记录', |
| | | news: '无新闻列表', |
| | | message: '消息列表为空', |
| | | list: '列表为空', |
| | | data: '数据为空', |
| | | comment: '暂无评论', |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | // 组件样式 |
| | | emptyStyle() { |
| | | const style = {} |
| | | style.marginTop = this.$uv.addUnit(this.marginTop) |
| | | // 合并customStyle样式,此参数通过mixin中的props传递 |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)) |
| | | }, |
| | | // 文本样式 |
| | | textStyle() { |
| | | const style = {} |
| | | style.color = this.textColor |
| | | style.fontSize = this.$uv.addUnit(this.textSize) |
| | | return style |
| | | }, |
| | | // 判断icon是否图片路径 |
| | | isImg() { |
| | | const isBase64 = this.icon.indexOf('data:') > -1 && this.icon.indexOf('base64') > -1; |
| | | return this.icon.indexOf('/') !== -1 || isBase64; |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | $uv-empty-text-margin-top: 20rpx !default; |
| | | $uv-empty-slot-margin-top: 20rpx !default; |
| | | .uv-empty { |
| | | @include flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | &__text { |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-top: $uv-empty-text-margin-top; |
| | | } |
| | | } |
| | | .uv-slot-wrap { |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-top: $uv-empty-slot-margin-top; |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-empty", |
| | | "displayName": "uv-empty 内容为空 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.5", |
| | | "description": "该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个 没有内容 的场景, 我们精心挑选了十几个场景的图标,方便您使用。", |
| | | "keywords": [ |
| | | "empty", |
| | | "uvui", |
| | | "uv-ui", |
| | | "空数据", |
| | | "暂无数据" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-icon" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Empty 内容为空 |
| | | |
| | | > **组件名:uv-empty** |
| | | |
| | | 该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个"没有内容"的场景, 我们精心挑选了十几个场景的图标,方便您使用。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/empty.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.9(2023-08-14) |
| | | 1. 修复设置labelWidth属性时,节点渲染有闪动的BUG |
| | | ## 1.0.8(2023-08-13) |
| | | 1. 修复未设置rules的情况下报错的BUG |
| | | 2. 优化错误提示 |
| | | ## 1.0.7(2023-08-10) |
| | | 1. 修复在vue3+setup语法糖中错误文字动画错乱 |
| | | ## 1.0.6(2023-07-17) |
| | | 1. 优化文档 |
| | | 2. 优化其他 |
| | | ## 1.0.5(2023-07-03) |
| | | 去除插槽判断,避免某些平台不显示的BUG |
| | | ## 1.0.4(2023-07-02) |
| | | uv-form 由于弹出层uv-transition的修改,组件内部做了相应的修改,参数不变。 |
| | | ## 1.0.3(2023-06-18) |
| | | 1. 修改某些情况下的BUG |
| | | ## 1.0.2(2023-06-15) |
| | | 1. 修复支付宝报错的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-form 表单 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // input的label提示语 |
| | | label: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 绑定的值 |
| | | prop: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否显示表单域的下划线边框 |
| | | borderBottom: { |
| | | type: [Boolean], |
| | | default: false |
| | | }, |
| | | // label的位置,left-左边,top-上边 |
| | | labelPosition: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // label的宽度,单位px |
| | | labelWidth: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 右侧图标 |
| | | rightIcon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 左侧图标 |
| | | leftIcon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否显示左边的必填星号,只作显示用,具体校验必填的逻辑,请在rules中配置 |
| | | required: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | leftIconStyle: { |
| | | type: [String, Object], |
| | | default: '' |
| | | }, |
| | | ...uni.$uv?.props?.formItem |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-form-item"> |
| | | <view |
| | | class="uv-form-item__body" |
| | | @tap="clickHandler" |
| | | :style="[$uv.addStyle(customStyle), { |
| | | flexDirection: (labelPosition || parentData.labelPosition) === 'left' ? 'row' : 'column' |
| | | }]" |
| | | > |
| | | <!-- 微信小程序中,将一个参数设置空字符串,结果会变成字符串"true" --> |
| | | <slot name="label"> |
| | | <view |
| | | class="uv-form-item__body__left" |
| | | v-if="required || leftIcon || label" |
| | | :style="{ |
| | | width: $uv.addUnit(labelWidth || parentData.labelWidth), |
| | | marginBottom: parentData.labelPosition === 'left' ? 0 : '5px', |
| | | }" |
| | | > |
| | | <!-- 为了块对齐 --> |
| | | <view class="uv-form-item__body__left__content"> |
| | | <!-- nvue不支持伪元素before --> |
| | | <text |
| | | v-if="required" |
| | | class="uv-form-item__body__left__content__required" |
| | | >*</text> |
| | | <view |
| | | class="uv-form-item__body__left__content__icon" |
| | | v-if="leftIcon" |
| | | > |
| | | <uv-icon |
| | | :name="leftIcon" |
| | | :custom-style="leftIconStyle" |
| | | ></uv-icon> |
| | | </view> |
| | | <text |
| | | class="uv-form-item__body__left__content__label" |
| | | :style="[parentData.labelStyle, { |
| | | justifyContent: parentData.labelAlign === 'left' ? 'flex-start' : parentData.labelAlign === 'center' ? 'center' : 'flex-end' |
| | | }]" |
| | | >{{ label }}</text> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | <view class="uv-form-item__body__right"> |
| | | <view class="uv-form-item__body__right__content"> |
| | | <view class="uv-form-item__body__right__content__slot"> |
| | | <slot /> |
| | | </view> |
| | | <view class="item__body__right__content__icon"> |
| | | <slot name="right" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <slot name="error"> |
| | | <uv-transition |
| | | :show="true" |
| | | :duration="100" |
| | | mode="fade" |
| | | v-if="!!message && parentData.errorType === 'message'" |
| | | > |
| | | <text |
| | | class="uv-form-item__body__right__message" |
| | | :style="{ |
| | | marginLeft: $uv.addUnit(parentData.labelPosition === 'top' ? 0 : (labelWidth || parentData.labelWidth)) |
| | | }" |
| | | >{{ message }}</text> |
| | | </uv-transition> |
| | | </slot> |
| | | <uv-line |
| | | v-if="borderBottom" |
| | | :color="message && parentData.errorType === 'border-bottom' ? '#f56c6c' : '#d6d7d9'" |
| | | ></uv-line> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * Form 表单 |
| | | * @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。 |
| | | * @tutorial https://www.uvui.cn/components/form.html |
| | | * @property {String} label input的label提示语 |
| | | * @property {String} prop 绑定的值 |
| | | * @property {String | Boolean} borderBottom 是否显示表单域的下划线边框 |
| | | * @property {String | Number} labelWidth label的宽度,单位px |
| | | * @property {String} rightIcon 右侧图标 |
| | | * @property {String} leftIcon 左侧图标 |
| | | * @property {String | Object} leftIconStyle 左侧图标的样式 |
| | | * @property {Boolean} required 是否显示左边的必填星号,只作显示用,具体校验必填的逻辑,请在rules中配置 (默认 false ) |
| | | * |
| | | * @example <uv-form-item label="姓名" prop="userInfo.name" borderBottom ref="item1"></uv-form-item> |
| | | */ |
| | | export default { |
| | | name: 'uv-form-item', |
| | | emits: ['click'], |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | // 错误提示语 |
| | | message: '', |
| | | parentData: { |
| | | // 提示文本的位置 |
| | | labelPosition: 'left', |
| | | // 提示文本对齐方式 |
| | | labelAlign: 'left', |
| | | // 提示文本的样式 |
| | | labelStyle: {}, |
| | | // 提示文本的宽度 |
| | | labelWidth: 45, |
| | | // 错误提示方式 |
| | | errorType: 'message' |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 父组件的实例 |
| | | this.updateParentData() |
| | | if (!this.parent) { |
| | | this.$uv.error('uv-form-item需要结合uv-form组件使用') |
| | | } |
| | | }, |
| | | // 获取父组件的参数 |
| | | updateParentData() { |
| | | // 此方法写在mixin中 |
| | | this.getParentData('uv-form'); |
| | | }, |
| | | // 移除uv-form-item的校验结果 |
| | | clearValidate() { |
| | | this.message = null |
| | | }, |
| | | // 清空当前的组件的校验结果,并重置为初始值 |
| | | resetField() { |
| | | // 找到原始值 |
| | | const value = this.$uv.getProperty(this.parent.originalModel, this.prop) |
| | | // 将uv-form的model的prop属性链还原原始值 |
| | | this.$uv.setProperty(this.parent.model, this.prop, value) |
| | | // 移除校验结果 |
| | | this.message = null |
| | | }, |
| | | // 点击组件 |
| | | clickHandler() { |
| | | this.$emit('click') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | .uv-form-item { |
| | | @include flex(column); |
| | | font-size: 14px; |
| | | color: $uv-main-color; |
| | | &__body { |
| | | @include flex; |
| | | padding: 10px 0; |
| | | &__left { |
| | | @include flex; |
| | | align-items: center; |
| | | &__content { |
| | | position: relative; |
| | | @include flex; |
| | | align-items: center; |
| | | padding-right: 10rpx; |
| | | flex: 1; |
| | | &__icon { |
| | | margin-right: 8rpx; |
| | | } |
| | | &__required { |
| | | position: absolute; |
| | | left: -9px; |
| | | color: $uv-error; |
| | | line-height: 20px; |
| | | font-size: 20px; |
| | | top: 3px; |
| | | } |
| | | &__label { |
| | | @include flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | color: $uv-main-color; |
| | | font-size: 15px; |
| | | } |
| | | } |
| | | } |
| | | &__right { |
| | | flex: 1; |
| | | &__content { |
| | | @include flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | &__slot { |
| | | flex: 1; |
| | | /* #ifndef MP */ |
| | | @include flex; |
| | | align-items: center; |
| | | /* #endif */ |
| | | } |
| | | &__icon { |
| | | margin-left: 10rpx; |
| | | color: $uv-light-color; |
| | | font-size: 30rpx; |
| | | } |
| | | } |
| | | &__message__box { |
| | | height: 16px; |
| | | line-height: 16px; |
| | | } |
| | | &__message { |
| | | margin-top: -6px; |
| | | line-height: 24px; |
| | | font-size: 12px; |
| | | color: $uv-error; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 当前form的需要验证字段的集合 |
| | | model: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | // 验证规则 |
| | | rules: { |
| | | type: [Object, Function, Array], |
| | | default: () => ({}) |
| | | }, |
| | | // 有错误时的提示方式,message-提示信息,toast-进行toast提示 |
| | | // border-bottom-下边框呈现红色,none-无提示 |
| | | errorType: { |
| | | type: String, |
| | | default: 'message' |
| | | }, |
| | | // 是否显示表单域的下划线边框 |
| | | borderBottom: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // label的位置,left-左边,top-上边 |
| | | labelPosition: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | // label的宽度,单位px |
| | | labelWidth: { |
| | | type: [String, Number], |
| | | default: 45 |
| | | }, |
| | | // lable字体的对齐方式 |
| | | labelAlign: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | // lable的样式,对象形式 |
| | | labelStyle: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | ...uni.$uv?.props?.form |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-form"> |
| | | <slot /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from "./props.js"; |
| | | import Schema from "./valid.js"; |
| | | // 去除警告信息 |
| | | Schema.warning = function() {}; |
| | | /** |
| | | * Form 表单 |
| | | * @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。 |
| | | * @tutorial https://www.uvui.cn/components/form.html |
| | | * @property {Object} model 当前form的需要验证字段的集合 |
| | | * @property {Object | Function | Array} rules 验证规则 |
| | | * @property {String} errorType 错误的提示方式,见上方说明 ( 默认 message ) |
| | | * @property {Boolean} borderBottom 是否显示表单域的下划线边框 ( 默认 true ) |
| | | * @property {String} labelPosition 表单域提示文字的位置,left-左侧,top-上方 ( 默认 'left' ) |
| | | * @property {String | Number} labelWidth 提示文字的宽度,单位px ( 默认 45 ) |
| | | * @property {String} labelAlign lable字体的对齐方式 ( 默认 ‘left' ) |
| | | * @property {Object} labelStyle lable的样式,对象形式 |
| | | * @example <uv-form labelPosition="left" :model="model1" :rules="rules" ref="form1"></uv-form> |
| | | */ |
| | | export default { |
| | | name: "uv-form", |
| | | mixins: [mpMixin, mixin, props], |
| | | provide() { |
| | | return { |
| | | uForm: this, |
| | | }; |
| | | }, |
| | | data() { |
| | | return { |
| | | formRules: {}, |
| | | // 规则校验器 |
| | | validator: {}, |
| | | // 原始的model快照,用于resetFields方法重置表单时使用 |
| | | originalModel: null, |
| | | }; |
| | | }, |
| | | watch: { |
| | | // 监听规则的变化 |
| | | rules: { |
| | | immediate: true, |
| | | handler(n) { |
| | | this.setRules(n); |
| | | }, |
| | | }, |
| | | // 监听属性的变化,通知子组件uv-form-item重新获取信息 |
| | | propsChange(n) { |
| | | if (this.children?.length) { |
| | | this.children.map((child) => { |
| | | // 判断子组件(uv-form-item)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) |
| | | typeof child.updateParentData == "function" && |
| | | child.updateParentData(); |
| | | }); |
| | | } |
| | | }, |
| | | // 监听model的初始值作为重置表单的快照 |
| | | model: { |
| | | immediate: true, |
| | | handler(n) { |
| | | if (!this.originalModel) { |
| | | this.originalModel = this.$uv.deepClone(n); |
| | | } |
| | | }, |
| | | }, |
| | | }, |
| | | computed: { |
| | | propsChange() { |
| | | return [ |
| | | this.errorType, |
| | | this.borderBottom, |
| | | this.labelPosition, |
| | | this.labelWidth, |
| | | this.labelAlign, |
| | | this.labelStyle, |
| | | ]; |
| | | }, |
| | | }, |
| | | created() { |
| | | // 存储当前form下的所有uv-form-item的实例 |
| | | // 不能定义在data中,否则微信小程序会造成循环引用而报错 |
| | | this.children = []; |
| | | }, |
| | | methods: { |
| | | // 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则 |
| | | setRules(rules) { |
| | | // 判断是否有规则 |
| | | if (Object.keys(rules).length === 0) return; |
| | | if (process.env.NODE_ENV === 'development' && Object.keys(this.model).length === 0) { |
| | | this.$uv.error('设置rules,model必须设置!如果已经设置,请刷新页面。'); |
| | | return; |
| | | }; |
| | | this.formRules = rules; |
| | | // 重新将规则赋予Validator |
| | | this.validator = new Schema(rules); |
| | | }, |
| | | // 清空所有uv-form-item组件的内容,本质上是调用了uv-form-item组件中的resetField()方法 |
| | | resetFields() { |
| | | this.resetModel(); |
| | | }, |
| | | // 重置model为初始值的快照 |
| | | resetModel(obj) { |
| | | // 历遍所有uv-form-item,根据其prop属性,还原model的原始快照 |
| | | this.children.map((child) => { |
| | | const prop = child?.prop; |
| | | const value = this.$uv.getProperty(this.originalModel, prop); |
| | | this.$uv.setProperty(this.model, prop, value); |
| | | }); |
| | | }, |
| | | // 清空校验结果 |
| | | clearValidate(props) { |
| | | props = [].concat(props); |
| | | this.children.map((child) => { |
| | | // 如果uv-form-item的prop在props数组中,则清除对应的校验结果信息 |
| | | if (props[0] === undefined || props.includes(child.prop)) { |
| | | child.message = null; |
| | | } |
| | | }); |
| | | }, |
| | | // 对部分表单字段进行校验 |
| | | async validateField(value, callback, event = null) { |
| | | // $nextTick是必须的,否则model的变更,可能会延后于此方法的执行 |
| | | this.$nextTick(() => { |
| | | // 校验错误信息,返回给回调方法,用于存放所有form-item的错误信息 |
| | | const errorsRes = []; |
| | | // 如果为字符串,转为数组 |
| | | value = [].concat(value); |
| | | // 历遍children所有子form-item |
| | | this.children.map((child) => { |
| | | // 用于存放form-item的错误信息 |
| | | const childErrors = []; |
| | | if (value.includes(child.prop)) { |
| | | // 获取对应的属性,通过类似'a.b.c'的形式 |
| | | const propertyVal = this.$uv.getProperty( |
| | | this.model, |
| | | child.prop |
| | | ); |
| | | // 属性链数组 |
| | | const propertyChain = child.prop.split("."); |
| | | const propertyName = |
| | | propertyChain[propertyChain.length - 1]; |
| | | |
| | | const rule = this.formRules[child.prop]; |
| | | // 如果不存在对应的规则,直接返回,否则校验器会报错 |
| | | if (!rule) return; |
| | | // rule规则可为数组形式,也可为对象形式,此处拼接成为数组 |
| | | const rules = [].concat(rule); |
| | | |
| | | // 对rules数组进行校验 |
| | | for (let i = 0; i < rules.length; i++) { |
| | | const ruleItem = rules[i]; |
| | | // 将uv-form-item的触发器转为数组形式 |
| | | const trigger = [].concat(ruleItem?.trigger); |
| | | // 如果是有传入触发事件,但是此form-item却没有配置此触发器的话,不执行校验操作 |
| | | if (event && !trigger.includes(event)) continue; |
| | | // 实例化校验对象,传入构造规则 |
| | | const validator = new Schema({ |
| | | [propertyName]: ruleItem, |
| | | }); |
| | | validator.validate({ |
| | | [propertyName]: propertyVal, |
| | | }, |
| | | (errors, fields) => { |
| | | if (this.$uv.test.array(errors)) { |
| | | errorsRes.push(...errors); |
| | | childErrors.push(...errors); |
| | | } |
| | | this.$nextTick(() => { |
| | | child.message = childErrors[0]?.message ? childErrors[0]?.message : null; |
| | | }) |
| | | } |
| | | ); |
| | | } |
| | | } |
| | | }); |
| | | // 执行回调函数 |
| | | typeof callback === "function" && callback(errorsRes); |
| | | }); |
| | | }, |
| | | // 校验全部数据 |
| | | validate(callback) { |
| | | return new Promise((resolve, reject) => { |
| | | // $nextTick是必须的,否则model的变更,可能会延后于validate方法 |
| | | this.$nextTick(() => { |
| | | // 获取所有form-item的prop,交给validateField方法进行校验 |
| | | const formItemProps = this.children.map( |
| | | (item) => item.prop |
| | | ); |
| | | this.validateField(formItemProps, (errors) => { |
| | | if (errors.length) { |
| | | // 如果错误提示方式为toast,则进行提示 |
| | | this.errorType === 'toast' && this.$uv.toast(errors[0].message) |
| | | reject(errors) |
| | | } else { |
| | | resolve(true) |
| | | } |
| | | }); |
| | | }); |
| | | }); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| New file |
| | |
| | | function _extends() { |
| | | _extends = Object.assign || function (target) { |
| | | for (let i = 1; i < arguments.length; i++) { |
| | | const source = arguments[i] |
| | | |
| | | for (const key in source) { |
| | | if (Object.prototype.hasOwnProperty.call(source, key)) { |
| | | target[key] = source[key] |
| | | } |
| | | } |
| | | } |
| | | |
| | | return target |
| | | } |
| | | |
| | | return _extends.apply(this, arguments) |
| | | } |
| | | |
| | | /* eslint no-console:0 */ |
| | | const formatRegExp = /%[sdj%]/g |
| | | let warning = function warning() {} // don't print warning message when in production env or node runtime |
| | | |
| | | if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production' && typeof window |
| | | !== 'undefined' && typeof document !== 'undefined') { |
| | | warning = function warning(type, errors) { |
| | | if (typeof console !== 'undefined' && console.warn) { |
| | | if (errors.every((e) => typeof e === 'string')) { |
| | | console.warn(type, errors) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | function convertFieldsError(errors) { |
| | | if (!errors || !errors.length) return null |
| | | const fields = {} |
| | | errors.forEach((error) => { |
| | | const { field } = error |
| | | fields[field] = fields[field] || [] |
| | | fields[field].push(error) |
| | | }) |
| | | return fields |
| | | } |
| | | |
| | | function format() { |
| | | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { |
| | | args[_key] = arguments[_key] |
| | | } |
| | | |
| | | let i = 1 |
| | | const f = args[0] |
| | | const len = args.length |
| | | |
| | | if (typeof f === 'function') { |
| | | return f.apply(null, args.slice(1)) |
| | | } |
| | | |
| | | if (typeof f === 'string') { |
| | | let str = String(f).replace(formatRegExp, (x) => { |
| | | if (x === '%%') { |
| | | return '%' |
| | | } |
| | | |
| | | if (i >= len) { |
| | | return x |
| | | } |
| | | |
| | | switch (x) { |
| | | case '%s': |
| | | return String(args[i++]) |
| | | |
| | | case '%d': |
| | | return Number(args[i++]) |
| | | |
| | | case '%j': |
| | | try { |
| | | return JSON.stringify(args[i++]) |
| | | } catch (_) { |
| | | return '[Circular]' |
| | | } |
| | | |
| | | break |
| | | |
| | | default: |
| | | return x |
| | | } |
| | | }) |
| | | |
| | | for (let arg = args[i]; i < len; arg = args[++i]) { |
| | | str += ` ${arg}` |
| | | } |
| | | |
| | | return str |
| | | } |
| | | |
| | | return f |
| | | } |
| | | |
| | | function isNativeStringType(type) { |
| | | return type === 'string' || type === 'url' || type === 'hex' || type === 'email' || type === 'pattern' |
| | | } |
| | | |
| | | function isEmptyValue(value, type) { |
| | | if (value === undefined || value === null) { |
| | | return true |
| | | } |
| | | |
| | | if (type === 'array' && Array.isArray(value) && !value.length) { |
| | | return true |
| | | } |
| | | |
| | | if (isNativeStringType(type) && typeof value === 'string' && !value) { |
| | | return true |
| | | } |
| | | |
| | | return false |
| | | } |
| | | |
| | | function asyncParallelArray(arr, func, callback) { |
| | | const results = [] |
| | | let total = 0 |
| | | const arrLength = arr.length |
| | | |
| | | function count(errors) { |
| | | results.push.apply(results, errors) |
| | | total++ |
| | | |
| | | if (total === arrLength) { |
| | | callback(results) |
| | | } |
| | | } |
| | | |
| | | arr.forEach((a) => { |
| | | func(a, count) |
| | | }) |
| | | } |
| | | |
| | | function asyncSerialArray(arr, func, callback) { |
| | | let index = 0 |
| | | const arrLength = arr.length |
| | | |
| | | function next(errors) { |
| | | if (errors && errors.length) { |
| | | callback(errors) |
| | | return |
| | | } |
| | | |
| | | const original = index |
| | | index += 1 |
| | | |
| | | if (original < arrLength) { |
| | | func(arr[original], next) |
| | | } else { |
| | | callback([]) |
| | | } |
| | | } |
| | | |
| | | next([]) |
| | | } |
| | | |
| | | function flattenObjArr(objArr) { |
| | | const ret = [] |
| | | Object.keys(objArr).forEach((k) => { |
| | | ret.push.apply(ret, objArr[k]) |
| | | }) |
| | | return ret |
| | | } |
| | | |
| | | function asyncMap(objArr, option, func, callback) { |
| | | if (option.first) { |
| | | const _pending = new Promise((resolve, reject) => { |
| | | const next = function next(errors) { |
| | | callback(errors) |
| | | return errors.length ? reject({ |
| | | errors, |
| | | fields: convertFieldsError(errors) |
| | | }) : resolve() |
| | | } |
| | | |
| | | const flattenArr = flattenObjArr(objArr) |
| | | asyncSerialArray(flattenArr, func, next) |
| | | }) |
| | | |
| | | _pending.catch((e) => e) |
| | | |
| | | return _pending |
| | | } |
| | | |
| | | let firstFields = option.firstFields || [] |
| | | |
| | | if (firstFields === true) { |
| | | firstFields = Object.keys(objArr) |
| | | } |
| | | |
| | | const objArrKeys = Object.keys(objArr) |
| | | const objArrLength = objArrKeys.length |
| | | let total = 0 |
| | | const results = [] |
| | | const pending = new Promise((resolve, reject) => { |
| | | const next = function next(errors) { |
| | | results.push.apply(results, errors) |
| | | total++ |
| | | |
| | | if (total === objArrLength) { |
| | | callback(results) |
| | | return results.length ? reject({ |
| | | errors: results, |
| | | fields: convertFieldsError(results) |
| | | }) : resolve() |
| | | } |
| | | } |
| | | |
| | | if (!objArrKeys.length) { |
| | | callback(results) |
| | | resolve() |
| | | } |
| | | |
| | | objArrKeys.forEach((key) => { |
| | | const arr = objArr[key] |
| | | |
| | | if (firstFields.indexOf(key) !== -1) { |
| | | asyncSerialArray(arr, func, next) |
| | | } else { |
| | | asyncParallelArray(arr, func, next) |
| | | } |
| | | }) |
| | | }) |
| | | pending.catch((e) => e) |
| | | return pending |
| | | } |
| | | |
| | | function complementError(rule) { |
| | | return function (oe) { |
| | | if (oe && oe.message) { |
| | | oe.field = oe.field || rule.fullField |
| | | return oe |
| | | } |
| | | |
| | | return { |
| | | message: typeof oe === 'function' ? oe() : oe, |
| | | field: oe.field || rule.fullField |
| | | } |
| | | } |
| | | } |
| | | |
| | | function deepMerge(target, source) { |
| | | if (source) { |
| | | for (const s in source) { |
| | | if (source.hasOwnProperty(s)) { |
| | | const value = source[s] |
| | | |
| | | if (typeof value === 'object' && typeof target[s] === 'object') { |
| | | target[s] = { ...target[s], ...value } |
| | | } else { |
| | | target[s] = value |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return target |
| | | } |
| | | |
| | | /** |
| | | * Rule for validating required fields. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param source The source object being validated. |
| | | * @param errors An array of errors that this rule may add |
| | | * validation errors to. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function required(rule, value, source, errors, options, type) { |
| | | if (rule.required && (!source.hasOwnProperty(rule.field) || isEmptyValue(value, type || rule.type))) { |
| | | errors.push(format(options.messages.required, rule.fullField)) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Rule for validating whitespace. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param source The source object being validated. |
| | | * @param errors An array of errors that this rule may add |
| | | * validation errors to. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function whitespace(rule, value, source, errors, options) { |
| | | if (/^\s+$/.test(value) || value === '') { |
| | | errors.push(format(options.messages.whitespace, rule.fullField)) |
| | | } |
| | | } |
| | | |
| | | /* eslint max-len:0 */ |
| | | |
| | | const pattern = { |
| | | // http://emailregex.com/ |
| | | email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, |
| | | url: new RegExp( |
| | | '^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', |
| | | 'i' |
| | | ), |
| | | hex: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i |
| | | } |
| | | var types = { |
| | | integer: function integer(value) { |
| | | return /^(-)?\d+$/.test(value); |
| | | }, |
| | | float: function float(value) { |
| | | return /^(-)?\d+(\.\d+)?$/.test(value); |
| | | }, |
| | | array: function array(value) { |
| | | return Array.isArray(value) |
| | | }, |
| | | regexp: function regexp(value) { |
| | | if (value instanceof RegExp) { |
| | | return true |
| | | } |
| | | |
| | | try { |
| | | return !!new RegExp(value) |
| | | } catch (e) { |
| | | return false |
| | | } |
| | | }, |
| | | date: function date(value) { |
| | | return typeof value.getTime === 'function' && typeof value.getMonth === 'function' && typeof value.getYear |
| | | === 'function' |
| | | }, |
| | | number: function number(value) { |
| | | if (isNaN(value)) { |
| | | return false |
| | | } |
| | | |
| | | // 修改源码,将字符串数值先转为数值 |
| | | return typeof +value === 'number' |
| | | }, |
| | | object: function object(value) { |
| | | return typeof value === 'object' && !types.array(value) |
| | | }, |
| | | method: function method(value) { |
| | | return typeof value === 'function' |
| | | }, |
| | | email: function email(value) { |
| | | return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255 |
| | | }, |
| | | url: function url(value) { |
| | | return typeof value === 'string' && !!value.match(pattern.url) |
| | | }, |
| | | hex: function hex(value) { |
| | | return typeof value === 'string' && !!value.match(pattern.hex) |
| | | } |
| | | } |
| | | /** |
| | | * Rule for validating the type of a value. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param source The source object being validated. |
| | | * @param errors An array of errors that this rule may add |
| | | * validation errors to. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function type(rule, value, source, errors, options) { |
| | | if (rule.required && value === undefined) { |
| | | required(rule, value, source, errors, options) |
| | | return |
| | | } |
| | | |
| | | const custom = ['integer', 'float', 'array', 'regexp', 'object', 'method', 'email', 'number', 'date', 'url', 'hex'] |
| | | const ruleType = rule.type |
| | | |
| | | if (custom.indexOf(ruleType) > -1) { |
| | | if (!types[ruleType](value)) { |
| | | errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type)) |
| | | } // straight typeof check |
| | | } else if (ruleType && typeof value !== rule.type) { |
| | | errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type)) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Rule for validating minimum and maximum allowed values. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param source The source object being validated. |
| | | * @param errors An array of errors that this rule may add |
| | | * validation errors to. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function range(rule, value, source, errors, options) { |
| | | const len = typeof rule.len === 'number' |
| | | const min = typeof rule.min === 'number' |
| | | const max = typeof rule.max === 'number' // 正则匹配码点范围从U+010000一直到U+10FFFF的文字(补充平面Supplementary Plane) |
| | | |
| | | const spRegexp = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g |
| | | let val = value |
| | | let key = null |
| | | const num = typeof value === 'number' |
| | | const str = typeof value === 'string' |
| | | const arr = Array.isArray(value) |
| | | |
| | | if (num) { |
| | | key = 'number' |
| | | } else if (str) { |
| | | key = 'string' |
| | | } else if (arr) { |
| | | key = 'array' |
| | | } // if the value is not of a supported type for range validation |
| | | // the validation rule rule should use the |
| | | // type property to also test for a particular type |
| | | |
| | | if (!key) { |
| | | return false |
| | | } |
| | | |
| | | if (arr) { |
| | | val = value.length |
| | | } |
| | | |
| | | if (str) { |
| | | // 处理码点大于U+010000的文字length属性不准确的bug,如"𠮷𠮷𠮷".lenght !== 3 |
| | | val = value.replace(spRegexp, '_').length |
| | | } |
| | | |
| | | if (len) { |
| | | if (val !== rule.len) { |
| | | errors.push(format(options.messages[key].len, rule.fullField, rule.len)) |
| | | } |
| | | } else if (min && !max && val < rule.min) { |
| | | errors.push(format(options.messages[key].min, rule.fullField, rule.min)) |
| | | } else if (max && !min && val > rule.max) { |
| | | errors.push(format(options.messages[key].max, rule.fullField, rule.max)) |
| | | } else if (min && max && (val < rule.min || val > rule.max)) { |
| | | errors.push(format(options.messages[key].range, rule.fullField, rule.min, rule.max)) |
| | | } |
| | | } |
| | | |
| | | const ENUM = 'enum' |
| | | /** |
| | | * Rule for validating a value exists in an enumerable list. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param source The source object being validated. |
| | | * @param errors An array of errors that this rule may add |
| | | * validation errors to. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function enumerable(rule, value, source, errors, options) { |
| | | rule[ENUM] = Array.isArray(rule[ENUM]) ? rule[ENUM] : [] |
| | | |
| | | if (rule[ENUM].indexOf(value) === -1) { |
| | | errors.push(format(options.messages[ENUM], rule.fullField, rule[ENUM].join(', '))) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Rule for validating a regular expression pattern. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param source The source object being validated. |
| | | * @param errors An array of errors that this rule may add |
| | | * validation errors to. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function pattern$1(rule, value, source, errors, options) { |
| | | if (rule.pattern) { |
| | | if (rule.pattern instanceof RegExp) { |
| | | // if a RegExp instance is passed, reset `lastIndex` in case its `global` |
| | | // flag is accidentally set to `true`, which in a validation scenario |
| | | // is not necessary and the result might be misleading |
| | | rule.pattern.lastIndex = 0 |
| | | |
| | | if (!rule.pattern.test(value)) { |
| | | errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern)) |
| | | } |
| | | } else if (typeof rule.pattern === 'string') { |
| | | const _pattern = new RegExp(rule.pattern) |
| | | |
| | | if (!_pattern.test(value)) { |
| | | errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern)) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | const rules = { |
| | | required, |
| | | whitespace, |
| | | type, |
| | | range, |
| | | enum: enumerable, |
| | | pattern: pattern$1 |
| | | } |
| | | |
| | | /** |
| | | * Performs validation for string types. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function string(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value, 'string') && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options, 'string') |
| | | |
| | | if (!isEmptyValue(value, 'string')) { |
| | | rules.type(rule, value, source, errors, options) |
| | | rules.range(rule, value, source, errors, options) |
| | | rules.pattern(rule, value, source, errors, options) |
| | | |
| | | if (rule.whitespace === true) { |
| | | rules.whitespace(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Validates a function. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function method(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (value !== undefined) { |
| | | rules.type(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Validates a number. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function number(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (value === '') { |
| | | value = undefined |
| | | } |
| | | |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (value !== undefined) { |
| | | rules.type(rule, value, source, errors, options) |
| | | rules.range(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Validates a boolean. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function _boolean(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (value !== undefined) { |
| | | rules.type(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Validates the regular expression type. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function regexp(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (!isEmptyValue(value)) { |
| | | rules.type(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Validates a number is an integer. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function integer(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (value !== undefined) { |
| | | rules.type(rule, value, source, errors, options) |
| | | rules.range(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Validates a number is a floating point number. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function floatFn(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (value !== undefined) { |
| | | rules.type(rule, value, source, errors, options) |
| | | rules.range(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Validates an array. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function array(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value, 'array') && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options, 'array') |
| | | |
| | | if (!isEmptyValue(value, 'array')) { |
| | | rules.type(rule, value, source, errors, options) |
| | | rules.range(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Validates an object. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function object(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (value !== undefined) { |
| | | rules.type(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | const ENUM$1 = 'enum' |
| | | /** |
| | | * Validates an enumerable list. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function enumerable$1(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (value !== undefined) { |
| | | rules[ENUM$1](rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Validates a regular expression pattern. |
| | | * |
| | | * Performs validation when a rule only contains |
| | | * a pattern property but is not declared as a string type. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function pattern$2(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value, 'string') && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (!isEmptyValue(value, 'string')) { |
| | | rules.pattern(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | function date(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | |
| | | if (!isEmptyValue(value)) { |
| | | let dateObject |
| | | |
| | | if (typeof value === 'number') { |
| | | dateObject = new Date(value) |
| | | } else { |
| | | dateObject = value |
| | | } |
| | | |
| | | rules.type(rule, dateObject, source, errors, options) |
| | | |
| | | if (dateObject) { |
| | | rules.range(rule, dateObject.getTime(), source, errors, options) |
| | | } |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | function required$1(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const type = Array.isArray(value) ? 'array' : typeof value |
| | | rules.required(rule, value, source, errors, options, type) |
| | | callback(errors) |
| | | } |
| | | |
| | | function type$1(rule, value, callback, source, options) { |
| | | const ruleType = rule.type |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value, ruleType) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options, ruleType) |
| | | |
| | | if (!isEmptyValue(value, ruleType)) { |
| | | rules.type(rule, value, source, errors, options) |
| | | } |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | /** |
| | | * Performs validation for any type. |
| | | * |
| | | * @param rule The validation rule. |
| | | * @param value The value of the field on the source object. |
| | | * @param callback The callback function. |
| | | * @param source The source object being validated. |
| | | * @param options The validation options. |
| | | * @param options.messages The validation messages. |
| | | */ |
| | | |
| | | function any(rule, value, callback, source, options) { |
| | | const errors = [] |
| | | const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field) |
| | | |
| | | if (validate) { |
| | | if (isEmptyValue(value) && !rule.required) { |
| | | return callback() |
| | | } |
| | | |
| | | rules.required(rule, value, source, errors, options) |
| | | } |
| | | |
| | | callback(errors) |
| | | } |
| | | |
| | | const validators = { |
| | | string, |
| | | method, |
| | | number, |
| | | boolean: _boolean, |
| | | regexp, |
| | | integer, |
| | | float: floatFn, |
| | | array, |
| | | object, |
| | | enum: enumerable$1, |
| | | pattern: pattern$2, |
| | | date, |
| | | url: type$1, |
| | | hex: type$1, |
| | | email: type$1, |
| | | required: required$1, |
| | | any |
| | | } |
| | | |
| | | function newMessages() { |
| | | return { |
| | | default: 'Validation error on field %s', |
| | | required: '%s is required', |
| | | enum: '%s must be one of %s', |
| | | whitespace: '%s cannot be empty', |
| | | date: { |
| | | format: '%s date %s is invalid for format %s', |
| | | parse: '%s date could not be parsed, %s is invalid ', |
| | | invalid: '%s date %s is invalid' |
| | | }, |
| | | types: { |
| | | string: '%s is not a %s', |
| | | method: '%s is not a %s (function)', |
| | | array: '%s is not an %s', |
| | | object: '%s is not an %s', |
| | | number: '%s is not a %s', |
| | | date: '%s is not a %s', |
| | | boolean: '%s is not a %s', |
| | | integer: '%s is not an %s', |
| | | float: '%s is not a %s', |
| | | regexp: '%s is not a valid %s', |
| | | email: '%s is not a valid %s', |
| | | url: '%s is not a valid %s', |
| | | hex: '%s is not a valid %s' |
| | | }, |
| | | string: { |
| | | len: '%s must be exactly %s characters', |
| | | min: '%s must be at least %s characters', |
| | | max: '%s cannot be longer than %s characters', |
| | | range: '%s must be between %s and %s characters' |
| | | }, |
| | | number: { |
| | | len: '%s must equal %s', |
| | | min: '%s cannot be less than %s', |
| | | max: '%s cannot be greater than %s', |
| | | range: '%s must be between %s and %s' |
| | | }, |
| | | array: { |
| | | len: '%s must be exactly %s in length', |
| | | min: '%s cannot be less than %s in length', |
| | | max: '%s cannot be greater than %s in length', |
| | | range: '%s must be between %s and %s in length' |
| | | }, |
| | | pattern: { |
| | | mismatch: '%s value %s does not match pattern %s' |
| | | }, |
| | | clone: function clone() { |
| | | const cloned = JSON.parse(JSON.stringify(this)) |
| | | cloned.clone = this.clone |
| | | return cloned |
| | | } |
| | | } |
| | | } |
| | | const messages = newMessages() |
| | | |
| | | /** |
| | | * Encapsulates a validation schema. |
| | | * |
| | | * @param descriptor An object declaring validation rules |
| | | * for this schema. |
| | | */ |
| | | |
| | | function Schema(descriptor) { |
| | | this.rules = null |
| | | this._messages = messages |
| | | this.define(descriptor) |
| | | } |
| | | |
| | | Schema.prototype = { |
| | | messages: function messages(_messages) { |
| | | if (_messages) { |
| | | this._messages = deepMerge(newMessages(), _messages) |
| | | } |
| | | |
| | | return this._messages |
| | | }, |
| | | define: function define(rules) { |
| | | if (!rules) { |
| | | throw new Error('Cannot configure a schema with no rules') |
| | | } |
| | | |
| | | if (typeof rules !== 'object' || Array.isArray(rules)) { |
| | | throw new Error('Rules must be an object') |
| | | } |
| | | |
| | | this.rules = {} |
| | | let z |
| | | let item |
| | | |
| | | for (z in rules) { |
| | | if (rules.hasOwnProperty(z)) { |
| | | item = rules[z] |
| | | this.rules[z] = Array.isArray(item) ? item : [item] |
| | | } |
| | | } |
| | | }, |
| | | validate: function validate(source_, o, oc) { |
| | | const _this = this |
| | | |
| | | if (o === void 0) { |
| | | o = {} |
| | | } |
| | | |
| | | if (oc === void 0) { |
| | | oc = function oc() {} |
| | | } |
| | | |
| | | let source = source_ |
| | | let options = o |
| | | let callback = oc |
| | | |
| | | if (typeof options === 'function') { |
| | | callback = options |
| | | options = {} |
| | | } |
| | | |
| | | if (!this.rules || Object.keys(this.rules).length === 0) { |
| | | if (callback) { |
| | | callback() |
| | | } |
| | | |
| | | return Promise.resolve() |
| | | } |
| | | |
| | | function complete(results) { |
| | | let i |
| | | let errors = [] |
| | | let fields = {} |
| | | |
| | | function add(e) { |
| | | if (Array.isArray(e)) { |
| | | let _errors |
| | | |
| | | errors = (_errors = errors).concat.apply(_errors, e) |
| | | } else { |
| | | errors.push(e) |
| | | } |
| | | } |
| | | |
| | | for (i = 0; i < results.length; i++) { |
| | | add(results[i]) |
| | | } |
| | | |
| | | if (!errors.length) { |
| | | errors = null |
| | | fields = null |
| | | } else { |
| | | fields = convertFieldsError(errors) |
| | | } |
| | | |
| | | callback(errors, fields) |
| | | } |
| | | |
| | | if (options.messages) { |
| | | let messages$1 = this.messages() |
| | | |
| | | if (messages$1 === messages) { |
| | | messages$1 = newMessages() |
| | | } |
| | | |
| | | deepMerge(messages$1, options.messages) |
| | | options.messages = messages$1 |
| | | } else { |
| | | options.messages = this.messages() |
| | | } |
| | | |
| | | let arr |
| | | let value |
| | | const series = {} |
| | | const keys = options.keys || Object.keys(this.rules) |
| | | keys.forEach((z) => { |
| | | arr = _this.rules[z] |
| | | value = source[z] |
| | | arr.forEach((r) => { |
| | | let rule = r |
| | | |
| | | if (typeof rule.transform === 'function') { |
| | | if (source === source_) { |
| | | source = { ...source } |
| | | } |
| | | |
| | | value = source[z] = rule.transform(value) |
| | | } |
| | | |
| | | if (typeof rule === 'function') { |
| | | rule = { |
| | | validator: rule |
| | | } |
| | | } else { |
| | | rule = { ...rule } |
| | | } |
| | | |
| | | rule.validator = _this.getValidationMethod(rule) |
| | | rule.field = z |
| | | rule.fullField = rule.fullField || z |
| | | rule.type = _this.getType(rule) |
| | | |
| | | if (!rule.validator) { |
| | | return |
| | | } |
| | | |
| | | series[z] = series[z] || [] |
| | | series[z].push({ |
| | | rule, |
| | | value, |
| | | source, |
| | | field: z |
| | | }) |
| | | }) |
| | | }) |
| | | const errorFields = {} |
| | | return asyncMap(series, options, (data, doIt) => { |
| | | const { rule } = data |
| | | let deep = (rule.type === 'object' || rule.type === 'array') && (typeof rule.fields === 'object' || typeof rule.defaultField |
| | | === 'object') |
| | | deep = deep && (rule.required || !rule.required && data.value) |
| | | rule.field = data.field |
| | | |
| | | function addFullfield(key, schema) { |
| | | return { ...schema, fullField: `${rule.fullField}.${key}` } |
| | | } |
| | | |
| | | function cb(e) { |
| | | if (e === void 0) { |
| | | e = [] |
| | | } |
| | | |
| | | let errors = e |
| | | |
| | | if (!Array.isArray(errors)) { |
| | | errors = [errors] |
| | | } |
| | | |
| | | if (!options.suppressWarning && errors.length) { |
| | | Schema.warning('async-validator:', errors) |
| | | } |
| | | |
| | | if (errors.length && rule.message) { |
| | | errors = [].concat(rule.message) |
| | | } |
| | | |
| | | errors = errors.map(complementError(rule)) |
| | | |
| | | if (options.first && errors.length) { |
| | | errorFields[rule.field] = 1 |
| | | return doIt(errors) |
| | | } |
| | | |
| | | if (!deep) { |
| | | doIt(errors) |
| | | } else { |
| | | // if rule is required but the target object |
| | | // does not exist fail at the rule level and don't |
| | | // go deeper |
| | | if (rule.required && !data.value) { |
| | | if (rule.message) { |
| | | errors = [].concat(rule.message).map(complementError(rule)) |
| | | } else if (options.error) { |
| | | errors = [options.error(rule, format(options.messages.required, rule.field))] |
| | | } else { |
| | | errors = [] |
| | | } |
| | | |
| | | return doIt(errors) |
| | | } |
| | | |
| | | let fieldsSchema = {} |
| | | |
| | | if (rule.defaultField) { |
| | | for (const k in data.value) { |
| | | if (data.value.hasOwnProperty(k)) { |
| | | fieldsSchema[k] = rule.defaultField |
| | | } |
| | | } |
| | | } |
| | | |
| | | fieldsSchema = { ...fieldsSchema, ...data.rule.fields } |
| | | |
| | | for (const f in fieldsSchema) { |
| | | if (fieldsSchema.hasOwnProperty(f)) { |
| | | const fieldSchema = Array.isArray(fieldsSchema[f]) ? fieldsSchema[f] : [fieldsSchema[f]] |
| | | fieldsSchema[f] = fieldSchema.map(addFullfield.bind(null, f)) |
| | | } |
| | | } |
| | | |
| | | const schema = new Schema(fieldsSchema) |
| | | schema.messages(options.messages) |
| | | |
| | | if (data.rule.options) { |
| | | data.rule.options.messages = options.messages |
| | | data.rule.options.error = options.error |
| | | } |
| | | |
| | | schema.validate(data.value, data.rule.options || options, (errs) => { |
| | | const finalErrors = [] |
| | | |
| | | if (errors && errors.length) { |
| | | finalErrors.push.apply(finalErrors, errors) |
| | | } |
| | | |
| | | if (errs && errs.length) { |
| | | finalErrors.push.apply(finalErrors, errs) |
| | | } |
| | | |
| | | doIt(finalErrors.length ? finalErrors : null) |
| | | }) |
| | | } |
| | | } |
| | | |
| | | let res |
| | | |
| | | if (rule.asyncValidator) { |
| | | res = rule.asyncValidator(rule, data.value, cb, data.source, options) |
| | | } else if (rule.validator) { |
| | | res = rule.validator(rule, data.value, cb, data.source, options) |
| | | |
| | | if (res === true) { |
| | | cb() |
| | | } else if (res === false) { |
| | | cb(rule.message || `${rule.field} fails`) |
| | | } else if (res instanceof Array) { |
| | | cb(res) |
| | | } else if (res instanceof Error) { |
| | | cb(res.message) |
| | | } |
| | | } |
| | | |
| | | if (res && res.then) { |
| | | res.then(() => cb(), (e) => cb(e)) |
| | | } |
| | | }, (results) => { |
| | | complete(results) |
| | | }) |
| | | }, |
| | | getType: function getType(rule) { |
| | | if (rule.type === undefined && rule.pattern instanceof RegExp) { |
| | | rule.type = 'pattern' |
| | | } |
| | | |
| | | if (typeof rule.validator !== 'function' && rule.type && !validators.hasOwnProperty(rule.type)) { |
| | | throw new Error(format('Unknown rule type %s', rule.type)) |
| | | } |
| | | |
| | | return rule.type || 'string' |
| | | }, |
| | | getValidationMethod: function getValidationMethod(rule) { |
| | | if (typeof rule.validator === 'function') { |
| | | return rule.validator |
| | | } |
| | | |
| | | const keys = Object.keys(rule) |
| | | const messageIndex = keys.indexOf('message') |
| | | |
| | | if (messageIndex !== -1) { |
| | | keys.splice(messageIndex, 1) |
| | | } |
| | | |
| | | if (keys.length === 1 && keys[0] === 'required') { |
| | | return validators.required |
| | | } |
| | | |
| | | return validators[this.getType(rule)] || false |
| | | } |
| | | } |
| | | |
| | | Schema.register = function register(type, validator) { |
| | | if (typeof validator !== 'function') { |
| | | throw new Error('Cannot register a validator by type, validator is not a function') |
| | | } |
| | | |
| | | validators[type] = validator |
| | | } |
| | | |
| | | Schema.warning = warning |
| | | Schema.messages = messages |
| | | |
| | | export default Schema |
| | | // # sourceMappingURL=index.js.map |
| New file |
| | |
| | | { |
| | | "id": "uv-form", |
| | | "displayName": "uv-form 表单 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.9", |
| | | "description": "此组件一般用于表单场景,可以配置Input输入框,Textarea文本域,Checkbox复选框,Radio单选框,开关选择器等,进行表单验证等。", |
| | | "keywords": [ |
| | | "form", |
| | | "uvui", |
| | | "uv-ui", |
| | | "表单", |
| | | "表单验证" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-icon", |
| | | "uv-line", |
| | | "uv-transition", |
| | | "uv-action-sheet", |
| | | "uv-input", |
| | | "uv-button" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Form 表单 |
| | | |
| | | > **组件名:uv-form** |
| | | |
| | | 此组件一般用于表单场景,可以配置`Input`输入框,`Textarea`文本域,`Checkbox`复选框,`Radio`单选框,开关选择器等,进行表单验证等。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/form.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small style="font-size:14px;font-weight:700;">(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | 1. 新增间隔槽组件 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 背景颜色(默认transparent) |
| | | bgColor: { |
| | | type: String, |
| | | default: 'transparent' |
| | | }, |
| | | // 分割槽高度,单位px(默认20) |
| | | height: { |
| | | type: [String, Number], |
| | | default: 20 |
| | | }, |
| | | // 与上一个组件的距离 |
| | | marginTop: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 与下一个组件的距离 |
| | | marginBottom: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | ...uni.$uv?.props?.gap |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-gap" :style="[gapStyle]"></view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * gap 间隔槽 |
| | | * @description 该组件一般用于内容块之间的用一个灰色块隔开的场景,方便用户风格统一,减少工作量 |
| | | * @tutorial https://www.uvui.cn/components/gap.html |
| | | * @property {String} bgColor 背景颜色 (默认 'transparent' ) |
| | | * @property {String | Number} height 分割槽高度,单位px (默认 20 ) |
| | | * @property {String | Number} marginTop 与前一个组件的距离,单位px( 默认 0 ) |
| | | * @property {String | Number} marginBottom 与后一个组件的距离,单位px (默认 0 ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * |
| | | * @example <uv-gap height="80" bg-color="#bbb"></uv-gap> |
| | | */ |
| | | export default { |
| | | name: "uv-gap", |
| | | mixins: [mpMixin, mixin,props], |
| | | computed: { |
| | | gapStyle() { |
| | | const style = { |
| | | backgroundColor: this.bgColor, |
| | | height: this.$uv.addUnit(this.height), |
| | | marginTop: this.$uv.addUnit(this.marginTop), |
| | | marginBottom: this.$uv.addUnit(this.marginBottom), |
| | | } |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)) |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| New file |
| | |
| | | { |
| | | "id": "uv-gap", |
| | | "displayName": "uv-gap 间隔槽 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.1", |
| | | "description": "该组件一般用于内容块之间的用一个灰色块隔开的场景,方便用户风格统一,减少工作量。", |
| | | "keywords": [ |
| | | "gap", |
| | | "uvui", |
| | | "uv-ui", |
| | | "间隔槽", |
| | | "内容块" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Gap 间隔槽 |
| | | |
| | | > **组件名:uv-gap** |
| | | |
| | | 该组件一般用于内容块之间的用一个灰色块隔开的场景,方便用户风格统一,减少工作量。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/gap.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| | | |
| New file |
| | |
| | | ## 1.0.9(2023-12-06) |
| | | 1. 优化 |
| | | ## 1.0.8(2023-12-06) |
| | | 1. 阻止事件冒泡问题 |
| | | ## 1.0.7(2023-10-13) |
| | | 1. unmounted兼容vue3 |
| | | ## 1.0.6(2023-08-14) |
| | | 1. 修复初始的时候闪动的BUG |
| | | ## 1.0.5(2023-06-22) |
| | | 1. 优化修改 |
| | | ## 1.0.4(2023-06-21) |
| | | 1. 修复BUG |
| | | ## 1.0.3(2023-06-01) |
| | | 1. 修复点击触发两次事件的BUG |
| | | ## 1.0.2(2023-05-23) |
| | | 1. 优化 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-grid 宫格布局 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 宫格的name |
| | | name: { |
| | | type: [String, Number, null], |
| | | default: null |
| | | }, |
| | | // 背景颜色 |
| | | bgColor: { |
| | | type: String, |
| | | default: 'transparent' |
| | | }, |
| | | ...uni.$uv?.props?.gridItem |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <view |
| | | class="uv-grid-item" |
| | | hover-class="uv-grid-item--hover-class" |
| | | :hover-stay-time="200" |
| | | @tap="clickHandler" |
| | | :class="classes" |
| | | :style="[itemStyle]" |
| | | > |
| | | <slot /> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <view |
| | | class="uv-grid-item" |
| | | :hover-stay-time="200" |
| | | @tap="clickHandler" |
| | | :class="classes" |
| | | :style="[itemStyle]" |
| | | > |
| | | <slot /> |
| | | </view> |
| | | <!-- #endif --> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * gridItem 提示 |
| | | * @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。搭配uv-grid使用 |
| | | * @tutorial https://www.uvui.cn/components/grid.html |
| | | * @property {String | Number} name 宫格的name ( 默认 null ) |
| | | * @property {String} bgColor 宫格的背景颜色 (默认 'transparent' ) |
| | | * @property {Object} customStyle 自定义样式,对象形式 |
| | | * @event {Function} click 点击宫格触发 |
| | | * @example <uv-grid-item></uv-grid-item> |
| | | */ |
| | | export default { |
| | | name: "uv-grid-item", |
| | | mixins: [mpMixin, mixin, props], |
| | | emits: ['$uvGridItem','click'], |
| | | data() { |
| | | return { |
| | | parentData: { |
| | | col: 3, // 父组件划分的宫格数 |
| | | border: true, // 是否显示边框,根据父组件决定 |
| | | }, |
| | | // #ifdef APP-NVUE |
| | | width: 0, // nvue下才这么计算,vue下放到computed中,否则会因为延时造成闪烁 |
| | | // #endif |
| | | classes: [], // 类名集合,用于判断是否显示右边和下边框 |
| | | }; |
| | | }, |
| | | created() { |
| | | // 父组件的实例 |
| | | this.updateParentData() |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | }, |
| | | computed: { |
| | | // #ifndef APP-NVUE |
| | | // vue下放到computed中,否则会因为延时造成闪烁 |
| | | width() { |
| | | return 100 / Number(this.parentData.col) + '%' |
| | | }, |
| | | // #endif |
| | | itemStyle() { |
| | | const style = { |
| | | background: this.bgColor, |
| | | width: this.width |
| | | } |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)) |
| | | } |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 用于在父组件uv-grid的children中被添加入子组件时, |
| | | // 重新计算item的边框 |
| | | uni.$on('$uvGridItem', () => { |
| | | this.gridItemClasses() |
| | | }) |
| | | // #ifdef APP-NVUE |
| | | // 获取元素该有的长度,nvue下要延时才准确 |
| | | this.$nextTick(function(){ |
| | | this.getItemWidth() |
| | | }) |
| | | // #endif |
| | | // 发出事件,通知所有的grid-item都重新计算自己的边框 |
| | | uni.$emit('$uvGridItem') |
| | | this.gridItemClasses() |
| | | }, |
| | | // 获取父组件的参数 |
| | | updateParentData() { |
| | | // 此方法写在mixin中 |
| | | this.getParentData('uv-grid'); |
| | | }, |
| | | clickHandler() { |
| | | let name = this.name |
| | | // 如果没有设置name属性,历遍父组件的children数组,判断当前的元素是否和本实例this相等,找出当前组件的索引 |
| | | const children = this.parent?.children |
| | | if(children && this.name === null) { |
| | | name = children.findIndex(child => child === this) |
| | | } |
| | | // 调用父组件方法,发出事件 |
| | | this.parent && this.parent.childClick(name) |
| | | this.$emit('click', name) |
| | | }, |
| | | async getItemWidth() { |
| | | // 如果是nvue,不能使用百分比,只能使用固定宽度 |
| | | let width = 0 |
| | | if(this.parent) { |
| | | // 获取父组件宽度后,除以栅格数,得出每个item的宽度 |
| | | const parentWidth = await this.getParentWidth() |
| | | width = parentWidth / Number(this.parentData.col) + 'px' |
| | | } |
| | | this.width = width |
| | | }, |
| | | // 获取父元素的尺寸 |
| | | getParentWidth() { |
| | | // #ifdef APP-NVUE |
| | | // 返回一个promise,让调用者可以用await同步获取 |
| | | const dom = uni.requireNativePlugin('dom') |
| | | return new Promise(resolve => { |
| | | // 调用父组件的ref |
| | | dom.getComponentRect(this.parent.$refs['uv-grid'], res => { |
| | | resolve(res.size.width) |
| | | }) |
| | | }) |
| | | // #endif |
| | | }, |
| | | gridItemClasses() { |
| | | if(this.parentData.border) { |
| | | let classes = [] |
| | | this.parent.children.map((child, index) =>{ |
| | | if(this === child) { |
| | | const len = this.parent.children.length |
| | | // 贴近右边屏幕边沿的child,并且最后一个(比如只有横向2个的时候),无需右边框 |
| | | if((index + 1) % this.parentData.col !== 0 && index + 1 !== len) { |
| | | classes.push('uv-border-right') |
| | | } |
| | | // 总的宫格数量对列数取余的值 |
| | | // 如果取余后,值为0,则意味着要将最后一排的宫格,都不需要下边框 |
| | | const lessNum = len % this.parentData.col === 0 ? this.parentData.col : len % this.parentData.col |
| | | // 最下面的一排child,无需下边框 |
| | | if(index < len - lessNum) { |
| | | classes.push('uv-border-bottom') |
| | | } |
| | | } |
| | | }) |
| | | // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效 |
| | | // #ifdef MP-ALIPAY || MP-TOUTIAO |
| | | classes = classes.join(' ') |
| | | // #endif |
| | | this.classes = classes |
| | | } |
| | | } |
| | | }, |
| | | // #ifdef VUE2 |
| | | beforeDestroy() { |
| | | // 移除事件监听,释放性能 |
| | | uni.$off('$uvGridItem') |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | unmounted() { |
| | | // 移除事件监听,释放性能 |
| | | uni.$off('$uvGridItem') |
| | | } |
| | | // #endif |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-border: 1; |
| | | $show-border-right: 1; |
| | | $show-border-bottom: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-grid-item-hover-class-opcatiy:.5 !default; |
| | | $uv-grid-item-margin-top:1rpx !default; |
| | | $uv-grid-item-border-right-width:0.5px !default; |
| | | $uv-grid-item-border-bottom-width:0.5px !default; |
| | | $uv-grid-item-border-right-color:$uv-border-color !default; |
| | | $uv-grid-item-border-bottom-color:$uv-border-color !default; |
| | | .uv-grid-item { |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: relative; |
| | | flex-direction: column; |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | display: flex; |
| | | /* #endif */ |
| | | |
| | | /* #ifdef MP */ |
| | | position: relative; |
| | | float: left; |
| | | /* #endif */ |
| | | |
| | | /* #ifdef MP-WEIXIN */ |
| | | margin-top:$uv-grid-item-margin-top; |
| | | /* #endif */ |
| | | |
| | | &--hover-class { |
| | | opacity:$uv-grid-item-hover-class-opcatiy; |
| | | } |
| | | } |
| | | |
| | | /* #ifdef APP-NVUE */ |
| | | // 由于nvue不支持组件内引入app.vue中再引入的样式,所以需要写在这里 |
| | | .uv-border-right { |
| | | border-right-width:$uv-grid-item-border-right-width; |
| | | border-color: $uv-grid-item-border-right-color; |
| | | } |
| | | |
| | | .uv-border-bottom { |
| | | border-bottom-width:$uv-grid-item-border-bottom-width; |
| | | border-color:$uv-grid-item-border-bottom-color; |
| | | } |
| | | /* #endif */ |
| | | </style> |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 分成几列 |
| | | col: { |
| | | type: [String, Number], |
| | | default: 3 |
| | | }, |
| | | // 是否显示边框 |
| | | border: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 宫格对齐方式,表现为数量少的时候,靠左,居中,还是靠右 |
| | | align: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | ...uni.$uv?.props?.grid |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-grid" |
| | | ref='uv-grid' |
| | | :style="[gridStyle]" |
| | | > |
| | | <slot /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * grid 宫格布局 |
| | | * @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。 |
| | | * @tutorial https://www.uvui.cn/components/grid.html |
| | | * @property {String | Number} col 宫格的列数(默认 3 ) |
| | | * @property {Boolean} border 是否显示宫格的边框(默认 false ) |
| | | * @property {String} align 宫格对齐方式,表现为数量少的时候,靠左,居中,还是靠右 (默认 'left' ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * @event {Function} click 点击宫格触发 |
| | | * @example <uv-grid :col="3" @click="click"></uv-grid> |
| | | */ |
| | | export default { |
| | | name: 'uv-grid', |
| | | mixins: [mpMixin, mixin, props], |
| | | emits: ['click'], |
| | | data() { |
| | | return { |
| | | index: 0, |
| | | width: 0 |
| | | } |
| | | }, |
| | | watch: { |
| | | // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件 |
| | | parentData() { |
| | | if (this.children.length) { |
| | | this.children.map(child => { |
| | | // 判断子组件(uv-radio)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值) |
| | | typeof(child.updateParentData) == 'function' && child.updateParentData(); |
| | | }) |
| | | } |
| | | }, |
| | | }, |
| | | created() { |
| | | // 如果将children定义在data中,在微信小程序会造成循环引用而报错 |
| | | this.children = [] |
| | | }, |
| | | computed: { |
| | | // 计算父组件的值是否发生变化 |
| | | parentData() { |
| | | return [this.hoverClass, this.col, this.size, this.border]; |
| | | }, |
| | | // 宫格对齐方式 |
| | | gridStyle() { |
| | | let style = {}; |
| | | switch (this.align) { |
| | | case 'left': |
| | | style.justifyContent = 'flex-start'; |
| | | break; |
| | | case 'center': |
| | | style.justifyContent = 'center'; |
| | | break; |
| | | case 'right': |
| | | style.justifyContent = 'flex-end'; |
| | | break; |
| | | default: |
| | | style.justifyContent = 'flex-start'; |
| | | }; |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)); |
| | | } |
| | | }, |
| | | methods: { |
| | | // 此方法由uv-grid-item触发,用于在uv-grid发出事件 |
| | | childClick(name) { |
| | | this.$emit('click', name) |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | $uv-grid-width:100% !default; |
| | | .uv-grid { |
| | | /* #ifdef MP */ |
| | | width: $uv-grid-width; |
| | | position: relative; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | display: block; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | @include flex; |
| | | flex-wrap: wrap; |
| | | align-items: center; |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-grid", |
| | | "displayName": "uv-grid 宫格布局 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.9", |
| | | "description": "uv-grid 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。", |
| | | "keywords": [ |
| | | "uv-grid", |
| | | "uvui", |
| | | "uv-ui", |
| | | "宫格布局", |
| | | "grid" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-icon" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Grid 宫格布局 |
| | | |
| | | > **组件名:uv-grid** |
| | | |
| | | 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/grid.html" target="_blank">查看文档</a> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.13(2023-12-06) |
| | | 1. 优化 |
| | | ## 1.0.12(2023-12-06) |
| | | 1. 阻止事件冒泡处理 |
| | | ## 1.0.11(2023-10-29) |
| | | 1. imgMode默认值改成aspectFit |
| | | ## 1.0.10(2023-08-13) |
| | | 1. 优化nvue,方便自定义图标 |
| | | ## 1.0.9(2023-07-28) |
| | | 1. 修改几个对应错误图标的BUG |
| | | ## 1.0.8(2023-07-24) |
| | | 1. 优化 支持base64图片 |
| | | ## 1.0.7(2023-07-17) |
| | | 1. 修复 uv-icon 恢复uv-empty相关的图标 |
| | | ## 1.0.6(2023-07-13) |
| | | 1. 修复icon设置name属性对应图标错误的BUG |
| | | ## 1.0.5(2023-07-04) |
| | | 1. 更新图标,删除一些不常用的图标 |
| | | 2. 删除base64,修改成ttf文件引入读取图标 |
| | | 3. 自定义图标文档说明:https://www.uvui.cn/guide/customIcon.html |
| | | ## 1.0.4(2023-07-03) |
| | | 1. 修复主题颜色在APP不生效的BUG |
| | | ## 1.0.3(2023-05-24) |
| | | 1. 将线上ttf字体包替换成base64,避免加载时或者网络差时候显示白色方块 |
| | | ## 1.0.2(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.1(2023-05-10) |
| | | 1. 修复小程序中异常显示 |
| | | ## 1.0.0(2023-05-04) |
| | | 新发版 |
| New file |
| | |
| | | export default { |
| | | 'uvicon-level': 'e68f', |
| | | 'uvicon-checkbox-mark': 'e659', |
| | | 'uvicon-folder': 'e694', |
| | | 'uvicon-movie': 'e67c', |
| | | 'uvicon-star-fill': 'e61e', |
| | | 'uvicon-star': 'e618', |
| | | 'uvicon-phone-fill': 'e6ac', |
| | | 'uvicon-phone': 'e6ba', |
| | | 'uvicon-apple-fill': 'e635', |
| | | 'uvicon-backspace': 'e64d', |
| | | 'uvicon-attach': 'e640', |
| | | 'uvicon-empty-data': 'e671', |
| | | 'uvicon-empty-address': 'e68a', |
| | | 'uvicon-empty-favor': 'e662', |
| | | 'uvicon-empty-car': 'e657', |
| | | 'uvicon-empty-order': 'e66b', |
| | | 'uvicon-empty-list': 'e672', |
| | | 'uvicon-empty-search': 'e677', |
| | | 'uvicon-empty-permission': 'e67d', |
| | | 'uvicon-empty-news': 'e67e', |
| | | 'uvicon-empty-history': 'e685', |
| | | 'uvicon-empty-coupon': 'e69b', |
| | | 'uvicon-empty-page': 'e60e', |
| | | 'uvicon-empty-wifi-off': 'e6cc', |
| | | 'uvicon-reload': 'e627', |
| | | 'uvicon-order': 'e695', |
| | | 'uvicon-server-man': 'e601', |
| | | 'uvicon-search': 'e632', |
| | | 'uvicon-more-dot-fill': 'e66f', |
| | | 'uvicon-scan': 'e631', |
| | | 'uvicon-map': 'e665', |
| | | 'uvicon-map-fill': 'e6a8', |
| | | 'uvicon-tags': 'e621', |
| | | 'uvicon-tags-fill': 'e613', |
| | | 'uvicon-eye': 'e664', |
| | | 'uvicon-eye-fill': 'e697', |
| | | 'uvicon-eye-off': 'e69c', |
| | | 'uvicon-eye-off-outline': 'e688', |
| | | 'uvicon-mic': 'e66d', |
| | | 'uvicon-mic-off': 'e691', |
| | | 'uvicon-calendar': 'e65c', |
| | | 'uvicon-trash': 'e623', |
| | | 'uvicon-trash-fill': 'e6ce', |
| | | 'uvicon-play-left': 'e6bf', |
| | | 'uvicon-play-right': 'e6b3', |
| | | 'uvicon-minus': 'e614', |
| | | 'uvicon-plus': 'e625', |
| | | 'uvicon-info-circle': 'e69f', |
| | | 'uvicon-info-circle-fill': 'e6a7', |
| | | 'uvicon-question-circle': 'e622', |
| | | 'uvicon-question-circle-fill': 'e6bc', |
| | | 'uvicon-close': 'e65a', |
| | | 'uvicon-checkmark': 'e64a', |
| | | 'uvicon-checkmark-circle': 'e643', |
| | | 'uvicon-checkmark-circle-fill': 'e668', |
| | | 'uvicon-setting': 'e602', |
| | | 'uvicon-setting-fill': 'e6d0', |
| | | 'uvicon-heart': 'e6a2', |
| | | 'uvicon-heart-fill': 'e68b', |
| | | 'uvicon-camera': 'e642', |
| | | 'uvicon-camera-fill': 'e650', |
| | | 'uvicon-more-circle': 'e69e', |
| | | 'uvicon-more-circle-fill': 'e684', |
| | | 'uvicon-chat': 'e656', |
| | | 'uvicon-chat-fill': 'e63f', |
| | | 'uvicon-bag': 'e647', |
| | | 'uvicon-error-circle': 'e66e', |
| | | 'uvicon-error-circle-fill': 'e655', |
| | | 'uvicon-close-circle': 'e64e', |
| | | 'uvicon-close-circle-fill': 'e666', |
| | | 'uvicon-share': 'e629', |
| | | 'uvicon-share-fill': 'e6bb', |
| | | 'uvicon-share-square': 'e6c4', |
| | | 'uvicon-shopping-cart': 'e6cb', |
| | | 'uvicon-shopping-cart-fill': 'e630', |
| | | 'uvicon-bell': 'e651', |
| | | 'uvicon-bell-fill': 'e604', |
| | | 'uvicon-list': 'e690', |
| | | 'uvicon-list-dot': 'e6a9', |
| | | 'uvicon-zhifubao-circle-fill': 'e617', |
| | | 'uvicon-weixin-circle-fill': 'e6cd', |
| | | 'uvicon-weixin-fill': 'e620', |
| | | 'uvicon-qq-fill': 'e608', |
| | | 'uvicon-qq-circle-fill': 'e6b9', |
| | | 'uvicon-moments-circel-fill': 'e6c2', |
| | | 'uvicon-moments': 'e6a0', |
| | | 'uvicon-car': 'e64f', |
| | | 'uvicon-car-fill': 'e648', |
| | | 'uvicon-warning-fill': 'e6c7', |
| | | 'uvicon-warning': 'e6c1', |
| | | 'uvicon-clock-fill': 'e64b', |
| | | 'uvicon-clock': 'e66c', |
| | | 'uvicon-edit-pen': 'e65d', |
| | | 'uvicon-edit-pen-fill': 'e679', |
| | | 'uvicon-email': 'e673', |
| | | 'uvicon-email-fill': 'e683', |
| | | 'uvicon-minus-circle': 'e6a5', |
| | | 'uvicon-plus-circle': 'e603', |
| | | 'uvicon-plus-circle-fill': 'e611', |
| | | 'uvicon-file-text': 'e687', |
| | | 'uvicon-file-text-fill': 'e67f', |
| | | 'uvicon-pushpin': 'e6d1', |
| | | 'uvicon-pushpin-fill': 'e6b6', |
| | | 'uvicon-grid': 'e68c', |
| | | 'uvicon-grid-fill': 'e698', |
| | | 'uvicon-play-circle': 'e6af', |
| | | 'uvicon-play-circle-fill': 'e62a', |
| | | 'uvicon-pause-circle-fill': 'e60c', |
| | | 'uvicon-pause': 'e61c', |
| | | 'uvicon-pause-circle': 'e696', |
| | | 'uvicon-gift-fill': 'e6b0', |
| | | 'uvicon-gift': 'e680', |
| | | 'uvicon-kefu-ermai': 'e660', |
| | | 'uvicon-server-fill': 'e610', |
| | | 'uvicon-coupon-fill': 'e64c', |
| | | 'uvicon-coupon': 'e65f', |
| | | 'uvicon-integral': 'e693', |
| | | 'uvicon-integral-fill': 'e6b1', |
| | | 'uvicon-home-fill': 'e68e', |
| | | 'uvicon-home': 'e67b', |
| | | 'uvicon-account': 'e63a', |
| | | 'uvicon-account-fill': 'e653', |
| | | 'uvicon-thumb-down-fill': 'e628', |
| | | 'uvicon-thumb-down': 'e60a', |
| | | 'uvicon-thumb-up': 'e612', |
| | | 'uvicon-thumb-up-fill': 'e62c', |
| | | 'uvicon-lock-fill': 'e6a6', |
| | | 'uvicon-lock-open': 'e68d', |
| | | 'uvicon-lock-opened-fill': 'e6a1', |
| | | 'uvicon-lock': 'e69d', |
| | | 'uvicon-red-packet': 'e6c3', |
| | | 'uvicon-photo-fill': 'e6b4', |
| | | 'uvicon-photo': 'e60d', |
| | | 'uvicon-volume-off-fill': 'e6c8', |
| | | 'uvicon-volume-off': 'e6bd', |
| | | 'uvicon-volume-fill': 'e624', |
| | | 'uvicon-volume': 'e605', |
| | | 'uvicon-download': 'e670', |
| | | 'uvicon-arrow-up-fill': 'e636', |
| | | 'uvicon-arrow-down-fill': 'e638', |
| | | 'uvicon-play-left-fill': 'e6ae', |
| | | 'uvicon-play-right-fill': 'e6ad', |
| | | 'uvicon-arrow-downward': 'e634', |
| | | 'uvicon-arrow-leftward': 'e63b', |
| | | 'uvicon-arrow-rightward': 'e644', |
| | | 'uvicon-arrow-upward': 'e641', |
| | | 'uvicon-arrow-down': 'e63e', |
| | | 'uvicon-arrow-right': 'e63c', |
| | | 'uvicon-arrow-left': 'e646', |
| | | 'uvicon-arrow-up': 'e633', |
| | | 'uvicon-skip-back-left': 'e6c5', |
| | | 'uvicon-skip-forward-right': 'e61f', |
| | | 'uvicon-arrow-left-double': 'e637', |
| | | 'uvicon-man': 'e675', |
| | | 'uvicon-woman': 'e626', |
| | | 'uvicon-en': 'e6b8', |
| | | 'uvicon-twitte': 'e607', |
| | | 'uvicon-twitter-circle-fill': 'e6cf' |
| | | } |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 图标类名 |
| | | name: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 图标颜色,可接受主题色 |
| | | color: { |
| | | type: String, |
| | | default: '#606266' |
| | | }, |
| | | // 字体大小,单位px |
| | | size: { |
| | | type: [String, Number], |
| | | default: '16px' |
| | | }, |
| | | // 是否显示粗体 |
| | | bold: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 点击图标的时候传递事件出去的index(用于区分点击了哪一个) |
| | | index: { |
| | | type: [String, Number], |
| | | default: null |
| | | }, |
| | | // 触摸图标时的类名 |
| | | hoverClass: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 自定义扩展前缀,方便用户扩展自己的图标库 |
| | | customPrefix: { |
| | | type: String, |
| | | default: 'uvicon' |
| | | }, |
| | | // 图标右边或者下面的文字 |
| | | label: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // label的位置,只能右边或者下边 |
| | | labelPos: { |
| | | type: String, |
| | | default: 'right' |
| | | }, |
| | | // label的大小 |
| | | labelSize: { |
| | | type: [String, Number], |
| | | default: '15px' |
| | | }, |
| | | // label的颜色 |
| | | labelColor: { |
| | | type: String, |
| | | default: '#606266' |
| | | }, |
| | | // label与图标的距离 |
| | | space: { |
| | | type: [String, Number], |
| | | default: '3px' |
| | | }, |
| | | // 图片的mode |
| | | imgMode: { |
| | | type: String, |
| | | default: 'aspectFit' |
| | | }, |
| | | // 用于显示图片小图标时,图片的宽度 |
| | | width: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 用于显示图片小图标时,图片的高度 |
| | | height: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 用于解决某些情况下,让图标垂直居中的用途 |
| | | top: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 是否阻止事件传播 |
| | | stop: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ...uni.$uv?.props?.icon |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-icon" |
| | | @tap="clickHandler" |
| | | :class="['uv-icon--' + labelPos]" |
| | | > |
| | | <image |
| | | class="uv-icon__img" |
| | | v-if="isImg" |
| | | :src="name" |
| | | :mode="imgMode" |
| | | :style="[imgStyle, $uv.addStyle(customStyle)]" |
| | | ></image> |
| | | <text |
| | | v-else |
| | | class="uv-icon__icon" |
| | | :class="uClasses" |
| | | :style="[iconStyle, $uv.addStyle(customStyle)]" |
| | | :hover-class="hoverClass" |
| | | >{{icon}}</text> |
| | | <!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示 --> |
| | | <text |
| | | v-if="label !== ''" |
| | | class="uv-icon__label" |
| | | :style="{ |
| | | color: labelColor, |
| | | fontSize: $uv.addUnit(labelSize), |
| | | marginLeft: labelPos == 'right' ? $uv.addUnit(space) : 0, |
| | | marginTop: labelPos == 'bottom' ? $uv.addUnit(space) : 0, |
| | | marginRight: labelPos == 'left' ? $uv.addUnit(space) : 0, |
| | | marginBottom: labelPos == 'top' ? $uv.addUnit(space) : 0 |
| | | }" |
| | | >{{ label }}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | // #ifdef APP-NVUE |
| | | // nvue通过weex的dom模块引入字体,相关文档地址如下: |
| | | // https://weex.apache.org/zh/docs/modules/dom.html#addrule |
| | | import iconUrl from './uvicons.ttf'; |
| | | const domModule = weex.requireModule('dom') |
| | | domModule.addRule('fontFace', { |
| | | 'fontFamily': "uvicon-iconfont", |
| | | 'src': "url('" + iconUrl + "')" |
| | | }) |
| | | // #endif |
| | | // 引入图标名称,已经对应的unicode |
| | | import icons from './icons'; |
| | | import props from './props.js'; |
| | | /** |
| | | * icon 图标 |
| | | * @description 基于字体的图标集,包含了大多数常见场景的图标。 |
| | | * @tutorial https://www.uvui.cn/components/icon.html |
| | | * @property {String} name 图标名称,见示例图标集 |
| | | * @property {String} color 图标颜色,可接受主题色 (默认 color['uv-content-color'] ) |
| | | * @property {String | Number} size 图标字体大小,单位px (默认 '16px' ) |
| | | * @property {Boolean} bold 是否显示粗体 (默认 false ) |
| | | * @property {String | Number} index 点击图标的时候传递事件出去的index(用于区分点击了哪一个) |
| | | * @property {String} hoverClass 图标按下去的样式类,用法同uni的view组件的hoverClass参数,详情见官网 |
| | | * @property {String} customPrefix 自定义扩展前缀,方便用户扩展自己的图标库 (默认 'uicon' ) |
| | | * @property {String | Number} label 图标右侧的label文字 |
| | | * @property {String} labelPos label相对于图标的位置,只能right或bottom (默认 'right' ) |
| | | * @property {String | Number} labelSize label字体大小,单位px (默认 '15px' ) |
| | | * @property {String} labelColor 图标右侧的label文字颜色 ( 默认 color['uv-content-color'] ) |
| | | * @property {String | Number} space label与图标的距离,单位px (默认 '3px' ) |
| | | * @property {String} imgMode 图片的mode |
| | | * @property {String | Number} width 显示图片小图标时的宽度 |
| | | * @property {String | Number} height 显示图片小图标时的高度 |
| | | * @property {String | Number} top 图标在垂直方向上的定位 用于解决某些情况下,让图标垂直居中的用途 (默认 0 ) |
| | | * @property {Boolean} stop 是否阻止事件传播 (默认 false ) |
| | | * @property {Object} customStyle icon的样式,对象形式 |
| | | * @event {Function} click 点击图标时触发 |
| | | * @event {Function} touchstart 事件触摸时触发 |
| | | * @example <uv-icon name="photo" color="#2979ff" size="28"></uv-icon> |
| | | */ |
| | | export default { |
| | | name: 'uv-icon', |
| | | emits: ['click'], |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | colorType: [ |
| | | 'primary', |
| | | 'success', |
| | | 'info', |
| | | 'error', |
| | | 'warning' |
| | | ] |
| | | } |
| | | }, |
| | | computed: { |
| | | uClasses() { |
| | | let classes = [] |
| | | classes.push(this.customPrefix) |
| | | classes.push(this.customPrefix + '-' + this.name) |
| | | // 主题色,通过类配置 |
| | | if (this.color && this.colorType.includes(this.color)) classes.push('uv-icon__icon--' + this.color) |
| | | // 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别 |
| | | // 故需将其拆成一个字符串的形式,通过空格隔开各个类名 |
| | | //#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU |
| | | classes = classes.join(' ') |
| | | //#endif |
| | | return classes |
| | | }, |
| | | iconStyle() { |
| | | let style = {} |
| | | style = { |
| | | fontSize: this.$uv.addUnit(this.size), |
| | | lineHeight: this.$uv.addUnit(this.size), |
| | | fontWeight: this.bold ? 'bold' : 'normal', |
| | | // 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中 |
| | | top: this.$uv.addUnit(this.top) |
| | | } |
| | | // 非主题色值时,才当作颜色值 |
| | | if (this.color && !this.colorType.includes(this.color)) style.color = this.color |
| | | return style |
| | | }, |
| | | // 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式 |
| | | isImg() { |
| | | const isBase64 = this.name.indexOf('data:') > -1 && this.name.indexOf('base64') > -1; |
| | | return this.name.indexOf('/') !== -1 || isBase64; |
| | | }, |
| | | imgStyle() { |
| | | let style = {} |
| | | // 如果设置width和height属性,则优先使用,否则使用size属性 |
| | | style.width = this.width ? this.$uv.addUnit(this.width) : this.$uv.addUnit(this.size) |
| | | style.height = this.height ? this.$uv.addUnit(this.height) : this.$uv.addUnit(this.size) |
| | | return style |
| | | }, |
| | | // 通过图标名,查找对应的图标 |
| | | icon() { |
| | | // 如果内置的图标中找不到对应的图标,就直接返回name值,因为用户可能传入的是unicode代码 |
| | | const code = icons['uvicon-' + this.name]; |
| | | // #ifdef APP-NVUE |
| | | if(!code) { |
| | | return code ? unescape(`%u${code}`) : ['uvicon'].indexOf(this.customPrefix) > -1 ? unescape(`%u${this.name}`) : ''; |
| | | } |
| | | // #endif |
| | | return code ? unescape(`%u${code}`) : ['uvicon'].indexOf(this.customPrefix) > -1 ? this.name : ''; |
| | | } |
| | | }, |
| | | methods: { |
| | | clickHandler(e) { |
| | | this.$emit('click', this.index) |
| | | // 是否阻止事件冒泡 |
| | | this.stop && this.preventEvent(e) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | // 变量定义 |
| | | $uv-icon-primary: $uv-primary !default; |
| | | $uv-icon-success: $uv-success !default; |
| | | $uv-icon-info: $uv-info !default; |
| | | $uv-icon-warning: $uv-warning !default; |
| | | $uv-icon-error: $uv-error !default; |
| | | $uv-icon-label-line-height: 1 !default; |
| | | /* #ifndef APP-NVUE */ |
| | | // 非nvue下加载字体 |
| | | @font-face { |
| | | font-family: 'uvicon-iconfont'; |
| | | src: url('./uvicons.ttf') format('truetype'); |
| | | } |
| | | /* #endif */ |
| | | .uv-icon { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | &--left { |
| | | flex-direction: row-reverse; |
| | | align-items: center; |
| | | } |
| | | &--right { |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | &--top { |
| | | flex-direction: column-reverse; |
| | | justify-content: center; |
| | | } |
| | | &--bottom { |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | } |
| | | &__icon { |
| | | font-family: uvicon-iconfont; |
| | | position: relative; |
| | | @include flex; |
| | | align-items: center; |
| | | &--primary { |
| | | color: $uv-icon-primary; |
| | | } |
| | | &--success { |
| | | color: $uv-icon-success; |
| | | } |
| | | &--error { |
| | | color: $uv-icon-error; |
| | | } |
| | | &--warning { |
| | | color: $uv-icon-warning; |
| | | } |
| | | &--info { |
| | | color: $uv-icon-info; |
| | | } |
| | | } |
| | | &__img { |
| | | /* #ifndef APP-NVUE */ |
| | | height: auto; |
| | | will-change: transform; |
| | | /* #endif */ |
| | | } |
| | | &__label { |
| | | /* #ifndef APP-NVUE */ |
| | | line-height: $uv-icon-label-line-height; |
| | | /* #endif */ |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-icon", |
| | | "displayName": "uv-icon 图标 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.13", |
| | | "description": "基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。可自定义颜色、大小。", |
| | | "keywords": [ |
| | | "uv-ui,uvui,uv-icon,icon,图标,字体图标" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## uv-icon 图标库 |
| | | |
| | | > **组件名:uv-icon** |
| | | |
| | | 基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/icon.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | |  |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.16(2023-12-21) |
| | | 1. 修复设置show-menu-by-longpress不生效的BUG |
| | | ## 1.0.15(2023-12-06) |
| | | 1. 优化 |
| | | ## 1.0.14(2023-12-06) |
| | | 1. 阻止事件冒泡问题 |
| | | ## 1.0.13(2023-11-15) |
| | | 1. 修复webp之前未使用的BUG |
| | | ## 1.0.12(2023-10-11) |
| | | 1. 修复懒加载报错:https://gitee.com/climblee/uv-ui/issues/I869JS |
| | | ## 1.0.11(2023-08-31) |
| | | 1. 修复设置widthFix时出现显示不全的BUG |
| | | 2. 修复抖音等平台在width和height属性改变时出现不显示的BUG |
| | | ## 1.0.10(2023-08-29) |
| | | 1. 修复异步修改宽高不生效的问题,问题来源:https://gitee.com/climblee/uv-ui/issues/I7WUQ3 |
| | | ## 1.0.9(2023-08-21) |
| | | 1. 修复设置宽高为百分比不生效的BUG |
| | | ## 1.0.8(2023-07-24) |
| | | 1. 优化 nvue模式下增加cellChild参数,是否在list中cell节点下,nvue中cell下建议设置成true |
| | | ## 1.0.7(2023-07-02) |
| | | 修复VUE3模式下可能不显示的BUG |
| | | ## 1.0.6(2023-07-02) |
| | | 优化修改 |
| | | ## 1.0.5(2023-06-28) |
| | | 修复duration属性不生效的BUG |
| | | ## 1.0.4(2023-05-27) |
| | | 1. 修复可能报错的问题 |
| | | ## 1.0.3(2023-05-24) |
| | | 1. 去掉template中存在的this.导致头条小程序编译警告 |
| | | ## 1.0.2(2023-05-23) |
| | | 1. 优化 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-image 图片 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 图片地址 |
| | | src: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 裁剪模式 |
| | | mode: { |
| | | type: String, |
| | | default: 'aspectFill' |
| | | }, |
| | | // 宽度,单位任意 |
| | | width: { |
| | | type: [String, Number], |
| | | default: '300' |
| | | }, |
| | | // 高度,单位任意 |
| | | height: { |
| | | type: [String, Number], |
| | | default: '225' |
| | | }, |
| | | // 图片形状,circle-圆形,square-方形 |
| | | shape: { |
| | | type: String, |
| | | default: 'square' |
| | | }, |
| | | // 圆角,单位任意 |
| | | radius: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 是否懒加载,微信小程序、App、百度小程序、字节跳动小程序 |
| | | lazyLoad: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否开启observer懒加载,nvue不生效 |
| | | observeLazyLoad: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 开启长按图片显示识别微信小程序码菜单 |
| | | showMenuByLongpress: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 加载中的图标,或者小图片 |
| | | loadingIcon: { |
| | | type: String, |
| | | default: 'photo' |
| | | }, |
| | | // 加载失败的图标,或者小图片 |
| | | errorIcon: { |
| | | type: String, |
| | | default: 'error-circle' |
| | | }, |
| | | // 是否显示加载中的图标或者自定义的slot |
| | | showLoading: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否显示加载错误的图标或者自定义的slot |
| | | showError: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否需要淡入效果 |
| | | fade: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 只支持网络资源,只对微信小程序有效 |
| | | webp: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 过渡时间,单位ms |
| | | duration: { |
| | | type: [String, Number], |
| | | default: 500 |
| | | }, |
| | | // 背景颜色,用于深色页面加载图片时,为了和背景色融合 |
| | | bgColor: { |
| | | type: String, |
| | | default: '#f3f4f6' |
| | | }, |
| | | // nvue模式下 是否直接显示,在uv-list等cell下面使用就需要设置 |
| | | cellChild: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ...uni.$uv?.props?.image |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <uv-transition |
| | | v-if="show" |
| | | :show="show" |
| | | mode="fade" |
| | | :duration="fade ? duration : 0" |
| | | :cell-child="cellChild" |
| | | :custom-style="wrapStyle" |
| | | > |
| | | <view |
| | | class="uv-image" |
| | | :class="[`uv-image--${elIndex}`]" |
| | | @tap="onClick" |
| | | :style="[wrapStyle, backgroundStyle]" |
| | | > |
| | | <image |
| | | v-if="!isError && observeShow" |
| | | :src="src" |
| | | :mode="mode" |
| | | @error="onErrorHandler" |
| | | @load="onLoadHandler" |
| | | :show-menu-by-longpress="showMenuByLongpress" |
| | | :lazy-load="lazyLoad" |
| | | class="uv-image__image" |
| | | :style="[imageStyle]" |
| | | :webp="webp" |
| | | ></image> |
| | | <view |
| | | v-if="showLoading && loading" |
| | | class="uv-image__loading" |
| | | :style="{ |
| | | borderRadius: shape == 'circle' ? '50%' : $uv.addUnit(radius), |
| | | backgroundColor: bgColor, |
| | | width: $uv.addUnit(width), |
| | | height: $uv.addUnit(height) |
| | | }" |
| | | > |
| | | <slot name="loading"> |
| | | <uv-icon |
| | | :name="loadingIcon" |
| | | :width="width" |
| | | :height="height" |
| | | ></uv-icon> |
| | | </slot> |
| | | </view> |
| | | <view |
| | | v-if="showError && isError && !loading" |
| | | class="uv-image__error" |
| | | :style="{ |
| | | borderRadius: shape == 'circle' ? '50%' : $uv.addUnit(radius), |
| | | width: $uv.addUnit(width), |
| | | height: $uv.addUnit(height) |
| | | }" |
| | | > |
| | | <slot name="error"> |
| | | <uv-icon |
| | | :name="errorIcon" |
| | | :width="width" |
| | | :height="height" |
| | | ></uv-icon> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </uv-transition> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * Image 图片 |
| | | * @description 此组件为uni-app的image组件的加强版,在继承了原有功能外,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。 |
| | | * @tutorial https://www.uvui.cn/components/image.html |
| | | * @property {String} src 图片地址 |
| | | * @property {String} mode 裁剪模式,见官网说明 (默认 'aspectFill' ) |
| | | * @property {String | Number} width 宽度,单位任意,如果为数值,则为px单位 (默认 '300' ) |
| | | * @property {String | Number} height 高度,单位任意,如果为数值,则为px单位 (默认 '225' ) |
| | | * @property {String} shape 图片形状,circle-圆形,square-方形 (默认 'square' ) |
| | | * @property {String | Number} radius 圆角值,单位任意,如果为数值,则为px单位 (默认 0 ) |
| | | * @property {Boolean} lazyLoad 是否懒加载,仅微信小程序、App、百度小程序、字节跳动小程序有效 (默认 true ) |
| | | * @property {Boolean} showMenuByLongpress 是否开启长按图片显示识别小程序码菜单,仅微信小程序有效 (默认 true ) |
| | | * @property {String} loadingIcon 加载中的图标,或者小图片 (默认 'photo' ) |
| | | * @property {String} errorIcon 加载失败的图标,或者小图片 (默认 'error-circle' ) |
| | | * @property {Boolean} showLoading 是否显示加载中的图标或者自定义的slot (默认 true ) |
| | | * @property {Boolean} showError 是否显示加载错误的图标或者自定义的slot (默认 true ) |
| | | * @property {Boolean} fade 是否需要淡入效果 (默认 true ) |
| | | * @property {Boolean} webp 只支持网络资源,只对微信小程序有效 (默认 false ) |
| | | * @property {String | Number} duration 搭配fade参数的过渡时间,单位ms (默认 500 ) |
| | | * @property {String} bgColor 背景颜色,用于深色页面加载图片时,为了和背景色融合 (默认 '#f3f4f6' ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * @event {Function} click 点击图片时触发 |
| | | * @event {Function} error 图片加载失败时触发 |
| | | * @event {Function} load 图片加载成功时触发 |
| | | * @example <uv-image width="100%" height="300px" :src="src"></uv-image> |
| | | */ |
| | | export default { |
| | | name: 'uv-image', |
| | | emits: ['click','load','error'], |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | // 图片是否加载错误,如果是,则显示错误占位图 |
| | | isError: false, |
| | | // 初始化组件时,默认为加载中状态 |
| | | loading: true, |
| | | // 图片加载完成时,去掉背景颜色,因为如果是png图片,就会显示灰色的背景 |
| | | backgroundStyle: {}, |
| | | // 用于fade模式的控制组件显示与否 |
| | | show: false, |
| | | // 是否开启图片出现在可视范围进行加载(另一种懒加载) |
| | | observeShow: !this.observeLazyLoad, |
| | | elIndex: '', |
| | | // 因为props的值无法修改,故需要一个中间值 |
| | | imgWidth: this.width, |
| | | // 因为props的值无法修改,故需要一个中间值 |
| | | imgHeight: this.height, |
| | | thresholdValue: 50 |
| | | }; |
| | | }, |
| | | watch: { |
| | | src: { |
| | | immediate: true, |
| | | handler(n) { |
| | | if (!n) { |
| | | // 如果传入null或者'',或者false,或者undefined,标记为错误状态 |
| | | this.isError = true |
| | | } else { |
| | | this.isError = false; |
| | | this.loading = true; |
| | | } |
| | | } |
| | | }, |
| | | width(newVal){ |
| | | // 这样做的目的是避免在更新时候,某些平台动画会恢复关闭状态 |
| | | this.show = false; |
| | | this.$uv.sleep(2).then(res=>{ |
| | | this.show = true; |
| | | }); |
| | | this.imgWidth = newVal; |
| | | }, |
| | | height(newVal){ |
| | | // 这样做的目的是避免在更新时候,某些平台动画会恢复关闭状态 |
| | | this.show = false; |
| | | this.$uv.sleep(2).then(res=>{ |
| | | this.show = true; |
| | | }); |
| | | this.imgHeight = newVal; |
| | | } |
| | | }, |
| | | computed: { |
| | | wrapStyle() { |
| | | let style = {}; |
| | | // 通过调用addUnit()方法,如果有单位,如百分比,px单位等,直接返回,如果是纯粹的数值,则加上rpx单位 |
| | | if(this.mode !== 'heightFix') { |
| | | style.width = this.$uv.addUnit(this.imgWidth); |
| | | } |
| | | if(this.mode !== 'widthFix') { |
| | | style.height = this.$uv.addUnit(this.imgHeight); |
| | | } |
| | | // 如果是显示圆形,设置一个很多的半径值即可 |
| | | style.borderRadius = this.shape == 'circle' ? '10000px' : this.$uv.addUnit(this.radius) |
| | | // 如果设置圆角,必须要有hidden,否则可能圆角无效 |
| | | style.overflow = this.radius > 0 ? 'hidden' : 'visible' |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)); |
| | | }, |
| | | imageStyle() { |
| | | let style = {}; |
| | | style.borderRadius = this.shape == 'circle' ? '10000px' : this.$uv.addUnit(this.radius); |
| | | // #ifdef APP-NVUE |
| | | style.width = this.$uv.addUnit(this.imgWidth); |
| | | style.height = this.$uv.addUnit(this.imgHeight); |
| | | // #endif |
| | | return style; |
| | | } |
| | | }, |
| | | created() { |
| | | this.elIndex = this.$uv.guid(); |
| | | this.observer = {} |
| | | this.observerName = 'lazyLoadContentObserver' |
| | | }, |
| | | mounted() { |
| | | this.show = true; |
| | | this.$nextTick(()=>{ |
| | | if(this.observeLazyLoad) this.observerFn(); |
| | | }) |
| | | }, |
| | | methods: { |
| | | // 点击图片 |
| | | onClick() { |
| | | this.$emit('click') |
| | | }, |
| | | // 图片加载失败 |
| | | onErrorHandler(err) { |
| | | this.loading = false |
| | | this.isError = true |
| | | this.$emit('error', err) |
| | | }, |
| | | // 图片加载完成,标记loading结束 |
| | | onLoadHandler(event) { |
| | | if(this.mode == 'widthFix') this.imgHeight = 'auto' |
| | | if(this.mode == 'heightFix') this.imgWidth = 'auto' |
| | | this.loading = false |
| | | this.isError = false |
| | | this.$emit('load', event) |
| | | this.removeBgColor() |
| | | }, |
| | | // 移除图片的背景色 |
| | | removeBgColor() { |
| | | // 淡入动画过渡完成后,将背景设置为透明色,否则png图片会看到灰色的背景 |
| | | this.backgroundStyle = { |
| | | backgroundColor: 'transparent' |
| | | }; |
| | | }, |
| | | // 观察图片是否在可见视口 |
| | | observerFn(){ |
| | | // 在需要用到懒加载的页面,在触发底部的时候触发tOnLazyLoadReachBottom事件,保证所有图片进行加载 |
| | | this.$nextTick(() => { |
| | | uni.$once('onLazyLoadReachBottom', () => { |
| | | if (!this.observeShow) this.observeShow = true |
| | | }) |
| | | }) |
| | | setTimeout(() => { |
| | | // #ifndef APP-NVUE |
| | | this.disconnectObserver(this.observerName) |
| | | const contentObserver = uni.createIntersectionObserver(this) |
| | | contentObserver.relativeToViewport({ |
| | | bottom: this.thresholdValue |
| | | }).observe(`.uv-image--${this.elIndex}`, (res) => { |
| | | if (res.intersectionRatio > 0) { |
| | | // 懒加载状态改变 |
| | | this.observeShow = true |
| | | // 如果图片已经加载,去掉监听,减少性能消耗 |
| | | this.disconnectObserver(this.observerName) |
| | | } |
| | | }) |
| | | this[this.observerName] = contentObserver |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | this.observeShow = true; |
| | | // #endif |
| | | }, 50) |
| | | }, |
| | | disconnectObserver(observerName) { |
| | | const observer = this[observerName] |
| | | observer && observer.disconnect() |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-image-error-top:0px !default; |
| | | $uv-image-error-left:0px !default; |
| | | $uv-image-error-width:100% !default; |
| | | $uv-image-error-hight:100% !default; |
| | | $uv-image-error-background-color:$uv-bg-color !default; |
| | | $uv-image-error-color:$uv-tips-color !default; |
| | | $uv-image-error-font-size: 46rpx !default; |
| | | |
| | | .uv-image { |
| | | position: relative; |
| | | transition: opacity 0.5s ease-in-out; |
| | | |
| | | &__image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | &__loading, |
| | | &__error { |
| | | position: absolute; |
| | | top: $uv-image-error-top; |
| | | left: $uv-image-error-left; |
| | | width: $uv-image-error-width; |
| | | height: $uv-image-error-hight; |
| | | @include flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background-color: $uv-image-error-background-color; |
| | | color: $uv-image-error-color; |
| | | font-size: $uv-image-error-font-size; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-image", |
| | | "displayName": "uv-image 图片 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.16", |
| | | "description": "uv-image 此组件为uni-app的image组件的加强版,在继承了原有功能外,增加observer懒加载功能,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。", |
| | | "keywords": [ |
| | | "uv-image", |
| | | "uvui", |
| | | "uv-ui", |
| | | "image", |
| | | "图片" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-transition", |
| | | "uv-icon" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Image 图片 |
| | | |
| | | > **组件名:uv-image** |
| | | |
| | | 此组件为`uni-app`的`image`组件的加强版,在继承了原有功能外,增加`observer`懒加载功能,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/image.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | |  |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.7(2023-11-07) |
| | | 1. 修复sticky属性不生效的BUG |
| | | ## 1.0.6(2023-09-01) |
| | | 1. 修复设置customNavHeight导致定位不准确的BUG |
| | | ## 1.0.5(2023-08-23) |
| | | 1. 修复ios端快速滑动+点击右侧导航会出现白屏的BUG |
| | | ## 1.0.4(2023-07-25) |
| | | 1. 修复全局设置成rpx存在的高度BUG |
| | | 2. 修复其他BUG |
| | | ## 1.0.3(2023-07-03) |
| | | 去除插槽判断,避免某些平台不显示的BUG |
| | | ## 1.0.2(2023-05-27) |
| | | 1. select事件修复 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-index-list 索引列表 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 列表锚点文本内容 |
| | | text: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 列表锚点文字颜色 |
| | | color: { |
| | | type: String, |
| | | default: '#606266' |
| | | }, |
| | | // 列表锚点文字大小,单位默认px |
| | | size: { |
| | | type: [String, Number], |
| | | default: 14 |
| | | }, |
| | | // 列表锚点背景颜色 |
| | | bgColor: { |
| | | type: String, |
| | | default: '#dedede' |
| | | }, |
| | | // 列表锚点高度,单位默认px |
| | | height: { |
| | | type: [String, Number], |
| | | default: 32 |
| | | }, |
| | | ...uni.$uv?.props?.indexAnchor |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <header> |
| | | <!-- #endif --> |
| | | <view |
| | | :class="['uv-index-anchor',{'uv-index-anchor-sticky': parentData.sticky}]" |
| | | :ref="`uv-index-anchor-${text}`" |
| | | :style="{ |
| | | height: $uv.addUnit(height), |
| | | backgroundColor: bgColor |
| | | }" |
| | | > |
| | | <text |
| | | class="uv-index-anchor__text" |
| | | :style="{ |
| | | fontSize: $uv.addUnit(size), |
| | | color: color |
| | | }" |
| | | >{{ text }}</text> |
| | | </view> |
| | | <!-- #ifdef APP-NVUE --> |
| | | </header> |
| | | <!-- #endif --> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | // #ifdef APP-NVUE |
| | | const dom = uni.requireNativePlugin('dom') |
| | | // #endif |
| | | /** |
| | | * IndexAnchor 列表锚点 |
| | | * @description |
| | | * @tutorial https://www.uvui.cn/components/indexList.html |
| | | * @property {String | Number} text 列表锚点文本内容 |
| | | * @property {String} color 列表锚点文字颜色 ( 默认 '#606266' ) |
| | | * @property {String | Number} size 列表锚点文字大小,单位默认px ( 默认 14 ) |
| | | * @property {String} bgColor 列表锚点背景颜色 ( 默认 '#dedede' ) |
| | | * @property {String | Number} height 列表锚点高度,单位默认px ( 默认 32 ) |
| | | * @example <uv-index-anchor :text="indexList[index]"></uv-index-anchor> |
| | | */ |
| | | export default { |
| | | name: 'uv-index-anchor', |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | parentData: { |
| | | sticky: true |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 此处会活动父组件实例,并赋值给实例的parent属性 |
| | | const indexList = this.$uv.$parent.call(this, 'uv-index-list') |
| | | if (!indexList) { |
| | | return this.$uv.error('uv-index-anchor必须要搭配uv-index-list组件使用') |
| | | } |
| | | this.parentData.sticky = indexList.sticky; |
| | | // 将当前实例放入到uv-index-list中 |
| | | indexList.anchors.push(this) |
| | | const indexListItem = this.$uv.$parent.call(this, 'uv-index-item') |
| | | // #ifndef APP-NVUE |
| | | // 只有在非nvue下,uv-index-anchor才是嵌套在uv-index-item中的 |
| | | if (!indexListItem) { |
| | | return this.$uv.error('uv-index-anchor必须要搭配uv-index-item组件使用') |
| | | } |
| | | // 设置uv-index-item的id为anchor的text标识符,因为非nvue下滚动列表需要依赖scroll-view滚动到元素的特性 |
| | | indexListItem.id = this.text.charCodeAt(0) |
| | | // #endif |
| | | } |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | |
| | | .uv-index-anchor { |
| | | @include flex; |
| | | align-items: center; |
| | | padding-left: 15px; |
| | | z-index: 1; |
| | | &-sticky { |
| | | position: sticky; |
| | | top: 0; |
| | | } |
| | | &__text { |
| | | @include flex; |
| | | align-items: center; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <cell ref="uv-index-item"> |
| | | <!-- #endif --> |
| | | <view |
| | | class="uv-index-item" |
| | | :id="`uv-index-item-${id}`" |
| | | :class="[`uv-index-item-${id}`]" |
| | | > |
| | | <slot /> |
| | | </view> |
| | | <!-- #ifdef APP-NVUE --> |
| | | </cell> |
| | | <!-- #endif --> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | // #ifdef APP-NVUE |
| | | // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度 |
| | | const dom = uni.requireNativePlugin('dom') |
| | | // #endif |
| | | /** |
| | | * IndexItem |
| | | * @description |
| | | * @tutorial https://www.uvui.cn/components/indexList.html |
| | | * @property {String} |
| | | * @event {Function} |
| | | * @example |
| | | */ |
| | | export default { |
| | | name: 'uv-index-item', |
| | | mixins: [mpMixin, mixin], |
| | | data() { |
| | | return { |
| | | // 本组件到滚动条顶部的距离 |
| | | top: 0, |
| | | height: 0, |
| | | id: '' |
| | | } |
| | | }, |
| | | created() { |
| | | // 子组件uv-index-anchor的实例 |
| | | this.anchor = {} |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 此处会活动父组件实例,并赋值给实例的parent属性 |
| | | this.getParentData('uv-index-list') |
| | | if (!this.parent) { |
| | | return this.$uv.error('uv-index-item必须要搭配uv-index-list组件使用') |
| | | } |
| | | this.$uv.sleep().then(() =>{ |
| | | this.getIndexItemRect().then(size => { |
| | | // 由于对象的引用特性,此处会同时生效到父组件的children数组的本实例的top属性中,供父组件判断读取 |
| | | this.top = Math.ceil(size.top) |
| | | this.height = Math.ceil(size.height) |
| | | }) |
| | | }) |
| | | }, |
| | | getIndexItemRect() { |
| | | return new Promise(resolve => { |
| | | // #ifndef APP-NVUE |
| | | this.$uvGetRect('.uv-index-item').then(size => { |
| | | resolve(size) |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifdef APP-NVUE |
| | | const ref = this.$refs['uv-index-item'] |
| | | dom.getComponentRect(ref, res => { |
| | | resolve(res.size) |
| | | }) |
| | | // #endif |
| | | }) |
| | | } |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | </style> |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 右边锚点非激活的颜色 |
| | | inactiveColor: { |
| | | type: String, |
| | | default: '#606266' |
| | | }, |
| | | // 右边锚点激活的颜色 |
| | | activeColor: { |
| | | type: String, |
| | | default: '#5677fc' |
| | | }, |
| | | // 索引字符列表,数组形式 |
| | | indexList: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | // 是否开启锚点自动吸顶 |
| | | sticky: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 自定义导航栏的高度 |
| | | customNavHeight: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | ...uni.$uv?.props?.indexList |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-index-list"> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <list |
| | | :scrollTop="scrollTop" |
| | | enable-back-to-top |
| | | :offset-accuracy="1" |
| | | :style="{ |
| | | maxHeight: $uv.addUnit(scrollViewHeight,'px') |
| | | }" |
| | | @scroll="scrollHandler" |
| | | ref="uvList" |
| | | > |
| | | <cell ref="header"> |
| | | <slot name="header" /> |
| | | </cell> |
| | | <slot /> |
| | | <cell> |
| | | <slot name="footer" /> |
| | | </cell> |
| | | </list> |
| | | <!-- #endif --> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <scroll-view |
| | | :scrollTop="scrollTop" |
| | | :scrollIntoView="scrollIntoView" |
| | | :offset-accuracy="1" |
| | | :style="{ |
| | | maxHeight: $uv.addUnit(scrollViewHeight,'px') |
| | | }" |
| | | scroll-y |
| | | @scroll="scrollHandler" |
| | | ref="uvList" |
| | | > |
| | | <view> |
| | | <slot name="header" /> |
| | | </view> |
| | | <slot /> |
| | | <view> |
| | | <slot name="footer" /> |
| | | </view> |
| | | </scroll-view> |
| | | <!-- #endif --> |
| | | <view |
| | | class="uv-index-list__letter" |
| | | ref="uv-index-list__letter" |
| | | :style="{ top: $uv.addUnit(letterInfo.top || 100 ,'px') }" |
| | | @touchstart="touchStart" |
| | | @touchmove.stop.prevent="touchMove" |
| | | @touchend.stop.prevent="touchEnd" |
| | | @touchcancel.stop.prevent="touchEnd" |
| | | > |
| | | <view |
| | | class="uv-index-list__letter__item" |
| | | v-for="(item, index) in uIndexList" |
| | | :key="index" |
| | | :style="{ |
| | | backgroundColor: activeIndex === index ? activeColor : 'transparent' |
| | | }" |
| | | > |
| | | <text |
| | | class="uv-index-list__letter__item__index" |
| | | :style="{color: activeIndex === index ? '#fff' : inactiveColor}" |
| | | >{{ item }}</text> |
| | | </view> |
| | | </view> |
| | | <uv-transition |
| | | mode="fade" |
| | | :show="touching" |
| | | :customStyle="{ |
| | | position: 'fixed', |
| | | right: '40px', |
| | | top: $uv.addUnit(indicatorTop,'px'), |
| | | zIndex: 2 |
| | | }" |
| | | > |
| | | <view class="uv-index-list__indicator__box"> |
| | | <view |
| | | class="uv-index-list__indicator" |
| | | :class="['uv-index-list__indicator--show']" |
| | | :style="{ |
| | | height: $uv.addUnit(indicatorHeight,'px'), |
| | | width: $uv.addUnit(indicatorHeight,'px') |
| | | }" |
| | | > |
| | | <text class="uv-index-list__indicator__text">{{ uIndexList[activeIndex] }}</text> |
| | | </view> |
| | | </view> |
| | | </uv-transition> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | const indexList = () => { |
| | | const indexList = []; |
| | | const charCodeOfA = 'A'.charCodeAt(0); |
| | | for (let i = 0; i < 26; i++) { |
| | | indexList.push(String.fromCharCode(charCodeOfA + i)); |
| | | } |
| | | return indexList; |
| | | } |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | // #ifdef APP-NVUE |
| | | // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度 |
| | | const dom = uni.requireNativePlugin('dom') |
| | | // #endif |
| | | /** |
| | | * IndexList 索引列表 |
| | | * @description 通过折叠面板收纳内容区域 |
| | | * @tutorial https://www.uvui.cn/components/indexList.html |
| | | * @property {String} inactiveColor 右边锚点非激活的颜色 ( 默认 '#606266' ) |
| | | * @property {String} activeColor 右边锚点激活的颜色 ( 默认 '#5677fc' ) |
| | | * @property {Array} indexList 索引字符列表,数组形式 |
| | | * @property {Boolean} sticky 是否开启锚点自动吸顶 ( 默认 true ) |
| | | * @property {String | Number} customNavHeight 自定义导航栏的高度 ( 默认 0 ) |
| | | * */ |
| | | export default { |
| | | name: 'uv-index-list', |
| | | mixins: [mpMixin, mixin, props], |
| | | // #ifdef MP-WEIXIN |
| | | // 将自定义节点设置成虚拟的,更加接近Vue组件的表现,能更好的使用flex属性 |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | // #endif |
| | | data() { |
| | | return { |
| | | // 当前正在被选中的字母索引 |
| | | activeIndex: -1, |
| | | touchmoveIndex: 1, |
| | | // 索引字母的信息 |
| | | letterInfo: { |
| | | height: 0, |
| | | itemHeight: 0, |
| | | top: 0 |
| | | }, |
| | | // 设置字母指示器的高度,后面为了让指示器跟随字母,并将尖角部分指向字母的中部,需要依赖此值 |
| | | indicatorHeight: 50, |
| | | // 字母放大指示器的top值,为了让其指向当前激活的字母 |
| | | // indicatorTop: 0 |
| | | // 当前是否正在被触摸状态 |
| | | touching: false, |
| | | // 滚动条顶部top值 |
| | | scrollTop: 0, |
| | | // scroll-view的高度 |
| | | scrollViewHeight: 0, |
| | | // 系统信息 |
| | | sys: '', |
| | | scrolling: false, |
| | | scrollIntoView: '', |
| | | hasHeight: 0, |
| | | timer: 0, |
| | | disTap: false |
| | | } |
| | | }, |
| | | computed: { |
| | | // 如果有传入外部的indexList锚点数组则使用,否则使用内部生成A-Z字母 |
| | | uIndexList() { |
| | | return this.indexList.length ? this.indexList : indexList() |
| | | }, |
| | | // 字母放大指示器的top值,为了让其指向当前激活的字母 |
| | | indicatorTop() { |
| | | const { |
| | | top, |
| | | itemHeight |
| | | } = this.letterInfo |
| | | return Math.floor(top + itemHeight * this.activeIndex + itemHeight / 2 - this.indicatorHeight / 2) - 8 |
| | | } |
| | | }, |
| | | watch: { |
| | | // 监听字母索引的变化,重新设置尺寸 |
| | | uIndexList: { |
| | | immediate: true, |
| | | handler() { |
| | | this.$uv.sleep().then(() => { |
| | | this.setIndexListLetterInfo() |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.sys = this.$uv.sys() |
| | | this.children = [] |
| | | this.anchors = [] |
| | | this.init() |
| | | }, |
| | | mounted() { |
| | | this.setIndexListLetterInfo() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 设置列表的高度为整个屏幕的高度 |
| | | //减去this.customNavHeight,并将this.scrollViewHeight设置为maxHeight |
| | | //解决当uv-index-list组件放在tabbar页面时,scroll-view内容较少时,还能滚动 |
| | | this.scrollViewHeight = this.sys.windowHeight - this.$uv.getPx(this.customNavHeight) |
| | | }, |
| | | // 索引列表被触摸 |
| | | touchStart(e) { |
| | | // 获取触摸点信息 |
| | | const touchStart = e.changedTouches[0] |
| | | if (!touchStart || this.disTap) return |
| | | this.touching = true |
| | | const { |
| | | pageY |
| | | } = touchStart |
| | | // 根据当前触摸点的坐标,获取当前触摸的为第几个字母 |
| | | const currentIndex = this.getIndexListLetter(pageY) |
| | | this.setValueForTouch(currentIndex) |
| | | }, |
| | | // 索引字母列表被触摸滑动中 |
| | | touchMove(e) { |
| | | // 获取触摸点信息 |
| | | let touchMove = e.changedTouches[0] |
| | | if (!touchMove || this.disTap) return; |
| | | |
| | | // 滑动结束后迅速开始第二次滑动时候 touching 为 false 造成不显示 indicator 问题 |
| | | if (!this.touching) { |
| | | this.touching = true |
| | | } |
| | | const { |
| | | pageY |
| | | } = touchMove |
| | | const currentIndex = this.getIndexListLetter(pageY) |
| | | this.setValueForTouch(currentIndex) |
| | | }, |
| | | // 触摸结束 |
| | | touchEnd(e) { |
| | | // 延时一定时间后再隐藏指示器,为了让用户看的更直观,同时也是为了消除快速切换uv-transition的show带来的影响 |
| | | this.$uv.sleep(300).then(() => { |
| | | this.touching = false |
| | | }) |
| | | this.$emit('select',this.activeIndex); |
| | | }, |
| | | // 获取索引列表的尺寸以及单个字符的尺寸信息 |
| | | getIndexListLetterRect() { |
| | | return new Promise(resolve => { |
| | | // 延时一定时间,以获取dom尺寸 |
| | | // #ifndef APP-NVUE |
| | | this.$uvGetRect('.uv-index-list__letter').then(size => { |
| | | resolve(size) |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifdef APP-NVUE |
| | | const ref = this.$refs['uv-index-list__letter'] |
| | | dom.getComponentRect(ref, res => { |
| | | resolve(res.size) |
| | | }) |
| | | // #endif |
| | | }) |
| | | }, |
| | | // 设置indexList索引的尺寸信息 |
| | | setIndexListLetterInfo() { |
| | | this.getIndexListLetterRect().then(size => { |
| | | const { |
| | | top, |
| | | height |
| | | } = size |
| | | const windowHeight = this.$uv.sys().windowHeight; |
| | | let customNavHeight = 0 |
| | | // 消除各端导航栏非原生和原生导致的差异,让索引列表字母对屏幕垂直居中 |
| | | if (this.customNavHeight == 0) { |
| | | // #ifdef H5 |
| | | customNavHeight = this.$uv.sys().windowTop |
| | | // #endif |
| | | // #ifndef H5 |
| | | // 在非H5中,为原生导航栏,其高度不算在windowHeight内,这里设置为负值,后面相加时变成减去其高度的一半 |
| | | customNavHeight = 0 |
| | | // #endif |
| | | } else { |
| | | customNavHeight = this.$uv.getPx(this.customNavHeight) |
| | | } |
| | | this.letterInfo = { |
| | | height, |
| | | // 为了让字母列表对屏幕绝对居中,让其对导航栏进行修正,也即往上偏移导航栏的一半高度 |
| | | top: (windowHeight - height) / 2 + customNavHeight / 2, |
| | | itemHeight: Math.floor(height / this.uIndexList.length) |
| | | } |
| | | }) |
| | | }, |
| | | // 获取当前被触摸的索引字母 |
| | | getIndexListLetter(pageY) { |
| | | const { |
| | | top, |
| | | height, |
| | | itemHeight |
| | | } = this.letterInfo |
| | | // 对H5的pageY进行修正,这是由于uni-app自作多情在H5中将触摸点的坐标跟H5的导航栏结合导致的问题 |
| | | // #ifdef H5 |
| | | pageY += this.$uv.sys().windowTop |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | pageY += top |
| | | // #endif |
| | | // 对第一和最后一个字母做边界处理,因为用户可能在字母列表上触摸到两端的尽头后依然继续滑动 |
| | | if (pageY < top) { |
| | | return 0 |
| | | } else if (pageY >= top + height) { |
| | | // 如果超出了,取最后一个字母 |
| | | return this.uIndexList.length - 1 |
| | | } else { |
| | | // 将触摸点的Y轴偏移值,减去索引字母的top值,除以每个字母的高度,即可得到当前触摸点落在哪个字母上 |
| | | return Math.floor((pageY - top) / itemHeight); |
| | | } |
| | | }, |
| | | // 设置各项由触摸而导致变化的值 |
| | | setValueForTouch(currentIndex) { |
| | | // 如果偏移量太小,前后得出的会是同一个索引字母,为了防抖,进行返回 |
| | | if (currentIndex === this.activeIndex) return |
| | | this.activeIndex = currentIndex |
| | | // #ifndef APP-NVUE || MP-WEIXIN |
| | | // 在非nvue中,由于anchor和item都在uv-index-item中,所以需要对index-item进行偏移 |
| | | this.scrollIntoView = `uv-index-item-${this.uIndexList[currentIndex].charCodeAt(0)}` |
| | | // #endif |
| | | // #ifdef MP-WEIXIN |
| | | // 微信小程序下,scroll-view的scroll-into-view属性无法对slot中的内容的id生效,只能通过设置scrollTop的形式去移动滚动条 |
| | | this.scrollTop = this.children[currentIndex].top |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | // 在nvue中,由于cell和header为同级元素,所以实际是需要对header(anchor)进行偏移 |
| | | const anchor = `uv-index-anchor-${this.uIndexList[currentIndex]}` |
| | | dom.scrollToElement(this.anchors[currentIndex].$refs[anchor], { |
| | | offset: 0, |
| | | animated: false |
| | | }) |
| | | // #endif |
| | | }, |
| | | getHeaderRect() { |
| | | // 获取header slot的高度,因为list组件中获取元素的尺寸是没有top值的 |
| | | return new Promise(resolve => { |
| | | dom.getComponentRect(this.$refs.header, res => { |
| | | resolve(res.size) |
| | | }) |
| | | }) |
| | | }, |
| | | // scroll-view的滚动事件 |
| | | async scrollHandler(e) { |
| | | if (this.touching || this.scrolling) return |
| | | // 每过一定时间取样一次,减少资源损耗以及可能带来的卡顿 |
| | | this.scrolling = true |
| | | this.disTap = true; |
| | | clearTimeout(this.timer); |
| | | this.timer = setTimeout(()=>{ |
| | | this.disTap = false; |
| | | },200) |
| | | this.$uv.sleep(30).then(() => { |
| | | this.scrolling = false |
| | | }) |
| | | let scrollTop = 0 |
| | | const len = this.children.length |
| | | let children = this.children |
| | | const anchors = this.anchors |
| | | const customNavHeight = this.$uv.getPx(this.customNavHeight); |
| | | // #ifdef APP-NVUE |
| | | // nvue下获取的滚动条偏移为负数,需要转为正数 |
| | | scrollTop = Math.abs(e.contentOffset.y) |
| | | // 获取header slot的尺寸信息 |
| | | const header = await this.getHeaderRect() |
| | | // item的top值,在nvue下,模拟出的anchor的top,类似非nvue下的index-item的top |
| | | let top = header.height |
| | | // 由于list组件无法获取cell的top值,这里通过header slot和各个item之间的height,模拟出类似非nvue下的位置信息 |
| | | children = this.children.map((item, index) => { |
| | | if(item.height>0) this.hasHeight = item.height; |
| | | item.height = item.height>0?item.height:this.hasHeight; |
| | | const child = { |
| | | height: item.height, |
| | | top |
| | | } |
| | | // 进行累加,给下一个item提供计算依据 |
| | | top += item.height + anchors[index].height |
| | | return child |
| | | }) |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | // 非nvue通过detail获取滚动条位移 |
| | | scrollTop = e.detail.scrollTop |
| | | // #endif |
| | | for (let i = 0; i < len; i++) { |
| | | const item = children[i], |
| | | nextItem = children[i + 1] |
| | | // 如果滚动条高度小于第一个item的top值,此时无需设置任意字母为高亮 |
| | | if (scrollTop + customNavHeight <= children[0].top || scrollTop >= children[len - 1].top + children[len - |
| | | 1].height) { |
| | | this.activeIndex = -1 |
| | | break |
| | | } else if (!nextItem) { |
| | | // 当不存在下一个item时,意味着历遍到了最后一个 |
| | | this.activeIndex = len - 1 |
| | | break |
| | | } else if (scrollTop + customNavHeight > item.top && scrollTop + customNavHeight < nextItem.top) { |
| | | this.activeIndex = i |
| | | break |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | .uv-index-list { |
| | | |
| | | &__letter { |
| | | position: fixed; |
| | | right: 0; |
| | | text-align: center; |
| | | z-index: 3; |
| | | padding: 0 6px; |
| | | |
| | | &__item { |
| | | width: 16px; |
| | | height: 16px; |
| | | border-radius: 100px; |
| | | margin: 1px 0; |
| | | @include flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | &--active { |
| | | background-color: $uv-primary; |
| | | } |
| | | |
| | | &__index { |
| | | font-size: 12px; |
| | | text-align: center; |
| | | line-height: 12px; |
| | | } |
| | | } |
| | | } |
| | | &__indicator__box { |
| | | width: 65px; |
| | | height: 65px; |
| | | padding: 6px; |
| | | transform: rotate(-45deg); |
| | | } |
| | | &__indicator { |
| | | width: 50px; |
| | | height: 50px; |
| | | border-radius: 100px 100px 0 100px; |
| | | text-align: center; |
| | | color: #ffffff; |
| | | background-color: #c9c9c9; |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | |
| | | &__text { |
| | | font-size: 28px; |
| | | line-height: 28px; |
| | | font-weight: bold; |
| | | color: #fff; |
| | | transform: rotate(45deg); |
| | | text-align: center; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-index-list", |
| | | "displayName": "uv-index-list 索引列表 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.7", |
| | | "description": "该组件用于展示索引列表,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景。类似于微信通讯录页面", |
| | | "keywords": [ |
| | | "uv-index-list", |
| | | "uvui", |
| | | "uv-ui", |
| | | "index-list", |
| | | "索引列表" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-transition" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## IndexList 索引列表 |
| | | |
| | | > **组件名:uv-index-list** |
| | | |
| | | 用于展示索引列表,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景,类似于微信通讯录页面。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/indexList.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.13(2023-12-06) |
| | | 1. 优化 |
| | | ## 1.0.12(2023-12-06) |
| | | 1. 阻止事件冒泡问题 |
| | | ## 1.0.11(2023-11-10) |
| | | 1. 调整清除按钮样式的marginLeft,避免微信上多数情况触发不了的BUG |
| | | ## 1.0.10(2023-10-07) |
| | | 1. 修复搜狗输入法下存在不可清空的情况 |
| | | ## 1.0.9(2023-09-14) |
| | | 1. 修复H5等情况设置禁用或可读情况下,点击事件无效的问题 |
| | | ## 1.0.8(2023-08-22) |
| | | 1. 修复无法@keyboardheightchange无法获取键盘高度的BUG |
| | | ## 1.0.7(2023-08-18) |
| | | 1. 修复ios端不能输入的BUG |
| | | ## 1.0.6(2023-08-05) |
| | | 1. 修复在vue2模式下,v-model设置为0时不生效的BUG |
| | | ## 1.0.5(2023-07-18) |
| | | 1. 修复在微信小程序端清除内容存在不能清除的BUG |
| | | ## 1.0.4(2023-07-13) |
| | | 1. 修复value/v-model更改不生效的BUG |
| | | ## 1.0.3(2023-07-03) |
| | | 去除插槽判断,避免某些平台不显示的BUG |
| | | ## 1.0.2(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.1(2023-05-12) |
| | | 1. 修复vue3双向绑定的BUG |
| | | ## 1.0.0(2023-05-10) |
| | | uv-input 输入框 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // 输入框类型 |
| | | // number-数字输入键盘,app-vue下可以输入浮点数,app-nvue和小程序平台下只能输入整数 |
| | | // idcard-身份证输入键盘,微信、支付宝、百度、QQ小程序 |
| | | // digit-带小数点的数字键盘,App的nvue页面、微信、支付宝、百度、头条、QQ小程序 |
| | | // text-文本输入键盘 |
| | | type: { |
| | | type: String, |
| | | default: 'text' |
| | | }, |
| | | // 是否禁用输入框 |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 禁用状态时的背景色 |
| | | disabledColor: { |
| | | type: String, |
| | | default: '#f5f7fa' |
| | | }, |
| | | // 是否显示清除控件 |
| | | clearable: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否密码类型 |
| | | password: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 最大输入长度,设置为 -1 的时候不限制最大长度 |
| | | maxlength: { |
| | | type: [String, Number], |
| | | default: -1 |
| | | }, |
| | | // 输入框为空时的占位符 |
| | | placeholder: { |
| | | type: String, |
| | | default: null |
| | | }, |
| | | // 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ |
| | | placeholderClass: { |
| | | type: String, |
| | | default: 'input-placeholder' |
| | | }, |
| | | // 指定placeholder的样式 |
| | | placeholderStyle: { |
| | | type: [String, Object], |
| | | default: 'color: #c0c4cc' |
| | | }, |
| | | // 设置右下角按钮的文字,有效值:send|search|next|go|done,兼容性详见uni-app文档 |
| | | // https://uniapp.dcloud.io/component/input |
| | | // https://uniapp.dcloud.io/component/textarea |
| | | confirmType: { |
| | | type: String, |
| | | default: 'done' |
| | | }, |
| | | // 点击键盘右下角按钮时是否保持键盘不收起,H5无效 |
| | | confirmHold: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // focus时,点击页面的时候不收起键盘,微信小程序有效 |
| | | holdKeyboard: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 自动获取焦点 |
| | | // 在 H5 平台能否聚焦以及软键盘是否跟随弹出,取决于当前浏览器本身的实现。nvue 页面不支持,需使用组件的 focus()、blur() 方法控制焦点 |
| | | focus: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 键盘收起时,是否自动失去焦点,目前仅App3.0.0+有效 |
| | | autoBlur: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 指定focus时光标的位置 |
| | | cursor: { |
| | | type: [String, Number], |
| | | default: -1 |
| | | }, |
| | | // 输入框聚焦时底部与键盘的距离 |
| | | cursorSpacing: { |
| | | type: [String, Number], |
| | | default: 30 |
| | | }, |
| | | // 光标起始位置,自动聚集时有效,需与selection-end搭配使用 |
| | | selectionStart: { |
| | | type: [String, Number], |
| | | default: -1 |
| | | }, |
| | | // 光标结束位置,自动聚集时有效,需与selection-start搭配使用 |
| | | selectionEnd: { |
| | | type: [String, Number], |
| | | default: -1 |
| | | }, |
| | | // 键盘弹起时,是否自动上推页面 |
| | | adjustPosition: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 输入框内容对齐方式,可选值为:left|center|right |
| | | inputAlign: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | // 输入框字体的大小 |
| | | fontSize: { |
| | | type: [String, Number], |
| | | default: '14px' |
| | | }, |
| | | // 输入框字体颜色 |
| | | color: { |
| | | type: String, |
| | | default: '#303133' |
| | | }, |
| | | // 输入框前置图标 |
| | | prefixIcon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 前置图标样式,对象或字符串 |
| | | prefixIconStyle: { |
| | | type: [String, Object], |
| | | default: '' |
| | | }, |
| | | // 输入框后置图标 |
| | | suffixIcon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 后置图标样式,对象或字符串 |
| | | suffixIconStyle: { |
| | | type: [String, Object], |
| | | default: '' |
| | | }, |
| | | // 边框类型,surround-四周边框,bottom-底部边框,none-无边框 |
| | | border: { |
| | | type: String, |
| | | default: 'surround' |
| | | }, |
| | | // 是否只读,与disabled不同之处在于disabled会置灰组件,而readonly则不会 |
| | | readonly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 输入框形状,circle-圆形,square-方形 |
| | | shape: { |
| | | type: String, |
| | | default: 'square' |
| | | }, |
| | | // 用于处理或者过滤输入框内容的方法 |
| | | formatter: { |
| | | type: [Function, null], |
| | | default: null |
| | | }, |
| | | // 是否忽略组件内对文本合成系统事件的处理 |
| | | ignoreCompositionEvent: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | ...uni.$uv?.props?.input |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view class="uv-input" :class="inputClass" :style="[wrapperStyle]"> |
| | | <view class="uv-input__content"> |
| | | <view class="uv-input__content__prefix-icon"> |
| | | <slot name="prefix"> |
| | | <uv-icon |
| | | v-if="prefixIcon" |
| | | :name="prefixIcon" |
| | | size="18" |
| | | :customStyle="prefixIconStyle" |
| | | ></uv-icon> |
| | | </slot> |
| | | </view> |
| | | <view class="uv-input__content__field-wrapper" @click="clickHandler"> |
| | | <!-- 根据uni-app的input组件文档,H5和APP中只要声明了password参数(无论true还是false),type均失效,此时 |
| | | 为了防止type=number时,又存在password属性,type无效,此时需要设置password为undefined |
| | | --> |
| | | <input |
| | | class="uv-input__content__field-wrapper__field" |
| | | :style="[inputStyle]" |
| | | :type="type" |
| | | :focus="focus" |
| | | :cursor="cursor" |
| | | :value="innerValue" |
| | | :auto-blur="autoBlur" |
| | | :disabled="disabled || readonly" |
| | | :maxlength="maxlength" |
| | | :placeholder="placeholder" |
| | | :placeholder-style="placeholderStyle" |
| | | :placeholder-class="placeholderClass" |
| | | :confirm-type="confirmType" |
| | | :confirm-hold="confirmHold" |
| | | :hold-keyboard="holdKeyboard" |
| | | :cursor-spacing="cursorSpacing" |
| | | :adjust-position="adjustPosition" |
| | | :selection-end="selectionEnd" |
| | | :selection-start="selectionStart" |
| | | :password="password || type === 'password' || undefined" |
| | | :ignoreCompositionEvent="ignoreCompositionEvent" |
| | | @input="onInput" |
| | | @blur="onBlur" |
| | | @focus="onFocus" |
| | | @confirm="onConfirm" |
| | | @keyboardheightchange="onkeyboardheightchange" |
| | | /> |
| | | </view> |
| | | <view |
| | | class="uv-input__content__clear" |
| | | v-if="isShowClear" |
| | | @tap="onClear" |
| | | > |
| | | <uv-icon |
| | | name="close" |
| | | size="11" |
| | | color="#ffffff" |
| | | customStyle="line-height: 12px" |
| | | ></uv-icon> |
| | | </view> |
| | | <view class="uv-input__content__subfix-icon"> |
| | | <slot name="suffix"> |
| | | <uv-icon |
| | | v-if="suffixIcon" |
| | | :name="suffixIcon" |
| | | size="18" |
| | | :customStyle="suffixIconStyle" |
| | | ></uv-icon> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from "./props.js"; |
| | | /** |
| | | * Input 输入框 |
| | | * @description 此组件为一个输入框,默认没有边框和样式,是专门为配合表单组件uv-form而设计的,利用它可以快速实现表单验证,输入内容,下拉选择等功能。 |
| | | * @tutorial https://www.uvui.cn/components/input.html |
| | | * @property {String | Number} value 输入的值 |
| | | * @property {String} type 输入框类型,见上方说明 ( 默认 'text' ) |
| | | * @property {Boolean} fixed 如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true,兼容性:微信小程序、百度小程序、字节跳动小程序、QQ小程序 ( 默认 false ) |
| | | * @property {Boolean} disabled 是否禁用输入框 ( 默认 false ) |
| | | * @property {String} disabledColor 禁用状态时的背景色( 默认 '#f5f7fa' ) |
| | | * @property {Boolean} clearable 是否显示清除控件 ( 默认 false ) |
| | | * @property {Boolean} password 是否密码类型 ( 默认 false ) |
| | | * @property {String | Number} maxlength 最大输入长度,设置为 -1 的时候不限制最大长度 ( 默认 -1 ) |
| | | * @property {String} placeholder 输入框为空时的占位符 |
| | | * @property {String} placeholderClass 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ ( 默认 'input-placeholder' ) |
| | | * @property {String | Object} placeholderStyle 指定placeholder的样式,字符串/对象形式,如"color: red;" |
| | | * @property {Boolean} showWordLimit 是否显示输入字数统计,只在 type ="text"或type ="textarea"时有效 ( 默认 false ) |
| | | * @property {String} confirmType 设置右下角按钮的文字,兼容性详见uni-app文档 ( 默认 'done' ) |
| | | * @property {Boolean} confirmHold 点击键盘右下角按钮时是否保持键盘不收起,H5无效 ( 默认 false ) |
| | | * @property {Boolean} holdKeyboard focus时,点击页面的时候不收起键盘,微信小程序有效 ( 默认 false ) |
| | | * @property {Boolean} focus 自动获取焦点,在 H5 平台能否聚焦以及软键盘是否跟随弹出,取决于当前浏览器本身的实现。nvue 页面不支持,需使用组件的 focus()、blur() 方法控制焦点 ( 默认 false ) |
| | | * @property {Boolean} autoBlur 键盘收起时,是否自动失去焦点,目前仅App3.0.0+有效 ( 默认 false ) |
| | | * @property {Boolean} disableDefaultPadding 是否去掉 iOS 下的默认内边距,仅微信小程序,且type=textarea时有效 ( 默认 false ) |
| | | * @property {String | Number} cursor 指定focus时光标的位置( 默认 -1 ) |
| | | * @property {String | Number} cursorSpacing 输入框聚焦时底部与键盘的距离 ( 默认 30 ) |
| | | * @property {String | Number} selectionStart 光标起始位置,自动聚集时有效,需与selection-end搭配使用 ( 默认 -1 ) |
| | | * @property {String | Number} selectionEnd 光标结束位置,自动聚集时有效,需与selection-start搭配使用 ( 默认 -1 ) |
| | | * @property {Boolean} adjustPosition 键盘弹起时,是否自动上推页面 ( 默认 true ) |
| | | * @property {String} inputAlign 输入框内容对齐方式( 默认 'left' ) |
| | | * @property {String | Number} fontSize 输入框字体的大小 ( 默认 '15px' ) |
| | | * @property {String} color 输入框字体颜色 ( 默认 '#303133' ) |
| | | * @property {Function} formatter 内容式化函数 |
| | | * @property {String} prefixIcon 输入框前置图标 |
| | | * @property {String | Object} prefixIconStyle 前置图标样式,对象或字符串 |
| | | * @property {String} suffixIcon 输入框后置图标 |
| | | * @property {String | Object} suffixIconStyle 后置图标样式,对象或字符串 |
| | | * @property {String} border 边框类型,surround-四周边框,bottom-底部边框,none-无边框 ( 默认 'surround' ) |
| | | * @property {Boolean} readonly 是否只读,与disabled不同之处在于disabled会置灰组件,而readonly则不会 ( 默认 false ) |
| | | * @property {String} shape 输入框形状,circle-圆形,square-方形 ( 默认 'square' ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * @property {Boolean} ignoreCompositionEvent 是否忽略组件内对文本合成系统事件的处理。 |
| | | * @example <uv-input v-model="value" :password="true" suffix-icon="lock-fill" /> |
| | | */ |
| | | export default { |
| | | name: "uv-input", |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | // 输入框的值 |
| | | innerValue: "", |
| | | // 是否处于获得焦点状态 |
| | | focused: false, |
| | | // 过滤处理方法 |
| | | innerFormatter: value => value |
| | | } |
| | | }, |
| | | created() { |
| | | // #ifdef VUE2 |
| | | this.innerValue = this.value; |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | this.innerValue = this.modelValue; |
| | | // #endif |
| | | }, |
| | | watch: { |
| | | value(newVal){ |
| | | this.innerValue = newVal; |
| | | }, |
| | | modelValue(newVal){ |
| | | this.innerValue = newVal; |
| | | } |
| | | }, |
| | | computed: { |
| | | // 是否显示清除控件 |
| | | isShowClear() { |
| | | const { clearable, readonly, focused, innerValue } = this; |
| | | return !!clearable && !readonly && !!focused && innerValue !== ""; |
| | | }, |
| | | // 组件的类名 |
| | | inputClass() { |
| | | let classes = [], |
| | | { border, disabled, shape } = this; |
| | | border === "surround" && |
| | | (classes = classes.concat(["uv-border", "uv-input--radius"])); |
| | | classes.push(`uv-input--${shape}`); |
| | | border === "bottom" && |
| | | (classes = classes.concat([ |
| | | "uv-border-bottom", |
| | | "uv-input--no-radius", |
| | | ])); |
| | | return classes.join(" "); |
| | | }, |
| | | // 组件的样式 |
| | | wrapperStyle() { |
| | | const style = {}; |
| | | // 禁用状态下,被背景色加上对应的样式 |
| | | if (this.disabled) { |
| | | style.backgroundColor = this.disabledColor; |
| | | } |
| | | // 无边框时,去除内边距 |
| | | if (this.border === "none") { |
| | | style.padding = "0"; |
| | | } else { |
| | | // 由于uni-app的iOS开发者能力有限,导致需要分开写才有效 |
| | | style.paddingTop = "6px"; |
| | | style.paddingBottom = "6px"; |
| | | style.paddingLeft = "9px"; |
| | | style.paddingRight = "9px"; |
| | | } |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)); |
| | | }, |
| | | // 输入框的样式 |
| | | inputStyle() { |
| | | const style = { |
| | | color: this.color, |
| | | fontSize: this.$uv.addUnit(this.fontSize), |
| | | textAlign: this.inputAlign |
| | | }; |
| | | // #ifndef APP-NVUE |
| | | if(this.disabled || this.readonly) { |
| | | style['pointer-events'] = 'none'; |
| | | } |
| | | // #endif |
| | | return style; |
| | | } |
| | | }, |
| | | methods: { |
| | | // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用 |
| | | setFormatter(e) { |
| | | this.innerFormatter = e |
| | | }, |
| | | // 当键盘输入时,触发input事件 |
| | | onInput(e) { |
| | | let { value = "" } = e.detail || {}; |
| | | // 格式化过滤方法 |
| | | const formatter = this.formatter || this.innerFormatter |
| | | const formatValue = formatter(value) |
| | | // 为了避免props的单向数据流特性,需要先将innerValue值设置为当前值,再在$nextTick中重新赋予设置后的值才有效 |
| | | this.innerValue = value |
| | | this.$nextTick(() => { |
| | | this.innerValue = formatValue; |
| | | this.valueChange(); |
| | | }) |
| | | }, |
| | | // 输入框失去焦点时触发 |
| | | onBlur(event) { |
| | | this.$emit("blur", event.detail.value); |
| | | // H5端的blur会先于点击清除控件的点击click事件触发,导致focused |
| | | // 瞬间为false,从而隐藏了清除控件而无法被点击到 |
| | | this.$uv.sleep(100).then(() => { |
| | | this.focused = false; |
| | | }); |
| | | // 尝试调用uv-form的验证方法 |
| | | this.$uv.formValidate(this, "blur"); |
| | | }, |
| | | // 输入框聚焦时触发 |
| | | onFocus(event) { |
| | | this.focused = true; |
| | | this.$emit("focus"); |
| | | }, |
| | | // 点击完成按钮时触发 |
| | | onConfirm(event) { |
| | | this.$emit("confirm", this.innerValue); |
| | | }, |
| | | // 键盘高度发生变化的时候触发此事件 |
| | | // 兼容性:微信小程序2.7.0+、App 3.1.0+ |
| | | onkeyboardheightchange(e) { |
| | | this.$emit("keyboardheightchange",e); |
| | | }, |
| | | // 内容发生变化,进行处理 |
| | | valueChange() { |
| | | if(this.isClear) this.innerValue = ''; |
| | | const value = this.innerValue; |
| | | this.$nextTick(() => { |
| | | this.$emit("input", value); |
| | | this.$emit("update:modelValue", value); |
| | | this.$emit("change", value); |
| | | // 尝试调用uv-form的验证方法 |
| | | this.$uv.formValidate(this, "change"); |
| | | }); |
| | | }, |
| | | // 点击清除控件 |
| | | onClear() { |
| | | this.innerValue = ""; |
| | | this.isClear = true; |
| | | this.$uv.sleep(200).then(res=>{ |
| | | this.isClear = false; |
| | | }) |
| | | this.$nextTick(() => { |
| | | this.$emit("clear"); |
| | | this.valueChange(); |
| | | }); |
| | | }, |
| | | /** |
| | | * 在安卓nvue上,事件无法冒泡 |
| | | * 在某些时间,我们希望监听uv-from-item的点击事件,此时会导致点击uv-form-item内的uv-input后 |
| | | * 无法触发uv-form-item的点击事件,这里通过手动调用uv-form-item的方法进行触发 |
| | | */ |
| | | clickHandler() { |
| | | // #ifdef APP-NVUE |
| | | if (this.$uv.os() === "android") { |
| | | const formItem = this.$uv.$parent.call(this, "uv-form-item"); |
| | | if (formItem) { |
| | | formItem.clickHandler(); |
| | | } |
| | | } |
| | | // #endif |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-border: 1; |
| | | $show-border-surround: 1; |
| | | $show-border-bottom: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | .uv-input { |
| | | @include flex(row); |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | flex: 1; |
| | | &--radius, |
| | | &--square { |
| | | border-radius: 4px; |
| | | } |
| | | &--no-radius { |
| | | border-radius: 0; |
| | | } |
| | | &--circle { |
| | | border-radius: 100px; |
| | | } |
| | | &__content { |
| | | flex: 1; |
| | | @include flex(row); |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | &__field-wrapper { |
| | | position: relative; |
| | | @include flex(row); |
| | | margin: 0; |
| | | flex: 1; |
| | | &__field { |
| | | line-height: 26px; |
| | | text-align: left; |
| | | color: $uv-main-color; |
| | | height: 24px; |
| | | font-size: 15px; |
| | | flex: 1; |
| | | } |
| | | } |
| | | &__clear { |
| | | width: 20px; |
| | | height: 20px; |
| | | border-radius: 100px; |
| | | background-color: #c6c7cb; |
| | | @include flex(row); |
| | | align-items: center; |
| | | justify-content: center; |
| | | transform: scale(0.82); |
| | | margin-left: 15px; |
| | | } |
| | | &__subfix-icon { |
| | | margin-left: 4px; |
| | | } |
| | | &__prefix-icon { |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-input", |
| | | "displayName": "uv-input 输入框 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.13", |
| | | "description": "uv-input 该组件为一个输入框,默认没有边框和样式,是专门为配合表单组件uv-form而设计的,利用它可以快速实现表单验证,输入内容,下拉选择等功能。", |
| | | "keywords": [ |
| | | "uv-input", |
| | | "uvui", |
| | | "uv-ui", |
| | | "input", |
| | | "输入框" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-icon" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Input 输入框 |
| | | |
| | | > **组件名:uv-input** |
| | | |
| | | 此组件为一个输入框,默认没有边框和样式,是专门为配合表单组件uv-form而设计的,利用它可以快速实现表单验证,输入内容,下拉选择等功能。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/input.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.5(2023-10-12) |
| | | 1. 增加disKeys参数,mode = "car"下,被禁用的键,如:['I','O'] |
| | | 2. 增加customabc参数,mode = "car"下,是否启用自定义中英文切换内容模式,为了兼容支付宝等小程序不兼容嵌套插槽,导致同时显示自定义内容和原始内容 |
| | | 3. 增加ref方法changeCarMode,mode = "car"下, 调用此方法可以切换中英文模式 |
| | | 4. 增加@changeCarInputMode,mode = "car"下,调用此方法可以进行切换中英文 |
| | | 5. 增加插槽abc,mode = "car"下,自定义中英文切换内容,具体参考[车牌键盘自定义中英文切换及禁用键等设置](https://www.uvui.cn/components/keyboard.html#车牌键盘自定义中英文切换及禁用键等设置) |
| | | ## 1.0.4(2023-09-04) |
| | | 1. 优化,修改文件名称 |
| | | ## 1.0.3(2023-09-04) |
| | | 1. 修复键盘change回调事件产生冲突的BUG |
| | | ## 1.0.2(2023-07-02) |
| | | uv-keyboard 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/keyboard.html |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-keyboard 键盘 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 是否打乱键盘按键的顺序 |
| | | random: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 输入一个中文后,是否自动切换到英文 |
| | | autoChange: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 被禁用的键 |
| | | disKeys: { |
| | | type: Array, |
| | | default: ()=>[] |
| | | }, |
| | | // 是否自定义abc |
| | | customabc: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-keyboard" |
| | | @touchmove.stop.prevent="noop" |
| | | > |
| | | <view |
| | | v-for="(group, i) in abc ? engKeyBoardList : areaList" |
| | | :key="i" |
| | | class="uv-keyboard__button" |
| | | :index="i" |
| | | :class="[i + 1 === 4 && 'uv-keyboard__button--center']" |
| | | > |
| | | <view |
| | | v-if="i === 3" |
| | | class="uv-keyboard__button__inner-wrapper" |
| | | > |
| | | <view |
| | | class="uv-keyboard__button__inner-wrapper__left" |
| | | hover-class="uv-hover-class" |
| | | :hover-stay-time="200" |
| | | @tap="changeCarInputMode" |
| | | > |
| | | <slot> |
| | | <template v-if="!customabc"> |
| | | <text |
| | | class="uv-keyboard__button__inner-wrapper__left__lang" |
| | | :class="[!abc && 'uv-keyboard__button__inner-wrapper__left__lang--active']" |
| | | >中</text> |
| | | <text class="uv-keyboard__button__inner-wrapper__left__line">/</text> |
| | | <text |
| | | class="uv-keyboard__button__inner-wrapper__left__lang" |
| | | :class="[abc && 'uv-keyboard__button__inner-wrapper__left__lang--active']" |
| | | >英</text> |
| | | </template> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | <view |
| | | class="uv-keyboard__button__inner-wrapper" |
| | | v-for="(item, j) in group" |
| | | :key="j" |
| | | > |
| | | <view |
| | | :class="['uv-keyboard__button__inner-wrapper__inner',{'uv-keyboard__button__inner-wrapper__inner--disabled': isDisabled(i,j)}]" |
| | | :hover-stay-time="200" |
| | | @tap="carInputClick(i, j)" |
| | | :hover-class="isDisabled(i,j)?'none':'uv-hover-class'" |
| | | > |
| | | <text class="uv-keyboard__button__inner-wrapper__inner__text">{{ item }}</text> |
| | | </view> |
| | | <view class="uv-keyboard__button__inner-wrapper__disabled--mask" v-if="isDisabled(i,j)"></view> |
| | | </view> |
| | | <view |
| | | v-if="i === 3" |
| | | @touchstart="backspaceClick" |
| | | @touchend="clearTimer" |
| | | class="uv-keyboard__button__inner-wrapper" |
| | | > |
| | | <view |
| | | class="uv-keyboard__button__inner-wrapper__right" |
| | | hover-class="uv-hover-class" |
| | | :hover-stay-time="200" |
| | | > |
| | | <uv-icon |
| | | size="28" |
| | | name="backspace" |
| | | color="#303133" |
| | | ></uv-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * keyboard 键盘组件 |
| | | * @description 此为uvui自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。 |
| | | * @tutorial https://www.uvui.cn/components/keyboard.html |
| | | * @property {Boolean} random 是否打乱键盘的顺序 |
| | | * @event {Function} change 点击键盘触发 |
| | | * @event {Function} backspace 点击退格键触发 |
| | | * @example <uv-keyboard ref="uKeyboard" mode="car" v-model="show"></uv-keyboard> |
| | | */ |
| | | export default { |
| | | name: "uv-keyboard", |
| | | mixins: [mpMixin, mixin, props], |
| | | emits: ['backspace','change','changeCarInputMode'], |
| | | data() { |
| | | return { |
| | | // 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称 |
| | | abc: false |
| | | }; |
| | | }, |
| | | computed: { |
| | | areaList() { |
| | | let data = [ |
| | | '京', |
| | | '沪', |
| | | '粤', |
| | | '津', |
| | | '冀', |
| | | '豫', |
| | | '云', |
| | | '辽', |
| | | '黑', |
| | | '湘', |
| | | '皖', |
| | | '鲁', |
| | | '苏', |
| | | '浙', |
| | | '赣', |
| | | '鄂', |
| | | '桂', |
| | | '甘', |
| | | '晋', |
| | | '陕', |
| | | '蒙', |
| | | '吉', |
| | | '闽', |
| | | '贵', |
| | | '渝', |
| | | '川', |
| | | '青', |
| | | '琼', |
| | | '宁', |
| | | '挂', |
| | | '藏', |
| | | '港', |
| | | '澳', |
| | | '新', |
| | | '使', |
| | | '学' |
| | | ]; |
| | | let tmp = []; |
| | | // 打乱顺序 |
| | | if (this.random) data = this.$uv.randomArray(data); |
| | | // 切割成二维数组 |
| | | tmp[0] = data.slice(0, 10); |
| | | tmp[1] = data.slice(10, 20); |
| | | tmp[2] = data.slice(20, 30); |
| | | tmp[3] = data.slice(30, 36); |
| | | return tmp; |
| | | }, |
| | | engKeyBoardList() { |
| | | let data = [ |
| | | 1, |
| | | 2, |
| | | 3, |
| | | 4, |
| | | 5, |
| | | 6, |
| | | 7, |
| | | 8, |
| | | 9, |
| | | 0, |
| | | 'Q', |
| | | 'W', |
| | | 'E', |
| | | 'R', |
| | | 'T', |
| | | 'Y', |
| | | 'U', |
| | | 'I', |
| | | 'O', |
| | | 'P', |
| | | 'A', |
| | | 'S', |
| | | 'D', |
| | | 'F', |
| | | 'G', |
| | | 'H', |
| | | 'J', |
| | | 'K', |
| | | 'L', |
| | | 'Z', |
| | | 'X', |
| | | 'C', |
| | | 'V', |
| | | 'B', |
| | | 'N', |
| | | 'M' |
| | | ]; |
| | | let tmp = []; |
| | | if (this.random) data = this.$uv.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; |
| | | }, |
| | | isDisabled(i,j) { |
| | | return (i,j)=>{ |
| | | let value = ''; |
| | | if (this.abc) value = this.engKeyBoardList[i][j]; |
| | | else value = this.areaList[i][j]; |
| | | return this.disKeys.indexOf(value) > -1; |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | // 点击键盘按钮 |
| | | carInputClick(i, j) { |
| | | if(this.isDisabled(i,j)) return; |
| | | let value = ''; |
| | | // 不同模式,获取不同数组的值 |
| | | if (this.abc) value = this.engKeyBoardList[i][j]; |
| | | else value = this.areaList[i][j]; |
| | | // 如果允许自动切换,则将中文状态切换为英文 |
| | | if (!this.abc && this.autoChange) this.$uv.sleep(200).then(() => this.abc = true) |
| | | this.$emit('change', value); |
| | | }, |
| | | // 修改汽车牌键盘的输入模式,中文|英文 |
| | | changeCarInputMode() { |
| | | this.abc = !this.abc; |
| | | this.$emit('changeCarInputMode',this.abc); |
| | | }, |
| | | // 点击退格键 |
| | | backspaceClick() { |
| | | this.$emit('backspace'); |
| | | clearInterval(this.timer); //再次清空定时器,防止重复注册定时器 |
| | | this.timer = null; |
| | | this.timer = setInterval(() => { |
| | | this.$emit('backspace'); |
| | | }, 250); |
| | | }, |
| | | clearTimer() { |
| | | clearInterval(this.timer); |
| | | this.timer = null; |
| | | }, |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-hover: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-car-keyboard-background-color: rgb(224, 228, 230) !default; |
| | | $uv-car-keyboard-padding:6px 0 6px !default; |
| | | $uv-car-keyboard-button-inner-width:64rpx !default; |
| | | $uv-car-keyboard-button-inner-background-color:#FFFFFF !default; |
| | | $uv-car-keyboard-button-height:80rpx !default; |
| | | $uv-car-keyboard-button-inner-box-shadow:0 1px 0px #999992 !default; |
| | | $uv-car-keyboard-button-border-radius:4px !default; |
| | | $uv-car-keyboard-button-inner-margin:8rpx 5rpx !default; |
| | | $uv-car-keyboard-button-text-font-size:16px !default; |
| | | $uv-car-keyboard-button-text-color:$uv-main-color !default; |
| | | $uv-car-keyboard-center-inner-margin: 0 4rpx !default; |
| | | $uv-car-keyboard-special-button-width:134rpx !default; |
| | | $uv-car-keyboard-lang-font-size:16px !default; |
| | | $uv-car-keyboard-lang-color:$uv-main-color !default; |
| | | $uv-car-keyboard-active-color:$uv-primary !default; |
| | | $uv-car-keyboard-line-font-size:15px !default; |
| | | $uv-car-keyboard-line-color:$uv-main-color !default; |
| | | $uv-car-keyboard-line-margin:0 1px !default; |
| | | $uv-car-keyboard-uv-hover-class-background-color:#BBBCC6 !default; |
| | | |
| | | .uv-keyboard { |
| | | @include flex(column); |
| | | justify-content: space-around; |
| | | background-color: $uv-car-keyboard-background-color; |
| | | align-items: stretch; |
| | | padding: $uv-car-keyboard-padding; |
| | | |
| | | &__button { |
| | | @include flex; |
| | | justify-content: center; |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | /* #endif */ |
| | | |
| | | &__inner-wrapper { |
| | | position: relative; |
| | | box-shadow: $uv-car-keyboard-button-inner-box-shadow; |
| | | margin: $uv-car-keyboard-button-inner-margin; |
| | | border-radius: $uv-car-keyboard-button-border-radius; |
| | | |
| | | &__disabled--mask { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | bottom: 0; |
| | | right: 0; |
| | | width: $uv-car-keyboard-button-inner-width; |
| | | height: $uv-car-keyboard-button-height; |
| | | } |
| | | |
| | | &__inner { |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: $uv-car-keyboard-button-inner-width; |
| | | background-color: $uv-car-keyboard-button-inner-background-color; |
| | | height: $uv-car-keyboard-button-height; |
| | | border-radius: $uv-car-keyboard-button-border-radius; |
| | | |
| | | &--disabled { |
| | | opacity: 0.5; |
| | | } |
| | | |
| | | &__text { |
| | | font-size: $uv-car-keyboard-button-text-font-size; |
| | | color: $uv-car-keyboard-button-text-color; |
| | | } |
| | | } |
| | | |
| | | &__left, |
| | | &__right { |
| | | border-radius: $uv-car-keyboard-button-border-radius; |
| | | width: $uv-car-keyboard-special-button-width; |
| | | height: $uv-car-keyboard-button-height; |
| | | background-color: $uv-car-keyboard-uv-hover-class-background-color; |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | box-shadow: $uv-car-keyboard-button-inner-box-shadow; |
| | | } |
| | | |
| | | &__left { |
| | | &__line { |
| | | font-size: $uv-car-keyboard-line-font-size; |
| | | color: $uv-car-keyboard-line-color; |
| | | margin: $uv-car-keyboard-line-margin; |
| | | } |
| | | |
| | | &__lang { |
| | | font-size: $uv-car-keyboard-lang-font-size; |
| | | color: $uv-car-keyboard-lang-color; |
| | | |
| | | &--active { |
| | | color: $uv-car-keyboard-active-color; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .uv-hover-class { |
| | | background-color: $uv-car-keyboard-uv-hover-class-background-color; |
| | | } |
| | | </style> |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 键盘的类型,number-数字键盘,card-身份证键盘 |
| | | mode: { |
| | | type: String, |
| | | default: 'number' |
| | | }, |
| | | // 是否显示键盘的"."符号 |
| | | dotDisabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否打乱键盘按键的顺序 |
| | | random: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-keyboard" |
| | | @touchmove.stop.prevent="noop" |
| | | > |
| | | <view |
| | | class="uv-keyboard__button-wrapper" |
| | | v-for="(item, index) in numList" |
| | | :key="index" |
| | | > |
| | | <view |
| | | class="uv-keyboard__button-wrapper__button" |
| | | :style="[itemStyle(index)]" |
| | | @tap="keyboardClick(item)" |
| | | hover-class="uv-hover-class" |
| | | :hover-stay-time="200" |
| | | > |
| | | <text class="uv-keyboard__button-wrapper__button__text">{{ item }}</text> |
| | | </view> |
| | | </view> |
| | | <view |
| | | class="uv-keyboard__button-wrapper" |
| | | > |
| | | <view |
| | | class="uv-keyboard__button-wrapper__button uv-keyboard__button-wrapper__button--gray" |
| | | hover-class="uv-hover-class" |
| | | :hover-stay-time="200" |
| | | @touchstart.stop="backspaceClick" |
| | | @touchend="clearTimer" |
| | | > |
| | | <uv-icon |
| | | name="backspace" |
| | | color="#303133" |
| | | size="28" |
| | | ></uv-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * keyboard 键盘组件 |
| | | * @description |
| | | * @tutorial |
| | | * @property {String} mode 键盘的类型,number-数字键盘,card-身份证键盘 |
| | | * @property {Boolean} dotDisabled 是否显示键盘的"."符号 |
| | | * @property {Boolean} random 是否打乱键盘按键的顺序 |
| | | * @event {Function} change 点击键盘触发 |
| | | * @event {Function} backspace 点击退格键触发 |
| | | * @example |
| | | */ |
| | | export default { |
| | | mixins: [mpMixin, mixin, props], |
| | | emits: ['backspace','change'], |
| | | data() { |
| | | return { |
| | | backspace: 'backspace', // 退格键内容 |
| | | dot: '.', // 点 |
| | | timer: null, // 长按多次删除的事件监听 |
| | | cardX: 'X' // 身份证的X符号 |
| | | }; |
| | | }, |
| | | computed: { |
| | | // 键盘需要显示的内容 |
| | | numList() { |
| | | let tmp = []; |
| | | if (this.dotDisabled && this.mode == 'number') { |
| | | if (!this.random) { |
| | | return [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; |
| | | } else { |
| | | return this.$uv.randomArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); |
| | | } |
| | | } else if (!this.dotDisabled && this.mode == 'number') { |
| | | if (!this.random) { |
| | | return [1, 2, 3, 4, 5, 6, 7, 8, 9, this.dot, 0]; |
| | | } else { |
| | | return this.$uv.randomArray([1, 2, 3, 4, 5, 6, 7, 8, 9, this.dot, 0]); |
| | | } |
| | | } else if (this.mode == 'card') { |
| | | if (!this.random) { |
| | | return [1, 2, 3, 4, 5, 6, 7, 8, 9, this.cardX, 0]; |
| | | } else { |
| | | return this.$uv.randomArray([1, 2, 3, 4, 5, 6, 7, 8, 9, this.cardX, 0]); |
| | | } |
| | | } |
| | | }, |
| | | // 按键的样式,在非乱序&&数字键盘&&不显示点按钮时,index为9时,按键占位两个空间 |
| | | itemStyle() { |
| | | return index => { |
| | | let style = {}; |
| | | if (this.mode == 'number' && this.dotDisabled && index == 9) style.width = '464rpx'; |
| | | return style; |
| | | }; |
| | | }, |
| | | // 是否让按键显示灰色,只在非乱序&&数字键盘&&且允许点按键的时候 |
| | | btnBgGray() { |
| | | return index => { |
| | | if (!this.random && index == 9 && (this.mode != 'number' || (this.mode == 'number' && !this |
| | | .dotDisabled))) return true; |
| | | else return false; |
| | | }; |
| | | }, |
| | | }, |
| | | created() { |
| | | |
| | | }, |
| | | methods: { |
| | | // 点击退格键 |
| | | backspaceClick() { |
| | | this.$emit('backspace'); |
| | | clearInterval(this.timer); //再次清空定时器,防止重复注册定时器 |
| | | this.timer = null; |
| | | this.timer = setInterval(() => { |
| | | this.$emit('backspace'); |
| | | }, 250); |
| | | }, |
| | | clearTimer() { |
| | | clearInterval(this.timer); |
| | | this.timer = null; |
| | | }, |
| | | // 获取键盘显示的内容 |
| | | keyboardClick(val) { |
| | | // 允许键盘显示点模式和触发非点按键时,将内容转为数字类型 |
| | | if (!this.dotDisabled && val != this.dot && val != this.cardX) val = Number(val); |
| | | this.$emit('change', val); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-hover: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-number-keyboard-background-color:rgb(224, 228, 230) !default; |
| | | $uv-number-keyboard-padding:8px 10rpx 8px 10rpx !default; |
| | | $uv-number-keyboard-button-width:222rpx !default; |
| | | $uv-number-keyboard-button-margin:4px 6rpx !default; |
| | | $uv-number-keyboard-button-border-top-left-radius:4px !default; |
| | | $uv-number-keyboard-button-border-top-right-radius:4px !default; |
| | | $uv-number-keyboard-button-border-bottom-left-radius:4px !default; |
| | | $uv-number-keyboard-button-border-bottom-right-radius:4px !default; |
| | | $uv-number-keyboard-button-height: 90rpx!default; |
| | | $uv-number-keyboard-button-background-color:#FFFFFF !default; |
| | | $uv-number-keyboard-button-box-shadow:0 2px 0px #BBBCBE !default; |
| | | $uv-number-keyboard-text-font-size:20px !default; |
| | | $uv-number-keyboard-text-font-weight:500 !default; |
| | | $uv-number-keyboard-text-color:$uv-main-color !default; |
| | | $uv-number-keyboard-gray-background-color:rgb(200, 202, 210) !default; |
| | | $uv-number-keyboard-uv-hover-class-background-color: #BBBCC6 !default; |
| | | |
| | | .uv-keyboard { |
| | | @include flex; |
| | | flex-direction: row; |
| | | justify-content: space-around; |
| | | background-color: $uv-number-keyboard-background-color; |
| | | flex-wrap: wrap; |
| | | padding: $uv-number-keyboard-padding; |
| | | |
| | | &__button-wrapper { |
| | | box-shadow: $uv-number-keyboard-button-box-shadow; |
| | | margin: $uv-number-keyboard-button-margin; |
| | | border-top-left-radius: $uv-number-keyboard-button-border-top-left-radius; |
| | | border-top-right-radius: $uv-number-keyboard-button-border-top-right-radius; |
| | | border-bottom-left-radius: $uv-number-keyboard-button-border-bottom-left-radius; |
| | | border-bottom-right-radius: $uv-number-keyboard-button-border-bottom-right-radius; |
| | | |
| | | &__button { |
| | | width: $uv-number-keyboard-button-width; |
| | | height: $uv-number-keyboard-button-height; |
| | | background-color: $uv-number-keyboard-button-background-color; |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | border-top-left-radius: $uv-number-keyboard-button-border-top-left-radius; |
| | | border-top-right-radius: $uv-number-keyboard-button-border-top-right-radius; |
| | | border-bottom-left-radius: $uv-number-keyboard-button-border-bottom-left-radius; |
| | | border-bottom-right-radius: $uv-number-keyboard-button-border-bottom-right-radius; |
| | | |
| | | &__text { |
| | | font-size: $uv-number-keyboard-text-font-size; |
| | | font-weight: $uv-number-keyboard-text-font-weight; |
| | | color: $uv-number-keyboard-text-color; |
| | | } |
| | | |
| | | &--gray { |
| | | background-color: $uv-number-keyboard-gray-background-color; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .uv-hover-class { |
| | | background-color: $uv-number-keyboard-uv-hover-class-background-color; |
| | | } |
| | | </style> |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 键盘的类型,number-数字键盘,card-身份证键盘,car-车牌号键盘 |
| | | mode: { |
| | | type: String, |
| | | default: 'number' |
| | | }, |
| | | // 是否显示键盘的"."符号 |
| | | dotDisabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示顶部工具条 |
| | | tooltip: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否显示工具条中间的提示 |
| | | showTips: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 工具条中间的提示文字 |
| | | tips: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否显示工具条左边的"取消"按钮 |
| | | showCancel: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否显示工具条右边的"完成"按钮 |
| | | showConfirm: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否打乱键盘按键的顺序 |
| | | random: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距 |
| | | safeAreaInsetBottom: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否允许通过点击遮罩关闭键盘 |
| | | closeOnClickOverlay: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否允许点击确认按钮关闭组件 |
| | | closeOnClickConfirm: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否显示遮罩,某些时候数字键盘时,用户希望看到自己的数值,所以可能不想要遮罩 |
| | | overlay: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // z-index值 |
| | | zIndex: { |
| | | type: [String, Number], |
| | | default: 10075 |
| | | }, |
| | | // 取消按钮的文字 |
| | | cancelText: { |
| | | type: String, |
| | | default: '取消' |
| | | }, |
| | | // 确认按钮的文字 |
| | | confirmText: { |
| | | type: String, |
| | | default: '确定' |
| | | }, |
| | | // 输入一个中文后,是否自动切换到英文 |
| | | autoChange: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 被禁用的键 |
| | | disKeys: { |
| | | type: Array, |
| | | default: ()=>[] |
| | | }, |
| | | // 是否自定义abc |
| | | customabc: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ...uni.$uv?.props?.keyboard |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <uv-popup |
| | | ref="keyboardPopup" |
| | | mode="bottom" |
| | | :overlay="overlay" |
| | | :closeOnClickOverlay="closeOnClickOverlay" |
| | | :safeAreaInsetBottom="safeAreaInsetBottom" |
| | | :zIndex="zIndex" |
| | | :customStyle="{ backgroundColor: 'rgb(214, 218, 220)' }" |
| | | @change="popupChange" |
| | | > |
| | | <view class="uv-keyboard"> |
| | | <slot /> |
| | | <view |
| | | class="uv-keyboard__tooltip" |
| | | v-if="tooltip" |
| | | > |
| | | <view |
| | | hover-class="uv-hover-class" |
| | | :hover-stay-time="100" |
| | | > |
| | | <text |
| | | class="uv-keyboard__tooltip__item uv-keyboard__tooltip__cancel" |
| | | v-if="showCancel" |
| | | @tap="onCancel" |
| | | >{{showCancel && cancelText}}</text> |
| | | </view> |
| | | <view> |
| | | <text |
| | | v-if="showTips" |
| | | class="uv-keyboard__tooltip__item uv-keyboard__tooltip__tips" |
| | | >{{tips ? tips : mode == 'number' ? '数字键盘' : mode == 'card' ? '身份证键盘' : '车牌号键盘'}}</text> |
| | | </view> |
| | | <view |
| | | hover-class="uv-hover-class" |
| | | :hover-stay-time="100" |
| | | > |
| | | <text |
| | | v-if="showConfirm" |
| | | @tap="onConfirm" |
| | | class="uv-keyboard__tooltip__item uv-keyboard__tooltip__submit" |
| | | hover-class="uv-hover-class" |
| | | >{{showConfirm && confirmText}}</text> |
| | | </view> |
| | | </view> |
| | | <template v-if="mode == 'number' || mode == 'card'"> |
| | | <uv-keyboard-number |
| | | :random="random" |
| | | @backspace="backspace" |
| | | @change="change" |
| | | :mode="mode" |
| | | :dotDisabled="dotDisabled" |
| | | ></uv-keyboard-number> |
| | | </template> |
| | | <template v-else> |
| | | <uv-keyboard-car |
| | | ref="uvKeyboardCarRef" |
| | | :random="random" |
| | | :autoChange="autoChange" |
| | | :disKeys="disKeys" |
| | | :customabc="customabc" |
| | | @backspace="backspace" |
| | | @change="change" |
| | | @changeCarInputMode="changeCarInputMode" |
| | | > |
| | | <slot name="abc"></slot> |
| | | </uv-keyboard-car> |
| | | </template> |
| | | </view> |
| | | </uv-popup> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * keyboard 键盘 |
| | | * @description 此为uViw自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3中模式,都有可以打乱按键顺序的选项。 |
| | | * @tutorial https://www.uvui.cn/components/keyboard.html |
| | | * @property {String} mode 键盘类型,见官网基本使用的说明 (默认 'number' ) |
| | | * @property {Boolean} dotDisabled 是否显示"."按键,只在mode=number时有效 (默认 false ) |
| | | * @property {Boolean} tooltip 是否显示键盘顶部工具条 (默认 true ) |
| | | * @property {Boolean} showTips 是否显示工具条中间的提示 (默认 true ) |
| | | * @property {String} tips 工具条中间的提示文字,见上方基本使用的说明,如不需要,请传""空字符 |
| | | * @property {Boolean} showCancel 是否显示工具条左边的"取消"按钮 (默认 true ) |
| | | * @property {Boolean} showConfirm 是否显示工具条右边的"完成"按钮( 默认 true ) |
| | | * @property {Boolean} random 是否打乱键盘按键的顺序 (默认 false ) |
| | | * @property {Boolean} safeAreaInsetBottom 是否开启底部安全区适配 (默认 true ) |
| | | * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩收起键盘 (默认 true ) |
| | | * @property {Boolean} show 控制键盘的弹出与收起(默认 false ) |
| | | * @property {Boolean} overlay 是否显示遮罩 (默认 true ) |
| | | * @property {String | Number} zIndex 弹出键盘的z-index值 (默认 1075 ) |
| | | * @property {String} cancelText 取消按钮的文字 (默认 '取消' ) |
| | | * @property {String} confirmText 确认按钮的文字 (默认 '确认' ) |
| | | * @property {Object} customStyle 自定义样式,对象形式 |
| | | * @event {Function} change 按键被点击(不包含退格键被点击) |
| | | * @event {Function} cancel 键盘顶部工具条左边的"取消"按钮被点击 |
| | | * @event {Function} confirm 键盘顶部工具条右边的"完成"按钮被点击 |
| | | * @event {Function} backspace 键盘退格键被点击 |
| | | * @example <uv-keyboard mode="number" v-model="show"></uv-keyboard> |
| | | */ |
| | | export default { |
| | | name: "uv-keyboard", |
| | | mixins: [mpMixin, mixin, props], |
| | | emits: ['close','change','confirm','cancel','backspace','changeCarInputMode'], |
| | | methods: { |
| | | open() { |
| | | this.$refs.keyboardPopup.open(); |
| | | }, |
| | | close() { |
| | | this.$refs.keyboardPopup.close(); |
| | | }, |
| | | popupChange(e) { |
| | | if(!e.show) this.$emit('close'); |
| | | }, |
| | | change(e) { |
| | | this.$emit('change', e); |
| | | }, |
| | | // 输入完成 |
| | | onConfirm() { |
| | | this.$emit('confirm'); |
| | | if(this.closeOnClickConfirm) this.close(); |
| | | }, |
| | | // 取消输入 |
| | | onCancel() { |
| | | this.$emit('cancel'); |
| | | this.close(); |
| | | }, |
| | | // 退格键 |
| | | backspace() { |
| | | this.$emit('backspace'); |
| | | }, |
| | | // car模式切换中文|英文方法 |
| | | changeCarInputMode(e) { |
| | | this.$emit('changeCarInputMode',e); |
| | | }, |
| | | changeCarMode() { |
| | | this.$refs.uvKeyboardCarRef && this.$refs.uvKeyboardCarRef.changeCarInputMode(); |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-hover: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | .uv-keyboard { |
| | | |
| | | &__tooltip { |
| | | @include flex; |
| | | justify-content: space-between; |
| | | background-color: #FFFFFF; |
| | | padding: 14px 12px; |
| | | |
| | | &__item { |
| | | color: #333333; |
| | | flex: 1; |
| | | text-align: center; |
| | | font-size: 15px; |
| | | } |
| | | |
| | | &__submit { |
| | | text-align: right; |
| | | color: $uv-primary; |
| | | } |
| | | |
| | | &__cancel { |
| | | text-align: left; |
| | | color: #888888; |
| | | } |
| | | |
| | | &__tips { |
| | | color: $uv-tips-color; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-keyboard", |
| | | "displayName": "uv-keyboard 键盘 全面兼容vue3+2、app、h5、小程序等多端", |
| | | "version": "1.0.5", |
| | | "description": "uv-keyboard 该组件为自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。", |
| | | "keywords": [ |
| | | "uv-keyboard", |
| | | "uvui", |
| | | "uv-ui", |
| | | "keyboard", |
| | | "键盘" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools", |
| | | "uv-popup", |
| | | "uv-icon" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Keyboard 键盘 |
| | | |
| | | > **组件名:uv-keyboard** |
| | | |
| | | 该组件为自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。 |
| | | |
| | | # <a href="https://www.uvui.cn/components/keyboard.html" target="_blank">查看文档</a> |
| | | |
| | | ## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small> |
| | | |
| | | ### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | <a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank"> |
| | | |
| | |  |
| | | |
| | | </a> |
| | | |
| | | #### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.2(2023-06-20) |
| | | 1. 适配height参数携带单位 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-line-progress 线形进度条 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 激活部分的颜色 |
| | | activeColor: { |
| | | type: String, |
| | | default: '#19be6b' |
| | | }, |
| | | inactiveColor: { |
| | | type: String, |
| | | default: '#ececec' |
| | | }, |
| | | // 进度百分比,数值 |
| | | percentage: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 是否在进度条内部显示百分比的值 |
| | | showText: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 进度条的高度,单位px |
| | | height: { |
| | | type: [String, Number], |
| | | default: 12 |
| | | }, |
| | | ...uni.$uv?.props?.lineProgress |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-line-progress" |
| | | :style="[$uv.addStyle(customStyle)]" |
| | | > |
| | | <view |
| | | class="uv-line-progress__background" |
| | | ref="uv-line-progress__background" |
| | | :style="[{ |
| | | backgroundColor: inactiveColor, |
| | | height: $uv.addUnit($uv.getPx(height)) |
| | | }]" |
| | | > |
| | | </view> |
| | | <view |
| | | class="uv-line-progress__line" |
| | | :style="[progressStyle]" |
| | | > |
| | | <slot> |
| | | <text v-if="showText && percentage >= 10" class="uv-line-progress__text">{{innserPercentage + '%'}}</text> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | // #ifdef APP-NVUE |
| | | const dom = uni.requireNativePlugin('dom') |
| | | // #endif |
| | | /** |
| | | * lineProgress 线型进度条 |
| | | * @description 展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。 |
| | | * @tutorial https://www.uvui.cn/components/lineProgress.html |
| | | * @property {String} activeColor 激活部分的颜色 ( 默认 '#19be6b' ) |
| | | * @property {String} inactiveColor 背景色 ( 默认 '#ececec' ) |
| | | * @property {String | Number} percentage 进度百分比,数值 ( 默认 0 ) |
| | | * @property {Boolean} showText 是否在进度条内部显示百分比的值 ( 默认 true ) |
| | | * @property {String | Number} height 进度条的高度,单位px ( 默认 12 ) |
| | | * |
| | | * @example <uv-line-progress :percent="70" :show-percent="true"></uv-line-progress> |
| | | */ |
| | | export default { |
| | | name: "uv-line-progress", |
| | | mixins: [mpMixin, mixin, props], |
| | | data() { |
| | | return { |
| | | lineWidth: 0, |
| | | } |
| | | }, |
| | | watch: { |
| | | percentage(n) { |
| | | this.resizeProgressWidth() |
| | | } |
| | | }, |
| | | computed: { |
| | | progressStyle() { |
| | | let style = {} |
| | | style.width = this.lineWidth |
| | | style.backgroundColor = this.activeColor |
| | | style.height = this.$uv.addUnit(this.$uv.getPx(this.height)) |
| | | return style |
| | | }, |
| | | innserPercentage() { |
| | | // 控制范围在0-100之间 |
| | | return this.$uv.range(0, 100, this.percentage) |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | this.$uv.sleep(20).then(() => { |
| | | this.resizeProgressWidth() |
| | | }) |
| | | }, |
| | | getProgressWidth() { |
| | | // #ifndef APP-NVUE |
| | | return this.$uvGetRect('.uv-line-progress__background') |
| | | // #endif |
| | | |
| | | // #ifdef APP-NVUE |
| | | // 返回一个promise |
| | | return new Promise(resolve => { |
| | | dom.getComponentRect(this.$refs['uv-line-progress__background'], (res) => { |
| | | resolve(res.size) |
| | | }) |
| | | }) |
| | | // #endif |
| | | }, |
| | | resizeProgressWidth() { |
| | | this.getProgressWidth().then(size => { |
| | | const { |
| | | width |
| | | } = size |
| | | // 通过设置的percentage值,计算其所占总长度的百分比 |
| | | this.lineWidth = width * this.innserPercentage / 100 + 'px' |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | |
| | | .uv-line-progress { |
| | | align-items: stretch; |
| | | position: relative; |
| | | @include flex(row); |
| | | flex: 1; |
| | | overflow: hidden; |
| | | border-radius: 100px; |
| | | |
| | | &__background { |
| | | background-color: #ececec; |
| | | border-radius: 100px; |
| | | flex: 1; |
| | | } |
| | | |
| | | &__line { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | bottom: 0; |
| | | align-items: center; |
| | | @include flex(row); |
| | | color: #ffffff; |
| | | border-radius: 100px; |
| | | transition: width 0.5s ease; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | &__text { |
| | | font-size: 10px; |
| | | align-items: center; |
| | | text-align: right; |
| | | color: #FFFFFF; |
| | | margin-right: 5px; |
| | | transform: scale(0.9); |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-line-progress", |
| | | "displayName": "uv-line-progress 线形进度条 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.2", |
| | | "description": "uv-line-progress 该组件展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。", |
| | | "keywords": [ |
| | | "uv-line-progress", |
| | | "uvui", |
| | | "uv-ui", |
| | | "progress", |
| | | "进度条" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## LineProgress 线形进度条 |
| | | |
| | | > **组件名:uv-line-progress** |
| | | |
| | | 展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/lineProgress.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | 1. 新增线条组件 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | color: { |
| | | type: String, |
| | | default: '#d6d7d9' |
| | | }, |
| | | // 长度,竖向时表现为高度,横向时表现为长度,可以为百分比,带px单位的值等 |
| | | length: { |
| | | type: [String, Number], |
| | | default: '100%' |
| | | }, |
| | | // 线条方向,col-竖向,row-横向 |
| | | direction: { |
| | | type: String, |
| | | default: 'row' |
| | | }, |
| | | // 是否显示细边框 |
| | | hairline: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 线条与上下左右元素的间距,字符串形式,如"30px"、"20px 30px" |
| | | margin: { |
| | | type: [String, Number], |
| | | default: 0 |
| | | }, |
| | | // 是否虚线,true-虚线,false-实线 |
| | | dashed: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ...uni.$uv?.props?.line |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view |
| | | class="uv-line" |
| | | :style="[lineStyle]" |
| | | > |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * line 线条 |
| | | * @description 此组件一般用于显示一根线条,用于分隔内容块,有横向和竖向两种模式,且能设置0.5px线条,使用也很简单 |
| | | * @tutorial https://www.uvui.cn/components/line.html |
| | | * @property {String} color 线条的颜色 ( 默认 '#d6d7d9' ) |
| | | * @property {String | Number} length 长度,竖向时表现为高度,横向时表现为长度,可以为百分比,带px单位的值等 ( 默认 '100%' ) |
| | | * @property {String} direction 线条的方向,row-横向,col-竖向 (默认 'row' ) |
| | | * @property {Boolean} hairline 是否显示细线条 (默认 true ) |
| | | * @property {String | Number} margin 线条与上下左右元素的间距,字符串形式,如"30px" (默认 0 ) |
| | | * @property {Boolean} dashed 是否虚线,true-虚线,false-实线 (默认 false ) |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * @example <uv-line color="red"></uv-line> |
| | | */ |
| | | export default { |
| | | name: 'uv-line', |
| | | mixins: [mpMixin, mixin, props], |
| | | computed: { |
| | | lineStyle() { |
| | | const style = {} |
| | | style.margin = this.margin |
| | | // 如果是水平线条,边框高度为1px,再通过transform缩小一半,就是0.5px了 |
| | | if (this.direction === 'row') { |
| | | // 此处采用兼容分开写,兼容nvue的写法 |
| | | style.borderBottomWidth = '1px' |
| | | style.borderBottomStyle = this.dashed ? 'dashed' : 'solid' |
| | | style.width = this.$uv.addUnit(this.length) |
| | | if (this.hairline) style.transform = 'scaleY(0.5)' |
| | | } else { |
| | | // 如果是竖向线条,边框宽度为1px,再通过transform缩小一半,就是0.5px了 |
| | | style.borderLeftWidth = '1px' |
| | | style.borderLeftStyle = this.dashed ? 'dashed' : 'solid' |
| | | style.height = this.$uv.addUnit(this.length) |
| | | if (this.hairline) style.transform = 'scaleX(0.5)' |
| | | } |
| | | style.borderColor = this.color |
| | | return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle)) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uv-line { |
| | | /* #ifndef APP-NVUE */ |
| | | vertical-align: middle; |
| | | /* #endif */ |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-line", |
| | | "displayName": "uv-line 线条 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.1", |
| | | "description": "uv-line 此组件一般用于显示一根线条,用于分隔内容块,有横向和竖向两种模式,且能设置0.5px线条,使用也很简单。", |
| | | "keywords": [ |
| | | "uv-line", |
| | | "uvui", |
| | | "uv-ui", |
| | | "line", |
| | | "线条" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Line 线条 |
| | | |
| | | > **组件名:uv-line** |
| | | |
| | | 此组件一般用于显示一根线条,用于分隔内容块,有横向和竖向两种模式,且能设置0.5px线条,使用也很简单。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/line.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.2(2023-08-13) |
| | | 1. 修复报错的BUG |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-link 超链接组件 |
| New file |
| | |
| | | export default { |
| | | props: { |
| | | // 文字颜色 |
| | | color: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 字体大小,单位px |
| | | fontSize: { |
| | | type: [String, Number], |
| | | default: 14 |
| | | }, |
| | | // 是否显示下划线 |
| | | underLine: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 要跳转的链接 |
| | | href: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 小程序中复制到粘贴板的提示语 |
| | | mpTips: { |
| | | type: String, |
| | | default: '链接已复制,请在浏览器打开' |
| | | }, |
| | | // 下划线颜色 |
| | | lineColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 超链接的问题,不使用slot形式传入,是因为nvue下无法修改颜色 |
| | | text: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | ...uni.$uv?.props?.link |
| | | } |
| | | } |
| New file |
| | |
| | | <template> |
| | | <text |
| | | class="uv-link" |
| | | @tap.stop="openLink" |
| | | :style="[linkStyle, $uv.addStyle(customStyle)]" |
| | | >{{text}}</text> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | import props from './props.js'; |
| | | /** |
| | | * link 超链接 |
| | | * @description 该组件为超链接组件,在不同平台有不同表现形式:在APP平台会通过plus环境打开内置浏览器,在小程序中把链接复制到粘贴板,同时提示信息,在H5中通过window.open打开链接。 |
| | | * @tutorial https://www.uvui.cn/components/link.html |
| | | * @property {String} color 文字颜色 (默认 color['uv-primary'] ) |
| | | * @property {String | Number} fontSize 字体大小,单位px (默认 15 ) |
| | | * @property {Boolean} underLine 是否显示下划线 (默认 false ) |
| | | * @property {String} href 跳转的链接,要带上http(s) |
| | | * @property {String} mpTips 各个小程序平台把链接复制到粘贴板后的提示语(默认“链接已复制,请在浏览器打开”) |
| | | * @property {String} lineColor 下划线颜色,默认同color参数颜色 |
| | | * @property {String} text 超链接的问题,不使用slot形式传入,是因为nvue下无法修改颜色 |
| | | * @property {Object} customStyle 定义需要用到的外部样式 |
| | | * |
| | | * @example <uv-link href="http://www.uvui.cn">蜀道难,难于上青天</uv-link> |
| | | */ |
| | | export default { |
| | | name: "uv-link", |
| | | emits:['click'], |
| | | mixins: [mpMixin, mixin, props], |
| | | computed: { |
| | | linkStyle() { |
| | | const style = { |
| | | color: this.color, |
| | | fontSize: this.$uv.addUnit(this.fontSize), |
| | | // line-height设置为比字体大小多2px |
| | | lineHeight: this.$uv.addUnit(this.$uv.getPx(this.fontSize) + 2), |
| | | textDecoration: this.underLine ? 'underline' : 'none' |
| | | } |
| | | return style |
| | | } |
| | | }, |
| | | methods: { |
| | | openLink() { |
| | | // #ifdef APP-PLUS |
| | | plus.runtime.openURL(this.href) |
| | | // #endif |
| | | // #ifdef H5 |
| | | window.open(this.href) |
| | | // #endif |
| | | // #ifdef MP |
| | | uni.setClipboardData({ |
| | | data: this.href, |
| | | success: () => { |
| | | uni.hideToast(); |
| | | this.$nextTick(() => { |
| | | this.$uv.toast(this.mpTips); |
| | | }) |
| | | } |
| | | }); |
| | | // #endif |
| | | this.$emit('click') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/color.scss'; |
| | | $uv-link-line-height:1 !default; |
| | | |
| | | .uv-link { |
| | | /* #ifndef APP-NVUE */ |
| | | line-height: $uv-link-line-height; |
| | | /* #endif */ |
| | | @include flex; |
| | | flex-wrap: wrap; |
| | | flex: 1; |
| | | color: $uv-primary; |
| | | } |
| | | </style> |
| New file |
| | |
| | | { |
| | | "id": "uv-link", |
| | | "displayName": "uv-link 超链接 全面兼容小程序、nvue、vue2、vue3等多端", |
| | | "version": "1.0.2", |
| | | "description": "uv-link 该组件为超链接组件", |
| | | "keywords": [ |
| | | "uv-link", |
| | | "uvui", |
| | | "uv-ui", |
| | | "link", |
| | | "超链接" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uv-ui-tools" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | ## Link 超链接 |
| | | |
| | | > **组件名:uv-link** |
| | | |
| | | 该组件为超链接组件,在不同平台有不同表现形式。 |
| | | |
| | | ### <a href="https://www.uvui.cn/components/link.html" target="_blank">查看文档</a> |
| | | |
| | | ### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui) |
| | | |
| | | #### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a> |
| New file |
| | |
| | | ## 1.0.9(2023-11-10) |
| | | 1. 修复设置ellipsis不生效的BUG |
| | | ## 1.0.8(2023-09-20) |
| | | 1. listItem优化可使用customStyle变量进行样式控制 |
| | | ## 1.0.7(2023-08-29) |
| | | 1. 修复边框的BUG |
| | | ## 1.0.6(2023-08-16) |
| | | 1. 修复switch开关返回undefined的问题 |
| | | 2. 优化初始化可能导致的闪动 |
| | | ## 1.0.5(2023-08-07) |
| | | 1. 修复分包页面在ios端,nvue编译不能滚动的BUG |
| | | ## 1.0.4(2023-08-04) |
| | | 1. nvue修复 触底不触发事件的BUG |
| | | 2. 更新文档说明事件触发 |
| | | ## 1.0.3(2023-07-28) |
| | | 1. 修改可能造成样式污染的BUG |
| | | ## 1.0.2(2023-07-26) |
| | | 1. 全面重构,用法与之前保持一致,参数全部变化 |
| | | 2. 新增多个功能参数,方便一键构建列表 |
| | | 3. List列表组件,包含基本列表样式、默认插槽机制、可扩展插槽机制、长列表性能优化、多端兼容。 |
| | | ## 1.0.1(2023-05-16) |
| | | 1. 优化组件依赖,修改后无需全局引入,组件导入即可使用 |
| | | 2. 优化部分功能 |
| | | ## 1.0.0(2023-05-10) |
| | | uv-list 列表 |
| New file |
| | |
| | | <template> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <cell :keep-scroll-position="keepScrollPosition"> |
| | | <!-- #endif --> |
| | | <view |
| | | :class="{ 'uv-list-item--disabled': disabled }" |
| | | :style="[$uv.addStyle(customStyle),{'background-color':customStyle.backgroundColor?customStyle.backgroundColor:'#fff'}]" |
| | | :hover-class="(!clickable && !link) || disabled || showSwitch ? '' : 'uv-list-item--hover'" |
| | | class="uv-list-item" @click="onClick"> |
| | | <view v-if="!isFirstChild" class="border--left" :class="{ 'uv-list--border': border }"></view> |
| | | <view class="uv-list-item__wrapper"> |
| | | <slot> |
| | | <view class="uv-list-item__container" |
| | | :class="{ 'container--right': showArrow || link, 'flex--direction': directionData === 'column'}" |
| | | :style="{paddingTop:padding.top,paddingLeft:padding.left,paddingRight:padding.right,paddingBottom:padding.bottom}"> |
| | | <slot name="header"> |
| | | <view class="uv-list-item__header"> |
| | | <view v-if="thumb" class="uv-list-item__icon"> |
| | | <image :src="thumb" class="uv-list-item__icon-img" :class="['uv-list--' + thumbSize]" /> |
| | | </view> |
| | | <view v-else-if="showExtraIcon" class="uv-list-item__icon"> |
| | | <uv-icon :name="extraIcon.icon" :customPrefix="extraIcon.customPrefix" :color="extraIcon.color" :size="extraIcon.size" /> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | <slot name="body"> |
| | | <view class="uv-list-item__content" |
| | | :class="{ 'uv-list-item__content--center': thumb || showExtraIcon || showBadge || showSwitch }"> |
| | | <text v-if="title" class="uv-list-item__content-title" |
| | | :class="[ellipsis && `uv-line-${ellipsis}`]">{{ title }}</text> |
| | | <text v-if="note" class="uv-list-item__content-note">{{ note }}</text> |
| | | </view> |
| | | </slot> |
| | | <slot name="footer"> |
| | | <view v-if="rightText || showBadge || showSwitch" class="uv-list-item__extra" |
| | | :class="{ 'flex--justify': directionData === 'column' }"> |
| | | <text v-if="rightText" class="uv-list-item__extra-text">{{ rightText }}</text> |
| | | <uv-badge |
| | | v-if="showBadge" |
| | | :show="!!(badge.show || badge.isDot || badge.value)" |
| | | :isDot="badge.isDot" |
| | | :value="badge.value" |
| | | :max="badge.max" |
| | | :type="badge.type" |
| | | :showZero="badge.showZero" |
| | | :bgColor="badge.bgColor" |
| | | :color="badge.color" |
| | | :shape="badge.shape" |
| | | :numberType="badge.numberType" |
| | | :inverted="badge.inverted" |
| | | customStyle="margin-left: 4px;" |
| | | ></uv-badge> |
| | | <uv-switch v-if="showSwitch" :value="switchChecked" :disabled="disabled" @change="onSwitchChange"></uv-switch> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <uv-icon v-if="showArrow || link" size="34rpx" class="uv-icon-wrapper" color="#bbb" name="arrow-right" /> |
| | | </view> |
| | | <!-- #ifdef APP-NVUE --> |
| | | </cell> |
| | | <!-- #endif --> |
| | | </template> |
| | | |
| | | <script> |
| | | import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' |
| | | import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' |
| | | /** |
| | | * ListItem 列表子组件 |
| | | * @description 列表子组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=24 |
| | | * @property {String} title 标题 |
| | | * @property {String} note 描述 |
| | | * @property {String} thumb 左侧缩略图,若thumb有值,则不会显示扩展图标 |
| | | * @property {String} thumbSize = [lg|base|sm] 略缩图大小 |
| | | * @value lg 大图 |
| | | * @value base 一般 |
| | | * @value sm 小图 |
| | | * @property {String} rightText 右侧文字内容 |
| | | * @property {Boolean} disabled = [true|false] 是否禁用 |
| | | * @property {Boolean} clickable = [true|false] 是否开启点击反馈 |
| | | * @property {String} link = [navigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈 |
| | | * @value navigateTo 同 uni.navigateTo() |
| | | * @value redirectTo 同 uni.redirectTo() |
| | | * @value reLaunch 同 uni.reLaunch() |
| | | * @value switchTab 同 uni.switchTab() |
| | | * @property {String | PageURIString} to 跳转目标页面 |
| | | * @property {Boolean} showBadge = [true|false] 是否显示数字角标 |
| | | * @property {Object} badge 扩展数字角标的参数,格式为 :badge="{value: 122}" |
| | | * @property {Boolean} showSwitch = [true|false] 是否显示Switch |
| | | * @property {Boolean} switchChecked = [true|false] Switch是否被选中 |
| | | * @property {Boolean} showExtraIcon = [true|false] 左侧是否显示扩展图标 |
| | | * @property {Object} extraIcon 扩展图标参数,格式为 :extraIcon="{icon: 'photo',size: '30px'}" |
| | | * @property {String} direction = [row|column] 排版方向 |
| | | * @value row 水平排列 |
| | | * @value column 垂直排列 |
| | | * @event {Function} click 点击 uniListItem 触发事件 |
| | | * @event {Function} switchChange 点击切换 Switch 时触发 |
| | | */ |
| | | export default { |
| | | name: 'uv-list-item', |
| | | mixins: [mpMixin, mixin], |
| | | emits: ['click', 'switchChange'], |
| | | props: { |
| | | direction: { |
| | | type: String, |
| | | default: 'row' |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | note: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | ellipsis: { |
| | | type: [Number, String], |
| | | default: 0 |
| | | }, |
| | | disabled: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | clickable: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | showArrow: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | link: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | to: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | showSwitch: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | switchChecked: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | showBadge: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | badge: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | rightText: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | thumb: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | thumbSize: { |
| | | type: String, |
| | | default: 'base' |
| | | }, |
| | | showExtraIcon: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | extraIcon: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | name: '', |
| | | color: '#000000', |
| | | size: 20, |
| | | customPrefix: '' |
| | | }; |
| | | } |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | customStyle: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | padding: '', |
| | | backgroundColor: '#FFFFFF' |
| | | } |
| | | } |
| | | }, |
| | | keepScrollPosition: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | computed: { |
| | | directionData(){ |
| | | return this.direction ? this.direction : (this.parentData.direction ? this.parentData.direction : 'row'); |
| | | } |
| | | }, |
| | | watch: { |
| | | 'customStyle.padding': { |
| | | handler(padding) { |
| | | if(padding) this.setPadding(padding); |
| | | }, |
| | | immediate: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | isFirstChild: false, |
| | | padding: { |
| | | top: "", |
| | | right: "", |
| | | bottom: "", |
| | | left: "" |
| | | }, |
| | | parentData: { |
| | | direction: 'row', |
| | | padding: 0 |
| | | } |
| | | }; |
| | | }, |
| | | created() { |
| | | this.updateParentData(); |
| | | }, |
| | | mounted() { |
| | | this.init(); |
| | | this.list = this.getForm() |
| | | // 判断是否存在 uv-list 组件 |
| | | if (this.list) { |
| | | if (!this.list.firstChildAppend) { |
| | | this.list.firstChildAppend = true; |
| | | this.isFirstChild = true; |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | init(){ |
| | | if (!this.parent) { |
| | | this.$uv.error('uv-list-item必须搭配uv-list组件使用'); |
| | | } |
| | | this.$nextTick(()=>{ |
| | | if(!(this.padding.top || this.padding.right|| this.padding.bottom|| this.padding.left)){ |
| | | this.setPadding(this.parentData.padding); |
| | | } |
| | | }) |
| | | }, |
| | | updateParentData() { |
| | | this.getParentData('uv-list'); |
| | | }, |
| | | setPadding(padding){ |
| | | if(typeof padding == 'number'){ |
| | | padding += '' |
| | | } |
| | | let paddingArr = padding.split(' ') |
| | | if (paddingArr.length === 1) { |
| | | const allPadding = paddingArr[0] |
| | | this.padding = { |
| | | "top": allPadding, |
| | | "right": allPadding, |
| | | "bottom": allPadding, |
| | | "left": allPadding |
| | | } |
| | | } else if (paddingArr.length === 2) { |
| | | const [verticalPadding, horizontalPadding] = paddingArr; |
| | | this.padding = { |
| | | "top": verticalPadding, |
| | | "right": horizontalPadding, |
| | | "bottom": verticalPadding, |
| | | "left": horizontalPadding |
| | | } |
| | | } else if (paddingArr.length === 4) { |
| | | const [topPadding, rightPadding, bottomPadding, leftPadding] = paddingArr; |
| | | this.padding = { |
| | | "top": topPadding, |
| | | "right": rightPadding, |
| | | "bottom": bottomPadding, |
| | | "left": leftPadding |
| | | } |
| | | } |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getForm(name = 'uniList') { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== name) { |
| | | parent = parent.$parent; |
| | | if (!parent) return false |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | }, |
| | | onClick() { |
| | | if (this.to !== '') { |
| | | this.openPage(); |
| | | return; |
| | | } |
| | | if (this.clickable || this.link) { |
| | | this.$emit('click', { |
| | | data: {} |
| | | }); |
| | | } |
| | | }, |
| | | onSwitchChange(e) { |
| | | this.$emit('switchChange', e); |
| | | }, |
| | | openPage() { |
| | | if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].indexOf(this.link) !== -1) { |
| | | this.pageApi(this.link); |
| | | } else { |
| | | this.pageApi('navigateTo'); |
| | | } |
| | | }, |
| | | pageApi(api) { |
| | | let callback = { |
| | | url: this.to, |
| | | success: res => { |
| | | this.$emit('click', { |
| | | data: res |
| | | }); |
| | | }, |
| | | fail: err => { |
| | | this.$emit('click', { |
| | | data: err |
| | | }); |
| | | } |
| | | } |
| | | switch (api) { |
| | | case 'navigateTo': |
| | | uni.navigateTo(callback) |
| | | break |
| | | case 'redirectTo': |
| | | uni.redirectTo(callback) |
| | | break |
| | | case 'reLaunch': |
| | | uni.reLaunch(callback) |
| | | break |
| | | case 'switchTab': |
| | | uni.switchTab(callback) |
| | | break |
| | | default: |
| | | uni.navigateTo(callback) |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $show-lines: 1; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss'; |
| | | @import '@/uni_modules/uv-ui-tools/libs/css/components.scss'; |
| | | $uv-font-size-sm:12px; |
| | | $uv-font-size-base:14px; |
| | | $uv-font-size-lg:16px; |
| | | $uv-spacing-col-lg: 12px; |
| | | $uv-spacing-row-lg: 15px; |
| | | $uv-img-size-sm:20px; |
| | | $uv-img-size-base:26px; |
| | | $uv-img-size-lg:40px; |
| | | $uv-border-color:#e5e5e5; |
| | | $uv-bg-color-hover:#f1f1f1; |
| | | $uv-text-color-grey:#999; |
| | | $list-item-pd: $uv-spacing-col-lg $uv-spacing-row-lg; |
| | | |
| | | .uv-list-item { |
| | | @include flex(row); |
| | | font-size: $uv-font-size-lg; |
| | | position: relative; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | background-color: #fff; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uv-list-item--disabled { |
| | | opacity: 0.3; |
| | | } |
| | | |
| | | .uv-list-item--hover { |
| | | background-color: $uv-bg-color-hover !important; |
| | | } |
| | | |
| | | .uv-list-item__wrapper { |
| | | @include flex(column); |
| | | flex: 1; |
| | | } |
| | | |
| | | .uv-list-item__container { |
| | | position: relative; |
| | | @include flex(row); |
| | | padding: $list-item-pd; |
| | | padding-left: $uv-spacing-row-lg; |
| | | flex: 1; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .container--right { |
| | | padding-right: 0; |
| | | } |
| | | |
| | | .uv-list--border { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | left: 0; |
| | | /* #ifdef APP-NVUE */ |
| | | border-top-color: $uv-border-color; |
| | | border-top-style: solid; |
| | | border-top-width: 0.5px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uv-list--border:after { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | left: 0; |
| | | height: 1px; |
| | | content: ''; |
| | | -webkit-transform: scaleY(0.5); |
| | | transform: scaleY(0.5); |
| | | background-color: $uv-border-color; |
| | | } |
| | | |
| | | /* #endif */ |
| | | .uv-list-item__content { |
| | | @include flex(column); |
| | | padding-right: 8px; |
| | | flex: 1; |
| | | color: #3b4144; |
| | | justify-content: space-between; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uv-list-item__content--center { |
| | | justify-content: center; |
| | | } |
| | | |
| | | .uv-list-item__content-title { |
| | | font-size: $uv-font-size-base; |
| | | color: #3b4144; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uv-list-item__content-note { |
| | | margin-top: 6rpx; |
| | | color: $uv-text-color-grey; |
| | | font-size: $uv-font-size-sm; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uv-list-item__extra { |
| | | @include flex(row); |
| | | justify-content: flex-end; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uv-list-item__header { |
| | | @include flex(row); |
| | | align-items: center; |
| | | } |
| | | |
| | | .uv-list-item__icon { |
| | | margin-right: 18rpx; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uv-list-item__icon-img { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | height: $uv-img-size-base; |
| | | width: $uv-img-size-base; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .uv-icon-wrapper { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .flex--direction { |
| | | flex-direction: column; |
| | | /* #ifndef APP-NVUE */ |
| | | align-items: initial; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .flex--justify { |
| | | /* #ifndef APP-NVUE */ |
| | | justify-content: initial; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uv-list--lg { |
| | | height: $uv-img-size-lg; |
| | | width: $uv-img-size-lg; |
| | | } |
| | | |
| | | .uv-list--base { |
| | | height: $uv-img-size-base; |
| | | width: $uv-img-size-base; |
| | | } |
| | | |
| | | .uv-list--sm { |
| | | height: $uv-img-size-sm; |
| | | width: $uv-img-size-sm; |
| | | } |
| | | |
| | | .uv-list-item__extra-text { |
| | | color: $uv-text-color-grey; |
| | | font-size: $uv-font-size-sm; |
| | | } |
| | | </style> |
| ledApp/uni_modules/uv-list/components/uv-list/uv-list.vue
ledApp/uni_modules/uv-list/package.json
ledApp/uni_modules/uv-list/readme.md
ledApp/uni_modules/uv-load-more/changelog.md
ledApp/uni_modules/uv-load-more/components/uv-load-more/props.js
ledApp/uni_modules/uv-load-more/components/uv-load-more/uv-load-more.vue
ledApp/uni_modules/uv-load-more/package.json
ledApp/uni_modules/uv-load-more/readme.md
ledApp/uni_modules/uv-loading-icon/changelog.md
ledApp/uni_modules/uv-loading-icon/components/uv-loading-icon/props.js
ledApp/uni_modules/uv-loading-icon/components/uv-loading-icon/uv-loading-icon.vue
ledApp/uni_modules/uv-loading-icon/package.json
ledApp/uni_modules/uv-loading-icon/readme.md
ledApp/uni_modules/uv-loading-page/changelog.md
ledApp/uni_modules/uv-loading-page/components/uv-loading-page/props.js
ledApp/uni_modules/uv-loading-page/components/uv-loading-page/uv-loading-page.vue
ledApp/uni_modules/uv-loading-page/package.json
ledApp/uni_modules/uv-loading-page/readme.md
ledApp/uni_modules/uv-modal/changelog.md
ledApp/uni_modules/uv-modal/components/uv-modal/props.js
ledApp/uni_modules/uv-modal/components/uv-modal/uv-modal.vue
ledApp/uni_modules/uv-modal/package.json
ledApp/uni_modules/uv-modal/readme.md
ledApp/uni_modules/uv-navbar/changelog.md
ledApp/uni_modules/uv-navbar/components/uv-navbar/props.js
ledApp/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue
ledApp/uni_modules/uv-navbar/package.json
ledApp/uni_modules/uv-navbar/readme.md
ledApp/uni_modules/uv-no-network/changelog.md
ledApp/uni_modules/uv-no-network/components/uv-no-network/props.js
ledApp/uni_modules/uv-no-network/components/uv-no-network/uv-no-network.vue
ledApp/uni_modules/uv-no-network/package.json
ledApp/uni_modules/uv-no-network/readme.md
ledApp/uni_modules/uv-notice-bar/changelog.md
ledApp/uni_modules/uv-notice-bar/components/uv-column-notice/props.js
ledApp/uni_modules/uv-notice-bar/components/uv-column-notice/uv-column-notice.vue
ledApp/uni_modules/uv-notice-bar/components/uv-notice-bar/props.js
ledApp/uni_modules/uv-notice-bar/components/uv-notice-bar/uv-notice-bar.vue
ledApp/uni_modules/uv-notice-bar/components/uv-row-notice/props.js
ledApp/uni_modules/uv-notice-bar/components/uv-row-notice/uv-row-notice.vue
ledApp/uni_modules/uv-notice-bar/package.json
ledApp/uni_modules/uv-notice-bar/readme.md
ledApp/uni_modules/uv-notify/changelog.md
ledApp/uni_modules/uv-notify/components/uv-notify/props.js
ledApp/uni_modules/uv-notify/components/uv-notify/uv-notify.vue
ledApp/uni_modules/uv-notify/package.json
ledApp/uni_modules/uv-notify/readme.md
ledApp/uni_modules/uv-number-box/changelog.md
ledApp/uni_modules/uv-number-box/components/uv-number-box/props.js
ledApp/uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue
ledApp/uni_modules/uv-number-box/package.json
ledApp/uni_modules/uv-number-box/readme.md
ledApp/uni_modules/uv-overlay/changelog.md
ledApp/uni_modules/uv-overlay/components/uv-overlay/props.js
ledApp/uni_modules/uv-overlay/components/uv-overlay/uv-overlay.vue
ledApp/uni_modules/uv-overlay/package.json
ledApp/uni_modules/uv-overlay/readme.md
ledApp/uni_modules/uv-parse/changelog.md
ledApp/uni_modules/uv-parse/components/uv-parse/node/node.vue
ledApp/uni_modules/uv-parse/components/uv-parse/parser.js
ledApp/uni_modules/uv-parse/components/uv-parse/uv-parse.vue
ledApp/uni_modules/uv-parse/package.json
ledApp/uni_modules/uv-parse/readme.md
ledApp/uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js
ledApp/uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js
ledApp/uni_modules/uv-parse/static/app-plus/uv-parse/local.html
ledApp/uni_modules/uv-pick-color/changelog.md
ledApp/uni_modules/uv-pick-color/components/uv-pick-color/colors.js
ledApp/uni_modules/uv-pick-color/components/uv-pick-color/props.js
ledApp/uni_modules/uv-pick-color/components/uv-pick-color/uv-pick-color.vue
ledApp/uni_modules/uv-pick-color/package.json
ledApp/uni_modules/uv-pick-color/readme.md
ledApp/uni_modules/uv-picker/changelog.md
ledApp/uni_modules/uv-picker/components/uv-picker/props.js
ledApp/uni_modules/uv-picker/components/uv-picker/uv-picker.vue
ledApp/uni_modules/uv-picker/package.json
ledApp/uni_modules/uv-picker/readme.md
ledApp/uni_modules/uv-popup/changelog.md
ledApp/uni_modules/uv-popup/components/uv-popup/keypress.js
ledApp/uni_modules/uv-popup/components/uv-popup/uv-popup.vue
ledApp/uni_modules/uv-popup/package.json
ledApp/uni_modules/uv-popup/readme.md
ledApp/uni_modules/uv-qrcode/changelog.md
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/cache.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/bridge/bridge-weex.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleLinearGradient.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStylePattern.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleRadialGradient.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/RenderingContext.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ActiveInfo.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Buffer.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Framebuffer.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLenum.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLmethod.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLtype.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Program.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Renderbuffer.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/RenderingContext.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Shader.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ShaderPrecisionFormat.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Texture.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/UniformLocation.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/classUtils.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/canvas.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/image.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/tool.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/index.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/props.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/qrcode.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/queue.js
ledApp/uni_modules/uv-qrcode/components/uv-qrcode/uv-qrcode.vue
ledApp/uni_modules/uv-qrcode/package.json
ledApp/uni_modules/uv-qrcode/readme.md
ledApp/uni_modules/uv-radio/changelog.md
ledApp/uni_modules/uv-radio/components/uv-radio-group/props.js
ledApp/uni_modules/uv-radio/components/uv-radio-group/uv-radio-group.vue
ledApp/uni_modules/uv-radio/components/uv-radio/props.js
ledApp/uni_modules/uv-radio/components/uv-radio/uv-radio.vue
ledApp/uni_modules/uv-radio/package.json
ledApp/uni_modules/uv-radio/readme.md
ledApp/uni_modules/uv-rate/changelog.md
ledApp/uni_modules/uv-rate/components/uv-rate/props.js
ledApp/uni_modules/uv-rate/components/uv-rate/uv-rate.vue
ledApp/uni_modules/uv-rate/package.json
ledApp/uni_modules/uv-rate/readme.md
ledApp/uni_modules/uv-read-more/changelog.md
ledApp/uni_modules/uv-read-more/components/uv-read-more/props.js
ledApp/uni_modules/uv-read-more/components/uv-read-more/uv-read-more.vue
ledApp/uni_modules/uv-read-more/package.json
ledApp/uni_modules/uv-read-more/readme.md
ledApp/uni_modules/uv-row/changelog.md
ledApp/uni_modules/uv-row/components/uv-col/props.js
ledApp/uni_modules/uv-row/components/uv-col/uv-col.vue
ledApp/uni_modules/uv-row/components/uv-row/props.js
ledApp/uni_modules/uv-row/components/uv-row/uv-row.vue
ledApp/uni_modules/uv-row/package.json
ledApp/uni_modules/uv-row/readme.md
ledApp/uni_modules/uv-safe-bottom/changelog.md
ledApp/uni_modules/uv-safe-bottom/components/uv-safe-bottom/uv-safe-bottom.vue
ledApp/uni_modules/uv-safe-bottom/package.json
ledApp/uni_modules/uv-safe-bottom/readme.md
ledApp/uni_modules/uv-scroll-list/changelog.md
ledApp/uni_modules/uv-scroll-list/components/uv-scroll-list/nvue.js
ledApp/uni_modules/uv-scroll-list/components/uv-scroll-list/props.js
ledApp/uni_modules/uv-scroll-list/components/uv-scroll-list/scrollWxs.wxs
ledApp/uni_modules/uv-scroll-list/components/uv-scroll-list/uv-scroll-list.vue
ledApp/uni_modules/uv-scroll-list/package.json
ledApp/uni_modules/uv-scroll-list/readme.md
ledApp/uni_modules/uv-search/changelog.md
ledApp/uni_modules/uv-search/components/uv-search/props.js
ledApp/uni_modules/uv-search/components/uv-search/uv-search.vue
ledApp/uni_modules/uv-search/package.json
ledApp/uni_modules/uv-search/readme.md
ledApp/uni_modules/uv-skeleton/changelog.md
ledApp/uni_modules/uv-skeleton/components/uv-skeleton/props.js
ledApp/uni_modules/uv-skeleton/components/uv-skeleton/uv-skeleton.vue
ledApp/uni_modules/uv-skeleton/package.json
ledApp/uni_modules/uv-skeleton/readme.md
ledApp/uni_modules/uv-skeletons/changelog.md
ledApp/uni_modules/uv-skeletons/components/uv-skeletons/uv-skeletons.vue
ledApp/uni_modules/uv-skeletons/package.json
ledApp/uni_modules/uv-skeletons/readme.md
ledApp/uni_modules/uv-slider/changelog.md
ledApp/uni_modules/uv-slider/components/uv-slider/props.js
ledApp/uni_modules/uv-slider/components/uv-slider/uv-slider.vue
ledApp/uni_modules/uv-slider/package.json
ledApp/uni_modules/uv-slider/readme.md
ledApp/uni_modules/uv-status-bar/changelog.md
ledApp/uni_modules/uv-status-bar/components/uv-status-bar/props.js
ledApp/uni_modules/uv-status-bar/components/uv-status-bar/uv-status-bar.vue
ledApp/uni_modules/uv-status-bar/package.json
ledApp/uni_modules/uv-status-bar/readme.md
ledApp/uni_modules/uv-steps/changelog.md
ledApp/uni_modules/uv-steps/components/uv-steps-item/props.js
ledApp/uni_modules/uv-steps/components/uv-steps-item/uv-steps-item.vue
ledApp/uni_modules/uv-steps/components/uv-steps/props.js
ledApp/uni_modules/uv-steps/components/uv-steps/uv-steps.vue
ledApp/uni_modules/uv-steps/package.json
ledApp/uni_modules/uv-steps/readme.md
ledApp/uni_modules/uv-sticky/changelog.md
ledApp/uni_modules/uv-sticky/components/uv-sticky/props.js
ledApp/uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue
ledApp/uni_modules/uv-sticky/package.json
ledApp/uni_modules/uv-sticky/readme.md
ledApp/uni_modules/uv-subsection/changelog.md
ledApp/uni_modules/uv-subsection/components/uv-subsection/props.js
ledApp/uni_modules/uv-subsection/components/uv-subsection/uv-subsection.vue
ledApp/uni_modules/uv-subsection/package.json
ledApp/uni_modules/uv-subsection/readme.md
ledApp/uni_modules/uv-swipe-action/changelog.md
ledApp/uni_modules/uv-swipe-action/components/uv-swipe-action-item/index - backup.wxs
ledApp/uni_modules/uv-swipe-action/components/uv-swipe-action-item/index.wxs
ledApp/uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue - backup.js
ledApp/uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue.js
ledApp/uni_modules/uv-swipe-action/components/uv-swipe-action-item/props.js
ledApp/uni_modules/uv-swipe-action/components/uv-swipe-action-item/uv-swipe-action-item.vue
ledApp/uni_modules/uv-swipe-action/components/uv-swipe-action-item/wxs.js
ledApp/uni_modules/uv-swipe-action/components/uv-swipe-action/props.js
ledApp/uni_modules/uv-swipe-action/components/uv-swipe-action/uv-swipe-action.vue
ledApp/uni_modules/uv-swipe-action/package.json
ledApp/uni_modules/uv-swipe-action/readme.md
ledApp/uni_modules/uv-swiper/changelog.md
ledApp/uni_modules/uv-swiper/components/uv-swiper-indicator/props.js
ledApp/uni_modules/uv-swiper/components/uv-swiper-indicator/uv-swiper-indicator.vue
ledApp/uni_modules/uv-swiper/components/uv-swiper/props.js
ledApp/uni_modules/uv-swiper/components/uv-swiper/uv-swiper.vue
ledApp/uni_modules/uv-swiper/package.json
ledApp/uni_modules/uv-swiper/readme.md
ledApp/uni_modules/uv-switch/changelog.md
ledApp/uni_modules/uv-switch/components/uv-switch/props.js
ledApp/uni_modules/uv-switch/components/uv-switch/uv-switch.vue
ledApp/uni_modules/uv-switch/package.json
ledApp/uni_modules/uv-switch/readme.md
ledApp/uni_modules/uv-tabbar/changelog.md
ledApp/uni_modules/uv-tabbar/components/uv-tabbar-item/props.js
ledApp/uni_modules/uv-tabbar/components/uv-tabbar-item/uv-tabbar-item.vue
ledApp/uni_modules/uv-tabbar/components/uv-tabbar/props.js
ledApp/uni_modules/uv-tabbar/components/uv-tabbar/uv-tabbar.vue
ledApp/uni_modules/uv-tabbar/package.json
ledApp/uni_modules/uv-tabbar/readme.md
ledApp/uni_modules/uv-tabs/changelog.md
ledApp/uni_modules/uv-tabs/components/uv-tabs/props.js
ledApp/uni_modules/uv-tabs/components/uv-tabs/uv-tabs.vue
ledApp/uni_modules/uv-tabs/package.json
ledApp/uni_modules/uv-tabs/readme.md
ledApp/uni_modules/uv-tags/changelog.md
ledApp/uni_modules/uv-tags/components/uv-tags/props.js
ledApp/uni_modules/uv-tags/components/uv-tags/uv-tags.vue
ledApp/uni_modules/uv-tags/package.json
ledApp/uni_modules/uv-tags/readme.md
ledApp/uni_modules/uv-text/changelog.md
ledApp/uni_modules/uv-text/components/uv-text/props.js
ledApp/uni_modules/uv-text/components/uv-text/uv-text.vue
ledApp/uni_modules/uv-text/components/uv-text/value.js
ledApp/uni_modules/uv-text/package.json
ledApp/uni_modules/uv-text/readme.md
ledApp/uni_modules/uv-textarea/changelog.md
ledApp/uni_modules/uv-textarea/components/uv-textarea/props.js
ledApp/uni_modules/uv-textarea/components/uv-textarea/uv-textarea.vue
ledApp/uni_modules/uv-textarea/package.json
ledApp/uni_modules/uv-textarea/readme.md
ledApp/uni_modules/uv-toast/changelog.md
ledApp/uni_modules/uv-toast/components/uv-toast/uv-toast.vue
ledApp/uni_modules/uv-toast/package.json
ledApp/uni_modules/uv-toast/readme.md
ledApp/uni_modules/uv-toolbar/changelog.md
ledApp/uni_modules/uv-toolbar/components/uv-toolbar/props.js
ledApp/uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue
ledApp/uni_modules/uv-toolbar/package.json
ledApp/uni_modules/uv-toolbar/readme.md
ledApp/uni_modules/uv-tooltip/changelog.md
ledApp/uni_modules/uv-tooltip/components/uv-tooltip/props.js
ledApp/uni_modules/uv-tooltip/components/uv-tooltip/uv-tooltip.vue
ledApp/uni_modules/uv-tooltip/package.json
ledApp/uni_modules/uv-tooltip/readme.md
ledApp/uni_modules/uv-transition/changelog.md
ledApp/uni_modules/uv-transition/components/uv-transition/createAnimation.js
ledApp/uni_modules/uv-transition/components/uv-transition/props.js
ledApp/uni_modules/uv-transition/components/uv-transition/uv-transition.vue
ledApp/uni_modules/uv-transition/package.json
ledApp/uni_modules/uv-transition/readme.md
ledApp/uni_modules/uv-ui-tools/changelog.md
ledApp/uni_modules/uv-ui-tools/components/uv-ui-tools/uv-ui-tools.vue
ledApp/uni_modules/uv-ui-tools/index.js
ledApp/uni_modules/uv-ui-tools/index.scss
ledApp/uni_modules/uv-ui-tools/libs/config/config.js
ledApp/uni_modules/uv-ui-tools/libs/css/color.scss
ledApp/uni_modules/uv-ui-tools/libs/css/common.scss
ledApp/uni_modules/uv-ui-tools/libs/css/components.scss
ledApp/uni_modules/uv-ui-tools/libs/css/variable.scss
ledApp/uni_modules/uv-ui-tools/libs/css/vue.scss
ledApp/uni_modules/uv-ui-tools/libs/function/colorGradient.js
ledApp/uni_modules/uv-ui-tools/libs/function/debounce.js
ledApp/uni_modules/uv-ui-tools/libs/function/digit.js
ledApp/uni_modules/uv-ui-tools/libs/function/index.js
ledApp/uni_modules/uv-ui-tools/libs/function/platform.js
ledApp/uni_modules/uv-ui-tools/libs/function/test.js
ledApp/uni_modules/uv-ui-tools/libs/function/throttle.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/adapters/index.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/core/InterceptorManager.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/core/Request.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/core/buildFullPath.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/core/defaults.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/core/dispatchRequest.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/core/mergeConfig.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/core/settle.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/helpers/buildURL.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/helpers/combineURLs.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/helpers/isAbsoluteURL.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/index.d.ts
ledApp/uni_modules/uv-ui-tools/libs/luch-request/index.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/utils.js
ledApp/uni_modules/uv-ui-tools/libs/luch-request/utils/clone.js
ledApp/uni_modules/uv-ui-tools/libs/mixin/button.js
ledApp/uni_modules/uv-ui-tools/libs/mixin/mixin.js
ledApp/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js
ledApp/uni_modules/uv-ui-tools/libs/mixin/mpShare.js
ledApp/uni_modules/uv-ui-tools/libs/mixin/openType.js
ledApp/uni_modules/uv-ui-tools/libs/mixin/touch.js
ledApp/uni_modules/uv-ui-tools/libs/util/dayjs.js
ledApp/uni_modules/uv-ui-tools/libs/util/route.js
ledApp/uni_modules/uv-ui-tools/package.json
ledApp/uni_modules/uv-ui-tools/readme.md
ledApp/uni_modules/uv-ui-tools/theme.scss
ledApp/uni_modules/uv-ui/changelog.md
ledApp/uni_modules/uv-ui/components/uv-ui/uv-ui.vue
ledApp/uni_modules/uv-ui/package.json
ledApp/uni_modules/uv-ui/readme.md
ledApp/uni_modules/uv-upload/changelog.md
ledApp/uni_modules/uv-upload/components/uv-preview-video/uv-preview-video.vue
ledApp/uni_modules/uv-upload/components/uv-upload/mixin.js
ledApp/uni_modules/uv-upload/components/uv-upload/props.js
ledApp/uni_modules/uv-upload/components/uv-upload/utils.js
ledApp/uni_modules/uv-upload/components/uv-upload/uv-upload.vue
ledApp/uni_modules/uv-upload/package.json
ledApp/uni_modules/uv-upload/readme.md
ledApp/uni_modules/uv-vtabs/changelog.md
ledApp/uni_modules/uv-vtabs/components/uv-vtabs-item/uv-vtabs-item.vue
ledApp/uni_modules/uv-vtabs/components/uv-vtabs/props.js
ledApp/uni_modules/uv-vtabs/components/uv-vtabs/uv-vtabs.vue
ledApp/uni_modules/uv-vtabs/package.json
ledApp/uni_modules/uv-vtabs/readme.md
ledApp/uni_modules/uv-waterfall/changelog.md
ledApp/uni_modules/uv-waterfall/components/uv-waterfall/props.js
ledApp/uni_modules/uv-waterfall/components/uv-waterfall/uv-waterfall.vue
ledApp/uni_modules/uv-waterfall/package.json
ledApp/uni_modules/uv-waterfall/readme.md
ledApp/uni_modules/x-color-picker/changelog.md
ledApp/uni_modules/x-color-picker/components/x-color-picker/x-color-picker.vue
ledApp/uni_modules/x-color-picker/package.json
ledApp/uni_modules/x-color-picker/readme.md |