refactor(@vben/web-antdv-next): migrate SelectOption usages to options and optionRender

Replace remaining SelectOption children with Select options across form-create, BPM, IoT, MES, WMS, Mall, and infra pages.

Use optionRender for custom option content and update the sync skill with Select slot migration guidance.
pull/364/head
XuZhiqiang 2026-06-18 23:17:32 +08:00
parent 456a91dfc2
commit c20eb8e1f4
19 changed files with 304 additions and 255 deletions

View File

@ -6,14 +6,7 @@ import { computed, useAttrs } from 'vue';
import { getDictOptions } from '@vben/hooks';
import {
Checkbox,
CheckboxGroup,
Radio,
RadioGroup,
Select,
SelectOption,
} from 'antdv-next';
import { Checkbox, CheckboxGroup, Radio, RadioGroup, Select } from 'antdv-next';
defineOptions({ name: 'DictSelect' });
@ -41,18 +34,22 @@ const getDictOption = computed(() => {
}
}
});
const selectOptions = computed(() =>
getDictOption.value.map((dict) => ({
label: dict.label,
value: dict.value as any,
})),
);
</script>
<template>
<Select v-if="selectType === 'select'" class="w-full" v-bind="attrs">
<SelectOption
v-for="(dict, index) in getDictOption"
:key="index"
:value="dict.value"
>
{{ dict.label }}
</SelectOption>
</Select>
<Select
v-if="selectType === 'select'"
class="w-full"
v-bind="attrs"
:options="selectOptions"
/>
<RadioGroup v-if="selectType === 'radio'" class="w-full" v-bind="attrs">
<Radio
v-for="(dict, index) in getDictOption"

View File

@ -5,14 +5,7 @@ import { defineComponent, onMounted, ref, useAttrs } from 'vue';
import { useUserStore } from '@vben/stores';
import { isEmpty } from '@vben/utils';
import {
Checkbox,
CheckboxGroup,
Radio,
RadioGroup,
Select,
SelectOption,
} from 'antdv-next';
import { Checkbox, CheckboxGroup, Radio, RadioGroup, Select } from 'antdv-next';
import { requestClient } from '#/api/request';
@ -256,18 +249,11 @@ export function useApiSelect(option: ApiSelectProps) {
onUpdate:value={onUpdateModelValue as any}
value={modelValue as any}
{...restAttrs}
options={options.value}
// TODO @xingyu remote 对等实现, 还是说没作用
// remote={props.remote}
{...(props.remote && { remoteMethod })}
>
{options.value.map(
(item: { label: any; value: any }, index: any) => (
<SelectOption key={index} value={item.value}>
{item.label}
</SelectOption>
),
)}
</Select>
/>
);
}
return (
@ -277,18 +263,11 @@ export function useApiSelect(option: ApiSelectProps) {
onUpdate:value={onUpdateModelValue as any}
value={modelValue as any}
{...restAttrs}
options={options.value}
// TODO: @xingyu remote 对等实现, 还是说没作用
// remote={props.remote}
{...(props.remote && { remoteMethod })}
>
{options.value.map(
(item: { label: any; value: any }, index: any) => (
<SelectOption key={index} value={item.value}>
{item.label}
</SelectOption>
),
)}
</Select>
/>
);
};
const buildCheckbox = () => {

View File

@ -17,7 +17,6 @@ import {
RadioGroup,
Row,
Select,
SelectOption,
Space,
Switch,
TextArea,
@ -69,6 +68,14 @@ const conditionConfigTypes = computed(() => {
/** 条件规则可选择的表单字段 */
const fieldOptions = useFormFieldsAndStartUser();
const fieldSelectOptions = computed(() =>
fieldOptions.map((field) => ({
label: field.title,
value: field.field,
disabled: !field.required,
raw: field,
})),
);
//
const formRules: Record<string, Rule[]> = reactive({
@ -172,10 +179,12 @@ defineExpose({ validate });
/>
</div>
</div>
<Space orientation="vertical" size="small" class="w-11/12 pl-1">
<template #split>
{{ condition.conditionGroups.and ? '且' : '或' }}
</template>
<Space
orientation="vertical"
size="small"
class="w-11/12 pl-1"
:separator="condition.conditionGroups.and ? '且' : '或'"
>
<Card
class="group relative w-full hover:border-blue-500"
v-for="(equation, cIdx) in condition.conditionGroups.conditions"
@ -190,7 +199,10 @@ defineExpose({ validate });
icon="lucide:circle-x"
class="size-4"
@click="
deleteConditionGroup(condition.conditionGroups.conditions, cIdx)
deleteConditionGroup(
condition.conditionGroups.conditions,
Number(cIdx),
)
"
/>
</div>
@ -236,23 +248,18 @@ defineExpose({ validate });
v-model:value="rule.leftSide"
allow-clear
placeholder="请选择表单字段"
:options="fieldSelectOptions"
>
<SelectOption
v-for="(field, fIdx) in fieldOptions"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="!field.required"
>
<template #optionRender="{ option }">
<Tooltip
title="表单字段非必填时不能作为流程分支条件"
placement="right"
v-if="!field.required"
v-if="!option.data.raw.required"
>
<span>{{ field.title }}</span>
<span>{{ option.data.raw.title }}</span>
</Tooltip>
<template v-else>{{ field.title }}</template>
</SelectOption>
<template v-else>{{ option.data.raw.title }}</template>
</template>
</Select>
</FormItem>
</Col>
@ -292,11 +299,11 @@ defineExpose({ validate });
<Trash2
v-if="equation.rules.length > 1"
class="mr-2 size-4 cursor-pointer text-red-500"
@click="deleteConditionRule(equation, rIdx)"
@click="deleteConditionRule(equation, Number(rIdx))"
/>
<Plus
class="size-4 cursor-pointer text-blue-500"
@click="addConditionRule(equation, rIdx)"
@click="addConditionRule(equation, Number(rIdx))"
/>
</div>
</Col>

View File

@ -15,7 +15,6 @@ import {
Radio,
RadioGroup,
Select,
SelectOption,
Tooltip,
} from 'antdv-next';
@ -107,16 +106,12 @@ defineExpose({ validate });
name="formId"
class="mb-5"
>
<Select v-model:value="modelData.formId" allow-clear>
<SelectOption
v-for="form in props.formList"
:key="form.id"
:value="form.id"
>
{{ form.name }}
</SelectOption>
>
</Select>
<Select
v-model:value="modelData.formId"
allow-clear
:options="props.formList"
:field-names="{ label: 'name', value: 'id' }"
/>
</FormItem>
<FormItem
v-if="modelData.formType === BpmModelFormType.CUSTOM"

View File

@ -19,7 +19,6 @@ import {
Input,
message,
Select,
SelectOption,
Tag,
TextArea,
} from 'antdv-next';
@ -101,7 +100,15 @@ watchEffect(() => {
/** 发送消息 */
const sendText = ref(''); //
const sendUserId = ref('all'); //
const sendUserId = ref<'all' | number>('all'); //
const sendUserOptions = computed(() => [
{ label: '所有人', value: 'all', raw: null },
...userList.value.map((user) => ({
label: user.nickname,
value: user.id,
raw: user,
})),
]);
function handlerSend() {
if (!sendText.value.trim()) {
message.warning('消息内容不能为空');
@ -229,26 +236,20 @@ onMounted(async () => {
size="large"
placeholder="请选择接收人"
:disabled="!getIsOpen"
:options="sendUserOptions"
>
<SelectOption key="" value="" label="所有人">
<div class="flex items-center">
<template #optionRender="{ option }">
<div class="flex items-center" v-if="!option.data.raw">
<Avatar size="small" class="mr-2"></Avatar>
<span>所有人</span>
</div>
</SelectOption>
<SelectOption
v-for="user in userList"
:key="user.id"
:value="user.id"
:label="user.nickname"
>
<div class="flex items-center">
<div class="flex items-center" v-else>
<Avatar size="small" class="mr-2">
{{ user.nickname.slice(0, 1) }}
{{ option.data.raw.nickname?.slice(0, 1) }}
</Avatar>
<span>{{ user.nickname }}</span>
<span>{{ option.data.raw.nickname }}</span>
</div>
</SelectOption>
</template>
</Select>
<TextArea

View File

@ -1,7 +1,5 @@
<!-- 当前时间条件配置组件 -->
<script setup lang="ts">
import type { Dayjs } from 'dayjs';
import type { RuleSceneApi } from '#/api/iot/rule/scene';
import { computed, watch } from 'vue';
@ -17,7 +15,6 @@ import {
FormItem,
Row,
Select,
SelectOption,
Tag,
TimePicker,
} from 'antdv-next';
@ -130,7 +127,7 @@ function updateConditionField(field: any, value: any) {
* 处理第一个时间值变化
* @param value 时间值
*/
function handleTimeValueChange(value: Dayjs | null | string) {
function handleTimeValueChange(value: any) {
const normalized = formatDayjs(value);
const currentParams = condition.value.param
? condition.value.param.split(',')
@ -147,7 +144,7 @@ function handleTimeValueChange(value: Dayjs | null | string) {
* 处理第二个时间值变化
* @param value 时间值
*/
function handleTimeValue2Change(value: Dayjs | null | string) {
function handleTimeValue2Change(value: any) {
const normalized = formatDayjs(value);
const currentParams = condition.value.param
? condition.value.param.split(',')
@ -187,23 +184,22 @@ watch(
"
placeholder="请选择时间条件"
class="w-full"
:options="timeOperatorOptions"
>
<SelectOption
v-for="option in timeOperatorOptions"
:key="option.value"
:label="option.label"
:value="option.value"
>
<template #optionRender="{ option }">
<div class="flex w-full items-center justify-between">
<div class="flex items-center gap-2">
<IconifyIcon :icon="option.icon" :class="option.iconClass" />
<span>{{ option.label }}</span>
<IconifyIcon
:icon="option.data.icon"
:class="option.data.iconClass"
/>
<span>{{ option.data.label }}</span>
</div>
<Tag :color="option.tag">
{{ option.category }}
<Tag :color="option.data.tag">
{{ option.data.category }}
</Tag>
</div>
</SelectOption>
</template>
</Select>
</FormItem>
</Col>

View File

@ -13,7 +13,7 @@ import {
import { isObject } from '@vben/utils';
import { useVModel } from '@vueuse/core';
import { Col, FormItem, Row, Select, SelectOption, Tag } from 'antdv-next';
import { Col, FormItem, Row, Select, Tag } from 'antdv-next';
import { getThingModelTSLByProductId } from '#/api/iot/thingmodel';
@ -39,6 +39,13 @@ const loadingThingModel = ref(false); // 物模型加载状态
const selectedService = ref<null | ThingModelApi.Service>(null); //
const serviceList = ref<ThingModelApi.Service[]>([]); //
const loadingServices = ref(false); //
const serviceOptions = computed(() =>
serviceList.value.map((service) => ({
label: service.name,
value: service.identifier,
raw: service,
})),
);
//
const paramsValue = computed({
@ -346,23 +353,22 @@ watch(
allow-clear
class="w-full"
:loading="loadingServices"
:options="serviceOptions"
option-filter-prop="label"
@change="handleServiceChange"
>
<SelectOption
v-for="service in serviceList"
:key="service.identifier"
:label="service.name"
:value="service.identifier"
>
<template #optionRender="{ option }">
<div class="flex items-center justify-between">
<span>{{ service.name }}</span>
<span>{{ option.data.raw.name }}</span>
<Tag
:color="service.callType === 'sync' ? 'processing' : 'success'"
:color="
option.data.raw.callType === 'sync' ? 'processing' : 'success'
"
>
{{ service.callType === 'sync' ? '同步' : '异步' }}
{{ option.data.raw.callType === 'sync' ? '同步' : '异步' }}
</Tag>
</div>
</SelectOption>
</template>
</Select>
</FormItem>

View File

@ -1,10 +1,10 @@
<!-- 设备选择器组件 -->
<script setup lang="ts">
import { ref, watch } from 'vue';
import { computed, ref, watch } from 'vue';
import { DEVICE_SELECTOR_OPTIONS, DICT_TYPE } from '@vben/constants';
import { Select, SelectOption } from 'antdv-next';
import { Select } from 'antdv-next';
import { getDeviceListByProductId } from '#/api/iot/device/device';
import { DictTag } from '#/components/dict-tag';
@ -24,6 +24,13 @@ const emit = defineEmits<{
const deviceLoading = ref(false); //
const deviceList = ref<any[]>([]); //
const deviceOptions = computed(() =>
deviceList.value.map((device) => ({
label: device.deviceName,
value: device.id,
raw: device,
})),
);
/** 处理选择变化事件 */
function handleChange(value: any) {
@ -90,28 +97,28 @@ watch(
allow-clear
class="w-full"
option-label-prop="label"
option-filter-prop="label"
:loading="deviceLoading"
:disabled="!productId"
:options="deviceOptions"
>
<SelectOption
v-for="device in deviceList"
:key="device.id"
:label="device.deviceName"
:value="device.id"
>
<template #optionRender="{ option }">
<div class="py-[4px] flex w-full items-center justify-between">
<div class="flex-1">
<div class="text-[14px] font-medium mb-[2px] text-foreground">
{{ device.deviceName }}
{{ option.data.raw.deviceName }}
</div>
<div class="text-[12px] text-muted-foreground">
{{ device.deviceKey }}
{{ option.data.raw.deviceKey }}
</div>
</div>
<div class="gap-[4px] flex items-center" v-if="device.id > 0">
<DictTag :type="DICT_TYPE.IOT_DEVICE_STATE" :value="device.state" />
<div class="gap-[4px] flex items-center" v-if="option.data.raw.id > 0">
<DictTag
:type="DICT_TYPE.IOT_DEVICE_STATE"
:value="option.data.raw.state"
/>
</div>
</div>
</SelectOption>
</template>
</Select>
</template>

View File

@ -8,7 +8,7 @@ import {
} from '@vben/constants';
import { useVModel } from '@vueuse/core';
import { Select, SelectOption } from 'antdv-next';
import { Select } from 'antdv-next';
/** 操作符选择器组件 */
defineOptions({ name: 'OperatorSelector' });
@ -204,6 +204,13 @@ const availableOperators = computed(() => {
(op.supportedTypes as any[]).includes(props.propertyType || ''),
);
});
const availableOperatorOptions = computed(() =>
availableOperators.value.map((operator) => ({
label: operator.label,
value: operator.value,
raw: operator,
})),
);
//
const selectedOperator = computed(() => {
@ -244,29 +251,25 @@ watch(
@change="handleChange"
class="w-full"
option-label-prop="label"
:options="availableOperatorOptions"
>
<SelectOption
v-for="operator in availableOperators"
:key="operator.value"
:label="operator.label"
:value="operator.value"
>
<template #optionRender="{ option }">
<div class="py-[4px] flex w-full items-center justify-between">
<div class="gap-[8px] flex items-center">
<div class="text-[14px] font-medium text-foreground">
{{ operator.label }}
{{ option.data.raw.label }}
</div>
<div
class="text-[12px] px-[6px] py-[2px] rounded-[4px] bg-primary-light-9 font-mono text-primary"
>
{{ operator.symbol }}
{{ option.data.raw.symbol }}
</div>
</div>
<div class="text-[12px] text-muted-foreground">
{{ operator.description }}
{{ option.data.raw.description }}
</div>
</div>
</SelectOption>
</template>
</Select>
</div>
</template>

View File

@ -1,10 +1,10 @@
<!-- 产品选择器组件 -->
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { Select, SelectOption } from 'antdv-next';
import { Select } from 'antdv-next';
import { getSimpleProductList } from '#/api/iot/product/product';
import { DictTag } from '#/components/dict-tag';
@ -23,6 +23,13 @@ const emit = defineEmits<{
const productLoading = ref(false); //
const productList = ref<any[]>([]); //
const productOptions = computed(() =>
productList.value.map((product) => ({
label: product.name,
value: product.id,
raw: product,
})),
);
/**
* 处理选择变化事件
@ -62,25 +69,25 @@ onMounted(() => {
allow-clear
class="w-full"
option-label-prop="label"
option-filter-prop="label"
:loading="productLoading"
:options="productOptions"
>
<SelectOption
v-for="product in productList"
:key="product.id"
:label="product.name"
:value="product.id"
>
<template #optionRender="{ option }">
<div class="py-[4px] flex w-full items-center justify-between">
<div class="flex-1">
<div class="text-[14px] font-medium mb-[2px] text-foreground">
{{ product.name }}
{{ option.data.raw.name }}
</div>
<div class="text-[12px] text-muted-foreground">
{{ product.productKey }}
{{ option.data.raw.productKey }}
</div>
</div>
<DictTag :type="DICT_TYPE.IOT_PRODUCT_STATUS" :value="product.status" />
<DictTag
:type="DICT_TYPE.IOT_PRODUCT_STATUS"
:value="option.data.raw.status"
/>
</div>
</SelectOption>
</template>
</Select>
</template>

View File

@ -16,14 +16,7 @@ import {
import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core';
import {
Button,
Popover,
Select,
SelectOptGroup,
SelectOption,
Tag,
} from 'antdv-next';
import { Button, Popover, Select, Tag } from 'antdv-next';
import { getThingModelTSLByProductId } from '#/api/iot/thingmodel';
@ -99,6 +92,16 @@ const propertyGroups = computed(() => {
);
return options.length > 0 ? [{ label: config.label, options }] : [];
});
const propertySelectOptions = computed(() =>
propertyGroups.value.map((group) => ({
label: group.label,
options: group.options.map((property) => ({
label: property.name,
value: property.identifier,
raw: property,
})),
})),
);
/** 当前选中的属性 */
const selectedProperty = computed(() =>
@ -224,31 +227,20 @@ watch(
@change="handleChange"
class="!w-[150px]"
option-label-prop="label"
option-filter-prop="label"
:loading="loading"
:options="propertySelectOptions"
>
<SelectOptGroup
v-for="group in propertyGroups"
:key="group.label"
:label="group.label"
>
<SelectOption
v-for="property in group.options"
:key="property.identifier"
:label="property.name"
:value="property.identifier"
>
<div class="py-[2px] flex w-full items-center justify-between">
<span
class="text-[14px] font-medium flex-1 truncate text-foreground"
>
{{ property.name }}
</span>
<Tag class="ml-[8px] flex-shrink-0">
{{ property.identifier }}
</Tag>
</div>
</SelectOption>
</SelectOptGroup>
<template #optionRender="{ option }">
<div class="py-[2px] flex w-full items-center justify-between">
<span class="text-[14px] font-medium flex-1 truncate text-foreground">
{{ option.data.raw.name }}
</span>
<Tag class="ml-[8px] flex-shrink-0">
{{ option.data.raw.identifier }}
</Tag>
</div>
</template>
</Select>
<!-- 属性详情弹出层 -->

View File

@ -11,7 +11,6 @@ import {
RadioButton,
RadioGroup,
Select,
SelectOption,
} from 'antdv-next';
import UploadImg from '#/components/upload/image-upload.vue';
@ -29,6 +28,11 @@ defineOptions({ name: 'TabBarProperty' });
const props = defineProps<{ modelValue: TabBarProperty }>();
const emit = defineEmits(['update:modelValue']);
const formData = useVModel(props, 'modelValue', emit);
const themeOptions = THEME_LIST.map((theme) => ({
label: theme.name,
value: theme.id,
raw: theme,
}));
//
component.property.items = formData.value.items;
@ -51,18 +55,20 @@ const handleThemeChange = () => {
:wrapper-col="{ span: 18 }"
>
<FormItem label="主题" name="theme">
<Select v-model:value="formData!.theme" @change="handleThemeChange">
<SelectOption
v-for="(theme, index) in THEME_LIST"
:key="index"
:label="theme.name"
:value="theme.id"
>
<Select
v-model:value="formData!.theme"
:options="themeOptions"
@change="handleThemeChange"
>
<template #optionRender="{ option }">
<div class="flex items-center justify-between">
<IconifyIcon :icon="theme.icon" :color="theme.color" />
<span>{{ theme.name }}</span>
<IconifyIcon
:icon="option.data.raw.icon"
:color="option.data.raw.color"
/>
<span>{{ option.data.raw.name }}</span>
</div>
</SelectOption>
</template>
</Select>
</FormItem>
<FormItem label="默认颜色">

View File

@ -3,7 +3,7 @@ import type { MesMdUnitMeasureApi } from '#/api/mes/md/unitmeasure';
import { computed, onMounted, ref, watch } from 'vue';
import { Select, SelectOption, Tag, Tooltip } from 'antdv-next';
import { Select, Tag, Tooltip } from 'antdv-next';
import { getUnitMeasureSimpleList } from '#/api/mes/md/unitmeasure';
@ -32,6 +32,13 @@ const emit = defineEmits<{
const allList = ref<MesMdUnitMeasureApi.UnitMeasure[]>([]); //
const filteredList = ref<MesMdUnitMeasureApi.UnitMeasure[]>([]); //
const selectedItem = ref<MesMdUnitMeasureApi.UnitMeasure>(); //
const selectOptions = computed(() =>
filteredList.value.map((item) => ({
label: item.name,
value: item.id,
raw: item,
})),
);
const selectValue = computed({
//
@ -44,7 +51,7 @@ const selectValue = computed({
/** 前端按名称和编码过滤计量单位 */
function handleFilter(input: string, option: any) {
const keyword = input.toLowerCase();
const item = option?.item as MesMdUnitMeasureApi.UnitMeasure | undefined;
const item = option?.raw as MesMdUnitMeasureApi.UnitMeasure | undefined;
return Boolean(
item?.name?.toLowerCase().includes(keyword) ||
item?.code?.toLowerCase().includes(keyword),
@ -104,19 +111,17 @@ onMounted(async () => {
:placeholder="placeholder"
class="w-full"
show-search
:options="selectOptions"
@change="handleChange"
>
<SelectOption
v-for="item in filteredList"
:key="item.id"
:item="item"
:value="item.id"
>
<template #optionRender="{ option }">
<div class="flex items-center gap-2">
<span>{{ item.name }}</span>
<Tag v-if="item.code" color="default">: {{ item.code }}</Tag>
<span>{{ option.data.raw.name }}</span>
<Tag v-if="option.data.raw.code" color="default">
编号: {{ option.data.raw.code }}
</Tag>
</div>
</SelectOption>
</template>
</Select>
</Tooltip>
</template>

View File

@ -3,7 +3,7 @@ import type { MesMdWorkshopApi } from '#/api/mes/md/workstation/workshop';
import { computed, onMounted, ref, watch } from 'vue';
import { Select, SelectOption, Tag, Tooltip } from 'antdv-next';
import { Select, Tag, Tooltip } from 'antdv-next';
import { getWorkshopSimpleList } from '#/api/mes/md/workstation/workshop';
@ -31,6 +31,13 @@ const emit = defineEmits<{
const allList = ref<MesMdWorkshopApi.Workshop[]>([]);
const selectedItem = ref<MesMdWorkshopApi.Workshop>();
const selectOptions = computed(() =>
allList.value.map((item) => ({
label: item.name,
value: item.id,
raw: item,
})),
);
const selectValue = computed({
get: () => props.modelValue,
@ -41,7 +48,7 @@ const selectValue = computed({
function handleFilter(input: string, option: any) {
const keyword = input.toLowerCase();
const item = option?.item as MesMdWorkshopApi.Workshop | undefined;
const item = option?.raw as MesMdWorkshopApi.Workshop | undefined;
return Boolean(
item?.name?.toLowerCase().includes(keyword) ||
item?.code?.toLowerCase().includes(keyword),
@ -99,19 +106,17 @@ onMounted(async () => {
:placeholder="placeholder"
class="w-full"
show-search
:options="selectOptions"
@change="handleChange"
>
<SelectOption
v-for="item in allList"
:key="item.id"
:item="item"
:value="item.id"
>
<template #optionRender="{ option }">
<div class="flex items-center gap-2">
<span>{{ item.name }}</span>
<Tag v-if="item.code" color="default">{{ item.code }}</Tag>
<span>{{ option.data.raw.name }}</span>
<Tag v-if="option.data.raw.code" color="default">
{{ option.data.raw.code }}
</Tag>
</div>
</SelectOption>
</template>
</Select>
</Tooltip>
</template>

View File

@ -5,7 +5,7 @@ import { computed, onMounted, ref } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { Select, SelectOption } from 'antdv-next';
import { Select } from 'antdv-next';
import { getAndonConfigList } from '#/api/mes/pro/andon/config';
import DictTag from '#/components/dict-tag/dict-tag.vue';
@ -34,6 +34,13 @@ const emit = defineEmits<{
}>();
const allList = ref<MesProAndonConfigApi.AndonConfig[]>([]);
const selectOptions = computed(() =>
allList.value.map((item) => ({
label: item.reason,
value: item.id,
raw: item,
})),
);
const selectValue = computed({
get: () => props.modelValue,
@ -45,7 +52,7 @@ const selectValue = computed({
/** 前端过滤:按 reason 模糊匹配 */
function handleFilter(input: string, option: any) {
const keyword = input.toLowerCase();
const item = option?.item as MesProAndonConfigApi.AndonConfig | undefined;
const item = option?.raw as MesProAndonConfigApi.AndonConfig | undefined;
return Boolean(item?.reason?.toLowerCase().includes(keyword));
}
@ -71,18 +78,17 @@ onMounted(async () => {
:placeholder="placeholder"
class="w-full"
show-search
:options="selectOptions"
@change="handleChange"
>
<SelectOption
v-for="item in allList"
:key="item.id"
:item="item"
:value="item.id"
>
<template #optionRender="{ option }">
<div class="flex items-center gap-2">
<span>{{ item.reason }}</span>
<DictTag :type="DICT_TYPE.MES_PRO_ANDON_LEVEL" :value="item.level" />
<span>{{ option.data.raw.reason }}</span>
<DictTag
:type="DICT_TYPE.MES_PRO_ANDON_LEVEL"
:value="option.data.raw.level"
/>
</div>
</SelectOption>
</template>
</Select>
</template>

View File

@ -3,7 +3,7 @@ import type { MesProProcessApi } from '#/api/mes/pro/process';
import { computed, onMounted, ref, watch } from 'vue';
import { Select, SelectOption, Tag, Tooltip } from 'antdv-next';
import { Select, Tag, Tooltip } from 'antdv-next';
import { getProcessSimpleList } from '#/api/mes/pro/process';
@ -31,6 +31,13 @@ const emit = defineEmits<{
const allList = ref<MesProProcessApi.Process[]>([]);
const selectedItem = ref<MesProProcessApi.Process>();
const selectOptions = computed(() =>
allList.value.map((item) => ({
label: item.name,
value: item.id,
raw: item,
})),
);
const selectValue = computed({
get: () => props.modelValue,
@ -42,7 +49,7 @@ const selectValue = computed({
/** 前端过滤:按工序名称或编码模糊匹配 */
function handleFilter(input: string, option: any) {
const keyword = input.toLowerCase();
const item = option?.item as MesProProcessApi.Process | undefined;
const item = option?.raw as MesProProcessApi.Process | undefined;
return Boolean(
item?.name?.toLowerCase().includes(keyword) ||
item?.code?.toLowerCase().includes(keyword),
@ -96,19 +103,17 @@ onMounted(async () => {
:placeholder="placeholder"
class="w-full"
show-search
:options="selectOptions"
@change="handleChange"
>
<SelectOption
v-for="item in allList"
:key="item.id"
:item="item"
:value="item.id"
>
<template #optionRender="{ option }">
<div class="flex items-center gap-2">
<span>{{ item.name }}</span>
<Tag v-if="item.code" color="default">{{ item.code }}</Tag>
<span>{{ option.data.raw.name }}</span>
<Tag v-if="option.data.raw.code" color="default">
{{ option.data.raw.code }}
</Tag>
</div>
</SelectOption>
</template>
</Select>
</Tooltip>
</template>

View File

@ -3,7 +3,7 @@ import type { MesWmWarehouseAreaApi } from '#/api/mes/wm/warehouse/area';
import { computed, ref, useAttrs, watch, watchEffect } from 'vue';
import { Select, SelectOption, Tag, Tooltip } from 'antdv-next';
import { Select, Tag, Tooltip } from 'antdv-next';
import { getWarehouseAreaSimpleList } from '#/api/mes/wm/warehouse/area';
@ -34,6 +34,13 @@ const emit = defineEmits<{
const attrs = useAttrs();
const allList = ref<MesWmWarehouseAreaApi.WarehouseArea[]>([]);
const selectedItem = ref<MesWmWarehouseAreaApi.WarehouseArea>();
const selectOptions = computed(() =>
allList.value.map((item) => ({
label: item.name,
value: item.id,
raw: item,
})),
);
const selectValue = computed({
get: () => props.modelValue,
@ -49,7 +56,7 @@ function handleChange(val: any) {
/** 前端过滤name + code */
function filterOption(input: string, option: any) {
const item = allList.value.find((o) => o.id === option.value);
const item = option?.raw as MesWmWarehouseAreaApi.WarehouseArea | undefined;
if (!item) {
return false;
}
@ -101,14 +108,17 @@ watchEffect(async () => {
:filter-option="filterOption"
:placeholder="placeholder"
show-search
:options="selectOptions"
@change="handleChange"
>
<SelectOption v-for="item in allList" :key="item.id" :value="item.id">
<template #optionRender="{ option }">
<div class="flex items-center gap-2">
<span>{{ item.name }}</span>
<Tag v-if="item.code" color="blue">: {{ item.code }}</Tag>
<span>{{ option.data.raw.name }}</span>
<Tag v-if="option.data.raw.code" color="blue">
编号: {{ option.data.raw.code }}
</Tag>
</div>
</SelectOption>
</template>
</Select>
</Tooltip>
</template>

View File

@ -3,7 +3,7 @@ import type { MesWmWarehouseLocationApi } from '#/api/mes/wm/warehouse/location'
import { computed, ref, useAttrs, watch, watchEffect } from 'vue';
import { Select, SelectOption, Tag, Tooltip } from 'antdv-next';
import { Select, Tag, Tooltip } from 'antdv-next';
import { getWarehouseLocationSimpleList } from '#/api/mes/wm/warehouse/location';
@ -34,6 +34,13 @@ const emit = defineEmits<{
const attrs = useAttrs();
const allList = ref<MesWmWarehouseLocationApi.WarehouseLocation[]>([]);
const selectedItem = ref<MesWmWarehouseLocationApi.WarehouseLocation>();
const selectOptions = computed(() =>
allList.value.map((item) => ({
label: item.name,
value: item.id,
raw: item,
})),
);
const selectValue = computed({
get: () => props.modelValue,
@ -49,7 +56,9 @@ function handleChange(val: any) {
/** 前端过滤name + code */
function filterOption(input: string, option: any) {
const item = allList.value.find((o) => o.id === option.value);
const item = option?.raw as
| MesWmWarehouseLocationApi.WarehouseLocation
| undefined;
if (!item) {
return false;
}
@ -100,14 +109,17 @@ watchEffect(async () => {
:filter-option="filterOption"
:placeholder="placeholder"
show-search
:options="selectOptions"
@change="handleChange"
>
<SelectOption v-for="item in allList" :key="item.id" :value="item.id">
<template #optionRender="{ option }">
<div class="flex items-center gap-2">
<span>{{ item.name }}</span>
<Tag v-if="item.code" color="blue">: {{ item.code }}</Tag>
<span>{{ option.data.raw.name }}</span>
<Tag v-if="option.data.raw.code" color="blue">
编号: {{ option.data.raw.code }}
</Tag>
</div>
</SelectOption>
</template>
</Select>
</Tooltip>
</template>

View File

@ -3,7 +3,7 @@ import type { MesWmWarehouseApi } from '#/api/mes/wm/warehouse';
import { computed, onMounted, ref, useAttrs, watch } from 'vue';
import { Select, SelectOption, Tag, Tooltip } from 'antdv-next';
import { Select, Tag, Tooltip } from 'antdv-next';
import { getWarehouseSimpleList } from '#/api/mes/wm/warehouse';
@ -32,6 +32,13 @@ const emit = defineEmits<{
const attrs = useAttrs();
const allList = ref<MesWmWarehouseApi.Warehouse[]>([]);
const selectedItem = ref<MesWmWarehouseApi.Warehouse>();
const selectOptions = computed(() =>
allList.value.map((item) => ({
label: item.name,
value: item.id,
raw: item,
})),
);
const selectValue = computed({
get: () => props.modelValue,
@ -47,7 +54,7 @@ function handleChange(val: any) {
/** 前端过滤name + code */
function filterOption(input: string, option: any) {
const item = allList.value.find((o) => o.id === option.value);
const item = option?.raw as MesWmWarehouseApi.Warehouse | undefined;
if (!item) {
return false;
}
@ -97,14 +104,17 @@ onMounted(async () => {
:filter-option="filterOption"
:placeholder="placeholder"
show-search
:options="selectOptions"
@change="handleChange"
>
<SelectOption v-for="item in allList" :key="item.id" :value="item.id">
<template #optionRender="{ option }">
<div class="flex items-center gap-2">
<span>{{ item.name }}</span>
<Tag v-if="item.code" color="blue">: {{ item.code }}</Tag>
<span>{{ option.data.raw.name }}</span>
<Tag v-if="option.data.raw.code" color="blue">
编号: {{ option.data.raw.code }}
</Tag>
</div>
</SelectOption>
</template>
</Select>
</Tooltip>
</template>