|
@@ -0,0 +1,347 @@
|
|
|
+<template>
|
|
|
+ <view>
|
|
|
+ <!-- <view @tap="handleOpen">
|
|
|
+ <slot></slot>
|
|
|
+ </view> -->
|
|
|
+ <uni-popup ref="popup">
|
|
|
+ <view class="popup-content" :style="props.popupStyle">
|
|
|
+ <view class="popup-content-label">
|
|
|
+ <view class="popup-content-label-item active">{{ label }}</view>
|
|
|
+ </view>
|
|
|
+ <view class="popup-content-body">
|
|
|
+ <view v-for="(arr, i) in showList" :key="i" class="popup-content-body-list">
|
|
|
+ <view class="popup-content-body-list-items">
|
|
|
+ <view class="content">
|
|
|
+ <view
|
|
|
+ v-for="_arr in arr.data"
|
|
|
+ :key="_arr[props.itemValue]"
|
|
|
+ class="py-1 dFlex"
|
|
|
+ :class="arr.choose === _arr[props.itemValue] || (Array.isArray(arr.choose) && arr.choose.includes(_arr[props.itemValue]))? 'active' : ''"
|
|
|
+ @tap="handleNext(_arr, i)"
|
|
|
+ >
|
|
|
+ {{ _arr[props.itemLabel] }}
|
|
|
+ <uni-icons
|
|
|
+ v-if="Array.isArray(arr.choose) && arr.choose.includes(_arr[props.itemValue])"
|
|
|
+ type="checkmarkempty"
|
|
|
+ color="#00B760"
|
|
|
+ size="16"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="popup-content-footer">
|
|
|
+ <!-- <button class="btn cancel" @tap="reset">重置</button> -->
|
|
|
+ <button class="btn submit" @tap="submit">确定</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </uni-popup>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, watch } from 'vue'
|
|
|
+
|
|
|
+const emit = defineEmits(['change', 'init'])
|
|
|
+const props = defineProps({
|
|
|
+ popupStyle: {
|
|
|
+ type: [String, Object],
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ type: [String, Array, Number, Object],
|
|
|
+ default: null
|
|
|
+ },
|
|
|
+ items: { // 树结构
|
|
|
+ type: Array,
|
|
|
+ default: () => []
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ multiple: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ itemLabel: {
|
|
|
+ type: String,
|
|
|
+ default: 'label'
|
|
|
+ },
|
|
|
+ itemValue: {
|
|
|
+ type: String,
|
|
|
+ default: 'value'
|
|
|
+ },
|
|
|
+ children: {
|
|
|
+ type: String,
|
|
|
+ default: 'children'
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const popup = ref()
|
|
|
+const showList = ref([])
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => props.items,
|
|
|
+ (val) => {
|
|
|
+ // 初始化赋值
|
|
|
+ showList.value = [{
|
|
|
+ choose: -1,
|
|
|
+ data: val
|
|
|
+ }]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ immediate: true,
|
|
|
+ deep: true
|
|
|
+ }
|
|
|
+)
|
|
|
+
|
|
|
+const handleData = (val) => {
|
|
|
+ if (!val) {
|
|
|
+ showList.value = [{
|
|
|
+ choose: -1,
|
|
|
+ data: props.items
|
|
|
+ }]
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 单选 设置回显
|
|
|
+ if (!props.multiple) {
|
|
|
+ const item = findItem(val, props.items)
|
|
|
+ if (!item) {
|
|
|
+ showList.value = [{
|
|
|
+ choose: -1,
|
|
|
+ data: props.items
|
|
|
+ }]
|
|
|
+ return
|
|
|
+ }
|
|
|
+ showList.value = item.map(e => {
|
|
|
+ return {
|
|
|
+ choose: e[props.itemValue],
|
|
|
+ data: e.data
|
|
|
+ }
|
|
|
+ })
|
|
|
+ emit('init', item[item.length - 1][props.itemLabel])
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!Array.isArray(val)) {
|
|
|
+ showList.value = [{
|
|
|
+ choose: -1,
|
|
|
+ data: props.items
|
|
|
+ }]
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 多选 设置回显
|
|
|
+ const arr = []
|
|
|
+ const label = []
|
|
|
+ val.forEach(e => {
|
|
|
+ const item = findItem(e, props.items)
|
|
|
+ if (!item) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ label.push(item[item.length - 1][props.itemLabel])
|
|
|
+ if (!arr.length) {
|
|
|
+ arr.push(...item.map((e, i) => {
|
|
|
+ return {
|
|
|
+ choose: i === item.length - 1 ? [e[props.itemValue]] : e[props.itemValue],
|
|
|
+ data: e.data
|
|
|
+ }
|
|
|
+ }))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ arr[arr.length - 1].choose.push(item[item.length - 1][props.itemValue])
|
|
|
+ })
|
|
|
+ if (!arr.length) {
|
|
|
+ showList.value = [{
|
|
|
+ choose: -1,
|
|
|
+ data: props.items
|
|
|
+ }]
|
|
|
+ return
|
|
|
+ }
|
|
|
+ showList.value = arr
|
|
|
+ emit('init', label)
|
|
|
+}
|
|
|
+
|
|
|
+const findItem = (value, lists) => {
|
|
|
+ let level = -1
|
|
|
+ return check(value, lists)
|
|
|
+ function check (val, items, arr = []) {
|
|
|
+ let i = 0
|
|
|
+ level++
|
|
|
+ while (i < items.length) {
|
|
|
+ arr[level] = {
|
|
|
+ ...items[i],
|
|
|
+ data: items
|
|
|
+ }
|
|
|
+ if (items[i][props.itemValue] === val) {
|
|
|
+ return arr
|
|
|
+ }
|
|
|
+ if (items[i][props.children] && items[i][props.children].length > 0) {
|
|
|
+ const data = check(val, items[i][props.children], arr)
|
|
|
+ if (data) {
|
|
|
+ return data
|
|
|
+ }
|
|
|
+ }
|
|
|
+ i++
|
|
|
+ }
|
|
|
+ level--
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleOpen = () => {
|
|
|
+ handleData(props.value)
|
|
|
+ popup.value.open('bottom')
|
|
|
+}
|
|
|
+
|
|
|
+const handleNext = (item, index) => {
|
|
|
+ const _i = index + 1
|
|
|
+ // active.value = _i
|
|
|
+ showList.value.splice(_i, showList.value.length - _i)
|
|
|
+ showList.value[index].label = item[props.itemLabel]
|
|
|
+ if (item[props.children] && item[props.children].length) {
|
|
|
+ showList.value[index].choose = item[props.itemValue]
|
|
|
+ showList.value.push({
|
|
|
+ choose: -1,
|
|
|
+ data: item[props.children]
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!props.multiple) {
|
|
|
+ showList.value[index].choose = item[props.itemValue]
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!Array.isArray(showList.value[index].choose)) {
|
|
|
+ showList.value[index].choose = [item[props.itemValue]]
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!showList.value[index].choose.includes(item[props.itemValue])) {
|
|
|
+ showList.value[index].choose.push(item[props.itemValue])
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const _index = showList.value[index].choose.indexOf(item[props.itemValue])
|
|
|
+ showList.value[index].choose.splice(_index, 1)
|
|
|
+}
|
|
|
+
|
|
|
+const reset = () => {
|
|
|
+ showList.value = [{
|
|
|
+ choose: -1,
|
|
|
+ data: props.items
|
|
|
+ }]
|
|
|
+ emit('change', props.multiple ? [] : null)
|
|
|
+ popup.value.close()
|
|
|
+}
|
|
|
+const submit = () => {
|
|
|
+ const item = showList.value[showList.value.length - 1]
|
|
|
+ if (item.choose === -1) {
|
|
|
+ const _item = showList.value[showList.value.length - 2]
|
|
|
+ if (!item) {
|
|
|
+ emit('change', props.multiple ? [] : null)
|
|
|
+ popup.value.close()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ emit('change', props.multiple ? [_item.choose] : _item.choose, props.multiple ? showList.value[0].label : _item.label, item)
|
|
|
+ popup.value.close()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ emit('change', item.choose, props.multiple ? showList.value[0].label : item.label, item)
|
|
|
+ popup.value.close()
|
|
|
+}
|
|
|
+
|
|
|
+defineExpose({
|
|
|
+ handleOpen
|
|
|
+})
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.popup-content {
|
|
|
+ height: 580px;
|
|
|
+ background: #FFF;
|
|
|
+ border-radius: 20rpx 20rpx 0 0;
|
|
|
+ padding: 10px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ &-label {
|
|
|
+ height: 50px;
|
|
|
+ display: flex;
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ border-bottom: 2rpx solid #EEE;
|
|
|
+ &-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0 15px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ &.active {
|
|
|
+ border-bottom: 2px solid #00B760;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &-body {
|
|
|
+ flex: 1;
|
|
|
+ height: 0;
|
|
|
+ display: flex;
|
|
|
+ &-list {
|
|
|
+ height: 100%;
|
|
|
+ min-width: 100px;
|
|
|
+ margin-right: 20px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ &-label {
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 50px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ &.active {
|
|
|
+ border-bottom: 2px solid #00B760;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &-items {
|
|
|
+ flex: 1;
|
|
|
+ height: 0;
|
|
|
+ .content {
|
|
|
+ height: 100%;
|
|
|
+ overflow-x: hidden;
|
|
|
+ overflow-y: auto;
|
|
|
+ .active {
|
|
|
+ color: #00B760;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &-footer {
|
|
|
+ height: 50px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ .btn {
|
|
|
+ font-size: 28rpx;
|
|
|
+ height: 30px;
|
|
|
+ line-height: 30px;
|
|
|
+ &.cancel {
|
|
|
+ width: 40%;
|
|
|
+ margin-right: 10px;
|
|
|
+ }
|
|
|
+ &.submit {
|
|
|
+ flex: 1;
|
|
|
+ background: #00B760;
|
|
|
+ color: #FFF ;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.dFlex {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ height: 20px;
|
|
|
+}
|
|
|
+.py-1 {
|
|
|
+ padding: 10px 0;
|
|
|
+}
|
|
|
+</style>
|