From 449a6c2c356803697ff20c03c3fc988a2826e654 Mon Sep 17 00:00:00 2001 From: xuxy <1059738716@qq.com> Date: 星期三, 26 六月 2024 13:49:13 +0800 Subject: [PATCH] 1 --- pages/farmer/order-records/order-records.vue | 17 components/dying318-picker/Demo.vue | 342 +++++++++++ pages/farmer/flower-manage/flower-add.vue | 92 ++ components/dying318-picker/Picker.vue | 426 ++++++++++++++ components/dying318-picker/Picker.nvue | 432 ++++++++++++++ components/dying318-picker/README.md | 479 +++++++++++++++ 6 files changed, 1,752 insertions(+), 36 deletions(-) diff --git a/components/dying318-picker/Demo.vue b/components/dying318-picker/Demo.vue new file mode 100644 index 0000000..88c6e9d --- /dev/null +++ b/components/dying318-picker/Demo.vue @@ -0,0 +1,342 @@ +<template> + <view class="container"> + <my-picker :picker-list="singleColumnPickerList" @confirm="confirm('singleColumn', $event)"> + <button class="button" type="primary">单列</button> + </my-picker> + <view class="picked-result" v-if="demos.singleColumn.picked"> + <h4>单列picker选择结果:</h4> + <pre v-html="JSON.stringify(demos.singleColumn.picked, null, 4)"></pre> + </view> + + <my-picker + :picker-list="multiColumnsPickerList" + column-num="3" + @change="changeMultiColumns" + @confirm="confirm('multiColumns', $event)"> + <button class="button" type="primary">3列联动</button> + </my-picker> + <view class="picked-result" v-if="demos.multiColumns.picked"> + <h4>3列联动选择结果:</h4> + <pre v-html="JSON.stringify(demos.multiColumns.picked, null, 4)"></pre> + </view> + <view class="picked-result" v-else-if="demos.multiColumns.columnPicked"> + <h4>多列联动第{{demos.multiColumns.columnPickedIndex + 1}}列更新:</h4> + <pre v-html="JSON.stringify(demos.multiColumns.columnPicked, null, 4)"></pre> + </view> + + <my-picker + column-num="3" + :picker-list="multiColumnsWithCustomKeyPickerList" + :picker-style="pickerStyle" + :picker-key="{value: 'id', label: 'title', children: 'sub'}" + :before-set-column="addPickerItem" + :default-value="[2,21,212]" + :item-rotate-deg="20" + @change="changeCustomMultiColumns" + @confirm="confirm('customMultiColumns', $event)"> + <button class="button" type="primary">完整自定义参数联动</button> + </my-picker> + <view class="picked-result" v-if="demos.customMultiColumns.picked"> + <h4>完整自定义参数联动选择结果:</h4> + <pre v-html="JSON.stringify(demos.customMultiColumns.picked, null, 4)"></pre> + </view> + <view class="picked-result" v-else-if="demos.customMultiColumns.columnPicked"> + <h4>完整自定义参数联动第{{demos.customMultiColumns.columnPickedIndex + 1}}列更新:</h4> + <pre v-html="JSON.stringify(demos.customMultiColumns.columnPicked, null, 4)"></pre> + </view> + + <my-picker + :picker-list="multiColumnsPickerList" + @confirm="confirm('dynamicColumns', $event)"> + <button class="button" type="primary">非固定列联动</button> + </my-picker> + <view class="picked-result" v-if="demos.dynamicColumns.picked"> + <h4>非固定列联动选择结果:</h4> + <pre v-html="JSON.stringify(demos.dynamicColumns.picked, null, 4)"></pre> + </view> + + <my-picker + :picker-list="independentMultiColumnsPickerList" + @confirm="confirm('independentMultiColumns', $event)"> + <button class="button" type="primary">多列非联动</button> + </my-picker> + <view class="picked-result" v-if="demos.independentMultiColumns.picked"> + <h4>多列非联动选择结果:</h4> + <pre v-html="JSON.stringify(demos.independentMultiColumns.picked, null, 4)"></pre> + </view> + </view> +</template> + +<script> + import MyPicker from './Picker'; + + export default { + components: {MyPicker}, + data() { + return { + demos: { + singleColumn: { + picked: null, + }, + multiColumns: { + picked: null, + columnPicked: null, + columnPickedIndex: null, + }, + customMultiColumns: { + picked: null, + columnPicked: null, + columnPickedIndex: null, + }, + dynamicColumns: { + picked: null, + columnPicked: null, + columnPickedIndex: null, + }, + independentMultiColumns: { + picked: null, + columnPicked: null, + }, + }, + pickerStyle: { + cancel: { + color: '#999', + 'font-size': '32rpx' + }, + confirm: { + color: 'green', + 'font-size': '32rpx' + }, + column: [ + {flex: 1}, + {flex: 1}, + {flex: 2}, + ] + }, + singleColumnPickerList: [ + {label: '选项1', value: 1}, + {label: '选项2', value: 2}, + {label: '选项3', value: 3}, + {label: '选项4', value: 4}, + {label: '选项5', value: 5}, + {label: '选项6', value: 6}, + {label: '选项7', value: 7}, + {label: '选项8', value: 8}, + {label: '选项9', value: 9}, + ], + multiColumnsPickerList: [ + { + label: '选项1', + value: 1, + children: [ + { + label: '选项11', + value: 11, + children: [ + {label: '选项111', value: 111}, + {label: '选项112', value: 112}, + {label: '选项113', value: 113}, + {label: '选项114', value: 114}, + {label: '选项115', value: 115}, + ] + }, + { + label: '选项12', + value: 12, + children: [ + {label: '选项121', value: 121}, + {label: '选项122', value: 122}, + {label: '选项123', value: 123}, + {label: '选项124', value: 124}, + {label: '选项125', value: 125}, + ] + }, + {label: '选项13', value: 13}, + {label: '选项14', value: 14}, + {label: '选项15', value: 15}, + ] + }, + { + label: '选项2', + value: 2, + children: [ + { + label: '选项21', + value: 21, + children: [ + {label: '选项211', value: 211}, + {label: '选项212', value: 212}, + {label: '选项213', value: 213}, + {label: '选项214', value: 214}, + {label: '选项215', value: 215}, + ] + }, + { + label: '选项22', + value: 22, + children: [ + {label: '选项221', value: 221}, + {label: '选项222', value: 222}, + {label: '选项223', value: 223}, + {label: '选项224', value: 224}, + {label: '选项225', value: 225}, + ] + }, + {label: '选项23', value: 23}, + {label: '选项24', value: 24}, + {label: '选项25', value: 25}, + ] + }, + {label: '选项3', value: 3}, + {label: '选项4', value: 4}, + {label: '选项5', value: 5}, + {label: '选项6', value: 6}, + {label: '选项7', value: 7}, + {label: '选项8', value: 8}, + {label: '选项9', value: 9}, + ], + multiColumnsWithCustomKeyPickerList: [ + { + title: '选项1', + id: 1, + sub: [ + { + title: '选项11', + id: 11, + sub: [ + {title: '选项111', id: 111}, + {title: '选项112', id: 112}, + {title: '选项113', id: 113}, + {title: '选项114', id: 114}, + {title: '选项115', id: 115}, + ] + }, + { + title: '选项12', + id: 12, + sub: [ + {title: '选项121', id: 121}, + {title: '选项122', id: 122}, + {title: '选项123', id: 123}, + {title: '选项124', id: 124}, + {title: '选项125', id: 125}, + ] + }, + {title: '选项13', id: 13}, + {title: '选项14', id: 14}, + {title: '选项15', id: 15}, + ] + }, + { + title: '选项2', + id: 2, + sub: [ + { + title: '选项21', + id: 21, + sub: [ + {title: '选项211', id: 211}, + {title: '选项212', id: 212}, + {title: '选项213', id: 213}, + {title: '选项214', id: 214}, + {title: '选项215', id: 215}, + ] + }, + { + title: '选项22', + id: 22, + sub: [ + {title: '选项221', id: 221}, + {title: '选项222', id: 222}, + {title: '选项223', id: 223}, + {title: '选项224', id: 224}, + {title: '选项225', id: 225}, + ] + }, + {title: '选项23', id: 23}, + {title: '选项24', id: 24}, + {title: '选项25', id: 25}, + ] + }, + {title: '选项3', id: 3}, + {title: '选项4', id: 4}, + {title: '选项5', id: 5}, + {title: '选项6', id: 6}, + {title: '选项7', id: 7}, + {title: '选项8', id: 8}, + {title: '选项9', id: 9}, + ], + independentMultiColumnsPickerList: [ + [ + {label: '选项1', value: 1}, + {label: '选项2', value: 2}, + {label: '选项3', value: 3}, + {label: '选项4', value: 4}, + {label: '选项5', value: 5}, + {label: '选项6', value: 6}, + {label: '选项7', value: 7}, + {label: '选项8', value: 8}, + {label: '选项9', value: 9}, + ], + [ + {label: '选项1', value: 1}, + {label: '选项2', value: 2}, + {label: '选项3', value: 3}, + {label: '选项4', value: 4}, + {label: '选项5', value: 5}, + {label: '选项6', value: 6}, + {label: '选项7', value: 7}, + {label: '选项8', value: 8}, + {label: '选项9', value: 9}, + ], + ] + } + }, + methods: { + confirm(type, picked) { + this.demos[type].picked = picked + }, + changeMultiColumns(index, picked) { + this.demos.multiColumns.columnPickedIndex = index + this.demos.multiColumns.columnPicked = picked + }, + changeCustomMultiColumns(index, picked) { + this.demos.customMultiColumns.columnPickedIndex = index + this.demos.customMultiColumns.columnPicked = picked + }, + addPickerItem(index, pickerList) { + if (pickerList.length > 0) { + pickerList = [{title: '全部', id: 0}].concat(pickerList) + } + + return pickerList + } + } + } +</script> + +<style lang="scss" scoped> + .container { + display: flex; + flex-direction: column; + align-items: center; + + height: 100vh; + padding: 20rpx; + + .button { + margin-top: 20rpx; + } + + .picked-result { + width: 80%; + margin-top: 20rpx; + padding: 20rpx; + background-color: #f3f3f3; + color: #555; + font-size: 24rpx; + line-height: 1; + } + } + +</style> diff --git a/components/dying318-picker/Picker.nvue b/components/dying318-picker/Picker.nvue new file mode 100644 index 0000000..c70e310 --- /dev/null +++ b/components/dying318-picker/Picker.nvue @@ -0,0 +1,432 @@ +<template> + <view> + <view @click="showPicker"> + <slot></slot> + </view> + <view ref="picker" class="picker-pop" v-if="show" @touchmove="stopEvent"> + <view class="picker-mask" @click="cancel" :style="{height: screenHeight}"></view> + <view class="picker-panel" :style="pickerPanelTranslate"> + <view class="picker-action"> + <text class="cancel" @click="cancel" :style="pickerStyle.cancel">取消</text> + <text class="confirm" @click="confirm" :style="pickerStyle.confirm">确定</text> + </view> + <view class="picker-content"> + <view class="picker-column" v-for="(column, columnIndex) in columns" :key="columnIndex" + :style="pickerStyle.column[columnIndex]" :data-column="columnIndex" + @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend"> + <view class="scroll-wrapper"> + <view class="scroll-list" :style="column.style"> + <view class="picker-item" v-for="(data, itemIndex) in column.pickerList" :key="itemIndex"> + <text class="picker-item-text">{{data[pickerKey.label]}}</text> + </view> + </view> + <view class="top-cover"></view> + <view class="top-cover-border"></view> + <view class="bottom-cover"></view> + <view class="bottom-cover-border"></view> + </view> + </view> + </view> + </view> + </view> + </view> +</template> + +<script> + export default { + props: { + pickerList: { + value: Array, + require: true, + default() { + return [] + } + }, + pickerKey: { + value: Object, + default() { + return { + value: 'value', + label: 'label', + children: 'children' + } + } + }, + pickerStyle: { + value: Object, + default() { + return { + cancel: {}, + confirm: {}, + column: [] + } + } + }, + defaultValue: { + value: Array, + default() { + return [] + } + }, + columnNum: { + value: Number, + default: 0 + }, + itemRotateDeg: { + value: Number, + default: 15 + }, + beforeSetColumn: { + value: Function, + default: null + }, + speedUpRatio: { + value: Number, + default: 1 + }, + }, + data() { + return { + show: false, + reactModel: true, + columns: [], + systemInfo: uni.getSystemInfoSync(), + startScrollTop: 0, + startPickedIndex: 0, + scrollingColumnIndex: 0, + } + }, + watch: { + pickerList() { + this.init() + }, + defaultValue() { + this.init() + }, + }, + computed: { + pickerItemStyle() { + return function(pickedIndex, itemIndex) { + let distance = Math.abs(pickedIndex - itemIndex) + if (distance <= 3) { + return { + transform: 'rotateX(' + distance * this.itemRotateDeg + 'deg)' + } + } else { + return {} + } + } + }, + screenHeight() { + return this.systemInfo.screenHeight + }, + pickerItemHeight() { + return Math.floor(68 * this.systemInfo.windowWidth / 750) + }, + pickerPanelTranslate() { + if (this.show) { + return { + transform: "translate(0, -" + this.systemInfo.windowBottom + ");" + } + } else { + return { + transform: "translate(0, 100%);" + } + } + } + }, + mounted() { + }, + methods: { + stopEvent(event) { + event.stopPropagation() + }, + init() { + if (Array.isArray(this.pickerList[0])) { + this.pickerList.forEach((pickerList, index) => { + this.setColumn(index, pickerList) + }) + this.reactModel = false; + } else { + this.setColumn(0, this.pickerList) + } + }, + showPicker(event) { + this.stopEvent(event) + this.init() + if (this.inited) { + this.show = true + } else { + // #ifdef H5 + let $picker = this.$refs.picker + document.body.appendChild($picker) + // #endif + setTimeout(() => { + this.show = true + }, 20) + this.inited = true + } + this.$emit('click') // 传递click事件 + }, + confirm() { + let picked = {index: [], value: [], label: [], indexes: [],values: [], labels: []} + for (let column of this.columns) { + let columnPicked = this.columnPickedInfo(column) + if (columnPicked) { + picked.index = columnPicked.index + picked.value = columnPicked.value + picked.label = columnPicked.label + + picked.indexes.push(columnPicked.index) + picked.values.push(columnPicked.value) + picked.labels.push(columnPicked.label) + } else { + picked.indexes.push(null) + picked.values.push(null) + picked.labels.push(null) + } + } + this.$emit('confirm', picked) + this.hide() + }, + cancel() { + this.$emit('cancel') + this.hide() + }, + hide() { + this.show = false + }, + columnPickedInfo(column) { + if (column.pickerList.length < 1) { + return null + } + return { + index: column.pickedIndex, + value: column.pickerList[column.pickedIndex][this.pickerKey.value], + label: column.pickerList[column.pickedIndex][this.pickerKey.label], + } + }, + touchstart(e) { + this.scrollingColumnIndex = e.target.dataset.column + this.startScrollTop = e.changedTouches[0].pageY + this.startPickedIndex = this.columns[this.scrollingColumnIndex].pickedIndex + + this.columns[this.scrollingColumnIndex].scrollEventQueue = [{ + index: this.startPickedIndex, + time: +new Date() + }] + }, + touchmove(e) { + let scrollDistance = this.startScrollTop - e.changedTouches[0].pageY + let scrollIndex = Math.round(scrollDistance/this.pickerItemHeight) + let column = this.columns[this.scrollingColumnIndex] + let currentPickedIndex = column.pickedIndex + this.setColumnIndex(column, this.startPickedIndex + scrollIndex) + if (column.pickedIndex !== currentPickedIndex) { + this.scrollColumn(column, true) + } + }, + touchend(e) { + let column = this.columns[this.scrollingColumnIndex] + this.scrollColumn(column, false, true) + }, + setColumn(columnIndex, pickerList) { + if (columnIndex === 5 || (this.columnNum > 0 && columnIndex >= this.columnNum)) { + // limit max 5 columns + return + } + let columnPickerList = pickerList || [] + if (this.beforeSetColumn) { + // 在开始渲染列之前使用钩子动态修改pickerList,注意避免对pickerList修改以保证渲染不污染源数据 + columnPickerList = this.beforeSetColumn(columnIndex, columnPickerList) + } + if (columnPickerList.length < 1) { + if (this.columnNum === 0) { + // 动态列数,当前列为空,清除后面全部列 + this.columns = this.columns.filter(column => { + return column.index < columnIndex + }) + return + } else if (columnIndex < this.columnNum) { + // 固定列数,清除下一列,递归清除后面全部列 + this.setColumn(columnIndex + 1, []) + } else { + return + } + } + + let currentColumn = this.columns[columnIndex] || {} + let column = { + index: columnIndex, + scrollEventQueue: [], + pickerList: columnPickerList, + pickedIndex: 0, + style: { + "transition-property": "transform", + "transition-duration": "200", + "transform": "translateY(0)" + } + } + this.setColumnIndex(column, currentColumn.pickedIndex || 0) // 使得column的index维持在当前选择位置 + let defaultValue = this.defaultValue && this.defaultValue[columnIndex] !== false ? this.defaultValue[columnIndex] : false + if (currentColumn.pickedIndex === undefined && defaultValue !== false) { + column.pickerList.map((pickerItem, index) => { + if (pickerItem[this.pickerKey.value] == defaultValue) { + column.pickedIndex = index + } + }) + } + + this.scrollColumn(column) + this.$set(this.columns, columnIndex, column) + }, + setColumnIndex(column, index) { + index = index < 0 ? 0 : index + column.pickedIndex = Math.min(index, column.pickerList.length - 1) + }, + scrollColumn(column, needThrottle = false, needSpeedUp = false) { + let now = +new Date() + let lastScrollEvent = column.scrollEventQueue[column.scrollEventQueue.length-1] + if (needThrottle && lastScrollEvent.time && now < (lastScrollEvent.time + 100)) { + return + } + let speedUpIndex = 0 + if (needSpeedUp && this.speedUpRatio) { + // 模拟惯性效果,在touch事件接触后,根据最后两次滚动事件的速度生成滑动的距离。在touch过程中,保持触摸距离和滚动距离的一致 + if (column.scrollEventQueue.length > 1) { + lastScrollEvent = column.scrollEventQueue[column.scrollEventQueue.length-2] + } + let speed = (column.pickedIndex - lastScrollEvent.index) / (now - lastScrollEvent.time) + speedUpIndex = Math.floor(Math.pow(speed, 2) * 800 * this.speedUpRatio) // 使用二次方曲线放大加速效果,其中效果800为默认调试参数 + speedUpIndex = speed > 0 ? speedUpIndex : -speedUpIndex; + this.setColumnIndex(column, column.pickedIndex + speedUpIndex) + } + + column.scrollEventQueue.push({ + index: column.pickedIndex, + time: now + }) + + let translateY = column.pickedIndex * this.pickerItemHeight + column.style = { + "transition-property": "transform", + "transition-duration": "200", + "transform": "translateY(" + -translateY + ")" + } + + if (this.reactModel && column.pickerList[column.pickedIndex]) { + this.setColumn(column.index + 1, column.pickerList[column.pickedIndex][this.pickerKey.children]) + } + + this.$emit('change', column.index, this.columnPickedInfo(column)) + } + }, + }; +</script> + +<style lang="scss" scoped> + .picker-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 999; + width: 750rpx; + background-color: rgba(0, 0, 0, .6); + } + + .picker-panel { + position: fixed; + bottom: 0; + left: 0; + width: 750rpx; + background-color: #fff; + transform: translate(0, 100%); + transition: transform .3s; + flex-direction: column; + } + .picker-action { + width: 750rpx; + height: 96rpx; + position: relative; + justify-content: space-between; + } + .confirm, .cancel { + padding: 30rpx; + font-size: 36rpx; + } + .confirm { + color: #007aff; + } + + .picker-content { + width: 750rpx; + height: 476rpx; + overflow: hidden; + position: relative; + } + .picker-column { + flex: 1; + font-size: 32rpx; + overflow: hidden; + flex-direction: column; + } + + .scroll-wrapper { + position: relative; + height: 476rpx; + flex-direction: column; + } + .top-cover, .bottom-cover { + width: 750rpx; + height: 204rpx; + position: absolute; + + transform: translateZ(0); + background-image: linear-gradient(to top,rgba(245, 245, 245, .2),rgba(245, 245, 245, .9)); + } + .top-cover { + top: 0; + } + .bottom-cover { + bottom: 0; + background-image: linear-gradient(to bottom,rgba(245, 245, 245,.2),rgba(245, 245, 245, .9)); + } + .top-cover-border, .bottom-cover-border { + position: absolute; + width: 750rpx; + height: 1px; + border-color: #ccc; + border-style: solid; + } + .top-cover-border { + top: 204rpx; + border-bottom-width: .5px; + } + .bottom-cover-border { + bottom: 204rpx; + border-top-width: .5px; + } + .scroll-list { + padding: 204rpx 0; + flex-direction: column; + overflow: hidden; + } + .picker-item { + justify-content: center; + align-items: center; + height: 68rpx; + } + .picker-item-text { + flex: 1; + lines: 1; + text-overflow: ellipsis; + /*无法给text设定宽度,此处无效 */ + padding: 10rpx; + text-align: center; + font-size: 32rpx; + color: #333; + + } +</style> diff --git a/components/dying318-picker/Picker.vue b/components/dying318-picker/Picker.vue new file mode 100644 index 0000000..ee9ba5b --- /dev/null +++ b/components/dying318-picker/Picker.vue @@ -0,0 +1,426 @@ +<template> + <div> + <div @click.prevent.stop="showPicker"> + <slot></slot> + </div> + <div ref="picker" class="picker-pop" v-show="show" @touchmove.prevent.stop> + <div class="picker-mask" @click="cancel"></div> + <div class="picker-panel" :class="{'picker-panel-translate': show}"> + <div class="picker-action"> + <p class="cancel" @click="cancel" :style="pickerStyle.cancel">取消</p> + <p class="confirm" @click="confirm" :style="pickerStyle.confirm">确定</p> + </div> + <div class="picker-content"> + <div class="picker-column" v-for="(column, columnIndex) in columns" :key="columnIndex" + :style="pickerStyle.column[columnIndex]" :data-column="columnIndex" + @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend"> + <div class="scroll-wrapper"> + <div class="top-cover"></div> + <div class="bottom-cover"></div> + <view class="scroll-list" :animation="column.animationData"> + <div v-for="(data, itemIndex) in column.pickerList" :key="itemIndex" > + <div class="picker-item" + <!-- #ifdef H5--> + :style="pickerItemStyle(column.pickedIndex, itemIndex)" + <!-- #endif--> + > + {{data[pickerKey.label]}} + </div> + </div> + </view> + </div> + </div> + </div> + </div> + </div> + </div> +</template> + +<script> + export default { + props: { + pickerList: { + value: Array, + require: true, + default() { + return [] + } + }, + pickerKey: { + value: Object, + default() { + return { + value: 'value', + label: 'label', + children: 'children' + } + } + }, + pickerStyle: { + value: Object, + default() { + return { + cancel: {}, + confirm: {}, + column: [] + } + } + }, + defaultValue: { + value: Array, + default() { + return [] + } + }, + columnNum: { + value: Number, + default: 0 + }, + itemRotateDeg: { + value: Number, + default: 15 + }, + beforeSetColumn: { + value: Function, + default: null + }, + speedUpRatio: { + value: Number, + default: 1 + }, + }, + data() { + return { + show: false, + reactModel: true, + columns: [], + pickerItemHeight: Math.floor(68 * uni.getSystemInfoSync().windowWidth / 750), + startScrollTop: 0, + startPickedIndex: 0, + scrollingColumnIndex: 0, + } + }, + watch: { + pickerList() { + this.init() + }, + defaultValue() { + this.init() + }, + }, + computed: { + pickerItemStyle() { + return function(pickedIndex, itemIndex) { + let distance = Math.abs(pickedIndex - itemIndex) + if (distance <= 3) { + return { + transform: 'rotateX(' + distance * this.itemRotateDeg + 'deg)' + } + } else { + return {} + } + } + } + }, + mounted() { + }, + methods: { + init() { + if (Array.isArray(this.pickerList[0])) { + this.pickerList.forEach((pickerList, index) => { + this.setColumn(index, pickerList) + }) + this.reactModel = false; + } else { + this.setColumn(0, this.pickerList) + } + + }, + showPicker() { + this.init() + if (this.inited) { + this.show = true + } else { + // #ifdef H5 + let $picker = this.$refs.picker + document.getElementsByTagName('uni-page')[0].appendChild($picker) + // #endif + setTimeout(() => { + this.show = true + }, 20) + this.inited = true + } + this.$emit('click') // 传递click事件 + }, + confirm() { + let picked = {index: [], value: [], label: [], indexes: [],values: [], labels: []} + for (let column of this.columns) { + let columnPicked = this.columnPickedInfo(column) + if (columnPicked) { + picked.index = columnPicked.index + picked.value = columnPicked.value + picked.label = columnPicked.label + + picked.indexes.push(columnPicked.index) + picked.values.push(columnPicked.value) + picked.labels.push(columnPicked.label) + } else { + picked.indexes.push(null) + picked.values.push(null) + picked.labels.push(null) + } + } + this.$emit('confirm', picked) + this.hide() + }, + cancel() { + this.$emit('cancel') + this.hide() + }, + hide() { + this.show = false + }, + columnPickedInfo(column) { + if (column.pickerList.length < 1) { + return null + } + return { + index: column.pickedIndex, + value: column.pickerList[column.pickedIndex][this.pickerKey.value], + label: column.pickerList[column.pickedIndex][this.pickerKey.label], + } + }, + touchstart(e) { + this.scrollingColumnIndex = e.currentTarget.dataset.column + this.startScrollTop = e.changedTouches[0].clientY + this.startPickedIndex = this.columns[this.scrollingColumnIndex].pickedIndex + + this.columns[this.scrollingColumnIndex].scrollEventQueue = [{ + index: this.startPickedIndex, + time: +new Date() + }] + }, + touchmove(e) { + let scrollDistance = this.startScrollTop - e.changedTouches[0].clientY + let scrollIndex = Math.round(scrollDistance/this.pickerItemHeight) + let column = this.columns[this.scrollingColumnIndex] + let currentPickedIndex = column.pickedIndex + this.setColumnIndex(column, this.startPickedIndex + scrollIndex) + if (column.pickedIndex !== currentPickedIndex) { + this.scrollColumn(column, true) + } + }, + touchend(e) { + let column = this.columns[this.scrollingColumnIndex] + this.scrollColumn(column, false, true) + }, + setColumn(columnIndex, pickerList) { + if (columnIndex === 5 || (this.columnNum > 0 && columnIndex >= this.columnNum)) { + // limit max 5 columns + return + } + let columnPickerList = pickerList || [] + if (this.beforeSetColumn) { + // 在开始渲染列之前使用钩子动态修改pickerList,注意避免对pickerList修改以保证渲染不污染源数据 + columnPickerList = this.beforeSetColumn(columnIndex, columnPickerList) + } + if (columnPickerList.length < 1) { + if (this.columnNum === 0) { + // 动态列数,当前列为空,清除后面全部列 + this.columns = this.columns.filter(column => { + return column.index < columnIndex + }) + return + } else if (columnIndex < this.columnNum) { + // 固定列数,清除下一列,递归清除后面全部列 + this.setColumn(columnIndex + 1, []) + } else { + return + } + } + + let currentColumn = this.columns[columnIndex] || {} + let column = { + index: columnIndex, + scrollEventQueue: [], + pickerList: columnPickerList, + pickedIndex: 0, + } + this.setColumnIndex(column, currentColumn.pickedIndex || 0) // 使得column的index维持在当前选择位置 + let defaultValue = this.defaultValue && this.defaultValue[columnIndex] !== false ? this.defaultValue[columnIndex] : false + if (currentColumn.pickedIndex === undefined && defaultValue !== false) { + column.pickerList.map((pickerItem, index) => { + if (pickerItem[this.pickerKey.value] == defaultValue) { + column.pickedIndex = index + } + }) + } + + this.scrollColumn(column) + this.$set(this.columns, columnIndex, column) + }, + setColumnIndex(column, index) { + index = index < 0 ? 0 : index + column.pickedIndex = Math.min(index, column.pickerList.length - 1) + }, + scrollColumn(column, needThrottle = false, needSpeedUp = false) { + let now = +new Date() + let lastScrollEvent = column.scrollEventQueue[column.scrollEventQueue.length-1] + if (needThrottle && lastScrollEvent.time && now < (lastScrollEvent.time + 100)) { + return + } + let speedUpIndex = 0 + if (needSpeedUp && this.speedUpRatio) { + // 模拟惯性效果,在touch事件接触后,根据最后两次滚动事件的速度生成滑动的距离。在touch过程中,保持触摸距离和滚动距离的一致 + if (column.scrollEventQueue.length > 1) { + lastScrollEvent = column.scrollEventQueue[column.scrollEventQueue.length-2] + } + let speed = (column.pickedIndex - lastScrollEvent.index) / (now - lastScrollEvent.time) + speedUpIndex = Math.floor(Math.pow(speed, 2) * 800 * this.speedUpRatio) // 使用二次方曲线放大加速效果,其中效果800为默认调试参数 + speedUpIndex = speed > 0 ? speedUpIndex : -speedUpIndex; + this.setColumnIndex(column, column.pickedIndex + speedUpIndex) + } + + column.scrollEventQueue.push({ + index: column.pickedIndex, + time: now + }) + + let translateY = column.pickedIndex * this.pickerItemHeight + column.animationData = uni.createAnimation({ + duration: 200, + timingFunction: 'linear', + }).translateY(-translateY).step().export() + + if (this.reactModel && column.pickerList[column.pickedIndex]) { + this.setColumn(column.index + 1, column.pickerList[column.pickedIndex][this.pickerKey.children]) + } + + this.$emit('change', column.index, this.columnPickedInfo(column)) + } + }, + }; +</script> + +<style lang="scss" scoped> + .picker-pop { + .picker-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 999; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, .6); + } + + .picker-panel { + position: fixed; + bottom: 0; + left: 0; + z-index: 999; + width: 100%; + background-color: #fff; + transform: translate(0, 100%); + transition: transform .3s; + + .picker-action { + display: flex; + position: relative; + justify-content: space-between; + + &:after { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 1px; + transform: scaleY(.5); + background-color: #dedede; + } + + p { + color: #999; + padding: 30rpx; + line-height: 1; + font-size: 36rpx; + } + .confirm { + color: #007aff; + } + } + + .picker-content { + height: calc(68rpx * 7); + overflow: hidden; + position: relative; + display: flex; + + .picker-column { + flex: 1; + font-size: 32rpx; + overflow: hidden; + } + + .scroll-wrapper { + position: relative; + height: calc(68rpx * 7); + + .top-cover, .bottom-cover { + width: 100%; + position: absolute; + z-index: 1; + transform: translateZ(0); + height: calc(68rpx * 3); + background: linear-gradient(0deg,hsla(0,0%,100%,.3),hsla(0,0%,100%,.9)); + + &:before { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 1px; + transform: scaleY(.5); + background-color: #dedede; + } + } + .top-cover { + top: 0; + } + .bottom-cover { + bottom: 0; + background: linear-gradient(180deg,hsla(0,0%,100%,.3),hsla(0,0%,100%,.9)); + + &:before { + top: 0; + } + } + .scroll-list { + padding-top: calc(68rpx * 3); + + .picker-item { + text-align: center; + font-size: 32rpx; + line-height: 68rpx; + height: 68rpx; + color: #333; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + } + + } + } + + .picker-panel-translate { + transform: translate(0, 0); + } + } +</style> diff --git a/components/dying318-picker/README.md b/components/dying318-picker/README.md new file mode 100644 index 0000000..04ae2d6 --- /dev/null +++ b/components/dying318-picker/README.md @@ -0,0 +1,479 @@ +由于官方picker是基于index维护默认选中,在复杂场景显得过于繁琐,另外官方picker在老版本安卓浏览器中无法显示(已提issue修复方案,后面应该会解决),所以就自己重新写了一套picker。 +目前还不支持日期时间选择,仅用于替代下拉多列联动的数据选择。 +## 属性/事件列表: + +| 属性/事件 | 必填 | 默认 | 功能 | +| :-----: | :-----: | :-----: | :----- | +| pickerList | 是 | [] | picker数据,支持多维,使用`children`存储多维数据 | +| pickerKey | 否 | {value: 'value',lable:'label',children: 'children'} | 用于指定用户自定义数据字段名和默认字段名对应关系 | +| pickerStyle | 否 | {} | 目前可以自定义按钮样式,列样式,参数格式见: `pickerStyle` | +| defaultValue | 否 | [] | 默认值,数组格式,和`pickerList`维数保持一致 | +| columnNum | 否 | 0 | 指定列,不传或者为0表示根据数据动态显示列数,最多限制5列 | +| itemRotateDeg | 否 | 15 | 每个选项滚动角度,模拟滚轴效果, 注意:该效果目前只支持H5 | +| beforeSetColumn | 否 | null | 每次更新列之前调用方法,参数:`columnIndex`,`pickerList`,其中`pickerList`对应当前列数据, 可使用该方法对列数据动态的处理,也可以用于ajax动态获取列数据。注意:如果未设置`columnNum`,只有`beforeSetColumn`返回空值或者超出5列才会停止渲染新的列 | +| speedUpRatio | 否 | 1 | 增加了滑动惯性特性,该参数可以设置惯性的速率比例。默认1为预设速率,设置为0则没有惯性。 | +| @confirm | 否 | null | 点击确定后触发,参数:`picked` | +| @change | 否 | null | 列选项更新的时候触发,参数:`columnIndex`, `columnPicked` | +| @cancel | 否 | null | 点击取消或者蒙版触发 | + + +### pickerList: +``` +// 联动多列 +[ + { + label: '选项1', + value: 1, + children: [ + { + label: '选项11', + value: 11, + children: [ + {label: '选项111', value: 111}, + ] + }, + ] + }, +] + +// 非联动多列 +[ + [ + {label: '选项1', value: 1}, + {label: '选项2', value: 2}, + {label: '选项3', value: 3}, + ], + [ + {label: '选项1', value: 1}, + {label: '选项2', value: 2}, + {label: '选项3', value: 3}, + ], +] + +``` + +### pickerStyle: +``` +{ + cancel: { + color: '#999', + }, + confirm: { + color: '#1CABEB', + }, + column: [ + {flex: 1}, + {flex: 1}, + {flex: 3}, + ] +} +``` + +### picked: +``` +{ + index: 1 + indexes: [3, 2, 1] + label: "1953年2分" + labels: ["3版", "2分", "1953年2分"] + value: 116 + values: [4, 115, 116] +} +``` + +### columnPicked: +``` +{ + index: 1 + label: "1953年2分" + value: 116 +} +``` + + +## Demo: +引入picker不要uni-app自带picker冲突,例:MyPicker. + +#### 单列: +``` +<my-picker :picker-list="" @confirm="confirm"> +</my-picker> +``` + +#### 固定多列: +``` +<my-picker + column-num="3" + :picker-list="" + @change="change" + @confirm="confirm"> +</my-picker> +``` + +#### 完整参数演示: +``` +<my-picker + column-num="3" + :picker-list="" + :picker-style="" + :picker-key="{value: 'id', label: 'title', children: 'sub'}" + :before-set-column="addPickerItem" + :default-value="[2,21,212]" + :item-rotate-deg="20" + @change="change" + @confirm="confirm"> +</my-picker> +``` + +#### 非固定列: +``` +<my-picker + :picker-list="" + @confirm="confirm"> +</my-picker> +``` + +#### 完整Demo +``` +<template> + <view class="container"> + <my-picker :picker-list="singleColumnPickerList" @confirm="confirm('singleColumn', $event)"> + <button class="button" type="primary">单列</button> + </my-picker> + <view class="picked-result" v-if="demos.singleColumn.picked"> + <h4>单列picker选择结果:</h4> + <pre v-html="JSON.stringify(demos.singleColumn.picked, null, 4)"></pre> + </view> + + <my-picker + :picker-list="multiColumnsPickerList" + column-num="3" + @change="change('multiColumns', ...arguments)" + @confirm="confirm('multiColumns', $event)"> + <button class="button" type="primary">3列联动</button> + </my-picker> + <view class="picked-result" v-if="demos.multiColumns.picked"> + <h4>3列联动选择结果:</h4> + <pre v-html="JSON.stringify(demos.multiColumns.picked, null, 4)"></pre> + </view> + <view class="picked-result" v-else-if="demos.multiColumns.columnPicked"> + <h4>多列联动第{{demos.multiColumns.columnPickedIndex + 1}}列更新:</h4> + <pre v-html="JSON.stringify(demos.multiColumns.columnPicked, null, 4)"></pre> + </view> + + <my-picker + column-num="3" + :picker-list="multiColumnsWithCustomKeyPickerList" + :picker-style="pickerStyle" + :picker-key="{value: 'id', label: 'title', children: 'sub'}" + :before-set-column="addPickerItem" + :default-value="[2,21,212]" + :item-rotate-deg="20" + @change="change('customMultiColumns', ...arguments)" + @confirm="confirm('customMultiColumns', $event)"> + <button class="button" type="primary">完整自定义参数联动</button> + </my-picker> + <view class="picked-result" v-if="demos.customMultiColumns.picked"> + <h4>完整自定义参数联动选择结果:</h4> + <pre v-html="JSON.stringify(demos.customMultiColumns.picked, null, 4)"></pre> + </view> + <view class="picked-result" v-else-if="demos.customMultiColumns.columnPicked"> + <h4>完整自定义参数联动第{{demos.customMultiColumns.columnPickedIndex + 1}}列更新:</h4> + <pre v-html="JSON.stringify(demos.customMultiColumns.columnPicked, null, 4)"></pre> + </view> + + <my-picker + :picker-list="multiColumnsPickerList" + @confirm="confirm('dynamicColumns', $event)"> + <button class="button" type="primary">非固定列联动</button> + </my-picker> + <view class="picked-result" v-if="demos.dynamicColumns.picked"> + <h4>非固定列联动选择结果:</h4> + <pre v-html="JSON.stringify(demos.dynamicColumns.picked, null, 4)"></pre> + </view> + + <my-picker + :picker-list="independentMultiColumnsPickerList" + @confirm="confirm('independentMultiColumns', $event)"> + <button class="button" type="primary">多列非联动</button> + </my-picker> + <view class="picked-result" v-if="demos.independentMultiColumns.picked"> + <h4>多列非联动选择结果:</h4> + <pre v-html="JSON.stringify(demos.independentMultiColumns.picked, null, 4)"></pre> + </view> + </view> +</template> + +<script> + import MyPicker from '../../components/Picker'; + + export default { + components: {MyPicker}, + data() { + return { + demos: { + singleColumn: { + picked: null, + }, + multiColumns: { + picked: null, + columnPicked: null, + columnPickedIndex: null, + }, + customMultiColumns: { + picked: null, + columnPicked: null, + columnPickedIndex: null, + }, + dynamicColumns: { + picked: null, + columnPicked: null, + columnPickedIndex: null, + }, + independentMultiColumns: { + picked: null, + columnPicked: null, + }, + }, + pickerStyle: { + cancel: { + color: '#999', + 'font-size': '32rpx' + }, + confirm: { + color: 'green', + 'font-size': '32rpx' + }, + column: [ + {flex: 1}, + {flex: 1}, + {flex: 2}, + ] + }, + singleColumnPickerList: [ + {label: '选项1', value: 1}, + {label: '选项2', value: 2}, + {label: '选项3', value: 3}, + {label: '选项4', value: 4}, + {label: '选项5', value: 5}, + {label: '选项6', value: 6}, + {label: '选项7', value: 7}, + {label: '选项8', value: 8}, + {label: '选项9', value: 9}, + ], + multiColumnsPickerList: [ + { + label: '选项1', + value: 1, + children: [ + { + label: '选项11', + value: 11, + children: [ + {label: '选项111', value: 111}, + {label: '选项112', value: 112}, + {label: '选项113', value: 113}, + {label: '选项114', value: 114}, + {label: '选项115', value: 115}, + ] + }, + { + label: '选项12', + value: 12, + children: [ + {label: '选项121', value: 121}, + {label: '选项122', value: 122}, + {label: '选项123', value: 123}, + {label: '选项124', value: 124}, + {label: '选项125', value: 125}, + ] + }, + {label: '选项13', value: 13}, + {label: '选项14', value: 14}, + {label: '选项15', value: 15}, + ] + }, + { + label: '选项2', + value: 2, + children: [ + { + label: '选项21', + value: 21, + children: [ + {label: '选项211', value: 211}, + {label: '选项212', value: 212}, + {label: '选项213', value: 213}, + {label: '选项214', value: 214}, + {label: '选项215', value: 215}, + ] + }, + { + label: '选项22', + value: 22, + children: [ + {label: '选项221', value: 221}, + {label: '选项222', value: 222}, + {label: '选项223', value: 223}, + {label: '选项224', value: 224}, + {label: '选项225', value: 225}, + ] + }, + {label: '选项23', value: 23}, + {label: '选项24', value: 24}, + {label: '选项25', value: 25}, + ] + }, + {label: '选项3', value: 3}, + {label: '选项4', value: 4}, + {label: '选项5', value: 5}, + {label: '选项6', value: 6}, + {label: '选项7', value: 7}, + {label: '选项8', value: 8}, + {label: '选项9', value: 9}, + ], + multiColumnsWithCustomKeyPickerList: [ + { + title: '选项1', + id: 1, + sub: [ + { + title: '选项11', + id: 11, + sub: [ + {title: '选项111', id: 111}, + {title: '选项112', id: 112}, + {title: '选项113', id: 113}, + {title: '选项114', id: 114}, + {title: '选项115', id: 115}, + ] + }, + { + title: '选项12', + id: 12, + sub: [ + {title: '选项121', id: 121}, + {title: '选项122', id: 122}, + {title: '选项123', id: 123}, + {title: '选项124', id: 124}, + {title: '选项125', id: 125}, + ] + }, + {title: '选项13', id: 13}, + {title: '选项14', id: 14}, + {title: '选项15', id: 15}, + ] + }, + { + title: '选项2', + id: 2, + sub: [ + { + title: '选项21', + id: 21, + sub: [ + {title: '选项211', id: 211}, + {title: '选项212', id: 212}, + {title: '选项213', id: 213}, + {title: '选项214', id: 214}, + {title: '选项215', id: 215}, + ] + }, + { + title: '选项22', + id: 22, + sub: [ + {title: '选项221', id: 221}, + {title: '选项222', id: 222}, + {title: '选项223', id: 223}, + {title: '选项224', id: 224}, + {title: '选项225', id: 225}, + ] + }, + {title: '选项23', id: 23}, + {title: '选项24', id: 24}, + {title: '选项25', id: 25}, + ] + }, + {title: '选项3', id: 3}, + {title: '选项4', id: 4}, + {title: '选项5', id: 5}, + {title: '选项6', id: 6}, + {title: '选项7', id: 7}, + {title: '选项8', id: 8}, + {title: '选项9', id: 9}, + ], + independentMultiColumnsPickerList: [ + [ + {label: '选项1', value: 1}, + {label: '选项2', value: 2}, + {label: '选项3', value: 3}, + {label: '选项4', value: 4}, + {label: '选项5', value: 5}, + {label: '选项6', value: 6}, + {label: '选项7', value: 7}, + {label: '选项8', value: 8}, + {label: '选项9', value: 9}, + ], + [ + {label: '选项1', value: 1}, + {label: '选项2', value: 2}, + {label: '选项3', value: 3}, + {label: '选项4', value: 4}, + {label: '选项5', value: 5}, + {label: '选项6', value: 6}, + {label: '选项7', value: 7}, + {label: '选项8', value: 8}, + {label: '选项9', value: 9}, + ], + ] + } + }, + methods: { + confirm(type, picked) { + this.demos[type].picked = picked + }, + change(type, index, picked) { + this.demos[type].columnPickedIndex = index + this.demos[type].columnPicked = picked + }, + addPickerItem(index, pickerList) { + if (index === 2 && pickerList.length > 0) { + pickerList = [{title: '全部', id: 0}].concat(pickerList) + } + + return pickerList + } + } + } +</script> + +<style lang="scss" scoped> + .container { + display: flex; + flex-direction: column; + align-items: center; + + height: 100vh; + padding: 20rpx; + + .button { + margin-top: 20rpx; + } + + .picked-result { + width: 80%; + margin-top: 20rpx; + padding: 20rpx; + background-color: #f3f3f3; + color: #555; + font-size: 24rpx; + line-height: 1; + } + } + +</style> + +``` + + diff --git a/pages/farmer/flower-manage/flower-add.vue b/pages/farmer/flower-manage/flower-add.vue index 0fe2353..99a077a 100644 --- a/pages/farmer/flower-manage/flower-add.vue +++ b/pages/farmer/flower-manage/flower-add.vue @@ -2,9 +2,10 @@ <view class="container-page" style="padding: 0rpx;"> <view> <view class="form-item"> - <view class="label required">商品分类todo</view> + <view class="label required">商品分类</view> <view class="m-l-a m-r-0 flex " :class="[!dto.category?'desc-gray':'']" @click="()=>{ - show_select_category=true + // show_select_category=true + $refs.picker_category.showPicker() }"> <view>{{dto.categoryStr||dto.category || '请选择'}}</view> <u-icon class="m-l-a" name="arrow-right"></u-icon> @@ -101,7 +102,7 @@ } }"> - <view>{{ '去设置'}}</view> + <view>{{ paramstr || '去设置'}}</view> <u-icon class="m-l-a" name="arrow-right"></u-icon> </view> @@ -173,11 +174,13 @@ </view> </uni-popup> + <dying318picker :pickerList="columns_categorys" ref="picker_category" :pickerKey="pickerKey"></dying318picker> </view> </template> <script> import environments from '@/environments' + import dying318picker from '@/components/dying318-picker/Picker.vue' import { mapState @@ -207,9 +210,52 @@ columns_categorys: [], columns_levels: [], // columns_params: [], //弹窗选择具体的值 + pickerKey: { + value: 'id', + lable: 'name', + children: 'children' + }, + cancel: { + color: '#999', + }, + confirm: { + color: '#20613D', + }, + // column: [ + // {flex: 1}, + // {flex: 1}, + // {flex: 3}, + // ] } }, async onLoad(options) { + //tree + this.$http.request('get', '/api/flower/category/tree', {}).then(res => { + var data = res.data + this.columns_categorys = data || [] + console.log('columns_categorys',this.columns_categorys) + // this.columns_categorys = [data || []] + // this.columns_categorys[0].unshift({ + // label: '全部', + // value: '', + // children: [] + // }) + + }) + + this.$http.request('get', '/api/code/value', { + params: { + typeCode: 'FLOWER_LEVEL' + } + }).then(res => { + var data = res.data + this.columns_levels = [data || []] + this.columns_levels[0].unshift({ + label: '全部', + value: '' + }) + + }) if (options.id) { this.id = options.id uni.setNavigationBarTitle({ @@ -231,31 +277,7 @@ }) } - //tree - this.$http.request('get', '/api/flower/category/tree', {}).then(res => { - var data = res.data - this.columns_categorys = [data || []] - this.columns_categorys[0].unshift({ - label: '全部', - value: '', - children: [] - }) - }) - - this.$http.request('get', '/api/code/value', { - params: { - typeCode: 'FLOWER_LEVEL' - } - }).then(res => { - var data = res.data - this.columns_levels = [data || []] - this.columns_levels[0].unshift({ - label: '全部', - value: '' - }) - - }) } }, @@ -436,10 +458,22 @@ }, computed: { - ...mapState(['currentInfo']) + ...mapState(['currentInfo']), + paramstr() { + if (this.dto && this.dto.params) { + var strs = [] + for (var i of this.dto.params) { + if (i.value) { + strs.push(i.value) + } + } + return strs.join(',') + } + return '' + } }, components: { - + dying318picker } } </script> diff --git a/pages/farmer/order-records/order-records.vue b/pages/farmer/order-records/order-records.vue index a1ff880..981a774 100644 --- a/pages/farmer/order-records/order-records.vue +++ b/pages/farmer/order-records/order-records.vue @@ -1,20 +1,20 @@ <template> <view class="order-records"> <view class="tj-container-p"> - <view class="tj-container"> - <view class="tj-each"> + <view class="tj-container flex"> + <view class="tj-each flex1"> <view class="value">9</view> <view class="label">买家</view> </view> - <view class="tj-each"> + <view class="tj-each flex1"> <view class="value">9扎</view> <view class="label">交易</view> </view> - <view class="tj-each"> + <view class="tj-each flex1"> <view class="value">0.00</view> <view class="label">理赔</view> </view> - <view class="tj-each"> + <view class="tj-each flex1"> <view class="value">401.12</view> <view class="label">交易合计</view> </view> @@ -132,12 +132,15 @@ padding: 30rpx; .tj-container { - height: 148rpx; + // height: 148rpx; background: #FFFFFF; border-radius: 8rpx; - + padding-top: 16rpx; + padding-bottom: 16rpx; .tj-each { + text-align: center; .label { + margin-top: 16rpx; font-weight: 400; font-size: 24rpx; color: #000000; -- Gitblit v1.9.3