111
This commit is contained in:
commit
7d450caf77
146 files changed
+39809
No files matched your search
@@ -0,0 +1,326 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch, type PropType, getCurrentInstance, type ComponentInstance, onUpdated, nextTick, type VueElement, inject } from 'vue';
|
||||
import { arrayNumberValid, arrayNumberValidByStyleMP, covetUniNumber, arrayNumberValidByStyleBorderColor, linearValid, getUnit, getUid } from "../../libs/tool";
|
||||
import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorObj, getThinColorObj } from "../../libs/colors";
|
||||
import { useTmConfig } from "../../libs/config";
|
||||
import pickerItem from './picker-item.vue';
|
||||
|
||||
const { config } = useTmConfig()
|
||||
type TM_PICKER_X_ITEM = Record<string, any>
|
||||
|
||||
|
||||
const boxHeight = ref(0)
|
||||
const id = ('tmPickerItem-' + getUid())
|
||||
const nowCurrentIndex = ref([0])
|
||||
let tid = 0
|
||||
let tid2 = 1
|
||||
|
||||
const emits = defineEmits(['changeDeep', 'countChange'])
|
||||
const props = defineProps({
|
||||
|
||||
list: {
|
||||
type: Array as PropType<TM_PICKER_X_ITEM[]>,
|
||||
default: (): TM_PICKER_X_ITEM[] => [] as TM_PICKER_X_ITEM[]
|
||||
},
|
||||
wrapWight: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
parentIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
selectedIndex: {
|
||||
type: Array as PropType<number[]>,
|
||||
default: (): number[] => [] as number[]
|
||||
},
|
||||
cellUnits: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: (): string[] => [] as string[]
|
||||
},
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: "30"
|
||||
},
|
||||
/**
|
||||
* 自定义标识id
|
||||
*/
|
||||
rangKey: {
|
||||
type: String,
|
||||
default: "id"
|
||||
},
|
||||
/**
|
||||
* 自定义标识文本字段名
|
||||
*/
|
||||
rangText: {
|
||||
type: String,
|
||||
default: "title"
|
||||
}
|
||||
})
|
||||
const _fontSize = computed((): string => {
|
||||
let fontSize = covetUniNumber(props.fontSize, config.unit)
|
||||
|
||||
if (config.fontSizeScale == 1) return fontSize
|
||||
let sizeNumber = parseInt(fontSize)
|
||||
if (isNaN(sizeNumber)) {
|
||||
sizeNumber = 16
|
||||
}
|
||||
return (sizeNumber * config.fontSizeScale).toString() + getUnit(props.fontSize)
|
||||
})
|
||||
|
||||
const _parentIndex = computed((): number => props.parentIndex)
|
||||
|
||||
const _wrapWight = computed((): number => props.wrapWight)
|
||||
|
||||
const _selectedIndex = computed((): number[] => props.selectedIndex)
|
||||
|
||||
const _unitsName = computed((): string => {
|
||||
if (props.cellUnits.length == 0) return ""
|
||||
if (props.cellUnits.length < _parentIndex.value) return ""
|
||||
return props.cellUnits[_parentIndex.value]! as string
|
||||
})
|
||||
|
||||
const _cellUnits = computed((): string[] => props.cellUnits as string[])
|
||||
|
||||
const _list = computed((): TM_PICKER_X_ITEM[] => props.list.slice(0))
|
||||
|
||||
const _nowChildren = computed((): TM_PICKER_X_ITEM[] => {
|
||||
let index = Math.max(0, Math.min(nowCurrentIndex.value[0], _list.value.length - 1))
|
||||
if (_list.value.length == 0 && index > _list.value.length - 1) return [] as TM_PICKER_X_ITEM[]
|
||||
let item = _list.value[index]
|
||||
return item?.children ?? []
|
||||
})
|
||||
|
||||
const _isDark = computed((): boolean => config.mode == 'dark')
|
||||
|
||||
const _maskStyle = computed((): string => {
|
||||
if (_isDark.value) {
|
||||
return 'background-image:linear-gradient(180deg,rgba(0, 0, 0, 0),rgba(0, 0, 0, 0)),linear-gradient(0deg, rgba(0, 0, 0, 0),rgba(0, 0, 0, 0))'
|
||||
}
|
||||
return 'background-image:linear-gradient(180deg,rgba(255,255,255,0),rgba(255,255,255,0)),linear-gradient(0deg, rgba(255,255,255,0),rgba(255,255,255,0))'
|
||||
})
|
||||
const _selectedMaskStyle = computed((): string => {
|
||||
return _isDark.value ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'
|
||||
})
|
||||
|
||||
const getIndexByid = (): number => {
|
||||
let index = 0
|
||||
|
||||
if (_selectedIndex.value.length == 0) return index;
|
||||
if (_parentIndex.value > _selectedIndex.value.length) return index
|
||||
|
||||
index = _selectedIndex.value[_parentIndex.value]
|
||||
|
||||
index = Math.max(0, Math.min(index, _list.value.length - 1))
|
||||
return index;
|
||||
}
|
||||
const getIdByindex = (): string | null => {
|
||||
let id = null;
|
||||
if (_list.value.length == 0) return id
|
||||
if (nowCurrentIndex.value.length == 0) return id;
|
||||
let index = Math.max(0, Math.min(nowCurrentIndex.value[0], _list.value.length - 1))
|
||||
return _list.value[index].id
|
||||
|
||||
}
|
||||
const mchange = (indexs: number[]) => {
|
||||
|
||||
let index = indexs[0];
|
||||
let parentIndex = _parentIndex.value;
|
||||
let ids = _selectedIndex.value.slice(0)
|
||||
|
||||
|
||||
|
||||
if (parentIndex <= ids.length - 1) {
|
||||
ids.splice(parentIndex, 1, index)
|
||||
} else {
|
||||
for (let i = 0; i < _parentIndex.value; i++) {
|
||||
ids.push(0)
|
||||
}
|
||||
ids.push(index)
|
||||
|
||||
}
|
||||
|
||||
emits("changeDeep", ids)
|
||||
}
|
||||
const change = (s: number[]) => {
|
||||
emits("changeDeep", s)
|
||||
}
|
||||
|
||||
const pushDeepCount = (n: number) => {
|
||||
|
||||
emits("countChange", n)
|
||||
}
|
||||
const onChange = (event: any) => {
|
||||
if (event.detail.value.length == 0 || _list.value.length == 0) return;
|
||||
let indexs = event.detail.value!;
|
||||
if (indexs.join('') == nowCurrentIndex.value.join('')) return;
|
||||
let index = indexs[0]
|
||||
index = Math.max(0, Math.min(index, _list.value.length - 1))
|
||||
let item = _list.value[index]
|
||||
|
||||
if (item.disabled) {
|
||||
index = nowCurrentIndex.value[0] > index ? index + 1 : index - 1;
|
||||
index = Math.max(_list.value.length, index)
|
||||
index = Math.min(0, index)
|
||||
indexs = [index]
|
||||
}
|
||||
nowCurrentIndex.value = indexs;
|
||||
|
||||
mchange(indexs)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
pushDeepCount(_parentIndex.value)
|
||||
nowCurrentIndex.value = [getIndexByid()] as number[]
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
clearTimeout(tid)
|
||||
clearTimeout(tid2)
|
||||
})
|
||||
|
||||
watch([() => props.selectedIndex], () => {
|
||||
nowCurrentIndex.value = [getIndexByid()] as number[]
|
||||
|
||||
}, { deep: true })
|
||||
watch(() => props.list, () => {
|
||||
nowCurrentIndex.value = [getIndexByid()] as number[]
|
||||
mchange(nowCurrentIndex.value.slice(0))
|
||||
|
||||
}, { deep: true })
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
options: {
|
||||
styleIsolation: "apply-shared",
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
multipleSlots: true,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<view>
|
||||
<view class="tmPickerViewUnit" v-if="_unitsName != ''">
|
||||
<tm-text font-size="24" class="tmPickerViewUnitText">{{ _unitsName }}</tm-text>
|
||||
</view>
|
||||
<view class="tmPickerView">
|
||||
<view style="padding: 0 2.5px;">
|
||||
<picker-view :immediate-change="false" :value="nowCurrentIndex" @change="onChange"
|
||||
:style="{ height: '250px', width: (_wrapWight - 5) + 'px' }" :mask-style="`${_maskStyle}`"
|
||||
:indicator-style="`height:50px;border:none;border-radius:10px`">
|
||||
<picker-view-column>
|
||||
<view :style="{ background: nowCurrentIndex[0] == index ? _selectedMaskStyle : 'none' }"
|
||||
v-for="(item, index) in _list" :key="index" class="tmPickerViewWrapCoumn">
|
||||
<text class="tmPickerViewWrapCoumnText" :style="{
|
||||
fontSize: _fontSize,
|
||||
lineHeight: 1,
|
||||
fontWeight: nowCurrentIndex[0] == index ? 'bold' : 'inherit',
|
||||
opacity: item.disabled ? 0.4 : (nowCurrentIndex[0] == index ? 1 : 0.6),
|
||||
color: _isDark ? 'rgba(255,255,255,0.8)' : '#000000'
|
||||
}">
|
||||
{{ item[rangText] }}
|
||||
</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
|
||||
</view>
|
||||
|
||||
<picker-item :rangKey="rangKey" :rangText="rangText" @countChange="pushDeepCount" :font-size="fontSize"
|
||||
:cellUnits="_cellUnits" @changeDeep="change" :selectedIndex="_selectedIndex" :wrapWight="_wrapWight"
|
||||
:parentIndex="_parentIndex + 1" v-if="_nowChildren.length > 0" :list="_nowChildren">
|
||||
</picker-item>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<style>
|
||||
.uni-picker-view-indicator:after,
|
||||
.uni-picker-view-indicator:before {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tmPickerViewUnit {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.tmPickerViewUnitText {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tmPickerViewWrapCoumnText {
|
||||
margin: 0 6px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
display: -webkit-box;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
|
||||
}
|
||||
|
||||
.tmPickerView {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.tmPickerViewWrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tmPickerContent {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.tmPickerMasker {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.xPickErBar {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 10px;
|
||||
margin: 0 3px;
|
||||
flex: 1;
|
||||
|
||||
}
|
||||
|
||||
.tmPickerContent {
|
||||
transition-duration: 350ms;
|
||||
transition-property: left, right, top, bottom;
|
||||
transition-timing-function: cubic-bezier(0, 0.55, 0.45, 1);
|
||||
}
|
||||
|
||||
.tmPickerViewWrapCoumn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
border-radius: 10px;
|
||||
/* background-color: #f5f5f5; */
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,242 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch, type PropType, getCurrentInstance, type ComponentInstance, onUpdated, nextTick, VueElement, inject } from 'vue';
|
||||
import { arrayNumberValid, arrayNumberValidByStyleMP, covetUniNumber, arrayNumberValidByStyleBorderColor, linearValid, getUnit, getUid } from "../../libs/tool";
|
||||
import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorObj, getThinColorObj } from "../../libs/colors";
|
||||
import { useTmConfig } from "../../libs/config";
|
||||
import pickerItem from './picker-item.vue';
|
||||
|
||||
const { config } = useTmConfig()
|
||||
type TM_PICKER_X_ITEM = Record<string, any>
|
||||
|
||||
|
||||
const boxHeight = ref(0)
|
||||
const id = ('tmPickerItem-' + getUid())
|
||||
const nowCurrentIndex = ref([0])
|
||||
let tid = 0
|
||||
let tid2 = 1
|
||||
|
||||
const emits = defineEmits(['changeDeep'])
|
||||
const props = defineProps({
|
||||
|
||||
list: {
|
||||
type: Array as PropType<TM_PICKER_X_ITEM[][]>,
|
||||
default: (): TM_PICKER_X_ITEM[] => [] as TM_PICKER_X_ITEM[]
|
||||
},
|
||||
|
||||
|
||||
selectedIndex: {
|
||||
type: Array as PropType<number[]>,
|
||||
default: (): number[] => [] as number[]
|
||||
},
|
||||
cellUnits: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: (): string[] => [] as string[]
|
||||
},
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: "30"
|
||||
},
|
||||
/**
|
||||
* 自定义标识id
|
||||
*/
|
||||
rangKey: {
|
||||
type: String,
|
||||
default: "id"
|
||||
},
|
||||
/**
|
||||
* 自定义标识文本字段名
|
||||
*/
|
||||
rangText: {
|
||||
type: String,
|
||||
default: "title"
|
||||
},
|
||||
})
|
||||
const _fontSize = computed((): string => {
|
||||
let fontSize = covetUniNumber(props.fontSize, config.unit)
|
||||
|
||||
if (config.fontSizeScale == 1) return fontSize
|
||||
let sizeNumber = parseInt(fontSize)
|
||||
if (isNaN(sizeNumber)) {
|
||||
sizeNumber = 16
|
||||
}
|
||||
return (sizeNumber * config.fontSizeScale).toString() + getUnit(props.fontSize)
|
||||
})
|
||||
|
||||
|
||||
|
||||
const refreshKey = ref(12)
|
||||
|
||||
const _cellUnits = computed((): string[] => props.cellUnits as string[])
|
||||
|
||||
const _list = computed((): TM_PICKER_X_ITEM[][] => props.list.slice(0))
|
||||
|
||||
|
||||
const _isDark = computed((): boolean => config.mode == 'dark')
|
||||
|
||||
const _maskStyle = computed((): string => {
|
||||
if (_isDark.value) {
|
||||
return 'background-image:linear-gradient(180deg,rgba(0, 0, 0, 0),rgba(0, 0, 0, 0)),linear-gradient(0deg, rgba(0, 0, 0, 0),rgba(0, 0, 0, 0))'
|
||||
}
|
||||
return 'background-image:linear-gradient(180deg,rgba(255,255,255,0),rgba(255,255,255,0)),linear-gradient(0deg, rgba(255,255,255,0),rgba(255,255,255,0))'
|
||||
})
|
||||
const _selectedMaskStyle = computed((): string => {
|
||||
return _isDark.value ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'
|
||||
})
|
||||
|
||||
|
||||
|
||||
const onChange = (event: any) => {
|
||||
|
||||
if (event.detail.value.length == 0 || _list.value.length == 0) return;
|
||||
let indexs = event.detail.value!;
|
||||
if (indexs.join('') == nowCurrentIndex.value.join('')) return;
|
||||
nowCurrentIndex.value = indexs
|
||||
emits("changeDeep", indexs)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nowCurrentIndex.value = props.selectedIndex.slice(0)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
clearTimeout(tid)
|
||||
clearTimeout(tid2)
|
||||
})
|
||||
|
||||
watch(() => props.selectedIndex, (newvalue: number[]) => {
|
||||
if (newvalue.join('') == nowCurrentIndex.value.join('')) return;
|
||||
nowCurrentIndex.value = newvalue.slice(0)
|
||||
}, { deep: true })
|
||||
watch(_list, (newvalue) => {
|
||||
refreshKey.value += 1;
|
||||
|
||||
}, { deep: true })
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
options: {
|
||||
styleIsolation: "apply-shared",
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
multipleSlots: true,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<view>
|
||||
<view class="tmPickerViewUnit" v-if="_cellUnits.length > 0">
|
||||
<view v-for="(item, index) in _cellUnits" :key="index" style="flex: 1;text-align: center;">
|
||||
<tm-text font-size="24" class="tmPickerViewUnitText">{{ item }}</tm-text>
|
||||
</view>
|
||||
</view>
|
||||
<picker-view :key="refreshKey" :immediate-change="true" :value="nowCurrentIndex" @change="onChange"
|
||||
:style="{ height: '250px', width: '100%' }" :mask-style="`${_maskStyle}`"
|
||||
:indicator-style="`height:50px;border:none;border-radius:10px`">
|
||||
<picker-view-column v-for="(item2, index2) in _list" :key="index2">
|
||||
<view
|
||||
:style="{ background: nowCurrentIndex[index2] == index ? _selectedMaskStyle : 'none', margin: '0 4px' }"
|
||||
v-for="(item, index) in item2" :key="index" class="tmPickerViewWrapCoumn">
|
||||
<text class="tmPickerViewWrapCoumnText" :style="{
|
||||
fontSize: _fontSize,
|
||||
lineHeight: 1,
|
||||
fontWeight: nowCurrentIndex[index2] == index ? 'bold' : 'inherit',
|
||||
opacity: item.disabled ? 0.4 : (nowCurrentIndex[index2] == index ? 1 : 0.6),
|
||||
color: _isDark ? 'rgba(255,255,255,0.8)' : '#000000'
|
||||
}">
|
||||
{{ item[rangText] || "" }}
|
||||
</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
</template>
|
||||
<style>
|
||||
.uni-picker-view-indicator:after,
|
||||
.uni-picker-view-indicator:before {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tmPickerViewUnit {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.tmPickerViewUnitText {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tmPickerViewWrapCoumnText {
|
||||
margin: 0 6px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
display: -webkit-box;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
|
||||
}
|
||||
|
||||
.tmPickerView {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.tmPickerViewWrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tmPickerContent {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.tmPickerMasker {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.xPickErBar {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 10px;
|
||||
margin: 0 3px;
|
||||
flex: 1;
|
||||
|
||||
}
|
||||
|
||||
.tmPickerContent {
|
||||
transition-duration: 350ms;
|
||||
transition-property: left, right, top, bottom;
|
||||
transition-timing-function: cubic-bezier(0, 0.55, 0.45, 1);
|
||||
}
|
||||
|
||||
.tmPickerViewWrapCoumn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
border-radius: 10px;
|
||||
/* background-color: #f5f5f5; */
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,283 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch, PropType, getCurrentInstance, ComponentInstance, onUpdated, nextTick, VueElement, inject } from 'vue';
|
||||
import { arrayNumberValid, arrayNumberValidByStyleMP, covetUniNumber, arrayNumberValidByStyleBorderColor, linearValid, getUnit, getUid } from "../../libs/tool";
|
||||
import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorObj, getThinColorObj } from "../../libs/colors";
|
||||
import { useTmConfig } from "../../libs/config";
|
||||
import pickerItem from './picker-item.vue';
|
||||
type TM_PICKER_X_ITEM = Record<string, any>
|
||||
type TM_PICKER_ITEM_INFO = Record<string, any>
|
||||
const proxy = getCurrentInstance()?.proxy ?? null;
|
||||
|
||||
/**
|
||||
* @displayName 选择器容器
|
||||
* @exportName tm-picker-view
|
||||
* @category 表单组件
|
||||
* @description 可单列或者多列
|
||||
* @constant 平台兼容
|
||||
* | H5 | uniAPP | 小程序 | version |
|
||||
| --- | --- | --- | --- |
|
||||
| ☑️| ☑️ | ☑️ | ☑️ | ☑️ | 1.0.0 |
|
||||
*/
|
||||
defineOptions({ name: 'TmPickerView' });
|
||||
const { config } = useTmConfig()
|
||||
const wrapTotalWidth = ref(0)
|
||||
const nowValue = ref<Array<string | number>>([])
|
||||
const _modelValueIndex = ref<Array<number>>([])
|
||||
const okNodeInfo = ref(false)
|
||||
const tid = ref(0)
|
||||
const parentIndex = ref(0)
|
||||
const emits = defineEmits([
|
||||
/**
|
||||
* 选项变化时触发
|
||||
* @param {string[]} ids - 当前选中项的id
|
||||
*/
|
||||
'change',
|
||||
/**
|
||||
* 等同v-model:model-str
|
||||
* 只对外输出当前回选区的选中项的文本,不要外部改变此值。
|
||||
*/
|
||||
'update:modelStr',
|
||||
|
||||
'update:modelValue'
|
||||
|
||||
])
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
/**
|
||||
* 数据项
|
||||
* 格式类型为:PICKER_ITEM_INFO
|
||||
*/
|
||||
list: {
|
||||
type: Array as PropType<TM_PICKER_ITEM_INFO[]>,
|
||||
default: (): TM_PICKER_ITEM_INFO[] => [] as TM_PICKER_ITEM_INFO[]
|
||||
},
|
||||
/**
|
||||
* 当前选中项的id值
|
||||
*/
|
||||
modelValue: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: (): string[] => [] as string[]
|
||||
},
|
||||
/**
|
||||
* 当前选中项的标题文本组
|
||||
*/
|
||||
modelStr: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/**
|
||||
* 显示在顶部的单位名称
|
||||
*/
|
||||
cellUnits: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: (): string[] => [] as string[]
|
||||
},
|
||||
/**
|
||||
* 项目的字体号大小
|
||||
*/
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: "32"
|
||||
},
|
||||
/**
|
||||
* 自定义标识id
|
||||
*/
|
||||
rangKey: {
|
||||
type: String,
|
||||
default: "id"
|
||||
},
|
||||
/**
|
||||
* 自定义标识文本字段名
|
||||
*/
|
||||
rangText: {
|
||||
type: String,
|
||||
default: "title"
|
||||
}
|
||||
})
|
||||
|
||||
const _cellUnits = computed(() => props.cellUnits)
|
||||
const _list = computed(() => props.list)
|
||||
const _maxDeep = computed(() => {
|
||||
if (_list.value.length == 0) return 0;
|
||||
function getdiepWidth(list: TM_PICKER_X_ITEM[]): number {
|
||||
let deepIndex = 1;
|
||||
const node = list[0];
|
||||
if (node?.children?.length > 0) {
|
||||
deepIndex += getdiepWidth(node.children);
|
||||
}
|
||||
|
||||
return deepIndex;
|
||||
}
|
||||
return getdiepWidth(_list.value.slice(0));
|
||||
})
|
||||
const _deepWidth = computed(() => {
|
||||
if (_list.value.length == 0) return 0;
|
||||
if (_list.value.length == 0) return wrapTotalWidth.value;
|
||||
function getdiepWidth(list: TM_PICKER_X_ITEM[]): number {
|
||||
let deepIndex = 1;
|
||||
const node = list[0];
|
||||
if (Array.isArray(node?.children) && node.children.length > 0) {
|
||||
deepIndex += getdiepWidth(node.children);
|
||||
}
|
||||
|
||||
return deepIndex;
|
||||
}
|
||||
return wrapTotalWidth.value / Math.max(1, getdiepWidth(_list.value))
|
||||
})
|
||||
const setChangeStrvmodel = () => {
|
||||
let listitem = getModelStr()
|
||||
let idvalue = [] as string[]
|
||||
let strs = [] as string[]
|
||||
listitem.forEach((el) => {
|
||||
idvalue.push(el[props.rangKey])
|
||||
strs.push(el[props.rangText])
|
||||
})
|
||||
|
||||
emits('update:modelStr', strs.join(","))
|
||||
}
|
||||
const oninitFun = () => {
|
||||
nowValue.value = props.modelValue.slice(0)
|
||||
_modelValueIndex.value = chuliIndex(getIdeByindex());
|
||||
setChangeStrvmodel()
|
||||
getNodeInfo();
|
||||
}
|
||||
const change = (ids: number[]) => {
|
||||
|
||||
_modelValueIndex.value = ids;
|
||||
let listitem = getModelStr()
|
||||
|
||||
let idvalue = [] as string[]
|
||||
let strs = [] as string[]
|
||||
listitem.forEach((el) => {
|
||||
idvalue.push(el[props.rangKey])
|
||||
strs.push(el[props.rangText])
|
||||
})
|
||||
|
||||
/**
|
||||
* 等同v-model
|
||||
*/
|
||||
emits('update:modelValue', idvalue)
|
||||
|
||||
emits('update:modelStr', strs.join(","))
|
||||
if (idvalue.join('') == props.modelValue.join('')) return;
|
||||
clearTimeout(tid.value);
|
||||
tid.value = setTimeout(function () {
|
||||
/**
|
||||
* 选项变化时触发
|
||||
* @param {string[]} ids 当前选中项的id
|
||||
*/
|
||||
emits('change', idvalue)
|
||||
|
||||
}, 30);
|
||||
}
|
||||
const chuliIndex = (indexs: number[]): number[] => {
|
||||
let ids = indexs.slice(0)
|
||||
let val = nowValue.value.slice(0)
|
||||
for (let i = 0; i < _maxDeep.value; i++) {
|
||||
if (i >= val.length) {
|
||||
ids.push(0)
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
const getIdeByindex = (): number[] => {
|
||||
let index = 0;
|
||||
let val = nowValue.value.slice(0)
|
||||
let indexs = [] as number[]
|
||||
function getIndex(nodes: TM_PICKER_X_ITEM[]) {
|
||||
if (val.length <= index || val.length == 0) return;
|
||||
let id = val[index]
|
||||
let sindex = 0
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let item = nodes[i]
|
||||
if (item[props.rangKey] == id) {
|
||||
sindex = i;
|
||||
indexs.push(sindex)
|
||||
if (item?.children?.length > 0) {
|
||||
index += 1
|
||||
getIndex(item.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
getIndex(_list.value)
|
||||
return indexs;
|
||||
}
|
||||
const getModelStr = (): TM_PICKER_X_ITEM[] => {
|
||||
let ids = _modelValueIndex.value.slice(0)
|
||||
let index = 0;
|
||||
|
||||
let selectedList = [] as TM_PICKER_X_ITEM[]
|
||||
|
||||
|
||||
|
||||
function getStr(nodes: TM_PICKER_X_ITEM[]) {
|
||||
if (ids.length <= index || ids.length == 0) return;
|
||||
let idx = ids[index]
|
||||
idx = Math.max(0, Math.min(nodes.length - 1, idx));
|
||||
let item = nodes[idx];
|
||||
selectedList.push(item)
|
||||
if (item?.children?.length > 0) {
|
||||
index += 1
|
||||
getStr(item.children)
|
||||
}
|
||||
}
|
||||
getStr(_list.value)
|
||||
return selectedList
|
||||
}
|
||||
const conutChangeEnd = (n: number) => {
|
||||
|
||||
}
|
||||
const getNodeInfo = () => {
|
||||
uni.createSelectorQuery().in(proxy)
|
||||
.select(".tmPickerView")
|
||||
.boundingClientRect().exec((ret) => {
|
||||
let nodeinfo = ret[0] as UniApp.NodeInfo
|
||||
wrapTotalWidth.value = nodeinfo.width!
|
||||
okNodeInfo.value = true;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
oninitFun();
|
||||
uni.$on('onResize', oninitFun)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
clearTimeout(tid.value);
|
||||
uni.$off('onResize', oninitFun)
|
||||
})
|
||||
watch(() => props.modelValue, (newValue: Array<string | number>) => {
|
||||
if (newValue.join('') == nowValue.value.join('')) return;
|
||||
nowValue.value = newValue.slice(0);
|
||||
_modelValueIndex.value = chuliIndex(getIdeByindex());
|
||||
setChangeStrvmodel()
|
||||
}, { deep: true })
|
||||
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
options: {
|
||||
styleIsolation: "apply-shared",
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
multipleSlots: true,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<view class="tmPickerView">
|
||||
<picker-item :rangKey="props.rangKey" :rangText="props.rangText" @countChange="conutChangeEnd"
|
||||
:font-size="props.fontSize" :cellUnits="_cellUnits" v-if="okNodeInfo" @changeDeep="change"
|
||||
:selectedIndex="_modelValueIndex" :parentIndex="parentIndex" :wrapWight="_deepWidth" :list="_list"
|
||||
class="tmPickerViewItem"></picker-item>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped>
|
||||
.tmPickerView {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,276 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch, PropType, getCurrentInstance, ComponentInstance, onUpdated, nextTick, VueElement, inject } from 'vue';
|
||||
import { arrayNumberValid, arrayNumberValidByStyleMP, covetUniNumber, arrayNumberValidByStyleBorderColor, linearValid, getUnit, getUid } from "../../libs/tool";
|
||||
import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorObj, getThinColorObj } from "../../libs/colors";
|
||||
import { useTmConfig } from "../../libs/config";
|
||||
import pickerItem from './picker-item.vue';
|
||||
type TM_PICKER_X_ITEM = Record<string, any>
|
||||
type TM_PICKER_ITEM_INFO = Record<string, any>
|
||||
const proxy = getCurrentInstance()?.proxy ?? null;
|
||||
|
||||
/**
|
||||
* @displayName 选择器容器
|
||||
* @exportName tm-picker-view
|
||||
* @category 表单组件
|
||||
* @description 可单列或者多列
|
||||
* @constant 平台兼容
|
||||
* | H5 | uniAPP | 小程序 | version |
|
||||
| --- | --- | --- | --- |
|
||||
| ☑️| ☑️ | ☑️ | ☑️ | ☑️ | 1.0.0 |
|
||||
*/
|
||||
defineOptions({ name: 'TmPickerView' });
|
||||
const { config } = useTmConfig()
|
||||
const nowValue = ref<Array<string | number>>([])
|
||||
const _modelValueIndex = ref<Array<number>>([])
|
||||
const tid = ref(0)
|
||||
const emits = defineEmits([
|
||||
/**
|
||||
* 选项变化时触发
|
||||
* @param {string[]} ids - 当前选中项的id
|
||||
*/
|
||||
'change',
|
||||
/**
|
||||
* 等同v-model:model-str
|
||||
* 只对外输出当前回选区的选中项的文本,不要外部改变此值。
|
||||
*/
|
||||
'update:modelStr',
|
||||
|
||||
'update:modelValue'
|
||||
|
||||
])
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
/**
|
||||
* 数据项
|
||||
* 格式类型为:PICKER_ITEM_INFO
|
||||
*/
|
||||
list: {
|
||||
type: Array as PropType<TM_PICKER_ITEM_INFO[]>,
|
||||
default: (): TM_PICKER_ITEM_INFO[] => [] as TM_PICKER_ITEM_INFO[]
|
||||
},
|
||||
/**
|
||||
* 当前选中项的id值
|
||||
*/
|
||||
modelValue: {
|
||||
type: Array<string|number>,
|
||||
default: (): string[] => [] as string[]
|
||||
},
|
||||
/**
|
||||
* 当前选中项的标题文本组
|
||||
*/
|
||||
modelStr: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
/**
|
||||
* 显示在顶部的单位名称
|
||||
*/
|
||||
cellUnits: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: (): string[] => [] as string[]
|
||||
},
|
||||
/**
|
||||
* 项目的字体号大小
|
||||
*/
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: "32"
|
||||
},
|
||||
/**
|
||||
* 自定义标识id
|
||||
*/
|
||||
rangKey: {
|
||||
type: String,
|
||||
default: "id"
|
||||
},
|
||||
/**
|
||||
* 自定义标识文本字段名
|
||||
*/
|
||||
rangText: {
|
||||
type: String,
|
||||
default: "title"
|
||||
}
|
||||
})
|
||||
|
||||
const _cellUnits = computed(() => props.cellUnits)
|
||||
const _list = computed(() => props.list)
|
||||
const _nowShowList = ref<Record<string,any>[][]>([])
|
||||
const _maxDeep = computed(() => {
|
||||
if (_list.value.length == 0) return 0;
|
||||
function getdiepWidth(list: TM_PICKER_X_ITEM[]): number {
|
||||
let deepIndex = 1;
|
||||
const node = list[0];
|
||||
if (node?.children?.length > 0) {
|
||||
deepIndex += getdiepWidth(node.children);
|
||||
}
|
||||
return deepIndex;
|
||||
}
|
||||
return getdiepWidth(_list.value.slice(0));
|
||||
})
|
||||
|
||||
|
||||
const oninitFun = () => {
|
||||
nowValue.value = props.modelValue.slice(0)
|
||||
let currneinexs = getIndexsByids(nowValue.value).indexs;
|
||||
_modelValueIndex.value = nowValue.value.length==0||currneinexs.length==0?new Array(_maxDeep.value).fill(0):currneinexs;
|
||||
const {data,strs,ids} = getIdsByindexs(_modelValueIndex.value)
|
||||
_nowShowList.value = data.slice(0)
|
||||
if(nowValue.value.length==0){
|
||||
nowValue.value = ids.slice(0)
|
||||
nextTick(()=>{
|
||||
emits('update:modelStr', strs.join(","))
|
||||
emits('update:modelValue', ids.slice(0))
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
const change = (ixs: number[]) => {
|
||||
|
||||
const {ids,data,strs} = getIdsByindexs(ixs)
|
||||
_modelValueIndex.value = getIndexsByids(ids).indexs;
|
||||
_nowShowList.value = data.slice(0)
|
||||
nowValue.value = ids;
|
||||
|
||||
/**
|
||||
* 等同v-model
|
||||
*/
|
||||
emits('update:modelValue', ids.slice(0))
|
||||
emits('update:modelStr', strs.join(","))
|
||||
|
||||
if (strs.join('') == props.modelValue.join('')) return;
|
||||
emits('change', ids.slice(0))
|
||||
}
|
||||
|
||||
|
||||
// 根据id返回索引
|
||||
const getIndexsByids = (ids:Array<string|number>):{indexs:Array<number>,data:TM_PICKER_X_ITEM[][],strs:string[]}=> {
|
||||
let index = 0;
|
||||
let val = ids.slice(0)
|
||||
let indexs = [] as number[]
|
||||
let data = [] as TM_PICKER_X_ITEM[][]
|
||||
let strs = [] as string[]
|
||||
|
||||
if(_list.value.length==0||ids.length==0) return {indexs:[],data:[],strs:[]}
|
||||
|
||||
function getIndex(nodes: TM_PICKER_X_ITEM[]) {
|
||||
if (val.length <= index || val.length == 0) return;
|
||||
let id = val[index]
|
||||
let sindex = 0
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let item = nodes[i]
|
||||
if (item[props.rangKey] == id) {
|
||||
sindex = i;
|
||||
indexs.push(sindex)
|
||||
data.push(nodes)
|
||||
strs.push(item[props.rangText])
|
||||
if (item?.children?.length > 0) {
|
||||
index += 1
|
||||
getIndex(item.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
getIndex(_list.value)
|
||||
return {indexs,data,strs}
|
||||
}
|
||||
//根据索引返回ids
|
||||
const getIdsByindexs = (indexs:number[]):{ids:Array<string|number>,data:TM_PICKER_X_ITEM[][],strs:string[]} => {
|
||||
let ids = [] as string[]
|
||||
let data = [] as TM_PICKER_X_ITEM[][]
|
||||
let index = 0;
|
||||
let val = indexs.slice(0)
|
||||
let strs = [] as string[]
|
||||
|
||||
if(_list.value.length==0||indexs.length==0) return {ids:[],data:[],strs:[]}
|
||||
function getIds(nodes: TM_PICKER_X_ITEM[]) {
|
||||
if (val.length <= index || val.length == 0) return;
|
||||
let currentsIndex = val[index]
|
||||
let id = ''
|
||||
let str = ''
|
||||
let children = [];
|
||||
if(nodes.length-1 >= currentsIndex){
|
||||
id = nodes[currentsIndex][props.rangKey]
|
||||
str = nodes[currentsIndex][props.rangText]
|
||||
children = nodes[currentsIndex]?.children??[]
|
||||
}else{
|
||||
id = nodes[0][props.rangKey]
|
||||
str = nodes[0][props.rangText]
|
||||
children = nodes[0]?.children??[]
|
||||
}
|
||||
ids.push(id)
|
||||
data.push(nodes)
|
||||
strs.push(str)
|
||||
|
||||
if(children.length>0){
|
||||
index += 1
|
||||
getIds(children)
|
||||
}
|
||||
}
|
||||
getIds(_list.value)
|
||||
return {ids,data,strs};
|
||||
}
|
||||
|
||||
const getNowCurrent = ()=>{
|
||||
return getIdsByindexs(_modelValueIndex.value)
|
||||
}
|
||||
// 获取相关数据。
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
oninitFun();
|
||||
uni.$on('onResize', oninitFun)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
clearTimeout(tid.value);
|
||||
uni.$off('onResize', oninitFun)
|
||||
})
|
||||
watch(() => props.modelValue, (newValue: Array<string | number>) => {
|
||||
if (newValue.join('') == nowValue.value.join('')) return;
|
||||
nowValue.value = newValue.slice(0);
|
||||
|
||||
let defauleindexs = nowValue.value.length==0?new Array(_maxDeep.value).fill(0):getIndexsByids(nowValue.value).indexs;
|
||||
_modelValueIndex.value = defauleindexs;
|
||||
|
||||
const {data,strs} = getIdsByindexs(_modelValueIndex.value)
|
||||
_nowShowList.value = data.slice(0)
|
||||
emits('update:modelStr', strs.join(","))
|
||||
|
||||
|
||||
}, { deep: true })
|
||||
|
||||
watch(() => props.list, (newValue) => {
|
||||
oninitFun()
|
||||
}, { deep: true })
|
||||
|
||||
defineExpose({getIndexsByids,getIdsByindexs,getNowCurrent,onInit:oninitFun})
|
||||
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
options: {
|
||||
styleIsolation: "apply-shared",
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
multipleSlots: true,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<view class="tmPickerView">
|
||||
<picker-item :rangKey="props.rangKey" :rangText="props.rangText"
|
||||
:font-size="props.fontSize" :cellUnits="_cellUnits" @changeDeep="change"
|
||||
:selectedIndex="_modelValueIndex" :list="_nowShowList"
|
||||
class="tmPickerViewItem"></picker-item>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped>
|
||||
.tmPickerView {
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,129 @@
|
||||
type TM_PICKER_X_ITEM = Record<string, any>
|
||||
/**
|
||||
* 当前树有多少个被选中了。
|
||||
*/
|
||||
export function getTreeSelectedNum(item: TM_PICKER_X_ITEM[], target: Set<string>): number {
|
||||
let inx = 0;
|
||||
function jshz(tree: TM_PICKER_X_ITEM[]) {
|
||||
for (let i = 0; i < tree.length; i++) {
|
||||
let item = tree[i]
|
||||
if (item.children.length > 0) {
|
||||
jshz(item.children)
|
||||
} else if (target.has(item.id)) {
|
||||
inx += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
jshz(item)
|
||||
return inx;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据目标节点,过滤掉父节点。
|
||||
* @param item
|
||||
* @param target
|
||||
*/
|
||||
export function filterParentNode(item: TM_PICKER_X_ITEM[], target: Set<string>): string[] {
|
||||
function jshz(tree: TM_PICKER_X_ITEM[]): string[] {
|
||||
let arr = [] as string[]
|
||||
for (let i = 0; i < tree.length; i++) {
|
||||
let item = tree[i]
|
||||
if (item.children.length > 0) {
|
||||
arr = arr.concat(jshz(item.children))
|
||||
} else if (target.has(item.id)) {
|
||||
arr.push(item.id)
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
return jshz(item)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定的长度值,赋值默认值
|
||||
*/
|
||||
export function setDefaultByValueAr(list: TM_PICKER_X_ITEM[], value: string[], maxLen: number): string[] {
|
||||
let v = value;
|
||||
let pr = [] as string[];
|
||||
for (let j = 0; j < maxLen; j++) {
|
||||
pr.push("")
|
||||
}
|
||||
v = v.concat(pr).slice(0, maxLen)
|
||||
let defaultAr = [] as string[];
|
||||
function setvalue(item: TM_PICKER_X_ITEM[]) {
|
||||
if (item.length > 0) {
|
||||
defaultAr.push(item[0].id);
|
||||
if (item[0].children.length > 0) {
|
||||
setvalue(item[0].children)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setvalue(list);
|
||||
|
||||
for (let i = 0; i < maxLen; i++) {
|
||||
if (defaultAr.length - 1 >= i && v[i] == "") {
|
||||
v[i] = defaultAr[i]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 通过 ID 查找节点的函数
|
||||
export function findNodeById(TreeNode: TM_PICKER_X_ITEM[], id: string): TM_PICKER_X_ITEM | null {
|
||||
let newnode = null as TM_PICKER_X_ITEM | null
|
||||
for (let i = 0; i < TreeNode.length; i++) {
|
||||
let node = TreeNode[i]
|
||||
if (node.id === id) {
|
||||
newnode = node;
|
||||
break;
|
||||
}
|
||||
if (node.children.length > 0) {
|
||||
const foundNode = findNodeById(node.children, id);
|
||||
if (foundNode != null) {
|
||||
newnode = foundNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
|
||||
// 查找节点的上级节点
|
||||
export function findParentNode(nodes: TM_PICKER_X_ITEM[], targetNodeId: string): TM_PICKER_X_ITEM | null {
|
||||
function search(node: TM_PICKER_X_ITEM): TM_PICKER_X_ITEM | null {
|
||||
if (node.children.length > 0) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
const child = node.children[i];
|
||||
if (child.id === targetNodeId) {
|
||||
return node;
|
||||
} else {
|
||||
const foundParent = search(child);
|
||||
if (foundParent != null) {
|
||||
return foundParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用for循环遍历根节点数组
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const root = nodes[i];
|
||||
const parentNode = search(root);
|
||||
if (parentNode != null) {
|
||||
return parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
return null; // 如果未找到目标节点的父节点,则返回undefined
|
||||
}
|
||||
Reference in new issue
Block a user