| | |
| | | |
| | | # dependencies |
| | | **/node_modules |
| | | **/uni_modules |
| | | |
| | | |
| | | # Libraries |
| | | *.lib |
New file |
| | |
| | | ## 2.0(2023-09-02) |
| | | 组件优化重构 |
| | | ## 1.0.0(2023-06-20) |
| | | 无 |
New file |
| | |
| | | <template> |
| | | <view> |
| | | <view class="tabContent " :class="{'flex': !isTrue }" v-show="true"> |
| | | <view style="width: 50%;" v-for="(item, index) in tableData" :key="index"> |
| | | <view class="paramBox "> |
| | | <!-- <view class="param-title">{{item.name}}</view> --> |
| | | <view v-for="(subItem, subIndex) in item.list" :key="subIndex"> |
| | | <view class="param-item"> |
| | | <label style="padding-left: .1rem;background-color: #5879a4;color: #fff;">{{subItem.name}}</label> |
| | | <text class="font-family" style="padding-left: .2rem;background-color: #ebf3fe;flex-grow: 1;">{{subItem.value}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- <view v-if="isTrue " style="height: 30rpx;"></view> --> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | export default { |
| | | props: { |
| | | tableData: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | isTrue: { |
| | | true: Boolean, |
| | | defalut() { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | }, |
| | | data() { |
| | | return { |
| | | |
| | | } |
| | | }, |
| | | methods: { |
| | | |
| | | |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | view { |
| | | |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .tabContent { |
| | | background: #fff; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .param-title { |
| | | background: #f5f5f5; |
| | | height: 80rpx; |
| | | line-height: 80rpx; |
| | | margin: 20rpx 30rpx 0; |
| | | font-size: 28rpx; |
| | | text-indent: 30rpx; |
| | | } |
| | | |
| | | .param-item { |
| | | height: .3rem; |
| | | line-height: .3rem; |
| | | font-size: .14rem; |
| | | border-bottom: .02rem solid #fff; |
| | | margin: 0 4rpx; |
| | | /* border-left: 1rpx solid #f5f5f5; |
| | | border-right: 1rpx solid #f5f5f5; */ |
| | | /* padding: 0 30rpx; */ |
| | | display: flex; |
| | | } |
| | | |
| | | .param-item label { |
| | | width: 1.55rem; |
| | | color: #999; |
| | | margin-right: .03rem !important; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "cc-defineTable", |
| | | "displayName": "自定义列表表格信息展示 可用于商品规格参数展示", |
| | | "version": "2.0", |
| | | "description": "自定义列表表格信息展示 可用于商品规格参数展示", |
| | | "keywords": [ |
| | | "表格", |
| | | "excel", |
| | | "table", |
| | | "list", |
| | | "商品规格" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.7.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "y", |
| | | "快手": "y", |
| | | "飞书": "y", |
| | | "京东": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "y", |
| | | "联盟": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | # cc-defineTable |
| | | |
| | | #### 使用方法 |
| | | |
| | | ```使用方法 |
| | | |
| | | <!-- tableData:表格数组 数组里对象可自定义字段 --> |
| | | <cc-defineTable :tableData="tableArr"></cc-defineTable> |
| | | |
| | | ``` |
| | | |
| | | #### HTML代码实现部分 |
| | | ```html |
| | | <template> |
| | | <view class="content"> |
| | | |
| | | <!-- 自定义顶部搜索框 用于搜索跳转 skipUrl:跳转url为绝对路径 /pages开头 --> |
| | | <cc-headSearch skipUrl="/pages/index/search"></cc-headSearch> |
| | | |
| | | <!-- table-list:表格数组 数组里对象可自定义字段 --> |
| | | <cc-defineTable :tableData="tableArr"></cc-defineTable> |
| | | |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | components: { |
| | | |
| | | }, |
| | | |
| | | data() { |
| | | return { |
| | | |
| | | tableArr: [{ |
| | | 'name': '基本信息', |
| | | 'list': [{ |
| | | 'name': '品牌', |
| | | 'value': '苹果' |
| | | }, |
| | | { |
| | | 'name': '型号', |
| | | 'value': 'iphoneX' |
| | | }, |
| | | { |
| | | 'name': '尺寸', |
| | | 'value': '152 * 73 * 6.5mm' |
| | | }, |
| | | { |
| | | 'name': '电池容量', |
| | | 'value': '3600mAh' |
| | | }, |
| | | { |
| | | 'name': '重量', |
| | | 'value': '166g' |
| | | } |
| | | ] |
| | | |
| | | }, |
| | | { |
| | | 'name': "详细信息", |
| | | 'list': [{ |
| | | 'name': '品牌', |
| | | 'value': '苹果' |
| | | }, |
| | | { |
| | | 'name': '型号', |
| | | 'value': 'iphoneX' |
| | | }, |
| | | { |
| | | 'name': '尺寸', |
| | | 'value': '173 * 23 * 6.5mm' |
| | | }, |
| | | { |
| | | 'name': '电池容量', |
| | | 'value': '3600mAh' |
| | | }, |
| | | { |
| | | 'name': '重量', |
| | | 'value': '166g' |
| | | } |
| | | ] |
| | | } |
| | | ], |
| | | |
| | | } |
| | | }, |
| | | mounted() { |
| | | |
| | | }, |
| | | methods: { |
| | | |
| | | |
| | | |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | page { |
| | | |
| | | background-color: #f7f7f7; |
| | | } |
| | | |
| | | .content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | |
| | | } |
| | | </style> |
| | | ``` |
New file |
| | |
| | | ## 1.0.0(2023-07-05) |
| | | 组件发布 |
New file |
| | |
| | | <template> |
| | | <view :class="{'uni-scroll-tab': scroll === true }" class="uni-tab "> |
| | | |
| | | <view v-for="(tab,index) in tabList" :key="index" |
| | | :class="{ 'uni-tab-active': index === value, 'uni-tab-scroll-item': scroll === true, ' uni-tab-scroll-active': index === value && scroll === true }" |
| | | :style="[{color:index === value ? activeColor : defaultColor,backgroundColor: bgColor}]" |
| | | @tap="itemClick(index,tab)" class="uni-tab-item"> |
| | | <span v-if="tab.icon != undefined" class="iconfont mr5" :class="tab.icon"></span> |
| | | |
| | | <text>{{rangeKey == '' ? tab.TabControl : tab[rangeKey]}}</text> |
| | | </view> |
| | | <view v-if="!scroll" :style="[{ right: barRight + '%', left : barLeft + '%', borderColor: activeColor }]" |
| | | class="uni-tab-bar" :class="back ? 'uni-tab-bar-backward' : 'uni-tab-bar-forward'"></view> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | export default { |
| | | name: 'uni-tab', |
| | | data() { |
| | | return { |
| | | average: 0, |
| | | back: false |
| | | }; |
| | | }, |
| | | props: { |
| | | value: { |
| | | type: Number, //当前选中下标 |
| | | default () { |
| | | return 0; |
| | | } |
| | | }, |
| | | tabList: { |
| | | type: Array, |
| | | default () { |
| | | return []; |
| | | } |
| | | }, |
| | | bgColor: { //背景颜色 |
| | | type: String, |
| | | default () { |
| | | return '#FFFFFF'; |
| | | } |
| | | }, |
| | | defaultColor: { //默认未选中文字颜色 |
| | | type: String, |
| | | default () { |
| | | return '#636363'; |
| | | } |
| | | }, |
| | | activeColor: { //选中时文字颜色 线条颜色 |
| | | type: String, |
| | | default () { |
| | | return '#000000'; |
| | | } |
| | | }, |
| | | rangeKey: { // 当tabList为对象时 显示指定下标值 |
| | | type: String, |
| | | default () { |
| | | return ''; |
| | | } |
| | | }, |
| | | scroll: { //横向滑动 |
| | | type: Boolean, |
| | | default () { |
| | | return false; |
| | | } |
| | | }, |
| | | }, |
| | | computed: { |
| | | barLeft() { |
| | | return this.value * this.average; |
| | | }, |
| | | barRight() { |
| | | let index = this.tabList.length - this.value - 1; |
| | | console.log(this.tabList.length,index * this.average); |
| | | return index * this.average; |
| | | }, |
| | | }, |
| | | created() { |
| | | this.average = 100 / this.tabList.length; |
| | | }, |
| | | methods: { |
| | | itemClick(index, tab) { |
| | | if (this.value == index) return false; |
| | | if (this.value > index) { |
| | | this.back = true; |
| | | } else { |
| | | this.back = false; |
| | | } |
| | | // this.value = index; |
| | | this.$emit('update:value', index); |
| | | this.$emit('change', { |
| | | tab: tab |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | @import "../../static/iconfont.css"; |
| | | |
| | | .uni-tab { |
| | | position: relative; |
| | | display: flex; |
| | | font-size: 14px; |
| | | height: 44px; |
| | | line-height: 44px; |
| | | // sdsd |
| | | // justify-content: center; |
| | | .uni-tab-item { |
| | | flex: 1; |
| | | // width: 20% !important; |
| | | height: 100%; |
| | | text-align: center; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-tab-scroll-item { |
| | | flex: none; |
| | | padding: 0px 12px; |
| | | } |
| | | |
| | | .uni-tab-active { |
| | | |
| | | color: #1e9fff; |
| | | } |
| | | |
| | | .uni-tab-scroll-active { |
| | | border-bottom: 3px solid #1e9fff; |
| | | } |
| | | |
| | | .uni-tab-bar { |
| | | display: block; |
| | | height: 3px; |
| | | position: absolute; |
| | | bottom: 0; |
| | | border-bottom: 3px solid #1e9fff; |
| | | } |
| | | |
| | | .uni-tab-bar-forward { |
| | | transition: right 0.3s cubic-bezier(0.35, 0, 0.25, 1), left 0.3s cubic-bezier(0.35, 0, 0.25, 1) 0.09s; |
| | | } |
| | | |
| | | .uni-tab-bar-backward { |
| | | transition: right 0.3s cubic-bezier(0.35, 0, 0.25, 1) 0.09s, left 0.3s cubic-bezier(0.35, 0, 0.25, 1); |
| | | } |
| | | } |
| | | |
| | | .uni-scroll-tab { |
| | | overflow-x: scroll; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "csr-tab", |
| | | "displayName": "tab选项卡", |
| | | "version": "1.0.0", |
| | | "description": "兼容性好,简单、易用的选项卡 。", |
| | | "keywords": [ |
| | | "选项卡", |
| | | "兼容", |
| | | "易用", |
| | | "简单" |
| | | ], |
| | | "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": [], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "u" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "u" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "u", |
| | | "Edge": "u", |
| | | "Firefox": "u", |
| | | "Safari": "u" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "u", |
| | | "百度": "u", |
| | | "字节跳动": "u", |
| | | "QQ": "u", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | # csr-tab |
| | | 具体用法请下载示例查看 |
| | | # 属性 |
| | | |属性名 |类型 |默认值 |说明 | |
| | | |:-: |:-: |:-: |:-: | |
| | | |value |Number |0 |默认下标(双向绑定) | |
| | | |tabList |Array |[] |可以是一维数组或是数组对象 | |
| | | |bgColor |String |#FFFFFF|背景颜色 | |
| | | |defaultColor |String |#000000|默认未选中文字颜色 | |
| | | |activeColor |String |#1e9fff|选中时文字颜色 线条颜色 | |
| | | |scroll |Boolean|false |横向滑动 | |
| | | |rangeKey |String |'' |当tabList为数组对象时指定显示对象key | |
| | | |
| | | # 事件 |
| | | |事件名 |说明 | |
| | | |:-: |:-: | |
| | | |change |点击事件时触发,返回对应数据| |
New file |
| | |
| | | /* Logo 字体 */ |
| | | @font-face { |
| | | font-family: "iconfont logo"; |
| | | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); |
| | | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), |
| | | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), |
| | | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), |
| | | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); |
| | | } |
| | | |
| | | .logo { |
| | | font-family: "iconfont logo"; |
| | | font-size: 160px; |
| | | font-style: normal; |
| | | -webkit-font-smoothing: antialiased; |
| | | -moz-osx-font-smoothing: grayscale; |
| | | } |
| | | |
| | | /* tabs */ |
| | | .nav-tabs { |
| | | position: relative; |
| | | } |
| | | |
| | | .nav-tabs .nav-more { |
| | | position: absolute; |
| | | right: 0; |
| | | bottom: 0; |
| | | height: 42px; |
| | | line-height: 42px; |
| | | color: #666; |
| | | } |
| | | |
| | | #tabs { |
| | | border-bottom: 1px solid #eee; |
| | | } |
| | | |
| | | #tabs li { |
| | | cursor: pointer; |
| | | width: 100px; |
| | | height: 40px; |
| | | line-height: 40px; |
| | | text-align: center; |
| | | font-size: 16px; |
| | | border-bottom: 2px solid transparent; |
| | | position: relative; |
| | | z-index: 1; |
| | | margin-bottom: -1px; |
| | | color: #666; |
| | | } |
| | | |
| | | |
| | | #tabs .active { |
| | | border-bottom-color: #f00; |
| | | color: #222; |
| | | } |
| | | |
| | | .tab-container .content { |
| | | display: none; |
| | | } |
| | | |
| | | /* 页面布局 */ |
| | | .main { |
| | | padding: 30px 100px; |
| | | width: 960px; |
| | | margin: 0 auto; |
| | | } |
| | | |
| | | .main .logo { |
| | | color: #333; |
| | | text-align: left; |
| | | margin-bottom: 30px; |
| | | line-height: 1; |
| | | height: 110px; |
| | | margin-top: -50px; |
| | | overflow: hidden; |
| | | *zoom: 1; |
| | | } |
| | | |
| | | .main .logo a { |
| | | font-size: 160px; |
| | | color: #333; |
| | | } |
| | | |
| | | .helps { |
| | | margin-top: 40px; |
| | | } |
| | | |
| | | .helps pre { |
| | | padding: 20px; |
| | | margin: 10px 0; |
| | | border: solid 1px #e7e1cd; |
| | | background-color: #fffdef; |
| | | overflow: auto; |
| | | } |
| | | |
| | | .icon_lists { |
| | | width: 100% !important; |
| | | overflow: hidden; |
| | | *zoom: 1; |
| | | } |
| | | |
| | | .icon_lists li { |
| | | width: 100px; |
| | | margin-bottom: 10px; |
| | | margin-right: 20px; |
| | | text-align: center; |
| | | list-style: none !important; |
| | | cursor: default; |
| | | } |
| | | |
| | | .icon_lists li .code-name { |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | .icon_lists .icon { |
| | | display: block; |
| | | height: 100px; |
| | | line-height: 100px; |
| | | font-size: 42px; |
| | | margin: 10px auto; |
| | | color: #333; |
| | | -webkit-transition: font-size 0.25s linear, width 0.25s linear; |
| | | -moz-transition: font-size 0.25s linear, width 0.25s linear; |
| | | transition: font-size 0.25s linear, width 0.25s linear; |
| | | } |
| | | |
| | | .icon_lists .icon:hover { |
| | | font-size: 100px; |
| | | } |
| | | |
| | | .icon_lists .svg-icon { |
| | | /* 通过设置 font-size 来改变图标大小 */ |
| | | width: 1em; |
| | | /* 图标和文字相邻时,垂直对齐 */ |
| | | vertical-align: -0.15em; |
| | | /* 通过设置 color 来改变 SVG 的颜色/fill */ |
| | | fill: currentColor; |
| | | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 |
| | | normalize.css 中也包含这行 */ |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .icon_lists li .name, |
| | | .icon_lists li .code-name { |
| | | color: #666; |
| | | } |
| | | |
| | | /* markdown 样式 */ |
| | | .markdown { |
| | | color: #666; |
| | | font-size: 14px; |
| | | line-height: 1.8; |
| | | } |
| | | |
| | | .highlight { |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | .markdown img { |
| | | vertical-align: middle; |
| | | max-width: 100%; |
| | | } |
| | | |
| | | .markdown h1 { |
| | | color: #404040; |
| | | font-weight: 500; |
| | | line-height: 40px; |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .markdown h2, |
| | | .markdown h3, |
| | | .markdown h4, |
| | | .markdown h5, |
| | | .markdown h6 { |
| | | color: #404040; |
| | | margin: 1.6em 0 0.6em 0; |
| | | font-weight: 500; |
| | | clear: both; |
| | | } |
| | | |
| | | .markdown h1 { |
| | | font-size: 28px; |
| | | } |
| | | |
| | | .markdown h2 { |
| | | font-size: 22px; |
| | | } |
| | | |
| | | .markdown h3 { |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .markdown h4 { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .markdown h5 { |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .markdown h6 { |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .markdown hr { |
| | | height: 1px; |
| | | border: 0; |
| | | background: #e9e9e9; |
| | | margin: 16px 0; |
| | | clear: both; |
| | | } |
| | | |
| | | .markdown p { |
| | | margin: 1em 0; |
| | | } |
| | | |
| | | .markdown>p, |
| | | .markdown>blockquote, |
| | | .markdown>.highlight, |
| | | .markdown>ol, |
| | | .markdown>ul { |
| | | width: 80%; |
| | | } |
| | | |
| | | .markdown ul>li { |
| | | list-style: circle; |
| | | } |
| | | |
| | | .markdown>ul li, |
| | | .markdown blockquote ul>li { |
| | | margin-left: 20px; |
| | | padding-left: 4px; |
| | | } |
| | | |
| | | .markdown>ul li p, |
| | | .markdown>ol li p { |
| | | margin: 0.6em 0; |
| | | } |
| | | |
| | | .markdown ol>li { |
| | | list-style: decimal; |
| | | } |
| | | |
| | | .markdown>ol li, |
| | | .markdown blockquote ol>li { |
| | | margin-left: 20px; |
| | | padding-left: 4px; |
| | | } |
| | | |
| | | .markdown code { |
| | | margin: 0 3px; |
| | | padding: 0 5px; |
| | | background: #eee; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | .markdown strong, |
| | | .markdown b { |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .markdown>table { |
| | | border-collapse: collapse; |
| | | border-spacing: 0px; |
| | | empty-cells: show; |
| | | border: 1px solid #e9e9e9; |
| | | width: 95%; |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .markdown>table th { |
| | | white-space: nowrap; |
| | | color: #333; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .markdown>table th, |
| | | .markdown>table td { |
| | | border: 1px solid #e9e9e9; |
| | | padding: 8px 16px; |
| | | text-align: left; |
| | | } |
| | | |
| | | .markdown>table th { |
| | | background: #F7F7F7; |
| | | } |
| | | |
| | | .markdown blockquote { |
| | | font-size: 90%; |
| | | color: #999; |
| | | border-left: 4px solid #e9e9e9; |
| | | padding-left: 0.8em; |
| | | margin: 1em 0; |
| | | } |
| | | |
| | | .markdown blockquote p { |
| | | margin: 0; |
| | | } |
| | | |
| | | .markdown .anchor { |
| | | opacity: 0; |
| | | transition: opacity 0.3s ease; |
| | | margin-left: 8px; |
| | | } |
| | | |
| | | .markdown .waiting { |
| | | color: #ccc; |
| | | } |
| | | |
| | | .markdown h1:hover .anchor, |
| | | .markdown h2:hover .anchor, |
| | | .markdown h3:hover .anchor, |
| | | .markdown h4:hover .anchor, |
| | | .markdown h5:hover .anchor, |
| | | .markdown h6:hover .anchor { |
| | | opacity: 1; |
| | | display: inline-block; |
| | | } |
| | | |
| | | .markdown>br, |
| | | .markdown>p>br { |
| | | clear: both; |
| | | } |
| | | |
| | | |
| | | .hljs { |
| | | display: block; |
| | | background: white; |
| | | padding: 0.5em; |
| | | color: #333333; |
| | | overflow-x: auto; |
| | | } |
| | | |
| | | .hljs-comment, |
| | | .hljs-meta { |
| | | color: #969896; |
| | | } |
| | | |
| | | .hljs-string, |
| | | .hljs-variable, |
| | | .hljs-template-variable, |
| | | .hljs-strong, |
| | | .hljs-emphasis, |
| | | .hljs-quote { |
| | | color: #df5000; |
| | | } |
| | | |
| | | .hljs-keyword, |
| | | .hljs-selector-tag, |
| | | .hljs-type { |
| | | color: #a71d5d; |
| | | } |
| | | |
| | | .hljs-literal, |
| | | .hljs-symbol, |
| | | .hljs-bullet, |
| | | .hljs-attribute { |
| | | color: #0086b3; |
| | | } |
| | | |
| | | .hljs-section, |
| | | .hljs-name { |
| | | color: #63a35c; |
| | | } |
| | | |
| | | .hljs-tag { |
| | | color: #333333; |
| | | } |
| | | |
| | | .hljs-title, |
| | | .hljs-attr, |
| | | .hljs-selector-id, |
| | | .hljs-selector-class, |
| | | .hljs-selector-attr, |
| | | .hljs-selector-pseudo { |
| | | color: #795da3; |
| | | } |
| | | |
| | | .hljs-addition { |
| | | color: #55a532; |
| | | background-color: #eaffea; |
| | | } |
| | | |
| | | .hljs-deletion { |
| | | color: #bd2c00; |
| | | background-color: #ffecec; |
| | | } |
| | | |
| | | .hljs-link { |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | /* 代码高亮 */ |
| | | /* PrismJS 1.15.0 |
| | | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ |
| | | /** |
| | | * prism.js default theme for JavaScript, CSS and HTML |
| | | * Based on dabblet (http://dabblet.com) |
| | | * @author Lea Verou |
| | | */ |
| | | code[class*="language-"], |
| | | pre[class*="language-"] { |
| | | color: black; |
| | | background: none; |
| | | text-shadow: 0 1px white; |
| | | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; |
| | | text-align: left; |
| | | white-space: pre; |
| | | word-spacing: normal; |
| | | word-break: normal; |
| | | word-wrap: normal; |
| | | line-height: 1.5; |
| | | |
| | | -moz-tab-size: 4; |
| | | -o-tab-size: 4; |
| | | tab-size: 4; |
| | | |
| | | -webkit-hyphens: none; |
| | | -moz-hyphens: none; |
| | | -ms-hyphens: none; |
| | | hyphens: none; |
| | | } |
| | | |
| | | pre[class*="language-"]::-moz-selection, |
| | | pre[class*="language-"] ::-moz-selection, |
| | | code[class*="language-"]::-moz-selection, |
| | | code[class*="language-"] ::-moz-selection { |
| | | text-shadow: none; |
| | | background: #b3d4fc; |
| | | } |
| | | |
| | | pre[class*="language-"]::selection, |
| | | pre[class*="language-"] ::selection, |
| | | code[class*="language-"]::selection, |
| | | code[class*="language-"] ::selection { |
| | | text-shadow: none; |
| | | background: #b3d4fc; |
| | | } |
| | | |
| | | @media print { |
| | | |
| | | code[class*="language-"], |
| | | pre[class*="language-"] { |
| | | text-shadow: none; |
| | | } |
| | | } |
| | | |
| | | /* Code blocks */ |
| | | pre[class*="language-"] { |
| | | padding: 1em; |
| | | margin: .5em 0; |
| | | overflow: auto; |
| | | } |
| | | |
| | | :not(pre)>code[class*="language-"], |
| | | pre[class*="language-"] { |
| | | background: #f5f2f0; |
| | | } |
| | | |
| | | /* Inline code */ |
| | | :not(pre)>code[class*="language-"] { |
| | | padding: .1em; |
| | | border-radius: .3em; |
| | | white-space: normal; |
| | | } |
| | | |
| | | .token.comment, |
| | | .token.prolog, |
| | | .token.doctype, |
| | | .token.cdata { |
| | | color: slategray; |
| | | } |
| | | |
| | | .token.punctuation { |
| | | color: #999; |
| | | } |
| | | |
| | | .namespace { |
| | | opacity: .7; |
| | | } |
| | | |
| | | .token.property, |
| | | .token.tag, |
| | | .token.boolean, |
| | | .token.number, |
| | | .token.constant, |
| | | .token.symbol, |
| | | .token.deleted { |
| | | color: #905; |
| | | } |
| | | |
| | | .token.selector, |
| | | .token.attr-name, |
| | | .token.string, |
| | | .token.char, |
| | | .token.builtin, |
| | | .token.inserted { |
| | | color: #690; |
| | | } |
| | | |
| | | .token.operator, |
| | | .token.entity, |
| | | .token.url, |
| | | .language-css .token.string, |
| | | .style .token.string { |
| | | color: #9a6e3a; |
| | | background: hsla(0, 0%, 100%, .5); |
| | | } |
| | | |
| | | .token.atrule, |
| | | .token.attr-value, |
| | | .token.keyword { |
| | | color: #07a; |
| | | } |
| | | |
| | | .token.function, |
| | | .token.class-name { |
| | | color: #DD4A68; |
| | | } |
| | | |
| | | .token.regex, |
| | | .token.important, |
| | | .token.variable { |
| | | color: #e90; |
| | | } |
| | | |
| | | .token.important, |
| | | .token.bold { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .token.italic { |
| | | font-style: italic; |
| | | } |
| | | |
| | | .token.entity { |
| | | cursor: help; |
| | | } |
New file |
| | |
| | | <!DOCTYPE html> |
| | | <html> |
| | | <head> |
| | | <meta charset="utf-8"/> |
| | | <title>IconFont Demo</title> |
| | | <link rel="shortcut icon" href="https://gtms04.alicdn.com/tps/i4/TB1_oz6GVXXXXaFXpXXJDFnIXXX-64-64.ico" type="image/x-icon"/> |
| | | <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css"> |
| | | <link rel="stylesheet" href="demo.css"> |
| | | <link rel="stylesheet" href="iconfont.css"> |
| | | <script src="iconfont.js"></script> |
| | | <!-- jQuery --> |
| | | <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script> |
| | | <!-- 代码高亮 --> |
| | | <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script> |
| | | </head> |
| | | <body> |
| | | <div class="main"> |
| | | <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank"></a></h1> |
| | | <div class="nav-tabs"> |
| | | <ul id="tabs" class="dib-box"> |
| | | <li class="dib active"><span>Unicode</span></li> |
| | | <li class="dib"><span>Font class</span></li> |
| | | <li class="dib"><span>Symbol</span></li> |
| | | </ul> |
| | | |
| | | <a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=1289328" target="_blank" class="nav-more">查看项目</a> |
| | | |
| | | </div> |
| | | <div class="tab-container"> |
| | | <div class="content unicode" style="display: block;"> |
| | | <ul class="icon_lists dib-box"> |
| | | |
| | | <li class="dib"> |
| | | <span class="icon iconfont"></span> |
| | | <div class="name">儿童</div> |
| | | <div class="code-name">&#xe60a;</div> |
| | | </li> |
| | | |
| | | <li class="dib"> |
| | | <span class="icon iconfont"></span> |
| | | <div class="name">男</div> |
| | | <div class="code-name">&#xe60e;</div> |
| | | </li> |
| | | |
| | | <li class="dib"> |
| | | <span class="icon iconfont"></span> |
| | | <div class="name">女</div> |
| | | <div class="code-name">&#xe64b;</div> |
| | | </li> |
| | | |
| | | </ul> |
| | | <div class="article markdown"> |
| | | <h2 id="unicode-">Unicode 引用</h2> |
| | | <hr> |
| | | |
| | | <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p> |
| | | <ul> |
| | | <li>兼容性最好,支持 IE6+,及所有现代浏览器。</li> |
| | | <li>支持按字体的方式去动态调整图标大小,颜色等等。</li> |
| | | <li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li> |
| | | </ul> |
| | | <blockquote> |
| | | <p>注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式</p> |
| | | </blockquote> |
| | | <p>Unicode 使用步骤如下:</p> |
| | | <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3> |
| | | <pre><code class="language-css" |
| | | >@font-face { |
| | | font-family: 'iconfont'; |
| | | src: url('iconfont.eot'); |
| | | src: url('iconfont.eot?#iefix') format('embedded-opentype'), |
| | | url('iconfont.woff2') format('woff2'), |
| | | url('iconfont.woff') format('woff'), |
| | | url('iconfont.ttf') format('truetype'), |
| | | url('iconfont.svg#iconfont') format('svg'); |
| | | } |
| | | </code></pre> |
| | | <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3> |
| | | <pre><code class="language-css" |
| | | >.iconfont { |
| | | font-family: "iconfont" !important; |
| | | font-size: 16px; |
| | | font-style: normal; |
| | | -webkit-font-smoothing: antialiased; |
| | | -moz-osx-font-smoothing: grayscale; |
| | | } |
| | | </code></pre> |
| | | <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3> |
| | | <pre> |
| | | <code class="language-html" |
| | | ><span class="iconfont">&#x33;</span> |
| | | </code></pre> |
| | | <blockquote> |
| | | <p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p> |
| | | </blockquote> |
| | | </div> |
| | | </div> |
| | | <div class="content font-class"> |
| | | <ul class="icon_lists dib-box"> |
| | | |
| | | <li class="dib"> |
| | | <span class="icon iconfont icon10"></span> |
| | | <div class="name"> |
| | | 儿童 |
| | | </div> |
| | | <div class="code-name">.icon10 |
| | | </div> |
| | | </li> |
| | | |
| | | <li class="dib"> |
| | | <span class="icon iconfont iconnan"></span> |
| | | <div class="name"> |
| | | 男 |
| | | </div> |
| | | <div class="code-name">.iconnan |
| | | </div> |
| | | </li> |
| | | |
| | | <li class="dib"> |
| | | <span class="icon iconfont iconziyuan"></span> |
| | | <div class="name"> |
| | | 女 |
| | | </div> |
| | | <div class="code-name">.iconziyuan |
| | | </div> |
| | | </li> |
| | | |
| | | </ul> |
| | | <div class="article markdown"> |
| | | <h2 id="font-class-">font-class 引用</h2> |
| | | <hr> |
| | | |
| | | <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p> |
| | | <p>与 Unicode 使用方式相比,具有如下特点:</p> |
| | | <ul> |
| | | <li>兼容性良好,支持 IE8+,及所有现代浏览器。</li> |
| | | <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li> |
| | | <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li> |
| | | <li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li> |
| | | </ul> |
| | | <p>使用步骤如下:</p> |
| | | <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3> |
| | | <pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css"> |
| | | </code></pre> |
| | | <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3> |
| | | <pre><code class="language-html"><span class="iconfont iconxxx"></span> |
| | | </code></pre> |
| | | <blockquote> |
| | | <p>" |
| | | iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p> |
| | | </blockquote> |
| | | </div> |
| | | </div> |
| | | <div class="content symbol"> |
| | | <ul class="icon_lists dib-box"> |
| | | |
| | | <li class="dib"> |
| | | <svg class="icon svg-icon" aria-hidden="true"> |
| | | <use xlink:href="#icon10"></use> |
| | | </svg> |
| | | <div class="name">儿童</div> |
| | | <div class="code-name">#icon10</div> |
| | | </li> |
| | | |
| | | <li class="dib"> |
| | | <svg class="icon svg-icon" aria-hidden="true"> |
| | | <use xlink:href="#iconnan"></use> |
| | | </svg> |
| | | <div class="name">男</div> |
| | | <div class="code-name">#iconnan</div> |
| | | </li> |
| | | |
| | | <li class="dib"> |
| | | <svg class="icon svg-icon" aria-hidden="true"> |
| | | <use xlink:href="#iconziyuan"></use> |
| | | </svg> |
| | | <div class="name">女</div> |
| | | <div class="code-name">#iconziyuan</div> |
| | | </li> |
| | | |
| | | </ul> |
| | | <div class="article markdown"> |
| | | <h2 id="symbol-">Symbol 引用</h2> |
| | | <hr> |
| | | |
| | | <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a> |
| | | 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p> |
| | | <ul> |
| | | <li>支持多色图标了,不再受单色限制。</li> |
| | | <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li> |
| | | <li>兼容性较差,支持 IE9+,及现代浏览器。</li> |
| | | <li>浏览器渲染 SVG 的性能一般,还不如 png。</li> |
| | | </ul> |
| | | <p>使用步骤如下:</p> |
| | | <h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3> |
| | | <pre><code class="language-html"><script src="./iconfont.js"></script> |
| | | </code></pre> |
| | | <h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3> |
| | | <pre><code class="language-html"><style> |
| | | .icon { |
| | | width: 1em; |
| | | height: 1em; |
| | | vertical-align: -0.15em; |
| | | fill: currentColor; |
| | | overflow: hidden; |
| | | } |
| | | </style> |
| | | </code></pre> |
| | | <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3> |
| | | <pre><code class="language-html"><svg class="icon" aria-hidden="true"> |
| | | <use xlink:href="#icon-xxx"></use> |
| | | </svg> |
| | | </code></pre> |
| | | </div> |
| | | </div> |
| | | |
| | | </div> |
| | | </div> |
| | | <script> |
| | | $(document).ready(function () { |
| | | $('.tab-container .content:first').show() |
| | | |
| | | $('#tabs li').click(function (e) { |
| | | var tabContent = $('.tab-container .content') |
| | | var index = $(this).index() |
| | | |
| | | if ($(this).hasClass('active')) { |
| | | return |
| | | } else { |
| | | $('#tabs li').removeClass('active') |
| | | $(this).addClass('active') |
| | | |
| | | tabContent.hide().eq(index).fadeIn() |
| | | } |
| | | }) |
| | | }) |
| | | </script> |
| | | </body> |
| | | </html> |
New file |
| | |
| | | @font-face {font-family: "iconfont"; |
| | | src: url('~@/uni_modules/csr-tab/static/iconfont.eot?t=1566520238593'); /* IE9 */ |
| | | src: url('~@/uni_modules/csr-tab/static/iconfont.eot?t=1566520238593#iefix') format('embedded-opentype'), /* IE6-IE8 */ |
| | | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAbkAAsAAAAADFAAAAaVAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDHAqMGIlyATYCJAMQCwoABCAFhG0HOxtXClGUTlKG7EdiDM6jQ3O0qdwOgfRovg2i2O+3J19EkylavXliOkk0EoomiI2SmY6X8IZwWzeGtRn4DgZYM4uvQszaQGzEYTTy0UMb/tv6jCa+Iigmd78YMgsYu1pTG3QsHG5AZiJsrHjg+w9jgVnIqtraCgWIsrIRthVWskZ4Nj+p1q3W5RAgTlswaDc2mQTWOLwg6HFVXqoA1o4wHmEUor5wwKGOvAELovrCugDgVft8+UeUNSghg7/S8cWUAPEjz/Zg5j9D3jCAyS4JXI8CAyYADnIWmD4FG/xEQ/Gg9mzWAaJCUiGrZelne///G3RTUPREsC88xUDC8CgJadXRZPCFSEHkLdojiEJMgshAKr1/FGgAGkfkAfCaJlOUCElZZTUbm1qglUWk6WCrHOfCabyX5d0vQHEuJpPUbJYZjZTBMF7kSR23X8Bkku+6pg3q0a9lsE5mrR6XmUlyDennRdhkxK0oZo1unXa9fq2iV7eQuy65Qd0B6OpCe/DHrsyFCdtSTdL9ZndIj6LO5nPyVDyvIpXICLjcLi5YaBxsIC+EbwHAEdIWC0ipWdY5HKXSM2VSZy1kpFTLzXF6Lorvj9l58ajRRed7yjSCh+0xIDpeNwXtNg89aIwBCwzRyClTdsOCVFf58FreVolTXAmX1CEoikgxvJPEcaxauvp8EM7rNEnni0mTCSlBYAkhr4Fg3LVnfcgdR1zeAxYdq5lvlmm9uqxAGY18cbBgzZkBOh6vmye1WR+03rkHF1bDu/HlntA8IY+3DntgBvJENIJtAYwrd66gQZjcfm0AhmCdGL+294pzSmyFzndf4DyhMPZpFkFa99NxZtncgLkCsJTAevl8MbhmUOlK0E79wO4gJcT/E3WKnTBkhS/SXTQBvlvZQ+jX04PTHEi72U+Gc/PCE/T36eKiCEBDWr49juJ++P6gabEatXIj+DvsAPv3b7ZsPPD7V4r965e156/f6HX/+fCly/A8eP6lS/NV7MzDwDwPFsm3vJW/30eoiPVtfMBvKyzc7z7GfX1R4bv3YMt76YNAt3acg7e1tp3rtb+19d9nQJeNocCVAtP2eNenCFvnP89vnv86sg+s8H6OY81vi0JhPofypDh8kGW55Ppq3iPAgCsFj8viRAWYOEiCLEYQvrd1dmlTtm133AMJJJdDUU4SPsO//wNIkERcgMWJyh5/G2YntBu2+cztEXF5cWJBMhuEWgyJ/XBltsOw+H0sKJkt5o+fZ52UWG9dB4d/GxD1+KZTdF+uh09OoKS9DU13DUaWCD0eHlia4iLIyA3wXeY+UgwqgLzbBblFpKwy3nP/bJpTcnCOjtne2CISbP/jeMlDoRON2QazbNzY2QvAGK9sy8h0MYecHvYkxXeejvDOZRVZcDq9M71Tx6ZnVIHps4W6Qw5QYdArwrp5vimG6WHD1lnsmbrHYt2J5q3ORVD68H32+TVLRrOngUi9RwpnKgzSz//B1jHHYSvFMXHkiZ3FJdFbE1g7EwbVpqZBjm8Xs9IOYKIdl2u/Uz65nNePbIeAfn4tvKx6OCF+52f50WiII8k6efyL5ItB6hX+LuodZ+KV5XX1GZZ1Xn6rtq3Pdsj0fN3UNojVewOLqmdmX5kA5g7k/INsY207KIlI8qxzuWJmeUfMuR5W2Sb1MjMaZ7OMnyqObMzI/JGVRYtGe7dXzJQ2/KB/pcd2O9/sOTAic/GWhC1d305g2job9r3XWLb1nfYxa065BpsyzXHAMXH7S2dQBwDBcdsPjwQguGfTYb9HM2OnjDn4NmOw63cXOgz7GkXXviUfLeQmrRgwDmgiOIIoMnWoUcQ5/mF43r/zkMhX1iW9JdcrEBdicIC1Qt69LKQSmTrYpJKFIRuhBTCidOSunkCIBKsQJsoGxBmLOzpBgRqMeAwYaeFAyNoMSpp3MLIuyl19F0KUvYUw2WCIc5LTKRP0Kkei1LRCQ5cQxeOIcmVtjUqMaRC9ZyZdOqpKobYzmklKqxvKa4iI0HB3SxxdQ6uz2KNhdKlIo1ERKnVtNRHLXY2uqqol6tS1FbRSE1qm0dQNCQtTBe0ZqqytBgg71GgKGsW1BKHYOM9ySrVqqBbXQDwfn4lWapQqCuqICd+eUjS1BuUrJ0QIFU6Qx6Ea0oRV6d9gtFIiGpZUIaTharWqEWJZklYlNNYi1AVvVIGmpBGqLEF9nSHC2EIqKgotXqoew4jRNyl/ShcVE5cQuhQO1yhqLMeXjxulhgAAAAAA') format('woff2'), |
| | | url('~@/uni_modules/csr-tab/static/iconfont.woff?t=1566520238593') format('woff'), |
| | | url('~@/uni_modules/csr-tab/static/iconfont.ttf?t=1566520238593') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ |
| | | url('~@/uni_modules/csr-tab/static/iconfont.svg?t=1566520238593#iconfont') format('svg'); /* iOS 4.1- */ |
| | | } |
| | | |
| | | .iconfont { |
| | | font-family: "iconfont" !important; |
| | | font-size: 16px; |
| | | font-style: normal; |
| | | -webkit-font-smoothing: antialiased; |
| | | -moz-osx-font-smoothing: grayscale; |
| | | } |
| | | |
| | | .icon10:before { |
| | | content: "\e60a"; |
| | | } |
| | | |
| | | .iconnan:before { |
| | | content: "\e60e"; |
| | | } |
| | | |
| | | .iconziyuan:before { |
| | | content: "\e64b"; |
| | | } |
| | | |
New file |
| | |
| | | !function(i){var t,a='<svg><symbol id="icon10" viewBox="0 0 1024 1024"><path d="M509.611092 956.608124c-245.583255 0-445.370487-199.786209-445.370487-445.369464 0-245.569952 199.787232-445.363324 445.370487-445.363324 245.582231 0 445.381743 199.794395 445.381743 445.363324C954.992835 756.820891 755.193323 956.608124 509.611092 956.608124zM509.611092 144.469346c-202.24419 0-366.775454 164.526147-366.775454 366.769314s164.531264 366.775454 366.775454 366.775454c202.243167 0 366.788757-164.532287 366.788757-366.775454S711.854259 144.469346 509.611092 144.469346z" ></path><path d="M716.22787 494.007207c-28.756951 0-55.774281-11.205203-76.098165-31.531134-20.314675-20.326954-31.508621-47.344285-31.508621-76.087932 0-28.756951 11.193947-55.773258 31.519878-76.100212 15.350616-15.325034 40.231283-15.337313 55.582923 0.013303 15.33629 15.350616 15.33629 40.231283-0.014326 55.581899-5.486968 5.475711-8.493441 12.753465-8.493441 20.506033 0 7.739265 3.018753 15.030321 8.493441 20.518313 10.962679 10.962679 30.048363 10.962679 41.037648 0l96.196922-96.195899c15.349593-15.350616 40.219004-15.350616 55.56962 0 15.350616 15.338337 15.350616 40.23026 0 55.568596l-96.197946 96.196922C771.989871 482.802004 744.972541 494.007207 716.22787 494.007207z" ></path><path d="M521.468141 493.994927c-28.741601 0-55.773258-11.192923-76.100212-31.519878-41.943274-41.971927-41.933041-110.22952 0.013303-152.174841 15.350616-15.350616 40.219004-15.350616 55.568596 0 15.350616 15.337313 15.350616 40.231283 0 55.56962-11.308557 11.307533-11.308557 29.715789 0.014326 41.036625 10.949376 10.9504 30.060643 10.962679 41.037648-0.013303L677.493683 271.412526c15.35164-15.350616 40.21798-15.350616 55.568596 0 15.35164 15.337313 15.35164 40.231283 0 55.568596L597.569376 462.476073C577.243445 482.802004 550.213835 493.994927 521.468141 493.994927z" ></path><path d="M322.384943 493.994927c-28.755927 0-55.773258-11.192923-76.100212-31.519878-20.313651-20.326954-31.506575-47.344285-31.506575-76.087932 0-28.756951 11.192923-55.773258 31.519878-76.100212 15.350616-15.325034 40.231283-15.337313 55.580876 0.013303 15.338337 15.350616 15.338337 40.231283-0.011256 55.581899-5.489014 5.475711-8.495488 12.753465-8.495488 20.506033 0 7.739265 3.019777 15.030321 8.495488 20.518313 10.974959 10.962679 30.060643 10.962679 41.036625 0l135.492904-135.492904c15.35164-15.350616 40.219004-15.350616 55.568596 0 15.35164 15.337313 15.35164 40.231283 0 55.568596L398.472875 462.476073C378.145921 482.802004 351.129614 493.994927 322.384943 493.994927z" ></path><path d="M386.320091 590.498842c0 25.942859-21.042245 46.971801-46.997384 46.971801-25.941835 0-46.985104-21.029966-46.985104-46.971801 0-25.969465 21.042245-46.998407 46.985104-46.998407C365.276822 543.500435 386.320091 564.529377 386.320091 590.498842z" ></path><path d="M726.897883 590.498842c0 25.942859-21.043269 46.971801-46.998407 46.971801-25.942859 0-46.985104-21.029966-46.985104-46.971801 0-25.969465 21.042245-46.998407 46.985104-46.998407C705.853591 543.500435 726.897883 564.529377 726.897883 590.498842z" ></path><path d="M509.611092 808.655444c-50.107211 0-90.87573-37.443797-90.87573-83.454714l0-23.704888c0-21.708418 17.588587-39.295981 39.298028-39.295981 21.707394 0 39.297005 17.588587 39.297005 39.295981l0 23.704888c0.48607 1.174756 4.925173 4.859681 12.280698 4.859681 7.366781 0 11.806907-3.684926 12.331863-5.155417l-0.038886-23.409153c0-21.708418 17.588587-39.295981 39.297005-39.295981s39.298028 17.588587 39.298028 39.295981l0 23.704888C600.499102 771.211647 559.730583 808.655444 509.611092 808.655444z" ></path></symbol><symbol id="iconnan" viewBox="0 0 1024 1024"><path d="M877.1 801.8c-20.3-34.7-49.2-65.8-85.9-92.4-47.4-34.3-104.6-58.9-167-72.2 48-22.3 81.3-70.9 81.3-127.2V350.8c6.7 0.4 13.5 0.6 20.3 0.6 4.5 0 9-0.1 13.5-0.3l10.5-0.4 1.8-10.4c0.6-3.4 1.1-6.7 1.5-9.9 14-115.3-68.4-220.5-183.7-234.5-38.2-4.6-76.7 1.2-111.7 16.9-4.8-0.4-9.6-0.6-14.3-0.6-88 0-159.5 71.6-159.5 159.5 0 13 1.6 26 4.7 38.6l2.2 8.7 8.9 1.1c6.2 0.8 12.5 1.3 18.8 1.7V510c0 56.3 33.3 104.9 81.3 127.2-62.4 13.4-119.6 37.9-167 72.2-36.7 26.6-65.6 57.7-85.9 92.4-21.2 36.3-32 75-32 114.9v13H909v-13c0.1-39.9-10.7-78.6-31.9-114.9zM477.2 656.5l13.7 27.6-62.7 72.4-93.1-72.9c42.9-16.7 91-27.5 142-31-0.5 1.2-0.5 2.6 0.1 3.9z m33.7 44.2h4.6l59.5 71-64.1 49.6-62.3-48.8 62.3-71.8z m84.7 55l-60.8-72.5 11.8-26.9c0.6-1.3 0.5-2.6 0-3.7 51.1 3.4 99.2 14.2 142.1 30.9l-93.1 72.2z m-285.7-484c0-73.6 59.9-133.5 133.5-133.5 5 0 10.1 0.3 15.1 0.8l3.7 0.4 3.3-1.6c31.4-14.8 66.2-20.4 100.8-16.2 49 5.9 92.7 30.6 123.1 69.4 30 38.3 43.5 85.9 38.2 134.3-13.4 0.1-26.8-0.7-40-2.3-67.8-8.2-129.7-37.4-179.1-84.4l-8.5-8.1-8.9 7.6c-43.9 37.4-100 58-157.7 58-7.1 0-14.2-0.3-21.3-0.9-1.5-7.8-2.2-15.6-2.2-23.5zM344.4 510V321.9c56.1-2.3 110.2-22.2 154.5-56.7 50.8 45.3 112.9 73.8 180.6 83V510c0 62.9-51.2 114.1-114.1 114.1H458.6c-63 0-114.2-51.2-114.2-114.1z m-203 393.7c5.9-87 70.7-163.1 166.5-208.4l187.9 147.2c4.4 3.5 9.8 5.2 15.1 5.2 5.3 0 10.6-1.7 15-5.1L716 695.3c95.8 45.3 160.6 121.4 166.5 208.4H141.4z" fill="" ></path></symbol><symbol id="iconziyuan" viewBox="0 0 1024 1024"><path d="M436.415367 642.812951l2.061399-2.061399-2.061399 2.061399z m209.060224 31.951686l6.527764 2.061399z m-34.356651-57.032042l3.435665 3.607449z m-116.297266 182.090254a23.362523 23.362523 0 0 1-4.981714 0h9.104512z m34.356652 0h-4.122798 4.122798zM436.415367 642.812951l-1.717833 1.546049 1.202483-0.858916 2.061399-2.061399 3.435665-3.435665 1.717833-2.061399c-2.576749 2.920315-4.466365 4.981714-6.699547 6.87133z m-85.891629 3.779232l5.84063-1.374267 5.325281-1.546049-5.153497 1.374266z" ></path><path d="M885.972152 729.907063l-142.580104-47.41218a172.642174 172.642174 0 0 0 103.069955-58.921657l9.104513-10.478779-8.41738-10.994128S804.031538 544.209361 804.031538 413.997651c0-23.877873-2.576749-148.248952-52.565677-253.552088S619.02097 0 512 0C367.702063 0 268.067774 102.039255 232.680423 287.39339a709.636638 709.636638 0 0 0-12.540178 126.604261C220.140245 542.835095 178.74048 601.241402 177.70978 602.100319l-8.245596 10.994128 9.276296 10.478779a171.783258 171.783258 0 0 0 101.867472 58.921657l-142.580104 47.41218A116.469049 116.469049 0 0 0 58.492199 840.363697v183.636303h907.015602V840.363697a116.469049 116.469049 0 0 0-79.535649-110.456634zM213.097131 610.345915c13.914444-24.908572 41.571548-88.124811 41.571549-196.348264a669.954706 669.954706 0 0 1 12.024828-120.24828C289.025331 175.56249 350.523738 34.356652 512 34.356652c94.137225 0 164.911928 47.412179 208.373092 140.690488 46.896829 98.775373 49.301795 216.446905 49.301795 238.950511 0 108.223452 27.657105 171.783258 41.399765 196.348264a140.690488 140.690488 0 0 1-118.015098 39.681933q-7.214897 0-13.914444-2.061399l-6.699547-1.374266-6.012414-1.54605c-3.435665 0-6.699547-1.717833-10.135212-2.920315s-9.104513-3.092099-13.227311-4.809931-8.41738-3.779232-12.368395-5.840631a111.830901 111.830901 0 0 1-13.74266-8.589163 2.920315 2.920315 0 0 0-1.0307-1.0307l-4.294581-3.435665a221.256836 221.256836 0 0 0 49.817144-44.491864A249.772857 249.772857 0 0 0 689.967455 532.528099c3.435665-6.184197 6.527764-12.368395 9.448079-18.896158a305.430632 305.430632 0 0 0 11.853045-31.09277l4.466365-15.460493c0-1.202483 0-2.404966 1.030699-3.951015a312.13018 312.13018 0 0 0 5.153498-126.088911v-2.233182a292.890455 292.890455 0 0 0-17.178326-61.15484l-1.889616-4.638148-3.951014-2.920316a126.088911 126.088911 0 0 1-40.712633-52.73746l-6.87133-15.975843-15.975843 7.214897a807.381312 807.381312 0 0 1-178.311022 55.657776 857.542023 857.542023 0 0 1-126.260694 14.601577h-11.681262l-3.779231 11.165911a305.430632 305.430632 0 0 0-17.178326 101.008556 310.58413 310.58413 0 0 0 9.276296 76.787116l2.061399 7.558464a317.283677 317.283677 0 0 0 13.227311 37.792316l1.546049 3.435665q4.638148 10.135212 9.791646 19.583292a249.772857 249.772857 0 0 0 28.344237 41.571548 224.692501 224.692501 0 0 0 50.332495 44.663647 2.920315 2.920315 0 0 1-1.0307 1.0307 2.233182 2.233182 0 0 0-1.202482 1.030699 15.975843 15.975843 0 0 1-2.576749 2.233183h-1.202483l-1.546049 1.202482c-3.435665 2.404966-7.38668 4.638148-11.337695 6.871331l-13.055528 6.012414c-4.122798 1.717833-8.41738 3.263882-12.540178 4.638148l-5.153498 1.546049-5.325281 1.546049-5.84063 1.374266-6.699547 1.202483c-4.466365 0.858916-9.104513 1.546049-13.742661 2.061399A141.892971 141.892971 0 0 1 213.097131 610.345915z m366.413689-13.742661l-3.092098 1.54605a145.672203 145.672203 0 0 1-129.352793 0 179.857071 179.857071 0 0 1-58.234525-46.553263A242.386177 242.386177 0 0 1 364.094615 515.349774c-2.404966-4.466365-4.809931-9.104513-7.043114-13.914444l-1.546049-3.263882a286.190908 286.190908 0 0 1-13.055527-36.418051l-1.717833-6.699547a276.227479 276.227479 0 0 1-7.38668-67.682604 272.276464 272.276464 0 0 1 10.478779-78.161382 879.01493 879.01493 0 0 0 119.217581-14.77336 849.811777 849.811777 0 0 0 171.783257-51.534977 162.335179 162.335179 0 0 0 40.197283 48.271095 260.423419 260.423419 0 0 1 13.227311 48.958229v2.061399a277.086395 277.086395 0 0 1-4.638148 113.37695l-1.374266 5.668847a273.822513 273.822513 0 0 1-13.570878 37.792317q-3.951015 8.41738-8.245596 17.178326a242.386177 242.386177 0 0 1-24.565006 36.074484 181.746687 181.746687 0 0 1-56.516692 44.32008z m-154.604932 56.516692l2.233183-1.546049 2.061399-1.546049 2.404965-1.889616 3.092099-2.576749 1.717833-1.546049c2.061399-1.889616 3.951015-3.951015 5.84063-6.012414l1.0307-1.202483v-1.0307a179.341721 179.341721 0 0 0 136.05234 0c1.374266 1.889616 2.920315 3.779232 4.294582 5.325281a48.099312 48.099312 0 0 0 4.638147 5.153498l2.920316 2.748532 1.889616 1.717833 1.374266 1.202483a89.842644 89.842644 0 0 0 8.932729 6.012414 44.83543 44.83543 0 0 0 4.809931 2.920315l5.153498 2.748532 4.809931 2.576749 4.809932 2.233182a195.832914 195.832914 0 0 0 21.301124 8.245597l6.527763 1.889615c-1.717833 3.435665-3.607448 6.699547-5.497064 9.963429s-2.920315 4.809931-4.466365 7.043114a148.592518 148.592518 0 0 1-15.460493 20.098641c-1.717833 2.061399-3.779232 4.122798-5.840631 6.184197a73.351451 73.351451 0 0 1-6.527763 6.184198 56.173125 56.173125 0 0 1-6.012414 5.153497l-2.233183 1.717833a89.327294 89.327294 0 0 1-8.073813 6.012414 78.848515 78.848515 0 0 1-8.932729 5.668848 62.185539 62.185539 0 0 1-7.38668 4.122798l-6.184198 3.263882a124.027512 124.027512 0 0 1-12.368394 4.981714 81.597047 81.597047 0 0 1-8.41738 2.920316c-3.435665 1.0307-7.043114 2.061399-10.478779 2.748532h-2.061399a141.721188 141.721188 0 0 1-60.467706 0h-2.0614c-3.435665 0-7.043114-1.717833-10.478778-2.748532a81.597047 81.597047 0 0 1-8.41738-2.920316 97.916457 97.916457 0 0 1-12.368394-4.981714c-2.404966-1.202483-4.809931-2.233182-7.043114-3.607449l-3.263882-1.717832-5.497064-3.263882a120.24828 120.24828 0 0 1-12.711961-8.760946l-6.699547-5.325281a75.241067 75.241067 0 0 1-7.730247-7.043114 82.112397 82.112397 0 0 1-7.558463-7.90203 148.592518 148.592518 0 0 1-15.460493-20.098641c-1.546049-2.233182-3.092099-4.638148-4.466365-7.043114s-3.779232-6.527764-5.497064-9.963428l6.699547-2.0614c5.153498-1.717833 9.963429-3.435665 14.945143-5.497064a154.604932 154.604932 0 0 0 16.147626-7.558463 120.24828 120.24828 0 0 0 14.945144-11.337695zM931.151149 989.643348H92.848851v-149.279651a81.940614 81.940614 0 0 1 56.001342-77.817815l188.961584-62.872673 6.012414-2.404965 4.122798 7.214896 1.202483 2.0614c1.374266 2.404966 2.920315 4.638148 4.466364 6.87133s2.920315 4.466365 4.466365 6.527764 5.325281 7.043114 8.073813 10.478778A197.894313 197.894313 0 0 0 415.286026 773.02466l10.135212 5.668848 8.073813 3.951015 7.043114 3.263882 4.294581 1.717832a91.73226 91.73226 0 0 0 8.93273 3.092099l3.951015 1.374266a43.632947 43.632947 0 0 0 6.35598 1.717832 61.67019 61.67019 0 0 0 8.073813 2.0614 135.021641 135.021641 0 0 0 17.178326 2.748532 23.362523 23.362523 0 0 0 4.981714 0h4.122799-4.122799v120.24828h34.356652v-120.24828h-4.122798 8.245596a124.886428 124.886428 0 0 0 17.178326-2.920316l7.214897-1.717832a71.977185 71.977185 0 0 0 8.073813-2.404966h2.233182l9.791646-3.435665 4.294581-1.717833 7.558464-3.435665c3.607448-1.546049 7.043114-3.435665 10.478778-5.325281L608.198624 773.02466a195.145781 195.145781 0 0 0 57.375609-53.424593c1.546049-2.061399 3.092099-4.294581 4.466364-6.527764s3.092099-4.466365 4.466365-6.87133L676.911928 704.311357l4.122798-7.214897a35.902701 35.902701 0 0 0 6.012414 2.233183l188.961583 63.044455a81.940614 81.940614 0 0 1 56.001342 77.817816z" ></path></symbol></svg>',e=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss");if(e&&!i.__iconfont__svg__cssinject__){i.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}!function(t){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(t,0);else{var e=function(){document.removeEventListener("DOMContentLoaded",e,!1),t()};document.addEventListener("DOMContentLoaded",e,!1)}else document.attachEvent&&(c=t,l=i.document,n=!1,(o=function(){try{l.documentElement.doScroll("left")}catch(t){return void setTimeout(o,50)}a()})(),l.onreadystatechange=function(){"complete"==l.readyState&&(l.onreadystatechange=null,a())});function a(){n||(n=!0,c())}var c,l,n,o}(function(){var t,e;(t=document.createElement("div")).innerHTML=a,a=null,(e=t.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",function(t,e){e.firstChild?function(t,e){e.parentNode.insertBefore(t,e)}(t,e.firstChild):e.appendChild(t)}(e,document.body))})}(window); |
New file |
| | |
| | | <?xml version="1.0" standalone="no"?> |
| | | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > |
| | | <!-- |
| | | 2013-9-30: Created. |
| | | --> |
| | | <svg> |
| | | <metadata> |
| | | Created by iconfont |
| | | </metadata> |
| | | <defs> |
| | | |
| | | <font id="iconfont" horiz-adv-x="1024" > |
| | | <font-face |
| | | font-family="iconfont" |
| | | font-weight="500" |
| | | font-stretch="normal" |
| | | units-per-em="1024" |
| | | ascent="896" |
| | | descent="-128" |
| | | /> |
| | | <missing-glyph /> |
| | | |
| | | <glyph glyph-name="10" unicode="" d="M509.611-60.608c-245.583 0-445.37 199.786-445.37 445.369 0 245.57 199.787 445.363 445.37 445.363 245.582 0 445.382-199.794 445.382-445.363 0-245.582-199.8-445.369-445.382-445.369zM509.611 751.531c-202.244 0-366.775-164.526-366.775-366.769s164.531-366.775 366.775-366.775c202.243 0 366.789 164.532 366.789 366.775s-164.546 366.769-366.789 366.769zM716.228 401.993c-28.757 0-55.774 11.205-76.098 31.531-20.315 20.327-31.509 47.344-31.509 76.088 0 28.757 11.194 55.773 31.52 76.1 15.351 15.325 40.231 15.337 55.583-0.013 15.336-15.351 15.336-40.231-0.014-55.582-5.487-5.476-8.493-12.753-8.493-20.506 0-7.739 3.019-15.030 8.493-20.518 10.963-10.963 30.048-10.963 41.038 0l96.197 96.196c15.35 15.351 40.219 15.351 55.57 0 15.351-15.338 15.351-40.23 0-55.569l-96.198-96.197c-20.326-20.325-47.343-31.53-76.088-31.53zM521.468 402.005c-28.742 0-55.773 11.193-76.1 31.52-41.943 41.972-41.933 110.23 0.013 152.175 15.351 15.351 40.219 15.351 55.569 0 15.351-15.337 15.351-40.231 0-55.57-11.309-11.308-11.309-29.716 0.014-41.037 10.949-10.95 30.061-10.963 41.038 0.013l135.492 135.481c15.352 15.351 40.218 15.351 55.569 0 15.352-15.337 15.352-40.231 0-55.569l-135.493-135.495c-20.326-20.326-47.356-31.519-76.101-31.519zM322.385 402.005c-28.756 0-55.773 11.193-76.1 31.52-20.314 20.327-31.507 47.344-31.507 76.088 0 28.757 11.193 55.773 31.52 76.1 15.351 15.325 40.231 15.337 55.581-0.013 15.338-15.351 15.338-40.231-0.011-55.582-5.489-5.476-8.495-12.753-8.495-20.506 0-7.739 3.020-15.030 8.495-20.518 10.975-10.963 30.061-10.963 41.037 0l135.493 135.493c15.352 15.351 40.219 15.351 55.569 0 15.352-15.337 15.352-40.231 0-55.569l-135.493-135.494c-20.327-20.326-47.343-31.519-76.088-31.519zM386.32 305.501c0-25.943-21.042-46.972-46.997-46.972-25.942 0-46.985 21.030-46.985 46.972 0 25.969 21.042 46.998 46.985 46.998 25.954-0 46.997-21.029 46.997-46.998zM726.898 305.501c0-25.943-21.043-46.972-46.998-46.972-25.943 0-46.985 21.030-46.985 46.972 0 25.969 21.042 46.998 46.985 46.998 25.954-0 46.998-21.029 46.998-46.998zM509.611 87.345c-50.107 0-90.876 37.444-90.876 83.455v23.705c0 21.708 17.589 39.296 39.298 39.296 21.707 0 39.297-17.589 39.297-39.296v-23.705c0.486-1.175 4.925-4.86 12.281-4.86 7.367 0 11.807 3.685 12.332 5.155l-0.039 23.409c0 21.708 17.589 39.296 39.297 39.296s39.298-17.589 39.298-39.296v-23.705c-0-46.011-40.769-83.455-90.888-83.455z" horiz-adv-x="1024" /> |
| | | |
| | | |
| | | <glyph glyph-name="nan" unicode="" d="M877.1 94.2c-20.3 34.7-49.2 65.8-85.9 92.4-47.4 34.3-104.6 58.9-167 72.2 48 22.3 81.3 70.9 81.3 127.2V545.2c6.7-0.4 13.5-0.6 20.3-0.6 4.5 0 9 0.1 13.5 0.3l10.5 0.4 1.8 10.4c0.6 3.4 1.1 6.7 1.5 9.9 14 115.3-68.4 220.5-183.7 234.5-38.2 4.6-76.7-1.2-111.7-16.9-4.8 0.4-9.6 0.6-14.3 0.6-88 0-159.5-71.6-159.5-159.5 0-13 1.6-26 4.7-38.6l2.2-8.7 8.9-1.1c6.2-0.8 12.5-1.3 18.8-1.7V386c0-56.3 33.3-104.9 81.3-127.2-62.4-13.4-119.6-37.9-167-72.2-36.7-26.6-65.6-57.7-85.9-92.4-21.2-36.3-32-75-32-114.9v-13H909v13c0.1 39.9-10.7 78.6-31.9 114.9zM477.2 239.5l13.7-27.6-62.7-72.4-93.1 72.9c42.9 16.7 91 27.5 142 31-0.5-1.2-0.5-2.6 0.1-3.9z m33.7-44.2h4.6l59.5-71-64.1-49.6-62.3 48.8 62.3 71.8z m84.7-55l-60.8 72.5 11.8 26.9c0.6 1.3 0.5 2.6 0 3.7 51.1-3.4 99.2-14.2 142.1-30.9l-93.1-72.2z m-285.7 484c0 73.6 59.9 133.5 133.5 133.5 5 0 10.1-0.3 15.1-0.8l3.7-0.4 3.3 1.6c31.4 14.8 66.2 20.4 100.8 16.2 49-5.9 92.7-30.6 123.1-69.4 30-38.3 43.5-85.9 38.2-134.3-13.4-0.1-26.8 0.7-40 2.3-67.8 8.2-129.7 37.4-179.1 84.4l-8.5 8.1-8.9-7.6c-43.9-37.4-100-58-157.7-58-7.1 0-14.2 0.3-21.3 0.9-1.5 7.8-2.2 15.6-2.2 23.5zM344.4 386V574.1c56.1 2.3 110.2 22.2 154.5 56.7 50.8-45.3 112.9-73.8 180.6-83V386c0-62.9-51.2-114.1-114.1-114.1H458.6c-63 0-114.2 51.2-114.2 114.1z m-203-393.7c5.9 87 70.7 163.1 166.5 208.4l187.9-147.2c4.4-3.5 9.8-5.2 15.1-5.2 5.3 0 10.6 1.7 15 5.1L716 200.7c95.8-45.3 160.6-121.4 166.5-208.4H141.4z" horiz-adv-x="1024" /> |
| | | |
| | | |
| | | <glyph glyph-name="ziyuan" unicode="" d="M436.415367 253.187049l2.061399 2.061399-2.061399-2.061399z m209.060224-31.951686l6.527764-2.061399z m-34.356651 57.032042l3.435665-3.607449z m-116.297266-182.090254a23.362523 23.362523 0 0 0-4.981714 0h9.104512z m34.356652 0h-4.122798 4.122798zM436.415367 253.187049l-1.717833-1.546049 1.202483 0.858916 2.061399 2.061399 3.435665 3.435665 1.717833 2.061399c-2.576749-2.920315-4.466365-4.981714-6.699547-6.87133z m-85.891629-3.779232l5.84063 1.374267 5.325281 1.546049-5.153497-1.374266zM885.972152 166.092937l-142.580104 47.41218a172.642174 172.642174 0 0 1 103.069955 58.921657l9.104513 10.478779-8.41738 10.994128S804.031538 351.79063900000006 804.031538 482.002349c0 23.877873-2.576749 148.248952-52.565677 253.552088S619.02097 896 512 896C367.702063 896 268.067774 793.960745 232.680423 608.60661a709.636638 709.636638 0 0 1-12.540178-126.604261C220.140245 353.164905 178.74048 294.758598 177.70978 293.899681l-8.245596-10.994128 9.276296-10.478779a171.783258 171.783258 0 0 1 101.867472-58.921657l-142.580104-47.41218A116.469049 116.469049 0 0 1 58.492199 55.636303v-183.636303h907.015602V55.636303a116.469049 116.469049 0 0 1-79.535649 110.456634zM213.097131 285.654085c13.914444 24.908572 41.571548 88.124811 41.571549 196.348264a669.954706 669.954706 0 0 0 12.024828 120.24828C289.025331 720.43751 350.523738 861.6433480000001 512 861.6433480000001c94.137225 0 164.911928-47.412179 208.373092-140.690488 46.896829-98.775373 49.301795-216.446905 49.301795-238.950511 0-108.223452 27.657105-171.783258 41.399765-196.348264a140.690488 140.690488 0 0 0-118.015098-39.681933q-7.214897 0-13.914444 2.061399l-6.699547 1.374266-6.012414 1.54605c-3.435665 0-6.699547 1.717833-10.135212 2.920315s-9.104513 3.092099-13.227311 4.809931-8.41738 3.779232-12.368395 5.840631a111.830901 111.830901 0 0 0-13.74266 8.589163 2.920315 2.920315 0 0 1-1.0307 1.0307l-4.294581 3.435665a221.256836 221.256836 0 0 1 49.817144 44.491864A249.772857 249.772857 0 0 1 689.967455 363.471901c3.435665 6.184197 6.527764 12.368395 9.448079 18.896158a305.430632 305.430632 0 0 1 11.853045 31.09277l4.466365 15.460493c0 1.202483 0 2.404966 1.030699 3.951015a312.13018 312.13018 0 0 1 5.153498 126.088911v2.233182a292.890455 292.890455 0 0 1-17.178326 61.15484l-1.889616 4.638148-3.951014 2.920316a126.088911 126.088911 0 0 0-40.712633 52.73746l-6.87133 15.975843-15.975843-7.214897a807.381312 807.381312 0 0 0-178.311022-55.657776 857.542023 857.542023 0 0 0-126.260694-14.601577h-11.681262l-3.779231-11.165911a305.430632 305.430632 0 0 1-17.178326-101.008556 310.58413 310.58413 0 0 1 9.276296-76.787116l2.061399-7.558464a317.283677 317.283677 0 0 1 13.227311-37.792316l1.546049-3.435665q4.638148-10.135212 9.791646-19.583292a249.772857 249.772857 0 0 1 28.344237-41.571548 224.692501 224.692501 0 0 1 50.332495-44.663647 2.920315 2.920315 0 0 0-1.0307-1.0307 2.233182 2.233182 0 0 1-1.202482-1.030699 15.975843 15.975843 0 0 0-2.576749-2.233183h-1.202483l-1.546049-1.202482c-3.435665-2.404966-7.38668-4.638148-11.337695-6.871331l-13.055528-6.012414c-4.122798-1.717833-8.41738-3.263882-12.540178-4.638148l-5.153498-1.546049-5.325281-1.546049-5.84063-1.374266-6.699547-1.202483c-4.466365-0.858916-9.104513-1.546049-13.742661-2.061399A141.892971 141.892971 0 0 0 213.097131 285.654085z m366.413689 13.742661l-3.092098-1.54605a145.672203 145.672203 0 0 0-129.352793 0 179.857071 179.857071 0 0 0-58.234525 46.553263A242.386177 242.386177 0 0 0 364.094615 380.650226c-2.404966 4.466365-4.809931 9.104513-7.043114 13.914444l-1.546049 3.263882a286.190908 286.190908 0 0 0-13.055527 36.418051l-1.717833 6.699547a276.227479 276.227479 0 0 0-7.38668 67.682604 272.276464 272.276464 0 0 0 10.478779 78.161382 879.01493 879.01493 0 0 1 119.217581 14.77336 849.811777 849.811777 0 0 1 171.783257 51.534977 162.335179 162.335179 0 0 1 40.197283-48.271095 260.423419 260.423419 0 0 0 13.227311-48.958229v-2.061399a277.086395 277.086395 0 0 0-4.638148-113.37695l-1.374266-5.668847a273.822513 273.822513 0 0 0-13.570878-37.792317q-3.951015-8.41738-8.245596-17.178326a242.386177 242.386177 0 0 0-24.565006-36.074484 181.746687 181.746687 0 0 0-56.516692-44.32008z m-154.604932-56.516692l2.233183 1.546049 2.061399 1.546049 2.404965 1.889616 3.092099 2.576749 1.717833 1.546049c2.061399 1.889616 3.951015 3.951015 5.84063 6.012414l1.0307 1.202483v1.0307a179.341721 179.341721 0 0 1 136.05234 0c1.374266-1.889616 2.920315-3.779232 4.294582-5.325281a48.099312 48.099312 0 0 1 4.638147-5.153498l2.920316-2.748532 1.889616-1.717833 1.374266-1.202483a89.842644 89.842644 0 0 1 8.932729-6.012414 44.83543 44.83543 0 0 1 4.809931-2.920315l5.153498-2.748532 4.809931-2.576749 4.809932-2.233182a195.832914 195.832914 0 0 1 21.301124-8.245597l6.527763-1.889615c-1.717833-3.435665-3.607448-6.699547-5.497064-9.963429s-2.920315-4.809931-4.466365-7.043114a148.592518 148.592518 0 0 0-15.460493-20.098641c-1.717833-2.061399-3.779232-4.122798-5.840631-6.184197a73.351451 73.351451 0 0 0-6.527763-6.184198 56.173125 56.173125 0 0 0-6.012414-5.153497l-2.233183-1.717833a89.327294 89.327294 0 0 0-8.073813-6.012414 78.848515 78.848515 0 0 0-8.932729-5.668848 62.185539 62.185539 0 0 0-7.38668-4.122798l-6.184198-3.263882a124.027512 124.027512 0 0 0-12.368394-4.981714 81.597047 81.597047 0 0 0-8.41738-2.920316c-3.435665-1.0307-7.043114-2.061399-10.478779-2.748532h-2.061399a141.721188 141.721188 0 0 0-60.467706 0h-2.0614c-3.435665 0-7.043114 1.717833-10.478778 2.748532a81.597047 81.597047 0 0 0-8.41738 2.920316 97.916457 97.916457 0 0 0-12.368394 4.981714c-2.404966 1.202483-4.809931 2.233182-7.043114 3.607449l-3.263882 1.717832-5.497064 3.263882a120.24828 120.24828 0 0 0-12.711961 8.760946l-6.699547 5.325281a75.241067 75.241067 0 0 0-7.730247 7.043114 82.112397 82.112397 0 0 0-7.558463 7.90203 148.592518 148.592518 0 0 0-15.460493 20.098641c-1.546049 2.233182-3.092099 4.638148-4.466365 7.043114s-3.779232 6.527764-5.497064 9.963428l6.699547 2.0614c5.153498 1.717833 9.963429 3.435665 14.945143 5.497064a154.604932 154.604932 0 0 1 16.147626 7.558463 120.24828 120.24828 0 0 1 14.945144 11.337695zM931.151149-93.64334799999995H92.848851v149.279651a81.940614 81.940614 0 0 0 56.001342 77.817815l188.961584 62.872673 6.012414 2.404965 4.122798-7.214896 1.202483-2.0614c1.374266-2.404966 2.920315-4.638148 4.466364-6.87133s2.920315-4.466365 4.466365-6.527764 5.325281-7.043114 8.073813-10.478778A197.894313 197.894313 0 0 1 415.286026 122.97533999999996l10.135212-5.668848 8.073813-3.951015 7.043114-3.263882 4.294581-1.717832a91.73226 91.73226 0 0 1 8.93273-3.092099l3.951015-1.374266a43.632947 43.632947 0 0 1 6.35598-1.717832 61.67019 61.67019 0 0 1 8.073813-2.0614 135.021641 135.021641 0 0 1 17.178326-2.748532 23.362523 23.362523 0 0 1 4.981714 0h4.122799-4.122799v-120.24828h34.356652v120.24828h-4.122798 8.245596a124.886428 124.886428 0 0 1 17.178326 2.920316l7.214897 1.717832a71.977185 71.977185 0 0 1 8.073813 2.404966h2.233182l9.791646 3.435665 4.294581 1.717833 7.558464 3.435665c3.607448 1.546049 7.043114 3.435665 10.478778 5.325281L608.198624 122.97533999999996a195.145781 195.145781 0 0 1 57.375609 53.424593c1.546049 2.061399 3.092099 4.294581 4.466364 6.527764s3.092099 4.466365 4.466365 6.87133L676.911928 191.68864299999996l4.122798 7.214897a35.902701 35.902701 0 0 1 6.012414-2.233183l188.961583-63.044455a81.940614 81.940614 0 0 0 56.001342-77.817816z" horiz-adv-x="1024" /> |
| | | |
| | | |
| | | |
| | | |
| | | </font> |
| | | </defs></svg> |
New file |
| | |
| | | ## 1.1.4(2024-03-18) |
| | | - form 新增属性classes [方便结合tailwindcss] |
| | | ## 1.1.3(2024-03-15) |
| | | - 新增form-item的labelStyle属性,可以定义lable样式 |
| | | ## 1.1.2(2023-05-12) |
| | | - 修复min或者max填写0不检测的Bug |
| | | ## 1.1.1(2023-05-11) |
| | | - jc-form-item 新增两个slot,分别是 label-desc、label-right |
| | | ## 1.1.0(2023-05-09) |
| | | - 修复已经问题 |
| | | ## 1.0.9(2023-05-08) |
| | | - 自定义验证参数增加为两个(当前值,表单所有值) |
| | | ## 1.0.8(2023-05-08) |
| | | - 修复已知问题 |
| | | ## 1.0.7(2023-05-08) |
| | | - form的rules属性,和formItem的rules合并 |
| | | - 重写验证返回错误 |
| | | ## 1.0.6(2023-05-08) |
| | | - form-item新增props: required |
| | | ## 1.0.5(2023-05-08) |
| | | - 修复 getModel 错误 |
| | | ## 1.0.4(2023-05-06) |
| | | - 修复已知问题 |
| | | ## 1.0.3(2023-05-06) |
| | | - jc-form 组件新增属性rules,当jc-form-item也存在rules属性时,会自动合并验证规则 |
| | | - 修复规则 required: false 不生效的bug |
| | | ## 1.0.2(2023-05-06) |
| | | - [修复] showError: false 边框显现错误颜色 |
| | | ## 1.0.1(2023-05-06) |
| | | - 去除 customInput | Boolean | false | 自定义input样式 |
| | | - 新增 boldLable | Boolean | false | 加粗label |
| | | ## 1.0.0(2023-05-05) |
| | | - jc-form表单验证组件内置required,max,min,validator,pattern规则 |
| | | - 支持taost提示 |
| | | - 支持自定义验证 |
| | | - 错误提示 |
New file |
| | |
| | | <script setup> |
| | | import { computed, inject, ref } from 'vue' |
| | | const props = defineProps({ |
| | | label: [String], |
| | | prop: [String], |
| | | rules: { |
| | | type: Array, |
| | | default: [] |
| | | }, |
| | | required: { // 是否必填 |
| | | type: [Boolean, Number, String], |
| | | default: false |
| | | }, |
| | | align: { // left | right |
| | | default: 'right' |
| | | }, |
| | | direction: { |
| | | default: 'row' |
| | | }, |
| | | border: { // 是否有下边框 |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | |
| | | labelStyle: { // lable样式 |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | }) |
| | | const $parent = inject('$parent') |
| | | const formRuleRef = ref(null) |
| | | |
| | | const required = computed(() => props.required || props.rules.length || ($parent.getProps('rules') || {})[props.prop]) |
| | | const modelValue = computed(() => $parent.getModel(props.prop)) |
| | | const showError = computed(() => $parent.getProps('showError')) |
| | | const itemRules = computed(() => { |
| | | if (!$parent) return [] |
| | | const parentRules = $parent.getProps('rules') || {} |
| | | const _rules = parentRules[props.prop] || [] |
| | | const rules = _rules ? [..._rules, ...props.rules] : props.rules |
| | | return rules.map(rule => { |
| | | if (!rule.message) { |
| | | rule.message = `请完善${props.label}` |
| | | } |
| | | return rule |
| | | }) |
| | | }) |
| | | const classes = computed(() => { |
| | | return { |
| | | row: props.direction == 'row', |
| | | col: props.direction == 'col', |
| | | error: (formRuleRef.value && formRuleRef.value.error && $parent.getProps('showError')), |
| | | boldLable: $parent.getProps('boldLable'), |
| | | hasBorder: $parent.getProps('border') && props.border |
| | | } |
| | | }) |
| | | |
| | | function check() { |
| | | return formRuleRef.value.check() |
| | | } |
| | | |
| | | function getState(name) { |
| | | return formRuleRef.value.getState(name) |
| | | } |
| | | |
| | | function getValid(name = 'valid') { |
| | | return getState(name) |
| | | } |
| | | |
| | | function reset() { |
| | | formRuleRef.value.reset() |
| | | } |
| | | |
| | | function init() { |
| | | $parent.addChild({ props, check, getValid, getState, reset }) |
| | | } |
| | | |
| | | init() |
| | | </script> |
| | | |
| | | |
| | | <template> |
| | | <view class="jc-form-item" :style="$parent.getProps('itemStyle')" :class="classes"> |
| | | <view class="jc-form-item__label" :style="labelStyle" v-if="label"> |
| | | <view class="main"> |
| | | <text class="main__label">{{label}}</text> |
| | | <text class="main__required" v-if="required">*</text> |
| | | <slot name="label-desc"></slot> |
| | | </view> |
| | | <slot name="label-right"></slot> |
| | | </view> |
| | | <view class="jc-form-item__cont" :class="[align]" :style="$parent.getProps('itemContentStyle')"> |
| | | <jc-form-rule ref="formRuleRef" :value="modelValue" :rules="itemRules" :model="$parent.getProps('model')"> |
| | | <slot></slot> |
| | | <view :style="$parent.getProps('errorStyle')" v-if="showError && formRuleRef && formRuleRef.error">{{formRuleRef.error}}</view> |
| | | </jc-form-rule> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <style lang="scss" scoped> |
| | | // // 穿透隔离css |
| | | // ::v-deep input { |
| | | // height: 100rpx; |
| | | |
| | | // &:active { |
| | | // background: rgba(0, 0, 0, .02); |
| | | // } |
| | | // } |
| | | .jc-form-item { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | &.hasBorder { |
| | | border-bottom: 1rpx solid RGB(237, 237, 239); |
| | | // transition: border-color .2s; |
| | | } |
| | | |
| | | &.error { |
| | | border-bottom-color: RGB(234, 88, 11); |
| | | } |
| | | |
| | | &.boldLable { |
| | | .jc-form-item__label { |
| | | font-size: 30rpx; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | |
| | | &__label { |
| | | display: flex; |
| | | align-items: center; |
| | | padding-right: 20rpx; |
| | | |
| | | .main { |
| | | flex: 1; |
| | | |
| | | &__label {} |
| | | |
| | | &__required { |
| | | margin-left: .1em; |
| | | color: RGB(234, 88, 11); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | &__cont { |
| | | width: 100%; |
| | | |
| | | &.right { |
| | | text-align: right; |
| | | } |
| | | } |
| | | |
| | | &.row { |
| | | flex-direction: row; |
| | | |
| | | .jc-form-item__label {} |
| | | |
| | | .jc-form-item__cont { |
| | | flex: 1; |
| | | } |
| | | } |
| | | |
| | | &.col { |
| | | flex-direction: column; |
| | | |
| | | .jc-form-item__label { |
| | | width: 100%; |
| | | margin: 0 0 20rpx 0; |
| | | } |
| | | |
| | | .jc-form-item__cont {} |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <script setup> |
| | | import { computed, ref, watch, onMounted, reactive, toRefs } from 'vue' |
| | | import { validationRules, combineRule } from '../../lib/js/rule.js' |
| | | const props = defineProps({ |
| | | value: '', |
| | | rules: { |
| | | type: Array, |
| | | default: [] |
| | | }, |
| | | lazy: { // 懒验证 关闭后实时验证 |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | model: '', |
| | | }) |
| | | const emit = defineEmits(['error']) |
| | | |
| | | const state = reactive({ |
| | | valid: false, // 验证是否通过 |
| | | error: '', // 错误信息 |
| | | dirty: false, // 是否污染 |
| | | rule: '' |
| | | }) |
| | | |
| | | const rules = computed(() => combineRule(props.rules)) |
| | | |
| | | watch(() => props.value, () => { |
| | | check() |
| | | if (state.dirty) return |
| | | state.dirty = true |
| | | }, { deep: true }) |
| | | |
| | | function setState(name, value) { |
| | | state[name] = value |
| | | } |
| | | |
| | | function getState(name) { |
| | | return name ? state[name] : state |
| | | } |
| | | |
| | | function valided(rule, value) { |
| | | const { type, message } = rule |
| | | const status = validationRules[type](rule.value, value, props.model) |
| | | setState('error', status ? '' : message) |
| | | setState('valid', status) |
| | | setState('rule', {rule, value}) |
| | | |
| | | if(!status) { |
| | | emit('error', message) |
| | | } |
| | | return status |
| | | } |
| | | |
| | | function check() { |
| | | if (rules.value.length) { |
| | | return rules.value.every(rule => { |
| | | return valided(rule, props.value) |
| | | }) |
| | | } |
| | | setState('valid', true) |
| | | return true |
| | | } |
| | | |
| | | function reset() { |
| | | state.valid = false |
| | | state.error = '' |
| | | state.dirty = false |
| | | } |
| | | |
| | | onMounted(() => { |
| | | if (!props.lazy) { |
| | | check() |
| | | } |
| | | }) |
| | | |
| | | defineExpose({ |
| | | ...toRefs(state), |
| | | check, |
| | | reset, |
| | | getState |
| | | }) |
| | | </script> |
| | | |
| | | <template> |
| | | <view> |
| | | <slot></slot> |
| | | </view> |
| | | </template> |
New file |
| | |
| | | <script setup> |
| | | import { ref, provide } from 'vue' |
| | | import { validationRules, combineRule } from '../../lib/js/rule.js' |
| | | const props = defineProps({ |
| | | model: '', |
| | | rules: { |
| | | type: [Object], |
| | | }, |
| | | checkAll: { // 是否检查所有,关闭:按顺序检查 |
| | | type: [Boolean, Number, String], |
| | | default: true |
| | | }, |
| | | showError: { // 是否显示错误信息 |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | toastError: { // toast提示错误信息 |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | errorStyle: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | fontSize: '24rpx', |
| | | color: 'RGB(234, 88, 11)', |
| | | margin: '10rpx 0' |
| | | } |
| | | } |
| | | }, |
| | | classes: '', |
| | | itemStyle: { // formItem样式 |
| | | type: Object |
| | | }, |
| | | itemContentStyle: { // formItem Content样式 |
| | | type: Object |
| | | }, |
| | | boldLable: { // 加粗label |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | border: { // 是否有下边框 |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | }) |
| | | |
| | | const emit = defineEmits(['reset']) |
| | | |
| | | const _rawModel = props.model ? { ...props.model } : {} |
| | | |
| | | provide('$parent', { addChild, getProps, getModel }) |
| | | |
| | | // 子组件 |
| | | const childs = ref([]) |
| | | |
| | | function addChild(child) { |
| | | childs.value.push(child) |
| | | } |
| | | |
| | | function getProps(name) { |
| | | return name ? props[name] : props |
| | | } |
| | | |
| | | function getModel(prop) { |
| | | if (!props.model) return |
| | | return props.model[prop] |
| | | } |
| | | |
| | | function checkField(name, model) { |
| | | return name in model |
| | | } |
| | | |
| | | function checkRules(rules, value) { |
| | | return rules.map(rule => { |
| | | const { type, message } = rule |
| | | return { ...rule, valid: validationRules[type](rule.value, value, props.model) } |
| | | }) |
| | | } |
| | | |
| | | // 获取props.rules中的错误 |
| | | function getFormRulesErrors(rules) { |
| | | let errors = [] |
| | | for (let field in rules) { |
| | | const hasField = checkField(field, props.model) |
| | | const _rules = combineRule(rules[field]) |
| | | const res = hasField ? checkRules(_rules, props.model[field]) : [] |
| | | res.forEach(r => { |
| | | if (!r.valid) { |
| | | errors.push({ ...r, prop: field }) |
| | | } |
| | | }) |
| | | } |
| | | return errors |
| | | } |
| | | |
| | | function validateChilds(childs, checkAll) { |
| | | if (props.checkAll) { // 检查所有 |
| | | childs.forEach(c => c.check()) |
| | | return callback(childs.every(c => c.getValid())) |
| | | } |
| | | return callback(childs.every(c => c.check())) |
| | | } |
| | | |
| | | // 获取child中的规则 |
| | | function getChildRules() { |
| | | let rules = {} |
| | | childs.value.forEach(c => { |
| | | const name = c.props.prop, |
| | | childRules = c.props.rules |
| | | if (name && childRules.length) { |
| | | rules[name] = childRules |
| | | } |
| | | }) |
| | | return rules |
| | | } |
| | | |
| | | // 合并 props.rules和child中的props.rules |
| | | function mergeRules(rules, childRules) { |
| | | for (let field in rules) { |
| | | const value = rules[field] |
| | | if (field in childRules) { |
| | | rules[field] = [...value, ...childRules[field]].reduce((acc, cur) => { // 去重 |
| | | const index = acc.findIndex(item => JSON.stringify(item) === JSON.stringify(cur)) |
| | | return index === -1 ? [...acc, cur] : acc |
| | | }, []) |
| | | } |
| | | } |
| | | return rules |
| | | } |
| | | |
| | | function getChildErrors() { |
| | | const errors = childs.value.filter(c => !c.getValid()) |
| | | return errors.map(c => { |
| | | const { valid, error, dirty, rule } = c.getState() |
| | | const { rule: { type, value } } = rule |
| | | return { message: error, valid, type, value } |
| | | }) |
| | | } |
| | | |
| | | function validate() { |
| | | let errors = [] |
| | | validateChilds(childs.value, props.checkAll) |
| | | if (props.rules) { |
| | | const childRules = getChildRules() |
| | | const rules = { ...childRules, ...mergeRules(props.rules, childRules) } |
| | | errors = getFormRulesErrors(rules) |
| | | } else { |
| | | errors = getChildErrors() |
| | | } |
| | | return callback(errors) |
| | | } |
| | | |
| | | function callback(errors) { |
| | | return new Promise((resolve, reject) => { |
| | | if (errors.length) { |
| | | if (props.toastError) { |
| | | showToastError([...errors]) |
| | | } |
| | | return reject(errors) |
| | | } |
| | | return resolve() |
| | | }) |
| | | } |
| | | |
| | | function showToastError(errors) { |
| | | const { message } = errors.shift() |
| | | if (!message) return |
| | | uni.showToast({ |
| | | icon: 'none', |
| | | title: message |
| | | }) |
| | | } |
| | | |
| | | function reset() { |
| | | childs.value.forEach(c => c.reset()) |
| | | emit('reset', _rawModel) |
| | | } |
| | | |
| | | defineExpose({ validate, reset }) |
| | | </script> |
| | | |
| | | <template> |
| | | <view :class="classes"> |
| | | <slot></slot> |
| | | </view> |
| | | </template> |
New file |
| | |
| | | export function getRule(rule) { |
| | | if ('required' in rule) { |
| | | return { type: 'required', value: rule.required } |
| | | } |
| | | if ('max' in rule || 'min' in rule) { |
| | | return { type: 'between', value: [rule.min, rule.max] } |
| | | } |
| | | if ('validator' in rule) { |
| | | return { type: 'validator', value: rule.validator } |
| | | } |
| | | if ('pattern' in rule) { |
| | | return { type: 'pattern', value: rule.pattern } |
| | | } |
| | | } |
| | | |
| | | export function filterRules(rules) { |
| | | return rules.filter(rule => { |
| | | const { type, value } = getRule(rule) |
| | | return value |
| | | }) |
| | | } |
| | | |
| | | export function combineRule(rules) { |
| | | return filterRules(rules).map(rule => { |
| | | const { message = '' } = rule |
| | | const { type, value } = getRule(rule) |
| | | return { message, type, value } |
| | | }) |
| | | } |
| | | |
| | | function isVariableEmpty(variable) { |
| | | // 判断变量是否为null或undefined |
| | | if (variable == null) { |
| | | return true |
| | | } |
| | | |
| | | // 判断字符串是否为空 |
| | | if (typeof variable === 'string' && variable.trim().length === 0) { |
| | | return true |
| | | } |
| | | |
| | | // 判断数组是否为空 |
| | | if (Array.isArray(variable) && variable.length === 0) { |
| | | return true |
| | | } |
| | | |
| | | // 判断对象是否为空 |
| | | if (typeof variable === 'object' && Object.keys(variable).length === 0) { |
| | | return true |
| | | } |
| | | |
| | | return false |
| | | } |
| | | |
| | | const validationRules = { |
| | | required(rule, value, model) { |
| | | return !isVariableEmpty(value) |
| | | }, |
| | | pattern(rule, value, model) { |
| | | return rule.test(value, model) |
| | | }, |
| | | between(rule, value, model) { |
| | | const [min = null, max = null] = rule |
| | | if (min !== null && max !== null) return (Number(value) >= min && Number(value) <= max) |
| | | if (min !== null && max === null) return Number(value) >= min |
| | | if (max !== null && min === null) return Number(value) <= max |
| | | }, |
| | | validator(rule, value, model) { // 自定义验证 |
| | | return rule(value, model) |
| | | } |
| | | } |
| | | export { validationRules } |
New file |
| | |
| | | { |
| | | "id": "jc-form", |
| | | "displayName": "jc-form", |
| | | "version": "1.1.4", |
| | | "description": "jc-form表单验证组件内置required,max,min,validator,pattern规则,支持自定义验证,灵活好用", |
| | | "keywords": [ |
| | | "jc-form" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.7.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "u", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "u", |
| | | "app-nvue": "u" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "u", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "u", |
| | | "百度": "u", |
| | | "字节跳动": "u", |
| | | "QQ": "u", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | # jc-form 表单验证 |
| | | |
| | | > 内置 ```required```, ```max```, ```min```, ```validator```, ```pattern```规则 |
| | | > |
| | | > required 支持空字符串,空数组,空对象的检查 |
| | | > |
| | | > max和min成对出现即检测区间,只有min时候为最小值,max时为最大值 |
| | | > |
| | | > 包含 jc-form, jc-form-item, jc-form-rule 组件 |
| | | > |
| | | > 支持toast提示 |
| | | > |
| | | > 支持逐个验证和全部验证 |
| | | > |
| | | > 已支持rules用在 ```jc-form```组件上 |
| | | |
| | | #### 使用 |
| | | |
| | | ```js |
| | | <script setup> |
| | | import { reactive, ref } from 'vue' |
| | | |
| | | const formRules = { |
| | | title: [{ required: true, message: '请输入姓名'}] |
| | | } |
| | | |
| | | const testForm = reactive({ |
| | | title: '', |
| | | name: '', |
| | | age: '', |
| | | tel: '', |
| | | email: '', |
| | | colors: [], |
| | | cont: '' |
| | | }) |
| | | |
| | | const formRef = ref() |
| | | |
| | | function validTel(val) { // 自定义验证 |
| | | return /^1\d{10}$/.test(val) |
| | | } |
| | | |
| | | function addColor() { |
| | | Math.random() > .5 ? testForm.colors.push(`#${Number(Math.random()).toString(16).substr(2,6)}`) : (testForm.colors = []) |
| | | } |
| | | |
| | | async function submit() { |
| | | try { |
| | | await formRef.value.validate() |
| | | console.log('验证通过'); |
| | | } catch (e) { |
| | | console.log('验证失败', e) |
| | | } |
| | | } |
| | | |
| | | function reset() { |
| | | formRef.value.reset() |
| | | } |
| | | </script> |
| | | ``` |
| | | |
| | | ```html |
| | | <view class="bg-white p-2 mb-4"> |
| | | <jc-form :model="testForm" :rules="formRules" ref="formRef" :toastError="true" :checkAll="true"> |
| | | <jc-form-item label="标题" prop="title"> |
| | | <input type="text" v-model="testForm.title" placeholder="请输入标题" /> |
| | | </jc-form-item> |
| | | <jc-form-item label="姓名" prop="name" :rules="[{ required: true, message: '请输入姓名'}]"> |
| | | <input type="text" v-model="testForm.name" placeholder="请输入姓名" /> |
| | | </jc-form-item> |
| | | <jc-form-item label="年龄" prop="age" :rules="[{ min:10, max: 18, message: '年龄10-18岁'}]"> |
| | | <input type="text" v-model="testForm.age" placeholder="年龄10-18岁" /> |
| | | </jc-form-item> |
| | | <jc-form-item label="电话" prop="tel" :rules="[{ validator: validTel, message: '电话号码不规范'}]"> |
| | | <input type="text" v-model="testForm.tel" placeholder="请输入电话号码" /> |
| | | </jc-form-item> |
| | | <jc-form-item label="邮箱" prop="email" :rules="[{ pattern: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/ }]"> |
| | | <input type="text" v-model="testForm.email" placeholder="请输入邮箱地址" /> |
| | | </jc-form-item> |
| | | <jc-form-item label="颜色" prop="colors" :rules="[{ required: true, message: '请输入颜色'}]"> |
| | | <view class="flex"> |
| | | <input type="text" v-model="testForm.colors" placeholder="请输入颜色" class="flex-1" /> |
| | | <button @tap="addColor">加颜色</button> |
| | | </view> |
| | | </jc-form-item> |
| | | <jc-form-item label="内容" prop="cont" :border="false" direction="col" :rules="[{ required: true, message: '请输入内容'}]"> |
| | | <textarea v-model="testForm.cont" style="height: 160rpx;" placeholder="请输入内容"></textarea> |
| | | </jc-form-item> |
| | | |
| | | <jc-form-item :border="false"> |
| | | <view class="flex"> |
| | | <button @tap="submit">验证</button> |
| | | <button @tap="reset">重置</button> |
| | | </view> |
| | | </jc-form-item> |
| | | </jc-form> |
| | | </view> |
| | | ``` |
| | | ### jc-form |
| | | |
| | | > props |
| | | |
| | | 参数 | 类型 | 默认值 | 说明 |
| | | --- | --- | --- | --- |
| | | model | - | - | 表单model |
| | | rules | Object | - | 验证规则 |
| | | checkAll | [Boolean, Number, String] | true | 是否验证所有 |
| | | showError | Boolean | true | 是否显示错误信息 |
| | | toastError | Boolean | false | toast提示错误信息 |
| | | errorStyle | Object | { fontSize: '24rpx', color: 'RGB(234, 88, 11)', margin: '10rpx 0' } | 错误提示样式 |
| | | itemStyle | Object | - | formItem样式 |
| | | itemContentStyle | Object | - | formItem Content样式 |
| | | boldLable | Boolean | false | 加粗label |
| | | border | Boolean | true | 是否有下边框 |
| | | |
| | | |
| | | > slot |
| | | |
| | | 名称 | scope | 说明 |
| | | --- | --- | --- |
| | | default | - | - |
| | | |
| | | > emit |
| | | |
| | | 事件名称 | 说明 |
| | | --- | --- | |
| | | reset | 重置验证 |
| | | |
| | | > expose |
| | | |
| | | 名称 | 类型 | 说明 |
| | | --- | --- | --- | |
| | | validate | Function | 验证表单 |
| | | reset | Function | 重置验证信息 |
| | | |
| | | --- |
| | | |
| | | |
| | | ### jc-form-item |
| | | |
| | | > props |
| | | |
| | | 参数 | 类型 | 默认值 | 说明 |
| | | --- | --- | --- | --- |
| | | label | String | - | 显示的标题 |
| | | prop | String | - | 表单对象 |
| | | rules | Array | [] | 验证规则 |
| | | required | [Boolean, Number, String] | false | 是否必填 |
| | | align | String | left | 水平方向 left,right |
| | | direction | String | row | 水平方向 row,col |
| | | border | Boolean | true | 是否有下边框 |
| | | |
| | | |
| | | > slot |
| | | |
| | | 名称 | scope | 说明 |
| | | --- | --- | --- |
| | | default | - | - |
| | | label-desc | - | label * 后面 |
| | | label-right | - | label 右侧 |
| | | |
| | | |
| | | > emit |
| | | |
| | | 无 |
| | | |
| | | --- |
| | | |
| | | ### jc-form-rule |
| | | |
| | | > props |
| | | |
| | | 参数 | 类型 | 默认值 | 说明 |
| | | --- | --- | --- | --- |
| | | value | - | - | 验证对象 |
| | | rules | Array | [] | 验证规则 |
| | | lazy | Boolean | true | 懒验证 |
| | | |
| | | |
| | | > slot |
| | | |
| | | 名称 | scope | 说明 |
| | | --- | --- | --- |
| | | default | - | - |
| | | |
| | | > emit |
| | | |
| | | 事件名称 | 说明 |
| | | --- | --- | |
| | | error | 错误 事件 |
| | | |
| | | > expose |
| | | |
| | | 名称 | 类型 | 说明 |
| | | --- | --- | --- | |
| | | valid | 变量 | 验证状态 |
| | | error | 变量 | 错误信息 |
| | | dirty | 变量 | 是否污染 |
| | | check | Function | 开始验证 |
| | | reset | Function | 重置验证 |
| | | getState | Function | 获取valid, error, dirty |
| | | |
| | | |
| | | |
| | | |
| | | |
New file |
| | |
| | | ## 1.0.0(2021-09-02) |
| | | 初次更新上传 |
New file |
| | |
| | | <template> |
| | | <view class="widget_style" > |
| | | <view class="ffff" style="display: flex;height: 100%;"> |
| | | <input placeholder-class="widget_input_placehold" v-model="content" class="widget_input" :style="style1" :placeholder="placehold" /> |
| | | <view class="widget_button" v-if="isSearchTrue" @click="onClickSearch">{{buttonText}}</view> |
| | | <view class="widget_button searchBg" v-else @click="onClickSearch"></view> |
| | | </view> |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | export default{ |
| | | data(){ |
| | | return{ |
| | | content:'', |
| | | style1:'', |
| | | style2:'', |
| | | } |
| | | }, |
| | | props:{ |
| | | width:{ |
| | | type:Number, |
| | | default:50 |
| | | }, |
| | | placehold:{ |
| | | type:String, |
| | | default:'请输入内容' |
| | | }, |
| | | buttonText:{ |
| | | type:String, |
| | | default(){ |
| | | return '搜索' |
| | | } |
| | | },isSearchTrue:{ |
| | | type:Boolean, |
| | | default(){ |
| | | return true |
| | | } |
| | | }, |
| | | }, |
| | | created() { |
| | | // 编织设置动态宽度 |
| | | // this.style1 = 'min-width:calc('+this.width+'vw);'; |
| | | // this.style2 = 'min-width:calc('+(this.width / 5)+'vw);'; |
| | | this.style1= 'width:4.5rem' |
| | | this.style2= 'width:.5rem' |
| | | }, |
| | | destroyed() { |
| | | |
| | | }, |
| | | methods:{ |
| | | onClickSearch(){ |
| | | this.$emit('onSearch',{text:this.content}); |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .widget_style{ |
| | | display: flex; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | border-radius: 30px; |
| | | height: 100% !important; |
| | | } |
| | | .ffff{ |
| | | border: 2px solid #597AA5; |
| | | border-radius: 30px; |
| | | overflow: hidden; |
| | | padding-left: 10px; |
| | | background-color: #fff; |
| | | align-items: center; |
| | | } |
| | | .widget_input{ |
| | | min-width: 320rpx; |
| | | height: 100%; |
| | | background-color: #fff; |
| | | /* border: 2rpx solid #FF6600; */ |
| | | /* border-radius: 10rpx; */ |
| | | padding-left: 12rpx; |
| | | padding-right: 12rpx; |
| | | font-size: 24rpx; |
| | | } |
| | | .widget_input_placehold{ |
| | | color: #BBB; |
| | | } |
| | | .widget_button{ |
| | | color: #fff; |
| | | width: .74rem; |
| | | height: .3rem; |
| | | font-size: .14rem; |
| | | padding: 5rpx 20rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border-top-right-radius: 10rpx; |
| | | border-top-left-radius: 0rpx; |
| | | border-bottom-left-radius: 0rpx; |
| | | border-bottom-right-radius: 10rpx; |
| | | background-color: #244A7B; |
| | | border-radius: 30px; |
| | | margin: 2rpx; |
| | | } |
| | | .searchBg{ |
| | | width: .46rem; |
| | | height: 0.46rem; |
| | | background-image:url(@/static/image/搜索.png) ; |
| | | background-size: 100% 100%; |
| | | } |
| | | ::v-deep .uni-input-input{ |
| | | height: 100% !important; |
| | | color: #000; |
| | | font-size: .16rem; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "luanqing-search", |
| | | "displayName": "luanqing-search输入搜索框", |
| | | "version": "1.0.0", |
| | | "description": "luanqing-search", |
| | | "keywords": [ |
| | | "luanqing-search" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "y", |
| | | "联盟": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | # luanqing-search |
| | | |
| | | |
| | | ##参数简要说明 |
| | | |
| | | 名称 | 参数说明 |默认值 |是否必须 |
| | | --|--|--|-- |
| | | |
| | | width |组件的输入区域和按钮区域的宽度,值为百分比,如传入10,即屏幕宽度10% |number |非 |
| | | placehold |组件输入区域的占位填充字符串 |'请输入内容' |非 |
| | | buttonText |按钮的文字 |'搜索' |非 |
| | | |
| | | ##使用方法: |
| | | ``` |
| | | <template> |
| | | <view class="content"> |
| | | <luanqing-search class="search_bar" :width="40" @onSearch="onSearch"></luanqing-search> |
| | | </view> |
| | | </template> |
| | | |
| | | ``` |
New file |
| | |
| | | ## 1.0.9(2023-07-06) |
| | | 增加clear时,同事发送input事件 |
| | | ## 1.0.8(2023-06-16) |
| | | 更新修复input事件 |
| | | ## 1.0.7(2023-06-15) |
| | | 增加输入事件 |
| | | ## 1.0.6(2023-06-09) |
| | | 增加demo |
| | | ## 1.0.5(2023-06-09) |
| | | 更新more插槽的使用说明 |
| | | ## 1.0.4(2023-06-09) |
| | | 修复mode=more模式下input事件 |
| | | ## 1.0.3(2023-06-08) |
| | | 更改模式 |
| | | ## 1.0.2(2023-06-08) |
| | | 更改说明文件模式 |
| | | ## 1.0.1(2023-06-08) |
| | | 更新说明 |
| | | ## 1.0.0(2023-06-08) |
| | | 初始化next-search-more |
New file |
| | |
| | | <template> |
| | | <view class="next-search-more"> |
| | | <view class="search" :style="{ backgroundColor: backgroundColor }"> |
| | | <!-- 下拉菜单 --> |
| | | <template> |
| | | <view class="button xiala active" @click="selectMore"> |
| | | <view class="button-item"> |
| | | <!-- <text class="icon icon-more"></text> --> |
| | | <uni-data-select style="width: 100%;" v-model="val" :localdata="selectValue" @change="change" |
| | | :clear="false" ></uni-data-select> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | <view class="content" :style="{ 'border-radius': radius + 'px', border: border }"> |
| | | <view class="content-box" :class="{ center: mode === 'center' }"> |
| | | <!-- <text class="icon icon-search"></text> --> |
| | | <text style="display: inline-block;padding-left: 20rpx;"></text> |
| | | <input class="input" :class="{ center: !active && mode === 'center' }" :focus="isFocus" |
| | | :placeholder="' '+placeholder" v-model="inputVal" id="sdf" @input="input" @focus="focus" @blur="blur" /> |
| | | <text v-if="isDelShow" class="icon icon-del" @click="clear"></text> |
| | | </view> |
| | | <view v-show="(active && isFixedSearchBtn && button === 'inside') || (isDelShow && button === 'inside')" |
| | | class="searchBtn" @click="search">搜索</view> |
| | | </view> |
| | | <!-- <template v-if="mode === 'common' || mode ==='center'"> |
| | | <view v-if="button === 'outside'" class="button" :class="{ active: isFixedSearchBtn || active }" @click="search"> |
| | | <view class="button-item">{{ !isFixedSearchBtn ? searchName : '搜索' }}</view> |
| | | </view> |
| | | </template> |
| | | <template v-else-if="mode === 'more'"> |
| | | <view class="button active" @click="selectMore"> |
| | | <view class="button-item"><text class="icon icon-more"></text></view> |
| | | </view> |
| | | </template> --> |
| | | <template> |
| | | <view class="button btnn" :class="{ active: isFixedSearchBtn || active }" @click="search"> |
| | | <view class="button-item btn">{{ !isFixedSearchBtn ? searchName : '搜索' }}</view> |
| | | </view> |
| | | </template> |
| | | |
| | | </view> |
| | | <view class="more-container-parent"> |
| | | <view v-if="mode === 'more' && showMore" class="more-container"> |
| | | <slot name="more"></slot> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | props: { |
| | | val:{ |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | selectValue: { |
| | | type: Array, |
| | | default () { |
| | | return [{ |
| | | value: 0, |
| | | text: '篮球sdf' |
| | | }] |
| | | } |
| | | }, |
| | | mode: { |
| | | type: String, |
| | | default: 'common' |
| | | }, |
| | | button: { |
| | | type: String, |
| | | default: 'outside' |
| | | }, |
| | | isFixedSearchBtn: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | radius: { |
| | | type: String, |
| | | default: '60' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '请输入搜索内容' |
| | | }, |
| | | backgroundColor: { |
| | | type: String, |
| | | default: '#fff' |
| | | }, |
| | | showMore: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | border: { |
| | | type: String, |
| | | default: '1px #f5f5f5 solid' |
| | | } |
| | | |
| | | }, |
| | | data() { |
| | | return { |
| | | active: false, |
| | | inputVal: '', |
| | | searchName: '取消', |
| | | isDelShow: false, |
| | | isFocus: false, |
| | | timer: 0 |
| | | }; |
| | | }, |
| | | methods: { |
| | | focus() { |
| | | this.active = true; |
| | | }, |
| | | blur() { |
| | | this.isFocus = false; |
| | | if (!this.inputVal) { |
| | | this.active = false; |
| | | } |
| | | }, |
| | | input() { |
| | | clearInterval(this.timer) |
| | | this.timer = setTimeout(() => { |
| | | this.$emit('input', this.inputVal); |
| | | }, 500) |
| | | }, |
| | | clear() { |
| | | this.inputVal = ''; |
| | | this.active = false; |
| | | this.$emit('input', this.inputVal); |
| | | this.$emit('search', ''); |
| | | }, |
| | | getFocus() { |
| | | this.isFocus = true; |
| | | }, |
| | | // 搜索 |
| | | search() { |
| | | if (!this.inputVal) return; |
| | | // console.log(this.inputVal); |
| | | this.$emit('search', this.inputVal); |
| | | }, |
| | | selectMore() { |
| | | this.$emit('moreClick') |
| | | }, |
| | | // 搜索下拉框 |
| | | change(e){ |
| | | this.$emit('select',e) |
| | | console.log(e); |
| | | } |
| | | }, |
| | | created() { |
| | | this.$watch(() => this.inputVal, (newVal) => { |
| | | if (newVal) { |
| | | this.searchName = '搜索'; |
| | | this.isDelShow = true; |
| | | } else { |
| | | this.searchName = '取消'; |
| | | this.isDelShow = false; |
| | | } |
| | | }) |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | ::v-deep .uni-select{ |
| | | border-style: none; |
| | | } |
| | | ::v-deep .content{ |
| | | border-radius: 0 !important; |
| | | // border: 1px solid #7d8ea5 !important; |
| | | } |
| | | ::v-deep .button-item { |
| | | width: 100% !important; |
| | | } |
| | | |
| | | .xiala { |
| | | width: 200rpx !important; |
| | | } |
| | | .next-search-more { |
| | | .search { |
| | | display: flex; |
| | | width: 100%; |
| | | border-bottom: 1px #f5f5f5 solid; |
| | | box-sizing: border-box; |
| | | // padding: 15upx; |
| | | border-radius: 35rpx; |
| | | font-size: $uni-font-size-base; |
| | | background: #fff; |
| | | border: 1px solid #7d8ea5 ; |
| | | margin: 30rpx 0; |
| | | |
| | | .content { |
| | | display: flex; |
| | | align-items: center; |
| | | width: 100%; |
| | | height: 60upx; |
| | | border: 1px #ccc solid; |
| | | background: #fff; |
| | | overflow: hidden; |
| | | transition: all 0.2s linear; |
| | | border-radius: 30px; |
| | | |
| | | .content-box { |
| | | width: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | &.center { |
| | | justify-content: center; |
| | | } |
| | | |
| | | .icon { |
| | | padding: 0 15upx; |
| | | |
| | | &.icon-del { |
| | | font-size: 38upx; |
| | | } |
| | | } |
| | | |
| | | .input { |
| | | width: 100%; |
| | | max-width: 100%; |
| | | line-height: 60upx; |
| | | height: 60upx; |
| | | transition: all 0.2s linear; |
| | | |
| | | &.center { |
| | | width: 200upx; |
| | | } |
| | | |
| | | &.sub { |
| | | // position: absolute; |
| | | width: auto; |
| | | color: grey; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .searchBtn { |
| | | height: 100%; |
| | | flex-shrink: 0; |
| | | padding: 0 30upx; |
| | | background: $uni-color-success; |
| | | line-height: 60upx; |
| | | color: #fff; |
| | | border-left: 1px #ccc solid; |
| | | transition: all 0.3s; |
| | | } |
| | | } |
| | | |
| | | .button { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: relative; |
| | | flex-shrink: 0; |
| | | // width: 0; |
| | | transition: all 0.2s linear; |
| | | white-space: nowrap; |
| | | // overflow: hidden; |
| | | &.active { |
| | | padding-left: 15upx; |
| | | width: 100upx; |
| | | } |
| | | |
| | | .icon-more { |
| | | font-size: 48upx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .more-container-parent { |
| | | flex-shrink: 0; |
| | | width: 100%; |
| | | // position: fixed; |
| | | // position: sticky; |
| | | z-index: 997; |
| | | flex-wrap: nowrap; |
| | | display: flex; |
| | | flex-direction: row; |
| | | position: relative; |
| | | flex-direction: column; |
| | | |
| | | .more-container { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: auto; |
| | | background-color: #ffffff; |
| | | padding: 20rpx; |
| | | border-radius: 0 0 30rpx 30rpx; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | } |
| | | } |
| | | } |
| | | |
| | | @font-face { |
| | | font-family: 'iconfont'; |
| | | src: url('https://at.alicdn.com/t/c/font_4110624_nikfg21uyk8.ttf?t=1686190660183') format('truetype'); |
| | | } |
| | | |
| | | .icon { |
| | | font-family: iconfont; |
| | | font-size: 32upx; |
| | | font-style: normal; |
| | | color: #999; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "next-search-more", |
| | | "displayName": "next-search-more(vue2 vue3多端通用)搜索框组合、搜索下拉框组件全端可用", |
| | | "version": "1.0.9", |
| | | "description": "搜索下拉框,支持多种展示形式,可以结合第三方组件一起使用,可自定义", |
| | | "keywords": [ |
| | | "远程搜索", |
| | | "组合搜索", |
| | | "配置搜索", |
| | | "自定义搜索", |
| | | "搜索下拉框" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.1" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "插件不采集任何数据", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "n", |
| | | "app-nvue": "n" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "n", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "n", |
| | | "Edge": "n", |
| | | "Firefox": "n", |
| | | "Safari": "n" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "u", |
| | | "百度": "u", |
| | | "字节跳动": "u", |
| | | "QQ": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "n", |
| | | "联盟": "n" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | ## next-search-more --搜索更多,搜索下拉,search-more |
| | | |
| | | > 遇到问题或有建议可以加入QQ群(<font color=#f00>455948571</font>)反馈 |
| | | > 如果觉得组件不错,<font color=#f00>给五星鼓励鼓励</font>咯! |
| | | |
| | | |
| | | ## 使用 |
| | | |
| | | >[从uniapp插件市场导入](https://ext.dcloud.net.cn/plugin?name=next-search-more) |
| | | |
| | | ```html |
| | | <template> |
| | | <view class="index"> |
| | | <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>1. mode=more模式</text></view> |
| | | <next-search-more mode="more" :showMore="showMore" @moreClick="moreClick"> |
| | | <!--以下demo你可以借助第三方的插件实现你想要的任何复杂功能--> |
| | | <template #more> |
| | | <u--form labelWidth="80" labelAlign="right"> |
| | | <u-form-item label="radio:"> |
| | | <u-radio-group v-model="searchForm.radio" placement="row"> |
| | | <u-radio v-for="(item, index) in optionslist" :key="index" :label="item.name" :name="item.name" /> |
| | | </u-radio-group> |
| | | </u-form-item> |
| | | <u-form-item label="checkbox:"> |
| | | <u-checkbox-group v-model="searchForm.checkbox" placement="row"> |
| | | <u-checkbox v-for="(item, index) in optionslist" :key="index" :label="item.name" :name="item.name" /> |
| | | </u-checkbox-group> |
| | | </u-form-item> |
| | | <u-form-item label="rate:"> |
| | | <u-rate :count="5" v-model="searchForm.rate" /> |
| | | </u-form-item> |
| | | <u-form-item label="switch:"> |
| | | <u-switch v-model="searchForm.switch"></u-switch> |
| | | </u-form-item> |
| | | <u-form-item label="other:"> |
| | | <next-search-select |
| | | :multiple="false" |
| | | :list="options" |
| | | label-key="projectName" |
| | | value-key="id" |
| | | placeholder=" 请选择报备项目" |
| | | title="选择报备项目" |
| | | v-model:value="searchForm.projectId" |
| | | @search="searchFunc" |
| | | @change="changeCallback" |
| | | clearable |
| | | ></next-search-select> |
| | | </u-form-item> |
| | | <view class="flex-row"> |
| | | <u-button @click="cancel" :customStyle="{margin: '10rpx'}" text="取消"></u-button> |
| | | <u-button @click="comfirm" :customStyle="{margin: '10rpx'}" type="primary" text="确定"></u-button> |
| | | </view> |
| | | </u--form> |
| | | </template> |
| | | </next-search-more> |
| | | <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>2. mode=common;button=outside模式</text></view> |
| | | <next-search-more mode="common" button="outside" /> |
| | | <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>3. mode=common;button=inside模式</text></view> |
| | | <next-search-more mode="common" button="inside" /> |
| | | <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>4. mode=center;button=outside模式</text></view> |
| | | <next-search-more mode="center" button="outside" /> |
| | | <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>5. mode=center;button=inside模式</text></view> |
| | | <next-search-more mode="center" button="inside" /> |
| | | <view style="padding: 10px 0;background-color:#000000;color: #fff;font-size: 13px;"><text>6. mode=center;button=inside;isFixedSearchBtn=false模式</text></view> |
| | | <next-search-more mode="center" button="inside" :isFixedSearchBtn="false" /> |
| | | <view class="content-block"><text>全站ICON图标海量下载iconfont图标大全,为你优选-包图网,全站ICON图标海量下载iconfont图标大全,为你优选-包图网</text></view> |
| | | </view> |
| | | </template> |
| | | ``` |
| | | |
| | | ### vue3 + ts 使用 |
| | | |
| | | ```js |
| | | <script lang="ts"> |
| | | import { ref, nextTick, toRefs, toRaw, unref, reactive } from 'vue' |
| | | |
| | | export default { |
| | | setup() { |
| | | const showMore = ref(false) |
| | | const searchForm = reactive({ |
| | | radio: '', |
| | | checkbox: '', |
| | | rate: 3, |
| | | switch: false, |
| | | projectId: '' |
| | | }) |
| | | let dataLength = 0 |
| | | const options = ref<any>([]) |
| | | const optionslist = ref([{ |
| | | name: '苹果', |
| | | disabled: false |
| | | }, |
| | | { |
| | | name: '香蕉', |
| | | }, |
| | | { |
| | | name: '橙子', |
| | | } |
| | | ]) |
| | | function cancel () { |
| | | showMore.value = false |
| | | } |
| | | function comfirm () { |
| | | showMore.value = false |
| | | } |
| | | function moreClick () { |
| | | showMore.value = !unref(showMore) |
| | | } |
| | | function searchFunc(val?) { |
| | | console.log("搜索的关键字:", val) |
| | | uni.showLoading({ |
| | | title: '请稍后...', |
| | | icon: 'none' |
| | | }) |
| | | // 模拟ajax请求 |
| | | setTimeout(() => { |
| | | options.value = [] |
| | | dataLength = 0 |
| | | if (dataLength < 40) { |
| | | for (let i = 0; i < 40; i++) { |
| | | options.value.push({ |
| | | id: `id-${val ? val + '-' : ''}${dataLength + i}`, |
| | | projectName: `项目item-${val ? val + '-' : ''}${dataLength + i}`, |
| | | ohterKey: `test-${i}` |
| | | }) |
| | | } |
| | | dataLength = unref(options).length |
| | | } |
| | | uni.hideLoading() |
| | | }, 1000) |
| | | } |
| | | function changeCallback(item) { |
| | | console.log("选中的item:", item) |
| | | } |
| | | searchFunc() |
| | | return { |
| | | showMore, |
| | | moreClick, |
| | | optionslist, |
| | | searchForm, |
| | | searchFunc, |
| | | options, |
| | | changeCallback, |
| | | comfirm, |
| | | cancel |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss"> |
| | | |
| | | .flex-row { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | } |
| | | .content-block { |
| | | border-radius: 20rpx; |
| | | border: 1rpx solid #ccc; |
| | | margin: 20rpx; |
| | | padding: 20rpx; |
| | | } |
| | | </style> |
| | | <style lang="scss"> |
| | | page { |
| | | background: #ccc; |
| | | } |
| | | </style> |
| | | |
| | | |
| | | ``` |
| | | ### vue2 同样支持,在这里不再写demo |
| | | |
| | | ### 组件按需加载 |
| | | 如果不需要组件全局加载,而已把组件拷贝到项目的components目录下,单独引入进来使用即可达到按需加载的效果 |
| | | ### 预览 |
| | | |
| | | *** |
| | | |
| | | | 功能预览 | | 项目中应用演示 | |
| | | | :--------------------------------------------------------------------------:| | :-----------------------------------------------------------------------------:| |
| | | |  | |  | |
| | | |
| | | |
| | | ## 参数 |
| | | |
| | | |
| | | ### next-search-more Props |
| | | |
| | | 可选参数属性列表 |
| | | |
| | | |参数名 |说明 |类型 |是否必填 |默认值 |可选值 | |
| | | |---- |---- |---- |---- |---- |---- | |
| | | |mode |模式mode,支持common模式 center模式 more模式 |String |否 |common |center,more| |
| | | |button |搜索按钮的模式,支持outside模式 inside模式 |String |否 |outside |inside | |
| | | |isFixedSearchBtn |是否固定搜索按钮 |Boolean |否 |true |false | |
| | | |radius |搜索控件的radius |String, Number |否 |60 |- | |
| | | |placeholder | placeholder |String |否 |请输入搜索内容 |- | |
| | | |backgroundColor |搜索控件的背景颜色 |String |否 |#fff |- | |
| | | |showMore | mode=more模式下,用于控制打开下拉弹层 |Boolean |否 |false |true | |
| | | |border| border |String |否 |1px #f5f5f5 solid |- | |
| | | |
| | | |
| | | # Event 事件 |
| | | |事件名 |说明 |类型 |回调参数 | |
| | | |---- |---- |---- |---- | |
| | | |input |搜索框输入事件 |emit |- | |
| | | |search|搜索触发的事件 |emit |- | |
| | | |moreClick|更多按钮点击触发事件 |emit |- | |
| | | |
| | | ## Slot 插槽 |
| | | |
| | | |名称 |说明 |参数 | |
| | | |---- |---- |---- | |
| | | |more |more插槽,在mode=more模式下用于存放下拉框内容 |无 | |
New file |
| | |
| | | ## 1.2.2(2023-01-28) |
| | | - 修复 运行/打包 控制台警告问题 |
| | | ## 1.2.1(2022-09-05) |
| | | - 修复 当 text 超过 max-num 时,badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473) |
| | | ## 1.2.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge) |
| | | ## 1.1.7(2021-11-08) |
| | | - 优化 升级ui |
| | | - 修改 size 属性默认值调整为 small |
| | | - 修改 type 属性,默认值调整为 error,info 替换 default |
| | | ## 1.1.6(2021-09-22) |
| | | - 修复 在字节小程序上样式不生效的 bug |
| | | ## 1.1.5(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.1.4(2021-07-29) |
| | | - 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性 |
| | | ## 1.1.3(2021-06-24) |
| | | - 优化 示例项目 |
| | | ## 1.1.1(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.1.0(2021-05-12) |
| | | - 新增 uni-badge 的 absolute 属性,支持定位 |
| | | - 新增 uni-badge 的 offset 属性,支持定位偏移 |
| | | - 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点 |
| | | - 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+ |
| | | - 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式 |
| | | ## 1.0.7(2021-05-07) |
| | | - 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug |
| | | - 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug |
| | | - 新增 uni-badge 属性 custom-style, 支持自定义样式 |
| | | ## 1.0.6(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
New file |
| | |
| | | <template> |
| | | <view class="uni-badge--x"> |
| | | <slot /> |
| | | <text v-if="text" :class="classNames" :style="[positionStyle, customStyle, dotStyle]" |
| | | class="uni-badge" @click="onClick()">{{displayValue}}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Badge 数字角标 |
| | | * @description 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=21 |
| | | * @property {String} text 角标内容 |
| | | * @property {String} size = [normal|small] 角标内容 |
| | | * @property {String} type = [info|primary|success|warning|error] 颜色类型 |
| | | * @value info 灰色 |
| | | * @value primary 蓝色 |
| | | * @value success 绿色 |
| | | * @value warning 黄色 |
| | | * @value error 红色 |
| | | * @property {String} inverted = [true|false] 是否无需背景颜色 |
| | | * @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+ |
| | | * @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上 |
| | | * @value rightTop 右上 |
| | | * @value rightBottom 右下 |
| | | * @value leftTop 左上 |
| | | * @value leftBottom 左下 |
| | | * @property {Array[number]} offset 距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px,[10, 10] 表示向 absolute 指定的内偏移 10px |
| | | * @property {String} isDot = [true|false] 是否显示为一个小点 |
| | | * @event {Function} click 点击 Badge 触发事件 |
| | | * @example <uni-badge text="1"></uni-badge> |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniBadge', |
| | | emits: ['click'], |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: 'error' |
| | | }, |
| | | inverted: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | isDot: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | maxNum: { |
| | | type: Number, |
| | | default: 99 |
| | | }, |
| | | absolute: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | offset: { |
| | | type: Array, |
| | | default () { |
| | | return [0, 0] |
| | | } |
| | | }, |
| | | text: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | size: { |
| | | type: String, |
| | | default: 'small' |
| | | }, |
| | | customStyle: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return {}; |
| | | }, |
| | | computed: { |
| | | width() { |
| | | return String(this.text).length * 8 + 12 |
| | | }, |
| | | classNames() { |
| | | const { |
| | | inverted, |
| | | type, |
| | | size, |
| | | absolute |
| | | } = this |
| | | return [ |
| | | inverted ? 'uni-badge--' + type + '-inverted' : '', |
| | | 'uni-badge--' + type, |
| | | 'uni-badge--' + size, |
| | | absolute ? 'uni-badge--absolute' : '' |
| | | ].join(' ') |
| | | }, |
| | | positionStyle() { |
| | | if (!this.absolute) return {} |
| | | let w = this.width / 2, |
| | | h = 10 |
| | | if (this.isDot) { |
| | | w = 5 |
| | | h = 5 |
| | | } |
| | | const x = `${- w + this.offset[0]}px` |
| | | const y = `${- h + this.offset[1]}px` |
| | | |
| | | const whiteList = { |
| | | rightTop: { |
| | | right: x, |
| | | top: y |
| | | }, |
| | | rightBottom: { |
| | | right: x, |
| | | bottom: y |
| | | }, |
| | | leftBottom: { |
| | | left: x, |
| | | bottom: y |
| | | }, |
| | | leftTop: { |
| | | left: x, |
| | | top: y |
| | | } |
| | | } |
| | | const match = whiteList[this.absolute] |
| | | return match ? match : whiteList['rightTop'] |
| | | }, |
| | | dotStyle() { |
| | | if (!this.isDot) return {} |
| | | return { |
| | | width: '10px', |
| | | minWidth: '0', |
| | | height: '10px', |
| | | padding: '0', |
| | | borderRadius: '10px' |
| | | } |
| | | }, |
| | | displayValue() { |
| | | const { |
| | | isDot, |
| | | text, |
| | | maxNum |
| | | } = this |
| | | return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text) |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick() { |
| | | this.$emit('click'); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | $uni-primary: #2979ff !default; |
| | | $uni-success: #4cd964 !default; |
| | | $uni-warning: #f0ad4e !default; |
| | | $uni-error: #dd524d !default; |
| | | $uni-info: #909399 !default; |
| | | |
| | | |
| | | $bage-size: 12px; |
| | | $bage-small: scale(0.8); |
| | | |
| | | .uni-badge--x { |
| | | /* #ifdef APP-NVUE */ |
| | | // align-self: flex-start; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | display: inline-block; |
| | | /* #endif */ |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-badge--absolute { |
| | | position: absolute; |
| | | } |
| | | |
| | | .uni-badge--small { |
| | | transform: $bage-small; |
| | | transform-origin: center center; |
| | | } |
| | | |
| | | .uni-badge { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | overflow: hidden; |
| | | box-sizing: border-box; |
| | | font-feature-settings: "tnum"; |
| | | min-width: 20px; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | flex-direction: row; |
| | | height: 20px; |
| | | padding: 0 4px; |
| | | line-height: 18px; |
| | | color: #fff; |
| | | border-radius: 100px; |
| | | background-color: $uni-info; |
| | | background-color: transparent; |
| | | border: 1px solid #fff; |
| | | text-align: center; |
| | | font-family: 'Helvetica Neue', Helvetica, sans-serif; |
| | | font-size: $bage-size; |
| | | /* #ifdef H5 */ |
| | | z-index: 999; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | |
| | | &--info { |
| | | color: #fff; |
| | | background-color: $uni-info; |
| | | } |
| | | |
| | | &--primary { |
| | | background-color: $uni-primary; |
| | | } |
| | | |
| | | &--success { |
| | | background-color: $uni-success; |
| | | } |
| | | |
| | | &--warning { |
| | | background-color: $uni-warning; |
| | | } |
| | | |
| | | &--error { |
| | | background-color: $uni-error; |
| | | } |
| | | |
| | | &--inverted { |
| | | padding: 0 5px 0 0; |
| | | color: $uni-info; |
| | | } |
| | | |
| | | &--info-inverted { |
| | | color: $uni-info; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | &--primary-inverted { |
| | | color: $uni-primary; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | &--success-inverted { |
| | | color: $uni-success; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | &--warning-inverted { |
| | | color: $uni-warning; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | &--error-inverted { |
| | | color: $uni-error; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-badge", |
| | | "displayName": "uni-badge 数字角标", |
| | | "version": "1.2.2", |
| | | "description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。", |
| | | "keywords": [ |
| | | "", |
| | | "badge", |
| | | "uni-ui", |
| | | "uniui", |
| | | "数字角标", |
| | | "徽章" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "y", |
| | | "联盟": "y" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | ## Badge 数字角标 |
| | | > **组件名:uni-badge** |
| | | > 代码块: `uBadge` |
| | | |
| | | 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景, |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
| | | |
| | | |
New file |
| | |
| | | ## 0.1.2(2022-06-08) |
| | | - 修复 微信小程序 separator 不显示的Bug |
| | | ## 0.1.1(2022-06-02) |
| | | - 新增 支持 uni.scss 修改颜色 |
| | | ## 0.1.0(2022-04-21) |
| | | - 初始化 |
New file |
| | |
| | | <template> |
| | | <view class="uni-breadcrumb-item"> |
| | | <view :class="{ |
| | | 'uni-breadcrumb-item--slot': true, |
| | | 'uni-breadcrumb-item--slot-link': to && currentPage !== to |
| | | }" @click="navTo"> |
| | | <slot /> |
| | | </view> |
| | | <i v-if="separatorClass" class="uni-breadcrumb-item--separator" :class="separatorClass" /> |
| | | <text v-else class="uni-breadcrumb-item--separator">{{ separator }}</text> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | /** |
| | | * BreadcrumbItem 面包屑导航子组件 |
| | | * @property {String/Object} to 路由跳转页面路径/对象 |
| | | * @property {Boolean} replace 在使用 to 进行路由跳转时,启用 replace 将不会向 history 添加新记录(仅 h5 支持) |
| | | */ |
| | | export default { |
| | | data() { |
| | | return { |
| | | currentPage: "" |
| | | } |
| | | }, |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | props: { |
| | | to: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | replace:{ |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | inject: { |
| | | uniBreadcrumb: { |
| | | from: "uniBreadcrumb", |
| | | default: null |
| | | } |
| | | }, |
| | | created(){ |
| | | const pages = getCurrentPages() |
| | | const page = pages[pages.length-1] |
| | | |
| | | if(page){ |
| | | this.currentPage = `/${page.route}` |
| | | } |
| | | }, |
| | | computed: { |
| | | separator() { |
| | | return this.uniBreadcrumb.separator |
| | | }, |
| | | separatorClass() { |
| | | return this.uniBreadcrumb.separatorClass |
| | | } |
| | | }, |
| | | methods: { |
| | | navTo() { |
| | | const { to } = this |
| | | |
| | | if (!to || this.currentPage === to){ |
| | | return |
| | | } |
| | | |
| | | if(this.replace){ |
| | | uni.redirectTo({ |
| | | url:to |
| | | }) |
| | | }else{ |
| | | uni.navigateTo({ |
| | | url:to |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss"> |
| | | $uni-primary: #2979ff !default; |
| | | $uni-base-color: #6a6a6a !default; |
| | | $uni-main-color: #3a3a3a !default; |
| | | .uni-breadcrumb-item { |
| | | display: flex; |
| | | align-items: center; |
| | | white-space: nowrap; |
| | | font-size: 14px; |
| | | |
| | | &--slot { |
| | | color: $uni-base-color; |
| | | padding: 0 10px; |
| | | |
| | | &-link { |
| | | color: $uni-main-color; |
| | | font-weight: bold; |
| | | /* #ifndef APP-NVUE */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | |
| | | &:hover { |
| | | color: $uni-primary; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &--separator { |
| | | font-size: 12px; |
| | | color: $uni-base-color; |
| | | } |
| | | |
| | | &:first-child &--slot { |
| | | padding-left: 0; |
| | | } |
| | | |
| | | &:last-child &--separator { |
| | | display: none; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-breadcrumb"> |
| | | <slot /> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | /** |
| | | * Breadcrumb 面包屑导航父组件 |
| | | * @description 显示当前页面的路径,快速返回之前的任意页面 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx |
| | | * @property {String} separator 分隔符,默认为斜杠'/' |
| | | * @property {String} separatorClass 图标分隔符 class |
| | | */ |
| | | export default { |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | props: { |
| | | separator: { |
| | | type: String, |
| | | default: '/' |
| | | }, |
| | | separatorClass: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | |
| | | provide() { |
| | | return { |
| | | uniBreadcrumb: this |
| | | } |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | <style lang="scss"> |
| | | .uni-breadcrumb { |
| | | display: flex; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-breadcrumb", |
| | | "displayName": "uni-breadcrumb 面包屑", |
| | | "version": "0.1.2", |
| | | "description": "Breadcrumb 面包屑", |
| | | "keywords": [ |
| | | "uni-breadcrumb", |
| | | "breadcrumb", |
| | | "uni-ui", |
| | | "面包屑导航", |
| | | "面包屑" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "n" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "u", |
| | | "百度": "u", |
| | | "字节跳动": "u", |
| | | "QQ": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | ## breadcrumb 面包屑导航 |
| | | > **组件名:uni-breadcrumb** |
| | | > 代码块: `ubreadcrumb` |
| | | |
| | | 显示当前页面的路径,快速返回之前的任意页面。 |
| | | |
| | | ### 安装方式 |
| | | |
| | | 本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 |
| | | |
| | | 如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) |
| | | |
| | | ### 基本用法 |
| | | |
| | | 在 ``template`` 中使用组件 |
| | | |
| | | ```html |
| | | <uni-breadcrumb separator="/"> |
| | | <uni-breadcrumb-item v-for="(route,index) in routes" :key="index" :to="route.to">{{route.name}}</uni-breadcrumb-item> |
| | | </uni-breadcrumb> |
| | | ``` |
| | | |
| | | ```js |
| | | export default { |
| | | name: "uni-stat-breadcrumb", |
| | | data() { |
| | | return { |
| | | routes: [{ |
| | | to: '/A', |
| | | name: 'A页面' |
| | | }, { |
| | | to: '/B', |
| | | name: 'B页面' |
| | | }, { |
| | | to: '/C', |
| | | name: 'C页面' |
| | | }] |
| | | }; |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | |
| | | ## API |
| | | |
| | | ### Breadcrumb Props |
| | | |
| | | |属性名 |类型 |默认值 |说明 | |
| | | |:-: |:-: |:-: |:-: | |
| | | |separator |String |斜杠'/' |分隔符 | |
| | | |separatorClass |String | |图标分隔符 class | |
| | | |
| | | ### Breadcrumb Item Props |
| | | |
| | | |属性名 |类型 |默认值 |说明 | |
| | | |:-: |:-: |:-: |:-: | |
| | | |to |String | |路由跳转页面路径 | |
| | | |replace|Boolean | |在使用 to 进行路由跳转时,启用 replace 将不会向 history 添加新记录(仅 h5 支持) | |
| | | |
| | | |
| | | |
| | | |
| | | ## 组件示例 |
| | | |
| | | 点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/breadcrumb/breadcrumb](https://hellouniapp.dcloud.net.cn/pages/extUI/breadcrumb/breadcrumb) |
New file |
| | |
| | | ## 1.4.10(2023-04-10) |
| | | - 修复 某些情况 monthSwitch 未触发的Bug |
| | | ## 1.4.9(2023-02-02) |
| | | - 修复 某些情况切换月份错误的Bug |
| | | ## 1.4.8(2023-01-30) |
| | | - 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/161964) |
| | | ## 1.4.7(2022-09-16) |
| | | - 优化 支持使用 uni-scss 控制主题色 |
| | | ## 1.4.6(2022-09-08) |
| | | - 修复 表头年月切换,导致改变当前日期为选择月1号,且未触发change事件的Bug |
| | | ## 1.4.5(2022-02-25) |
| | | - 修复 条件编译 nvue 不支持的 css 样式的Bug |
| | | ## 1.4.4(2022-02-25) |
| | | - 修复 条件编译 nvue 不支持的 css 样式的Bug |
| | | ## 1.4.3(2021-09-22) |
| | | - 修复 startDate、 endDate 属性失效的Bug |
| | | ## 1.4.2(2021-08-24) |
| | | - 新增 支持国际化 |
| | | ## 1.4.1(2021-08-05) |
| | | - 修复 弹出层被 tabbar 遮盖的Bug |
| | | ## 1.4.0(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.3.16(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.3.15(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
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 |
| | |
| | | { |
| | | "uni-calender.ok": "ok", |
| | | "uni-calender.cancel": "cancel", |
| | | "uni-calender.today": "today", |
| | | "uni-calender.MON": "MON", |
| | | "uni-calender.TUE": "TUE", |
| | | "uni-calender.WED": "WED", |
| | | "uni-calender.THU": "THU", |
| | | "uni-calender.FRI": "FRI", |
| | | "uni-calender.SAT": "SAT", |
| | | "uni-calender.SUN": "SUN" |
| | | } |
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 |
| | |
| | | { |
| | | "uni-calender.ok": "确定", |
| | | "uni-calender.cancel": "取消", |
| | | "uni-calender.today": "今日", |
| | | "uni-calender.SUN": "日", |
| | | "uni-calender.MON": "一", |
| | | "uni-calender.TUE": "二", |
| | | "uni-calender.WED": "三", |
| | | "uni-calender.THU": "四", |
| | | "uni-calender.FRI": "五", |
| | | "uni-calender.SAT": "六" |
| | | } |
New file |
| | |
| | | { |
| | | "uni-calender.ok": "確定", |
| | | "uni-calender.cancel": "取消", |
| | | "uni-calender.today": "今日", |
| | | "uni-calender.SUN": "日", |
| | | "uni-calender.MON": "一", |
| | | "uni-calender.TUE": "二", |
| | | "uni-calender.WED": "三", |
| | | "uni-calender.THU": "四", |
| | | "uni-calender.FRI": "五", |
| | | "uni-calender.SAT": "六" |
| | | } |
New file |
| | |
| | | <template> |
| | | <view class="uni-calendar-item__weeks-box" :class="{ |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) , |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | }" |
| | | @click="choiceDate(weeks)"> |
| | | <view class="uni-calendar-item__weeks-box-item"> |
| | | <text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> |
| | | <text class="uni-calendar-item__weeks-box-text" :class="{ |
| | | 'uni-calendar-item--isDay-text': weeks.isDay, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | }">{{weeks.date}}</text> |
| | | <text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{ |
| | | 'uni-calendar-item--isDay-text':weeks.isDay, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | }">{{todayText}}</text> |
| | | <text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{ |
| | | 'uni-calendar-item--isDay-text':weeks.isDay, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | }">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text> |
| | | <text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{ |
| | | 'uni-calendar-item--extra':weeks.extraInfo.info, |
| | | 'uni-calendar-item--isDay-text':weeks.isDay, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | }">{{weeks.extraInfo.info}}</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | 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 |
| | | } |
| | | }, |
| | | computed: { |
| | | todayText() { |
| | | return t("uni-calender.today") |
| | | }, |
| | | }, |
| | | methods: { |
| | | choiceDate(weeks) { |
| | | this.$emit('change', weeks) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $uni-font-size-base:14px; |
| | | $uni-text-color:#333; |
| | | $uni-font-size-sm:12px; |
| | | $uni-color-error: #e43d33; |
| | | $uni-opacity-disabled: 0.3; |
| | | $uni-text-color-disable:#c0c0c0; |
| | | $uni-primary: #2979ff !default; |
| | | .uni-calendar-item__weeks-box { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-text { |
| | | font-size: $uni-font-size-base; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-lunar-text { |
| | | font-size: $uni-font-size-sm; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-item { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-circle { |
| | | position: absolute; |
| | | top: 5px; |
| | | right: 5px; |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 8px; |
| | | background-color: $uni-color-error; |
| | | |
| | | } |
| | | |
| | | .uni-calendar-item--disable { |
| | | background-color: rgba(249, 249, 249, $uni-opacity-disabled); |
| | | color: $uni-text-color-disable; |
| | | } |
| | | |
| | | .uni-calendar-item--isDay-text { |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .uni-calendar-item--isDay { |
| | | background-color: $uni-primary; |
| | | opacity: 0.8; |
| | | color: #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--extra { |
| | | color: $uni-color-error; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .uni-calendar-item--checked { |
| | | background-color: $uni-primary; |
| | | color: #fff; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .uni-calendar-item--multiple { |
| | | background-color: $uni-primary; |
| | | color: #fff; |
| | | opacity: 0.8; |
| | | } |
| | | .uni-calendar-item--before-checked { |
| | | background-color: #ff5a5f; |
| | | color: #fff; |
| | | } |
| | | .uni-calendar-item--after-checked { |
| | | background-color: #ff5a5f; |
| | | color: #fff; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-calendar"> |
| | | <view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view> |
| | | <view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}"> |
| | | <view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top"> |
| | | <view class="uni-calendar__header-btn-box" @click="close"> |
| | | <text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__header-btn-box" @click="confirm"> |
| | | <text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text> |
| | | </view> |
| | | </view> |
| | | <view class="uni-calendar__header"> |
| | | <view class="uni-calendar__header-btn-box" @click.stop="pre"> |
| | | <view class="uni-calendar__header-btn uni-calendar--left"></view> |
| | | </view> |
| | | <picker mode="date" :value="date" fields="month" @change="bindDateChange"> |
| | | <text class="uni-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text> |
| | | </picker> |
| | | <view class="uni-calendar__header-btn-box" @click.stop="next"> |
| | | <view class="uni-calendar__header-btn uni-calendar--right"></view> |
| | | </view> |
| | | <text class="uni-calendar__backtoday" @click="backToday">{{todayText}}</text> |
| | | |
| | | </view> |
| | | <view class="uni-calendar__box"> |
| | | <view v-if="showMonth" class="uni-calendar__box-bg"> |
| | | <text class="uni-calendar__box-bg-text">{{nowDate.month}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks"> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{SUNText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{monText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{TUEText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{WEDText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{THUText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{FRIText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{SATText}}</text> |
| | | </view> |
| | | </view> |
| | | <view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex"> |
| | | <view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex"> |
| | | <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import Calendar from './util.js'; |
| | | import CalendarItem from './uni-calendar-item.vue' |
| | | |
| | | import { initVueI18n } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | const { t } = initVueI18n(i18nMessages) |
| | | |
| | | /** |
| | | * Calendar 日历 |
| | | * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=56 |
| | | * @property {String} date 自定义当前时间,默认为今天 |
| | | * @property {Boolean} lunar 显示农历 |
| | | * @property {String} startDate 日期选择范围-开始日期 |
| | | * @property {String} endDate 日期选择范围-结束日期 |
| | | * @property {Boolean} range 范围选择 |
| | | * @property {Boolean} insert = [true|false] 插入模式,默认为false |
| | | * @value true 弹窗模式 |
| | | * @value false 插入模式 |
| | | * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容 |
| | | * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
| | | * @property {Boolean} showMonth 是否选择月份为背景 |
| | | * @event {Function} change 日期改变,`insert :ture` 时生效 |
| | | * @event {Function} confirm 确认选择`insert :false` 时生效 |
| | | * @event {Function} monthSwitch 切换月份时触发 |
| | | * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> |
| | | */ |
| | | export default { |
| | | components: { |
| | | CalendarItem |
| | | }, |
| | | emits:['close','confirm','change','monthSwitch'], |
| | | props: { |
| | | date: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | lunar: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | startDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | endDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | range: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | insert: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showMonth: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | clearDate: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | show: false, |
| | | weeks: [], |
| | | calendar: {}, |
| | | nowDate: '', |
| | | aniMaskShow: false |
| | | } |
| | | }, |
| | | computed:{ |
| | | /** |
| | | * for i18n |
| | | */ |
| | | |
| | | okText() { |
| | | return t("uni-calender.ok") |
| | | }, |
| | | cancelText() { |
| | | return t("uni-calender.cancel") |
| | | }, |
| | | todayText() { |
| | | return t("uni-calender.today") |
| | | }, |
| | | monText() { |
| | | return t("uni-calender.MON") |
| | | }, |
| | | TUEText() { |
| | | return t("uni-calender.TUE") |
| | | }, |
| | | WEDText() { |
| | | return t("uni-calender.WED") |
| | | }, |
| | | THUText() { |
| | | return t("uni-calender.THU") |
| | | }, |
| | | FRIText() { |
| | | return t("uni-calender.FRI") |
| | | }, |
| | | SATText() { |
| | | return t("uni-calender.SAT") |
| | | }, |
| | | SUNText() { |
| | | return t("uni-calender.SUN") |
| | | }, |
| | | }, |
| | | watch: { |
| | | date(newVal) { |
| | | // this.cale.setDate(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.cale = new Calendar({ |
| | | selected: this.selected, |
| | | startDate: this.startDate, |
| | | endDate: this.endDate, |
| | | range: this.range, |
| | | }) |
| | | this.init(this.date) |
| | | }, |
| | | methods: { |
| | | // 取消穿透 |
| | | clean() {}, |
| | | 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) { |
| | | this.cale.setDate(date) |
| | | this.weeks = this.cale.weeks |
| | | this.nowDate = this.calendar = this.cale.getInfo(date) |
| | | }, |
| | | /** |
| | | * 打开日历弹窗 |
| | | */ |
| | | open() { |
| | | // 弹窗模式并且清理数据 |
| | | if (this.clearDate && !this.insert) { |
| | | this.cale.cleanMultipleStatus() |
| | | // this.cale.setDate(this.date) |
| | | this.init(this.date) |
| | | } |
| | | this.show = true |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this.aniMaskShow = true |
| | | }, 50) |
| | | }) |
| | | }, |
| | | /** |
| | | * 关闭日历弹窗 |
| | | */ |
| | | close() { |
| | | this.aniMaskShow = false |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this.show = false |
| | | this.$emit('close') |
| | | }, 300) |
| | | }) |
| | | }, |
| | | /** |
| | | * 确认按钮 |
| | | */ |
| | | confirm() { |
| | | this.setEmit('confirm') |
| | | this.close() |
| | | }, |
| | | /** |
| | | * 变化触发 |
| | | */ |
| | | change() { |
| | | if (!this.insert) return |
| | | this.setEmit('change') |
| | | }, |
| | | /** |
| | | * 选择月份触发 |
| | | */ |
| | | monthSwitch() { |
| | | let { |
| | | year, |
| | | month |
| | | } = this.nowDate |
| | | this.$emit('monthSwitch', { |
| | | year, |
| | | month: Number(month) |
| | | }) |
| | | }, |
| | | /** |
| | | * 派发事件 |
| | | * @param {Object} name |
| | | */ |
| | | setEmit(name) { |
| | | let { |
| | | year, |
| | | month, |
| | | date, |
| | | fullDate, |
| | | lunar, |
| | | extraInfo |
| | | } = this.calendar |
| | | this.$emit(name, { |
| | | range: this.cale.multipleStatus, |
| | | year, |
| | | month, |
| | | date, |
| | | fulldate: fullDate, |
| | | lunar, |
| | | extraInfo: extraInfo || {} |
| | | }) |
| | | }, |
| | | /** |
| | | * 选择天触发 |
| | | * @param {Object} weeks |
| | | */ |
| | | choiceDate(weeks) { |
| | | if (weeks.disable) return |
| | | this.calendar = weeks |
| | | // 设置多选 |
| | | 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}` |
| | | |
| | | if(nowYearMonth !== todayYearMonth) { |
| | | this.monthSwitch() |
| | | } |
| | | |
| | | this.init(date.fullDate) |
| | | 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 lang="scss" scoped> |
| | | $uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4); |
| | | $uni-border-color: #EDEDED; |
| | | $uni-text-color: #333; |
| | | $uni-bg-color-hover:#f1f1f1; |
| | | $uni-font-size-base:14px; |
| | | $uni-text-color-placeholder: #808080; |
| | | $uni-color-subtitle: #555555; |
| | | $uni-text-color-grey:#999; |
| | | .uni-calendar { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-calendar__mask { |
| | | position: fixed; |
| | | bottom: 0; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background-color: $uni-bg-color-mask; |
| | | transition-property: opacity; |
| | | transition-duration: 0.3s; |
| | | opacity: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-calendar--mask-show { |
| | | opacity: 1 |
| | | } |
| | | |
| | | .uni-calendar--fixed { |
| | | position: fixed; |
| | | /* #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 */ |
| | | } |
| | | |
| | | .uni-calendar--ani-show { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | .uni-calendar__content { |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .uni-calendar__header { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 50px; |
| | | border-bottom-color: $uni-border-color; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar--fixed-top { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | border-top-color: $uni-border-color; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar--fixed-width { |
| | | width: 50px; |
| | | } |
| | | |
| | | .uni-calendar__backtoday { |
| | | position: absolute; |
| | | right: 0; |
| | | top: 25rpx; |
| | | padding: 0 5px; |
| | | padding-left: 10px; |
| | | height: 25px; |
| | | line-height: 25px; |
| | | font-size: 12px; |
| | | border-top-left-radius: 25px; |
| | | border-bottom-left-radius: 25px; |
| | | color: $uni-text-color; |
| | | background-color: $uni-bg-color-hover; |
| | | } |
| | | |
| | | .uni-calendar__header-text { |
| | | text-align: center; |
| | | width: 100px; |
| | | font-size: $uni-font-size-base; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-calendar__header-btn-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 50px; |
| | | height: 50px; |
| | | } |
| | | |
| | | .uni-calendar__header-btn { |
| | | width: 10px; |
| | | height: 10px; |
| | | border-left-color: $uni-text-color-placeholder; |
| | | border-left-style: solid; |
| | | border-left-width: 2px; |
| | | border-top-color: $uni-color-subtitle; |
| | | border-top-style: solid; |
| | | border-top-width: 2px; |
| | | } |
| | | |
| | | .uni-calendar--left { |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .uni-calendar--right { |
| | | transform: rotate(135deg); |
| | | } |
| | | |
| | | |
| | | .uni-calendar__weeks { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-calendar__weeks-item { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-calendar__weeks-day { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 45px; |
| | | border-bottom-color: #F5F5F5; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar__weeks-day-text { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-calendar__box { |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-calendar__box-bg { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | } |
| | | |
| | | .uni-calendar__box-bg-text { |
| | | font-size: 200px; |
| | | font-weight: bold; |
| | | color: $uni-text-color-grey; |
| | | opacity: 0.1; |
| | | text-align: center; |
| | | /* #ifndef APP-NVUE */ |
| | | line-height: 1; |
| | | /* #endif */ |
| | | } |
| | | </style> |
New file |
| | |
| | | import CALENDAR from './calendar.js' |
| | | |
| | | class Calendar { |
| | | constructor({ |
| | | date, |
| | | selected, |
| | | startDate, |
| | | endDate, |
| | | range |
| | | } = {}) { |
| | | // 当前日期 |
| | | this.date = this.getDate(new Date()) // 当前初入日期 |
| | | // 打点信息 |
| | | this.selected = selected || []; |
| | | // 范围开始 |
| | | this.startDate = startDate |
| | | // 范围结束 |
| | | this.endDate = endDate |
| | | this.range = range |
| | | // 多选状态 |
| | | this.cleanMultipleStatus() |
| | | // 每周日期 |
| | | this.weeks = {} |
| | | // this._getWeek(this.date.fullDate) |
| | | } |
| | | /** |
| | | * 设置日期 |
| | | * @param {Object} date |
| | | */ |
| | | setDate(date) { |
| | | this.selectDate = this.getDate(date) |
| | | this._getWeek(this.selectDate.fullDate) |
| | | } |
| | | |
| | | /** |
| | | * 清理多选状态 |
| | | */ |
| | | cleanMultipleStatus() { |
| | | this.multipleStatus = { |
| | | before: '', |
| | | after: '', |
| | | 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 multiples = this.multipleStatus.data |
| | | let checked = false |
| | | let multiplesStatus = -1 |
| | | if (this.range) { |
| | | if (multiples) { |
| | | multiplesStatus = multiples.findIndex((item) => { |
| | | return this.dateEqual(item, nowDate) |
| | | }) |
| | | } |
| | | if (multiplesStatus !== -1) { |
| | | checked = true |
| | | } |
| | | } |
| | | let data = { |
| | | fullDate: nowDate, |
| | | year: full.year, |
| | | date: i, |
| | | multiple: this.range ? checked : false, |
| | | beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate), |
| | | afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate), |
| | | 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() |
| | | } |
| | | 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 |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取日期范围内所有日期 |
| | | * @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) { |
| | | let { |
| | | before, |
| | | after |
| | | } = this.multipleStatus |
| | | |
| | | if (!this.range) return |
| | | if (before && after) { |
| | | this.multipleStatus.before = '' |
| | | this.multipleStatus.after = '' |
| | | this.multipleStatus.data = [] |
| | | } else { |
| | | if (!before) { |
| | | this.multipleStatus.before = fullDate |
| | | } else { |
| | | this.multipleStatus.after = fullDate |
| | | if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); |
| | | } else { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before); |
| | | } |
| | | } |
| | | } |
| | | this._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 |
| | |
| | | { |
| | | "id": "uni-calendar", |
| | | "displayName": "uni-calendar 日历", |
| | | "version": "1.4.10", |
| | | "description": "日历组件", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "日历", |
| | | "", |
| | | "打卡", |
| | | "日历选择" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ## Calendar 日历 |
| | | > **组件名:uni-calendar** |
| | | > 代码块: `uCalendar` |
| | | |
| | | |
| | | 日历组件 |
| | | |
| | | > **注意事项** |
| | | > 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 |
| | | > - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js) |
| | | > - 仅支持自定义组件模式 |
| | | > - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date() |
| | | > - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意 |
| | | > - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动 |
| | | |
| | | |
| | | ### 安装方式 |
| | | |
| | | 本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 |
| | | |
| | | 如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) |
| | | |
| | | ### 基本用法 |
| | | |
| | | 在 ``template`` 中使用组件 |
| | | |
| | | ```html |
| | | <view> |
| | | <uni-calendar |
| | | :insert="true" |
| | | :lunar="true" |
| | | :start-date="'2019-3-2'" |
| | | :end-date="'2019-5-20'" |
| | | @change="change" |
| | | /> |
| | | </view> |
| | | ``` |
| | | |
| | | ### 通过方法打开日历 |
| | | |
| | | 需要设置 `insert` 为 `false` |
| | | |
| | | ```html |
| | | <view> |
| | | <uni-calendar |
| | | ref="calendar" |
| | | :insert="false" |
| | | @confirm="confirm" |
| | | /> |
| | | <button @click="open">打开日历</button> |
| | | </view> |
| | | ``` |
| | | |
| | | ```javascript |
| | | |
| | | export default { |
| | | data() { |
| | | return {}; |
| | | }, |
| | | methods: { |
| | | open(){ |
| | | this.$refs.calendar.open(); |
| | | }, |
| | | confirm(e) { |
| | | console.log(e); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | ``` |
| | | |
| | | |
| | | ## API |
| | | |
| | | ### Calendar Props |
| | | |
| | | | 属性名 | 类型 | 默认值| 说明 | |
| | | | - | - | - | - | |
| | | | date | String |- | 自定义当前时间,默认为今天 | |
| | | | lunar | Boolean | false | 显示农历 | |
| | | | startDate | String |- | 日期选择范围-开始日期 | |
| | | | endDate | String |- | 日期选择范围-结束日期 | |
| | | | range | Boolean | false | 范围选择 | |
| | | | insert | Boolean | false | 插入模式,可选值,ture:插入模式;false:弹窗模式;默认为插入模式 | |
| | | |clearDate |Boolean |true |弹窗模式是否清空上次选择内容 | |
| | | | selected | Array |- | 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] | |
| | | |showMonth | Boolean | true | 是否显示月份为背景 | |
| | | |
| | | ### Calendar Events |
| | | |
| | | | 事件名 | 说明 |返回值| |
| | | | - | - | - | |
| | | | open | 弹出日历组件,`insert :false` 时生效|- | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | ## 组件示例 |
| | | |
| | | 点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar) |
New file |
| | |
| | | ## 1.3.1(2021-12-20) |
| | | - 修复 在vue页面下略缩图显示不正常的bug |
| | | ## 1.3.0(2021-11-19) |
| | | - 重构插槽的用法 ,header 替换为 title |
| | | - 新增 actions 插槽 |
| | | - 新增 cover 封面图属性和插槽 |
| | | - 新增 padding 内容默认内边距离 |
| | | - 新增 margin 卡片默认外边距离 |
| | | - 新增 spacing 卡片默认内边距 |
| | | - 新增 shadow 卡片阴影属性 |
| | | - 取消 mode 属性,可使用组合插槽代替 |
| | | - 取消 note 属性 ,使用actions插槽代替 |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-card](https://uniapp.dcloud.io/component/uniui/uni-card) |
| | | ## 1.2.1(2021-07-30) |
| | | - 优化 vue3下事件警告的问题 |
| | | ## 1.2.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.1.8(2021-07-01) |
| | | - 优化 图文卡片无图片加载时,提供占位图标 |
| | | - 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持) |
| | | - 修复 thumbnail 不存在仍然占位的 bug |
| | | ## 1.1.7(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.1.6(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
New file |
| | |
| | | <template> |
| | | <view class="uni-card" :class="{ 'uni-card--full': isFull, 'uni-card--shadow': isShadow,'uni-card--border':border}" |
| | | :style="{'margin':isFull?0:margin,'padding':spacing,'box-shadow':isShadow?shadow:''}"> |
| | | <!-- 封面 --> |
| | | <slot name="cover"> |
| | | <view v-if="cover" class="uni-card__cover"> |
| | | <image class="uni-card__cover-image" mode="widthFix" @click="onClick('cover')" :src="cover"></image> |
| | | </view> |
| | | </slot> |
| | | <slot name="title"> |
| | | <view v-if="title || extra" class="uni-card__header"> |
| | | <!-- 卡片标题 --> |
| | | <view class="uni-card__header-box" @click="onClick('title')"> |
| | | <view v-if="thumbnail" class="uni-card__header-avatar"> |
| | | <image class="uni-card__header-avatar-image" :src="thumbnail" mode="aspectFit" /> |
| | | </view> |
| | | <view class="uni-card__header-content"> |
| | | <text class="uni-card__header-content-title uni-ellipsis">{{ title }}</text> |
| | | <text v-if="title&&subTitle" |
| | | class="uni-card__header-content-subtitle uni-ellipsis">{{ subTitle }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="uni-card__header-extra" @click="onClick('extra')"> |
| | | <slot name="extra"> |
| | | <text class="uni-card__header-extra-text">{{ extra }}</text> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | <!-- 卡片内容 --> |
| | | <view class="uni-card__content" :style="{padding:padding}" @click="onClick('content')"> |
| | | <slot></slot> |
| | | </view> |
| | | <view class="uni-card__actions" @click="onClick('actions')"> |
| | | <slot name="actions"></slot> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Card 卡片 |
| | | * @description 卡片视图组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=22 |
| | | * @property {String} title 标题文字 |
| | | * @property {String} subTitle 副标题 |
| | | * @property {Number} padding 内容内边距 |
| | | * @property {Number} margin 卡片外边距 |
| | | * @property {Number} spacing 卡片内边距 |
| | | * @property {String} extra 标题额外信息 |
| | | * @property {String} cover 封面图(本地路径需要引入) |
| | | * @property {String} thumbnail 标题左侧缩略图 |
| | | * @property {Boolean} is-full = [true | false] 卡片内容是否通栏,为 true 时将去除padding值 |
| | | * @property {Boolean} is-shadow = [true | false] 卡片内容是否开启阴影 |
| | | * @property {String} shadow 卡片阴影 |
| | | * @property {Boolean} border 卡片边框 |
| | | * @event {Function} click 点击 Card 触发事件 |
| | | */ |
| | | export default { |
| | | name: 'UniCard', |
| | | emits: ['click'], |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | subTitle: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | padding: { |
| | | type: String, |
| | | default: '10px' |
| | | }, |
| | | margin: { |
| | | type: String, |
| | | default: '15px' |
| | | }, |
| | | spacing: { |
| | | type: String, |
| | | default: '0 10px' |
| | | }, |
| | | extra: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | cover: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | thumbnail: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | isFull: { |
| | | // 内容区域是否通栏 |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | isShadow: { |
| | | // 是否开启阴影 |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | shadow: { |
| | | type: String, |
| | | default: '0px 0px 3px 1px rgba(0, 0, 0, 0.08)' |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick(type) { |
| | | this.$emit('click', type) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-border-3: #EBEEF5 !default; |
| | | $uni-shadow-base:0 0px 6px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default; |
| | | $uni-main-color: #3a3a3a !default; |
| | | $uni-base-color: #6a6a6a !default; |
| | | $uni-secondary-color: #909399 !default; |
| | | $uni-spacing-sm: 8px !default; |
| | | $uni-border-color:$uni-border-3; |
| | | $uni-shadow: $uni-shadow-base; |
| | | $uni-card-title: 15px; |
| | | $uni-cart-title-color:$uni-main-color; |
| | | $uni-card-subtitle: 12px; |
| | | $uni-cart-subtitle-color:$uni-secondary-color; |
| | | $uni-card-spacing: 10px; |
| | | $uni-card-content-color: $uni-base-color; |
| | | |
| | | .uni-card { |
| | | margin: $uni-card-spacing; |
| | | padding: 0 $uni-spacing-sm; |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif; |
| | | background-color: #fff; |
| | | flex: 1; |
| | | |
| | | .uni-card__cover { |
| | | position: relative; |
| | | margin-top: $uni-card-spacing; |
| | | flex-direction: row; |
| | | overflow: hidden; |
| | | border-radius: 4px; |
| | | .uni-card__cover-image { |
| | | flex: 1; |
| | | // width: 100%; |
| | | /* #ifndef APP-PLUS */ |
| | | vertical-align: middle; |
| | | /* #endif */ |
| | | } |
| | | } |
| | | |
| | | .uni-card__header { |
| | | display: flex; |
| | | border-bottom: 1px $uni-border-color solid; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: $uni-card-spacing; |
| | | overflow: hidden; |
| | | |
| | | .uni-card__header-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-card__header-avatar { |
| | | width: 40px; |
| | | height: 40px; |
| | | overflow: hidden; |
| | | border-radius: 5px; |
| | | margin-right: $uni-card-spacing; |
| | | .uni-card__header-avatar-image { |
| | | flex: 1; |
| | | width: 40px; |
| | | height: 40px; |
| | | } |
| | | } |
| | | |
| | | .uni-card__header-content { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | flex: 1; |
| | | // height: 40px; |
| | | overflow: hidden; |
| | | |
| | | .uni-card__header-content-title { |
| | | font-size: $uni-card-title; |
| | | color: $uni-cart-title-color; |
| | | // line-height: 22px; |
| | | } |
| | | |
| | | .uni-card__header-content-subtitle { |
| | | font-size: $uni-card-subtitle; |
| | | margin-top: 5px; |
| | | color: $uni-cart-subtitle-color; |
| | | } |
| | | } |
| | | |
| | | .uni-card__header-extra { |
| | | line-height: 12px; |
| | | |
| | | .uni-card__header-extra-text { |
| | | font-size: 12px; |
| | | color: $uni-cart-subtitle-color; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .uni-card__content { |
| | | padding: $uni-card-spacing; |
| | | font-size: 14px; |
| | | color: $uni-card-content-color; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .uni-card__actions { |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | |
| | | .uni-card--border { |
| | | border: 1px solid $uni-border-color; |
| | | } |
| | | |
| | | .uni-card--shadow { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | box-shadow: $uni-shadow; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-card--full { |
| | | margin: 0; |
| | | border-left-width: 0; |
| | | border-left-width: 0; |
| | | border-radius: 0; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-card--full:after { |
| | | border-radius: 0; |
| | | } |
| | | |
| | | /* #endif */ |
| | | .uni-ellipsis { |
| | | /* #ifndef APP-NVUE */ |
| | | overflow: hidden; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 1; |
| | | /* #endif */ |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-card", |
| | | "displayName": "uni-card 卡片", |
| | | "version": "1.3.1", |
| | | "description": "Card 组件,提供常见的卡片样式。", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "card", |
| | | "", |
| | | "卡片" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-icons", |
| | | "uni-scss" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ## Card 卡片 |
| | | > **组件名:uni-card** |
| | | > 代码块: `uCard` |
| | | |
| | | 卡片视图组件。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-card) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
| | | |
| | | |
New file |
| | |
| | | ## 1.4.3(2022-01-25) |
| | | - 修复 初始化的时候 ,open 属性失效的bug |
| | | ## 1.4.2(2022-01-21) |
| | | - 修复 微信小程序resize后组件收起的bug |
| | | ## 1.4.1(2021-11-22) |
| | | - 修复 vue3中个别scss变量无法找到的问题 |
| | | ## 1.4.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-collapse](https://uniapp.dcloud.io/component/uniui/uni-collapse) |
| | | ## 1.3.3(2021-08-17) |
| | | - 优化 show-arrow 属性默认为true |
| | | ## 1.3.2(2021-08-17) |
| | | - 新增 show-arrow 属性,控制是否显示右侧箭头 |
| | | ## 1.3.1(2021-07-30) |
| | | - 优化 vue3下小程序事件警告的问题 |
| | | ## 1.3.0(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.2.2(2021-07-21) |
| | | - 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug |
| | | ## 1.2.1(2021-07-21) |
| | | - 优化 组件示例 |
| | | ## 1.2.0(2021-07-21) |
| | | - 新增 组件折叠动画 |
| | | - 新增 value\v-model 属性 ,动态修改面板折叠状态 |
| | | - 新增 title 插槽 ,可定义面板标题 |
| | | - 新增 border 属性 ,显示隐藏面板内容分隔线 |
| | | - 新增 title-border 属性 ,显示隐藏面板标题分隔线 |
| | | - 修复 resize 方法失效的Bug |
| | | - 修复 change 事件返回参数不正确的Bug |
| | | - 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法 |
| | | ## 1.1.7(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.1.6(2021-02-05) |
| | | - 优化 组件引用关系,通过uni_modules引用组件 |
| | | ## 1.1.5(2021-02-05) |
| | | - 调整为uni_modules目录规范 |
New file |
| | |
| | | <template> |
| | | <view class="uni-collapse-item"> |
| | | <!-- onClick(!isOpen) --> |
| | | <view @click="onClick(!isOpen)" class="uni-collapse-item__title" |
| | | :class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}"> |
| | | <view class="uni-collapse-item__title-wrap"> |
| | | <slot name="title"> |
| | | <view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}"> |
| | | <image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" /> |
| | | <text class="uni-collapse-item__title-text">{{ title }}</text> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <view v-if="showArrow" |
| | | :class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }" |
| | | class="uni-collapse-item__title-arrow"> |
| | | <uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="bottom" /> |
| | | </view> |
| | | </view> |
| | | <view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}" |
| | | :style="{height: (isOpen?height:0) +'px'}"> |
| | | <view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content" |
| | | :class="{open:isheight,'uni-collapse-item--border':border&&isOpen}"> |
| | | <slot></slot> |
| | | </view> |
| | | </view> |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | // #ifdef APP-NVUE |
| | | const dom = weex.requireModule('dom') |
| | | // #endif |
| | | /** |
| | | * CollapseItem 折叠面板子组件 |
| | | * @description 折叠面板子组件 |
| | | * @property {String} title 标题文字 |
| | | * @property {String} thumb 标题左侧缩略图 |
| | | * @property {String} name 唯一标志符 |
| | | * @property {Boolean} open = [true|false] 是否展开组件 |
| | | * @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线 |
| | | * @property {Boolean} border = [true|false] 是否显示分隔线 |
| | | * @property {Boolean} disabled = [true|false] 是否展开面板 |
| | | * @property {Boolean} showAnimation = [true|false] 开启动画 |
| | | * @property {Boolean} showArrow = [true|false] 是否显示右侧箭头 |
| | | */ |
| | | export default { |
| | | name: 'uniCollapseItem', |
| | | props: { |
| | | // 列表标题 |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | name: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | // 是否禁用 |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // #ifdef APP-PLUS |
| | | // 是否显示动画,app 端默认不开启动画,卡顿严重 |
| | | showAnimation: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // #endif |
| | | // #ifndef APP-PLUS |
| | | // 是否显示动画 |
| | | showAnimation: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // #endif |
| | | // 是否展开 |
| | | open: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 缩略图 |
| | | thumb: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 标题分隔线显示类型 |
| | | titleBorder: { |
| | | type: String, |
| | | default: 'auto' |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showArrow: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | // TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug |
| | | const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` |
| | | return { |
| | | isOpen: false, |
| | | isheight: null, |
| | | height: 0, |
| | | elId, |
| | | nameSync: 0 |
| | | } |
| | | }, |
| | | watch: { |
| | | open(val) { |
| | | this.isOpen = val |
| | | this.onClick(val, 'init') |
| | | } |
| | | }, |
| | | updated(e) { |
| | | this.$nextTick(() => { |
| | | this.init(true) |
| | | }) |
| | | }, |
| | | created() { |
| | | this.collapse = this.getCollapse() |
| | | this.oldHeight = 0 |
| | | this.onClick(this.open, 'init') |
| | | }, |
| | | // #ifndef VUE3 |
| | | // TODO vue2 |
| | | destroyed() { |
| | | if (this.__isUnmounted) return |
| | | this.uninstall() |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | // TODO vue3 |
| | | unmounted() { |
| | | this.__isUnmounted = true |
| | | this.uninstall() |
| | | }, |
| | | // #endif |
| | | mounted() { |
| | | if (!this.collapse) return |
| | | if (this.name !== '') { |
| | | this.nameSync = this.name |
| | | } else { |
| | | this.nameSync = this.collapse.childrens.length + '' |
| | | } |
| | | if (this.collapse.names.indexOf(this.nameSync) === -1) { |
| | | this.collapse.names.push(this.nameSync) |
| | | } else { |
| | | console.warn(`name 值 ${this.nameSync} 重复`); |
| | | } |
| | | if (this.collapse.childrens.indexOf(this) === -1) { |
| | | this.collapse.childrens.push(this) |
| | | } |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init(type) { |
| | | // #ifndef APP-NVUE |
| | | this.getCollapseHeight(type) |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | this.getNvueHwight(type) |
| | | // #endif |
| | | }, |
| | | uninstall() { |
| | | if (this.collapse) { |
| | | this.collapse.childrens.forEach((item, index) => { |
| | | if (item === this) { |
| | | this.collapse.childrens.splice(index, 1) |
| | | } |
| | | }) |
| | | this.collapse.names.forEach((item, index) => { |
| | | if (item === this.nameSync) { |
| | | this.collapse.names.splice(index, 1) |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | onClick(isOpen, type) { |
| | | if (this.disabled) return |
| | | this.isOpen = isOpen |
| | | if (this.isOpen && this.collapse) { |
| | | this.collapse.setAccordion(this) |
| | | } |
| | | if (type !== 'init') { |
| | | this.collapse.onChange(isOpen, this) |
| | | } |
| | | }, |
| | | getCollapseHeight(type, index = 0) { |
| | | const views = uni.createSelectorQuery().in(this) |
| | | views |
| | | .select(`#${this.elId}`) |
| | | .fields({ |
| | | size: true |
| | | }, data => { |
| | | // TODO 百度中可能获取不到节点信息 ,需要循环获取 |
| | | if (index >= 10) return |
| | | if (!data) { |
| | | index++ |
| | | this.getCollapseHeight(false, index) |
| | | return |
| | | } |
| | | // #ifdef APP-NVUE |
| | | this.height = data.height + 1 |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | this.height = data.height |
| | | // #endif |
| | | this.isheight = true |
| | | if (type) return |
| | | this.onClick(this.isOpen, 'init') |
| | | }) |
| | | .exec() |
| | | }, |
| | | getNvueHwight(type) { |
| | | const result = dom.getComponentRect(this.$refs['collapse--hook'], option => { |
| | | if (option && option.result && option.size) { |
| | | // #ifdef APP-NVUE |
| | | this.height = option.size.height + 1 |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | this.height = option.size.height |
| | | // #endif |
| | | this.isheight = true |
| | | if (type) return |
| | | this.onClick(this.open, 'init') |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getCollapse(name = 'uniCollapse') { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== name) { |
| | | parent = parent.$parent; |
| | | if (!parent) return false; |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-collapse-item { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | |
| | | /* #endif */ |
| | | &__title { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | transition: border-bottom-color .3s; |
| | | |
| | | // transition-property: border-bottom-color; |
| | | // transition-duration: 5s; |
| | | &-wrap { |
| | | width: 100%; |
| | | flex: 1; |
| | | |
| | | } |
| | | |
| | | &-box { |
| | | padding: 0 15px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | height: 48px; |
| | | line-height: 48px; |
| | | background-color: #fff; |
| | | color: #303133; |
| | | font-size: 13px; |
| | | font-weight: 500; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | outline: none; |
| | | |
| | | /* #endif */ |
| | | &.is-disabled { |
| | | .uni-collapse-item__title-text { |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | &.uni-collapse-item-border { |
| | | border-bottom: 1px solid #ebeef5; |
| | | } |
| | | |
| | | &.is-open { |
| | | border-bottom-color: transparent; |
| | | } |
| | | |
| | | &-img { |
| | | height: 22px; |
| | | width: 22px; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | &-text { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | /* #ifndef APP-NVUE */ |
| | | white-space: nowrap; |
| | | color: inherit; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 1; |
| | | /* #endif */ |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | |
| | | &-arrow { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-right: 10px; |
| | | transform: rotate(0deg); |
| | | |
| | | &-active { |
| | | transform: rotate(-180deg); |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | &__wrap { |
| | | /* #ifndef APP-NVUE */ |
| | | will-change: height; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | background-color: #fff; |
| | | overflow: hidden; |
| | | position: relative; |
| | | height: 0; |
| | | |
| | | &.is--transition { |
| | | // transition: all 0.3s; |
| | | transition-property: height, border-bottom-width; |
| | | transition-duration: 0.3s; |
| | | /* #ifndef APP-NVUE */ |
| | | will-change: height; |
| | | /* #endif */ |
| | | } |
| | | |
| | | |
| | | |
| | | &-content { |
| | | position: absolute; |
| | | font-size: 13px; |
| | | color: #303133; |
| | | // transition: height 0.3s; |
| | | border-bottom-color: transparent; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 0; |
| | | |
| | | &.uni-collapse-item--border { |
| | | border-bottom-width: 1px; |
| | | border-bottom-color: red; |
| | | border-bottom-color: #ebeef5; |
| | | } |
| | | |
| | | &.open { |
| | | position: relative; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &--animation { |
| | | transition-property: transform; |
| | | transition-duration: 0.3s; |
| | | transition-timing-function: ease; |
| | | } |
| | | |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-collapse"> |
| | | <slot /> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | /** |
| | | * Collapse 折叠面板 |
| | | * @description 展示可以折叠 / 展开的内容区域 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=23 |
| | | * @property {String|Array} value 当前激活面板改变时触发(如果是手风琴模式,参数类型为string,否则为array) |
| | | * @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果 |
| | | * @event {Function} change 切换面板时触发,如果是手风琴模式,返回类型为string,否则为array |
| | | */ |
| | | export default { |
| | | name: 'uniCollapse', |
| | | emits:['change','activeItem','input','update:modelValue'], |
| | | props: { |
| | | value: { |
| | | type: [String, Array], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Array], |
| | | default: '' |
| | | }, |
| | | accordion: { |
| | | // 是否开启手风琴效果 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | }, |
| | | data() { |
| | | return {} |
| | | }, |
| | | computed: { |
| | | // TODO 兼容 vue2 和 vue3 |
| | | dataValue() { |
| | | let value = (typeof this.value === 'string' && this.value === '') || |
| | | (Array.isArray(this.value) && this.value.length === 0) |
| | | let modelValue = (typeof this.modelValue === 'string' && this.modelValue === '') || |
| | | (Array.isArray(this.modelValue) && this.modelValue.length === 0) |
| | | if (value) { |
| | | return this.modelValue |
| | | } |
| | | if (modelValue) { |
| | | return this.value |
| | | } |
| | | |
| | | return this.value |
| | | } |
| | | }, |
| | | watch: { |
| | | dataValue(val) { |
| | | this.setOpen(val) |
| | | } |
| | | }, |
| | | created() { |
| | | this.childrens = [] |
| | | this.names = [] |
| | | }, |
| | | mounted() { |
| | | this.$nextTick(()=>{ |
| | | this.setOpen(this.dataValue) |
| | | }) |
| | | }, |
| | | methods: { |
| | | setOpen(val) { |
| | | let str = typeof val === 'string' |
| | | let arr = Array.isArray(val) |
| | | this.childrens.forEach((vm, index) => { |
| | | if (str) { |
| | | if (val === vm.nameSync) { |
| | | if (!this.accordion) { |
| | | console.warn('accordion 属性为 false ,v-model 类型应该为 array') |
| | | return |
| | | } |
| | | vm.isOpen = true |
| | | } |
| | | } |
| | | if (arr) { |
| | | val.forEach(v => { |
| | | if (v === vm.nameSync) { |
| | | if (this.accordion) { |
| | | console.warn('accordion 属性为 true ,v-model 类型应该为 string') |
| | | return |
| | | } |
| | | vm.isOpen = true |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | this.emit(val) |
| | | }, |
| | | setAccordion(self) { |
| | | if (!this.accordion) return |
| | | this.childrens.forEach((vm, index) => { |
| | | if (self !== vm) { |
| | | vm.isOpen = false |
| | | } |
| | | }) |
| | | }, |
| | | resize() { |
| | | this.childrens.forEach((vm, index) => { |
| | | // #ifndef APP-NVUE |
| | | vm.getCollapseHeight() |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | vm.getNvueHwight() |
| | | // #endif |
| | | }) |
| | | }, |
| | | onChange(isOpen, self) { |
| | | let activeItem = [] |
| | | |
| | | if (this.accordion) { |
| | | activeItem = isOpen ? self.nameSync : '' |
| | | } else { |
| | | this.childrens.forEach((vm, index) => { |
| | | if (vm.isOpen) { |
| | | activeItem.push(vm.nameSync) |
| | | } |
| | | }) |
| | | } |
| | | this.$emit('change', activeItem) |
| | | this.emit(activeItem) |
| | | }, |
| | | emit(val){ |
| | | this.$emit('input', val) |
| | | this.$emit('update:modelValue', val) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" > |
| | | .uni-collapse { |
| | | /* #ifndef APP-NVUE */ |
| | | width: 100%; |
| | | display: flex; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | flex: 1; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | background-color: #fff; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-collapse", |
| | | "displayName": "uni-collapse 折叠面板", |
| | | "version": "1.4.3", |
| | | "description": "Collapse 组件,可以折叠 / 展开的内容区域。", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "折叠", |
| | | "折叠面板", |
| | | "手风琴" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-scss", |
| | | "uni-icons" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ## Collapse 折叠面板 |
| | | > **组件名:uni-collapse** |
| | | > 代码块: `uCollapse` |
| | | > 关联组件:`uni-collapse-item`、`uni-icons`。 |
| | | |
| | | |
| | | 折叠面板用来折叠/显示过长的内容或者是列表。通常是在多内容分类项使用,折叠不重要的内容,显示重要内容。点击可以展开折叠部分。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-collapse) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.0.1(2021-11-23) |
| | | - 优化 label、label-width 属性 |
| | | ## 1.0.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-combox](https://uniapp.dcloud.io/component/uniui/uni-combox) |
| | | ## 0.1.0(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 0.0.6(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 0.0.5(2021-04-21) |
| | | - 优化 添加依赖 uni-icons, 导入后自动下载依赖 |
| | | ## 0.0.4(2021-02-05) |
| | | - 优化 组件引用关系,通过uni_modules引用组件 |
| | | ## 0.0.3(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
New file |
| | |
| | | <template> |
| | | <view class="uni-combox" :class="border ? '' : 'uni-combox__no-border'"> |
| | | <view v-if="label" class="uni-combox__label" :style="labelStyle"> |
| | | <text>{{label}}</text> |
| | | </view> |
| | | <view class="uni-combox__input-box"> |
| | | <input class="uni-combox__input" type="text" :placeholder="placeholder" |
| | | placeholder-class="uni-combox__input-plac" v-model="inputVal" @input="onInput" @focus="onFocus" @blur="onBlur" /> |
| | | <uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" @click="toggleSelector"> |
| | | </uni-icons> |
| | | </view> |
| | | <view class="uni-combox__selector" v-if="showSelector"> |
| | | <view class="uni-popper__arrow"></view> |
| | | <scroll-view scroll-y="true" class="uni-combox__selector-scroll" @scroll="onScroll"> |
| | | <view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0"> |
| | | <text>{{emptyTips}}</text> |
| | | </view> |
| | | <view class="uni-combox__selector-item" v-for="(item,index) in filterCandidates" :key="index" @click="onSelectorClick(index)"> |
| | | <text>{{item}}</text> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | <!-- 新增蒙层,点击蒙层时关闭选项显示 --> |
| | | <view class="uni-combox__mask" v-show="showSelector" @click="showSelector = false"></view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Combox 组合输入框 |
| | | * @description 组合输入框一般用于既可以输入也可以选择的场景 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=1261 |
| | | * @property {String} label 左侧文字 |
| | | * @property {String} labelWidth 左侧内容宽度 |
| | | * @property {String} placeholder 输入框占位符 |
| | | * @property {Array} candidates 候选项列表 |
| | | * @property {String} emptyTips 筛选结果为空时显示的文字 |
| | | * @property {String} value 组合框的值 |
| | | */ |
| | | export default { |
| | | name: 'uniCombox', |
| | | emits: ['input', 'update:modelValue'], |
| | | props: { |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | label: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | labelWidth: { |
| | | type: String, |
| | | default: 'auto' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | candidates: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | emptyTips: { |
| | | type: String, |
| | | default: '无匹配项' |
| | | }, |
| | | // #ifndef VUE3 |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // #endif |
| | | }, |
| | | data() { |
| | | return { |
| | | showSelector: false, |
| | | inputVal: '', |
| | | blurTimer:null, |
| | | } |
| | | }, |
| | | computed: { |
| | | labelStyle() { |
| | | if (this.labelWidth === 'auto') { |
| | | return "" |
| | | } |
| | | return `width: ${this.labelWidth}` |
| | | }, |
| | | filterCandidates() { |
| | | if (this.inputVal !== 0 && !this.inputVal) { |
| | | return this.candidates |
| | | } |
| | | return this.candidates.filter((item) => { |
| | | return item.toString().indexOf(this.inputVal) > -1 |
| | | }) |
| | | }, |
| | | filterCandidatesLength() { |
| | | return this.filterCandidates.length |
| | | } |
| | | }, |
| | | watch: { |
| | | // #ifndef VUE3 |
| | | value: { |
| | | handler(newVal) { |
| | | this.inputVal = newVal |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | handler(newVal) { |
| | | this.inputVal = newVal |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | }, |
| | | methods: { |
| | | toggleSelector() { |
| | | this.showSelector = !this.showSelector |
| | | }, |
| | | onFocus() { |
| | | this.showSelector = true |
| | | }, |
| | | onBlur() { |
| | | this.blurTimer = setTimeout(() => { |
| | | this.showSelector = false |
| | | }, 153) |
| | | }, |
| | | onScroll(){ // 滚动时将blur的定时器关掉 |
| | | if(this.blurTimer) { |
| | | clearTimeout(this.blurTimer) |
| | | this.blurTimer = null |
| | | } |
| | | }, |
| | | onSelectorClick(index) { |
| | | this.inputVal = this.filterCandidates[index] |
| | | this.showSelector = false |
| | | this.$emit('input', this.inputVal) |
| | | this.$emit('update:modelValue', this.inputVal) |
| | | }, |
| | | onInput() { |
| | | setTimeout(() => { |
| | | this.$emit('input', this.inputVal) |
| | | this.$emit('update:modelValue', this.inputVal) |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-combox { |
| | | font-size: 14px; |
| | | border: 1px solid #DCDFE6; |
| | | border-radius: 4px; |
| | | padding: 6px 10px; |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | // height: 40px; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | // border-bottom: solid 1px #DDDDDD; |
| | | } |
| | | |
| | | .uni-combox__label { |
| | | font-size: 16px; |
| | | line-height: 22px; |
| | | padding-right: 10px; |
| | | color: #999999; |
| | | } |
| | | |
| | | .uni-combox__input-box { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-combox__input { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | height: 22px; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .uni-combox__input-plac { |
| | | font-size: 14px; |
| | | color: #999; |
| | | } |
| | | |
| | | .uni-combox__selector { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | position: absolute; |
| | | top: calc(100% + 12px); |
| | | left: 0; |
| | | width: 100%; |
| | | background-color: #FFFFFF; |
| | | border: 1px solid #EBEEF5; |
| | | border-radius: 6px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | z-index: 3; |
| | | padding: 4px 0; |
| | | } |
| | | |
| | | .uni-combox__selector-scroll { |
| | | /* #ifndef APP-NVUE */ |
| | | max-height: 200px; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-combox__selector-empty, |
| | | .uni-combox__selector-item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | line-height: 36px; |
| | | font-size: 14px; |
| | | text-align: center; |
| | | // border-bottom: solid 1px #DDDDDD; |
| | | padding: 0px 10px; |
| | | } |
| | | |
| | | .uni-combox__selector-item:hover { |
| | | background-color: #f9f9f9; |
| | | } |
| | | |
| | | .uni-combox__selector-empty:last-child, |
| | | .uni-combox__selector-item:last-child { |
| | | /* #ifndef APP-NVUE */ |
| | | border-bottom: none; |
| | | /* #endif */ |
| | | } |
| | | |
| | | // picker 弹出层通用的指示小三角 |
| | | .uni-popper__arrow, |
| | | .uni-popper__arrow::after { |
| | | position: absolute; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border-color: transparent; |
| | | border-style: solid; |
| | | border-width: 6px; |
| | | } |
| | | |
| | | .uni-popper__arrow { |
| | | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
| | | top: -6px; |
| | | left: 10%; |
| | | margin-right: 3px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #EBEEF5; |
| | | } |
| | | |
| | | .uni-popper__arrow::after { |
| | | content: " "; |
| | | top: 1px; |
| | | margin-left: -6px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #fff; |
| | | } |
| | | |
| | | .uni-combox__no-border { |
| | | border: none; |
| | | } |
| | | |
| | | .uni-combox__mask { |
| | | width:100%; |
| | | height:100%; |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | z-index: 1; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-combox", |
| | | "displayName": "uni-combox 组合框", |
| | | "version": "1.0.1", |
| | | "description": "可以选择也可以输入的表单项 ", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "combox", |
| | | "组合框", |
| | | "select" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-scss", |
| | | "uni-icons" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "n" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ## Combox 组合框 |
| | | > **组件名:uni-combox** |
| | | > 代码块: `uCombox` |
| | | |
| | | |
| | | 组合框组件。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-combox) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.2.2(2022-01-19) |
| | | - 修复 在微信小程序中样式不生效的bug |
| | | ## 1.2.1(2022-01-18) |
| | | - 新增 update 方法 ,在动态更新时间后,刷新组件 |
| | | ## 1.2.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-countdown](https://uniapp.dcloud.io/component/uniui/uni-countdown) |
| | | ## 1.1.3(2021-10-18) |
| | | - 重构 |
| | | - 新增 font-size 支持自定义字体大小 |
| | | ## 1.1.2(2021-08-24) |
| | | - 新增 支持国际化 |
| | | ## 1.1.1(2021-07-30) |
| | | - 优化 vue3下小程序事件警告的问题 |
| | | ## 1.1.0(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.0.5(2021-06-18) |
| | | - 修复 uni-countdown 重复赋值跳两秒的 bug |
| | | ## 1.0.4(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.0.3(2021-05-08) |
| | | - 修复 uni-countdown 不能控制倒计时的 bug |
| | | ## 1.0.2(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
New file |
| | |
| | | { |
| | | "uni-countdown.day": "day", |
| | | "uni-countdown.h": "h", |
| | | "uni-countdown.m": "m", |
| | | "uni-countdown.s": "s" |
| | | } |
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 |
| | |
| | | { |
| | | "uni-countdown.day": "天", |
| | | "uni-countdown.h": "时", |
| | | "uni-countdown.m": "分", |
| | | "uni-countdown.s": "秒" |
| | | } |
New file |
| | |
| | | { |
| | | "uni-countdown.day": "天", |
| | | "uni-countdown.h": "時", |
| | | "uni-countdown.m": "分", |
| | | "uni-countdown.s": "秒" |
| | | } |
New file |
| | |
| | | <template> |
| | | <view class="uni-countdown"> |
| | | <text v-if="showDay" :style="[timeStyle]" class="uni-countdown__number">{{ d }}</text> |
| | | <text v-if="showDay" :style="[splitorStyle]" class="uni-countdown__splitor">{{dayText}}</text> |
| | | <text :style="[timeStyle]" class="uni-countdown__number">{{ h }}</text> |
| | | <text :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : hourText }}</text> |
| | | <text :style="[timeStyle]" class="uni-countdown__number">{{ i }}</text> |
| | | <text :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : minuteText }}</text> |
| | | <text :style="[timeStyle]" class="uni-countdown__number">{{ s }}</text> |
| | | <text v-if="!showColon" :style="[splitorStyle]" class="uni-countdown__splitor">{{secondText}}</text> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import { |
| | | initVueI18n |
| | | } from '@dcloudio/uni-i18n' |
| | | import messages from './i18n/index.js' |
| | | const { |
| | | t |
| | | } = initVueI18n(messages) |
| | | /** |
| | | * Countdown 倒计时 |
| | | * @description 倒计时组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=25 |
| | | * @property {String} backgroundColor 背景色 |
| | | * @property {String} color 文字颜色 |
| | | * @property {Number} day 天数 |
| | | * @property {Number} hour 小时 |
| | | * @property {Number} minute 分钟 |
| | | * @property {Number} second 秒 |
| | | * @property {Number} timestamp 时间戳 |
| | | * @property {Boolean} showDay = [true|false] 是否显示天数 |
| | | * @property {Boolean} show-colon = [true|false] 是否以冒号为分隔符 |
| | | * @property {String} splitorColor 分割符号颜色 |
| | | * @event {Function} timeup 倒计时时间到触发事件 |
| | | * @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown> |
| | | */ |
| | | export default { |
| | | name: 'UniCountdown', |
| | | emits: ['timeup'], |
| | | props: { |
| | | showDay: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showColon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | start: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | backgroundColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#333' |
| | | }, |
| | | fontSize: { |
| | | type: Number, |
| | | default: 14 |
| | | }, |
| | | splitorColor: { |
| | | type: String, |
| | | default: '#333' |
| | | }, |
| | | day: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | hour: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | minute: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | second: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | timestamp: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | zeroPad: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | timer: null, |
| | | syncFlag: false, |
| | | d: '00', |
| | | h: '00', |
| | | i: '00', |
| | | s: '00', |
| | | leftTime: 0, |
| | | seconds: 0 |
| | | } |
| | | }, |
| | | computed: { |
| | | dayText() { |
| | | return t("uni-countdown.day") |
| | | }, |
| | | hourText(val) { |
| | | return t("uni-countdown.h") |
| | | }, |
| | | minuteText(val) { |
| | | return t("uni-countdown.m") |
| | | }, |
| | | secondText(val) { |
| | | return t("uni-countdown.s") |
| | | }, |
| | | timeStyle() { |
| | | const { |
| | | color, |
| | | backgroundColor, |
| | | fontSize |
| | | } = this |
| | | return { |
| | | color, |
| | | backgroundColor, |
| | | fontSize: `${fontSize}px`, |
| | | width: `${fontSize * 22 / 14}px`, // 按字体大小为 14px 时的比例缩放 |
| | | lineHeight: `${fontSize * 20 / 14}px`, |
| | | borderRadius: `${fontSize * 3 / 14}px`, |
| | | } |
| | | }, |
| | | splitorStyle() { |
| | | const { splitorColor, fontSize, backgroundColor } = this |
| | | return { |
| | | color: splitorColor, |
| | | fontSize: `${fontSize * 12 / 14}px`, |
| | | margin: backgroundColor ? `${fontSize * 4 / 14}px` : '' |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | day(val) { |
| | | this.changeFlag() |
| | | }, |
| | | hour(val) { |
| | | this.changeFlag() |
| | | }, |
| | | minute(val) { |
| | | this.changeFlag() |
| | | }, |
| | | second(val) { |
| | | this.changeFlag() |
| | | }, |
| | | start: { |
| | | immediate: true, |
| | | handler(newVal, oldVal) { |
| | | if (newVal) { |
| | | this.startData(); |
| | | } else { |
| | | if (!oldVal) return |
| | | clearInterval(this.timer) |
| | | } |
| | | } |
| | | |
| | | } |
| | | }, |
| | | created: function(e) { |
| | | this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second) |
| | | this.countDown() |
| | | }, |
| | | // #ifndef VUE3 |
| | | destroyed() { |
| | | clearInterval(this.timer) |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | unmounted() { |
| | | clearInterval(this.timer) |
| | | }, |
| | | // #endif |
| | | methods: { |
| | | toSeconds(timestamp, day, hours, minutes, seconds) { |
| | | if (timestamp) { |
| | | return timestamp - parseInt(new Date().getTime() / 1000, 10) |
| | | } |
| | | return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds |
| | | }, |
| | | timeUp() { |
| | | clearInterval(this.timer) |
| | | this.$emit('timeup') |
| | | }, |
| | | countDown() { |
| | | let seconds = this.seconds |
| | | let [day, hour, minute, second] = [0, 0, 0, 0] |
| | | if (seconds > 0) { |
| | | day = Math.floor(seconds / (60 * 60 * 24)) |
| | | hour = Math.floor(seconds / (60 * 60)) - (day * 24) |
| | | minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60) |
| | | second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60) |
| | | } else { |
| | | this.timeUp() |
| | | } |
| | | day = (day < 10 && this.zeroPad) ? `0${day}` : day |
| | | hour = (hour < 10 && this.zeroPad) ? `0${hour}` : hour |
| | | minute = (minute < 10 && this.zeroPad) ? `0${minute}` : minute |
| | | second = (second < 10 && this.zeroPad) ? `0${second}` : second |
| | | this.d = day |
| | | this.h = hour |
| | | this.i = minute |
| | | this.s = second |
| | | }, |
| | | startData() { |
| | | this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second) |
| | | if (this.seconds <= 0) { |
| | | this.seconds = this.toSeconds(0, 0, 0, 0, 0) |
| | | this.countDown() |
| | | return |
| | | } |
| | | clearInterval(this.timer) |
| | | this.countDown() |
| | | this.timer = setInterval(() => { |
| | | this.seconds-- |
| | | if (this.seconds < 0) { |
| | | this.timeUp() |
| | | return |
| | | } |
| | | this.countDown() |
| | | }, 1000) |
| | | }, |
| | | update(){ |
| | | this.startData(); |
| | | }, |
| | | changeFlag() { |
| | | if (!this.syncFlag) { |
| | | this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second) |
| | | this.startData(); |
| | | this.syncFlag = true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | $font-size: 14px; |
| | | |
| | | .uni-countdown { |
| | | display: flex; |
| | | flex-direction: row; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | |
| | | &__splitor { |
| | | margin: 0 2px; |
| | | font-size: $font-size; |
| | | color: #333; |
| | | } |
| | | |
| | | &__number { |
| | | border-radius: 3px; |
| | | text-align: center; |
| | | font-size: $font-size; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-countdown", |
| | | "displayName": "uni-countdown 倒计时", |
| | | "version": "1.2.2", |
| | | "description": "CountDown 倒计时组件", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "countdown", |
| | | "倒计时" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ## CountDown 倒计时 |
| | | > **组件名:uni-countdown** |
| | | > 代码块: `uCountDown` |
| | | |
| | | 倒计时组件。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-countdown) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.0.3(2022-09-16) |
| | | - 可以使用 uni-scss 控制主题色 |
| | | ## 1.0.2(2022-06-30) |
| | | - 优化 在 uni-forms 中的依赖注入方式 |
| | | ## 1.0.1(2022-02-07) |
| | | - 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug |
| | | ## 1.0.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-checkbox](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox) |
| | | ## 0.2.5(2021-08-23) |
| | | - 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题 |
| | | ## 0.2.4(2021-08-17) |
| | | - 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题 |
| | | ## 0.2.3(2021-08-11) |
| | | - 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 |
| | | ## 0.2.2(2021-07-30) |
| | | - 优化 在uni-forms组件,与label不对齐的问题 |
| | | ## 0.2.1(2021-07-27) |
| | | - 修复 单选默认值为0不能选中的Bug |
| | | ## 0.2.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 0.1.11(2021-07-06) |
| | | - 优化 删除无用日志 |
| | | ## 0.1.10(2021-07-05) |
| | | - 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题 |
| | | ## 0.1.9(2021-07-05) |
| | | - 修复 nvue 黑框样式问题 |
| | | ## 0.1.8(2021-06-28) |
| | | - 修复 selectedTextColor 属性不生效的Bug |
| | | ## 0.1.7(2021-06-02) |
| | | - 新增 map 属性,可以方便映射text/value属性 |
| | | ## 0.1.6(2021-05-26) |
| | | - 修复 不关联服务空间的情况下组件报错的Bug |
| | | ## 0.1.5(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 0.1.4(2021-04-09) |
| | | - 修复 nvue 下无法选中的问题 |
| | | ## 0.1.3(2021-03-22) |
| | | - 新增 disabled属性 |
| | | ## 0.1.2(2021-02-24) |
| | | - 优化 默认颜色显示 |
| | | ## 0.1.1(2021-02-24) |
| | | - 新增 支持nvue |
| | | ## 0.1.0(2021-02-18) |
| | | - “暂无数据”显示居中 |
New file |
| | |
| | | <template> |
| | | <view class="uni-data-checklist" :style="{'margin-top':isTop+'px'}"> |
| | | <template v-if="!isLocal"> |
| | | <view class="uni-data-loading"> |
| | | <uni-load-more v-if="!mixinDatacomErrorMessage" status="loading" iconType="snow" :iconSize="18" :content-text="contentText"></uni-load-more> |
| | | <text v-else>{{mixinDatacomErrorMessage}}</text> |
| | | </view> |
| | | </template> |
| | | <template v-else> |
| | | <checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list' || wrap}" @change="chagne"> |
| | | <label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']" |
| | | :style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index"> |
| | | <checkbox class="hidden" hidden :disabled="disabled || !!item.disabled" :value="item[map.value]+''" :checked="item.selected" /> |
| | | <view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="checkbox__inner" :style="item.styleIcon"> |
| | | <view class="checkbox__inner-icon"></view> |
| | | </view> |
| | | <view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}"> |
| | | <text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text> |
| | | <view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :style="item.styleBackgroud"></view> |
| | | </view> |
| | | </label> |
| | | </checkbox-group> |
| | | <radio-group v-else class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="chagne"> |
| | | <!-- --> |
| | | <label class="checklist-box" :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']" |
| | | :style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index"> |
| | | <radio class="hidden" hidden :disabled="disabled || item.disabled" :value="item[map.value]+''" :checked="item.selected" /> |
| | | <view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner" |
| | | :style="item.styleBackgroud"> |
| | | <view class="radio__inner-icon" :style="item.styleIcon"></view> |
| | | </view> |
| | | <view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}"> |
| | | <text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text> |
| | | <view v-if="mode === 'list' && icon === 'right'" :style="item.styleRightIcon" class="checkobx__list"></view> |
| | | </view> |
| | | </label> |
| | | </radio-group> |
| | | </template> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * DataChecklist 数据选择器 |
| | | * @description 通过数据渲染 checkbox 和 radio |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx |
| | | * @property {String} mode = [default| list | button | tag] 显示模式 |
| | | * @value default 默认横排模式 |
| | | * @value list 列表模式 |
| | | * @value button 按钮模式 |
| | | * @value tag 标签模式 |
| | | * @property {Boolean} multiple = [true|false] 是否多选 |
| | | * @property {Array|String|Number} value 默认值 |
| | | * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}] |
| | | * @property {Number|String} min 最小选择个数 ,multiple为true时生效 |
| | | * @property {Number|String} max 最大选择个数 ,multiple为true时生效 |
| | | * @property {Boolean} wrap 是否换行显示 |
| | | * @property {String} icon = [left|right] list 列表模式下icon显示位置 |
| | | * @property {Boolean} selectedColor 选中颜色 |
| | | * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效 |
| | | * @property {Boolean} selectedTextColor 选中文本颜色,如不填写则自动显示 |
| | | * @property {Object} map 字段映射, 默认 map={text:'text',value:'value'} |
| | | * @value left 左侧显示 |
| | | * @value right 右侧显示 |
| | | * @event {Function} change 选中发生变化触发 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'uniDataChecklist', |
| | | mixins: [uniCloud.mixinDatacom || {}], |
| | | emits:['input','update:modelValue','change'], |
| | | props: { |
| | | mode: { |
| | | type: String, |
| | | default: 'default' |
| | | }, |
| | | |
| | | multiple: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | value: { |
| | | type: [Array, String, Number], |
| | | default () { |
| | | return '' |
| | | } |
| | | }, |
| | | // TODO vue3 |
| | | modelValue: { |
| | | type: [Array, String, Number], |
| | | default() { |
| | | return ''; |
| | | } |
| | | }, |
| | | localdata: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | min: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | max: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | wrap: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | icon: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | selectedColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | selectedTextColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | emptyText:{ |
| | | type: String, |
| | | default: '暂无数据' |
| | | }, |
| | | disabled:{ |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | map:{ |
| | | type: Object, |
| | | default(){ |
| | | return { |
| | | text:'text', |
| | | value:'value' |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | localdata: { |
| | | handler(newVal) { |
| | | this.range = newVal |
| | | this.dataList = this.getDataList(this.getSelectedValue(newVal)) |
| | | }, |
| | | deep: true |
| | | }, |
| | | mixinDatacomResData(newVal) { |
| | | this.range = newVal |
| | | this.dataList = this.getDataList(this.getSelectedValue(newVal)) |
| | | }, |
| | | value(newVal) { |
| | | this.dataList = this.getDataList(newVal) |
| | | // fix by mehaotian is_reset 在 uni-forms 中定义 |
| | | // if(!this.is_reset){ |
| | | // this.is_reset = false |
| | | // this.formItem && this.formItem.setValue(newVal) |
| | | // } |
| | | }, |
| | | modelValue(newVal) { |
| | | this.dataList = this.getDataList(newVal); |
| | | // if(!this.is_reset){ |
| | | // this.is_reset = false |
| | | // this.formItem && this.formItem.setValue(newVal) |
| | | // } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | dataList: [], |
| | | range: [], |
| | | contentText: { |
| | | contentdown: '查看更多', |
| | | contentrefresh: '加载中', |
| | | contentnomore: '没有更多' |
| | | }, |
| | | isLocal:true, |
| | | styles: { |
| | | selectedColor: '#2979ff', |
| | | selectedTextColor: '#666', |
| | | }, |
| | | isTop:0 |
| | | }; |
| | | }, |
| | | computed:{ |
| | | dataValue(){ |
| | | if(this.value === '')return this.modelValue |
| | | if(this.modelValue === '') return this.value |
| | | return this.value |
| | | } |
| | | }, |
| | | created() { |
| | | // this.form = this.getForm('uniForms') |
| | | // this.formItem = this.getForm('uniFormsItem') |
| | | // this.formItem && this.formItem.setValue(this.value) |
| | | |
| | | // if (this.formItem) { |
| | | // this.isTop = 6 |
| | | // if (this.formItem.name) { |
| | | // // 如果存在name添加默认值,否则formData 中不存在这个字段不校验 |
| | | // if(!this.is_reset){ |
| | | // this.is_reset = false |
| | | // this.formItem.setValue(this.dataValue) |
| | | // } |
| | | // this.rename = this.formItem.name |
| | | // this.form.inputChildrens.push(this) |
| | | // } |
| | | // } |
| | | |
| | | if (this.localdata && this.localdata.length !== 0) { |
| | | this.isLocal = true |
| | | this.range = this.localdata |
| | | this.dataList = this.getDataList(this.getSelectedValue(this.range)) |
| | | } else { |
| | | if (this.collection) { |
| | | this.isLocal = false |
| | | this.loadData() |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | loadData() { |
| | | this.mixinDatacomGet().then(res=>{ |
| | | this.mixinDatacomResData = res.result.data |
| | | if(this.mixinDatacomResData.length === 0){ |
| | | this.isLocal = false |
| | | this.mixinDatacomErrorMessage = this.emptyText |
| | | }else{ |
| | | this.isLocal = true |
| | | } |
| | | }).catch(err=>{ |
| | | this.mixinDatacomErrorMessage = err.message |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getForm(name = 'uniForms') { |
| | | 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; |
| | | }, |
| | | chagne(e) { |
| | | const values = e.detail.value |
| | | |
| | | let detail = { |
| | | value: [], |
| | | data: [] |
| | | } |
| | | |
| | | if (this.multiple) { |
| | | this.range.forEach(item => { |
| | | |
| | | if (values.includes(item[this.map.value] + '')) { |
| | | detail.value.push(item[this.map.value]) |
| | | detail.data.push(item) |
| | | } |
| | | }) |
| | | } else { |
| | | const range = this.range.find(item => (item[this.map.value] + '') === values) |
| | | if (range) { |
| | | detail = { |
| | | value: range[this.map.value], |
| | | data: range |
| | | } |
| | | } |
| | | } |
| | | // this.formItem && this.formItem.setValue(detail.value) |
| | | // TODO 兼容 vue2 |
| | | this.$emit('input', detail.value); |
| | | // // TOTO 兼容 vue3 |
| | | this.$emit('update:modelValue', detail.value); |
| | | this.$emit('change', { |
| | | detail |
| | | }) |
| | | if (this.multiple) { |
| | | // 如果 v-model 没有绑定 ,则走内部逻辑 |
| | | // if (this.value.length === 0) { |
| | | this.dataList = this.getDataList(detail.value, true) |
| | | // } |
| | | } else { |
| | | this.dataList = this.getDataList(detail.value) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 获取渲染的新数组 |
| | | * @param {Object} value 选中内容 |
| | | */ |
| | | getDataList(value) { |
| | | // 解除引用关系,破坏原引用关系,避免污染源数据 |
| | | let dataList = JSON.parse(JSON.stringify(this.range)) |
| | | let list = [] |
| | | if (this.multiple) { |
| | | if (!Array.isArray(value)) { |
| | | value = [] |
| | | } |
| | | } |
| | | dataList.forEach((item, index) => { |
| | | item.disabled = item.disable || item.disabled || false |
| | | if (this.multiple) { |
| | | if (value.length > 0) { |
| | | let have = value.find(val => val === item[this.map.value]) |
| | | item.selected = have !== undefined |
| | | } else { |
| | | item.selected = false |
| | | } |
| | | } else { |
| | | item.selected = value === item[this.map.value] |
| | | } |
| | | |
| | | list.push(item) |
| | | }) |
| | | return this.setRange(list) |
| | | }, |
| | | /** |
| | | * 处理最大最小值 |
| | | * @param {Object} list |
| | | */ |
| | | setRange(list) { |
| | | let selectList = list.filter(item => item.selected) |
| | | let min = Number(this.min) || 0 |
| | | let max = Number(this.max) || '' |
| | | list.forEach((item, index) => { |
| | | if (this.multiple) { |
| | | if (selectList.length <= min) { |
| | | let have = selectList.find(val => val[this.map.value] === item[this.map.value]) |
| | | if (have !== undefined) { |
| | | item.disabled = true |
| | | } |
| | | } |
| | | |
| | | if (selectList.length >= max && max !== '') { |
| | | let have = selectList.find(val => val[this.map.value] === item[this.map.value]) |
| | | if (have === undefined) { |
| | | item.disabled = true |
| | | } |
| | | } |
| | | } |
| | | this.setStyles(item, index) |
| | | list[index] = item |
| | | }) |
| | | return list |
| | | }, |
| | | /** |
| | | * 设置 class |
| | | * @param {Object} item |
| | | * @param {Object} index |
| | | */ |
| | | setStyles(item, index) { |
| | | // 设置自定义样式 |
| | | item.styleBackgroud = this.setStyleBackgroud(item) |
| | | item.styleIcon = this.setStyleIcon(item) |
| | | item.styleIconText = this.setStyleIconText(item) |
| | | item.styleRightIcon = this.setStyleRightIcon(item) |
| | | }, |
| | | |
| | | /** |
| | | * 获取选中值 |
| | | * @param {Object} range |
| | | */ |
| | | getSelectedValue(range) { |
| | | if (!this.multiple) return this.dataValue |
| | | let selectedArr = [] |
| | | range.forEach((item) => { |
| | | if (item.selected) { |
| | | selectedArr.push(item[this.map.value]) |
| | | } |
| | | }) |
| | | return this.dataValue.length > 0 ? this.dataValue : selectedArr |
| | | }, |
| | | |
| | | /** |
| | | * 设置背景样式 |
| | | */ |
| | | setStyleBackgroud(item) { |
| | | let styles = {} |
| | | let selectedColor = this.selectedColor?this.selectedColor:'#2979ff' |
| | | if (this.selectedColor) { |
| | | if (this.mode !== 'list') { |
| | | styles['border-color'] = item.selected?selectedColor:'#DCDFE6' |
| | | } |
| | | if (this.mode === 'tag') { |
| | | styles['background-color'] = item.selected? selectedColor:'#f5f5f5' |
| | | } |
| | | } |
| | | let classles = '' |
| | | for (let i in styles) { |
| | | classles += `${i}:${styles[i]};` |
| | | } |
| | | return classles |
| | | }, |
| | | setStyleIcon(item) { |
| | | let styles = {} |
| | | let classles = '' |
| | | if (this.selectedColor) { |
| | | let selectedColor = this.selectedColor?this.selectedColor:'#2979ff' |
| | | styles['background-color'] = item.selected?selectedColor:'#fff' |
| | | styles['border-color'] = item.selected?selectedColor:'#DCDFE6' |
| | | |
| | | if(!item.selected && item.disabled){ |
| | | styles['background-color'] = '#F2F6FC' |
| | | styles['border-color'] = item.selected?selectedColor:'#DCDFE6' |
| | | } |
| | | } |
| | | for (let i in styles) { |
| | | classles += `${i}:${styles[i]};` |
| | | } |
| | | return classles |
| | | }, |
| | | setStyleIconText(item) { |
| | | let styles = {} |
| | | let classles = '' |
| | | if (this.selectedColor) { |
| | | let selectedColor = this.selectedColor?this.selectedColor:'#2979ff' |
| | | if (this.mode === 'tag') { |
| | | styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:'#fff'):'#666' |
| | | } else { |
| | | styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:selectedColor):'#666' |
| | | } |
| | | if(!item.selected && item.disabled){ |
| | | styles.color = '#999' |
| | | } |
| | | } |
| | | for (let i in styles) { |
| | | classles += `${i}:${styles[i]};` |
| | | } |
| | | return classles |
| | | }, |
| | | setStyleRightIcon(item) { |
| | | let styles = {} |
| | | let classles = '' |
| | | if (this.mode === 'list') { |
| | | styles['border-color'] = item.selected?this.styles.selectedColor:'#DCDFE6' |
| | | } |
| | | for (let i in styles) { |
| | | classles += `${i}:${styles[i]};` |
| | | } |
| | | |
| | | return classles |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-primary: #2979ff !default; |
| | | $border-color: #DCDFE6; |
| | | $disable:0.4; |
| | | |
| | | @mixin flex { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-data-loading { |
| | | @include flex; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 36px; |
| | | padding-left: 10px; |
| | | color: #999; |
| | | } |
| | | |
| | | .uni-data-checklist { |
| | | position: relative; |
| | | z-index: 0; |
| | | flex: 1; |
| | | // 多选样式 |
| | | .checklist-group { |
| | | @include flex; |
| | | flex-direction: row; |
| | | flex-wrap: wrap; |
| | | |
| | | &.is-list { |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .checklist-box { |
| | | @include flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | position: relative; |
| | | margin: 5px 0; |
| | | margin-right: 25px; |
| | | |
| | | .hidden { |
| | | position: absolute; |
| | | opacity: 0; |
| | | } |
| | | |
| | | // 文字样式 |
| | | .checklist-content { |
| | | @include flex; |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | .checklist-text { |
| | | font-size: 14px; |
| | | color: #666; |
| | | margin-left: 5px; |
| | | line-height: 14px; |
| | | } |
| | | |
| | | .checkobx__list { |
| | | border-right-width: 1px; |
| | | border-right-color: #007aff; |
| | | border-right-style: solid; |
| | | border-bottom-width:1px; |
| | | border-bottom-color: #007aff; |
| | | border-bottom-style: solid; |
| | | height: 12px; |
| | | width: 6px; |
| | | left: -5px; |
| | | transform-origin: center; |
| | | transform: rotate(45deg); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | // 多选样式 |
| | | .checkbox__inner { |
| | | /* #ifndef APP-NVUE */ |
| | | flex-shrink: 0; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | position: relative; |
| | | width: 16px; |
| | | height: 16px; |
| | | border: 1px solid $border-color; |
| | | border-radius: 4px; |
| | | background-color: #fff; |
| | | z-index: 1; |
| | | .checkbox__inner-icon { |
| | | position: absolute; |
| | | /* #ifdef APP-NVUE */ |
| | | top: 2px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | top: 1px; |
| | | /* #endif */ |
| | | left: 5px; |
| | | height: 8px; |
| | | width: 4px; |
| | | border-right-width: 1px; |
| | | border-right-color: #fff; |
| | | border-right-style: solid; |
| | | border-bottom-width:1px ; |
| | | border-bottom-color: #fff; |
| | | border-bottom-style: solid; |
| | | opacity: 0; |
| | | transform-origin: center; |
| | | transform: rotate(40deg); |
| | | } |
| | | } |
| | | |
| | | // 单选样式 |
| | | .radio__inner { |
| | | @include flex; |
| | | /* #ifndef APP-NVUE */ |
| | | flex-shrink: 0; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: relative; |
| | | width: 16px; |
| | | height: 16px; |
| | | border: 1px solid $border-color; |
| | | border-radius: 16px; |
| | | background-color: #fff; |
| | | z-index: 1; |
| | | |
| | | .radio__inner-icon { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 10px; |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | // 默认样式 |
| | | &.is--default { |
| | | |
| | | // 禁用 |
| | | &.is-disable { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | .checkbox__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .radio__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | } |
| | | .checklist-text { |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | // 选中 |
| | | &.is-checked { |
| | | .checkbox__inner { |
| | | border-color: $uni-primary; |
| | | background-color: $uni-primary; |
| | | |
| | | .checkbox__inner-icon { |
| | | opacity: 1; |
| | | transform: rotate(45deg); |
| | | } |
| | | } |
| | | .radio__inner { |
| | | border-color: $uni-primary; |
| | | .radio__inner-icon { |
| | | opacity: 1; |
| | | background-color: $uni-primary; |
| | | } |
| | | } |
| | | .checklist-text { |
| | | color: $uni-primary; |
| | | } |
| | | // 选中禁用 |
| | | &.is-disable { |
| | | .checkbox__inner { |
| | | opacity: $disable; |
| | | } |
| | | |
| | | .checklist-text { |
| | | opacity: $disable; |
| | | } |
| | | .radio__inner { |
| | | opacity: $disable; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 按钮样式 |
| | | &.is--button { |
| | | margin-right: 10px; |
| | | padding: 5px 10px; |
| | | border: 1px $border-color solid; |
| | | border-radius: 3px; |
| | | transition: border-color 0.2s; |
| | | |
| | | // 禁用 |
| | | &.is-disable { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | border: 1px #eee solid; |
| | | opacity: $disable; |
| | | .checkbox__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | .radio__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | .checklist-text { |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | &.is-checked { |
| | | border-color: $uni-primary; |
| | | .checkbox__inner { |
| | | border-color: $uni-primary; |
| | | background-color: $uni-primary; |
| | | .checkbox__inner-icon { |
| | | opacity: 1; |
| | | transform: rotate(45deg); |
| | | } |
| | | } |
| | | |
| | | .radio__inner { |
| | | border-color: $uni-primary; |
| | | |
| | | .radio__inner-icon { |
| | | opacity: 1; |
| | | background-color: $uni-primary; |
| | | } |
| | | } |
| | | |
| | | .checklist-text { |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | // 选中禁用 |
| | | &.is-disable { |
| | | opacity: $disable; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 标签样式 |
| | | &.is--tag { |
| | | margin-right: 10px; |
| | | padding: 5px 10px; |
| | | border: 1px $border-color solid; |
| | | border-radius: 3px; |
| | | background-color: #f5f5f5; |
| | | |
| | | .checklist-text { |
| | | margin: 0; |
| | | color: #666; |
| | | } |
| | | |
| | | // 禁用 |
| | | &.is-disable { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | opacity: $disable; |
| | | } |
| | | |
| | | &.is-checked { |
| | | background-color: $uni-primary; |
| | | border-color: $uni-primary; |
| | | |
| | | .checklist-text { |
| | | color: #fff; |
| | | } |
| | | } |
| | | } |
| | | // 列表样式 |
| | | &.is--list { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | padding: 10px 15px; |
| | | padding-left: 0; |
| | | margin: 0; |
| | | |
| | | &.is-list-border { |
| | | border-top: 1px #eee solid; |
| | | } |
| | | |
| | | // 禁用 |
| | | &.is-disable { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | .checkbox__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | .checklist-text { |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | &.is-checked { |
| | | .checkbox__inner { |
| | | border-color: $uni-primary; |
| | | background-color: $uni-primary; |
| | | |
| | | .checkbox__inner-icon { |
| | | opacity: 1; |
| | | transform: rotate(45deg); |
| | | } |
| | | } |
| | | .radio__inner { |
| | | .radio__inner-icon { |
| | | opacity: 1; |
| | | } |
| | | } |
| | | .checklist-text { |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .checklist-content { |
| | | .checkobx__list { |
| | | opacity: 1; |
| | | border-color: $uni-primary; |
| | | } |
| | | } |
| | | |
| | | // 选中禁用 |
| | | &.is-disable { |
| | | .checkbox__inner { |
| | | opacity: $disable; |
| | | } |
| | | |
| | | .checklist-text { |
| | | opacity: $disable; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-data-checkbox", |
| | | "displayName": "uni-data-checkbox 数据选择器", |
| | | "version": "1.0.3", |
| | | "description": "通过数据驱动的单选框和复选框", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "checkbox", |
| | | "单选", |
| | | "多选", |
| | | "单选多选" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.1" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-load-more","uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ## DataCheckbox 数据驱动的单选复选框 |
| | | > **组件名:uni-data-checkbox** |
| | | > 代码块: `uDataCheckbox` |
| | | |
| | | |
| | | 本组件是基于uni-app基础组件checkbox的封装。本组件要解决问题包括: |
| | | |
| | | 1. 数据绑定型组件:给本组件绑定一个data,会自动渲染一组候选内容。再以往,开发者需要编写不少代码实现类似功能 |
| | | 2. 自动的表单校验:组件绑定了data,且符合[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)组件的表单校验规范,搭配使用会自动实现表单校验 |
| | | 3. 本组件合并了单选多选 |
| | | 4. 本组件有若干风格选择,如普通的单选多选框、并列button风格、tag风格。开发者可以快速选择需要的风格。但作为一个封装组件,样式代码虽然不用自己写了,却会牺牲一定的样式自定义性 |
| | | |
| | | 在uniCloud开发中,`DB Schema`中配置了enum枚举等类型后,在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.1.2(2023-04-11) |
| | | - 修复 更改 modelValue 报错的 bug |
| | | - 修复 v-for 未使用 key 值控制台 warning |
| | | ## 1.1.1(2023-02-21) |
| | | - 修复代码合并时引发 value 属性为空时不渲染数据的问题 |
| | | ## 1.1.0(2023-02-15) |
| | | - 修复 localdata 不支持动态更新的bug |
| | | ## 1.0.9(2023-02-15) |
| | | - 修复 localdata 不支持动态更新的bug |
| | | ## 1.0.8(2022-09-16) |
| | | - 可以使用 uni-scss 控制主题色 |
| | | ## 1.0.7(2022-07-06) |
| | | - 优化 pc端图标位置不正确的问题 |
| | | ## 1.0.6(2022-07-05) |
| | | - 优化 显示样式 |
| | | ## 1.0.5(2022-07-04) |
| | | - 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug |
| | | ## 1.0.4(2022-04-19) |
| | | - 修复 字节小程序 本地数据无法选择下一级的Bug |
| | | ## 1.0.3(2022-02-25) |
| | | - 修复 nvue 不支持的 v-show 的 bug |
| | | ## 1.0.2(2022-02-25) |
| | | - 修复 条件编译 nvue 不支持的 css 样式 |
| | | ## 1.0.1(2021-11-23) |
| | | - 修复 由上个版本引发的map、v-model等属性不生效的bug |
| | | ## 1.0.0(2021-11-19) |
| | | - 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker) |
| | | ## 0.4.9(2021-10-28) |
| | | - 修复 VUE2 v-model 概率无效的 bug |
| | | ## 0.4.8(2021-10-27) |
| | | - 修复 v-model 概率无效的 bug |
| | | ## 0.4.7(2021-10-25) |
| | | - 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+ |
| | | - 修复 树型 uniCloud 数据类型为 int 时报错的 bug |
| | | ## 0.4.6(2021-10-19) |
| | | - 修复 非 VUE3 v-model 为 0 时无法选中的 bug |
| | | ## 0.4.5(2021-09-26) |
| | | - 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效 |
| | | - 修复 readonly 为 true 时报错的 bug |
| | | ## 0.4.4(2021-09-26) |
| | | - 修复 上一版本造成的 map 属性失效的 bug |
| | | - 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略 |
| | | ## 0.4.3(2021-09-24) |
| | | - 修复 某些情况下级联未触发的 bug |
| | | ## 0.4.2(2021-09-23) |
| | | - 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用 |
| | | - 新增 选项内容过长自动添加省略号 |
| | | ## 0.4.1(2021-09-15) |
| | | - 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段 |
| | | ## 0.4.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 0.3.5(2021-06-04) |
| | | - 修复 无法加载云端数据的问题 |
| | | ## 0.3.4(2021-05-28) |
| | | - 修复 v-model 无效问题 |
| | | - 修复 loaddata 为空数据组时加载时间过长问题 |
| | | - 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点 |
| | | ## 0.3.3(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 0.3.2(2021-04-22) |
| | | - 修复 非树形数据有 where 属性查询报错的问题 |
| | | ## 0.3.1(2021-04-15) |
| | | - 修复 本地数据概率无法回显时问题 |
| | | ## 0.3.0(2021-04-07) |
| | | - 新增 支持云端非树形表结构数据 |
| | | - 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题 |
| | | ## 0.2.0(2021-03-15) |
| | | - 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题 |
| | | ## 0.1.9(2021-03-09) |
| | | - 修复 微信小程序某些情况下无法选择的问题 |
| | | ## 0.1.8(2021-02-05) |
| | | - 优化 部分样式在 nvue 上的兼容表现 |
| | | ## 0.1.7(2021-02-05) |
| | | - 调整为 uni_modules 目录规范 |
New file |
| | |
| | | // #ifdef H5 |
| | | export default { |
| | | name: 'Keypress', |
| | | props: { |
| | | disable: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | mounted () { |
| | | const keyNames = { |
| | | esc: ['Esc', 'Escape'], |
| | | tab: 'Tab', |
| | | enter: 'Enter', |
| | | space: [' ', 'Spacebar'], |
| | | up: ['Up', 'ArrowUp'], |
| | | left: ['Left', 'ArrowLeft'], |
| | | right: ['Right', 'ArrowRight'], |
| | | down: ['Down', 'ArrowDown'], |
| | | delete: ['Backspace', 'Delete', 'Del'] |
| | | } |
| | | const listener = ($event) => { |
| | | if (this.disable) { |
| | | return |
| | | } |
| | | const keyName = Object.keys(keyNames).find(key => { |
| | | const keyName = $event.key |
| | | const value = keyNames[key] |
| | | return value === keyName || (Array.isArray(value) && value.includes(keyName)) |
| | | }) |
| | | if (keyName) { |
| | | // 避免和其他按键事件冲突 |
| | | setTimeout(() => { |
| | | this.$emit(keyName, {}) |
| | | }, 0) |
| | | } |
| | | } |
| | | document.addEventListener('keyup', listener) |
| | | this.$once('hook:beforeDestroy', () => { |
| | | document.removeEventListener('keyup', listener) |
| | | }) |
| | | }, |
| | | render: () => {} |
| | | } |
| | | // #endif |
New file |
| | |
| | | <template> |
| | | <view class="uni-data-tree"> |
| | | <view class="uni-data-tree-input" @click="handleInput"> |
| | | <slot :options="options" :data="inputSelected" :error="errorMessage"> |
| | | <view class="input-value" :class="{'input-value-border': border}"> |
| | | <text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text> |
| | | <view v-else-if="loading && !isOpened" class="selected-area"> |
| | | <uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more> |
| | | </view> |
| | | <scroll-view v-else-if="inputSelected.length" class="selected-area" scroll-x="true"> |
| | | <view class="selected-list"> |
| | | <view class="selected-item" v-for="(item,index) in inputSelected" :key="index"> |
| | | <text class="text-color">{{item.text}}</text><text v-if="index<inputSelected.length-1" |
| | | class="input-split-line">{{split}}</text> |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | <text v-else class="selected-area placeholder">{{placeholder}}</text> |
| | | <view v-if="clearIcon && !readonly && inputSelected.length" class="icon-clear" @click.stop="clear"> |
| | | <uni-icons type="clear" color="#c0c4cc" size="24"></uni-icons> |
| | | </view> |
| | | <view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly "> |
| | | <view class="input-arrow"></view> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view> |
| | | <view class="uni-data-tree-dialog" v-if="isOpened"> |
| | | <view class="uni-popper__arrow"></view> |
| | | <view class="dialog-caption"> |
| | | <view class="title-area"> |
| | | <text class="dialog-title">{{popupTitle}}</text> |
| | | </view> |
| | | <view class="dialog-close" @click="handleClose"> |
| | | <view class="dialog-close-plus" data-id="close"></view> |
| | | <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> |
| | | </view> |
| | | </view> |
| | | <data-picker-view class="picker-view" ref="pickerView" v-model="dataValue" :localdata="localdata" |
| | | :preload="preload" :collection="collection" :field="field" :orderby="orderby" :where="where" |
| | | :step-searh="stepSearh" :self-field="selfField" :parent-field="parentField" :managed-mode="true" :map="map" |
| | | :ellipsis="ellipsis" @change="onchange" @datachange="ondatachange" @nodeclick="onnodeclick"> |
| | | </data-picker-view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import dataPicker from "../uni-data-pickerview/uni-data-picker.js" |
| | | import DataPickerView from "../uni-data-pickerview/uni-data-pickerview.vue" |
| | | |
| | | /** |
| | | * DataPicker 级联选择 |
| | | * @description 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 |
| | | * @property {String} popup-title 弹出窗口标题 |
| | | * @property {Array} localdata 本地数据,参考 |
| | | * @property {Boolean} border = [true|false] 是否有边框 |
| | | * @property {Boolean} readonly = [true|false] 是否仅读 |
| | | * @property {Boolean} preload = [true|false] 是否预加载数据 |
| | | * @value true 开启预加载数据,点击弹出窗口后显示已加载数据 |
| | | * @value false 关闭预加载数据,点击弹出窗口后开始加载数据 |
| | | * @property {Boolean} step-searh = [true|false] 是否分布查询 |
| | | * @value true 启用分布查询,仅查询当前选中节点 |
| | | * @value false 关闭分布查询,一次查询出所有数据 |
| | | * @property {String|DBFieldString} self-field 分布查询当前字段名称 |
| | | * @property {String|DBFieldString} parent-field 分布查询父字段名称 |
| | | * @property {String|DBCollectionString} collection 表名 |
| | | * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 |
| | | * @property {String} orderby 排序字段及正序倒叙设置 |
| | | * @property {String|JQLString} where 查询条件 |
| | | * @event {Function} popupshow 弹出的选择窗口打开时触发此事件 |
| | | * @event {Function} popuphide 弹出的选择窗口关闭时触发此事件 |
| | | */ |
| | | export default { |
| | | name: 'UniDataPicker', |
| | | emits: ['popupopened', 'popupclosed', 'nodeclick', 'input', 'change', 'update:modelValue','inputclick'], |
| | | mixins: [dataPicker], |
| | | components: { |
| | | DataPickerView |
| | | }, |
| | | props: { |
| | | options: { |
| | | type: [Object, Array], |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | popupTitle: { |
| | | type: String, |
| | | default: '请选择' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '请选择' |
| | | }, |
| | | heightMobile: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | readonly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | clearIcon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | split: { |
| | | type: String, |
| | | default: '/' |
| | | }, |
| | | ellipsis: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | isOpened: false, |
| | | inputSelected: [] |
| | | } |
| | | }, |
| | | created() { |
| | | this.$nextTick(() => { |
| | | this.load(); |
| | | }) |
| | | }, |
| | | watch: { |
| | | localdata: { |
| | | handler() { |
| | | this.load() |
| | | }, |
| | | deep: true |
| | | }, |
| | | }, |
| | | methods: { |
| | | clear() { |
| | | this._dispatchEvent([]); |
| | | }, |
| | | onPropsChange() { |
| | | this._treeData = []; |
| | | this.selectedIndex = 0; |
| | | |
| | | this.load(); |
| | | }, |
| | | load() { |
| | | if (this.readonly) { |
| | | this._processReadonly(this.localdata, this.dataValue); |
| | | return; |
| | | } |
| | | |
| | | // 回显本地数据 |
| | | if (this.isLocalData) { |
| | | this.loadData(); |
| | | this.inputSelected = this.selected.slice(0); |
| | | } else if (this.isCloudDataList || this.isCloudDataTree) { // 回显 Cloud 数据 |
| | | this.loading = true; |
| | | this.getCloudDataValue().then((res) => { |
| | | this.loading = false; |
| | | this.inputSelected = res; |
| | | }).catch((err) => { |
| | | this.loading = false; |
| | | this.errorMessage = err; |
| | | }) |
| | | } |
| | | }, |
| | | show() { |
| | | this.isOpened = true |
| | | setTimeout(() => { |
| | | this.$refs.pickerView.updateData({ |
| | | treeData: this._treeData, |
| | | selected: this.selected, |
| | | selectedIndex: this.selectedIndex |
| | | }) |
| | | }, 200) |
| | | this.$emit('popupopened') |
| | | }, |
| | | hide() { |
| | | this.isOpened = false |
| | | this.$emit('popupclosed') |
| | | }, |
| | | handleInput() { |
| | | if (this.readonly) { |
| | | this.$emit('inputclick') |
| | | return |
| | | } |
| | | this.show() |
| | | }, |
| | | handleClose(e) { |
| | | this.hide() |
| | | }, |
| | | onnodeclick(e) { |
| | | this.$emit('nodeclick', e) |
| | | }, |
| | | ondatachange(e) { |
| | | this._treeData = this.$refs.pickerView._treeData |
| | | }, |
| | | onchange(e) { |
| | | this.hide() |
| | | this.$nextTick(() => { |
| | | this.inputSelected = e; |
| | | }) |
| | | this._dispatchEvent(e) |
| | | }, |
| | | _processReadonly(dataList, value) { |
| | | var isTree = dataList.findIndex((item) => { |
| | | return item.children |
| | | }) |
| | | if (isTree > -1) { |
| | | let inputValue |
| | | if (Array.isArray(value)) { |
| | | inputValue = value[value.length - 1] |
| | | if (typeof inputValue === 'object' && inputValue.value) { |
| | | inputValue = inputValue.value |
| | | } |
| | | } else { |
| | | inputValue = value |
| | | } |
| | | this.inputSelected = this._findNodePath(inputValue, this.localdata) |
| | | return |
| | | } |
| | | |
| | | if (!this.hasValue) { |
| | | this.inputSelected = [] |
| | | return |
| | | } |
| | | |
| | | let result = [] |
| | | for (let i = 0; i < value.length; i++) { |
| | | var val = value[i] |
| | | var item = dataList.find((v) => { |
| | | return v.value == val |
| | | }) |
| | | if (item) { |
| | | result.push(item) |
| | | } |
| | | } |
| | | if (result.length) { |
| | | this.inputSelected = result |
| | | } |
| | | }, |
| | | _filterForArray(data, valueArray) { |
| | | var result = [] |
| | | for (let i = 0; i < valueArray.length; i++) { |
| | | var value = valueArray[i] |
| | | var found = data.find((item) => { |
| | | return item.value == value |
| | | }) |
| | | if (found) { |
| | | result.push(found) |
| | | } |
| | | } |
| | | return result |
| | | }, |
| | | _dispatchEvent(selected) { |
| | | let item = {} |
| | | if (selected.length) { |
| | | var value = new Array(selected.length) |
| | | for (var i = 0; i < selected.length; i++) { |
| | | value[i] = selected[i].value |
| | | } |
| | | item = selected[selected.length - 1] |
| | | } else { |
| | | item.value = '' |
| | | } |
| | | if (this.formItem) { |
| | | this.formItem.setValue(item.value) |
| | | } |
| | | |
| | | this.$emit('input', item.value) |
| | | this.$emit('update:modelValue', item.value) |
| | | this.$emit('change', { |
| | | detail: { |
| | | value: selected |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .uni-data-tree { |
| | | flex: 1; |
| | | position: relative; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .error-text { |
| | | color: #DD524D; |
| | | } |
| | | |
| | | .input-value { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | flex-wrap: nowrap; |
| | | font-size: 14px; |
| | | /* line-height: 35px; */ |
| | | padding: 0 10px; |
| | | padding-right: 5px; |
| | | overflow: hidden; |
| | | height: 35px; |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .input-value-border { |
| | | border: 1px solid #e5e5e5; |
| | | border-radius: 5px; |
| | | } |
| | | |
| | | .selected-area { |
| | | flex: 1; |
| | | overflow: hidden; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .load-more { |
| | | /* #ifndef APP-NVUE */ |
| | | margin-right: auto; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | width: 40px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .selected-list { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex-wrap: nowrap; |
| | | /* padding: 0 5px; */ |
| | | } |
| | | |
| | | .selected-item { |
| | | flex-direction: row; |
| | | /* padding: 0 1px; */ |
| | | /* #ifndef APP-NVUE */ |
| | | white-space: nowrap; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .text-color { |
| | | color: #333; |
| | | } |
| | | |
| | | .placeholder { |
| | | color: grey; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .input-split-line { |
| | | opacity: .5; |
| | | } |
| | | |
| | | .arrow-area { |
| | | position: relative; |
| | | width: 20px; |
| | | /* #ifndef APP-NVUE */ |
| | | margin-bottom: 5px; |
| | | margin-left: auto; |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | transform: rotate(-45deg); |
| | | transform-origin: center; |
| | | } |
| | | |
| | | .input-arrow { |
| | | width: 7px; |
| | | height: 7px; |
| | | border-left: 1px solid #999; |
| | | border-bottom: 1px solid #999; |
| | | } |
| | | |
| | | .uni-data-tree-cover { |
| | | position: fixed; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: rgba(0, 0, 0, .4); |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | z-index: 100; |
| | | } |
| | | |
| | | .uni-data-tree-dialog { |
| | | position: fixed; |
| | | left: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | top: 20%; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | top: 200px; |
| | | /* #endif */ |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: #FFFFFF; |
| | | border-top-left-radius: 10px; |
| | | border-top-right-radius: 10px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | z-index: 102; |
| | | overflow: hidden; |
| | | /* #ifdef APP-NVUE */ |
| | | width: 750rpx; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .dialog-caption { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | /* border-bottom: 1px solid #f0f0f0; */ |
| | | } |
| | | |
| | | .title-area { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | /* #ifndef APP-NVUE */ |
| | | margin: auto; |
| | | /* #endif */ |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .dialog-title { |
| | | /* font-weight: bold; */ |
| | | line-height: 44px; |
| | | } |
| | | |
| | | .dialog-close { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: 0 15px; |
| | | } |
| | | |
| | | .dialog-close-plus { |
| | | width: 16px; |
| | | height: 2px; |
| | | background-color: #666; |
| | | border-radius: 2px; |
| | | transform: rotate(45deg); |
| | | } |
| | | |
| | | .dialog-close-rotate { |
| | | position: absolute; |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .picker-view { |
| | | flex: 1; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .icon-clear { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* #ifdef H5 */ |
| | | @media all and (min-width: 768px) { |
| | | .uni-data-tree-cover { |
| | | background-color: transparent; |
| | | } |
| | | |
| | | .uni-data-tree-dialog { |
| | | position: absolute; |
| | | top: 55px; |
| | | height: auto; |
| | | min-height: 400px; |
| | | max-height: 50vh; |
| | | background-color: #fff; |
| | | border: 1px solid #EBEEF5; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | border-radius: 4px; |
| | | overflow: unset; |
| | | } |
| | | |
| | | .dialog-caption { |
| | | display: none; |
| | | } |
| | | |
| | | .icon-clear { |
| | | /* margin-right: 5px; */ |
| | | } |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */ |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-popper__arrow, |
| | | .uni-popper__arrow::after { |
| | | position: absolute; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border-color: transparent; |
| | | border-style: solid; |
| | | border-width: 6px; |
| | | } |
| | | |
| | | .uni-popper__arrow { |
| | | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
| | | top: -6px; |
| | | left: 10%; |
| | | margin-right: 3px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #EBEEF5; |
| | | } |
| | | |
| | | .uni-popper__arrow::after { |
| | | content: " "; |
| | | top: 1px; |
| | | margin-left: -6px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #fff; |
| | | } |
| | | |
| | | /* #endif */ |
| | | </style> |
New file |
| | |
| | | export default { |
| | | props: { |
| | | localdata: { |
| | | type: [Array, Object], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | spaceInfo: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | collection: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | action: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | field: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | orderby: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | where: { |
| | | type: [String, Object], |
| | | default: '' |
| | | }, |
| | | pageData: { |
| | | type: String, |
| | | default: 'add' |
| | | }, |
| | | pageCurrent: { |
| | | type: Number, |
| | | default: 1 |
| | | }, |
| | | pageSize: { |
| | | type: Number, |
| | | default: 500 |
| | | }, |
| | | getcount: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | getone: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | gettree: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | manual: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | value: { |
| | | type: [Array, String, Number], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | modelValue: { |
| | | type: [Array, String, Number], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | preload: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | stepSearh: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | selfField: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | parentField: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | multiple: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | map: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | text: "text", |
| | | value: "value" |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | errorMessage: '', |
| | | loadMore: { |
| | | contentdown: '', |
| | | contentrefresh: '', |
| | | contentnomore: '' |
| | | }, |
| | | dataList: [], |
| | | selected: [], |
| | | selectedIndex: 0, |
| | | page: { |
| | | current: this.pageCurrent, |
| | | size: this.pageSize, |
| | | count: 0 |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | isLocalData() { |
| | | return !this.collection.length; |
| | | }, |
| | | isCloudData() { |
| | | return this.collection.length > 0; |
| | | }, |
| | | isCloudDataList() { |
| | | return (this.isCloudData && (!this.parentField && !this.selfField)); |
| | | }, |
| | | isCloudDataTree() { |
| | | return (this.isCloudData && this.parentField && this.selfField); |
| | | }, |
| | | dataValue() { |
| | | let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || |
| | | this.modelValue !== undefined); |
| | | return isModelValue ? this.modelValue : this.value; |
| | | }, |
| | | hasValue() { |
| | | if (typeof this.dataValue === 'number') { |
| | | return true |
| | | } |
| | | return (this.dataValue != null) && (this.dataValue.length > 0) |
| | | } |
| | | }, |
| | | created() { |
| | | this.$watch(() => { |
| | | var al = []; |
| | | ['pageCurrent', |
| | | 'pageSize', |
| | | 'spaceInfo', |
| | | 'value', |
| | | 'modelValue', |
| | | 'localdata', |
| | | 'collection', |
| | | 'action', |
| | | 'field', |
| | | 'orderby', |
| | | 'where', |
| | | 'getont', |
| | | 'getcount', |
| | | 'gettree' |
| | | ].forEach(key => { |
| | | al.push(this[key]) |
| | | }); |
| | | return al |
| | | }, (newValue, oldValue) => { |
| | | let needReset = false |
| | | for (let i = 2; i < newValue.length; i++) { |
| | | if (newValue[i] != oldValue[i]) { |
| | | needReset = true |
| | | break |
| | | } |
| | | } |
| | | if (newValue[0] != oldValue[0]) { |
| | | this.page.current = this.pageCurrent |
| | | } |
| | | this.page.size = this.pageSize |
| | | |
| | | this.onPropsChange() |
| | | }) |
| | | this._treeData = [] |
| | | }, |
| | | methods: { |
| | | onPropsChange() { |
| | | this._treeData = []; |
| | | }, |
| | | |
| | | // 填充 pickview 数据 |
| | | async loadData() { |
| | | if (this.isLocalData) { |
| | | this.loadLocalData(); |
| | | } else if (this.isCloudDataList) { |
| | | this.loadCloudDataList(); |
| | | } else if (this.isCloudDataTree) { |
| | | this.loadCloudDataTree(); |
| | | } |
| | | }, |
| | | |
| | | // 加载本地数据 |
| | | async loadLocalData() { |
| | | this._treeData = []; |
| | | this._extractTree(this.localdata, this._treeData); |
| | | |
| | | let inputValue = this.dataValue; |
| | | if (inputValue === undefined) { |
| | | return; |
| | | } |
| | | |
| | | if (Array.isArray(inputValue)) { |
| | | inputValue = inputValue[inputValue.length - 1]; |
| | | if (typeof inputValue === 'object' && inputValue[this.map.value]) { |
| | | inputValue = inputValue[this.map.value]; |
| | | } |
| | | } |
| | | |
| | | this.selected = this._findNodePath(inputValue, this.localdata); |
| | | }, |
| | | |
| | | // 加载 Cloud 数据 (单列) |
| | | async loadCloudDataList() { |
| | | if (this.loading) { |
| | | return; |
| | | } |
| | | this.loading = true; |
| | | |
| | | try { |
| | | let response = await this.getCommand(); |
| | | let responseData = response.result.data; |
| | | |
| | | this._treeData = responseData; |
| | | |
| | | this._updateBindData(); |
| | | this._updateSelected(); |
| | | |
| | | this.onDataChange(); |
| | | } catch (e) { |
| | | this.errorMessage = e; |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | // 加载 Cloud 数据 (树形) |
| | | async loadCloudDataTree() { |
| | | if (this.loading) { |
| | | return; |
| | | } |
| | | this.loading = true; |
| | | |
| | | try { |
| | | let commandOptions = { |
| | | field: this._cloudDataPostField(), |
| | | where: this._cloudDataTreeWhere() |
| | | }; |
| | | if (this.gettree) { |
| | | commandOptions.startwith = `${this.selfField}=='${this.dataValue}'`; |
| | | } |
| | | |
| | | let response = await this.getCommand(commandOptions); |
| | | let responseData = response.result.data; |
| | | |
| | | this._treeData = responseData; |
| | | this._updateBindData(); |
| | | this._updateSelected(); |
| | | |
| | | this.onDataChange(); |
| | | } catch (e) { |
| | | this.errorMessage = e; |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | // 加载 Cloud 数据 (节点) |
| | | async loadCloudDataNode(callback) { |
| | | if (this.loading) { |
| | | return; |
| | | } |
| | | this.loading = true; |
| | | |
| | | try { |
| | | let commandOptions = { |
| | | field: this._cloudDataPostField(), |
| | | where: this._cloudDataNodeWhere() |
| | | }; |
| | | |
| | | let response = await this.getCommand(commandOptions); |
| | | let responseData = response.result.data; |
| | | |
| | | callback(responseData); |
| | | } catch (e) { |
| | | this.errorMessage = e; |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | // 回显 Cloud 数据 |
| | | getCloudDataValue() { |
| | | if (this.isCloudDataList) { |
| | | return this.getCloudDataListValue(); |
| | | } |
| | | |
| | | if (this.isCloudDataTree) { |
| | | return this.getCloudDataTreeValue(); |
| | | } |
| | | }, |
| | | |
| | | // 回显 Cloud 数据 (单列) |
| | | getCloudDataListValue() { |
| | | // 根据 field's as value标识匹配 where 条件 |
| | | let where = []; |
| | | let whereField = this._getForeignKeyByField(); |
| | | if (whereField) { |
| | | where.push(`${whereField} == '${this.dataValue}'`) |
| | | } |
| | | |
| | | where = where.join(' || '); |
| | | |
| | | if (this.where) { |
| | | where = `(${this.where}) && (${where})` |
| | | } |
| | | |
| | | return this.getCommand({ |
| | | field: this._cloudDataPostField(), |
| | | where |
| | | }).then((res) => { |
| | | this.selected = res.result.data; |
| | | return res.result.data; |
| | | }); |
| | | }, |
| | | |
| | | // 回显 Cloud 数据 (树形) |
| | | getCloudDataTreeValue() { |
| | | return this.getCommand({ |
| | | field: this._cloudDataPostField(), |
| | | getTreePath: { |
| | | startWith: `${this.selfField}=='${this.dataValue}'` |
| | | } |
| | | }).then((res) => { |
| | | let treePath = []; |
| | | this._extractTreePath(res.result.data, treePath); |
| | | this.selected = treePath; |
| | | return treePath; |
| | | }); |
| | | }, |
| | | |
| | | getCommand(options = {}) { |
| | | /* eslint-disable no-undef */ |
| | | let db = uniCloud.database(this.spaceInfo) |
| | | |
| | | const action = options.action || this.action |
| | | if (action) { |
| | | db = db.action(action) |
| | | } |
| | | |
| | | const collection = options.collection || this.collection |
| | | db = db.collection(collection) |
| | | |
| | | const where = options.where || this.where |
| | | if (!(!where || !Object.keys(where).length)) { |
| | | db = db.where(where) |
| | | } |
| | | |
| | | const field = options.field || this.field |
| | | if (field) { |
| | | db = db.field(field) |
| | | } |
| | | |
| | | const orderby = options.orderby || this.orderby |
| | | if (orderby) { |
| | | db = db.orderBy(orderby) |
| | | } |
| | | |
| | | const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current |
| | | const size = options.pageSize !== undefined ? options.pageSize : this.page.size |
| | | const getCount = options.getcount !== undefined ? options.getcount : this.getcount |
| | | const getTree = options.gettree !== undefined ? options.gettree : this.gettree |
| | | |
| | | const getOptions = { |
| | | getCount, |
| | | getTree |
| | | } |
| | | if (options.getTreePath) { |
| | | getOptions.getTreePath = options.getTreePath |
| | | } |
| | | |
| | | db = db.skip(size * (current - 1)).limit(size).get(getOptions) |
| | | |
| | | return db |
| | | }, |
| | | |
| | | _cloudDataPostField() { |
| | | let fields = [this.field]; |
| | | if (this.parentField) { |
| | | fields.push(`${this.parentField} as parent_value`); |
| | | } |
| | | return fields.join(','); |
| | | }, |
| | | |
| | | _cloudDataTreeWhere() { |
| | | let result = [] |
| | | let selected = this.selected |
| | | let parentField = this.parentField |
| | | if (parentField) { |
| | | result.push(`${parentField} == null || ${parentField} == ""`) |
| | | } |
| | | if (selected.length) { |
| | | for (var i = 0; i < selected.length - 1; i++) { |
| | | result.push(`${parentField} == '${selected[i].value}'`) |
| | | } |
| | | } |
| | | |
| | | let where = [] |
| | | if (this.where) { |
| | | where.push(`(${this.where})`) |
| | | } |
| | | |
| | | if (result.length) { |
| | | where.push(`(${result.join(' || ')})`) |
| | | } |
| | | |
| | | return where.join(' && ') |
| | | }, |
| | | |
| | | _cloudDataNodeWhere() { |
| | | let where = [] |
| | | let selected = this.selected; |
| | | if (selected.length) { |
| | | where.push(`${this.parentField} == '${selected[selected.length - 1].value}'`); |
| | | } |
| | | |
| | | where = where.join(' || '); |
| | | |
| | | if (this.where) { |
| | | return `(${this.where}) && (${where})` |
| | | } |
| | | |
| | | return where |
| | | }, |
| | | |
| | | _getWhereByForeignKey() { |
| | | let result = [] |
| | | let whereField = this._getForeignKeyByField(); |
| | | if (whereField) { |
| | | result.push(`${whereField} == '${this.dataValue}'`) |
| | | } |
| | | |
| | | if (this.where) { |
| | | return `(${this.where}) && (${result.join(' || ')})` |
| | | } |
| | | |
| | | return result.join(' || ') |
| | | }, |
| | | |
| | | _getForeignKeyByField() { |
| | | let fields = this.field.split(','); |
| | | let whereField = null; |
| | | for (let i = 0; i < fields.length; i++) { |
| | | const items = fields[i].split('as'); |
| | | if (items.length < 2) { |
| | | continue; |
| | | } |
| | | if (items[1].trim() === 'value') { |
| | | whereField = items[0].trim(); |
| | | break; |
| | | } |
| | | } |
| | | return whereField; |
| | | }, |
| | | |
| | | _updateBindData(node) { |
| | | const { |
| | | dataList, |
| | | hasNodes |
| | | } = this._filterData(this._treeData, this.selected) |
| | | |
| | | let isleaf = this._stepSearh === false && !hasNodes |
| | | |
| | | if (node) { |
| | | node.isleaf = isleaf |
| | | } |
| | | |
| | | this.dataList = dataList |
| | | this.selectedIndex = dataList.length - 1 |
| | | |
| | | if (!isleaf && this.selected.length < dataList.length) { |
| | | this.selected.push({ |
| | | value: null, |
| | | text: "请选择" |
| | | }) |
| | | } |
| | | |
| | | return { |
| | | isleaf, |
| | | hasNodes |
| | | } |
| | | }, |
| | | |
| | | _updateSelected() { |
| | | let dl = this.dataList |
| | | let sl = this.selected |
| | | let textField = this.map.text |
| | | let valueField = this.map.value |
| | | for (let i = 0; i < sl.length; i++) { |
| | | let value = sl[i].value |
| | | let dl2 = dl[i] |
| | | for (let j = 0; j < dl2.length; j++) { |
| | | let item2 = dl2[j] |
| | | if (item2[valueField] === value) { |
| | | sl[i].text = item2[textField] |
| | | break |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | |
| | | _filterData(data, paths) { |
| | | let dataList = [] |
| | | let hasNodes = true |
| | | |
| | | dataList.push(data.filter((item) => { |
| | | return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '') |
| | | })) |
| | | for (let i = 0; i < paths.length; i++) { |
| | | let value = paths[i].value |
| | | let nodes = data.filter((item) => { |
| | | return item.parent_value === value |
| | | }) |
| | | |
| | | if (nodes.length) { |
| | | dataList.push(nodes) |
| | | } else { |
| | | hasNodes = false |
| | | } |
| | | } |
| | | |
| | | return { |
| | | dataList, |
| | | hasNodes |
| | | } |
| | | }, |
| | | |
| | | _extractTree(nodes, result, parent_value) { |
| | | let list = result || [] |
| | | let valueField = this.map.value |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | let node = nodes[i] |
| | | |
| | | let child = {} |
| | | for (let key in node) { |
| | | if (key !== 'children') { |
| | | child[key] = node[key] |
| | | } |
| | | } |
| | | if (parent_value !== null && parent_value !== undefined && parent_value !== '') { |
| | | child.parent_value = parent_value |
| | | } |
| | | result.push(child) |
| | | |
| | | let children = node.children |
| | | if (children) { |
| | | this._extractTree(children, result, node[valueField]) |
| | | } |
| | | } |
| | | }, |
| | | |
| | | _extractTreePath(nodes, result) { |
| | | let list = result || [] |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | let node = nodes[i] |
| | | |
| | | let child = {} |
| | | for (let key in node) { |
| | | if (key !== 'children') { |
| | | child[key] = node[key] |
| | | } |
| | | } |
| | | result.push(child) |
| | | |
| | | let children = node.children |
| | | if (children) { |
| | | this._extractTreePath(children, result) |
| | | } |
| | | } |
| | | }, |
| | | |
| | | _findNodePath(key, nodes, path = []) { |
| | | let textField = this.map.text |
| | | let valueField = this.map.value |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | let node = nodes[i] |
| | | let children = node.children |
| | | let text = node[textField] |
| | | let value = node[valueField] |
| | | |
| | | path.push({ |
| | | value, |
| | | text |
| | | }) |
| | | |
| | | if (value === key) { |
| | | return path |
| | | } |
| | | |
| | | if (children) { |
| | | const p = this._findNodePath(key, children, path) |
| | | if (p.length) { |
| | | return p |
| | | } |
| | | } |
| | | |
| | | path.pop() |
| | | } |
| | | return [] |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <view class="uni-data-pickerview"> |
| | | <scroll-view v-if="!isCloudDataList" class="selected-area" scroll-x="true"> |
| | | <view class="selected-list"> |
| | | <view |
| | | class="selected-item" |
| | | v-for="(item,index) in selected" |
| | | :key="index" |
| | | :class="{ |
| | | 'selected-item-active':index == selectedIndex |
| | | }" |
| | | @click="handleSelect(index)" |
| | | > |
| | | <text>{{item.text || ''}}</text> |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | <view class="tab-c"> |
| | | <scroll-view class="list" :scroll-y="true"> |
| | | <view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in dataList[selectedIndex]" :key="j" |
| | | @click="handleNodeClick(item, selectedIndex, j)"> |
| | | <text class="item-text">{{item[map.text]}}</text> |
| | | <view class="check" v-if="selected.length > selectedIndex && item[map.value] == selected[selectedIndex].value"></view> |
| | | </view> |
| | | </scroll-view> |
| | | |
| | | <view class="loading-cover" v-if="loading"> |
| | | <uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more> |
| | | </view> |
| | | <view class="error-message" v-if="errorMessage"> |
| | | <text class="error-text">{{errorMessage}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import dataPicker from "./uni-data-picker.js" |
| | | |
| | | /** |
| | | * DataPickerview |
| | | * @description uni-data-pickerview |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 |
| | | * @property {Array} localdata 本地数据,参考 |
| | | * @property {Boolean} step-searh = [true|false] 是否分布查询 |
| | | * @value true 启用分布查询,仅查询当前选中节点 |
| | | * @value false 关闭分布查询,一次查询出所有数据 |
| | | * @property {String|DBFieldString} self-field 分布查询当前字段名称 |
| | | * @property {String|DBFieldString} parent-field 分布查询父字段名称 |
| | | * @property {String|DBCollectionString} collection 表名 |
| | | * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 |
| | | * @property {String} orderby 排序字段及正序倒叙设置 |
| | | * @property {String|JQLString} where 查询条件 |
| | | */ |
| | | export default { |
| | | name: 'UniDataPickerView', |
| | | emits: ['nodeclick', 'change', 'datachange', 'update:modelValue'], |
| | | mixins: [dataPicker], |
| | | props: { |
| | | managedMode: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ellipsis: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | created() { |
| | | if (!this.managedMode) { |
| | | this.$nextTick(() => { |
| | | this.loadData(); |
| | | }) |
| | | } |
| | | }, |
| | | methods: { |
| | | onPropsChange() { |
| | | this._treeData = []; |
| | | this.selectedIndex = 0; |
| | | this.$nextTick(() => { |
| | | this.loadData(); |
| | | }) |
| | | }, |
| | | handleSelect(index) { |
| | | this.selectedIndex = index; |
| | | }, |
| | | handleNodeClick(item, i, j) { |
| | | if (item.disable) { |
| | | return; |
| | | } |
| | | |
| | | const node = this.dataList[i][j]; |
| | | const text = node[this.map.text]; |
| | | const value = node[this.map.value]; |
| | | |
| | | if (i < this.selected.length - 1) { |
| | | this.selected.splice(i, this.selected.length - i) |
| | | this.selected.push({ |
| | | text, |
| | | value |
| | | }) |
| | | } else if (i === this.selected.length - 1) { |
| | | this.selected.splice(i, 1, { |
| | | text, |
| | | value |
| | | }) |
| | | } |
| | | |
| | | if (node.isleaf) { |
| | | this.onSelectedChange(node, node.isleaf) |
| | | return |
| | | } |
| | | |
| | | const { |
| | | isleaf, |
| | | hasNodes |
| | | } = this._updateBindData() |
| | | |
| | | // 本地数据 |
| | | if (this.isLocalData) { |
| | | this.onSelectedChange(node, (!hasNodes || isleaf)) |
| | | } else if (this.isCloudDataList) { // Cloud 数据 (单列) |
| | | this.onSelectedChange(node, true) |
| | | } else if (this.isCloudDataTree) { // Cloud 数据 (树形) |
| | | if (isleaf) { |
| | | this.onSelectedChange(node, node.isleaf) |
| | | } else if (!hasNodes) { // 请求一次服务器以确定是否为叶子节点 |
| | | this.loadCloudDataNode((data) => { |
| | | if (!data.length) { |
| | | node.isleaf = true |
| | | } else { |
| | | this._treeData.push(...data) |
| | | this._updateBindData(node) |
| | | } |
| | | this.onSelectedChange(node, node.isleaf) |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | updateData(data) { |
| | | this._treeData = data.treeData |
| | | this.selected = data.selected |
| | | if (!this._treeData.length) { |
| | | this.loadData() |
| | | } else { |
| | | //this.selected = data.selected |
| | | this._updateBindData() |
| | | } |
| | | }, |
| | | onDataChange() { |
| | | this.$emit('datachange'); |
| | | }, |
| | | onSelectedChange(node, isleaf) { |
| | | if (isleaf) { |
| | | this._dispatchEvent() |
| | | } |
| | | |
| | | if (node) { |
| | | this.$emit('nodeclick', node) |
| | | } |
| | | }, |
| | | _dispatchEvent() { |
| | | this.$emit('change', this.selected.slice(0)) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-data-pickerview { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | overflow: hidden; |
| | | height: 100%; |
| | | } |
| | | |
| | | .error-text { |
| | | color: #DD524D; |
| | | } |
| | | |
| | | .loading-cover { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: rgba(255, 255, 255, .5); |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | align-items: center; |
| | | z-index: 1001; |
| | | } |
| | | |
| | | .load-more { |
| | | /* #ifndef APP-NVUE */ |
| | | margin: auto; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .error-message { |
| | | background-color: #fff; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | padding: 15px; |
| | | opacity: .9; |
| | | z-index: 102; |
| | | } |
| | | |
| | | /* #ifdef APP-NVUE */ |
| | | .selected-area { |
| | | width: 750rpx; |
| | | } |
| | | /* #endif */ |
| | | |
| | | .selected-list { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | flex-wrap: nowrap; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | padding: 0 5px; |
| | | border-bottom: 1px solid #f8f8f8; |
| | | } |
| | | |
| | | .selected-item { |
| | | margin-left: 10px; |
| | | margin-right: 10px; |
| | | padding: 12px 0; |
| | | text-align: center; |
| | | /* #ifndef APP-NVUE */ |
| | | white-space: nowrap; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .selected-item-text-overflow { |
| | | width: 168px; |
| | | /* fix nvue */ |
| | | overflow: hidden; |
| | | /* #ifndef APP-NVUE */ |
| | | width: 6em; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | -o-text-overflow: ellipsis; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .selected-item-active { |
| | | border-bottom: 2px solid $uni-primary; |
| | | } |
| | | |
| | | .selected-item-text { |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .tab-c { |
| | | position: relative; |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .list { |
| | | flex: 1; |
| | | } |
| | | |
| | | .item { |
| | | padding: 12px 15px; |
| | | /* border-bottom: 1px solid #f0f0f0; */ |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .is-disabled { |
| | | opacity: .5; |
| | | } |
| | | |
| | | .item-text { |
| | | /* flex: 1; */ |
| | | color: #333333; |
| | | } |
| | | |
| | | .item-text-overflow { |
| | | width: 280px; |
| | | /* fix nvue */ |
| | | overflow: hidden; |
| | | /* #ifndef APP-NVUE */ |
| | | width: 20em; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | -o-text-overflow: ellipsis; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .check { |
| | | margin-right: 5px; |
| | | border: 2px solid $uni-primary; |
| | | border-left: 0; |
| | | border-top: 0; |
| | | height: 12px; |
| | | width: 6px; |
| | | transform-origin: center; |
| | | /* #ifndef APP-NVUE */ |
| | | transition: all 0.3s; |
| | | /* #endif */ |
| | | transform: rotate(45deg); |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-data-picker", |
| | | "displayName": "uni-data-picker 数据驱动的picker选择器", |
| | | "version": "1.1.2", |
| | | "description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "picker", |
| | | "级联", |
| | | "省市区", |
| | | "" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-load-more", |
| | | "uni-icons", |
| | | "uni-scss" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "u" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | ## DataPicker 级联选择 |
| | | > **组件名:uni-data-picker** |
| | | > 代码块: `uDataPicker` |
| | | > 关联组件:`uni-data-pickerview`、`uni-load-more`。 |
| | | |
| | | |
| | | `<uni-data-picker>` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。 |
| | | |
| | | 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 |
| | | |
| | | 候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。 |
| | | |
| | | `<uni-data-picker>` 组件尤其适用于地址选择、分类选择等选择类。 |
| | | |
| | | `<uni-data-picker>` 支持本地数据、云端静态数据(json),uniCloud云数据库数据。 |
| | | |
| | | `<uni-data-picker>` 可以通过JQL直连uniCloud云数据库,配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema),可在schema2code中自动生成前端页面,还支持服务器端校验。 |
| | | |
| | | 在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”,这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面,会自动生成地址管理的维护页面,自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.0.6(2023-04-12) |
| | | - 修复 微信小程序点击时会改变背景颜色的 bug |
| | | ## 1.0.5(2023-02-03) |
| | | - 修复 禁用时会显示清空按钮 |
| | | ## 1.0.4(2023-02-02) |
| | | - 优化 查询条件短期内多次变更只查询最后一次变更后的结果 |
| | | - 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue |
| | | ## 1.0.3(2023-01-16) |
| | | - 修复 不关联服务空间报错的问题 |
| | | ## 1.0.2(2023-01-14) |
| | | - 新增 属性 `format` 可用于格式化显示选项内容 |
| | | ## 1.0.1(2022-12-06) |
| | | - 修复 当where变化时,数据不会自动更新的问题 |
| | | ## 0.1.9(2022-09-05) |
| | | - 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框 |
| | | ## 0.1.8(2022-08-29) |
| | | - 修复 点击的位置不准确 |
| | | ## 0.1.7(2022-08-12) |
| | | - 新增 支持 disabled 属性 |
| | | ## 0.1.6(2022-07-06) |
| | | - 修复 pc端宽度异常的bug |
| | | ## 0.1.5 |
| | | - 修复 pc端宽度异常的bug |
| | | ## 0.1.4(2022-07-05) |
| | | - 优化 显示样式 |
| | | ## 0.1.3(2022-06-02) |
| | | - 修复 localdata 赋值不生效的 bug |
| | | - 新增 支持 uni.scss 修改颜色 |
| | | - 新增 支持选项禁用(数据选项设置 disabled: true 即禁用) |
| | | ## 0.1.2(2022-05-08) |
| | | - 修复 当 value 为 0 时选择不生效的 bug |
| | | ## 0.1.1(2022-05-07) |
| | | - 新增 记住上次的选项(仅 collection 存在时有效) |
| | | ## 0.1.0(2022-04-22) |
| | | - 初始化 |
New file |
| | |
| | | <template> |
| | | <view class="uni-stat__select"> |
| | | <span v-if="label" class="uni-label-text hide-on-phone">{{label + ':'}}</span> |
| | | <view class="uni-stat-box" :class="{'uni-stat__actived': current}"> |
| | | <view class="uni-select" :class="{'uni-select--disabled':disabled}"> |
| | | <view class="uni-select__input-box" @click="toggleSelector"> |
| | | <view v-if="current" class="uni-select__input-text">{{current}}</view> |
| | | <view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view> |
| | | <view v-if="current && clear && !disabled" @click.stop="clearVal" > |
| | | <uni-icons type="clear" color="#c0c4cc" size="24"/> |
| | | </view> |
| | | <view v-else> |
| | | <uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" /> |
| | | </view> |
| | | </view> |
| | | <view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" /> |
| | | <view class="uni-select__selector" v-if="showSelector"> |
| | | <view class="uni-popper__arrow"></view> |
| | | <scroll-view scroll-y="true" class="uni-select__selector-scroll"> |
| | | <view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0"> |
| | | <text>{{emptyTips}}</text> |
| | | </view> |
| | | <view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index" |
| | | @click="change(item)"> |
| | | <text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * DataChecklist 数据选择器 |
| | | * @description 通过数据渲染的下拉框组件 |
| | | * @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select |
| | | * @property {String} value 默认值 |
| | | * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}] |
| | | * @property {Boolean} clear 是否可以清空已选项 |
| | | * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效 |
| | | * @property {String} label 左侧标题 |
| | | * @property {String} placeholder 输入框的提示文字 |
| | | * @property {Boolean} disabled 是否禁用 |
| | | * @event {Function} change 选中发生变化触发 |
| | | */ |
| | | |
| | | export default { |
| | | name: "uni-data-select", |
| | | mixins: [uniCloud.mixinDatacom || {}], |
| | | props: { |
| | | localdata: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | label: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '请选择' |
| | | }, |
| | | emptyTips: { |
| | | type: String, |
| | | default: '无选项' |
| | | }, |
| | | clear: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | defItem: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}" |
| | | format: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | showSelector: false, |
| | | current: '', |
| | | mixinDatacomResData: [], |
| | | apps: [], |
| | | channels: [], |
| | | cacheKey: "uni-data-select-lastSelectedValue", |
| | | }; |
| | | }, |
| | | created() { |
| | | this.debounceGet = this.debounce(() => { |
| | | this.query(); |
| | | }, 300); |
| | | if (this.collection && !this.localdata.length) { |
| | | this.debounceGet(); |
| | | } |
| | | }, |
| | | computed: { |
| | | typePlaceholder() { |
| | | const text = { |
| | | 'opendb-stat-app-versions': '版本', |
| | | 'opendb-app-channels': '渠道', |
| | | 'opendb-app-list': '应用' |
| | | } |
| | | const common = this.placeholder |
| | | const placeholder = text[this.collection] |
| | | return placeholder ? |
| | | common + placeholder : |
| | | common |
| | | }, |
| | | valueCom(){ |
| | | // #ifdef VUE3 |
| | | return this.modelValue; |
| | | // #endif |
| | | // #ifndef VUE3 |
| | | return this.value; |
| | | // #endif |
| | | } |
| | | }, |
| | | watch: { |
| | | localdata: { |
| | | immediate: true, |
| | | handler(val, old) { |
| | | if (Array.isArray(val) && old !== val) { |
| | | this.mixinDatacomResData = val |
| | | } |
| | | } |
| | | }, |
| | | valueCom(val, old) { |
| | | this.initDefVal() |
| | | }, |
| | | mixinDatacomResData: { |
| | | immediate: true, |
| | | handler(val) { |
| | | if (val.length) { |
| | | this.initDefVal() |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | debounce(fn, time = 100){ |
| | | let timer = null |
| | | return function(...args) { |
| | | if (timer) clearTimeout(timer) |
| | | timer = setTimeout(() => { |
| | | fn.apply(this, args) |
| | | }, time) |
| | | } |
| | | }, |
| | | // 执行数据库查询 |
| | | query(){ |
| | | this.mixinDatacomEasyGet(); |
| | | }, |
| | | // 监听查询条件变更事件 |
| | | onMixinDatacomPropsChange(){ |
| | | if (this.collection) { |
| | | this.debounceGet(); |
| | | } |
| | | }, |
| | | initDefVal() { |
| | | let defValue = '' |
| | | if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) { |
| | | defValue = this.valueCom |
| | | } else { |
| | | let strogeValue |
| | | if (this.collection) { |
| | | strogeValue = this.getCache() |
| | | } |
| | | if (strogeValue || strogeValue === 0) { |
| | | defValue = strogeValue |
| | | } else { |
| | | let defItem = '' |
| | | if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) { |
| | | defItem = this.mixinDatacomResData[this.defItem - 1].value |
| | | } |
| | | defValue = defItem |
| | | } |
| | | if (defValue || defValue === 0) { |
| | | this.emit(defValue) |
| | | } |
| | | } |
| | | const def = this.mixinDatacomResData.find(item => item.value === defValue) |
| | | this.current = def ? this.formatItemName(def) : '' |
| | | }, |
| | | |
| | | /** |
| | | * @param {[String, Number]} value |
| | | * 判断用户给的 value 是否同时为禁用状态 |
| | | */ |
| | | isDisabled(value) { |
| | | let isDisabled = false; |
| | | |
| | | this.mixinDatacomResData.forEach(item => { |
| | | if (item.value === value) { |
| | | isDisabled = item.disable |
| | | } |
| | | }) |
| | | |
| | | return isDisabled; |
| | | }, |
| | | |
| | | clearVal() { |
| | | this.emit('') |
| | | if (this.collection) { |
| | | this.removeCache() |
| | | } |
| | | }, |
| | | change(item) { |
| | | if (!item.disable) { |
| | | this.showSelector = false |
| | | this.current = this.formatItemName(item) |
| | | this.emit(item.value) |
| | | } |
| | | }, |
| | | emit(val) { |
| | | this.$emit('input', val) |
| | | this.$emit('update:modelValue', val) |
| | | this.$emit('change', val) |
| | | if (this.collection) { |
| | | this.setCache(val); |
| | | } |
| | | }, |
| | | toggleSelector() { |
| | | if (this.disabled) { |
| | | return |
| | | } |
| | | |
| | | this.showSelector = !this.showSelector |
| | | }, |
| | | formatItemName(item) { |
| | | let { |
| | | text, |
| | | value, |
| | | channel_code |
| | | } = item |
| | | channel_code = channel_code ? `(${channel_code})` : '' |
| | | |
| | | if (this.format) { |
| | | // 格式化输出 |
| | | let str = ""; |
| | | str = this.format; |
| | | for (let key in item) { |
| | | str = str.replace(new RegExp(`{${key}}`,"g"),item[key]); |
| | | } |
| | | return str; |
| | | } else { |
| | | return this.collection.indexOf('app-list') > 0 ? |
| | | `${text}(${value})` : |
| | | ( |
| | | text ? |
| | | text : |
| | | `未命名${channel_code}` |
| | | ) |
| | | } |
| | | }, |
| | | // 获取当前加载的数据 |
| | | getLoadData(){ |
| | | return this.mixinDatacomResData; |
| | | }, |
| | | // 获取当前缓存key |
| | | getCurrentCacheKey(){ |
| | | return this.collection; |
| | | }, |
| | | // 获取缓存 |
| | | getCache(name=this.getCurrentCacheKey()){ |
| | | let cacheData = uni.getStorageSync(this.cacheKey) || {}; |
| | | return cacheData[name]; |
| | | }, |
| | | // 设置缓存 |
| | | setCache(value, name=this.getCurrentCacheKey()){ |
| | | let cacheData = uni.getStorageSync(this.cacheKey) || {}; |
| | | cacheData[name] = value; |
| | | uni.setStorageSync(this.cacheKey, cacheData); |
| | | }, |
| | | // 删除缓存 |
| | | removeCache(name=this.getCurrentCacheKey()){ |
| | | let cacheData = uni.getStorageSync(this.cacheKey) || {}; |
| | | delete cacheData[name]; |
| | | uni.setStorageSync(this.cacheKey, cacheData); |
| | | }, |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-base-color: #6a6a6a !default; |
| | | $uni-main-color: #333 !default; |
| | | $uni-secondary-color: #909399 !default; |
| | | $uni-border-3: #e5e5e5; |
| | | |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | @media screen and (max-width: 500px) { |
| | | .hide-on-phone { |
| | | display: none; |
| | | } |
| | | } |
| | | |
| | | /* #endif */ |
| | | .uni-stat__select { |
| | | display: flex; |
| | | align-items: center; |
| | | // padding: 15px; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | width: 100%; |
| | | flex: 1; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .uni-stat-box { |
| | | width: 100%; |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-stat__actived { |
| | | width: 100%; |
| | | flex: 1; |
| | | // outline: 1px solid #2979ff; |
| | | } |
| | | |
| | | .uni-label-text { |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | | color: $uni-base-color; |
| | | margin: auto 0; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .uni-select { |
| | | font-size: 14px; |
| | | border: 1px solid $uni-border-3; |
| | | box-sizing: border-box; |
| | | border-radius: 4px; |
| | | padding: 0 5px; |
| | | padding-left: 10px; |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | user-select: none; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | border-bottom: solid 1px $uni-border-3; |
| | | width: 100%; |
| | | flex: 1; |
| | | height: 35px; |
| | | |
| | | &--disabled { |
| | | background-color: #f5f7fa; |
| | | cursor: not-allowed; |
| | | } |
| | | } |
| | | |
| | | .uni-select__label { |
| | | font-size: 16px; |
| | | // line-height: 22px; |
| | | height: 35px; |
| | | padding-right: 10px; |
| | | color: $uni-secondary-color; |
| | | } |
| | | |
| | | .uni-select__input-box { |
| | | height: 35px; |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-select__input { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | height: 22px; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .uni-select__input-plac { |
| | | font-size: 14px; |
| | | color: $uni-secondary-color; |
| | | } |
| | | |
| | | .uni-select__selector { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | position: absolute; |
| | | top: calc(100% + 12px); |
| | | left: 0; |
| | | width: 100%; |
| | | background-color: #FFFFFF; |
| | | border: 1px solid #EBEEF5; |
| | | border-radius: 6px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | z-index: 3; |
| | | padding: 4px 0; |
| | | } |
| | | |
| | | .uni-select__selector-scroll { |
| | | /* #ifndef APP-NVUE */ |
| | | max-height: 200px; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | } |
| | | |
| | | /* #ifdef H5 */ |
| | | @media (min-width: 768px) { |
| | | .uni-select__selector-scroll { |
| | | max-height: 600px; |
| | | } |
| | | } |
| | | /* #endif */ |
| | | |
| | | .uni-select__selector-empty, |
| | | .uni-select__selector-item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | line-height: 35px; |
| | | font-size: 14px; |
| | | text-align: center; |
| | | /* border-bottom: solid 1px $uni-border-3; */ |
| | | padding: 0px 10px; |
| | | } |
| | | |
| | | .uni-select__selector-item:hover { |
| | | background-color: #f9f9f9; |
| | | } |
| | | |
| | | .uni-select__selector-empty:last-child, |
| | | .uni-select__selector-item:last-child { |
| | | /* #ifndef APP-NVUE */ |
| | | border-bottom: none; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-select__selector__disabled { |
| | | opacity: 0.4; |
| | | cursor: default; |
| | | } |
| | | |
| | | /* picker 弹出层通用的指示小三角 */ |
| | | .uni-popper__arrow, |
| | | .uni-popper__arrow::after { |
| | | position: absolute; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border-color: transparent; |
| | | border-style: solid; |
| | | border-width: 6px; |
| | | } |
| | | |
| | | .uni-popper__arrow { |
| | | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
| | | top: -6px; |
| | | left: 10%; |
| | | margin-right: 3px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #EBEEF5; |
| | | } |
| | | |
| | | .uni-popper__arrow::after { |
| | | content: " "; |
| | | top: 1px; |
| | | margin-left: -6px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #fff; |
| | | } |
| | | |
| | | .uni-select__input-text { |
| | | // width: 280px; |
| | | width: 100%; |
| | | color: $uni-main-color; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | -o-text-overflow: ellipsis; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-select__input-placeholder { |
| | | color: $uni-base-color; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .uni-select--mask { |
| | | position: fixed; |
| | | top: 0; |
| | | bottom: 0; |
| | | right: 0; |
| | | left: 0; |
| | | z-index: 2; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-data-select", |
| | | "displayName": "uni-data-select 下拉框选择器", |
| | | "version": "1.0.6", |
| | | "description": "通过数据驱动的下拉框选择器", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "select", |
| | | "uni-data-select", |
| | | "下拉框", |
| | | "下拉选" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.1" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-load-more"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "u", |
| | | "app-nvue": "n" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "u", |
| | | "百度": "u", |
| | | "字节跳动": "u", |
| | | "QQ": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | ## DataSelect 下拉框选择器 |
| | | > **组件名:uni-data-select** |
| | | > 代码块: `uDataSelect` |
| | | |
| | | 当选项过多时,使用下拉菜单展示并选择内容 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.0.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-dateformat](https://uniapp.dcloud.io/component/uniui/uni-dateformat) |
| | | ## 0.0.5(2021-07-08) |
| | | - 调整 默认时间不再是当前时间,而是显示'-'字符 |
| | | ## 0.0.4(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 0.0.3(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
| | | - 修复 iOS 平台日期格式化出错的问题 |
New file |
| | |
| | | // yyyy-MM-dd hh:mm:ss.SSS 所有支持的类型 |
| | | function pad(str, length = 2) { |
| | | str += '' |
| | | while (str.length < length) { |
| | | str = '0' + str |
| | | } |
| | | return str.slice(-length) |
| | | } |
| | | |
| | | const parser = { |
| | | yyyy: (dateObj) => { |
| | | return pad(dateObj.year, 4) |
| | | }, |
| | | yy: (dateObj) => { |
| | | return pad(dateObj.year) |
| | | }, |
| | | MM: (dateObj) => { |
| | | return pad(dateObj.month) |
| | | }, |
| | | M: (dateObj) => { |
| | | return dateObj.month |
| | | }, |
| | | dd: (dateObj) => { |
| | | return pad(dateObj.day) |
| | | }, |
| | | d: (dateObj) => { |
| | | return dateObj.day |
| | | }, |
| | | hh: (dateObj) => { |
| | | return pad(dateObj.hour) |
| | | }, |
| | | h: (dateObj) => { |
| | | return dateObj.hour |
| | | }, |
| | | mm: (dateObj) => { |
| | | return pad(dateObj.minute) |
| | | }, |
| | | m: (dateObj) => { |
| | | return dateObj.minute |
| | | }, |
| | | ss: (dateObj) => { |
| | | return pad(dateObj.second) |
| | | }, |
| | | s: (dateObj) => { |
| | | return dateObj.second |
| | | }, |
| | | SSS: (dateObj) => { |
| | | return pad(dateObj.millisecond, 3) |
| | | }, |
| | | S: (dateObj) => { |
| | | return dateObj.millisecond |
| | | }, |
| | | } |
| | | |
| | | // 这都n年了iOS依然不认识2020-12-12,需要转换为2020/12/12 |
| | | function getDate(time) { |
| | | if (time instanceof Date) { |
| | | return time |
| | | } |
| | | switch (typeof time) { |
| | | case 'string': |
| | | { |
| | | // 2020-12-12T12:12:12.000Z、2020-12-12T12:12:12.000 |
| | | if (time.indexOf('T') > -1) { |
| | | return new Date(time) |
| | | } |
| | | return new Date(time.replace(/-/g, '/')) |
| | | } |
| | | default: |
| | | return new Date(time) |
| | | } |
| | | } |
| | | |
| | | export function formatDate(date, format = 'yyyy/MM/dd hh:mm:ss') { |
| | | if (!date && date !== 0) { |
| | | return '' |
| | | } |
| | | date = getDate(date) |
| | | const dateObj = { |
| | | year: date.getFullYear(), |
| | | month: date.getMonth() + 1, |
| | | day: date.getDate(), |
| | | hour: date.getHours(), |
| | | minute: date.getMinutes(), |
| | | second: date.getSeconds(), |
| | | millisecond: date.getMilliseconds() |
| | | } |
| | | const tokenRegExp = /yyyy|yy|MM|M|dd|d|hh|h|mm|m|ss|s|SSS|SS|S/ |
| | | let flag = true |
| | | let result = format |
| | | while (flag) { |
| | | flag = false |
| | | result = result.replace(tokenRegExp, function(matched) { |
| | | flag = true |
| | | return parser[matched](dateObj) |
| | | }) |
| | | } |
| | | return result |
| | | } |
| | | |
| | | export function friendlyDate(time, { |
| | | locale = 'zh', |
| | | threshold = [60000, 3600000], |
| | | format = 'yyyy/MM/dd hh:mm:ss' |
| | | }) { |
| | | if (time === '-') { |
| | | return time |
| | | } |
| | | if (!time && time !== 0) { |
| | | return '' |
| | | } |
| | | const localeText = { |
| | | zh: { |
| | | year: '年', |
| | | month: '月', |
| | | day: '天', |
| | | hour: '小时', |
| | | minute: '分钟', |
| | | second: '秒', |
| | | ago: '前', |
| | | later: '后', |
| | | justNow: '刚刚', |
| | | soon: '马上', |
| | | template: '{num}{unit}{suffix}' |
| | | }, |
| | | en: { |
| | | year: 'year', |
| | | month: 'month', |
| | | day: 'day', |
| | | hour: 'hour', |
| | | minute: 'minute', |
| | | second: 'second', |
| | | ago: 'ago', |
| | | later: 'later', |
| | | justNow: 'just now', |
| | | soon: 'soon', |
| | | template: '{num} {unit} {suffix}' |
| | | } |
| | | } |
| | | const text = localeText[locale] || localeText.zh |
| | | let date = getDate(time) |
| | | let ms = date.getTime() - Date.now() |
| | | let absMs = Math.abs(ms) |
| | | if (absMs < threshold[0]) { |
| | | return ms < 0 ? text.justNow : text.soon |
| | | } |
| | | if (absMs >= threshold[1]) { |
| | | return formatDate(date, format) |
| | | } |
| | | let num |
| | | let unit |
| | | let suffix = text.later |
| | | if (ms < 0) { |
| | | suffix = text.ago |
| | | ms = -ms |
| | | } |
| | | const seconds = Math.floor((ms) / 1000) |
| | | const minutes = Math.floor(seconds / 60) |
| | | const hours = Math.floor(minutes / 60) |
| | | const days = Math.floor(hours / 24) |
| | | const months = Math.floor(days / 30) |
| | | const years = Math.floor(months / 12) |
| | | switch (true) { |
| | | case years > 0: |
| | | num = years |
| | | unit = text.year |
| | | break |
| | | case months > 0: |
| | | num = months |
| | | unit = text.month |
| | | break |
| | | case days > 0: |
| | | num = days |
| | | unit = text.day |
| | | break |
| | | case hours > 0: |
| | | num = hours |
| | | unit = text.hour |
| | | break |
| | | case minutes > 0: |
| | | num = minutes |
| | | unit = text.minute |
| | | break |
| | | default: |
| | | num = seconds |
| | | unit = text.second |
| | | break |
| | | } |
| | | |
| | | if (locale === 'en') { |
| | | if (num === 1) { |
| | | num = 'a' |
| | | } else { |
| | | unit += 's' |
| | | } |
| | | } |
| | | |
| | | return text.template.replace(/{\s*num\s*}/g, num + '').replace(/{\s*unit\s*}/g, unit).replace(/{\s*suffix\s*}/g, |
| | | suffix) |
| | | } |
New file |
| | |
| | | <template> |
| | | <text>{{dateShow}}</text> |
| | | </template> |
| | | |
| | | <script> |
| | | import {friendlyDate} from './date-format.js' |
| | | /** |
| | | * Dateformat 日期格式化 |
| | | * @description 日期格式化组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3279 |
| | | * @property {Object|String|Number} date 日期对象/日期字符串/时间戳 |
| | | * @property {String} locale 格式化使用的语言 |
| | | * @value zh 中文 |
| | | * @value en 英文 |
| | | * @property {Array} threshold 应用不同类型格式化的阈值 |
| | | * @property {String} format 输出日期字符串时的格式 |
| | | */ |
| | | export default { |
| | | name: 'uniDateformat', |
| | | props: { |
| | | date: { |
| | | type: [Object, String, Number], |
| | | default () { |
| | | return '-' |
| | | } |
| | | }, |
| | | locale: { |
| | | type: String, |
| | | default: 'zh', |
| | | }, |
| | | threshold: { |
| | | type: Array, |
| | | default () { |
| | | return [0, 0] |
| | | } |
| | | }, |
| | | format: { |
| | | type: String, |
| | | default: 'yyyy/MM/dd hh:mm:ss' |
| | | }, |
| | | // refreshRate使用不当可能导致性能问题,谨慎使用 |
| | | refreshRate: { |
| | | type: [Number, String], |
| | | default: 0 |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | refreshMark: 0 |
| | | } |
| | | }, |
| | | computed: { |
| | | dateShow() { |
| | | this.refreshMark |
| | | return friendlyDate(this.date, { |
| | | locale: this.locale, |
| | | threshold: this.threshold, |
| | | format: this.format |
| | | }) |
| | | } |
| | | }, |
| | | watch: { |
| | | refreshRate: { |
| | | handler() { |
| | | this.setAutoRefresh() |
| | | }, |
| | | immediate: true |
| | | } |
| | | }, |
| | | methods: { |
| | | refresh() { |
| | | this.refreshMark++ |
| | | }, |
| | | setAutoRefresh() { |
| | | clearInterval(this.refreshInterval) |
| | | if (this.refreshRate) { |
| | | this.refreshInterval = setInterval(() => { |
| | | this.refresh() |
| | | }, parseInt(this.refreshRate)) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-dateformat", |
| | | "displayName": "uni-dateformat 日期格式化", |
| | | "version": "1.0.0", |
| | | "description": "日期格式化组件,可以将日期格式化为1分钟前、刚刚等形式", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "日期格式化", |
| | | "时间格式化", |
| | | "格式化时间", |
| | | "" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "y", |
| | | "联盟": "y" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ### DateFormat 日期格式化 |
| | | > **组件名:uni-dateformat** |
| | | > 代码块: `uDateformat` |
| | | |
| | | |
| | | 日期格式化组件。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-dateformat) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 2.2.22(2023-03-30) |
| | | - 修复 日历 picker 修改年月后,自动选中当月1日 [详情](https://ask.dcloud.net.cn/question/165937) |
| | | - 修复 小程序端 低版本 ios NaN [详情](https://ask.dcloud.net.cn/question/162979) |
| | | ## 2.2.21(2023-02-20) |
| | | - 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362) |
| | | ## 2.2.20(2023-02-17) |
| | | - 优化 值为空依然选中当天问题 |
| | | - 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间 |
| | | - 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间 |
| | | - 优化 字节小程序日期时间范围选择,底部日期换行问题 |
| | | ## 2.2.19(2023-02-09) |
| | | - 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686) |
| | | ## 2.2.18(2023-02-08) |
| | | - 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684) |
| | | - 优化 PC端输入日期格式错误时返回当前日期时间 |
| | | - 优化 PC端输入日期时间超出 start、end 限制的Bug |
| | | - 优化 移动端日期时间范围用法时间展示不完整问题 |
| | | ## 2.2.17(2023-02-04) |
| | | - 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679) |
| | | - 修复 vue3 time-picker 无法显示绑定时分秒的Bug |
| | | ## 2.2.16(2023-02-02) |
| | | - 修复 字节小程序报错的Bug |
| | | ## 2.2.15(2023-02-02) |
| | | - 修复 某些情况切换月份错误的Bug |
| | | ## 2.2.14(2023-01-30) |
| | | - 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033) |
| | | ## 2.2.13(2023-01-10) |
| | | - 修复 多次加载组件造成内存占用的Bug |
| | | ## 2.2.12(2022-12-01) |
| | | - 修复 vue3 下 i18n 国际化初始值不正确的Bug |
| | | ## 2.2.11(2022-09-19) |
| | | - 修复 支付宝小程序样式错乱的Bug [详情](https://github.com/dcloudio/uni-app/issues/3861) |
| | | ## 2.2.10(2022-09-19) |
| | | - 修复 反向选择日期范围,日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false) |
| | | ## 2.2.9(2022-09-16) |
| | | - 可以使用 uni-scss 控制主题色 |
| | | ## 2.2.8(2022-09-08) |
| | | - 修复 close事件无效的Bug |
| | | ## 2.2.7(2022-09-05) |
| | | - 修复 移动端 maskClick 无效的Bug [详情](https://ask.dcloud.net.cn/question/140824) |
| | | ## 2.2.6(2022-06-30) |
| | | - 优化 组件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致 |
| | | ## 2.2.5(2022-06-24) |
| | | - 修复 日历顶部年月及底部确认未国际化的Bug |
| | | ## 2.2.4(2022-03-31) |
| | | - 修复 Vue3 下动态赋值,单选类型未响应的Bug |
| | | ## 2.2.3(2022-03-28) |
| | | - 修复 Vue3 下动态赋值未响应的Bug |
| | | ## 2.2.2(2021-12-10) |
| | | - 修复 clear-icon 属性在小程序平台不生效的Bug |
| | | ## 2.2.1(2021-12-10) |
| | | - 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的Bug |
| | | ## 2.2.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源 [详情](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移 [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker) |
| | | ## 2.1.5(2021-11-09) |
| | | - 新增 提供组件设计资源,组件样式调整 |
| | | ## 2.1.4(2021-09-10) |
| | | - 修复 hide-second 在移动端的Bug |
| | | - 修复 单选赋默认值时,赋值日期未高亮的Bug |
| | | - 修复 赋默认值时,移动端未正确显示时间的Bug |
| | | ## 2.1.3(2021-09-09) |
| | | - 新增 hide-second 属性,支持只使用时分,隐藏秒 |
| | | ## 2.1.2(2021-09-03) |
| | | - 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次 |
| | | - 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法 |
| | | - 优化 调整字号大小,美化日历界面 |
| | | - 修复 因国际化导致的 placeholder 失效的Bug |
| | | ## 2.1.1(2021-08-24) |
| | | - 新增 支持国际化 |
| | | - 优化 范围选择器在 pc 端过宽的问题 |
| | | ## 2.1.0(2021-08-09) |
| | | - 新增 适配 vue3 |
| | | ## 2.0.19(2021-08-09) |
| | | - 新增 支持作为 uni-forms 子组件相关功能 |
| | | - 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的Bug |
| | | ## 2.0.18(2021-08-05) |
| | | - 修复 type 属性动态赋值无效的Bug |
| | | - 修复 ‘确认’按钮被 tabbar 遮盖 bug |
| | | - 修复 组件未赋值时范围选左、右日历相同的Bug |
| | | ## 2.0.17(2021-08-04) |
| | | - 修复 范围选未正确显示当前值的Bug |
| | | - 修复 h5 平台(移动端)报错 'cale' of undefined 的Bug |
| | | ## 2.0.16(2021-07-21) |
| | | - 新增 return-type 属性支持返回 date 日期对象 |
| | | ## 2.0.15(2021-07-14) |
| | | - 修复 单选日期类型,初始赋值后不在当前日历的Bug |
| | | - 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效) |
| | | - 优化 移动端移除显示框的清空按钮,无实际用途 |
| | | ## 2.0.14(2021-07-14) |
| | | - 修复 组件赋值为空,界面未更新的Bug |
| | | - 修复 start 和 end 不能动态赋值的Bug |
| | | - 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的Bug |
| | | ## 2.0.13(2021-07-08) |
| | | - 修复 范围选择不能动态赋值的Bug |
| | | ## 2.0.12(2021-07-08) |
| | | - 修复 范围选择的初始时间在一个月内时,造成无法选择的bug |
| | | ## 2.0.11(2021-07-08) |
| | | - 优化 弹出层在超出视窗边缘定位不准确的问题 |
| | | ## 2.0.10(2021-07-08) |
| | | - 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的Bug |
| | | - 优化 弹出层在超出视窗边缘被遮盖的问题 |
| | | ## 2.0.9(2021-07-07) |
| | | - 新增 maskClick 事件 |
| | | - 修复 特殊情况日历 rpx 布局错误的Bug,rpx -> px |
| | | - 修复 范围选择时清空返回值不合理的bug,['', ''] -> [] |
| | | ## 2.0.8(2021-07-07) |
| | | - 新增 日期时间显示框支持插槽 |
| | | ## 2.0.7(2021-07-01) |
| | | - 优化 添加 uni-icons 依赖 |
| | | ## 2.0.6(2021-05-22) |
| | | - 修复 图标在小程序上不显示的Bug |
| | | - 优化 重命名引用组件,避免潜在组件命名冲突 |
| | | ## 2.0.5(2021-05-20) |
| | | - 优化 代码目录扁平化 |
| | | ## 2.0.4(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 2.0.3(2021-05-10) |
| | | - 修复 ios 下不识别 '-' 日期格式的Bug |
| | | - 优化 pc 下弹出层添加边框和阴影 |
| | | ## 2.0.2(2021-05-08) |
| | | - 修复 在 admin 中获取弹出层定位错误的bug |
| | | ## 2.0.1(2021-05-08) |
| | | - 修复 type 属性向下兼容,默认值从 date 变更为 datetime |
| | | ## 2.0.0(2021-04-30) |
| | | - 支持日历形式的日期+时间的范围选择 |
| | | > 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker) |
| | | ## 1.0.6(2021-03-18) |
| | | - 新增 hide-second 属性,时间支持仅选择时、分 |
| | | - 修复 选择跟显示的日期不一样的Bug |
| | | - 修复 chang事件触发2次的Bug |
| | | - 修复 分、秒 end 范围错误的Bug |
| | | - 优化 更好的 nvue 适配 |
New file |
| | |
| | | <template> |
| | | <view class="uni-calendar-item__weeks-box" :class="{ |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | 'uni-calendar-item--before-checked-x':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked-x':weeks.afterMultiple, |
| | | }" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)"> |
| | | <view class="uni-calendar-item__weeks-box-item" :class="{ |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover), |
| | | 'uni-calendar-item--checked-range-text': checkHover, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | }"> |
| | | <text v-if="selected && weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> |
| | | <text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text> |
| | | </view> |
| | | <view :class="{'uni-calendar-item--today': weeks.isToday}"></view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | props: { |
| | | weeks: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | calendar: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | } |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default: () => { |
| | | return [] |
| | | } |
| | | }, |
| | | checkHover: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | methods: { |
| | | choiceDate(weeks) { |
| | | this.$emit('change', weeks) |
| | | }, |
| | | handleMousemove(weeks) { |
| | | this.$emit('handleMouse', weeks) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-calendar-item__weeks-box { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin: 1px 0; |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-text { |
| | | font-size: 14px; |
| | | // font-family: Lato-Bold, Lato; |
| | | font-weight: bold; |
| | | color: darken($color: $uni-primary, $amount: 40%); |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-item { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: 40px; |
| | | height: 40px; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | |
| | | .uni-calendar-item__weeks-box-circle { |
| | | position: absolute; |
| | | top: 5px; |
| | | right: 5px; |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 8px; |
| | | background-color: #dd524d; |
| | | |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box .uni-calendar-item--disable { |
| | | cursor: default; |
| | | } |
| | | |
| | | .uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable { |
| | | color: #D1D1D1; |
| | | } |
| | | |
| | | .uni-calendar-item--today { |
| | | position: absolute; |
| | | top: 10px; |
| | | right: 17%; |
| | | background-color: #dd524d; |
| | | width:6px; |
| | | height: 6px; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .uni-calendar-item--extra { |
| | | color: #dd524d; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box .uni-calendar-item--checked { |
| | | background-color: $uni-primary; |
| | | border-radius: 50%; |
| | | box-sizing: border-box; |
| | | border: 3px solid #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--checked .uni-calendar-item--checked-text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--multiple .uni-calendar-item--checked-range-text { |
| | | color: #333; |
| | | } |
| | | |
| | | .uni-calendar-item--multiple { |
| | | background-color: #F6F7FC; |
| | | // color: #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--multiple .uni-calendar-item--before-checked, |
| | | .uni-calendar-item--multiple .uni-calendar-item--after-checked { |
| | | background-color: $uni-primary; |
| | | border-radius: 50%; |
| | | box-sizing: border-box; |
| | | border: 3px solid #F6F7FC; |
| | | } |
| | | |
| | | .uni-calendar-item--before-checked .uni-calendar-item--checked-text, |
| | | .uni-calendar-item--after-checked .uni-calendar-item--checked-text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--before-checked-x { |
| | | border-top-left-radius: 50px; |
| | | border-bottom-left-radius: 50px; |
| | | box-sizing: border-box; |
| | | background-color: #F6F7FC; |
| | | } |
| | | |
| | | .uni-calendar-item--after-checked-x { |
| | | border-top-right-radius: 50px; |
| | | border-bottom-right-radius: 50px; |
| | | background-color: #F6F7FC; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-calendar" @mouseleave="leaveCale"> |
| | | |
| | | <view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" |
| | | @click="maskClick"></view> |
| | | |
| | | <view v-if="insert || show" class="uni-calendar__content" |
| | | :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}"> |
| | | <view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}"> |
| | | |
| | | <view class="uni-calendar__header-btn-box" @click.stop="changeMonth('pre')"> |
| | | <view class="uni-calendar__header-btn uni-calendar--left"></view> |
| | | </view> |
| | | |
| | | <picker mode="date" :value="date" fields="month" @change="bindDateChange"> |
| | | <text |
| | | class="uni-calendar__header-text">{{ (nowDate.year||'') + yearText + ( nowDate.month||'') + monthText}}</text> |
| | | </picker> |
| | | |
| | | <view class="uni-calendar__header-btn-box" @click.stop="changeMonth('next')"> |
| | | <view class="uni-calendar__header-btn uni-calendar--right"></view> |
| | | </view> |
| | | |
| | | <view v-if="!insert" class="dialog-close" @click="close"> |
| | | <view class="dialog-close-plus" data-id="close"></view> |
| | | <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> |
| | | </view> |
| | | </view> |
| | | <view class="uni-calendar__box"> |
| | | |
| | | <view v-if="showMonth" class="uni-calendar__box-bg"> |
| | | <text class="uni-calendar__box-bg-text">{{nowDate.month}}</text> |
| | | </view> |
| | | |
| | | <view class="uni-calendar__weeks" style="padding-bottom: 7px;"> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{SUNText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{MONText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{TUEText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{WEDText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{THUText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{FRIText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{SATText}}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex"> |
| | | <view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex"> |
| | | <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" |
| | | :selected="selected" :checkHover="range" @change="choiceDate" |
| | | @handleMouse="handleMouse"> |
| | | </calendar-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="!insert && !range && hasTime" class="uni-date-changed uni-calendar--fixed-top" |
| | | style="padding: 0 80px;"> |
| | | <view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view> |
| | | <time-picker type="time" :start="timepickerStartTime" :end="timepickerEndTime" v-model="time" |
| | | :disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style"> |
| | | </time-picker> |
| | | </view> |
| | | |
| | | <view v-if="!insert && range && hasTime" class="uni-date-changed uni-calendar--fixed-top"> |
| | | <view class="uni-date-changed--time-start"> |
| | | <view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}} |
| | | </view> |
| | | <time-picker type="time" :start="timepickerStartTime" v-model="timeRange.startTime" :border="false" |
| | | :hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style"> |
| | | </time-picker> |
| | | </view> |
| | | <view style="line-height: 50px;"> |
| | | <uni-icons type="arrowthinright" color="#999"></uni-icons> |
| | | </view> |
| | | <view class="uni-date-changed--time-end"> |
| | | <view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view> |
| | | <time-picker type="time" :end="timepickerEndTime" v-model="timeRange.endTime" :border="false" |
| | | :hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style"> |
| | | </time-picker> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="!insert" class="uni-date-changed uni-date-btn--ok"> |
| | | <view class="uni-datetime-picker--btn" @click="confirm">{{confirmText}}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { Calendar, getDate, getTime } from './util.js'; |
| | | import calendarItem from './calendar-item.vue' |
| | | import timePicker from './time-picker.vue' |
| | | |
| | | import { initVueI18n } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | const { t } = initVueI18n(i18nMessages) |
| | | |
| | | /** |
| | | * Calendar 日历 |
| | | * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=56 |
| | | * @property {String} date 自定义当前时间,默认为今天 |
| | | * @property {String} startDate 日期选择范围-开始日期 |
| | | * @property {String} endDate 日期选择范围-结束日期 |
| | | * @property {Boolean} range 范围选择 |
| | | * @property {Boolean} insert = [true|false] 插入模式,默认为false |
| | | * @value true 弹窗模式 |
| | | * @value false 插入模式 |
| | | * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容 |
| | | * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
| | | * @property {Boolean} showMonth 是否选择月份为背景 |
| | | * @property {[String} defaultValue 选择器打开时默认显示的时间 |
| | | * @event {Function} change 日期改变,`insert :ture` 时生效 |
| | | * @event {Function} confirm 确认选择`insert :false` 时生效 |
| | | * @event {Function} monthSwitch 切换月份时触发 |
| | | * @example <uni-calendar :insert="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> |
| | | */ |
| | | export default { |
| | | components: { |
| | | calendarItem, |
| | | timePicker |
| | | }, |
| | | props: { |
| | | date: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | defTime: { |
| | | type: [String, Object], |
| | | default: '' |
| | | }, |
| | | selectableTimes: { |
| | | type: [Object], |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | startDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | endDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | startPlaceholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | endPlaceholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | range: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | hasTime: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | insert: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showMonth: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | clearDate: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | checkHover: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | hideSecond: { |
| | | type: [Boolean], |
| | | default: false |
| | | }, |
| | | pleStatus: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | before: '', |
| | | after: '', |
| | | data: [], |
| | | fulldate: '' |
| | | } |
| | | } |
| | | }, |
| | | defaultValue: { |
| | | type: [String, Object, Array], |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | show: false, |
| | | weeks: [], |
| | | calendar: {}, |
| | | nowDate: {}, |
| | | aniMaskShow: false, |
| | | firstEnter: true, |
| | | time: '', |
| | | timeRange: { |
| | | startTime: '', |
| | | endTime: '' |
| | | }, |
| | | tempSingleDate: '', |
| | | tempRange: { |
| | | before: '', |
| | | after: '' |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | date: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (!this.range) { |
| | | this.tempSingleDate = newVal |
| | | setTimeout(() => { |
| | | this.init(newVal) |
| | | }, 100) |
| | | } |
| | | } |
| | | }, |
| | | defTime: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (!this.range) { |
| | | this.time = newVal |
| | | } else { |
| | | this.timeRange.startTime = newVal.start |
| | | this.timeRange.endTime = newVal.end |
| | | } |
| | | } |
| | | }, |
| | | startDate(val) { |
| | | // 字节小程序 watch 早于 created |
| | | if(!this.cale){ |
| | | return |
| | | } |
| | | this.cale.setStartDate(val) |
| | | this.cale.setDate(this.nowDate.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | endDate(val) { |
| | | // 字节小程序 watch 早于 created |
| | | if(!this.cale){ |
| | | return |
| | | } |
| | | this.cale.setEndDate(val) |
| | | this.cale.setDate(this.nowDate.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | selected(newVal) { |
| | | // 字节小程序 watch 早于 created |
| | | if(!this.cale){ |
| | | return |
| | | } |
| | | this.cale.setSelectInfo(this.nowDate.fullDate, newVal) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | pleStatus: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | const { |
| | | before, |
| | | after, |
| | | fulldate, |
| | | which |
| | | } = newVal |
| | | this.tempRange.before = before |
| | | this.tempRange.after = after |
| | | setTimeout(() => { |
| | | if (fulldate) { |
| | | this.cale.setHoverMultiple(fulldate) |
| | | if (before && after) { |
| | | this.cale.lastHover = true |
| | | if (this.rangeWithinMonth(after, before)) return |
| | | this.setDate(before) |
| | | } else { |
| | | this.cale.setMultiple(fulldate) |
| | | this.setDate(this.nowDate.fullDate) |
| | | this.calendar.fullDate = '' |
| | | this.cale.lastHover = false |
| | | } |
| | | } else { |
| | | // 字节小程序 watch 早于 created |
| | | if(!this.cale){ |
| | | return |
| | | } |
| | | |
| | | this.cale.setDefaultMultiple(before, after) |
| | | if (which === 'left' && before) { |
| | | this.setDate(before) |
| | | this.weeks = this.cale.weeks |
| | | } else if(after) { |
| | | this.setDate(after) |
| | | this.weeks = this.cale.weeks |
| | | } |
| | | this.cale.lastHover = true |
| | | } |
| | | }, 16) |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | timepickerStartTime() { |
| | | const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate |
| | | return activeDate === this.startDate ? this.selectableTimes.start : '' |
| | | }, |
| | | timepickerEndTime() { |
| | | const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate |
| | | return activeDate === this.endDate ? this.selectableTimes.end : '' |
| | | }, |
| | | /** |
| | | * for i18n |
| | | */ |
| | | selectDateText() { |
| | | return t("uni-datetime-picker.selectDate") |
| | | }, |
| | | startDateText() { |
| | | return this.startPlaceholder || t("uni-datetime-picker.startDate") |
| | | }, |
| | | endDateText() { |
| | | return this.endPlaceholder || t("uni-datetime-picker.endDate") |
| | | }, |
| | | okText() { |
| | | return t("uni-datetime-picker.ok") |
| | | }, |
| | | yearText() { |
| | | return t("uni-datetime-picker.year") |
| | | }, |
| | | monthText() { |
| | | return t("uni-datetime-picker.month") |
| | | }, |
| | | MONText() { |
| | | return t("uni-calender.MON") |
| | | }, |
| | | TUEText() { |
| | | return t("uni-calender.TUE") |
| | | }, |
| | | WEDText() { |
| | | return t("uni-calender.WED") |
| | | }, |
| | | THUText() { |
| | | return t("uni-calender.THU") |
| | | }, |
| | | FRIText() { |
| | | return t("uni-calender.FRI") |
| | | }, |
| | | SATText() { |
| | | return t("uni-calender.SAT") |
| | | }, |
| | | SUNText() { |
| | | return t("uni-calender.SUN") |
| | | }, |
| | | confirmText() { |
| | | return t("uni-calender.confirm") |
| | | }, |
| | | }, |
| | | created() { |
| | | // 获取日历方法实例 |
| | | this.cale = new Calendar({ |
| | | selected: this.selected, |
| | | startDate: this.startDate, |
| | | endDate: this.endDate, |
| | | range: this.range, |
| | | }) |
| | | // 选中某一天 |
| | | this.init(this.date) |
| | | }, |
| | | methods: { |
| | | leaveCale() { |
| | | this.firstEnter = true |
| | | }, |
| | | handleMouse(weeks) { |
| | | if (weeks.disable) return |
| | | if (this.cale.lastHover) return |
| | | let { |
| | | before, |
| | | after |
| | | } = this.cale.multipleStatus |
| | | if (!before) return |
| | | this.calendar = weeks |
| | | // 设置范围选 |
| | | this.cale.setHoverMultiple(this.calendar.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | // hover时,进入一个日历,更新另一个 |
| | | if (this.firstEnter) { |
| | | this.$emit('firstEnterCale', this.cale.multipleStatus) |
| | | this.firstEnter = false |
| | | } |
| | | }, |
| | | rangeWithinMonth(A, B) { |
| | | const [yearA, monthA] = A.split('-') |
| | | const [yearB, monthB] = B.split('-') |
| | | return yearA === yearB && monthA === monthB |
| | | }, |
| | | // 蒙版点击事件 |
| | | maskClick() { |
| | | this.close() |
| | | this.$emit('maskClose') |
| | | }, |
| | | |
| | | clearCalender() { |
| | | if (this.range) { |
| | | this.timeRange.startTime = '' |
| | | this.timeRange.endTime = '' |
| | | this.tempRange.before = '' |
| | | this.tempRange.after = '' |
| | | this.cale.multipleStatus.before = '' |
| | | this.cale.multipleStatus.after = '' |
| | | this.cale.multipleStatus.data = [] |
| | | this.cale.lastHover = false |
| | | } else { |
| | | this.time = '' |
| | | this.tempSingleDate = '' |
| | | } |
| | | this.calendar.fullDate = '' |
| | | this.setDate(new Date()) |
| | | }, |
| | | |
| | | bindDateChange(e) { |
| | | const value = e.detail.value + '-1' |
| | | this.setDate(value) |
| | | }, |
| | | /** |
| | | * 初始化日期显示 |
| | | * @param {Object} date |
| | | */ |
| | | init(date) { |
| | | // 字节小程序 watch 早于 created |
| | | if(!this.cale){ |
| | | return |
| | | } |
| | | this.cale.setDate(date || new Date()) |
| | | this.weeks = this.cale.weeks |
| | | this.nowDate = this.cale.getInfo(date) |
| | | this.calendar = {...this.nowDate} |
| | | if(!date){ |
| | | // 优化date为空默认不选中今天 |
| | | this.calendar.fullDate = '' |
| | | if(this.defaultValue && !this.range){ |
| | | // 暂时只支持移动端非范围选择 |
| | | const defaultDate = new Date(this.defaultValue) |
| | | const fullDate = getDate(defaultDate) |
| | | const year = defaultDate.getFullYear() |
| | | const month = defaultDate.getMonth()+1 |
| | | const date = defaultDate.getDate() |
| | | const day = defaultDate.getDay() |
| | | this.calendar = { |
| | | fullDate, |
| | | year, |
| | | month, |
| | | date, |
| | | day |
| | | }, |
| | | this.tempSingleDate = fullDate |
| | | this.time = getTime(defaultDate, this.hideSecond) |
| | | } |
| | | } |
| | | }, |
| | | /** |
| | | * 打开日历弹窗 |
| | | */ |
| | | open() { |
| | | // 弹窗模式并且清理数据 |
| | | if (this.clearDate && !this.insert) { |
| | | this.cale.cleanMultipleStatus() |
| | | this.init(this.date) |
| | | } |
| | | this.show = true |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this.aniMaskShow = true |
| | | }, 50) |
| | | }) |
| | | }, |
| | | /** |
| | | * 关闭日历弹窗 |
| | | */ |
| | | close() { |
| | | this.aniMaskShow = false |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this.show = false |
| | | this.$emit('close') |
| | | }, 300) |
| | | }) |
| | | }, |
| | | /** |
| | | * 确认按钮 |
| | | */ |
| | | confirm() { |
| | | this.setEmit('confirm') |
| | | this.close() |
| | | }, |
| | | /** |
| | | * 变化触发 |
| | | */ |
| | | change() { |
| | | if (!this.insert) return |
| | | this.setEmit('change') |
| | | }, |
| | | /** |
| | | * 选择月份触发 |
| | | */ |
| | | monthSwitch() { |
| | | let { |
| | | year, |
| | | month |
| | | } = this.nowDate |
| | | this.$emit('monthSwitch', { |
| | | year, |
| | | month: Number(month) |
| | | }) |
| | | }, |
| | | /** |
| | | * 派发事件 |
| | | * @param {Object} name |
| | | */ |
| | | setEmit(name) { |
| | | if(!this.range){ |
| | | if(!this.calendar.fullDate){ |
| | | this.calendar = this.cale.getInfo(new Date()) |
| | | this.tempSingleDate = this.calendar.fullDate |
| | | } |
| | | if(this.hasTime && !this.time) { |
| | | this.time = getTime(new Date(), this.hideSecond) |
| | | } |
| | | } |
| | | let { |
| | | year, |
| | | month, |
| | | date, |
| | | fullDate, |
| | | extraInfo |
| | | } = this.calendar |
| | | this.$emit(name, { |
| | | range: this.cale.multipleStatus, |
| | | year, |
| | | month, |
| | | date, |
| | | time: this.time, |
| | | timeRange: this.timeRange, |
| | | fulldate: fullDate, |
| | | extraInfo: extraInfo || {} |
| | | }) |
| | | }, |
| | | /** |
| | | * 选择天触发 |
| | | * @param {Object} weeks |
| | | */ |
| | | choiceDate(weeks) { |
| | | if (weeks.disable) return |
| | | this.calendar = weeks |
| | | this.calendar.userChecked = true |
| | | // 设置多选 |
| | | this.cale.setMultiple(this.calendar.fullDate, true) |
| | | this.weeks = this.cale.weeks |
| | | this.tempSingleDate = this.calendar.fullDate |
| | | const beforeDate = new Date(this.cale.multipleStatus.before).getTime() |
| | | const afterDate = new Date(this.cale.multipleStatus.after).getTime() |
| | | if (beforeDate > afterDate && afterDate) { |
| | | this.tempRange.before = this.cale.multipleStatus.after |
| | | this.tempRange.after = this.cale.multipleStatus.before |
| | | } else { |
| | | this.tempRange.before = this.cale.multipleStatus.before |
| | | this.tempRange.after = this.cale.multipleStatus.after |
| | | } |
| | | this.change() |
| | | }, |
| | | changeMonth(type) { |
| | | let newDate |
| | | if(type === 'pre') { |
| | | newDate = this.cale.getPreMonthObj(this.nowDate.fullDate).fullDate |
| | | } else if(type === 'next') { |
| | | newDate = this.cale.getNextMonthObj(this.nowDate.fullDate).fullDate |
| | | } |
| | | |
| | | this.setDate(newDate) |
| | | this.monthSwitch() |
| | | }, |
| | | /** |
| | | * 设置日期 |
| | | * @param {Object} date |
| | | */ |
| | | setDate(date) { |
| | | this.cale.setDate(date) |
| | | this.weeks = this.cale.weeks |
| | | this.nowDate = this.cale.getInfo(date) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-calendar { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-calendar__mask { |
| | | position: fixed; |
| | | bottom: 0; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background-color: rgba(0, 0, 0, 0.4); |
| | | transition-property: opacity; |
| | | transition-duration: 0.3s; |
| | | opacity: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-calendar--mask-show { |
| | | opacity: 1 |
| | | } |
| | | |
| | | .uni-calendar--fixed { |
| | | position: fixed; |
| | | bottom: calc(var(--window-bottom)); |
| | | left: 0; |
| | | right: 0; |
| | | transition-property: transform; |
| | | transition-duration: 0.3s; |
| | | transform: translateY(460px); |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-calendar--ani-show { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | .uni-calendar__content { |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .uni-calendar__content-mobile { |
| | | border-top-left-radius: 10px; |
| | | border-top-right-radius: 10px; |
| | | box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .uni-calendar__header { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 50px; |
| | | } |
| | | |
| | | .uni-calendar__header-mobile { |
| | | padding: 10px; |
| | | padding-bottom: 0; |
| | | } |
| | | |
| | | .uni-calendar--fixed-top { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | border-top-color: rgba(0, 0, 0, 0.4); |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar--fixed-width { |
| | | width: 50px; |
| | | } |
| | | |
| | | .uni-calendar__backtoday { |
| | | position: absolute; |
| | | right: 0; |
| | | top: 25rpx; |
| | | padding: 0 5px; |
| | | padding-left: 10px; |
| | | height: 25px; |
| | | line-height: 25px; |
| | | font-size: 12px; |
| | | border-top-left-radius: 25px; |
| | | border-bottom-left-radius: 25px; |
| | | color: #fff; |
| | | background-color: #f1f1f1; |
| | | } |
| | | |
| | | .uni-calendar__header-text { |
| | | text-align: center; |
| | | width: 100px; |
| | | font-size: 15px; |
| | | color: #666; |
| | | } |
| | | |
| | | .uni-calendar__button-text { |
| | | text-align: center; |
| | | width: 100px; |
| | | font-size: 14px; |
| | | color: $uni-primary; |
| | | /* #ifndef APP-NVUE */ |
| | | letter-spacing: 3px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-calendar__header-btn-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 50px; |
| | | height: 50px; |
| | | } |
| | | |
| | | .uni-calendar__header-btn { |
| | | width: 9px; |
| | | height: 9px; |
| | | border-left-color: #808080; |
| | | border-left-style: solid; |
| | | border-left-width: 1px; |
| | | border-top-color: #555555; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar--left { |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .uni-calendar--right { |
| | | transform: rotate(135deg); |
| | | } |
| | | |
| | | |
| | | .uni-calendar__weeks { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-calendar__weeks-item { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-calendar__weeks-day { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 40px; |
| | | border-bottom-color: #F5F5F5; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar__weeks-day-text { |
| | | font-size: 12px; |
| | | color: #B2B2B2; |
| | | } |
| | | |
| | | .uni-calendar__box { |
| | | position: relative; |
| | | // padding: 0 10px; |
| | | padding-bottom: 7px; |
| | | } |
| | | |
| | | .uni-calendar__box-bg { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | } |
| | | |
| | | .uni-calendar__box-bg-text { |
| | | font-size: 200px; |
| | | font-weight: bold; |
| | | color: #999; |
| | | opacity: 0.1; |
| | | text-align: center; |
| | | /* #ifndef APP-NVUE */ |
| | | line-height: 1; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-date-changed { |
| | | padding: 0 10px; |
| | | // line-height: 50px; |
| | | text-align: center; |
| | | color: #333; |
| | | border-top-color: #DCDCDC; |
| | | ; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-date-btn--ok { |
| | | padding: 20px 15px; |
| | | } |
| | | |
| | | .uni-date-changed--time-start { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-date-changed--time-end { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-date-changed--time-date { |
| | | color: #999; |
| | | line-height: 50px; |
| | | /* #ifdef MP-TOUTIAO */ |
| | | font-size: 16px; |
| | | /* #endif */ |
| | | margin-right: 5px; |
| | | // opacity: 0.6; |
| | | } |
| | | |
| | | .time-picker-style { |
| | | // width: 62px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center |
| | | } |
| | | |
| | | .mr-10 { |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .dialog-close { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: 0 25px; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .dialog-close-plus { |
| | | width: 16px; |
| | | height: 2px; |
| | | background-color: #737987; |
| | | border-radius: 2px; |
| | | transform: rotate(45deg); |
| | | } |
| | | |
| | | .dialog-close-rotate { |
| | | position: absolute; |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .uni-datetime-picker--btn { |
| | | border-radius: 100px; |
| | | height: 40px; |
| | | line-height: 40px; |
| | | background-color: $uni-primary; |
| | | color: #fff; |
| | | font-size: 16px; |
| | | letter-spacing: 2px; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-datetime-picker--btn:active { |
| | | opacity: 0.7; |
| | | } |
| | | /* #endif */ |
| | | </style> |
New file |
| | |
| | | { |
| | | "uni-datetime-picker.selectDate": "select date", |
| | | "uni-datetime-picker.selectTime": "select time", |
| | | "uni-datetime-picker.selectDateTime": "select date and time", |
| | | "uni-datetime-picker.startDate": "start date", |
| | | "uni-datetime-picker.endDate": "end date", |
| | | "uni-datetime-picker.startTime": "start time", |
| | | "uni-datetime-picker.endTime": "end time", |
| | | "uni-datetime-picker.ok": "ok", |
| | | "uni-datetime-picker.clear": "clear", |
| | | "uni-datetime-picker.cancel": "cancel", |
| | | "uni-datetime-picker.year": "-", |
| | | "uni-datetime-picker.month": "", |
| | | "uni-calender.MON": "MON", |
| | | "uni-calender.TUE": "TUE", |
| | | "uni-calender.WED": "WED", |
| | | "uni-calender.THU": "THU", |
| | | "uni-calender.FRI": "FRI", |
| | | "uni-calender.SAT": "SAT", |
| | | "uni-calender.SUN": "SUN", |
| | | "uni-calender.confirm": "confirm" |
| | | } |
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 |
| | |
| | | { |
| | | "uni-datetime-picker.selectDate": "选择日期", |
| | | "uni-datetime-picker.selectTime": "选择时间", |
| | | "uni-datetime-picker.selectDateTime": "选择日期时间", |
| | | "uni-datetime-picker.startDate": "开始日期", |
| | | "uni-datetime-picker.endDate": "结束日期", |
| | | "uni-datetime-picker.startTime": "开始时间", |
| | | "uni-datetime-picker.endTime": "结束时间", |
| | | "uni-datetime-picker.ok": "确定", |
| | | "uni-datetime-picker.clear": "清除", |
| | | "uni-datetime-picker.cancel": "取消", |
| | | "uni-datetime-picker.year": "年", |
| | | "uni-datetime-picker.month": "月", |
| | | "uni-calender.SUN": "日", |
| | | "uni-calender.MON": "一", |
| | | "uni-calender.TUE": "二", |
| | | "uni-calender.WED": "三", |
| | | "uni-calender.THU": "四", |
| | | "uni-calender.FRI": "五", |
| | | "uni-calender.SAT": "六", |
| | | "uni-calender.confirm": "确认" |
| | | } |
New file |
| | |
| | | { |
| | | "uni-datetime-picker.selectDate": "選擇日期", |
| | | "uni-datetime-picker.selectTime": "選擇時間", |
| | | "uni-datetime-picker.selectDateTime": "選擇日期時間", |
| | | "uni-datetime-picker.startDate": "開始日期", |
| | | "uni-datetime-picker.endDate": "結束日期", |
| | | "uni-datetime-picker.startTime": "開始时间", |
| | | "uni-datetime-picker.endTime": "結束时间", |
| | | "uni-datetime-picker.ok": "確定", |
| | | "uni-datetime-picker.clear": "清除", |
| | | "uni-datetime-picker.cancel": "取消", |
| | | "uni-datetime-picker.year": "年", |
| | | "uni-datetime-picker.month": "月", |
| | | "uni-calender.SUN": "日", |
| | | "uni-calender.MON": "一", |
| | | "uni-calender.TUE": "二", |
| | | "uni-calender.WED": "三", |
| | | "uni-calender.THU": "四", |
| | | "uni-calender.FRI": "五", |
| | | "uni-calender.SAT": "六", |
| | | "uni-calender.confirm": "確認" |
| | | } |
New file |
| | |
| | | <template> |
| | | <view class="uni-datetime-picker"> |
| | | <view @click="initTimePicker"> |
| | | <slot> |
| | | <view class="uni-datetime-picker-timebox-pointer" |
| | | :class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}"> |
| | | <text class="uni-datetime-picker-text">{{time}}</text> |
| | | <view v-if="!time" class="uni-datetime-picker-time"> |
| | | <text class="uni-datetime-picker-text">{{selectTimeText}}</text> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view> |
| | | <view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']" |
| | | :style="fixNvueBug"> |
| | | <view class="uni-title"> |
| | | <text class="uni-datetime-picker-text">{{selectTimeText}}</text> |
| | | </view> |
| | | <view v-if="dateShow" class="uni-datetime-picker__container-box"> |
| | | <picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd" |
| | | @change="bindDateChange"> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | </picker-view> |
| | | <!-- 兼容 nvue 不支持伪类 --> |
| | | <text class="uni-datetime-picker-sign sign-left">-</text> |
| | | <text class="uni-datetime-picker-sign sign-right">-</text> |
| | | </view> |
| | | <view v-if="timeShow" class="uni-datetime-picker__container-box"> |
| | | <picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']" |
| | | :indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange"> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column v-if="!hideSecond"> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | </picker-view> |
| | | <!-- 兼容 nvue 不支持伪类 --> |
| | | <text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text> |
| | | <text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text> |
| | | </view> |
| | | <view class="uni-datetime-picker-btn"> |
| | | <view @click="clearTime"> |
| | | <text class="uni-datetime-picker-btn-text">{{clearText}}</text> |
| | | </view> |
| | | <view class="uni-datetime-picker-btn-group"> |
| | | <view class="uni-datetime-picker-cancel" @click="tiggerTimePicker"> |
| | | <text class="uni-datetime-picker-btn-text">{{cancelText}}</text> |
| | | </view> |
| | | <view @click="setTime"> |
| | | <text class="uni-datetime-picker-btn-text">{{okText}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { initVueI18n } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | const { t } = initVueI18n(i18nMessages) |
| | | import { fixIosDateFormat } from './util' |
| | | |
| | | /** |
| | | * DatetimePicker 时间选择器 |
| | | * @description 可以同时选择日期和时间的选择器 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx |
| | | * @property {String} type = [datetime | date | time] 显示模式 |
| | | * @property {Boolean} multiple = [true|false] 是否多选 |
| | | * @property {String|Number} value 默认值 |
| | | * @property {String|Number} start 起始日期或时间 |
| | | * @property {String|Number} end 起始日期或时间 |
| | | * @property {String} return-type = [timestamp | string] |
| | | * @event {Function} change 选中发生变化触发 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniDatetimePicker', |
| | | data() { |
| | | return { |
| | | indicatorStyle: `height: 50px;`, |
| | | visible: false, |
| | | fixNvueBug: {}, |
| | | dateShow: true, |
| | | timeShow: true, |
| | | title: '日期和时间', |
| | | // 输入框当前时间 |
| | | time: '', |
| | | // 当前的年月日时分秒 |
| | | year: 1920, |
| | | month: 0, |
| | | day: 0, |
| | | hour: 0, |
| | | minute: 0, |
| | | second: 0, |
| | | // 起始时间 |
| | | startYear: 1920, |
| | | startMonth: 1, |
| | | startDay: 1, |
| | | startHour: 0, |
| | | startMinute: 0, |
| | | startSecond: 0, |
| | | // 结束时间 |
| | | endYear: 2120, |
| | | endMonth: 12, |
| | | endDay: 31, |
| | | endHour: 23, |
| | | endMinute: 59, |
| | | endSecond: 59, |
| | | } |
| | | }, |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: 'datetime' |
| | | }, |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | start: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | end: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | returnType: { |
| | | type: String, |
| | | default: 'string' |
| | | }, |
| | | disabled: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | border: { |
| | | type: [Boolean, String], |
| | | default: true |
| | | }, |
| | | hideSecond: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | } |
| | | }, |
| | | watch: { |
| | | // #ifndef VUE3 |
| | | value: { |
| | | handler(newVal) { |
| | | if (newVal) { |
| | | this.parseValue(fixIosDateFormat(newVal)) |
| | | this.initTime(false) |
| | | } else { |
| | | this.time = '' |
| | | this.parseValue(Date.now()) |
| | | } |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | handler(newVal) { |
| | | if (newVal) { |
| | | this.parseValue(fixIosDateFormat(newVal)) |
| | | this.initTime(false) |
| | | } else { |
| | | this.time = '' |
| | | this.parseValue(Date.now()) |
| | | } |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | type: { |
| | | handler(newValue) { |
| | | if (newValue === 'date') { |
| | | this.dateShow = true |
| | | this.timeShow = false |
| | | this.title = '日期' |
| | | } else if (newValue === 'time') { |
| | | this.dateShow = false |
| | | this.timeShow = true |
| | | this.title = '时间' |
| | | } else { |
| | | this.dateShow = true |
| | | this.timeShow = true |
| | | this.title = '日期和时间' |
| | | } |
| | | }, |
| | | immediate: true |
| | | }, |
| | | start: { |
| | | handler(newVal) { |
| | | this.parseDatetimeRange(fixIosDateFormat(newVal), 'start') |
| | | }, |
| | | immediate: true |
| | | }, |
| | | end: { |
| | | handler(newVal) { |
| | | this.parseDatetimeRange(fixIosDateFormat(newVal), 'end') |
| | | }, |
| | | immediate: true |
| | | }, |
| | | |
| | | // 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项 |
| | | months(newVal) { |
| | | this.checkValue('month', this.month, newVal) |
| | | }, |
| | | days(newVal) { |
| | | this.checkValue('day', this.day, newVal) |
| | | }, |
| | | hours(newVal) { |
| | | this.checkValue('hour', this.hour, newVal) |
| | | }, |
| | | minutes(newVal) { |
| | | this.checkValue('minute', this.minute, newVal) |
| | | }, |
| | | seconds(newVal) { |
| | | this.checkValue('second', this.second, newVal) |
| | | } |
| | | }, |
| | | computed: { |
| | | // 当前年、月、日、时、分、秒选择范围 |
| | | years() { |
| | | return this.getCurrentRange('year') |
| | | }, |
| | | |
| | | months() { |
| | | return this.getCurrentRange('month') |
| | | }, |
| | | |
| | | days() { |
| | | return this.getCurrentRange('day') |
| | | }, |
| | | |
| | | hours() { |
| | | return this.getCurrentRange('hour') |
| | | }, |
| | | |
| | | minutes() { |
| | | return this.getCurrentRange('minute') |
| | | }, |
| | | |
| | | seconds() { |
| | | return this.getCurrentRange('second') |
| | | }, |
| | | |
| | | // picker 当前值数组 |
| | | ymd() { |
| | | return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay] |
| | | }, |
| | | hms() { |
| | | return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond] |
| | | }, |
| | | |
| | | // 当前 date 是 start |
| | | currentDateIsStart() { |
| | | return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay |
| | | }, |
| | | |
| | | // 当前 date 是 end |
| | | currentDateIsEnd() { |
| | | return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay |
| | | }, |
| | | |
| | | // 当前年、月、日、时、分、秒的最小值和最大值 |
| | | minYear() { |
| | | return this.startYear |
| | | }, |
| | | maxYear() { |
| | | return this.endYear |
| | | }, |
| | | minMonth() { |
| | | if (this.year === this.startYear) { |
| | | return this.startMonth |
| | | } else { |
| | | return 1 |
| | | } |
| | | }, |
| | | maxMonth() { |
| | | if (this.year === this.endYear) { |
| | | return this.endMonth |
| | | } else { |
| | | return 12 |
| | | } |
| | | }, |
| | | minDay() { |
| | | if (this.year === this.startYear && this.month === this.startMonth) { |
| | | return this.startDay |
| | | } else { |
| | | return 1 |
| | | } |
| | | }, |
| | | maxDay() { |
| | | if (this.year === this.endYear && this.month === this.endMonth) { |
| | | return this.endDay |
| | | } else { |
| | | return this.daysInMonth(this.year, this.month) |
| | | } |
| | | }, |
| | | minHour() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsStart) { |
| | | return this.startHour |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | return this.startHour |
| | | } |
| | | }, |
| | | maxHour() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsEnd) { |
| | | return this.endHour |
| | | } else { |
| | | return 23 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | return this.endHour |
| | | } |
| | | }, |
| | | minMinute() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsStart && this.hour === this.startHour) { |
| | | return this.startMinute |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.startHour) { |
| | | return this.startMinute |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | }, |
| | | maxMinute() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsEnd && this.hour === this.endHour) { |
| | | return this.endMinute |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.endHour) { |
| | | return this.endMinute |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | }, |
| | | minSecond() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) { |
| | | return this.startSecond |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.startHour && this.minute === this.startMinute) { |
| | | return this.startSecond |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | }, |
| | | maxSecond() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) { |
| | | return this.endSecond |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.endHour && this.minute === this.endMinute) { |
| | | return this.endSecond |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * for i18n |
| | | */ |
| | | selectTimeText() { |
| | | return t("uni-datetime-picker.selectTime") |
| | | }, |
| | | okText() { |
| | | return t("uni-datetime-picker.ok") |
| | | }, |
| | | clearText() { |
| | | return t("uni-datetime-picker.clear") |
| | | }, |
| | | cancelText() { |
| | | return t("uni-datetime-picker.cancel") |
| | | } |
| | | }, |
| | | |
| | | mounted() { |
| | | // #ifdef APP-NVUE |
| | | const res = uni.getSystemInfoSync(); |
| | | this.fixNvueBug = { |
| | | top: res.windowHeight / 2, |
| | | left: res.windowWidth / 2 |
| | | } |
| | | // #endif |
| | | }, |
| | | |
| | | methods: { |
| | | /** |
| | | * @param {Object} item |
| | | * 小于 10 在前面加个 0 |
| | | */ |
| | | |
| | | lessThanTen(item) { |
| | | return item < 10 ? '0' + item : item |
| | | }, |
| | | |
| | | /** |
| | | * 解析时分秒字符串,例如:00:00:00 |
| | | * @param {String} timeString |
| | | */ |
| | | parseTimeType(timeString) { |
| | | if (timeString) { |
| | | let timeArr = timeString.split(':') |
| | | this.hour = Number(timeArr[0]) |
| | | this.minute = Number(timeArr[1]) |
| | | this.second = Number(timeArr[2]) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000 |
| | | * @param {String | Number} datetime |
| | | */ |
| | | initPickerValue(datetime) { |
| | | let defaultValue = null |
| | | if (datetime) { |
| | | defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end) |
| | | } else { |
| | | defaultValue = Date.now() |
| | | defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end) |
| | | } |
| | | this.parseValue(defaultValue) |
| | | }, |
| | | |
| | | /** |
| | | * 初始值规则: |
| | | * - 用户设置初始值 value |
| | | * - 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start |
| | | * - 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start |
| | | * - 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end |
| | | * - 无起始终止时间,则初始值为 value |
| | | * - 无初始值 value,则初始值为当前本地时间 Date.now() |
| | | * @param {Object} value |
| | | * @param {Object} dateBase |
| | | */ |
| | | compareValueWithStartAndEnd(value, start, end) { |
| | | let winner = null |
| | | value = this.superTimeStamp(value) |
| | | start = this.superTimeStamp(start) |
| | | end = this.superTimeStamp(end) |
| | | |
| | | if (start && end) { |
| | | if (value < start) { |
| | | winner = new Date(start) |
| | | } else if (value > end) { |
| | | winner = new Date(end) |
| | | } else { |
| | | winner = new Date(value) |
| | | } |
| | | } else if (start && !end) { |
| | | winner = start <= value ? new Date(value) : new Date(start) |
| | | } else if (!start && end) { |
| | | winner = value <= end ? new Date(value) : new Date(end) |
| | | } else { |
| | | winner = new Date(value) |
| | | } |
| | | |
| | | return winner |
| | | }, |
| | | |
| | | /** |
| | | * 转换为可比较的时间戳,接受日期、时分秒、时间戳 |
| | | * @param {Object} value |
| | | */ |
| | | superTimeStamp(value) { |
| | | let dateBase = '' |
| | | if (this.type === 'time' && value && typeof value === 'string') { |
| | | const now = new Date() |
| | | const year = now.getFullYear() |
| | | const month = now.getMonth() + 1 |
| | | const day = now.getDate() |
| | | dateBase = year + '/' + month + '/' + day + ' ' |
| | | } |
| | | if (Number(value)) { |
| | | value = parseInt(value) |
| | | dateBase = 0 |
| | | } |
| | | return this.createTimeStamp(dateBase + value) |
| | | }, |
| | | |
| | | /** |
| | | * 解析默认值 value,字符串、时间戳 |
| | | * @param {Object} defaultTime |
| | | */ |
| | | parseValue(value) { |
| | | if (!value) { |
| | | return |
| | | } |
| | | if (this.type === 'time' && typeof value === "string") { |
| | | this.parseTimeType(value) |
| | | } else { |
| | | let defaultDate = null |
| | | defaultDate = new Date(value) |
| | | if (this.type !== 'time') { |
| | | this.year = defaultDate.getFullYear() |
| | | this.month = defaultDate.getMonth() + 1 |
| | | this.day = defaultDate.getDate() |
| | | } |
| | | if (this.type !== 'date') { |
| | | this.hour = defaultDate.getHours() |
| | | this.minute = defaultDate.getMinutes() |
| | | this.second = defaultDate.getSeconds() |
| | | } |
| | | } |
| | | if (this.hideSecond) { |
| | | this.second = 0 |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 解析可选择时间范围 start、end,年月日字符串、时间戳 |
| | | * @param {Object} defaultTime |
| | | */ |
| | | parseDatetimeRange(point, pointType) { |
| | | // 时间为空,则重置为初始值 |
| | | if (!point) { |
| | | if (pointType === 'start') { |
| | | this.startYear = 1920 |
| | | this.startMonth = 1 |
| | | this.startDay = 1 |
| | | this.startHour = 0 |
| | | this.startMinute = 0 |
| | | this.startSecond = 0 |
| | | } |
| | | if (pointType === 'end') { |
| | | this.endYear = 2120 |
| | | this.endMonth = 12 |
| | | this.endDay = 31 |
| | | this.endHour = 23 |
| | | this.endMinute = 59 |
| | | this.endSecond = 59 |
| | | } |
| | | return |
| | | } |
| | | if (this.type === 'time') { |
| | | const pointArr = point.split(':') |
| | | this[pointType + 'Hour'] = Number(pointArr[0]) |
| | | this[pointType + 'Minute'] = Number(pointArr[1]) |
| | | this[pointType + 'Second'] = Number(pointArr[2]) |
| | | } else { |
| | | if (!point) { |
| | | pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60 |
| | | return |
| | | } |
| | | if (Number(point)) { |
| | | point = parseInt(point) |
| | | } |
| | | // datetime 的 end 没有时分秒, 则不限制 |
| | | const hasTime = /[0-9]:[0-9]/ |
| | | if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test( |
| | | point)) { |
| | | point = point + ' 23:59:59' |
| | | } |
| | | const pointDate = new Date(point) |
| | | this[pointType + 'Year'] = pointDate.getFullYear() |
| | | this[pointType + 'Month'] = pointDate.getMonth() + 1 |
| | | this[pointType + 'Day'] = pointDate.getDate() |
| | | if (this.type === 'datetime') { |
| | | this[pointType + 'Hour'] = pointDate.getHours() |
| | | this[pointType + 'Minute'] = pointDate.getMinutes() |
| | | this[pointType + 'Second'] = pointDate.getSeconds() |
| | | } |
| | | } |
| | | }, |
| | | |
| | | // 获取 年、月、日、时、分、秒 当前可选范围 |
| | | getCurrentRange(value) { |
| | | const range = [] |
| | | for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) { |
| | | range.push(i) |
| | | } |
| | | return range |
| | | }, |
| | | |
| | | // 字符串首字母大写 |
| | | capitalize(str) { |
| | | return str.charAt(0).toUpperCase() + str.slice(1) |
| | | }, |
| | | |
| | | // 检查当前值是否在范围内,不在则当前值重置为可选范围第一项 |
| | | checkValue(name, value, values) { |
| | | if (values.indexOf(value) === -1) { |
| | | this[name] = values[0] |
| | | } |
| | | }, |
| | | |
| | | // 每个月的实际天数 |
| | | daysInMonth(year, month) { // Use 1 for January, 2 for February, etc. |
| | | return new Date(year, month, 0).getDate(); |
| | | }, |
| | | |
| | | //兼容 iOS、safari 日期格式 |
| | | fixIosDateFormat(value) { |
| | | if (typeof value === 'string') { |
| | | value = value.replace(/-/g, '/') |
| | | } |
| | | return value |
| | | }, |
| | | |
| | | /** |
| | | * 生成时间戳 |
| | | * @param {Object} time |
| | | */ |
| | | createTimeStamp(time) { |
| | | if (!time) return |
| | | if (typeof time === "number") { |
| | | return time |
| | | } else { |
| | | time = time.replace(/-/g, '/') |
| | | if (this.type === 'date') { |
| | | time = time + ' ' + '00:00:00' |
| | | } |
| | | return Date.parse(time) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 生成日期或时间的字符串 |
| | | */ |
| | | createDomSting() { |
| | | const yymmdd = this.year + |
| | | '-' + |
| | | this.lessThanTen(this.month) + |
| | | '-' + |
| | | this.lessThanTen(this.day) |
| | | |
| | | let hhmmss = this.lessThanTen(this.hour) + |
| | | ':' + |
| | | this.lessThanTen(this.minute) |
| | | |
| | | if (!this.hideSecond) { |
| | | hhmmss = hhmmss + ':' + this.lessThanTen(this.second) |
| | | } |
| | | |
| | | if (this.type === 'date') { |
| | | return yymmdd |
| | | } else if (this.type === 'time') { |
| | | return hhmmss |
| | | } else { |
| | | return yymmdd + ' ' + hhmmss |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 初始化返回值,并抛出 change 事件 |
| | | */ |
| | | initTime(emit = true) { |
| | | this.time = this.createDomSting() |
| | | if (!emit) return |
| | | if (this.returnType === 'timestamp' && this.type !== 'time') { |
| | | this.$emit('change', this.createTimeStamp(this.time)) |
| | | this.$emit('input', this.createTimeStamp(this.time)) |
| | | this.$emit('update:modelValue', this.createTimeStamp(this.time)) |
| | | } else { |
| | | this.$emit('change', this.time) |
| | | this.$emit('input', this.time) |
| | | this.$emit('update:modelValue', this.time) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 用户选择日期或时间更新 data |
| | | * @param {Object} e |
| | | */ |
| | | bindDateChange(e) { |
| | | const val = e.detail.value |
| | | this.year = this.years[val[0]] |
| | | this.month = this.months[val[1]] |
| | | this.day = this.days[val[2]] |
| | | }, |
| | | bindTimeChange(e) { |
| | | const val = e.detail.value |
| | | this.hour = this.hours[val[0]] |
| | | this.minute = this.minutes[val[1]] |
| | | this.second = this.seconds[val[2]] |
| | | }, |
| | | |
| | | /** |
| | | * 初始化弹出层 |
| | | */ |
| | | initTimePicker() { |
| | | if (this.disabled) return |
| | | const value = fixIosDateFormat(this.time) |
| | | this.initPickerValue(value) |
| | | this.visible = !this.visible |
| | | }, |
| | | |
| | | /** |
| | | * 触发或关闭弹框 |
| | | */ |
| | | tiggerTimePicker(e) { |
| | | this.visible = !this.visible |
| | | }, |
| | | |
| | | /** |
| | | * 用户点击“清空”按钮,清空当前值 |
| | | */ |
| | | clearTime() { |
| | | this.time = '' |
| | | this.$emit('change', this.time) |
| | | this.$emit('input', this.time) |
| | | this.$emit('update:modelValue', this.time) |
| | | this.tiggerTimePicker() |
| | | }, |
| | | |
| | | /** |
| | | * 用户点击“确定”按钮 |
| | | */ |
| | | setTime() { |
| | | this.initTime() |
| | | this.tiggerTimePicker() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-datetime-picker { |
| | | /* #ifndef APP-NVUE */ |
| | | /* width: 100%; */ |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-view { |
| | | height: 130px; |
| | | width: 270px; |
| | | /* #ifndef APP-NVUE */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-item { |
| | | height: 50px; |
| | | line-height: 50px; |
| | | text-align: center; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn { |
| | | margin-top: 60px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn-text { |
| | | font-size: 14px; |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn-group { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-datetime-picker-cancel { |
| | | margin-right: 30px; |
| | | } |
| | | |
| | | .uni-datetime-picker-mask { |
| | | position: fixed; |
| | | bottom: 0px; |
| | | top: 0px; |
| | | left: 0px; |
| | | right: 0px; |
| | | background-color: rgba(0, 0, 0, 0.4); |
| | | transition-duration: 0.3s; |
| | | z-index: 998; |
| | | } |
| | | |
| | | .uni-datetime-picker-popup { |
| | | border-radius: 8px; |
| | | padding: 30px; |
| | | width: 270px; |
| | | /* #ifdef APP-NVUE */ |
| | | height: 500px; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | width: 330px; |
| | | /* #endif */ |
| | | background-color: #fff; |
| | | position: fixed; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%); |
| | | transition-duration: 0.3s; |
| | | z-index: 999; |
| | | } |
| | | |
| | | .fix-nvue-height { |
| | | /* #ifdef APP-NVUE */ |
| | | height: 330px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-time { |
| | | color: grey; |
| | | } |
| | | |
| | | .uni-datetime-picker-column { |
| | | height: 50px; |
| | | } |
| | | |
| | | .uni-datetime-picker-timebox { |
| | | |
| | | border: 1px solid #E5E5E5; |
| | | border-radius: 5px; |
| | | padding: 7px 10px; |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-timebox-pointer { |
| | | /* #ifndef APP-NVUE */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | |
| | | .uni-datetime-picker-disabled { |
| | | opacity: 0.4; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed !important; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-text { |
| | | font-size: 14px; |
| | | line-height: 50px |
| | | } |
| | | |
| | | .uni-datetime-picker-sign { |
| | | position: absolute; |
| | | top: 53px; |
| | | /* 减掉 10px 的元素高度,兼容nvue */ |
| | | color: #999; |
| | | /* #ifdef APP-NVUE */ |
| | | font-size: 16px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .sign-left { |
| | | left: 86px; |
| | | } |
| | | |
| | | .sign-right { |
| | | right: 86px; |
| | | } |
| | | |
| | | .sign-center { |
| | | left: 135px; |
| | | } |
| | | |
| | | .uni-datetime-picker__container-box { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-top: 40px; |
| | | } |
| | | |
| | | .time-hide-second { |
| | | width: 180px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-date"> |
| | | <view class="uni-date-editor" @click="show"> |
| | | <slot> |
| | | <view |
| | | class="uni-date-editor--x" |
| | | :class="{'uni-date-editor--x__disabled': disabled,'uni-date-x--border': border}" |
| | | > |
| | | <view v-if="!isRange" class="uni-date-x uni-date-single"> |
| | | <uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons> |
| | | <view class="uni-date__x-input">{{ displayValue || singlePlaceholderText }}</view> |
| | | </view> |
| | | |
| | | <view v-else class="uni-date-x uni-date-range"> |
| | | <uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons> |
| | | <view class="uni-date__x-input text-center">{{ displayRangeValue.startDate || startPlaceholderText }}</view> |
| | | |
| | | <view class="range-separator">{{rangeSeparator}}</view> |
| | | |
| | | <view class="uni-date__x-input text-center">{{ displayRangeValue.endDate || endPlaceholderText }}</view> |
| | | </view> |
| | | |
| | | <view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear"> |
| | | <uni-icons type="clear" color="#c0c4cc" size="22"></uni-icons> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | |
| | | <view v-show="pickerVisible" class="uni-date-mask--pc" @click="close"></view> |
| | | |
| | | <view v-if="!isPhone" v-show="pickerVisible" ref="datePicker" class="uni-date-picker__container"> |
| | | <view v-if="!isRange" class="uni-date-single--x" :style="pickerPositionStyle"> |
| | | <view class="uni-popper__arrow"></view> |
| | | |
| | | <view v-if="hasTime" class="uni-date-changed popup-x-header"> |
| | | <input class="uni-date__input text-center" type="text" v-model="inputDate" |
| | | :placeholder="selectDateText" /> |
| | | |
| | | <time-picker type="time" v-model="pickerTime" :border="false" :disabled="!inputDate" |
| | | :start="timepickerStartTime" :end="timepickerEndTime" :hideSecond="hideSecond" style="width: 100%;"> |
| | | <input class="uni-date__input text-center" type="text" v-model="pickerTime" :placeholder="selectTimeText" |
| | | :disabled="!inputDate" /> |
| | | </time-picker> |
| | | </view> |
| | | |
| | | <Calendar ref="pcSingle" :showMonth="false" :start-date="calendarRange.startDate" |
| | | :end-date="calendarRange.endDate" :date="calendarDate" @change="singleChange" |
| | | :default-value="defaultValue" |
| | | style="padding: 0 8px;" /> |
| | | |
| | | <view v-if="hasTime" class="popup-x-footer"> |
| | | <text class="confirm-text" @click="confirmSingleChange">{{okText}}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="uni-date-range--x" :style="pickerPositionStyle"> |
| | | <view class="uni-popper__arrow"></view> |
| | | <view v-if="hasTime" class="popup-x-header uni-date-changed"> |
| | | <view class="popup-x-header--datetime"> |
| | | <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate" |
| | | :placeholder="startDateText" /> |
| | | |
| | | <time-picker type="time" v-model="tempRange.startTime" :start="timepickerStartTime" :border="false" |
| | | :disabled="!tempRange.startDate" :hideSecond="hideSecond"> |
| | | <input class="uni-date__input uni-date-range__input" type="text" |
| | | v-model="tempRange.startTime" :placeholder="startTimeText" |
| | | :disabled="!tempRange.startDate" /> |
| | | </time-picker> |
| | | </view> |
| | | |
| | | <uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons> |
| | | |
| | | <view class="popup-x-header--datetime"> |
| | | <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate" |
| | | :placeholder="endDateText" /> |
| | | |
| | | <time-picker type="time" v-model="tempRange.endTime" :end="timepickerEndTime" :border="false" |
| | | :disabled="!tempRange.endDate" :hideSecond="hideSecond"> |
| | | <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime" |
| | | :placeholder="endTimeText" :disabled="!tempRange.endDate" /> |
| | | </time-picker> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="popup-x-body"> |
| | | <Calendar ref="left" :showMonth="false" :start-date="calendarRange.startDate" |
| | | :end-date="calendarRange.endDate" :range="true" :pleStatus="endMultipleStatus" |
| | | @change="leftChange" @firstEnterCale="updateRightCale" style="padding: 0 8px;" /> |
| | | <Calendar ref="right" :showMonth="false" :start-date="calendarRange.startDate" |
| | | :end-date="calendarRange.endDate" :range="true" @change="rightChange" |
| | | :pleStatus="startMultipleStatus" @firstEnterCale="updateLeftCale" |
| | | style="padding: 0 8px;border-left: 1px solid #F1F1F1;" /> |
| | | </view> |
| | | |
| | | <view v-if="hasTime" class="popup-x-footer"> |
| | | <text @click="clear">{{clearText}}</text> |
| | | <text class="confirm-text" @click="confirmRangeChange">{{okText}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <Calendar v-if="isPhone" ref="mobile" :clearDate="false" :date="calendarDate" :defTime="mobileCalendarTime" |
| | | :start-date="calendarRange.startDate" :end-date="calendarRange.endDate" :selectableTimes="mobSelectableTime" |
| | | :startPlaceholder="startPlaceholder" :endPlaceholder="endPlaceholder" |
| | | :default-value="defaultValue" |
| | | :pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :hasTime="hasTime" :insert="false" |
| | | :hideSecond="hideSecond" @confirm="mobileChange" @maskClose="close" /> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | /** |
| | | * DatetimePicker 时间选择器 |
| | | * @description 同时支持 PC 和移动端使用日历选择日期和日期范围 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3962 |
| | | * @property {String} type 选择器类型 |
| | | * @property {String|Number|Array|Date} value 绑定值 |
| | | * @property {String} placeholder 单选择时的占位内容 |
| | | * @property {String} start 起始时间 |
| | | * @property {String} end 终止时间 |
| | | * @property {String} start-placeholder 范围选择时开始日期的占位内容 |
| | | * @property {String} end-placeholder 范围选择时结束日期的占位内容 |
| | | * @property {String} range-separator 选择范围时的分隔符 |
| | | * @property {Boolean} border = [true|false] 是否有边框 |
| | | * @property {Boolean} disabled = [true|false] 是否禁用 |
| | | * @property {Boolean} clearIcon = [true|false] 是否显示清除按钮(仅PC端适用) |
| | | * @property {[String} defaultValue 选择器打开时默认显示的时间 |
| | | * @event {Function} change 确定日期时触发的事件 |
| | | * @event {Function} maskClick 点击遮罩层触发的事件 |
| | | * @event {Function} show 打开弹出层 |
| | | * @event {Function} close 关闭弹出层 |
| | | * @event {Function} clear 清除上次选中的状态和值 |
| | | **/ |
| | | import Calendar from './calendar.vue' |
| | | import TimePicker from './time-picker.vue' |
| | | import { initVueI18n } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | import { getDateTime, getDate, getTime, getDefaultSecond, dateCompare, checkDate, fixIosDateFormat } from './util' |
| | | |
| | | export default { |
| | | name: 'UniDatetimePicker', |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | components: { |
| | | Calendar, |
| | | TimePicker |
| | | }, |
| | | data() { |
| | | return { |
| | | isRange: false, |
| | | hasTime: false, |
| | | displayValue: '', |
| | | inputDate: '', |
| | | calendarDate: '', |
| | | pickerTime: '', |
| | | calendarRange: { |
| | | startDate: '', |
| | | startTime: '', |
| | | endDate: '', |
| | | endTime: '' |
| | | }, |
| | | displayRangeValue: { |
| | | startDate: '', |
| | | endDate: '', |
| | | }, |
| | | tempRange: { |
| | | startDate: '', |
| | | startTime: '', |
| | | endDate: '', |
| | | endTime: '' |
| | | }, |
| | | // 左右日历同步数据 |
| | | startMultipleStatus: { |
| | | before: '', |
| | | after: '', |
| | | data: [], |
| | | fulldate: '' |
| | | }, |
| | | endMultipleStatus: { |
| | | before: '', |
| | | after: '', |
| | | data: [], |
| | | fulldate: '' |
| | | }, |
| | | pickerVisible: false, |
| | | pickerPositionStyle: null, |
| | | isEmitValue: false, |
| | | isPhone: false, |
| | | isFirstShow: true, |
| | | i18nT: () => {} |
| | | } |
| | | }, |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: 'datetime' |
| | | }, |
| | | value: { |
| | | type: [String, Number, Array, Date], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number, Array, Date], |
| | | default: '' |
| | | }, |
| | | start: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | end: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | returnType: { |
| | | type: String, |
| | | default: 'string' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | startPlaceholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | endPlaceholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | rangeSeparator: { |
| | | type: String, |
| | | default: '-' |
| | | }, |
| | | border: { |
| | | type: [Boolean], |
| | | default: true |
| | | }, |
| | | disabled: { |
| | | type: [Boolean], |
| | | default: false |
| | | }, |
| | | clearIcon: { |
| | | type: [Boolean], |
| | | default: true |
| | | }, |
| | | hideSecond: { |
| | | type: [Boolean], |
| | | default: false |
| | | }, |
| | | defaultValue: { |
| | | type: [String, Object, Array], |
| | | default: '' |
| | | } |
| | | }, |
| | | watch: { |
| | | type: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | this.hasTime = newVal.indexOf('time') !== -1 |
| | | this.isRange = newVal.indexOf('range') !== -1 |
| | | } |
| | | }, |
| | | // #ifndef VUE3 |
| | | value: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (this.isEmitValue) { |
| | | this.isEmitValue = false |
| | | return |
| | | } |
| | | this.initPicker(newVal) |
| | | } |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (this.isEmitValue) { |
| | | this.isEmitValue = false |
| | | return |
| | | } |
| | | this.initPicker(newVal) |
| | | } |
| | | }, |
| | | // #endif |
| | | start: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (!newVal) return |
| | | this.calendarRange.startDate = getDate(newVal) |
| | | if (this.hasTime) { |
| | | this.calendarRange.startTime = getTime(newVal) |
| | | } |
| | | } |
| | | }, |
| | | end: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (!newVal) return |
| | | this.calendarRange.endDate = getDate(newVal) |
| | | if (this.hasTime) { |
| | | this.calendarRange.endTime = getTime(newVal, this.hideSecond) |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | computed: { |
| | | timepickerStartTime() { |
| | | const activeDate = this.isRange ? this.tempRange.startDate : this.inputDate |
| | | return activeDate === this.calendarRange.startDate ? this.calendarRange.startTime : '' |
| | | }, |
| | | timepickerEndTime() { |
| | | const activeDate = this.isRange ? this.tempRange.endDate : this.inputDate |
| | | return activeDate === this.calendarRange.endDate ? this.calendarRange.endTime : '' |
| | | }, |
| | | mobileCalendarTime() { |
| | | const timeRange = { |
| | | start: this.tempRange.startTime, |
| | | end: this.tempRange.endTime |
| | | } |
| | | return this.isRange ? timeRange : this.pickerTime |
| | | }, |
| | | mobSelectableTime() { |
| | | return { |
| | | start: this.calendarRange.startTime, |
| | | end: this.calendarRange.endTime |
| | | } |
| | | }, |
| | | datePopupWidth() { |
| | | // todo |
| | | return this.isRange ? 653 : 301 |
| | | }, |
| | | |
| | | /** |
| | | * for i18n |
| | | */ |
| | | singlePlaceholderText() { |
| | | return this.placeholder || (this.type === 'date' ? this.selectDateText : this.selectDateTimeText) |
| | | }, |
| | | startPlaceholderText() { |
| | | return this.startPlaceholder || this.startDateText |
| | | }, |
| | | endPlaceholderText() { |
| | | return this.endPlaceholder || this.endDateText |
| | | }, |
| | | selectDateText() { |
| | | return this.i18nT("uni-datetime-picker.selectDate") |
| | | }, |
| | | selectDateTimeText() { |
| | | return this.i18nT("uni-datetime-picker.selectDateTime") |
| | | }, |
| | | selectTimeText() { |
| | | return this.i18nT("uni-datetime-picker.selectTime") |
| | | }, |
| | | startDateText() { |
| | | return this.startPlaceholder || this.i18nT("uni-datetime-picker.startDate") |
| | | }, |
| | | startTimeText() { |
| | | return this.i18nT("uni-datetime-picker.startTime") |
| | | }, |
| | | endDateText() { |
| | | return this.endPlaceholder || this.i18nT("uni-datetime-picker.endDate") |
| | | }, |
| | | endTimeText() { |
| | | return this.i18nT("uni-datetime-picker.endTime") |
| | | }, |
| | | okText() { |
| | | return this.i18nT("uni-datetime-picker.ok") |
| | | }, |
| | | clearText() { |
| | | return this.i18nT("uni-datetime-picker.clear") |
| | | }, |
| | | showClearIcon() { |
| | | return this.clearIcon && !this.disabled && (this.displayValue || (this.displayRangeValue.startDate && this.displayRangeValue.endDate)) |
| | | } |
| | | }, |
| | | created() { |
| | | this.initI18nT() |
| | | this.platform() |
| | | }, |
| | | methods: { |
| | | initI18nT() { |
| | | const vueI18n = initVueI18n(i18nMessages) |
| | | this.i18nT = vueI18n.t |
| | | }, |
| | | initPicker(newVal) { |
| | | if ((!newVal && !this.defaultValue) || Array.isArray(newVal) && !newVal.length) { |
| | | this.$nextTick(() => { |
| | | this.clear(false) |
| | | }) |
| | | return |
| | | } |
| | | |
| | | if (!Array.isArray(newVal) && !this.isRange) { |
| | | if(newVal){ |
| | | this.displayValue = this.inputDate = this.calendarDate = getDate(newVal) |
| | | if (this.hasTime) { |
| | | this.pickerTime = getTime(newVal, this.hideSecond) |
| | | this.displayValue = `${this.displayValue} ${this.pickerTime}` |
| | | } |
| | | }else if(this.defaultValue){ |
| | | this.inputDate = this.calendarDate = getDate(this.defaultValue) |
| | | if(this.hasTime){ |
| | | this.pickerTime = getTime(this.defaultValue, this.hideSecond) |
| | | } |
| | | } |
| | | } else { |
| | | const [before, after] = newVal |
| | | if (!before && !after) return |
| | | const beforeDate = getDate(before) |
| | | const beforeTime = getTime(before, this.hideSecond) |
| | | |
| | | const afterDate = getDate(after) |
| | | const afterTime = getTime(after, this.hideSecond) |
| | | const startDate = beforeDate |
| | | const endDate = afterDate |
| | | this.displayRangeValue.startDate = this.tempRange.startDate = startDate |
| | | this.displayRangeValue.endDate = this.tempRange.endDate = endDate |
| | | |
| | | if (this.hasTime) { |
| | | this.displayRangeValue.startDate = `${beforeDate} ${beforeTime}` |
| | | this.displayRangeValue.endDate = `${afterDate} ${afterTime}` |
| | | this.tempRange.startTime = beforeTime |
| | | this.tempRange.endTime = afterTime |
| | | } |
| | | const defaultRange = { |
| | | before: beforeDate, |
| | | after: afterDate |
| | | } |
| | | this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, { |
| | | which: 'right' |
| | | }) |
| | | this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, { |
| | | which: 'left' |
| | | }) |
| | | } |
| | | }, |
| | | updateLeftCale(e) { |
| | | const left = this.$refs.left |
| | | // 设置范围选 |
| | | left.cale.setHoverMultiple(e.after) |
| | | left.setDate(this.$refs.left.nowDate.fullDate) |
| | | }, |
| | | updateRightCale(e) { |
| | | const right = this.$refs.right |
| | | // 设置范围选 |
| | | right.cale.setHoverMultiple(e.after) |
| | | right.setDate(this.$refs.right.nowDate.fullDate) |
| | | }, |
| | | platform() { |
| | | const { windowWidth } = uni.getSystemInfoSync() |
| | | this.isPhone = windowWidth <= 500 |
| | | this.windowWidth = windowWidth |
| | | }, |
| | | show() { |
| | | if (this.disabled) { |
| | | return |
| | | } |
| | | this.platform() |
| | | if (this.isPhone) { |
| | | this.$refs.mobile.open() |
| | | return |
| | | } |
| | | this.pickerPositionStyle = { |
| | | top: '10px' |
| | | } |
| | | const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor") |
| | | dateEditor.boundingClientRect(rect => { |
| | | if (this.windowWidth - rect.left < this.datePopupWidth) { |
| | | this.pickerPositionStyle.right = 0 |
| | | } |
| | | }).exec() |
| | | setTimeout(() => { |
| | | this.pickerVisible = !this.pickerVisible |
| | | if (!this.isPhone && this.isRange && this.isFirstShow) { |
| | | this.isFirstShow = false |
| | | const { |
| | | startDate, |
| | | endDate |
| | | } = this.calendarRange |
| | | if (startDate && endDate) { |
| | | if (this.diffDate(startDate, endDate) < 30) { |
| | | this.$refs.right.changeMonth('pre') |
| | | } |
| | | } else { |
| | | this.$refs.right.changeMonth('next') |
| | | this.$refs.right.cale.lastHover = false |
| | | } |
| | | } |
| | | |
| | | }, 50) |
| | | }, |
| | | close() { |
| | | setTimeout(() => { |
| | | this.pickerVisible = false |
| | | this.$emit('maskClick', this.value) |
| | | this.$refs.mobile && this.$refs.mobile.close() |
| | | }, 20) |
| | | }, |
| | | setEmit(value) { |
| | | if (this.returnType === "timestamp" || this.returnType === "date") { |
| | | if (!Array.isArray(value)) { |
| | | if (!this.hasTime) { |
| | | value = value + ' ' + '00:00:00' |
| | | } |
| | | value = this.createTimestamp(value) |
| | | if (this.returnType === "date") { |
| | | value = new Date(value) |
| | | } |
| | | } else { |
| | | if (!this.hasTime) { |
| | | value[0] = value[0] + ' ' + '00:00:00' |
| | | value[1] = value[1] + ' ' + '00:00:00' |
| | | } |
| | | value[0] = this.createTimestamp(value[0]) |
| | | value[1] = this.createTimestamp(value[1]) |
| | | if (this.returnType === "date") { |
| | | value[0] = new Date(value[0]) |
| | | value[1] = new Date(value[1]) |
| | | } |
| | | } |
| | | } |
| | | |
| | | this.$emit('update:modelValue', value) |
| | | this.$emit('input', value) |
| | | this.$emit('change', value) |
| | | this.isEmitValue = true |
| | | }, |
| | | createTimestamp(date) { |
| | | date = fixIosDateFormat(date) |
| | | return Date.parse(new Date(date)) |
| | | }, |
| | | singleChange(e) { |
| | | this.calendarDate = this.inputDate = e.fulldate |
| | | if (this.hasTime) return |
| | | this.confirmSingleChange() |
| | | }, |
| | | confirmSingleChange() { |
| | | if(!checkDate(this.inputDate)){ |
| | | const now = new Date() |
| | | this.calendarDate = this.inputDate = getDate(now) |
| | | this.pickerTime = getTime(now, this.hideSecond) |
| | | } |
| | | |
| | | let startLaterInputDate = false |
| | | let startDate, startTime |
| | | if(this.start) { |
| | | let startString = this.start |
| | | if(typeof this.start === 'number'){ |
| | | startString = getDateTime(this.start, this.hideSecond) |
| | | } |
| | | [startDate, startTime] = startString.split(' ') |
| | | if(this.start && !dateCompare(startDate, this.inputDate)) { |
| | | startLaterInputDate = true |
| | | this.inputDate = startDate |
| | | } |
| | | } |
| | | |
| | | let endEarlierInputDate = false |
| | | let endDate, endTime |
| | | if(this.end) { |
| | | let endString = this.end |
| | | if(typeof this.end === 'number'){ |
| | | endString = getDateTime(this.end, this.hideSecond) |
| | | } |
| | | [endDate, endTime] = endString.split(' ') |
| | | if(this.end && !dateCompare(this.inputDate, endDate)) { |
| | | endEarlierInputDate = true |
| | | this.inputDate = endDate |
| | | } |
| | | } |
| | | if (this.hasTime) { |
| | | if(startLaterInputDate){ |
| | | this.pickerTime = startTime || getDefaultSecond(this.hideSecond) |
| | | } |
| | | if(endEarlierInputDate){ |
| | | this.pickerTime = endTime || getDefaultSecond(this.hideSecond) |
| | | } |
| | | if(!this.pickerTime){ |
| | | this.pickerTime = getTime(Date.now(), this.hideSecond) |
| | | } |
| | | this.displayValue = `${this.inputDate} ${this.pickerTime}` |
| | | } else { |
| | | this.displayValue = this.inputDate |
| | | } |
| | | this.setEmit(this.displayValue) |
| | | this.pickerVisible = false |
| | | }, |
| | | leftChange(e) { |
| | | const { |
| | | before, |
| | | after |
| | | } = e.range |
| | | this.rangeChange(before, after) |
| | | const obj = { |
| | | before: e.range.before, |
| | | after: e.range.after, |
| | | data: e.range.data, |
| | | fulldate: e.fulldate |
| | | } |
| | | this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj) |
| | | }, |
| | | rightChange(e) { |
| | | const { |
| | | before, |
| | | after |
| | | } = e.range |
| | | this.rangeChange(before, after) |
| | | const obj = { |
| | | before: e.range.before, |
| | | after: e.range.after, |
| | | data: e.range.data, |
| | | fulldate: e.fulldate |
| | | } |
| | | this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj) |
| | | }, |
| | | mobileChange(e) { |
| | | if (this.isRange) { |
| | | const {before, after} = e.range |
| | | |
| | | if(!before || !after){ |
| | | return |
| | | } |
| | | |
| | | this.handleStartAndEnd(before, after, true) |
| | | if (this.hasTime) { |
| | | const { |
| | | startTime, |
| | | endTime |
| | | } = e.timeRange |
| | | this.tempRange.startTime = startTime |
| | | this.tempRange.endTime = endTime |
| | | } |
| | | this.confirmRangeChange() |
| | | } else { |
| | | if (this.hasTime) { |
| | | this.displayValue = e.fulldate + ' ' + e.time |
| | | } else { |
| | | this.displayValue = e.fulldate |
| | | } |
| | | this.setEmit(this.displayValue) |
| | | } |
| | | this.$refs.mobile.close() |
| | | }, |
| | | rangeChange(before, after) { |
| | | if (!(before && after)) return |
| | | this.handleStartAndEnd(before, after, true) |
| | | if (this.hasTime) return |
| | | this.confirmRangeChange() |
| | | }, |
| | | confirmRangeChange() { |
| | | if (!this.tempRange.startDate || !this.tempRange.endDate) { |
| | | this.pickerVisible = false |
| | | return |
| | | } |
| | | if(!checkDate(this.tempRange.startDate)){ |
| | | this.tempRange.startDate = getDate(Date.now()) |
| | | } |
| | | if(!checkDate(this.tempRange.endDate)){ |
| | | this.tempRange.endDate = getDate(Date.now()) |
| | | } |
| | | |
| | | let start, end |
| | | |
| | | let startDateLaterRangeStartDate = false |
| | | let startDateLaterRangeEndDate = false |
| | | let startDate, startTime |
| | | if(this.start) { |
| | | let startString = this.start |
| | | if(typeof this.start === 'number'){ |
| | | startString = getDateTime(this.start, this.hideSecond) |
| | | } |
| | | [startDate,startTime] = startString.split(' ') |
| | | if(this.start && !dateCompare(this.start, this.tempRange.startDate)) { |
| | | startDateLaterRangeStartDate = true |
| | | this.tempRange.startDate = startDate |
| | | } |
| | | if(this.start && !dateCompare(this.start, this.tempRange.endDate)) { |
| | | startDateLaterRangeEndDate = true |
| | | this.tempRange.endDate = startDate |
| | | } |
| | | } |
| | | let endDateEarlierRangeStartDate = false |
| | | let endDateEarlierRangeEndDate = false |
| | | let endDate, endTime |
| | | if(this.end) { |
| | | let endString = this.end |
| | | if(typeof this.end === 'number'){ |
| | | endString = getDateTime(this.end, this.hideSecond) |
| | | } |
| | | [endDate,endTime] = endString.split(' ') |
| | | |
| | | if(this.end && !dateCompare(this.tempRange.startDate, this.end)) { |
| | | endDateEarlierRangeStartDate = true |
| | | this.tempRange.startDate = endDate |
| | | } |
| | | if(this.end && !dateCompare(this.tempRange.endDate, this.end)) { |
| | | endDateEarlierRangeEndDate = true |
| | | this.tempRange.endDate = endDate |
| | | } |
| | | } |
| | | if (!this.hasTime) { |
| | | start = this.displayRangeValue.startDate = this.tempRange.startDate |
| | | end = this.displayRangeValue.endDate = this.tempRange.endDate |
| | | } else { |
| | | if(startDateLaterRangeStartDate){ |
| | | this.tempRange.startTime = startTime || getDefaultSecond(this.hideSecond) |
| | | }else if(endDateEarlierRangeStartDate){ |
| | | this.tempRange.startTime = endTime || getDefaultSecond(this.hideSecond) |
| | | } |
| | | if(!this.tempRange.startTime){ |
| | | this.tempRange.startTime = getTime(Date.now(), this.hideSecond) |
| | | } |
| | | |
| | | if(startDateLaterRangeEndDate){ |
| | | this.tempRange.endTime = startTime || getDefaultSecond(this.hideSecond) |
| | | }else if(endDateEarlierRangeEndDate){ |
| | | this.tempRange.endTime = endTime || getDefaultSecond(this.hideSecond) |
| | | } |
| | | if(!this.tempRange.endTime){ |
| | | this.tempRange.endTime = getTime(Date.now(), this.hideSecond) |
| | | } |
| | | start = this.displayRangeValue.startDate = `${this.tempRange.startDate} ${this.tempRange.startTime}` |
| | | end = this.displayRangeValue.endDate = `${this.tempRange.endDate} ${this.tempRange.endTime}` |
| | | } |
| | | if(!dateCompare(start,end)){ |
| | | [start, end] = [end, start] |
| | | } |
| | | this.displayRangeValue.startDate = start |
| | | this.displayRangeValue.endDate = end |
| | | const displayRange = [start, end] |
| | | this.setEmit(displayRange) |
| | | this.pickerVisible = false |
| | | }, |
| | | handleStartAndEnd(before, after, temp = false) { |
| | | if (!(before && after)) return |
| | | |
| | | const type = temp ? 'tempRange' : 'range' |
| | | const isStartEarlierEnd = dateCompare(before, after) |
| | | this[type].startDate = isStartEarlierEnd ? before : after |
| | | this[type].endDate = isStartEarlierEnd ? after : before |
| | | }, |
| | | /** |
| | | * 比较时间大小 |
| | | */ |
| | | dateCompare(startDate, endDate) { |
| | | // 计算截止时间 |
| | | startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
| | | // 计算详细项的截止时间 |
| | | endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
| | | return startDate <= endDate |
| | | }, |
| | | |
| | | /** |
| | | * 比较时间差 |
| | | */ |
| | | diffDate(startDate, endDate) { |
| | | // 计算截止时间 |
| | | startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
| | | // 计算详细项的截止时间 |
| | | endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
| | | const diff = (endDate - startDate) / (24 * 60 * 60 * 1000) |
| | | return Math.abs(diff) |
| | | }, |
| | | |
| | | clear(needEmit = true) { |
| | | if (!this.isRange) { |
| | | this.displayValue = '' |
| | | this.inputDate = '' |
| | | this.pickerTime = '' |
| | | if (this.isPhone) { |
| | | this.$refs.mobile && this.$refs.mobile.clearCalender() |
| | | } else { |
| | | this.$refs.pcSingle && this.$refs.pcSingle.clearCalender() |
| | | } |
| | | if (needEmit) { |
| | | this.$emit('change', '') |
| | | this.$emit('input', '') |
| | | this.$emit('update:modelValue', '') |
| | | } |
| | | } else { |
| | | this.displayRangeValue.startDate = '' |
| | | this.displayRangeValue.endDate = '' |
| | | this.tempRange.startDate = '' |
| | | this.tempRange.startTime = '' |
| | | this.tempRange.endDate = '' |
| | | this.tempRange.endTime = '' |
| | | if (this.isPhone) { |
| | | this.$refs.mobile && this.$refs.mobile.clearCalender() |
| | | } else { |
| | | this.$refs.left && this.$refs.left.clearCalender() |
| | | this.$refs.right && this.$refs.right.clearCalender() |
| | | this.$refs.right && this.$refs.right.changeMonth('next') |
| | | } |
| | | if (needEmit) { |
| | | this.$emit('change', []) |
| | | this.$emit('input', []) |
| | | this.$emit('update:modelValue', []) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-date { |
| | | width: 100%; |
| | | flex: 1; |
| | | } |
| | | .uni-date-x { |
| | | display: flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border-radius: 4px; |
| | | background-color: #fff; |
| | | color: #666; |
| | | font-size: 14px; |
| | | flex: 1; |
| | | |
| | | .icon-calendar{ |
| | | padding-left: 3px; |
| | | } |
| | | .range-separator{ |
| | | height: 35px; |
| | | /* #ifndef MP */ |
| | | padding: 0 2px; |
| | | /* #endif */ |
| | | line-height: 35px; |
| | | } |
| | | } |
| | | |
| | | .uni-date-x--border { |
| | | box-sizing: border-box; |
| | | border-radius: 4px; |
| | | border: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .uni-date-editor--x { |
| | | display: flex; |
| | | align-items: center; |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-date-editor--x .uni-date__icon-clear { |
| | | padding-right: 3px; |
| | | display: flex; |
| | | align-items: center; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-date__x-input { |
| | | width: auto; |
| | | height: 35px; |
| | | /* #ifndef MP */ |
| | | padding-left: 5px; |
| | | /* #endif */ |
| | | position: relative; |
| | | flex: 1; |
| | | line-height: 35px; |
| | | font-size: 14px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .text-center { |
| | | text-align: center; |
| | | } |
| | | |
| | | .uni-date__input { |
| | | height: 40px; |
| | | width: 100%; |
| | | line-height: 40px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-date-range__input { |
| | | text-align: center; |
| | | max-width: 142px; |
| | | } |
| | | |
| | | .uni-date-picker__container { |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-date-mask--pc { |
| | | position: fixed; |
| | | bottom: 0px; |
| | | top: 0px; |
| | | left: 0px; |
| | | right: 0px; |
| | | background-color: rgba(0, 0, 0, 0); |
| | | transition-duration: 0.3s; |
| | | z-index: 996; |
| | | } |
| | | |
| | | .uni-date-single--x { |
| | | background-color: #fff; |
| | | position: absolute; |
| | | top: 0; |
| | | z-index: 999; |
| | | border: 1px solid #EBEEF5; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .uni-date-range--x { |
| | | background-color: #fff; |
| | | position: absolute; |
| | | top: 0; |
| | | z-index: 999; |
| | | border: 1px solid #EBEEF5; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .uni-date-editor--x__disabled { |
| | | opacity: 0.4; |
| | | cursor: default; |
| | | } |
| | | |
| | | .uni-date-editor--logo { |
| | | width: 16px; |
| | | height: 16px; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | /* 添加时间 */ |
| | | .popup-x-header { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .popup-x-header--datetime { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex: 1; |
| | | } |
| | | |
| | | .popup-x-body { |
| | | display: flex; |
| | | } |
| | | |
| | | .popup-x-footer { |
| | | padding: 0 15px; |
| | | border-top-color: #F1F1F1; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | line-height: 40px; |
| | | text-align: right; |
| | | color: #666; |
| | | } |
| | | |
| | | .popup-x-footer text:hover { |
| | | color: $uni-primary; |
| | | cursor: pointer; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .popup-x-footer .confirm-text { |
| | | margin-left: 20px; |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .uni-date-changed { |
| | | text-align: center; |
| | | color: #333; |
| | | border-bottom-color: #F1F1F1; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | |
| | | .uni-date-changed--time text { |
| | | height: 50px; |
| | | line-height: 50px; |
| | | } |
| | | |
| | | .uni-date-changed .uni-date-changed--time { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-date-changed--time-date { |
| | | color: #333; |
| | | opacity: 0.6; |
| | | } |
| | | |
| | | .mr-50 { |
| | | margin-right: 50px; |
| | | } |
| | | |
| | | /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */ |
| | | .uni-popper__arrow, |
| | | .uni-popper__arrow::after { |
| | | position: absolute; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border: 6px solid transparent; |
| | | border-top-width: 0; |
| | | } |
| | | |
| | | .uni-popper__arrow { |
| | | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
| | | top: -6px; |
| | | left: 10%; |
| | | margin-right: 3px; |
| | | border-bottom-color: #EBEEF5; |
| | | } |
| | | |
| | | .uni-popper__arrow::after { |
| | | content: " "; |
| | | top: 1px; |
| | | margin-left: -6px; |
| | | border-bottom-color: #fff; |
| | | } |
| | | </style> |
New file |
| | |
| | | class Calendar { |
| | | constructor({ |
| | | selected, |
| | | startDate, |
| | | endDate, |
| | | range, |
| | | } = {}) { |
| | | // 当前日期 |
| | | this.date = this.getDateObj(new Date()) // 当前初入日期 |
| | | // 打点信息 |
| | | this.selected = selected || []; |
| | | // 起始时间 |
| | | this.startDate = startDate |
| | | // 终止时间 |
| | | this.endDate = endDate |
| | | // 是否范围选择 |
| | | this.range = range |
| | | // 多选状态 |
| | | this.cleanMultipleStatus() |
| | | // 每周日期 |
| | | this.weeks = {} |
| | | this.lastHover = false |
| | | } |
| | | /** |
| | | * 设置日期 |
| | | * @param {Object} date |
| | | */ |
| | | setDate(date) { |
| | | const selectDate = this.getDateObj(date) |
| | | this.getWeeks(selectDate.fullDate) |
| | | } |
| | | |
| | | /** |
| | | * 清理多选状态 |
| | | */ |
| | | cleanMultipleStatus() { |
| | | this.multipleStatus = { |
| | | before: '', |
| | | after: '', |
| | | data: [] |
| | | } |
| | | } |
| | | |
| | | setStartDate(startDate) { |
| | | this.startDate = startDate |
| | | } |
| | | |
| | | setEndDate(endDate) { |
| | | this.endDate = endDate |
| | | } |
| | | |
| | | getPreMonthObj(date){ |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | |
| | | const oldMonth = date.getMonth() |
| | | date.setMonth(oldMonth - 1) |
| | | const newMonth = date.getMonth() |
| | | if(oldMonth !== 0 && newMonth - oldMonth === 0){ |
| | | date.setMonth(newMonth - 1) |
| | | } |
| | | return this.getDateObj(date) |
| | | } |
| | | getNextMonthObj(date){ |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | |
| | | const oldMonth = date.getMonth() |
| | | date.setMonth(oldMonth + 1) |
| | | const newMonth = date.getMonth() |
| | | if(newMonth - oldMonth > 1){ |
| | | date.setMonth(newMonth - 1) |
| | | } |
| | | return this.getDateObj(date) |
| | | } |
| | | |
| | | /** |
| | | * 获取指定格式Date对象 |
| | | */ |
| | | getDateObj(date) { |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | |
| | | return { |
| | | fullDate: getDate(date), |
| | | year: date.getFullYear(), |
| | | month: addZero(date.getMonth() + 1), |
| | | date: addZero(date.getDate()), |
| | | day: date.getDay() |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取上一个月日期集合 |
| | | */ |
| | | getPreMonthDays(amount, dateObj) { |
| | | const result = [] |
| | | for (let i = amount - 1; i >= 0; i--) { |
| | | const month = dateObj.month - 1 |
| | | result.push({ |
| | | date: new Date(dateObj.year, month, -i).getDate(), |
| | | month, |
| | | disable: true |
| | | }) |
| | | } |
| | | return result |
| | | } |
| | | /** |
| | | * 获取本月日期集合 |
| | | */ |
| | | getCurrentMonthDays(amount, dateObj) { |
| | | const result = [] |
| | | const fullDate = this.date.fullDate |
| | | for (let i = 1; i <= amount; i++) { |
| | | const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}` |
| | | const isToday = fullDate === currentDate |
| | | // 获取打点信息 |
| | | const info = this.selected && this.selected.find((item) => { |
| | | if (this.dateEqual(currentDate, item.date)) { |
| | | return item |
| | | } |
| | | }) |
| | | |
| | | // 日期禁用 |
| | | let disableBefore = true |
| | | let disableAfter = true |
| | | if (this.startDate) { |
| | | disableBefore = dateCompare(this.startDate, currentDate) |
| | | } |
| | | |
| | | if (this.endDate) { |
| | | disableAfter = dateCompare(currentDate, this.endDate) |
| | | } |
| | | |
| | | let multiples = this.multipleStatus.data |
| | | let multiplesStatus = -1 |
| | | if (this.range && multiples) { |
| | | multiplesStatus = multiples.findIndex((item) => { |
| | | return this.dateEqual(item, currentDate) |
| | | }) |
| | | } |
| | | const checked = multiplesStatus !== -1 |
| | | |
| | | result.push({ |
| | | fullDate: currentDate, |
| | | year: dateObj.year, |
| | | date: i, |
| | | multiple: this.range ? checked : false, |
| | | beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after), |
| | | afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after), |
| | | month: dateObj.month, |
| | | disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare(currentDate,this.endDate)), |
| | | isToday, |
| | | userChecked: false, |
| | | extraInfo: info |
| | | }) |
| | | } |
| | | return result |
| | | } |
| | | /** |
| | | * 获取下一个月日期集合 |
| | | */ |
| | | _getNextMonthDays(amount, dateObj) { |
| | | const result = [] |
| | | const month = dateObj.month + 1 |
| | | for (let i = 1; i <= amount; i++) { |
| | | result.push({ |
| | | date: i, |
| | | month, |
| | | disable: true |
| | | }) |
| | | } |
| | | return result |
| | | } |
| | | |
| | | /** |
| | | * 获取当前日期详情 |
| | | * @param {Object} date |
| | | */ |
| | | getInfo(date) { |
| | | if (!date) { |
| | | date = new Date() |
| | | } |
| | | |
| | | return this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate) |
| | | } |
| | | |
| | | /** |
| | | * 比较时间是否相等 |
| | | */ |
| | | dateEqual(before, after) { |
| | | before = new Date(fixIosDateFormat(before)) |
| | | after = new Date(fixIosDateFormat(after)) |
| | | return before.valueOf() === after.valueOf() |
| | | } |
| | | |
| | | /** |
| | | * 比较真实起始日期 |
| | | */ |
| | | |
| | | isLogicBefore(currentDate, before, after) { |
| | | let logicBefore = before |
| | | if (before && after) { |
| | | logicBefore = dateCompare(before, after) ? before : after |
| | | } |
| | | return this.dateEqual(logicBefore, currentDate) |
| | | } |
| | | |
| | | isLogicAfter(currentDate, before, after) { |
| | | let logicAfter = after |
| | | if (before && after) { |
| | | logicAfter = dateCompare(before, after) ? after : before |
| | | } |
| | | return this.dateEqual(logicAfter, currentDate) |
| | | } |
| | | |
| | | /** |
| | | * 获取日期范围内所有日期 |
| | | * @param {Object} begin |
| | | * @param {Object} end |
| | | */ |
| | | geDateAll(begin, end) { |
| | | var arr = [] |
| | | var ab = begin.split('-') |
| | | var ae = end.split('-') |
| | | var db = new Date() |
| | | db.setFullYear(ab[0], ab[1] - 1, ab[2]) |
| | | var de = new Date() |
| | | de.setFullYear(ae[0], ae[1] - 1, ae[2]) |
| | | var unixDb = db.getTime() - 24 * 60 * 60 * 1000 |
| | | var unixDe = de.getTime() - 24 * 60 * 60 * 1000 |
| | | for (var k = unixDb; k <= unixDe;) { |
| | | k = k + 24 * 60 * 60 * 1000 |
| | | arr.push(this.getDateObj(new Date(parseInt(k))).fullDate) |
| | | } |
| | | return arr |
| | | } |
| | | |
| | | /** |
| | | * 获取多选状态 |
| | | */ |
| | | setMultiple(fullDate) { |
| | | if (!this.range) return |
| | | |
| | | let { |
| | | before, |
| | | after |
| | | } = this.multipleStatus |
| | | if (before && after) { |
| | | if (!this.lastHover) { |
| | | this.lastHover = true |
| | | return |
| | | } |
| | | this.multipleStatus.before = fullDate |
| | | this.multipleStatus.after = '' |
| | | this.multipleStatus.data = [] |
| | | this.multipleStatus.fulldate = '' |
| | | this.lastHover = false |
| | | } else { |
| | | if (!before) { |
| | | this.multipleStatus.before = fullDate |
| | | this.lastHover = false |
| | | } else { |
| | | this.multipleStatus.after = fullDate |
| | | if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus |
| | | .after); |
| | | } else { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus |
| | | .before); |
| | | } |
| | | this.lastHover = true |
| | | } |
| | | } |
| | | this.getWeeks(fullDate) |
| | | } |
| | | |
| | | /** |
| | | * 鼠标 hover 更新多选状态 |
| | | */ |
| | | setHoverMultiple(fullDate) { |
| | | if (!this.range || this.lastHover) return |
| | | |
| | | const { before } = this.multipleStatus |
| | | |
| | | if (!before) { |
| | | this.multipleStatus.before = fullDate |
| | | } else { |
| | | this.multipleStatus.after = fullDate |
| | | if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); |
| | | } else { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before); |
| | | } |
| | | } |
| | | this.getWeeks(fullDate) |
| | | } |
| | | |
| | | /** |
| | | * 更新默认值多选状态 |
| | | */ |
| | | setDefaultMultiple(before, after) { |
| | | this.multipleStatus.before = before |
| | | this.multipleStatus.after = after |
| | | if (before && after) { |
| | | if (dateCompare(before, after)) { |
| | | this.multipleStatus.data = this.geDateAll(before, after); |
| | | this.getWeeks(after) |
| | | } else { |
| | | this.multipleStatus.data = this.geDateAll(after, before); |
| | | this.getWeeks(before) |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取每周数据 |
| | | * @param {Object} dateData |
| | | */ |
| | | getWeeks(dateData) { |
| | | const { |
| | | year, |
| | | month, |
| | | } = this.getDateObj(dateData) |
| | | |
| | | const preMonthDayAmount = new Date(year, month - 1, 1).getDay() |
| | | const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData)) |
| | | |
| | | const currentMonthDayAmount = new Date(year, month, 0).getDate() |
| | | const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData)) |
| | | |
| | | const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount |
| | | const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData)) |
| | | |
| | | const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays] |
| | | |
| | | const weeks = new Array(6) |
| | | for (let i = 0; i < calendarDays.length; i++) { |
| | | const index = Math.floor(i / 7) |
| | | if(!weeks[index]){ |
| | | weeks[index] = new Array(7) |
| | | } |
| | | weeks[index][i % 7] = calendarDays[i] |
| | | } |
| | | |
| | | this.calendar = calendarDays |
| | | this.weeks = weeks |
| | | } |
| | | } |
| | | |
| | | function getDateTime(date, hideSecond){ |
| | | return `${getDate(date)} ${getTime(date, hideSecond)}` |
| | | } |
| | | |
| | | function getDate(date) { |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | const year = date.getFullYear() |
| | | const month = date.getMonth()+1 |
| | | const day = date.getDate() |
| | | return `${year}-${addZero(month)}-${addZero(day)}` |
| | | } |
| | | |
| | | function getTime(date, hideSecond){ |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | const hour = date.getHours() |
| | | const minute = date.getMinutes() |
| | | const second = date.getSeconds() |
| | | return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}` |
| | | } |
| | | |
| | | function addZero(num) { |
| | | if(num < 10){ |
| | | num = `0${num}` |
| | | } |
| | | return num |
| | | } |
| | | |
| | | function getDefaultSecond(hideSecond) { |
| | | return hideSecond ? '00:00' : '00:00:00' |
| | | } |
| | | |
| | | function dateCompare(startDate, endDate) { |
| | | startDate = new Date(fixIosDateFormat(startDate)) |
| | | endDate = new Date(fixIosDateFormat(endDate)) |
| | | return startDate <= endDate |
| | | } |
| | | |
| | | function checkDate(date){ |
| | | const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g |
| | | return date.match(dateReg) |
| | | } |
| | | |
| | | const dateTimeReg = /^\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])( [0-5][0-9]:[0-5][0-9]:[0-5][0-9])?$/ |
| | | function fixIosDateFormat(value) { |
| | | if (typeof value === 'string' && dateTimeReg.test(value)) { |
| | | value = value.replace(/-/g, '/') |
| | | } |
| | | return value |
| | | } |
| | | |
| | | export {Calendar, getDateTime, getDate, getTime, addZero, getDefaultSecond, dateCompare, checkDate, fixIosDateFormat} |
New file |
| | |
| | | { |
| | | "id": "uni-datetime-picker", |
| | | "displayName": "uni-datetime-picker 日期选择器", |
| | | "version": "2.2.22", |
| | | "description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择", |
| | | "keywords": [ |
| | | "uni-datetime-picker", |
| | | "uni-ui", |
| | | "uniui", |
| | | "日期时间选择器", |
| | | "日期时间" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-scss", |
| | | "uni-icons" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "n" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | > `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护` |
| | | |
| | | ## DatetimePicker 时间选择器 |
| | | |
| | | > **组件名:uni-datetime-picker** |
| | | > 代码块: `uDatetimePicker` |
| | | |
| | | |
| | | 该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。 |
| | | |
| | | 若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。 |
| | | |
| | | **_点击 picker 默认值规则:_** |
| | | |
| | | - 若设置初始值 value, 会显示在 picker 显示框中 |
| | | - 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.2.1(2021-11-22) |
| | | - 修复 vue3中个别scss变量无法找到的问题 |
| | | ## 1.2.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-drawer](https://uniapp.dcloud.io/component/uniui/uni-drawer) |
| | | ## 1.1.1(2021-07-30) |
| | | - 优化 vue3下事件警告的问题 |
| | | ## 1.1.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.0.7(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.0.6(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
New file |
| | |
| | | // #ifdef H5 |
| | | export default { |
| | | name: 'Keypress', |
| | | props: { |
| | | disable: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | mounted () { |
| | | const keyNames = { |
| | | esc: ['Esc', 'Escape'], |
| | | tab: 'Tab', |
| | | enter: 'Enter', |
| | | space: [' ', 'Spacebar'], |
| | | up: ['Up', 'ArrowUp'], |
| | | left: ['Left', 'ArrowLeft'], |
| | | right: ['Right', 'ArrowRight'], |
| | | down: ['Down', 'ArrowDown'], |
| | | delete: ['Backspace', 'Delete', 'Del'] |
| | | } |
| | | const listener = ($event) => { |
| | | if (this.disable) { |
| | | return |
| | | } |
| | | const keyName = Object.keys(keyNames).find(key => { |
| | | const keyName = $event.key |
| | | const value = keyNames[key] |
| | | return value === keyName || (Array.isArray(value) && value.includes(keyName)) |
| | | }) |
| | | if (keyName) { |
| | | // 避免和其他按键事件冲突 |
| | | setTimeout(() => { |
| | | this.$emit(keyName, {}) |
| | | }, 0) |
| | | } |
| | | } |
| | | document.addEventListener('keyup', listener) |
| | | // this.$once('hook:beforeDestroy', () => { |
| | | // document.removeEventListener('keyup', listener) |
| | | // }) |
| | | }, |
| | | render: () => {} |
| | | } |
| | | // #endif |
New file |
| | |
| | | <template> |
| | | <view v-if="visibleSync" :class="{ 'uni-drawer--visible': showDrawer }" class="uni-drawer" @touchmove.stop.prevent="clear"> |
| | | <view class="uni-drawer__mask" :class="{ 'uni-drawer__mask--visible': showDrawer && mask }" @tap="close('mask')" /> |
| | | <view class="uni-drawer__content" :class="{'uni-drawer--right': rightMode,'uni-drawer--left': !rightMode, 'uni-drawer__content--visible': showDrawer}" :style="{width:drawerWidth+'px'}"> |
| | | <slot /> |
| | | </view> |
| | | <!-- #ifdef H5 --> |
| | | <keypress @esc="close('mask')" /> |
| | | <!-- #endif --> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | // #ifdef H5 |
| | | import keypress from './keypress.js' |
| | | // #endif |
| | | /** |
| | | * Drawer 抽屉 |
| | | * @description 抽屉侧滑菜单 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=26 |
| | | * @property {Boolean} mask = [true | false] 是否显示遮罩 |
| | | * @property {Boolean} maskClick = [true | false] 点击遮罩是否关闭 |
| | | * @property {Boolean} mode = [left | right] Drawer 滑出位置 |
| | | * @value left 从左侧滑出 |
| | | * @value right 从右侧侧滑出 |
| | | * @property {Number} width 抽屉的宽度 ,仅 vue 页面生效 |
| | | * @event {Function} close 组件关闭时触发事件 |
| | | */ |
| | | export default { |
| | | name: 'UniDrawer', |
| | | components: { |
| | | // #ifdef H5 |
| | | keypress |
| | | // #endif |
| | | }, |
| | | emits:['change'], |
| | | props: { |
| | | /** |
| | | * 显示模式(左、右),只在初始化生效 |
| | | */ |
| | | mode: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | /** |
| | | * 蒙层显示状态 |
| | | */ |
| | | mask: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | /** |
| | | * 遮罩是否可点击关闭 |
| | | */ |
| | | maskClick:{ |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | /** |
| | | * 抽屉宽度 |
| | | */ |
| | | width: { |
| | | type: Number, |
| | | default: 220 |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | visibleSync: false, |
| | | showDrawer: false, |
| | | rightMode: false, |
| | | watchTimer: null, |
| | | drawerWidth: 220 |
| | | } |
| | | }, |
| | | created() { |
| | | // #ifndef APP-NVUE |
| | | this.drawerWidth = this.width |
| | | // #endif |
| | | this.rightMode = this.mode === 'right' |
| | | }, |
| | | methods: { |
| | | clear(){}, |
| | | close(type) { |
| | | // fixed by mehaotian 抽屉尚未完全关闭或遮罩禁止点击时不触发以下逻辑 |
| | | if((type === 'mask' && !this.maskClick) || !this.visibleSync) return |
| | | this._change('showDrawer', 'visibleSync', false) |
| | | }, |
| | | open() { |
| | | // fixed by mehaotian 处理重复点击打开的事件 |
| | | if(this.visibleSync) return |
| | | this._change('visibleSync', 'showDrawer', true) |
| | | }, |
| | | _change(param1, param2, status) { |
| | | this[param1] = status |
| | | if (this.watchTimer) { |
| | | clearTimeout(this.watchTimer) |
| | | } |
| | | this.watchTimer = setTimeout(() => { |
| | | this[param2] = status |
| | | this.$emit('change',status) |
| | | }, status ? 50 : 300) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | $uni-mask: rgba($color: #000000, $alpha: 0.4) ; |
| | | // 抽屉宽度 |
| | | $drawer-width: 220px; |
| | | |
| | | .uni-drawer { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | overflow: hidden; |
| | | z-index: 999; |
| | | } |
| | | |
| | | .uni-drawer__content { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | position: absolute; |
| | | top: 0; |
| | | width: $drawer-width; |
| | | bottom: 0; |
| | | background-color: $uni-bg-color; |
| | | transition: transform 0.3s ease; |
| | | } |
| | | |
| | | .uni-drawer--left { |
| | | left: 0; |
| | | /* #ifdef APP-NVUE */ |
| | | transform: translateX(-$drawer-width); |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | transform: translateX(-100%); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-drawer--right { |
| | | right: 0; |
| | | /* #ifdef APP-NVUE */ |
| | | transform: translateX($drawer-width); |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | transform: translateX(100%); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-drawer__content--visible { |
| | | transform: translateX(0px); |
| | | } |
| | | |
| | | |
| | | .uni-drawer__mask { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | opacity: 0; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | bottom: 0; |
| | | right: 0; |
| | | background-color: $uni-mask; |
| | | transition: opacity 0.3s; |
| | | } |
| | | |
| | | .uni-drawer__mask--visible { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | opacity: 1; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-drawer", |
| | | "displayName": "uni-drawer 抽屉", |
| | | "version": "1.2.1", |
| | | "description": "抽屉式导航,用于展示侧滑菜单,侧滑导航。", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "drawer", |
| | | "抽屉", |
| | | "侧滑导航" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ## Drawer 抽屉 |
| | | > **组件名:uni-drawer** |
| | | > 代码块: `uDrawer` |
| | | |
| | | 抽屉侧滑菜单。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-drawer) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.1.9(2023-04-11) |
| | | - 修复 vue3 下 keyboardheightchange 事件报错的bug |
| | | ## 1.1.8(2023-03-29) |
| | | - 优化 trim 属性默认值 |
| | | ## 1.1.7(2023-03-29) |
| | | - 新增 cursor-spacing 属性 |
| | | ## 1.1.6(2023-01-28) |
| | | - 新增 keyboardheightchange 事件,可监听键盘高度变化 |
| | | ## 1.1.5(2022-11-29) |
| | | - 优化 主题样式 |
| | | ## 1.1.4(2022-10-27) |
| | | - 修复 props 中背景颜色无默认值的bug |
| | | ## 1.1.0(2022-06-30) |
| | | |
| | | - 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容 |
| | | - 新增 clear 事件,点击右侧叉号图标触发 |
| | | - 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发 |
| | | - 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等 |
| | | |
| | | ## 1.0.5(2022-06-07) |
| | | |
| | | - 优化 clearable 显示策略 |
| | | |
| | | ## 1.0.4(2022-06-07) |
| | | |
| | | - 优化 clearable 显示策略 |
| | | |
| | | ## 1.0.3(2022-05-20) |
| | | |
| | | - 修复 关闭图标某些情况下无法取消的 bug |
| | | |
| | | ## 1.0.2(2022-04-12) |
| | | |
| | | - 修复 默认值不生效的 bug |
| | | |
| | | ## 1.0.1(2022-04-02) |
| | | |
| | | - 修复 value 不能为 0 的 bug |
| | | |
| | | ## 1.0.0(2021-11-19) |
| | | |
| | | - 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput) |
| | | |
| | | ## 0.1.4(2021-08-20) |
| | | |
| | | - 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug |
| | | |
| | | ## 0.1.3(2021-08-11) |
| | | |
| | | - 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 |
| | | |
| | | ## 0.1.2(2021-07-30) |
| | | |
| | | - 优化 vue3 下事件警告的问题 |
| | | |
| | | ## 0.1.1 |
| | | |
| | | - 优化 errorMessage 属性支持 Boolean 类型 |
| | | |
| | | ## 0.1.0(2021-07-13) |
| | | |
| | | - 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | |
| | | ## 0.0.16(2021-06-29) |
| | | |
| | | - 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug |
| | | |
| | | ## 0.0.15(2021-06-21) |
| | | |
| | | - 修复 passwordIcon 属性拼写错误的 bug |
| | | |
| | | ## 0.0.14(2021-06-18) |
| | | |
| | | - 新增 passwordIcon 属性,当 type=password 时是否显示小眼睛图标 |
| | | - 修复 confirmType 属性不生效的问题 |
| | | |
| | | ## 0.0.13(2021-06-04) |
| | | |
| | | - 修复 disabled 状态可清出内容的 bug |
| | | |
| | | ## 0.0.12(2021-05-12) |
| | | |
| | | - 新增 组件示例地址 |
| | | |
| | | ## 0.0.11(2021-05-07) |
| | | |
| | | - 修复 input-border 属性不生效的问题 |
| | | |
| | | ## 0.0.10(2021-04-30) |
| | | |
| | | - 修复 ios 遮挡文字、显示一半的问题 |
| | | |
| | | ## 0.0.9(2021-02-05) |
| | | |
| | | - 调整为 uni_modules 目录规范 |
| | | - 优化 兼容 nvue 页面 |
New file |
| | |
| | | /** |
| | | * @desc 函数防抖 |
| | | * @param func 目标函数 |
| | | * @param wait 延迟执行毫秒数 |
| | | * @param immediate true - 立即执行, false - 延迟执行 |
| | | */ |
| | | export const debounce = function(func, wait = 1000, immediate = true) { |
| | | let timer; |
| | | console.log(1); |
| | | return function() { |
| | | console.log(123); |
| | | let context = this, |
| | | args = arguments; |
| | | if (timer) clearTimeout(timer); |
| | | if (immediate) { |
| | | let callNow = !timer; |
| | | timer = setTimeout(() => { |
| | | timer = null; |
| | | }, wait); |
| | | if (callNow) func.apply(context, args); |
| | | } else { |
| | | timer = setTimeout(() => { |
| | | func.apply(context, args); |
| | | }, wait) |
| | | } |
| | | } |
| | | } |
| | | /** |
| | | * @desc 函数节流 |
| | | * @param func 函数 |
| | | * @param wait 延迟执行毫秒数 |
| | | * @param type 1 使用表时间戳,在时间段开始的时候触发 2 使用表定时器,在时间段结束的时候触发 |
| | | */ |
| | | export const throttle = (func, wait = 1000, type = 1) => { |
| | | let previous = 0; |
| | | let timeout; |
| | | return function() { |
| | | let context = this; |
| | | let args = arguments; |
| | | if (type === 1) { |
| | | let now = Date.now(); |
| | | |
| | | if (now - previous > wait) { |
| | | func.apply(context, args); |
| | | previous = now; |
| | | } |
| | | } else if (type === 2) { |
| | | if (!timeout) { |
| | | timeout = setTimeout(() => { |
| | | timeout = null; |
| | | func.apply(context, args) |
| | | }, wait) |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <view class="uni-easyinput" :class="{ 'uni-easyinput-error': msg }" :style="boxStyle"> |
| | | <view class="uni-easyinput__content" :class="inputContentClass" :style="inputContentStyle"> |
| | | <uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc" @click="onClickIcon('prefix')" size="22"></uni-icons> |
| | | <textarea |
| | | v-if="type === 'textarea'" |
| | | class="uni-easyinput__content-textarea" |
| | | :class="{ 'input-padding': inputBorder }" |
| | | :name="name" |
| | | :value="val" |
| | | :placeholder="placeholder" |
| | | :placeholderStyle="placeholderStyle" |
| | | :disabled="disabled" |
| | | placeholder-class="uni-easyinput__placeholder-class" |
| | | :maxlength="inputMaxlength" |
| | | :focus="focused" |
| | | :autoHeight="autoHeight" |
| | | :cursor-spacing="cursorSpacing" |
| | | @input="onInput" |
| | | @blur="_Blur" |
| | | @focus="_Focus" |
| | | @confirm="onConfirm" |
| | | @keyboardheightchange="onkeyboardheightchange" |
| | | ></textarea> |
| | | <input |
| | | v-else |
| | | :type="type === 'password' ? 'text' : type" |
| | | class="uni-easyinput__content-input" |
| | | :style="inputStyle" |
| | | :name="name" |
| | | :value="val" |
| | | :password="!showPassword && type === 'password'" |
| | | :placeholder="placeholder" |
| | | :placeholderStyle="placeholderStyle" |
| | | placeholder-class="uni-easyinput__placeholder-class" |
| | | :disabled="disabled" |
| | | :maxlength="inputMaxlength" |
| | | :focus="focused" |
| | | :confirmType="confirmType" |
| | | :cursor-spacing="cursorSpacing" |
| | | @focus="_Focus" |
| | | @blur="_Blur" |
| | | @input="onInput" |
| | | @confirm="onConfirm" |
| | | @keyboardheightchange="onkeyboardheightchange" |
| | | /> |
| | | <template v-if="type === 'password' && passwordIcon"> |
| | | <!-- 开启密码时显示小眼睛 --> |
| | | <uni-icons |
| | | v-if="isVal" |
| | | class="content-clear-icon" |
| | | :class="{ 'is-textarea-icon': type === 'textarea' }" |
| | | :type="showPassword ? 'eye-slash-filled' : 'eye-filled'" |
| | | :size="22" |
| | | :color="focusShow ? primaryColor : '#c0c4cc'" |
| | | @click="onEyes" |
| | | ></uni-icons> |
| | | </template> |
| | | <template v-else-if="suffixIcon"> |
| | | <uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc" @click="onClickIcon('suffix')" size="22"></uni-icons> |
| | | </template> |
| | | <template v-else> |
| | | <uni-icons |
| | | v-if="clearable && isVal && !disabled && type !== 'textarea'" |
| | | class="content-clear-icon" |
| | | :class="{ 'is-textarea-icon': type === 'textarea' }" |
| | | type="clear" |
| | | :size="clearSize" |
| | | :color="msg ? '#dd524d' : focusShow ? primaryColor : '#c0c4cc'" |
| | | @click="onClear" |
| | | ></uni-icons> |
| | | </template> |
| | | <slot name="right"></slot> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Easyinput 输入框 |
| | | * @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3455 |
| | | * @property {String} value 输入内容 |
| | | * @property {String } type 输入框的类型(默认text) password/text/textarea/.. |
| | | * @value text 文本输入键盘 |
| | | * @value textarea 多行文本输入键盘 |
| | | * @value password 密码输入键盘 |
| | | * @value number 数字输入键盘,注意iOS上app-vue弹出的数字键盘并非9宫格方式 |
| | | * @value idcard 身份证输入键盘,信、支付宝、百度、QQ小程序 |
| | | * @value digit 带小数点的数字键盘 ,App的nvue页面、微信、支付宝、百度、头条、QQ小程序支持 |
| | | * @property {Boolean} clearable 是否显示右侧清空内容的图标控件,点击可清空输入框内容(默认true) |
| | | * @property {Boolean} autoHeight 是否自动增高输入区域,type为textarea时有效(默认true) |
| | | * @property {String } placeholder 输入框的提示文字 |
| | | * @property {String } placeholderStyle placeholder的样式(内联样式,字符串),如"color: #ddd" |
| | | * @property {Boolean} focus 是否自动获得焦点(默认false) |
| | | * @property {Boolean} disabled 是否禁用(默认false) |
| | | * @property {Number } maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认140) |
| | | * @property {String } confirmType 设置键盘右下角按钮的文字,仅在type="text"时生效(默认done) |
| | | * @property {Number } clearSize 清除图标的大小,单位px(默认15) |
| | | * @property {String} prefixIcon 输入框头部图标 |
| | | * @property {String} suffixIcon 输入框尾部图标 |
| | | * @property {String} primaryColor 设置主题色(默认#2979ff) |
| | | * @property {Boolean} trim 是否自动去除两端的空格 |
| | | * @property {Boolean} cursorSpacing 指定光标与键盘的距离,单位 px |
| | | * @value both 去除两端空格 |
| | | * @value left 去除左侧空格 |
| | | * @value right 去除右侧空格 |
| | | * @value start 去除左侧空格 |
| | | * @value end 去除右侧空格 |
| | | * @value all 去除全部空格 |
| | | * @value none 不去除空格 |
| | | * @property {Boolean} inputBorder 是否显示input输入框的边框(默认true) |
| | | * @property {Boolean} passwordIcon type=password时是否显示小眼睛图标 |
| | | * @property {Object} styles 自定义颜色 |
| | | * @event {Function} input 输入框内容发生变化时触发 |
| | | * @event {Function} focus 输入框获得焦点时触发 |
| | | * @event {Function} blur 输入框失去焦点时触发 |
| | | * @event {Function} confirm 点击完成按钮时触发 |
| | | * @event {Function} iconClick 点击图标时触发 |
| | | * @example <uni-easyinput v-model="mobile"></uni-easyinput> |
| | | */ |
| | | function obj2strClass(obj) { |
| | | let classess = ''; |
| | | for (let key in obj) { |
| | | const val = obj[key]; |
| | | if (val) { |
| | | classess += `${key} `; |
| | | } |
| | | } |
| | | return classess; |
| | | } |
| | | |
| | | function obj2strStyle(obj) { |
| | | let style = ''; |
| | | for (let key in obj) { |
| | | const val = obj[key]; |
| | | style += `${key}:${val};`; |
| | | } |
| | | return style; |
| | | } |
| | | export default { |
| | | name: 'uni-easyinput', |
| | | emits: ['click', 'iconClick', 'update:modelValue', 'input', 'focus', 'blur', 'confirm', 'clear', 'eyes', 'change', 'keyboardheightchange'], |
| | | model: { |
| | | prop: 'modelValue', |
| | | event: 'update:modelValue' |
| | | }, |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | inject: { |
| | | form: { |
| | | from: 'uniForm', |
| | | default: null |
| | | }, |
| | | formItem: { |
| | | from: 'uniFormItem', |
| | | default: null |
| | | } |
| | | }, |
| | | props: { |
| | | name: String, |
| | | value: [Number, String], |
| | | modelValue: [Number, String], |
| | | type: { |
| | | type: String, |
| | | default: 'text' |
| | | }, |
| | | clearable: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | autoHeight: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: ' ' |
| | | }, |
| | | placeholderStyle: String, |
| | | focus: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | maxlength: { |
| | | type: [Number, String], |
| | | default: 140 |
| | | }, |
| | | confirmType: { |
| | | type: String, |
| | | default: 'done' |
| | | }, |
| | | clearSize: { |
| | | type: [Number, String], |
| | | default: 24 |
| | | }, |
| | | inputBorder: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | prefixIcon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | suffixIcon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | trim: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | cursorSpacing: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | passwordIcon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | primaryColor: { |
| | | type: String, |
| | | default: '#2979ff' |
| | | }, |
| | | styles: { |
| | | type: Object, |
| | | default() { |
| | | return { |
| | | color: '#333', |
| | | backgroundColor: '#fff', |
| | | disableColor: '#F7F6F6', |
| | | borderColor: '#e5e5e5' |
| | | }; |
| | | } |
| | | }, |
| | | errorMessage: { |
| | | type: [String, Boolean], |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | focused: false, |
| | | val: '', |
| | | showMsg: '', |
| | | border: false, |
| | | isFirstBorder: false, |
| | | showClearIcon: false, |
| | | showPassword: false, |
| | | focusShow: false, |
| | | localMsg: '', |
| | | isEnter: false // 用于判断当前是否是使用回车操作 |
| | | }; |
| | | }, |
| | | computed: { |
| | | // 输入框内是否有值 |
| | | isVal() { |
| | | const val = this.val; |
| | | // fixed by mehaotian 处理值为0的情况,字符串0不在处理范围 |
| | | if (val || val === 0) { |
| | | return true; |
| | | } |
| | | return false; |
| | | }, |
| | | |
| | | msg() { |
| | | // console.log('computed', this.form, this.formItem); |
| | | // if (this.form) { |
| | | // return this.errorMessage || this.formItem.errMsg; |
| | | // } |
| | | // TODO 处理头条 formItem 中 errMsg 不更新的问题 |
| | | return this.localMsg || this.errorMessage; |
| | | }, |
| | | // 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,用户可以传入字符串数值 |
| | | inputMaxlength() { |
| | | return Number(this.maxlength); |
| | | }, |
| | | |
| | | // 处理外层样式的style |
| | | boxStyle() { |
| | | return `color:${this.inputBorder && this.msg ? '#e43d33' : this.styles.color};`; |
| | | }, |
| | | // input 内容的类和样式处理 |
| | | inputContentClass() { |
| | | return obj2strClass({ |
| | | 'is-input-border': this.inputBorder, |
| | | 'is-input-error-border': this.inputBorder && this.msg, |
| | | 'is-textarea': this.type === 'textarea', |
| | | 'is-disabled': this.disabled, |
| | | 'is-focused': this.focusShow |
| | | }); |
| | | }, |
| | | inputContentStyle() { |
| | | const focusColor = this.focusShow ? this.primaryColor : this.styles.borderColor; |
| | | const borderColor = this.inputBorder && this.msg ? '#dd524d' : focusColor; |
| | | return obj2strStyle({ |
| | | 'border-color': borderColor || '#e5e5e5', |
| | | 'background-color': this.disabled ? this.styles.disableColor : this.styles.backgroundColor |
| | | }); |
| | | }, |
| | | // input右侧样式 |
| | | inputStyle() { |
| | | const paddingRight = this.type === 'password' || this.clearable || this.prefixIcon ? '' : '10px'; |
| | | return obj2strStyle({ |
| | | 'padding-right': paddingRight, |
| | | 'padding-left': this.prefixIcon ? '' : '10px' |
| | | }); |
| | | } |
| | | }, |
| | | watch: { |
| | | value(newVal) { |
| | | this.val = newVal; |
| | | }, |
| | | modelValue(newVal) { |
| | | this.val = newVal; |
| | | }, |
| | | focus(newVal) { |
| | | this.$nextTick(() => { |
| | | this.focused = this.focus; |
| | | this.focusShow = this.focus; |
| | | }); |
| | | } |
| | | }, |
| | | created() { |
| | | this.init(); |
| | | // TODO 处理头条vue3 computed 不监听 inject 更改的问题(formItem.errMsg) |
| | | if (this.form && this.formItem) { |
| | | this.$watch('formItem.errMsg', newVal => { |
| | | this.localMsg = newVal; |
| | | }); |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.$nextTick(() => { |
| | | this.focused = this.focus; |
| | | this.focusShow = this.focus; |
| | | }); |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 初始化变量值 |
| | | */ |
| | | init() { |
| | | if (this.value || this.value === 0) { |
| | | this.val = this.value; |
| | | } else if (this.modelValue || this.modelValue === 0 || this.modelValue === '') { |
| | | this.val = this.modelValue; |
| | | } else { |
| | | this.val = null; |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 点击图标时触发 |
| | | * @param {Object} type |
| | | */ |
| | | onClickIcon(type) { |
| | | this.$emit('iconClick', type); |
| | | }, |
| | | |
| | | /** |
| | | * 显示隐藏内容,密码框时生效 |
| | | */ |
| | | onEyes() { |
| | | this.showPassword = !this.showPassword; |
| | | this.$emit('eyes', this.showPassword); |
| | | }, |
| | | |
| | | /** |
| | | * 输入时触发 |
| | | * @param {Object} event |
| | | */ |
| | | onInput(event) { |
| | | let value = event.detail.value; |
| | | // 判断是否去除空格 |
| | | if (this.trim) { |
| | | if (typeof this.trim === 'boolean' && this.trim) { |
| | | value = this.trimStr(value); |
| | | } |
| | | if (typeof this.trim === 'string') { |
| | | value = this.trimStr(value, this.trim); |
| | | } |
| | | } |
| | | if (this.errMsg) this.errMsg = ''; |
| | | this.val = value; |
| | | // TODO 兼容 vue2 |
| | | this.$emit('input', value); |
| | | // TODO 兼容 vue3 |
| | | this.$emit('update:modelValue', value); |
| | | }, |
| | | |
| | | /** |
| | | * 外部调用方法 |
| | | * 获取焦点时触发 |
| | | * @param {Object} event |
| | | */ |
| | | onFocus() { |
| | | this.$nextTick(() => { |
| | | this.focused = true; |
| | | }); |
| | | this.$emit('focus', null); |
| | | }, |
| | | |
| | | _Focus(event) { |
| | | this.focusShow = true; |
| | | this.$emit('focus', event); |
| | | }, |
| | | |
| | | /** |
| | | * 外部调用方法 |
| | | * 失去焦点时触发 |
| | | * @param {Object} event |
| | | */ |
| | | onBlur() { |
| | | this.focused = false; |
| | | this.$emit('focus', null); |
| | | }, |
| | | _Blur(event) { |
| | | let value = event.detail.value; |
| | | this.focusShow = false; |
| | | this.$emit('blur', event); |
| | | // 根据类型返回值,在event中获取的值理论上讲都是string |
| | | if (this.isEnter === false) { |
| | | this.$emit('change', this.val); |
| | | } |
| | | // 失去焦点时参与表单校验 |
| | | if (this.form && this.formItem) { |
| | | const { validateTrigger } = this.form; |
| | | if (validateTrigger === 'blur') { |
| | | this.formItem.onFieldChange(); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 按下键盘的发送键 |
| | | * @param {Object} e |
| | | */ |
| | | onConfirm(e) { |
| | | this.$emit('confirm', this.val); |
| | | this.isEnter = true; |
| | | this.$emit('change', this.val); |
| | | this.$nextTick(() => { |
| | | this.isEnter = false; |
| | | }); |
| | | }, |
| | | |
| | | /** |
| | | * 清理内容 |
| | | * @param {Object} event |
| | | */ |
| | | onClear(event) { |
| | | this.val = ''; |
| | | // TODO 兼容 vue2 |
| | | this.$emit('input', ''); |
| | | // TODO 兼容 vue2 |
| | | // TODO 兼容 vue3 |
| | | this.$emit('update:modelValue', ''); |
| | | // 点击叉号触发 |
| | | this.$emit('clear'); |
| | | }, |
| | | |
| | | /** |
| | | * 键盘高度发生变化的时候触发此事件 |
| | | * 兼容性:微信小程序2.7.0+、App 3.1.0+ |
| | | * @param {Object} event |
| | | */ |
| | | onkeyboardheightchange(event) { |
| | | this.$emit("keyboardheightchange",event); |
| | | }, |
| | | |
| | | /** |
| | | * 去除空格 |
| | | */ |
| | | trimStr(str, pos = 'both') { |
| | | if (pos === 'both') { |
| | | return str.trim(); |
| | | } else if (pos === 'left') { |
| | | return str.trimLeft(); |
| | | } else if (pos === 'right') { |
| | | return str.trimRight(); |
| | | } else if (pos === 'start') { |
| | | return str.trimStart(); |
| | | } else if (pos === 'end') { |
| | | return str.trimEnd(); |
| | | } else if (pos === 'all') { |
| | | return str.replace(/\s+/g, ''); |
| | | } else if (pos === 'none') { |
| | | return str; |
| | | } |
| | | return str; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-error: #e43d33; |
| | | $uni-border-1: #dcdfe6 !default; |
| | | |
| | | .uni-easyinput { |
| | | /* #ifndef APP-NVUE */ |
| | | width: 100%; |
| | | /* #endif */ |
| | | flex: 1; |
| | | position: relative; |
| | | text-align: left; |
| | | color: #333; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-easyinput__content { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | width: 100%; |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | // min-height: 36px; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | // 处理border动画刚开始显示黑色的问题 |
| | | border-color: #fff; |
| | | transition-property: border-color; |
| | | transition-duration: 0.3s; |
| | | } |
| | | |
| | | .uni-easyinput__content-input { |
| | | /* #ifndef APP-NVUE */ |
| | | width: auto; |
| | | /* #endif */ |
| | | position: relative; |
| | | overflow: hidden; |
| | | flex: 1; |
| | | line-height: 1; |
| | | font-size: 14px; |
| | | height: 35px; |
| | | // min-height: 36px; |
| | | } |
| | | |
| | | .uni-easyinput__placeholder-class { |
| | | color: #999; |
| | | font-size: 12px; |
| | | // font-weight: 200; |
| | | } |
| | | |
| | | .is-textarea { |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .is-textarea-icon { |
| | | margin-top: 5px; |
| | | } |
| | | |
| | | .uni-easyinput__content-textarea { |
| | | position: relative; |
| | | overflow: hidden; |
| | | flex: 1; |
| | | line-height: 1.5; |
| | | font-size: 14px; |
| | | margin: 6px; |
| | | margin-left: 0; |
| | | height: 80px; |
| | | min-height: 80px; |
| | | /* #ifndef APP-NVUE */ |
| | | min-height: 80px; |
| | | width: auto; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .input-padding { |
| | | padding-left: 10px; |
| | | } |
| | | |
| | | .content-clear-icon { |
| | | padding: 0 5px; |
| | | } |
| | | |
| | | .label-icon { |
| | | margin-right: 5px; |
| | | margin-top: -1px; |
| | | } |
| | | |
| | | // 显示边框 |
| | | .is-input-border { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | border: 1px solid $uni-border-1; |
| | | border-radius: 4px; |
| | | /* #ifdef MP-ALIPAY */ |
| | | overflow: hidden; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-error-message { |
| | | position: absolute; |
| | | bottom: -17px; |
| | | left: 0; |
| | | line-height: 12px; |
| | | color: $uni-error; |
| | | font-size: 12px; |
| | | text-align: left; |
| | | } |
| | | |
| | | .uni-error-msg--boeder { |
| | | position: relative; |
| | | bottom: 0; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .is-input-error-border { |
| | | border-color: $uni-error; |
| | | |
| | | .uni-easyinput__placeholder-class { |
| | | color: mix(#fff, $uni-error, 50%); |
| | | } |
| | | } |
| | | |
| | | .uni-easyinput--border { |
| | | margin-bottom: 0; |
| | | padding: 10px 15px; |
| | | // padding-bottom: 0; |
| | | border-top: 1px #eee solid; |
| | | } |
| | | |
| | | .uni-easyinput-error { |
| | | padding-bottom: 0; |
| | | } |
| | | |
| | | .is-first-border { |
| | | /* #ifndef APP-NVUE */ |
| | | border: none; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | border-width: 0; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .is-disabled { |
| | | background-color: #f7f6f6; |
| | | color: #d5d5d5; |
| | | |
| | | .uni-easyinput__placeholder-class { |
| | | color: #d5d5d5; |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-easyinput", |
| | | "displayName": "uni-easyinput 增强输入框", |
| | | "version": "1.1.9", |
| | | "description": "Easyinput 组件是对原生input组件的增强", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "input", |
| | | "uni-easyinput", |
| | | "输入框" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-scss", |
| | | "uni-icons" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ### Easyinput 增强输入框 |
| | | > **组件名:uni-easyinput** |
| | | > 代码块: `uEasyinput` |
| | | |
| | | |
| | | easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的,easyinput 内置了边框,图标等,同时包含 input 所有功能 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.2.5(2023-03-29) |
| | | - 新增 pattern.icon 属性,可自定义图标 |
| | | ## 1.2.4(2022-09-07) |
| | | 小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false) |
| | | ## 1.2.3(2022-09-05) |
| | | - 修复 nvue 环境下,具有 tabBar 时,fab 组件下部位置无法正常获取 --window-bottom 的bug,详见:[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310) |
| | | ## 1.2.2(2021-12-29) |
| | | - 更新 组件依赖 |
| | | ## 1.2.1(2021-11-19) |
| | | - 修复 阴影颜色不正确的bug |
| | | ## 1.2.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab) |
| | | ## 1.1.1(2021-11-09) |
| | | - 新增 提供组件设计资源,组件样式调整 |
| | | ## 1.1.0(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.0.7(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.0.6(2021-02-05) |
| | | - 调整为uni_modules目录规范 |
| | | - 优化 按钮背景色调整 |
| | | - 优化 兼容pc端 |
New file |
| | |
| | | <template> |
| | | <view class="uni-cursor-point"> |
| | | <view v-if="popMenu && (leftBottom||rightBottom||leftTop||rightTop) && content.length > 0" :class="{ |
| | | 'uni-fab--leftBottom': leftBottom, |
| | | 'uni-fab--rightBottom': rightBottom, |
| | | 'uni-fab--leftTop': leftTop, |
| | | 'uni-fab--rightTop': rightTop |
| | | }" class="uni-fab" |
| | | :style="nvueBottom" |
| | | > |
| | | <view :class="{ |
| | | 'uni-fab__content--left': horizontal === 'left', |
| | | 'uni-fab__content--right': horizontal === 'right', |
| | | 'uni-fab__content--flexDirection': direction === 'vertical', |
| | | 'uni-fab__content--flexDirectionStart': flexDirectionStart, |
| | | 'uni-fab__content--flexDirectionEnd': flexDirectionEnd, |
| | | 'uni-fab__content--other-platform': !isAndroidNvue |
| | | }" :style="{ width: boxWidth, height: boxHeight, backgroundColor: styles.backgroundColor }" |
| | | class="uni-fab__content" elevation="5"> |
| | | <view v-if="flexDirectionStart || horizontalLeft" class="uni-fab__item uni-fab__item--first" /> |
| | | <view v-for="(item, index) in content" :key="index" :class="{ 'uni-fab__item--active': isShow }" |
| | | class="uni-fab__item" @click="_onItemClick(index, item)"> |
| | | <image :src="item.active ? item.selectedIconPath : item.iconPath" class="uni-fab__item-image" |
| | | mode="aspectFit" /> |
| | | <text class="uni-fab__item-text" |
| | | :style="{ color: item.active ? styles.selectedColor : styles.color }">{{ item.text }}</text> |
| | | </view> |
| | | <view v-if="flexDirectionEnd || horizontalRight" class="uni-fab__item uni-fab__item--first" /> |
| | | </view> |
| | | </view> |
| | | <view :class="{ |
| | | 'uni-fab__circle--leftBottom': leftBottom, |
| | | 'uni-fab__circle--rightBottom': rightBottom, |
| | | 'uni-fab__circle--leftTop': leftTop, |
| | | 'uni-fab__circle--rightTop': rightTop, |
| | | 'uni-fab__content--other-platform': !isAndroidNvue |
| | | }" class="uni-fab__circle uni-fab__plus" :style="{ 'background-color': styles.buttonColor, 'bottom': nvueBottom }" @click="_onClick"> |
| | | <uni-icons class="fab-circle-icon" :type="styles.icon" :color="styles.iconColor" size="32" |
| | | :class="{'uni-fab__plus--active': isShow && content.length > 0}"></uni-icons> |
| | | <!-- <view class="fab-circle-v" :class="{'uni-fab__plus--active': isShow && content.length > 0}"></view> |
| | | <view class="fab-circle-h" :class="{'uni-fab__plus--active': isShow && content.length > 0}"></view> --> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | let platform = 'other' |
| | | // #ifdef APP-NVUE |
| | | platform = uni.getSystemInfoSync().platform |
| | | // #endif |
| | | |
| | | /** |
| | | * Fab 悬浮按钮 |
| | | * @description 点击可展开一个图形按钮菜单 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=144 |
| | | * @property {Object} pattern 可选样式配置项 |
| | | * @property {Object} horizontal = [left | right] 水平对齐方式 |
| | | * @value left 左对齐 |
| | | * @value right 右对齐 |
| | | * @property {Object} vertical = [bottom | top] 垂直对齐方式 |
| | | * @value bottom 下对齐 |
| | | * @value top 上对齐 |
| | | * @property {Object} direction = [horizontal | vertical] 展开菜单显示方式 |
| | | * @value horizontal 水平显示 |
| | | * @value vertical 垂直显示 |
| | | * @property {Array} content 展开菜单内容配置项 |
| | | * @property {Boolean} popMenu 是否使用弹出菜单 |
| | | * @event {Function} trigger 展开菜单点击事件,返回点击信息 |
| | | * @event {Function} fabClick 悬浮按钮点击事件 |
| | | */ |
| | | export default { |
| | | name: 'UniFab', |
| | | emits: ['fabClick', 'trigger'], |
| | | props: { |
| | | pattern: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | horizontal: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | vertical: { |
| | | type: String, |
| | | default: 'bottom' |
| | | }, |
| | | direction: { |
| | | type: String, |
| | | default: 'horizontal' |
| | | }, |
| | | content: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | show: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | popMenu: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | fabShow: false, |
| | | isShow: false, |
| | | isAndroidNvue: platform === 'android', |
| | | styles: { |
| | | color: '#3c3e49', |
| | | selectedColor: '#007AFF', |
| | | backgroundColor: '#fff', |
| | | buttonColor: '#007AFF', |
| | | iconColor: '#fff', |
| | | icon: 'plusempty' |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | contentWidth(e) { |
| | | return (this.content.length + 1) * 55 + 15 + 'px' |
| | | }, |
| | | contentWidthMin() { |
| | | return '55px' |
| | | }, |
| | | // 动态计算宽度 |
| | | boxWidth() { |
| | | return this.getPosition(3, 'horizontal') |
| | | }, |
| | | // 动态计算高度 |
| | | boxHeight() { |
| | | return this.getPosition(3, 'vertical') |
| | | }, |
| | | // 计算左下位置 |
| | | leftBottom() { |
| | | return this.getPosition(0, 'left', 'bottom') |
| | | }, |
| | | // 计算右下位置 |
| | | rightBottom() { |
| | | return this.getPosition(0, 'right', 'bottom') |
| | | }, |
| | | // 计算左上位置 |
| | | leftTop() { |
| | | return this.getPosition(0, 'left', 'top') |
| | | }, |
| | | rightTop() { |
| | | return this.getPosition(0, 'right', 'top') |
| | | }, |
| | | flexDirectionStart() { |
| | | return this.getPosition(1, 'vertical', 'top') |
| | | }, |
| | | flexDirectionEnd() { |
| | | return this.getPosition(1, 'vertical', 'bottom') |
| | | }, |
| | | horizontalLeft() { |
| | | return this.getPosition(2, 'horizontal', 'left') |
| | | }, |
| | | horizontalRight() { |
| | | return this.getPosition(2, 'horizontal', 'right') |
| | | }, |
| | | // 计算 nvue bottom |
| | | nvueBottom() { |
| | | const safeBottom = uni.getSystemInfoSync().windowBottom; |
| | | // #ifdef APP-NVUE |
| | | return 30 + safeBottom |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | return 30 |
| | | // #endif |
| | | } |
| | | }, |
| | | watch: { |
| | | pattern: { |
| | | handler(val, oldVal) { |
| | | this.styles = Object.assign({}, this.styles, val) |
| | | }, |
| | | deep: true |
| | | } |
| | | }, |
| | | created() { |
| | | this.isShow = this.show |
| | | if (this.top === 0) { |
| | | this.fabShow = true |
| | | } |
| | | // 初始化样式 |
| | | this.styles = Object.assign({}, this.styles, this.pattern) |
| | | }, |
| | | methods: { |
| | | _onClick() { |
| | | this.$emit('fabClick') |
| | | if (!this.popMenu) { |
| | | return |
| | | } |
| | | this.isShow = !this.isShow |
| | | }, |
| | | open() { |
| | | this.isShow = true |
| | | }, |
| | | close() { |
| | | this.isShow = false |
| | | }, |
| | | /** |
| | | * 按钮点击事件 |
| | | */ |
| | | _onItemClick(index, item) { |
| | | if (!this.isShow) { |
| | | return |
| | | } |
| | | this.$emit('trigger', { |
| | | index, |
| | | item |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取 位置信息 |
| | | */ |
| | | getPosition(types, paramA, paramB) { |
| | | if (types === 0) { |
| | | return this.horizontal === paramA && this.vertical === paramB |
| | | } else if (types === 1) { |
| | | return this.direction === paramA && this.vertical === paramB |
| | | } else if (types === 2) { |
| | | return this.direction === paramA && this.horizontal === paramB |
| | | } else { |
| | | return this.isShow && this.direction === paramA ? this.contentWidth : this.contentWidthMin |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | $uni-shadow-base:0 1px 5px 2px rgba($color: #000000, $alpha: 0.3) !default; |
| | | |
| | | .uni-fab { |
| | | position: fixed; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | z-index: 10; |
| | | border-radius: 45px; |
| | | box-shadow: $uni-shadow-base; |
| | | } |
| | | |
| | | .uni-cursor-point { |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-fab--active { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .uni-fab--leftBottom { |
| | | left: 15px; |
| | | bottom: 30px; |
| | | /* #ifdef H5 */ |
| | | left: calc(15px + var(--window-left)); |
| | | bottom: calc(30px + var(--window-bottom)); |
| | | /* #endif */ |
| | | // padding: 10px; |
| | | } |
| | | |
| | | .uni-fab--leftTop { |
| | | left: 15px; |
| | | top: 30px; |
| | | /* #ifdef H5 */ |
| | | left: calc(15px + var(--window-left)); |
| | | top: calc(30px + var(--window-top)); |
| | | /* #endif */ |
| | | // padding: 10px; |
| | | } |
| | | |
| | | .uni-fab--rightBottom { |
| | | right: 15px; |
| | | bottom: 30px; |
| | | /* #ifdef H5 */ |
| | | right: calc(15px + var(--window-right)); |
| | | bottom: calc(30px + var(--window-bottom)); |
| | | /* #endif */ |
| | | // padding: 10px; |
| | | } |
| | | |
| | | .uni-fab--rightTop { |
| | | right: 15px; |
| | | top: 30px; |
| | | /* #ifdef H5 */ |
| | | right: calc(15px + var(--window-right)); |
| | | top: calc(30px + var(--window-top)); |
| | | /* #endif */ |
| | | // padding: 10px; |
| | | } |
| | | |
| | | .uni-fab__circle { |
| | | position: fixed; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: 55px; |
| | | height: 55px; |
| | | background-color: #3c3e49; |
| | | border-radius: 45px; |
| | | z-index: 11; |
| | | // box-shadow: $uni-shadow-base; |
| | | } |
| | | |
| | | .uni-fab__circle--leftBottom { |
| | | left: 15px; |
| | | bottom: 30px; |
| | | /* #ifdef H5 */ |
| | | left: calc(15px + var(--window-left)); |
| | | bottom: calc(30px + var(--window-bottom)); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-fab__circle--leftTop { |
| | | left: 15px; |
| | | top: 30px; |
| | | /* #ifdef H5 */ |
| | | left: calc(15px + var(--window-left)); |
| | | top: calc(30px + var(--window-top)); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-fab__circle--rightBottom { |
| | | right: 15px; |
| | | bottom: 30px; |
| | | /* #ifdef H5 */ |
| | | right: calc(15px + var(--window-right)); |
| | | bottom: calc(30px + var(--window-bottom)); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-fab__circle--rightTop { |
| | | right: 15px; |
| | | top: 30px; |
| | | /* #ifdef H5 */ |
| | | right: calc(15px + var(--window-right)); |
| | | top: calc(30px + var(--window-top)); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-fab__circle--left { |
| | | left: 0; |
| | | } |
| | | |
| | | .uni-fab__circle--right { |
| | | right: 0; |
| | | } |
| | | |
| | | .uni-fab__circle--top { |
| | | top: 0; |
| | | } |
| | | |
| | | .uni-fab__circle--bottom { |
| | | bottom: 0; |
| | | } |
| | | |
| | | .uni-fab__plus { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | // .fab-circle-v { |
| | | // position: absolute; |
| | | // width: 2px; |
| | | // height: 24px; |
| | | // left: 0; |
| | | // top: 0; |
| | | // right: 0; |
| | | // bottom: 0; |
| | | // /* #ifndef APP-NVUE */ |
| | | // margin: auto; |
| | | // /* #endif */ |
| | | // background-color: white; |
| | | // transform: rotate(0deg); |
| | | // transition: transform 0.3s; |
| | | // } |
| | | |
| | | // .fab-circle-h { |
| | | // position: absolute; |
| | | // width: 24px; |
| | | // height: 2px; |
| | | // left: 0; |
| | | // top: 0; |
| | | // right: 0; |
| | | // bottom: 0; |
| | | // /* #ifndef APP-NVUE */ |
| | | // margin: auto; |
| | | // /* #endif */ |
| | | // background-color: white; |
| | | // transform: rotate(0deg); |
| | | // transition: transform 0.3s; |
| | | // } |
| | | |
| | | .fab-circle-icon { |
| | | transform: rotate(0deg); |
| | | transition: transform 0.3s; |
| | | font-weight: 200; |
| | | } |
| | | |
| | | .uni-fab__plus--active { |
| | | transform: rotate(135deg); |
| | | } |
| | | |
| | | .uni-fab__content { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | border-radius: 55px; |
| | | overflow: hidden; |
| | | transition-property: width, height; |
| | | transition-duration: 0.2s; |
| | | width: 55px; |
| | | border-color: #DDDDDD; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | } |
| | | |
| | | .uni-fab__content--other-platform { |
| | | border-width: 0px; |
| | | box-shadow: $uni-shadow-base; |
| | | } |
| | | |
| | | .uni-fab__content--left { |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .uni-fab__content--right { |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .uni-fab__content--flexDirection { |
| | | flex-direction: column; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .uni-fab__content--flexDirectionStart { |
| | | flex-direction: column; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .uni-fab__content--flexDirectionEnd { |
| | | flex-direction: column; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .uni-fab__item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: 55px; |
| | | height: 55px; |
| | | opacity: 0; |
| | | transition: opacity 0.2s; |
| | | } |
| | | |
| | | .uni-fab__item--active { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .uni-fab__item-image { |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .uni-fab__item-text { |
| | | color: #FFFFFF; |
| | | font-size: 12px; |
| | | line-height: 12px; |
| | | margin-top: 2px; |
| | | } |
| | | |
| | | .uni-fab__item--first { |
| | | width: 55px; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-fab", |
| | | "displayName": "uni-fab 悬浮按钮", |
| | | "version": "1.2.5", |
| | | "description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "按钮", |
| | | "悬浮按钮", |
| | | "fab" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss","uni-icons"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | ## Fab 悬浮按钮 |
| | | > **组件名:uni-fab** |
| | | > 代码块: `uFab` |
| | | |
| | | |
| | | 点击可展开一个图形按钮菜单 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fab) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.2.1(2022-05-30) |
| | | - 新增 stat 属性 ,是否开启uni统计功能 |
| | | ## 1.2.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fav](https://uniapp.dcloud.io/component/uniui/uni-fav) |
| | | ## 1.1.1(2021-08-24) |
| | | - 新增 支持国际化 |
| | | ## 1.1.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.0.6(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.0.5(2021-04-21) |
| | | - 优化 添加依赖 uni-icons, 导入后自动下载依赖 |
| | | ## 1.0.4(2021-02-05) |
| | | - 优化 组件引用关系,通过uni_modules引用组件 |
| | | ## 1.0.3(2021-02-05) |
| | | - 优化 组件引用关系,通过uni_modules引用组件 |
| | | ## 1.0.2(2021-02-05) |
| | | - 调整为uni_modules目录规范 |
New file |
| | |
| | | { |
| | | "uni-fav.collect": "collect", |
| | | "uni-fav.collected": "collected" |
| | | } |
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 |
| | |
| | | { |
| | | "uni-fav.collect": "收藏", |
| | | "uni-fav.collected": "已收藏" |
| | | } |
New file |
| | |
| | | { |
| | | "uni-fav.collect": "收藏", |
| | | "uni-fav.collected": "已收藏" |
| | | } |
New file |
| | |
| | | <template> |
| | | <view :class="[circle === true || circle === 'true' ? 'uni-fav--circle' : '']" :style="[{ backgroundColor: checked ? bgColorChecked : bgColor }]" |
| | | @click="onClick" class="uni-fav"> |
| | | <!-- #ifdef MP-ALIPAY --> |
| | | <view class="uni-fav-star" v-if="!checked && (star === true || star === 'true')"> |
| | | <uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" size="14" type="star-filled" /> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifndef MP-ALIPAY --> |
| | | <uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-star" size="14" type="star-filled" |
| | | v-if="!checked && (star === true || star === 'true')" /> |
| | | <!-- #endif --> |
| | | <text :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-text">{{ checked ? contentFav : contentDefault }}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | |
| | | /** |
| | | * Fav 收藏按钮 |
| | | * @description 用于收藏功能,可点击切换选中、不选中的状态 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=864 |
| | | * @property {Boolean} star = [true|false] 按钮是否带星星 |
| | | * @property {String} bgColor 未收藏时的背景色 |
| | | * @property {String} bgColorChecked 已收藏时的背景色 |
| | | * @property {String} fgColor 未收藏时的文字颜色 |
| | | * @property {String} fgColorChecked 已收藏时的文字颜色 |
| | | * @property {Boolean} circle = [true|false] 是否为圆角 |
| | | * @property {Boolean} checked = [true|false] 是否为已收藏 |
| | | * @property {Object} contentText = [true|false] 收藏按钮文字 |
| | | * @property {Boolean} stat 是否开启统计功能 |
| | | * @event {Function} click 点击 fav按钮触发事件 |
| | | * @example <uni-fav :checked="true"/> |
| | | */ |
| | | |
| | | import { |
| | | initVueI18n |
| | | } from '@dcloudio/uni-i18n' |
| | | import messages from './i18n/index.js' |
| | | const { t } = initVueI18n(messages) |
| | | |
| | | export default { |
| | | name: "UniFav", |
| | | // TODO 兼容 vue3,需要注册事件 |
| | | emits: ['click'], |
| | | props: { |
| | | star: { |
| | | type: [Boolean, String], |
| | | default: true |
| | | }, |
| | | bgColor: { |
| | | type: String, |
| | | default: "#eeeeee" |
| | | }, |
| | | fgColor: { |
| | | type: String, |
| | | default: "#666666" |
| | | }, |
| | | bgColorChecked: { |
| | | type: String, |
| | | default: "#007aff" |
| | | }, |
| | | fgColorChecked: { |
| | | type: String, |
| | | default: "#FFFFFF" |
| | | }, |
| | | circle: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | checked: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | contentText: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | contentDefault: "", |
| | | contentFav: "" |
| | | }; |
| | | } |
| | | }, |
| | | stat:{ |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | computed: { |
| | | contentDefault() { |
| | | return this.contentText.contentDefault || t("uni-fav.collect") |
| | | }, |
| | | contentFav() { |
| | | return this.contentText.contentFav || t("uni-fav.collected") |
| | | }, |
| | | }, |
| | | watch: { |
| | | checked() { |
| | | if (uni.report && this.stat) { |
| | | if (this.checked) { |
| | | uni.report("收藏", "收藏"); |
| | | } else { |
| | | uni.report("取消收藏", "取消收藏"); |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick() { |
| | | this.$emit("click"); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | $fav-height: 25px; |
| | | |
| | | .uni-fav { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 60px; |
| | | height: $fav-height; |
| | | line-height: $fav-height; |
| | | text-align: center; |
| | | border-radius: 3px; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-fav--circle { |
| | | border-radius: 30px; |
| | | } |
| | | |
| | | .uni-fav-star { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | height: $fav-height; |
| | | line-height: 24px; |
| | | margin-right: 3px; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .uni-fav-text { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | height: $fav-height; |
| | | line-height: $fav-height; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 12px; |
| | | } |
| | | </style> |
New file |
| | |
| | | { |
| | | "id": "uni-fav", |
| | | "displayName": "uni-fav 收藏按钮", |
| | | "version": "1.2.1", |
| | | "description": " Fav 收藏组件,可自定义颜色、大小。", |
| | | "keywords": [ |
| | | "fav", |
| | | "uni-ui", |
| | | "uniui", |
| | | "收藏" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-scss", |
| | | "uni-icons" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | |
| | | ## Fav 收藏按钮 |
| | | > **组件名:uni-fav** |
| | | > 代码块: `uFav` |
| | | |
| | | 用于收藏功能,可点击切换选中、不选中的状态。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fav) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.0.4(2023-03-29) |
| | | - 修复 手动上传删除一个文件后不能再上传的bug |
| | | ## 1.0.3(2022-12-19) |
| | | - 新增 sourceType 属性, 可以自定义图片和视频选择的来源 |
| | | ## 1.0.2(2022-07-04) |
| | | - 修复 在uni-forms下样式不生效的bug |
| | | ## 1.0.1(2021-11-23) |
| | | - 修复 参数为对象的情况下,url在某些情况显示错误的bug |
| | | ## 1.0.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-file-picker](https://uniapp.dcloud.io/component/uniui/uni-file-picker) |
| | | ## 0.2.16(2021-11-08) |
| | | - 修复 传入空对象 ,显示错误的Bug |
| | | ## 0.2.15(2021-08-30) |
| | | - 修复 return-type="object" 时且存在v-model时,无法删除文件的Bug |
| | | ## 0.2.14(2021-08-23) |
| | | - 新增 参数中返回 fileID 字段 |
| | | ## 0.2.13(2021-08-23) |
| | | - 修复 腾讯云传入fileID 不能回显的bug |
| | | - 修复 选择图片后,不能放大的问题 |
| | | ## 0.2.12(2021-08-17) |
| | | - 修复 由于 0.2.11 版本引起的不能回显图片的Bug |
| | | ## 0.2.11(2021-08-16) |
| | | - 新增 clearFiles(index) 方法,可以手动删除指定文件 |
| | | - 修复 v-model 值设为 null 报错的Bug |
| | | ## 0.2.10(2021-08-13) |
| | | - 修复 return-type="object" 时,无法删除文件的Bug |
| | | ## 0.2.9(2021-08-03) |
| | | - 修复 auto-upload 属性失效的Bug |
| | | ## 0.2.8(2021-07-31) |
| | | - 修复 fileExtname属性不指定值报错的Bug |
| | | ## 0.2.7(2021-07-31) |
| | | - 修复 在某种场景下图片不回显的Bug |
| | | ## 0.2.6(2021-07-30) |
| | | - 修复 return-type为object下,返回值不正确的Bug |
| | | ## 0.2.5(2021-07-30) |
| | | - 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题 |
| | | ## 0.2.3(2021-07-28) |
| | | - 优化 调整示例代码 |
| | | ## 0.2.2(2021-07-27) |
| | | - 修复 vue3 下赋值错误的Bug |
| | | - 优化 h5平台下上传文件导致页面卡死的问题 |
| | | ## 0.2.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 0.1.1(2021-07-02) |
| | | - 修复 sourceType 缺少默认值导致 ios 无法选择文件 |
| | | ## 0.1.0(2021-06-30) |
| | | - 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改 |
| | | ## 0.0.11(2021-06-30) |
| | | - 修复 由 0.0.10 版本引发的 returnType 属性失效的问题 |
| | | ## 0.0.10(2021-06-29) |
| | | - 优化 文件上传后进度条消失时机 |
| | | ## 0.0.9(2021-06-29) |
| | | - 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug |
| | | ## 0.0.8(2021-06-15) |
| | | - 修复 删除文件时无法触发 v-model 的Bug |
| | | ## 0.0.7(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 0.0.6(2021-04-09) |
| | | - 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug |
| | | ## 0.0.5(2021-04-09) |
| | | - 优化 更新组件示例 |
| | | ## 0.0.4(2021-04-09) |
| | | - 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔 |
| | | ## 0.0.3(2021-02-05) |
| | | - 调整为uni_modules目录规范 |
| | | - 修复 微信小程序不指定 fileExtname 属性选择失败的Bug |
New file |
| | |
| | | 'use strict'; |
| | | |
| | | const ERR_MSG_OK = 'chooseAndUploadFile:ok'; |
| | | const ERR_MSG_FAIL = 'chooseAndUploadFile:fail'; |
| | | |
| | | function chooseImage(opts) { |
| | | const { |
| | | count, |
| | | sizeType = ['original', 'compressed'], |
| | | sourceType, |
| | | extension |
| | | } = opts |
| | | return new Promise((resolve, reject) => { |
| | | uni.chooseImage({ |
| | | count, |
| | | sizeType, |
| | | sourceType, |
| | | extension, |
| | | success(res) { |
| | | resolve(normalizeChooseAndUploadFileRes(res, 'image')); |
| | | }, |
| | | fail(res) { |
| | | reject({ |
| | | errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), |
| | | }); |
| | | }, |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | function chooseVideo(opts) { |
| | | const { |
| | | camera, |
| | | compressed, |
| | | maxDuration, |
| | | sourceType, |
| | | extension |
| | | } = opts; |
| | | return new Promise((resolve, reject) => { |
| | | uni.chooseVideo({ |
| | | camera, |
| | | compressed, |
| | | maxDuration, |
| | | sourceType, |
| | | extension, |
| | | success(res) { |
| | | const { |
| | | tempFilePath, |
| | | duration, |
| | | size, |
| | | height, |
| | | width |
| | | } = res; |
| | | resolve(normalizeChooseAndUploadFileRes({ |
| | | errMsg: 'chooseVideo:ok', |
| | | tempFilePaths: [tempFilePath], |
| | | tempFiles: [ |
| | | { |
| | | name: (res.tempFile && res.tempFile.name) || '', |
| | | path: tempFilePath, |
| | | size, |
| | | type: (res.tempFile && res.tempFile.type) || '', |
| | | width, |
| | | height, |
| | | duration, |
| | | fileType: 'video', |
| | | cloudPath: '', |
| | | }, ], |
| | | }, 'video')); |
| | | }, |
| | | fail(res) { |
| | | reject({ |
| | | errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL), |
| | | }); |
| | | }, |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | function chooseAll(opts) { |
| | | const { |
| | | count, |
| | | extension |
| | | } = opts; |
| | | return new Promise((resolve, reject) => { |
| | | let chooseFile = uni.chooseFile; |
| | | if (typeof wx !== 'undefined' && |
| | | typeof wx.chooseMessageFile === 'function') { |
| | | chooseFile = wx.chooseMessageFile; |
| | | } |
| | | if (typeof chooseFile !== 'function') { |
| | | return reject({ |
| | | errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。', |
| | | }); |
| | | } |
| | | chooseFile({ |
| | | type: 'all', |
| | | count, |
| | | extension, |
| | | success(res) { |
| | | resolve(normalizeChooseAndUploadFileRes(res)); |
| | | }, |
| | | fail(res) { |
| | | reject({ |
| | | errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL), |
| | | }); |
| | | }, |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | function normalizeChooseAndUploadFileRes(res, fileType) { |
| | | res.tempFiles.forEach((item, index) => { |
| | | if (!item.name) { |
| | | item.name = item.path.substring(item.path.lastIndexOf('/') + 1); |
| | | } |
| | | if (fileType) { |
| | | item.fileType = fileType; |
| | | } |
| | | item.cloudPath = |
| | | Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.')); |
| | | }); |
| | | if (!res.tempFilePaths) { |
| | | res.tempFilePaths = res.tempFiles.map((file) => file.path); |
| | | } |
| | | return res; |
| | | } |
| | | |
| | | function uploadCloudFiles(files, max = 5, onUploadProgress) { |
| | | files = JSON.parse(JSON.stringify(files)) |
| | | const len = files.length |
| | | let count = 0 |
| | | let self = this |
| | | return new Promise(resolve => { |
| | | while (count < max) { |
| | | next() |
| | | } |
| | | |
| | | function next() { |
| | | let cur = count++ |
| | | if (cur >= len) { |
| | | !files.find(item => !item.url && !item.errMsg) && resolve(files) |
| | | return |
| | | } |
| | | const fileItem = files[cur] |
| | | const index = self.files.findIndex(v => v.uuid === fileItem.uuid) |
| | | fileItem.url = '' |
| | | delete fileItem.errMsg |
| | | |
| | | uniCloud |
| | | .uploadFile({ |
| | | filePath: fileItem.path, |
| | | cloudPath: fileItem.cloudPath, |
| | | fileType: fileItem.fileType, |
| | | onUploadProgress: res => { |
| | | res.index = index |
| | | onUploadProgress && onUploadProgress(res) |
| | | } |
| | | }) |
| | | .then(res => { |
| | | fileItem.url = res.fileID |
| | | fileItem.index = index |
| | | if (cur < len) { |
| | | next() |
| | | } |
| | | }) |
| | | .catch(res => { |
| | | fileItem.errMsg = res.errMsg || res.message |
| | | fileItem.index = index |
| | | if (cur < len) { |
| | | next() |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | function uploadFiles(choosePromise, { |
| | | onChooseFile, |
| | | onUploadProgress |
| | | }) { |
| | | return choosePromise |
| | | .then((res) => { |
| | | if (onChooseFile) { |
| | | const customChooseRes = onChooseFile(res); |
| | | if (typeof customChooseRes !== 'undefined') { |
| | | return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ? |
| | | res : chooseRes); |
| | | } |
| | | } |
| | | return res; |
| | | }) |
| | | .then((res) => { |
| | | if (res === false) { |
| | | return { |
| | | errMsg: ERR_MSG_OK, |
| | | tempFilePaths: [], |
| | | tempFiles: [], |
| | | }; |
| | | } |
| | | return res |
| | | }) |
| | | } |
| | | |
| | | function chooseAndUploadFile(opts = { |
| | | type: 'all' |
| | | }) { |
| | | if (opts.type === 'image') { |
| | | return uploadFiles(chooseImage(opts), opts); |
| | | } |
| | | else if (opts.type === 'video') { |
| | | return uploadFiles(chooseVideo(opts), opts); |
| | | } |
| | | return uploadFiles(chooseAll(opts), opts); |
| | | } |
| | | |
| | | export { |
| | | chooseAndUploadFile, |
| | | uploadCloudFiles |
| | | }; |
New file |
| | |
| | | <template> |
| | | <view class="uni-file-picker"> |
| | | <view v-if="title" class="uni-file-picker__header"> |
| | | <text class="file-title">{{ title }}</text> |
| | | <text class="file-count">{{ filesList.length }}/{{ limitLength }}</text> |
| | | </view> |
| | | <upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly" |
| | | :image-styles="imageStyles" :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview" |
| | | :delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile"> |
| | | <slot> |
| | | <view class="is-add"> |
| | | <view class="icon-add"></view> |
| | | <view class="icon-add rotate"></view> |
| | | </view> |
| | | </slot> |
| | | </upload-image> |
| | | <upload-file v-if="fileMediatype !== 'image' || showType !== 'grid'" :readonly="readonly" |
| | | :list-styles="listStyles" :files-list="filesList" :showType="showType" :delIcon="delIcon" |
| | | @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile"> |
| | | <slot><button type="primary" size="mini">选择文件</button></slot> |
| | | </upload-file> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { |
| | | chooseAndUploadFile, |
| | | uploadCloudFiles |
| | | } from './choose-and-upload-file.js' |
| | | import { |
| | | get_file_ext, |
| | | get_extname, |
| | | get_files_and_is_max, |
| | | get_file_info, |
| | | get_file_data |
| | | } from './utils.js' |
| | | import uploadImage from './upload-image.vue' |
| | | import uploadFile from './upload-file.vue' |
| | | let fileInput = null |
| | | /** |
| | | * FilePicker 文件选择上传 |
| | | * @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=4079 |
| | | * @property {Object|Array} value 组件数据,通常用来回显 ,类型由return-type属性决定 |
| | | * @property {Boolean} disabled = [true|false] 组件禁用 |
| | | * @value true 禁用 |
| | | * @value false 取消禁用 |
| | | * @property {Boolean} readonly = [true|false] 组件只读,不可选择,不显示进度,不显示删除按钮 |
| | | * @value true 只读 |
| | | * @value false 取消只读 |
| | | * @property {String} return-type = [array|object] 限制 value 格式,当为 object 时 ,组件只能单选,且会覆盖 |
| | | * @value array 规定 value 属性的类型为数组 |
| | | * @value object 规定 value 属性的类型为对象 |
| | | * @property {Boolean} disable-preview = [true|false] 禁用图片预览,仅 mode:grid 时生效 |
| | | * @value true 禁用图片预览 |
| | | * @value false 取消禁用图片预览 |
| | | * @property {Boolean} del-icon = [true|false] 是否显示删除按钮 |
| | | * @value true 显示删除按钮 |
| | | * @value false 不显示删除按钮 |
| | | * @property {Boolean} auto-upload = [true|false] 是否自动上传,值为true则只触发@select,可自行上传 |
| | | * @value true 自动上传 |
| | | * @value false 取消自动上传 |
| | | * @property {Number|String} limit 最大选择个数 ,h5 会自动忽略多选的部分 |
| | | * @property {String} title 组件标题,右侧显示上传计数 |
| | | * @property {String} mode = [list|grid] 选择文件后的文件列表样式 |
| | | * @value list 列表显示 |
| | | * @value grid 宫格显示 |
| | | * @property {String} file-mediatype = [image|video|all] 选择文件类型 |
| | | * @value image 只选择图片 |
| | | * @value video 只选择视频 |
| | | * @value all 选择所有文件 |
| | | * @property {Array} file-extname 选择文件后缀,根据 file-mediatype 属性而不同 |
| | | * @property {Object} list-style mode:list 时的样式 |
| | | * @property {Object} image-styles 选择文件后缀,根据 file-mediatype 属性而不同 |
| | | * @event {Function} select 选择文件后触发 |
| | | * @event {Function} progress 文件上传时触发 |
| | | * @event {Function} success 上传成功触发 |
| | | * @event {Function} fail 上传失败触发 |
| | | * @event {Function} delete 文件从列表移除时触发 |
| | | */ |
| | | export default { |
| | | name: 'uniFilePicker', |
| | | components: { |
| | | uploadImage, |
| | | uploadFile |
| | | }, |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'], |
| | | props: { |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | type: [Array, Object], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | // #endif |
| | | |
| | | // #ifndef VUE3 |
| | | value: { |
| | | type: [Array, Object], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | // #endif |
| | | |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | disablePreview: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | delIcon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 自动上传 |
| | | autoUpload: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 最大选择个数 ,h5只能限制单选或是多选 |
| | | limit: { |
| | | type: [Number, String], |
| | | default: 9 |
| | | }, |
| | | // 列表样式 grid | list | list-card |
| | | mode: { |
| | | type: String, |
| | | default: 'grid' |
| | | }, |
| | | // 选择文件类型 image/video/all |
| | | fileMediatype: { |
| | | type: String, |
| | | default: 'image' |
| | | }, |
| | | // 文件类型筛选 |
| | | fileExtname: { |
| | | type: [Array, String], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | listStyles: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | // 是否显示边框 |
| | | border: true, |
| | | // 是否显示分隔线 |
| | | dividline: true, |
| | | // 线条样式 |
| | | borderStyle: {} |
| | | } |
| | | } |
| | | }, |
| | | imageStyles: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | width: 'auto', |
| | | height: 'auto' |
| | | } |
| | | } |
| | | }, |
| | | readonly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | returnType: { |
| | | type: String, |
| | | default: 'array' |
| | | }, |
| | | sizeType: { |
| | | type: Array, |
| | | default () { |
| | | return ['original', 'compressed'] |
| | | } |
| | | }, |
| | | sourceType: { |
| | | type: Array, |
| | | default () { |
| | | return ['album', 'camera'] |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | files: [], |
| | | localValue: [] |
| | | } |
| | | }, |
| | | watch: { |
| | | // #ifndef VUE3 |
| | | value: { |
| | | handler(newVal, oldVal) { |
| | | this.setValue(newVal, oldVal) |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | handler(newVal, oldVal) { |
| | | this.setValue(newVal, oldVal) |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | }, |
| | | computed: { |
| | | filesList() { |
| | | let files = [] |
| | | this.files.forEach(v => { |
| | | files.push(v) |
| | | }) |
| | | return files |
| | | }, |
| | | showType() { |
| | | if (this.fileMediatype === 'image') { |
| | | return this.mode |
| | | } |
| | | return 'list' |
| | | }, |
| | | limitLength() { |
| | | if (this.returnType === 'object') { |
| | | return 1 |
| | | } |
| | | if (!this.limit) { |
| | | return 1 |
| | | } |
| | | if (this.limit >= 9) { |
| | | return 9 |
| | | } |
| | | return this.limit |
| | | } |
| | | }, |
| | | created() { |
| | | // TODO 兼容不开通服务空间的情况 |
| | | if (!(uniCloud.config && uniCloud.config.provider)) { |
| | | this.noSpace = true |
| | | uniCloud.chooseAndUploadFile = chooseAndUploadFile |
| | | } |
| | | this.form = this.getForm('uniForms') |
| | | this.formItem = this.getForm('uniFormsItem') |
| | | if (this.form && this.formItem) { |
| | | if (this.formItem.name) { |
| | | this.rename = this.formItem.name |
| | | this.form.inputChildrens.push(this) |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 公开用户使用,清空文件 |
| | | * @param {Object} index |
| | | */ |
| | | clearFiles(index) { |
| | | if (index !== 0 && !index) { |
| | | this.files = [] |
| | | this.$nextTick(() => { |
| | | this.setEmit() |
| | | }) |
| | | } else { |
| | | this.files.splice(index, 1) |
| | | } |
| | | this.$nextTick(() => { |
| | | this.setEmit() |
| | | }) |
| | | }, |
| | | /** |
| | | * 公开用户使用,继续上传 |
| | | */ |
| | | upload() { |
| | | let files = [] |
| | | this.files.forEach((v, index) => { |
| | | if (v.status === 'ready' || v.status === 'error') { |
| | | files.push(Object.assign({}, v)) |
| | | } |
| | | }) |
| | | return this.uploadFiles(files) |
| | | }, |
| | | async setValue(newVal, oldVal) { |
| | | const newData = async (v) => { |
| | | const reg = /cloud:\/\/([\w.]+\/?)\S*/ |
| | | let url = '' |
| | | if(v.fileID){ |
| | | url = v.fileID |
| | | }else{ |
| | | url = v.url |
| | | } |
| | | if (reg.test(url)) { |
| | | v.fileID = url |
| | | v.url = await this.getTempFileURL(url) |
| | | } |
| | | if(v.url) v.path = v.url |
| | | return v |
| | | } |
| | | if (this.returnType === 'object') { |
| | | if (newVal) { |
| | | await newData(newVal) |
| | | } else { |
| | | newVal = {} |
| | | } |
| | | } else { |
| | | if (!newVal) newVal = [] |
| | | for(let i =0 ;i < newVal.length ;i++){ |
| | | let v = newVal[i] |
| | | await newData(v) |
| | | } |
| | | } |
| | | this.localValue = newVal |
| | | if (this.form && this.formItem &&!this.is_reset) { |
| | | this.is_reset = false |
| | | this.formItem.setValue(this.localValue) |
| | | } |
| | | let filesData = Object.keys(newVal).length > 0 ? newVal : []; |
| | | this.files = [].concat(filesData) |
| | | }, |
| | | |
| | | /** |
| | | * 选择文件 |
| | | */ |
| | | choose() { |
| | | |
| | | if (this.disabled) return |
| | | if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType === |
| | | 'array') { |
| | | uni.showToast({ |
| | | title: `您最多选择 ${this.limitLength} 个文件`, |
| | | icon: 'none' |
| | | }) |
| | | return |
| | | } |
| | | this.chooseFiles() |
| | | }, |
| | | |
| | | /** |
| | | * 选择文件并上传 |
| | | */ |
| | | chooseFiles() { |
| | | const _extname = get_extname(this.fileExtname) |
| | | // 获取后缀 |
| | | uniCloud |
| | | .chooseAndUploadFile({ |
| | | type: this.fileMediatype, |
| | | compressed: false, |
| | | sizeType: this.sizeType, |
| | | sourceType: this.sourceType, |
| | | // TODO 如果为空,video 有问题 |
| | | extension: _extname.length > 0 ? _extname : undefined, |
| | | count: this.limitLength - this.files.length, //默认9 |
| | | onChooseFile: this.chooseFileCallback, |
| | | onUploadProgress: progressEvent => { |
| | | this.setProgress(progressEvent, progressEvent.index) |
| | | } |
| | | }) |
| | | .then(result => { |
| | | this.setSuccessAndError(result.tempFiles) |
| | | }) |
| | | .catch(err => { |
| | | console.log('选择失败', err) |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * 选择文件回调 |
| | | * @param {Object} res |
| | | */ |
| | | async chooseFileCallback(res) { |
| | | const _extname = get_extname(this.fileExtname) |
| | | const is_one = (Number(this.limitLength) === 1 && |
| | | this.disablePreview && |
| | | !this.disabled) || |
| | | this.returnType === 'object' |
| | | // 如果这有一个文件 ,需要清空本地缓存数据 |
| | | if (is_one) { |
| | | this.files = [] |
| | | } |
| | | |
| | | let { |
| | | filePaths, |
| | | files |
| | | } = get_files_and_is_max(res, _extname) |
| | | if (!(_extname && _extname.length > 0)) { |
| | | filePaths = res.tempFilePaths |
| | | files = res.tempFiles |
| | | } |
| | | |
| | | let currentData = [] |
| | | for (let i = 0; i < files.length; i++) { |
| | | if (this.limitLength - this.files.length <= 0) break |
| | | files[i].uuid = Date.now() |
| | | let filedata = await get_file_data(files[i], this.fileMediatype) |
| | | filedata.progress = 0 |
| | | filedata.status = 'ready' |
| | | this.files.push(filedata) |
| | | currentData.push({ |
| | | ...filedata, |
| | | file: files[i] |
| | | }) |
| | | } |
| | | this.$emit('select', { |
| | | tempFiles: currentData, |
| | | tempFilePaths: filePaths |
| | | }) |
| | | res.tempFiles = files |
| | | // 停止自动上传 |
| | | if (!this.autoUpload || this.noSpace) { |
| | | res.tempFiles = [] |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 批传 |
| | | * @param {Object} e |
| | | */ |
| | | uploadFiles(files) { |
| | | files = [].concat(files) |
| | | return uploadCloudFiles.call(this, files, 5, res => { |
| | | this.setProgress(res, res.index, true) |
| | | }) |
| | | .then(result => { |
| | | this.setSuccessAndError(result) |
| | | return result; |
| | | }) |
| | | .catch(err => { |
| | | console.log(err) |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * 成功或失败 |
| | | */ |
| | | async setSuccessAndError(res, fn) { |
| | | let successData = [] |
| | | let errorData = [] |
| | | let tempFilePath = [] |
| | | let errorTempFilePath = [] |
| | | for (let i = 0; i < res.length; i++) { |
| | | const item = res[i] |
| | | const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index |
| | | |
| | | if (index === -1 || !this.files) break |
| | | if (item.errMsg === 'request:fail') { |
| | | this.files[index].url = item.path |
| | | this.files[index].status = 'error' |
| | | this.files[index].errMsg = item.errMsg |
| | | // this.files[index].progress = -1 |
| | | errorData.push(this.files[index]) |
| | | errorTempFilePath.push(this.files[index].url) |
| | | } else { |
| | | this.files[index].errMsg = '' |
| | | this.files[index].fileID = item.url |
| | | const reg = /cloud:\/\/([\w.]+\/?)\S*/ |
| | | if (reg.test(item.url)) { |
| | | this.files[index].url = await this.getTempFileURL(item.url) |
| | | }else{ |
| | | this.files[index].url = item.url |
| | | } |
| | | |
| | | this.files[index].status = 'success' |
| | | this.files[index].progress += 1 |
| | | successData.push(this.files[index]) |
| | | tempFilePath.push(this.files[index].fileID) |
| | | } |
| | | } |
| | | |
| | | if (successData.length > 0) { |
| | | this.setEmit() |
| | | // 状态改变返回 |
| | | this.$emit('success', { |
| | | tempFiles: this.backObject(successData), |
| | | tempFilePaths: tempFilePath |
| | | }) |
| | | } |
| | | |
| | | if (errorData.length > 0) { |
| | | this.$emit('fail', { |
| | | tempFiles: this.backObject(errorData), |
| | | tempFilePaths: errorTempFilePath |
| | | }) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 获取进度 |
| | | * @param {Object} progressEvent |
| | | * @param {Object} index |
| | | * @param {Object} type |
| | | */ |
| | | setProgress(progressEvent, index, type) { |
| | | const fileLenth = this.files.length |
| | | const percentNum = (index / fileLenth) * 100 |
| | | const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total) |
| | | let idx = index |
| | | if (!type) { |
| | | idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid) |
| | | } |
| | | if (idx === -1 || !this.files[idx]) return |
| | | // fix by mehaotian 100 就会消失,-1 是为了让进度条消失 |
| | | this.files[idx].progress = percentCompleted - 1 |
| | | // 上传中 |
| | | this.$emit('progress', { |
| | | index: idx, |
| | | progress: parseInt(percentCompleted), |
| | | tempFile: this.files[idx] |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * 删除文件 |
| | | * @param {Object} index |
| | | */ |
| | | delFile(index) { |
| | | this.$emit('delete', { |
| | | tempFile: this.files[index], |
| | | tempFilePath: this.files[index].url |
| | | }) |
| | | this.files.splice(index, 1) |
| | | this.$nextTick(() => { |
| | | this.setEmit() |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * 获取文件名和后缀 |
| | | * @param {Object} name |
| | | */ |
| | | getFileExt(name) { |
| | | const last_len = name.lastIndexOf('.') |
| | | const len = name.length |
| | | return { |
| | | name: name.substring(0, last_len), |
| | | ext: name.substring(last_len + 1, len) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 处理返回事件 |
| | | */ |
| | | setEmit() { |
| | | let data = [] |
| | | if (this.returnType === 'object') { |
| | | data = this.backObject(this.files)[0] |
| | | this.localValue = data?data:null |
| | | } else { |
| | | data = this.backObject(this.files) |
| | | if (!this.localValue) { |
| | | this.localValue = [] |
| | | } |
| | | this.localValue = [...data] |
| | | } |
| | | // #ifdef VUE3 |
| | | this.$emit('update:modelValue', this.localValue) |
| | | // #endif |
| | | // #ifndef VUE3 |
| | | this.$emit('input', this.localValue) |
| | | // #endif |
| | | }, |
| | | |
| | | /** |
| | | * 处理返回参数 |
| | | * @param {Object} files |
| | | */ |
| | | backObject(files) { |
| | | let newFilesData = [] |
| | | files.forEach(v => { |
| | | newFilesData.push({ |
| | | extname: v.extname, |
| | | fileType: v.fileType, |
| | | image: v.image, |
| | | name: v.name, |
| | | path: v.path, |
| | | size: v.size, |
| | | fileID:v.fileID, |
| | | url: v.url, |
| | | // 修改删除一个文件后不能再上传的bug, #694 |
| | | uuid: v.uuid, |
| | | status: v.status, |
| | | cloudPath: v.cloudPath |
| | | }) |
| | | }) |
| | | return newFilesData |
| | | }, |
| | | async getTempFileURL(fileList) { |
| | | fileList = { |
| | | fileList: [].concat(fileList) |
| | | } |
| | | const urls = await uniCloud.getTempFileURL(fileList) |
| | | return urls.fileList[0].tempFileURL || '' |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getForm(name = 'uniForms') { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== name) { |
| | | parent = parent.$parent; |
| | | if (!parent) return false; |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .uni-file-picker { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | width: 100%; |
| | | /* #endif */ |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-file-picker__header { |
| | | padding-top: 5px; |
| | | padding-bottom: 10px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .file-title { |
| | | font-size: 14px; |
| | | color: #333; |
| | | } |
| | | |
| | | .file-count { |
| | | font-size: 14px; |
| | | color: #999; |
| | | } |
| | | |
| | | .is-add { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .icon-add { |
| | | width: 50px; |
| | | height: 5px; |
| | | background-color: #f1f1f1; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .rotate { |
| | | position: absolute; |
| | | transform: rotate(90deg); |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-file-picker__files"> |
| | | <view v-if="!readonly" class="files-button" @click="choose"> |
| | | <slot></slot> |
| | | </view> |
| | | <!-- :class="{'is-text-box':showType === 'list'}" --> |
| | | <view v-if="list.length > 0" class="uni-file-picker__lists is-text-box" :style="borderStyle"> |
| | | <!-- ,'is-list-card':showType === 'list-card' --> |
| | | |
| | | <view class="uni-file-picker__lists-box" v-for="(item ,index) in list" :key="index" :class="{ |
| | | 'files-border':index !== 0 && styles.dividline}" |
| | | :style="index !== 0 && styles.dividline &&borderLineStyle"> |
| | | <view class="uni-file-picker__item"> |
| | | <!-- :class="{'is-text-image':showType === 'list'}" --> |
| | | <!-- <view class="files__image is-text-image"> |
| | | <image class="header-image" :src="item.logo" mode="aspectFit"></image> |
| | | </view> --> |
| | | <view class="files__name">{{item.name}}</view> |
| | | <view v-if="delIcon&&!readonly" class="icon-del-box icon-files" @click="delFile(index)"> |
| | | <view class="icon-del icon-files"></view> |
| | | <view class="icon-del rotate"></view> |
| | | </view> |
| | | </view> |
| | | <view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress"> |
| | | <progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4" |
| | | :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" /> |
| | | </view> |
| | | <view v-if="item.status === 'error'" class="file-picker__mask" @click.stop="uploadFiles(item,index)"> |
| | | 点击重试 |
| | | </view> |
| | | </view> |
| | | |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: "uploadFile", |
| | | emits:['uploadFiles','choose','delFile'], |
| | | props: { |
| | | filesList: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | delIcon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | limit: { |
| | | type: [Number, String], |
| | | default: 9 |
| | | }, |
| | | showType: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | listStyles: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | // 是否显示边框 |
| | | border: true, |
| | | // 是否显示分隔线 |
| | | dividline: true, |
| | | // 线条样式 |
| | | borderStyle: {} |
| | | } |
| | | } |
| | | }, |
| | | readonly:{ |
| | | type:Boolean, |
| | | default:false |
| | | } |
| | | }, |
| | | computed: { |
| | | list() { |
| | | let files = [] |
| | | this.filesList.forEach(v => { |
| | | files.push(v) |
| | | }) |
| | | return files |
| | | }, |
| | | styles() { |
| | | let styles = { |
| | | border: true, |
| | | dividline: true, |
| | | 'border-style': {} |
| | | } |
| | | return Object.assign(styles, this.listStyles) |
| | | }, |
| | | borderStyle() { |
| | | let { |
| | | borderStyle, |
| | | border |
| | | } = this.styles |
| | | let obj = {} |
| | | if (!border) { |
| | | obj.border = 'none' |
| | | } else { |
| | | let width = (borderStyle && borderStyle.width) || 1 |
| | | width = this.value2px(width) |
| | | let radius = (borderStyle && borderStyle.radius) || 5 |
| | | radius = this.value2px(radius) |
| | | obj = { |
| | | 'border-width': width, |
| | | 'border-style': (borderStyle && borderStyle.style) || 'solid', |
| | | 'border-color': (borderStyle && borderStyle.color) || '#eee', |
| | | 'border-radius': radius |
| | | } |
| | | } |
| | | let classles = '' |
| | | for (let i in obj) { |
| | | classles += `${i}:${obj[i]};` |
| | | } |
| | | return classles |
| | | }, |
| | | borderLineStyle() { |
| | | let obj = {} |
| | | let { |
| | | borderStyle |
| | | } = this.styles |
| | | if (borderStyle && borderStyle.color) { |
| | | obj['border-color'] = borderStyle.color |
| | | } |
| | | if (borderStyle && borderStyle.width) { |
| | | let width = borderStyle && borderStyle.width || 1 |
| | | let style = borderStyle && borderStyle.style || 0 |
| | | if (typeof width === 'number') { |
| | | width += 'px' |
| | | } else { |
| | | width = width.indexOf('px') ? width : width + 'px' |
| | | } |
| | | obj['border-width'] = width |
| | | |
| | | if (typeof style === 'number') { |
| | | style += 'px' |
| | | } else { |
| | | style = style.indexOf('px') ? style : style + 'px' |
| | | } |
| | | obj['border-top-style'] = style |
| | | } |
| | | let classles = '' |
| | | for (let i in obj) { |
| | | classles += `${i}:${obj[i]};` |
| | | } |
| | | return classles |
| | | } |
| | | }, |
| | | |
| | | methods: { |
| | | uploadFiles(item, index) { |
| | | this.$emit("uploadFiles", { |
| | | item, |
| | | index |
| | | }) |
| | | }, |
| | | choose() { |
| | | this.$emit("choose") |
| | | }, |
| | | delFile(index) { |
| | | this.$emit('delFile', index) |
| | | }, |
| | | value2px(value) { |
| | | if (typeof value === 'number') { |
| | | value += 'px' |
| | | } else { |
| | | value = value.indexOf('px') !== -1 ? value : value + 'px' |
| | | } |
| | | return value |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-file-picker__files { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .files-button { |
| | | // border: 1px red solid; |
| | | } |
| | | |
| | | .uni-file-picker__lists { |
| | | position: relative; |
| | | margin-top: 5px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .file-picker__mask { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: absolute; |
| | | right: 0; |
| | | top: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | color: #fff; |
| | | font-size: 14px; |
| | | background-color: rgba(0, 0, 0, 0.4); |
| | | } |
| | | |
| | | .uni-file-picker__lists-box { |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-file-picker__item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | padding: 8px 10px; |
| | | padding-right: 5px; |
| | | padding-left: 10px; |
| | | } |
| | | |
| | | .files-border { |
| | | border-top: 1px #eee solid; |
| | | } |
| | | |
| | | .files__name { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #666; |
| | | margin-right: 25px; |
| | | /* #ifndef APP-NVUE */ |
| | | word-break: break-all; |
| | | word-wrap: break-word; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .icon-files { |
| | | /* #ifndef APP-NVUE */ |
| | | position: static; |
| | | background-color: initial; |
| | | /* #endif */ |
| | | } |
| | | |
| | | // .icon-files .icon-del { |
| | | // background-color: #333; |
| | | // width: 12px; |
| | | // height: 1px; |
| | | // } |
| | | |
| | | |
| | | .is-list-card { |
| | | border: 1px #eee solid; |
| | | margin-bottom: 5px; |
| | | border-radius: 5px; |
| | | box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.1); |
| | | padding: 5px; |
| | | } |
| | | |
| | | .files__image { |
| | | width: 40px; |
| | | height: 40px; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .header-image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .is-text-box { |
| | | border: 1px #eee solid; |
| | | border-radius: 5px; |
| | | } |
| | | |
| | | .is-text-image { |
| | | width: 25px; |
| | | height: 25px; |
| | | margin-left: 5px; |
| | | } |
| | | |
| | | .rotate { |
| | | position: absolute; |
| | | transform: rotate(90deg); |
| | | } |
| | | |
| | | .icon-del-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | margin: auto 0; |
| | | /* #endif */ |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: absolute; |
| | | top: 0px; |
| | | bottom: 0; |
| | | right: 5px; |
| | | height: 26px; |
| | | width: 26px; |
| | | // border-radius: 50%; |
| | | // background-color: rgba(0, 0, 0, 0.5); |
| | | z-index: 2; |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .icon-del { |
| | | width: 15px; |
| | | height: 1px; |
| | | background-color: #333; |
| | | // border-radius: 1px; |
| | | } |
| | | |
| | | /* #ifdef H5 */ |
| | | @media all and (min-width: 768px) { |
| | | .uni-file-picker__files { |
| | | max-width: 375px; |
| | | } |
| | | } |
| | | |
| | | /* #endif */ |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-file-picker__container"> |
| | | <view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle"> |
| | | <view class="file-picker__box-content" :style="borderStyle"> |
| | | <image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image> |
| | | <view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)"> |
| | | <view class="icon-del"></view> |
| | | <view class="icon-del rotate"></view> |
| | | </view> |
| | | <view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress"> |
| | | <progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4" |
| | | :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" /> |
| | | </view> |
| | | <view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)"> |
| | | 点击重试 |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle"> |
| | | <view class="file-picker__box-content is-add" :style="borderStyle" @click="choose"> |
| | | <slot> |
| | | <view class="icon-add"></view> |
| | | <view class="icon-add rotate"></view> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: "uploadImage", |
| | | emits:['uploadFiles','choose','delFile'], |
| | | props: { |
| | | filesList: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | disabled:{ |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | disablePreview: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | limit: { |
| | | type: [Number, String], |
| | | default: 9 |
| | | }, |
| | | imageStyles: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | width: 'auto', |
| | | height: 'auto', |
| | | border: {} |
| | | } |
| | | } |
| | | }, |
| | | delIcon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | readonly:{ |
| | | type:Boolean, |
| | | default:false |
| | | } |
| | | }, |
| | | computed: { |
| | | styles() { |
| | | let styles = { |
| | | width: 'auto', |
| | | height: 'auto', |
| | | border: {} |
| | | } |
| | | return Object.assign(styles, this.imageStyles) |
| | | }, |
| | | boxStyle() { |
| | | const { |
| | | width = 'auto', |
| | | height = 'auto' |
| | | } = this.styles |
| | | let obj = {} |
| | | if (height === 'auto') { |
| | | if (width !== 'auto') { |
| | | obj.height = this.value2px(width) |
| | | obj['padding-top'] = 0 |
| | | } else { |
| | | obj.height = 0 |
| | | } |
| | | } else { |
| | | obj.height = this.value2px(height) |
| | | obj['padding-top'] = 0 |
| | | } |
| | | |
| | | if (width === 'auto') { |
| | | if (height !== 'auto') { |
| | | obj.width = this.value2px(height) |
| | | } else { |
| | | obj.width = '33.3%' |
| | | } |
| | | } else { |
| | | obj.width = this.value2px(width) |
| | | } |
| | | |
| | | let classles = '' |
| | | for(let i in obj){ |
| | | classles+= `${i}:${obj[i]};` |
| | | } |
| | | return classles |
| | | }, |
| | | borderStyle() { |
| | | let { |
| | | border |
| | | } = this.styles |
| | | let obj = {} |
| | | const widthDefaultValue = 1 |
| | | const radiusDefaultValue = 3 |
| | | if (typeof border === 'boolean') { |
| | | obj.border = border ? '1px #eee solid' : 'none' |
| | | } else { |
| | | let width = (border && border.width) || widthDefaultValue |
| | | width = this.value2px(width) |
| | | let radius = (border && border.radius) || radiusDefaultValue |
| | | radius = this.value2px(radius) |
| | | obj = { |
| | | 'border-width': width, |
| | | 'border-style': (border && border.style) || 'solid', |
| | | 'border-color': (border && border.color) || '#eee', |
| | | 'border-radius': radius |
| | | } |
| | | } |
| | | let classles = '' |
| | | for(let i in obj){ |
| | | classles+= `${i}:${obj[i]};` |
| | | } |
| | | return classles |
| | | } |
| | | }, |
| | | methods: { |
| | | uploadFiles(item, index) { |
| | | this.$emit("uploadFiles", item) |
| | | }, |
| | | choose() { |
| | | this.$emit("choose") |
| | | }, |
| | | delFile(index) { |
| | | this.$emit('delFile', index) |
| | | }, |
| | | prviewImage(img, index) { |
| | | let urls = [] |
| | | if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){ |
| | | this.$emit("choose") |
| | | } |
| | | if(this.disablePreview) return |
| | | this.filesList.forEach(i => { |
| | | urls.push(i.url) |
| | | }) |
| | | |
| | | uni.previewImage({ |
| | | urls: urls, |
| | | current: index |
| | | }); |
| | | }, |
| | | value2px(value) { |
| | | if (typeof value === 'number') { |
| | | value += 'px' |
| | | } else { |
| | | if (value.indexOf('%') === -1) { |
| | | value = value.indexOf('px') !== -1 ? value : value + 'px' |
| | | } |
| | | } |
| | | return value |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-file-picker__container { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-wrap: wrap; |
| | | margin: -5px; |
| | | } |
| | | |
| | | .file-picker__box { |
| | | position: relative; |
| | | // flex: 0 0 33.3%; |
| | | width: 33.3%; |
| | | height: 0; |
| | | padding-top: 33.33%; |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .file-picker__box-content { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | margin: 5px; |
| | | border: 1px #eee solid; |
| | | border-radius: 5px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .file-picker__progress { |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | /* border: 1px red solid; */ |
| | | z-index: 2; |
| | | } |
| | | |
| | | .file-picker__progress-item { |
| | | width: 100%; |
| | | } |
| | | |
| | | .file-picker__mask { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: absolute; |
| | | right: 0; |
| | | top: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | color: #fff; |
| | | font-size: 12px; |
| | | background-color: rgba(0, 0, 0, 0.4); |
| | | } |
| | | |
| | | .file-image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .is-add { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .icon-add { |
| | | width: 50px; |
| | | height: 5px; |
| | | background-color: #f1f1f1; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .rotate { |
| | | position: absolute; |
| | | transform: rotate(90deg); |
| | | } |
| | | |
| | | .icon-del-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: absolute; |
| | | top: 3px; |
| | | right: 3px; |
| | | height: 26px; |
| | | width: 26px; |
| | | border-radius: 50%; |
| | | background-color: rgba(0, 0, 0, 0.5); |
| | | z-index: 2; |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .icon-del { |
| | | width: 15px; |
| | | height: 2px; |
| | | background-color: #fff; |
| | | border-radius: 2px; |
| | | } |
| | | </style> |
New file |
| | |
| | | /** |
| | | * 获取文件名和后缀 |
| | | * @param {String} name |
| | | */ |
| | | export const get_file_ext = (name) => { |
| | | const last_len = name.lastIndexOf('.') |
| | | const len = name.length |
| | | return { |
| | | name: name.substring(0, last_len), |
| | | ext: name.substring(last_len + 1, len) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取扩展名 |
| | | * @param {Array} fileExtname |
| | | */ |
| | | export const get_extname = (fileExtname) => { |
| | | if (!Array.isArray(fileExtname)) { |
| | | let extname = fileExtname.replace(/(\[|\])/g, '') |
| | | return extname.split(',') |
| | | } else { |
| | | return fileExtname |
| | | } |
| | | return [] |
| | | } |
| | | |
| | | /** |
| | | * 获取文件和检测是否可选 |
| | | */ |
| | | export const get_files_and_is_max = (res, _extname) => { |
| | | let filePaths = [] |
| | | let files = [] |
| | | if(!_extname || _extname.length === 0){ |
| | | return { |
| | | filePaths, |
| | | files |
| | | } |
| | | } |
| | | res.tempFiles.forEach(v => { |
| | | let fileFullName = get_file_ext(v.name) |
| | | const extname = fileFullName.ext.toLowerCase() |
| | | if (_extname.indexOf(extname) !== -1) { |
| | | files.push(v) |
| | | filePaths.push(v.path) |
| | | } |
| | | }) |
| | | if (files.length !== res.tempFiles.length) { |
| | | uni.showToast({ |
| | | title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`, |
| | | icon: 'none', |
| | | duration: 5000 |
| | | }) |
| | | } |
| | | |
| | | return { |
| | | filePaths, |
| | | files |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取图片信息 |
| | | * @param {Object} filepath |
| | | */ |
| | | export const get_file_info = (filepath) => { |
| | | return new Promise((resolve, reject) => { |
| | | uni.getImageInfo({ |
| | | src: filepath, |
| | | success(res) { |
| | | resolve(res) |
| | | }, |
| | | fail(err) { |
| | | reject(err) |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | /** |
| | | * 获取封装数据 |
| | | */ |
| | | export const get_file_data = async (files, type = 'image') => { |
| | | // 最终需要上传数据库的数据 |
| | | let fileFullName = get_file_ext(files.name) |
| | | const extname = fileFullName.ext.toLowerCase() |
| | | let filedata = { |
| | | name: files.name, |
| | | uuid: files.uuid, |
| | | extname: extname || '', |
| | | cloudPath: files.cloudPath, |
| | | fileType: files.fileType, |
| | | url: files.path || files.path, |
| | | size: files.size, //单位是字节 |
| | | image: {}, |
| | | path: files.path, |
| | | video: {} |
| | | } |
| | | if (type === 'image') { |
| | | const imageinfo = await get_file_info(files.path) |
| | | delete filedata.video |
| | | filedata.image.width = imageinfo.width |
| | | filedata.image.height = imageinfo.height |
| | | filedata.image.location = imageinfo.path |
| | | } else { |
| | | delete filedata.image |
| | | } |
| | | return filedata |
| | | } |
New file |
| | |
| | | { |
| | | "id": "uni-file-picker", |
| | | "displayName": "uni-file-picker 文件选择上传", |
| | | "version": "1.0.4", |
| | | "description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "图片上传", |
| | | "文件上传" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "n" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | ## FilePicker 文件选择上传 |
| | | |
| | | > **组件名:uni-file-picker** |
| | | > 代码块: `uFilePicker` |
| | | |
| | | |
| | | 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-file-picker) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
New file |
| | |
| | | ## 1.4.9(2023-02-10) |
| | | - 修复 required 参数无法动态绑定 |
| | | ## 1.4.8(2022-08-23) |
| | | - 优化 根据 rules 自动添加 required 的问题 |
| | | ## 1.4.7(2022-08-22) |
| | | - 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540) |
| | | ## 1.4.6(2022-07-13) |
| | | - 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug |
| | | ## 1.4.5(2022-07-05) |
| | | - 新增 更多表单示例 |
| | | - 优化 子表单组件过期提示的问题 |
| | | - 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式 |
| | | ## 1.4.4(2022-07-04) |
| | | - 更新 删除组件日志 |
| | | ## 1.4.3(2022-07-04) |
| | | - 修复 由 1.4.0 引发的 label 插槽不生效的bug |
| | | ## 1.4.2(2022-07-04) |
| | | - 修复 子组件找不到 setValue 报错的bug |
| | | ## 1.4.1(2022-07-04) |
| | | - 修复 uni-data-picker 在 uni-forms-item 中报错的bug |
| | | - 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug |
| | | ## 1.4.0(2022-06-30) |
| | | - 【重要】组件逻辑重构,部分用法用旧版本不兼容,请注意兼容问题 |
| | | - 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力 |
| | | - 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃 |
| | | - 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效 |
| | | - 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法 |
| | | - 新增 子表单的 setRules 方法,配合自定义校验函数使用 |
| | | - 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则 |
| | | - 优化 动态表单校验方式,废弃拼接name的方式 |
| | | ## 1.3.3(2022-06-22) |
| | | - 修复 表单校验顺序无序问题 |
| | | ## 1.3.2(2021-12-09) |
| | | - |
| | | ## 1.3.1(2021-11-19) |
| | | - 修复 label 插槽不生效的bug |
| | | ## 1.3.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-forms](https://uniapp.dcloud.io/component/uniui/uni-forms) |
| | | ## 1.2.7(2021-08-13) |
| | | - 修复 没有添加校验规则的字段依然报错的Bug |
| | | ## 1.2.6(2021-08-11) |
| | | - 修复 重置表单错误信息无法清除的问题 |
| | | ## 1.2.5(2021-08-11) |
| | | - 优化 组件文档 |
| | | ## 1.2.4(2021-08-11) |
| | | - 修复 表单验证只生效一次的问题 |
| | | ## 1.2.3(2021-07-30) |
| | | - 优化 vue3下事件警告的问题 |
| | | ## 1.2.2(2021-07-26) |
| | | - 修复 vue2 下条件编译导致destroyed生命周期失效的Bug |
| | | - 修复 1.2.1 引起的示例在小程序平台报错的Bug |
| | | ## 1.2.1(2021-07-22) |
| | | - 修复 动态校验表单,默认值为空的情况下校验失效的Bug |
| | | - 修复 不指定name属性时,运行报错的Bug |
| | | - 优化 label默认宽度从65调整至70,使required为true且四字时不换行 |
| | | - 优化 组件示例,新增动态校验示例代码 |
| | | - 优化 组件文档,使用方式更清晰 |
| | | ## 1.2.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.1.2(2021-06-25) |
| | | - 修复 pattern 属性在微信小程序平台无效的问题 |
| | | ## 1.1.1(2021-06-22) |
| | | - 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug |
| | | ## 1.1.0(2021-06-22) |
| | | - 修复 只写setRules方法而导致校验不生效的Bug |
| | | - 修复 由上个办法引发的错误提示文字错位的Bug |
| | | ## 1.0.48(2021-06-21) |
| | | - 修复 不设置 label 属性 ,无法设置label插槽的问题 |
| | | ## 1.0.47(2021-06-21) |
| | | - 修复 不设置label属性,label-width属性不生效的bug |
| | | - 修复 setRules 方法与rules属性冲突的问题 |
| | | ## 1.0.46(2021-06-04) |
| | | - 修复 动态删减数据导致报错的问题 |
| | | ## 1.0.45(2021-06-04) |
| | | - 新增 modelValue 属性 ,value 即将废弃 |
| | | ## 1.0.44(2021-06-02) |
| | | - 新增 uni-forms-item 可以设置单独的 rules |
| | | - 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤 |
| | | - 优化 submit 事件重命名为 validate |
| | | ## 1.0.43(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.0.42(2021-04-30) |
| | | - 修复 自定义检验器失效的问题 |
| | | ## 1.0.41(2021-03-05) |
| | | - 更新 校验器 |
| | | - 修复 表单规则设置类型为 number 的情况下,值为0校验失败的Bug |
| | | ## 1.0.40(2021-03-04) |
| | | - 修复 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug |
| | | ## 1.0.39(2021-02-05) |
| | | - 调整为uni_modules目录规范 |
| | | - 修复 校验器传入 int 等类型 ,返回String类型的Bug |
New file |
| | |
| | | <template> |
| | | <view class="uni-forms-item" |
| | | :class="['is-direction-' + localLabelPos ,border?'uni-forms-item--border':'' ,border && isFirstBorder?'is-first-border':'']"> |
| | | <slot name="label"> |
| | | <view class="uni-forms-item__label" :class="{'no-label':!label && !required}" |
| | | :style="{width:localLabelWidth,justifyContent: localLabelAlign}"> |
| | | <text v-if="required" class="is-required">*</text> |
| | | <text>{{label}}</text> |
| | | </view> |
| | | </slot> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <view class="uni-forms-item__content"> |
| | | <slot></slot> |
| | | <view class="uni-forms-item__error" :class="{'msg--active':msg}"> |
| | | <text>{{msg}}</text> |
| | | </view> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <view class="uni-forms-item__nuve-content"> |
| | | <view class="uni-forms-item__content"> |
| | | <slot></slot> |
| | | </view> |
| | | <view class="uni-forms-item__error" :class="{'msg--active':msg}"> |
| | | <text class="error-text">{{msg}}</text> |
| | | </view> |
| | | </view> |
| | | <!-- #endif --> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * uni-fomrs-item 表单子组件 |
| | | * @description uni-fomrs-item 表单子组件,提供了基础布局已经校验能力 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=2773 |
| | | * @property {Boolean} required 是否必填,左边显示红色"*"号 |
| | | * @property {String } label 输入框左边的文字提示 |
| | | * @property {Number } labelWidth label的宽度,单位px(默认65) |
| | | * @property {String } labelAlign = [left|center|right] label的文字对齐方式(默认left) |
| | | * @value left label 左侧显示 |
| | | * @value center label 居中 |
| | | * @value right label 右侧对齐 |
| | | * @property {String } errorMessage 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息 |
| | | * @property {String } name 表单域的属性名,在使用校验规则时必填 |
| | | * @property {String } leftIcon 【1.4.0废弃】label左边的图标,限 uni-ui 的图标名称 |
| | | * @property {String } iconColor 【1.4.0废弃】左边通过icon配置的图标的颜色(默认#606266) |
| | | * @property {String} validateTrigger = [bind|submit|blur] 【1.4.0废弃】校验触发器方式 默认 submit |
| | | * @value bind 发生变化时触发 |
| | | * @value submit 提交时触发 |
| | | * @value blur 失去焦点触发 |
| | | * @property {String } labelPosition = [top|left] 【1.4.0废弃】label的文字的位置(默认left) |
| | | * @value top 顶部显示 label |
| | | * @value left 左侧显示 label |
| | | */ |
| | | |
| | | export default { |
| | | name: 'uniFormsItem', |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | provide() { |
| | | return { |
| | | uniFormItem: this |
| | | } |
| | | }, |
| | | inject: { |
| | | form: { |
| | | from: 'uniForm', |
| | | default: null |
| | | }, |
| | | }, |
| | | props: { |
| | | // 表单校验规则 |
| | | rules: { |
| | | type: Array, |
| | | default () { |
| | | return null; |
| | | } |
| | | }, |
| | | // 表单域的属性名,在使用校验规则时必填 |
| | | name: { |
| | | type: [String, Array], |
| | | default: '' |
| | | }, |
| | | required: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | label: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // label的宽度 ,默认 80 |
| | | labelWidth: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // label 居中方式,默认 left 取值 left/center/right |
| | | labelAlign: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 强制显示错误信息 |
| | | errorMessage: { |
| | | type: [String, Boolean], |
| | | default: '' |
| | | }, |
| | | // 1.4.0 弃用,统一使用 form 的校验时机 |
| | | // validateTrigger: { |
| | | // type: String, |
| | | // default: '' |
| | | // }, |
| | | // 1.4.0 弃用,统一使用 form 的label 位置 |
| | | // labelPosition: { |
| | | // type: String, |
| | | // default: '' |
| | | // }, |
| | | // 1.4.0 以下属性已经废弃,请使用 #label 插槽代替 |
| | | leftIcon: String, |
| | | iconColor: { |
| | | type: String, |
| | | default: '#606266' |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | errMsg: '', |
| | | userRules: null, |
| | | localLabelAlign: 'left', |
| | | localLabelWidth: '65px', |
| | | localLabelPos: 'left', |
| | | border: false, |
| | | isFirstBorder: false, |
| | | }; |
| | | }, |
| | | computed: { |
| | | // 处理错误信息 |
| | | msg() { |
| | | return this.errorMessage || this.errMsg; |
| | | } |
| | | }, |
| | | watch: { |
| | | // 规则发生变化通知子组件更新 |
| | | 'form.formRules'(val) { |
| | | // TODO 处理头条vue3 watch不生效的问题 |
| | | // #ifndef MP-TOUTIAO |
| | | this.init() |
| | | // #endif |
| | | }, |
| | | 'form.labelWidth'(val) { |
| | | // 宽度 |
| | | this.localLabelWidth = this._labelWidthUnit(val) |
| | | |
| | | }, |
| | | 'form.labelPosition'(val) { |
| | | // 标签位置 |
| | | this.localLabelPos = this._labelPosition() |
| | | }, |
| | | 'form.labelAlign'(val) { |
| | | |
| | | } |
| | | }, |
| | | created() { |
| | | this.init(true) |
| | | if (this.name && this.form) { |
| | | // TODO 处理头条vue3 watch不生效的问题 |
| | | // #ifdef MP-TOUTIAO |
| | | this.$watch('form.formRules', () => { |
| | | this.init() |
| | | }) |
| | | // #endif |
| | | |
| | | // 监听变化 |
| | | this.$watch( |
| | | () => { |
| | | const val = this.form._getDataValue(this.name, this.form.localData) |
| | | return val |
| | | }, |
| | | (value, oldVal) => { |
| | | const isEqual = this.form._isEqual(value, oldVal) |
| | | // 简单判断前后值的变化,只有发生变化才会发生校验 |
| | | // TODO 如果 oldVal = undefined ,那么大概率是源数据里没有值导致 ,这个情况不哦校验 ,可能不严谨 ,需要在做观察 |
| | | // fix by mehaotian 暂时取消 && oldVal !== undefined ,如果formData 中不存在,可能会不校验 |
| | | if (!isEqual) { |
| | | const val = this.itemSetValue(value) |
| | | this.onFieldChange(val, false) |
| | | } |
| | | }, { |
| | | immediate: false |
| | | } |
| | | ); |
| | | } |
| | | |
| | | }, |
| | | // #ifndef VUE3 |
| | | destroyed() { |
| | | if (this.__isUnmounted) return |
| | | this.unInit() |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | unmounted() { |
| | | this.__isUnmounted = true |
| | | this.unInit() |
| | | }, |
| | | // #endif |
| | | methods: { |
| | | /** |
| | | * 外部调用方法 |
| | | * 设置规则 ,主要用于小程序自定义检验规则 |
| | | * @param {Array} rules 规则源数据 |
| | | */ |
| | | setRules(rules = null) { |
| | | this.userRules = rules |
| | | this.init(false) |
| | | }, |
| | | // 兼容老版本表单组件 |
| | | setValue() { |
| | | // console.log('setValue 方法已经弃用,请使用最新版本的 uni-forms 表单组件以及其他关联组件。'); |
| | | }, |
| | | /** |
| | | * 外部调用方法 |
| | | * 校验数据 |
| | | * @param {any} value 需要校验的数据 |
| | | * @param {boolean} 是否立即校验 |
| | | * @return {Array|null} 校验内容 |
| | | */ |
| | | async onFieldChange(value, formtrigger = true) { |
| | | const { |
| | | formData, |
| | | localData, |
| | | errShowType, |
| | | validateCheck, |
| | | validateTrigger, |
| | | _isRequiredField, |
| | | _realName |
| | | } = this.form |
| | | const name = _realName(this.name) |
| | | if (!value) { |
| | | value = this.form.formData[name] |
| | | } |
| | | // fixd by mehaotian 不在校验前清空信息,解决闪屏的问题 |
| | | // this.errMsg = ''; |
| | | |
| | | // fix by mehaotian 解决没有检验规则的情况下,抛出错误的问题 |
| | | const ruleLen = this.itemRules.rules && this.itemRules.rules.length |
| | | if (!this.validator || !ruleLen || ruleLen === 0) return; |
| | | |
| | | // 检验时机 |
| | | // let trigger = this.isTrigger(this.itemRules.validateTrigger, this.validateTrigger, validateTrigger); |
| | | const isRequiredField = _isRequiredField(this.itemRules.rules || []); |
| | | let result = null; |
| | | // 只有等于 bind 时 ,才能开启时实校验 |
| | | if (validateTrigger === 'bind' || formtrigger) { |
| | | // 校验当前表单项 |
| | | result = await this.validator.validateUpdate({ |
| | | [name]: value |
| | | }, |
| | | formData |
| | | ); |
| | | |
| | | // 判断是否必填,非必填,不填不校验,填写才校验 ,暂时只处理 undefined 和空的情况 |
| | | if (!isRequiredField && (value === undefined || value === '')) { |
| | | result = null; |
| | | } |
| | | |
| | | // 判断错误信息显示类型 |
| | | if (result && result.errorMessage) { |
| | | if (errShowType === 'undertext') { |
| | | // 获取错误信息 |
| | | this.errMsg = !result ? '' : result.errorMessage; |
| | | } |
| | | if (errShowType === 'toast') { |
| | | uni.showToast({ |
| | | title: result.errorMessage || '校验错误', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | if (errShowType === 'modal') { |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: result.errorMessage || '校验错误' |
| | | }); |
| | | } |
| | | } else { |
| | | this.errMsg = '' |
| | | } |
| | | // 通知 form 组件更新事件 |
| | | validateCheck(result ? result : null) |
| | | } else { |
| | | this.errMsg = '' |
| | | } |
| | | return result ? result : null; |
| | | }, |
| | | /** |
| | | * 初始组件数据 |
| | | */ |
| | | init(type = false) { |
| | | const { |
| | | validator, |
| | | formRules, |
| | | childrens, |
| | | formData, |
| | | localData, |
| | | _realName, |
| | | labelWidth, |
| | | _getDataValue, |
| | | _setDataValue |
| | | } = this.form || {} |
| | | // 对齐方式 |
| | | this.localLabelAlign = this._justifyContent() |
| | | // 宽度 |
| | | this.localLabelWidth = this._labelWidthUnit(labelWidth) |
| | | // 标签位置 |
| | | this.localLabelPos = this._labelPosition() |
| | | // 将需要校验的子组件加入form 队列 |
| | | this.form && type && childrens.push(this) |
| | | |
| | | if (!validator || !formRules) return |
| | | // 判断第一个 item |
| | | if (!this.form.isFirstBorder) { |
| | | this.form.isFirstBorder = true; |
| | | this.isFirstBorder = true; |
| | | } |
| | | |
| | | // 判断 group 里的第一个 item |
| | | if (this.group) { |
| | | if (!this.group.isFirstBorder) { |
| | | this.group.isFirstBorder = true; |
| | | this.isFirstBorder = true; |
| | | } |
| | | } |
| | | this.border = this.form.border; |
| | | // 获取子域的真实名称 |
| | | const name = _realName(this.name) |
| | | const itemRule = this.userRules || this.rules |
| | | if (typeof formRules === 'object' && itemRule) { |
| | | // 子规则替换父规则 |
| | | formRules[name] = { |
| | | rules: itemRule |
| | | } |
| | | validator.updateSchema(formRules); |
| | | } |
| | | // 注册校验规则 |
| | | const itemRules = formRules[name] || {} |
| | | this.itemRules = itemRules |
| | | // 注册校验函数 |
| | | this.validator = validator |
| | | // 默认值赋予 |
| | | this.itemSetValue(_getDataValue(this.name, localData)) |
| | | }, |
| | | unInit() { |
| | | if (this.form) { |
| | | const { |
| | | childrens, |
| | | formData, |
| | | _realName |
| | | } = this.form |
| | | childrens.forEach((item, index) => { |
| | | if (item === this) { |
| | | this.form.childrens.splice(index, 1) |
| | | delete formData[_realName(item.name)] |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | // 设置item 的值 |
| | | itemSetValue(value) { |
| | | const name = this.form._realName(this.name) |
| | | const rules = this.itemRules.rules || [] |
| | | const val = this.form._getValue(name, value, rules) |
| | | this.form._setDataValue(name, this.form.formData, val) |
| | | return val |
| | | }, |
| | | |
| | | /** |
| | | * 移除该表单项的校验结果 |
| | | */ |
| | | clearValidate() { |
| | | this.errMsg = ''; |
| | | }, |
| | | |
| | | // 是否显示星号 |
| | | _isRequired() { |
| | | // TODO 不根据规则显示 星号,考虑后续兼容 |
| | | // if (this.form) { |
| | | // if (this.form._isRequiredField(this.itemRules.rules || []) && this.required) { |
| | | // return true |
| | | // } |
| | | // return false |
| | | // } |
| | | return this.required |
| | | }, |
| | | |
| | | // 处理对齐方式 |
| | | _justifyContent() { |
| | | if (this.form) { |
| | | const { |
| | | labelAlign |
| | | } = this.form |
| | | let labelAli = this.labelAlign ? this.labelAlign : labelAlign; |
| | | if (labelAli === 'left') return 'flex-start'; |
| | | if (labelAli === 'center') return 'center'; |
| | | if (labelAli === 'right') return 'flex-end'; |
| | | } |
| | | return 'flex-start'; |
| | | }, |
| | | // 处理 label宽度单位 ,继承父元素的值 |
| | | _labelWidthUnit(labelWidth) { |
| | | |
| | | // if (this.form) { |
| | | // const { |
| | | // labelWidth |
| | | // } = this.form |
| | | return this.num2px(this.labelWidth ? this.labelWidth : (labelWidth || (this.label ? 65 : 'auto'))) |
| | | // } |
| | | // return '65px' |
| | | }, |
| | | // 处理 label 位置 |
| | | _labelPosition() { |
| | | if (this.form) return this.form.labelPosition || 'left' |
| | | return 'left' |
| | | |
| | | }, |
| | | |
| | | /** |
| | | * 触发时机 |
| | | * @param {Object} rule 当前规则内时机 |
| | | * @param {Object} itemRlue 当前组件时机 |
| | | * @param {Object} parentRule 父组件时机 |
| | | */ |
| | | isTrigger(rule, itemRlue, parentRule) { |
| | | // bind submit |
| | | if (rule === 'submit' || !rule) { |
| | | if (rule === undefined) { |
| | | if (itemRlue !== 'bind') { |
| | | if (!itemRlue) { |
| | | return parentRule === '' ? 'bind' : 'submit'; |
| | | } |
| | | return 'submit'; |
| | | } |
| | | return 'bind'; |
| | | } |
| | | return 'submit'; |
| | | } |
| | | return 'bind'; |
| | | }, |
| | | num2px(num) { |
| | | if (typeof num === 'number') { |
| | | return `${num}px` |
| | | } |
| | | return num |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-forms-item { |
| | | position: relative; |
| | | display: flex; |
| | | /* #ifdef APP-NVUE */ |
| | | // 在 nvue 中,使用 margin-bottom error 信息会被隐藏 |
| | | padding-bottom: 22px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | margin-bottom: 22px; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | |
| | | &__label { |
| | | display: flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | text-align: left; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | height: 36px; |
| | | padding: 0 12px 0 0; |
| | | /* #ifndef APP-NVUE */ |
| | | vertical-align: middle; |
| | | flex-shrink: 0; |
| | | /* #endif */ |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | |
| | | /* #endif */ |
| | | &.no-label { |
| | | padding: 0; |
| | | } |
| | | } |
| | | |
| | | &__content { |
| | | /* #ifndef MP-TOUTIAO */ |
| | | // display: flex; |
| | | // align-items: center; |
| | | /* #endif */ |
| | | position: relative; |
| | | font-size: 14px; |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | |
| | | /* #ifndef APP || H5 || MP-WEIXIN || APP-NVUE */ |
| | | // TODO 因为小程序平台会多一层标签节点 ,所以需要在多余节点继承当前样式 |
| | | &>uni-easyinput, |
| | | &>uni-data-picker { |
| | | width: 100%; |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | } |
| | | |
| | | & .uni-forms-item__nuve-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | flex: 1; |
| | | } |
| | | |
| | | &__error { |
| | | color: #f56c6c; |
| | | font-size: 12px; |
| | | line-height: 1; |
| | | padding-top: 4px; |
| | | position: absolute; |
| | | /* #ifndef APP-NVUE */ |
| | | top: 100%; |
| | | left: 0; |
| | | transition: transform 0.3s; |
| | | transform: translateY(-100%); |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | bottom: 5px; |
| | | /* #endif */ |
| | | |
| | | opacity: 0; |
| | | |
| | | .error-text { |
| | | // 只有 nvue 下这个样式才生效 |
| | | color: #f56c6c; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | &.msg--active { |
| | | opacity: 1; |
| | | transform: translateY(0%); |
| | | } |
| | | } |
| | | |
| | | // 位置修饰样式 |
| | | &.is-direction-left { |
| | | flex-direction: row; |
| | | } |
| | | |
| | | &.is-direction-top { |
| | | flex-direction: column; |
| | | |
| | | .uni-forms-item__label { |
| | | padding: 0 0 8px; |
| | | line-height: 1.5715; |
| | | text-align: left; |
| | | /* #ifndef APP-NVUE */ |
| | | white-space: initial; |
| | | /* #endif */ |
| | | } |
| | | } |
| | | |
| | | .is-required { |
| | | // color: $uni-color-error; |
| | | color: #dd524d; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | |
| | | |
| | | .uni-forms-item--border { |
| | | margin-bottom: 0; |
| | | padding: 10px 0; |
| | | // padding-bottom: 0; |
| | | border-top: 1px #eee solid; |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-forms-item__content { |
| | | flex-direction: column; |
| | | justify-content: flex-start; |
| | | align-items: flex-start; |
| | | |
| | | .uni-forms-item__error { |
| | | position: relative; |
| | | top: 5px; |
| | | left: 0; |
| | | padding-top: 0; |
| | | } |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | /* #ifdef APP-NVUE */ |
| | | display: flex; |
| | | flex-direction: column; |
| | | |
| | | .uni-forms-item__error { |
| | | position: relative; |
| | | top: 0px; |
| | | left: 0; |
| | | padding-top: 0; |
| | | margin-top: 5px; |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | } |
| | | |
| | | .is-first-border { |
| | | /* #ifndef APP-NVUE */ |
| | | border: none; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | border-width: 0; |
| | | /* #endif */ |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-forms"> |
| | | <form> |
| | | <slot></slot> |
| | | </form> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import Validator from './validate.js'; |
| | | import { |
| | | deepCopy, |
| | | getValue, |
| | | isRequiredField, |
| | | setDataValue, |
| | | getDataValue, |
| | | realName, |
| | | isRealName, |
| | | rawData, |
| | | isEqual |
| | | } from './utils.js' |
| | | |
| | | // #ifndef VUE3 |
| | | // 后续会慢慢废弃这个方法 |
| | | import Vue from 'vue'; |
| | | Vue.prototype.binddata = function(name, value, formName) { |
| | | if (formName) { |
| | | this.$refs[formName].setValue(name, value); |
| | | } else { |
| | | let formVm; |
| | | for (let i in this.$refs) { |
| | | const vm = this.$refs[i]; |
| | | if (vm && vm.$options && vm.$options.name === 'uniForms') { |
| | | formVm = vm; |
| | | break; |
| | | } |
| | | } |
| | | if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性'); |
| | | formVm.setValue(name, value); |
| | | } |
| | | }; |
| | | // #endif |
| | | /** |
| | | * Forms 表单 |
| | | * @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=2773 |
| | | * @property {Object} rules 表单校验规则 |
| | | * @property {String} validateTrigger = [bind|submit|blur] 校验触发器方式 默认 submit |
| | | * @value bind 发生变化时触发 |
| | | * @value submit 提交时触发 |
| | | * @value blur 失去焦点时触发 |
| | | * @property {String} labelPosition = [top|left] label 位置 默认 left |
| | | * @value top 顶部显示 label |
| | | * @value left 左侧显示 label |
| | | * @property {String} labelWidth label 宽度,默认 65px |
| | | * @property {String} labelAlign = [left|center|right] label 居中方式 默认 left |
| | | * @value left label 左侧显示 |
| | | * @value center label 居中 |
| | | * @value right label 右侧对齐 |
| | | * @property {String} errShowType = [undertext|toast|modal] 校验错误信息提示方式 |
| | | * @value undertext 错误信息在底部显示 |
| | | * @value toast 错误信息toast显示 |
| | | * @value modal 错误信息modal显示 |
| | | * @event {Function} submit 提交时触发 |
| | | * @event {Function} validate 校验结果发生变化触发 |
| | | */ |
| | | export default { |
| | | name: 'uniForms', |
| | | emits: ['validate', 'submit'], |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | props: { |
| | | // 即将弃用 |
| | | value: { |
| | | type: Object, |
| | | default () { |
| | | return null; |
| | | } |
| | | }, |
| | | // vue3 替换 value 属性 |
| | | modelValue: { |
| | | type: Object, |
| | | default () { |
| | | return null; |
| | | } |
| | | }, |
| | | // 1.4.0 开始将不支持 v-model ,且废弃 value 和 modelValue |
| | | model: { |
| | | type: Object, |
| | | default () { |
| | | return null; |
| | | } |
| | | }, |
| | | // 表单校验规则 |
| | | rules: { |
| | | type: Object, |
| | | default () { |
| | | return {}; |
| | | } |
| | | }, |
| | | //校验错误信息提示方式 默认 undertext 取值 [undertext|toast|modal] |
| | | errShowType: { |
| | | type: String, |
| | | default: 'undertext' |
| | | }, |
| | | // 校验触发器方式 默认 bind 取值 [bind|submit] |
| | | validateTrigger: { |
| | | type: String, |
| | | default: 'submit' |
| | | }, |
| | | // label 位置,默认 left 取值 top/left |
| | | labelPosition: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | // label 宽度 |
| | | labelWidth: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // label 居中方式,默认 left 取值 left/center/right |
| | | labelAlign: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | provide() { |
| | | return { |
| | | uniForm: this |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | // 表单本地值的记录,不应该与传如的值进行关联 |
| | | formData: {}, |
| | | formRules: {} |
| | | }; |
| | | }, |
| | | computed: { |
| | | // 计算数据源变化的 |
| | | localData() { |
| | | const localVal = this.model || this.modelValue || this.value |
| | | if (localVal) { |
| | | return deepCopy(localVal) |
| | | } |
| | | return {} |
| | | } |
| | | }, |
| | | watch: { |
| | | // 监听数据变化 ,暂时不使用,需要单独赋值 |
| | | // localData: {}, |
| | | // 监听规则变化 |
| | | rules: { |
| | | handler: function(val, oldVal) { |
| | | this.setRules(val) |
| | | }, |
| | | deep: true, |
| | | immediate: true |
| | | } |
| | | }, |
| | | created() { |
| | | // #ifdef VUE3 |
| | | let getbinddata = getApp().$vm.$.appContext.config.globalProperties.binddata |
| | | if (!getbinddata) { |
| | | getApp().$vm.$.appContext.config.globalProperties.binddata = function(name, value, formName) { |
| | | if (formName) { |
| | | this.$refs[formName].setValue(name, value); |
| | | } else { |
| | | let formVm; |
| | | for (let i in this.$refs) { |
| | | const vm = this.$refs[i]; |
| | | if (vm && vm.$options && vm.$options.name === 'uniForms') { |
| | | formVm = vm; |
| | | break; |
| | | } |
| | | } |
| | | if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性'); |
| | | formVm.setValue(name, value); |
| | | } |
| | | } |
| | | } |
| | | // #endif |
| | | |
| | | // 子组件实例数组 |
| | | this.childrens = [] |
| | | // TODO 兼容旧版 uni-data-picker ,新版本中无效,只是避免报错 |
| | | this.inputChildrens = [] |
| | | this.setRules(this.rules) |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 外部调用方法 |
| | | * 设置规则 ,主要用于小程序自定义检验规则 |
| | | * @param {Array} rules 规则源数据 |
| | | */ |
| | | setRules(rules) { |
| | | // TODO 有可能子组件合并规则的时机比这个要早,所以需要合并对象 ,而不是直接赋值,可能会被覆盖 |
| | | this.formRules = Object.assign({}, this.formRules, rules) |
| | | // 初始化校验函数 |
| | | this.validator = new Validator(rules); |
| | | }, |
| | | |
| | | /** |
| | | * 外部调用方法 |
| | | * 设置数据,用于设置表单数据,公开给用户使用 , 不支持在动态表单中使用 |
| | | * @param {Object} key |
| | | * @param {Object} value |
| | | */ |
| | | setValue(key, value) { |
| | | let example = this.childrens.find(child => child.name === key); |
| | | if (!example) return null; |
| | | this.formData[key] = getValue(key, value, (this.formRules[key] && this.formRules[key].rules) || []) |
| | | return example.onFieldChange(this.formData[key]); |
| | | }, |
| | | |
| | | /** |
| | | * 外部调用方法 |
| | | * 手动提交校验表单 |
| | | * 对整个表单进行校验的方法,参数为一个回调函数。 |
| | | * @param {Array} keepitem 保留不参与校验的字段 |
| | | * @param {type} callback 方法回调 |
| | | */ |
| | | validate(keepitem, callback) { |
| | | return this.checkAll(this.formData, keepitem, callback); |
| | | }, |
| | | |
| | | /** |
| | | * 外部调用方法 |
| | | * 部分表单校验 |
| | | * @param {Array|String} props 需要校验的字段 |
| | | * @param {Function} 回调函数 |
| | | */ |
| | | validateField(props = [], callback) { |
| | | props = [].concat(props); |
| | | let invalidFields = {}; |
| | | this.childrens.forEach(item => { |
| | | const name = realName(item.name) |
| | | if (props.indexOf(name) !== -1) { |
| | | invalidFields = Object.assign({}, invalidFields, { |
| | | [name]: this.formData[name] |
| | | }); |
| | | } |
| | | }); |
| | | return this.checkAll(invalidFields, [], callback); |
| | | }, |
| | | |
| | | /** |
| | | * 外部调用方法 |
| | | * 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果 |
| | | * @param {Array|String} props 需要移除校验的字段 ,不填为所有 |
| | | */ |
| | | clearValidate(props = []) { |
| | | props = [].concat(props); |
| | | this.childrens.forEach(item => { |
| | | if (props.length === 0) { |
| | | item.errMsg = ''; |
| | | } else { |
| | | const name = realName(item.name) |
| | | if (props.indexOf(name) !== -1) { |
| | | item.errMsg = ''; |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | /** |
| | | * 外部调用方法 ,即将废弃 |
| | | * 手动提交校验表单 |
| | | * 对整个表单进行校验的方法,参数为一个回调函数。 |
| | | * @param {Array} keepitem 保留不参与校验的字段 |
| | | * @param {type} callback 方法回调 |
| | | */ |
| | | submit(keepitem, callback, type) { |
| | | for (let i in this.dataValue) { |
| | | const itemData = this.childrens.find(v => v.name === i); |
| | | if (itemData) { |
| | | if (this.formData[i] === undefined) { |
| | | this.formData[i] = this._getValue(i, this.dataValue[i]); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!type) { |
| | | console.warn('submit 方法即将废弃,请使用validate方法代替!'); |
| | | } |
| | | |
| | | return this.checkAll(this.formData, keepitem, callback, 'submit'); |
| | | }, |
| | | |
| | | // 校验所有 |
| | | async checkAll(invalidFields, keepitem, callback, type) { |
| | | // 不存在校验规则 ,则停止校验流程 |
| | | if (!this.validator) return |
| | | let childrens = [] |
| | | // 处理参与校验的item实例 |
| | | for (let i in invalidFields) { |
| | | const item = this.childrens.find(v => realName(v.name) === i) |
| | | if (item) { |
| | | childrens.push(item) |
| | | } |
| | | } |
| | | |
| | | // 如果validate第一个参数是funciont ,那就走回调 |
| | | if (!callback && typeof keepitem === 'function') { |
| | | callback = keepitem; |
| | | } |
| | | |
| | | let promise; |
| | | // 如果不存在回调,那么使用 Promise 方式返回 |
| | | if (!callback && typeof callback !== 'function' && Promise) { |
| | | promise = new Promise((resolve, reject) => { |
| | | callback = function(valid, invalidFields) { |
| | | !valid ? resolve(invalidFields) : reject(valid); |
| | | }; |
| | | }); |
| | | } |
| | | |
| | | let results = []; |
| | | // 避免引用错乱 ,建议拷贝对象处理 |
| | | let tempFormData = JSON.parse(JSON.stringify(invalidFields)) |
| | | // 所有子组件参与校验,使用 for 可以使用 awiat |
| | | for (let i in childrens) { |
| | | const child = childrens[i] |
| | | let name = realName(child.name); |
| | | const result = await child.onFieldChange(tempFormData[name]); |
| | | if (result) { |
| | | results.push(result); |
| | | // toast ,modal 只需要执行第一次就可以 |
| | | if (this.errShowType === 'toast' || this.errShowType === 'modal') break; |
| | | } |
| | | } |
| | | |
| | | |
| | | if (Array.isArray(results)) { |
| | | if (results.length === 0) results = null; |
| | | } |
| | | if (Array.isArray(keepitem)) { |
| | | keepitem.forEach(v => { |
| | | let vName = realName(v); |
| | | let value = getDataValue(v, this.localData) |
| | | if (value !== undefined) { |
| | | tempFormData[vName] = value |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // TODO submit 即将废弃 |
| | | if (type === 'submit') { |
| | | this.$emit('submit', { |
| | | detail: { |
| | | value: tempFormData, |
| | | errors: results |
| | | } |
| | | }); |
| | | } else { |
| | | this.$emit('validate', results); |
| | | } |
| | | |
| | | // const resetFormData = rawData(tempFormData, this.localData, this.name) |
| | | let resetFormData = {} |
| | | resetFormData = rawData(tempFormData, this.name) |
| | | callback && typeof callback === 'function' && callback(results, resetFormData); |
| | | |
| | | if (promise && callback) { |
| | | return promise; |
| | | } else { |
| | | return null; |
| | | } |
| | | |
| | | }, |
| | | |
| | | /** |
| | | * 返回validate事件 |
| | | * @param {Object} result |
| | | */ |
| | | validateCheck(result) { |
| | | this.$emit('validate', result); |
| | | }, |
| | | _getValue: getValue, |
| | | _isRequiredField: isRequiredField, |
| | | _setDataValue: setDataValue, |
| | | _getDataValue: getDataValue, |
| | | _realName: realName, |
| | | _isRealName: isRealName, |
| | | _isEqual: isEqual |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-forms {} |
| | | </style> |
src/uni_modules/uni-forms/components/uni-forms/utils.js
src/uni_modules/uni-forms/components/uni-forms/validate.js
src/uni_modules/uni-forms/package.json
src/uni_modules/uni-forms/readme.md
src/uni_modules/uni-goods-nav/changelog.md
src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json
src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js
src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json
src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json
src/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue
src/uni_modules/uni-goods-nav/package.json
src/uni_modules/uni-goods-nav/readme.md
src/uni_modules/uni-grid/changelog.md
src/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue
src/uni_modules/uni-grid/components/uni-grid/uni-grid.vue
src/uni_modules/uni-grid/package.json
src/uni_modules/uni-grid/readme.md
src/uni_modules/uni-group/changelog.md
src/uni_modules/uni-group/components/uni-group/uni-group.vue
src/uni_modules/uni-group/package.json
src/uni_modules/uni-group/readme.md
src/uni_modules/uni-icons/changelog.md
src/uni_modules/uni-icons/components/uni-icons/icons.js
src/uni_modules/uni-icons/components/uni-icons/uni-icons.vue
src/uni_modules/uni-icons/components/uni-icons/uniicons.css
src/uni_modules/uni-icons/components/uni-icons/uniicons.ttf
src/uni_modules/uni-icons/package.json
src/uni_modules/uni-icons/readme.md
src/uni_modules/uni-indexed-list/changelog.md
src/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue
src/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue
src/uni_modules/uni-indexed-list/package.json
src/uni_modules/uni-indexed-list/readme.md
src/uni_modules/uni-link/changelog.md
src/uni_modules/uni-link/components/uni-link/uni-link.vue
src/uni_modules/uni-link/package.json
src/uni_modules/uni-link/readme.md
src/uni_modules/uni-list/changelog.md
src/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue
src/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss
src/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue
src/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue
src/uni_modules/uni-list/components/uni-list/uni-list.vue
src/uni_modules/uni-list/components/uni-list/uni-refresh.vue
src/uni_modules/uni-list/components/uni-list/uni-refresh.wxs
src/uni_modules/uni-list/package.json
src/uni_modules/uni-list/readme.md
src/uni_modules/uni-load-more/changelog.md
src/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json
src/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js
src/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json
src/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json
src/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue
src/uni_modules/uni-load-more/package.json
src/uni_modules/uni-load-more/readme.md
src/uni_modules/uni-nav-bar/changelog.md
src/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue
src/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue
src/uni_modules/uni-nav-bar/package.json
src/uni_modules/uni-nav-bar/readme.md
src/uni_modules/uni-notice-bar/changelog.md
src/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue
src/uni_modules/uni-notice-bar/package.json
src/uni_modules/uni-notice-bar/readme.md
src/uni_modules/uni-number-box/changelog.md
src/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue
src/uni_modules/uni-number-box/package.json
src/uni_modules/uni-number-box/readme.md
src/uni_modules/uni-pagination/changelog.md
src/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json
src/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json
src/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json
src/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js
src/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json
src/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json
src/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue
src/uni_modules/uni-pagination/package.json
src/uni_modules/uni-pagination/readme.md
src/uni_modules/uni-popup/changelog.md
src/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js
src/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue
src/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue
src/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue
src/uni_modules/uni-popup/components/uni-popup/i18n/en.json
src/uni_modules/uni-popup/components/uni-popup/i18n/index.js
src/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json
src/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json
src/uni_modules/uni-popup/components/uni-popup/keypress.js
src/uni_modules/uni-popup/components/uni-popup/popup.js
src/uni_modules/uni-popup/components/uni-popup/uni-popup.vue
src/uni_modules/uni-popup/package.json
src/uni_modules/uni-popup/readme.md
src/uni_modules/uni-rate/changelog.md
src/uni_modules/uni-rate/components/uni-rate/uni-rate.vue
src/uni_modules/uni-rate/package.json
src/uni_modules/uni-rate/readme.md
src/uni_modules/uni-row/changelog.md
src/uni_modules/uni-row/components/uni-col/uni-col.vue
src/uni_modules/uni-row/components/uni-row/uni-row.vue
src/uni_modules/uni-row/package.json
src/uni_modules/uni-row/readme.md
src/uni_modules/uni-scss/changelog.md
src/uni_modules/uni-scss/index.scss
src/uni_modules/uni-scss/package.json
src/uni_modules/uni-scss/readme.md
src/uni_modules/uni-scss/styles/index.scss
src/uni_modules/uni-scss/styles/setting/_border.scss
src/uni_modules/uni-scss/styles/setting/_color.scss
src/uni_modules/uni-scss/styles/setting/_radius.scss
src/uni_modules/uni-scss/styles/setting/_space.scss
src/uni_modules/uni-scss/styles/setting/_styles.scss
src/uni_modules/uni-scss/styles/setting/_text.scss
src/uni_modules/uni-scss/styles/setting/_variables.scss
src/uni_modules/uni-scss/styles/tools/functions.scss
src/uni_modules/uni-scss/theme.scss
src/uni_modules/uni-scss/variables.scss
src/uni_modules/uni-search-bar/changelog.md
src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json
src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js
src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json
src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json
src/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue
src/uni_modules/uni-search-bar/package.json
src/uni_modules/uni-search-bar/readme.md
src/uni_modules/uni-section/changelog.md
src/uni_modules/uni-section/components/uni-section/uni-section.vue
src/uni_modules/uni-section/package.json
src/uni_modules/uni-section/readme.md
src/uni_modules/uni-segmented-control/changelog.md
src/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue
src/uni_modules/uni-segmented-control/package.json
src/uni_modules/uni-segmented-control/readme.md
src/uni_modules/uni-steps/changelog.md
src/uni_modules/uni-steps/components/uni-steps/uni-steps.vue
src/uni_modules/uni-steps/package.json
src/uni_modules/uni-steps/readme.md
src/uni_modules/uni-swipe-action/changelog.md
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue
src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs
src/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue
src/uni_modules/uni-swipe-action/package.json
src/uni_modules/uni-swipe-action/readme.md
src/uni_modules/uni-swiper-dot/changelog.md
src/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue
src/uni_modules/uni-swiper-dot/package.json
src/uni_modules/uni-swiper-dot/readme.md
src/uni_modules/uni-table/changelog.md
src/uni_modules/uni-table/components/uni-table/uni-table.vue
src/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue
src/uni_modules/uni-table/components/uni-td/uni-td.vue
src/uni_modules/uni-table/components/uni-th/filter-dropdown.vue
src/uni_modules/uni-table/components/uni-th/uni-th.vue
src/uni_modules/uni-table/components/uni-thead/uni-thead.vue
src/uni_modules/uni-table/components/uni-tr/table-checkbox.vue
src/uni_modules/uni-table/components/uni-tr/uni-tr.vue
src/uni_modules/uni-table/i18n/en.json
src/uni_modules/uni-table/i18n/es.json
src/uni_modules/uni-table/i18n/fr.json
src/uni_modules/uni-table/i18n/index.js
src/uni_modules/uni-table/i18n/zh-Hans.json
src/uni_modules/uni-table/i18n/zh-Hant.json
src/uni_modules/uni-table/package.json
src/uni_modules/uni-table/readme.md
src/uni_modules/uni-tag/changelog.md
src/uni_modules/uni-tag/components/uni-tag/uni-tag.vue
src/uni_modules/uni-tag/package.json
src/uni_modules/uni-tag/readme.md
src/uni_modules/uni-test/changelog.md
src/uni_modules/uni-test/components/uni-test/uni-test.vue
src/uni_modules/uni-test/package.json
src/uni_modules/uni-test/readme.md
src/uni_modules/uni-title/changelog.md
src/uni_modules/uni-title/components/uni-title/uni-title.vue
src/uni_modules/uni-title/package.json
src/uni_modules/uni-title/readme.md
src/uni_modules/uni-tooltip/changelog.md
src/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue
src/uni_modules/uni-tooltip/package.json
src/uni_modules/uni-tooltip/readme.md
src/uni_modules/uni-transition/changelog.md
src/uni_modules/uni-transition/components/uni-transition/createAnimation.js
src/uni_modules/uni-transition/components/uni-transition/uni-transition.vue
src/uni_modules/uni-transition/package.json
src/uni_modules/uni-transition/readme.md
src/uni_modules/uni-ui/changelog.md
src/uni_modules/uni-ui/components/uni-ui/uni-ui.vue
src/uni_modules/uni-ui/package.json
src/uni_modules/uni-ui/readme.md |