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
parent
456a91dfc2
commit
c20eb8e1f4
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 = () => {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
<!-- 属性详情弹出层 -->
|
||||
|
|
|
|||
|
|
@ -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="默认颜色">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue