Commit 0633d9fecd9e36ec2ccca6cf1c35473065246485
0 parents
Exists in
master
初始化
Showing
15 changed files
with
964 additions
and
0 deletions
Show diff stats
| 1 | +++ a/components/amount/index.vue | |
| ... | ... | @@ -0,0 +1,209 @@ |
| 1 | +<template> | |
| 2 | + <text class="zui-amount" :class="{ numerical: !isCapital }"> | |
| 3 | + <slot v-if="$slots.prefix" name="prefix"></slot> | |
| 4 | + <template v-else-if="prefix"> | |
| 5 | + {{ prefix }} | |
| 6 | + </template> | |
| 7 | + <template v-if="!isCapital"> | |
| 8 | + {{ Number(value || 0) | doPrecision(precision > 0 ? precision : 0, isRoundUp) | doFormat(hasSeparator, separator) }} | |
| 9 | + </template> | |
| 10 | + <template v-else> | |
| 11 | + {{ Number(value || 0) | doPrecision(4, isRoundUp) | doCapital }} | |
| 12 | + </template> | |
| 13 | + </text> | |
| 14 | +</template> | |
| 15 | + | |
| 16 | +<script> | |
| 17 | +/* istanbul ignore file */ | |
| 18 | +var formatValueByGapStep = function(step, value, gap, direction, range, isAdd, oldValue) { | |
| 19 | + var gap = gap || ' '; | |
| 20 | + var direction = direction || 'right'; | |
| 21 | + var isAdd = isAdd || 1; | |
| 22 | + var oldValue = oldValue || 0; | |
| 23 | + if (value.length == 0) { | |
| 24 | + return { value: value, range: range }; | |
| 25 | + } | |
| 26 | + var arr = value && value.split(''); | |
| 27 | + var _range = range; | |
| 28 | + var showValue = ''; | |
| 29 | + if (direction == 'right') { | |
| 30 | + for (var j = arr.length - 1, k = 0; j >= 0; j--, k++) { | |
| 31 | + var m = arr[j]; | |
| 32 | + showValue = k > 0 && k % step == 0 ? m + gap + showValue : m + '' + showValue; | |
| 33 | + } | |
| 34 | + if (isAdd == 1) { | |
| 35 | + // 在添加的情况下,如果添加前字符串的长度减去新的字符串的长度为2,说明多了一个间隔符,需要调整range | |
| 36 | + if (oldValue.length - showValue.length == -2) { | |
| 37 | + _range = range + 1; | |
| 38 | + } | |
| 39 | + } else { | |
| 40 | + // 在删除情况下,如果删除前字符串的长度减去新的字符串的长度为2,说明少了一个间隔符,需要调整range | |
| 41 | + if (oldValue.length - showValue.length == 2) { | |
| 42 | + _range = range - 1; | |
| 43 | + } | |
| 44 | + // 删除到最开始,range 保持 0 | |
| 45 | + if (_range <= 0) { | |
| 46 | + _range = 0; | |
| 47 | + } | |
| 48 | + } | |
| 49 | + } else { | |
| 50 | + arr.some(function(n, i) { | |
| 51 | + showValue = i > 0 && i % step == 0 ? showValue + gap + n : showValue + '' + n; | |
| 52 | + }); | |
| 53 | + var adapt = range % (step + 1) == 0 ? 1 * isAdd : 0; | |
| 54 | + _range = typeof range !== 'undefined' ? (range == 0 ? 0 : range + adapt) : showValue.length; | |
| 55 | + } | |
| 56 | + return { value: showValue, range: _range }; | |
| 57 | +}; | |
| 58 | +var numberCapital = function(number) { | |
| 59 | + var cnNums = ['\u96f6', '\u58f9', '\u8d30', '\u53c1', '\u8086', '\u4f0d', '\u9646', '\u67d2', '\u634c', '\u7396']; | |
| 60 | + // 拾 \u62fe 佰 \u4f70 仟 \u4edf | |
| 61 | + var cnIntRadice = ['', '\u62fe', '\u4f70', '\u4edf']; | |
| 62 | + // 万 \u4e07 亿 \u4ebf 兆 \u5146 | |
| 63 | + var cnIntUnits = ['', '\u4e07', '\u4ebf', '兆']; | |
| 64 | + // 角 \u89d2 分 \u5206 毫 \u6beb 厘 \u5398 | |
| 65 | + var cnDecUnits = ['\u89d2', '\u5206', '\u6beb', '\u5398']; | |
| 66 | + var cnInteger = '\u6574'; // 整 \u6574 | |
| 67 | + var cnIntLast = '\u5143'; // 元 \u5143 | |
| 68 | + var cnNegative = '\u8d1f'; // 负 | |
| 69 | + // Maximum number | |
| 70 | + var maxNum = 999999999999999.9999; | |
| 71 | + var negative; | |
| 72 | + // Integral part | |
| 73 | + var integerNum; | |
| 74 | + // Decimal part | |
| 75 | + var decimalNum; | |
| 76 | + // Capital number | |
| 77 | + var capitalStr = ''; | |
| 78 | + var parts; | |
| 79 | + /* istanbul ignore if */ | |
| 80 | + if (number == '') { | |
| 81 | + return ''; | |
| 82 | + } | |
| 83 | + number = parseFloat(number); | |
| 84 | + if (number < 0) { | |
| 85 | + negative = true; | |
| 86 | + number = Math.abs(number); | |
| 87 | + } | |
| 88 | + /* istanbul ignore if */ | |
| 89 | + if (number >= maxNum) { | |
| 90 | + return ''; | |
| 91 | + } | |
| 92 | + /* istanbul ignore if */ | |
| 93 | + if (number == 0) { | |
| 94 | + capitalStr = cnNums[0] + cnIntLast + cnInteger; | |
| 95 | + return capitalStr; | |
| 96 | + } | |
| 97 | + // Convert to String | |
| 98 | + number += ''; | |
| 99 | + if (number.indexOf('.') == -1) { | |
| 100 | + integerNum = number; | |
| 101 | + decimalNum = ''; | |
| 102 | + } else { | |
| 103 | + parts = number.split('.'); | |
| 104 | + integerNum = parts[0]; | |
| 105 | + decimalNum = parts[1].substr(0, 4); | |
| 106 | + } | |
| 107 | + // Convert integer part | |
| 108 | + if (parseInt(integerNum, 10) > 0) { | |
| 109 | + var zeroCount = 0; | |
| 110 | + for (var i = 0, IntLen = integerNum.length; i < IntLen; i++) { | |
| 111 | + var n = integerNum.substr(i, 1); | |
| 112 | + var p = IntLen - i - 1; | |
| 113 | + var q = p / 4; | |
| 114 | + var m = p % 4; | |
| 115 | + if (n == '0') { | |
| 116 | + zeroCount++; | |
| 117 | + } else { | |
| 118 | + if (zeroCount > 0) { | |
| 119 | + capitalStr += cnNums[0]; | |
| 120 | + } | |
| 121 | + zeroCount = 0; | |
| 122 | + capitalStr += cnNums[parseInt(n)] + cnIntRadice[m]; | |
| 123 | + } | |
| 124 | + if (m == 0 && zeroCount < 4) { | |
| 125 | + capitalStr += cnIntUnits[q]; | |
| 126 | + } | |
| 127 | + } | |
| 128 | + capitalStr += cnIntLast; | |
| 129 | + } | |
| 130 | + // Convert decimal part | |
| 131 | + if (decimalNum !== '') { | |
| 132 | + for (var i = 0, decLen = decimalNum.length; i < decLen; i++) { | |
| 133 | + var n = decimalNum.substr(i, 1); | |
| 134 | + if (n !== '0') { | |
| 135 | + capitalStr += cnNums[Number(n)] + cnDecUnits[i]; | |
| 136 | + } | |
| 137 | + } | |
| 138 | + } | |
| 139 | + /* istanbul ignore if */ | |
| 140 | + if (capitalStr == '') { | |
| 141 | + capitalStr += cnNums[0] + cnIntLast + cnInteger; | |
| 142 | + } else if (decimalNum == '') { | |
| 143 | + capitalStr += cnInteger; | |
| 144 | + } | |
| 145 | + if (negative) { | |
| 146 | + capitalStr = '' + cnNegative + capitalStr; | |
| 147 | + } | |
| 148 | + return capitalStr; | |
| 149 | +}; | |
| 150 | +export default { | |
| 151 | + name: 'zui-amount', | |
| 152 | + filters: { | |
| 153 | + doPrecision: function(value, precision, isRoundUp) { | |
| 154 | + var exponentialForm = Number(value + 'e' + precision); | |
| 155 | + var rounded = isRoundUp ? Math.round(exponentialForm) : Math.floor(exponentialForm); | |
| 156 | + return Number(rounded + 'e-' + precision).toFixed(precision); | |
| 157 | + }, | |
| 158 | + doFormat: function(value, hasSeparator, separator) { | |
| 159 | + if (!hasSeparator) { | |
| 160 | + return value; | |
| 161 | + } | |
| 162 | + var numberParts = value.split('.'); | |
| 163 | + var integerValue = numberParts[0]; | |
| 164 | + var decimalValue = numberParts[1] || ''; | |
| 165 | + var sign = ''; | |
| 166 | + if (integerValue.startsWith('-')) { | |
| 167 | + integerValue = integerValue.substring(1); | |
| 168 | + sign = '-'; | |
| 169 | + } | |
| 170 | + var formateValue = formatValueByGapStep(3, integerValue, separator, 'right', 0, 1); | |
| 171 | + return decimalValue ? '' + sign + formateValue.value + '.' + decimalValue : '' + sign + formateValue.value; | |
| 172 | + }, | |
| 173 | + doCapital: function(value) { | |
| 174 | + return numberCapital(value); | |
| 175 | + } | |
| 176 | + }, | |
| 177 | + props: { | |
| 178 | + value: { | |
| 179 | + type: [Number, String], | |
| 180 | + default: 0 | |
| 181 | + }, | |
| 182 | + precision: { | |
| 183 | + type: Number, | |
| 184 | + default: 2 | |
| 185 | + }, | |
| 186 | + isRoundUp: { | |
| 187 | + type: Boolean, | |
| 188 | + default: true | |
| 189 | + }, | |
| 190 | + hasSeparator: { | |
| 191 | + type: Boolean, | |
| 192 | + default: false | |
| 193 | + }, | |
| 194 | + separator: { | |
| 195 | + type: String, | |
| 196 | + default: ',' | |
| 197 | + }, | |
| 198 | + isCapital: { | |
| 199 | + type: Boolean, | |
| 200 | + default: false | |
| 201 | + }, | |
| 202 | + prefix: String | |
| 203 | + }, | |
| 204 | +}; | |
| 205 | +</script> | |
| 206 | + | |
| 207 | +<style lang="scss"> | |
| 208 | +@import './index.scss'; | |
| 209 | +</style> | ... | ... |
| 1 | +++ a/components/button/index.scss | |
| ... | ... | @@ -0,0 +1,69 @@ |
| 1 | +.zui-button { | |
| 2 | + color: #323233; | |
| 3 | + background-color: #fff; | |
| 4 | + border: 1upx solid $color-border; | |
| 5 | + padding: $h-gap-md $v-gap-md; | |
| 6 | + font-size: $font-md; | |
| 7 | + cursor: pointer; | |
| 8 | + transition: all 150ms; | |
| 9 | + display: flex; | |
| 10 | + align-items: center; | |
| 11 | + justify-content: center; | |
| 12 | + border-radius: 10upx; | |
| 13 | + &.sm { | |
| 14 | + padding: $h-gap-sm $v-gap-sm; | |
| 15 | + font-size: $font-sm; | |
| 16 | + } | |
| 17 | + &.lg { | |
| 18 | + padding: $h-gap-lg $v-gap-lg; | |
| 19 | + font-size: $font-lg; | |
| 20 | + } | |
| 21 | + &.active { | |
| 22 | + background-color: $bg-active; | |
| 23 | + border-color: $bg-active; | |
| 24 | + } | |
| 25 | + &.primary { | |
| 26 | + background-color: $color-primary; | |
| 27 | + border-color: $color-primary; | |
| 28 | + color: #FFF; | |
| 29 | + &.active { | |
| 30 | + background-color: darken($color-primary, 3%); | |
| 31 | + border-color: darken($color-primary, 3%); | |
| 32 | + } | |
| 33 | + } | |
| 34 | + &.secondary { | |
| 35 | + background-color: lighten($color-primary, 40%); | |
| 36 | + border-color: lighten($color-primary, 40%); | |
| 37 | + color: $color-primary; | |
| 38 | + &.active { | |
| 39 | + background-color: lighten($color-primary, 30%); | |
| 40 | + border-color: lighten($color-primary, 30%); | |
| 41 | + color: darken($color-primary, 3%); | |
| 42 | + } | |
| 43 | + } | |
| 44 | + &.link { | |
| 45 | + padding: $h-gap-sm $v-gap-sm; | |
| 46 | + background: inherit; | |
| 47 | + border: inherit; | |
| 48 | + color: $color-primary; | |
| 49 | + &.active { | |
| 50 | + color: darken($color-primary, 5%); | |
| 51 | + } | |
| 52 | + } | |
| 53 | + &.disabled { | |
| 54 | + border-color: $bg-disabled; | |
| 55 | + background-color: $bg-disabled; | |
| 56 | + color: $color-disabled; | |
| 57 | + cursor: not-allowed; | |
| 58 | + &.active { | |
| 59 | + border-color: $bg-disabled; | |
| 60 | + background-color: $bg-disabled; | |
| 61 | + } | |
| 62 | + } | |
| 63 | + &.round { | |
| 64 | + border-radius: 48upx; | |
| 65 | + } | |
| 66 | + &.square { | |
| 67 | + border-radius: 0upx; | |
| 68 | + } | |
| 69 | +} | |
| 0 | 70 | \ No newline at end of file | ... | ... |
| 1 | +++ a/components/button/index.vue | |
| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | +<template> | |
| 2 | + <view class="zui-button" hover-class="active" :class="classRender" :style="style" @click="onClick"> | |
| 3 | + <slot></slot> | |
| 4 | + </view> | |
| 5 | +</template> | |
| 6 | + | |
| 7 | +<script> | |
| 8 | +export default { | |
| 9 | + name: 'zui-button', | |
| 10 | + props: { | |
| 11 | + type: { | |
| 12 | + type: String, | |
| 13 | + default: 'default' | |
| 14 | + }, | |
| 15 | + size: { | |
| 16 | + type: String, | |
| 17 | + default: 'md' | |
| 18 | + }, | |
| 19 | + block: { | |
| 20 | + type: Boolean, | |
| 21 | + default: false | |
| 22 | + }, | |
| 23 | + round: Boolean, | |
| 24 | + square: Boolean, | |
| 25 | + disabled: Boolean, | |
| 26 | + }, | |
| 27 | + computed: { | |
| 28 | + style: function() { | |
| 29 | + return `display: ${this.block ? '' : 'inline-block'};` ; | |
| 30 | + }, | |
| 31 | + classRender: function() { | |
| 32 | + return [this.size, this.disabled ? 'disabled' : '', this.block ? 'block' : '', this.type, this.round ? 'round' : '', this.square ? 'square' : '']; | |
| 33 | + } | |
| 34 | + }, | |
| 35 | + methods: { | |
| 36 | + onClick: function() { | |
| 37 | + if (!this.disabled) { | |
| 38 | + this.$emit('click'); | |
| 39 | + } | |
| 40 | + } | |
| 41 | + } | |
| 42 | +} | |
| 43 | +</script> | |
| 44 | + | |
| 45 | +<style lang="scss"> | |
| 46 | +@import "./index.scss"; | |
| 47 | +</style> | |
| 0 | 48 | \ No newline at end of file | ... | ... |
| 1 | +++ a/components/cell/index.scss | |
| ... | ... | @@ -0,0 +1,52 @@ |
| 1 | +.zui-cell { | |
| 2 | + position: relative; | |
| 3 | + display: flex; | |
| 4 | + box-sizing: border-box; | |
| 5 | + width: 100%; | |
| 6 | + padding: $h-gap-lg $v-gap-md; | |
| 7 | + border-bottom: 1upx solid $color-border; | |
| 8 | + overflow: hidden; | |
| 9 | + background-color: #fff; | |
| 10 | + &--clickable { | |
| 11 | + cursor: pointer; | |
| 12 | + transition: background-color 150ms; | |
| 13 | + &.active { | |
| 14 | + background-color: $bg-active; | |
| 15 | + } | |
| 16 | + } | |
| 17 | + &__left-icon, &__right-icon { | |
| 18 | + display: flex; | |
| 19 | + align-items: center; | |
| 20 | + font-size: 42upx; | |
| 21 | + } | |
| 22 | + &__left-icon { | |
| 23 | + margin-left: $v-gap-sm; | |
| 24 | + } | |
| 25 | + &__right-icon { | |
| 26 | + margin-left: $v-gap-sm; | |
| 27 | + color: $color-disabled; | |
| 28 | + } | |
| 29 | + &__title, &__value { | |
| 30 | + flex: 1; | |
| 31 | + display: flex; | |
| 32 | + } | |
| 33 | + &__title { | |
| 34 | + flex-direction: column; | |
| 35 | + justify-content: center; | |
| 36 | + } | |
| 37 | + &__value { | |
| 38 | + align-items: center; | |
| 39 | + justify-content: flex-end; | |
| 40 | + position: relative; | |
| 41 | + overflow: hidden; | |
| 42 | + color: $color-minor; | |
| 43 | + text-align: right; | |
| 44 | + vertical-align: middle; | |
| 45 | + word-wrap: break-word; | |
| 46 | + } | |
| 47 | + &__label { | |
| 48 | + padding-top: $h-gap-sm; | |
| 49 | + color: $color-minor; | |
| 50 | + font-size: $font-sm; | |
| 51 | + } | |
| 52 | +} | ... | ... |
| 1 | +++ a/components/cell/index.vue | |
| ... | ... | @@ -0,0 +1,72 @@ |
| 1 | +<template> | |
| 2 | + <view class="zui-cell" hover-class="active" :class="classRender" @click="onClick"> | |
| 3 | + <view v-if="$slots.icon || icon" class="zui-cell__left-icon"> | |
| 4 | + <slot v-if="$slots.icon" name="icon"></slot> | |
| 5 | + <zui-icon v-else-if="icon" class="zui-cell__left-icon" :name="icon"></zui-icon> | |
| 6 | + </view> | |
| 7 | + <view v-if="$slots.title" class="zui-cell__title"> | |
| 8 | + <slot name="title"></slot> | |
| 9 | + <view v-if="$slots.label" class="zui-cell__label"> | |
| 10 | + <slot name="label"></slot> | |
| 11 | + </view> | |
| 12 | + <view v-else-if="label" class="zui-cell__label"> | |
| 13 | + <text>{{ label }}</text> | |
| 14 | + </view> | |
| 15 | + </view> | |
| 16 | + <view v-else-if="title" class="zui-cell__title"> | |
| 17 | + <text>{{ title }}</text> | |
| 18 | + <view v-if="$slots.label" class="zui-cell__label"> | |
| 19 | + <slot name="label"></slot> | |
| 20 | + </view> | |
| 21 | + <view v-else-if="label" class="zui-cell__label"> | |
| 22 | + <text>{{ label }}</text> | |
| 23 | + </view> | |
| 24 | + </view> | |
| 25 | + <view v-if="$slots.default" class="zui-cell__value"> | |
| 26 | + <slot></slot> | |
| 27 | + </view> | |
| 28 | + <view v-else-if="value" class="zui-cell__value">{{ value }}</view> | |
| 29 | + <view v-if="$slots.right || rightIcon" class="zui-cell__right-icon"> | |
| 30 | + <slot v-if="$slots.right" name="right"></slot> | |
| 31 | + <zui-icon v-else-if="rightIcon && isLink" :name="rightIcon"></zui-icon> | |
| 32 | + </view> | |
| 33 | + </view> | |
| 34 | +</template> | |
| 35 | + | |
| 36 | +<script> | |
| 37 | +import ZuiIcon from '@/components/zui/icon'; | |
| 38 | + | |
| 39 | +export default { | |
| 40 | + name: "zui-cell", | |
| 41 | + components: { | |
| 42 | + ZuiIcon | |
| 43 | + }, | |
| 44 | + props: { | |
| 45 | + icon: String, | |
| 46 | + title: String, | |
| 47 | + value: String, | |
| 48 | + label: String, | |
| 49 | + isLink: Boolean, | |
| 50 | + rightIcon: { | |
| 51 | + type: String, | |
| 52 | + default: "enter" | |
| 53 | + } | |
| 54 | + }, | |
| 55 | + computed: { | |
| 56 | + classRender: function() { | |
| 57 | + return [this.isLink ? 'zui-cell--clickable' : '']; | |
| 58 | + } | |
| 59 | + }, | |
| 60 | + methods: { | |
| 61 | + onClick: function() { | |
| 62 | + if (this.$listeners["click"]) { | |
| 63 | + this.$emit("click"); | |
| 64 | + } | |
| 65 | + } | |
| 66 | + } | |
| 67 | +}; | |
| 68 | +</script> | |
| 69 | + | |
| 70 | +<style lang="scss"> | |
| 71 | +@import "./index.scss"; | |
| 72 | +</style> | |
| 0 | 73 | \ No newline at end of file | ... | ... |
| 1 | +++ a/components/icon/index.scss | |
| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | +.zui-icon { | |
| 2 | + position: relative; | |
| 3 | + display: inline-block; | |
| 4 | + text-rendering: auto; | |
| 5 | + -webkit-font-smoothing: antialiased; | |
| 6 | +} | |
| 7 | + | |
| 8 | +.zui-info { | |
| 9 | + position: absolute; | |
| 10 | + top: 0; | |
| 11 | + right: 0; | |
| 12 | + box-sizing: border-box; | |
| 13 | + min-width: 32upx; | |
| 14 | + padding: 0 4upx; | |
| 15 | + color: #fff; | |
| 16 | + font-weight: 500; | |
| 17 | + font-size: 20upx; | |
| 18 | + font-family: PingFang SC, Helvetica Neue, Arial, sans-serif; | |
| 19 | + line-height: 1.5; | |
| 20 | + text-align: center; | |
| 21 | + background-color: $color-error; | |
| 22 | + border: 1upx solid #fff; | |
| 23 | + border-radius: 1rem; | |
| 24 | + transform: translate(50%, -50%); | |
| 25 | + transform-origin: 100%; | |
| 26 | + &.dot { | |
| 27 | + height: 24upx; | |
| 28 | + width: 24upx; | |
| 29 | + min-width: 24upx !important; | |
| 30 | + } | |
| 31 | +} | ... | ... |
| 1 | +++ a/components/icon/index.vue | |
| ... | ... | @@ -0,0 +1,43 @@ |
| 1 | +<template> | |
| 2 | + <view class="zui-icon" :class="classRender" :style="styleRender"> | |
| 3 | + <view v-if="info || dot" class="zui-info" :class="dot ? 'dot' : ''">{{ dot ? '' : info }}</view> | |
| 4 | + </view> | |
| 5 | +</template> | |
| 6 | + | |
| 7 | +<script> | |
| 8 | +export default { | |
| 9 | + name: 'zui-icon', | |
| 10 | + props: { | |
| 11 | + name: { | |
| 12 | + type: String, | |
| 13 | + default: '', | |
| 14 | + }, | |
| 15 | + fontFamily: { | |
| 16 | + type: String, | |
| 17 | + default: 'iconfont', | |
| 18 | + }, | |
| 19 | + classPrefix: { | |
| 20 | + type: String, | |
| 21 | + default: 'icon-' | |
| 22 | + }, | |
| 23 | + size: String, | |
| 24 | + color: String, | |
| 25 | + info: [Number, String], | |
| 26 | + dot: Boolean, | |
| 27 | + }, | |
| 28 | + computed: { | |
| 29 | + classRender: function() { | |
| 30 | + var fontFamily = this.fontFamily || ''; | |
| 31 | + var iconClass = this.classPrefix + this.name; | |
| 32 | + return [fontFamily, iconClass]; | |
| 33 | + }, | |
| 34 | + styleRender: function() { | |
| 35 | + return `${this.size ? `font-size: ${this.size};` : ''} ${this.color ? `color: ${this.color}` : ''};`; | |
| 36 | + }, | |
| 37 | + }, | |
| 38 | +} | |
| 39 | +</script> | |
| 40 | + | |
| 41 | +<style lang="scss"> | |
| 42 | +@import "./index.scss"; | |
| 43 | +</style> | |
| 0 | 44 | \ No newline at end of file | ... | ... |
| 1 | +++ a/components/loading/index.scss | |
| ... | ... | @@ -0,0 +1,126 @@ |
| 1 | +.zui-loading { | |
| 2 | + color: $color-minor; | |
| 3 | + display: inline-flex; | |
| 4 | + align-items: center; | |
| 5 | + justify-content: center; | |
| 6 | + &__spinner { | |
| 7 | + position: relative; | |
| 8 | + display: inline-block; | |
| 9 | + width: 50upx; | |
| 10 | + max-width: 100%; | |
| 11 | + height: 50upx; | |
| 12 | + max-height: 100%; | |
| 13 | + vertical-align: middle; | |
| 14 | + animation: zui-rotate 0.8s linear infinite; | |
| 15 | + &--spinner { | |
| 16 | + animation-timing-function: steps(12); | |
| 17 | + .zui-loading__dot { | |
| 18 | + position: absolute; | |
| 19 | + top: 0; | |
| 20 | + left: 0; | |
| 21 | + width: 100%; | |
| 22 | + height: 100%; | |
| 23 | + &::before { | |
| 24 | + display: block; | |
| 25 | + width: 2upx; | |
| 26 | + height: 25%; | |
| 27 | + margin: 0 auto; | |
| 28 | + background-color: currentColor; | |
| 29 | + border-radius: 40%; | |
| 30 | + content: ' '; | |
| 31 | + } | |
| 32 | + &:nth-of-type(1) { | |
| 33 | + transform: rotate(30deg); | |
| 34 | + opacity: 1; | |
| 35 | + } | |
| 36 | + &:nth-of-type(2) { | |
| 37 | + transform: rotate(60deg); | |
| 38 | + opacity: 0.9375; | |
| 39 | + } | |
| 40 | + &:nth-of-type(3) { | |
| 41 | + transform: rotate(90deg); | |
| 42 | + opacity: 0.875; | |
| 43 | + } | |
| 44 | + &:nth-of-type(4) { | |
| 45 | + transform: rotate(120deg); | |
| 46 | + opacity: 0.8125; | |
| 47 | + } | |
| 48 | + &:nth-of-type(5) { | |
| 49 | + transform: rotate(150deg); | |
| 50 | + opacity: 0.75; | |
| 51 | + } | |
| 52 | + &:nth-of-type(6) { | |
| 53 | + transform: rotate(180deg); | |
| 54 | + opacity: 0.6875; | |
| 55 | + } | |
| 56 | + &:nth-of-type(7) { | |
| 57 | + transform: rotate(210deg); | |
| 58 | + opacity: 0.625; | |
| 59 | + } | |
| 60 | + &:nth-of-type(8) { | |
| 61 | + transform: rotate(240deg); | |
| 62 | + opacity: 0.5625; | |
| 63 | + } | |
| 64 | + &:nth-of-type(9) { | |
| 65 | + transform: rotate(270deg); | |
| 66 | + opacity: 0.5; | |
| 67 | + } | |
| 68 | + &:nth-of-type(10) { | |
| 69 | + transform: rotate(300deg); | |
| 70 | + opacity: 0.4375; | |
| 71 | + } | |
| 72 | + &:nth-of-type(11) { | |
| 73 | + transform: rotate(330deg); | |
| 74 | + opacity: 0.375; | |
| 75 | + } | |
| 76 | + &:nth-of-type(12) { | |
| 77 | + transform: rotate(360deg); | |
| 78 | + opacity: 0.3125; | |
| 79 | + } | |
| 80 | + } | |
| 81 | + } | |
| 82 | + &--circular { | |
| 83 | + border: 1upx solid transparent; | |
| 84 | + border-top-color: currentColor; | |
| 85 | + border-radius: 100%; | |
| 86 | + } | |
| 87 | + } | |
| 88 | + &__text { | |
| 89 | + margin-left: $v-gap-sm; | |
| 90 | + color: $color-minor; | |
| 91 | + font-size: $font-md; | |
| 92 | + } | |
| 93 | + &--vertical { | |
| 94 | + flex-direction: column; | |
| 95 | + .zui-loading__text { | |
| 96 | + margin: $h-gap-sm 0 0; | |
| 97 | + } | |
| 98 | + } | |
| 99 | +} | |
| 100 | + | |
| 101 | +@keyframes zui-circular { | |
| 102 | + 0% { | |
| 103 | + stroke-dasharray: 1, 200; | |
| 104 | + stroke-dashoffset: 0; | |
| 105 | + } | |
| 106 | + | |
| 107 | + 50% { | |
| 108 | + stroke-dasharray: 90, 150; | |
| 109 | + stroke-dashoffset: -40; | |
| 110 | + } | |
| 111 | + | |
| 112 | + 100% { | |
| 113 | + stroke-dasharray: 90, 150; | |
| 114 | + stroke-dashoffset: -120; | |
| 115 | + } | |
| 116 | +} | |
| 117 | + | |
| 118 | +@keyframes zui-rotate { | |
| 119 | + from { | |
| 120 | + transform: rotate(0deg); | |
| 121 | + } | |
| 122 | + | |
| 123 | + to { | |
| 124 | + transform: rotate(360deg); | |
| 125 | + } | |
| 126 | +} | ... | ... |
| 1 | +++ a/components/loading/index.vue | |
| ... | ... | @@ -0,0 +1,57 @@ |
| 1 | +<template> | |
| 2 | + <view :class="classRender"> | |
| 3 | + <view | |
| 4 | + :class="innerClassRender" | |
| 5 | + :style="styleRender" | |
| 6 | + > | |
| 7 | + <template v-if="type === 'spinner'"> | |
| 8 | + <view | |
| 9 | + v-for="index in 12" | |
| 10 | + :key="index" | |
| 11 | + class="zui-loading__dot" | |
| 12 | + /> | |
| 13 | + </template> | |
| 14 | + </view> | |
| 15 | + <view class="zui-loading__text" :style="textSizeRender"> | |
| 16 | + <slot v-if="$slots.default"></slot> | |
| 17 | + </view> | |
| 18 | + </view> | |
| 19 | +</template> | |
| 20 | + | |
| 21 | +<script> | |
| 22 | +export default { | |
| 23 | + name: 'zui-loading', | |
| 24 | + props: { | |
| 25 | + color: String, | |
| 26 | + size: { | |
| 27 | + type: [Number, String], | |
| 28 | + default: '2rem' | |
| 29 | + }, | |
| 30 | + vertical: Boolean, | |
| 31 | + textSize: [Number, String], | |
| 32 | + type: { | |
| 33 | + type: String, | |
| 34 | + default: 'circular', | |
| 35 | + }, | |
| 36 | + }, | |
| 37 | + computed: { | |
| 38 | + classRender: function() { | |
| 39 | + return ['zui-loading', this.type ? `zui-loading--${this.type}` : '', this.vertical ? 'zui-loading--vertical' : '']; | |
| 40 | + }, | |
| 41 | + innerClassRender: function() { | |
| 42 | + return ['zui-loading__spinner', `zui-loading__spinner--${this.type}`]; | |
| 43 | + }, | |
| 44 | + styleRender: function() { | |
| 45 | + const iconSize = typeof this.textSize === 'number' ? this.textSize + 'px' : this.textSize; | |
| 46 | + return `color: ${this.color}; width: ${iconSize}; height: ${iconSize}`; | |
| 47 | + }, | |
| 48 | + textSizeRender: function() { | |
| 49 | + return `font-size: ${this.textSize}${typeof this.textSize === 'number' ? 'px' : ''};`; | |
| 50 | + } | |
| 51 | + } | |
| 52 | +}; | |
| 53 | +</script> | |
| 54 | + | |
| 55 | +<style lang="scss"> | |
| 56 | +@import "./index.scss"; | |
| 57 | +</style> | |
| 0 | 58 | \ No newline at end of file | ... | ... |
| 1 | +++ a/components/tag/index.scss | |
| ... | ... | @@ -0,0 +1,68 @@ |
| 1 | +.zui-tag { | |
| 2 | + font-size: $font-sm; | |
| 3 | + text-align: center; | |
| 4 | + display: inline-block; | |
| 5 | + -webkit-user-select: none; | |
| 6 | + .default { | |
| 7 | + background: rgba(0,0,0,0); | |
| 8 | + color: $color-error; | |
| 9 | + border-color: $color-error; | |
| 10 | + } | |
| 11 | + .shape-dot { | |
| 12 | + border-radius: 50%; | |
| 13 | + height: 32upx; | |
| 14 | + width: 32upx; | |
| 15 | + &.size-tiny { | |
| 16 | + height: 16upx; | |
| 17 | + width: 16upx; | |
| 18 | + } | |
| 19 | + } | |
| 20 | + .shape-square { | |
| 21 | + padding: 0 $v-gap-sm; | |
| 22 | + border-radius: 0; | |
| 23 | + } | |
| 24 | + .shape-fillet { | |
| 25 | + padding: $h-gap-sm $v-gap-sm; | |
| 26 | + } | |
| 27 | + .shape-bubble { | |
| 28 | + width: 50upx; | |
| 29 | + padding: 7upx 0; | |
| 30 | + border-radius: 50%; | |
| 31 | + border-bottom-left-radius: 0; | |
| 32 | + box-sizing: border-box; | |
| 33 | + &.size-small { | |
| 34 | + width: 40upx; | |
| 35 | + padding: 5upx 0; | |
| 36 | + } | |
| 37 | + &.size-tiny { | |
| 38 | + width: 30upx; | |
| 39 | + padding: 2upx 0; | |
| 40 | + } | |
| 41 | + } | |
| 42 | + .type-fill { | |
| 43 | + color: #fff; | |
| 44 | + } | |
| 45 | + .type-ghost { | |
| 46 | + border: 1px solid $color-error; | |
| 47 | + background: rgba(0,0,0,0); | |
| 48 | + } | |
| 49 | + .font-weight-normal { | |
| 50 | + font-weight: normal; | |
| 51 | + } | |
| 52 | + .font-weight-bold { | |
| 53 | + font-weight: bold; | |
| 54 | + } | |
| 55 | + .font-weight-bolder { | |
| 56 | + font-weight: bolder; | |
| 57 | + } | |
| 58 | + .size-large { | |
| 59 | + font-size: $font-md; | |
| 60 | + } | |
| 61 | + .size-small { | |
| 62 | + font-size: $font-sm; | |
| 63 | + } | |
| 64 | + .size-tiny { | |
| 65 | + font-size: $font-sm * 0.9; | |
| 66 | + } | |
| 67 | +} | |
| 68 | + | ... | ... |
| 1 | +++ a/components/tag/index.vue | |
| ... | ... | @@ -0,0 +1,161 @@ |
| 1 | +<template> | |
| 2 | + <view class="zui-tag"> | |
| 3 | + <template v-if="showQuarter"> | |
| 4 | + <view :class="computedClass"> | |
| 5 | + <view class="quarter-content"> | |
| 6 | + <slot></slot> | |
| 7 | + </view> | |
| 8 | + <view class="quarter-bg" :style="colorStyle"></view> | |
| 9 | + </view> | |
| 10 | + </template> | |
| 11 | + <template v-else-if="showCoupon"> | |
| 12 | + <view :class="computedClass"> | |
| 13 | + <view class="coupon-container" :style="colorStyle"> | |
| 14 | + <view class="left-coupon" :style="leftCoupon" v-if="showCoupon"></view> | |
| 15 | + <slot></slot> | |
| 16 | + <view class="right-coupon" :style="rightCoupon" v-if="showCoupon"></view> | |
| 17 | + </view> | |
| 18 | + </view> | |
| 19 | + </template> | |
| 20 | + <template v-else> | |
| 21 | + <view :class="computedClass" :style="[colorStyle, sizeStyle]"> | |
| 22 | + <slot></slot> | |
| 23 | + </view> | |
| 24 | + </template> | |
| 25 | + </view> | |
| 26 | +</template> | |
| 27 | + | |
| 28 | +<script> | |
| 29 | +export default { | |
| 30 | + name: "Tag", | |
| 31 | + props: { | |
| 32 | + size: { | |
| 33 | + type: String, // tiny, small, large | |
| 34 | + default: "large" | |
| 35 | + }, | |
| 36 | + shape: { | |
| 37 | + // square, circle, fillet, quarter, coupon, bubble | |
| 38 | + type: String, | |
| 39 | + default: "square" | |
| 40 | + }, | |
| 41 | + sharp: { | |
| 42 | + // top-left, top-right, bottom-left, bottom-right | |
| 43 | + type: String, | |
| 44 | + default: "" | |
| 45 | + }, | |
| 46 | + type: { | |
| 47 | + // fill ghost | |
| 48 | + type: String, | |
| 49 | + default: "ghost" | |
| 50 | + }, | |
| 51 | + fillColor: { | |
| 52 | + type: String, | |
| 53 | + default: "" | |
| 54 | + }, | |
| 55 | + fontWeight: { | |
| 56 | + // normal, bold, bolder | |
| 57 | + type: String, | |
| 58 | + default: "normal" | |
| 59 | + }, | |
| 60 | + fontColor: { | |
| 61 | + type: String, | |
| 62 | + default: "" | |
| 63 | + }, | |
| 64 | + dotSize: String, | |
| 65 | + radius: String, | |
| 66 | + }, | |
| 67 | + data: function() { | |
| 68 | + return { | |
| 69 | + sizeStyle: {} | |
| 70 | + }; | |
| 71 | + }, | |
| 72 | + mounted: function() { | |
| 73 | + this.$nextTick(function() { | |
| 74 | + if (this.shape == "circle") { | |
| 75 | + var radius = this.$el.offsetHeight / 2; | |
| 76 | + this.$set(this.sizeStyle, "paddingLeft", radius / 2 + "px"); | |
| 77 | + this.$set(this.sizeStyle, "paddingRight", radius / 2 + "px"); | |
| 78 | + this.$set(this.sizeStyle, "borderRadius", radius + "px"); | |
| 79 | + if (this.sharp) { | |
| 80 | + this.$set( | |
| 81 | + this.sizeStyle, | |
| 82 | + this.transformCamelCase("border-" + this.sharp + "-radius"), | |
| 83 | + 0 | |
| 84 | + ); | |
| 85 | + } | |
| 86 | + } | |
| 87 | + }); | |
| 88 | + }, | |
| 89 | + computed: { | |
| 90 | + computedClass: function() { | |
| 91 | + return [ | |
| 92 | + "default", | |
| 93 | + "size-" + this.size, | |
| 94 | + "shape-" + this.shape, | |
| 95 | + "type-" + this.type, | |
| 96 | + "font-weight-" + this.fontWeight | |
| 97 | + ]; | |
| 98 | + }, | |
| 99 | + colorStyle: function() { | |
| 100 | + var style = {}; | |
| 101 | + if (this.type == "fill") { | |
| 102 | + // eslint-disable-next-line | |
| 103 | + style.background = this.fillColor || '#ff5151'; | |
| 104 | + } | |
| 105 | + if (this.shape == 'dot') { | |
| 106 | + style.height = this.dotSize; | |
| 107 | + style.width = this.dotSize; | |
| 108 | + style.display = 'flex'; | |
| 109 | + style.alignItems = 'center'; | |
| 110 | + style.justifyContent = 'center'; | |
| 111 | + } | |
| 112 | + if (this.fontColor) { | |
| 113 | + if (this.type == "ghost") { | |
| 114 | + style.borderColor = this.fontColor; | |
| 115 | + } | |
| 116 | + style.color = this.fontColor; | |
| 117 | + } | |
| 118 | + if (this.radius) { | |
| 119 | + style.borderRadius = this.radius; | |
| 120 | + } | |
| 121 | + return style; | |
| 122 | + }, | |
| 123 | + leftCoupon: function() { | |
| 124 | + return { | |
| 125 | + background: this.fillColor | |
| 126 | + ? "radial-gradient(circle at left, transparent 33%, " + | |
| 127 | + this.fillColor + | |
| 128 | + " 33%)" | |
| 129 | + : "" | |
| 130 | + }; | |
| 131 | + }, | |
| 132 | + rightCoupon: function() { | |
| 133 | + return { | |
| 134 | + background: this.fillColor | |
| 135 | + ? "radial-gradient(circle at right, transparent 33%, " + | |
| 136 | + this.fillColor + | |
| 137 | + " 33%)" | |
| 138 | + : "" | |
| 139 | + }; | |
| 140 | + }, | |
| 141 | + showQuarter: function() { | |
| 142 | + return this.shape == "quarter"; | |
| 143 | + }, | |
| 144 | + showCoupon: function() { | |
| 145 | + return this.shape == "coupon"; | |
| 146 | + } | |
| 147 | + }, | |
| 148 | + methods: { | |
| 149 | + transformCamelCase: function(str) { | |
| 150 | + var re = /-(\w)/g; | |
| 151 | + return str.replace(re, function($0, $1) { | |
| 152 | + return $1.toUpperCase(); | |
| 153 | + }); | |
| 154 | + }, | |
| 155 | + } | |
| 156 | +}; | |
| 157 | +</script> | |
| 158 | + | |
| 159 | +<style lang="scss"> | |
| 160 | +@import "./index.scss"; | |
| 161 | +</style> | |
| 0 | 162 | \ No newline at end of file | ... | ... |
| 1 | +++ a/index.js | |
| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | +import Amount from './components/amount'; | |
| 2 | +import Button from './components/button'; | |
| 3 | +import Cell from './components/cell'; | |
| 4 | +import Icon from './components/icon'; | |
| 5 | +import Loading from './components/loading'; | |
| 6 | +import Tag from './components/tag'; | |
| 7 | + | |
| 8 | +export default { | |
| 9 | + Amount, | |
| 10 | + Button, | |
| 11 | + Cell, | |
| 12 | + Icon, | |
| 13 | + Loading, | |
| 14 | + Tag | |
| 15 | +}; | |
| 0 | 16 | \ No newline at end of file | ... | ... |