New file |
| | |
| | | <script> |
| | | export default { |
| | | onLaunch: function() { |
| | | console.log('App Launch') |
| | | }, |
| | | onShow: function() { |
| | | console.log('App Show') |
| | | }, |
| | | onHide: function() { |
| | | console.log('App Hide') |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | /*每个页面公共css */ |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <text v-if="text" :class="inverted ? 'uni-badge--' + type + ' uni-badge--' + size + ' uni-badge--' + type + '-inverted' : 'uni-badge--' + type + ' uni-badge--' + size" |
| | | :style="badgeStyle" class="uni-badge" @click="onClick()">{{ text }}</text> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Badge 数字角标 |
| | | * @description 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=21 |
| | | * @property {String} text 角标内容 |
| | | * @property {String} type = [default|primary|success|warning|error] 颜色类型 |
| | | * @value default 灰色 |
| | | * @value primary 蓝色 |
| | | * @value success 绿色 |
| | | * @value warning 黄色 |
| | | * @value error 红色 |
| | | * @property {String} size = [normal|small] Badge 大小 |
| | | * @value normal 一般尺寸 |
| | | * @value small 小尺寸 |
| | | * @property {String} inverted = [true|false] 是否无需背景颜色 |
| | | * @event {Function} click 点击 Badge 触发事件 |
| | | * @example <uni-badge text="1"></uni-badge> |
| | | */ |
| | | export default { |
| | | name: 'UniBadge', |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: 'default' |
| | | }, |
| | | inverted: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | text: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | size: { |
| | | type: String, |
| | | default: 'normal' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | badgeStyle: '' |
| | | }; |
| | | }, |
| | | watch: { |
| | | text() { |
| | | this.setStyle() |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.setStyle() |
| | | }, |
| | | methods: { |
| | | setStyle() { |
| | | this.badgeStyle = `width: ${String(this.text).length * 8 + 12}px` |
| | | }, |
| | | onClick() { |
| | | this.$emit('click'); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $bage-size: 12px; |
| | | $bage-small: scale(0.8); |
| | | $bage-height: 20px; |
| | | |
| | | .uni-badge { |
| | | /* #ifndef APP-PLUS */ |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | flex-direction: row; |
| | | height: $bage-height; |
| | | line-height: $bage-height; |
| | | color: $uni-text-color; |
| | | border-radius: 100px; |
| | | background-color: $uni-bg-color-hover; |
| | | background-color: transparent; |
| | | text-align: center; |
| | | font-family: 'Helvetica Neue', Helvetica, sans-serif; |
| | | font-size: $bage-size; |
| | | padding: 0px 6px; |
| | | } |
| | | |
| | | .uni-badge--inverted { |
| | | padding: 0 5px 0 0; |
| | | color: $uni-bg-color-hover; |
| | | } |
| | | |
| | | .uni-badge--default { |
| | | color: $uni-text-color; |
| | | background-color: $uni-bg-color-hover; |
| | | } |
| | | |
| | | .uni-badge--default-inverted { |
| | | color: $uni-text-color-grey; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | .uni-badge--primary { |
| | | color: $uni-text-color-inverse; |
| | | background-color: $uni-color-primary; |
| | | } |
| | | |
| | | .uni-badge--primary-inverted { |
| | | color: $uni-color-primary; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | .uni-badge--success { |
| | | color: $uni-text-color-inverse; |
| | | background-color: $uni-color-success; |
| | | } |
| | | |
| | | .uni-badge--success-inverted { |
| | | color: $uni-color-success; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | .uni-badge--warning { |
| | | color: $uni-text-color-inverse; |
| | | background-color: $uni-color-warning; |
| | | } |
| | | |
| | | .uni-badge--warning-inverted { |
| | | color: $uni-color-warning; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | .uni-badge--error { |
| | | color: $uni-text-color-inverse; |
| | | background-color: $uni-color-error; |
| | | } |
| | | |
| | | .uni-badge--error-inverted { |
| | | color: $uni-color-error; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | .uni-badge--small { |
| | | transform: $bage-small; |
| | | transform-origin: center center; |
| | | } |
| | | </style> |
New file |
| | |
| | | /** |
| | | * @1900-2100区间内的公历、农历互转 |
| | | * @charset UTF-8 |
| | | * @github https://github.com/jjonline/calendar.js |
| | | * @Author Jea杨(JJonline@JJonline.Cn) |
| | | * @Time 2014-7-21 |
| | | * @Time 2016-8-13 Fixed 2033hex、Attribution Annals |
| | | * @Time 2016-9-25 Fixed lunar LeapMonth Param Bug |
| | | * @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year |
| | | * @Version 1.0.3 |
| | | * @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0] |
| | | * @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0] |
| | | */ |
| | | /* eslint-disable */ |
| | | var calendar = { |
| | | |
| | | /** |
| | | * 农历1900-2100的润大小信息表 |
| | | * @Array Of Property |
| | | * @return Hex |
| | | */ |
| | | lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909 |
| | | 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919 |
| | | 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929 |
| | | 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939 |
| | | 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949 |
| | | 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959 |
| | | 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969 |
| | | 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979 |
| | | 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989 |
| | | 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999 |
| | | 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009 |
| | | 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019 |
| | | 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029 |
| | | 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039 |
| | | 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049 |
| | | /** Add By JJonline@JJonline.Cn**/ |
| | | 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059 |
| | | 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069 |
| | | 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079 |
| | | 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089 |
| | | 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099 |
| | | 0x0d520], // 2100 |
| | | |
| | | /** |
| | | * 公历每个月份的天数普通表 |
| | | * @Array Of Property |
| | | * @return Number |
| | | */ |
| | | solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], |
| | | |
| | | /** |
| | | * 天干地支之天干速查表 |
| | | * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"] |
| | | * @return Cn string |
| | | */ |
| | | Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'], |
| | | |
| | | /** |
| | | * 天干地支之地支速查表 |
| | | * @Array Of Property |
| | | * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"] |
| | | * @return Cn string |
| | | */ |
| | | Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'], |
| | | |
| | | /** |
| | | * 天干地支之地支速查表<=>生肖 |
| | | * @Array Of Property |
| | | * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"] |
| | | * @return Cn string |
| | | */ |
| | | Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'], |
| | | |
| | | /** |
| | | * 24节气速查表 |
| | | * @Array Of Property |
| | | * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"] |
| | | * @return Cn string |
| | | */ |
| | | solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'], |
| | | |
| | | /** |
| | | * 1900-2100各年的24节气日期速查表 |
| | | * @Array Of Property |
| | | * @return 0x string For splice |
| | | */ |
| | | sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', |
| | | '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', |
| | | 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f', |
| | | '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa', |
| | | '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', |
| | | '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f', |
| | | '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', |
| | | '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f', |
| | | '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
| | | '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
| | | '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722', |
| | | '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
| | | '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
| | | '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
| | | '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
| | | '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
| | | '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
| | | '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
| | | '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721', |
| | | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2', |
| | | '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
| | | '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
| | | '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
| | | '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
| | | '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721', |
| | | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5', |
| | | '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722', |
| | | '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', |
| | | '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
| | | '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721', |
| | | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd', |
| | | '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35', |
| | | '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
| | | '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721', |
| | | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5', |
| | | '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35', |
| | | '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
| | | '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
| | | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35', |
| | | '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'], |
| | | |
| | | /** |
| | | * 数字转中文速查表 |
| | | * @Array Of Property |
| | | * @trans ['日','一','二','三','四','五','六','七','八','九','十'] |
| | | * @return Cn string |
| | | */ |
| | | nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'], |
| | | |
| | | /** |
| | | * 日期转农历称呼速查表 |
| | | * @Array Of Property |
| | | * @trans ['初','十','廿','卅'] |
| | | * @return Cn string |
| | | */ |
| | | nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'], |
| | | |
| | | /** |
| | | * 月份转农历称呼速查表 |
| | | * @Array Of Property |
| | | * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊'] |
| | | * @return Cn string |
| | | */ |
| | | nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'], |
| | | |
| | | /** |
| | | * 返回农历y年一整年的总天数 |
| | | * @param lunar Year |
| | | * @return Number |
| | | * @eg:var count = calendar.lYearDays(1987) ;//count=387 |
| | | */ |
| | | lYearDays: function (y) { |
| | | var i; var sum = 348 |
| | | for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 } |
| | | return (sum + this.leapDays(y)) |
| | | }, |
| | | |
| | | /** |
| | | * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 |
| | | * @param lunar Year |
| | | * @return Number (0-12) |
| | | * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6 |
| | | */ |
| | | leapMonth: function (y) { // 闰字编码 \u95f0 |
| | | return (this.lunarInfo[y - 1900] & 0xf) |
| | | }, |
| | | |
| | | /** |
| | | * 返回农历y年闰月的天数 若该年没有闰月则返回0 |
| | | * @param lunar Year |
| | | * @return Number (0、29、30) |
| | | * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29 |
| | | */ |
| | | leapDays: function (y) { |
| | | if (this.leapMonth(y)) { |
| | | return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29) |
| | | } |
| | | return (0) |
| | | }, |
| | | |
| | | /** |
| | | * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 |
| | | * @param lunar Year |
| | | * @return Number (-1、29、30) |
| | | * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29 |
| | | */ |
| | | monthDays: function (y, m) { |
| | | if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1 |
| | | return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29) |
| | | }, |
| | | |
| | | /** |
| | | * 返回公历(!)y年m月的天数 |
| | | * @param solar Year |
| | | * @return Number (-1、28、29、30、31) |
| | | * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30 |
| | | */ |
| | | solarDays: function (y, m) { |
| | | if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 |
| | | var ms = m - 1 |
| | | if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29 |
| | | return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28) |
| | | } else { |
| | | return (this.solarMonth[ms]) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 农历年份转换为干支纪年 |
| | | * @param lYear 农历年的年份数 |
| | | * @return Cn string |
| | | */ |
| | | toGanZhiYear: function (lYear) { |
| | | var ganKey = (lYear - 3) % 10 |
| | | var zhiKey = (lYear - 3) % 12 |
| | | if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干 |
| | | if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支 |
| | | return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1] |
| | | }, |
| | | |
| | | /** |
| | | * 公历月、日判断所属星座 |
| | | * @param cMonth [description] |
| | | * @param cDay [description] |
| | | * @return Cn string |
| | | */ |
| | | toAstro: function (cMonth, cDay) { |
| | | var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf' |
| | | var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22] |
| | | return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座 |
| | | }, |
| | | |
| | | /** |
| | | * 传入offset偏移量返回干支 |
| | | * @param offset 相对甲子的偏移量 |
| | | * @return Cn string |
| | | */ |
| | | toGanZhi: function (offset) { |
| | | return this.Gan[offset % 10] + this.Zhi[offset % 12] |
| | | }, |
| | | |
| | | /** |
| | | * 传入公历(!)y年获得该年第n个节气的公历日期 |
| | | * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 |
| | | * @return day Number |
| | | * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春 |
| | | */ |
| | | getTerm: function (y, n) { |
| | | if (y < 1900 || y > 2100) { return -1 } |
| | | if (n < 1 || n > 24) { return -1 } |
| | | var _table = this.sTermInfo[y - 1900] |
| | | var _info = [ |
| | | parseInt('0x' + _table.substr(0, 5)).toString(), |
| | | parseInt('0x' + _table.substr(5, 5)).toString(), |
| | | parseInt('0x' + _table.substr(10, 5)).toString(), |
| | | parseInt('0x' + _table.substr(15, 5)).toString(), |
| | | parseInt('0x' + _table.substr(20, 5)).toString(), |
| | | parseInt('0x' + _table.substr(25, 5)).toString() |
| | | ] |
| | | var _calday = [ |
| | | _info[0].substr(0, 1), |
| | | _info[0].substr(1, 2), |
| | | _info[0].substr(3, 1), |
| | | _info[0].substr(4, 2), |
| | | |
| | | _info[1].substr(0, 1), |
| | | _info[1].substr(1, 2), |
| | | _info[1].substr(3, 1), |
| | | _info[1].substr(4, 2), |
| | | |
| | | _info[2].substr(0, 1), |
| | | _info[2].substr(1, 2), |
| | | _info[2].substr(3, 1), |
| | | _info[2].substr(4, 2), |
| | | |
| | | _info[3].substr(0, 1), |
| | | _info[3].substr(1, 2), |
| | | _info[3].substr(3, 1), |
| | | _info[3].substr(4, 2), |
| | | |
| | | _info[4].substr(0, 1), |
| | | _info[4].substr(1, 2), |
| | | _info[4].substr(3, 1), |
| | | _info[4].substr(4, 2), |
| | | |
| | | _info[5].substr(0, 1), |
| | | _info[5].substr(1, 2), |
| | | _info[5].substr(3, 1), |
| | | _info[5].substr(4, 2) |
| | | ] |
| | | return parseInt(_calday[n - 1]) |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历数字月份返回汉语通俗表示法 |
| | | * @param lunar month |
| | | * @return Cn string |
| | | * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月' |
| | | */ |
| | | toChinaMonth: function (m) { // 月 => \u6708 |
| | | if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1 |
| | | var s = this.nStr3[m - 1] |
| | | s += '\u6708'// 加上月字 |
| | | return s |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历日期数字返回汉字表示法 |
| | | * @param lunar day |
| | | * @return Cn string |
| | | * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一' |
| | | */ |
| | | toChinaDay: function (d) { // 日 => \u65e5 |
| | | var s |
| | | switch (d) { |
| | | case 10: |
| | | s = '\u521d\u5341'; break |
| | | case 20: |
| | | s = '\u4e8c\u5341'; break |
| | | break |
| | | case 30: |
| | | s = '\u4e09\u5341'; break |
| | | break |
| | | default : |
| | | s = this.nStr2[Math.floor(d / 10)] |
| | | s += this.nStr1[d % 10] |
| | | } |
| | | return (s) |
| | | }, |
| | | |
| | | /** |
| | | * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” |
| | | * @param y year |
| | | * @return Cn string |
| | | * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔' |
| | | */ |
| | | getAnimal: function (y) { |
| | | return this.Animals[(y - 4) % 12] |
| | | }, |
| | | |
| | | /** |
| | | * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON |
| | | * @param y solar year |
| | | * @param m solar month |
| | | * @param d solar day |
| | | * @return JSON object |
| | | * @eg:console.log(calendar.solar2lunar(1987,11,01)); |
| | | */ |
| | | solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31 |
| | | // 年份限定、上限 |
| | | if (y < 1900 || y > 2100) { |
| | | return -1// undefined转换为数字变为NaN |
| | | } |
| | | // 公历传参最下限 |
| | | if (y == 1900 && m == 1 && d < 31) { |
| | | return -1 |
| | | } |
| | | // 未传参 获得当天 |
| | | if (!y) { |
| | | var objDate = new Date() |
| | | } else { |
| | | var objDate = new Date(y, parseInt(m) - 1, d) |
| | | } |
| | | var i; var leap = 0; var temp = 0 |
| | | // 修正ymd参数 |
| | | var y = objDate.getFullYear() |
| | | var m = objDate.getMonth() + 1 |
| | | var d = objDate.getDate() |
| | | var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000 |
| | | for (i = 1900; i < 2101 && offset > 0; i++) { |
| | | temp = this.lYearDays(i) |
| | | offset -= temp |
| | | } |
| | | if (offset < 0) { |
| | | offset += temp; i-- |
| | | } |
| | | |
| | | // 是否今天 |
| | | var isTodayObj = new Date() |
| | | var isToday = false |
| | | if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) { |
| | | isToday = true |
| | | } |
| | | // 星期几 |
| | | var nWeek = objDate.getDay() |
| | | var cWeek = this.nStr1[nWeek] |
| | | // 数字表示周几顺应天朝周一开始的惯例 |
| | | if (nWeek == 0) { |
| | | nWeek = 7 |
| | | } |
| | | // 农历年 |
| | | var year = i |
| | | var leap = this.leapMonth(i) // 闰哪个月 |
| | | var isLeap = false |
| | | |
| | | // 效验闰月 |
| | | for (i = 1; i < 13 && offset > 0; i++) { |
| | | // 闰月 |
| | | if (leap > 0 && i == (leap + 1) && isLeap == false) { |
| | | --i |
| | | isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数 |
| | | } else { |
| | | temp = this.monthDays(year, i)// 计算农历普通月天数 |
| | | } |
| | | // 解除闰月 |
| | | if (isLeap == true && i == (leap + 1)) { isLeap = false } |
| | | offset -= temp |
| | | } |
| | | // 闰月导致数组下标重叠取反 |
| | | if (offset == 0 && leap > 0 && i == leap + 1) { |
| | | if (isLeap) { |
| | | isLeap = false |
| | | } else { |
| | | isLeap = true; --i |
| | | } |
| | | } |
| | | if (offset < 0) { |
| | | offset += temp; --i |
| | | } |
| | | // 农历月 |
| | | var month = i |
| | | // 农历日 |
| | | var day = offset + 1 |
| | | // 天干地支处理 |
| | | var sm = m - 1 |
| | | var gzY = this.toGanZhiYear(year) |
| | | |
| | | // 当月的两个节气 |
| | | // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year` |
| | | var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始 |
| | | var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始 |
| | | |
| | | // 依据12节气修正干支月 |
| | | var gzM = this.toGanZhi((y - 1900) * 12 + m + 11) |
| | | if (d >= firstNode) { |
| | | gzM = this.toGanZhi((y - 1900) * 12 + m + 12) |
| | | } |
| | | |
| | | // 传入的日期的节气与否 |
| | | var isTerm = false |
| | | var Term = null |
| | | if (firstNode == d) { |
| | | isTerm = true |
| | | Term = this.solarTerm[m * 2 - 2] |
| | | } |
| | | if (secondNode == d) { |
| | | isTerm = true |
| | | Term = this.solarTerm[m * 2 - 1] |
| | | } |
| | | // 日柱 当月一日与 1900/1/1 相差天数 |
| | | var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10 |
| | | var gzD = this.toGanZhi(dayCyclical + d - 1) |
| | | // 该日期所属的星座 |
| | | var astro = this.toAstro(m, d) |
| | | |
| | | return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro } |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON |
| | | * @param y lunar year |
| | | * @param m lunar month |
| | | * @param d lunar day |
| | | * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] |
| | | * @return JSON object |
| | | * @eg:console.log(calendar.lunar2solar(1987,9,10)); |
| | | */ |
| | | lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1 |
| | | var isLeapMonth = !!isLeapMonth |
| | | var leapOffset = 0 |
| | | var leapMonth = this.leapMonth(y) |
| | | var leapDay = this.leapDays(y) |
| | | if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 |
| | | if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值 |
| | | var day = this.monthDays(y, m) |
| | | var _day = day |
| | | // bugFix 2016-9-25 |
| | | // if month is leap, _day use leapDays method |
| | | if (isLeapMonth) { |
| | | _day = this.leapDays(y, m) |
| | | } |
| | | if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验 |
| | | |
| | | // 计算农历的时间差 |
| | | var offset = 0 |
| | | for (var i = 1900; i < y; i++) { |
| | | offset += this.lYearDays(i) |
| | | } |
| | | var leap = 0; var isAdd = false |
| | | for (var i = 1; i < m; i++) { |
| | | leap = this.leapMonth(y) |
| | | if (!isAdd) { // 处理闰月 |
| | | if (leap <= i && leap > 0) { |
| | | offset += this.leapDays(y); isAdd = true |
| | | } |
| | | } |
| | | offset += this.monthDays(y, i) |
| | | } |
| | | // 转换闰月农历 需补充该年闰月的前一个月的时差 |
| | | if (isLeapMonth) { offset += day } |
| | | // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) |
| | | var stmap = Date.UTC(1900, 1, 30, 0, 0, 0) |
| | | var calObj = new Date((offset + d - 31) * 86400000 + stmap) |
| | | var cY = calObj.getUTCFullYear() |
| | | var cM = calObj.getUTCMonth() + 1 |
| | | var cD = calObj.getUTCDate() |
| | | |
| | | return this.solar2lunar(cY, cM, cD) |
| | | } |
| | | } |
| | | |
| | | export default calendar |
New file |
| | |
| | | <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, |
| | | }">今天</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?'今天': (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> |
| | | export default { |
| | | props: { |
| | | weeks: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | calendar: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | } |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default: () => { |
| | | return [] |
| | | } |
| | | }, |
| | | lunar: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | methods: { |
| | | choiceDate(weeks) { |
| | | this.$emit('change', weeks) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .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-color-primary; |
| | | } |
| | | |
| | | .uni-calendar-item--isDay { |
| | | background-color: $uni-color-primary; |
| | | opacity: 0.8; |
| | | color: #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--extra { |
| | | color: $uni-color-error; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .uni-calendar-item--checked { |
| | | background-color: $uni-color-primary; |
| | | color: #fff; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .uni-calendar-item--multiple { |
| | | background-color: $uni-color-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">取消</text> |
| | | </view> |
| | | <view class="uni-calendar__header-btn-box" @click="confirm"> |
| | | <text class="uni-calendar__header-text uni-calendar--fixed-width">确定</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">回到今天</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">日</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">一</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">二</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">三</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">四</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">五</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">六</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' |
| | | /** |
| | | * 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 |
| | | }, |
| | | 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 |
| | | } |
| | | }, |
| | | watch: { |
| | | date(newVal) { |
| | | // this.cale.setDate(newVal) |
| | | this.init(newVal) |
| | | }, |
| | | startDate(val){ |
| | | this.cale.resetSatrtDate(val) |
| | | }, |
| | | endDate(val){ |
| | | this.cale.resetEndDate(val) |
| | | }, |
| | | selected(newVal) { |
| | | this.cale.setSelectInfo(this.nowDate.fullDate, newVal) |
| | | this.weeks = this.cale.weeks |
| | | } |
| | | }, |
| | | created() { |
| | | // 获取日历方法实例 |
| | | this.cale = new Calendar({ |
| | | // date: new Date(), |
| | | selected: this.selected, |
| | | startDate: this.startDate, |
| | | endDate: this.endDate, |
| | | range: this.range, |
| | | }) |
| | | // 选中某一天 |
| | | // this.cale.setDate(this.date) |
| | | this.init(this.date) |
| | | // this.setDay |
| | | }, |
| | | methods: { |
| | | // 取消穿透 |
| | | clean() {}, |
| | | bindDateChange(e) { |
| | | const value = e.detail.value + '-1' |
| | | console.log(this.cale.getDate(value)); |
| | | this.init(value) |
| | | }, |
| | | /** |
| | | * 初始化日期显示 |
| | | * @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() { |
| | | console.log(this.cale.getDate(new Date()).fullDate); |
| | | let date = this.cale.getDate(new Date()).fullDate |
| | | // this.cale.setDate(date) |
| | | this.init(date) |
| | | 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-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; |
| | | bottom: 0; |
| | | 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__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; |
| | | // padding: 0 15px; |
| | | } |
| | | |
| | | .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) { |
| | | dd.setDate(dd.getDate() + AddDayCount) |
| | | } else { |
| | | dd.setMonth(dd.getMonth() + AddDayCount) // 获取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 isinfo = false |
| | | 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) |
| | | } |
| | | |
| | | if (this.endDate) { |
| | | let dateCompAfter = this.dateCompare(fullDate, this.endDate) |
| | | disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate) |
| | | } |
| | | 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 { |
| | | fullDate, |
| | | year, |
| | | month, |
| | | date, |
| | | day |
| | | } = this.getDate(dateData) |
| | | let firstDay = new Date(year, month - 1, 1).getDay() |
| | | let currentDay = new Date(year, month, 0).getDate() |
| | | let dates = { |
| | | lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天 |
| | | currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数 |
| | | nextMonthDays: [], // 下个月开始几天 |
| | | weeks: [] |
| | | } |
| | | let canlender = [] |
| | | const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length) |
| | | dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData)) |
| | | canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays) |
| | | let weeks = {} |
| | | // 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天 |
| | | for (let i = 0; i < canlender.length; i++) { |
| | | if (i % 7 === 0) { |
| | | weeks[parseInt(i / 7)] = new Array(7) |
| | | } |
| | | weeks[parseInt(i / 7)][i % 7] = canlender[i] |
| | | } |
| | | this.canlender = canlender |
| | | this.weeks = weeks |
| | | } |
| | | |
| | | //静态方法 |
| | | // static init(date) { |
| | | // if (!this.instance) { |
| | | // this.instance = new Calendar(date); |
| | | // } |
| | | // return this.instance; |
| | | // } |
| | | } |
| | | |
| | | |
| | | export default Calendar |
New file |
| | |
| | | <template> |
| | | <view class="uni-card uni-border" :class="{ 'uni-card--full': isFull === true || isFull === 'true', 'uni-card--shadow': isShadow === true || isShadow === 'true'}"> |
| | | <!-- 基础 --> |
| | | <view v-if="mode === 'basic' && title" class="uni-card__header uni-border-bottom" @click.stop="onClick"> |
| | | <view v-if="thumbnail" class="uni-card__header-extra-img-view"> |
| | | <image :src="thumbnail" class="uni-card__header-extra-img" /> |
| | | </view> |
| | | <text class="uni-card__header-title-text">{{ title }}</text> |
| | | <text v-if="extra" class="uni-card__header-extra-text">{{ extra }}</text> |
| | | </view> |
| | | <!-- 标题 --> |
| | | <view v-if="mode === 'title'" class="uni-card__title uni-border-bottom" @click.stop="onClick"> |
| | | <view class="uni-card__title-box"> |
| | | <view class="uni-card__title-header"> |
| | | <image class="uni-card__title-header-image" :src="thumbnail" mode="scaleToFill" /> |
| | | </view> |
| | | <view class="uni-card__title-content"> |
| | | <text class="uni-card__title-content-title uni-ellipsis">{{ title }}</text> |
| | | <text class="uni-card__title-content-extra uni-ellipsis">{{ subTitle }}</text> |
| | | </view> |
| | | </view> |
| | | <view v-if="extra"> |
| | | <text class="uni-card__header-extra-text">{{ extra }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- 图文 --> |
| | | <view v-if="mode === 'style'" class="uni-card__thumbnailimage" @click.stop="onClick"> |
| | | <view class="uni-card__thumbnailimage-box"> |
| | | <image class="uni-card__thumbnailimage-image" :src="thumbnail" mode="aspectFill" /> |
| | | </view> |
| | | <view v-if="title" class="uni-card__thumbnailimage-title"><text class="uni-card__thumbnailimage-title-text">{{ title }}</text></view> |
| | | </view> |
| | | <!-- 内容 --> |
| | | <view class="uni-card__content uni-card__content--pd" @click.stop="onClick"> |
| | | <view v-if="mode === 'style' && extra" class=""><text class="uni-card__content-extra">{{ extra }}</text></view> |
| | | <slot /> |
| | | </view> |
| | | <!-- 底部 --> |
| | | <view v-if="note" class="uni-card__footer uni-border-top"> |
| | | <slot name="footer"> |
| | | <text class="uni-card__footer-text">{{ note }}</text> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Card 卡片 |
| | | * @description 卡片视图组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=22 |
| | | * @property {String} title 标题文字 |
| | | * @property {String} subTitle 副标题(仅仅mode=title下生效) |
| | | * @property {String} extra 标题额外信息 |
| | | * @property {String} note 标题左侧缩略图 |
| | | * @property {String} thumbnail 底部信息 |
| | | * @property {String} mode = [basic|style|title] 卡片模式 |
| | | * @value basic 基础卡片 |
| | | * @value style 图文卡片 |
| | | * @value title 标题卡片 |
| | | * @property {Boolean} isFull = [true | false] 卡片内容是否通栏,为 true 时将去除padding值 |
| | | * @property {Boolean} isShadow = [true | false] 卡片内容是否开启阴影 |
| | | * @event {Function} click 点击 Card 触发事件 |
| | | * @example <uni-card title="标题文字" thumbnail="https://img-cdn-qiniu.dcloud.net.cn/new-page/uni.png" extra="额外信息" note="Tips">内容主体,可自定义内容及样式</uni-card> |
| | | */ |
| | | export default { |
| | | name: 'UniCard', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | subTitle: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | extra: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | note: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | thumbnail: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | mode: { |
| | | type: String, |
| | | default: 'basic' |
| | | }, |
| | | isFull: { |
| | | // 内容区域是否通栏 |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | isShadow: { |
| | | // 是否开启阴影 |
| | | type: [Boolean, String], |
| | | default: false |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick() { |
| | | this.$emit('click') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-card { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | flex: 1; |
| | | box-shadow: 0 0 0 rgba(0, 0, 0, 0); |
| | | /* #endif */ |
| | | margin: $uni-spacing-col-lg $uni-spacing-row-lg; |
| | | background-color: $uni-bg-color; |
| | | position: relative; |
| | | flex-direction: column; |
| | | border-radius: 5px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | |
| | | |
| | | .uni-border { |
| | | position: relative; |
| | | /* #ifdef APP-NVUE */ |
| | | border-color: $uni-border-color; |
| | | border-style: solid; |
| | | border-width: 0.5px; |
| | | /* #endif */ |
| | | z-index: 1; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-border:after { |
| | | content: ''; |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | border: 1px solid $uni-border-color; |
| | | border-radius: 10px; |
| | | box-sizing: border-box; |
| | | width: 200%; |
| | | height: 200%; |
| | | transform: scale(0.5); |
| | | transform-origin: left top; |
| | | z-index: -1; |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | .uni-border-bottom { |
| | | position: relative; |
| | | /* #ifdef APP-NVUE */ |
| | | border-bottom-color: $uni-border-color; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 0.5px; |
| | | /* #endif */ |
| | | z-index: 1; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-border-bottom:after { |
| | | content: ''; |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | border-bottom: 1px solid $uni-border-color; |
| | | box-sizing: border-box; |
| | | width: 200%; |
| | | height: 200%; |
| | | transform: scale(0.5); |
| | | transform-origin: left top; |
| | | z-index: -1; |
| | | } |
| | | |
| | | /* #endif */ |
| | | .uni-border-top { |
| | | position: relative; |
| | | /* #ifdef APP-NVUE */ |
| | | border-top-color: $uni-border-color; |
| | | border-top-style: solid; |
| | | border-top-width: 0.5px; |
| | | /* #endif */ |
| | | z-index: 1; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-border-top:after { |
| | | content: ''; |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | border-top: 1px solid $uni-border-color; |
| | | box-sizing: border-box; |
| | | width: 200%; |
| | | height: 200%; |
| | | transform: scale(0.5); |
| | | transform-origin: left top; |
| | | z-index: -1; |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | .uni-card__thumbnailimage { |
| | | position: relative; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | height: 150px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-card__thumbnailimage-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-card__thumbnailimage-image { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-card__thumbnailimage-title { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | flex-direction: row; |
| | | padding: $uni-spacing-col-base $uni-spacing-col-lg; |
| | | background-color: $uni-bg-color-mask; |
| | | } |
| | | |
| | | .uni-card__thumbnailimage-title-text { |
| | | flex: 1; |
| | | font-size: $uni-font-size-base; |
| | | color: #fff; |
| | | } |
| | | |
| | | .uni-card__title { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: 10px; |
| | | |
| | | } |
| | | |
| | | .uni-card__title-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-card__title-header { |
| | | width: 40px; |
| | | height: 40px; |
| | | overflow: hidden; |
| | | border-radius: 5px; |
| | | } |
| | | |
| | | .uni-card__title-header-image { |
| | | width: 40px; |
| | | height: 40px; |
| | | } |
| | | |
| | | .uni-card__title-content { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | flex: 1; |
| | | padding-left: 10px; |
| | | height: 40px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-card__title-content-title { |
| | | font-size: $uni-font-size-base; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .uni-card__title-content-extra { |
| | | font-size: $uni-font-size-sm; |
| | | line-height: 27px; |
| | | color: $uni-text-color-grey; |
| | | } |
| | | |
| | | .uni-card__header { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | position: relative; |
| | | flex-direction: row; |
| | | padding: $uni-spacing-col-lg; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-card__header-title { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | margin-right: $uni-spacing-col-base; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-card__header-title-text { |
| | | font-size: $uni-font-size-lg; |
| | | flex: 1; |
| | | color: #333; |
| | | } |
| | | |
| | | .uni-card__header-extra-img { |
| | | height: $uni-img-size-sm; |
| | | width: $uni-img-size-sm; |
| | | margin-right: $uni-spacing-col-base; |
| | | } |
| | | |
| | | .uni-card__header-extra-text { |
| | | flex: 1; |
| | | margin-left: $uni-spacing-col-base; |
| | | font-size: $uni-font-size-sm; |
| | | text-align: right; |
| | | color: $uni-text-color-grey; |
| | | } |
| | | |
| | | .uni-card__content { |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-card__content--pd { |
| | | padding: $uni-spacing-col-lg; |
| | | } |
| | | |
| | | .uni-card__content-extra { |
| | | font-size: $uni-font-size-base; |
| | | padding-bottom: 10px; |
| | | color: $uni-text-color-grey; |
| | | } |
| | | |
| | | .uni-card__footer { |
| | | justify-content: space-between; |
| | | padding: $uni-spacing-col-lg; |
| | | } |
| | | |
| | | .uni-card__footer-text { |
| | | color: $uni-text-color-grey; |
| | | font-size: $uni-font-size-sm; |
| | | } |
| | | |
| | | .uni-card--shadow { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.1); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-card--full { |
| | | margin: 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 |
| | |
| | | <template> |
| | | <view :class="{ 'uni-collapse-cell--disabled': disabled,'uni-collapse-cell--notdisabled': !disabled, 'uni-collapse-cell--open': isOpen,'uni-collapse-cell--hide':!isOpen }" |
| | | class="uni-collapse-cell"> |
| | | <view class="uni-collapse-cell__title" @click="onClick"> |
| | | <image v-if="thumb" :src="thumb" class="uni-collapse-cell__title-img" /> |
| | | <text class="uni-collapse-cell__title-text">{{ title }}</text> |
| | | <!-- #ifdef MP-ALIPAY --> |
| | | <view :class="{ 'uni-collapse-cell__title-arrow-active': isOpen, 'uni-collapse-cell--animation': showAnimation === true }" |
| | | class="uni-collapse-cell__title-arrow"> |
| | | <uni-icons color="#bbb" size="20" type="arrowdown" /> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifndef MP-ALIPAY --> |
| | | <uni-icons :class="{ 'uni-collapse-cell__title-arrow-active': isOpen, 'uni-collapse-cell--animation': showAnimation === true }" |
| | | class="uni-collapse-cell__title-arrow" color="#bbb" size="20" type="arrowdown" /> |
| | | <!-- #endif --> |
| | | </view> |
| | | <view :class="{'uni-collapse-cell__content--hide':!isOpen}" class="uni-collapse-cell__content"> |
| | | <view :class="{ 'uni-collapse-cell--animation': showAnimation === true }" class="uni-collapse-cell__wrapper" :style="{'transform':isOpen?'translateY(0)':'translateY(-50%)','-webkit-transform':isOpen?'translateY(0)':'translateY(-50%)'}"> |
| | | <slot /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from '../uni-icons/uni-icons.vue' |
| | | /** |
| | | * CollapseItem 折叠面板子组件 |
| | | * @description 折叠面板子组件 |
| | | * @property {String} title 标题文字 |
| | | * @property {String} thumb 标题左侧缩略图 |
| | | * @property {Boolean} disabled = [true|false] 是否展开面板 |
| | | * @property {Boolean} showAnimation = [true|false] 开启动画 |
| | | */ |
| | | export default { |
| | | name: 'UniCollapseItem', |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | props: { |
| | | title: { |
| | | // 列表标题 |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | name: { |
| | | // 唯一标识符 |
| | | type: [Number, String], |
| | | default: 0 |
| | | }, |
| | | disabled: { |
| | | // 是否禁用 |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | showAnimation: { |
| | | // 是否显示动画 |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | open: { |
| | | // 是否展开 |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | thumb: { |
| | | // 缩略图 |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | isOpen: false |
| | | } |
| | | }, |
| | | watch: { |
| | | open(val) { |
| | | this.isOpen = val |
| | | } |
| | | }, |
| | | inject: ['collapse'], |
| | | created() { |
| | | this.isOpen = this.open |
| | | this.nameSync = this.name ? this.name : this.collapse.childrens.length |
| | | this.collapse.childrens.push(this) |
| | | if (String(this.collapse.accordion) === 'true') { |
| | | if (this.isOpen) { |
| | | let lastEl = this.collapse.childrens[this.collapse.childrens.length - 2] |
| | | if (lastEl) { |
| | | this.collapse.childrens[this.collapse.childrens.length - 2].isOpen = false |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick() { |
| | | if (this.disabled) { |
| | | return |
| | | } |
| | | if (String(this.collapse.accordion) === 'true') { |
| | | this.collapse.childrens.forEach(vm => { |
| | | if (vm === this) { |
| | | return |
| | | } |
| | | vm.isOpen = false |
| | | }) |
| | | } |
| | | this.isOpen = !this.isOpen |
| | | this.collapse.onChange && this.collapse.onChange() |
| | | this.$forceUpdate() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-collapse-cell { |
| | | flex-direction: column; |
| | | border-color: $uni-border-color; |
| | | border-bottom-width: 1px; |
| | | border-bottom-style: solid; |
| | | } |
| | | |
| | | |
| | | .uni-collapse-cell--hover { |
| | | background-color: $uni-bg-color-hover; |
| | | } |
| | | |
| | | .uni-collapse-cell--open { |
| | | background-color: $uni-bg-color-hover; |
| | | } |
| | | |
| | | .uni-collapse-cell--disabled { |
| | | background-color: $uni-bg-color-hover; |
| | | // opacity: 0.3; |
| | | } |
| | | |
| | | |
| | | .uni-collapse-cell--hide { |
| | | height: 48px; |
| | | } |
| | | |
| | | .uni-collapse-cell--animation { |
| | | // transition: transform 0.3s ease; |
| | | transition-property: transform; |
| | | transition-duration: 0.3s; |
| | | transition-timing-function: ease; |
| | | } |
| | | |
| | | .uni-collapse-cell__title { |
| | | padding: 12px 12px; |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | height: 48px; |
| | | line-height: 24px; |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-collapse-cell__title:active { |
| | | background-color: $uni-bg-color-hover; |
| | | } |
| | | |
| | | .uni-collapse-cell__title-img { |
| | | height: $uni-img-size-base; |
| | | width: $uni-img-size-base; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .uni-collapse-cell__title-arrow { |
| | | width: 20px; |
| | | height: 20px; |
| | | transform: rotate(0deg); |
| | | transform-origin: center center; |
| | | |
| | | } |
| | | |
| | | .uni-collapse-cell__title-arrow-active { |
| | | transform: rotate(180deg); |
| | | } |
| | | |
| | | .uni-collapse-cell__title-text { |
| | | flex: 1; |
| | | font-size: $uni-font-size-base; |
| | | /* #ifndef APP-NVUE */ |
| | | white-space: nowrap; |
| | | color: inherit; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 1; |
| | | /* #endif */ |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | |
| | | .uni-collapse-cell__content { |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-collapse-cell__wrapper { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-collapse-cell__content--hide { |
| | | height: 0px; |
| | | line-height: 0px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-collapse"> |
| | | <slot /> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | /** |
| | | * Collapse 折叠面板 |
| | | * @description 展示可以折叠 / 展开的内容区域 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=23 |
| | | * @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果 |
| | | * @event {Function} change 切换面板时触发,activeNames(Array):展开状态的uniCollapseItem的 name 值 |
| | | */ |
| | | export default { |
| | | name: 'UniCollapse', |
| | | props: { |
| | | accordion: { |
| | | // 是否开启手风琴效果 |
| | | type: [Boolean, String], |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return {} |
| | | }, |
| | | provide() { |
| | | return { |
| | | collapse: this |
| | | } |
| | | }, |
| | | created() { |
| | | this.childrens = [] |
| | | }, |
| | | methods: { |
| | | onChange() { |
| | | let activeItem = [] |
| | | this.childrens.forEach((vm, index) => { |
| | | if (vm.isOpen) { |
| | | activeItem.push(vm.nameSync) |
| | | } |
| | | }) |
| | | this.$emit('change', activeItem) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .uni-collapse { |
| | | /* #ifndef APP-NVUE */ |
| | | width: 100%; |
| | | display: flex; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | flex: 1; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | background-color: $uni-bg-color; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-combox"> |
| | | <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" v-model="inputVal" @input="onInput" |
| | | @focus="onFocus" @blur="onBlur" /> |
| | | <uni-icons class="uni-combox__input-arrow" type="arrowdown" size="14" @click="toggleSelector"></uni-icons> |
| | | <view class="uni-combox__selector" v-if="showSelector"> |
| | | <scroll-view scroll-y="true" class="uni-combox__selector-scroll"> |
| | | <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> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from '../uni-icons/uni-icons.vue' |
| | | /** |
| | | * 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', |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | props: { |
| | | label: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | labelWidth: { |
| | | type: String, |
| | | default: 'auto' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | candidates: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | emptyTips: { |
| | | type: String, |
| | | default: '无匹配项' |
| | | }, |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | showSelector: false, |
| | | inputVal: '' |
| | | } |
| | | }, |
| | | computed: { |
| | | labelStyle() { |
| | | if (this.labelWidth === 'auto') { |
| | | return {} |
| | | } |
| | | return { |
| | | width: this.labelWidth |
| | | } |
| | | }, |
| | | filterCandidates() { |
| | | return this.candidates.filter((item) => { |
| | | return item.toString().indexOf(this.inputVal) > -1 |
| | | }) |
| | | }, |
| | | filterCandidatesLength() { |
| | | return this.filterCandidates.length |
| | | } |
| | | }, |
| | | watch: { |
| | | value: { |
| | | handler(newVal) { |
| | | this.inputVal = newVal |
| | | }, |
| | | immediate: true |
| | | } |
| | | }, |
| | | methods: { |
| | | toggleSelector() { |
| | | this.showSelector = !this.showSelector |
| | | }, |
| | | onFocus() { |
| | | this.showSelector = true |
| | | }, |
| | | onBlur() { |
| | | setTimeout(() => { |
| | | this.showSelector = false |
| | | },50) |
| | | }, |
| | | onSelectorClick(index) { |
| | | this.inputVal = this.filterCandidates[index] |
| | | this.showSelector = false |
| | | this.$emit('input', this.inputVal) |
| | | }, |
| | | onInput() { |
| | | setTimeout(() => { |
| | | this.$emit('input', this.inputVal) |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-combox { |
| | | /* #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: 16px; |
| | | height: 22px; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .uni-combox__input-arrow { |
| | | padding: 10px; |
| | | } |
| | | |
| | | .uni-combox__selector { |
| | | box-sizing: border-box; |
| | | position: absolute; |
| | | top: 42px; |
| | | left: 0; |
| | | width: 100%; |
| | | background-color: #FFFFFF; |
| | | border-radius: 6px; |
| | | box-shadow: #DDDDDD 4px 4px 8px, #DDDDDD -4px -4px 8px; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .uni-combox__selector-scroll { |
| | | max-height: 200px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .uni-combox__selector::before { |
| | | content: ''; |
| | | position: absolute; |
| | | width: 0; |
| | | height: 0; |
| | | border-bottom: solid 6px #FFFFFF; |
| | | border-right: solid 6px transparent; |
| | | border-left: solid 6px transparent; |
| | | left: 50%; |
| | | top: -6px; |
| | | margin-left: -6px; |
| | | } |
| | | |
| | | .uni-combox__selector-empty, |
| | | .uni-combox__selector-item { |
| | | /* #ifdef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | line-height: 36px; |
| | | font-size: 14px; |
| | | text-align: center; |
| | | border-bottom: solid 1px #DDDDDD; |
| | | margin: 0px 10px; |
| | | } |
| | | |
| | | .uni-combox__selector-empty:last-child, |
| | | .uni-combox__selector-item:last-child { |
| | | border-bottom: none; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-countdown"> |
| | | <text v-if="showDay" :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ d }}</text> |
| | | <text v-if="showDay" :style="{ color: splitorColor }" class="uni-countdown__splitor">天</text> |
| | | <text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ h }}</text> |
| | | <text :style="{ color: splitorColor }" class="uni-countdown__splitor">{{ showColon ? ':' : '时' }}</text> |
| | | <text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ i }}</text> |
| | | <text :style="{ color: splitorColor }" class="uni-countdown__splitor">{{ showColon ? ':' : '分' }}</text> |
| | | <text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ s }}</text> |
| | | <text v-if="!showColon" :style="{ color: splitorColor }" class="uni-countdown__splitor">秒</text> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | /** |
| | | * 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} showColon = [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', |
| | | props: { |
| | | showDay: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showColon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | backgroundColor: { |
| | | type: String, |
| | | default: '#FFFFFF' |
| | | }, |
| | | borderColor: { |
| | | type: String, |
| | | default: '#000000' |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#000000' |
| | | }, |
| | | splitorColor: { |
| | | type: String, |
| | | default: '#000000' |
| | | }, |
| | | 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 |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | timer: null, |
| | | syncFlag: false, |
| | | d: '00', |
| | | h: '00', |
| | | i: '00', |
| | | s: '00', |
| | | leftTime: 0, |
| | | seconds: 0 |
| | | } |
| | | }, |
| | | watch: { |
| | | day(val) { |
| | | this.changeFlag() |
| | | }, |
| | | hour(val) { |
| | | this.changeFlag() |
| | | }, |
| | | minute(val) { |
| | | this.changeFlag() |
| | | }, |
| | | second(val) { |
| | | this.changeFlag() |
| | | } |
| | | }, |
| | | created: function(e) { |
| | | this.startData(); |
| | | }, |
| | | beforeDestroy() { |
| | | clearInterval(this.timer) |
| | | }, |
| | | 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() |
| | | } |
| | | if (day < 10) { |
| | | day = '0' + day |
| | | } |
| | | if (hour < 10) { |
| | | hour = '0' + hour |
| | | } |
| | | if (minute < 10) { |
| | | minute = '0' + minute |
| | | } |
| | | if (second < 10) { |
| | | second = '0' + 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) { |
| | | return |
| | | } |
| | | this.countDown() |
| | | this.timer = setInterval(() => { |
| | | this.seconds-- |
| | | if (this.seconds < 0) { |
| | | this.timeUp() |
| | | return |
| | | } |
| | | this.countDown() |
| | | }, 1000) |
| | | }, |
| | | 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> |
| | | $countdown-height: 48rpx; |
| | | $countdown-width: 52rpx; |
| | | |
| | | .uni-countdown { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: flex-start; |
| | | padding: 2rpx 0; |
| | | } |
| | | |
| | | .uni-countdown__splitor { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | line-height: $countdown-height; |
| | | padding: 5rpx; |
| | | font-size: $uni-font-size-sm; |
| | | } |
| | | |
| | | .uni-countdown__number { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: $countdown-width; |
| | | height: $countdown-height; |
| | | line-height: $countdown-height; |
| | | margin: 5rpx; |
| | | text-align: center; |
| | | font-size: $uni-font-size-sm; |
| | | } |
| | | </style> |
New file |
| | |
| | | |
| | | const events = { |
| | | load: 'load', |
| | | error: 'error' |
| | | } |
| | | const pageMode = { |
| | | add: 'add', |
| | | replace: 'replace' |
| | | } |
| | | |
| | | const attrs = [ |
| | | 'pageCurrent', |
| | | 'pageSize', |
| | | 'collection', |
| | | 'action', |
| | | 'field', |
| | | 'getcount', |
| | | 'orderby', |
| | | 'where' |
| | | ] |
| | | |
| | | export default { |
| | | props: { |
| | | options: { |
| | | type: [Object, Array], |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | collection: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | action: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | field: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | pageData: { |
| | | type: String, |
| | | default: 'add' |
| | | }, |
| | | pageCurrent: { |
| | | type: Number, |
| | | default: 1 |
| | | }, |
| | | pageSize: { |
| | | type: Number, |
| | | default: 20 |
| | | }, |
| | | getcount: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | orderby: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | where: { |
| | | type: [String, Object], |
| | | default: '' |
| | | }, |
| | | getone: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | manual: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | listData: this.getone ? {} : [], |
| | | paginationInternal: { |
| | | current: this.pageCurrent, |
| | | size: this.pageSize, |
| | | count: 0 |
| | | }, |
| | | errorMessage: '' |
| | | } |
| | | }, |
| | | created() { |
| | | let db = null; |
| | | let dbCmd = null; |
| | | |
| | | if(this.collection){ |
| | | this.db = uniCloud.database(); |
| | | this.dbCmd = this.db.command; |
| | | } |
| | | |
| | | this._isEnded = false |
| | | |
| | | this.$watch(() => { |
| | | var al = [] |
| | | attrs.forEach(key => { |
| | | al.push(this[key]) |
| | | }) |
| | | return al |
| | | }, (newValue, oldValue) => { |
| | | this.paginationInternal.pageSize = this.pageSize |
| | | |
| | | let needReset = false |
| | | for (let i = 2; i < newValue.length; i++) { |
| | | if (newValue[i] != oldValue[i]) { |
| | | needReset = true |
| | | break |
| | | } |
| | | } |
| | | if (needReset) { |
| | | this.clear() |
| | | this.reset() |
| | | } |
| | | if (newValue[0] != oldValue[0]) { |
| | | this.paginationInternal.current = this.pageCurrent |
| | | } |
| | | |
| | | this._execLoadData() |
| | | }) |
| | | |
| | | // #ifdef H5 |
| | | if (process.env.NODE_ENV === 'development') { |
| | | this._debugDataList = [] |
| | | if (!window.unidev) { |
| | | window.unidev = { |
| | | clientDB: { |
| | | data: [] |
| | | } |
| | | } |
| | | } |
| | | unidev.clientDB.data.push(this._debugDataList) |
| | | } |
| | | // #endif |
| | | |
| | | // #ifdef MP-TOUTIAO |
| | | let changeName |
| | | let events = this.$scope.dataset.eventOpts |
| | | for (var i = 0; i < events.length; i++) { |
| | | let event = events[i] |
| | | if (event[0].includes('^load')) { |
| | | changeName = event[1][0][0] |
| | | } |
| | | } |
| | | if (changeName) { |
| | | let parent = this.$parent |
| | | let maxDepth = 16 |
| | | this._changeDataFunction = null |
| | | while (parent && maxDepth > 0) { |
| | | let fun = parent[changeName] |
| | | if (fun && typeof fun === 'function') { |
| | | this._changeDataFunction = fun |
| | | maxDepth = 0 |
| | | break |
| | | } |
| | | parent = parent.$parent |
| | | maxDepth--; |
| | | } |
| | | } |
| | | // #endif |
| | | |
| | | // if (!this.manual) { |
| | | // this.loadData() |
| | | // } |
| | | }, |
| | | // #ifdef H5 |
| | | beforeDestroy() { |
| | | if (process.env.NODE_ENV === 'development' && window.unidev) { |
| | | var cd = this._debugDataList |
| | | var dl = unidev.clientDB.data |
| | | for (var i = dl.length - 1; i >= 0; i--) { |
| | | if (dl[i] === cd) { |
| | | dl.splice(i, 1) |
| | | break |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | // #endif |
| | | methods: { |
| | | loadData(args1, args2) { |
| | | let callback = null |
| | | if (typeof args1 === 'object') { |
| | | if (args1.clear) { |
| | | this.clear() |
| | | this.reset() |
| | | } |
| | | if (args1.current !== undefined) { |
| | | this.paginationInternal.current = args1.current |
| | | } |
| | | if (typeof args2 === 'function') { |
| | | callback = args2 |
| | | } |
| | | } else if (typeof args1 === 'function') { |
| | | callback = args1 |
| | | } |
| | | |
| | | this._execLoadData(callback) |
| | | }, |
| | | loadMore() { |
| | | if (this._isEnded) { |
| | | return |
| | | } |
| | | this._execLoadData() |
| | | }, |
| | | refresh() { |
| | | this.clear() |
| | | this._execLoadData() |
| | | }, |
| | | clear() { |
| | | this._isEnded = false |
| | | this.listData = [] |
| | | }, |
| | | reset() { |
| | | this.paginationInternal.current = 1 |
| | | }, |
| | | remove(id, { |
| | | action, |
| | | callback, |
| | | confirmTitle, |
| | | confirmContent |
| | | } = {}) { |
| | | if (!id || !id.length) { |
| | | return |
| | | } |
| | | uni.showModal({ |
| | | title: confirmTitle || '提示', |
| | | content: confirmContent || '是否删除该数据', |
| | | showCancel: true, |
| | | success: (res) => { |
| | | if (!res.confirm) { |
| | | return |
| | | } |
| | | this._execRemove(id, action, callback) |
| | | } |
| | | }) |
| | | }, |
| | | _execLoadData(callback) { |
| | | if (this.loading) { |
| | | return |
| | | } |
| | | this.loading = true |
| | | this.errorMessage = '' |
| | | |
| | | this._getExec().then((res) => { |
| | | this.loading = false |
| | | const { |
| | | data, |
| | | count |
| | | } = res.result |
| | | this._isEnded = data.length < this.pageSize |
| | | |
| | | callback && callback(data, this._isEnded) |
| | | this._dispatchEvent(events.load, data) |
| | | |
| | | if (this.getone) { |
| | | this.listData = data.length ? data[0] : undefined |
| | | } else if (this.pageData === pageMode.add) { |
| | | this.listData.push(...data) |
| | | if (this.listData.length) { |
| | | this.paginationInternal.current++ |
| | | } |
| | | } else if (this.pageData === pageMode.replace) { |
| | | this.listData = data |
| | | this.paginationInternal.count = count |
| | | } |
| | | |
| | | // #ifdef H5 |
| | | if (process.env.NODE_ENV === 'development') { |
| | | this._debugDataList.length = 0 |
| | | this._debugDataList.push(...JSON.parse(JSON.stringify(this.listData))) |
| | | } |
| | | // #endif |
| | | }).catch((err) => { |
| | | this.loading = false |
| | | this.errorMessage = err |
| | | callback && callback() |
| | | this.$emit(events.error, err) |
| | | }) |
| | | }, |
| | | _getExec() { |
| | | let exec = this.db |
| | | if (this.action) { |
| | | exec = exec.action(this.action) |
| | | } |
| | | |
| | | exec = exec.collection(this.collection) |
| | | |
| | | if (!(!this.where || !Object.keys(this.where).length)) { |
| | | exec = exec.where(this.where) |
| | | } |
| | | if (this.field) { |
| | | exec = exec.field(this.field) |
| | | } |
| | | if (this.orderby) { |
| | | exec = exec.orderBy(this.orderby) |
| | | } |
| | | |
| | | const { |
| | | current, |
| | | size |
| | | } = this.paginationInternal |
| | | exec = exec.skip(size * (current - 1)).limit(size).get({ |
| | | getCount: this.getcount |
| | | }) |
| | | |
| | | return exec |
| | | }, |
| | | _execRemove(id, action, callback) { |
| | | if (!this.collection || !id) { |
| | | return |
| | | } |
| | | |
| | | const ids = Array.isArray(id) ? id : [id] |
| | | if (!ids.length) { |
| | | return |
| | | } |
| | | |
| | | uni.showLoading({ |
| | | mask: true |
| | | }) |
| | | |
| | | let exec = this.db |
| | | if (action) { |
| | | exec = exec.action(action) |
| | | } |
| | | |
| | | exec.collection(this.collection).where({ |
| | | _id: dbCmd.in(ids) |
| | | }).remove().then((res) => { |
| | | callback && callback(res.result) |
| | | if (this.pageData === pageMode.replace) { |
| | | this.refresh() |
| | | } else { |
| | | this.removeData(ids) |
| | | } |
| | | }).catch((err) => { |
| | | uni.showModal({ |
| | | content: err.message, |
| | | showCancel: false |
| | | }) |
| | | }).finally(() => { |
| | | uni.hideLoading() |
| | | }) |
| | | }, |
| | | removeData(ids) { |
| | | let il = ids.slice(0) |
| | | let dl = this.listData |
| | | for (let i = dl.length - 1; i >= 0; i--) { |
| | | let index = il.indexOf(dl[i]._id) |
| | | if (index >= 0) { |
| | | dl.splice(i, 1) |
| | | il.splice(index, 1) |
| | | } |
| | | } |
| | | }, |
| | | _dispatchEvent(type, data) { |
| | | if (this._changeDataFunction) { |
| | | this._changeDataFunction(data, this._isEnded) |
| | | } else { |
| | | this.$emit(type, data, this._isEnded) |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <view class="uni-data-checklist"> |
| | | <template v-if="loading"> |
| | | <view class="uni-data-loading"> |
| | | <uni-load-more status="loading" iconType="snow" :iconSize="18" :content-text="contentText"></uni-load-more> |
| | | </view> |
| | | </template> |
| | | <template v-else> |
| | | <checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="chagne"> |
| | | <label class="checklist-box" :class="item.labelClass" :style="[item.styleBackgroud]" v-for="(item,index) in dataList" |
| | | :key="index"> |
| | | <checkbox hidden :disabled="!!item.disable" :value="item.value+''" :checked="item.selected" /> |
| | | <view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="checkbox__inner" |
| | | :class="item.checkboxBgClass" :style="[item.styleIcon]"> |
| | | <view class="checkbox__inner-icon" :class="item.checkboxClass"></view> |
| | | </view> |
| | | <view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}"> |
| | | <text class="checklist-text" :class="item.textClass" :style="[item.styleIconText]">{{item.text}}</text> |
| | | <view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :class="item.listClass" :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="item.labelClass" :style="[item.styleBackgroud]" v-for="(item,index) in dataList" :key="index"> |
| | | <radio hidden :disabled="item.disable" :value="item.value+''" :checked="item.selected" /> |
| | | <view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner" |
| | | :class="item.checkboxBgClass" :style="[item.styleBackgroud]"> |
| | | <view class="radio__inner-icon" :class="item.checkboxClass" :style="[item.styleIcon]"></view> |
| | | </view> |
| | | <view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}"> |
| | | <text class="checklist-text" :class="item.textClass" :style="[item.styleIconText]">{{item.text}}</text> |
| | | <view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :class="item.listClass" :style="[item.styleRightIcon]"></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} selectedTextColor 选中文本颜色,如不填写则自动显示 |
| | | * @value left 左侧显示 |
| | | * @value right 右侧显示 |
| | | * @event {Function} change 选中发生变化触发 |
| | | */ |
| | | |
| | | import clientdb from './clientdb.js' |
| | | export default { |
| | | name: 'uniDataChecklist', |
| | | mixins: [clientdb], |
| | | props: { |
| | | mode: { |
| | | type: String, |
| | | default: 'default' |
| | | }, |
| | | multiple: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | value: { |
| | | 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: '' |
| | | } |
| | | }, |
| | | watch: { |
| | | localdata: { |
| | | handler(newVal) { |
| | | this.dataList = this.getDataList(this.getSelectedValue(newVal)) |
| | | }, |
| | | deep: true |
| | | }, |
| | | |
| | | listData(newVal) { |
| | | this.range = newVal |
| | | this.dataList = this.getDataList(this.getSelectedValue(newVal)) |
| | | // console.log('----listData', this.dataList); |
| | | }, |
| | | value(newVal) { |
| | | this.dataList = this.getDataList(newVal) |
| | | this.formItem && this.formItem.setValue(newVal) |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | dataList: [], |
| | | range: [], |
| | | contentText: { |
| | | contentdown: '查看更多', |
| | | contentrefresh: '加载中', |
| | | contentnomore: '没有更多' |
| | | }, |
| | | styles: { |
| | | selectedColor: '#007aff', |
| | | selectedTextColor: '#333', |
| | | } |
| | | }; |
| | | }, |
| | | created() { |
| | | this.form = this.getForm('uniForms') |
| | | this.formItem = this.getForm('uniFormsItem') |
| | | this.formItem && this.formItem.setValue(this.value) |
| | | this.styles = { |
| | | selectedColor: this.selectedColor, |
| | | selectedTextColor: this.selectedTextColor |
| | | } |
| | | |
| | | if (this.formItem) { |
| | | if (this.formItem.name) { |
| | | this.rename = this.formItem.name |
| | | this.form.inputChildrens.push(this) |
| | | } |
| | | } |
| | | |
| | | if (this.localdata && this.localdata.length !== 0) { |
| | | this.range = this.localdata |
| | | this.dataList = this.getDataList(this.getSelectedValue(this.range)) |
| | | } else { |
| | | if (this.collection) { |
| | | this.loadData() |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | init(range) {}, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | 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.value + '')) { |
| | | detail.value.push(item.value) |
| | | detail.data.push(item) |
| | | } |
| | | }) |
| | | } else { |
| | | const range = this.range.find(item => (item.value + '') === values) |
| | | if (range) { |
| | | detail = { |
| | | value: range.value, |
| | | data: range |
| | | } |
| | | } |
| | | } |
| | | this.formItem && this.formItem.setValue(detail.value) |
| | | this.$emit('input', 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) |
| | | } |
| | | }, |
| | | getLabelClass(item, index) { |
| | | let classes = [] |
| | | switch (this.mode) { |
| | | case 'default': |
| | | item.disable && classes.push('disabled-cursor') |
| | | break |
| | | case 'button': |
| | | classes.push(...['is-button', ...this.getClasses(item, 'button')]) |
| | | break |
| | | case 'list': |
| | | if (this.multiple) { |
| | | classes.push('is-list-multiple-box') |
| | | } else { |
| | | classes.push('is-list-box') |
| | | } |
| | | item.disable && classes.push('is-list-disabled') |
| | | index !== 0 && classes.push('is-list-border') |
| | | break |
| | | case 'tag': |
| | | classes.push(...['is-tag', ...this.getClasses(item, 'tag')]) |
| | | break |
| | | } |
| | | classes = classes.join(' ') |
| | | return classes |
| | | }, |
| | | getCheckboxClass(item, type = '') { |
| | | let classes = [] |
| | | if (this.multiple) { |
| | | classes.push(...this.getClasses(item, 'default-multiple', type)) |
| | | } else { |
| | | classes.push(...this.getClasses(item, 'default', type)) |
| | | } |
| | | classes = classes.join(' ') |
| | | return classes |
| | | }, |
| | | getTextClass(item) { |
| | | let classes = [] |
| | | switch (this.mode) { |
| | | case 'default': |
| | | classes.push(...this.getClasses(item, 'list')) |
| | | break |
| | | case 'button': |
| | | classes.push(...this.getClasses(item, 'list')) |
| | | break |
| | | case 'list': |
| | | classes.push(...this.getClasses(item, 'list')) |
| | | break |
| | | case 'tag': |
| | | classes.push(...['is-tag-text', ...this.getClasses(item, 'tag-text')]) |
| | | break |
| | | } |
| | | classes = classes.join(' ') |
| | | return classes |
| | | }, |
| | | |
| | | /** |
| | | * 获取渲染的新数组 |
| | | * @param {Object} value 选中内容 |
| | | */ |
| | | getDataList(value) { |
| | | // 解除引用关系,破坏原引用关系,避免污染源数据 |
| | | let dataList = JSON.parse(JSON.stringify(this.range)) |
| | | let list = [] |
| | | if (this.multiple) { |
| | | if (!Array.isArray(value)) { |
| | | value = [] |
| | | // console.error('props 类型错误'); |
| | | } |
| | | } |
| | | dataList.forEach((item, index) => { |
| | | if (this.multiple) { |
| | | if (value.length > 0) { |
| | | let have = value.find(val => val === item.value) |
| | | item.selected = have !== undefined |
| | | } else { |
| | | item.selected = false |
| | | } |
| | | } else { |
| | | item.selected = value === item.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.value === item.value) |
| | | if (have !== undefined) { |
| | | item.disable = true |
| | | } |
| | | } |
| | | |
| | | if (selectList.length >= max && max !== '') { |
| | | let have = selectList.find(val => val.value === item.value) |
| | | if (have === undefined) { |
| | | item.disable = true |
| | | } |
| | | } |
| | | } |
| | | this.setClass(item, index) |
| | | list[index] = item |
| | | }) |
| | | return list |
| | | }, |
| | | /** |
| | | * 设置 class |
| | | * @param {Object} item |
| | | * @param {Object} index |
| | | */ |
| | | setClass(item, index) { |
| | | // 设置 label 的 class |
| | | item.labelClass = this.getLabelClass(item, index) |
| | | // 设置 checkbox外层样式 |
| | | item.checkboxBgClass = this.getCheckboxClass(item, '-bg') |
| | | // 设置 checkbox 内层样式 |
| | | item.checkboxClass = this.getCheckboxClass(item) |
| | | // 设置文本样式 |
| | | item.textClass = this.getTextClass(item) |
| | | // 设置 list 对勾右侧样式 |
| | | item.listClass = this.getCheckboxClass(item, '-list') |
| | | |
| | | // 设置自定义样式 |
| | | item.styleBackgroud = this.setStyleBackgroud(item) |
| | | item.styleIcon = this.setStyleIcon(item) |
| | | item.styleIconText = this.setStyleIconText(item) |
| | | item.styleRightIcon = this.setStyleRightIcon(item) |
| | | }, |
| | | /** |
| | | * 获取 class |
| | | */ |
| | | getClasses(item, name, type = '') { |
| | | let classes = [] |
| | | item.disable && classes.push('is-' + name + '-disabled' + type) |
| | | item.selected && classes.push('is-' + name + '-checked' + type) |
| | | |
| | | if (this.mode !== 'button' || name === 'button') { |
| | | item.selected && item.disable && classes.push('is-' + name + '-disabled-checked' + type) |
| | | } |
| | | |
| | | return classes |
| | | }, |
| | | /** |
| | | * 获取选中值 |
| | | * @param {Object} range |
| | | */ |
| | | getSelectedValue(range) { |
| | | if (!this.multiple) return this.value |
| | | let selectedArr = [] |
| | | range.forEach((item) => { |
| | | if (item.selected) { |
| | | selectedArr.push(item.value) |
| | | } |
| | | }) |
| | | return this.value.length > 0 ? this.value : selectedArr |
| | | }, |
| | | |
| | | /** |
| | | * 设置背景样式 |
| | | */ |
| | | setStyleBackgroud(item) { |
| | | let styles = {} |
| | | |
| | | if (item.selected) { |
| | | if (this.mode !== 'list') { |
| | | styles.borderColor = this.styles.selectedColor |
| | | } |
| | | if (this.mode === 'tag') { |
| | | styles.backgroundColor = this.styles.selectedColor |
| | | } |
| | | } |
| | | return styles |
| | | }, |
| | | setStyleIcon(item) { |
| | | let styles = {} |
| | | |
| | | if (item.selected) { |
| | | styles.backgroundColor = this.styles.selectedColor |
| | | styles.borderColor = this.styles.selectedColor |
| | | } |
| | | return styles |
| | | }, |
| | | setStyleIconText(item) { |
| | | let styles = {} |
| | | if (item.selected) { |
| | | if (this.styles.selectedTextColor) { |
| | | styles.color = this.styles.selectedTextColor |
| | | } else { |
| | | if(this.mode === 'tag'){ |
| | | styles.color = '#fff' |
| | | }else{ |
| | | styles.color = this.styles.selectedColor |
| | | } |
| | | } |
| | | } |
| | | return styles |
| | | }, |
| | | setStyleRightIcon(item){ |
| | | let styles = {} |
| | | if (item.selected) { |
| | | if(this.mode === 'list'){ |
| | | styles.borderColor = this.styles.selectedColor |
| | | } |
| | | } |
| | | |
| | | return styles |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .uni-data-checklist { |
| | | /* min-height: 36px; */ |
| | | } |
| | | |
| | | .uni-data-loading { |
| | | display: flex; |
| | | align-items: center; |
| | | /* justify-content: center; */ |
| | | height: 36px; |
| | | padding-left: 10px; |
| | | } |
| | | |
| | | .checklist-group { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .checklist-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | margin: 5px 0; |
| | | margin-right: 25px; |
| | | } |
| | | |
| | | .checklist-text { |
| | | font-size: 14px; |
| | | color: #333; |
| | | margin-left: 5px; |
| | | transition: color 0.2s; |
| | | } |
| | | |
| | | .is-button { |
| | | margin-right: 10px; |
| | | padding: 3px 15px; |
| | | border: 1px #DCDFE6 solid; |
| | | border-radius: 3px; |
| | | transition: border-color 0.2s; |
| | | } |
| | | |
| | | .is-list { |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .is-list-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | padding: 10px 15px; |
| | | padding-left: 0; |
| | | margin: 0; |
| | | } |
| | | |
| | | .checklist-content { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .list-content { |
| | | margin-left: 5px; |
| | | } |
| | | |
| | | .is-list-multiple-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | padding: 10px 15px; |
| | | padding-left: 0; |
| | | margin: 0; |
| | | } |
| | | |
| | | .is-list-border { |
| | | border-top: 1px #eee solid; |
| | | } |
| | | |
| | | .is-tag { |
| | | margin-right: 10px; |
| | | padding: 3px 10px; |
| | | border: 1px #eee solid; |
| | | border-radius: 3px; |
| | | background-color: #f5f5f5; |
| | | /* transition: border-color 0.1s; */ |
| | | } |
| | | |
| | | .is-tag-text { |
| | | margin: 0; |
| | | color: #666; |
| | | } |
| | | |
| | | .checkbox__inner { |
| | | flex-shrink: 0; |
| | | position: relative; |
| | | border: 1px solid #DCDFE6; |
| | | border-radius: 2px; |
| | | box-sizing: border-box; |
| | | width: 16px; |
| | | height: 16px; |
| | | background-color: #fff; |
| | | z-index: 1; |
| | | transition: border-color 0.1s; |
| | | } |
| | | |
| | | .checkbox__inner-icon { |
| | | border: 1px solid #fff; |
| | | border-left: 0; |
| | | border-top: 0; |
| | | height: 8px; |
| | | left: 5px; |
| | | position: absolute; |
| | | top: 1px; |
| | | width: 3px; |
| | | opacity: 0; |
| | | transition: transform .2s; |
| | | transform-origin: center; |
| | | transform: rotate(40deg) scaleY(0.4); |
| | | } |
| | | |
| | | .radio__inner { |
| | | flex-shrink: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: relative; |
| | | border: 1px solid #DCDFE6; |
| | | border-radius: 2px; |
| | | box-sizing: border-box; |
| | | width: 16px; |
| | | height: 16px; |
| | | border-radius: 16px; |
| | | background-color: #fff; |
| | | z-index: 1; |
| | | transition: border-color .3s; |
| | | } |
| | | |
| | | .radio__inner-icon { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 10px; |
| | | opacity: 0; |
| | | transition: transform .3s; |
| | | } |
| | | |
| | | .checkobx__list { |
| | | border: 1px solid #fff; |
| | | border-left: 0; |
| | | border-top: 0; |
| | | height: 12px; |
| | | width: 6px; |
| | | transform-origin: center; |
| | | opacity: 0; |
| | | transition: all 0.3s; |
| | | transform: rotate(45deg); |
| | | } |
| | | |
| | | |
| | | /* 禁用样式 */ |
| | | .is-default-disabled-bg { |
| | | background-color: #F2F6FC; |
| | | border-color: #DCDFE6; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .is-default-multiple-disabled-bg { |
| | | background-color: #F2F6FC; |
| | | border-color: #DCDFE6; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .is-default-disabled { |
| | | border-color: #F2F6FC; |
| | | } |
| | | |
| | | .is-default-multiple-disabled { |
| | | border-color: #F2F6FC; |
| | | } |
| | | |
| | | .is-list-disabled { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | color: #999; |
| | | } |
| | | |
| | | .is-list-disabled-checked { |
| | | color: #a1dcc1; |
| | | } |
| | | |
| | | |
| | | .is-button-disabled { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | border-color: #EBEEF5; |
| | | } |
| | | |
| | | .is-button-text-disabled { |
| | | color: #C0C4CC; |
| | | } |
| | | |
| | | .is-button-disabled-checked { |
| | | border-color: #a1dcc1; |
| | | } |
| | | |
| | | .is-tag-disabled { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | border-color: #e9e9eb; |
| | | background-color: #f4f4f5; |
| | | } |
| | | |
| | | .is-tag-text-disabled { |
| | | color: #bcbec2; |
| | | } |
| | | |
| | | /* 选中样式 */ |
| | | .is-default-checked-bg { |
| | | border-color: #007aff; |
| | | } |
| | | |
| | | .is-default-multiple-checked-bg { |
| | | border-color: #007aff; |
| | | background-color: #007aff; |
| | | } |
| | | |
| | | .is-default-checked { |
| | | opacity: 1; |
| | | background-color: #007aff; |
| | | transform: rotate(45deg) scaleY(1); |
| | | } |
| | | |
| | | .is-default-multiple-checked { |
| | | opacity: 1; |
| | | transform: rotate(45deg) scaleY(1); |
| | | } |
| | | |
| | | .is-default-disabled-checked-bg { |
| | | opacity: 0.4; |
| | | } |
| | | |
| | | .is-default-multiple-disabled-checked-bg { |
| | | opacity: 0.4; |
| | | } |
| | | |
| | | |
| | | .is-default-checked-list { |
| | | border-color: #007aff; |
| | | opacity: 1; |
| | | transform: rotate(45deg) scaleY(1); |
| | | } |
| | | |
| | | .is-default-multiple-checked-list { |
| | | border-color: #007aff; |
| | | opacity: 1; |
| | | transform: rotate(45deg) scaleY(1); |
| | | } |
| | | |
| | | .is-list-disabled-checked { |
| | | opacity: 0.4; |
| | | } |
| | | |
| | | .is-default-disabled-checked-list { |
| | | opacity: 0.4; |
| | | } |
| | | |
| | | .is-default-multiple-disabled-checked-list { |
| | | opacity: 0.4; |
| | | } |
| | | |
| | | .is-button-checked { |
| | | border-color: #007aff; |
| | | } |
| | | |
| | | .is-button-disabled-checked { |
| | | opacity: 0.4; |
| | | } |
| | | |
| | | .is-list-checked { |
| | | color: #007aff; |
| | | } |
| | | |
| | | .is-tag-checked { |
| | | border-color: #007aff; |
| | | background-color: #007aff; |
| | | } |
| | | |
| | | .is-tag-text-checked { |
| | | color: #fff; |
| | | } |
| | | |
| | | .is-tag-disabled-checked { |
| | | opacity: 0.4; |
| | | } |
| | | |
| | | |
| | | .disabled-cursor { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .is-wrap { |
| | | flex-direction: column; |
| | | } |
| | | </style> |
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': |
| | | 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 && 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> |
| | | /** |
| | | * Dateformat 日期格式化 |
| | | * @description 日期格式化组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx |
| | | * @property {Object|String|Number} date 日期对象/日期字符串/时间戳 |
| | | * @property {String} locale 格式化使用的语言 |
| | | * @value zh 中文 |
| | | * @value en 英文 |
| | | * @property {Array} threshold 应用不同类型格式化的阈值 |
| | | * @property {String} format 输出日期字符串时的格式 |
| | | */ |
| | | import { |
| | | friendlyDate |
| | | } from './date-format.js' |
| | | export default { |
| | | name: 'uniDateformat', |
| | | props: { |
| | | date: { |
| | | type: [Object, String, Number], |
| | | default () { |
| | | return Date.now() |
| | | } |
| | | }, |
| | | 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 |
| | |
| | | <template> |
| | | <view class="uni-datetime-picker"> |
| | | <view @click="tiggerTimePicker"> |
| | | <slot> |
| | | <view class="uni-datetime-picker-timebox uni-datetime-picker-flex"> |
| | | {{time}}<view v-if="!time" class="uni-datetime-picker-time">选择日期时间</view> |
| | | <view class="uni-datetime-picker-down-arrow"></view> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <view v-if="visible" class="uni-datetime-picker-mask" @click="initTimePicker"></view> |
| | | <view v-if="visible" class="uni-datetime-picker-popup"> |
| | | <view class="uni-title"> |
| | | 设置日期和时间 |
| | | </view> |
| | | <picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd" @change="bindDateChange"> |
| | | <picker-view-column class="uni-datetime-picker-hyphen"> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">{{item}}</view> |
| | | </picker-view-column> |
| | | <picker-view-column class="uni-datetime-picker-hyphen"> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">{{item < 10 ? '0' + item : item}}</view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">{{item < 10 ? '0' + item : item}}</view> |
| | | </picker-view-column> |
| | | </picker-view> |
| | | <picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange"> |
| | | <picker-view-column class="uni-datetime-picker-colon"> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">{{item < 10 ? '0' + item : item}}</view> |
| | | </picker-view-column> |
| | | <picker-view-column class="uni-datetime-picker-colon"> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">{{item < 10 ? '0' + item : item}}</view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">{{item < 10 ? '0' + item : item}}</view> |
| | | </picker-view-column> |
| | | </picker-view> |
| | | <view class="uni-datetime-picker-btn"> |
| | | <view class="" @click="clearTime">重置</view> |
| | | <view class="uni-datetime-picker-btn-group"> |
| | | <view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">取消</view> |
| | | <view class="" @click="setTime">确定</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | visible: false, |
| | | time: '', |
| | | years: [], |
| | | months: [], |
| | | days: [], |
| | | hours: [], |
| | | minutes: [], |
| | | seconds: [], |
| | | year: 1900, |
| | | month: 0, |
| | | day: 0, |
| | | hour: 0, |
| | | minute: 0, |
| | | second: 0, |
| | | indicatorStyle: `height: 50px;`, |
| | | } |
| | | }, |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: 'datetime-local' |
| | | }, |
| | | timestamp: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | maxYear: { |
| | | type: [Number, String], |
| | | default: 2100 |
| | | }, |
| | | minYear: { |
| | | type: [Number, String], |
| | | default: 1900 |
| | | } |
| | | }, |
| | | computed: { |
| | | ymd() { |
| | | return [this.year - this.minYear, this.month - 1, this.day - 1] |
| | | }, |
| | | hms() { |
| | | return [this.hour, this.minute, this.second] |
| | | } |
| | | }, |
| | | watch: { |
| | | value(newValue) { |
| | | this.parseValue(this.value) |
| | | this.initTime() |
| | | } |
| | | }, |
| | | created() { |
| | | this.form = this.getForm('uniForms') |
| | | this.formItem = this.getForm('uniFormsItem') |
| | | |
| | | if (this.formItem) { |
| | | if (this.formItem.name) { |
| | | this.rename = this.formItem.name |
| | | this.form.inputChildrens.push(this) |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | const date = new Date() |
| | | for (let i = this.minYear; i <= this.maxYear; i++) { |
| | | this.years.push(i) |
| | | } |
| | | |
| | | for (let i = 1; i <= 12; i++) { |
| | | this.months.push(i) |
| | | } |
| | | |
| | | for (let i = 1; i <= 31; i++) { |
| | | this.days.push(i) |
| | | } |
| | | |
| | | for (let i = 0; i <= 23; i++) { |
| | | this.hours.push(i) |
| | | } |
| | | |
| | | for (let i = 0; i <= 59; i++) { |
| | | this.minutes.push(i) |
| | | } |
| | | |
| | | for (let i = 0; i <= 59; i++) { |
| | | this.seconds.push(i) |
| | | } |
| | | this.parseValue(this.value) |
| | | if (this.value) { |
| | | this.initTime() |
| | | } |
| | | |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | 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; |
| | | }, |
| | | parseDateTime(datetime) { |
| | | let defaultDate = null |
| | | if (!datetime) { |
| | | defaultDate = new Date() |
| | | } else { |
| | | defaultDate = new Date(datetime) |
| | | } |
| | | this.year = defaultDate.getFullYear() |
| | | if (this.year < this.minYear || this.year > this.maxYear) { |
| | | const now = Date.now() |
| | | this.parseDateTime(now) |
| | | return |
| | | } |
| | | this.month = defaultDate.getMonth() + 1 |
| | | this.day = defaultDate.getDate() |
| | | this.hour = defaultDate.getHours() |
| | | this.minute = defaultDate.getMinutes() |
| | | this.second = defaultDate.getSeconds() |
| | | }, |
| | | parseValue(defaultTime) { |
| | | if (Number(defaultTime)) { |
| | | defaultTime = parseInt(defaultTime) |
| | | } |
| | | this.parseDateTime(defaultTime) |
| | | }, |
| | | 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.time) { |
| | | // this.parseValue() |
| | | // } |
| | | this.parseValue(this.value) |
| | | this.visible = !this.visible |
| | | }, |
| | | tiggerTimePicker() { |
| | | this.visible = !this.visible |
| | | }, |
| | | clearTime() { |
| | | this.time = '' |
| | | this.tiggerTimePicker() |
| | | }, |
| | | initTime() { |
| | | this.time = this.createDomSting() |
| | | if (!this.timestamp) { |
| | | this.formItem && this.formItem.setValue(this.time) |
| | | this.$emit('change', this.time) |
| | | } else { |
| | | this.formItem && this.formItem.setValue(this.createTimeStamp(this.time)) |
| | | this.$emit('change', this.createTimeStamp(this.time)) |
| | | } |
| | | }, |
| | | setTime() { |
| | | this.initTime() |
| | | this.tiggerTimePicker() |
| | | }, |
| | | createTimeStamp(time) { |
| | | return Date.parse(new Date(time)) |
| | | }, |
| | | createDomSting() { |
| | | const yymmdd = this.year + |
| | | '-' + |
| | | (this.month < 10 ? '0' + this.month : this.month) + |
| | | '-' + |
| | | (this.day < 10 ? '0' + this.day : this.day) + |
| | | ' ' + |
| | | (this.hour < 10 ? '0' + this.hour : this.hour) + |
| | | ':' + |
| | | (this.minute < 10 ? '0' + this.minute : this.minute) + |
| | | ':' + |
| | | (this.second < 10 ? '0' + this.second : this.second) |
| | | |
| | | return yymmdd |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .uni-datetime-picker-view { |
| | | width: 100%; |
| | | height: 130px; |
| | | margin-top: 30px; |
| | | } |
| | | |
| | | .uni-datetime-picker-item { |
| | | line-height: 50px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn { |
| | | margin-top: 60px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | color: blue; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn-group { |
| | | display: flex; |
| | | } |
| | | |
| | | .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; |
| | | background-color: #fff; |
| | | position: fixed; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%); |
| | | transition-duration: 0.3s; |
| | | z-index: 999; |
| | | } |
| | | |
| | | .uni-datetime-picker-time { |
| | | color: grey; |
| | | } |
| | | |
| | | .uni-datetime-picker-colon::after { |
| | | content: ':'; |
| | | position: absolute; |
| | | top: 53px; |
| | | right: 0; |
| | | } |
| | | |
| | | .uni-datetime-picker-hyphen::after { |
| | | content: '-'; |
| | | position: absolute; |
| | | top: 53px; |
| | | right: -2px; |
| | | } |
| | | |
| | | .uni-datetime-picker-timebox { |
| | | border: 1px solid #E5E5E5; |
| | | border-radius: 5px; |
| | | padding: 7px 10px; |
| | | box-sizing: border-box; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | // 下箭头 |
| | | .uni-datetime-picker-down-arrow { |
| | | display :inline-block; |
| | | position: relative; |
| | | width: 20px; |
| | | height: 15px; |
| | | } |
| | | |
| | | .uni-datetime-picker-down-arrow::after { |
| | | display: inline-block; |
| | | content: " "; |
| | | height: 9px; |
| | | width: 9px; |
| | | border-width: 0 1px 1px 0; |
| | | border-color: #E5E5E5; |
| | | border-style: solid; |
| | | transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0); |
| | | transform-origin: center; |
| | | transition: transform .3s; |
| | | position: absolute; |
| | | top: 50%; |
| | | right: 5px; |
| | | margin-top: -5px; |
| | | } |
| | | .uni-datetime-picker-flex { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | </style> |
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> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * 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', |
| | | 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" scoped> |
| | | // 抽屉宽度 |
| | | $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-bg-color-mask; |
| | | transition: opacity 0.3s; |
| | | } |
| | | |
| | | .uni-drawer__mask--visible { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | opacity: 1; |
| | | } |
| | | </style> |
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}"> |
| | | <view class="uni-easyinput__content" :class="{'is-input-border':inputBorder ,'is-input-error-border':inputBorder && msg,'is-textarea':type==='textarea','is-disabled':disabled}"> |
| | | <uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc"></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" |
| | | :maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" @input="onInput" @blur="onBlur" @focus="onFocus" |
| | | @confirm="onConfirm"></textarea> |
| | | <input v-else :type="type === 'password'?'text':type" class="uni-easyinput__content-input" :style="{ |
| | | 'padding-right':type === 'password' ||clearable || prefixIcon?'':'10px', |
| | | 'padding-left':prefixIcon?'':'10px', |
| | | 'color':msg?'#dd524d':'' |
| | | }" |
| | | :name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder" |
| | | :placeholderStyle="placeholderStyle" :disabled="disabled" :maxlength="inputMaxlength" :focus="focused" @focus="onFocus" |
| | | @blur="onBlur" @input="onInput" @confirm="onConfirm" /> |
| | | <template v-if="type === 'password'"> |
| | | <uni-icons v-if="val != '' " class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}" :type="showPassword?'eye-slash-filled':'eye-filled'" |
| | | :size="18" color="#c0c4cc" @click="onEyes"></uni-icons> |
| | | </template> |
| | | <template v-else-if="suffixIcon"> |
| | | <uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc"></uni-icons> |
| | | </template> |
| | | <template v-else> |
| | | <uni-icons class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}" type="clear" :size="clearSize" |
| | | v-if="clearable && focused && val " color="#c0c4cc" @click="onClear"></uni-icons> |
| | | </template> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Field 输入框 |
| | | * @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=21001 |
| | | * @property {String| Number} 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 {Boolean} trim 是否自动去除两端的空格 |
| | | * @property {Boolean} inputBorder 是否显示input输入框的边框(默认false) |
| | | * @event {Function} input 输入框内容发生变化时触发 |
| | | * @event {Function} focus 输入框获得焦点时触发 |
| | | * @event {Function} blur 输入框失去焦点时触发 |
| | | * @event {Function} confirm 点击完成按钮时触发 |
| | | * @example <uni-easyinput v-model="mobile"></uni-easyinput> |
| | | */ |
| | | |
| | | import { |
| | | debounce, |
| | | throttle |
| | | } from './common.js' |
| | | |
| | | export default { |
| | | name: 'uni-easyinput', |
| | | props: { |
| | | name: String, |
| | | value: [Number, String], |
| | | type: { |
| | | type: String, |
| | | default: 'text' |
| | | }, |
| | | clearable: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | autoHeight: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | placeholder: String, |
| | | 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: 15 |
| | | }, |
| | | // 是否显示 input 边框 |
| | | inputBorder: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | prefixIcon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | suffixIcon: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 是否自动去除两端的空格 |
| | | trim: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | focused: false, |
| | | errMsg: '', |
| | | val: '', |
| | | showMsg: '', |
| | | border: false, |
| | | isFirstBorder: false, |
| | | showClearIcon: false, |
| | | showPassword: false |
| | | }; |
| | | }, |
| | | computed: { |
| | | msg() { |
| | | return this.errorMessage || this.errMsg; |
| | | }, |
| | | // 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,给用户可以传入字符串数值 |
| | | inputMaxlength() { |
| | | return Number(this.maxlength); |
| | | }, |
| | | }, |
| | | watch: { |
| | | value(newVal) { |
| | | this.val = newVal |
| | | if (this.formItem) { |
| | | this.formItem.setValue(newVal) |
| | | } |
| | | }, |
| | | focus(newVal) { |
| | | this.$nextTick(() => { |
| | | this.focused = this.focus |
| | | }) |
| | | } |
| | | }, |
| | | created() { |
| | | this.val = this.value |
| | | this.form = this.getForm('uniForms') |
| | | this.formItem = this.getForm('uniFormsItem') |
| | | if (this.formItem) { |
| | | if (this.formItem.name) { |
| | | this.rename = this.formItem.name |
| | | this.form.inputChildrens.push(this) |
| | | } |
| | | } |
| | | |
| | | }, |
| | | mounted() { |
| | | // this.onInput = throttle(this.input, 500) |
| | | this.$nextTick(() => { |
| | | this.focused = this.focus |
| | | }) |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 初始化变量值 |
| | | */ |
| | | init() { |
| | | |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | 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; |
| | | }, |
| | | |
| | | onEyes() { |
| | | this.showPassword = !this.showPassword |
| | | }, |
| | | onInput(event) { |
| | | let value = event.detail.value; |
| | | // 判断是否去除空格 |
| | | if (this.trim) value = this.trimStr(value); |
| | | if (this.errMsg) this.errMsg = '' |
| | | this.val = value |
| | | this.$emit('input', value); |
| | | }, |
| | | |
| | | onFocus(event) { |
| | | this.focused = true; |
| | | this.$emit('focus', event); |
| | | }, |
| | | onBlur(event) { |
| | | let value = event.detail.value; |
| | | // 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错 |
| | | // 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时 |
| | | setTimeout(() => { |
| | | this.focused = false; |
| | | }, 100); |
| | | this.$emit('blur', event); |
| | | }, |
| | | onConfirm(e) { |
| | | this.$emit('confirm', e.detail.value); |
| | | }, |
| | | onClear(event) { |
| | | this.val = ''; |
| | | this.$emit('input', ''); |
| | | }, |
| | | fieldClick() { |
| | | this.$emit('click'); |
| | | }, |
| | | trimStr(str, pos = 'both') { |
| | | if (pos == 'both') { |
| | | return str.replace(/^\s+|\s+$/g, ''); |
| | | } else if (pos == 'left') { |
| | | return str.replace(/^\s*/, ''); |
| | | } else if (pos == 'right') { |
| | | return str.replace(/(\s*$)/g, ''); |
| | | } else if (pos == 'all') { |
| | | return str.replace(/\s+/g, ''); |
| | | } else { |
| | | return str; |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-easyinput { |
| | | /* #ifndef APP-NVUE */ |
| | | width: 100%; |
| | | /* #endif */ |
| | | flex: 1; |
| | | position: relative; |
| | | // padding: 16px 14px; |
| | | text-align: left; |
| | | color: #333; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-easyinput__content { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | width: 100%; |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | box-sizing: border-box; |
| | | min-height: 36px; |
| | | } |
| | | |
| | | .uni-easyinput__content-input { |
| | | position: relative; |
| | | overflow: hidden; |
| | | flex: 1; |
| | | width: auto; |
| | | line-height: 2; |
| | | font-size: 14px; |
| | | // padding-right: 10px; |
| | | } |
| | | |
| | | .is-textarea { |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .is-textarea-icon { |
| | | margin-top: 5px; |
| | | } |
| | | |
| | | .uni-easyinput__content-textarea { |
| | | position: relative; |
| | | overflow: hidden; |
| | | flex: 1; |
| | | width: auto; |
| | | line-height: 1.5; |
| | | font-size: 14px; |
| | | // padding-right: 10px; |
| | | padding-top: 6px; |
| | | padding-bottom: 10px; |
| | | // box-sizing: border-box; |
| | | min-height: 80px; |
| | | height: 80px; |
| | | } |
| | | |
| | | .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; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | border: 1px solid $uni-border-color; |
| | | border-radius: 4px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .uni-easyinput__right { |
| | | // margin-left: 5px; |
| | | } |
| | | |
| | | // 必填 |
| | | .is-required { |
| | | color: $uni-color-error; |
| | | } |
| | | |
| | | .uni-error-message { |
| | | position: absolute; |
| | | bottom: -17px; |
| | | left: 0; |
| | | line-height: 12px; |
| | | color: $uni-color-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-color-error; |
| | | } |
| | | |
| | | .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 { |
| | | border: none; |
| | | } |
| | | |
| | | .is-disabled { |
| | | background-color: #eee; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view> |
| | | <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"> |
| | | <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="widthFix" /> |
| | | <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 }" @click="_onClick"> |
| | | <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', |
| | | 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: '#3c3e49' |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | contentWidth(e) { |
| | | return (this.content.length + 1) * 55 + 10 + 'px' |
| | | }, |
| | | contentWidthMin() { |
| | | return 55 + 'px' |
| | | }, |
| | | // 动态计算宽度 |
| | | 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') |
| | | } |
| | | }, |
| | | watch: { |
| | | pattern(newValue, oldValue) { |
| | | //console.log(JSON.stringify(newValue)) |
| | | this.styles = Object.assign({}, this.styles, newValue) |
| | | } |
| | | }, |
| | | 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) { |
| | | 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" scoped> |
| | | .uni-fab { |
| | | position: fixed; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .uni-fab--active { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .uni-fab--leftBottom { |
| | | left: 5px; |
| | | bottom: 20px; |
| | | /* #ifdef H5 */ |
| | | bottom: calc(20px + var(--window-bottom)); |
| | | /* #endif */ |
| | | padding: 10px; |
| | | } |
| | | |
| | | .uni-fab--leftTop { |
| | | left: 5px; |
| | | top: 30px; |
| | | /* #ifdef H5 */ |
| | | top: calc(30px + var(--window-top)); |
| | | /* #endif */ |
| | | padding: 10px; |
| | | } |
| | | |
| | | .uni-fab--rightBottom { |
| | | right: 5px; |
| | | bottom: 20px; |
| | | /* #ifdef H5 */ |
| | | bottom: calc(20px + var(--window-bottom)); |
| | | /* #endif */ |
| | | padding: 10px; |
| | | } |
| | | |
| | | .uni-fab--rightTop { |
| | | right: 5px; |
| | | top: 30px; |
| | | /* #ifdef H5 */ |
| | | 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: 55px; |
| | | z-index: 11; |
| | | } |
| | | |
| | | .uni-fab__circle--leftBottom { |
| | | left: 15px; |
| | | bottom: 30px; |
| | | /* #ifdef H5 */ |
| | | bottom: calc(30px + var(--window-bottom)); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-fab__circle--leftTop { |
| | | left: 15px; |
| | | top: 40px; |
| | | /* #ifdef H5 */ |
| | | top: calc(40px + var(--window-top)); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-fab__circle--rightBottom { |
| | | right: 15px; |
| | | bottom: 30px; |
| | | /* #ifdef H5 */ |
| | | bottom: calc(30px + var(--window-bottom)); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-fab__circle--rightTop { |
| | | right: 15px; |
| | | top: 40px; |
| | | /* #ifdef H5 */ |
| | | top: calc(40px + 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: 3px; |
| | | height: 31px; |
| | | left: 26px; |
| | | top: 12px; |
| | | background-color: white; |
| | | transform: rotate(0deg); |
| | | transition: transform 0.3s; |
| | | } |
| | | |
| | | .fab-circle-h { |
| | | position: absolute; |
| | | width: 31px; |
| | | height: 3px; |
| | | left: 12px; |
| | | top: 26px; |
| | | background-color: white; |
| | | transform: rotate(0deg); |
| | | transition: transform 0.3s; |
| | | } |
| | | |
| | | .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: 0 0 5px 2px rgba(0, 0, 0, 0.2); |
| | | } |
| | | |
| | | .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: 25px; |
| | | height: 25px; |
| | | margin-bottom: 3px; |
| | | } |
| | | |
| | | .uni-fab__item-text { |
| | | color: #FFFFFF; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .uni-fab__item--first { |
| | | width: 55px; |
| | | } |
| | | </style> |
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 ? contentText.contentFav : contentText.contentDefault }}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from "../uni-icons/uni-icons.vue"; |
| | | |
| | | /** |
| | | * 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] 收藏按钮文字 |
| | | * @event {Function} click 点击 fav按钮触发事件 |
| | | * @example <uni-fav :checked="true"/> |
| | | */ |
| | | export default { |
| | | name: "UniFav", |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | 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: "已收藏" |
| | | }; |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | checked() { |
| | | if (uni.report) { |
| | | if (this.checked) { |
| | | uni.report("收藏", "收藏"); |
| | | } else { |
| | | uni.report("取消收藏", "取消收藏"); |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick() { |
| | | this.$emit("click"); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $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; |
| | | } |
| | | |
| | | .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: $uni-font-size-base; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-field" :class="{ 'uni-border-top': borderTop, 'uni-border-bottom': borderBottom }" :style="[fieldStyle]"> |
| | | <view class="uni-field-inner" :class="[type == 'textarea' ? 'uni-textarea-inner' : '', 'uni-label-postion-' + labelPos]"> |
| | | <view :class="errorTop ? 'uni-error-in-label' : ''"> |
| | | <view class="uni-field-label" :class="[required ? 'uni-required' : '']" :style="{ |
| | | justifyContent: justifyContent, |
| | | width: labelWid + 'px', |
| | | marginBottom: labelMarginBottom |
| | | }"> |
| | | <view class="uni-icon-wrap" v-if="leftIcon"> |
| | | <uni-icons size="16" :type="leftIcon" :color="iconColor" /> |
| | | </view> |
| | | <slot name="leftIcon"></slot> |
| | | <text class="uni-label-text" :class="[leftIcon ? 'uni-label-left-gap' : '']">{{ label }}</text> |
| | | </view> |
| | | <view v-if="errorTop" class="uni-error-message" :style="{ paddingLeft: '4px' }">{{ msg }}</view> |
| | | </view> |
| | | <view class="fild-body" :class="[inputBorder ? 'uni-input-border' : '']" :style="[borderEixstTextareaStyle]"> |
| | | <view class="uni-flex-1 uni-flex" :style="[inputWrapStyle]"> |
| | | <textarea v-if="type == 'textarea'" class="uni-flex-1 uni-textarea-class" :name="name" :value="value" :placeholder="placeholder" |
| | | :placeholderStyle="placeholderStyle" :disabled="disabled" :maxlength="inputMaxlength" :focus="focus" :autoHeight="autoHeight" |
| | | @input="onInput" @blur="onBlur" @focus="onFocus" @confirm="onConfirm" @tap="fieldClick" /> |
| | | <input |
| | | v-else |
| | | :type="type" |
| | | class="uni-flex-1 uni-field__input-wrap" |
| | | :name="name" |
| | | :value="value" |
| | | :password="password || this.type === 'password'" |
| | | :placeholder="placeholder" |
| | | :placeholderStyle="placeholderStyle" |
| | | :disabled="disabled" |
| | | :maxlength="inputMaxlength" |
| | | :focus="focus" |
| | | :confirmType="confirmType" |
| | | @focus="onFocus" |
| | | @blur="onBlur" |
| | | @input="onInput" |
| | | @confirm="onConfirm" |
| | | @tap="fieldClick" |
| | | /> |
| | | <uni-icons :size="clearSize" v-if="clearable && value != ''" type="clear" color="#c0c4cc" @click="onClear" class="uni-clear-icon" /> |
| | | </view> |
| | | <view class="uni-button-wrap"><slot name="right" /></view> |
| | | <uni-icons v-if="rightIcon" size="16" @click="rightIconClick" :type="rightIcon" color="#c0c4cc" :style="[rightIconStyle]" /> |
| | | </view> |
| | | </view> |
| | | <view |
| | | v-if="errorBottom" |
| | | class="uni-error-message" |
| | | :style="{ |
| | | paddingLeft: Number(labelWid) + 4 + 'px' |
| | | }" |
| | | > |
| | | {{ msg }} |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Field 输入框 |
| | | * @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=21001 |
| | | * @property {String } type 输入框的类型(默认text) |
| | | * @property {Boolean} required 是否必填,左边您显示红色"*"号(默认false) |
| | | * @property {String } leftIcon label左边的图标,限uni-ui的图标名称 |
| | | * @property {String } iconColor 左边通过icon配置的图标的颜色(默认#606266) |
| | | * @property {Boolean} rightIcon 输入框右边的图标名称,限uni-ui的图标名称(默认false) |
| | | * @property {String } label 输入框左边的文字提示 |
| | | * @property {Number } labelWidth label的宽度,单位px(默认65) |
| | | * @property {String } labelAlign label的文字对齐方式(默认left) |
| | | * @property {String } labelPosition label的文字的位置(默认left) |
| | | * @property {Boolean} clearable 是否显示右侧清空内容的图标控件(输入框有内容,且获得焦点时才显示),点击可清空输入框内容(默认true) |
| | | * @property {String } placeholder 输入框的提示文字 |
| | | * @property {String } placeholderStyle placeholder的样式(内联样式,字符串),如"color: #ddd" |
| | | * @property {Boolean} password 是否密码输入方式(用点替换文字),type为text时有效(默认false) |
| | | * @property {Boolean} focus 是否自动获得焦点(默认false) |
| | | * @property {Boolean} disabled 是否不可输入(默认false) |
| | | * @property {Number } maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认140) |
| | | * @property {String } confirmType 设置键盘右下角按钮的文字,仅在type="text"时生效(默认done) |
| | | * @property {String } errorMessage 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息 |
| | | * @property {Number } clearSize 清除图标的大小,单位px(默认15) |
| | | * @property {Boolean} trim 是否自动去除两端的空格 |
| | | * @property {String } name 表单域的属性名,在使用校验规则时必填 |
| | | * @property {Array } rules 单行表单验证规则,接受一个数组 |
| | | * @property {Boolean} inputBorder 是否显示input输入框的边框(默认false) |
| | | * @property {Boolean} border-bottom 是否显示field的下边框(默认true) |
| | | * @property {Boolean} border-top 是否显示field的上边框(默认false) |
| | | * @property {Boolean} auto-height 是否自动增高输入区域,type为textarea时有效(默认true) |
| | | * @event {Function} input 输入框内容发生变化时触发 |
| | | * @event {Function} focus 输入框获得焦点时触发 |
| | | * @event {Function} blur 输入框失去焦点时触发 |
| | | * @event {Function} confirm 点击完成按钮时触发 |
| | | * @event {Function} right-icon-click 通过right-icon生成的图标被点击时触发 |
| | | * @event {Function} click 输入框被点击或者通过right-icon生成的图标被点击时触发,这样设计是考虑到传递右边的图标,一般都为需要弹出"picker"等操作时的场景,点击倒三角图标,理应发出此事件,见上方说明 |
| | | * @example <uni-field v-model="mobile" label="手机号" required :error-message="errorMessage"></uni-field> |
| | | */ |
| | | export default { |
| | | name: 'uni-field', |
| | | props: { |
| | | // rules:{ |
| | | // type:Array, |
| | | // default(){ |
| | | // return [] |
| | | // } |
| | | // }, |
| | | trigger: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | leftIcon: String, |
| | | rightIcon: String, |
| | | required: Boolean, |
| | | label: String, |
| | | password: Boolean, |
| | | clearable: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 左边标题的宽度单位px |
| | | labelWidth: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | // 对齐方式,left|center|right |
| | | labelAlign: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | iconColor: { |
| | | type: String, |
| | | default: '#606266' |
| | | }, |
| | | autoHeight: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | errorMessage: { |
| | | type: [String, Boolean], |
| | | default: '' |
| | | }, |
| | | placeholder: String, |
| | | placeholderStyle: String, |
| | | focus: Boolean, |
| | | name: String, |
| | | value: [Number, String], |
| | | type: { |
| | | type: String, |
| | | default: 'text' |
| | | }, |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | maxlength: { |
| | | type: [Number, String], |
| | | default: 140 |
| | | }, |
| | | confirmType: { |
| | | type: String, |
| | | default: 'done' |
| | | }, |
| | | // lable的位置,可选为 left-左边,top-上边 |
| | | labelPosition: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 清除按钮的大小 |
| | | clearSize: { |
| | | type: [Number, String], |
| | | default: 15 |
| | | }, |
| | | // 是否显示 input 边框 |
| | | inputBorder: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示上边框 |
| | | borderTop: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示下边框 |
| | | borderBottom: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 是否自动去除两端的空格 |
| | | trim: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | focused: false, |
| | | itemIndex: 0, |
| | | errorTop: false, |
| | | errorBottom: false, |
| | | labelMarginBottom: '', |
| | | errorWidth: '', |
| | | errMsg: '', |
| | | errorBorderColor: false, |
| | | val: '', |
| | | labelPos: '', |
| | | labelWid: '', |
| | | labelAli: '' |
| | | }; |
| | | }, |
| | | computed: { |
| | | msg() { |
| | | return this.errorMessage || this.errMsg; |
| | | }, |
| | | fieldStyle() { |
| | | let style = {}; |
| | | if (this.labelPos === 'top') { |
| | | style.padding = '10px 14px'; |
| | | this.labelMarginBottom = '6px'; |
| | | } |
| | | if (this.labelPos === 'left' && this.msg !== false && this.msg !== '') { |
| | | style.paddingBottom = '0px'; |
| | | this.errorBottom = true; |
| | | this.errorTop = false; |
| | | } else if (this.labelPos === 'top' && this.msg !== false && this.msg !== '') { |
| | | this.errorBottom = false; |
| | | this.errorTop = true; |
| | | } else { |
| | | // style.paddingBottom = '' |
| | | this.errorTop = false; |
| | | this.errorBottom = false; |
| | | } |
| | | return style; |
| | | }, |
| | | |
| | | borderEixstTextareaStyle() { |
| | | let style = {}; |
| | | if (this.inputBorder) { |
| | | if (this.type === 'textarea') { |
| | | style.minHeight = '60px'; |
| | | } |
| | | if (this.msg !== false && this.msg != '') { |
| | | style.borderColor = '#dd524d'; |
| | | } |
| | | } |
| | | return style; |
| | | }, |
| | | |
| | | inputWrapStyle() { |
| | | let style = {}; |
| | | // 判断lable的位置,如果是left的话,让input左边两边有间隙 |
| | | if (this.labelPos == 'left') { |
| | | style.margin = `0 4px`; |
| | | } else { |
| | | // 如果lable是top的,input的左边就没必要有间隙了 |
| | | style.marginRight = `4px`; |
| | | // this.fieldStyle.style.padding = '10px 14px' |
| | | } |
| | | return style; |
| | | }, |
| | | rightIconStyle() { |
| | | let style = {}; |
| | | if (this.arrowDirection == 'top') style.transform = 'roate(-90deg)'; |
| | | if (this.arrowDirection == 'bottom') style.transform = 'roate(90deg)'; |
| | | else style.transform = 'roate(0deg)'; |
| | | return style; |
| | | }, |
| | | labelStyle() { |
| | | let style = {}; |
| | | if (this.labelAli == 'left') style.justifyContent = 'flext-start'; |
| | | if (this.labelAli == 'center') style.justifyContent = 'center'; |
| | | if (this.labelAli == 'right') style.justifyContent = 'flext-end'; |
| | | return style; |
| | | }, |
| | | // uni不支持在computed中写style.justifyContent = 'center'的形式,故用此方法 |
| | | justifyContent() { |
| | | if (this.labelAli == 'left') return 'flex-start'; |
| | | if (this.labelAli == 'center') return 'center'; |
| | | if (this.labelAli == 'right') return 'flex-end'; |
| | | }, |
| | | // 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,给用户可以传入字符串数值 |
| | | inputMaxlength() { |
| | | return Number(this.maxlength); |
| | | }, |
| | | // label的位置 |
| | | fieldInnerStyle() { |
| | | let style = {}; |
| | | if (this.labelPos == 'left') { |
| | | style.flexDirection = 'row'; |
| | | } else { |
| | | style.flexDirection = 'column'; |
| | | } |
| | | |
| | | return style; |
| | | } |
| | | }, |
| | | watch: { |
| | | trigger(trigger) { |
| | | this.formTrigger = trigger; |
| | | } |
| | | }, |
| | | created() { |
| | | this.form = this.getForm(); |
| | | this.formRules = []; |
| | | this.formTrigger = this.trigger; |
| | | this.init(); |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 初始化变量值 |
| | | */ |
| | | init() { |
| | | if (this.form) { |
| | | this.form.childrens.push(this); |
| | | this.labelPos = this.labelPosition ? this.labelPosition : this.form.labelPosition; |
| | | this.labelWid = this.labelWidth ? this.labelWidth : this.form.labelWidth; |
| | | this.labelAli = this.labelAlign ? this.labelAlign : this.form.labelAlign; |
| | | |
| | | if (this.form.formRules) { |
| | | this.formRules = this.form.formRules[this.name]; |
| | | } |
| | | this.validator = this.form.validator; |
| | | if(this.name){ |
| | | this.form.formData[this.name] = this.value || ''; |
| | | } |
| | | } else { |
| | | this.labelPos = this.labelPosition || 'left'; |
| | | this.labelWid = this.labelWidth || 65; |
| | | this.labelAli = this.labelAlign || 'left'; |
| | | } |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getForm() { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== 'uniForms') { |
| | | parent = parent.$parent; |
| | | if (!parent) return false; |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | }, |
| | | |
| | | /** |
| | | * 移除该表单项的校验结果 |
| | | */ |
| | | clearValidate() { |
| | | this.errMsg = ''; |
| | | }, |
| | | /** |
| | | * 父组件处理函数 |
| | | * @param {Object} callback |
| | | */ |
| | | parentVal(callback) { |
| | | if (this.type === 'number') { |
| | | this.val = this.val === '' ? this.val : Number(this.val); |
| | | } |
| | | typeof callback === 'function' && |
| | | callback( |
| | | { |
| | | [this.name]: this.val |
| | | }, |
| | | this.name |
| | | ); |
| | | }, |
| | | /** |
| | | * 触发校验 |
| | | * @param {Object} trigger |
| | | * @param {Object} value |
| | | */ |
| | | triggerValidator(trigger, value) { |
| | | let isValid = false; |
| | | // 如果 name 不存在,则不开启校验 |
| | | this.formRules && |
| | | this.formRules.rules && |
| | | this.formRules.rules.forEach(item => { |
| | | item.trigger = this.isTrigger(this.form.formTrigger, this.formTrigger, item.trigger); |
| | | if (item.trigger !== trigger || item.trigger === 'submit') return; |
| | | isValid = true; |
| | | }); |
| | | |
| | | isValid && this.triggerCheck(value); |
| | | }, |
| | | /** |
| | | * 校验规则 |
| | | * @param {Object} value |
| | | */ |
| | | triggerCheck(value, item) { |
| | | // 输入值为 number |
| | | if (this.type === 'number') { |
| | | value = value === '' ? value : Number(value); |
| | | } |
| | | const result = this.validator.validateUpdate({ |
| | | [this.name]: value |
| | | }); |
| | | this.errMsg = !result ? '' : result.errorMessage; |
| | | this.form.validateCheck(result); |
| | | }, |
| | | /** |
| | | * 触发时机 |
| | | * @param {Object} event |
| | | */ |
| | | isTrigger(parentRule, itemRlue, rule) { |
| | | let rl = 'none'; |
| | | if (rule) { |
| | | rl = rule; |
| | | } else if (itemRlue) { |
| | | rl = itemRlue; |
| | | } else if (parentRule) { |
| | | rl = parentRule; |
| | | } else { |
| | | rl = 'blur'; |
| | | } |
| | | return rl; |
| | | }, |
| | | |
| | | onInput(event) { |
| | | let value = event.detail.value; |
| | | // 判断是否去除空格 |
| | | if (this.trim) value = this.trimStr(value); |
| | | this.form.formData[this.name] = value || ''; |
| | | this.val = value; |
| | | this.$emit('input', value); |
| | | // 校验输入 |
| | | this.triggerValidator('change', value); |
| | | }, |
| | | |
| | | onFocus(event) { |
| | | this.focused = true; |
| | | this.$emit('focus', event); |
| | | }, |
| | | onBlur(event) { |
| | | let value = event.detail.value; |
| | | // 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错 |
| | | // 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时 |
| | | setTimeout(() => { |
| | | this.focused = false; |
| | | }, 100); |
| | | this.$emit('blur', event); |
| | | |
| | | // 校验输入 |
| | | this.triggerValidator('blur', value); |
| | | }, |
| | | onConfirm(e) { |
| | | this.$emit('confirm', e.detail.value); |
| | | }, |
| | | onClear(event) { |
| | | this.val = ''; |
| | | this.$emit('input', ''); |
| | | this.clearValidate(); |
| | | }, |
| | | rightIconClick() { |
| | | this.$emit('right-icon-click'); |
| | | this.$emit('click'); |
| | | }, |
| | | fieldClick() { |
| | | this.$emit('click'); |
| | | }, |
| | | trimStr(str, pos = 'both') { |
| | | if (pos == 'both') { |
| | | return str.replace(/^\s+|\s+$/g, ''); |
| | | } else if (pos == 'left') { |
| | | return str.replace(/^\s*/, ''); |
| | | } else if (pos == 'right') { |
| | | return str.replace(/(\s*$)/g, ''); |
| | | } else if (pos == 'all') { |
| | | return str.replace(/\s+/g, ''); |
| | | } else { |
| | | return str; |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-field { |
| | | padding: 16px 14px; |
| | | text-align: left; |
| | | color: #333; |
| | | font-size: 14px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .uni-field-inner { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-textarea-inner { |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .uni-textarea-class { |
| | | min-height: 48px; |
| | | width: auto; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .fild-body { |
| | | width: 100%; |
| | | display: flex; |
| | | flex: 1; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-arror-right { |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | .uni-label-text { |
| | | display: inline-block; |
| | | } |
| | | |
| | | .uni-label-left-gap { |
| | | margin-left: 3px; |
| | | } |
| | | |
| | | .uni-label-postion-top { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-field-label { |
| | | width: 65px; |
| | | flex: 1 1 65px; |
| | | text-align: left; |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-required::before { |
| | | content: '*'; |
| | | position: absolute; |
| | | left: -8px; |
| | | font-size: 14px; |
| | | color: $uni-color-error; |
| | | height: 9px; |
| | | line-height: 1; |
| | | } |
| | | |
| | | .uni-field__input-wrap { |
| | | position: relative; |
| | | overflow: hidden; |
| | | font-size: 14px; |
| | | height: 24px; |
| | | flex: 1; |
| | | width: auto; |
| | | } |
| | | |
| | | .uni-clear-icon { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-error-message { |
| | | line-height: 12px; |
| | | padding-top: 2px; |
| | | padding-bottom: 2px; |
| | | color: $uni-color-error; |
| | | font-size: 12px; |
| | | text-align: left; |
| | | } |
| | | |
| | | .uni-input-error-border { |
| | | border-color: $uni-color-error; |
| | | } |
| | | |
| | | .placeholder-style { |
| | | color: rgb(150, 151, 153); |
| | | } |
| | | |
| | | .uni-input-class { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-button-wrap { |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | /* start--Retina 屏幕下的 1px 边框--start */ |
| | | .uni-border, |
| | | .uni-border-bottom, |
| | | .uni-border-left, |
| | | .uni-border-right, |
| | | .uni-border-top, |
| | | .uni-border-top-bottom { |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-border-bottom:after, |
| | | .uni-border-left:after, |
| | | .uni-border-right:after, |
| | | .uni-border-top-bottom:after, |
| | | .uni-border-top:after, |
| | | .uni-border:after { |
| | | /* #ifndef APP-NVUE */ |
| | | content: ' '; |
| | | /* #endif */ |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | pointer-events: none; |
| | | box-sizing: border-box; |
| | | -webkit-transform-origin: 0 0; |
| | | transform-origin: 0 0; |
| | | // 多加0.1%,能解决有时候边框缺失的问题 |
| | | width: 199.8%; |
| | | height: 199.7%; |
| | | transform: scale(0.5, 0.5); |
| | | border: 0 solid $uni-border-color; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .uni-input-border { |
| | | min-height: 34px; |
| | | padding-left: 4px; |
| | | border: 1px solid $uni-border-color; |
| | | border-radius: 6px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .uni-border-top:after { |
| | | border-top-width: 1px; |
| | | } |
| | | |
| | | .uni-border-left:after { |
| | | border-left-width: 1px; |
| | | } |
| | | |
| | | .uni-border-right:after { |
| | | border-right-width: 1px; |
| | | } |
| | | |
| | | .uni-border-bottom:after { |
| | | border-bottom-width: 1px; |
| | | } |
| | | |
| | | .uni-border-top-bottom:after { |
| | | border-width: 1px 0; |
| | | } |
| | | |
| | | .uni-border:after { |
| | | border-width: 1px; |
| | | } |
| | | /* end--Retina 屏幕下的 1px 边框--end */ |
| | | |
| | | .uni-icon-wrap { |
| | | padding-left: 3px; |
| | | padding-right: 3px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .uni-button-wrap { |
| | | display: flex; |
| | | align-items: right; |
| | | justify-content: center; |
| | | } |
| | | .uni-clear-icon { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | .uni-flex { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-flex-1 { |
| | | flex: 1; |
| | | } |
| | | .uni-error-in-label { |
| | | display: flex; |
| | | flex-direction: row; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-forms-item" :class="{'uni-forms-item--border':border,'is-first-border':border&&isFirstBorder,'uni-forms-item-error':msg}"> |
| | | <view class="uni-forms-item__inner" :class="['is-direction-'+labelPos,]"> |
| | | <view v-if="label" class="uni-forms-item__label" :style="{width:labelWid+'px',justifyContent: justifyContent}"> |
| | | <slot name="left"> |
| | | <uni-icons v-if="leftIcon" class="label-icon" size="16" :type="leftIcon" :color="iconColor" /> |
| | | <text>{{label}}</text> |
| | | <text v-if="required" class="is-required">*</text> |
| | | </slot> |
| | | </view> |
| | | <view class="uni-forms-item__content" :class="{'is-input-error-border': msg}"> |
| | | <slot></slot> |
| | | </view> |
| | | </view> |
| | | <view class="uni-error-message" :class="{'uni-error-msg--boeder':border}" :style="{ |
| | | paddingLeft: (labelPos === 'left'? Number(labelWid)+5:5) + 'px' |
| | | }">{{ showMsg === 'undertext' ? msg:'' }}</view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Field 输入框 |
| | | * @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=21001 |
| | | * @property {Boolean} required 是否必填,左边显示红色"*"号(默认false) |
| | | * @property {String} validateTrigger = [bind|submit] 校验触发器方式 默认 submit 可选 |
| | | * @value bind 发生变化时触发 |
| | | * @value submit 提交时触发 |
| | | * @property {String } leftIcon label左边的图标,限 uni-ui 的图标名称 |
| | | * @property {String } iconColor 左边通过icon配置的图标的颜色(默认#606266) |
| | | * @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 } labelPosition = [top|left] label的文字的位置(默认left) |
| | | * @value top 顶部显示 label |
| | | * @value left 左侧显示 label |
| | | * @property {String } errorMessage 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息 |
| | | * @property {String } name 表单域的属性名,在使用校验规则时必填 |
| | | */ |
| | | |
| | | |
| | | |
| | | export default { |
| | | name: "uniFormsItem", |
| | | props: { |
| | | // 自定义内容 |
| | | custom: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示报错信息 |
| | | showMessage: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | name: String, |
| | | required: Boolean, |
| | | validateTrigger: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | leftIcon: String, |
| | | iconColor: { |
| | | type: String, |
| | | default: '#606266' |
| | | }, |
| | | label: String, |
| | | // 左边标题的宽度单位px |
| | | labelWidth: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | // 对齐方式,left|center|right |
| | | labelAlign: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // lable的位置,可选为 left-左边,top-上边 |
| | | labelPosition: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | errorMessage: { |
| | | type: [String, Boolean], |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | errorTop: false, |
| | | errorBottom: false, |
| | | labelMarginBottom: '', |
| | | errorWidth: '', |
| | | errMsg: '', |
| | | val: '', |
| | | labelPos: '', |
| | | labelWid: '', |
| | | labelAli: '', |
| | | showMsg: 'undertext', |
| | | border: false, |
| | | isFirstBorder: false |
| | | }; |
| | | }, |
| | | computed: { |
| | | msg() { |
| | | return this.errorMessage || this.errMsg; |
| | | }, |
| | | fieldStyle() { |
| | | let style = {} |
| | | if (this.labelPos == 'top') { |
| | | style.padding = '0 0' |
| | | this.labelMarginBottom = '6px' |
| | | } |
| | | if (this.labelPos == 'left' && this.msg !== false && this.msg != '') { |
| | | style.paddingBottom = '0px' |
| | | this.errorBottom = true |
| | | this.errorTop = false |
| | | } else if (this.labelPos == 'top' && this.msg !== false && this.msg != '') { |
| | | this.errorBottom = false |
| | | this.errorTop = true |
| | | } else { |
| | | // style.paddingBottom = '' |
| | | this.errorTop = false |
| | | this.errorBottom = false |
| | | } |
| | | return style |
| | | }, |
| | | |
| | | // uni不支持在computed中写style.justifyContent = 'center'的形式,故用此方法 |
| | | justifyContent() { |
| | | if (this.labelAli === 'left') return 'flex-start'; |
| | | if (this.labelAli === 'center') return 'center'; |
| | | if (this.labelAli === 'right') return 'flex-end'; |
| | | } |
| | | |
| | | }, |
| | | watch: { |
| | | validateTrigger(trigger) { |
| | | this.formTrigger = trigger |
| | | } |
| | | }, |
| | | created() { |
| | | this.form = this.getForm() |
| | | this.group = this.getForm('uniGroup') |
| | | this.formRules = [] |
| | | this.formTrigger = this.validateTrigger |
| | | // if (this.form) { |
| | | this.form.childrens.push(this) |
| | | // } |
| | | this.init() |
| | | }, |
| | | destroyed() { |
| | | if (this.form) { |
| | | this.form.childrens.forEach((item, index) => { |
| | | if (item === this) { |
| | | this.form.childrens.splice(index, 1) |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | methods: { |
| | | init() { |
| | | if (this.form) { |
| | | let { |
| | | formRules, |
| | | validator, |
| | | formData, |
| | | value, |
| | | labelPosition, |
| | | labelWidth, |
| | | labelAlign, |
| | | errShowType |
| | | } = this.form |
| | | |
| | | this.labelPos = this.labelPosition ? this.labelPosition : labelPosition |
| | | this.labelWid = this.label ? (this.labelWidth ? this.labelWidth : labelWidth):0 |
| | | this.labelAli = this.labelAlign ? this.labelAlign : labelAlign |
| | | console.log(this.labelWid); |
| | | // 判断第一个 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 |
| | | this.showMsg = errShowType |
| | | |
| | | if (formRules) { |
| | | this.formRules = formRules[this.name] || {} |
| | | } |
| | | |
| | | this.validator = validator |
| | | |
| | | if (this.name) { |
| | | formData[this.name] = value.hasOwnProperty(this.name) ? value[this.name] : this.form._getValue(this, '') |
| | | } |
| | | } else { |
| | | this.labelPos = this.labelPosition || 'left' |
| | | this.labelWid = this.labelWidth || 65 |
| | | this.labelAli = this.labelAlign || 'left' |
| | | } |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | 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; |
| | | }, |
| | | /** |
| | | * 移除该表单项的校验结果 |
| | | */ |
| | | clearValidate() { |
| | | this.errMsg = '' |
| | | }, |
| | | setValue(value){ |
| | | if (this.name) { |
| | | if(this.errMsg) this.errMsg = '' |
| | | this.form.formData[this.name] = this.form._getValue(this, value) |
| | | } |
| | | }, |
| | | /** |
| | | * 校验规则 |
| | | * @param {Object} value |
| | | */ |
| | | async triggerCheck(value, callback) { |
| | | let promise = null; |
| | | this.errMsg = '' |
| | | // if no callback, return promise |
| | | if (callback && typeof callback !== 'function' && Promise) { |
| | | promise = new Promise((resolve, reject) => { |
| | | callback = function(valid) { |
| | | !valid ? resolve(valid) : reject(valid) |
| | | }; |
| | | }); |
| | | } |
| | | |
| | | if (!this.validator) { |
| | | typeof callback === 'function' && callback(null); |
| | | if (promise) return promise |
| | | } |
| | | |
| | | const isNoField = this.isRequired(this.formRules.rules || []) |
| | | |
| | | |
| | | let isTrigger = this.isTrigger(this.formRules.validateTrigger, this.validateTrigger, this.form.validateTrigger) |
| | | |
| | | let result = null |
| | | |
| | | if (!(!isTrigger)) { |
| | | result = this.validator && (await this.validator.validateUpdate({ |
| | | [this.name]: value |
| | | }, this.form.formData)) |
| | | } |
| | | // 判断是否必填 |
| | | if (!isNoField && !value) { |
| | | result = null |
| | | } |
| | | |
| | | if (isTrigger && result && result.errorMessage) { |
| | | if (this.form.errShowType === 'toast') { |
| | | uni.showToast({ |
| | | title: result.errorMessage || '校验错误', |
| | | icon: 'none' |
| | | }) |
| | | } |
| | | if (this.form.errShowType === 'modal') { |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: result.errorMessage || '校验错误' |
| | | }) |
| | | } |
| | | } |
| | | |
| | | this.errMsg = !result ? '' : result.errorMessage |
| | | this.form.validateCheck(result ? result : null) |
| | | typeof callback === 'function' && callback(result ? result : null); |
| | | if (promise) return promise |
| | | |
| | | }, |
| | | /** |
| | | * 触发时机 |
| | | * @param {Object} event |
| | | */ |
| | | isTrigger(rule, itemRlue, parentRule) { |
| | | let rl = true; |
| | | // bind submit |
| | | if (rule === 'submit' || !rule) { |
| | | if (rule === undefined) { |
| | | if (itemRlue !== 'bind') { |
| | | if (!itemRlue) { |
| | | return parentRule === 'bind' ? true : false |
| | | } |
| | | return false |
| | | } |
| | | return true |
| | | } |
| | | return false |
| | | } |
| | | return true; |
| | | }, |
| | | // 是否有必填字段 |
| | | isRequired(rules) { |
| | | let isNoField = false |
| | | for (let i = 0; i < rules.length; i++) { |
| | | const ruleData = rules[i] |
| | | if (ruleData.required) { |
| | | isNoField = true |
| | | break |
| | | } |
| | | } |
| | | return isNoField |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-forms-item { |
| | | position: relative; |
| | | // padding: 16px 14px; |
| | | text-align: left; |
| | | color: #333; |
| | | font-size: 14px; |
| | | margin-bottom: 22px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .uni-forms-item__inner { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | // flex-direction: row; |
| | | // align-items: center; |
| | | } |
| | | |
| | | .is-direction-left { |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .is-direction-top { |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-forms-item__label { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | flex-shrink: 0; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | font-size: 14px; |
| | | color: #333; |
| | | width: 65px; |
| | | // line-height: 2; |
| | | // margin-top: 3px; |
| | | padding: 5px 0; |
| | | box-sizing: border-box; |
| | | height: 36px; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .uni-forms-item__content { |
| | | /* #ifndef APP-NVUE */ |
| | | width: 100%; |
| | | // display: flex; |
| | | /* #endif */ |
| | | // flex: 1; |
| | | // flex-direction: row; |
| | | // align-items: center; |
| | | box-sizing: border-box; |
| | | min-height: 36px; |
| | | } |
| | | |
| | | |
| | | .label-icon { |
| | | margin-right: 5px; |
| | | margin-top: -1px; |
| | | } |
| | | |
| | | // 必填 |
| | | .is-required { |
| | | color: $uni-color-error; |
| | | } |
| | | |
| | | .uni-error-message { |
| | | position: absolute; |
| | | bottom: -17px; |
| | | left: 0; |
| | | line-height: 12px; |
| | | color: $uni-color-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-color-error; |
| | | } |
| | | |
| | | .uni-forms-item--border { |
| | | margin-bottom: 0; |
| | | padding: 10px 15px; |
| | | // padding-bottom: 0; |
| | | border-top: 1px #eee solid; |
| | | } |
| | | |
| | | .uni-forms-item-error { |
| | | padding-bottom: 0; |
| | | } |
| | | |
| | | .is-first-border { |
| | | border: none; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <!-- --> |
| | | <view class="uni-forms" :class="{'uni-forms--top':!border}"> |
| | | <form @submit.stop="submitForm" @reset="resetForm"> |
| | | <slot></slot> |
| | | </form> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Forms 表单 |
| | | * @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=2773 |
| | | * @property {Object} rules 表单校验规则 |
| | | * @property {String} validateTrigger = [bind|submit] 校验触发器方式 默认 submit 可选 |
| | | * @value bind 发生变化时触发 |
| | | * @value submit 提交时触发 |
| | | * @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 提交时触发 |
| | | */ |
| | | 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) |
| | | } |
| | | } |
| | | |
| | | import Validator from './validate.js' |
| | | |
| | | export default { |
| | | name: 'uniForms', |
| | | props: { |
| | | value: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | // 表单校验规则 |
| | | rules: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | // 校验触发器方式,默认 关闭 |
| | | validateTrigger: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // label 位置,可选值 top/left |
| | | labelPosition: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | // label 宽度,单位 px |
| | | labelWidth: { |
| | | type: [String, Number], |
| | | default: 65 |
| | | }, |
| | | // label 居中方式,可选值 left/center/right |
| | | labelAlign: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | errShowType: { |
| | | type: String, |
| | | default: 'undertext' |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | formData: {} |
| | | }; |
| | | }, |
| | | watch: { |
| | | rules(newVal) { |
| | | this.init(newVal) |
| | | }, |
| | | trigger(trigger) { |
| | | this.formTrigger = trigger |
| | | }, |
| | | value: { |
| | | handler(newVal) { |
| | | if (this.isChildEdit) { |
| | | this.isChildEdit = false |
| | | return |
| | | } |
| | | this.childrens.forEach((item) => { |
| | | if (item.name) { |
| | | const formDataValue = newVal.hasOwnProperty(item.name) ? newVal[item.name] : null |
| | | this.formData[item.name] = this._getValue(item, formDataValue) |
| | | } |
| | | }) |
| | | }, |
| | | deep: true |
| | | } |
| | | }, |
| | | created() { |
| | | let _this = this |
| | | this.childrens = [] |
| | | this.inputChildrens = [] |
| | | this.checkboxChildrens = [] |
| | | this.formRules = [] |
| | | this.init(this.rules) |
| | | }, |
| | | methods: { |
| | | init(formRules) { |
| | | if (Object.keys(formRules).length > 0) { |
| | | this.formTrigger = this.trigger |
| | | this.formRules = formRules |
| | | if (!this.validator) { |
| | | this.validator = new Validator(formRules) |
| | | } |
| | | } |
| | | this.childrens.forEach((item) => { |
| | | item.init() |
| | | }) |
| | | }, |
| | | /** |
| | | * 设置校验规则 |
| | | * @param {Object} formRules |
| | | */ |
| | | setRules(formRules) { |
| | | this.init(formRules) |
| | | }, |
| | | /** |
| | | * 公开给用户使用 |
| | | * 设置自定义表单组件 value 值 |
| | | * @param {String} name 字段名称 |
| | | * @param {String} value 字段值 |
| | | */ |
| | | setValue(name, value, callback) { |
| | | let example = this.childrens.find(child => child.name === name) |
| | | if (!example) return null |
| | | this.isChildEdit = true |
| | | value = this._getValue(example, value) |
| | | this.formData[name] = value |
| | | example.val = value |
| | | this.$emit('input', Object.assign({}, this.value, this.formData)) |
| | | return example.triggerCheck(value, callback) |
| | | }, |
| | | |
| | | /** |
| | | * TODO 表单提交, 小程序暂不支持这种用法 |
| | | * @param {Object} event |
| | | */ |
| | | submitForm(event) { |
| | | const value = event.detail.value |
| | | return this.validateAll(value || this.formData, 'submit') |
| | | }, |
| | | /** |
| | | * 表单重置 |
| | | * @param {Object} event |
| | | */ |
| | | resetForm(event) { |
| | | this.childrens.forEach(item => { |
| | | item.errMsg = '' |
| | | const inputComp = this.inputChildrens.find(child => child.rename === item.name) |
| | | if (inputComp) { |
| | | inputComp.errMsg = '' |
| | | inputComp.$emit('input', inputComp.multiple?[]:'') |
| | | } |
| | | }) |
| | | |
| | | this.isChildEdit = true |
| | | this.childrens.forEach((item) => { |
| | | if (item.name) { |
| | | this.formData[item.name] = this._getValue(item, '') |
| | | } |
| | | }) |
| | | |
| | | this.$emit('input', this.formData) |
| | | this.$emit('reset', event) |
| | | }, |
| | | |
| | | /** |
| | | * 触发表单校验,通过 @validate 获取 |
| | | * @param {Object} validate |
| | | */ |
| | | validateCheck(validate) { |
| | | if (validate === null) validate = null |
| | | this.$emit('validate', validate) |
| | | }, |
| | | /** |
| | | * 校验所有或者部分表单 |
| | | */ |
| | | async validateAll(invalidFields, type, callback) { |
| | | |
| | | this.childrens.forEach(item => { |
| | | item.errMsg = '' |
| | | }) |
| | | |
| | | let promise; |
| | | if (!callback && typeof callback !== 'function' && Promise) { |
| | | promise = new Promise((resolve, reject) => { |
| | | callback = function(valid, invalidFields) { |
| | | !valid ? resolve(invalidFields) : reject(valid); |
| | | }; |
| | | }); |
| | | } |
| | | |
| | | let fieldsValue = {} |
| | | let tempInvalidFields = Object.assign({}, invalidFields) |
| | | |
| | | Object.keys(this.formRules).forEach(item => { |
| | | const values = this.formRules[item] |
| | | const rules = (values && values.rules) || [] |
| | | let isNoField = false |
| | | for (let i = 0; i < rules.length; i++) { |
| | | const rule = rules[i] |
| | | if (rule.required) { |
| | | isNoField = true |
| | | break |
| | | } |
| | | } |
| | | |
| | | // 如果存在 required 才会将内容插入校验对象 |
| | | if (!isNoField && (!tempInvalidFields[item] && tempInvalidFields[item] !== false)) { |
| | | delete tempInvalidFields[item] |
| | | } |
| | | |
| | | }) |
| | | // 循环字段是否存在于校验规则中 |
| | | for (let i in this.formRules) { |
| | | for (let j in tempInvalidFields) { |
| | | if (i === j) { |
| | | fieldsValue[i] = tempInvalidFields[i] |
| | | } |
| | | } |
| | | } |
| | | let result = [] |
| | | let example = null |
| | | if (this.validator) { |
| | | for (let i in fieldsValue) { |
| | | const resultData = await this.validator.validateUpdate({ |
| | | [i]: fieldsValue[i] |
| | | }, this.formData) |
| | | if (resultData) { |
| | | example = this.childrens.find(child => child.name === resultData.key) |
| | | const inputComp = this.inputChildrens.find(child => child.rename === example.name) |
| | | if (inputComp) { |
| | | inputComp.errMsg = resultData.errorMessage |
| | | } |
| | | result.push(resultData) |
| | | if (this.errShowType === 'undertext') { |
| | | if (example) example.errMsg = resultData.errorMessage |
| | | } else { |
| | | if (this.errShowType === 'toast') { |
| | | uni.showToast({ |
| | | title: resultData.errorMessage || '校验错误', |
| | | icon: 'none' |
| | | }) |
| | | break |
| | | } else if (this.errShowType === 'modal') { |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: resultData.errorMessage || '校验错误' |
| | | }) |
| | | break |
| | | } else { |
| | | if (example) example.errMsg = resultData.errorMessage |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (Array.isArray(result)) { |
| | | if (result.length === 0) result = null |
| | | } |
| | | if (type === 'submit') { |
| | | this.$emit('submit', { |
| | | detail: { |
| | | value: invalidFields, |
| | | errors: result |
| | | } |
| | | }) |
| | | } else { |
| | | this.$emit('validate', result) |
| | | } |
| | | callback && typeof callback === 'function' && callback(result, invalidFields) |
| | | if (promise && callback) { |
| | | return promise |
| | | } else { |
| | | return null |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 外部调用方法 |
| | | * 手动提交校验表单 |
| | | * 对整个表单进行校验的方法,参数为一个回调函数。 |
| | | */ |
| | | submit(callback) { |
| | | // Object.assign(this.formData,formData) |
| | | return this.validateAll(this.formData, 'submit', callback) |
| | | }, |
| | | |
| | | /** |
| | | * 外部调用方法 |
| | | * 校验表单 |
| | | * 对整个表单进行校验的方法,参数为一个回调函数。 |
| | | */ |
| | | validate(callback) { |
| | | return this.validateAll(this.formData, '', callback) |
| | | }, |
| | | |
| | | /** |
| | | * 部分表单校验 |
| | | * @param {Object} props |
| | | * @param {Object} cb |
| | | */ |
| | | validateField(props, callback) { |
| | | props = [].concat(props); |
| | | let invalidFields = {} |
| | | this.childrens.forEach(item => { |
| | | // item.parentVal((val, name) => { |
| | | if (props.indexOf(item.name) !== -1) { |
| | | invalidFields = Object.assign({}, invalidFields, { |
| | | [item.name]: this.formData[item.name] |
| | | }) |
| | | } |
| | | // }) |
| | | |
| | | }) |
| | | return this.validateAll(invalidFields, '', callback) |
| | | }, |
| | | |
| | | /** |
| | | * 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果 |
| | | */ |
| | | resetFields() { |
| | | this.resetForm() |
| | | }, |
| | | |
| | | /** |
| | | * 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果 |
| | | */ |
| | | clearValidate(props) { |
| | | props = [].concat(props); |
| | | this.childrens.forEach(item => { |
| | | const inputComp = this.inputChildrens.find(child => child.rename === item.name) |
| | | if (props.length === 0) { |
| | | item.errMsg = '' |
| | | if (inputComp) { |
| | | inputComp.errMsg = '' |
| | | } |
| | | } else { |
| | | if (props.indexOf(item.name) !== -1) { |
| | | item.errMsg = '' |
| | | if (inputComp) { |
| | | inputComp.errMsg = '' |
| | | } |
| | | } |
| | | } |
| | | }) |
| | | }, |
| | | // 把 value 转换成指定的类型 |
| | | _getValue(item, value) { |
| | | const rules = item.formRules.rules || [] |
| | | const isRuleNum = rules.find(val => val.format && this.type_filter(val.format)) |
| | | const isRuleBool = rules.find(val => val.format && val.format === 'boolean' || val.format === 'bool') |
| | | // 输入值为 number |
| | | if (isRuleNum) { |
| | | value = value === '' || value === null ? null : Number(value) |
| | | } |
| | | // 简单判断真假值 |
| | | if (isRuleBool) { |
| | | value = !value ? false : true |
| | | } |
| | | return value |
| | | }, |
| | | // 过滤数字类型 |
| | | type_filter(format) { |
| | | return format === 'int' || format === 'double' || format === 'number' |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-forms { |
| | | overflow: hidden; |
| | | // padding: 10px 15px; |
| | | // background-color: #fff; |
| | | } |
| | | |
| | | .uni-forms--top { |
| | | padding: 10px 15px; |
| | | // padding-top: 22px; |
| | | } |
| | | </style> |
New file |
| | |
| | | |
| | | var pattern = { |
| | | email: /^\S+?@\S+?\.\S+?$/, |
| | | url: new RegExp("^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$", 'i') |
| | | }; |
| | | |
| | | const FORMAT_MAPPING = { |
| | | "int": 'number', |
| | | "bool": 'boolean', |
| | | "double": 'number', |
| | | "long": 'number', |
| | | "password": 'string' |
| | | } |
| | | |
| | | function formatMessage(args, resources) { |
| | | var defaultMessage = ['label'] |
| | | defaultMessage.forEach((item) => { |
| | | if (args[item] === undefined) { |
| | | args[item] = '' |
| | | } |
| | | }) |
| | | |
| | | let str = resources |
| | | for (let key in args) { |
| | | let reg = new RegExp('{' + key + '}') |
| | | str = str.replace(reg, args[key]) |
| | | } |
| | | return str |
| | | } |
| | | |
| | | function isEmptyValue(value, type) { |
| | | if (value === undefined || value === null) { |
| | | return true; |
| | | } |
| | | |
| | | if (typeof value === 'string' && !value) { |
| | | return true; |
| | | } |
| | | |
| | | if (Array.isArray(value) && !value.length) { |
| | | return true; |
| | | } |
| | | |
| | | if (type === 'object' && !Object.keys(value).length) { |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | const types = { |
| | | integer(value) { |
| | | return types.number(value) && parseInt(value, 10) === value; |
| | | }, |
| | | string(value) { |
| | | return typeof value === 'string'; |
| | | }, |
| | | number(value) { |
| | | if (isNaN(value)) { |
| | | return false; |
| | | } |
| | | return typeof value === 'number'; |
| | | }, |
| | | "boolean": function (value) { |
| | | return typeof value === 'boolean'; |
| | | }, |
| | | "float": function (value) { |
| | | return types.number(value) && !types.integer(value); |
| | | }, |
| | | array(value) { |
| | | return Array.isArray(value); |
| | | }, |
| | | object(value) { |
| | | return typeof value === 'object' && !types.array(value); |
| | | }, |
| | | date(value) { |
| | | var v |
| | | if (value instanceof Date) { |
| | | v = value; |
| | | } else { |
| | | v = new Date(value); |
| | | } |
| | | return typeof v.getTime === 'function' && typeof v.getMonth === 'function' && typeof v.getYear === 'function' && !isNaN(v.getTime()); |
| | | }, |
| | | timestamp(value) { |
| | | if (!this.integer(value) || Math.abs(value).toString().length > 16) { |
| | | return false |
| | | } |
| | | |
| | | return this.date(value); |
| | | }, |
| | | email(value) { |
| | | return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255; |
| | | }, |
| | | url(value) { |
| | | return typeof value === 'string' && !!value.match(pattern.url); |
| | | }, |
| | | pattern(reg, value) { |
| | | try { |
| | | return new RegExp(reg).test(value); |
| | | } catch (e) { |
| | | return false; |
| | | } |
| | | }, |
| | | method(value) { |
| | | return typeof value === 'function'; |
| | | } |
| | | } |
| | | |
| | | class RuleValidator { |
| | | |
| | | constructor(message) { |
| | | this._message = message |
| | | } |
| | | |
| | | async validateRule(key, value, data, allData) { |
| | | var result = null |
| | | |
| | | let rules = key.rules |
| | | |
| | | let hasRequired = rules.findIndex((item) => { |
| | | return item.required |
| | | }) |
| | | if (hasRequired < 0) { |
| | | if (value === null || value === undefined) { |
| | | return result |
| | | } |
| | | if (typeof value === 'string' && !value.length) { |
| | | return result |
| | | } |
| | | } |
| | | |
| | | var message = this._message |
| | | |
| | | if (rules === undefined) { |
| | | return message['default'] |
| | | } |
| | | |
| | | for (var i = 0; i < rules.length; i++) { |
| | | let rule = rules[i] |
| | | let vt = this._getValidateType(rule) |
| | | |
| | | if (key.label !== undefined) { |
| | | Object.assign(rule, { |
| | | label: key.label |
| | | }) |
| | | } |
| | | |
| | | if (RuleValidatorHelper[vt]) { |
| | | result = RuleValidatorHelper[vt](rule, value, message) |
| | | if (result != null) { |
| | | break |
| | | } |
| | | } |
| | | |
| | | if (rule.validateExpr) { |
| | | let now = Date.now() |
| | | let resultExpr = rule.validateExpr(value, allData, now) |
| | | if (resultExpr === false) { |
| | | result = this._getMessage(rule, rule.errorMessage || this._message['default']) |
| | | break |
| | | } |
| | | } |
| | | |
| | | if (rule.validateFunction) { |
| | | result = await this.validateFunction(rule, value, data, allData, vt) |
| | | if (result !== null) { |
| | | break |
| | | } |
| | | } |
| | | } |
| | | |
| | | return result |
| | | } |
| | | |
| | | async validateFunction(rule, value, data, allData, vt) { |
| | | let result = null |
| | | try { |
| | | let callbackMessage = null |
| | | const res = await rule.validateFunction(rule, value, allData || data, (message) => { |
| | | callbackMessage = message |
| | | }) |
| | | if (callbackMessage || (typeof res === 'string' && res) || res === false) { |
| | | result = this._getMessage(rule, callbackMessage || res, vt) |
| | | } |
| | | } catch (e) { |
| | | result = this._getMessage(rule, e.message, vt) |
| | | } |
| | | return result |
| | | } |
| | | |
| | | _getMessage(rule, message, vt) { |
| | | return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default']) |
| | | } |
| | | |
| | | _getValidateType(rule) { |
| | | // TODO |
| | | var result = '' |
| | | if (rule.required) { |
| | | result = 'required' |
| | | } else if (rule.format) { |
| | | result = 'format' |
| | | } else if (rule.range) { |
| | | result = 'range' |
| | | } else if (rule.maximum || rule.minimum) { |
| | | result = 'rangeNumber' |
| | | } else if (rule.maxLength || rule.minLength) { |
| | | result = 'rangeLength' |
| | | } else if (rule.pattern) { |
| | | result = 'pattern' |
| | | } |
| | | return result |
| | | } |
| | | } |
| | | |
| | | const RuleValidatorHelper = { |
| | | required(rule, value, message) { |
| | | if (rule.required && isEmptyValue(value, rule.format || typeof value)) { |
| | | return formatMessage(rule, rule.errorMessage || message.required); |
| | | } |
| | | |
| | | return null |
| | | }, |
| | | |
| | | range(rule, value, message) { |
| | | const { range, errorMessage } = rule; |
| | | |
| | | let list = new Array(range.length); |
| | | for (let i = 0; i < range.length; i++) { |
| | | const item = range[i]; |
| | | if (types.object(item) && item.value !== undefined) { |
| | | list[i] = item.value; |
| | | } else { |
| | | list[i] = item; |
| | | } |
| | | } |
| | | |
| | | let result = false |
| | | if (Array.isArray(value)) { |
| | | result = (new Set(value.concat(list)).size === list.length); |
| | | } else { |
| | | if (list.indexOf(value) > -1) { |
| | | result = true; |
| | | } |
| | | } |
| | | |
| | | if (!result) { |
| | | return formatMessage(rule, errorMessage || message['enum']); |
| | | } |
| | | |
| | | return null |
| | | }, |
| | | |
| | | rangeNumber(rule, value, message) { |
| | | if (!types.number(value)) { |
| | | return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); |
| | | } |
| | | |
| | | let { minimum, maximum, exclusiveMinimum, exclusiveMaximum } = rule; |
| | | let min = exclusiveMinimum ? value <= minimum : value < minimum; |
| | | let max = exclusiveMaximum ? value >= maximum : value > maximum; |
| | | |
| | | if (minimum !== undefined && min) { |
| | | return formatMessage(rule, rule.errorMessage || message['number'].min) |
| | | } else if (maximum !== undefined && max) { |
| | | return formatMessage(rule, rule.errorMessage || message['number'].max) |
| | | } else if (minimum !== undefined && maximum !== undefined && (min || max)) { |
| | | return formatMessage(rule, rule.errorMessage || message['number'].range) |
| | | } |
| | | |
| | | return null |
| | | }, |
| | | |
| | | rangeLength(rule, value, message) { |
| | | if (!types.string(value) && !types.array(value)) { |
| | | return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); |
| | | } |
| | | |
| | | let min = rule.minLength; |
| | | let max = rule.maxLength; |
| | | let val = value.length; |
| | | |
| | | if (min !== undefined && val < min) { |
| | | return formatMessage(rule, rule.errorMessage || message['length'].min) |
| | | } else if (max !== undefined && val > max) { |
| | | return formatMessage(rule, rule.errorMessage || message['length'].max) |
| | | } else if (min !== undefined && max !== undefined && (val < min || val > max)) { |
| | | return formatMessage(rule, rule.errorMessage || message['length'].range) |
| | | } |
| | | |
| | | return null |
| | | }, |
| | | |
| | | pattern(rule, value, message) { |
| | | if (!types['pattern'](rule.pattern, value)) { |
| | | return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); |
| | | } |
| | | |
| | | return null |
| | | }, |
| | | |
| | | format(rule, value, message) { |
| | | var customTypes = Object.keys(types); |
| | | var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : rule.format; |
| | | |
| | | if (customTypes.indexOf(format) > -1) { |
| | | if (!types[format](value)) { |
| | | return formatMessage(rule, rule.errorMessage || message.types[format]); |
| | | } |
| | | } |
| | | |
| | | return null |
| | | } |
| | | } |
| | | |
| | | class SchemaValidator extends RuleValidator { |
| | | |
| | | constructor(schema, options) { |
| | | super(SchemaValidator.message); |
| | | |
| | | this._schema = schema |
| | | this._options = options || null |
| | | } |
| | | |
| | | updateSchema(schema) { |
| | | this._schema = schema |
| | | } |
| | | |
| | | async validate(data, allData) { |
| | | let result = this._checkFieldInSchema(data) |
| | | if (!result) { |
| | | result = await this.invokeValidate(data, false, allData) |
| | | } |
| | | return result.length ? result[0] : null |
| | | } |
| | | |
| | | async validateAll(data, allData) { |
| | | let result = this._checkFieldInSchema(data) |
| | | if (!result) { |
| | | result = await this.invokeValidate(data, true, allData) |
| | | } |
| | | return result |
| | | } |
| | | |
| | | async validateUpdate(data, allData) { |
| | | let result = this._checkFieldInSchema(data) |
| | | if (!result) { |
| | | result = await this.invokeValidateUpdate(data, false, allData) |
| | | } |
| | | return result.length ? result[0] : null |
| | | } |
| | | |
| | | async invokeValidate(data, all, allData) { |
| | | let result = [] |
| | | let schema = this._schema |
| | | for (let key in schema) { |
| | | let value = schema[key] |
| | | let errorMessage = await this.validateRule(value, data[key], data, allData) |
| | | if (errorMessage != null) { |
| | | result.push({ |
| | | key, |
| | | errorMessage |
| | | }) |
| | | if (!all) break |
| | | } |
| | | } |
| | | return result |
| | | } |
| | | |
| | | async invokeValidateUpdate(data, all, allData) { |
| | | let result = [] |
| | | for (let key in data) { |
| | | let errorMessage = await this.validateRule(this._schema[key], data[key], data, allData) |
| | | if (errorMessage != null) { |
| | | result.push({ |
| | | key, |
| | | errorMessage |
| | | }) |
| | | if (!all) break |
| | | } |
| | | } |
| | | return result |
| | | } |
| | | |
| | | _checkFieldInSchema(data) { |
| | | var keys = Object.keys(data) |
| | | var keys2 = Object.keys(this._schema) |
| | | if (new Set(keys.concat(keys2)).size === keys2.length) { |
| | | return '' |
| | | } |
| | | return [{ |
| | | key: 'invalid', |
| | | errorMessage: SchemaValidator.message['defaultInvalid'] |
| | | }] |
| | | } |
| | | } |
| | | |
| | | function Message() { |
| | | return { |
| | | default: '验证错误', |
| | | defaultInvalid: '字段超出范围', |
| | | required: '{label}必填', |
| | | 'enum': '{label}超出范围', |
| | | whitespace: '{label}不能为空', |
| | | date: { |
| | | format: '{label}日期{value}格式无效', |
| | | parse: '{label}日期无法解析,{value}无效', |
| | | invalid: '{label}日期{value}无效' |
| | | }, |
| | | types: { |
| | | string: '{label}类型无效', |
| | | array: '{label}类型无效', |
| | | object: '{label}类型无效', |
| | | number: '{label}类型无效', |
| | | date: '{label}类型无效', |
| | | boolean: '{label}类型无效', |
| | | integer: '{label}类型无效', |
| | | float: '{label}类型无效', |
| | | regexp: '{label}无效', |
| | | email: '{label}类型无效', |
| | | url: '{label}类型无效' |
| | | }, |
| | | length: { |
| | | min: '{label}长度不能少于{minLength}', |
| | | max: '{label}长度不能超过{maxLength}', |
| | | range: '{label}必须介于{minLength}和{maxLength}之间' |
| | | }, |
| | | number: { |
| | | min: '{label}不能小于{minimum}', |
| | | max: '{label}不能大于{maximum}', |
| | | range: '{label}必须介于{minimum}and{maximum}之间' |
| | | }, |
| | | pattern: { |
| | | mismatch: '{label}格式不匹配' |
| | | } |
| | | }; |
| | | } |
| | | |
| | | |
| | | SchemaValidator.message = new Message(); |
| | | |
| | | export default SchemaValidator |
New file |
| | |
| | | <template> |
| | | <view class="uni-goods-nav"> |
| | | <!-- 底部占位 --> |
| | | <view class="uni-tab__seat" /> |
| | | <view class="uni-tab__cart-box flex"> |
| | | <view class="flex uni-tab__cart-sub-left"> |
| | | <view v-for="(item,index) in options" :key="index" class="flex uni-tab__cart-button-left uni-tab__shop-cart" @click="onClick(index,item)"> |
| | | <view class="uni-tab__icon"> |
| | | <uni-icons :type="item.icon" size="20" color="#646566"></uni-icons> |
| | | <!-- <image class="image" :src="item.icon" mode="widthFix" /> --> |
| | | </view> |
| | | <text class="uni-tab__text">{{ item.text }}</text> |
| | | <view class="flex uni-tab__dot-box"> |
| | | <text v-if="item.info" :class="{ 'uni-tab__dots': item.info > 9 }" class="uni-tab__dot " :style="{'backgroundColor':item.infoBackgroundColor?item.infoBackgroundColor:'#ff0000', |
| | | color:item.infoColor?item.infoColor:'#fff' |
| | | }">{{ item.info }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view :class="{'uni-tab__right':fill}" class="flex uni-tab__cart-sub-right "> |
| | | <view v-for="(item,index) in buttonGroup" :key="index" :style="{backgroundColor:item.backgroundColor,color:item.color}" |
| | | class="flex uni-tab__cart-button-right" @click="buttonClick(index,item)"><text :style="{color:item.color}" class="uni-tab__cart-button-right-text">{{ item.text }}</text></view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from '../uni-icons/uni-icons.vue' |
| | | /** |
| | | * GoodsNav 商品导航 |
| | | * @description 商品加入购物车、立即购买等 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=865 |
| | | * @property {Array} options 组件参数 |
| | | * @property {Array} buttonGroup 组件按钮组参数 |
| | | * @property {Boolean} fill = [true | false] 组件按钮组参数 |
| | | * @event {Function} click 左侧点击事件 |
| | | * @event {Function} buttonClick 右侧按钮组点击事件 |
| | | * @example <uni-goods-nav :fill="true" options="" buttonGroup="buttonGroup" @click="" @buttonClick="" /> |
| | | */ |
| | | export default { |
| | | name: 'UniGoodsNav', |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | props: { |
| | | options: { |
| | | type: Array, |
| | | default () { |
| | | return [{ |
| | | icon: 'shop', |
| | | text: '店铺', |
| | | }, { |
| | | icon: 'cart', |
| | | text: '购物车' |
| | | }] |
| | | } |
| | | }, |
| | | buttonGroup: { |
| | | type: Array, |
| | | default () { |
| | | return [{ |
| | | text: '加入购物车', |
| | | backgroundColor: '#ffa200', |
| | | color: '#fff' |
| | | }, |
| | | { |
| | | text: '立即购买', |
| | | backgroundColor: '#ff0000', |
| | | color: '#fff' |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | | fill: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick(index, item) { |
| | | this.$emit('click', { |
| | | index, |
| | | content: item, |
| | | |
| | | }) |
| | | }, |
| | | buttonClick(index, item) { |
| | | if (uni.report) { |
| | | uni.report(item.text, item.text) |
| | | } |
| | | this.$emit('buttonClick', { |
| | | index, |
| | | content: item |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .flex { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-goods-nav { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-tab__cart-box { |
| | | flex: 1; |
| | | height: 50px; |
| | | background-color: #fff; |
| | | z-index: 900; |
| | | } |
| | | |
| | | .uni-tab__cart-sub-left { |
| | | padding: 0 5px; |
| | | } |
| | | |
| | | .uni-tab__cart-sub-right { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-tab__right { |
| | | margin: 5px 0; |
| | | margin-right: 10px; |
| | | border-radius: 100px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-tab__cart-button-left { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | // flex: 1; |
| | | position: relative; |
| | | justify-content: center; |
| | | align-items: center; |
| | | flex-direction: column; |
| | | margin: 0 10px; |
| | | } |
| | | |
| | | .uni-tab__icon { |
| | | width: 18px; |
| | | height: 18px; |
| | | } |
| | | |
| | | .image { |
| | | width: 18px; |
| | | height: 18px; |
| | | } |
| | | |
| | | .uni-tab__text { |
| | | margin-top: 3px; |
| | | font-size: $uni-font-size-sm; |
| | | color: #646566; |
| | | } |
| | | |
| | | .uni-tab__cart-button-right { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | flex-direction: column; |
| | | /* #endif */ |
| | | flex: 1; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-tab__cart-button-right-text { |
| | | font-size: $uni-font-size-base; |
| | | color: #fff; |
| | | } |
| | | |
| | | .uni-tab__cart-button-right:active { |
| | | opacity: 0.7; |
| | | } |
| | | |
| | | .uni-tab__dot-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | flex-direction: column; |
| | | /* #endif */ |
| | | position: absolute; |
| | | right: -2px; |
| | | top: 2px; |
| | | justify-content: center; |
| | | align-items: center; |
| | | // width: 0; |
| | | // height: 0; |
| | | } |
| | | |
| | | .uni-tab__dot { |
| | | // width: 30rpx; |
| | | // height: 30rpx; |
| | | padding: 0 4px; |
| | | line-height: 15px; |
| | | color: #ffffff; |
| | | text-align: center; |
| | | font-size: 12px; |
| | | background-color: #ff0000; |
| | | border-radius: 15px; |
| | | } |
| | | |
| | | .uni-tab__dots { |
| | | padding: 0 4px; |
| | | // width: auto; |
| | | border-radius: 15px; |
| | | } |
| | | |
| | | .uni-tab__color-y { |
| | | background-color: #ffa200; |
| | | } |
| | | |
| | | .uni-tab__color-r { |
| | | background-color: #ff0000; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view v-if="width" :style="'width:'+width+';'+(square?'height:'+width:'')" class="uni-grid-item"> |
| | | <view :class="{ 'uni-grid-item--border': showBorder, 'uni-grid-item--border-top': showBorder && index < column, 'uni-highlight': highlight }" |
| | | :style="{'border-right-color': borderColor ,'border-bottom-color': borderColor ,'border-top-color': borderColor }" |
| | | class="uni-grid-item__box" @click="_onClick"> |
| | | <slot /> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * GridItem 宫格 |
| | | * @description 宫格组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=27 |
| | | * @property {Number} index 子组件的唯一标识 ,点击gird会返回当前的标识 |
| | | */ |
| | | export default { |
| | | name: 'UniGridItem', |
| | | inject: ['grid'], |
| | | props: { |
| | | index: { |
| | | type: Number, |
| | | default: 0 |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | column: 0, |
| | | showBorder: true, |
| | | square: true, |
| | | highlight: true, |
| | | left: 0, |
| | | top: 0, |
| | | openNum: 2, |
| | | width: 0, |
| | | borderColor: '#e5e5e5' |
| | | } |
| | | }, |
| | | created() { |
| | | this.column = this.grid.column |
| | | this.showBorder = this.grid.showBorder |
| | | this.square = this.grid.square |
| | | this.highlight = this.grid.highlight |
| | | this.top = this.hor === 0 ? this.grid.hor : this.hor |
| | | this.left = this.ver === 0 ? this.grid.ver : this.ver |
| | | this.borderColor = this.grid.borderColor |
| | | this.grid.children.push(this) |
| | | // this.grid.init() |
| | | this.width = this.grid.width |
| | | }, |
| | | beforeDestroy() { |
| | | this.grid.children.forEach((item, index) => { |
| | | if (item === this) { |
| | | this.grid.children.splice(index, 1) |
| | | } |
| | | }) |
| | | }, |
| | | methods: { |
| | | _onClick() { |
| | | this.grid.change({ |
| | | detail: { |
| | | index: this.index |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-grid-item { |
| | | /* #ifndef APP-NVUE */ |
| | | height: 100%; |
| | | display: flex; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-grid-item__box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | width: 100%; |
| | | /* #endif */ |
| | | position: relative; |
| | | flex: 1; |
| | | flex-direction: column; |
| | | // justify-content: center; |
| | | // align-items: center; |
| | | } |
| | | |
| | | .uni-grid-item--border { |
| | | position: relative; |
| | | /* #ifdef APP-NVUE */ |
| | | border-bottom-color: $uni-border-color; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 0.5px; |
| | | border-right-color: $uni-border-color; |
| | | border-right-style: solid; |
| | | border-right-width: 0.5px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 0; |
| | | border-bottom: 1px $uni-border-color solid; |
| | | border-right: 1px $uni-border-color solid; |
| | | /* #endif */ |
| | | } |
| | | .uni-grid-item--border-top { |
| | | position: relative; |
| | | /* #ifdef APP-NVUE */ |
| | | border-top-color: $uni-border-color; |
| | | border-top-style: solid; |
| | | border-top-width: 0.5px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | border-top: 1px $uni-border-color solid; |
| | | z-index: 0; |
| | | /* #endif */ |
| | | } |
| | | |
| | | |
| | | .uni-highlight:active { |
| | | background-color: $uni-bg-color-hover; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-grid-wrap"> |
| | | <view :id="elId" ref="uni-grid" class="uni-grid" :class="{ 'uni-grid--border': showBorder }" :style="{ 'border-left-color':borderColor}"> |
| | | <slot /> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | // #ifdef APP-NVUE |
| | | const dom = uni.requireNativePlugin('dom'); |
| | | // #endif |
| | | |
| | | /** |
| | | * Grid 宫格 |
| | | * @description 宫格组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=27 |
| | | * @property {Number} column 每列显示个数 |
| | | * @property {String} borderColor 边框颜色 |
| | | * @property {Boolean} showBorder 是否显示边框 |
| | | * @property {Boolean} square 是否方形显示 |
| | | * @property {Boolean} Boolean 点击背景是否高亮 |
| | | * @event {Function} change 点击 grid 触发,e={detail:{index:0}},index 为当前点击 gird 下标 |
| | | */ |
| | | export default { |
| | | name: 'UniGrid', |
| | | props: { |
| | | // 每列显示个数 |
| | | column: { |
| | | type: Number, |
| | | default: 3 |
| | | }, |
| | | // 是否显示边框 |
| | | showBorder: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 边框颜色 |
| | | borderColor: { |
| | | type: String, |
| | | default: '#e5e5e5' |
| | | }, |
| | | // 是否正方形显示,默认为 true |
| | | square: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | highlight: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | provide() { |
| | | return { |
| | | grid: this |
| | | } |
| | | }, |
| | | data() { |
| | | const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` |
| | | return { |
| | | elId, |
| | | width: 0 |
| | | } |
| | | }, |
| | | created() { |
| | | this.children = [] |
| | | }, |
| | | mounted() { |
| | | this.$nextTick(()=>{ |
| | | this.init() |
| | | }) |
| | | }, |
| | | methods: { |
| | | init() { |
| | | setTimeout(() => { |
| | | this._getSize((width) => { |
| | | this.children.forEach((item, index) => { |
| | | item.width = width |
| | | }) |
| | | }) |
| | | }, 50) |
| | | }, |
| | | change(e) { |
| | | this.$emit('change', e) |
| | | }, |
| | | _getSize(fn) { |
| | | // #ifndef APP-NVUE |
| | | uni.createSelectorQuery() |
| | | .in(this) |
| | | .select(`#${this.elId}`) |
| | | .boundingClientRect() |
| | | .exec(ret => { |
| | | this.width = parseInt((ret[0].width - 1) / this.column) + 'px' |
| | | fn(this.width) |
| | | }) |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | dom.getComponentRect(this.$refs['uni-grid'], (ret) => { |
| | | this.width = parseInt((ret.size.width - 1) / this.column) + 'px' |
| | | fn(this.width) |
| | | }) |
| | | // #endif |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-grid-wrap { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: column; |
| | | /* #ifdef H5 */ |
| | | width: 100%; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-grid { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | // flex: 1; |
| | | flex-direction: row; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .uni-grid--border { |
| | | position: relative; |
| | | /* #ifdef APP-NVUE */ |
| | | border-left-color: $uni-border-color; |
| | | border-left-style: solid; |
| | | border-left-width: 0.5px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 1; |
| | | border-left: 1px $uni-border-color solid; |
| | | /* #endif */ |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-group" :class="['uni-group--'+mode ,margin?'group-margin':'']" :style="{marginTop: `${top}px` }"> |
| | | <slot name="title"> |
| | | <view v-if="title" class="uni-group__title" :style="{'padding-left':border?'30px':'15px'}"> |
| | | <text class="uni-group__title-text">{{ title }}</text> |
| | | </view> |
| | | </slot> |
| | | <view class="uni-group__content" :class="{'group-conent-padding':border}"> |
| | | <slot /> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Group 分组 |
| | | * @description 表单字段分组 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=21002 |
| | | * @property {String} title 主标题 |
| | | * @property {Number} top 分组间隔 |
| | | */ |
| | | export default { |
| | | name: 'uniGroup', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | top: { |
| | | type: [Number, String], |
| | | default: 10 |
| | | }, |
| | | mode: { |
| | | type: String, |
| | | default: 'default' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | margin: false, |
| | | border: false |
| | | } |
| | | }, |
| | | watch: { |
| | | title(newVal) { |
| | | if (uni.report && newVal !== '') { |
| | | uni.report('title', newVal) |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.form = this.getForm() |
| | | if (this.form) { |
| | | this.margin = true |
| | | this.border = this.form.border |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getForm() { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== 'uniForms') { |
| | | parent = parent.$parent; |
| | | if (!parent) return false |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | }, |
| | | onClick() { |
| | | this.$emit('click') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .uni-group { |
| | | background: #fff; |
| | | margin-top: 10px; |
| | | // border: 1px red solid; |
| | | } |
| | | |
| | | .group-margin { |
| | | margin: 0 -15px; |
| | | } |
| | | |
| | | .uni-group__title { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | padding-left: 15px; |
| | | height: 40px; |
| | | background-color: $uni-bg-color-grey; |
| | | font-weight: normal; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-group__content { |
| | | padding: 15px; |
| | | // padding-bottom: 5px; |
| | | background-color: #FFF; |
| | | } |
| | | |
| | | .group-conent-padding { |
| | | padding: 0 15px; |
| | | } |
| | | |
| | | .uni-group__title-text { |
| | | font-size: $uni-font-size-base; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .distraction { |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-group--card { |
| | | margin: 10px; |
| | | border-radius: 5px; |
| | | overflow: hidden; |
| | | box-shadow: 0 0 5px 1px rgba($color: #000000, $alpha: 0.08); |
| | | } |
| | | </style> |
New file |
| | |
| | | export default { |
| | | "pulldown": "\ue588", |
| | | "refreshempty": "\ue461", |
| | | "back": "\ue471", |
| | | "forward": "\ue470", |
| | | "more": "\ue507", |
| | | "more-filled": "\ue537", |
| | | "scan": "\ue612", |
| | | "qq": "\ue264", |
| | | "weibo": "\ue260", |
| | | "weixin": "\ue261", |
| | | "pengyouquan": "\ue262", |
| | | "loop": "\ue565", |
| | | "refresh": "\ue407", |
| | | "refresh-filled": "\ue437", |
| | | "arrowthindown": "\ue585", |
| | | "arrowthinleft": "\ue586", |
| | | "arrowthinright": "\ue587", |
| | | "arrowthinup": "\ue584", |
| | | "undo-filled": "\ue7d6", |
| | | "undo": "\ue406", |
| | | "redo": "\ue405", |
| | | "redo-filled": "\ue7d9", |
| | | "bars": "\ue563", |
| | | "chatboxes": "\ue203", |
| | | "camera": "\ue301", |
| | | "chatboxes-filled": "\ue233", |
| | | "camera-filled": "\ue7ef", |
| | | "cart-filled": "\ue7f4", |
| | | "cart": "\ue7f5", |
| | | "checkbox-filled": "\ue442", |
| | | "checkbox": "\ue7fa", |
| | | "arrowleft": "\ue582", |
| | | "arrowdown": "\ue581", |
| | | "arrowright": "\ue583", |
| | | "smallcircle-filled": "\ue801", |
| | | "arrowup": "\ue580", |
| | | "circle": "\ue411", |
| | | "eye-filled": "\ue568", |
| | | "eye-slash-filled": "\ue822", |
| | | "eye-slash": "\ue823", |
| | | "eye": "\ue824", |
| | | "flag-filled": "\ue825", |
| | | "flag": "\ue508", |
| | | "gear-filled": "\ue532", |
| | | "reload": "\ue462", |
| | | "gear": "\ue502", |
| | | "hand-thumbsdown-filled": "\ue83b", |
| | | "hand-thumbsdown": "\ue83c", |
| | | "hand-thumbsup-filled": "\ue83d", |
| | | "heart-filled": "\ue83e", |
| | | "hand-thumbsup": "\ue83f", |
| | | "heart": "\ue840", |
| | | "home": "\ue500", |
| | | "info": "\ue504", |
| | | "home-filled": "\ue530", |
| | | "info-filled": "\ue534", |
| | | "circle-filled": "\ue441", |
| | | "chat-filled": "\ue847", |
| | | "chat": "\ue263", |
| | | "mail-open-filled": "\ue84d", |
| | | "email-filled": "\ue231", |
| | | "mail-open": "\ue84e", |
| | | "email": "\ue201", |
| | | "checkmarkempty": "\ue472", |
| | | "list": "\ue562", |
| | | "locked-filled": "\ue856", |
| | | "locked": "\ue506", |
| | | "map-filled": "\ue85c", |
| | | "map-pin": "\ue85e", |
| | | "map-pin-ellipse": "\ue864", |
| | | "map": "\ue364", |
| | | "minus-filled": "\ue440", |
| | | "mic-filled": "\ue332", |
| | | "minus": "\ue410", |
| | | "micoff": "\ue360", |
| | | "mic": "\ue302", |
| | | "clear": "\ue434", |
| | | "smallcircle": "\ue868", |
| | | "close": "\ue404", |
| | | "closeempty": "\ue460", |
| | | "paperclip": "\ue567", |
| | | "paperplane": "\ue503", |
| | | "paperplane-filled": "\ue86e", |
| | | "person-filled": "\ue131", |
| | | "contact-filled": "\ue130", |
| | | "person": "\ue101", |
| | | "contact": "\ue100", |
| | | "images-filled": "\ue87a", |
| | | "phone": "\ue200", |
| | | "images": "\ue87b", |
| | | "image": "\ue363", |
| | | "image-filled": "\ue877", |
| | | "location-filled": "\ue333", |
| | | "location": "\ue303", |
| | | "plus-filled": "\ue439", |
| | | "plus": "\ue409", |
| | | "plusempty": "\ue468", |
| | | "help-filled": "\ue535", |
| | | "help": "\ue505", |
| | | "navigate-filled": "\ue884", |
| | | "navigate": "\ue501", |
| | | "mic-slash-filled": "\ue892", |
| | | "search": "\ue466", |
| | | "settings": "\ue560", |
| | | "sound": "\ue590", |
| | | "sound-filled": "\ue8a1", |
| | | "spinner-cycle": "\ue465", |
| | | "download-filled": "\ue8a4", |
| | | "personadd-filled": "\ue132", |
| | | "videocam-filled": "\ue8af", |
| | | "personadd": "\ue102", |
| | | "upload": "\ue402", |
| | | "upload-filled": "\ue8b1", |
| | | "starhalf": "\ue463", |
| | | "star-filled": "\ue438", |
| | | "star": "\ue408", |
| | | "trash": "\ue401", |
| | | "phone-filled": "\ue230", |
| | | "compose": "\ue400", |
| | | "videocam": "\ue300", |
| | | "trash-filled": "\ue8dc", |
| | | "download": "\ue403", |
| | | "chatbubble-filled": "\ue232", |
| | | "chatbubble": "\ue202", |
| | | "cloud-download": "\ue8e4", |
| | | "cloud-upload-filled": "\ue8e5", |
| | | "cloud-upload": "\ue8e6", |
| | | "cloud-download-filled": "\ue8e9", |
| | | "headphones":"\ue8bf", |
| | | "shop":"\ue609" |
| | | } |
New file |
| | |
| | | <template> |
| | | <text :style="{ color: color, 'font-size': size + 'px' }" class="uni-icons" :class="[customIcons,customIcons?type:'']" @click="_onClick">{{icons[type]}}</text> |
| | | </template> |
| | | |
| | | <script> |
| | | import icons from './icons.js'; |
| | | // #ifdef APP-NVUE |
| | | var domModule = weex.requireModule('dom'); |
| | | domModule.addRule('fontFace', { |
| | | 'fontFamily': "uniicons", |
| | | 'src': "url('data:font/truetype;charset=utf-8;base64,AAEAAAANAIAAAwBQRkZUTYoJ48wAAGf4AAAAHEdERUYAJwCMAABn2AAAAB5PUy8yWXpc3QAAAVgAAABgY21hcB9SCa8AAAPQAAADImdhc3D//wADAABn0AAAAAhnbHlmWWfecQAACAQAAFYcaGVhZBehAMAAAADcAAAANmhoZWEH+gSHAAABFAAAACRobXR4D3IujAAAAbgAAAIYbG9jYa77miAAAAb0AAABDm1heHABnACoAAABOAAAACBuYW1lj4vbUwAAXiAAAAM5cG9zdH/g11YAAGFcAAAGcwABAAAAAQAAGbvTeF8PPPUACwQAAAAAANoxE3MAAAAA2jSpUAAA/5UEHANrAAAACAACAAAAAAAAAAEAAAOA/4AAXASAAAAAAAQcAAEAAAAAAAAAAAAAAAAAAACGAAEAAACGAJwADAAAAAAAAgAAAAoACgAAAP8AAAAAAAAAAwQBAZAABQAAAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAEAAAAAAAAAAAAAAAUGZFZABAAB3o6QOA/4AAXAOAAIAAAAABAAAAAAIAAs0AAAAgAAEEAAAAAAAAAAFVAAAEAABLBAAAiQQAACEEAABLBAAAlwQAACkEAABdBAAAJwQAACgEAAAABAAAcwQAACcEAAAoBAAAAAQAACAEgABVBAAAegQAACgEAACcBAAAkgQAAAgEAADNBAAAyQQAAN0EAADJBAAAeAQAAAYEAABCBAAAVgQAAGoEAACEBAAAhAQAAEsEAAAxBAAAMQQAAEsEAAAcBAAASwQAAEsEAABLBAAASwQAAEsEAAAcBAAASwQAAEsEAABLBAAASQQAAOMEAAEABAAASwQAABwEAAAdBAAAbQQAAJ8EAAFABAABQAQAALgEAAALBAAASwQAAFYEAAA/BAAASwQAAEsEAADRBAAAZAQAAIMEAAALBAAAVgQAAEsEAABLBAAAZAQAAFAEAABRBAAAkgQAAAQEAABqBAAAAAQAAIwEAACMBAABLwQAAS4EAAC7BAAAuwQAAHIEAAByBAABHgQAAA0EAAA5BAAAQAQAADEEAAAxBAAACAQAABEEAAASBAAASQQAAEsEAAAABAAAAAQAAAAEAACDBAAAVQQAADwEAABVBAAAVgQAADwEAABWBAAAKAQAACYEAAAmBAAA1gQAAEEEAAFfBAAAZwQAAEsEAAA/BAAABgQAAAAEAAAABAAASwQAAHgEAAAABAAAhAQAAJIEAACEBAAARQQAAIQEEgAcBBIAHAQSABwEEgAcAVUAAAAAAAMAAAADAAAAHAABAAAAAAIcAAMAAQAAABwABAIAAAAAfABAAAUAPAAAAB3hAuEy4gPiM+Jk4wPjM+Ng42TkCeQR5DTkOeRC5GPkZuRo5HLlCOUw5TLlNeU35WDlY+Vl5WjliOWQ5gnmEufW59nn7+f15/roAegl6EDoR+hO6FboXOhe6GToaOhu6Hfoe+iE6JLooeik6K/osei/6Nzo5ujp//8AAAAAAB3hAOEw4gDiMOJg4wDjMuNg42PkAOQQ5DTkN+RA5GDkZeRo5HDlAOUw5TLlNOU35WDlYuVl5WflgOWQ5gnmEufW59nn7+f05/roAegi6DvoR+hN6FboXOhe6GToaOhu6HfoeuiE6JLooeik6K/osei/6Nzo5Ojp//8AAf/kHwMe1h4JHd0dsR0WHOgcvBy6HB8cGRv3G/Ub7xvSG9Eb0BvJGzwbFRsUGxMbEhrqGuka6BrnGtAayRpRGkkYhhiEGG8YaxhnGGEYQRgsGCYYIRgaGBUYFBgPGAwYBxf/F/0X9RfoF9oX2BfOF80XwBekF50XmwABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQYAAAEAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEoAmgEgAWIBkAH4AnACwgMUA5YD3AQkBE4EoAU0Ba4GPgauBvQHVgfsCFAIigjgCRIJmgnkCkAKigsUC2oLvgwUDHQM1A1ADaYN+A42DmQOqA8CDzIPcA+aD9oQEhBAEGoQsBEAEfoSNhJmEnoSjhK6ExwTaBQuFIAU2hVIFYwV6BY+FpwXChdSF6wX4Bh4GN4ZHhmAGd4aGho8GmIahBqqGtwbDhtAG3IbhBwMHLgdOh1wHaYeEB5oHsgfFB8uH5QgAiBSIIog7iGgIgQiMCLiIzQjhCPUJDwkbCSmJNolNCViJZwl5iY+Jpgm0CdCJ64n+CgqKHIowik6KcQqJCquKw4rDgAAAAMAS//LA7UDNQALAB0AKQAABT4BNy4BJw4BBx4BEw4BBy4BJz4BNx4BFxQGBy4BJz4BNy4BJw4BBx4BAgC4+AUF+Li59wUF+LhijCIrMAEEzJybzQQxKyKMYj9TAQJSPz9TAQJSNQX4uLj4BQX4uLj4AR4BOScwfEebzQQEzZtHfDEoOUoBWkZDWgICWkNGWQAAAAAEAIn/8gN3Aw0ACwAXACIALQAAAT4BNy4BJw4BBx4BNy4BJz4BNx4BFw4BASEWJy4BJw4BBwY3Bjc0NjceARUWJwIAT2gCAmhPTmkCAmlOMEMBAUIxMkEBAUL+wgIaagEBxbCwxQEBVhEBnZSUnQEQAYACclVUbgICb1RVcT4CTDo5SgEBSTk6Tf4xAUZbsQYGsVtGQgENO4kGBok7DQEABQAh/6wD4ANUAAsAFwAsADgAVQAAAT4BNy4BJw4BBx4BNy4BJz4BNx4BFw4BByIGBxYXNjceARcWJyEGByEWJy4BAT4BNy4BJw4BBx4BNyImPQEjLgE0NjsBNTQ2MhYdATMyFhQGByMVFAYCaU5pAgJoT05pAgJpTjFCAQFCMTFCAQFCMTplKRsVP1mUnAEBEf6FAQoBcmoBAsT94lt8AgJ7XFx7AwN7XAsRUgsPDwtSERcQUgsPDwtSEAHHAnJVVW0CAm9TVXI/AUw7OUkBAUk5Ok13GRYWHB8BBok7DQEhIAFGW7H+IQJ8XFx7AgJ7XF17Sg4NWAEPFg9ZDA4ODFkPFg8BWA0OAAAAAAMAS//LA7UDNQALABcAJAAABT4BNy4BJw4BBx4BEx4BFw4BBy4BJz4BARcOASImJzc+ATceAQIAuPgFBfi4ufcFBfi4P1ICAVM/P1MBAVMBRAEziJaJMgEchmNjhTUF+Li4+AUF+Li4+AKnAlpDRloBAllGQ1r+DQU1Ojo1BSlBAgJBAAIAl///A2kDAQALABgAAAE+ATcuAScOAQceAQMhMjY1LgEnDgEHFBYCAEdjAgJjR0diAgJixgIaMioCv6iovwIqAYwBalJRZgEBZ1FRav5yHB1ZqAYGqFkdHAAABAAp/7ID2ANOAAsAGgAmAEMAAAE+ATcuAScOAQceARciBgceARUUByEyNicuAQE+ATcuAScOAQceATciJic1IyImNDY7ATU+ATIWFxUzMhYUBisBFQ4BAm9HYgICYkdHYgICYkc3XycvNggBbTIqAQG//etcewMCfFxcewICe10MEAFRDA8PDFEBEBcQAVEMDg4MUQEQAdkCaVJRZgEBZ1FRaU8YFCZuQSAfHRxZqP4sAnxbXHwCAnxcXHtKDgxZDxcPWQwODgxZDxcPWQwOAAIAXf/cA6QDJAAnAE4AAAUWNj8BNic2LwEmIg8BBicuAycmPwE+AS8BJiMmDwEOARUUHgI3Ii4CJzY3Njc+AR8BFhQPAQYUFx4DFxYyPwE2Mh8BFgYPAQYCzDdQIQknAQE5fR0/GyEODxI6MiwNCg4hGgEVVycuKSsMJCBu0NRdU8CrbwEBMgQFEycMUwcKJhYQEzc0QhoWMhYmChUKfRMBEgYuIwEhJQosKC8oVhQaIQ4KDDIyMxUODiEbPx19OAEnCSBQN13V0G5Ca6vEVEgtAwQQAhN9ChUKJhcxFho+NDoUEBYmCgZUDCcUCDEAAAUAJwAPA9kC8QANABcAHQAhAC4AADchMjY1ETQjISIGFREUCQE2MyEyFwEGIgURNRcHJgERJzcBIiclFxY3Fj8BBQYjrQKyOz+G/U46QAGm/rwOFAKqFA/+vRsy/oD39gEDMPX1/RMSDQEAHCwtLC0cAQAOEw9CQwHZhEJC/ieFAVsBQAYH/sEbuwHZBPLzBAHc/iLx8f3gBv0bKwEBKxv9BgAAAgAo/74D2AM5ABkAMAAAJTYXFjM+ATcuAScOAQcUFh8BMiMXHgEXNzYBNiQ3FgQXBgQHIicxJgYHBj4BLwEuAQFlKSsjJK/kBATkr6/kBEQ/EgECBxkZARoM/ssFAQrJyQEKBQX+9skrKCtZaC1EIx0XSlVkDgkFBLyJibwEBLyJRnwvDQQTLxsNBgFMqOAEBOCoqd8FBghFHwxHVhkQN5kAAAADAAD/tQQAAuUAJwBAAFkAABcyNj8BFhczFx4BMz4BPQEzPgE3NS4BJyM1LgEnIQ4BBxEeARczFRQ3LgErASImJxE+ATMhMhYXFSMOAQcVFBcHBScuASsBIiYnNT4BMyEyFhcVDgErASIGB/ENGRB4J0t7dxAWDRIUD0RQAQFQRDgBUEn+AkdTAQFTRy82AQ8MRjA0AQE0MAH3MDQB6UdNAQeDAiZyChIOdi0xAQExLQFULTEBATEtJgwPASMMD2srAWYNDwEXFVUBTEfSR0wBG0lPAQFPSf63SU8BYSqjEA4zMgFFMjMzMhkBTEfSHRh5J2cJBzAv0C8wMC/QLzAODwAAAAEAc//xA40DDwAsAAAlHgEXFjc+ATU0Ji8BJiMGDwEGIicuAycmND8BNjc0LwEmByIGBw4BFR4BAUxf0V5TOxITDQ+EHRccHB8HFAcUPUEzCwUGHh4BFVwYJBUqEx8dAnjNXnsCAT8TKxYQHgtdFQEeHgYEDDNBPRQIEgcgHBwXHoEfARMSHkkpXs8ABAAnAA8D2QLxAAoAEQAYACQAAAEWNwEmIyEiBwEWBQkBBhURFAU2NRE0JwkBITI3AQcGIi8BARYCARobAXQYP/1ONxUBdxv+SwEv/tAKA6gKCf7R/gwCsjYV/swdKlwqHf7MGAFPARwBcRYV/o4c+gErASwSLP4nLhITLQHZKxL+1f6QFAEyHCoqHP7PFQAAAQAo/74D2AM5ABYAABM2JDcWBBcGBAciJzEmBgcGPgEvAS4BKAUBCsnJAQoFBf72ySsoK1loLUQjHRdKVQGtqOAEBOCoqd8FBghFHwxHVhkQN5kAAgAA/7IEAALtABwANQAAFzI2PwEuASc1PgE7AScuASchDgEHER4BFzMVFBYFPgE9ATM+ATc1LgEnIQ4BHQEUFhczFx4B5gsSDWoQFQEBXlT+AQRHPv4PP0oBAUo/PBECWQ8RJj9KAQFKP/6aQkhIQnODDRIbCwxiCy4s8FRdDjhCAQFGQf6cQksBaREUMwEUEGoBS0HdQUYBAUZB3UFLAXcMDAAHACAAGgP6AzYACwAgACwAOABEAE0AVgAAATYmBwYmNzYWBwYmAS4BJzQ2Nz4BBwY2NzYWBwYWFxYCAy4BBw4BFx4BNz4BAwYWNzYWBwYWNzYmAQ4BJy4BNz4BFx4BIyYOAR4BPgEmNyYOAR4BPgEmAxsJLCMgCx5JWRMNNf6enPEGS0SU0yEEGANzhCIECQu3zhoLrXp6mAULrXp6mBkjDiVqgxwGPQ8nuP7XGnU9OioYG2w7PDG5EygXCSUqGAsqBw8IAw8QCAQCEyMxBgM2CwxlRh0R/iYBh3g/ikSNBIYRBQEvMF8NCQNN/ssBAVBaCg56UVBbCg96AkIMPwMRkGkkFCGTzP2hODESFV80MzAOEV0IDiUkEQ4mJCEDBg4NBwYPDQAAAAYAVf/2BBwDCgAWAB8AKAA5AEMATQAAATIXLgEnDgEHFBYXBzceATMyNyY1PgEnMhYUBiImNDYHIiY0NjIWFAYBLgEnDgEHHgEXMjY3Fyc+ASUiJjQ2Nx4BFAYzIiY0NjceARQGAuMREBm/gZHBBEhBIncgOR8QEAoCozsUFxcoHh7bFB8fJxcXAs0EqHd9owMDo30ZNBpeGjRD/oAOFBQOExcXqQ4UFQ0TFxcCGwJqhQIDo31GdC1nPAcKASImc5hgFycXFycXVRcnFxcnF/7uaYwDA4xpaowDCwc0VidkZRUaFAEBFBsUFRoUAQEUGxQAAAAJAHr/+gOGAwYABwAQABgAIAAoAEAASABQAFgAACUOAR8BPgE3JRUWFzI3JyYGEyIHFxY3NSYFBgcUFzc2JzcOAQchMjYnBxUUHwEWOwEyPwE2PQE0LwEmKwEiDwEGJQcGFzM2NzQDERQWPwEuAQEeARcRLgEHAfUCAgKQPGMj/hRNWigl7QIFpycl7gUBTf5FJQEI7gMGETxjIwFRAgICtgJeAgOFAwJeAgJeAgOFAwJeAgIm7QQGzCUBrQUCkBZN/YQVTjUBBAKoAQQCkBVNNUXNJQEI7QICAhcH7gMFzSXdTVsoJe4FAsgWTTUFAp2EAwJeAgJeAgOEBAJdAwNdAwftBQJNWycBHf6wAgICkDxj/lw8YyMBUQICAgAAAAAFACj/xQPYAzsAGAAxADoAQwBMAAAFMjY/ASE+ATURNCYjISIGFREUFhczFRQWNzU0JisBIiY1ETQ2MyEyFhURFAYjISIGBwMuASIGFBYyNjcuASIGFBYyNjc0JiIGFBYyNgEvEBsTlAETYGRkYP3YYGRkYBUYKA8RNUE+PkECKEE+PkH+6hEXDFEBIDAgIDAgxAEgMCAgMCDEITAgIDAhOxERgwFlXwFIX2VlX/64X2UBbxkdTnwSD0A/AUg/QEA//rg/QAgNAScYICAwISEYGCAgMCEhGBggIDAhIQAAAAEAnP/ZA2QDJgApAAAlLgEnFAYHHgEHBiYnDgEnJjY3LgE1DgEHIiY3Nj8BJjY3HgEHFxYXFgYDWhE2AykrGDsIE8A0NMATCDsYKykDNhEIAhoMECYFgI2MgAQmEAwaAnEETQYoWiYHHhQOAgYGAg4UHgcmWigGTQROVigoX5TKBATIll8oKFZOAAAABACSAKUDbgJbAA8AHwAtAD8AABMVHgEzITI2PQE0JiMhIgYnITIWFREUBiMhIiYnET4BBRUUHwEWNjc1LgEPAQYnNz4BHgEVERQOASYvASY9ATTbARQQASUPFRUP/tsQFAEBbh4rKx7+kh4qAQEqAjEHJAkUAQEUCSQHKW0JFBQLCxQUCW0OAe7cDxUVD9wPFRVeKx7+3B4rKx4BJB4rtUwJBR4GCguGCwoGHgUlWAYDCRIL/uILEgkCB1gLEXARAAAAAAUACP/nA/gDGQAbADsARwBVAGQAABchNjcRJisBIiYvAS4BKwEiBg8BDgErASIHERY3IiY1ETQ2OwEyNj8BPgE7ATIWHwEeATsBMhYVERQGIyU+ATcuAScOAQceAQEyNjc0LgEiDgEVFBYXAS4BJz4BNzIeAhQOAo8C4oYBAYZkGBoNIw8nIasgKA8jDRoYYYYBAYcgIyMgcR0kECIRHhx/HB4RIhAkHXQgIyMg/pBkgwMDg2RkgwMDgwGYFh4BDhkcGQ4eF/7MSF8CAl9IIj0wGhowPRkBhAHBhA0QJhITExImEA2E/j+ERCIiAbkiIQ4SJRQPDxQlEg4hIv5HIiJEA4RkZIQCAoRkZIQBUR4WDxgODhgPFh4B/u8BYEhIXwIZMD5EPjAZAAAAAAMAzf+1AzMDSwANABkAQgAAAREuAScOAQcRHgEXPgEnFAYiJjURNDYyFhcBIgYUFjMhMjY0JisBNT4BNzU0JiIGHQEOAQcuASc1NCYiBgcVHgEXFQKcAVVGRlUBAVVGRlVAMVMyMlMxAf7lDhISDgF/DRMTDaB9lAITGxMBgXBvggETGhMBApR9AZUBDktbAgJbS/7yS1wBAVxLMDg4MAEOMDc3MP1TExsTExsTZAyggFcNExMNVW+CAgKCb1UNExMNV4CgDGQAAgDJ/8QDNwM3ABAAHwAAAS4BJw4BBx4BHwEWMj8BPgElPgE3HgEXBgIHBiInJgIC7gKCamqCAgJsWQoLJAsKWWz93QOwhISwAwm4SRQzE0m4AduBkQEBkYFL0nUODQ0OddNKprUBAbWmg/7YVhYWVQEpAAACAN3/xAMjAzwADQA2AAABES4BJw4BBxEeARc+AQEOARQWMyEyNjQmJyM1PgE3NTQmIgYHFQ4BBy4BJzUuASIGHQEeARcVAoIBRzo6RwEBRzo6R/63DRMTDQGQDRMTDah3iwESGhIBAX1mZn0BARIaEgGLdgGAATI9TAEBTD3+zjxNAQFN/sEBExoTExoTAV4MmndlDRISDWVkfAICfGRlDRISDWV3mgxeAAAAAgDJ/8QDNwM3AA4AGgAAEz4BNx4BFwYCBwYiJyYCJT4BNy4BJw4BBx4ByQOwhISwAwm4SRQzE0m4AS4vPgEBPi8vPgEBPgHbprUBAbWmg/7YVhYWVQEpOgE+Ly8+AQE+Ly8+AAUAeP/AA4cDQAARAB0APgBKAFkAAAEeAR0BFxEuAScOAQ8BFzU+AQEWMjY0JwEmIgYUFxMiBhQWMyEyNjQmKwE1NjcnBgcuASc1NCYiBh0BHgEXFQE0JiIGBxUUBxc2NQUyNjcnBiMiJic1JxUeAQHoJSxCAk9COkwJAT8BLAGLChsTCv00ChsUCp4NExMNAZANExMNp1I7LjVLZnwCEhsSAop3AUMSGhIBBTUP/tUZJA41Bg8iJgFCAU0DAgEzJ85CAQ5EVgEBQzYNPiwnM/0dChMbCgLNChQbCv0WExoTExoTXggqLiQBAn1kZQ0SEg1ld5sMXgHhDRISDWUZGTMuN5MJCTQHKSIaQ1RKSQAAAwAG//UD+gMLAAwAHwArAAAXITI3ESYnISIHERYzAS4BDwEnJiciDwERNjMhMhYVESU+ATcuAScOAQceAY0C5oYBAYb9GoYBAYYCQR1HHcFQGx4dGoABQQLkICL9kio5AQE5Kis5AQE5CoQCDIQBhf30hQGMGgEbrUgYARhzAdhDISL+J9MBOiorOQICOSsqOQAAAAQAQv/RA74DLwAbACUALAA4AAAFMj8BNjURJiciDwEnJiIPAQYVERQWMzI/ARcWJSI1ETQ/AREHBgUmLwERHwETETc2NxYXERQPAQYCjRgT4SUBMA8U5OkTMBTeJhoXDxXZ7Rj+GAYOwMIDAdYJCboNv0XCBAIFAQ6sCi8LfxUrAlIwAQt+jgwMfxUq/a4YGgx1hQxpBwITDwlv/cxrAQ4FBWkCMgh0/c8CNWkCAQEG/e0QCGQGAAADAFb/zQOmAzAACQARACkAAAE3NjQvASYGDwEBNwEnAQcGFgMhMjY3EQcRDgEjISInETYzITchIgcRFgN8HwsLCgobCx/+TVMBezv+hicCCasB9zo/AUUBHhf+C0ECAkEBc0X+R4YBAQLKHwwbCwsKAgof/gckAXo6/oZQBgr+w0NCAd1F/mshIkMB50NFhP4ShQAABgBq/6EDlgNfAB8AKQAzAEAATQBZAAAlEzMyNjQmJyM1NCYnIw4BBxUjDgEUFjsBEx4BFyE+AQE0NjsBMhYdASMDLgEnAyEDDgEHJzI2NxM0JiIGBwMUFiMyNjUDNCYiBhUTHgE3ETQmIgYHER4BMjYDLh4rDRISDbw5MqEyOAG6DRMTDSsdAzgvAYkuOP5eGBSWFBjuRxMYAR4CDxwBGBQ/Cw4BDA4VDgENDvMLDg0PFQ4NAQ2kDxUPAQEPFQ8GAnMSGxMBQC42AQE2LkABEhwS/Y0vNQEBNQMfEhcXEjz9JwEYEwJs/ZQTGAFMDw0BxA0PDwz+OwwQEAwBxQwPDw3+PA0PHAHFDA8PDP47DBAQAAAAAgCE/5wDfQNkABoAOAAAJTI2NREnFxYyNjQvASYiDwEOARYyPwEHERQWAyE2JxE2JyMVMzIWFREUBiMhJicRNjczNSMiFREUAgAOFAJdChsSCpEMGgyRCQERHApeAxToAeyHAQGHd3YgIiIg/hdCAQFCdniG7BMOAbhAYwoQGwmMDAyMCRoRCmRB/kgOE/6wAYQBp4QBRSIi/mEiIgFDAZ9DAUWF/lmFAAAAAAIAhP+xA30DTgAaADgAACUyPwE2NCYiDwE3ETQmIgYVERcnJiIGFh8BFgMhMicRNicjFTMyFhURFAYjISInETY3MzUjBhURFAIADQyRChIbCl0CFBwUA14KHBEBCpAM6QHshwEBh3x7ICIiIP4XQgEBQnp8hssMiwobEApkQAHEDhMTDv48QGQKEBsKiw3+6IQBu4QBRSMh/k0iIUMBs0MBRQGE/kWFAAMAS//LA7UDNQALABcANAAABT4BNy4BJw4BBx4BNy4BJz4BNx4BFw4BJTI/ARcWMjY0LwE3NjQmIg8BJyYiBhQfAQcGFBYCALj4BQX4uLn3BQX4uJvNBATMnJvNBAPO/tIPCnp5Ch4TCnp7ChQcCnt7ChwUCnp6ChQ1Bfi4uPgFBfi4uPhEBMycm80EBM2bnMyyC3p6ChMeCXp7ChwUCnt6ChMdCnp6CR4TAAACADH/9gPPAwkAIAA+AAAFMjY3ATY0JwEuASMiBh0BIwYCFx4BMxY2Nz4BFzMVFBY3Ij0BNAcjDgEHBiI1PgE3MxY9ATQ2MhcBFhQHAQYCKw8cEAFSFxf+rhIZDxccDebVAQEZEg4bCzinew0cLwYOOpnCJQIFAqzZOg4DBwMBMQUF/s8ECQ4OAT8YLBgBPBAPHheiAv7w8BwdAQ0TaFABpBYcXAamDwEBX1IEBZ7xBwEPqgMDA/7bBAgE/t8EAAACADH/9gPPAwkAIAA+AAAFMjY9ATM2FhceATcyNjc2AicjNTQmIyIGBwEGFBcBHgEnIicBJjQ3ATYyFh0BFDczHgEXFCInLgEnIyYdARQB1RYcDXunNwwbDhIZAQHV5g0cFw8aEf6uFxcBUhAbCQME/s8FBQExAwcDDjrZrAIGASXCmToOCRwWpAFQZxQNAR0c8AEQA6IWHg8Q/sQYLBj+wQ4OXAQBIQQIBAElAwMDqg8BB/GfBARSXwEBD6YGAAADAEv/ywO1AzUACwAXAEMAAAU+ATcuAScOAQceATcuASc+ATceARcOAQEeARc+ATc0JiIGFQ4BBy4BJz4BNzIXBwYeATI/ATY0LwEmIgYUHwEmIw4BAgC4+AUF+Li59wUF+LibzQQEzJybzQQDzv6lAmxSUWsCERgRAko4OUoCAko5CAcqCAEOFwhTCAhSCBgOBx4GBkpqNQX4uLj4BQX4uLj4RATMnJvNBATNm5zMAVRSbQICbVEMEBAMOUoCAko5OEoCASkIGA8IUwgXCVQIEBcIHwECaQACABz/sQPkA0kAGQA9AAAXFj8BFxY2JwM3NiYjBQMmIgcDJSIGHwEDBjciPwE2LwEmNjMFFj8BNjIfARY3JTIWDwEGHwEWBi8BJg8BBtsaKOPjKDUQWeUoFDL+51UPQRBV/ucxFSnlWhBZAQFVCRbVAwEEAQMaCEoCAwFKCBoBBAMBA9UWClUBAgPOFhXPAjwTHqamHicuAQukHD8CAQwvL/70Aj8cpP71LkEE9RkPkwIDBQEa+AQE+BoBBQMCkw8Z9QQCA50QEJ4CAAADAEv/ywO1AzUACwAXADQAAAU+ATcuAScOAQceATcuASc+ATceARcOASc+ATc1MzI2NCYnIzUuASIGHQEjDgEUFjsBFRQWAgC4+AUF+Li59wUF+LibzQQEzJybzQQDzpwREwGGEhYVE4YBEyIShhMWFxKGEjUF+Li4+AUF+Li4+EQEzJybzQQEzZuczJwBFRN/EiMSAYYTFhYThgESIxJ/EhYAAAMAS//LA7UDNQALABcAIwAABT4BNy4BJw4BBx4BNy4BJz4BNx4BFw4BASEyNjQmIyEiBhQWAgC4+AUF+Li59wUF+LibzQQEzJybzQQDzv64AVkSFhUT/qcTFhc1Bfi4uPgFBfi4uPhEBMycm80EBM2bnMwBRBIiExMiEgACAEv/ywO1AzUACwAXAAAFPgE3LgEnDgEHHgE3LgEnPgE3HgEXDgECALj4BQX4uLn3BQX4uJvNBATMnJvNBAPONQX4uLj4BQX4uLj4RATMnJvNBATNm5zMAAAAAAIAS//LA7UDNQALACgAAAU+ATcuAScOAQceATciJjQ/AScmNDYyHwE3Nh4CDwEXFhQGIi8BBwYCALj4BQX4uLn3BQX4Hg8VC4CACxUeCoGBCx0UAQuAgAoVHgqAgAs1Bfi4uPgFBfi4uPjyFR4KgYAKHhQKgIAMARQeCoGACh8VCoGBCgAAAAACAEv/ywO1AzUACwA3AAAFPgE3LgEnDgEHHgEDPgE3MhcnJjQ2Mh8BHgEPAQYiJjQ/ASYHDgEHHgEXPgE3NDYyFhUOAQcuAQIAuPgFBfi4ufcFBfgLA2tMBgYfBw8YCFQHAQhUCRcPCCoHCDpMAQFMOjlLAhEZEQJtUlNuNQX4uLj4BQX4uLj4AZ5SawIBHwgYEAhWCBgIVAgPGAgqAQEBSzk6SwICSzoMEREMU24CAm8AAAABABz/sQPkA0kAGQAAFxY/ARcWNicDNzYmIwUDJiIHAyUiBh8BAwbbGijj4yg1EFnlKBQy/udVD0EQVf7nMRUp5VoQPBMepqYeJy4BC6QcPwIBDC8v/vQCPxyk/vUuAAACAEv/ywO1AzUACwAoAAAFPgE3LgEnDgEHHgE3IiY9ASMiJjQ2OwE1NDYyFh0BMzIWDgErARUUBgIAuPgFBfi4ufcFBfi3EhONExcWFI0TJBSNFBcBFhSNFDUF+Li4+AUF+Li4+NsXE4UTJROOExcWFI4TJROFFBYAAAAAAgBL/8sDtQM1AAsAFwAABT4BNy4BJw4BBx4BEyImNDYzITIWFAYjAgC4+AUF+Li59wUF+AMUFxcUAWoUFhcTNQX4uLj4BQX4uLj4AYoTJRMTJRMAAwBL/8sDtQM1AAsAFwAjAAAFPgE3LgEnDgEHHgE3LgEnPgE3HgEXDgEnPgE3LgEnDgEHHgECALj4BQX4uLn3BQX4uJvNBATMnJvNBAPOm3SeAwOedHadAwOeNQX4uLj4BQX4uLj4RATMnJvNBATNm5zMUgOedXWeAwOedXWeAAACAEn/yQO3AzcACwAgAAAFLgEnPgE3HgEXDgETJiIPAQYiLwEmIgYUHwEWMj8BNjQCALr4BQX4urr4BQX4CwkaCcgKGQk7ChkTCWgJGQr0CTcF+Lq6+AUF+Lq6+AJGCQnICgo6ChMZCmcJCfQKGQAAAQDjAGMDHQKdABsAADcGFBYyPwEXFjI2NC8BNzY0JiIPAScmIgYUHwHuCxYfDNzcCx8XC9zcCxcfC9zcDB8WC9ukCx8XC9zcCxcfC9zcCx8XC9zcCxcfC9wAAAABAQAAgAMAAtgAFgAAJS4BJz4BNzUXBzUOAQceARc+ATczDgECAG2QAwOQbcDAXHoCAnpcXHoCKAOQgAOQbW2QA1iAb28CelxcegICelxtkQAAAAABAEv/nQO1A14AKQAABT4BNy4BJyYOARYXHgEXDgEHLgEnPgE3FR4BPwE2NC8BJgYHFQ4BBx4BAgC4+AUBYlQPHREHDUVRAQPOm5vNBAOafAEZEooODokSGgGZxgMF+GMF+LhtuD0LBRwbCjKYXZvNBATNm4XAHj4WDA1gChsLYAwLFz0g66K4+AAAAAIAHP+xA+QDSQAZAC0AABcWPwEXFjYnAzc2JiMFAyYiBwMlIgYfAQMGJRE2HwEWNyUyFg8BBh8BFgYvASbbGijj4yg1EFnlKBQy/udVD0EQVf7nMRUp5VoQAUACAUoIGgEEAwED1RYKVQECA84KPBMepqYeJy4BC6QcPwIBDC8v/vQCPxyk/vUu5wIiAQT4GgEFAwKTDxn1BAIDnQgAAAAMAB3/nQPjA2MADAAZACYAMwBAAE0AWgBnAHQAgQCOAJsAAAEiBgcVHgEyNjc1LgEHDgEfAR4BPgEvAS4BBSYGDwEGHgE2PwE2JgUGFh8BFj4BJi8BJgYFLgEPAQ4BHgE/AT4BFzQmJyMOARQWFzM+ASUUFhczPgE0JicjDgEFNiYvASYOARYfARY2JR4BPwE+AS4BDwEOAQU+AS8BLgEOAR8BHgElFjY/ATYuAQYPAQYWFzI2NzUuASIGBxUeAQIADREBAREaEQEBEf4MBgZMBxgWBwdMBxcB2AwXCEwGBxYYB0wGBv1gBgcLhAsYDQYMhAsYAz8HGAuFCwYNFwyECwc6EQ6YDRERDZgOEfw6EQ2ZDRERDZkNEQOGBgcLhAwXDQYLhAwY/MEHGAuFCwYNGAuECwcCmgwGBkwHGBcGBkwIF/4pCxcISwcGGBcHTAYG/Q0RAQERGhEBAREDYxEOmA0REQ2YDhFABxgLhQsGDRgLhAsHBgYHC4QLGQwGC4ULGKoMFwhMBgYXGAdMBgYMDAYGTAcYFwYGTAgX5g0RAQERGhEBARENDREBAREaEQEBEf4LFwhMBgYXGAdMBgYMDAYGTAcYFwYGTAgXvQcYC4ULBg0XDIQLBwYGBwuEDBcNBgyECxhHEQ6YDRERDZgOEQAAAAIAbf/pA5QDFwAVACEAACUyNjcXFjI+AS8BPgE3LgEnDgEHHgE3LgEnPgE3HgEXDgEBtjRhK8sOKhoBDsogIwEEuoyMugMDuoxtkwICk21tkwMDk4QgHssOGykPyiplOYu7AwO7i4y6QwOTbW2SAwOSbW2TAAAAAAEAnwAXA2EC6AAcAAAlPgE1ESE+ATQmIyERNCYiBhURISIGFBYXIREUFgIAEBYBFRAWFhD+6xYgFv7rEBYWEAEVFhcBFQ8BHQEWIBYBHg8VFQ/+4hYgFgH+4w8VAAAAAAEBQABAAsACwAAFAAABNwkBJwEBQEEBP/7BQQD/An9B/sD+wEEA/wABAUAAQALAAsAABQAAAScJATcDAsBB/sEBP0H/An9B/sD+wEEA/wAAAQC4AIUDWgJ/ABcAAAEXFhQHAQYiLwEmND8BNjIfARYyNwE2MgNDDQoK/lwLHQy1CwsNCx0LdQwdCwFjCx0CdA0LHQv+XAsLtgsdDAwLC3UKCgFjCwAAAAIAC/+9A/UDQwAnAD0AABchPgE1ERcWFzI2NyYvATU0JicjDgEdAScmIgcBBgceATM2PwERFBYBNCYrASIGFREjJicRAT4BFwERBgcj5wIzLjI3DRIQFAEBDJURDjgOEaoXOBf+SwwBARQQEg03MwHCEQ+2DxKPKQEBJgcQBwEmASmQQwExLQGHMg4BEg8TCof9DhABARAOkZoVFf5yCRMPEgEOMv55LjABYQ8REQ/+4wEqAbUBDAYBB/70/ksqAQAAAAADAEv/ywO1AzUACwAXACwAAAU+ATcuAScOAQceATcuASc+ATceARcOASUyPwE2Mh8BFjI2JicDJiIHAwYUFgIAuPgFBfi4ufcFBfi4m80EBMycm80EA87+tQsGlAYKBpMIFQ0BA6QLKgqlAww1Bfi4uPgFBfi4uPhEBMycm80EBM2bnMyIB5QFBZQHDRMJAaMaGv5dCBQNAAQAVv/TA6wDKgAtAGYAcgB+AAAlNjc+ATc2NyY2NyYnBiY3NSYnBwYiLwEHFRYGJyMHFxYUDwEWFzM2FgcWFz4BByYnNzYmDwEmJzc2NC8BNjcXFjYvATY3FxYyPwEWFwcGFj8BFhcHBhQfAQYHJyYGHwEGBycmIg8BEz4BNy4BJw4BBx4BFy4BJz4BNx4BFw4BAoYPDwJlTQcGNwI4BAZSaQILDAI6lzoGEwJrUwoHBj09AwUFBFJrAQ4OOJGsQzsCATovMSMRJyEhKQ8gOC86AQM4PiUgUyAiQDcCATsuLCMOHiIiHBElJS86AQI8RBkgUyAcXy8+AQE+Ly8+AQE+L0ZdAgJdRkZdAgJdGwYHTWUCDw85kTgODgFrUgQFBQM9PQcIClNrAhMGOpc5AwwLAmlSBgQ4An4OIywvOgECN0AiIFMgJT44AwE6LzggDykhIScRIzEvOgECO0McIFMgGUQ8AgE7LiUlERwiIh8BPwE+Ly8+AQE+Ly8+OAJdRkZdAgJdRkZdAAAAAAMAP/+/A8EDQQAUACAALQAABTI2NwE2NCYiBwEOARUUFhcFEx4BAyUmNDclNj8BBwYHAyInAwE+ATcHBgcDBgJXFyIMARkMGCse/R8cJCgfATVaCRxr/tgKCQJEGRkxLhcSmAQDWgEmEigRFwwK2wRBJR8C3R4rGAz+5QohFx0cCVr+ziEpAb1aAwgE2woMFyUTEv15CgEoAScSMBYxGRr9vAkABABL/8sDtQM1AAsAFwAgADkAAAU+ATcuAScOAQceATcuASc+ATceARcOAQMyNjQmIgYUFgMzPgE0JisBNTQmKwEiBhQWOwEVIyIGFBYCALj4BQX4uLn3BQX4uJvNBATMnJvNBAPOnxgfHzAfIDCuDhERDjUREFENEhINLjUOERE1Bfi4uPgFBfi4uPhEBMycm80EBM2bnMwCASAvICAvIP57ARAaEdoSFREaEcURGhAAAAAABABL/8sDtQM1AAsAFwA8AEUAAAU+ATcuAScOAQceATcuASc+ATceARcOAQM+AT0BNDY3PgE3LgEOAQcGFRQWMzI2NzY3HgEVFAYHDgEdARQXPgE0JiIGFBYCALj4BQX4uLn3BQX4uJvNBATMnJvNBAPOpBATFRYgJwECTnBFCQQSCxIPCRUrHSMbHBgeIRMbGicbGzUF+Li4+AUF+Li4+EQEzJybzQQEzZuczAEPARENBREbDxMvJTY4ASseCwsODxEMJQEBHRkVHhIQJx8GIoABGSYZGSYZAAAAAAMA0f/LAy8DNQAUABwAKwAAASIGBxUGFREUFjMhMjY1ETQnNS4BBz4BMhYXFSEFMhYVERQGIyEGNRE0NjMCAF2FA0owMAGeMDBKA4X9AlqIWgL+wAFtDw4OD/5mHQ4PAzWBg2IJW/7FNDExNAE7Wwlig4H7Wl9fWmlBDhL+vBIPASIBRBIOAAAGAGQBLgOdAdMACAASABsAJQAuADgAAAEeARQGIiY0NjcOARQWMjY0JicFHgEUBiImNDY3DgEUFjI2NCYnBR4BFAYiJjQ2Nw4BFBYyNjQmJwIAFBoaKBoaFCMvL0YvLyP+thMbGycaGhQkLi5HLy8jApUUGhonGxsTIy8vRy4uJAGvARooGhooGiUBL0YvL0YvASQBGigaGigaJQEvRi8vRi8BJAEaKBoaKBolAS9GLy9GLwEAAAAAAgCD/9sDfQMlACEANAAAFz4BNzU+ATceARcyPgI3ES4BIw4BBy4BJyIOAgcRHgEBLgEnIgYHET4BMx4BFzY3EQ4BpA4SAQg6MHO4bTE1LRoBARkTD0A3brd0MTUtGgEBEgI5Z7l4JDwSBDYybrhzRiwFNSUBEg7uBA8BBUQFCxUkHQG0ERMBEAEFRAULFSQd/TgOEgEVBUQFCAgBkwsWBEQFAQ3+bwsWAAAAAAIAC/+9A/UDQwAhADkAABMeATM2NwE2MhcBFhcyNjcmLwE1NCYnIw4BHQEnJiIHAQYTFBYXMxE0NjczHgEVETM+ATURASYiBwELARQQEg0BogcQBwGiDRIQFAEBDJUQDjkOEaoXOBf+Swx6My2uEg+XDxKtLjL+lAcPB/6VAYMPEgEOAX0HB/6DDgESDxMKh/0OEAEBEA6SmxUV/nMK/oYtMQEBMQ8RAQERD/7PATEtATkBSAcH/rYAAAAAAgBW/9MDrAMqADgARAAABSYnNzYmDwEmJzc2NC8BNjcXFjYvATY3FxYyPwEWFwcGFj8BFhcHBhQfAQYHJyYGHwEGBycmIg8BNz4BNy4BJw4BBx4BAaFDOwIBOi8xIxEnISEpDyA4LzoBAzg+JSBTICJANwIBOy4sIw4eIiIcESUlLzoBAjxEGSBTIBxfTmcCAmdOTmcCAmcsDiMsLzoBAjdAIiBTICU+OAMBOi84IA8pISEnESMxLzoBAjtDHCBTIBlEPAIBOy4lJREcIiIf9gJnTk5nAgJnTk5nAAMAS//LA7UDNQALABQALQAABT4BNy4BJw4BBx4BEyImNDYyHgEGAy4BNDY7ATUjIiY0NjsBMhYdATMyFhQGBwIAuPgFBfi4ufcFBfi0FyAfMB8BIV4OEREONS4NEhINURARNQ4REQ41Bfi4uPgFBfi4uPgCSSAvICAvIP57ARAaEcURGhEVEtoRGhABAAAAAAMAS//LA7UDNQALADAAOQAABT4BNy4BJw4BBx4BEyI9ATQ2Nz4BNTQmJwYHDgEjIiYnNDc+AhYXDgEHDgEdARQGByImNDYyFg4BAgC4+AUF+Li59wUF+LAkHxkeHCQfLRUKERILEwEECUl2UgIBKSEXGBIQExwcJxwBHDUF+Li4+AUF+Li4+AFTIwYhKRETIBYaHgECJg0REA8LCyAtATs4JzEVDxwTBQ4SgRopGRkpGgAAAAMAZAEuA50B0wAJABMAHQAAAQ4BFBYyNjQmJyEOARQWMjY0JichDgEUFjI2NCYnAgAjLy9GLy8j/rYkLi5HLy8jApUjLy9HLi4kAdMBL0YvL0YvAQEvRi8vRi8BAS9GLy9GLwEAAAAABgBQABMDsALsABgAIQA5AEIAWwBkAAABMjY3MzI2NCYrAS4BIgYHISIOARYzIR4BNy4BNDYyFhQGBSIGFBYXMx4BMjY3IT4CJichLgEiBgcXIiY0Nh4BFAYBPgE3MzI2NCYnIy4BIgYHIQ4BHgEzIR4BNyImNDYyHgEGApohNAyUDRMTDZQMM0Q0C/46DxMBFQ4Bxgs0IhYcHSocHP3CDRMTDZkLNEQ0CwHBDxMBFQ7+Pws0RDMMYRUdHSscHAEZIjMLlQ0TEw2VCzRDNAv+Og4VARMPAcYLNCIVHRwrHAEeAh8lHxQdFB4mJh4UHRQfJTQBHCsdHCwcshMeEwEeJiUfARMdFAEeJSUeVR0qHQEcKxz+xQElHxMeEwEfJCQfARQdEx8lNB0rHBwrHQAAAAYAUQBHA7ACuQAIABQAHQApADIAPgAAEz4BNCYOARQWNyEyNjQmJyEOARQWAzI2NCYiBhQWNyE+AS4BJyEOARQWAz4BNCYOARQWNyEyNjQmJyEOARQWhhYgIC0eHtUCSQ8TEw/9tw8TE68WICAtHh7VAkkOFQETD/23DxMTrxcfIC0eHtUCSQ8TEw/9tw8TEwJNAR8sIAEeLh4TEx4TAQETHhP+6SAsIB8uHhIBFB0TAQETHhP+6QEeLSABHi4eExMeEwEBEx4TAAAAAAMAkgClA20CWwAMABkAJgAAEz4BMyEyFhQGByEiJhU+ATchHgEUBgchLgEVPgE3IR4BFAYjISImkgEUEAKSEBQUEP1uDxYBFBACkhAUFBD9bg8WARQQApIQFBQQ/W4PFgI3DxUVHxQBFqgQFAEBFCAUAQEVqBAUAQEUHxUVAAAAAgAE/88D/AMYAB0AOwAAASMuAScOAQcGHgE2Nz4BNx4BFyMiBh8BFjI/ATYmBTMeARc+ATc2LgEGBw4BBy4BJzM+AS8BLgEPAQYWA9o4FeilX6M7CwIZGgsyiU2HwRM9FgsMXAoaCl0MC/w1OBXopV+jOwsCGBsKMIlQiMATPRYLDFwKGgpdDAsBn6HUBAFORA0dEQQMOT4BA6qGGRGEDg6DEhlYodMEAU5DDh0RBAw4PwECqoYBGBKDDgEPgxEZAAAAAAEAav+3A50DUAAzAAAJAQYuAjcBPgEXFgYHAQYuAjcBPgEmBgcBDgEXFjY3ATY0Jy4BBwEGFhceATcBNi4BBgMm/sU/kG0DPAGuJl4lIgYl/lwQIhcDDwElCgETGAr+2SABHiBTIgGmPDU1jD/+UE4ESEvDUwE9CgETGgFw/sU9BG2PQAGtJgcjJV4m/lwQBBchEQElChgTAQr+2iJVHiACIQGmPos2NAE8/lBTw0tIBE4BPQocEwEAAAAAAwAAAC8EAAKyAAsAFwAgAAAlNiQ3JiQnBgQHFgQ3LgEnPgE3HgEXDgEnMjY0JiIGFBYCAOcBFQQE/urm5f7pBAQBGORadwICd1padwICd1ogLCtBLCwvDe5HRu4NDe5GR+5iA3dZWnYCAnZaWXeELEArK0AsAAAAAQCMAK8DdAJRABAAADcGFBYyNwkBFjI2NCcBJiIHlwsWIgsBMQExCyIWC/60DCIM8QojFQsBOP7ICxUjCgFUDAwAAAABAIwArgN0AlIAEQAAJTY3ATY0JgYHCQEuAQYUFwEWAgARDAFMCxcgDP7P/s8MIBcLAUwMrgEMAVQLIBgBC/7IATgLARggDP6tDAAAAQEvAAwC0QL0ABAAACUWMjY0JwkBNjQmIgcBBhQXAo8LIhUL/sgBOAsVIgv+rAwMFwsWIQwBMQExDCEWC/60DCIMAAABAS4ADALRAvQAEQAAJTI3ATY0JwEmIgYWFwkBBhQWAVYQDAFTDAz+rQwgGAELATj+yAsWDAsBTA0hDAFLDBcgDP7P/s8LIhYAAAAAAQC7/+sDRQMVABwAAAUyNjURJx8BFjI2NCcBJiIHAQYUFjI/AgcRFBYCABEVA4BiCyAVDP7kDSAM/uMMFSALYoADFRUVEQI0XI1gChUfDQEdDQ3+4w0fFQpgjVz9zBEVAAAAAAEAu//rA0UDFQAcAAABIgYVERcvASYiBhQXARYyNwE2NCYiDwI3ETQmAgARFQOAYgsgFQwBHA0gDAEdDBUgC2KAAxUDFRUR/cxcjWAKFR8N/uMNDQEdDR8VCmCNXAI0ERUAAAABAHIAOwOOAsYAHAAAExQXARYyNjQvAhchMjY0JiMhBz8BNi4CBwEGcg0BHQ0fFQpgkWgCHhEVFRH94meQYAsBFR8O/uQNAYAQDf7kDBUgC2KDBhUiFQaDYgsgFQEO/uUNAAAAAQByADsDjgLGABwAAAE0JwEmDgEUHwInISIGFBYzITcPAQYUFjI3ATYDjg3+5A4fFQpgkWj94hEVFRECHmiRYAoVHw0BHQ0BgBANARsOARUgC2KDBhUiFQaDYgsgFQwBHA0AAAEBHgAHAtoC3wAGAAAlEyMRIxEjAfzekZuQBwEoAbD+UAAAAAQADf/3A/MDCQAZAC4ARQBbAAAFMjY1ETQmIyIGDwEGKwEmHQEUNzMyHwEeASUWNjc+ATQmJy4BDgEXHgEUBgcGFgUiLwEuASsBBj0BNDsBMjY/ATYyFREUNxY2Nz4BNCYnLgEHDgEXHgEUBgcGFgH2FhwcFw8aEckEB39bW38HBMkQGwGCDRsKKi8uKwobGQMJJCgoJAkD/oEDBL4IDgiPGRmPCA4IvgMK2gwaChocHRkKGgwOAwoTFRYSCgMJHBYCqxceDxCyBAFgq2ABBLQODlcIBg07l6aXPA0FERsPNIGQgjMOHAYEqwcFARq1GQQIrAMG/bAGcAgFDSJdZl0jDAUHCh0OGkdORxoOHAAABgA5/98D0gMiACQATABQAGIAZgByAAABNDEmLwEuAQchJgYPAgYVHgEXMzI2Nx4BNzY3HgEzMRY3PgEHBisBIiYvAQcGBwYHIiYvAQcOASsBLgE9ATQ/AjY3ITIWHwIWBgcmJwcXIwYHFSE1JicRFBYzITI2NRElJicHASEiJjQ2NyEeARQGA74BAkwLNCH95CAyC1MBCQFiSwcoRxozjjsMChpHKC4pOi+MFxkEGCoPODgGCB0mFyoPOTgQKhcGLDoFAlIFDgInBwwDTAIMHNoCAgPxAh8j/ZYnIh8XApAXH/3/AgEDAWv+PBAWFhABxBAWFgIUAQUEwR8kAQEiH8gFHB5NZwMiIDsMMAsMICEBFiF5WgwUE0RECAYYARQTREUSFQI9LgESEQXHDgEKB8MGKElrAQECCg8G4OEHEv70FxsbFwEKBwEBAgEAFR8UAQEUHxUAAAAFAED/4APAAyAACwAfADMASABdAAABISImNDYzITIWFAYDIyImNDY7ATI2PQE0NjIWHQEOAQUjLgEnNTQ2MhYdARQWOwEyFhQGAyImPQE+ATczMhYUBisBIgYdARQGISImPQE0JisBIiY0NjsBHgEXFRQGA6D8wA4SEg4DQA4SEm7ADhISDsAOEhIcEgE2/fegKTYBEhwSEg6gDhIS7g4SATYpoA4SEg6gDhISAvIOEhIOwA4SEg7AKTYBEgFgEhwSEhwS/oASHBISDqAOEhIOoCk2AQE2KaAOEhIOoA4SEhwSAiASDqApNgESHBISDqAOEhIOoA4SEhwSATYpoA4SAAAAAAEAMf/2A88DCQAgAAAFMjY9ATM2FhceATcyNjc2AicjNTQmIyIGBwEGFBcBHgEB1RYcDXunNwwbDhIZAQHV5g0cFw8aEf6uFxcBUhAbCRwWpAFQZxQNAR0c8AEQA6IWHg8Q/sQYLBj+wQ4OAAEAMf/2A88DCQAgAAAFMjY3ATY0JwEuASMiBh0BIwYCFx4BMxY2Nz4BFzMVFBYCKw8cEAFSFxf+rhIZDxccDebVAQEZEg4bCzinew0cCQ4OAT8YLBgBPBAPHheiAv7w8BwdAQ0TaFABpBYcAAQACP/nA/gDGQAbACcANQBEAAAXITY3ESYrASImLwEuASsBIgYPAQ4BKwEiBxEWJS4BJz4BNx4BFw4BEyImNTQ+ATIeARUOAQcBMj4CNC4CIw4BBx4BjwLihgEBhmQYGg0jDychqyAoDyMNGhhhhgEBAfdkgwMDg2RkgwMDg9AXHg4ZHBkOAR4W/swiPTAaGjA9IkhfAgJfGQGEAcGEDRAmEhMTEiYQDYT+P4SIA4RkZIQCAoRkZIQBUR4WDxgODhgPFh4B/u8ZMD5EPjAZAl9ISGAAAwAR/9sD7wMlACUALgA3AAATHgE7ARMeATMhMjY0JiMhLgEvASEyNj8BNjcuASMhJy4BKwEiBgEeATI2NCYiBgUUFjI2NCYiBhEBEg2RRQYyLwH0DRISDf4TEhYDBwIgLzIHIgEBARUR/UQIAxkglw0SATgBJzopKTonAZAoOygoOygDBQ0T/ikuNRIcEgEXFC01LuMKBhATNxgZE/0OHicoOignHh4nJzwnJwAAAAAEABL/2wPvAyUAJAArADQAPQAAJSEyNjQmIyEuAS8BITI2PwE2Ny4BIyEnLgErASIGFBY7ARMeAQEHDgEjIScTMjY0JiIGFBYhMjY0JiIGFBYBbgH0DRISDf4TEhYDBwIgLzIHIgEBARUR/UQIAxkglw0SEg2RRQYyAmcfAhYT/d4lexwpKTonJwGuHigoOygoqxIcEgEXFC01LuMKBhATNxgZExoT/ikuNQHRzRQX+P1fKDooJzwnJzwnJzwnAAADAEn/yQO3AzcAFAAgACwAAAEWFA8BBiIvASY0NjIfARYyPwE2MgM+ATcuAScOAQceARcuASc+ATceARcOAQLFCQn0ChkJaAkTGQo7CRkKyAoZvJvPBATPm5vPBATPm7r4BQX4urr4BQX4AhQKGQr0CQlnChkTCjoKCsgJ/fUEz5ubzwQEz5ubz00F+Lq6+AUF+Lq6+AAAAAEAS//LA7UDNQALAAAFPgE3LgEnDgEHHgECALj4BQX4uLn3BQX4NQX4uLj4BQX4uLj4AAAFAAAAFgQAAr4ACwAcAC0ANgA8AAAlFjI+AScBJg4CFwE+ATcmJCcGBxc2Mx4BFxQPATY3JwYjLgEnNDcnDgEHFgQBLgMjIgcXJx4BFzMnAyQJFxABCf2RCBgQAQkCklhgAQP+6+hfUmImKVl1AhG/aFZiKzFZdQIWgl1mAQQBFgFdAREiKxgHB4TtAkMzD4YfCREXCQJvCAEQGAj9+zt7I0bqDQEcYRECdForJO0BH2MWAnZXMyqDPH8lReoBNxgrIhEBgw8yQwGGAAAFAAAAGAQAArsACwAdAC8ANwA/AAAlFj4BNCcBJg4CFyUGBxc2Mx4BFxQGBxc+ATcmJAM2NycGBy4BJz4BNycOAQcWBCU2NS4BJwYHEzY3AQYVHgEDHwoWEQj9lgkXEAEIAUtgUDA9Q8D7BFlOLlhhAQP+7OlnVjBCS8D7BAFeUi5dZgEEARYBpBECdVgsJVExKv7rFQJ0IQkBEBcJAmoIARAXCRQBHTATDMktGWMxLjx8I0bq/ZQBHjEVAQvFMhZoMy48fyRF698mK1l0AgEQ/nMBFQEVKjJXdQAABAAAAC8EAAKxAAsAFwAjACwAACU2JDcmJCcGBAcWBDcuASc+ATceARcOASc+ATcuAScOAQceATcuATQ2MhYUBgIA5wEVBAT+6+fk/ugEBAEY5L78BAT8vr39BAT9vVp2AgJ2Wlp3AQJ2Wh4oKDwnJy8M7kdG7g0N7kZH7i8LyDMtzA0NzC0zyCkCeFhadQICdVpYeIoBJzsoKDsnAAAAAAEAg//bA30DJQAhAAAXPgE3NT4BNx4BFzI+AjcRLgEjDgEHLgEnIg4CBxEeAaQOEgEIOjBzuG0xNS0aAQEZEw9AN263dDE1LRoBARIlARIO7gQPAQVEBQsVJB0BtBETARABBUQFCxUkHf04DhIAAAACAFX/wwOrAzwAMgBAAAATDgEHFhcWBw4BFRQXFgcOARUUHgEOARUUFjsBHgEVDgEHFBYzMjY3PgE3PgE3NCYnIyIBLgEnIx4BBw4BBzM+AdwaKAEBCgQHFB0PBwsPEgkTFgsqIZkdIwRABCIaFh0MMXQzKikBrpk8VQKrAWhSTTo3AQMxHj9KYAMzBiIfGQ0JAwkkGh4TCgcIIhYPHRARHRIgLAEbGC+HPB8hHRlem0I2bkt6mwT+62OGAyt8SlF1IwKFAAAAAAMAPP+dA8QDYwAwAGkAdwAAASMiBgcOAQcWFw4BFBcOARUUFwYVHgEXMzYXDgEHHgEXMjY3PgE3Mz4BNy4BJyMuAQczHgEXFgYHDgEHDgEnIic+ATcuASsBLgE1JjY3NjQnLgE1NDc2NTQnLgE1Jjc2NTQnLgE1NDc+AQUeARcOAQcjPgE1NCYnAZ47KUIYLDMBAQQWGAoPEQ4TAT8xohEBBUAEATUsHy8UJmZMUVJrAgJ1WI0rZXY8iaICASUrMnYyDBIKIQEFQAQBMyeZFRsBCw0GBAsJHQ0DCQgBLgoCCAQvEzoBuDtSAQFJNCcYFionA2MFBgs5KBAPEC81Fw8qFyEZGygxQAIBDiWJRC04ASMoSpRbA5dwb5YDFxlBA4dvRGk5QJ5fGRABJTeGNSUpARsWDxcMBg0FDxcNHhYJCwUEERQLIhcFCgMFEhAHIQwFBTADcVRVcgItYTg8ZygAAAAAAgBV/8QDqwM9ADIAQAAABT4BNyYnJjc+ATc0JyY3PgE1NC4BPgE1NCYnIyImJz4BNzQmIyIGBw4BBw4BFRQWFzMyAR4BFzMuATc+ATcjDgEDIxsoAQEKBQgUHAEPCAwPEgoSFgsqIZkeIgEFQAQiGhYdDDF0Myoqrpk8VP1WAWhSTTo3AQMxHj9KYDMGIh8YDgkDCSQaHhMKBwgiFg8dEBEdEiArARwYL4c8HiIdGV6cQTZuTHmbBAEVY4YDK3tLUXUjAoUAAAABAFb/9wOqAwkAFwAABTI2NzYSNy4BJyIGBy4BIw4BBxYSFx4BAgAHEQe40QIDhmo9XR0dXjxqhgMC0LkHEQkHBHQBB4pzjQJANzdAAo1ziv73cgQHAAAAAwA8/50DxANjADEAagB4AAAFMzI2Nz4BNyYnPgE0Jz4BNTQmJzY1LgEnIyInPgE3LgEnIgYHDgEHIw4BBx4BFzMeATcnLgEnJjY3PgE3PgEXMhcOAQceATsBHgEVFgYHBhQXHgEVFAcGFQYXHgEVFgcGFRQXHgEVFAcOASUuASc+ATczDgEVFBYXAmI7KUIYLDMBAQQWGAoPEQcHEwE/MaIRAQVABAE1LB8vFCZmTFFSawICdViNK2V2PImiAgElKzJ2MgwSCiEBBUAEATMnmRUbAQsNBgQLCR0MAQMJCAEuCgIIBC8TOv5IO1IBAUk0JxgWKidjBQYLOSgQDxAvNhYPKhgPHgwbKDFAAg0liUQtOAEjKEqUWwOWcW+WAxcZQQECh29EaTlAnl8ZEAElNoc1JSkBGxYPFwwGDQUPFwwfFgkLBAURFAsiFwUKAwUSEAchDAUFMANxVFVyAi1hODxnKAACAFb/9wOqAwkAFgAwAAATFhIXHgEyNjc2EjcuASciBgcuASMOARc+ATceARceATI2Nz4BNx4BFw4BBwYiJy4BVgLStwcRDhEHt9ICA4ZqPV0dHV48aoZCAl9NO0oVCA4ODQkVSjtNXwIFzIsHBQaLzAIHiv76dQQHBwR1AQaKc40CMywsMwKNc1ZmAQE6IgwKCgwiOgEBZlZ67VwFBVztAAAAAAQAKP/DA9gDPQAYACEAKgAzAAAFMjY/ASEyNjURNCYnIQ4BFREUFjsBFRQWEw4BIiY0NjIWFw4BIiY0NjIWFw4BIiY0NjIWASYNFg+bASFgZGRg/dhgZGRgFBRZASU1JCM2JdcBJTQlJDUl1wEkNSQkNSQ9Dg6NZV8BSF9lAQFlX/64X2V8FRcB/xskJDUlJRobJCQ1JSUaGyQkNSUlAAACACb/mwPaA2UAIgAsAAAXAScmNwE+ATIWFwEWFA8BATY1ETYmJwEuASIGBwEOARcRFBchMjcBJiIHARYxARToDQ4BbhAYGRYRAW8HB+YBEgoBERf+pxgqLioX/qYXEQF6ArI/Gf6LGzQb/okVHwER4RALARwNDQ0N/uQFDwfh/vASLAGyIioTAQsTFhYT/vUTKiL+Ti1YFwFxGxv+jRUABQAm/5UD2gNrABMAIwApADAAOgAAFyEyNRE2JicBLgEiBgcBDgEXERQBLgEiBg8BLQE+ATIWFw0CETcXByYBERQHJzcWASIjATYyFwEiI60CpoYBGB7+tRcrLioX/rUeGQECMxYtLSwWHP7/AUIPFxsWDwFD/wD98gH28QYDMAXw9AH9EwQFASsbMhsBKgUFa4UBqi02FwEEExYWE/78FzYt/laFAZsWFBQWG/37DA0NDPz8+wG2DPTsDAHI/koRDe3xBP4BASYcHP7aAAAAAAIA1v/OAyoDMgAUABwAAAEiBgcVBhURFBYzITI2NRE0JzUuAQc+ATIWFxUhAgBdhQNFKyoBqiorRQOF/QJaiFoC/sADMoGDZwdS/rkuKysuAUhSB2aDgftaX19abwAAAAMAQf/UA74DLAAHABQAIAAABREnJicRFxYlMj8BEQYPAQYXERQWBTY/ATY1ESYnIg8BAnHUCw3YCv4MDxW0DAzLJwEaAlAGB+AlATAPFL4sAs2BBwP9KXkFCgthAtcFB3UVKv2uGBkMAQV/FSoCUzABC2kAAgFf/7oCoQNGABMAHAAABTI2NxE+ATcuAScOAQceARcRHgEDLgE0NjIWFAYCAA4ZATZCAQJaRUVaAgFDNQEZIBcfHy4fH0ZkYAGKD1U4RVsCAltFOVUO/nZgYwLgASAuHx8uIAAAAAADAGf/ywOZAzUAFQAeADgAACUyNjc1PgE3NC4CIw4BBx4BFxUeAQMiJjQ2MhYUBhM+ATcuAScVHgEXDgEHLgEnPgE3NQ4BBx4BAgAOGQE2QgEYLjsgRVoCAUM1ARkgFx8fLh8fF8TUAQjFYUSJBgKnlZWnAgaJRGHFCAHUamRfyg9VOSA7LhgCW0Q6VA/KX2QCISAuICAuIP1AA2xLV2EBQwE7MDRGAgFHNDA7AUMBYVdLbAACAEv/ywO1AzUACwAXAAAFPgE3LgEnDgEHHgETLgEnPgE3HgEXDgECALj4BQX4uLn3BQX4uDdMAQFMNzhLAQFLNQX4uLj4BQX4uLj4ASwBSzg3SwEBSzc4SwAAAAEAP/+/A8EDQQAfAAAFMjY3ATY0JiIHAQ4BFRQWHwEWNjcBNhYHAQ4BHwEeAQJXFyIMARkMGCse/R8cJCgf6BQbDgHWCQ4H/koMBAdDCRxBJR8C3R4rGAz+5QohFx0cCUYGAw0BtwcOCf4oDBwV4iEpAAAAAwAG//UD+gMLAAwAGAAsAAAXITI3ESYnISIHERYzEy4BJz4BNx4BFw4BAy4BPQE3NjMyHwE3NjIfARUUBgeNAuaGAQGG/RqGAQGGuy08AQE8LSw8AQE85h8jgx0eIB1SzSBJIMUjHwqEAgyEAYX99IUBgQE7LS08AQE8LS07/sMBIx8bchobSbYbHLZDHyIBAAAAAAQAAP/ABAADAgAOABoAIwA6AAATNDYzITU0IyEiFREUOwEXITI1ETQjISIXEQYBLgE0NjIWFAYDIiY9ATc+ATIWHwE3PgEyFh8BFQ4BI41VUwIJef21enoTrgJLenr9tXoBAQEPJzIyTDMzvhscQxokKCcaJX8fMDIxH2MBHBoB2VNUCnh4/mh3u3gBnHd3/mR4AUoBM0wzM0wz/vMdGiA+FxwcGCByHSMiHl5RGh0ABAAA/8AEAAMCABMAIQAzADwAADczFQYzITI1ETQrATU0IyEiFREUNyInETY3IRYXFSEiFxETNjMhMhcRJy4BDwEnJiMiDwE3PgE0JiIGFBZ6SAF6Akt6ekh5/bV6ezsCAjsCSTsB/jt6AT4BOwJJOwKOGkAbrkUZHBkZZdImNDRNMzN7Q3h4AZx3P3h4/mh3Pj0BkTwBATw8d/7lARc9Pf69hhcBGJo/FhZYqQE0TTQ0TTQAAgBL/8sDtQM1AAsAIAAABT4BNy4BJw4BBx4BNyImNDcTNjIXExYUBiIvASYiDwEGAgC4+AUF+Li59wUF+A4KDAOfCikJnwQMFQePBQoFjwc1Bfi4uPgFBfi4uPjVDBMIAZUZGf5rCBMMBo8GBo8GAAUAeP/EA4cDPAAIABQANQBBAEgAAAERLgEnDgEHFQEWMjY0JwEmIgYUFxMOARQWMyEyNjQmJyM1NjcnBgcuASc1NCYiBh0BHgEXFQE0JiIGBxUUBxc2NQcnFR4BFzICagFGOzlHAQHoChsTCv00ChsUCp4NExMNAZANExMNp1I7LjVLZnwCER0RAop3AUMRHBEBBTUP570BRjojAawBBj1MAQFKOgf9cgoUGgsCzAoUGwr9FwETGhMTGhMBXgcqLiMBAnxkZA8REQ9kd5oMXgHfDxERD2QZGDQvNnq9QzxNAQAAAAMAAP/4BAADBAAZAC4ARAAABTI2NRE0JiciBg8BBisBIgcVFhczMh8BHgElFjY3PgE0JicuAQ4BFx4BFAYHBhYnFjY3PgE0JicuAQcOARceARQGBwYWAdsXGxwWEBkRuwUGf1sBAVt/BgW7DxwBqwwbCiouLioKGxkDCiMnJyQJBJ4LGwoZHB0YChoMDgQLEhUVEwkDCBwWAqYWHQEPELEEYKVgAQSzDg1XCQYNO5allTwMBhEcDjOBjoEzDhxqBwUMIlxmXCINBQgKHA8ZRk1HGg4cAAAAAAIAhP+xA30DTgALAC4AAAE+ATIWFzU0JiIGFRcRBz8BPgEWFA8BBiIvASY0NjIfAScRIyYVERQ3ITInETYjAd4BExwTARQcFEUCG0EJHRILkA0aDZALEhwKXQPThoYB7IcBAYcCWA0TEw3VDhMTDrr+8T4dRQoBEhsKjAwMjAobEgtiPgEPAYX+R4UBhAG5hAAAAAADAJIApQNuAlsAAAAMAB4AABMzITIVERQjISI1ETQFNz4BHgEVERQOASYvASY9ATSSSQFuSUn+kkkCM20JFBQLCxQUCW0OAltJ/txJSQEkSYdYBgMJEgv+4gsSCQIHWAsRcBEAAgCE/5wDfQNkABoALgAAAT4BPQEnFxYyNjQvASYiDwEOARYyPwEHFRQWAyE2JxE2KwERDgEiJjURIyIVERQCAA4UAl0KGxIKkQwaDJEJAREcCl4DFOgB7IcBAYfKARkkGcuGAh4BEg6HP2MKERoJjAwMjAkaEQpkQIcOEv19AYQBmoX+zRIYGBIBM4X+ZoUAAwBF/+QDuwMcABwAKgA4AAA3MzI9AT4BNx4BFxUUOwEyNj0BLgEnIw4BBxUUFhczMjY9ATQmJyMGBxUWITMyPQE0JyMOAR0BFBZlFwsCzaqrzAILFw4SA+u+Hr7rAxKJKScrKycpKwEBAkMoLS0oKCoqjgvxm7MBAbOb8QsQDe+u0QMD0a7vDRCqKCW5JicBASv8LCz8KwEBJya5JSgAAAUAhP+vA3wDUQAfACkANgBDAE8AABsBHgEzITI2NxMzMjY0JicjNS4BKwEiBgcVIw4BFBYzNzQ2OwEyFh0BIxMuATUTPgEyFhUDDgEFLgE1AzQ2MhYVExQGNw4BIiYnET4BMhYV1RsCLSgBcigtAhwxDRISDbABMyudKjMBrw4SEg7pGBSOFBjm/gwQEgEQGBATAQ/+3gwPFBEYEBIPnAERGBABARAYEgJ0/Y4pKiopAnISHBIBPSwzMyw9ARIcEn4SFxcSPf1RARIOAfENEhIN/g4OEQEBEQ4B8g0SEg3+Dw4SIA4SEg4B8Q0SEg0AAAIAHP+5BAkDOwBBAFwAACU1MzI+AjcuAS8BNzY1LgEnIgYPAScmDgIfAQcOAQcUHgI7ARUjLgEnPgE3Jj4CFz4BNx4BFxQHHgEXDgEHBRY/ATY0JiIPATcRNCYiBhURFycmDgEUHwEWAonOFigfEAEBLiY6BgEDeFs2Xh4cMRYqIxMBATcpMwESIysZwsJRagIBUUABID5LJSd6S3ifAwE8TAECZEz+ug0MkQoSGwpdAhQcFANeChwQCpANp0UQICgWJzoJDjsKClt4AjMuKg4GCBsnFjkMC0ArGCwiEkUCa1BFZBAnRDAOCzpFAQOfeA0ND15ATGUC7QEMjAkbEApjQAFKDhMTDv62QGMLARAbCYwMAAIAHAADBAoDOwAgADwAACUVDgEmJzUhLgEnPgE3Jj4CFz4BNx4BFxQHHgEXDgEHASYPAQYUFjI/AgcVHgEyNjc1JxcWMjY0LwEmAjYBJCMB/uxQawIBUUABIT5KJiZ7S3ifAwI9SwICZUz+ugwNkAoRHAlBHQMBEx0TAQNeChsRCpEMp3sUFRUUewJrUEVkECdEMA4LOkUBA594DQ0PXkBMZQIBagEMjAkbEQtEH0C2DhMTDrZAYwsRGwmMDAAAAAIAHAAIBAoDOwA/AFoAACU1Mz4BNy4BLwE3NjUuASciBg8BJyYOAh8BBw4BFRQeAjsBFSMuASc+ATcmPgIXPgE3HgEXFAceARcOAQcBNh8BFhQGIi8BFxEOASImJxE3BwYiJjQ/ATYCis0vPgEBLiU6BQECeVo3Xh4cMRUrIxMBAjcqMxIiLBjDw1BrAgFRQAEhPkomJntLeJ8DAj1LAgJlTP66DQyRChEbCl4DARMdEwEDXgkcEQqQDadFAT4vJzoJDjsKClt4AjMuKg4GCBsnFjkMC0ArGCwiEkUCa1BFZBAnRDAOCzpFAQOfeA0ND15ATGUCAWoBDIwJGxELY0D+tg4TEw4BSkBjCxEbCYwMAAAAAAIAHP+5BAkDOwAjAD4AACU1NC4BIg4BHQEhLgEnPgE3Jj4CFz4BNx4BFxQHHgEXDgEHBRY/ATY0JiIPATc1NCYiBh0BFycmDgEUHwEWAj0MFBgUDP70UWoCAVFAASA+SyUnekt4nwMBPEwBAmRM/roNDJEKEhsKXQIUHBQDXgocEAqQDafxDBQMDBQM8QJrUEVkECdEMA4LOkUBA594DQ0PXkBMZQLtAQyMCRsQCmNATw4TEw5PQGMLARAbCYwMAAAAEgDeAAEAAAAAAAAAEwAoAAEAAAAAAAEACABOAAEAAAAAAAIABwBnAAEAAAAAAAMAFQCbAAEAAAAAAAQACADDAAEAAAAAAAUAOwFEAAEAAAAAAAYACAGSAAEAAAAAAAoAKwHzAAEAAAAAAAsAEwJHAAMAAQQJAAAAJgAAAAMAAQQJAAEAEAA8AAMAAQQJAAIADgBXAAMAAQQJAAMAKgBvAAMAAQQJAAQAEACxAAMAAQQJAAUAdgDMAAMAAQQJAAYAEAGAAAMAAQQJAAoAVgGbAAMAAQQJAAsAJgIfAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQAAENyZWF0ZWQgYnkgaWNvbmZvbnQAAHUAbgBpAGkAYwBvAG4AcwAAdW5paWNvbnMAAFIAZQBnAHUAbABhAHIAAFJlZ3VsYXIAAHUAbgBpAGkAYwBvAG4AcwA6AFYAZQByAHMAaQBvAG4AIAAxAC4AMAAwAAB1bmlpY29uczpWZXJzaW9uIDEuMDAAAHUAbgBpAGkAYwBvAG4AcwAAdW5paWNvbnMAAFYAZQByAHMAaQBvAG4AIAAxAC4AMAAwADsASgBhAG4AdQBhAHIAeQAgADMALAAgADIAMAAyADAAOwBGAG8AbgB0AEMAcgBlAGEAdABvAHIAIAAxADIALgAwAC4AMAAuADIANQAzADUAIAA2ADQALQBiAGkAdAAAVmVyc2lvbiAxLjAwO0phbnVhcnkgMywgMjAyMDtGb250Q3JlYXRvciAxMi4wLjAuMjUzNSA2NC1iaXQAAHUAbgBpAGkAYwBvAG4AcwAAdW5paWNvbnMAAEcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAAcwB2AGcAMgB0AHQAZgAgAGYAcgBvAG0AIABGAG8AbgB0AGUAbABsAG8AIABwAHIAbwBqAGUAYwB0AC4AAEdlbmVyYXRlZCBieSBzdmcydHRmIGZyb20gRm9udGVsbG8gcHJvamVjdC4AAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAGh0dHA6Ly9mb250ZWxsby5jb20AAAAAAAIAAAAAAAAACQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgAAAQIAAgEDAQQBBQEGAQcBCAEJAQoBCwEMAQ0BDgEPARABEQESARMBFAEVARYBFwEYARkBGgEbARwBHQEeAR8BIAEhASIBIwEkASUBJgEnAA4A7wEoASkBKgErASwBLQEuAS8BMAExATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVABUQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFtAW4BbwFwAXEBcgFzAXQBdQF2AXcBeAF5AXoBewF8AX0BfgF/AYABgQGCAYMHdW5pMDAwMAdjb250YWN0BnBlcnNvbglwZXJzb25hZGQNY29udGFjdGZpbGxlZAxwZXJzb25maWxsZWQPcGVyc29uYWRkZmlsbGVkBXBob25lBWVtYWlsCmNoYXRidWJibGUJY2hhdGJveGVzC3Bob25lZmlsbGVkC2VtYWlsZmlsbGVkEGNoYXRidWJibGVmaWxsZWQPY2hhdGJveGVzZmlsbGVkBXdlaWJvBndlaXhpbgtwZW5neW91cXVhbgRjaGF0AnFxCHZpZGVvY2FtBmNhbWVyYQNtaWMIbG9jYXRpb24JbWljZmlsbGVkDmxvY2F0aW9uZmlsbGVkBm1pY29mZgVpbWFnZQNtYXAHY29tcG9zZQV0cmFzaAZ1cGxvYWQIZG93bmxvYWQFY2xvc2UEcmVkbwR1bmRvB3JlZnJlc2gEc3Rhcgt3aGl0ZWNpcmNsZQVjbGVhcg1yZWZyZXNoZmlsbGVkCnN0YXJmaWxsZWQKcGx1c2ZpbGxlZAttaW51c2ZpbGxlZAxjaXJjbGVmaWxsZWQOY2hlY2tib3hmaWxsZWQKY2xvc2VlbXB0eQxyZWZyZXNoZW1wdHkGcmVsb2FkCHN0YXJoYWxmDHNwaW5uZXJjeWNsZQZzZWFyY2gJcGx1c2VtcHR5B2ZvcndhcmQEYmFjaw5jaGVja21hcmtlbXB0eQRob21lCG5hdmlnYXRlBGdlYXIKcGFwZXJwbGFuZQRpbmZvBGhlbHAGbG9ja2VkBG1vcmUEZmxhZwpob21lZmlsbGVkCmdlYXJmaWxsZWQKaW5mb2ZpbGxlZApoZWxwZmlsbGVkCm1vcmVmaWxsZWQIc2V0dGluZ3MEbGlzdARiYXJzBGxvb3AJcGFwZXJjbGlwCWV5ZWZpbGxlZAx1cHdhcmRzYXJyb3cOZG93bndhcmRzYXJyb3cObGVmdHdhcmRzYXJyb3cPcmlnaHR3YXJkc2Fycm93C2Fycm93dGhpbnVwDWFycm93dGhpbmRvd24NYXJyb3d0aGlubGVmdA5hcnJvd3RoaW5yaWdodAhwdWxsZG93bgVzb3VuZARzaG9wBHNjYW4KdW5kb2ZpbGxlZApyZWRvZmlsbGVkDGNhbWVyYWZpbGxlZApjYXJ0ZmlsbGVkBGNhcnQIY2hlY2tib3gRc21hbGxjaXJjbGVmaWxsZWQOZXllc2xhc2hmaWxsZWQIZXllc2xhc2gDZXllCmZsYWdmaWxsZWQVaGFuZHRodW1ic2Rvd25fZmlsbGVkDmhhbmR0aHVtYnNkb3duEmhhbmR0aHVtYnN1cGZpbGxlZAtoZWFydGZpbGxlZAxoYW5kdGh1bWJzdXAOYmxhY2toZWFydHN1aXQKY2hhdGZpbGxlZA5tYWlsb3BlbmZpbGxlZAhtYWlsb3Blbgxsb2NrZWRmaWxsZWQJbWFwZmlsbGVkBm1hcHBpbg1tYXBwaW5lbGxpcHNlC3NtYWxsY2lyY2xlEHBhcGVycGxhbmVmaWxsZWQLaW1hZ2VmaWxsZWQMaW1hZ2VzZmlsbGVkBmltYWdlcw5uYXZpZ2F0ZWZpbGxlZA5taWNzbGFzaGZpbGxlZAtzb3VuZGZpbGxlZA5kb3dubG9hZGZpbGxlZA52aWRlb2NhbWZpbGxlZAx1cGxvYWRmaWxsZWQKaGVhZHBob25lcwt0cmFzaGZpbGxlZA1jbG91ZGRvd25sb2FkEWNsb3VkdXBsb2FkZmlsbGVkC2Nsb3VkdXBsb2FkE2Nsb3VkZG93bmxvYWRmaWxsZWQHdW5pMDAwOQAAAAAB//8AAgABAAAADAAAABYAAAACAAEAAQCFAAEABAAAAAIAAAAAAAAAAQAAAADVpCcIAAAAANoxE3MAAAAA2jSpUA==')" |
| | | }); |
| | | // #endif |
| | | |
| | | /** |
| | | * Icons 图标 |
| | | * @description 用于展示 icons 图标 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=28 |
| | | * @property {Number} size 图标大小 |
| | | * @property {String} type 图标图案,参考示例 |
| | | * @property {String} color 图标颜色 |
| | | * @event {Function} click 点击 Icon 触发事件 |
| | | */ |
| | | export default { |
| | | name: 'UniIcons', |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#333333' |
| | | }, |
| | | size: { |
| | | type: [Number, String], |
| | | default: 16 |
| | | }, |
| | | customIcons:{ |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | icons: icons |
| | | } |
| | | }, |
| | | methods: { |
| | | _onClick() { |
| | | this.$emit('click') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | /* #ifndef APP-NVUE */ |
| | | @font-face { |
| | | font-family: uniicons; |
| | | src: url('./uni.ttf') format('truetype'); |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | .uni-icons { |
| | | font-family: uniicons; |
| | | text-decoration: none; |
| | | text-align: center; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view> |
| | | <view v-if="loaded || list.itemIndex < 15" class="uni-indexed-list__title-wrapper"> |
| | | <text v-if="list.items && list.items.length > 0" class="uni-indexed-list__title">{{ list.key }}</text> |
| | | </view> |
| | | <view v-if="(loaded || list.itemIndex < 15) && list.items && list.items.length > 0" class="uni-indexed-list__list"> |
| | | <view v-for="(item, index) in list.items" :key="index" class="uni-indexed-list__item" hover-class="uni-indexed-list__item--hover"> |
| | | <view class="uni-indexed-list__item-container" @click="onClick(idx, index)"> |
| | | <view class="uni-indexed-list__item-border" :class="{'uni-indexed-list__item-border--last':index===list.items.length-1}"> |
| | | <view v-if="showSelect" style="margin-right: 20rpx;"> |
| | | <uni-icons :type="item.checked ? 'checkbox-filled' : 'circle'" :color="item.checked ? '#007aff' : '#aaa'" size="24" /> |
| | | </view> |
| | | <text class="uni-indexed-list__item-content">{{ item.name }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from '../uni-icons/uni-icons.vue' |
| | | export default { |
| | | name: 'UniIndexedList', |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | props: { |
| | | loaded: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | idx: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | list: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | showSelect: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick(idx, index) { |
| | | this.$emit("itemClick", { |
| | | idx, |
| | | index |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-indexed-list__list { |
| | | background-color: $uni-bg-color; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | border-top-color: $uni-border-color; |
| | | } |
| | | |
| | | .uni-indexed-list__item { |
| | | font-size: $uni-font-size-lg; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-indexed-list__item-container { |
| | | padding-left: $uni-spacing-row-lg; |
| | | flex: 1; |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-indexed-list__item-border { |
| | | flex: 1; |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | height: 50px; |
| | | padding: $uni-spacing-row-lg; |
| | | padding-left: 0; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | border-bottom-color: $uni-border-color; |
| | | } |
| | | |
| | | .uni-indexed-list__item-border--last { |
| | | border-bottom-width: 0px; |
| | | } |
| | | |
| | | .uni-indexed-list__item-content { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-indexed-list { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-indexed-list__title-wrapper { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | width: 100%; |
| | | /* #endif */ |
| | | background-color: #f7f7f7; |
| | | } |
| | | |
| | | .uni-indexed-list__title { |
| | | padding: 6px 12px; |
| | | line-height: 24px; |
| | | font-size: $uni-font-size-sm; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-indexed-list" ref="list" id="list"> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false"> |
| | | <cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx"> |
| | | <!-- #endif --> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y> |
| | | <view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx"> |
| | | <!-- #endif --> |
| | | <uni-indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect" @itemClick="onClick"></uni-indexed-list-item> |
| | | <!-- #ifndef APP-NVUE --> |
| | | </view> |
| | | </scroll-view> |
| | | <!-- #endif --> |
| | | <!-- #ifdef APP-NVUE --> |
| | | </cell> |
| | | </list> |
| | | <!-- #endif --> |
| | | <view :class="touchmove ? 'uni-indexed-list__menu--active' : ''" @touchstart="touchStart" @touchmove.stop.prevent="touchMove" |
| | | @touchend="touchEnd" class="uni-indexed-list__menu"> |
| | | <view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item"> |
| | | <text class="uni-indexed-list__menu-text" :class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text> |
| | | </view> |
| | | </view> |
| | | <view v-if="touchmove" class="uni-indexed-list__alert-wrapper"> |
| | | <text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import uniIcons from '../uni-icons/uni-icons.vue' |
| | | import uniIndexedListItem from './uni-indexed-list-item.vue' |
| | | // #ifdef APP-NVUE |
| | | const dom = weex.requireModule('dom'); |
| | | // #endif |
| | | // #ifdef APP-PLUS |
| | | function throttle(func, delay) { |
| | | var prev = Date.now(); |
| | | return function() { |
| | | var context = this; |
| | | var args = arguments; |
| | | var now = Date.now(); |
| | | if (now - prev >= delay) { |
| | | func.apply(context, args); |
| | | prev = Date.now(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | function touchMove(e) { |
| | | let pageY = e.touches[0].pageY |
| | | let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight) |
| | | if (this.touchmoveIndex === index) { |
| | | return false |
| | | } |
| | | let item = this.lists[index] |
| | | if (item) { |
| | | // #ifndef APP-NVUE |
| | | this.scrollViewId = 'uni-indexed-list-' + index |
| | | this.touchmoveIndex = index |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], { |
| | | animated: false |
| | | }) |
| | | this.touchmoveIndex = index |
| | | // #endif |
| | | } |
| | | } |
| | | const throttleTouchMove = throttle(touchMove, 40) |
| | | // #endif |
| | | |
| | | /** |
| | | * IndexedList 索引列表 |
| | | * @description 用于展示索引列表 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=375 |
| | | * @property {Boolean} showSelect = [true|false] 展示模式 |
| | | * @value true 展示模式 |
| | | * @value false 选择模式 |
| | | * @property {Object} options 索引列表需要的数据对象 |
| | | * @event {Function} click 点击列表事件 ,返回当前选择项的事件对象 |
| | | * @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list> |
| | | */ |
| | | export default { |
| | | name: 'UniIndexedList', |
| | | components: { |
| | | uniIcons, |
| | | uniIndexedListItem |
| | | }, |
| | | props: { |
| | | options: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | showSelect: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | lists: [], |
| | | winHeight: 0, |
| | | itemHeight: 0, |
| | | winOffsetY: 0, |
| | | touchmove: false, |
| | | touchmoveIndex: -1, |
| | | scrollViewId: '', |
| | | touchmoveTimeout: '', |
| | | loaded: false |
| | | } |
| | | }, |
| | | watch: { |
| | | options: { |
| | | handler: function() { |
| | | this.setList() |
| | | }, |
| | | deep: true |
| | | } |
| | | }, |
| | | mounted() { |
| | | setTimeout(() => { |
| | | this.setList() |
| | | }, 50) |
| | | setTimeout(() => { |
| | | this.loaded = true |
| | | }, 300); |
| | | }, |
| | | methods: { |
| | | setList() { |
| | | let index = 0; |
| | | this.lists = [] |
| | | this.options.forEach((value, index) => { |
| | | if (value.data.length === 0) { |
| | | return |
| | | } |
| | | let indexBefore = index |
| | | let items = value.data.map(item => { |
| | | let obj = {} |
| | | obj['key'] = value.letter |
| | | obj['name'] = item |
| | | obj['itemIndex'] = index |
| | | index++ |
| | | obj.checked = item.checked ? item.checked : false |
| | | return obj |
| | | }) |
| | | this.lists.push({ |
| | | title: value.letter, |
| | | key: value.letter, |
| | | items: items, |
| | | itemIndex: indexBefore |
| | | }) |
| | | }) |
| | | // #ifndef APP-NVUE |
| | | uni.createSelectorQuery() |
| | | .in(this) |
| | | .select('#list') |
| | | .boundingClientRect() |
| | | .exec(ret => { |
| | | this.winOffsetY = ret[0].top |
| | | this.winHeight = ret[0].height |
| | | this.itemHeight = this.winHeight / this.lists.length |
| | | }) |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | dom.getComponentRect(this.$refs['list'], (res) => { |
| | | this.winOffsetY = res.size.top |
| | | this.winHeight = res.size.height |
| | | this.itemHeight = this.winHeight / this.lists.length |
| | | }) |
| | | // #endif |
| | | }, |
| | | touchStart(e) { |
| | | this.touchmove = true |
| | | let pageY = e.touches[0].pageY |
| | | let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight) |
| | | let item = this.lists[index] |
| | | if (item) { |
| | | this.scrollViewId = 'uni-indexed-list-' + index |
| | | this.touchmoveIndex = index |
| | | // #ifdef APP-NVUE |
| | | dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], { |
| | | animated: false |
| | | }) |
| | | // #endif |
| | | } |
| | | }, |
| | | touchMove(e) { |
| | | // #ifndef APP-PLUS |
| | | let pageY = e.touches[0].pageY |
| | | let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight) |
| | | if (this.touchmoveIndex === index) { |
| | | return false |
| | | } |
| | | let item = this.lists[index] |
| | | if (item) { |
| | | this.scrollViewId = 'uni-indexed-list-' + index |
| | | this.touchmoveIndex = index |
| | | } |
| | | // #endif |
| | | // #ifdef APP-PLUS |
| | | throttleTouchMove.call(this, e) |
| | | // #endif |
| | | }, |
| | | touchEnd() { |
| | | this.touchmove = false |
| | | this.touchmoveIndex = -1 |
| | | }, |
| | | onClick(e) { |
| | | let { |
| | | idx, |
| | | index |
| | | } = e |
| | | let obj = {} |
| | | for (let key in this.lists[idx].items[index]) { |
| | | obj[key] = this.lists[idx].items[index][key] |
| | | } |
| | | let select = [] |
| | | if (this.showSelect) { |
| | | this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked |
| | | this.lists.forEach((value, idx) => { |
| | | value.items.forEach((item, index) => { |
| | | if (item.checked) { |
| | | let obj = {} |
| | | for (let key in this.lists[idx].items[index]) { |
| | | obj[key] = this.lists[idx].items[index][key] |
| | | } |
| | | select.push(obj) |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | this.$emit('click', { |
| | | item: obj, |
| | | select: select |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .uni-indexed-list { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-indexed-list__scroll { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-indexed-list__menu { |
| | | width: 24px; |
| | | background-color: lightgrey; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-indexed-list__menu-item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .uni-indexed-list__menu-text { |
| | | line-height: 20px; |
| | | font-size: 12px; |
| | | text-align: center; |
| | | color: #aaa; |
| | | } |
| | | |
| | | .uni-indexed-list__menu--active { |
| | | background-color: rgb(200, 200, 200); |
| | | } |
| | | |
| | | .uni-indexed-list__menu-text--active { |
| | | color: #007aff; |
| | | } |
| | | |
| | | .uni-indexed-list__alert-wrapper { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .uni-indexed-list__alert { |
| | | width: 80px; |
| | | height: 80px; |
| | | border-radius: 80px; |
| | | text-align: center; |
| | | line-height: 80px; |
| | | font-size: 35px; |
| | | color: #fff; |
| | | background-color: rgba(0, 0, 0, 0.5); |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <text class="uni-link" :class="{'uni-link--withline':showUnderLine===true||showUnderLine==='true'}" :style="{color,fontSize:fontSize+'px'}" |
| | | @click="openURL">{{text}}</text> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Link 外部网页超链接组件 |
| | | * @description uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打开新网页 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=1182 |
| | | * @property {String} href 点击后打开的外部网页url |
| | | * @property {String} text 显示的文字 |
| | | * @property {Boolean} showUnderLine 是否显示下划线 |
| | | * @property {String} copyTips 在小程序端复制链接时显示的提示语 |
| | | * @property {String} color 链接文字颜色 |
| | | * @property {String} fontSize 链接文字大小 |
| | | * @example * <uni-link href="https://ext.dcloud.net.cn" text="https://ext.dcloud.net.cn"></uni-link> |
| | | */ |
| | | export default { |
| | | name: 'uniLink', |
| | | props: { |
| | | href: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | text: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | showUnderLine: { |
| | | type: [Boolean, String], |
| | | default: true |
| | | }, |
| | | copyTips: { |
| | | type: String, |
| | | default: '已自动复制网址,请在手机浏览器里粘贴该网址' |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#999999' |
| | | }, |
| | | fontSize: { |
| | | type: [Number, String], |
| | | default: 14 |
| | | } |
| | | }, |
| | | methods: { |
| | | openURL() { |
| | | // #ifdef APP-PLUS |
| | | plus.runtime.openURL(this.href) |
| | | // #endif |
| | | // #ifdef H5 |
| | | window.open(this.href) |
| | | // #endif |
| | | // #ifdef MP |
| | | uni.setClipboardData({ |
| | | data: this.href |
| | | }); |
| | | uni.showModal({ |
| | | content: this.copyTips, |
| | | showCancel: false |
| | | }); |
| | | // #endif |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-link { |
| | | cursor: pointer; |
| | | } |
| | | /* #endif */ |
| | | .uni-link--withline { |
| | | text-decoration: underline; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <cell> |
| | | <!-- #endif --> |
| | | <view class="uni-list-ad"> |
| | | <view v-if="borderShow" :class="{'uni-list--border':border,'uni-list-item--first':isFirstChild}"></view> |
| | | <ad style="width: 200px;height: 300px;border-width: 1px;border-color: red;border-style: solid;" adpid="1111111111" |
| | | unit-id="" appid="" apid="" type="feed" @error="aderror" @close="closeAd"></ad> |
| | | </view> |
| | | <!-- #ifdef APP-NVUE --> |
| | | </cell> |
| | | <!-- #endif --> |
| | | |
| | | </template> |
| | | |
| | | <script> |
| | | // #ifdef APP-NVUE |
| | | const dom = uni.requireNativePlugin('dom'); |
| | | // #endif |
| | | export default { |
| | | name: 'UniListAd', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '', |
| | | |
| | | } |
| | | }, |
| | | // inject: ['list'], |
| | | data() { |
| | | return { |
| | | isFirstChild: false, |
| | | border: false, |
| | | borderShow: true, |
| | | } |
| | | }, |
| | | |
| | | mounted() { |
| | | this.list = this.getForm() |
| | | if (this.list) { |
| | | if (!this.list.firstChildAppend) { |
| | | this.list.firstChildAppend = true |
| | | this.isFirstChild = true |
| | | } |
| | | this.border = this.list.border |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getForm(name = 'uniList') { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== name) { |
| | | parent = parent.$parent; |
| | | if (!parent) return false |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | }, |
| | | aderror(e) { |
| | | console.log("aderror: " + JSON.stringify(e.detail)); |
| | | }, |
| | | closeAd(e) { |
| | | this.borderShow = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-list-ad { |
| | | position: relative; |
| | | border: 1px red solid; |
| | | } |
| | | |
| | | .uni-list--border { |
| | | position: relative; |
| | | padding-bottom: 1px; |
| | | /* #ifdef APP-PLUS */ |
| | | border-top-color: $uni-border-color; |
| | | border-top-style: solid; |
| | | border-top-width: 0.5px; |
| | | /* #endif */ |
| | | margin-left: $uni-spacing-row-lg; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-list--border:after { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | left: 0; |
| | | height: 1px; |
| | | content: ''; |
| | | -webkit-transform: scaleY(.5); |
| | | transform: scaleY(.5); |
| | | background-color: $uni-border-color; |
| | | } |
| | | |
| | | .uni-list-item--first:after { |
| | | height: 0px; |
| | | } |
| | | |
| | | /* #endif */ |
| | | </style> |
New file |
| | |
| | | /** |
| | | * 这里是 uni-list 组件内置的常用样式变量 |
| | | * 如果需要覆盖样式,这里提供了基本的组件样式变量,您可以尝试修改这里的变量,去完成样式替换,而不用去修改源码 |
| | | * |
| | | */ |
| | | |
| | | // 背景色 |
| | | $background-color : #fff; |
| | | // 分割线颜色 |
| | | $divide-line-color : #e5e5e5; |
| | | |
| | | // 默认头像大小,如需要修改此值,注意同步修改 js 中的值 const avatarWidth = xx ,目前只支持方形头像 |
| | | // nvue 页面不支持修改头像大小 |
| | | $avatar-width : 45px ; |
| | | |
| | | // 头像边框 |
| | | $avatar-border-radius: 5px; |
| | | $avatar-border-color: #eee; |
| | | $avatar-border-width: 1px; |
| | | |
| | | // 标题文字样式 |
| | | $title-size : 16px; |
| | | $title-color : #3b4144; |
| | | $title-weight : normal; |
| | | |
| | | // 描述文字样式 |
| | | $note-size : 12px; |
| | | $note-color : #999; |
| | | $note-weight : normal; |
| | | |
| | | // 右侧额外内容默认样式 |
| | | $right-text-size : 12px; |
| | | $right-text-color : #999; |
| | | $right-text-weight : normal; |
| | | |
| | | // 角标样式 |
| | | // nvue 页面不支持修改圆点位置以及大小 |
| | | // 角标在左侧时,角标的位置,默认为 0 ,负数左/下移动,正数右/上移动 |
| | | $badge-left: 0px; |
| | | $badge-top: 0px; |
| | | |
| | | // 显示圆点时,圆点大小 |
| | | $dot-width: 10px; |
| | | $dot-height: 10px; |
| | | |
| | | // 显示角标时,角标大小和字体大小 |
| | | $badge-size : 18px; |
| | | $badge-font : 12px; |
| | | // 显示角标时,角标前景色 |
| | | $badge-color : #fff; |
| | | // 显示角标时,角标背景色 |
| | | $badge-background-color : #ff5a5f; |
| | | // 显示角标时,角标左右间距 |
| | | $badge-space : 6px; |
| | | |
| | | // 状态样式 |
| | | // 选中颜色 |
| | | $hover : #f5f5f5; |
New file |
| | |
| | | <template> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <cell> |
| | | <!-- #endif --> |
| | | <view :hover-class="!clickable && !link ? '' : 'uni-list-chat--hover'" class="uni-list-chat" @click.stop="onClick"> |
| | | <view :class="{ 'uni-list--border': border, 'uni-list-chat--first': isFirstChild }"></view> |
| | | <view class="uni-list-chat__container"> |
| | | <view class="uni-list-chat__header-warp"> |
| | | <view v-if="avatarCircle || avatarList.length === 0" class="uni-list-chat__header" :class="{ 'header--circle': avatarCircle }"> |
| | | <image class="uni-list-chat__header-image" :src="avatar" mode="aspectFill"></image> |
| | | </view> |
| | | <!-- 头像组 --> |
| | | <view v-else class="uni-list-chat__header"> |
| | | <view v-for="(item, index) in avatarList" :key="index" class="uni-list-chat__header-box" :class="computedAvatar" |
| | | :style="{ width: imageWidth + 'px', height: imageWidth + 'px' }"> |
| | | <image class="uni-list-chat__header-image" :style="{ width: imageWidth + 'px', height: imageWidth + 'px' }" :src="item.url" |
| | | mode="aspectFill"></image> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="badgeText && badgePositon === 'left'" class="uni-list-chat__badge uni-list-chat__badge-pos" :class="[isSingle]"> |
| | | <text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text> |
| | | </view> |
| | | <view class="uni-list-chat__content"> |
| | | <view class="uni-list-chat__content-main"> |
| | | <text class="uni-list-chat__content-title uni-ellipsis">{{ title }}</text> |
| | | <text class="uni-list-chat__content-note uni-ellipsis">{{ note }}</text> |
| | | </view> |
| | | <view class="uni-list-chat__content-extra"> |
| | | <slot> |
| | | <text class="uni-list-chat__content-extra-text">{{ time }}</text> |
| | | <view v-if="badgeText && badgePositon === 'right'" class="uni-list-chat__badge" :class="[isSingle, badgePositon === 'right' ? 'uni-list-chat--right' : '']"> |
| | | <text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- #ifdef APP-NVUE --> |
| | | </cell> |
| | | <!-- #endif --> |
| | | </template> |
| | | |
| | | <script> |
| | | // 头像大小 |
| | | const avatarWidth = 45; |
| | | |
| | | /** |
| | | * ListChat 聊天列表 |
| | | * @description 聊天列表,用于创建聊天类列表 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=24 |
| | | * @property {String} title 标题 |
| | | * @property {String} note 描述 |
| | | * @property {Boolean} clickable = [true|false] 是否开启点击反馈,默认为false |
| | | * @property {String} badgeText 数字角标内容 |
| | | * @property {String} badgePositon = [left|right] 角标位置,默认为 right |
| | | * @property {String} link = [false|navigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈,默认为false |
| | | * @value false 不开启 |
| | | * @value navigateTo 同 uni.navigateTo() |
| | | * @value redirectTo 同 uni.redirectTo() |
| | | * @value reLaunch 同 uni.reLaunch() |
| | | * @value switchTab 同 uni.switchTab() |
| | | * @property {String | PageURIString} to 跳转目标页面 |
| | | * @property {String} time 右侧时间显示 |
| | | * @property {Boolean} avatarCircle = [true|false] 是否显示圆形头像,默认为false |
| | | * @property {String} avatar 头像地址,avatarCircle 不填时生效 |
| | | * @property {Array} avatarList 头像组,格式为 [{url:''}] |
| | | * @event {Function} click 点击 uniListChat 触发事件 |
| | | */ |
| | | export default { |
| | | name: 'UniListChat', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | note: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | clickable: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | link: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | to: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | badgeText: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | badgePositon: { |
| | | type: String, |
| | | default: 'right' |
| | | }, |
| | | time: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | avatarCircle: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | avatar: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | avatarList: { |
| | | type: Array, |
| | | default () { |
| | | return []; |
| | | } |
| | | } |
| | | }, |
| | | // inject: ['list'], |
| | | computed: { |
| | | isSingle() { |
| | | if (this.badgeText === 'dot') { |
| | | return 'uni-badge--dot'; |
| | | } else { |
| | | const badgeText = this.badgeText.toString(); |
| | | if (badgeText.length > 1) { |
| | | return 'uni-badge--complex'; |
| | | } else { |
| | | return 'uni-badge--single'; |
| | | } |
| | | } |
| | | }, |
| | | computedAvatar() { |
| | | if (this.avatarList.length > 4) { |
| | | this.imageWidth = avatarWidth * 0.31; |
| | | return 'avatarItem--3'; |
| | | } else if (this.avatarList.length > 1) { |
| | | this.imageWidth = avatarWidth * 0.47; |
| | | return 'avatarItem--2'; |
| | | } else { |
| | | this.imageWidth = avatarWidth; |
| | | return 'avatarItem--1'; |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | isFirstChild: false, |
| | | border: true, |
| | | // avatarList: 3, |
| | | imageWidth: 50 |
| | | }; |
| | | }, |
| | | mounted() { |
| | | this.list = this.getForm() |
| | | if (this.list) { |
| | | if (!this.list.firstChildAppend) { |
| | | this.list.firstChildAppend = true; |
| | | this.isFirstChild = true; |
| | | } |
| | | this.border = this.list.border; |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getForm(name = 'uniList') { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== name) { |
| | | parent = parent.$parent; |
| | | if (!parent) return false |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | }, |
| | | onClick() { |
| | | if (this.to !== '') { |
| | | this.openPage(); |
| | | return; |
| | | } |
| | | |
| | | if (this.clickable || this.link) { |
| | | this.$emit('click', { |
| | | data: {} |
| | | }); |
| | | } |
| | | }, |
| | | openPage() { |
| | | if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].indexOf(this.link) !== -1) { |
| | | this.pageApi(this.link); |
| | | } else { |
| | | this.pageApi('navigateTo'); |
| | | } |
| | | }, |
| | | pageApi(api) { |
| | | uni[api]({ |
| | | url: this.to, |
| | | success: res => { |
| | | this.$emit('click', { |
| | | data: res |
| | | }); |
| | | }, |
| | | fail: err => { |
| | | this.$emit('click', { |
| | | data: err |
| | | }); |
| | | console.error(err.errMsg); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $background-color: #fff; |
| | | $divide-line-color: #e5e5e5; |
| | | $avatar-width: 45px; |
| | | $avatar-border-radius: 5px; |
| | | $avatar-border-color: #eee; |
| | | $avatar-border-width: 1px; |
| | | $title-size: 16px; |
| | | $title-color: #3b4144; |
| | | $title-weight: normal; |
| | | $note-size: 12px; |
| | | $note-color: #999; |
| | | $note-weight: normal; |
| | | $right-text-size: 12px; |
| | | $right-text-color: #999; |
| | | $right-text-weight: normal; |
| | | $badge-left: 0px; |
| | | $badge-top: 0px; |
| | | $dot-width: 10px; |
| | | $dot-height: 10px; |
| | | $badge-size: 18px; |
| | | $badge-font: 12px; |
| | | $badge-color: #fff; |
| | | $badge-background-color: #ff5a5f; |
| | | $badge-space: 6px; |
| | | $hover: #f5f5f5; |
| | | |
| | | .uni-list-chat { |
| | | font-size: $uni-font-size-lg; |
| | | position: relative; |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | background-color: $background-color; |
| | | } |
| | | |
| | | // .uni-list-chat--disabled { |
| | | // opacity: 0.3; |
| | | // } |
| | | |
| | | .uni-list-chat--hover { |
| | | background-color: $hover; |
| | | } |
| | | |
| | | .uni-list--border { |
| | | position: relative; |
| | | margin-left: $uni-spacing-row-lg; |
| | | /* #ifdef APP-PLUS */ |
| | | border-top-color: $divide-line-color; |
| | | border-top-style: solid; |
| | | border-top-width: 0.5px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-list--border:after { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | left: 0; |
| | | height: 1px; |
| | | content: ''; |
| | | -webkit-transform: scaleY(0.5); |
| | | transform: scaleY(0.5); |
| | | background-color: $divide-line-color; |
| | | } |
| | | |
| | | .uni-list-item--first:after { |
| | | height: 0px; |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | .uni-list-chat--first { |
| | | border-top-width: 0px; |
| | | } |
| | | |
| | | .uni-ellipsis { |
| | | /* #ifndef APP-NVUE */ |
| | | overflow: hidden; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 1; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-ellipsis-2 { |
| | | /* #ifndef APP-NVUE */ |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | display: -webkit-box; |
| | | -webkit-line-clamp: 2; |
| | | -webkit-box-orient: vertical; |
| | | /* #endif */ |
| | | |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 2; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-list-chat__container { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex: 1; |
| | | padding: $uni-spacing-row-base $uni-spacing-row-lg; |
| | | position: relative; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-list-chat__header-warp { |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-list-chat__header { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | align-content: center; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | flex-wrap: wrap-reverse; |
| | | /* #ifdef APP-NVUE */ |
| | | width: 50px; |
| | | height: 50px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | width: $avatar-width; |
| | | height: $avatar-width; |
| | | /* #endif */ |
| | | |
| | | border-radius: $avatar-border-radius; |
| | | border-color: $avatar-border-color; |
| | | border-width: $avatar-border-width; |
| | | border-style: solid; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-list-chat__header-box { |
| | | /* #ifndef APP-PLUS */ |
| | | box-sizing: border-box; |
| | | display: flex; |
| | | width: $avatar-width; |
| | | height: $avatar-width; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | width: 50px; |
| | | height: 50px; |
| | | /* #endif */ |
| | | overflow: hidden; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .uni-list-chat__header-image { |
| | | margin: 1px; |
| | | /* #ifdef APP-NVUE */ |
| | | width: 50px; |
| | | height: 50px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | width: $avatar-width; |
| | | height: $avatar-width; |
| | | /* #endif */ |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-list-chat__header-image { |
| | | display: block; |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .avatarItem--1 { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .avatarItem--2 { |
| | | width: 47%; |
| | | height: 47%; |
| | | } |
| | | |
| | | .avatarItem--3 { |
| | | width: 32%; |
| | | height: 32%; |
| | | } |
| | | |
| | | /* #endif */ |
| | | .header--circle { |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .uni-list-chat__content { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex: 1; |
| | | overflow: hidden; |
| | | padding: 2px 0; |
| | | } |
| | | |
| | | .uni-list-chat__content-main { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | padding-left: $uni-spacing-row-base; |
| | | flex: 1; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-list-chat__content-title { |
| | | font-size: $title-size; |
| | | color: $title-color; |
| | | font-weight: $title-weight; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-list-chat__content-note { |
| | | margin-top: 3px; |
| | | color: $note-color; |
| | | font-size: $note-size; |
| | | font-weight: $title-weight; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-list-chat__content-extra { |
| | | /* #ifndef APP-NVUE */ |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | align-items: flex-end; |
| | | margin-left: 5px; |
| | | } |
| | | |
| | | .uni-list-chat__content-extra-text { |
| | | color: $right-text-color; |
| | | font-size: $right-text-size; |
| | | font-weight: $right-text-weight; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-list-chat__badge-pos { |
| | | position: absolute; |
| | | /* #ifdef APP-NVUE */ |
| | | left: 55px; |
| | | top: 3px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | left: calc(#{$avatar-width} + 10px - #{$badge-space} + #{$badge-left}); |
| | | top: calc(#{$uni-spacing-row-base}/ 2 + 1px + #{$badge-top}); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-list-chat__badge { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | border-radius: 100px; |
| | | background-color: $badge-background-color; |
| | | } |
| | | |
| | | .uni-list-chat__badge-text { |
| | | color: $badge-color; |
| | | font-size: $badge-font; |
| | | } |
| | | |
| | | .uni-badge--single { |
| | | /* #ifndef APP-NVUE */ |
| | | // left: calc(#{$avatar-width} + 7px + #{$badge-left}); |
| | | /* #endif */ |
| | | width: $badge-size; |
| | | height: $badge-size; |
| | | } |
| | | |
| | | .uni-badge--complex { |
| | | /* #ifdef APP-NVUE */ |
| | | left: 50px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | width: auto; |
| | | /* #endif */ |
| | | height: $badge-size; |
| | | padding: 0 $badge-space; |
| | | } |
| | | |
| | | .uni-badge--dot { |
| | | /* #ifdef APP-NVUE */ |
| | | left: 60px; |
| | | top: 6px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | left: calc(#{$avatar-width} + 15px - #{$dot-width}/ 2 + 1px + #{$badge-left}); |
| | | /* #endif */ |
| | | width: $dot-width; |
| | | height: $dot-height; |
| | | padding: 0; |
| | | } |
| | | |
| | | .uni-list-chat--right { |
| | | /* #ifdef APP-NVUE */ |
| | | left: 0; |
| | | /* #endif */ |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <cell> |
| | | <!-- #endif --> |
| | | |
| | | <view |
| | | :class="{ 'uni-list-item--disabled': disabled }" |
| | | :hover-class="(!clickable && !link) || disabled || showSwitch ? '' : 'uni-list-item--hover'" |
| | | class="uni-list-item" |
| | | @click.stop="onClick" |
| | | > |
| | | <view v-if="!isFirstChild" class="border--left" :class="{ 'uni-list--border': border }"></view> |
| | | <view class="uni-list-item__container" :class="{ 'container--right': showArrow || link, 'flex--direction': direction === 'column' }"> |
| | | <slot name="header"> |
| | | <view class="uni-list-item__header"> |
| | | <view v-if="thumb" class="uni-list-item__icon"><image :src="thumb" class="uni-list-item__icon-img" :class="['uni-list--' + thumbSize]" /></view> |
| | | <view v-else-if="showExtraIcon" class="uni-list-item__icon"><uni-icons :color="extraIcon.color" :size="extraIcon.size" :type="extraIcon.type" /></view> |
| | | </view> |
| | | </slot> |
| | | <slot name="body"> |
| | | <view class="uni-list-item__content" :class="{ 'uni-list-item__content--center': thumb || showExtraIcon || showBadge || showSwitch }"> |
| | | <text v-if="title" class="uni-list-item__content-title" :class="[ellipsis !== 0 && ellipsis <= 2 ? 'uni-ellipsis-' + ellipsis : '']">{{ title }}</text> |
| | | <text v-if="note" class="uni-list-item__content-note">{{ note }}</text> |
| | | </view> |
| | | </slot> |
| | | <slot name="footer"> |
| | | <view v-if="rightText || showBadge || showSwitch" class="uni-list-item__extra" :class="{ 'flex--justify': direction === 'column' }"> |
| | | <text v-if="rightText" class="uni-list-item__extra-text">{{ rightText }}</text> |
| | | <uni-badge v-if="showBadge" :type="badgeType" :text="badgeText" /> |
| | | <switch v-if="showSwitch" :disabled="disabled" :checked="switchChecked" @change="onSwitchChange" /> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <uni-icons v-if="showArrow || link" :size="16" class="uni-icon-wrapper" color="#bbb" type="arrowright" /> |
| | | </view> |
| | | <!-- #ifdef APP-NVUE --> |
| | | </cell> |
| | | <!-- #endif --> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from '../uni-icons/uni-icons.vue'; |
| | | import uniBadge from '../uni-badge/uni-badge.vue'; |
| | | |
| | | /** |
| | | * ListItem 列表子组件 |
| | | * @description 列表子组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=24 |
| | | * @property {String} title 标题 |
| | | * @property {String} note 描述 |
| | | * @property {String} thumb 左侧缩略图,若thumb有值,则不会显示扩展图标 |
| | | * @property {String} thumbSize = [lg|base|sm] 略缩图大小 |
| | | * @value lg 大图 |
| | | * @value base 一般 |
| | | * @value sm 小图 |
| | | * @property {String} badgeText 数字角标内容 |
| | | * @property {String} badgeType 数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21) |
| | | * @property {String} rightText 右侧文字内容 |
| | | * @property {Boolean} disabled = [true|false] 是否禁用 |
| | | * @property {Boolean} clickable = [true|false] 是否开启点击反馈 |
| | | * @property {String} link = [navigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈 |
| | | * @value navigateTo 同 uni.navigateTo() |
| | | * @value redirectTo 同 uni.redirectTo() |
| | | * @value reLaunch 同 uni.reLaunch() |
| | | * @value switchTab 同 uni.switchTab() |
| | | * @property {String | PageURIString} to 跳转目标页面 |
| | | * @property {Boolean} showBadge = [true|false] 是否显示数字角标 |
| | | * @property {Boolean} showSwitch = [true|false] 是否显示Switch |
| | | * @property {Boolean} switchChecked = [true|false] Switch是否被选中 |
| | | * @property {Boolean} showExtraIcon = [true|false] 左侧是否显示扩展图标 |
| | | * @property {Object} extraIcon 扩展图标参数,格式为 {color: '#4cd964',size: '22',type: 'spinner'} |
| | | * @property {String} direction = [row|column] 排版方向 |
| | | * @value row 水平排列 |
| | | * @value column 垂直排列 |
| | | * @event {Function} click 点击 uniListItem 触发事件 |
| | | * @event {Function} switchChange 点击切换 Switch 时触发 |
| | | */ |
| | | export default { |
| | | name: 'UniListItem', |
| | | components: { |
| | | uniIcons, |
| | | uniBadge |
| | | }, |
| | | props: { |
| | | direction: { |
| | | type: String, |
| | | default: 'row' |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | note: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | ellipsis: { |
| | | type: [Number], |
| | | default: 0 |
| | | }, |
| | | disabled: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | clickable: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | showArrow: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | link: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | to: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | showBadge: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | showSwitch: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | switchChecked: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | badgeText: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | badgeType: { |
| | | type: String, |
| | | default: 'success' |
| | | }, |
| | | rightText: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | thumb: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | thumbSize: { |
| | | type: String, |
| | | default: 'base' |
| | | }, |
| | | showExtraIcon: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | extraIcon: { |
| | | type: Object, |
| | | default() { |
| | | return { |
| | | type: 'contact', |
| | | color: '#000000', |
| | | size: 20 |
| | | }; |
| | | } |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | // inject: ['list'], |
| | | data() { |
| | | return { |
| | | isFirstChild: false |
| | | }; |
| | | }, |
| | | mounted() { |
| | | this.list = this.getForm() |
| | | // 判断是否存在 uni-list 组件 |
| | | if(this.list){ |
| | | if (!this.list.firstChildAppend) { |
| | | this.list.firstChildAppend = true; |
| | | this.isFirstChild = true; |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getForm(name = 'uniList') { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== name) { |
| | | parent = parent.$parent; |
| | | if (!parent) return false |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | }, |
| | | onClick() { |
| | | if (this.to !== '') { |
| | | this.openPage(); |
| | | return; |
| | | } |
| | | if (this.clickable || this.link) { |
| | | this.$emit('click', { |
| | | data: {} |
| | | }); |
| | | } |
| | | }, |
| | | onSwitchChange(e) { |
| | | this.$emit('switchChange', e.detail); |
| | | }, |
| | | openPage() { |
| | | if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].indexOf(this.link) !== -1) { |
| | | this.pageApi(this.link); |
| | | } else { |
| | | this.pageApi('navigateTo'); |
| | | } |
| | | }, |
| | | pageApi(api) { |
| | | uni[api]({ |
| | | url: this.to, |
| | | success: res => { |
| | | this.$emit('click', { |
| | | data: res |
| | | }); |
| | | }, |
| | | fail: err => { |
| | | this.$emit('click', { |
| | | data: err |
| | | }); |
| | | console.error(err.errMsg); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $list-item-pd: $uni-spacing-col-lg $uni-spacing-row-lg; |
| | | |
| | | .uni-list-item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | font-size: $uni-font-size-lg; |
| | | position: relative; |
| | | justify-content: space-between; |
| | | background-color: #fff; |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-list-item--disabled { |
| | | opacity: 0.3; |
| | | } |
| | | |
| | | .uni-list-item--hover { |
| | | background-color: $uni-bg-color-hover; |
| | | } |
| | | |
| | | .uni-list-item__container { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | padding: $list-item-pd; |
| | | padding-left: $uni-spacing-row-lg; |
| | | flex: 1; |
| | | overflow: hidden; |
| | | // align-items: center; |
| | | } |
| | | |
| | | .container--right { |
| | | padding-right: 0; |
| | | } |
| | | |
| | | // .border--left { |
| | | // margin-left: $uni-spacing-row-lg; |
| | | // } |
| | | |
| | | .uni-list--border { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | left: 0; |
| | | /* #ifdef APP-NVUE */ |
| | | border-top-color: $uni-border-color; |
| | | border-top-style: solid; |
| | | border-top-width: 0.5px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-list--border:after { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | left: 0; |
| | | height: 1px; |
| | | content: ''; |
| | | -webkit-transform: scaleY(0.5); |
| | | transform: scaleY(0.5); |
| | | background-color: $uni-border-color; |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | .uni-list-item__content { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | padding-right: 8px; |
| | | flex: 1; |
| | | color: #3b4144; |
| | | // overflow: hidden; |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-list-item__content--center { |
| | | justify-content: center; |
| | | } |
| | | |
| | | .uni-list-item__content-title { |
| | | font-size: $uni-font-size-base; |
| | | color: #3b4144; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-list-item__content-note { |
| | | margin-top: 6rpx; |
| | | color: $uni-text-color-grey; |
| | | font-size: $uni-font-size-sm; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-list-item__extra { |
| | | // width: 25%; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: flex-end; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-list-item__header { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-list-item__icon { |
| | | margin-right: 18rpx; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-list-item__icon-img { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | height: $uni-img-size-base; |
| | | width: $uni-img-size-base; |
| | | } |
| | | |
| | | .uni-icon-wrapper { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .flex--direction { |
| | | flex-direction: column; |
| | | /* #ifndef APP-NVUE */ |
| | | align-items: initial; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .flex--justify { |
| | | /* #ifndef APP-NVUE */ |
| | | justify-content: initial; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-list--lg { |
| | | height: $uni-img-size-lg; |
| | | width: $uni-img-size-lg; |
| | | } |
| | | |
| | | .uni-list--base { |
| | | height: $uni-img-size-base; |
| | | width: $uni-img-size-base; |
| | | } |
| | | |
| | | .uni-list--sm { |
| | | height: $uni-img-size-sm; |
| | | width: $uni-img-size-sm; |
| | | } |
| | | |
| | | .uni-list-item__extra-text { |
| | | color: $uni-text-color-grey; |
| | | font-size: $uni-font-size-sm; |
| | | } |
| | | .uni-ellipsis-1 { |
| | | /* #ifndef APP-NVUE */ |
| | | overflow: hidden; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 1; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-ellipsis-2 { |
| | | /* #ifndef APP-NVUE */ |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | display: -webkit-box; |
| | | -webkit-line-clamp: 2; |
| | | -webkit-box-orient: vertical; |
| | | /* #endif */ |
| | | |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 2; |
| | | /* #endif */ |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <view class="uni-list uni-border-top-bottom"> |
| | | <view v-if="border" class="uni-list--border-top"></view> |
| | | <slot /> |
| | | <view v-if="border" class="uni-list--border-bottom"></view> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <list class="uni-list" :class="{ 'uni-list--border': border }" :enableBackToTop="enableBackToTop" loadmoreoffset="15"><slot /></list> |
| | | <!-- #endif --> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * List 列表 |
| | | * @description 列表组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=24 |
| | | * @property {String} border = [true|false] 标题 |
| | | */ |
| | | export default { |
| | | name: 'uniList', |
| | | 'mp-weixin': { |
| | | options: { |
| | | multipleSlots: false |
| | | } |
| | | }, |
| | | props: { |
| | | enableBackToTop: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | scrollY: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | // provide() { |
| | | // return { |
| | | // list: this |
| | | // }; |
| | | // }, |
| | | created() { |
| | | this.firstChildAppend = false; |
| | | }, |
| | | methods: { |
| | | loadMore(e) { |
| | | this.$emit('scrolltolower'); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | <style lang="scss"> |
| | | .uni-list { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | background-color: $uni-bg-color; |
| | | position: relative; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-list--border { |
| | | position: relative; |
| | | /* #ifdef APP-NVUE */ |
| | | border-top-color: $uni-border-color; |
| | | border-top-style: solid; |
| | | border-top-width: 0.5px; |
| | | border-bottom-color: $uni-border-color; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 0.5px; |
| | | /* #endif */ |
| | | z-index: -1; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | |
| | | .uni-list--border-top { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | left: 0; |
| | | height: 1px; |
| | | -webkit-transform: scaleY(0.5); |
| | | transform: scaleY(0.5); |
| | | background-color: $uni-border-color; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .uni-list--border-bottom { |
| | | position: absolute; |
| | | bottom: 0; |
| | | right: 0; |
| | | left: 0; |
| | | height: 1px; |
| | | -webkit-transform: scaleY(0.5); |
| | | transform: scaleY(0.5); |
| | | background-color: $uni-border-color; |
| | | } |
| | | |
| | | /* #endif */ |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <refresh :display="display" @refresh="onrefresh" @pullingdown="onpullingdown"> |
| | | <slot /> |
| | | </refresh> |
| | | <!-- #endif --> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <view ref="uni-refresh" class="uni-refresh" v-show="isShow"> |
| | | <slot /> |
| | | </view> |
| | | <!-- #endif --> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'UniRefresh', |
| | | props: { |
| | | display: { |
| | | type: [String], |
| | | default: "hide" |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | pulling: false |
| | | } |
| | | }, |
| | | computed: { |
| | | isShow() { |
| | | if (this.display === "show" || this.pulling === true) { |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | }, |
| | | created() {}, |
| | | methods: { |
| | | onchange(value) { |
| | | this.pulling = value; |
| | | }, |
| | | onrefresh(e) { |
| | | this.$emit("refresh", e); |
| | | }, |
| | | onpullingdown(e) { |
| | | // #ifdef APP-NVUE |
| | | this.$emit("pullingdown", e); |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | var detail = { |
| | | viewHeight: 90, |
| | | pullingDistance: e.height |
| | | } |
| | | this.$emit("pullingdown", detail); |
| | | // #endif |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .uni-refresh { |
| | | height: 0; |
| | | overflow: hidden; |
| | | } |
| | | </style> |
New file |
| | |
| | | var pullDown = { |
| | | threshold: 95, |
| | | maxHeight: 200, |
| | | callRefresh: 'onrefresh', |
| | | callPullingDown: 'onpullingdown', |
| | | refreshSelector: '.uni-refresh' |
| | | }; |
| | | |
| | | function ready(newValue, oldValue, ownerInstance, instance) { |
| | | var state = instance.getState() |
| | | state.canPullDown = newValue; |
| | | // console.log(newValue); |
| | | } |
| | | |
| | | function touchStart(e, instance) { |
| | | var state = instance.getState(); |
| | | state.refreshInstance = instance.selectComponent(pullDown.refreshSelector); |
| | | state.canPullDown = (state.refreshInstance != null && state.refreshInstance != undefined); |
| | | if (!state.canPullDown) { |
| | | return |
| | | } |
| | | |
| | | // console.log("touchStart"); |
| | | |
| | | state.height = 0; |
| | | state.touchStartY = e.touches[0].pageY || e.changedTouches[0].pageY; |
| | | state.refreshInstance.setStyle({ |
| | | 'height': 0 |
| | | }); |
| | | state.refreshInstance.callMethod("onchange", true); |
| | | } |
| | | |
| | | function touchMove(e, ownerInstance) { |
| | | var instance = e.instance; |
| | | var state = instance.getState(); |
| | | if (!state.canPullDown) { |
| | | return |
| | | } |
| | | |
| | | var oldHeight = state.height; |
| | | var endY = e.touches[0].pageY || e.changedTouches[0].pageY; |
| | | var height = endY - state.touchStartY; |
| | | if (height > pullDown.maxHeight) { |
| | | return; |
| | | } |
| | | |
| | | var refreshInstance = state.refreshInstance; |
| | | refreshInstance.setStyle({ |
| | | 'height': height + 'px' |
| | | }); |
| | | |
| | | height = height < pullDown.maxHeight ? height : pullDown.maxHeight; |
| | | state.height = height; |
| | | refreshInstance.callMethod(pullDown.callPullingDown, { |
| | | height: height |
| | | }); |
| | | } |
| | | |
| | | function touchEnd(e, ownerInstance) { |
| | | var state = e.instance.getState(); |
| | | if (!state.canPullDown) { |
| | | return |
| | | } |
| | | |
| | | state.refreshInstance.callMethod("onchange", false); |
| | | |
| | | var refreshInstance = state.refreshInstance; |
| | | if (state.height > pullDown.threshold) { |
| | | refreshInstance.callMethod(pullDown.callRefresh); |
| | | return; |
| | | } |
| | | |
| | | refreshInstance.setStyle({ |
| | | 'height': 0 |
| | | }); |
| | | } |
| | | |
| | | function propObserver(newValue, oldValue, instance) { |
| | | pullDown = newValue; |
| | | } |
| | | |
| | | module.exports = { |
| | | touchmove: touchMove, |
| | | touchstart: touchStart, |
| | | touchend: touchEnd, |
| | | propObserver: propObserver |
| | | } |
New file |
| | |
| | | <template> |
| | | <view class="uni-load-more" @click="onClick"> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <loading-indicator v-if="!webviewHide && status === 'loading' && showIcon" :style="{color: color,width:iconSize+'px',height:iconSize+'px'}" :animating="true" class="uni-load-more__img uni-load-more__img--nvue"></loading-indicator> |
| | | <!-- #endif --> |
| | | <!-- #ifdef H5 --> |
| | | <svg width="24" height="24" viewBox="25 25 50 50" v-if="!webviewHide && (iconType==='circle' || iconType==='auto' && platform === 'android') && status === 'loading' && showIcon" |
| | | :style="{width:iconSize+'px',height:iconSize+'px'}" class="uni-load-more__img uni-load-more__img--android-H5"> |
| | | <circle cx="50" cy="50" r="20" fill="none" :style="{color:color}" :stroke-width="3"></circle> |
| | | </svg> |
| | | <!-- #endif --> |
| | | <!-- #ifndef APP-NVUE || H5 --> |
| | | <view v-if="!webviewHide && (iconType==='circle' || iconType==='auto' && platform === 'android') && status === 'loading' && showIcon" |
| | | :style="{width:iconSize+'px',height:iconSize+'px'}" class="uni-load-more__img uni-load-more__img--android-MP"> |
| | | <view :style="{borderTopColor:color,borderTopWidth:iconSize/12}"></view> |
| | | <view :style="{borderTopColor:color,borderTopWidth:iconSize/12}"></view> |
| | | <view :style="{borderTopColor:color,borderTopWidth:iconSize/12}"></view> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <view v-else-if="!webviewHide && status === 'loading' && showIcon" :style="{width:iconSize+'px',height:iconSize+'px'}" class="uni-load-more__img uni-load-more__img--ios-H5"> |
| | | <image src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzlBMzU3OTlEOUM0MTFFOUI0NTZDNERBQURBQzI4RkUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzlBMzU3OUFEOUM0MTFFOUI0NTZDNERBQURBQzI4RkUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDOUEzNTc5N0Q5QzQxMUU5QjQ1NkM0REFBREFDMjhGRSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDOUEzNTc5OEQ5QzQxMUU5QjQ1NkM0REFBREFDMjhGRSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pt+ALSwAAA6CSURBVHja1FsLkFZV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" |
| | | mode="widthFix"></image> |
| | | </view> |
| | | <!-- #endif --> |
| | | <text class="uni-load-more__text" :style="{color: color}">{{ status === 'more' ? contentText.contentdown : status === 'loading' ? contentText.contentrefresh : contentText.contentnomore }}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | const platform = uni.getSystemInfoSync().platform |
| | | |
| | | /** |
| | | * LoadMore 加载更多 |
| | | * @description 用于列表中,做滚动加载使用,展示 loading 的各种状态 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=29 |
| | | * @property {String} status = [more|loading|noMore] loading 的状态 |
| | | * @value more loading前 |
| | | * @value loading loading中 |
| | | * @value noMore 没有更多了 |
| | | * @property {Number} iconSize 指定图标大小 |
| | | * @property {Boolean} iconSize = [true|false] 是否显示 loading 图标 |
| | | * @property {String} iconType = [snow|circle|auto] 指定图标样式 |
| | | * @value snow ios雪花加载样式 |
| | | * @value circle 安卓唤醒加载样式 |
| | | * @value auto 根据平台自动选择加载样式 |
| | | * @property {String} color 图标和文字颜色 |
| | | * @property {Object} contentText 各状态文字说明,值为:{contentdown: "上拉显示更多",contentrefresh: "正在加载...",contentnomore: "没有更多数据了"} |
| | | * @event {Function} clickLoadMore 点击加载更多时触发 |
| | | */ |
| | | export default { |
| | | name: 'UniLoadMore', |
| | | props: { |
| | | status: { |
| | | // 上拉的状态:more-loading前;loading-loading中;noMore-没有更多了 |
| | | type: String, |
| | | default: 'more' |
| | | }, |
| | | showIcon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | iconType: { |
| | | type: String, |
| | | default: 'auto' |
| | | }, |
| | | iconSize: { |
| | | type: Number, |
| | | default: 24 |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#777777' |
| | | }, |
| | | contentText: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | contentdown: '上拉显示更多', |
| | | contentrefresh: '正在加载...', |
| | | contentnomore: '没有更多数据了' |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | webviewHide: false, |
| | | platform: platform |
| | | } |
| | | }, |
| | | // #ifndef APP-NVUE |
| | | computed:{ |
| | | iconSnowWidth(){ |
| | | return (Math.floor(this.iconSize/24)||1)*2 |
| | | } |
| | | }, |
| | | // #endif |
| | | mounted() { |
| | | // #ifdef APP-PLUS |
| | | var pages = getCurrentPages(); |
| | | var page = pages[pages.length - 1]; |
| | | var currentWebview = page.$getAppWebview(); |
| | | currentWebview.addEventListener('hide', () => { |
| | | this.webviewHide = true |
| | | }) |
| | | currentWebview.addEventListener('show', () => { |
| | | this.webviewHide = false |
| | | }) |
| | | // #endif |
| | | }, |
| | | methods: { |
| | | onClick() { |
| | | this.$emit('clickLoadMore', { |
| | | detail: { |
| | | status: this.status, |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | |
| | | .uni-load-more { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | height: 40px; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .uni-load-more__text { |
| | | font-size: 15px; |
| | | } |
| | | |
| | | .uni-load-more__img { |
| | | width: 24px; |
| | | height: 24px; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .uni-load-more__img--nvue { |
| | | color: #666666; |
| | | } |
| | | |
| | | .uni-load-more__img--android, |
| | | .uni-load-more__img--ios { |
| | | width: 24px; |
| | | height: 24px; |
| | | transform: rotate(0deg); |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-load-more__img--android { |
| | | animation: loading-ios 1s 0s linear infinite; |
| | | } |
| | | |
| | | @keyframes loading-android { |
| | | 0% { |
| | | transform: rotate(0deg); |
| | | } |
| | | |
| | | 100% { |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | .uni-load-more__img--ios-H5 { |
| | | position: relative; |
| | | animation: loading-ios-H5 1s 0s step-end infinite; |
| | | } |
| | | |
| | | .uni-load-more__img--ios-H5>image { |
| | | position: absolute; |
| | | width: 100%; |
| | | height: 100%; |
| | | left: 0; |
| | | top: 0; |
| | | } |
| | | |
| | | @keyframes loading-ios-H5 { |
| | | 0% { |
| | | transform: rotate(0deg); |
| | | } |
| | | |
| | | 8% { |
| | | transform: rotate(30deg); |
| | | } |
| | | |
| | | 16% { |
| | | transform: rotate(60deg); |
| | | } |
| | | |
| | | 24% { |
| | | transform: rotate(90deg); |
| | | } |
| | | |
| | | 32% { |
| | | transform: rotate(120deg); |
| | | } |
| | | |
| | | 40% { |
| | | transform: rotate(150deg); |
| | | } |
| | | |
| | | 48% { |
| | | transform: rotate(180deg); |
| | | } |
| | | |
| | | 56% { |
| | | transform: rotate(210deg); |
| | | } |
| | | |
| | | 64% { |
| | | transform: rotate(240deg); |
| | | } |
| | | |
| | | 73% { |
| | | transform: rotate(270deg); |
| | | } |
| | | |
| | | 82% { |
| | | transform: rotate(300deg); |
| | | } |
| | | |
| | | 91% { |
| | | transform: rotate(330deg); |
| | | } |
| | | |
| | | 100% { |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | /* #ifdef H5 */ |
| | | .uni-load-more__img--android-H5 { |
| | | animation: loading-android-H5-rotate 2s linear infinite; |
| | | transform-origin: center center; |
| | | } |
| | | |
| | | .uni-load-more__img--android-H5>circle { |
| | | display: inline-block; |
| | | animation: loading-android-H5-dash 1.5s ease-in-out infinite; |
| | | stroke: currentColor; |
| | | stroke-linecap: round; |
| | | } |
| | | |
| | | @keyframes loading-android-H5-rotate { |
| | | 0% { |
| | | transform: rotate(0deg); |
| | | } |
| | | |
| | | 100% { |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes loading-android-H5-dash { |
| | | 0% { |
| | | stroke-dasharray: 1, 200; |
| | | stroke-dashoffset: 0; |
| | | } |
| | | |
| | | 50% { |
| | | stroke-dasharray: 90, 150; |
| | | stroke-dashoffset: -40; |
| | | } |
| | | |
| | | 100% { |
| | | stroke-dasharray: 90, 150; |
| | | stroke-dashoffset: -120; |
| | | } |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | /* #ifndef APP-NVUE || H5 */ |
| | | .uni-load-more__img--android-MP { |
| | | position: relative; |
| | | width: 24px; |
| | | height: 24px; |
| | | transform: rotate(0deg); |
| | | animation: loading-ios 1s 0s ease infinite; |
| | | } |
| | | |
| | | .uni-load-more__img--android-MP>view { |
| | | position: absolute; |
| | | box-sizing: border-box; |
| | | width: 100%; |
| | | height: 100%; |
| | | border-radius: 50%; |
| | | border: solid 2px transparent; |
| | | border-top: solid 2px #777777; |
| | | transform-origin: center; |
| | | } |
| | | |
| | | .uni-load-more__img--android-MP>view:nth-child(1){ |
| | | animation: loading-android-MP-1 1s 0s linear infinite; |
| | | } |
| | | |
| | | .uni-load-more__img--android-MP>view:nth-child(2){ |
| | | animation: loading-android-MP-2 1s 0s linear infinite; |
| | | } |
| | | |
| | | .uni-load-more__img--android-MP>view:nth-child(3){ |
| | | animation: loading-android-MP-3 1s 0s linear infinite; |
| | | } |
| | | |
| | | @keyframes loading-android { |
| | | 0% { |
| | | transform: rotate(0deg); |
| | | } |
| | | |
| | | 100% { |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | |
| | | @keyframes loading-android-MP-1{ |
| | | 0%{ |
| | | transform: rotate(0deg); |
| | | } |
| | | 50%{ |
| | | transform: rotate(90deg); |
| | | } |
| | | 100%{ |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | @keyframes loading-android-MP-2{ |
| | | 0%{ |
| | | transform: rotate(0deg); |
| | | } |
| | | 50%{ |
| | | transform: rotate(180deg); |
| | | } |
| | | 100%{ |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | @keyframes loading-android-MP-3{ |
| | | 0%{ |
| | | transform: rotate(0deg); |
| | | } |
| | | 50%{ |
| | | transform: rotate(270deg); |
| | | } |
| | | 100%{ |
| | | transform: rotate(360deg); |
| | | } |
| | | } |
| | | /* #endif */ |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-navbar"> |
| | | <view :class="{ 'uni-navbar--fixed': fixed, 'uni-navbar--shadow': shadow, 'uni-navbar--border': border }" :style="{ 'background-color': backgroundColor }" |
| | | class="uni-navbar__content"> |
| | | <uni-status-bar v-if="statusBar" /> |
| | | <view :style="{ color: color,backgroundColor: backgroundColor }" class="uni-navbar__header uni-navbar__content_view"> |
| | | <view @tap="onClickLeft" class="uni-navbar__header-btns uni-navbar__header-btns-left uni-navbar__content_view"> |
| | | <view class="uni-navbar__content_view" v-if="leftIcon.length"> |
| | | <uni-icons :color="color" :type="leftIcon" size="24" /> |
| | | </view> |
| | | <view :class="{ 'uni-navbar-btn-icon-left': !leftIcon.length }" class="uni-navbar-btn-text uni-navbar__content_view" |
| | | v-if="leftText.length"> |
| | | <text :style="{ color: color, fontSize: '14px' }">{{ leftText }}</text> |
| | | </view> |
| | | <slot name="left" /> |
| | | </view> |
| | | <view class="uni-navbar__header-container uni-navbar__content_view" @tap="onClickTitle"> |
| | | <view class="uni-navbar__header-container-inner uni-navbar__content_view" v-if="title.length"> |
| | | <text class="uni-nav-bar-text" :style="{color: color }">{{ title }}</text> |
| | | </view> |
| | | <!-- 标题插槽 --> |
| | | <slot /> |
| | | </view> |
| | | <view :class="title.length ? 'uni-navbar__header-btns-right' : ''" @tap="onClickRight" class="uni-navbar__header-btns uni-navbar__content_view"> |
| | | <view class="uni-navbar__content_view" v-if="rightIcon.length"> |
| | | <uni-icons :color="color" :type="rightIcon" size="24" /> |
| | | </view> |
| | | <!-- 优先显示图标 --> |
| | | <view class="uni-navbar-btn-text uni-navbar__content_view" v-if="rightText.length && !rightIcon.length"> |
| | | <text class="uni-nav-bar-right-text">{{ rightText }}</text> |
| | | </view> |
| | | <slot name="right" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="uni-navbar__placeholder" v-if="fixed"> |
| | | <uni-status-bar v-if="statusBar" /> |
| | | <view class="uni-navbar__placeholder-view" /> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniStatusBar from "../uni-status-bar/uni-status-bar.vue"; |
| | | import uniIcons from "../uni-icons/uni-icons.vue"; |
| | | |
| | | /** |
| | | * NavBar 自定义导航栏 |
| | | * @description 导航栏组件,主要用于头部导航 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=52 |
| | | * @property {String} title 标题文字 |
| | | * @property {String} leftText 左侧按钮文本 |
| | | * @property {String} rightText 右侧按钮文本 |
| | | * @property {String} leftIcon 左侧按钮图标(图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type 属性) |
| | | * @property {String} rightIcon 右侧按钮图标(图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type 属性) |
| | | * @property {String} color 图标和文字颜色 |
| | | * @property {String} backgroundColor 导航栏背景颜色 |
| | | * @property {Boolean} fixed = [true|false] 是否固定顶部 |
| | | * @property {Boolean} statusBar = [true|false] 是否包含状态栏 |
| | | * @property {Boolean} shadow = [true|false] 导航栏下是否有阴影 |
| | | * @event {Function} clickLeft 左侧按钮点击时触发 |
| | | * @event {Function} clickRight 右侧按钮点击时触发 |
| | | * @event {Function} clickTitle 中间标题点击时触发 |
| | | */ |
| | | export default { |
| | | name: "UniNavBar", |
| | | components: { |
| | | uniStatusBar, |
| | | uniIcons |
| | | }, |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: "" |
| | | }, |
| | | leftText: { |
| | | type: String, |
| | | default: "" |
| | | }, |
| | | rightText: { |
| | | type: String, |
| | | default: "" |
| | | }, |
| | | leftIcon: { |
| | | type: String, |
| | | default: "" |
| | | }, |
| | | rightIcon: { |
| | | type: String, |
| | | default: "" |
| | | }, |
| | | fixed: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: "#000000" |
| | | }, |
| | | backgroundColor: { |
| | | type: String, |
| | | default: "#FFFFFF" |
| | | }, |
| | | statusBar: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | shadow: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | border: { |
| | | type: [Boolean, String], |
| | | default: true |
| | | } |
| | | }, |
| | | mounted() { |
| | | if(uni.report && this.title !== '') { |
| | | uni.report('title', this.title) |
| | | } |
| | | }, |
| | | methods: { |
| | | onClickLeft() { |
| | | this.$emit("clickLeft"); |
| | | }, |
| | | onClickRight() { |
| | | this.$emit("clickRight"); |
| | | }, |
| | | onClickTitle() { |
| | | this.$emit("clickTitle"); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $nav-height: 44px; |
| | | |
| | | .uni-nav-bar-text { |
| | | /* #ifdef APP-PLUS */ |
| | | font-size: 34rpx; |
| | | /* #endif */ |
| | | /* #ifndef APP-PLUS */ |
| | | font-size: $uni-font-size-lg; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-nav-bar-right-text { |
| | | font-size: $uni-font-size-base; |
| | | } |
| | | |
| | | .uni-navbar__content { |
| | | position: relative; |
| | | background-color: $uni-bg-color; |
| | | overflow: hidden; |
| | | width: 750rpx; |
| | | } |
| | | |
| | | .uni-navbar__content_view { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | flex-direction: row; |
| | | // background-color: #FFFFFF; |
| | | } |
| | | |
| | | .uni-navbar__header { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | height: $nav-height; |
| | | line-height: $nav-height; |
| | | font-size: 16px; |
| | | // background-color: #ffffff; |
| | | } |
| | | |
| | | .uni-navbar__header-btns { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-wrap: nowrap; |
| | | width: 120rpx; |
| | | padding: 0 6px; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-navbar__header-btns-left { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | width: 150rpx; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .uni-navbar__header-btns-right { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | width: 150rpx; |
| | | padding-right: 30rpx; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .uni-navbar__header-container { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-navbar__header-container-inner { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: $uni-font-size-base; |
| | | } |
| | | |
| | | |
| | | .uni-navbar__placeholder-view { |
| | | height: $nav-height; |
| | | } |
| | | |
| | | .uni-navbar--fixed { |
| | | position: fixed; |
| | | z-index: 998; |
| | | } |
| | | |
| | | .uni-navbar--shadow { |
| | | /* #ifndef APP-NVUE */ |
| | | box-shadow: 0 1px 6px #ccc; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-navbar--border { |
| | | border-bottom-width: 1rpx; |
| | | border-bottom-style: solid; |
| | | border-bottom-color: $uni-border-color; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view v-if="show" class="uni-noticebar" :style="{ backgroundColor: backgroundColor }" @click="onClick"> |
| | | <!-- #ifdef MP-ALIPAY --> |
| | | <view v-if="showClose === true || showClose === 'true'" class="uni-noticebar-close" @click="close"> |
| | | <uni-icons type="closeempty" :color="color" size="12" /> |
| | | </view> |
| | | <view v-if="showIcon === true || showIcon === 'true'" class="uni-noticebar-icon"> |
| | | <uni-icons type="sound" :color="color" size="14" /> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifndef MP-ALIPAY --> |
| | | <uni-icons v-if="showClose === true || showClose === 'true'" class="uni-noticebar-close" type="closeempty" :color="color" |
| | | size="12" @click="close" /> |
| | | <uni-icons v-if="showIcon === true || showIcon === 'true'" class="uni-noticebar-icon" type="sound" :color="color" |
| | | size="14" /> |
| | | <!-- #endif --> |
| | | <view ref="textBox" class="uni-noticebar__content-wrapper" :class="{'uni-noticebar__content-wrapper--scrollable':scrollable, 'uni-noticebar__content-wrapper--single':!scrollable && (single || moreText)}"> |
| | | <view :id="elIdBox" class="uni-noticebar__content" :class="{'uni-noticebar__content--scrollable':scrollable, 'uni-noticebar__content--single':!scrollable && (single || moreText)}"> |
| | | <text :id="elId" ref="animationEle" class="uni-noticebar__content-text" :class="{'uni-noticebar__content-text--scrollable':scrollable,'uni-noticebar__content-text--single':!scrollable && (single || moreText)}" |
| | | :style="{color:color, width:wrapWidth+'px', 'animationDuration': animationDuration, '-webkit-animationDuration': animationDuration ,animationPlayState: webviewHide?'paused':animationPlayState,'-webkit-animationPlayState':webviewHide?'paused':animationPlayState, animationDelay: animationDelay, '-webkit-animationDelay':animationDelay}">{{text}}</text> |
| | | </view> |
| | | </view> |
| | | <view v-if="showGetMore === true || showGetMore === 'true'" class="uni-noticebar__more" @click="clickMore"> |
| | | <text v-if="moreText" :style="{ color: moreColor }" class="uni-noticebar__more-text">{{ moreText }}</text> |
| | | <uni-icons type="arrowright" :color="moreColor" size="14" /> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from '../uni-icons/uni-icons.vue' |
| | | // #ifdef APP-NVUE |
| | | const dom = weex.requireModule('dom'); |
| | | const animation = weex.requireModule('animation'); |
| | | // #endif |
| | | |
| | | /** |
| | | * NoticeBar 自定义导航栏 |
| | | * @description 通告栏组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=30 |
| | | * @property {Number} speed 文字滚动的速度,默认100px/秒 |
| | | * @property {String} text 显示文字 |
| | | * @property {String} backgroundColor 背景颜色 |
| | | * @property {String} color 文字颜色 |
| | | * @property {String} moreColor 查看更多文字的颜色 |
| | | * @property {String} moreText 设置“查看更多”的文本 |
| | | * @property {Boolean} single = [true|false] 是否单行 |
| | | * @property {Boolean} scrollable = [true|false] 是否滚动,为true时,NoticeBar为单行 |
| | | * @property {Boolean} showIcon = [true|false] 是否显示左侧喇叭图标 |
| | | * @property {Boolean} showClose = [true|false] 是否显示左侧关闭按钮 |
| | | * @property {Boolean} showGetMore = [true|false] 是否显示右侧查看更多图标,为true时,NoticeBar为单行 |
| | | * @event {Function} click 点击 NoticeBar 触发事件 |
| | | * @event {Function} close 关闭 NoticeBar 触发事件 |
| | | * @event {Function} getmore 点击”查看更多“时触发事件 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniNoticeBar', |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | props: { |
| | | text: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | moreText: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | backgroundColor: { |
| | | type: String, |
| | | default: '#fffbe8' |
| | | }, |
| | | speed: { |
| | | // 默认1s滚动100px |
| | | type: Number, |
| | | default: 100 |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#de8c17' |
| | | }, |
| | | moreColor: { |
| | | type: String, |
| | | default: '#999999' |
| | | }, |
| | | single: { |
| | | // 是否单行 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | scrollable: { |
| | | // 是否滚动,添加后控制单行效果取消 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | showIcon: { |
| | | // 是否显示左侧icon |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | showGetMore: { |
| | | // 是否显示右侧查看更多 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | showClose: { |
| | | // 是否显示左侧关闭按钮 |
| | | type: [Boolean, String], |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` |
| | | const elIdBox = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` |
| | | return { |
| | | textWidth: 0, |
| | | boxWidth: 0, |
| | | wrapWidth: '', |
| | | webviewHide: false, |
| | | // #ifdef APP-NVUE |
| | | stopAnimation: false, |
| | | // #endif |
| | | elId: elId, |
| | | elIdBox: elIdBox, |
| | | show: true, |
| | | animationDuration: 'none', |
| | | animationPlayState: 'paused', |
| | | animationDelay: '0s' |
| | | } |
| | | }, |
| | | mounted() { |
| | | // #ifdef APP-PLUS |
| | | var pages = getCurrentPages(); |
| | | var page = pages[pages.length - 1]; |
| | | var currentWebview = page.$getAppWebview(); |
| | | currentWebview.addEventListener('hide',()=>{ |
| | | this.webviewHide = true |
| | | }) |
| | | currentWebview.addEventListener('show',()=>{ |
| | | this.webviewHide = false |
| | | }) |
| | | // #endif |
| | | this.$nextTick(() => { |
| | | this.initSize() |
| | | }) |
| | | }, |
| | | // #ifdef APP-NVUE |
| | | beforeDestroy() { |
| | | this.stopAnimation = true |
| | | }, |
| | | // #endif |
| | | methods: { |
| | | initSize() { |
| | | if (this.scrollable) { |
| | | // #ifndef APP-NVUE |
| | | let query = [], |
| | | boxWidth = 0, |
| | | textWidth = 0; |
| | | let textQuery = new Promise((resolve, reject) => { |
| | | uni.createSelectorQuery() |
| | | // #ifndef MP-ALIPAY |
| | | .in(this) |
| | | // #endif |
| | | .select(`#${this.elId}`) |
| | | .boundingClientRect() |
| | | .exec(ret => { |
| | | this.textWidth = ret[0].width |
| | | resolve() |
| | | }) |
| | | }) |
| | | let boxQuery = new Promise((resolve, reject) => { |
| | | uni.createSelectorQuery() |
| | | // #ifndef MP-ALIPAY |
| | | .in(this) |
| | | // #endif |
| | | .select(`#${this.elIdBox}`) |
| | | .boundingClientRect() |
| | | .exec(ret => { |
| | | this.boxWidth = ret[0].width |
| | | resolve() |
| | | }) |
| | | }) |
| | | query.push(textQuery) |
| | | query.push(boxQuery) |
| | | Promise.all(query).then(() => { |
| | | this.animationDuration = `${this.textWidth / this.speed}s` |
| | | this.animationDelay = `-${this.boxWidth / this.speed}s` |
| | | setTimeout(() => { |
| | | this.animationPlayState = 'running' |
| | | }, 1000) |
| | | }) |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | dom.getComponentRect(this.$refs['animationEle'], (res) => { |
| | | let winWidth = uni.getSystemInfoSync().windowWidth |
| | | this.textWidth = res.size.width |
| | | animation.transition(this.$refs['animationEle'], { |
| | | styles: { |
| | | transform: `translateX(-${winWidth}px)` |
| | | }, |
| | | duration: 0, |
| | | timingFunction: 'linear', |
| | | delay: 0 |
| | | }, () => { |
| | | if (!this.stopAnimation) { |
| | | animation.transition(this.$refs['animationEle'], { |
| | | styles: { |
| | | transform: `translateX(-${this.textWidth}px)` |
| | | }, |
| | | timingFunction: 'linear', |
| | | duration: (this.textWidth - winWidth) / this.speed * 1000, |
| | | delay: 1000 |
| | | }, () => { |
| | | if (!this.stopAnimation) { |
| | | this.loopAnimation() |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | }) |
| | | // #endif |
| | | } |
| | | // #ifdef APP-NVUE |
| | | if (!this.scrollable && (this.single || this.moreText)) { |
| | | dom.getComponentRect(this.$refs['textBox'], (res) => { |
| | | this.wrapWidth = res.size.width |
| | | }) |
| | | } |
| | | // #endif |
| | | }, |
| | | loopAnimation() { |
| | | // #ifdef APP-NVUE |
| | | animation.transition(this.$refs['animationEle'], { |
| | | styles: { |
| | | transform: `translateX(0px)` |
| | | }, |
| | | duration: 0 |
| | | }, () => { |
| | | if (!this.stopAnimation) { |
| | | animation.transition(this.$refs['animationEle'], { |
| | | styles: { |
| | | transform: `translateX(-${this.textWidth}px)` |
| | | }, |
| | | duration: this.textWidth / this.speed * 1000, |
| | | timingFunction: 'linear', |
| | | delay: 0 |
| | | }, () => { |
| | | if (!this.stopAnimation) { |
| | | this.loopAnimation() |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | // #endif |
| | | }, |
| | | clickMore() { |
| | | this.$emit('getmore') |
| | | }, |
| | | close() { |
| | | this.show = false; |
| | | this.$emit('close') |
| | | }, |
| | | onClick() { |
| | | this.$emit('click') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | |
| | | .uni-noticebar { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: 6px 12px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .uni-noticebar-close { |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .uni-noticebar-icon { |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .uni-noticebar__content-wrapper { |
| | | flex: 1; |
| | | flex-direction: column; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-noticebar__content-wrapper--single { |
| | | /* #ifndef APP-NVUE */ |
| | | line-height: 18px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-noticebar__content-wrapper--single, |
| | | .uni-noticebar__content-wrapper--scrollable { |
| | | flex-direction: row; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-noticebar__content-wrapper--scrollable { |
| | | position: relative; |
| | | height: 18px; |
| | | } |
| | | /* #endif */ |
| | | |
| | | .uni-noticebar__content--scrollable { |
| | | /* #ifdef APP-NVUE */ |
| | | flex: 0; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | flex: 1; |
| | | display: block; |
| | | overflow: hidden; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-noticebar__content--single { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | flex: none; |
| | | width: 100%; |
| | | justify-content: center; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-noticebar__content-text { |
| | | font-size: 14px; |
| | | line-height: 18px; |
| | | /* #ifndef APP-NVUE */ |
| | | word-break: break-all; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-noticebar__content-text--single { |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 1; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | width: 100%; |
| | | white-space: nowrap; |
| | | /* #endif */ |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | |
| | | .uni-noticebar__content-text--scrollable { |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 1; |
| | | padding-left: 750rpx; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | position: absolute; |
| | | display: block; |
| | | height: 18px; |
| | | line-height: 18px; |
| | | white-space: nowrap; |
| | | padding-left: 100%; |
| | | animation: notice 10s 0s linear infinite both; |
| | | animation-play-state: paused; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-noticebar__more { |
| | | /* #ifndef APP-NVUE */ |
| | | display: inline-flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex-wrap: nowrap; |
| | | align-items: center; |
| | | padding-left: 5px; |
| | | } |
| | | |
| | | .uni-noticebar__more-text { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | @keyframes notice { |
| | | 100% { |
| | | transform: translate3d(-100%, 0, 0); |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-numbox"> |
| | | <view @click="_calcValue('minus')" class="uni-numbox__minus"> |
| | | <text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue <= min || disabled }">-</text> |
| | | </view> |
| | | <input :disabled="disabled" @blur="_onBlur" class="uni-numbox__value" type="number" v-model="inputValue" /> |
| | | <view @click="_calcValue('plus')" class="uni-numbox__plus"> |
| | | <text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue >= max || disabled }">+</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | |
| | | /** |
| | | * NumberBox 数字输入框 |
| | | * @description 带加减按钮的数字输入框 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=31 |
| | | * @property {Number} value 输入框当前值 |
| | | * @property {Number} min 最小值 |
| | | * @property {Number} max 最大值 |
| | | * @property {Number} step 每次点击改变的间隔大小 |
| | | * @property {Boolean} disabled = [true|false] 是否为禁用状态 |
| | | * @event {Function} change 输入框值改变时触发的事件,参数为输入框当前的 value |
| | | */ |
| | | |
| | | export default { |
| | | name: "UniNumberBox", |
| | | props: { |
| | | value: { |
| | | type: [Number, String], |
| | | default: 1 |
| | | }, |
| | | min: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | max: { |
| | | type: Number, |
| | | default: 100 |
| | | }, |
| | | step: { |
| | | type: Number, |
| | | default: 1 |
| | | }, |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | inputValue: 0 |
| | | }; |
| | | }, |
| | | watch: { |
| | | value(val) { |
| | | this.inputValue = +val; |
| | | }, |
| | | inputValue(newVal, oldVal) { |
| | | if (+newVal !== +oldVal) { |
| | | this.$emit("change", newVal); |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.inputValue = +this.value; |
| | | }, |
| | | methods: { |
| | | _calcValue(type) { |
| | | if (this.disabled) { |
| | | return; |
| | | } |
| | | const scale = this._getDecimalScale(); |
| | | let value = this.inputValue * scale; |
| | | let step = this.step * scale; |
| | | if (type === "minus") { |
| | | value -= step; |
| | | if (value < (this.min * scale)) { |
| | | return; |
| | | } |
| | | if (value > (this.max * scale)) { |
| | | value = this.max * scale |
| | | } |
| | | } else if (type === "plus") { |
| | | value += step; |
| | | if (value > (this.max * scale)) { |
| | | return; |
| | | } |
| | | if (value < (this.min * scale)) { |
| | | value = this.min * scale |
| | | } |
| | | } |
| | | |
| | | this.inputValue = String(value / scale); |
| | | }, |
| | | _getDecimalScale() { |
| | | let scale = 1; |
| | | // 浮点型 |
| | | if (~~this.step !== this.step) { |
| | | scale = Math.pow(10, (this.step + "").split(".")[1].length); |
| | | } |
| | | return scale; |
| | | }, |
| | | _onBlur(event) { |
| | | let value = event.detail.value; |
| | | if (!value) { |
| | | // this.inputValue = 0; |
| | | return; |
| | | } |
| | | value = +value; |
| | | if (value > this.max) { |
| | | value = this.max; |
| | | } else if (value < this.min) { |
| | | value = this.min; |
| | | } |
| | | this.inputValue = value; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | $box-height: 35px; |
| | | /* #ifdef APP-NVUE */ |
| | | $box-line-height: 35px; |
| | | /* #endif */ |
| | | $box-line-height: 26px; |
| | | $box-width: 35px; |
| | | |
| | | .uni-numbox { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | height: $box-height; |
| | | line-height: $box-height; |
| | | width: 120px; |
| | | } |
| | | |
| | | .uni-numbox__value { |
| | | background-color: $uni-bg-color; |
| | | width: 40px; |
| | | height: $box-height; |
| | | text-align: center; |
| | | font-size: $uni-font-size-lg; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-border-color; |
| | | border-left-width: 0; |
| | | border-right-width: 0; |
| | | } |
| | | |
| | | .uni-numbox__minus { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: $box-width; |
| | | height: $box-height; |
| | | // line-height: $box-line-height; |
| | | // text-align: center; |
| | | font-size: 20px; |
| | | color: $uni-text-color; |
| | | background-color: $uni-bg-color-grey; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-border-color; |
| | | border-top-left-radius: $uni-border-radius-base; |
| | | border-bottom-left-radius: $uni-border-radius-base; |
| | | border-right-width: 0; |
| | | } |
| | | |
| | | .uni-numbox__plus { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: $box-width; |
| | | height: $box-height; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-border-color; |
| | | border-top-right-radius: $uni-border-radius-base; |
| | | border-bottom-right-radius: $uni-border-radius-base; |
| | | background-color: $uni-bg-color-grey; |
| | | border-left-width: 0; |
| | | } |
| | | |
| | | .uni-numbox--text { |
| | | font-size: 40rpx; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-numbox--disabled { |
| | | color: $uni-text-color-disable; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-pagination"> |
| | | <view class="uni-pagination__btn" :class="currentIndex === 1 ? 'uni-pagination--disabled' : 'uni-pagination--enabled'" |
| | | :hover-class="currentIndex === 1 ? '' : 'uni-pagination--hover'" :hover-start-time="20" :hover-stay-time="70" |
| | | @click="clickLeft"> |
| | | <template v-if="showIcon===true || showIcon === 'true'"> |
| | | <uni-icons color="#000" size="20" type="arrowleft" /> |
| | | </template> |
| | | <template v-else><text class="uni-pagination__child-btn">{{ prevText }}</text></template> |
| | | </view> |
| | | <view class="uni-pagination__num"> |
| | | <view class="uni-pagination__num-current"> |
| | | <text class="uni-pagination__num-current-text" style="color:#007aff">{{ currentIndex }}</text><text class="uni-pagination__num-current-text">/{{ maxPage || 0 }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="uni-pagination__btn" :class="currentIndex === maxPage ? 'uni-pagination--disabled' : 'uni-pagination--enabled'" |
| | | :hover-class="currentIndex === maxPage ? '' : 'uni-pagination--hover'" :hover-start-time="20" :hover-stay-time="70" |
| | | @click="clickRight"> |
| | | <template v-if="showIcon===true || showIcon === 'true'"> |
| | | <uni-icons color="#000" size="20" type="arrowright" /> |
| | | </template> |
| | | <template v-else><text class="uni-pagination__child-btn">{{ nextText }}</text></template> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from '../uni-icons/uni-icons.vue' |
| | | |
| | | /** |
| | | * Pagination 分页器 |
| | | * @description 分页器组件,用于展示页码、请求数据等 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=32 |
| | | * @property {String} prevText 左侧按钮文字 |
| | | * @property {String} nextText 右侧按钮文字 |
| | | * @property {Number} current 当前页 |
| | | * @property {Number} total 数据总量 |
| | | * @property {Number} pageSize 每页数据量 |
| | | * @property {Number} showIcon = [true|false] 是否以 icon 形式展示按钮 |
| | | * @event {Function} change 点击页码按钮时触发 ,e={type,current} current为当前页,type值为:next/prev,表示点击的是上一页还是下一个 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniPagination', |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | props: { |
| | | prevText: { |
| | | type: String, |
| | | default: '上一页' |
| | | }, |
| | | nextText: { |
| | | type: String, |
| | | default: '下一页' |
| | | }, |
| | | current: { |
| | | type: [Number, String], |
| | | default: 1 |
| | | }, |
| | | total: { // 数据总量 |
| | | type: [Number, String], |
| | | default: 0 |
| | | }, |
| | | pageSize: { // 每页数据量 |
| | | type: [Number, String], |
| | | default: 10 |
| | | }, |
| | | showIcon: { // 是否以 icon 形式展示按钮 |
| | | type: [Boolean, String], |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | currentIndex: 1 |
| | | } |
| | | }, |
| | | computed: { |
| | | maxPage() { |
| | | let maxPage = 1 |
| | | let total = Number(this.total) |
| | | let pageSize = Number(this.pageSize) |
| | | if (total && pageSize) { |
| | | maxPage = Math.ceil(total / pageSize) |
| | | } |
| | | return maxPage |
| | | } |
| | | }, |
| | | watch: { |
| | | current(val) { |
| | | this.currentIndex = +val |
| | | } |
| | | }, |
| | | created() { |
| | | this.currentIndex = +this.current |
| | | }, |
| | | methods: { |
| | | clickLeft() { |
| | | if (Number(this.currentIndex) === 1) { |
| | | return |
| | | } |
| | | this.currentIndex -= 1 |
| | | this.change('prev') |
| | | }, |
| | | clickRight() { |
| | | if (Number(this.currentIndex) === this.maxPage) { |
| | | return |
| | | } |
| | | this.currentIndex += 1 |
| | | this.change('next') |
| | | }, |
| | | change(e) { |
| | | this.$emit('change', { |
| | | type: e, |
| | | current: this.currentIndex |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-pagination { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | position: relative; |
| | | overflow: hidden; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-pagination__btn { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | width: 60px; |
| | | height: 30px; |
| | | line-height: 30px; |
| | | font-size: $uni-font-size-base; |
| | | position: relative; |
| | | background-color: $uni-bg-color-grey; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | text-align: center; |
| | | border-width: 1px; |
| | | border-style: solid; |
| | | border-color: $uni-border-color; |
| | | } |
| | | |
| | | .uni-pagination__child-btn { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | font-size: $uni-font-size-base; |
| | | position: relative; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | text-align: center; |
| | | } |
| | | |
| | | .uni-pagination__num { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 30px; |
| | | line-height: 30px; |
| | | font-size: $uni-font-size-base; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-pagination__num-current { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-pagination__num-current-text { |
| | | font-size: 15px; |
| | | } |
| | | |
| | | .uni-pagination--enabled { |
| | | color: #333333; |
| | | opacity: 1; |
| | | } |
| | | |
| | | .uni-pagination--disabled { |
| | | opacity: 0.3; |
| | | } |
| | | |
| | | .uni-pagination--hover { |
| | | color: rgba(0, 0, 0, .6); |
| | | background-color: $uni-bg-color-hover; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-popup-dialog"> |
| | | <view class="uni-dialog-title"> |
| | | <text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{title}}</text> |
| | | </view> |
| | | <view class="uni-dialog-content"> |
| | | <text class="uni-dialog-content-text" v-if="mode === 'base'">{{content}}</text> |
| | | <input v-else class="uni-dialog-input" v-model="val" type="text" :placeholder="placeholder" :focus="focus" > |
| | | </view> |
| | | <view class="uni-dialog-button-group"> |
| | | <view class="uni-dialog-button" @click="close"> |
| | | <text class="uni-dialog-button-text">取消</text> |
| | | </view> |
| | | <view class="uni-dialog-button uni-border-left" @click="onOk"> |
| | | <text class="uni-dialog-button-text uni-button-color">确定</text> |
| | | </view> |
| | | </view> |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * PopUp 弹出层-对话框样式 |
| | | * @description 弹出层-对话框样式 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=329 |
| | | * @property {String} value input 模式下的默认值 |
| | | * @property {String} placeholder input 模式下输入提示 |
| | | * @property {String} type = [success|warning|info|error] 主题样式 |
| | | * @value success 成功 |
| | | * @value warning 提示 |
| | | * @value info 消息 |
| | | * @value error 错误 |
| | | * @property {String} mode = [base|input] 模式、 |
| | | * @value base 基础对话框 |
| | | * @value input 可输入对话框 |
| | | * @property {String} content 对话框内容 |
| | | * @property {Boolean} beforeClose 是否拦截取消事件 |
| | | * @event {Function} confirm 点击确认按钮触发 |
| | | * @event {Function} close 点击取消按钮触发 |
| | | */ |
| | | |
| | | export default { |
| | | name: "uniPopupDialog", |
| | | props: { |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | placeholder: { |
| | | type: [String, Number], |
| | | default: '请输入内容' |
| | | }, |
| | | /** |
| | | * 对话框主题 success/warning/info/error 默认 success |
| | | */ |
| | | type: { |
| | | type: String, |
| | | default: 'error' |
| | | }, |
| | | /** |
| | | * 对话框模式 base/input |
| | | */ |
| | | mode: { |
| | | type: String, |
| | | default: 'base' |
| | | }, |
| | | /** |
| | | * 对话框标题 |
| | | */ |
| | | title: { |
| | | type: String, |
| | | default: '提示' |
| | | }, |
| | | /** |
| | | * 对话框内容 |
| | | */ |
| | | content: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | /** |
| | | * 拦截取消事件 ,如果拦截取消事件,必须监听close事件,执行 done() |
| | | */ |
| | | beforeClose: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | dialogType: 'error', |
| | | focus: false, |
| | | val: "" |
| | | } |
| | | }, |
| | | inject: ['popup'], |
| | | watch: { |
| | | type(val) { |
| | | this.dialogType = val |
| | | }, |
| | | mode(val) { |
| | | if (val === 'input') { |
| | | this.dialogType = 'info' |
| | | } |
| | | }, |
| | | value(val) { |
| | | this.val = val |
| | | } |
| | | }, |
| | | created() { |
| | | // 对话框遮罩不可点击 |
| | | this.popup.mkclick = false |
| | | if (this.mode === 'input') { |
| | | this.dialogType = 'info' |
| | | this.val = this.value |
| | | } else { |
| | | this.dialogType = this.type |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.focus = true |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 点击确认按钮 |
| | | */ |
| | | onOk() { |
| | | this.$emit('confirm', () => { |
| | | this.popup.close() |
| | | if (this.mode === 'input') this.val = this.value |
| | | }, this.mode === 'input' ? this.val : '') |
| | | }, |
| | | /** |
| | | * 点击取消按钮 |
| | | */ |
| | | close() { |
| | | if (this.beforeClose) { |
| | | this.$emit('close', () => { |
| | | this.popup.close() |
| | | }) |
| | | return |
| | | } |
| | | this.popup.close() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-popup-dialog { |
| | | width: 300px; |
| | | border-radius: 15px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .uni-dialog-title { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | padding-top: 15px; |
| | | padding-bottom: 5px; |
| | | } |
| | | |
| | | .uni-dialog-title-text { |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .uni-dialog-content { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | padding: 5px 15px 15px 15px; |
| | | } |
| | | |
| | | .uni-dialog-content-text { |
| | | font-size: 14px; |
| | | color: #6e6e6e; |
| | | } |
| | | |
| | | .uni-dialog-button-group { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | border-top-color: #f5f5f5; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | } |
| | | |
| | | .uni-dialog-button { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | |
| | | flex: 1; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 45px; |
| | | } |
| | | |
| | | .uni-border-left { |
| | | border-left-color: #f0f0f0; |
| | | border-left-style: solid; |
| | | border-left-width: 1px; |
| | | } |
| | | |
| | | .uni-dialog-button-text { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-button-color { |
| | | color: $uni-color-primary; |
| | | } |
| | | |
| | | .uni-dialog-input { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-popup__success { |
| | | color: $uni-color-success; |
| | | } |
| | | |
| | | .uni-popup__warn { |
| | | color: $uni-color-warning; |
| | | } |
| | | |
| | | .uni-popup__error { |
| | | color: $uni-color-error; |
| | | } |
| | | |
| | | .uni-popup__info { |
| | | color: #909399; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-popup-message" :class="'uni-popup__'+[type]"> |
| | | <text class="uni-popup-message-text" :class="'uni-popup__'+[type]+'-text'">{{message}}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | |
| | | /** |
| | | * PopUp 弹出层-消息提示 |
| | | * @description 弹出层-消息提示 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=329 |
| | | * @property {String} type = [success|warning|info|error] 主题样式 |
| | | * @value success 成功 |
| | | * @value warning 提示 |
| | | * @value info 消息 |
| | | * @value error 错误 |
| | | * @property {String} message 消息提示文字 |
| | | * @property {String} duration 显示时间,设置为 0 则不会自动关闭 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniPopupMessage', |
| | | props: { |
| | | /** |
| | | * 主题 success/warning/info/error 默认 success |
| | | */ |
| | | type: { |
| | | type: String, |
| | | default: 'success' |
| | | }, |
| | | /** |
| | | * 消息文字 |
| | | */ |
| | | message: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | /** |
| | | * 显示时间,设置为 0 则不会自动关闭 |
| | | */ |
| | | duration: { |
| | | type: Number, |
| | | default: 3000 |
| | | } |
| | | }, |
| | | inject: ['popup'], |
| | | data() { |
| | | return {} |
| | | }, |
| | | created() { |
| | | this.popup.childrenMsg = this |
| | | }, |
| | | methods: { |
| | | open() { |
| | | if (this.duration === 0) return |
| | | clearTimeout(this.popuptimer) |
| | | this.popuptimer = setTimeout(() => { |
| | | this.popup.close() |
| | | }, this.duration) |
| | | }, |
| | | close() { |
| | | clearTimeout(this.popuptimer) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .uni-popup-message { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | background-color: #e1f3d8; |
| | | padding: 10px 15px; |
| | | border-color: #eee; |
| | | border-style: solid; |
| | | border-width: 1px; |
| | | } |
| | | .uni-popup-message-text { |
| | | font-size: 14px; |
| | | padding: 0; |
| | | } |
| | | |
| | | .uni-popup__success { |
| | | background-color: #e1f3d8; |
| | | } |
| | | |
| | | .uni-popup__success-text { |
| | | color: #67C23A; |
| | | } |
| | | |
| | | .uni-popup__warn { |
| | | background-color: #faecd8; |
| | | } |
| | | |
| | | .uni-popup__warn-text { |
| | | color: #E6A23C; |
| | | } |
| | | |
| | | .uni-popup__error { |
| | | background-color: #fde2e2; |
| | | } |
| | | |
| | | .uni-popup__error-text { |
| | | color: #F56C6C; |
| | | } |
| | | |
| | | .uni-popup__info { |
| | | background-color: #F2F6FC; |
| | | } |
| | | |
| | | .uni-popup__info-text { |
| | | color: #909399; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-popup-share"> |
| | | <view class="uni-share-title"><text class="uni-share-title-text">{{title}}</text></view> |
| | | <view class="uni-share-content"> |
| | | <view class="uni-share-content-box"> |
| | | <view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)"> |
| | | <image class="uni-share-image" :src="item.icon" mode="aspectFill"></image> |
| | | <text class="uni-share-text">{{item.text}}</text> |
| | | </view> |
| | | |
| | | </view> |
| | | </view> |
| | | <view class="uni-share-button-box"> |
| | | <button class="uni-share-button" @click="close">取消</button> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'UniPopupShare', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '分享到' |
| | | } |
| | | }, |
| | | inject: ['popup'], |
| | | data() { |
| | | return { |
| | | bottomData: [{ |
| | | text: '微信', |
| | | icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-2.png', |
| | | name: 'wx' |
| | | }, |
| | | { |
| | | text: '支付宝', |
| | | icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-8.png', |
| | | name: 'wx' |
| | | }, |
| | | { |
| | | text: 'QQ', |
| | | icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/gird-3.png', |
| | | name: 'qq' |
| | | }, |
| | | { |
| | | text: '新浪', |
| | | icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-1.png', |
| | | name: 'sina' |
| | | }, |
| | | { |
| | | text: '百度', |
| | | icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-7.png', |
| | | name: 'copy' |
| | | }, |
| | | { |
| | | text: '其他', |
| | | icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-5.png', |
| | | name: 'more' |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | | created() {}, |
| | | methods: { |
| | | /** |
| | | * 选择内容 |
| | | */ |
| | | select(item, index) { |
| | | this.$emit('select', { |
| | | item, |
| | | index |
| | | }, () => { |
| | | this.popup.close() |
| | | }) |
| | | }, |
| | | /** |
| | | * 关闭窗口 |
| | | */ |
| | | close() { |
| | | this.popup.close() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .uni-popup-share { |
| | | background-color: #fff; |
| | | } |
| | | .uni-share-title { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 40px; |
| | | } |
| | | .uni-share-title-text { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | .uni-share-content { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | padding-top: 10px; |
| | | } |
| | | |
| | | .uni-share-content-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex-wrap: wrap; |
| | | width: 360px; |
| | | } |
| | | |
| | | .uni-share-content-item { |
| | | width: 90px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | padding: 10px 0; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-share-content-item:active { |
| | | background-color: #f5f5f5; |
| | | } |
| | | |
| | | .uni-share-image { |
| | | width: 30px; |
| | | height: 30px; |
| | | } |
| | | |
| | | .uni-share-text { |
| | | margin-top: 10px; |
| | | font-size: 14px; |
| | | color: #3B4144; |
| | | } |
| | | |
| | | .uni-share-button-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | padding: 10px 15px; |
| | | } |
| | | |
| | | .uni-share-button { |
| | | flex: 1; |
| | | border-radius: 50px; |
| | | color: #666; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .uni-share-button::after { |
| | | border-radius: 50px; |
| | | } |
| | | </style> |
New file |
| | |
| | | export default { |
| | | created() { |
| | | if (this.type === 'message') { |
| | | // 不显示遮罩 |
| | | this.maskShow = false |
| | | // 获取子组件对象 |
| | | this.childrenMsg = null |
| | | } |
| | | }, |
| | | methods: { |
| | | customOpen() { |
| | | if (this.childrenMsg) { |
| | | this.childrenMsg.open() |
| | | } |
| | | }, |
| | | customClose() { |
| | | if (this.childrenMsg) { |
| | | this.childrenMsg.close() |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | import message from './message.js'; |
| | | // 定义 type 类型:弹出类型:top/bottom/center |
| | | const config = { |
| | | // 顶部弹出 |
| | | top:'top', |
| | | // 底部弹出 |
| | | bottom:'bottom', |
| | | // 居中弹出 |
| | | center:'center', |
| | | // 消息提示 |
| | | message:'top', |
| | | // 对话框 |
| | | dialog:'center', |
| | | // 分享 |
| | | share:'bottom', |
| | | } |
| | | |
| | | export default { |
| | | data(){ |
| | | return { |
| | | config:config |
| | | } |
| | | }, |
| | | mixins: [message] |
| | | } |
New file |
| | |
| | | export default { |
| | | created() { |
| | | if (this.type === 'share') { |
| | | // 关闭点击 |
| | | this.mkclick = false |
| | | } |
| | | }, |
| | | methods: { |
| | | customOpen() { |
| | | console.log('share 打开了'); |
| | | }, |
| | | customClose() { |
| | | console.log('share 关闭了'); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <view v-if="showPopup" class="uni-popup" :class="[popupstyle]" @touchmove.stop.prevent="clear"> |
| | | <uni-transition v-if="maskShow" class="uni-mask--hook" :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans" |
| | | @click="onTap" /> |
| | | <uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap"> |
| | | <view class="uni-popup__wrapper-box" @click.stop="clear"> |
| | | <slot /> |
| | | </view> |
| | | </uni-transition> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniTransition from '../uni-transition/uni-transition.vue' |
| | | import popup from './popup.js' |
| | | /** |
| | | * PopUp 弹出层 |
| | | * @description 弹出层组件,为了解决遮罩弹层的问题 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=329 |
| | | * @property {String} type = [top|center|bottom] 弹出方式 |
| | | * @value top 顶部弹出 |
| | | * @value center 中间弹出 |
| | | * @value bottom 底部弹出 |
| | | * @value message 消息提示 |
| | | * @value dialog 对话框 |
| | | * @value share 底部分享示例 |
| | | * @property {Boolean} animation = [ture|false] 是否开启动画 |
| | | * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗 |
| | | * @event {Function} change 打开关闭弹窗触发,e={show: false} |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniPopup', |
| | | components: { |
| | | uniTransition |
| | | }, |
| | | props: { |
| | | // 开启动画 |
| | | animation: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层 |
| | | // message: 消息提示 ; dialog : 对话框 |
| | | type: { |
| | | type: String, |
| | | default: 'center' |
| | | }, |
| | | // maskClick |
| | | maskClick: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | provide() { |
| | | return { |
| | | popup: this |
| | | } |
| | | }, |
| | | mixins: [popup], |
| | | watch: { |
| | | /** |
| | | * 监听type类型 |
| | | */ |
| | | type: { |
| | | handler: function(newVal) { |
| | | this[this.config[newVal]]() |
| | | }, |
| | | immediate: true |
| | | }, |
| | | /** |
| | | * 监听遮罩是否可点击 |
| | | * @param {Object} val |
| | | */ |
| | | maskClick: { |
| | | handler: function(val) { |
| | | this.mkclick = val |
| | | }, |
| | | immediate: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | duration: 300, |
| | | ani: [], |
| | | showPopup: false, |
| | | showTrans: false, |
| | | maskClass: { |
| | | 'position': 'fixed', |
| | | 'bottom': 0, |
| | | 'top': 0, |
| | | 'left': 0, |
| | | 'right': 0, |
| | | 'backgroundColor': 'rgba(0, 0, 0, 0.4)' |
| | | }, |
| | | transClass: { |
| | | 'position': 'fixed', |
| | | 'left': 0, |
| | | 'right': 0, |
| | | }, |
| | | maskShow: true, |
| | | mkclick: true, |
| | | popupstyle: 'top' |
| | | } |
| | | }, |
| | | created() { |
| | | this.mkclick = this.maskClick |
| | | if (this.animation) { |
| | | this.duration = 300 |
| | | } else { |
| | | this.duration = 0 |
| | | } |
| | | }, |
| | | methods: { |
| | | clear(e) { |
| | | // TODO nvue 取消冒泡 |
| | | e.stopPropagation() |
| | | }, |
| | | open() { |
| | | this.showPopup = true |
| | | this.$nextTick(() => { |
| | | new Promise(resolve => { |
| | | clearTimeout(this.timer) |
| | | this.timer = setTimeout(() => { |
| | | this.showTrans = true |
| | | // fixed by mehaotian 兼容 app 端 |
| | | this.$nextTick(() => { |
| | | resolve(); |
| | | }) |
| | | }, 50); |
| | | }).then(res => { |
| | | // 自定义打开事件 |
| | | clearTimeout(this.msgtimer) |
| | | this.msgtimer = setTimeout(() => { |
| | | this.customOpen && this.customOpen() |
| | | }, 100) |
| | | this.$emit('change', { |
| | | show: true, |
| | | type: this.type |
| | | }) |
| | | }) |
| | | }) |
| | | }, |
| | | close(type) { |
| | | this.showTrans = false |
| | | this.$nextTick(() => { |
| | | this.$emit('change', { |
| | | show: false, |
| | | type: this.type |
| | | }) |
| | | clearTimeout(this.timer) |
| | | // 自定义关闭事件 |
| | | this.customOpen && this.customClose() |
| | | this.timer = setTimeout(() => { |
| | | this.showPopup = false |
| | | }, 300) |
| | | }) |
| | | }, |
| | | onTap() { |
| | | if (!this.mkclick) return |
| | | this.close() |
| | | }, |
| | | /** |
| | | * 顶部弹出样式处理 |
| | | */ |
| | | top() { |
| | | this.popupstyle = 'top' |
| | | this.ani = ['slide-top'] |
| | | this.transClass = { |
| | | 'position': 'fixed', |
| | | 'left': 0, |
| | | 'right': 0, |
| | | } |
| | | }, |
| | | /** |
| | | * 底部弹出样式处理 |
| | | */ |
| | | bottom() { |
| | | this.popupstyle = 'bottom' |
| | | this.ani = ['slide-bottom'] |
| | | this.transClass = { |
| | | 'position': 'fixed', |
| | | 'left': 0, |
| | | 'right': 0, |
| | | 'bottom': 0 |
| | | } |
| | | }, |
| | | /** |
| | | * 中间弹出样式处理 |
| | | */ |
| | | center() { |
| | | this.popupstyle = 'center' |
| | | this.ani = ['zoom-out', 'fade'] |
| | | this.transClass = { |
| | | 'position': 'fixed', |
| | | /* #ifndef APP-NVUE */ |
| | | 'display': 'flex', |
| | | 'flexDirection': 'column', |
| | | /* #endif */ |
| | | 'bottom': 0, |
| | | 'left': 0, |
| | | 'right': 0, |
| | | 'top': 0, |
| | | 'justifyContent': 'center', |
| | | 'alignItems': 'center' |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .uni-popup { |
| | | position: fixed; |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-popup__mask { |
| | | position: absolute; |
| | | top: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background-color: $uni-bg-color-mask; |
| | | opacity: 0; |
| | | } |
| | | |
| | | .mask-ani { |
| | | transition-property: opacity; |
| | | transition-duration: 0.2s; |
| | | } |
| | | |
| | | .uni-top-mask { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .uni-bottom-mask { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .uni-center-mask { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .uni-popup__wrapper { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | position: absolute; |
| | | } |
| | | |
| | | .top { |
| | | /* #ifdef H5 */ |
| | | top: var(--window-top); |
| | | /* #endif */ |
| | | /* #ifndef H5 */ |
| | | top: 0; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .bottom { |
| | | bottom: 0; |
| | | } |
| | | |
| | | .uni-popup__wrapper-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: block; |
| | | /* #endif */ |
| | | position: relative; |
| | | /* iphonex 等安全区设置,底部安全区适配 */ |
| | | /* #ifndef APP-NVUE */ |
| | | padding-bottom: constant(safe-area-inset-bottom); |
| | | padding-bottom: env(safe-area-inset-bottom); |
| | | /* #endif */ |
| | | } |
| | | |
| | | .content-ani { |
| | | // transition: transform 0.3s; |
| | | transition-property: transform, opacity; |
| | | transition-duration: 0.2s; |
| | | } |
| | | |
| | | |
| | | .uni-top-content { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | .uni-bottom-content { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | .uni-center-content { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view> |
| | | <view |
| | | ref="uni-rate" |
| | | class="uni-rate" |
| | | > |
| | | <view |
| | | class="uni-rate__icon" |
| | | :style="{ 'margin-right': margin + 'px' }" |
| | | v-for="(star, index) in stars" |
| | | :key="index" |
| | | @touchstart.stop="touchstart" |
| | | @touchmove.stop="touchmove" |
| | | > |
| | | <uni-icons |
| | | :color="color" |
| | | :size="size" |
| | | :type="isFill ? 'star-filled' : 'star'" |
| | | /> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <view |
| | | :style="{ width: star.activeWitch.replace('%','')*size/100+'px'}" |
| | | class="uni-rate__icon-on" |
| | | > |
| | | <uni-icons |
| | | style="text-align: left;" |
| | | :color="disabled?'#ccc':activeColor" |
| | | :size="size" |
| | | type="star-filled" |
| | | /> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <view |
| | | :style="{ width: star.activeWitch}" |
| | | class="uni-rate__icon-on" |
| | | > |
| | | <uni-icons |
| | | :color="disabled?disabledColor:activeColor" |
| | | :size="size" |
| | | type="star-filled" |
| | | /> |
| | | </view> |
| | | <!-- #endif --> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | // #ifdef APP-NVUE |
| | | const dom = uni.requireNativePlugin('dom'); |
| | | // #endif |
| | | import uniIcons from "../uni-icons/uni-icons.vue"; |
| | | /** |
| | | * Rate 评分 |
| | | * @description 评分组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=33 |
| | | * @property {Boolean} isFill = [true|false] 星星的类型,是否为实心类型, 默认为实心 |
| | | * @property {String} color 未选中状态的星星颜色,默认为 "#ececec" |
| | | * @property {String} activeColor 选中状态的星星颜色,默认为 "#ffca3e" |
| | | * @property {String} disabledColor 禁用状态的星星颜色,默认为 "#c0c0c0" |
| | | * @property {Number} size 星星的大小 |
| | | * @property {Number} value/v-model 当前评分 |
| | | * @property {Number} max 最大评分评分数量,目前一分一颗星 |
| | | * @property {Number} margin 星星的间距,单位 px |
| | | * @property {Boolean} disabled = [true|false] 是否为禁用状态,默认为 false |
| | | * @property {Boolean} readonly = [true|false] 是否为只读状态,默认为 false |
| | | * @property {Boolean} allowHalf = [true|false] 是否实现半星,默认为 false |
| | | * @property {Boolean} touchable = [true|false] 是否支持滑动手势,默认为 true |
| | | * @event {Function} change uniRate 的 value 改变时触发事件,e={value:Number} |
| | | */ |
| | | |
| | | export default { |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | name: "UniRate", |
| | | props: { |
| | | isFill: { |
| | | // 星星的类型,是否镂空 |
| | | type: [Boolean, String], |
| | | default: true |
| | | }, |
| | | color: { |
| | | // 星星未选中的颜色 |
| | | type: String, |
| | | default: "#ececec" |
| | | }, |
| | | activeColor: { |
| | | // 星星选中状态颜色 |
| | | type: String, |
| | | default: "#ffca3e" |
| | | }, |
| | | disabledColor: { |
| | | // 星星禁用状态颜色 |
| | | type: String, |
| | | default: "#c0c0c0" |
| | | }, |
| | | size: { |
| | | // 星星的大小 |
| | | type: [Number, String], |
| | | default: 24 |
| | | }, |
| | | value: { |
| | | // 当前评分 |
| | | type: [Number, String], |
| | | default: 1 |
| | | }, |
| | | max: { |
| | | // 最大评分 |
| | | type: [Number, String], |
| | | default: 5 |
| | | }, |
| | | margin: { |
| | | // 星星的间距 |
| | | type: [Number, String], |
| | | default: 0 |
| | | }, |
| | | disabled: { |
| | | // 是否可点击 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | readonly: { |
| | | // 是否只读 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | allowHalf: { |
| | | // 是否显示半星 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | touchable: { |
| | | // 是否支持滑动手势 |
| | | type: [Boolean, String], |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | valueSync: "" |
| | | }; |
| | | }, |
| | | watch: { |
| | | value(newVal) { |
| | | this.valueSync = Number(newVal); |
| | | } |
| | | }, |
| | | computed: { |
| | | stars() { |
| | | const value = this.valueSync ? this.valueSync : 0; |
| | | const starList = []; |
| | | const floorValue = Math.floor(value); |
| | | const ceilValue = Math.ceil(value); |
| | | for (let i = 0; i < this.max; i++) { |
| | | if (floorValue > i) { |
| | | starList.push({ |
| | | activeWitch: "100%" |
| | | }); |
| | | } else if (ceilValue - 1 === i) { |
| | | starList.push({ |
| | | activeWitch: (value - floorValue) * 100 + "%" |
| | | }); |
| | | } else { |
| | | starList.push({ |
| | | activeWitch: "0" |
| | | }); |
| | | } |
| | | } |
| | | return starList; |
| | | } |
| | | }, |
| | | created() { |
| | | this.valueSync = Number(this.value); |
| | | this._rateBoxLeft = 0 |
| | | this._oldValue = null |
| | | }, |
| | | mounted() { |
| | | setTimeout(() => { |
| | | this._getSize() |
| | | }, 100) |
| | | }, |
| | | methods: { |
| | | touchstart(e) { |
| | | if (this.readonly || this.disabled) return |
| | | const { |
| | | clientX, |
| | | screenX |
| | | } = e.changedTouches[0] |
| | | // TODO 做一下兼容,只有 Nvue 下才有 screenX,其他平台式 clientX |
| | | this._getRateCount(clientX || screenX) |
| | | }, |
| | | touchmove(e) { |
| | | if (this.readonly || this.disabled || !this.touchable) return |
| | | const { |
| | | clientX, |
| | | screenX |
| | | } = e.changedTouches[0] |
| | | this._getRateCount(clientX || screenX) |
| | | }, |
| | | /** |
| | | * 获取星星个数 |
| | | */ |
| | | _getRateCount(clientX) { |
| | | const rateMoveRange = clientX - this._rateBoxLeft |
| | | let index = parseInt(rateMoveRange / (this.size + this.margin)) |
| | | index = index < 0 ? 0 : index; |
| | | index = index > this.max ? this.max : index; |
| | | const range = parseInt(rateMoveRange - (this.size + this.margin) * index); |
| | | let value = 0; |
| | | if (this._oldValue === index) return; |
| | | this._oldValue = index; |
| | | |
| | | if (this.allowHalf) { |
| | | if (range > (this.size / 2)) { |
| | | value = index + 1 |
| | | } else { |
| | | value = index + 0.5 |
| | | } |
| | | } else { |
| | | value = index + 1 |
| | | } |
| | | |
| | | value = Math.max(0.5, Math.min(value, this.max)) |
| | | this.valueSync = value |
| | | this._onChange() |
| | | }, |
| | | |
| | | /** |
| | | * 触发动态修改 |
| | | */ |
| | | _onChange() { |
| | | |
| | | this.$emit("input", this.valueSync); |
| | | this.$emit("change", { |
| | | value: this.valueSync |
| | | }); |
| | | }, |
| | | /** |
| | | * 获取星星距离屏幕左侧距离 |
| | | */ |
| | | _getSize() { |
| | | // #ifndef APP-NVUE |
| | | uni.createSelectorQuery() |
| | | .in(this) |
| | | .select('.uni-rate') |
| | | .boundingClientRect() |
| | | .exec(ret => { |
| | | if (ret) { |
| | | this._rateBoxLeft = ret[0].left |
| | | } |
| | | }) |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | dom.getComponentRect(this.$refs['uni-rate'], (ret) => { |
| | | const size = ret.size |
| | | if (size) { |
| | | this._rateBoxLeft = size.left |
| | | } |
| | | }) |
| | | // #endif |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style |
| | | lang="scss" |
| | | scoped |
| | | > |
| | | .uni-rate { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | line-height: 1; |
| | | font-size: 0; |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-rate__icon { |
| | | position: relative; |
| | | line-height: 1; |
| | | font-size: 0; |
| | | } |
| | | |
| | | .uni-rate__icon-on { |
| | | overflow: hidden; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | line-height: 1; |
| | | text-align: left; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-searchbar"> |
| | | <view :style="{borderRadius:radius+'px',backgroundColor: bgColor}" class="uni-searchbar__box" @click="searchClick"> |
| | | <!-- #ifdef MP-ALIPAY --> |
| | | <view class="uni-searchbar__box-icon-search"> |
| | | <uni-icons color="#999999" size="18" type="search" /> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifndef MP-ALIPAY --> |
| | | <uni-icons color="#999999" class="uni-searchbar__box-icon-search" size="18" type="search" /> |
| | | <!-- #endif --> |
| | | <input v-if="show" :focus="showSync" :placeholder="placeholder" :maxlength="maxlength" @confirm="confirm" class="uni-searchbar__box-search-input" |
| | | confirm-type="search" type="text" v-model="searchVal" /> |
| | | <text v-else class="uni-searchbar__text-placeholder">{{ placeholder }}</text> |
| | | <view v-if="show && (clearButton==='always'||clearButton==='auto'&&searchVal!=='')" class="uni-searchbar__box-icon-clear" @click="clear"> |
| | | <uni-icons color="#999999" class="" size="24" type="clear" /> |
| | | </view> |
| | | </view> |
| | | <text @click="cancel" class="uni-searchbar__cancel" v-if="cancelButton ==='always' || show && cancelButton ==='auto'">{{cancelText}}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from "../uni-icons/uni-icons.vue"; |
| | | |
| | | /** |
| | | * SearchBar 搜索栏 |
| | | * @description 评分组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=866 |
| | | * @property {Number} radius 搜索栏圆角 |
| | | * @property {Number} maxlength 输入最大长度 |
| | | * @property {String} placeholder 搜索栏Placeholder |
| | | * @property {String} clearButton = [always|auto|none] 是否显示清除按钮 |
| | | * @value always 一直显示 |
| | | * @value auto 输入框不为空时显示 |
| | | * @value none 一直不显示 |
| | | * @property {String} cancelButton = [always|auto|none] 是否显示取消按钮 |
| | | * @value always 一直显示 |
| | | * @value auto 输入框不为空时显示 |
| | | * @value none 一直不显示 |
| | | * @property {String} cancelText 取消按钮的文字 |
| | | * @property {String} bgColor 输入框背景颜色 |
| | | * @event {Function} confirm uniSearchBar 的输入框 confirm 事件,返回参数为uniSearchBar的value,e={value:Number} |
| | | * @event {Function} input uniSearchBar 的 value 改变时触发事件,返回参数为uniSearchBar的value,e={value:Number} |
| | | * @event {Function} cancel 点击取消按钮时触发事件,返回参数为uniSearchBar的value,e={value:Number} |
| | | */ |
| | | |
| | | export default { |
| | | name: "UniSearchBar", |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | props: { |
| | | placeholder: { |
| | | type: String, |
| | | default: "请输入搜索内容" |
| | | }, |
| | | radius: { |
| | | type: [Number, String], |
| | | default: 5 |
| | | }, |
| | | clearButton: { |
| | | type: String, |
| | | default: "auto" |
| | | }, |
| | | cancelButton: { |
| | | type: String, |
| | | default: "auto" |
| | | }, |
| | | cancelText: { |
| | | type: String, |
| | | default: '取消' |
| | | }, |
| | | bgColor: { |
| | | type: String, |
| | | default: "#F8F8F8" |
| | | }, |
| | | maxlength: { |
| | | type: [Number, String], |
| | | default: 100 |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | show: false, |
| | | showSync: false, |
| | | searchVal: "" |
| | | } |
| | | }, |
| | | watch: { |
| | | searchVal() { |
| | | this.$emit("input", { |
| | | value: this.searchVal |
| | | }) |
| | | } |
| | | }, |
| | | methods: { |
| | | searchClick() { |
| | | if (this.show) { |
| | | return |
| | | } |
| | | this.searchVal = "" |
| | | this.show = true; |
| | | this.$nextTick(() => { |
| | | this.showSync = true; |
| | | }) |
| | | }, |
| | | clear() { |
| | | this.searchVal = "" |
| | | }, |
| | | cancel() { |
| | | this.$emit("cancel", { |
| | | value: this.searchVal |
| | | }); |
| | | this.searchVal = "" |
| | | this.show = false |
| | | this.showSync = false |
| | | // #ifndef APP-PLUS |
| | | uni.hideKeyboard() |
| | | // #endif |
| | | // #ifdef APP-PLUS |
| | | plus.key.hideSoftKeybord() |
| | | // #endif |
| | | }, |
| | | confirm() { |
| | | // #ifndef APP-PLUS |
| | | uni.hideKeyboard(); |
| | | // #endif |
| | | // #ifdef APP-PLUS |
| | | plus.key.hideSoftKeybord() |
| | | // #endif |
| | | this.$emit("confirm", { |
| | | value: this.searchVal |
| | | }) |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $uni-searchbar-height: 36px; |
| | | |
| | | .uni-searchbar { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | position: relative; |
| | | padding: $uni-spacing-col-base; |
| | | background-color: $uni-bg-color; |
| | | } |
| | | |
| | | .uni-searchbar__box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | overflow: hidden; |
| | | position: relative; |
| | | flex: 1; |
| | | justify-content: center; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | height: $uni-searchbar-height; |
| | | padding: 5px 8px 5px 0px; |
| | | border-width: 0.5px; |
| | | border-style: solid; |
| | | border-color: $uni-border-color; |
| | | } |
| | | |
| | | .uni-searchbar__box-icon-search { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | width: 32px; |
| | | justify-content: center; |
| | | align-items: center; |
| | | color: $uni-text-color-placeholder; |
| | | } |
| | | |
| | | .uni-searchbar__box-search-input { |
| | | flex: 1; |
| | | font-size: $uni-font-size-base; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-searchbar__box-icon-clear { |
| | | align-items: center; |
| | | line-height: 24px; |
| | | padding-left: 5px; |
| | | } |
| | | |
| | | .uni-searchbar__text-placeholder { |
| | | font-size: $uni-font-size-base; |
| | | color: $uni-text-color-placeholder; |
| | | margin-left: 5px; |
| | | } |
| | | |
| | | .uni-searchbar__cancel { |
| | | padding-left: 10px; |
| | | line-height: $uni-searchbar-height; |
| | | font-size: 14px; |
| | | color: $uni-text-color; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-section" nvue> |
| | | <view v-if="type" class="uni-section__head"> |
| | | <view :class="type" class="uni-section__head-tag" /> |
| | | </view> |
| | | <view class="uni-section__content"> |
| | | <text :class="{'distraction':!subTitle}" class="uni-section__content-title">{{ title }}</text> |
| | | <text v-if="subTitle" class="uni-section__content-sub">{{ subTitle }}</text> |
| | | </view> |
| | | <slot /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | |
| | | /** |
| | | * Section 标题栏 |
| | | * @description 标题栏 |
| | | * @property {String} type = [line|circle] 标题装饰类型 |
| | | * @value line 竖线 |
| | | * @value circle 圆形 |
| | | * @property {String} title 主标题 |
| | | * @property {String} subTitle 副标题 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniSection', |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | subTitle: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return {} |
| | | }, |
| | | watch: { |
| | | title(newVal) { |
| | | if (uni.report && newVal !== '') { |
| | | uni.report('title', newVal) |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick() { |
| | | this.$emit('click') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .uni-section { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | margin-top: 10px; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: 0 10px; |
| | | height: 50px; |
| | | background-color: $uni-bg-color-grey; |
| | | /* #ifdef APP-NVUE */ |
| | | // border-bottom-color: $uni-border-color; |
| | | // border-bottom-style: solid; |
| | | // border-bottom-width: 0.5px; |
| | | /* #endif */ |
| | | font-weight: normal; |
| | | } |
| | | /* #ifndef APP-NVUE */ |
| | | // .uni-section:after { |
| | | // position: absolute; |
| | | // bottom: 0; |
| | | // right: 0; |
| | | // left: 0; |
| | | // height: 1px; |
| | | // content: ''; |
| | | // -webkit-transform: scaleY(.5); |
| | | // transform: scaleY(.5); |
| | | // background-color: $uni-border-color; |
| | | // } |
| | | /* #endif */ |
| | | |
| | | .uni-section__head { |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .line { |
| | | height: 15px; |
| | | background-color: $uni-text-color-disable; |
| | | border-radius: 5px; |
| | | width: 3px; |
| | | } |
| | | |
| | | .circle { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-top-right-radius: 50px; |
| | | border-top-left-radius: 50px; |
| | | border-bottom-left-radius: 50px; |
| | | border-bottom-right-radius: 50px; |
| | | background-color: $uni-text-color-disable; |
| | | } |
| | | |
| | | .uni-section__content { |
| | | flex-direction: column; |
| | | flex: 1; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-section__content-title { |
| | | font-size: $uni-font-size-base; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .distraction { |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-section__content-sub { |
| | | font-size: $uni-font-size-sm; |
| | | color: $uni-text-color-grey; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view :class="[styleType === 'text'?'segmented-control--text' : 'segmented-control--button' ]" :style="{ borderColor: styleType === 'text' ? '' : activeColor }" |
| | | class="segmented-control"> |
| | | <view v-for="(item, index) in values" :class="[ styleType === 'text'?'segmented-control__item--text': 'segmented-control__item--button' , index === currentIndex&&styleType === 'button'?'segmented-control__item--button--active': '' , index === 0&&styleType === 'button'?'segmented-control__item--button--first': '',index === values.length - 1&&styleType === 'button'?'segmented-control__item--button--last': '' ]" |
| | | :key="index" :style="{ |
| | | backgroundColor: index === currentIndex && styleType === 'button' ? activeColor : '',borderColor: index === currentIndex&&styleType === 'text'||styleType === 'button'?activeColor:'transparent' |
| | | }" |
| | | class="segmented-control__item" @click="_onClick(index)"> |
| | | <text :style="{color: |
| | | index === currentIndex |
| | | ? styleType === 'text' |
| | | ? activeColor |
| | | : '#fff' |
| | | : styleType === 'text' |
| | | ? '#000' |
| | | : activeColor}" |
| | | class="segmented-control__text">{{ item }}</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * SegmentedControl 分段器 |
| | | * @description 用作不同视图的显示 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=54 |
| | | * @property {Number} current 当前选中的tab索引值,从0计数 |
| | | * @property {String} styleType = [button|text] 分段器样式类型 |
| | | * @value button 按钮类型 |
| | | * @value text 文字类型 |
| | | * @property {String} activeColor 选中的标签背景色与边框颜色 |
| | | * @property {Array} values 选项数组 |
| | | * @event {Function} clickItem 组件触发点击事件时触发,e={currentIndex} |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniSegmentedControl', |
| | | props: { |
| | | current: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | values: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | activeColor: { |
| | | type: String, |
| | | default: '#007aff' |
| | | }, |
| | | styleType: { |
| | | type: String, |
| | | default: 'button' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | currentIndex: 0 |
| | | } |
| | | }, |
| | | watch: { |
| | | current(val) { |
| | | if (val !== this.currentIndex) { |
| | | this.currentIndex = val |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.currentIndex = this.current |
| | | }, |
| | | methods: { |
| | | _onClick(index) { |
| | | if (this.currentIndex !== index) { |
| | | this.currentIndex = index |
| | | this.$emit('clickItem', { |
| | | currentIndex: index |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | |
| | | .segmented-control { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | height: 36px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .segmented-control__item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: inline-flex; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | position: relative; |
| | | flex: 1; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .segmented-control__item--button { |
| | | border-style: solid; |
| | | border-top-width: 1px; |
| | | border-bottom-width: 1px; |
| | | border-right-width: 1px; |
| | | border-left-width: 0; |
| | | } |
| | | |
| | | .segmented-control__item--button--first { |
| | | border-left-width: 1px; |
| | | border-top-left-radius: 5px; |
| | | border-bottom-left-radius: 5px; |
| | | } |
| | | |
| | | .segmented-control__item--button--last { |
| | | border-top-right-radius: 5px; |
| | | border-bottom-right-radius: 5px; |
| | | } |
| | | |
| | | .segmented-control__item--text { |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 3px; |
| | | } |
| | | |
| | | .segmented-control__text { |
| | | font-size: 16px; |
| | | line-height: 20px; |
| | | text-align: center; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view :style="{ height: statusBarHeight }" class="uni-status-bar"> |
| | | <slot /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | var statusBarHeight = uni.getSystemInfoSync().statusBarHeight + 'px' |
| | | export default { |
| | | name: 'UniStatusBar', |
| | | data() { |
| | | return { |
| | | statusBarHeight: statusBarHeight |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-status-bar { |
| | | width: 750rpx; |
| | | height: 20px; |
| | | // height: var(--status-bar-height); |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-steps"> |
| | | <view :class="[direction==='column'?'uni-steps__column':'uni-steps__row']"> |
| | | <view :class="[direction==='column'?'uni-steps__column-text-container':'uni-steps__row-text-container']"> |
| | | <view v-for="(item,index) in options" :key="index" :class="[direction==='column'?'uni-steps__column-text':'uni-steps__row-text']"> |
| | | <text :style="{color:index<=active?activeColor:deactiveColor}" :class="[direction==='column'?'uni-steps__column-title':'uni-steps__row-title']">{{item.title}}</text> |
| | | <text :style="{color:index<=active?activeColor:deactiveColor}" :class="[direction==='column'?'uni-steps__column-desc':'uni-steps__row-desc']">{{item.desc}}</text> |
| | | </view> |
| | | </view> |
| | | <view :class="[direction==='column'?'uni-steps__column-container':'uni-steps__row-container']"> |
| | | <view :class="[direction==='column'?'uni-steps__column-line-item':'uni-steps__row-line-item']" v-for="(item,index) in options" |
| | | :key="index"> |
| | | <view :class="[direction==='column'?'uni-steps__column-line':'uni-steps__row-line',direction==='column'?'uni-steps__column-line--before':'uni-steps__row-line--before']" |
| | | :style="{backgroundColor:index<=active&&index!==0?activeColor:index===0?'transparent':deactiveColor}"></view> |
| | | <view :class="[direction==='column'?'uni-steps__column-check':'uni-steps__row-check']" v-if="index === active"> |
| | | <uni-icons :color="activeColor" type="checkbox-filled" size="14"></uni-icons> |
| | | </view> |
| | | <view :class="[direction==='column'?'uni-steps__column-circle':'uni-steps__row-circle']" v-else :style="{backgroundColor:index<active?activeColor:deactiveColor}"></view> |
| | | <view :class="[direction==='column'?'uni-steps__column-line':'uni-steps__row-line',direction==='column'?'uni-steps__column-line--after':'uni-steps__row-line--after']" |
| | | :style="{backgroundColor:index<active&&index!==options.length-1?activeColor:index===options.length-1?'transparent':deactiveColor}"></view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniIcons from '../uni-icons/uni-icons.vue' |
| | | |
| | | /** |
| | | * Steps 步骤条 |
| | | * @description 评分组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=34 |
| | | * @property {Number} active 当前步骤 |
| | | * @property {String} direction = [row|column] 当前步骤 |
| | | * @value row 横向 |
| | | * @value column 纵向 |
| | | * @property {String} activeColor 选中状态的颜色 |
| | | * @property {Array} options 数据源,格式为:[{title:'xxx',desc:'xxx'},{title:'xxx',desc:'xxx'}] |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniSteps', |
| | | components: { |
| | | uniIcons |
| | | }, |
| | | props: { |
| | | direction: { |
| | | // 排列方向 row column |
| | | type: String, |
| | | default: 'row' |
| | | }, |
| | | activeColor: { |
| | | // 激活状态颜色 |
| | | type: String, |
| | | default: '#1aad19' |
| | | }, |
| | | deactiveColor: { |
| | | // 未激活状态颜色 |
| | | type: String, |
| | | default: '#999999' |
| | | }, |
| | | active: { |
| | | // 当前步骤 |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | options: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | } // 数据 |
| | | }, |
| | | data() { |
| | | return {} |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-steps { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | width: 100%; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | flex: 1; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-steps__row { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-steps__column { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row-reverse; |
| | | } |
| | | |
| | | .uni-steps__row-text-container { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-steps__column-text-container { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-steps__row-text { |
| | | /* #ifndef APP-NVUE */ |
| | | display: inline-flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-steps__column-text { |
| | | padding: 6px 0px; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | border-bottom-color: $uni-border-color; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-steps__row-title { |
| | | font-size: $uni-font-size-base; |
| | | line-height: 16px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .uni-steps__column-title { |
| | | font-size: $uni-font-size-base; |
| | | text-align: left; |
| | | line-height: 18px; |
| | | } |
| | | |
| | | .uni-steps__row-desc { |
| | | font-size: 12px; |
| | | line-height: 14px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .uni-steps__column-desc { |
| | | font-size: $uni-font-size-sm; |
| | | text-align: left; |
| | | line-height: 18px; |
| | | } |
| | | |
| | | .uni-steps__row-container { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-steps__column-container { |
| | | /* #ifndef APP-NVUE */ |
| | | display: inline-flex; |
| | | /* #endif */ |
| | | width: 30px; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-steps__row-line-item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: inline-flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex: 1; |
| | | height: 14px; |
| | | line-height: 14px; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .uni-steps__column-line-item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | flex: 1; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .uni-steps__row-line { |
| | | flex: 1; |
| | | height: 1px; |
| | | background-color: $uni-text-color-grey; |
| | | } |
| | | |
| | | .uni-steps__column-line { |
| | | width: 1px; |
| | | background-color: $uni-text-color-grey; |
| | | } |
| | | |
| | | .uni-steps__row-line--after { |
| | | transform: translateX(1px); |
| | | } |
| | | |
| | | .uni-steps__column-line--after { |
| | | flex: 1; |
| | | transform: translate(0px, 1px); |
| | | } |
| | | |
| | | .uni-steps__row-line--before { |
| | | transform: translateX(-1px); |
| | | } |
| | | |
| | | .uni-steps__column-line--before { |
| | | height: 6px; |
| | | transform: translate(0px, -1px); |
| | | } |
| | | |
| | | .uni-steps__row-circle { |
| | | width: 5px; |
| | | height: 5px; |
| | | border-radius: 100px; |
| | | background-color: $uni-text-color-grey; |
| | | margin: 0px 3px; |
| | | } |
| | | |
| | | .uni-steps__column-circle { |
| | | width: 5px; |
| | | height: 5px; |
| | | border-radius: 100px; |
| | | background-color: $uni-text-color-grey; |
| | | margin: 4px 0px 5px 0px; |
| | | } |
| | | |
| | | .uni-steps__row-check { |
| | | margin: 0px 6px; |
| | | } |
| | | |
| | | .uni-steps__column-check { |
| | | height: 14px; |
| | | line-height: 14px; |
| | | margin: 2px 0px; |
| | | } |
| | | </style> |
New file |
| | |
| | | const BindingX = uni.requireNativePlugin('bindingx'); |
| | | const dom = uni.requireNativePlugin('dom'); |
| | | const animation = uni.requireNativePlugin('animation'); |
| | | |
| | | export default { |
| | | data() { |
| | | return {} |
| | | }, |
| | | |
| | | watch: { |
| | | show(newVal) { |
| | | if (this.autoClose) return |
| | | if (this.stop) return |
| | | this.stop = true |
| | | if (newVal) { |
| | | this.open(newVal) |
| | | } else { |
| | | this.close() |
| | | } |
| | | }, |
| | | leftOptions() { |
| | | this.getSelectorQuery() |
| | | this.init() |
| | | }, |
| | | rightOptions(newVal) { |
| | | this.init() |
| | | } |
| | | }, |
| | | created() { |
| | | if (this.swipeaction.children !== undefined) { |
| | | this.swipeaction.children.push(this) |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.box = this.getEl(this.$refs['selector-box--hock']) |
| | | this.selector = this.getEl(this.$refs['selector-content--hock']); |
| | | this.leftButton = this.getEl(this.$refs['selector-left-button--hock']); |
| | | this.rightButton = this.getEl(this.$refs['selector-right-button--hock']); |
| | | this.init() |
| | | }, |
| | | beforeDestroy() { |
| | | this.swipeaction.children.forEach((item, index) => { |
| | | if (item === this) { |
| | | this.swipeaction.children.splice(index, 1) |
| | | } |
| | | }) |
| | | }, |
| | | methods: { |
| | | init() { |
| | | this.$nextTick(() => { |
| | | this.x = 0 |
| | | this.button = { |
| | | show: false |
| | | } |
| | | setTimeout(() => { |
| | | this.getSelectorQuery() |
| | | }, 200) |
| | | }) |
| | | }, |
| | | onClick(index, item, position) { |
| | | this.$emit('click', { |
| | | content: item, |
| | | index, |
| | | position |
| | | }) |
| | | }, |
| | | touchstart(e) { |
| | | // 每次只触发一次,避免多次监听造成闪烁 |
| | | if (this.stop) return |
| | | this.stop = true |
| | | if (this.autoClose) { |
| | | this.swipeaction.closeOther(this) |
| | | } |
| | | |
| | | const leftWidth = this.button.left.width |
| | | const rightWidth = this.button.right.width |
| | | let expression = this.range(this.x, -rightWidth, leftWidth) |
| | | let leftExpression = this.range(this.x - leftWidth, -leftWidth, 0) |
| | | let rightExpression = this.range(this.x + rightWidth, 0, rightWidth) |
| | | |
| | | this.eventpan = BindingX.bind({ |
| | | anchor: this.box, |
| | | eventType: 'pan', |
| | | props: [{ |
| | | element: this.selector, |
| | | property: 'transform.translateX', |
| | | expression |
| | | }, { |
| | | element: this.leftButton, |
| | | property: 'transform.translateX', |
| | | expression: leftExpression |
| | | }, { |
| | | element: this.rightButton, |
| | | property: 'transform.translateX', |
| | | expression: rightExpression |
| | | }, ] |
| | | }, (e) => { |
| | | // nope |
| | | if (e.state === 'end') { |
| | | this.x = e.deltaX + this.x; |
| | | this.isclick = true |
| | | this.bindTiming(e.deltaX) |
| | | } |
| | | }); |
| | | }, |
| | | touchend(e) { |
| | | if (this.isopen !== 'none' && !this.isclick) { |
| | | this.open('none') |
| | | } |
| | | }, |
| | | bindTiming(x) { |
| | | const left = this.x |
| | | const leftWidth = this.button.left.width |
| | | const rightWidth = this.button.right.width |
| | | const threshold = this.threshold |
| | | if (!this.isopen || this.isopen === 'none') { |
| | | if (left > threshold) { |
| | | this.open('left') |
| | | } else if (left < -threshold) { |
| | | this.open('right') |
| | | } else { |
| | | this.open('none') |
| | | } |
| | | } else { |
| | | if ((x > -leftWidth && x < 0) || x > rightWidth) { |
| | | if ((x > -threshold && x < 0) || (x - rightWidth > threshold)) { |
| | | this.open('left') |
| | | } else { |
| | | this.open('none') |
| | | } |
| | | } else { |
| | | if ((x < threshold && x > 0) || (x + leftWidth < -threshold)) { |
| | | this.open('right') |
| | | } else { |
| | | this.open('none') |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 移动范围 |
| | | * @param {Object} num |
| | | * @param {Object} mix |
| | | * @param {Object} max |
| | | */ |
| | | range(num, mix, max) { |
| | | return `min(max(x+${num}, ${mix}), ${max})` |
| | | }, |
| | | |
| | | /** |
| | | * 开启swipe |
| | | */ |
| | | open(type) { |
| | | this.animation(type) |
| | | }, |
| | | |
| | | /** |
| | | * 关闭swipe |
| | | */ |
| | | close() { |
| | | this.animation('none') |
| | | }, |
| | | |
| | | /** |
| | | * 开启关闭动画 |
| | | * @param {Object} type |
| | | */ |
| | | animation(type) { |
| | | const time = 300 |
| | | const leftWidth = this.button.left.width |
| | | const rightWidth = this.button.right.width |
| | | if (this.eventpan && this.eventpan.token) { |
| | | BindingX.unbind({ |
| | | token: this.eventpan.token, |
| | | eventType: 'pan' |
| | | }) |
| | | } |
| | | |
| | | switch (type) { |
| | | case 'left': |
| | | Promise.all([ |
| | | this.move(this.selector, leftWidth), |
| | | this.move(this.leftButton, 0), |
| | | this.move(this.rightButton, rightWidth * 2) |
| | | ]).then(() => { |
| | | this.setEmit(leftWidth, type) |
| | | }) |
| | | break |
| | | case 'right': |
| | | Promise.all([ |
| | | this.move(this.selector, -rightWidth), |
| | | this.move(this.leftButton, -leftWidth * 2), |
| | | this.move(this.rightButton, 0) |
| | | ]).then(() => { |
| | | this.setEmit(-rightWidth, type) |
| | | }) |
| | | break |
| | | default: |
| | | Promise.all([ |
| | | this.move(this.selector, 0), |
| | | this.move(this.leftButton, -leftWidth), |
| | | this.move(this.rightButton, rightWidth) |
| | | ]).then(() => { |
| | | this.setEmit(0, type) |
| | | }) |
| | | |
| | | } |
| | | }, |
| | | setEmit(x, type) { |
| | | const leftWidth = this.button.left.width |
| | | const rightWidth = this.button.right.width |
| | | this.isopen = this.isopen || 'none' |
| | | this.stop = false |
| | | this.isclick = false |
| | | // 只有状态不一致才会返回结果 |
| | | if (this.isopen !== type && this.x !== x) { |
| | | if (type === 'left' && leftWidth > 0) { |
| | | this.$emit('change', 'left') |
| | | } |
| | | if (type === 'right' && rightWidth > 0) { |
| | | this.$emit('change', 'right') |
| | | } |
| | | if (type === 'none') { |
| | | this.$emit('change', 'none') |
| | | } |
| | | } |
| | | this.x = x |
| | | this.isopen = type |
| | | }, |
| | | move(ref, value) { |
| | | return new Promise((resolve, reject) => { |
| | | animation.transition(ref, { |
| | | styles: { |
| | | transform: `translateX(${value})`, |
| | | }, |
| | | duration: 150, //ms |
| | | timingFunction: 'linear', |
| | | needLayout: false, |
| | | delay: 0 //ms |
| | | }, function(res) { |
| | | resolve(res) |
| | | }) |
| | | }) |
| | | |
| | | }, |
| | | |
| | | /** |
| | | * 获取ref |
| | | * @param {Object} el |
| | | */ |
| | | getEl(el) { |
| | | return el.ref |
| | | }, |
| | | /** |
| | | * 获取节点信息 |
| | | */ |
| | | getSelectorQuery() { |
| | | Promise.all([ |
| | | this.getDom('left'), |
| | | this.getDom('right'), |
| | | ]).then((data) => { |
| | | let show = 'none' |
| | | if (this.autoClose) { |
| | | show = 'none' |
| | | } else { |
| | | show = this.show |
| | | } |
| | | |
| | | if (show === 'none') { |
| | | // this.close() |
| | | } else { |
| | | this.open(show) |
| | | } |
| | | |
| | | }) |
| | | |
| | | }, |
| | | getDom(str) { |
| | | return new Promise((resolve, reject) => { |
| | | dom.getComponentRect(this.$refs[`selector-${str}-button--hock`], (data) => { |
| | | if (data) { |
| | | this.button[str] = data.size |
| | | resolve(data) |
| | | } else { |
| | | reject() |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | var MIN_DISTANCE = 10; |
| | | |
| | | /** |
| | | * 监听页面内值的变化,主要用于动态开关swipe-action |
| | | * @param {Object} newValue |
| | | * @param {Object} oldValue |
| | | * @param {Object} ownerInstance |
| | | * @param {Object} instance |
| | | */ |
| | | function sizeReady(newValue, oldValue, ownerInstance, instance) { |
| | | var state = instance.getState() |
| | | var buttonPositions = JSON.parse(newValue) |
| | | if (!buttonPositions || !buttonPositions.data || buttonPositions.data.length === 0) return |
| | | state.leftWidth = buttonPositions.data[0].width |
| | | state.rightWidth = buttonPositions.data[1].width |
| | | state.threshold = instance.getDataset().threshold |
| | | |
| | | if (buttonPositions.show && buttonPositions.show !== 'none') { |
| | | openState(buttonPositions.show, instance, ownerInstance) |
| | | return |
| | | } |
| | | |
| | | if (state.left) { |
| | | openState('none', instance, ownerInstance) |
| | | } |
| | | resetTouchStatus(instance) |
| | | } |
| | | |
| | | /** |
| | | * 开始触摸操作 |
| | | * @param {Object} e |
| | | * @param {Object} ins |
| | | */ |
| | | function touchstart(e, ins) { |
| | | var instance = e.instance; |
| | | var disabled = instance.getDataset().disabled |
| | | var state = instance.getState(); |
| | | // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 |
| | | disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; |
| | | if (disabled) return |
| | | // 开始触摸时移除动画类 |
| | | instance.requestAnimationFrame(function(){ |
| | | instance.removeClass('ani'); |
| | | ins.callMethod('closeSwipe'); |
| | | }) |
| | | |
| | | // 记录上次的位置 |
| | | state.x = state.left || 0 |
| | | // 计算滑动开始位置 |
| | | stopTouchStart(e, ins) |
| | | } |
| | | |
| | | /** |
| | | * 开始滑动操作 |
| | | * @param {Object} e |
| | | * @param {Object} ownerInstance |
| | | */ |
| | | function touchmove(e, ownerInstance) { |
| | | |
| | | var instance = e.instance; |
| | | var disabled = instance.getDataset().disabled |
| | | var state = instance.getState() |
| | | // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 |
| | | disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; |
| | | if (disabled) return |
| | | // 是否可以滑动页面 |
| | | stopTouchMove(e); |
| | | if (state.direction !== 'horizontal') { |
| | | return; |
| | | } |
| | | |
| | | if (e.preventDefault) { |
| | | // 阻止页面滚动 |
| | | e.preventDefault() |
| | | } |
| | | |
| | | move(state.x + state.deltaX, instance, ownerInstance) |
| | | } |
| | | |
| | | /** |
| | | * 结束触摸操作 |
| | | * @param {Object} e |
| | | * @param {Object} ownerInstance |
| | | */ |
| | | function touchend(e, ownerInstance) { |
| | | var instance = e.instance; |
| | | var disabled = instance.getDataset().disabled |
| | | var state = instance.getState() |
| | | // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 |
| | | disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; |
| | | |
| | | if (disabled) return |
| | | // 滑动过程中触摸结束,通过阙值判断是开启还是关闭 |
| | | // fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13 |
| | | moveDirection(state.left, instance, ownerInstance) |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 设置移动距离 |
| | | * @param {Object} value |
| | | * @param {Object} instance |
| | | * @param {Object} ownerInstance |
| | | */ |
| | | function move(value, instance, ownerInstance) { |
| | | value = value || 0 |
| | | var state = instance.getState() |
| | | var leftWidth = state.leftWidth |
| | | var rightWidth = state.rightWidth |
| | | // 获取可滑动范围 |
| | | state.left = range(value, -rightWidth, leftWidth); |
| | | instance.requestAnimationFrame(function(){ |
| | | instance.setStyle({ |
| | | transform: 'translateX(' + state.left + 'px)', |
| | | '-webkit-transform': 'translateX(' + state.left + 'px)' |
| | | }) |
| | | }) |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 获取范围 |
| | | * @param {Object} num |
| | | * @param {Object} min |
| | | * @param {Object} max |
| | | */ |
| | | function range(num, min, max) { |
| | | return Math.min(Math.max(num, min), max); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 移动方向判断 |
| | | * @param {Object} left |
| | | * @param {Object} value |
| | | * @param {Object} ownerInstance |
| | | * @param {Object} ins |
| | | */ |
| | | function moveDirection(left, ins, ownerInstance) { |
| | | var state = ins.getState() |
| | | var threshold = state.threshold |
| | | var position = state.position |
| | | var isopen = state.isopen || 'none' |
| | | var leftWidth = state.leftWidth |
| | | var rightWidth = state.rightWidth |
| | | if (state.deltaX === 0) { |
| | | openState('none', ins, ownerInstance) |
| | | return |
| | | } |
| | | if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && rightWidth + |
| | | left < threshold)) { |
| | | // right |
| | | openState('right', ins, ownerInstance) |
| | | } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && |
| | | leftWidth - left < threshold)) { |
| | | // left |
| | | openState('left', ins, ownerInstance) |
| | | } else { |
| | | // default |
| | | openState('none', ins, ownerInstance) |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 开启状态 |
| | | * @param {Boolean} type |
| | | * @param {Object} ins |
| | | * @param {Object} ownerInstance |
| | | */ |
| | | function openState(type, ins, ownerInstance) { |
| | | var state = ins.getState() |
| | | var position = state.position |
| | | var leftWidth = state.leftWidth |
| | | var rightWidth = state.rightWidth |
| | | var left = '' |
| | | state.isopen = state.isopen ? state.isopen : 'none' |
| | | switch (type) { |
| | | case "left": |
| | | left = leftWidth |
| | | break |
| | | case "right": |
| | | left = -rightWidth |
| | | break |
| | | default: |
| | | left = 0 |
| | | } |
| | | |
| | | // && !state.throttle |
| | | |
| | | if (state.isopen !== type ) { |
| | | state.throttle = true |
| | | ownerInstance.callMethod('change', { |
| | | open: type |
| | | }) |
| | | |
| | | } |
| | | |
| | | state.isopen = type |
| | | // 添加动画类 |
| | | ins.requestAnimationFrame(function(){ |
| | | ins.addClass('ani'); |
| | | move(left, ins, ownerInstance) |
| | | }) |
| | | // 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的 |
| | | } |
| | | |
| | | |
| | | function getDirection(x, y) { |
| | | if (x > y && x > MIN_DISTANCE) { |
| | | return 'horizontal'; |
| | | } |
| | | if (y > x && y > MIN_DISTANCE) { |
| | | return 'vertical'; |
| | | } |
| | | return ''; |
| | | } |
| | | |
| | | /** |
| | | * 重置滑动状态 |
| | | * @param {Object} event |
| | | */ |
| | | function resetTouchStatus(instance) { |
| | | var state = instance.getState(); |
| | | state.direction = ''; |
| | | state.deltaX = 0; |
| | | state.deltaY = 0; |
| | | state.offsetX = 0; |
| | | state.offsetY = 0; |
| | | } |
| | | |
| | | /** |
| | | * 设置滑动开始位置 |
| | | * @param {Object} event |
| | | */ |
| | | function stopTouchStart(event) { |
| | | var instance = event.instance; |
| | | var state = instance.getState(); |
| | | resetTouchStatus(instance); |
| | | var touch = event.touches[0]; |
| | | state.startX = touch.clientX; |
| | | state.startY = touch.clientY; |
| | | } |
| | | |
| | | /** |
| | | * 滑动中,是否禁止打开 |
| | | * @param {Object} event |
| | | */ |
| | | function stopTouchMove(event) { |
| | | var instance = event.instance; |
| | | var state = instance.getState(); |
| | | var touch = event.touches[0]; |
| | | state.deltaX = touch.clientX - state.startX; |
| | | state.deltaY = touch.clientY - state.startY; |
| | | state.offsetX = Math.abs(state.deltaX); |
| | | state.offsetY = Math.abs(state.deltaY); |
| | | state.direction = state.direction || getDirection(state.offsetX, state.offsetY); |
| | | } |
| | | |
| | | |
| | | module.exports = { |
| | | sizeReady: sizeReady, |
| | | touchstart: touchstart, |
| | | touchmove: touchmove, |
| | | touchend: touchend |
| | | } |
New file |
| | |
| | | export default { |
| | | data() { |
| | | return { |
| | | x: 0, |
| | | transition: false, |
| | | width: 0, |
| | | viewWidth: 0, |
| | | swipeShow: 0 |
| | | } |
| | | }, |
| | | watch: { |
| | | show(newVal) { |
| | | if (this.autoClose) return |
| | | if (newVal && newVal !== 'none' ) { |
| | | this.transition = true |
| | | this.open(newVal) |
| | | } else { |
| | | this.close() |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | if (this.swipeaction.children !== undefined) { |
| | | this.swipeaction.children.push(this) |
| | | } |
| | | }, |
| | | |
| | | beforeDestroy() { |
| | | this.swipeaction.children.forEach((item, index) => { |
| | | if (item === this) { |
| | | this.swipeaction.children.splice(index, 1) |
| | | } |
| | | }) |
| | | }, |
| | | mounted() { |
| | | this.isopen = false |
| | | setTimeout(() => { |
| | | this.getQuerySelect() |
| | | }, 50) |
| | | }, |
| | | methods: { |
| | | appTouchStart(e) { |
| | | const { |
| | | clientX |
| | | } = e.changedTouches[0] |
| | | this.clientX = clientX |
| | | this.timestamp = new Date().getTime() |
| | | }, |
| | | appTouchEnd(e, index, item, position) { |
| | | const { |
| | | clientX |
| | | } = e.changedTouches[0] |
| | | // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 |
| | | let diff = Math.abs(this.clientX - clientX) |
| | | let time = (new Date().getTime()) - this.timestamp |
| | | if (diff < 40 && time < 300) { |
| | | this.$emit('click', { |
| | | content: item, |
| | | index, |
| | | position |
| | | }) |
| | | } |
| | | }, |
| | | // onClick(index, item, position) { |
| | | // this.$emit('click', { |
| | | // content: item, |
| | | // index, |
| | | // position |
| | | // }) |
| | | // }, |
| | | /** |
| | | * 移动触发 |
| | | * @param {Object} e |
| | | */ |
| | | onChange(e) { |
| | | this.moveX = e.detail.x |
| | | this.isclose = false |
| | | }, |
| | | touchstart(e) { |
| | | this.transition = false |
| | | this.isclose = true |
| | | this.autoClose && this.swipeaction.closeOther(this) |
| | | }, |
| | | touchmove(e) {}, |
| | | touchend(e) { |
| | | // 0的位置什么都不执行 |
| | | if (this.isclose && this.isopen === 'none') return |
| | | if (this.isclose && this.isopen !== 'none') { |
| | | this.transition = true |
| | | this.close() |
| | | } else { |
| | | this.move(this.moveX + this.leftWidth) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 移动 |
| | | * @param {Object} moveX |
| | | */ |
| | | move(moveX) { |
| | | // 打开关闭的处理逻辑不太一样 |
| | | this.transition = true |
| | | // 未打开状态 |
| | | if (!this.isopen || this.isopen === 'none') { |
| | | if (moveX > this.threshold) { |
| | | this.open('left') |
| | | } else if (moveX < -this.threshold) { |
| | | this.open('right') |
| | | } else { |
| | | this.close() |
| | | } |
| | | } else { |
| | | if (moveX < 0 && moveX < this.rightWidth) { |
| | | const rightX = this.rightWidth + moveX |
| | | if (rightX < this.threshold) { |
| | | this.open('right') |
| | | } else { |
| | | this.close() |
| | | } |
| | | } else if (moveX > 0 && moveX < this.leftWidth) { |
| | | const leftX = this.leftWidth - moveX |
| | | if (leftX < this.threshold) { |
| | | this.open('left') |
| | | } else { |
| | | this.close() |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | }, |
| | | |
| | | /** |
| | | * 打开 |
| | | */ |
| | | open(type) { |
| | | this.x = this.moveX |
| | | this.animation(type) |
| | | }, |
| | | |
| | | /** |
| | | * 关闭 |
| | | */ |
| | | close() { |
| | | this.x = this.moveX |
| | | // TODO 解决 x 值不更新的问题,所以会多触发一次 nextTick ,待优化 |
| | | this.$nextTick(() => { |
| | | this.x = -this.leftWidth |
| | | if(this.isopen!=='none'){ |
| | | this.$emit('change', 'none') |
| | | } |
| | | this.isopen = 'none' |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * 执行结束动画 |
| | | * @param {Object} type |
| | | */ |
| | | animation(type) { |
| | | this.$nextTick(() => { |
| | | if (type === 'left') { |
| | | this.x = 0 |
| | | } else { |
| | | this.x = -this.rightWidth - this.leftWidth |
| | | } |
| | | |
| | | if(this.isopen!==type){ |
| | | this.$emit('change', type) |
| | | } |
| | | this.isopen = type |
| | | }) |
| | | |
| | | }, |
| | | getSlide(x) {}, |
| | | getQuerySelect() { |
| | | const query = uni.createSelectorQuery().in(this); |
| | | query.selectAll('.movable-view--hock').boundingClientRect(data => { |
| | | this.leftWidth = data[1].width |
| | | this.rightWidth = data[2].width |
| | | this.width = data[0].width |
| | | this.viewWidth = this.width + this.rightWidth + this.leftWidth |
| | | if (this.leftWidth === 0) { |
| | | // TODO 疑似bug ,初始化的时候如果x 是0,会导致移动位置错误,所以让元素超出一点 |
| | | this.x = -0.1 |
| | | } else { |
| | | this.x = -this.leftWidth |
| | | } |
| | | this.moveX = this.x |
| | | this.$nextTick(() => { |
| | | this.swipeShow = 1 |
| | | }) |
| | | |
| | | if (!this.buttonWidth) { |
| | | this.disabledView = true |
| | | } |
| | | |
| | | if (this.autoClose) return |
| | | if (this.show !== 'none') { |
| | | this.transition = true |
| | | this.open(this.shows) |
| | | } |
| | | }).exec(); |
| | | |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | const MIN_DISTANCE = 10; |
| | | export default { |
| | | data() { |
| | | return { |
| | | uniShow: false, |
| | | left: 0, |
| | | buttonShow: 'none', |
| | | ani: false, |
| | | moveLeft:'' |
| | | } |
| | | }, |
| | | watch: { |
| | | show(newVal) { |
| | | if (this.autoClose) return |
| | | this.openState(newVal) |
| | | }, |
| | | left(){ |
| | | this.moveLeft = `translateX(${this.left}px)` |
| | | }, |
| | | buttonShow(newVal){ |
| | | if (this.autoClose) return |
| | | this.openState(newVal) |
| | | }, |
| | | leftOptions() { |
| | | this.init() |
| | | }, |
| | | rightOptions() { |
| | | this.init() |
| | | } |
| | | }, |
| | | mounted() { |
| | | // this.position = {} |
| | | if (this.swipeaction.children !== undefined) { |
| | | this.swipeaction.children.push(this) |
| | | } |
| | | this.init() |
| | | }, |
| | | beforeDestoy() { |
| | | this.swipeaction.children.forEach((item, index) => { |
| | | if (item === this) { |
| | | this.swipeaction.children.splice(index, 1) |
| | | } |
| | | }) |
| | | }, |
| | | methods: { |
| | | init(){ |
| | | clearTimeout(this.timer) |
| | | this.timer = setTimeout(() => { |
| | | this.getSelectorQuery() |
| | | }, 100) |
| | | // 移动距离 |
| | | this.left = 0 |
| | | this.x = 0 |
| | | }, |
| | | closeSwipe(e) { |
| | | if (!this.autoClose) return |
| | | this.swipeaction.closeOther(this) |
| | | }, |
| | | appTouchStart(e) { |
| | | const { |
| | | clientX |
| | | } = e.changedTouches[0] |
| | | this.clientX = clientX |
| | | this.timestamp = new Date().getTime() |
| | | }, |
| | | appTouchEnd(e, index, item, position) { |
| | | const { |
| | | clientX |
| | | } = e.changedTouches[0] |
| | | // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 |
| | | let diff = Math.abs(this.clientX - clientX) |
| | | let time = (new Date().getTime()) - this.timestamp |
| | | if (diff < 40 && time < 300) { |
| | | this.$emit('click', { |
| | | content: item, |
| | | index, |
| | | position |
| | | }) |
| | | } |
| | | }, |
| | | touchstart(e) { |
| | | if (this.disabled) return |
| | | this.ani = false |
| | | this.x = this.left || 0 |
| | | this.stopTouchStart(e) |
| | | this.autoClose && this.closeSwipe() |
| | | }, |
| | | touchmove(e) { |
| | | if (this.disabled) return |
| | | // 是否可以滑动页面 |
| | | this.stopTouchMove(e); |
| | | if (this.direction !== 'horizontal') { |
| | | return; |
| | | } |
| | | |
| | | this.move(this.x + this.deltaX) |
| | | }, |
| | | touchend() { |
| | | if (this.disabled) return |
| | | this.moveDirection(this.left) |
| | | }, |
| | | /** |
| | | * 设置移动距离 |
| | | * @param {Object} value |
| | | */ |
| | | move(value) { |
| | | value = value || 0 |
| | | const leftWidth = this.leftWidth |
| | | const rightWidth = this.rightWidth |
| | | // 获取可滑动范围 |
| | | this.left = this.range(value, -rightWidth, leftWidth); |
| | | }, |
| | | |
| | | /** |
| | | * 获取范围 |
| | | * @param {Object} num |
| | | * @param {Object} min |
| | | * @param {Object} max |
| | | */ |
| | | range(num, min, max) { |
| | | return Math.min(Math.max(num, min), max); |
| | | }, |
| | | /** |
| | | * 移动方向判断 |
| | | * @param {Object} left |
| | | * @param {Object} value |
| | | */ |
| | | moveDirection(left) { |
| | | const threshold = this.threshold |
| | | const isopen = this.isopen || 'none' |
| | | const leftWidth = this.leftWidth |
| | | const rightWidth = this.rightWidth |
| | | if (this.deltaX === 0) { |
| | | this.openState('none') |
| | | return |
| | | } |
| | | if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && rightWidth + |
| | | left < threshold)) { |
| | | // right |
| | | this.openState('right') |
| | | } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && |
| | | leftWidth - left < threshold)) { |
| | | // left |
| | | this.openState('left') |
| | | } else { |
| | | // default |
| | | this.openState('none') |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 开启状态 |
| | | * @param {Boolean} type |
| | | */ |
| | | openState(type) { |
| | | const leftWidth = this.leftWidth |
| | | const rightWidth = this.rightWidth |
| | | let left = '' |
| | | this.isopen = this.isopen ? this.isopen : 'none' |
| | | switch (type) { |
| | | case "left": |
| | | left = leftWidth |
| | | break |
| | | case "right": |
| | | left = -rightWidth |
| | | break |
| | | default: |
| | | left = 0 |
| | | } |
| | | |
| | | |
| | | if (this.isopen !== type) { |
| | | this.throttle = true |
| | | this.$emit('change', type) |
| | | } |
| | | |
| | | this.isopen = type |
| | | // 添加动画类 |
| | | this.ani = true |
| | | this.$nextTick(() => { |
| | | this.move(left) |
| | | }) |
| | | // 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的 |
| | | }, |
| | | close() { |
| | | this.openState('none') |
| | | }, |
| | | getDirection(x, y) { |
| | | if (x > y && x > MIN_DISTANCE) { |
| | | return 'horizontal'; |
| | | } |
| | | if (y > x && y > MIN_DISTANCE) { |
| | | return 'vertical'; |
| | | } |
| | | return ''; |
| | | }, |
| | | |
| | | /** |
| | | * 重置滑动状态 |
| | | * @param {Object} event |
| | | */ |
| | | resetTouchStatus() { |
| | | this.direction = ''; |
| | | this.deltaX = 0; |
| | | this.deltaY = 0; |
| | | this.offsetX = 0; |
| | | this.offsetY = 0; |
| | | }, |
| | | |
| | | /** |
| | | * 设置滑动开始位置 |
| | | * @param {Object} event |
| | | */ |
| | | stopTouchStart(event) { |
| | | this.resetTouchStatus(); |
| | | const touch = event.touches[0]; |
| | | this.startX = touch.clientX; |
| | | this.startY = touch.clientY; |
| | | }, |
| | | |
| | | /** |
| | | * 滑动中,是否禁止打开 |
| | | * @param {Object} event |
| | | */ |
| | | stopTouchMove(event) { |
| | | const touch = event.touches[0]; |
| | | this.deltaX = touch.clientX - this.startX; |
| | | this.deltaY = touch.clientY - this.startY; |
| | | this.offsetX = Math.abs(this.deltaX); |
| | | this.offsetY = Math.abs(this.deltaY); |
| | | this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY); |
| | | }, |
| | | |
| | | getSelectorQuery() { |
| | | const views = uni.createSelectorQuery().in(this) |
| | | views |
| | | .selectAll('.uni-swipe_button-group') |
| | | .boundingClientRect(data => { |
| | | let show = 'none' |
| | | if (this.autoClose) { |
| | | show = 'none' |
| | | } else { |
| | | show = this.show |
| | | } |
| | | this.leftWidth = data[0].width || 0 |
| | | this.rightWidth = data[1].width || 0 |
| | | this.buttonShow = show |
| | | }) |
| | | .exec() |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | export default { |
| | | data() { |
| | | return { |
| | | position: [], |
| | | button: {}, |
| | | btn: "[]" |
| | | } |
| | | }, |
| | | // computed: { |
| | | // pos() { |
| | | // return JSON.stringify(this.position) |
| | | // }, |
| | | // btn() { |
| | | // return JSON.stringify(this.button) |
| | | // } |
| | | // }, |
| | | watch: { |
| | | button: { |
| | | handler(newVal) { |
| | | this.btn = JSON.stringify(newVal) |
| | | }, |
| | | deep: true |
| | | }, |
| | | show(newVal) { |
| | | if (this.autoClose) return |
| | | if (!this.button) { |
| | | this.init() |
| | | return |
| | | } |
| | | this.button.show = newVal |
| | | }, |
| | | leftOptions() { |
| | | this.init() |
| | | }, |
| | | rightOptions() { |
| | | this.init() |
| | | } |
| | | }, |
| | | created() { |
| | | if (this.swipeaction.children !== undefined) { |
| | | this.swipeaction.children.push(this) |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | }, |
| | | beforeDestroy() { |
| | | this.swipeaction.children.forEach((item, index) => { |
| | | if (item === this) { |
| | | this.swipeaction.children.splice(index, 1) |
| | | } |
| | | }) |
| | | }, |
| | | methods: { |
| | | init() { |
| | | clearTimeout(this.swipetimer) |
| | | this.swipetimer = setTimeout(() => { |
| | | this.getButtonSize() |
| | | }, 50) |
| | | }, |
| | | closeSwipe(e) { |
| | | if (!this.autoClose) return |
| | | this.swipeaction.closeOther(this) |
| | | }, |
| | | |
| | | change(e) { |
| | | this.$emit('change', e.open) |
| | | let show = this.button.show |
| | | if (show !== e.open) { |
| | | this.button.show = e.open |
| | | } |
| | | |
| | | }, |
| | | |
| | | appTouchStart(e) { |
| | | const { |
| | | clientX |
| | | } = e.changedTouches[0] |
| | | this.clientX = clientX |
| | | this.timestamp = new Date().getTime() |
| | | }, |
| | | appTouchEnd(e, index, item, position) { |
| | | const { |
| | | clientX |
| | | } = e.changedTouches[0] |
| | | // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 |
| | | let diff = Math.abs(this.clientX - clientX) |
| | | let time = (new Date().getTime()) - this.timestamp |
| | | if (diff < 40 && time < 300) { |
| | | this.$emit('click', { |
| | | content: item, |
| | | index, |
| | | position |
| | | }) |
| | | } |
| | | }, |
| | | getButtonSize() { |
| | | const views = uni.createSelectorQuery().in(this) |
| | | views |
| | | .selectAll('.uni-swipe_button-group') |
| | | .boundingClientRect(data => { |
| | | let show = 'none' |
| | | if (this.autoClose) { |
| | | show = 'none' |
| | | } else { |
| | | show = this.show |
| | | } |
| | | this.button = { |
| | | data, |
| | | show |
| | | } |
| | | }) |
| | | .exec() |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <!-- 在微信小程序 app vue端 h5 使用wxs 实现--> |
| | | <!-- #ifdef APP-VUE || MP-WEIXIN || H5 --> |
| | | <view class="uni-swipe"> |
| | | <view |
| | | class="uni-swipe_box" |
| | | :data-threshold="threshold" |
| | | :data-disabled="disabled" |
| | | :change:prop="swipe.sizeReady" |
| | | :prop="btn" |
| | | @touchstart="swipe.touchstart" |
| | | @touchmove="swipe.touchmove" |
| | | @touchend="swipe.touchend" |
| | | > |
| | | <!-- 在微信小程序 app vue端 h5 使用wxs 实现--> |
| | | <view class="uni-swipe_button-group button-group--left"> |
| | | <slot name="left"> |
| | | <view |
| | | v-for="(item,index) in leftOptions" |
| | | :data-button="btn" |
| | | :key="index" |
| | | :style="{ |
| | | backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD', |
| | | fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px' |
| | | }" |
| | | class="uni-swipe_button button-hock" |
| | | @touchstart="appTouchStart" |
| | | @touchend="appTouchEnd($event,index,item,'left')" |
| | | ><text |
| | | class="uni-swipe_button-text" |
| | | :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}" |
| | | >{{ item.text }}</text></view> |
| | | </slot> |
| | | </view> |
| | | <slot></slot> |
| | | <view class="uni-swipe_button-group button-group--right"> |
| | | <slot name="right"> |
| | | <view |
| | | v-for="(item,index) in rightOptions" |
| | | :data-button="btn" |
| | | :key="index" |
| | | :style="{ |
| | | backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD', |
| | | fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px' |
| | | }" |
| | | class="uni-swipe_button button-hock" |
| | | @touchstart="appTouchStart" |
| | | @touchend="appTouchEnd($event,index,item,'right')" |
| | | ><text |
| | | class="uni-swipe_button-text" |
| | | :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}" |
| | | >{{ item.text }}</text></view> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- app nvue端 使用 bindingx --> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <view |
| | | ref="selector-box--hock" |
| | | class="uni-swipe" |
| | | @horizontalpan="touchstart" |
| | | @touchend="touchend" |
| | | > |
| | | <view |
| | | ref='selector-left-button--hock' |
| | | class="uni-swipe_button-group button-group--left" |
| | | > |
| | | <slot name="left"> |
| | | <view |
| | | v-for="(item,index) in leftOptions" |
| | | :data-button="btn" |
| | | :key="index" |
| | | :style="{ |
| | | backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD', |
| | | fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px' |
| | | }" |
| | | class="uni-swipe_button button-hock" |
| | | @click.stop="onClick(index,item,'left')" |
| | | ><text |
| | | class="uni-swipe_button-text" |
| | | :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}" |
| | | >{{ item.text }}</text></view> |
| | | </slot> |
| | | </view> |
| | | <view |
| | | ref='selector-right-button--hock' |
| | | class="uni-swipe_button-group button-group--right" |
| | | > |
| | | <slot name="right"> |
| | | <view |
| | | v-for="(item,index) in rightOptions" |
| | | :data-button="btn" |
| | | :key="index" |
| | | :style="{ |
| | | backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD', |
| | | fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px' |
| | | }" |
| | | class="uni-swipe_button button-hock" |
| | | @click.stop="onClick(index,item,'right')" |
| | | ><text |
| | | class="uni-swipe_button-text" |
| | | :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}" |
| | | >{{ item.text }}</text></view> |
| | | </slot> |
| | | </view> |
| | | <view |
| | | ref='selector-content--hock' |
| | | class="uni-swipe_box" |
| | | > |
| | | <slot></slot> |
| | | </view> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- 其他平台使用 js ,长列表性能可能会有影响--> |
| | | <!-- #ifdef MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQ --> |
| | | <view class="uni-swipe"> |
| | | <view |
| | | class="uni-swipe_box" |
| | | @touchstart="touchstart" |
| | | @touchmove="touchmove" |
| | | @touchend="touchend" |
| | | :style="{transform:moveLeft}" |
| | | :class="{ani:ani}" |
| | | > |
| | | <view class="uni-swipe_button-group button-group--left"> |
| | | <slot name="left"> |
| | | <view |
| | | v-for="(item,index) in leftOptions" |
| | | :data-button="btn" |
| | | :key="index" |
| | | :style="{ |
| | | backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD', |
| | | fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px' |
| | | }" |
| | | class="uni-swipe_button button-hock" |
| | | @touchstart="appTouchStart" |
| | | @touchend="appTouchEnd($event,index,item,'left')" |
| | | ><text |
| | | class="uni-swipe_button-text" |
| | | :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}" |
| | | >{{ item.text }}</text></view> |
| | | </slot> |
| | | </view> |
| | | <slot></slot> |
| | | <view class="uni-swipe_button-group button-group--right"> |
| | | <slot name="right"> |
| | | <view |
| | | v-for="(item,index) in rightOptions" |
| | | :data-button="btn" |
| | | :key="index" |
| | | :style="{ |
| | | backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD', |
| | | fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px' |
| | | }" |
| | | @touchstart="appTouchStart" |
| | | @touchend="appTouchEnd($event,index,item,'right')" |
| | | class="uni-swipe_button button-hock" |
| | | ><text |
| | | class="uni-swipe_button-text" |
| | | :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}" |
| | | >{{ item.text }}</text></view> |
| | | </slot> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- #endif --> |
| | | |
| | | </template> |
| | | <script |
| | | src="./index.wxs" |
| | | module="swipe" |
| | | lang="wxs" |
| | | ></script> |
| | | <script> |
| | | // #ifdef APP-VUE|| MP-WEIXIN || H5 |
| | | import mpwxs from './mpwxs' |
| | | // #endif |
| | | |
| | | // #ifdef APP-NVUE |
| | | import bindingx from './bindingx.js' |
| | | // #endif |
| | | |
| | | // #ifndef APP-PLUS|| MP-WEIXIN || H5 |
| | | import mixins from './mpother' |
| | | // #endif |
| | | |
| | | /** |
| | | * SwipeActionItem 滑动操作子组件 |
| | | * @description 通过滑动触发选项的容器 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=181 |
| | | * @property {Boolean} show = [left|right|none] 开启关闭组件,auto-close = false 时生效 |
| | | * @property {Boolean} disabled = [true|false] 是否禁止滑动 |
| | | * @property {Boolean} autoClose = [true|false] 滑动打开当前组件,是否关闭其他组件 |
| | | * @property {Number} threshold 滑动缺省值 |
| | | * @property {Array} leftOptions 左侧选项内容及样式 |
| | | * @property {Array} rgihtOptions 右侧选项内容及样式 |
| | | * @event {Function} click 点击选项按钮时触发事件,e = {content,index} ,content(点击内容)、index(下标) |
| | | * @event {Function} change 组件打开或关闭时触发,left\right\none |
| | | */ |
| | | |
| | | export default { |
| | | // #ifdef APP-VUE|| MP-WEIXIN||H5 |
| | | mixins: [mpwxs], |
| | | // #endif |
| | | |
| | | // #ifdef APP-NVUE |
| | | mixins: [bindingx], |
| | | // #endif |
| | | |
| | | // #ifndef APP-PLUS|| MP-WEIXIN || H5 |
| | | mixins: [mixins], |
| | | // #endif |
| | | |
| | | props: { |
| | | // 控制开关 |
| | | show: { |
| | | type: String, |
| | | default: 'none' |
| | | }, |
| | | |
| | | // 禁用 |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | |
| | | // 是否自动关闭 |
| | | autoClose: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | |
| | | // 滑动缺省距离 |
| | | threshold: { |
| | | type: Number, |
| | | default: 20 |
| | | }, |
| | | |
| | | // 左侧按钮内容 |
| | | leftOptions: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | |
| | | // 右侧按钮内容 |
| | | rightOptions: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | } |
| | | |
| | | }, |
| | | inject: ['swipeaction'] |
| | | } |
| | | </script> |
| | | <style |
| | | lang="scss" |
| | | scoped |
| | | > |
| | | .uni-swipe { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | overflow: hidden; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-swipe_box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | flex-shrink: 0; |
| | | /* #endif */ |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-swipe_content { |
| | | // border: 1px red solid; |
| | | } |
| | | |
| | | .uni-swipe_button-group { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | position: absolute; |
| | | top: 0; |
| | | bottom: 0; |
| | | } |
| | | |
| | | .button-group--left { |
| | | left: 0; |
| | | transform: translateX(-100%) |
| | | } |
| | | |
| | | .button-group--right { |
| | | right: 0; |
| | | transform: translateX(100%) |
| | | } |
| | | |
| | | .uni-swipe_button { |
| | | /* #ifdef APP-NVUE */ |
| | | flex: 1; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | padding: 0 20px; |
| | | } |
| | | |
| | | .uni-swipe_button-text { |
| | | /* #ifndef APP-NVUE */ |
| | | flex-shrink: 0; |
| | | /* #endif */ |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .ani { |
| | | transition-property: transform; |
| | | transition-duration: 0.3s; |
| | | transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1); |
| | | } |
| | | |
| | | /* #ifdef MP-ALIPAY */ |
| | | .movable-area { |
| | | /* width: 100%; */ |
| | | height: 45px; |
| | | } |
| | | |
| | | .movable-view { |
| | | display: flex; |
| | | /* justify-content: center; */ |
| | | position: relative; |
| | | flex: 1; |
| | | height: 45px; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .movable-view-button { |
| | | display: flex; |
| | | flex-shrink: 0; |
| | | flex-direction: row; |
| | | height: 100%; |
| | | background: #C0C0C0; |
| | | } |
| | | |
| | | /* .transition { |
| | | transition: all 0.3s; |
| | | } */ |
| | | |
| | | .movable-view-box { |
| | | flex-shrink: 0; |
| | | height: 100%; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | /* #endif */ |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view> |
| | | <slot></slot> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * SwipeAction 滑动操作 |
| | | * @description 通过滑动触发选项的容器 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=181 |
| | | */ |
| | | export default { |
| | | data() { |
| | | return {}; |
| | | }, |
| | | provide() { |
| | | return { |
| | | swipeaction: this |
| | | }; |
| | | }, |
| | | created() { |
| | | this.children = []; |
| | | }, |
| | | methods: { |
| | | closeOther(vm) { |
| | | if (this.openItem && this.openItem !== vm) { |
| | | // #ifdef APP-VUE || H5 || MP-WEIXIN |
| | | this.openItem.button.show = 'none' |
| | | // #endif |
| | | |
| | | // #ifndef APP-VUE || H5 || MP-WEIXIN |
| | | this.openItem.close() |
| | | // #endif |
| | | } |
| | | this.openItem = vm |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style></style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-swiper__warp"> |
| | | <slot /> |
| | | <view v-if="mode === 'default'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box" key='default'> |
| | | <view v-for="(item,index) in info" :style="{ |
| | | 'width': (index === current? dots.width*2:dots.width ) + 'px','height':dots.width/3 +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border-radius':'0px'}" |
| | | :key="index" class="uni-swiper__dots-item uni-swiper__dots-bar" /> |
| | | </view> |
| | | <view v-if="mode === 'dot'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box" key='dot'> |
| | | <view v-for="(item,index) in info" :style="{ |
| | | 'width': dots.width + 'px','height':dots.height +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}" |
| | | :key="index" class="uni-swiper__dots-item" /> |
| | | </view> |
| | | <view v-if="mode === 'round'" :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box" key='round'> |
| | | <view v-for="(item,index) in info" :class="[index === current&&'uni-swiper__dots-long']" :style="{ |
| | | 'width':(index === current? dots.width*3:dots.width ) + 'px','height':dots.height +'px' ,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}" |
| | | :key="index" class="uni-swiper__dots-item " /> |
| | | </view> |
| | | <view v-if="mode === 'nav'" key='nav' :style="{'background-color':dotsStyles.backgroundColor,'bottom':'0'}" class="uni-swiper__dots-box uni-swiper__dots-nav"> |
| | | <text :style="{'color':dotsStyles.color}" class="uni-swiper__dots-nav-item">{{ (current+1)+"/"+info.length +' ' +info[current][field] }}</text> |
| | | </view> |
| | | <view v-if="mode === 'indexes'" key='indexes' :style="{'bottom':dots.bottom + 'px'}" class="uni-swiper__dots-box"> |
| | | <view v-for="(item,index) in info" :style="{ |
| | | 'width':dots.width + 'px','height':dots.height +'px' ,'color':index === current?dots.selectedColor:dots.color,'background-color':index !== current?dots.backgroundColor:dots.selectedBackgroundColor,'border':index !==current ? dots.border:dots.selectedBorder}" |
| | | :key="index" class="uni-swiper__dots-item uni-swiper__dots-indexes"><text class="uni-swiper__dots-indexes-text">{{ index+1 }}</text></view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | |
| | | /** |
| | | * SwiperDod 轮播图指示点 |
| | | * @description 自定义轮播图指示点 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=284 |
| | | * @property {Number} current 当前指示点索引,必须是通过 `swiper` 的 `change` 事件获取到的 `e.detail.current` |
| | | * @property {String} mode = [default|round|nav|indexes] 指示点的类型 |
| | | * @value defualt 默认指示点 |
| | | * @value round 圆形指示点 |
| | | * @value nav 条形指示点 |
| | | * @value indexes 索引指示点 |
| | | * @property {String} field mode 为 nav 时,显示的内容字段(mode = nav 时必填) |
| | | * @property {String} info 轮播图的数据,通过数组长度决定指示点个数 |
| | | * @property {Object} dotsStyles 指示点样式 |
| | | * @event {Function} clickItem 组件触发点击事件时触发,e={currentIndex} |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniSwiperDot', |
| | | props: { |
| | | info: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | current: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | dotsStyles: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | // 类型 :default(默认) indexes long nav |
| | | mode: { |
| | | type: String, |
| | | default: 'default' |
| | | }, |
| | | // 只在 nav 模式下生效,变量名称 |
| | | field: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | dots: { |
| | | width: 8, |
| | | height: 8, |
| | | bottom: 10, |
| | | color: '#fff', |
| | | backgroundColor: 'rgba(0, 0, 0, .3)', |
| | | border: '1px rgba(0, 0, 0, .3) solid', |
| | | selectedBackgroundColor: '#333', |
| | | selectedBorder: '1px rgba(0, 0, 0, .9) solid' |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | dotsStyles(newVal) { |
| | | this.dots = Object.assign(this.dots, this.dotsStyles) |
| | | }, |
| | | mode(newVal) { |
| | | if (newVal === 'indexes') { |
| | | this.dots.width = 20 |
| | | this.dots.height = 20 |
| | | } else { |
| | | this.dots.width = 8 |
| | | this.dots.height = 8 |
| | | } |
| | | } |
| | | |
| | | }, |
| | | created() { |
| | | if (this.mode === 'indexes') { |
| | | this.dots.width = 20 |
| | | this.dots.height = 20 |
| | | } |
| | | this.dots = Object.assign(this.dots, this.dotsStyles) |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-swiper__warp { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: column; |
| | | position: relative; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-swiper__dots-box { |
| | | position: absolute; |
| | | bottom: 10px; |
| | | left: 0; |
| | | right: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-swiper__dots-item { |
| | | width: 8px; |
| | | border-radius: 100px; |
| | | margin-left: 6px; |
| | | background-color: $uni-bg-color-mask; |
| | | // transition: width 0.2s linear; 不要取消注释,不然会不能变色 |
| | | } |
| | | |
| | | .uni-swiper__dots-item:first-child { |
| | | margin: 0; |
| | | } |
| | | |
| | | .uni-swiper__dots-default { |
| | | border-radius: 100px; |
| | | } |
| | | |
| | | .uni-swiper__dots-long { |
| | | border-radius: 50px; |
| | | } |
| | | |
| | | .uni-swiper__dots-bar { |
| | | border-radius: 50px; |
| | | } |
| | | |
| | | .uni-swiper__dots-nav { |
| | | bottom: 0px; |
| | | height: 40px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | background-color: rgba(0, 0, 0, 0.2); |
| | | } |
| | | |
| | | .uni-swiper__dots-nav-item { |
| | | /* overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; */ |
| | | font-size: $uni-font-size-base; |
| | | color: #fff; |
| | | margin: 0 15px; |
| | | } |
| | | |
| | | .uni-swiper__dots-indexes { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | // flex: 1; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-swiper__dots-indexes-text { |
| | | color: #fff; |
| | | font-size: $uni-font-size-sm; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-table-scroll" :class="{'table--border':border,'border-none':!noData}"> |
| | | <view class="uni-table" :style="{'min-width':minWidth+'px'}" :class="{ 'table--stripe':stripe}"> |
| | | <slot></slot> |
| | | <view v-if="noData" class="uni-table-loading"> |
| | | <view class="uni-table-text">{{emptyText}}</view> |
| | | </view> |
| | | <view v-show="loading" class="uni-table-mask"> |
| | | <div class="uni-table--loader"></div> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Table 表格 |
| | | * @description 用于展示多条结构类似的数据 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id= |
| | | * @property {Boolean} border 是否带有纵向边框 |
| | | * @property {Boolean} stripe 是否显示斑马线 |
| | | * @property {Boolean} type 是否开启多选 |
| | | * @property {String} emptyText 空数据时显示的文本内容 |
| | | * @property {Boolean} loading 显示加载中 |
| | | * @event {Function} selection-change 开启多选时,当选择项发生变化时会触发该事件 |
| | | */ |
| | | export default { |
| | | name: 'uniTable', |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | props: { |
| | | // 是否有竖线 |
| | | border: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 是否显示斑马线 |
| | | stripe: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 多选 |
| | | type: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 没有更多数据 |
| | | emptyText: { |
| | | type: String, |
| | | default: '没有更多数据' |
| | | }, |
| | | loading: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | noData: true, |
| | | minWidth:0 |
| | | }; |
| | | }, |
| | | watch: { |
| | | loading(val) { |
| | | |
| | | } |
| | | }, |
| | | created() { |
| | | // 定义tr的实例数组 |
| | | this.trChildren = [] |
| | | this.backData = [] |
| | | }, |
| | | methods: { |
| | | isNodata() { |
| | | if (this.trChildren.length > 1) { |
| | | this.noData = false |
| | | } else { |
| | | this.noData = true |
| | | } |
| | | }, |
| | | /** |
| | | * 清除选中 |
| | | */ |
| | | clearSelection(){ |
| | | this.trChildren.forEach((item, index) => { |
| | | item.value = false |
| | | }) |
| | | this.$emit('selection-change', { |
| | | detail: { |
| | | index: [], |
| | | value: [] |
| | | } |
| | | }) |
| | | }, |
| | | check(child, check) { |
| | | const childDom = this.trChildren.find((item, index) => child === item) |
| | | const childDomIndex = this.trChildren.findIndex((item, index) => child === item) |
| | | if (childDomIndex === 0) { |
| | | if (childDom.value !== check) { |
| | | this.backData = [] |
| | | this.trChildren.map((item, index) => item.value = check) |
| | | } |
| | | this.trChildren.forEach((item, index) => { |
| | | if (index > 0 && item.value) { |
| | | this.backData.push(index - 1) |
| | | } |
| | | }) |
| | | |
| | | } else { |
| | | if (!check) { |
| | | this.trChildren[0].value = false |
| | | } |
| | | childDom.value = check |
| | | if (check) { |
| | | this.backData.push(childDomIndex - 1) |
| | | } else { |
| | | const index = this.backData.findIndex(item => item === (childDomIndex - 1)) |
| | | this.backData.splice(index, 1) |
| | | } |
| | | const domCheckAll = this.trChildren.find((item, index) => index > 0 && !item.value) |
| | | if (!domCheckAll) { |
| | | this.trChildren[0].value = true |
| | | } |
| | | } |
| | | |
| | | this.$emit('selection-change', { |
| | | detail: { |
| | | index: this.backData.sort(), |
| | | value: [] |
| | | } |
| | | }) |
| | | |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-table-scroll { |
| | | width: 100%; |
| | | overflow-x: auto; |
| | | } |
| | | |
| | | .uni-table { |
| | | position: relative; |
| | | width: 100%; |
| | | display: table; |
| | | box-sizing: border-box; |
| | | border-radius: 5px; |
| | | box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1); |
| | | overflow-x: auto; |
| | | background-color: #fff; |
| | | /deep/ .uni-table-tr:nth-child(n+2) { |
| | | &:hover { |
| | | background-color: #f5f7fa; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .table--border { |
| | | border: 1px #ddd solid; |
| | | } |
| | | |
| | | .border-none { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .table--stripe { |
| | | /deep/ .uni-table-tr:nth-child(2n+3) { |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | } |
| | | |
| | | /* 表格加载、无数据样式 */ |
| | | .uni-table-loading { |
| | | position: relative; |
| | | display: table-row; |
| | | height: 50px; |
| | | line-height: 50px; |
| | | } |
| | | |
| | | .uni-table-text { |
| | | position: absolute; |
| | | right: 0; |
| | | left: 0; |
| | | text-align: center; |
| | | font-size: 14px; |
| | | color: #999; |
| | | } |
| | | |
| | | .uni-table-mask { |
| | | position: absolute; |
| | | top: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | margin: auto; |
| | | background-color: rgba(255, 255, 255, 0.8); |
| | | z-index: 99; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | transition: all 0.5s; |
| | | } |
| | | |
| | | .uni-table--loader { |
| | | width: 30px; |
| | | height: 30px; |
| | | border: 2px solid #aaa; |
| | | // border-bottom-color: transparent; |
| | | border-radius: 50%; |
| | | animation: 2s uni-table--loader linear infinite; |
| | | position: relative; |
| | | } |
| | | |
| | | @keyframes uni-table--loader { |
| | | 0% { |
| | | transform: rotate(360deg); |
| | | } |
| | | |
| | | 10% { |
| | | border-left-color: transparent; |
| | | } |
| | | |
| | | 20% { |
| | | border-bottom-color: transparent; |
| | | } |
| | | |
| | | 30% { |
| | | border-right-color: transparent; |
| | | } |
| | | |
| | | 40% { |
| | | border-top-color: transparent; |
| | | } |
| | | |
| | | 50% { |
| | | transform: rotate(0deg); |
| | | } |
| | | |
| | | 60% { |
| | | border-top-color: transparent; |
| | | } |
| | | |
| | | 70% { |
| | | border-left-color: transparent; |
| | | } |
| | | |
| | | 80% { |
| | | border-bottom-color: transparent; |
| | | } |
| | | |
| | | 90% { |
| | | border-right-color: transparent; |
| | | } |
| | | |
| | | 100% { |
| | | transform: rotate(-360deg); |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view :class="[ |
| | | 'uni-tag--' + type, |
| | | disabled === true || disabled === 'true' ? 'uni-tag--disabled' : '', |
| | | inverted === true || inverted === 'true' ? type + '-uni-tag--inverted' : '', |
| | | circle === true || circle === 'true' ? 'uni-tag--circle' : '', |
| | | mark === true || mark === 'true' ? 'uni-tag--mark' : '', |
| | | 'uni-tag--' + size |
| | | ]" |
| | | @click="onClick()" class="uni-tag" v-if="text"> |
| | | <text :class="[type === 'default' ? 'uni-tag--default':'uni-tag-text',inverted === true || inverted === 'true' ? 'uni-tag-text--'+type : '',size === 'small' ? 'uni-tag-text--small':'' ]">{{ text }}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Tag 标签 |
| | | * @description 用于展示1个或多个文字标签,可点击切换选中、不选中的状态 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=35 |
| | | * @property {String} text 标签内容 |
| | | * @property {String} size = [normal|small] 大小尺寸 |
| | | * @value normal 正常 |
| | | * @value small 小尺寸 |
| | | * @property {String} type = [default|primary|success|warning|error|royal] 颜色类型 |
| | | * @value default 灰色 |
| | | * @value primary 蓝色 |
| | | * @value success 绿色 |
| | | * @value warning 黄色 |
| | | * @value error 红色 |
| | | * @value royal 紫色 |
| | | * @property {Boolean} disabled = [true|false] 是否为禁用状态 |
| | | * @property {Boolean} inverted = [true|false] 是否无需背景颜色(空心标签) |
| | | * @property {Boolean} circle = [true|false] 是否为圆角 |
| | | * @event {Function} click 点击 Tag 触发事件 |
| | | */ |
| | | |
| | | export default { |
| | | name: "UniTag", |
| | | props: { |
| | | type: { |
| | | // 标签类型default、primary、success、warning、error、royal |
| | | type: String, |
| | | default: "default" |
| | | }, |
| | | size: { |
| | | // 标签大小 normal, small |
| | | type: String, |
| | | default: "normal" |
| | | }, |
| | | // 标签内容 |
| | | text: { |
| | | type: String, |
| | | default: "" |
| | | }, |
| | | disabled: { |
| | | // 是否为禁用状态 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | inverted: { |
| | | // 是否为空心 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | circle: { |
| | | // 是否为圆角样式 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | mark: { |
| | | // 是否为标记样式 |
| | | type: [Boolean, String], |
| | | default: false |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick() { |
| | | if (this.disabled === true || this.disabled === "true") { |
| | | return; |
| | | } |
| | | this.$emit("click"); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $tag-pd: 0px 16px; |
| | | $tag-small-pd: 0px 8px; |
| | | |
| | | .uni-tag { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | padding: $tag-pd; |
| | | height: 30px; |
| | | line-height: 30px; |
| | | justify-content: center; |
| | | color: $uni-text-color; |
| | | border-radius: $uni-border-radius-base; |
| | | background-color: $uni-bg-color-grey; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-bg-color-grey; |
| | | } |
| | | |
| | | .uni-tag--circle { |
| | | border-radius: 15px; |
| | | } |
| | | |
| | | .uni-tag--mark { |
| | | border-top-left-radius: 0; |
| | | border-bottom-left-radius: 0; |
| | | border-top-right-radius: 15px; |
| | | border-bottom-right-radius: 15px; |
| | | } |
| | | |
| | | .uni-tag--disabled { |
| | | opacity: 0.5; |
| | | } |
| | | |
| | | .uni-tag--small { |
| | | height: 20px; |
| | | padding: $tag-small-pd; |
| | | line-height: 20px; |
| | | font-size: $uni-font-size-sm; |
| | | } |
| | | |
| | | .uni-tag--default { |
| | | color: $uni-text-color; |
| | | font-size: $uni-font-size-base; |
| | | } |
| | | |
| | | .uni-tag-text--small { |
| | | font-size: $uni-font-size-sm !important; |
| | | } |
| | | |
| | | .uni-tag-text { |
| | | color: $uni-text-color-inverse; |
| | | font-size: $uni-font-size-base; |
| | | } |
| | | |
| | | .uni-tag-text--primary { |
| | | color: $uni-color-primary !important; |
| | | } |
| | | |
| | | .uni-tag-text--success { |
| | | color: $uni-color-success !important; |
| | | } |
| | | |
| | | .uni-tag-text--warning { |
| | | color: $uni-color-warning !important; |
| | | } |
| | | |
| | | .uni-tag-text--error { |
| | | color: $uni-color-error !important; |
| | | } |
| | | |
| | | .uni-tag--primary { |
| | | color: $uni-text-color-inverse; |
| | | background-color: $uni-color-primary; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-color-primary; |
| | | } |
| | | |
| | | .primary-uni-tag--inverted { |
| | | color: $uni-color-primary; |
| | | background-color: $uni-bg-color; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-color-primary; |
| | | } |
| | | |
| | | .uni-tag--success { |
| | | color: $uni-text-color-inverse; |
| | | background-color: $uni-color-success; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-color-success; |
| | | } |
| | | |
| | | .success-uni-tag--inverted { |
| | | color: $uni-color-success; |
| | | background-color: $uni-bg-color; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-color-success; |
| | | } |
| | | |
| | | .uni-tag--warning { |
| | | color: $uni-text-color-inverse; |
| | | background-color: $uni-color-warning; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-color-warning; |
| | | } |
| | | |
| | | .warning-uni-tag--inverted { |
| | | color: $uni-color-warning; |
| | | background-color: $uni-bg-color; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-color-warning; |
| | | } |
| | | |
| | | .uni-tag--error { |
| | | color: $uni-text-color-inverse; |
| | | background-color: $uni-color-error; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-color-error; |
| | | } |
| | | |
| | | .error-uni-tag--inverted { |
| | | color: $uni-color-error; |
| | | background-color: $uni-bg-color; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-color-error; |
| | | } |
| | | |
| | | .uni-tag--inverted { |
| | | color: $uni-text-color; |
| | | background-color: $uni-bg-color; |
| | | border-width: 1rpx; |
| | | border-style: solid; |
| | | border-color: $uni-bg-color-grey; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <!-- :class="{'table--border':border}" --> |
| | | <view class="uni-table-td" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}"> |
| | | <slot></slot> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Td 单元格 |
| | | * @description 表格中的标准单元格组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id= |
| | | * @property {Number} align = [left|center|right] 单元格对齐方式 |
| | | */ |
| | | export default { |
| | | name: 'uniTd', |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | props: { |
| | | width: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | align: { |
| | | type: String, |
| | | default: 'left' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | border: false |
| | | }; |
| | | }, |
| | | created() { |
| | | this.root = this.getTable() |
| | | this.border = this.root.border |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getTable() { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== 'uniTable') { |
| | | parent = parent.$parent; |
| | | if (!parent) return false; |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | }, |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-table-td { |
| | | display: table-cell; |
| | | padding: 12px 10px; |
| | | // text-align: center; |
| | | vertical-align: middle; |
| | | border-bottom: 1px #ddd solid; |
| | | // min-width: 150px; |
| | | color: #666; |
| | | font-size: 14px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .table--border { |
| | | border-right: 1px #ddd solid; |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view> |
| | | 测试插件 |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | |
| | | }; |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-table-th" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}"> |
| | | <slot></slot> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Th 表头 |
| | | * @description 表格内的表头单元格组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id= |
| | | * @property {Number} width 单元格宽度 |
| | | * @property {Number} align = [left|center|right] 单元格对齐方式 |
| | | * @value left 单元格文字左侧对齐 |
| | | * @value center 单元格文字居中 |
| | | * @value right 单元格文字右侧对齐 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'uniTh', |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | props: { |
| | | width: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | align: { |
| | | type: String, |
| | | default: 'left' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | border:false |
| | | }; |
| | | }, |
| | | created() { |
| | | this.root = this.getTable('uniTable') |
| | | this.rootTr = this.getTable('uniTr') |
| | | this.rootTr.minWidthUpdate(this.width?this.width:140) |
| | | this.border = this.root.border |
| | | }, |
| | | methods:{ |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getTable(name) { |
| | | 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-table-th { |
| | | padding: 12px 10px; |
| | | display: table-cell; |
| | | // text-align: center; |
| | | color: #333; |
| | | font-weight: 500; |
| | | border-bottom: 1px #ddd solid; |
| | | font-size: 14px; |
| | | // background-color: #efefef; |
| | | box-sizing: border-box; |
| | | } |
| | | .table--border { |
| | | border-right: 1px #ddd solid; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-title__box" :style="{'align-items':textAlign}"> |
| | | <text class="uni-title__base" :class="['uni-'+type]" :style="{'color':color}">{{title}}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Title 章节标题 |
| | | * @description 章节标题,通常用于记录页面标题,使用当前组件,uni-app 如果开启统计,将会自动统计页面标题 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=1066 |
| | | * @property {String} type = [h1|h2|h3|h4|h5] 标题类型 |
| | | * @value h1 一级标题 |
| | | * @value h2 二级标题 |
| | | * @value h3 三级标题 |
| | | * @value h4 四级标题 |
| | | * @value h5 五级标题 |
| | | * @property {String} title 章节标题内容 |
| | | * @property {String} align = [left|center|right] 对齐方式 |
| | | * @value left 做对齐 |
| | | * @value center 居中对齐 |
| | | * @value right 右对齐 |
| | | * @property {String} color 字体颜色 |
| | | * @property {Boolean} stat = [true|false] 是否开启统计功能呢,如不填写type值,默认为开启,填写 type 属性,默认为关闭 |
| | | */ |
| | | export default { |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | align: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#333333' |
| | | }, |
| | | stat: { |
| | | type: [Boolean, String], |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | |
| | | }; |
| | | }, |
| | | computed: { |
| | | textAlign() { |
| | | let align = 'center'; |
| | | switch (this.align) { |
| | | case 'left': |
| | | align = 'flex-start' |
| | | break; |
| | | case 'center': |
| | | align = 'center' |
| | | break; |
| | | case 'right': |
| | | align = 'flex-end' |
| | | break; |
| | | } |
| | | return align |
| | | } |
| | | }, |
| | | watch: { |
| | | title(newVal) { |
| | | if (this.isOpenStat()) { |
| | | // 上报数据 |
| | | if (uni.report) { |
| | | uni.report('title', this.title) |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | if (this.isOpenStat()) { |
| | | // 上报数据 |
| | | if (uni.report) { |
| | | uni.report('title', this.title) |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | isOpenStat() { |
| | | if (this.stat === '') { |
| | | this.isStat = false |
| | | } |
| | | let stat_type = (typeof(this.stat) === 'boolean' && this.stat) || (typeof(this.stat) === 'string' && this.stat !== |
| | | '') |
| | | if (this.type === "") { |
| | | this.isStat = true |
| | | if (this.stat.toString() === 'false') { |
| | | this.isStat = false |
| | | } |
| | | } |
| | | |
| | | if (this.type !== '') { |
| | | this.isStat = true |
| | | if (stat_type) { |
| | | this.isStat = true |
| | | } else { |
| | | this.isStat = false |
| | | } |
| | | } |
| | | return this.isStat |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | /* .uni-title { |
| | | |
| | | } */ |
| | | .uni-title__box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | justify-content: center; |
| | | padding: 8px 0; |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-title__base { |
| | | font-size: 15px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .uni-h1 { |
| | | font-size: 20px; |
| | | color: #333; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .uni-h2 { |
| | | font-size: 18px; |
| | | color: #333; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .uni-h3 { |
| | | font-size: 16px; |
| | | color: #333; |
| | | font-weight: bold; |
| | | /* font-weight: 400; */ |
| | | } |
| | | |
| | | .uni-h4 { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: bold; |
| | | /* font-weight: 300; */ |
| | | } |
| | | |
| | | .uni-h5 { |
| | | font-size: 12px; |
| | | color: #333; |
| | | font-weight: bold; |
| | | /* font-weight: 200; */ |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="uni-table-tr"> |
| | | <checkbox-group v-if="selection === 'selection'" class="checkbox" :class="{'tr-table--border':border}" @change="change"> |
| | | <label> |
| | | <checkbox value="check" :checked="value"/> |
| | | </label> |
| | | </checkbox-group> |
| | | <slot></slot> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Tr 表格行组件 |
| | | * @description 表格行组件 仅包含 th,td 组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id= |
| | | */ |
| | | export default { |
| | | name: 'uniTr', |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | data() { |
| | | return { |
| | | value: false, |
| | | border: false, |
| | | selection:false, |
| | | widthThArr:[] |
| | | }; |
| | | }, |
| | | created() { |
| | | this.root = this.getTable() |
| | | this.border = this.root.border |
| | | this.selection = this.root.type |
| | | this.root.trChildren.push(this) |
| | | this.root.isNodata() |
| | | }, |
| | | mounted() { |
| | | if(this.widthThArr.length > 0){ |
| | | const selectionWidth = this.selection === 'selection'? 50:0 |
| | | this.root.minWidth = this.widthThArr.reduce((a,b)=> Number(a) + Number(b)) + selectionWidth |
| | | } |
| | | }, |
| | | destroyed() { |
| | | const index = this.root.trChildren.findIndex(i=>i===this) |
| | | this.root.trChildren.splice(index,1) |
| | | this.root.isNodata() |
| | | }, |
| | | methods: { |
| | | minWidthUpdate(width){ |
| | | this.widthThArr.push(width) |
| | | }, |
| | | change(e) { |
| | | this.root.trChildren.forEach((item) => { |
| | | if (item === this) { |
| | | this.root.check(this,e.detail.value.length > 0 ? true : false) |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getTable() { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== 'uniTable') { |
| | | parent = parent.$parent; |
| | | if (!parent) return false; |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | }, |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-table-tr { |
| | | display: table-row; |
| | | transition: all .3s; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .checkbox { |
| | | padding: 12px 8px; |
| | | width: 26px; |
| | | padding-left: 12px; |
| | | display: table-cell; |
| | | // text-align: center; |
| | | vertical-align: middle; |
| | | color: #333; |
| | | font-weight: 500; |
| | | border-bottom: 1px #ddd solid; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .tr-table--border { |
| | | border-right: 1px #ddd solid; |
| | | } |
| | | |
| | | .uni-table-tr { |
| | | /deep/ .uni-table-th { |
| | | &.table--border:last-child { |
| | | border-right: none; |
| | | } |
| | | } |
| | | /deep/ .uni-table-td { |
| | | &.table--border:last-child { |
| | | border-right: none; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view v-if="isShow" ref="ani" class="uni-transition" :class="[ani.in]" :style="'transform:' +transform+';'+stylesObject" |
| | | @click="change"> |
| | | <slot></slot> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | // #ifdef APP-NVUE |
| | | const animation = uni.requireNativePlugin('animation'); |
| | | // #endif |
| | | /** |
| | | * Transition 过渡动画 |
| | | * @description 简单过渡动画组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=985 |
| | | * @property {Boolean} show = [false|true] 控制组件显示或隐藏 |
| | | * @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型 |
| | | * @value fade 渐隐渐出过渡 |
| | | * @value slide-top 由上至下过渡 |
| | | * @value slide-right 由右至左过渡 |
| | | * @value slide-bottom 由下至上过渡 |
| | | * @value slide-left 由左至右过渡 |
| | | * @value zoom-in 由小到大过渡 |
| | | * @value zoom-out 由大到小过渡 |
| | | * @property {Number} duration 过渡动画持续时间 |
| | | * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red` |
| | | */ |
| | | export default { |
| | | name: 'uniTransition', |
| | | props: { |
| | | show: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | modeClass: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | duration: { |
| | | type: Number, |
| | | default: 300 |
| | | }, |
| | | styles: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | isShow: false, |
| | | transform: '', |
| | | ani: { in: '', |
| | | active: '' |
| | | } |
| | | }; |
| | | }, |
| | | watch: { |
| | | show: { |
| | | handler(newVal) { |
| | | if (newVal) { |
| | | this.open() |
| | | } else { |
| | | this.close() |
| | | } |
| | | }, |
| | | immediate: true |
| | | } |
| | | }, |
| | | computed: { |
| | | stylesObject() { |
| | | let styles = { |
| | | ...this.styles, |
| | | 'transition-duration': this.duration / 1000 + 's' |
| | | } |
| | | let transfrom = '' |
| | | for (let i in styles) { |
| | | let line = this.toLine(i) |
| | | transfrom += line + ':' + styles[i] + ';' |
| | | } |
| | | return transfrom |
| | | } |
| | | }, |
| | | created() { |
| | | // this.timer = null |
| | | // this.nextTick = (time = 50) => new Promise(resolve => { |
| | | // clearTimeout(this.timer) |
| | | // this.timer = setTimeout(resolve, time) |
| | | // return this.timer |
| | | // }); |
| | | }, |
| | | methods: { |
| | | change() { |
| | | this.$emit('click', { |
| | | detail: this.isShow |
| | | }) |
| | | }, |
| | | open() { |
| | | clearTimeout(this.timer) |
| | | this.isShow = true |
| | | this.transform = '' |
| | | this.ani.in = '' |
| | | for (let i in this.getTranfrom(false)) { |
| | | if (i === 'opacity') { |
| | | this.ani.in = 'fade-in' |
| | | } else { |
| | | this.transform += `${this.getTranfrom(false)[i]} ` |
| | | } |
| | | } |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this._animation(true) |
| | | }, 50) |
| | | }) |
| | | |
| | | }, |
| | | close(type) { |
| | | clearTimeout(this.timer) |
| | | this._animation(false) |
| | | }, |
| | | _animation(type) { |
| | | let styles = this.getTranfrom(type) |
| | | // #ifdef APP-NVUE |
| | | if(!this.$refs['ani']) return |
| | | animation.transition(this.$refs['ani'].ref, { |
| | | styles, |
| | | duration: this.duration, //ms |
| | | timingFunction: 'ease', |
| | | needLayout: false, |
| | | delay: 0 //ms |
| | | }, () => { |
| | | if (!type) { |
| | | this.isShow = false |
| | | } |
| | | this.$emit('change', { |
| | | detail: this.isShow |
| | | }) |
| | | }) |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | this.transform = '' |
| | | for (let i in styles) { |
| | | if (i === 'opacity') { |
| | | this.ani.in = `fade-${type?'out':'in'}` |
| | | } else { |
| | | this.transform += `${styles[i]} ` |
| | | } |
| | | } |
| | | this.timer = setTimeout(() => { |
| | | if (!type) { |
| | | this.isShow = false |
| | | } |
| | | this.$emit('change', { |
| | | detail: this.isShow |
| | | }) |
| | | |
| | | }, this.duration) |
| | | // #endif |
| | | |
| | | }, |
| | | getTranfrom(type) { |
| | | let styles = { |
| | | transform: '' |
| | | } |
| | | this.modeClass.forEach((mode) => { |
| | | switch (mode) { |
| | | case 'fade': |
| | | styles.opacity = type ? 1 : 0 |
| | | break; |
| | | case 'slide-top': |
| | | styles.transform += `translateY(${type?'0':'-100%'}) ` |
| | | break; |
| | | case 'slide-right': |
| | | styles.transform += `translateX(${type?'0':'100%'}) ` |
| | | break; |
| | | case 'slide-bottom': |
| | | styles.transform += `translateY(${type?'0':'100%'}) ` |
| | | break; |
| | | case 'slide-left': |
| | | styles.transform += `translateX(${type?'0':'-100%'}) ` |
| | | break; |
| | | case 'zoom-in': |
| | | styles.transform += `scale(${type?1:0.8}) ` |
| | | break; |
| | | case 'zoom-out': |
| | | styles.transform += `scale(${type?1:1.2}) ` |
| | | break; |
| | | } |
| | | }) |
| | | return styles |
| | | }, |
| | | _modeClassArr(type) { |
| | | let mode = this.modeClass |
| | | if (typeof(mode) !== "string") { |
| | | let modestr = '' |
| | | mode.forEach((item) => { |
| | | modestr += (item + '-' + type + ',') |
| | | }) |
| | | return modestr.substr(0, modestr.length - 1) |
| | | } else { |
| | | return mode + '-' + type |
| | | } |
| | | }, |
| | | // getEl(el) { |
| | | // console.log(el || el.ref || null); |
| | | // return el || el.ref || null |
| | | // }, |
| | | toLine(name) { |
| | | return name.replace(/([A-Z])/g, "-$1").toLowerCase(); |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .uni-transition { |
| | | transition-timing-function: ease; |
| | | transition-duration: 0.3s; |
| | | transition-property: transform, opacity; |
| | | } |
| | | |
| | | .fade-in { |
| | | opacity: 0; |
| | | } |
| | | |
| | | .fade-active { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .slide-top-in { |
| | | /* transition-property: transform, opacity; */ |
| | | transform: translateY(-100%); |
| | | } |
| | | |
| | | .slide-top-active { |
| | | transform: translateY(0); |
| | | /* opacity: 1; */ |
| | | } |
| | | |
| | | .slide-right-in { |
| | | transform: translateX(100%); |
| | | } |
| | | |
| | | .slide-right-active { |
| | | transform: translateX(0); |
| | | } |
| | | |
| | | .slide-bottom-in { |
| | | transform: translateY(100%); |
| | | } |
| | | |
| | | .slide-bottom-active { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | .slide-left-in { |
| | | transform: translateX(-100%); |
| | | } |
| | | |
| | | .slide-left-active { |
| | | transform: translateX(0); |
| | | opacity: 1; |
| | | } |
| | | |
| | | .zoom-in-in { |
| | | transform: scale(0.8); |
| | | } |
| | | |
| | | .zoom-out-active { |
| | | transform: scale(1); |
| | | } |
| | | |
| | | .zoom-out-in { |
| | | transform: scale(1.2); |
| | | } |
| | | </style> |
New file |
| | |
| | | import Vue from 'vue' |
| | | import App from './App' |
| | | |
| | | Vue.config.productionTip = false |
| | | |
| | | App.mpType = 'app' |
| | | |
| | | const app = new Vue({ |
| | | ...App |
| | | }) |
| | | app.$mount() |
New file |
| | |
| | | { |
| | | "name" : "hive-app", |
| | | "appid" : "__UNI__EB91E09", |
| | | "description": "", |
| | | "versionName": "1.0.0", |
| | | "versionCode": "100", |
| | | "transformPx": false, |
| | | "app-plus": { /* 5+App特有相关 */ |
| | | "usingComponents": true, |
| | | "nvueCompiler": "uni-app", |
| | | "splashscreen": { |
| | | "alwaysShowBeforeRender": true, |
| | | "waiting": true, |
| | | "autoclose": true, |
| | | "delay": 0 |
| | | }, |
| | | "modules": { /* 模块配置 */ |
| | | |
| | | }, |
| | | "distribute": { /* 应用发布信息 */ |
| | | "android": { /* android打包配置 */ |
| | | "permissions": [ |
| | | "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
| | | "<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
| | | "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
| | | "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
| | | "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.CAMERA\"/>", |
| | | "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
| | | "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
| | | "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
| | | "<uses-feature android:name=\"android.hardware.camera\"/>", |
| | | "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
| | | ] |
| | | }, |
| | | "ios": { /* ios打包配置 */ |
| | | |
| | | }, |
| | | "sdkConfigs": { /* SDK配置 */ |
| | | |
| | | } |
| | | } |
| | | }, |
| | | "quickapp": { /* 快应用特有相关 */ |
| | | |
| | | }, |
| | | "mp-weixin": { /* 小程序特有相关 */ |
| | | "appid": "", |
| | | "setting": { |
| | | "urlCheck": false |
| | | }, |
| | | "usingComponents": true |
| | | } |
| | | } |
New file |
| | |
| | | { |
| | | "pages": [{ |
| | | "path": "pages/index/index", |
| | | "style": { |
| | | "navigationBarTitleText": "uni-app" |
| | | } |
| | | }], |
| | | "globalStyle": { |
| | | "navigationBarTextStyle": "black", |
| | | "navigationBarTitleText": "uni-app", |
| | | "navigationBarBackgroundColor": "#F8F8F8", |
| | | "backgroundColor": "#F8F8F8", |
| | | "app-plus": { |
| | | "background": "#efeff4" |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <view class="container"> |
| | | |
| | | <view class="intro">本项目已包含uni ui组件,无需import和注册,可直接使用。在代码区键入字母u,即可通过代码助手列出所有可用组件。光标置于组件名称处按F1,即可查看组件文档。</view> |
| | | <text class="intro">详见:</text> |
| | | <uni-link :href="href" :text="href"></uni-link> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | href: 'https://uniapp.dcloud.io/component/README?id=uniui' |
| | | } |
| | | }, |
| | | methods: { |
| | | |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .container { |
| | | padding: 20px; |
| | | font-size: 14px; |
| | | line-height: 24px; |
| | | } |
| | | </style> |
New file |
| | |
| | | /** |
| | | * 这里是uni-app内置的常用样式变量 |
| | | * |
| | | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 |
| | | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App |
| | | * |
| | | */ |
| | | |
| | | /** |
| | | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 |
| | | * |
| | | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 |
| | | */ |
| | | |
| | | /* 颜色变量 */ |
| | | |
| | | /* 行为相关颜色 */ |
| | | $uni-color-primary: #007aff; |
| | | $uni-color-success: #4cd964; |
| | | $uni-color-warning: #f0ad4e; |
| | | $uni-color-error: #dd524d; |
| | | |
| | | /* 文字基本颜色 */ |
| | | $uni-text-color:#333;//基本色 |
| | | $uni-text-color-inverse:#fff;//反色 |
| | | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 |
| | | $uni-text-color-placeholder: #808080; |
| | | $uni-text-color-disable:#c0c0c0; |
| | | |
| | | /* 背景颜色 */ |
| | | $uni-bg-color:#ffffff; |
| | | $uni-bg-color-grey:#f8f8f8; |
| | | $uni-bg-color-hover:#f1f1f1;//点击状态颜色 |
| | | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 |
| | | |
| | | /* 边框颜色 */ |
| | | $uni-border-color:#e5e5e5; |
| | | |
| | | /* 尺寸变量 */ |
| | | |
| | | /* 文字尺寸 */ |
| | | $uni-font-size-sm:12px; |
| | | $uni-font-size-base:14px; |
| | | $uni-font-size-lg:16px; |
| | | |
| | | /* 图片尺寸 */ |
| | | $uni-img-size-sm:20px; |
| | | $uni-img-size-base:26px; |
| | | $uni-img-size-lg:40px; |
| | | |
| | | /* Border Radius */ |
| | | $uni-border-radius-sm: 2px; |
| | | $uni-border-radius-base: 3px; |
| | | $uni-border-radius-lg: 6px; |
| | | $uni-border-radius-circle: 50%; |
| | | |
| | | /* 水平间距 */ |
| | | $uni-spacing-row-sm: 5px; |
| | | $uni-spacing-row-base: 10px; |
| | | $uni-spacing-row-lg: 15px; |
| | | |
| | | /* 垂直间距 */ |
| | | $uni-spacing-col-sm: 4px; |
| | | $uni-spacing-col-base: 8px; |
| | | $uni-spacing-col-lg: 12px; |
| | | |
| | | /* 透明度 */ |
| | | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度 |
| | | |
| | | /* 文章场景相关 */ |
| | | $uni-color-title: #2C405A; // 文章标题颜色 |
| | | $uni-font-size-title:20px; |
| | | $uni-color-subtitle: #555555; // 二级标题颜色 |
| | | $uni-font-size-subtitle:26px; |
| | | $uni-color-paragraph: #3F536E; // 文章段落颜色 |
| | | $uni-font-size-paragraph:15px; |