|
@@ -1,203 +1,220 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <view class="w-full" @click="handleClick">
|
|
|
|
|
- <view class="flex items-center gap-x-6 justify-start" :style="customStyle">
|
|
|
|
|
- <view v-if="matchValue || customLabel" class="text-[15px] h-[24px] leading-[26px] text-fore-title flex-1 min-w-1 ellipsis-1" :style="getValueStyle"
|
|
|
|
|
- :class="{ 'text-[#dce4f6]': disabled || readonly }">
|
|
|
|
|
- <slot :label="label">
|
|
|
|
|
- {{ label }}
|
|
|
|
|
- </slot>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view v-else class="text-[15px] h-[24px] leading-[26px] text-[#c0c4cc] flex-1 min-w-1 ellipsis-1" :style="getPlaceholderStyle">{{ placeholder }}</view>
|
|
|
|
|
- <slot name="right">
|
|
|
|
|
- <view v-if="!readonly && showArrow" class="transition-all duration-300">
|
|
|
|
|
- <uv-icon :name="icon" size="15" color="#B3B3B3" />
|
|
|
|
|
|
|
+ <view :class="width" @click="handleClick">
|
|
|
|
|
+ <view class="flex items-center gap-x-6 justify-start" :style="customStyle">
|
|
|
|
|
+ <view v-if="matchValue || customLabel"
|
|
|
|
|
+ class="text-[15px] h-[24px] leading-[26px] flex-1 min-w-1 ellipsis-1"
|
|
|
|
|
+ :style="getValueStyle"
|
|
|
|
|
+ :class="[(disabled || readonly) ? 'text-[#dce4f6]': color]">
|
|
|
|
|
+ <slot :label="label">
|
|
|
|
|
+ {{ label }}
|
|
|
|
|
+ </slot>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view v-else class="text-[15px] h-[24px] leading-[26px] text-[#c0c4cc] flex-1 min-w-1 ellipsis-1"
|
|
|
|
|
+ :style="getPlaceholderStyle">{{ placeholder }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <slot name="right">
|
|
|
|
|
+ <view v-if="!readonly && showArrow" class="transition-all duration-300">
|
|
|
|
|
+ <uv-icon :name="icon" size="15" :color="iconColor"/>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </slot>
|
|
|
</view>
|
|
</view>
|
|
|
- </slot>
|
|
|
|
|
</view>
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
- <!-- #ifdef H5 -->
|
|
|
|
|
- <teleport to="body">
|
|
|
|
|
- <!-- #endif -->
|
|
|
|
|
- <!-- #ifdef MP-WEIXIN -->
|
|
|
|
|
- <root-portal externalClass="theme-ie">
|
|
|
|
|
- <!-- #endif -->
|
|
|
|
|
- <uv-picker ref="pickerRef" :showToolbar="false" :columns="columns" :defaultIndex="defaultIndex" :round="16"
|
|
|
|
|
- activeColor="#31A0FC" :keyName="keyLabel" :title="title" @change="handleChange" @close="onClose">
|
|
|
|
|
- <template #toolbar>
|
|
|
|
|
- <view class="theme-ie">
|
|
|
|
|
- <view class="flex items-center justify-between pt-20">
|
|
|
|
|
- <view class="px-46 py-20 text-28 text-fore-light" @click="handleCancel">取消</view>
|
|
|
|
|
- <text class="text-30 text-fore-title font-bold">{{ title }}</text>
|
|
|
|
|
- <view class="px-46 py-20 text-28 text-fore-title" @click="handleConfirm">确认</view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </template>
|
|
|
|
|
- </uv-picker>
|
|
|
|
|
- <!-- #ifdef MP-WEIXIN -->
|
|
|
|
|
- </root-portal>
|
|
|
|
|
- <!-- #endif -->
|
|
|
|
|
<!-- #ifdef H5 -->
|
|
<!-- #ifdef H5 -->
|
|
|
- </teleport>
|
|
|
|
|
- <!-- #endif -->
|
|
|
|
|
|
|
+ <teleport to="body">
|
|
|
|
|
+ <!-- #endif -->
|
|
|
|
|
+ <!-- #ifdef MP-WEIXIN -->
|
|
|
|
|
+ <root-portal externalClass="theme-ie">
|
|
|
|
|
+ <!-- #endif -->
|
|
|
|
|
+ <uv-picker ref="pickerRef" :showToolbar="false" :columns="columns" :defaultIndex="defaultIndex" :round="16"
|
|
|
|
|
+ activeColor="#31A0FC" :keyName="keyLabel" :title="title" @change="handleChange" @close="onClose">
|
|
|
|
|
+ <template #toolbar>
|
|
|
|
|
+ <view class="theme-ie">
|
|
|
|
|
+ <view class="flex items-center justify-between pt-20">
|
|
|
|
|
+ <view class="px-46 py-20 text-28 text-fore-light" @click="handleCancel">取消</view>
|
|
|
|
|
+ <text class="text-30 text-fore-title font-bold">{{ title }}</text>
|
|
|
|
|
+ <view class="px-46 py-20 text-28 text-fore-title" @click="handleConfirm">确认</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </uv-picker>
|
|
|
|
|
+ <!-- #ifdef MP-WEIXIN -->
|
|
|
|
|
+ </root-portal>
|
|
|
|
|
+ <!-- #endif -->
|
|
|
|
|
+ <!-- #ifdef H5 -->
|
|
|
|
|
+ </teleport>
|
|
|
|
|
+ <!-- #endif -->
|
|
|
</template>
|
|
</template>
|
|
|
<script lang="ts" setup>
|
|
<script lang="ts" setup>
|
|
|
defineOptions({
|
|
defineOptions({
|
|
|
- options: {
|
|
|
|
|
- virtualHost: true
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ options: {
|
|
|
|
|
+ virtualHost: true
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
const modelValue = defineModel<string | number>('modelValue', {
|
|
const modelValue = defineModel<string | number>('modelValue', {
|
|
|
- default: ''
|
|
|
|
|
|
|
+ default: ''
|
|
|
});
|
|
});
|
|
|
const props = defineProps({
|
|
const props = defineProps({
|
|
|
- placeholder: {
|
|
|
|
|
- type: String,
|
|
|
|
|
- default: '请选择',
|
|
|
|
|
- },
|
|
|
|
|
- customLabel: {
|
|
|
|
|
- type: String,
|
|
|
|
|
- default: '',
|
|
|
|
|
- },
|
|
|
|
|
- title: {
|
|
|
|
|
- type: String,
|
|
|
|
|
- default: '',
|
|
|
|
|
- },
|
|
|
|
|
- icon: {
|
|
|
|
|
- type: String,
|
|
|
|
|
- default: 'arrow-right',
|
|
|
|
|
- },
|
|
|
|
|
- list: {
|
|
|
|
|
- type: Array as PropType<any[]>,
|
|
|
|
|
- default: () => [],
|
|
|
|
|
- },
|
|
|
|
|
- keyLabel: {
|
|
|
|
|
- type: String,
|
|
|
|
|
- default: 'label',
|
|
|
|
|
- },
|
|
|
|
|
- keyValue: {
|
|
|
|
|
- type: String,
|
|
|
|
|
- default: 'value',
|
|
|
|
|
- },
|
|
|
|
|
- disabled: {
|
|
|
|
|
- type: Boolean,
|
|
|
|
|
- default: false,
|
|
|
|
|
- },
|
|
|
|
|
- readonly: {
|
|
|
|
|
- type: Boolean,
|
|
|
|
|
- default: false,
|
|
|
|
|
- },
|
|
|
|
|
- customStyle: {
|
|
|
|
|
- type: Object,
|
|
|
|
|
- default: () => ({}),
|
|
|
|
|
- },
|
|
|
|
|
- placeholderStyle: {
|
|
|
|
|
- type: Object,
|
|
|
|
|
- default: () => ({}),
|
|
|
|
|
- },
|
|
|
|
|
- fontSize: {
|
|
|
|
|
- type: Number,
|
|
|
|
|
- default: 15,
|
|
|
|
|
- },
|
|
|
|
|
- showArrow: {
|
|
|
|
|
- type: Boolean,
|
|
|
|
|
- default: true,
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ placeholder: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: '请选择',
|
|
|
|
|
+ },
|
|
|
|
|
+ customLabel: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: '',
|
|
|
|
|
+ },
|
|
|
|
|
+ title: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: '',
|
|
|
|
|
+ },
|
|
|
|
|
+ icon: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: 'arrow-right',
|
|
|
|
|
+ },
|
|
|
|
|
+ iconColor: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: '#B3B3B3'
|
|
|
|
|
+ },
|
|
|
|
|
+ list: {
|
|
|
|
|
+ type: Array as PropType<any[]>,
|
|
|
|
|
+ default: () => [],
|
|
|
|
|
+ },
|
|
|
|
|
+ keyLabel: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: 'label',
|
|
|
|
|
+ },
|
|
|
|
|
+ keyValue: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: 'value',
|
|
|
|
|
+ },
|
|
|
|
|
+ disabled: {
|
|
|
|
|
+ type: Boolean,
|
|
|
|
|
+ default: false,
|
|
|
|
|
+ },
|
|
|
|
|
+ readonly: {
|
|
|
|
|
+ type: Boolean,
|
|
|
|
|
+ default: false,
|
|
|
|
|
+ },
|
|
|
|
|
+ customStyle: {
|
|
|
|
|
+ type: Object,
|
|
|
|
|
+ default: () => ({}),
|
|
|
|
|
+ },
|
|
|
|
|
+ width: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: 'w-full'
|
|
|
|
|
+ },
|
|
|
|
|
+ color: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: 'text-fore-title'
|
|
|
|
|
+ },
|
|
|
|
|
+ placeholderStyle: {
|
|
|
|
|
+ type: Object,
|
|
|
|
|
+ default: () => ({}),
|
|
|
|
|
+ },
|
|
|
|
|
+ fontSize: {
|
|
|
|
|
+ type: Number,
|
|
|
|
|
+ default: 15,
|
|
|
|
|
+ },
|
|
|
|
|
+ showArrow: {
|
|
|
|
|
+ type: Boolean,
|
|
|
|
|
+ default: true,
|
|
|
|
|
+ },
|
|
|
});
|
|
});
|
|
|
const defaultIndex = ref([0]);
|
|
const defaultIndex = ref([0]);
|
|
|
const label = ref('');
|
|
const label = ref('');
|
|
|
const isOpen = ref(false);
|
|
const isOpen = ref(false);
|
|
|
const matchValue = ref(false);
|
|
const matchValue = ref(false);
|
|
|
const columns = computed(() => {
|
|
const columns = computed(() => {
|
|
|
- return [props.list];
|
|
|
|
|
|
|
+ return [props.list];
|
|
|
});
|
|
});
|
|
|
const getPlaceholderStyle = computed(() => {
|
|
const getPlaceholderStyle = computed(() => {
|
|
|
- return {
|
|
|
|
|
- ...props.placeholderStyle,
|
|
|
|
|
- fontSize: props.fontSize + 'px'
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...props.placeholderStyle,
|
|
|
|
|
+ fontSize: props.fontSize + 'px'
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
const getValueStyle = computed(() => {
|
|
const getValueStyle = computed(() => {
|
|
|
- return {
|
|
|
|
|
- fontSize: props.fontSize + 'px'
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return {
|
|
|
|
|
+ fontSize: props.fontSize + 'px'
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
const init = () => {
|
|
const init = () => {
|
|
|
- if (props.customLabel) {
|
|
|
|
|
- label.value = props.customLabel;
|
|
|
|
|
- matchValue.value = true;
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (props.customLabel) {
|
|
|
|
|
+ label.value = props.customLabel;
|
|
|
|
|
+ matchValue.value = true;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (modelValue.value !== null && modelValue.value !== undefined && modelValue.value !== '') {
|
|
|
|
|
- const index = props.list.findIndex(item => item[props.keyValue] == modelValue.value);
|
|
|
|
|
- if (index !== -1) {
|
|
|
|
|
- defaultIndex.value = [index];
|
|
|
|
|
- label.value = props.list[index][props.keyLabel];
|
|
|
|
|
- matchValue.value = true;
|
|
|
|
|
|
|
+ if (modelValue.value !== null && modelValue.value !== undefined && modelValue.value !== '') {
|
|
|
|
|
+ const index = props.list.findIndex(item => item[props.keyValue] == modelValue.value);
|
|
|
|
|
+ if (index !== -1) {
|
|
|
|
|
+ defaultIndex.value = [index];
|
|
|
|
|
+ label.value = props.list[index][props.keyLabel];
|
|
|
|
|
+ matchValue.value = true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果找不到匹配项,重置状态
|
|
|
|
|
+ defaultIndex.value = [0];
|
|
|
|
|
+ label.value = modelValue.value + '';
|
|
|
|
|
+ matchValue.value = true;
|
|
|
|
|
+ }
|
|
|
} else {
|
|
} else {
|
|
|
- // 如果找不到匹配项,重置状态
|
|
|
|
|
- defaultIndex.value = [0];
|
|
|
|
|
- label.value = modelValue.value + '';
|
|
|
|
|
- matchValue.value = true;
|
|
|
|
|
|
|
+ defaultIndex.value = [0];
|
|
|
|
|
+ label.value = props.placeholder;
|
|
|
|
|
+ matchValue.value = false;
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- defaultIndex.value = [0];
|
|
|
|
|
- label.value = props.placeholder;
|
|
|
|
|
- matchValue.value = false;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 使用 watch 替代 watchEffect,避免循环更新
|
|
// 使用 watch 替代 watchEffect,避免循环更新
|
|
|
-watch([() => modelValue.value, () => props.list], init, { immediate: true });
|
|
|
|
|
|
|
+watch([() => modelValue.value, () => props.list], init, {immediate: true});
|
|
|
|
|
|
|
|
const pickerRef = ref();
|
|
const pickerRef = ref();
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
const emit = defineEmits<{
|
|
|
- (e: 'change', value: number, selectedItem: any): void;
|
|
|
|
|
- (e: 'click'): void;
|
|
|
|
|
|
|
+ (e: 'change', value: number, selectedItem: any): void;
|
|
|
|
|
+ (e: 'click'): void;
|
|
|
}>();
|
|
}>();
|
|
|
|
|
|
|
|
const handleConfirm = () => {
|
|
const handleConfirm = () => {
|
|
|
- const { value } = pickerRef.value.manualConfirm();
|
|
|
|
|
- const oldValue = modelValue.value;
|
|
|
|
|
- const newValue = value[0][props.keyValue];
|
|
|
|
|
|
|
+ const {value} = pickerRef.value.manualConfirm();
|
|
|
|
|
+ const oldValue = modelValue.value;
|
|
|
|
|
+ const newValue = value[0][props.keyValue];
|
|
|
|
|
|
|
|
- // 更新 modelValue
|
|
|
|
|
- modelValue.value = newValue;
|
|
|
|
|
|
|
+ // 更新 modelValue
|
|
|
|
|
+ modelValue.value = newValue;
|
|
|
|
|
|
|
|
- // 手动更新显示状态,确保界面同步
|
|
|
|
|
- const selectedItem = value[0];
|
|
|
|
|
- label.value = selectedItem[props.keyLabel];
|
|
|
|
|
- matchValue.value = true;
|
|
|
|
|
|
|
+ // 手动更新显示状态,确保界面同步
|
|
|
|
|
+ const selectedItem = value[0];
|
|
|
|
|
+ label.value = selectedItem[props.keyLabel];
|
|
|
|
|
+ matchValue.value = true;
|
|
|
|
|
|
|
|
- // 发出 change 事件
|
|
|
|
|
- if (oldValue !== newValue) {
|
|
|
|
|
- emit('change', newValue, selectedItem);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 发出 change 事件
|
|
|
|
|
+ if (oldValue !== newValue) {
|
|
|
|
|
+ emit('change', newValue, selectedItem);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- pickerRef.value.close();
|
|
|
|
|
|
|
+ pickerRef.value.close();
|
|
|
}
|
|
}
|
|
|
const handleCancel = () => {
|
|
const handleCancel = () => {
|
|
|
- pickerRef.value.close();
|
|
|
|
|
|
|
+ pickerRef.value.close();
|
|
|
|
|
+}
|
|
|
|
|
+const handleChange = () => {
|
|
|
}
|
|
}
|
|
|
-const handleChange = () => { }
|
|
|
|
|
|
|
|
|
|
const handleClick = () => {
|
|
const handleClick = () => {
|
|
|
- emit('click');
|
|
|
|
|
- if (props.disabled || props.readonly) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (!props.list.length) {
|
|
|
|
|
- uni.$ie.showToast('暂无数据');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- init();
|
|
|
|
|
- isOpen.value = true;
|
|
|
|
|
- pickerRef.value.open();
|
|
|
|
|
|
|
+ emit('click');
|
|
|
|
|
+ if (props.disabled || props.readonly) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!props.list.length) {
|
|
|
|
|
+ uni.$ie.showToast('暂无数据');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ init();
|
|
|
|
|
+ isOpen.value = true;
|
|
|
|
|
+ pickerRef.value.open();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const onClose = () => {
|
|
const onClose = () => {
|
|
|
- isOpen.value = false;
|
|
|
|
|
|
|
+ isOpen.value = false;
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
<style lang="scss" scoped></style>
|
|
<style lang="scss" scoped></style>
|