!364 feat(@vben/web-antdv-next): sync Antdv Next compatibility fixes and BPM attachment support
Merge pull request !364 from XuZhiqiang/feat-antdv-nextpull/366/head^2
commit
e26f91d9fa
|
|
@ -0,0 +1,86 @@
|
|||
import type { Component } from 'vue';
|
||||
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { defineAsyncComponent, defineComponent, h, ref } from 'vue';
|
||||
|
||||
const RawDatePicker = defineAsyncComponent(
|
||||
() => import('antdv-next/dist/date-picker/index'),
|
||||
);
|
||||
const RawRangePicker = defineAsyncComponent(() =>
|
||||
import('antdv-next/dist/date-picker/index').then(
|
||||
(res) => res.DateRangePicker,
|
||||
),
|
||||
);
|
||||
|
||||
const TIMESTAMP_VALUE_FORMATS = new Set(['x', 'X']);
|
||||
|
||||
function isTimestampValueFormat(valueFormat: unknown) {
|
||||
return (
|
||||
typeof valueFormat === 'string' && TIMESTAMP_VALUE_FORMATS.has(valueFormat)
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeTimestampPickerValue(value: any, valueFormat: unknown): any {
|
||||
if (!isTimestampValueFormat(valueFormat)) {
|
||||
return value;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => normalizeTimestampPickerValue(item, valueFormat));
|
||||
}
|
||||
return typeof value === 'number' ? String(value) : value;
|
||||
}
|
||||
|
||||
function withTimestampValueFormat(
|
||||
component: Component,
|
||||
name: 'DatePicker' | 'RangePicker',
|
||||
) {
|
||||
return defineComponent({
|
||||
name,
|
||||
inheritAttrs: false,
|
||||
setup(_, { attrs, expose, slots }) {
|
||||
const innerRef = ref();
|
||||
expose(
|
||||
new Proxy(
|
||||
{},
|
||||
{
|
||||
get: (_target, key) => innerRef.value?.[key],
|
||||
has: (_target, key) => key in (innerRef.value || {}),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return () => {
|
||||
const pickerAttrs: Recordable<any> = { ...attrs };
|
||||
if (
|
||||
'value-format' in pickerAttrs &&
|
||||
!Reflect.has(pickerAttrs, 'valueFormat')
|
||||
) {
|
||||
pickerAttrs.valueFormat = pickerAttrs['value-format'];
|
||||
}
|
||||
const valueFormat = pickerAttrs.valueFormat;
|
||||
|
||||
for (const key of [
|
||||
'value',
|
||||
'defaultValue',
|
||||
'pickerValue',
|
||||
'defaultPickerValue',
|
||||
]) {
|
||||
if (Reflect.has(pickerAttrs, key)) {
|
||||
pickerAttrs[key] = normalizeTimestampPickerValue(
|
||||
pickerAttrs[key],
|
||||
valueFormat,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return h(component, { ...pickerAttrs, ref: innerRef }, slots);
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const DatePicker = withTimestampValueFormat(RawDatePicker, 'DatePicker');
|
||||
const RangePicker = withTimestampValueFormat(RawRangePicker, 'RangePicker');
|
||||
|
||||
export { DatePicker, RangePicker };
|
||||
|
|
@ -75,6 +75,9 @@ import { message, Modal, notification } from 'antdv-next';
|
|||
import { uploadFile as uploadFileApi } from '#/api/infra/file';
|
||||
import { Tinymce as RichTextarea } from '#/components/tinymce';
|
||||
import { FileUpload, ImageUpload } from '#/components/upload';
|
||||
|
||||
import { DatePicker, RangePicker } from './date-picker';
|
||||
|
||||
type AdapterUploadProps = UploadProps & {
|
||||
aspectRatio?: string;
|
||||
crop?: boolean;
|
||||
|
|
@ -97,9 +100,6 @@ const Checkbox = defineAsyncComponent(
|
|||
const CheckboxGroup = defineAsyncComponent(() =>
|
||||
import('antdv-next/dist/checkbox/index').then((res) => res.CheckboxGroup),
|
||||
);
|
||||
const DatePicker = defineAsyncComponent(
|
||||
() => import('antdv-next/dist/date-picker/index'),
|
||||
);
|
||||
const Divider = defineAsyncComponent(
|
||||
() => import('antdv-next/dist/divider/index'),
|
||||
);
|
||||
|
|
@ -117,11 +117,6 @@ const Radio = defineAsyncComponent(() => import('antdv-next/dist/radio/index'));
|
|||
const RadioGroup = defineAsyncComponent(() =>
|
||||
import('antdv-next/dist/radio/index').then((res) => res.RadioGroup),
|
||||
);
|
||||
const RangePicker = defineAsyncComponent(() =>
|
||||
import('antdv-next/dist/date-picker/index').then(
|
||||
(res) => res.DateRangePicker,
|
||||
),
|
||||
);
|
||||
const Rate = defineAsyncComponent(() => import('antdv-next/dist/rate/index'));
|
||||
const Select = defineAsyncComponent(
|
||||
() => import('antdv-next/dist/select/index'),
|
||||
|
|
@ -794,4 +789,4 @@ async function initComponentAdapter() {
|
|||
});
|
||||
}
|
||||
|
||||
export { initComponentAdapter };
|
||||
export { DatePicker, initComponentAdapter, RangePicker };
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ export namespace BpmProcessInstanceApi {
|
|||
reason: string;
|
||||
signPicUrl: string;
|
||||
status: number;
|
||||
attachments?: string[];
|
||||
}
|
||||
|
||||
/** 抄送流程实例 */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { Rule } from 'antdv-next/es/form';
|
||||
import type { FormItemProps } from 'antdv-next';
|
||||
|
||||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
|
|
@ -6,6 +6,8 @@ import { isEmpty } from '@vben/utils';
|
|||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
type FormItemRule = NonNullable<FormItemProps['rules']>[number];
|
||||
|
||||
export namespace ThingModelApi {
|
||||
/** IoT 物模型数据 */
|
||||
export interface ThingModel {
|
||||
|
|
@ -95,46 +97,42 @@ export namespace ThingModelApi {
|
|||
|
||||
/** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */
|
||||
function buildRequiredNumberValidator(label: string) {
|
||||
return (_rule: any, value: any, callback: any) => {
|
||||
return (_rule: any, value: any) => {
|
||||
if (isEmpty(value)) {
|
||||
callback(new Error(`${label}不能为空`));
|
||||
return;
|
||||
return Promise.reject(new Error(`${label}不能为空`));
|
||||
}
|
||||
if (Number.isNaN(Number(value))) {
|
||||
callback(new Error(`${label}必须是数字`));
|
||||
return;
|
||||
return Promise.reject(new Error(`${label}必须是数字`));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
};
|
||||
}
|
||||
|
||||
/** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */
|
||||
export function buildIdentifierLikeNameValidator(label: string) {
|
||||
return (_rule: any, value: string, callback: any) => {
|
||||
return (_rule: any, value: string) => {
|
||||
if (isEmpty(value)) {
|
||||
callback(new Error(`${label}不能为空`));
|
||||
return;
|
||||
return Promise.reject(new Error(`${label}不能为空`));
|
||||
}
|
||||
if (!/^[一-龥A-Za-z0-9]/.test(value)) {
|
||||
callback(new Error(`${label}必须以中文、英文字母或数字开头`));
|
||||
return;
|
||||
return Promise.reject(
|
||||
new Error(`${label}必须以中文、英文字母或数字开头`),
|
||||
);
|
||||
}
|
||||
if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) {
|
||||
callback(
|
||||
return Promise.reject(
|
||||
new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (value.length > 20) {
|
||||
callback(new Error(`${label}长度不能超过 20 个字符`));
|
||||
return;
|
||||
return Promise.reject(new Error(`${label}长度不能超过 20 个字符`));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
};
|
||||
}
|
||||
|
||||
/** IoT 物模型表单校验规则 */
|
||||
export const ThingModelFormRules: Record<string, Rule[]> = {
|
||||
export const ThingModelFormRules: Record<string, FormItemRule[]> = {
|
||||
name: [
|
||||
{ required: true, message: '功能名称不能为空', trigger: 'blur' },
|
||||
{
|
||||
|
|
@ -153,7 +151,7 @@ export const ThingModelFormRules: Record<string, Rule[]> = {
|
|||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
validator: (_rule: any, value: string, callback: any) => {
|
||||
validator: (_rule: any, value: string) => {
|
||||
const reservedKeywords = [
|
||||
'set',
|
||||
'get',
|
||||
|
|
@ -164,18 +162,16 @@ export const ThingModelFormRules: Record<string, Rule[]> = {
|
|||
'value',
|
||||
];
|
||||
if (reservedKeywords.includes(value)) {
|
||||
callback(
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (/^\d+$/.test(value)) {
|
||||
callback(new Error('标识符不能是纯数字'));
|
||||
return;
|
||||
return Promise.reject(new Error('标识符不能是纯数字'));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { computed, onMounted, reactive, ref, watch } from 'vue';
|
|||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
InputNumber,
|
||||
message,
|
||||
|
|
@ -417,22 +418,21 @@ function inputChange() {
|
|||
@input="inputChange"
|
||||
>
|
||||
<template #addonAfter>
|
||||
<Select v-model:value="select" placeholder="生成器" class="w-36">
|
||||
<SelectOption value="0 * * * * ?">每分钟</SelectOption>
|
||||
<SelectOption value="0 0 * * * ?">每小时</SelectOption>
|
||||
<SelectOption value="0 0 0 * * ?">每天零点</SelectOption>
|
||||
<SelectOption value="0 0 0 1 * ?">每月一号零点</SelectOption>
|
||||
<SelectOption value="0 0 0 L * ?">每月最后一天零点</SelectOption>
|
||||
<SelectOption value="0 0 0 ? * 1">每周星期日零点</SelectOption>
|
||||
<SelectOption
|
||||
v-for="(item, index) in shortcuts"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.text }}
|
||||
</SelectOption>
|
||||
<SelectOption value="custom">自定义</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="select"
|
||||
placeholder="生成器"
|
||||
class="w-36"
|
||||
:options="[
|
||||
{ label: '每分钟', value: '0 * * * * ?' },
|
||||
{ label: '每小时', value: '0 0 * * * ?' },
|
||||
{ label: '每天零点', value: '0 0 0 * * ?' },
|
||||
{ label: '每月一号零点', value: '0 0 0 1 * ?' },
|
||||
{ label: '每月最后一天零点', value: '0 0 0 L * ?' },
|
||||
{ label: '每周星期日零点', value: '0 0 0 ? * 1' },
|
||||
...shortcuts.map((item) => ({ label: item.text, value: item.value })),
|
||||
{ label: '自定义', value: 'custom' },
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Input>
|
||||
|
||||
|
|
@ -496,14 +496,10 @@ function inputChange() {
|
|||
v-model:value="cronValue.second.appoint"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.second"
|
||||
:key="index"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</Select>
|
||||
:options="
|
||||
data.second.map((item) => ({ label: item, value: item }))
|
||||
"
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
|
@ -560,14 +556,10 @@ function inputChange() {
|
|||
v-model:value="cronValue.minute.appoint"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.minute"
|
||||
:key="index"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</Select>
|
||||
:options="
|
||||
data.minute.map((item) => ({ label: item, value: item }))
|
||||
"
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
|
@ -624,14 +616,10 @@ function inputChange() {
|
|||
v-model:value="cronValue.hour.appoint"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.hour"
|
||||
:key="index"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</Select>
|
||||
:options="
|
||||
data.hour.map((item) => ({ label: item, value: item }))
|
||||
"
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
|
@ -690,14 +678,10 @@ function inputChange() {
|
|||
v-model:value="cronValue.day.appoint"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.day"
|
||||
:key="index"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</Select>
|
||||
:options="
|
||||
data.day.map((item) => ({ label: item, value: item }))
|
||||
"
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
|
@ -754,14 +738,10 @@ function inputChange() {
|
|||
v-model:value="cronValue.month.appoint"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.month"
|
||||
:key="index"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</Select>
|
||||
:options="
|
||||
data.month.map((item) => ({ label: item, value: item }))
|
||||
"
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
|
@ -785,23 +765,15 @@ function inputChange() {
|
|||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem v-if="cronValue.week.type === '1'" label="范围">
|
||||
<Select v-model:value="cronValue.week.range.start">
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.week"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="cronValue.week.range.start"
|
||||
:options="data.week"
|
||||
/>
|
||||
<span style="padding: 0 15px">-</span>
|
||||
<Select v-model:value="cronValue.week.range.end">
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.week"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="cronValue.week.range.end"
|
||||
:options="data.week"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem v-if="cronValue.week.type === '2'" label="间隔">
|
||||
第
|
||||
|
|
@ -812,14 +784,10 @@ function inputChange() {
|
|||
controls-position="right"
|
||||
/>
|
||||
周的星期
|
||||
<Select v-model:value="cronValue.week.loop.end">
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.week"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="cronValue.week.loop.end"
|
||||
:options="data.week"
|
||||
/>
|
||||
执行一次
|
||||
</FormItem>
|
||||
<FormItem v-if="cronValue.week.type === '3'" label="指定">
|
||||
|
|
@ -827,24 +795,14 @@ function inputChange() {
|
|||
v-model:value="cronValue.week.appoint"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.week"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</Select>
|
||||
:options="data.week"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem v-if="cronValue.week.type === '4'" label="最后一周">
|
||||
<Select v-model:value="cronValue.week.last">
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.week"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="cronValue.week.last"
|
||||
:options="data.week"
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
|
@ -895,14 +853,10 @@ function inputChange() {
|
|||
v-model:value="cronValue.year.appoint"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, index) in data.year"
|
||||
:key="index"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</Select>
|
||||
:options="
|
||||
data.year.map((item) => ({ label: item, value: item }))
|
||||
"
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { computed, defineComponent, ref, unref, useAttrs } from 'vue';
|
|||
|
||||
import { get, getNestedValue, isFunction } from '@vben/utils';
|
||||
|
||||
import { Card, Descriptions } from 'antdv-next';
|
||||
import { Card, Descriptions, DescriptionsItem } from 'antdv-next';
|
||||
|
||||
const props = {
|
||||
bordered: { default: true, type: Boolean },
|
||||
|
|
|
|||
|
|
@ -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 = () => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { nextTick, reactive, ref } from 'vue';
|
|||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Form, Input, Select, Space } from 'antdv-next';
|
||||
import { Button, Form, FormItem, Input, Select, Space } from 'antdv-next';
|
||||
|
||||
import { loadBaiduMapSdk } from './utils';
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ onMounted(() => {
|
|||
<DateRangePicker
|
||||
v-model:value="times"
|
||||
:format="rangePickerProps.format"
|
||||
:value-format="rangePickerProps.valueFormat"
|
||||
:placeholder="rangePickerProps.placeholder"
|
||||
:presets="rangePickerProps.presets"
|
||||
class="!w-full !max-w-96"
|
||||
|
|
|
|||
|
|
@ -188,8 +188,8 @@ watch(
|
|||
<Dropdown v-if="getDropdownList.length > 0" :trigger="['hover']">
|
||||
<slot name="more">
|
||||
<Button type="link">
|
||||
{{ $t('page.action.more') }}
|
||||
<template #icon>
|
||||
{{ $t('page.action.more') }}
|
||||
<IconifyIcon icon="lucide:ellipsis-vertical" />
|
||||
</template>
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import type { UploadFile, UploadProps } from 'antdv-next';
|
||||
import type { UploadRequestOption } from 'antdv-next/lib/vc-upload/interface';
|
||||
|
||||
import type { FileUploadProps } from './typing';
|
||||
|
||||
|
|
@ -17,6 +16,8 @@ import { Button, message, Upload } from 'antdv-next';
|
|||
import { UploadResultStatus } from './typing';
|
||||
import { useUpload, useUploadType } from './use-upload';
|
||||
|
||||
type UploadRequestOption = any;
|
||||
|
||||
defineOptions({ name: 'FileUpload', inheritAttrs: false });
|
||||
|
||||
const props = withDefaults(defineProps<FileUploadProps>(), {
|
||||
|
|
@ -34,6 +35,7 @@ const props = withDefaults(defineProps<FileUploadProps>(), {
|
|||
resultField: '',
|
||||
returnText: false,
|
||||
showDescription: false,
|
||||
showDownloadIcon: true,
|
||||
});
|
||||
const emit = defineEmits([
|
||||
'change',
|
||||
|
|
@ -295,7 +297,7 @@ function getValue() {
|
|||
:show-upload-list="{
|
||||
showPreviewIcon: true,
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: true,
|
||||
showDownloadIcon,
|
||||
}"
|
||||
@remove="handleRemove"
|
||||
@preview="handlePreview"
|
||||
|
|
|
|||
|
|
@ -29,5 +29,6 @@ export interface FileUploadProps {
|
|||
resultField?: string; // support xxx.xxx.xx
|
||||
returnText?: boolean; // 是否返回文件文本内容
|
||||
showDescription?: boolean; // 是否显示下面的描述
|
||||
showDownloadIcon?: boolean; // 是否显示下载按钮
|
||||
value?: string | string[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,16 @@
|
|||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
type DateRangeTuple = [Dayjs, Dayjs];
|
||||
type StringRangeTuple = [string, string];
|
||||
|
||||
function dateRange(start: Dayjs, end: Dayjs): DateRangeTuple {
|
||||
return [start, end];
|
||||
}
|
||||
|
||||
/** 时间段选择器拓展 */
|
||||
export function getRangePickerDefaultProps() {
|
||||
return {
|
||||
|
|
@ -13,55 +22,55 @@ export function getRangePickerDefaultProps() {
|
|||
placeholder: [
|
||||
$t('utils.rangePicker.beginTime'),
|
||||
$t('utils.rangePicker.endTime'),
|
||||
],
|
||||
] as StringRangeTuple,
|
||||
// 快捷时间范围
|
||||
presets: [
|
||||
{
|
||||
label: $t('utils.rangePicker.today'),
|
||||
value: [dayjs().startOf('day'), dayjs().endOf('day')],
|
||||
value: dateRange(dayjs().startOf('day'), dayjs().endOf('day')),
|
||||
},
|
||||
{
|
||||
label: $t('utils.rangePicker.last7Days'),
|
||||
value: [
|
||||
value: dateRange(
|
||||
dayjs().subtract(7, 'day').startOf('day'),
|
||||
dayjs().endOf('day'),
|
||||
],
|
||||
),
|
||||
},
|
||||
{
|
||||
label: $t('utils.rangePicker.last30Days'),
|
||||
value: [
|
||||
value: dateRange(
|
||||
dayjs().subtract(30, 'day').startOf('day'),
|
||||
dayjs().endOf('day'),
|
||||
],
|
||||
),
|
||||
},
|
||||
{
|
||||
label: $t('utils.rangePicker.yesterday'),
|
||||
value: [
|
||||
value: dateRange(
|
||||
dayjs().subtract(1, 'day').startOf('day'),
|
||||
dayjs().subtract(1, 'day').endOf('day'),
|
||||
],
|
||||
),
|
||||
},
|
||||
{
|
||||
label: $t('utils.rangePicker.thisWeek'),
|
||||
value: [dayjs().startOf('week'), dayjs().endOf('day')],
|
||||
value: dateRange(dayjs().startOf('week'), dayjs().endOf('day')),
|
||||
},
|
||||
{
|
||||
label: $t('utils.rangePicker.thisMonth'),
|
||||
value: [dayjs().startOf('month'), dayjs().endOf('day')],
|
||||
value: dateRange(dayjs().startOf('month'), dayjs().endOf('day')),
|
||||
},
|
||||
{
|
||||
label: $t('utils.rangePicker.lastWeek'),
|
||||
value: [
|
||||
value: dateRange(
|
||||
dayjs().subtract(1, 'week').startOf('day'),
|
||||
dayjs().endOf('day'),
|
||||
],
|
||||
),
|
||||
},
|
||||
],
|
||||
showTime: {
|
||||
defaultValue: [
|
||||
defaultValue: dateRange(
|
||||
dayjs('00:00:00', 'HH:mm:ss'),
|
||||
dayjs('23:59:59', 'HH:mm:ss'),
|
||||
],
|
||||
),
|
||||
format: 'HH:mm:ss',
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -147,15 +147,9 @@ defineExpose({ settingValues });
|
|||
size="large"
|
||||
class="!w-80"
|
||||
@change="handlerPlatformChange"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in OtherPlatformEnum"
|
||||
:key="item.key"
|
||||
:value="item.key"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="OtherPlatformEnum"
|
||||
:field-names="{ label: 'name', value: 'key' }"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
|
|
@ -169,15 +163,9 @@ defineExpose({ settingValues });
|
|||
placeholder="Select"
|
||||
size="large"
|
||||
class="!w-80"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in platformModels"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="platformModels"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -214,15 +214,8 @@ defineExpose({ settingValues });
|
|||
class="!w-80"
|
||||
allow-clear
|
||||
placeholder="请选择版本"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in versionList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="versionList"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -168,15 +168,9 @@ defineExpose({ settingValues });
|
|||
placeholder="Select"
|
||||
size="large"
|
||||
class="!w-80"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in StableDiffusionSamplers"
|
||||
:key="item.key"
|
||||
:value="item.key"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="StableDiffusionSamplers"
|
||||
:field-names="{ label: 'name', value: 'key' }"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
|
|
@ -189,15 +183,9 @@ defineExpose({ settingValues });
|
|||
placeholder="Select"
|
||||
size="large"
|
||||
class="!w-80"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in StableDiffusionClipGuidancePresets"
|
||||
:key="item.key"
|
||||
:value="item.key"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="StableDiffusionClipGuidancePresets"
|
||||
:field-names="{ label: 'name', value: 'key' }"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
|
|
@ -210,16 +198,9 @@ defineExpose({ settingValues });
|
|||
placeholder="Select"
|
||||
size="large"
|
||||
class="!w-80"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in StableDiffusionStylePresets"
|
||||
:key="item.key"
|
||||
:label="item.name"
|
||||
:value="item.key"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="StableDiffusionStylePresets"
|
||||
:field-names="{ label: 'name', value: 'key' }"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
Dropdown,
|
||||
Empty,
|
||||
Form,
|
||||
FormItem,
|
||||
InputNumber,
|
||||
Menu,
|
||||
MenuItem,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { computed, getCurrentInstance, inject, onMounted, ref } from 'vue';
|
|||
import { IconifyIcon } from '@vben/icons';
|
||||
import { generateAcceptedFileTypes } from '@vben/utils';
|
||||
|
||||
import { Button, Form, message, UploadDragger } from 'antdv-next';
|
||||
import { Button, Form, FormItem, message, UploadDragger } from 'antdv-next';
|
||||
|
||||
import { useUpload } from '#/components/upload/use-upload';
|
||||
type UploadRequestOption = any;
|
||||
|
|
|
|||
|
|
@ -47,9 +47,8 @@ defineExpose({
|
|||
v-model:value="formData.version"
|
||||
class="w-full"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in [
|
||||
:options="[
|
||||
...[
|
||||
{
|
||||
value: '3',
|
||||
label: 'V3',
|
||||
|
|
@ -58,13 +57,9 @@ defineExpose({
|
|||
value: '2',
|
||||
label: 'V2',
|
||||
},
|
||||
]"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
].map((item) => ({ label: item.label, value: item.value })),
|
||||
]"
|
||||
/>
|
||||
</Title>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { Nullable, Recordable } from '@vben/types';
|
|||
|
||||
import { ref, unref } from 'vue';
|
||||
|
||||
import { Button, Card } from 'antdv-next';
|
||||
import { Button, Card, RadioButton, RadioGroup } from 'antdv-next';
|
||||
|
||||
import desc from './desc.vue';
|
||||
import lyric from './lyric.vue';
|
||||
|
|
|
|||
|
|
@ -80,9 +80,8 @@ defineExpose({
|
|||
v-model:value="formData.version"
|
||||
class="w-full"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in [
|
||||
:options="[
|
||||
...[
|
||||
{
|
||||
value: '3',
|
||||
label: 'V3',
|
||||
|
|
@ -91,13 +90,9 @@ defineExpose({
|
|||
value: '2',
|
||||
label: 'V2',
|
||||
},
|
||||
]"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
].map((item) => ({ label: item.label, value: item.value })),
|
||||
]"
|
||||
/>
|
||||
</Title>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { ref } from 'vue';
|
|||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { Form, Input, Select, TextArea } from 'antdv-next';
|
||||
import { Form, FormItem, Input, Select, TextArea } from 'antdv-next';
|
||||
|
||||
type Rule = any;
|
||||
const modelData = defineModel<any>(); // 创建本地数据副本
|
||||
|
|
@ -53,15 +53,13 @@ defineExpose({ validate });
|
|||
v-model:value="modelData.status"
|
||||
allow-clear
|
||||
placeholder="请选择状态"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="
|
||||
getDictOptions(DICT_TYPE.COMMON_STATUS, 'number').map((dict) => ({
|
||||
label: dict.label,
|
||||
value: dict.value as any,
|
||||
}))
|
||||
"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="流程描述" name="description" class="mb-5">
|
||||
<TextArea v-model:value="modelData.description" allow-clear />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts" setup>
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import { inject, ref } from 'vue';
|
||||
import { computed, inject, ref } from 'vue';
|
||||
|
||||
import { useVbenDrawer } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
|
@ -20,6 +20,15 @@ const tinyflowRef = ref<InstanceType<typeof Tinyflow> | null>(null);
|
|||
const workflowData = inject('workflowData') as Ref;
|
||||
const params4Test = ref<any[]>([]);
|
||||
const paramsOfStartNode = ref<any>({});
|
||||
const startNodeParamOptions = computed(() =>
|
||||
Object.entries(paramsOfStartNode.value).map(
|
||||
([key, value]: [string, any]) => ({
|
||||
label: value?.description || key,
|
||||
value: key,
|
||||
disabled: !!value?.disabled,
|
||||
}),
|
||||
),
|
||||
);
|
||||
const testResult = ref(null);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
|
@ -231,16 +240,12 @@ defineExpose({ validate });
|
|||
v-for="(param, index) in params4Test"
|
||||
:key="index"
|
||||
>
|
||||
<Select class="w-48" v-model="param.key" placeholder="参数名">
|
||||
<SelectOption
|
||||
v-for="(value, key) in paramsOfStartNode"
|
||||
:key="key"
|
||||
:value="key"
|
||||
:disabled="!!value?.disabled"
|
||||
>
|
||||
{{ value?.description || key }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model="param.key"
|
||||
:options="startNodeParamOptions"
|
||||
class="w-48"
|
||||
placeholder="参数名"
|
||||
/>
|
||||
<Input
|
||||
class="mx-2 w-48"
|
||||
v-model:value="param.value"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue';
|
|||
import { IconifyIcon } from '@vben/icons';
|
||||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import { Collapse } from 'antdv-next';
|
||||
import { Collapse, CollapsePanel } from 'antdv-next';
|
||||
|
||||
import ElementCustomConfig from '#/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue';
|
||||
import ElementForm from '#/views/bpm/components/bpmn-process-designer/package/penal/form/ElementForm.vue';
|
||||
|
|
@ -57,8 +57,6 @@ const props = defineProps({
|
|||
}, // 流程模型的数据
|
||||
});
|
||||
|
||||
const CollapsePanel = CollapsePanel;
|
||||
|
||||
const activeTab = ref('base');
|
||||
const elementId = ref('');
|
||||
const elementType = ref<any>('');
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {
|
|||
RadioGroup,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
Switch,
|
||||
TypographyText,
|
||||
} from 'antdv-next';
|
||||
|
|
@ -277,18 +276,10 @@ watch(
|
|||
<Col>
|
||||
<Select
|
||||
v-model:value="timeUnit"
|
||||
:options="TIME_UNIT_TYPES"
|
||||
class="mr-2 !w-24"
|
||||
@change="onTimeUnitChange"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in TIME_UNIT_TYPES"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
<TypographyText class="mr-2 mt-2 inline-flex text-sm">
|
||||
未处理
|
||||
</TypographyText>
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ import { IconifyIcon } from '@vben/icons';
|
|||
import {
|
||||
Button,
|
||||
Divider,
|
||||
FormItem,
|
||||
Input,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Select,
|
||||
SelectOption,
|
||||
Switch,
|
||||
} from 'antdv-next';
|
||||
|
||||
|
|
@ -487,15 +487,9 @@ onMounted(async () => {
|
|||
style="width: 100%"
|
||||
@change="updateReturnNodeId"
|
||||
placeholder="请选择驳回节点"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in returnTaskList"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="returnTaskList"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<Divider orientation="left">审批人为空时</Divider>
|
||||
|
|
@ -524,15 +518,9 @@ onMounted(async () => {
|
|||
mode="multiple"
|
||||
style="width: 100%"
|
||||
@change="updateAssignEmptyUserIds"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="userOptions"
|
||||
:field-names="{ label: 'nickname', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<Divider orientation="left">审批人与提交人为同一人时</Divider>
|
||||
|
|
|
|||
|
|
@ -157,21 +157,28 @@ watch(
|
|||
:wrapper-col="{ span: 18 }"
|
||||
>
|
||||
<FormItem label="流转类型">
|
||||
<Select v-model:value="flowConditionForm.type" @change="updateFlowType">
|
||||
<SelectOption value="normal">普通流转路径</SelectOption>
|
||||
<SelectOption value="default">默认流转路径</SelectOption>
|
||||
<SelectOption value="condition">条件流转路径</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="flowConditionForm.type"
|
||||
@change="updateFlowType"
|
||||
:options="[
|
||||
{ label: '普通流转路径', value: 'normal' },
|
||||
{ label: '默认流转路径', value: 'default' },
|
||||
{ label: '条件流转路径', value: 'condition' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="条件格式"
|
||||
v-if="flowConditionForm.type === 'condition'"
|
||||
key="condition"
|
||||
>
|
||||
<Select v-model:value="flowConditionForm.conditionType">
|
||||
<SelectOption value="expression">表达式</SelectOption>
|
||||
<SelectOption value="script">脚本</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="flowConditionForm.conditionType"
|
||||
:options="[
|
||||
{ label: '表达式', value: 'expression' },
|
||||
{ label: '脚本', value: 'script' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="表达式"
|
||||
|
|
@ -202,10 +209,13 @@ watch(
|
|||
/>
|
||||
</FormItem>
|
||||
<FormItem label="脚本类型" key="scriptType">
|
||||
<Select v-model:value="flowConditionForm.scriptType">
|
||||
<SelectOption value="inlineScript">内联脚本</SelectOption>
|
||||
<SelectOption value="externalScript">外部脚本</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="flowConditionForm.scriptType"
|
||||
:options="[
|
||||
{ label: '内联脚本', value: 'inlineScript' },
|
||||
{ label: '外部脚本', value: 'externalScript' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="脚本"
|
||||
|
|
|
|||
|
|
@ -319,14 +319,13 @@ watch(
|
|||
<FormItem label="业务标识">
|
||||
<Select
|
||||
v-model:value="businessKey"
|
||||
:options="[
|
||||
...fieldList.map((i) => ({ label: i.label, value: i.id })),
|
||||
{ label: '无', value: '' },
|
||||
]"
|
||||
@change="_updateElementBusinessKey"
|
||||
allow-clear
|
||||
>
|
||||
<SelectOption v-for="i in fieldList" :key="i.id" :value="i.id">
|
||||
{{ i.label }}
|
||||
</SelectOption>
|
||||
<SelectOption value="">无</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
|
||||
|
|
@ -388,7 +387,6 @@ watch(
|
|||
<!-- allowClear-->
|
||||
<!-- @change="changeFieldTypeType"-->
|
||||
<!-- >-->
|
||||
<!-- <SelectOption v-for="(value, key) of fieldType" :key="key" :value="key">{{ value }}</SelectOption>-->
|
||||
<!-- </Select>-->
|
||||
<!-- </FormItem>-->
|
||||
<!-- <FormItem label="类型名称" v-if="formFieldForm.typeType === 'custom'">-->
|
||||
|
|
|
|||
|
|
@ -5,15 +5,7 @@ import { confirm, useVbenDrawer, useVbenModal } from '@vben/common-ui';
|
|||
import { IconifyIcon } from '@vben/icons';
|
||||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
Select,
|
||||
SelectOption,
|
||||
} from 'antdv-next';
|
||||
import { Button, Divider, Form, FormItem, Input, Select } from 'antdv-next';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { ProcessListenerSelectModal } from '#/views/bpm/processListener/components';
|
||||
|
|
@ -392,10 +384,13 @@ watch(
|
|||
},
|
||||
]"
|
||||
>
|
||||
<Select v-model:value="listenerForm.event">
|
||||
<SelectOption value="start">start</SelectOption>
|
||||
<SelectOption value="end">end</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="listenerForm.event"
|
||||
:options="[
|
||||
{ label: 'start', value: 'start' },
|
||||
{ label: 'end', value: 'end' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="监听器类型"
|
||||
|
|
@ -408,15 +403,15 @@ watch(
|
|||
},
|
||||
]"
|
||||
>
|
||||
<Select v-model:value="listenerForm.listenerType">
|
||||
<SelectOption
|
||||
v-for="i in Object.keys(listenerTypeObject)"
|
||||
:key="i"
|
||||
:value="i"
|
||||
>
|
||||
{{ listenerTypeObject[i as keyof typeof listenerType] }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="listenerForm.listenerType"
|
||||
:options="
|
||||
Object.keys(listenerTypeObject).map((i) => ({
|
||||
label: listenerTypeObject[i as keyof typeof listenerType],
|
||||
value: i,
|
||||
}))
|
||||
"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="listenerForm.listenerType === 'classListener'"
|
||||
|
|
@ -490,10 +485,13 @@ watch(
|
|||
},
|
||||
]"
|
||||
>
|
||||
<Select v-model:value="listenerForm.scriptType">
|
||||
<SelectOption value="inlineScript">内联脚本</SelectOption>
|
||||
<SelectOption value="externalScript">外部脚本</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="listenerForm.scriptType"
|
||||
:options="[
|
||||
{ label: '内联脚本', value: 'inlineScript' },
|
||||
{ label: '外部脚本', value: 'externalScript' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="listenerForm.scriptType === 'inlineScript'"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { ref } from 'vue';
|
|||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Form, FormItem, Input, Select, SelectOption } from 'antdv-next';
|
||||
import { Form, FormItem, Input, Select } from 'antdv-next';
|
||||
|
||||
import { fieldType } from './utilSelf';
|
||||
|
||||
|
|
@ -72,15 +72,15 @@ const [Modal, modalApi] = useVbenModal({
|
|||
},
|
||||
]"
|
||||
>
|
||||
<Select v-model:value="form.fieldType">
|
||||
<SelectOption
|
||||
v-for="i in Object.keys(fieldTypeObject)"
|
||||
:key="i"
|
||||
:value="i"
|
||||
>
|
||||
{{ fieldTypeObject[i as keyof typeof fieldType] }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="form.fieldType"
|
||||
:options="[
|
||||
...Object.keys(fieldTypeObject).map((i) => ({
|
||||
label: fieldTypeObject[i as keyof typeof fieldType],
|
||||
value: i,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="form.fieldType === 'string'"
|
||||
|
|
|
|||
|
|
@ -5,15 +5,7 @@ import { confirm, useVbenDrawer, useVbenModal } from '@vben/common-ui';
|
|||
import { IconifyIcon } from '@vben/icons';
|
||||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
Select,
|
||||
SelectOption,
|
||||
} from 'antdv-next';
|
||||
import { Button, Divider, Form, FormItem, Input, Select } from 'antdv-next';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { ProcessListenerSelectModal } from '#/views/bpm/processListener/components';
|
||||
|
|
@ -379,15 +371,15 @@ watch(
|
|||
name="event"
|
||||
:rules="[{ required: true, message: '请选择事件类型' }]"
|
||||
>
|
||||
<Select v-model:value="listenerForm.event">
|
||||
<SelectOption
|
||||
v-for="i in Object.keys(listenerEventTypeObject)"
|
||||
:key="i"
|
||||
:value="i"
|
||||
>
|
||||
{{ listenerEventTypeObject[i as keyof typeof eventType] }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="listenerForm.event"
|
||||
:options="[
|
||||
...Object.keys(listenerEventTypeObject).map((i) => ({
|
||||
label: listenerEventTypeObject[i as keyof typeof eventType],
|
||||
value: i,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="监听器ID"
|
||||
|
|
@ -401,15 +393,15 @@ watch(
|
|||
name="listenerType"
|
||||
:rules="[{ required: true, message: '请选择监听器类型' }]"
|
||||
>
|
||||
<Select v-model:value="listenerForm.listenerType">
|
||||
<SelectOption
|
||||
v-for="i in Object.keys(listenerTypeObject)"
|
||||
:key="i"
|
||||
:value="i"
|
||||
>
|
||||
{{ listenerTypeObject[i as keyof typeof listenerType] }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="listenerForm.listenerType"
|
||||
:options="[
|
||||
...Object.keys(listenerTypeObject).map((i) => ({
|
||||
label: listenerTypeObject[i as keyof typeof listenerType],
|
||||
value: i,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="listenerForm.listenerType === 'classListener'"
|
||||
|
|
@ -456,10 +448,13 @@ watch(
|
|||
key="listener-script-type"
|
||||
:rules="[{ required: true, message: '请选择脚本类型' }]"
|
||||
>
|
||||
<Select v-model:value="listenerForm.scriptType">
|
||||
<SelectOption value="inlineScript">内联脚本</SelectOption>
|
||||
<SelectOption value="externalScript">外部脚本</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="listenerForm.scriptType"
|
||||
:options="[
|
||||
{ label: '内联脚本', value: 'inlineScript' },
|
||||
{ label: '外部脚本', value: 'externalScript' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="listenerForm.scriptType === 'inlineScript'"
|
||||
|
|
@ -487,12 +482,15 @@ watch(
|
|||
name="eventDefinitionType"
|
||||
key="eventDefinitionType"
|
||||
>
|
||||
<Select v-model:value="listenerForm.eventDefinitionType">
|
||||
<SelectOption value="date">日期</SelectOption>
|
||||
<SelectOption value="duration">持续时长</SelectOption>
|
||||
<SelectOption value="cycle">循环</SelectOption>
|
||||
<SelectOption value="null">无</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="listenerForm.eventDefinitionType"
|
||||
:options="[
|
||||
{ label: '日期', value: 'date' },
|
||||
{ label: '持续时长', value: 'duration' },
|
||||
{ label: '循环', value: 'cycle' },
|
||||
{ label: '无', value: 'null' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
|
|||
|
|
@ -447,15 +447,12 @@ watch(
|
|||
<Select
|
||||
v-model:value="loopCharacteristics"
|
||||
@change="changeLoopCharacteristicsType"
|
||||
>
|
||||
<SelectOption value="ParallelMultiInstance">
|
||||
并行多重事件
|
||||
</SelectOption>
|
||||
<SelectOption value="SequentialMultiInstance">
|
||||
时序多重事件
|
||||
</SelectOption>
|
||||
<SelectOption value="Null">无</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
{ label: '并行多重事件', value: 'ParallelMultiInstance' },
|
||||
{ label: '时序多重事件', value: 'SequentialMultiInstance' },
|
||||
{ label: '无', value: 'Null' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<template
|
||||
v-if="
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import {
|
|||
FormItem,
|
||||
Input,
|
||||
Select,
|
||||
SelectOption,
|
||||
Switch,
|
||||
} from 'antdv-next';
|
||||
|
||||
|
|
@ -269,16 +268,9 @@ onMounted(async () => {
|
|||
placeholder="请选择子流程"
|
||||
allow-clear
|
||||
@change="handleChildProcessChange"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in childProcessOptions"
|
||||
:key="item.key"
|
||||
:value="item.key"
|
||||
:label="item.name"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="childProcessOptions"
|
||||
:field-names="{ label: 'name', value: 'key' }"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="继承变量">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { nextTick, onBeforeUnmount, onMounted, ref, toRaw, watch } from 'vue';
|
|||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { Button, message, Select, SelectOption } from 'antdv-next';
|
||||
import { Button, message, Select } from 'antdv-next';
|
||||
|
||||
import SignalMessageModal from '../../signal-message/SignalMessageModal.vue';
|
||||
|
||||
|
|
@ -118,17 +118,15 @@ watch(
|
|||
<span class="w-20 text-foreground">消息实例:</span>
|
||||
<Select
|
||||
v-model:value="bindMessageId"
|
||||
:options="
|
||||
Object.keys(messageMap).map((key) => ({
|
||||
label: messageMap[key],
|
||||
value: key,
|
||||
}))
|
||||
"
|
||||
class="w-full"
|
||||
@change="(value: any) => updateTaskMessage(value)"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="key in Object.keys(messageMap)"
|
||||
:key="key"
|
||||
:value="key"
|
||||
>
|
||||
{{ messageMap[key] }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</div>
|
||||
<Modal @confirm="handleConfirm" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { nextTick, onBeforeUnmount, ref, toRaw, watch } from 'vue';
|
||||
|
||||
import {
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
Select,
|
||||
SelectOption,
|
||||
TextArea,
|
||||
} from 'antdv-next';
|
||||
import { Form, FormItem, Input, Select, TextArea } from 'antdv-next';
|
||||
|
||||
defineOptions({ name: 'ScriptTask' });
|
||||
const props = defineProps({
|
||||
|
|
@ -86,10 +79,13 @@ watch(
|
|||
</FormItem>
|
||||
<!-- TODO scriptType 外部资源 和 内联脚本, flowable 文档 https://www.flowable.com/open-source/docs/bpmn/ch07b-BPMN-Constructs#script-task 没看到到有相应的属性 -->
|
||||
<FormItem label="脚本类型">
|
||||
<Select v-model:value="scriptTaskForm.scriptType">
|
||||
<SelectOption value="inline">内联脚本</SelectOption>
|
||||
<SelectOption value="external">外部资源</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="scriptTaskForm.scriptType"
|
||||
:options="[
|
||||
{ label: '内联脚本', value: 'inline' },
|
||||
{ label: '外部资源', value: 'external' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="脚本" v-show="scriptTaskForm.scriptType === 'inline'">
|
||||
<TextArea
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import {
|
|||
Form,
|
||||
FormItem,
|
||||
Select,
|
||||
SelectOption,
|
||||
TextArea,
|
||||
TreeSelect,
|
||||
} from 'antdv-next';
|
||||
|
|
@ -356,15 +355,8 @@ onBeforeUnmount(() => {
|
|||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="changeCandidateStrategy"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(dict, index) in CANDIDATE_STRATEGY"
|
||||
:key="index"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="CANDIDATE_STRATEGY"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.ROLE"
|
||||
|
|
@ -377,15 +369,9 @@ onBeforeUnmount(() => {
|
|||
mode="multiple"
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in roleOptions"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="roleOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -419,15 +405,9 @@ onBeforeUnmount(() => {
|
|||
mode="multiple"
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in postOptions"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="postOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.USER"
|
||||
|
|
@ -440,15 +420,9 @@ onBeforeUnmount(() => {
|
|||
mode="multiple"
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="userOptions"
|
||||
:field-names="{ label: 'nickname', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.USER_GROUP"
|
||||
|
|
@ -461,15 +435,9 @@ onBeforeUnmount(() => {
|
|||
mode="multiple"
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userGroupOptions"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="userGroupOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.FORM_USER"
|
||||
|
|
@ -478,19 +446,17 @@ onBeforeUnmount(() => {
|
|||
>
|
||||
<Select
|
||||
v-model:value="userTaskForm.candidateParam"
|
||||
:options="
|
||||
userFieldOnFormOptions.map((item) => ({
|
||||
label: item.title,
|
||||
value: item.field,
|
||||
disabled: !item.required,
|
||||
}))
|
||||
"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="handleFormUserChange"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in userFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
>
|
||||
{{ item.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -504,16 +470,14 @@ onBeforeUnmount(() => {
|
|||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in deptFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
>
|
||||
{{ item.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...deptFieldOnFormOptions.map((item) => ({
|
||||
label: item.title,
|
||||
value: item.field,
|
||||
disabled: !item.required,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -528,15 +492,12 @@ onBeforeUnmount(() => {
|
|||
:label="deptLevelLabel!"
|
||||
name="deptLevel"
|
||||
>
|
||||
<Select v-model:value="deptLevel" allow-clear @change="updateElementTask">
|
||||
<SelectOption
|
||||
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="deptLevel"
|
||||
allow-clear
|
||||
@change="updateElementTask"
|
||||
:options="MULTI_LEVEL_DEPT"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.EXPRESSION"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
Input,
|
||||
InputNumber,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
TabPane,
|
||||
Tabs,
|
||||
} from 'antdv-next';
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import {
|
|||
RadioGroup,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
Switch,
|
||||
} from 'antdv-next';
|
||||
|
||||
|
|
@ -432,15 +431,9 @@ onMounted(async () => {
|
|||
v-model:value="configForm.calledProcessDefinitionKey"
|
||||
allow-clear
|
||||
@change="handleCalledElementChange"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, index) in childProcessOptions"
|
||||
:key="index"
|
||||
:value="item.key"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="childProcessOptions"
|
||||
:field-names="{ label: 'name', value: 'key' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="是否自动跳过子流程发起节点"
|
||||
|
|
@ -464,41 +457,39 @@ onMounted(async () => {
|
|||
<div class="mr-2">
|
||||
<FormItem
|
||||
:name="['inVariables', index, 'source']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '变量不能为空',
|
||||
trigger: 'blur',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '变量不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Select class="!w-40" v-model:value="item.source">
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in formFieldOptions"
|
||||
:key="fIdx"
|
||||
:value="field.field"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
class="!w-40"
|
||||
v-model:value="item.source"
|
||||
:options="formFieldOptions"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<FormItem
|
||||
:name="['inVariables', index, 'target']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '变量不能为空',
|
||||
trigger: 'blur',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '变量不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Select class="!w-40" v-model:value="item.target">
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in childFormFieldOptions"
|
||||
:key="fIdx"
|
||||
:value="field.field"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
class="!w-40"
|
||||
v-model:value="item.target"
|
||||
:options="childFormFieldOptions"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
<div class="mr-1 flex h-8 items-center">
|
||||
|
|
@ -534,41 +525,39 @@ onMounted(async () => {
|
|||
<div class="mr-2">
|
||||
<FormItem
|
||||
:name="['outVariables', index, 'source']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '变量不能为空',
|
||||
trigger: 'blur',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '变量不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Select class="!w-40" v-model:value="item.source">
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in childFormFieldOptions"
|
||||
:key="fIdx"
|
||||
:value="field.field"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
class="!w-40"
|
||||
v-model:value="item.source"
|
||||
:options="childFormFieldOptions"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<FormItem
|
||||
:name="['outVariables', index, 'target']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '变量不能为空',
|
||||
trigger: 'blur',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '变量不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Select class="!w-40" v-model:value="item.target">
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in formFieldOptions"
|
||||
:key="fIdx"
|
||||
:value="field.field"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
class="!w-40"
|
||||
v-model:value="item.target"
|
||||
:options="formFieldOptions"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
<div class="mr-1 flex h-8 items-center">
|
||||
|
|
@ -609,16 +598,12 @@ onMounted(async () => {
|
|||
label="子流程发起人字段"
|
||||
name="startUserFormField"
|
||||
>
|
||||
<Select v-model:value="configForm.startUserFormField" allow-clear>
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in startUserFormFieldOptions"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.startUserFormField"
|
||||
allow-clear
|
||||
:options="startUserFormFieldOptions"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -682,16 +667,11 @@ onMounted(async () => {
|
|||
</FormItem>
|
||||
</Col>
|
||||
<Col>
|
||||
<Select v-model:value="configForm.timeUnit" class="w-24">
|
||||
<SelectOption
|
||||
v-for="item in TIME_UNIT_TYPES"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.timeUnit"
|
||||
class="w-24"
|
||||
:options="TIME_UNIT_TYPES"
|
||||
/>
|
||||
</Col>
|
||||
<Col>
|
||||
<span class="inline-flex h-8 items-center">后进入下一节点</span>
|
||||
|
|
@ -774,16 +754,8 @@ onMounted(async () => {
|
|||
<Select
|
||||
v-model:value="configForm.multiInstanceSourceType"
|
||||
@change="handleMultiInstanceSourceTypeChange"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -795,11 +767,13 @@ onMounted(async () => {
|
|||
label-align="left"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 12 }"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '固定数量不能为空',
|
||||
trigger: 'change',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '固定数量不能为空',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<InputNumber
|
||||
v-model:value="configForm.multiInstanceSource"
|
||||
|
|
@ -816,22 +790,19 @@ onMounted(async () => {
|
|||
label-align="left"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 12 }"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '数字表单字段不能为空',
|
||||
trigger: 'change',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '数字表单字段不能为空',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Select v-model:value="configForm.multiInstanceSource">
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in digitalFormFieldOptions"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.multiInstanceSource"
|
||||
:options="digitalFormFieldOptions"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -843,22 +814,19 @@ onMounted(async () => {
|
|||
label-align="left"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 12 }"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '多选表单字段不能为空',
|
||||
trigger: 'change',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '多选表单字段不能为空',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Select v-model:value="configForm.multiInstanceSource">
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in multiFormFieldOptions"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.multiInstanceSource"
|
||||
:options="multiFormFieldOptions"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import {
|
|||
RadioGroup,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
TabPane,
|
||||
Tabs,
|
||||
TextArea,
|
||||
|
|
@ -259,18 +258,11 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
|||
>
|
||||
<Select
|
||||
v-model:value="configForm.roleIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in roleOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="roleOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -306,18 +298,11 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
|||
>
|
||||
<Select
|
||||
v-model:value="configForm.postIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in postOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id!"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="postOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="configForm.candidateStrategy === CandidateStrategy.USER"
|
||||
|
|
@ -326,18 +311,11 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
|||
>
|
||||
<Select
|
||||
v-model:value="configForm.userIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="userOptions"
|
||||
:field-names="{ label: 'nickname', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -348,18 +326,11 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
|||
>
|
||||
<Select
|
||||
v-model:value="configForm.userGroups"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userGroupOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="userGroupOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -368,17 +339,17 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
|||
label="表单内用户字段"
|
||||
name="formUser"
|
||||
>
|
||||
<Select v-model:value="configForm.formUser" clearable>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in userFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
>
|
||||
{{ item.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.formUser"
|
||||
:options="
|
||||
userFieldOnFormOptions.map((item) => ({
|
||||
label: item.title,
|
||||
value: item.field,
|
||||
disabled: !item.required,
|
||||
}))
|
||||
"
|
||||
allow-clear
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -388,17 +359,17 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
|||
label="表单内部门字段"
|
||||
name="formDept"
|
||||
>
|
||||
<Select v-model:value="configForm.formDept" clearable>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in deptFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
>
|
||||
{{ item.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.formDept"
|
||||
allow-clear
|
||||
:options="[
|
||||
...deptFieldOnFormOptions.map((item) => ({
|
||||
label: item.title,
|
||||
value: item.field,
|
||||
disabled: !item.required,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -414,16 +385,11 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
|||
:label="deptLevelLabel!"
|
||||
name="deptLevel"
|
||||
>
|
||||
<Select v-model:value="configForm.deptLevel" clearable>
|
||||
<SelectOption
|
||||
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.deptLevel"
|
||||
allow-clear
|
||||
:options="MULTI_LEVEL_DEPT"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import {
|
|||
RadioGroup,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
} from 'antdv-next';
|
||||
|
||||
import {
|
||||
|
|
@ -207,15 +206,11 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
|
|||
</FormItem>
|
||||
</Col>
|
||||
<Col>
|
||||
<Select v-model:value="configForm.timeUnit" class="w-28">
|
||||
<SelectOption
|
||||
v-for="item in TIME_UNIT_TYPES"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.timeUnit"
|
||||
class="w-28"
|
||||
:options="TIME_UNIT_TYPES"
|
||||
/>
|
||||
</Col>
|
||||
<Col>
|
||||
<span class="inline-flex h-8 items-center">后进入下一节点</span>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -224,47 +236,39 @@ defineExpose({ validate });
|
|||
rIdx,
|
||||
'leftSide',
|
||||
]"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '左值不能为空',
|
||||
trigger: 'change',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '左值不能为空',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Select
|
||||
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>
|
||||
<Col :span="6">
|
||||
<Select v-model:value="rule.opCode" placeholder="请选择操作符">
|
||||
<SelectOption
|
||||
v-for="operator in COMPARISON_OPERATORS"
|
||||
:key="operator.value"
|
||||
:label="operator.label"
|
||||
:value="operator.value"
|
||||
>
|
||||
{{ operator.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="rule.opCode"
|
||||
placeholder="请选择操作符"
|
||||
:options="COMPARISON_OPERATORS"
|
||||
/>
|
||||
</Col>
|
||||
<Col :span="7">
|
||||
<FormItem
|
||||
|
|
@ -276,11 +280,13 @@ defineExpose({ validate });
|
|||
rIdx,
|
||||
'rightSide',
|
||||
]"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '右值不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '右值不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Input
|
||||
v-model:value="rule.rightSide"
|
||||
|
|
@ -293,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>
|
||||
|
|
|
|||
|
|
@ -3,15 +3,7 @@ import type { HttpRequestParam } from '../../../consts';
|
|||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
FormItem,
|
||||
Input,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
} from 'antdv-next';
|
||||
import { Button, Col, FormItem, Input, Row, Select } from 'antdv-next';
|
||||
|
||||
import {
|
||||
BPM_HTTP_REQUEST_PARAM_TYPES,
|
||||
|
|
@ -65,59 +57,59 @@ function deleteHttpRequestParam(arr: HttpRequestParam[], index: number) {
|
|||
<Col :span="7">
|
||||
<FormItem
|
||||
:name="[bind, 'header', index, 'key']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数名不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '参数名不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Input placeholder="参数名不能为空" v-model:value="item.key" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="5">
|
||||
<Select v-model:value="item.type">
|
||||
<SelectOption
|
||||
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
:key="types.value"
|
||||
:label="types.label"
|
||||
:value="types.value"
|
||||
>
|
||||
{{ types.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="item.type"
|
||||
:options="BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
/>
|
||||
</Col>
|
||||
<Col :span="10">
|
||||
<FormItem
|
||||
:name="[bind, 'header', index, 'value']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
|
||||
>
|
||||
<Input placeholder="请求头" v-model:value="item.value" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
:name="[bind, 'header', index, 'value']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: 'change',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
|
||||
>
|
||||
<Select v-model:value="item.value" placeholder="请选择表单字段">
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in formFieldOptions"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
:disabled="!field.required"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="item.value"
|
||||
:options="
|
||||
formFieldOptions.map((field) => ({
|
||||
label: field.title,
|
||||
value: field.field,
|
||||
disabled: !field.required,
|
||||
}))
|
||||
"
|
||||
placeholder="请选择表单字段"
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="2">
|
||||
|
|
@ -150,59 +142,59 @@ function deleteHttpRequestParam(arr: HttpRequestParam[], index: number) {
|
|||
<Col :span="7">
|
||||
<FormItem
|
||||
:name="[bind, 'body', index, 'key']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数名不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '参数名不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Input placeholder="参数名" v-model:value="item.key" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="5">
|
||||
<Select v-model:value="item.type">
|
||||
<SelectOption
|
||||
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
:key="types.value"
|
||||
:label="types.label"
|
||||
:value="types.value"
|
||||
>
|
||||
{{ types.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="item.type"
|
||||
:options="BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
/>
|
||||
</Col>
|
||||
<Col :span="10">
|
||||
<FormItem
|
||||
:name="[bind, 'body', index, 'value']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
|
||||
>
|
||||
<Input placeholder="参数值" v-model:value="item.value" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
:name="[bind, 'body', index, 'value']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: 'change',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
|
||||
>
|
||||
<Select v-model:value="item.value" placeholder="请选择表单字段">
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in formFieldOptions"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
:disabled="!field.required"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="item.value"
|
||||
:options="
|
||||
formFieldOptions.map((field) => ({
|
||||
label: field.title,
|
||||
value: field.field,
|
||||
disabled: !field.required,
|
||||
}))
|
||||
"
|
||||
placeholder="请选择表单字段"
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="2">
|
||||
|
|
|
|||
|
|
@ -3,16 +3,7 @@ import { toRefs, watch } from 'vue';
|
|||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Col,
|
||||
FormItem,
|
||||
Input,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
} from 'antdv-next';
|
||||
import { Alert, Button, Col, FormItem, Input, Row, Select } from 'antdv-next';
|
||||
|
||||
import { useFormFields } from '../../../helpers';
|
||||
import HttpRequestParamSetting from './http-request-param-setting.vue';
|
||||
|
|
@ -79,11 +70,13 @@ function deleteHttpResponseSetting(
|
|||
:label-col="{ span: 24 }"
|
||||
:wrapper-col="{ span: 24 }"
|
||||
:name="[formItemPrefix, 'url']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请求地址不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请求地址不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Input v-model:value="setting.url" placeholder="请输入请求地址" />
|
||||
</FormItem>
|
||||
|
|
@ -117,37 +110,38 @@ function deleteHttpResponseSetting(
|
|||
<Col :span="10">
|
||||
<FormItem
|
||||
:name="[formItemPrefix, 'response', index, 'key']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '表单字段不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '表单字段不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Select
|
||||
v-model:value="item.key"
|
||||
placeholder="请选择表单字段"
|
||||
allow-clear
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in formFields"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
:disabled="!field.required"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...formFields.map((field) => ({
|
||||
label: field.title,
|
||||
value: field.field,
|
||||
disabled: !field.required,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="12">
|
||||
<FormItem
|
||||
:name="[formItemPrefix, 'response', index, 'value']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请求返回字段不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请求返回字段不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Input v-model:value="item.value" placeholder="请求返回字段" />
|
||||
</FormItem>
|
||||
|
|
@ -157,7 +151,9 @@ function deleteHttpResponseSetting(
|
|||
<IconifyIcon
|
||||
class="size-4 cursor-pointer text-red-500"
|
||||
icon="lucide:trash-2"
|
||||
@click="deleteHttpResponseSetting(setting.response!, index)"
|
||||
@click="
|
||||
deleteHttpResponseSetting(setting.response!, Number(index))
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
|
|
|
|||
|
|
@ -90,11 +90,13 @@ defineExpose({ validate });
|
|||
<FormItem
|
||||
label="请求地址"
|
||||
:name="`task${listener.type}ListenerPath`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请求地址不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请求地址不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Input
|
||||
v-model:value="configForm[`task${listener.type}ListenerPath`]"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import {
|
|||
message,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
} from 'antdv-next';
|
||||
|
||||
import { ConditionType } from '../../consts';
|
||||
|
|
@ -237,25 +236,20 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
|
|||
<FormItem
|
||||
class="mb-0 ml-4 inline-block w-48"
|
||||
:name="['routerGroups', index, 'nodeId']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '路由目标节点不能为空',
|
||||
trigger: 'change',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '路由目标节点不能为空',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Select
|
||||
v-model:value="item.nodeId"
|
||||
placeholder="请选择路由目标节点"
|
||||
allow-clear
|
||||
>
|
||||
<SelectOption
|
||||
v-for="node in nodeOptions"
|
||||
:key="node.value"
|
||||
:value="node.value"
|
||||
>
|
||||
{{ node.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="nodeOptions"
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import {
|
|||
message,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
Tag,
|
||||
} from 'antdv-next';
|
||||
|
||||
|
|
@ -412,16 +411,11 @@ onMounted(() => {
|
|||
:rules="formRules"
|
||||
>
|
||||
<FormItem label="触发器类型" name="type">
|
||||
<Select v-model:value="configForm.type" @change="changeTriggerType">
|
||||
<SelectOption
|
||||
v-for="(item, index) in TRIGGER_TYPES"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.type"
|
||||
@change="changeTriggerType"
|
||||
:options="TRIGGER_TYPES"
|
||||
/>
|
||||
</FormItem>
|
||||
<!-- HTTP 请求触发器 -->
|
||||
<div
|
||||
|
|
@ -509,17 +503,9 @@ onMounted(() => {
|
|||
placeholder="请选择表单字段"
|
||||
:disabled="key !== ''"
|
||||
allow-clear
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(field, fIdx) in optionalUpdateFormFields"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
:disabled="field.disabled"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="optionalUpdateFormFields"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="4">
|
||||
|
|
@ -528,11 +514,13 @@ onMounted(() => {
|
|||
<Col :span="10">
|
||||
<FormItem
|
||||
:name="['formSettings', index, 'updateFormFields', key]"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '值不能为空',
|
||||
trigger: 'blur',
|
||||
}"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '值不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<Input
|
||||
v-model:value="formSetting.updateFormFields![key]"
|
||||
|
|
@ -651,16 +639,9 @@ onMounted(() => {
|
|||
mode="multiple"
|
||||
placeholder="请选择要删除的字段"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="field in formFields"
|
||||
:key="field.field"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
>
|
||||
{{ field.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="formFields"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import {
|
|||
RadioGroup,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
Switch,
|
||||
TabPane,
|
||||
Tabs,
|
||||
|
|
@ -148,6 +147,20 @@ const deptFieldOnFormOptions = computed(() => {
|
|||
return formFieldOptions.filter((item) => item.type === 'DeptSelect');
|
||||
});
|
||||
|
||||
const userFieldSelectOptions = computed(() =>
|
||||
userFieldOnFormOptions.value.map((item) => ({
|
||||
...item,
|
||||
disabled: !item.required,
|
||||
})),
|
||||
);
|
||||
|
||||
const deptFieldSelectOptions = computed(() =>
|
||||
deptFieldOnFormOptions.value.map((item) => ({
|
||||
...item,
|
||||
disabled: !item.required,
|
||||
})),
|
||||
);
|
||||
|
||||
// 操作按钮设置
|
||||
const {
|
||||
buttonsSetting,
|
||||
|
|
@ -697,18 +710,11 @@ onMounted(() => {
|
|||
>
|
||||
<Select
|
||||
v-model:value="configForm.roleIds"
|
||||
clearable
|
||||
:options="roleOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in roleOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -744,18 +750,11 @@ onMounted(() => {
|
|||
>
|
||||
<Select
|
||||
v-model:value="configForm.postIds"
|
||||
clearable
|
||||
:options="postOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in postOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id!"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="configForm.candidateStrategy === CandidateStrategy.USER"
|
||||
|
|
@ -764,18 +763,11 @@ onMounted(() => {
|
|||
>
|
||||
<Select
|
||||
v-model:value="configForm.userIds"
|
||||
clearable
|
||||
:options="userOptions"
|
||||
:field-names="{ label: 'nickname', value: 'id' }"
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -786,18 +778,11 @@ onMounted(() => {
|
|||
>
|
||||
<Select
|
||||
v-model:value="configForm.userGroups"
|
||||
clearable
|
||||
:options="userGroupOptions"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userGroupOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -806,17 +791,12 @@ onMounted(() => {
|
|||
label="表单内用户字段"
|
||||
name="formUser"
|
||||
>
|
||||
<Select v-model:value="configForm.formUser" clearable>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in userFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
>
|
||||
{{ item.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.formUser"
|
||||
:options="userFieldSelectOptions"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
allow-clear
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -826,17 +806,12 @@ onMounted(() => {
|
|||
label="表单内部门字段"
|
||||
name="formDept"
|
||||
>
|
||||
<Select v-model:value="configForm.formDept" clearable>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in deptFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
>
|
||||
{{ item.title }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.formDept"
|
||||
:options="deptFieldSelectOptions"
|
||||
:field-names="{ label: 'title', value: 'field' }"
|
||||
allow-clear
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -852,16 +827,11 @@ onMounted(() => {
|
|||
:label="deptLevelLabel!"
|
||||
name="deptLevel"
|
||||
>
|
||||
<Select v-model:value="configForm.deptLevel" clearable>
|
||||
<SelectOption
|
||||
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.deptLevel"
|
||||
:options="MULTI_LEVEL_DEPT"
|
||||
allow-clear
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
|
|
@ -943,16 +913,12 @@ onMounted(() => {
|
|||
label="驳回节点"
|
||||
name="returnNodeId"
|
||||
>
|
||||
<Select v-model:value="configForm.returnNodeId" clearable>
|
||||
<SelectOption
|
||||
v-for="item in returnTaskList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="configForm.returnNodeId"
|
||||
:options="returnTaskList"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
allow-clear
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
||||
|
|
@ -1021,19 +987,11 @@ onMounted(() => {
|
|||
<Col>
|
||||
<Select
|
||||
v-model:value="timeUnit"
|
||||
:options="TIME_UNIT_TYPES"
|
||||
class="mr-2"
|
||||
:style="{ width: '100px' }"
|
||||
@change="timeUnitChange"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in TIME_UNIT_TYPES"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
<TypographyText class="mr-2 mt-2 inline-flex text-sm">
|
||||
未处理
|
||||
</TypographyText>
|
||||
|
|
@ -1087,18 +1045,11 @@ onMounted(() => {
|
|||
>
|
||||
<Select
|
||||
v-model:value="configForm.assignEmptyHandlerUserIds"
|
||||
clearable
|
||||
:options="userOptions"
|
||||
:field-names="{ label: 'nickname', value: 'id' }"
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<div v-if="currentNode.type === BpmNodeTypeEnum.USER_TASK_NODE">
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ import {
|
|||
Avatar,
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Select,
|
||||
TextArea,
|
||||
Tooltip,
|
||||
|
|
@ -68,20 +70,18 @@ const rules: Record<string, Rule[]> = {
|
|||
key: [
|
||||
{ required: true, message: '流程标识不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (_rule: any, value: string, callback: any) => {
|
||||
validator: (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
callback();
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (!/^[a-z_][-\w.$]*$/i.test(value)) {
|
||||
callback(
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
'只能包含字母、数字、下划线、连字符和点号,且必须以字母或下划线开头',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
|
|
@ -281,16 +281,10 @@ defineExpose({ validate });
|
|||
class="w-full"
|
||||
v-model:value="modelData.category"
|
||||
allow-clear
|
||||
:field-names="{ label: 'name', value: 'code' }"
|
||||
:options="categoryList"
|
||||
placeholder="请选择流程分类"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="category in categoryList"
|
||||
:key="category.code"
|
||||
:value="category.code"
|
||||
>
|
||||
{{ category.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="流程图标">
|
||||
<ImageUpload v-model:value="modelData.icon" />
|
||||
|
|
@ -326,13 +320,14 @@ defineExpose({ validate });
|
|||
<FormItem label="谁可以发起" name="startUserType">
|
||||
<Select
|
||||
v-model:value="modelData.startUserType"
|
||||
:options="[
|
||||
{ label: '全员', value: 0 },
|
||||
{ label: '指定人员', value: 1 },
|
||||
{ label: '指定部门', value: 2 },
|
||||
]"
|
||||
placeholder="请选择谁可以发起"
|
||||
@change="handleStartUserTypeChange"
|
||||
>
|
||||
<SelectOption :value="0">全员</SelectOption>
|
||||
<SelectOption :value="1">指定人员</SelectOption>
|
||||
<SelectOption :value="2">指定部门</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
<div
|
||||
v-if="modelData.startUserType === 1"
|
||||
class="mt-2 flex flex-wrap gap-1"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
Button,
|
||||
Card,
|
||||
Collapse,
|
||||
CollapsePanel,
|
||||
Dropdown,
|
||||
Menu,
|
||||
MenuItem,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import {
|
|||
message,
|
||||
Popover,
|
||||
Select,
|
||||
SelectOption,
|
||||
Space,
|
||||
TextArea,
|
||||
} from 'antdv-next';
|
||||
|
|
@ -55,6 +54,7 @@ import {
|
|||
transferTask,
|
||||
} from '#/api/bpm/task';
|
||||
import { setConfAndFields2 } from '#/components/form-create';
|
||||
import { FileUpload } from '#/components/upload';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import Signature from './signature.vue';
|
||||
|
|
@ -120,6 +120,7 @@ const approveReasonForm: any = reactive({
|
|||
reason: '',
|
||||
signPicUrl: '',
|
||||
nextAssignees: {},
|
||||
attachments: [],
|
||||
});
|
||||
const approveReasonRule: Record<string, any> = computed(() => {
|
||||
return {
|
||||
|
|
@ -140,7 +141,8 @@ const approveReasonRule: Record<string, any> = computed(() => {
|
|||
});
|
||||
|
||||
const rejectFormRef = ref<FormInstance>();
|
||||
const rejectReasonForm = reactive({
|
||||
const rejectReasonForm = reactive<{ attachments: string[]; reason: string }>({
|
||||
attachments: [],
|
||||
reason: '',
|
||||
}); // 拒绝表单
|
||||
const rejectReasonRule: any = computed(() => {
|
||||
|
|
@ -290,6 +292,14 @@ function closePopover(type: string, formRef: any | FormInstance) {
|
|||
if (formRef) {
|
||||
formRef.resetFields();
|
||||
}
|
||||
if (type === 'approve') {
|
||||
approveReasonForm.reason = '';
|
||||
approveReasonForm.attachments = [];
|
||||
approveReasonForm.signPicUrl = '';
|
||||
} else if (type === 'reject') {
|
||||
rejectReasonForm.reason = '';
|
||||
rejectReasonForm.attachments = [];
|
||||
}
|
||||
if (popOverVisible.value[type]) popOverVisible.value[type] = false;
|
||||
nextAssigneesActivityNode.value = [];
|
||||
// 清理 Timeline 组件中的自定义审批人数据
|
||||
|
|
@ -401,6 +411,7 @@ async function handleAudit(pass: boolean, formRef: FormInstance | undefined) {
|
|||
const data = {
|
||||
id: runningTask.value.id,
|
||||
reason: approveReasonForm.reason,
|
||||
attachments: approveReasonForm.attachments,
|
||||
variables, // 审批通过, 把修改的字段值赋于流程实例变量
|
||||
nextAssignees: approveReasonForm.nextAssignees, // 下个自选节点选择的审批人信息
|
||||
} as any;
|
||||
|
|
@ -414,6 +425,9 @@ async function handleAudit(pass: boolean, formRef: FormInstance | undefined) {
|
|||
await formCreateApi.validate();
|
||||
}
|
||||
await approveTask(data);
|
||||
approveReasonForm.reason = '';
|
||||
approveReasonForm.attachments = [];
|
||||
approveReasonForm.signPicUrl = '';
|
||||
popOverVisible.value.approve = false;
|
||||
nextAssigneesActivityNode.value = [];
|
||||
// 清理 Timeline 组件中的自定义审批人数据
|
||||
|
|
@ -425,9 +439,12 @@ async function handleAudit(pass: boolean, formRef: FormInstance | undefined) {
|
|||
// 审批不通过数据
|
||||
const data = {
|
||||
id: runningTask.value.id,
|
||||
attachments: rejectReasonForm.attachments,
|
||||
reason: rejectReasonForm.reason,
|
||||
};
|
||||
await rejectTask(data);
|
||||
rejectReasonForm.reason = '';
|
||||
rejectReasonForm.attachments = [];
|
||||
popOverVisible.value.reject = false;
|
||||
message.success('审批不通过成功');
|
||||
}
|
||||
|
|
@ -748,6 +765,38 @@ function handleSignFinish(url: string) {
|
|||
approveFormRef.value?.validateFields(['signPicUrl']);
|
||||
}
|
||||
|
||||
/** 附件图片预览 */
|
||||
const imagePreviewOpen = ref(false);
|
||||
const imagePreviewUrl = ref('');
|
||||
|
||||
/** 判断文件是否为图片类型 */
|
||||
function isImageUrl(url: string) {
|
||||
return /\.(bmp|gif|jpe?g|png|svg|webp)$/i.test(url);
|
||||
}
|
||||
|
||||
/** 处理文件预览 */
|
||||
function handleFilePreview(file: any) {
|
||||
if (!file?.url && !file?.response) {
|
||||
message.warning('文件地址不存在,无法预览');
|
||||
return;
|
||||
}
|
||||
const url = file.url || file?.response?.url || file?.response;
|
||||
if (!url) {
|
||||
message.warning('文件地址不存在,无法预览');
|
||||
return;
|
||||
}
|
||||
if (isImageUrl(url)) {
|
||||
imagePreviewUrl.value = url;
|
||||
imagePreviewOpen.value = true;
|
||||
} else {
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
function handleImagePreviewOpenChange(open: boolean) {
|
||||
imagePreviewOpen.value = open;
|
||||
}
|
||||
|
||||
/** 处理弹窗可见性 */
|
||||
function handlePopoverVisible(visible: boolean) {
|
||||
if (!visible) {
|
||||
|
|
@ -843,6 +892,16 @@ defineExpose({ loadTodoTask });
|
|||
:rows="4"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="上传附件/图片" name="attachments">
|
||||
<FileUpload
|
||||
v-model:value="approveReasonForm.attachments"
|
||||
:max-number="10"
|
||||
:multiple="true"
|
||||
:show-download-icon="false"
|
||||
help-text="支持多文件/图片上传"
|
||||
@preview="handleFilePreview"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Space>
|
||||
<Button
|
||||
|
|
@ -900,6 +959,15 @@ defineExpose({ loadTodoTask });
|
|||
:rows="4"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="上传附件/图片" name="attachments">
|
||||
<FileUpload
|
||||
v-model:value="rejectReasonForm.attachments"
|
||||
:max-number="10"
|
||||
:multiple="true"
|
||||
help-text="支持多文件/图片上传"
|
||||
@preview="handleFilePreview"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button
|
||||
:disabled="formLoading"
|
||||
|
|
@ -956,16 +1024,9 @@ defineExpose({ loadTodoTask });
|
|||
mode="multiple"
|
||||
placeholder="请选择抄送人"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="userOptions"
|
||||
:field-names="{ label: 'nickname', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="抄送意见" name="copyReason">
|
||||
<TextArea
|
||||
|
|
@ -1026,16 +1087,9 @@ defineExpose({ loadTodoTask });
|
|||
v-model:value="transferForm.assigneeUserId"
|
||||
:allow-clear="true"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="userOptions"
|
||||
:field-names="{ label: 'nickname', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="审批意见" name="reason">
|
||||
<TextArea
|
||||
|
|
@ -1099,16 +1153,9 @@ defineExpose({ loadTodoTask });
|
|||
v-model:value="delegateForm.delegateUserId"
|
||||
:allow-clear="true"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="userOptions"
|
||||
:field-names="{ label: 'nickname', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="审批意见" name="reason">
|
||||
<TextArea
|
||||
|
|
@ -1173,16 +1220,9 @@ defineExpose({ loadTodoTask });
|
|||
:allow-clear="true"
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="userOptions"
|
||||
:field-names="{ label: 'nickname', value: 'id' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="审批意见" name="reason">
|
||||
<TextArea
|
||||
|
|
@ -1250,18 +1290,15 @@ defineExpose({ loadTodoTask });
|
|||
<FormItem label="减签人员" name="deleteSignTaskId">
|
||||
<Select
|
||||
v-model:value="deleteSignForm.deleteSignTaskId"
|
||||
:options="
|
||||
(runningTask.children as any[]).map((item) => ({
|
||||
label: getDeleteSignUserLabel(item),
|
||||
value: item.id,
|
||||
}))
|
||||
"
|
||||
:allow-clear="true"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in runningTask.children"
|
||||
:key="item.id"
|
||||
:label="getDeleteSignUserLabel(item)"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ getDeleteSignUserLabel(item) }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="审批意见" name="reason">
|
||||
<TextArea
|
||||
|
|
@ -1323,16 +1360,9 @@ defineExpose({ loadTodoTask });
|
|||
v-model:value="returnForm.targetTaskDefinitionKey"
|
||||
:allow-clear="true"
|
||||
style="width: 100%"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in returnList"
|
||||
:key="item.taskDefinitionKey"
|
||||
:label="item.name"
|
||||
:value="item.taskDefinitionKey"
|
||||
>
|
||||
{{ item.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="returnList"
|
||||
:field-names="{ label: 'name', value: 'taskDefinitionKey' }"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="退回理由" name="returnReason">
|
||||
<TextArea
|
||||
|
|
@ -1444,4 +1474,14 @@ defineExpose({ loadTodoTask });
|
|||
|
||||
<!-- 签名弹窗 -->
|
||||
<SignatureModal @success="handleSignFinish" />
|
||||
|
||||
<!-- 图片预览(隐藏的 Image 组件,仅用于附件预览弹窗) -->
|
||||
<Image
|
||||
:preview="{
|
||||
open: imagePreviewOpen,
|
||||
onOpenChange: handleImagePreviewOpenChange,
|
||||
}"
|
||||
:src="imagePreviewUrl"
|
||||
style="display: none"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -201,16 +201,25 @@ function shouldShowCustomUserSelect(
|
|||
);
|
||||
}
|
||||
|
||||
/** 判断是否需要显示审批意见 */
|
||||
function shouldShowApprovalReason(task: any, nodeType: BpmNodeTypeEnum) {
|
||||
/** 判断是否需要显示审批意见和附件 */
|
||||
function shouldShowReasonAndAttachment(task: any, nodeType: BpmNodeTypeEnum) {
|
||||
return (
|
||||
task.reason &&
|
||||
(task.reason || task.attachments?.length > 0) &&
|
||||
[BpmNodeTypeEnum.START_USER_NODE, BpmNodeTypeEnum.USER_TASK_NODE].includes(
|
||||
nodeType,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getAttachmentName(url: string) {
|
||||
return decodeURIComponent(url.slice(url.lastIndexOf('/') + 1));
|
||||
}
|
||||
|
||||
function isImageAttachment(url: string) {
|
||||
const ext = url.split('.').pop()?.toLowerCase();
|
||||
return ['bmp', 'gif', 'jpeg', 'jpg', 'png', 'webp'].includes(ext || '');
|
||||
}
|
||||
|
||||
/** 用户选择弹窗关闭 */
|
||||
function handleUserSelectClosed() {
|
||||
selectedUsers.value = [];
|
||||
|
|
@ -410,29 +419,74 @@ defineExpose({ setCustomApproveUsers, batchSetCustomApproveUsers });
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 审批意见和签名 -->
|
||||
<!-- 审批意见、附件和签名 -->
|
||||
<div
|
||||
v-if="shouldShowApprovalReason(task, activity.nodeType)"
|
||||
v-if="shouldShowReasonAndAttachment(task, activity.nodeType)"
|
||||
class="mt-1 w-full rounded-md bg-gray-100 p-2 text-sm text-gray-500"
|
||||
>
|
||||
审批意见:{{ task.reason }}
|
||||
</div>
|
||||
<div v-if="task.reason">审批意见:{{ task.reason }}</div>
|
||||
<div
|
||||
v-if="
|
||||
task.signPicUrl &&
|
||||
activity.nodeType === BpmNodeTypeEnum.USER_TASK_NODE
|
||||
"
|
||||
class="mt-1 flex w-full items-center rounded-md bg-gray-100 p-2 text-sm text-gray-500"
|
||||
v-if="(task.attachments?.length || 0) > 0"
|
||||
:class="{
|
||||
'mt-2 border-t border-dashed border-gray-300 pt-2':
|
||||
task.reason,
|
||||
}"
|
||||
>
|
||||
签名:
|
||||
<Image
|
||||
class="ml-2"
|
||||
:width="180"
|
||||
:height="60"
|
||||
:src="task.signPicUrl"
|
||||
:preview="{ src: task.signPicUrl }"
|
||||
/>
|
||||
<div class="mb-1 text-xs font-semibold text-gray-400">
|
||||
附件列表:
|
||||
</div>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<template
|
||||
v-for="(attachment, attachmentIndex) in task.attachments"
|
||||
:key="attachmentIndex"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<IconifyIcon
|
||||
:icon="
|
||||
isImageAttachment(attachment)
|
||||
? 'lucide:image'
|
||||
: 'lucide:file-text'
|
||||
"
|
||||
class="text-gray-400"
|
||||
/>
|
||||
<Image
|
||||
v-if="isImageAttachment(attachment)"
|
||||
:width="32"
|
||||
:height="32"
|
||||
class="rounded border border-solid border-gray-200 object-cover"
|
||||
:src="attachment"
|
||||
:preview="{ src: attachment }"
|
||||
/>
|
||||
<a
|
||||
v-else
|
||||
:href="attachment"
|
||||
target="_blank"
|
||||
class="max-w-[240px] truncate text-blue-500 hover:text-blue-600 hover:underline"
|
||||
:title="getAttachmentName(attachment)"
|
||||
>
|
||||
{{ getAttachmentName(attachment) }}
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
task.signPicUrl &&
|
||||
activity.nodeType === BpmNodeTypeEnum.USER_TASK_NODE
|
||||
"
|
||||
class="mt-1 flex w-full items-center rounded-md bg-gray-100 p-2 text-sm text-gray-500"
|
||||
>
|
||||
签名:
|
||||
<Image
|
||||
class="ml-2"
|
||||
:width="180"
|
||||
:height="60"
|
||||
:src="task.signPicUrl"
|
||||
:preview="{ src: task.signPicUrl }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 情况二:遍历每个审批节点下的【候选的】task 任务 -->
|
||||
|
|
|
|||
|
|
@ -90,15 +90,11 @@ function filterDictTypeOption(input: string, option: any) {
|
|||
|
||||
<!-- Java 类型 -->
|
||||
<template #javaType="{ row, column }">
|
||||
<Select v-model:value="row.javaType" style="width: 100%">
|
||||
<SelectOption
|
||||
v-for="option in column.params.options"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="row.javaType"
|
||||
style="width: 100%"
|
||||
:options="column.params.options"
|
||||
/>
|
||||
</template>
|
||||
<!-- Java 属性 -->
|
||||
<template #javaField="{ row }">
|
||||
|
|
@ -124,15 +120,11 @@ function filterDictTypeOption(input: string, option: any) {
|
|||
|
||||
<!-- 查询方式 -->
|
||||
<template #listOperationCondition="{ row, column }">
|
||||
<Select v-model:value="row.listOperationCondition" class="w-full">
|
||||
<SelectOption
|
||||
v-for="option in column.params.options"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="row.listOperationCondition"
|
||||
class="w-full"
|
||||
:options="column.params.options"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 允许空 -->
|
||||
|
|
@ -142,15 +134,11 @@ function filterDictTypeOption(input: string, option: any) {
|
|||
|
||||
<!-- 显示类型 -->
|
||||
<template #htmlType="{ row, column }">
|
||||
<Select v-model:value="row.htmlType" class="w-full">
|
||||
<SelectOption
|
||||
v-for="option in column.params.options"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="row.htmlType"
|
||||
class="w-full"
|
||||
:options="column.params.options"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 字典类型 -->
|
||||
|
|
@ -161,15 +149,9 @@ function filterDictTypeOption(input: string, option: any) {
|
|||
allow-clear
|
||||
show-search
|
||||
:filter-option="filterDictTypeOption"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="option in dictTypeOptions"
|
||||
:key="option.type"
|
||||
:value="option.type"
|
||||
>
|
||||
{{ option.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="dictTypeOptions"
|
||||
:field-names="{ label: 'name', value: 'type' }"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 示例 -->
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ const loading = ref(false);
|
|||
const fileTree = ref<FileNode[]>([]);
|
||||
const previewFiles = ref<InfraCodegenApi.CodegenPreview[]>([]);
|
||||
const activeKey = ref<string>('');
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
|
||||
/** 代码地图 */
|
||||
const codeMap = ref<Map<string, string>>(new Map<string, string>());
|
||||
|
|
@ -53,31 +54,35 @@ function removeCodeMapKey(targetKey: any) {
|
|||
/** 复制代码 */
|
||||
async function copyCode() {
|
||||
const { copy } = useClipboard();
|
||||
const file = previewFiles.value.find(
|
||||
(item) => item.filePath === activeKey.value,
|
||||
);
|
||||
const file = findPreviewFile(activeKey.value);
|
||||
if (file) {
|
||||
await copy(file.code);
|
||||
message.success('复制成功');
|
||||
}
|
||||
}
|
||||
|
||||
/** 文件节点点击事件 */
|
||||
function handleNodeClick(_: any[], e: any) {
|
||||
if (!e.node.isLeaf) {
|
||||
return;
|
||||
}
|
||||
|
||||
activeKey.value = e.node.key;
|
||||
const file = previewFiles.value.find((item) => {
|
||||
const list = activeKey.value.split('.');
|
||||
function findPreviewFile(fileKey: string) {
|
||||
return previewFiles.value.find((item) => {
|
||||
const list = fileKey.split('.');
|
||||
// 特殊处理 - 包合并
|
||||
if (list.length > 2) {
|
||||
const lang = list.pop();
|
||||
return item.filePath === `${list.join('/')}.${lang}`;
|
||||
}
|
||||
return item.filePath === activeKey.value;
|
||||
return item.filePath === fileKey;
|
||||
});
|
||||
}
|
||||
|
||||
/** 文件节点点击事件 */
|
||||
function handleNodeClick(_: any[], e: any) {
|
||||
if (!e.node.isLeaf) {
|
||||
selectedKeys.value = activeKey.value ? [activeKey.value] : [];
|
||||
return;
|
||||
}
|
||||
|
||||
activeKey.value = String(e.node.key);
|
||||
selectedKeys.value = [activeKey.value];
|
||||
const file = findPreviewFile(activeKey.value);
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -179,6 +184,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||
if (!isOpen) {
|
||||
// 关闭时清除代码视图缓存
|
||||
codeMap.value.clear();
|
||||
selectedKeys.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -197,6 +203,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||
fileTree.value = handleFiles(data);
|
||||
if (data.length > 0) {
|
||||
activeKey.value = data[0]?.filePath || '';
|
||||
selectedKeys.value = activeKey.value ? [activeKey.value] : [];
|
||||
const code = data[0]?.code || '';
|
||||
setCodeMap(activeKey.value, code);
|
||||
}
|
||||
|
|
@ -217,7 +224,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||
<DirectoryTree
|
||||
v-if="fileTree.length > 0"
|
||||
default-expand-all
|
||||
v-model:active-key="activeKey"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
@select="handleNodeClick"
|
||||
:tree-data="fileTree"
|
||||
/>
|
||||
|
|
@ -240,7 +247,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||
>
|
||||
<CodeEditor
|
||||
class="max-h-200"
|
||||
:value="codeMap.get(activeKey)"
|
||||
:value="codeMap.get(key)"
|
||||
mode="application/json"
|
||||
:readonly="true"
|
||||
:bordered="true"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
Pagination,
|
||||
|
|
@ -180,18 +181,12 @@ onMounted(() => {
|
|||
placeholder="请选择性别"
|
||||
allow-clear
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="dict in getDictOptions(
|
||||
DICT_TYPE.SYSTEM_USER_SEX,
|
||||
'number',
|
||||
)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number').map(
|
||||
(dict) => ({ label: dict.label, value: dict.value as any }),
|
||||
),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="创建时间" name="createTime">
|
||||
<RangePicker
|
||||
|
|
|
|||
|
|
@ -7,15 +7,9 @@ import { useVbenModal } from '@vben/common-ui';
|
|||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import {
|
||||
DatePicker,
|
||||
Form,
|
||||
Input,
|
||||
message,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
} from 'antdv-next';
|
||||
import { Form, FormItem, Input, message, Radio, RadioGroup } from 'antdv-next';
|
||||
|
||||
import { DatePicker } from '#/adapter/component/date-picker';
|
||||
import {
|
||||
createDemo01Contact,
|
||||
getDemo01Contact,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,14 @@ import {
|
|||
isEmpty,
|
||||
} from '@vben/utils';
|
||||
|
||||
import { Button, DateRangePicker, Form, Input, message } from 'antdv-next';
|
||||
import {
|
||||
Button,
|
||||
DateRangePicker,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
} from 'antdv-next';
|
||||
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { computed, ref } from 'vue';
|
|||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { Form, Input, message, TreeSelect } from 'antdv-next';
|
||||
import { Form, FormItem, Input, message, TreeSelect } from 'antdv-next';
|
||||
|
||||
import {
|
||||
createDemo02Category,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
Pagination,
|
||||
|
|
@ -191,18 +192,12 @@ onMounted(() => {
|
|||
placeholder="请选择性别"
|
||||
allow-clear
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="dict in getDictOptions(
|
||||
DICT_TYPE.SYSTEM_USER_SEX,
|
||||
'number',
|
||||
)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number').map(
|
||||
(dict) => ({ label: dict.label, value: dict.value as any }),
|
||||
),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="创建时间" name="createTime">
|
||||
<RangePicker
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { computed, ref } from 'vue';
|
|||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Form, Input, message } from 'antdv-next';
|
||||
import { Form, FormItem, Input, message } from 'antdv-next';
|
||||
|
||||
import {
|
||||
createDemo03Course,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { cloneDeep, formatDateTime, isEmpty } from '@vben/utils';
|
|||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
Pagination,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { computed, ref } from 'vue';
|
|||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Form, Input, message } from 'antdv-next';
|
||||
import { Form, FormItem, Input, message } from 'antdv-next';
|
||||
|
||||
import {
|
||||
createDemo03Grade,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { cloneDeep, formatDateTime, isEmpty } from '@vben/utils';
|
|||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
Pagination,
|
||||
|
|
|
|||
|
|
@ -7,15 +7,9 @@ import { useVbenModal } from '@vben/common-ui';
|
|||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import {
|
||||
DatePicker,
|
||||
Form,
|
||||
Input,
|
||||
message,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
} from 'antdv-next';
|
||||
import { Form, FormItem, Input, message, Radio, RadioGroup } from 'antdv-next';
|
||||
|
||||
import { DatePicker } from '#/adapter/component/date-picker';
|
||||
import {
|
||||
createDemo03Student,
|
||||
getDemo03Student,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
Pagination,
|
||||
|
|
@ -184,18 +185,12 @@ onMounted(() => {
|
|||
placeholder="请选择性别"
|
||||
allow-clear
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="dict in getDictOptions(
|
||||
DICT_TYPE.SYSTEM_USER_SEX,
|
||||
'number',
|
||||
)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number').map(
|
||||
(dict) => ({ label: dict.label, value: dict.value as any }),
|
||||
),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="创建时间" name="createTime">
|
||||
<RangePicker
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
|||
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { Form, Input } from 'antdv-next';
|
||||
import { Form, FormItem, Input } from 'antdv-next';
|
||||
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import { DICT_TYPE } from '@vben/constants';
|
|||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import {
|
||||
DatePicker,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
Radio,
|
||||
|
|
@ -17,6 +17,7 @@ import {
|
|||
Tabs,
|
||||
} from 'antdv-next';
|
||||
|
||||
import { DatePicker } from '#/adapter/component/date-picker';
|
||||
import {
|
||||
createDemo03Student,
|
||||
getDemo03Student,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
Pagination,
|
||||
|
|
@ -181,18 +182,12 @@ onMounted(() => {
|
|||
placeholder="请选择性别"
|
||||
allow-clear
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="dict in getDictOptions(
|
||||
DICT_TYPE.SYSTEM_USER_SEX,
|
||||
'number',
|
||||
)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number').map(
|
||||
(dict) => ({ label: dict.label, value: dict.value as any }),
|
||||
),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="创建时间" name="createTime">
|
||||
<RangePicker
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
|||
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { Form, Input } from 'antdv-next';
|
||||
import { Form, FormItem, Input } from 'antdv-next';
|
||||
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import { DICT_TYPE } from '@vben/constants';
|
|||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import {
|
||||
DatePicker,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
Radio,
|
||||
|
|
@ -17,6 +17,7 @@ import {
|
|||
Tabs,
|
||||
} from 'antdv-next';
|
||||
|
||||
import { DatePicker } from '#/adapter/component/date-picker';
|
||||
import {
|
||||
createDemo03Student,
|
||||
getDemo03Student,
|
||||
|
|
|
|||
|
|
@ -100,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('消息内容不能为空');
|
||||
|
|
@ -228,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
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { DICT_TYPE, IotDeviceMessageMethodEnum } from '@vben/constants';
|
|||
import { IconifyIcon } from '@vben/icons';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Button, Select, SelectOption, Space, Switch, Tag } from 'antdv-next';
|
||||
import { Button, Select, Space, Switch, Tag } from 'antdv-next';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getDeviceMessagePage } from '#/api/iot/device/device';
|
||||
|
|
@ -195,25 +195,18 @@ defineExpose({
|
|||
allow-clear
|
||||
placeholder="所有方法"
|
||||
style="width: 160px"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in methodOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="methodOptions"
|
||||
/>
|
||||
<Select
|
||||
v-model:value="queryParams.upstream"
|
||||
allow-clear
|
||||
placeholder="上行/下行"
|
||||
style="width: 160px"
|
||||
>
|
||||
<SelectOption label="上行" value="true">上行</SelectOption>
|
||||
<SelectOption label="下行" value="false">下行</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
{ label: '上行', value: 'true' },
|
||||
{ label: '下行', value: 'false' },
|
||||
]"
|
||||
/>
|
||||
<Space>
|
||||
<Button type="primary" @click="handleQuery">
|
||||
<IconifyIcon icon="ep:search" class="mr-[5px]" /> 搜索
|
||||
|
|
|
|||
|
|
@ -14,14 +14,7 @@ import {
|
|||
import { IconifyIcon } from '@vben/icons';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import {
|
||||
Button,
|
||||
DateRangePicker,
|
||||
Select,
|
||||
SelectOption,
|
||||
Space,
|
||||
Tag,
|
||||
} from 'antdv-next';
|
||||
import { Button, DateRangePicker, Select, Space, Tag } from 'antdv-next';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getDeviceMessagePairPage } from '#/api/iot/device/device';
|
||||
|
|
@ -217,15 +210,13 @@ defineExpose({
|
|||
allow-clear
|
||||
placeholder="请选择事件标识符"
|
||||
style="width: 240px"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="event in eventThingModels"
|
||||
:key="event.identifier"
|
||||
:value="event.identifier!"
|
||||
>
|
||||
{{ event.name }}({{ event.identifier }})
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...eventThingModels.map((event) => ({
|
||||
label: `${event.name}(${event.identifier})`,
|
||||
value: event.identifier!,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span>时间范围:</span>
|
||||
|
|
|
|||
|
|
@ -14,14 +14,7 @@ import {
|
|||
import { IconifyIcon } from '@vben/icons';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import {
|
||||
Button,
|
||||
DateRangePicker,
|
||||
Select,
|
||||
SelectOption,
|
||||
Space,
|
||||
Tag,
|
||||
} from 'antdv-next';
|
||||
import { Button, DateRangePicker, Select, Space, Tag } from 'antdv-next';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getDeviceMessagePairPage } from '#/api/iot/device/device';
|
||||
|
|
@ -231,15 +224,13 @@ defineExpose({
|
|||
allow-clear
|
||||
placeholder="请选择服务标识符"
|
||||
style="width: 240px"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="service in serviceThingModels"
|
||||
:key="service.identifier"
|
||||
:value="service.identifier!"
|
||||
>
|
||||
{{ service.name }}({{ service.identifier }})
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...serviceThingModels.map((service) => ({
|
||||
label: `${service.name}(${service.identifier})`,
|
||||
value: service.identifier!,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span>时间范围:</span>
|
||||
|
|
|
|||
|
|
@ -15,16 +15,7 @@ import { getDictOptions } from '@vben/hooks';
|
|||
import { IconifyIcon } from '@vben/icons';
|
||||
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Input,
|
||||
message,
|
||||
Select,
|
||||
SelectOption,
|
||||
Space,
|
||||
Tag,
|
||||
} from 'antdv-next';
|
||||
import { Button, Card, Input, message, Select, Space, Tag } from 'antdv-next';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
|
|
@ -292,15 +283,9 @@ onMounted(async () => {
|
|||
placeholder="请选择产品"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="product in products"
|
||||
:key="product.id"
|
||||
:value="product.id"
|
||||
>
|
||||
{{ product.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="products"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="queryParams.deviceName"
|
||||
placeholder="请输入 DeviceName"
|
||||
|
|
@ -317,49 +302,34 @@ onMounted(async () => {
|
|||
/>
|
||||
<Select
|
||||
v-model:value="queryParams.deviceType"
|
||||
:options="
|
||||
getDictOptions(DICT_TYPE.IOT_PRODUCT_DEVICE_TYPE, 'number').map(
|
||||
(dict) => ({ label: dict.label, value: dict.value as any }),
|
||||
)
|
||||
"
|
||||
placeholder="请选择设备类型"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="dict in getDictOptions(
|
||||
DICT_TYPE.IOT_PRODUCT_DEVICE_TYPE,
|
||||
'number',
|
||||
)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
<Select
|
||||
v-model:value="queryParams.status"
|
||||
placeholder="请选择设备状态"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="dict in getDictOptions(DICT_TYPE.IOT_DEVICE_STATE, 'number')"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="
|
||||
getDictOptions(DICT_TYPE.IOT_DEVICE_STATE, 'number').map(
|
||||
(dict) => ({ label: dict.label, value: dict.value as any }),
|
||||
)
|
||||
"
|
||||
/>
|
||||
<Select
|
||||
v-model:value="queryParams.groupId"
|
||||
placeholder="请选择设备分组"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="group in deviceGroups"
|
||||
:key="group.id"
|
||||
:value="group.id"
|
||||
>
|
||||
{{ group.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="deviceGroups"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
/>
|
||||
<Button type="primary" @click="handleSearch">
|
||||
<IconifyIcon icon="ant-design:search-outlined" class="mr-1" />
|
||||
{{ $t('common.search') }}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { computed, onMounted, ref, watch } from 'vue';
|
|||
import { isEmpty } from '@vben/utils';
|
||||
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { FormItem, Input, Select, SelectOption, TextArea } from 'antdv-next';
|
||||
import { FormItem, Input, Select, TextArea } from 'antdv-next';
|
||||
|
||||
import { IotDataSinkTypeEnum } from '#/api/iot/rule/data/sink';
|
||||
|
||||
|
|
@ -67,10 +67,14 @@ onMounted(() => {
|
|||
>
|
||||
<Input v-model:value="urlPath" placeholder="请输入请求地址">
|
||||
<template #addonBefore>
|
||||
<Select v-model:value="urlPrefix" class="w-[100px]">
|
||||
<SelectOption value="http://">http://</SelectOption>
|
||||
<SelectOption value="https://">https://</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="urlPrefix"
|
||||
class="w-[100px]"
|
||||
:options="[
|
||||
{ label: 'http://', value: 'http://' },
|
||||
{ label: 'https://', value: 'https://' },
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Input>
|
||||
</FormItem>
|
||||
|
|
@ -81,12 +85,16 @@ onMounted(() => {
|
|||
]"
|
||||
label="请求方法"
|
||||
>
|
||||
<Select v-model:value="config.method" placeholder="请选择请求方法">
|
||||
<SelectOption value="GET">GET</SelectOption>
|
||||
<SelectOption value="POST">POST</SelectOption>
|
||||
<SelectOption value="PUT">PUT</SelectOption>
|
||||
<SelectOption value="DELETE">DELETE</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="config.method"
|
||||
placeholder="请选择请求方法"
|
||||
:options="[
|
||||
{ label: 'GET', value: 'GET' },
|
||||
{ label: 'POST', value: 'POST' },
|
||||
{ label: 'PUT', value: 'PUT' },
|
||||
{ label: 'DELETE', value: 'DELETE' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="请求头">
|
||||
<KeyValueEditor v-model="config.headers" add-button-text="添加请求头" />
|
||||
|
|
|
|||
|
|
@ -4,14 +4,7 @@ import { onMounted } from 'vue';
|
|||
import { isEmpty } from '@vben/utils';
|
||||
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import {
|
||||
FormItem,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
SelectOption,
|
||||
Switch,
|
||||
} from 'antdv-next';
|
||||
import { FormItem, Input, InputNumber, Select, Switch } from 'antdv-next';
|
||||
|
||||
import { IotDataSinkTypeEnum } from '#/api/iot/rule/data/sink';
|
||||
|
||||
|
|
@ -120,10 +113,14 @@ onMounted(() => {
|
|||
]"
|
||||
label="数据格式"
|
||||
>
|
||||
<Select v-model:value="config.dataFormat" placeholder="请选择数据格式">
|
||||
<SelectOption value="JSON">JSON</SelectOption>
|
||||
<SelectOption value="BINARY">BINARY</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="config.dataFormat"
|
||||
placeholder="请选择数据格式"
|
||||
:options="[
|
||||
{ label: 'JSON', value: 'JSON' },
|
||||
{ label: 'BINARY', value: 'BINARY' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem :name="['config', 'heartbeatIntervalMs']" label="心跳间隔(ms)">
|
||||
<InputNumber
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import {
|
|||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
SelectOption,
|
||||
Switch,
|
||||
TextArea,
|
||||
} from 'antdv-next';
|
||||
|
|
@ -127,10 +126,14 @@ onMounted(() => {
|
|||
]"
|
||||
label="数据格式"
|
||||
>
|
||||
<Select v-model:value="config.dataFormat" placeholder="请选择数据格式">
|
||||
<SelectOption value="JSON">JSON</SelectOption>
|
||||
<SelectOption value="TEXT">TEXT</SelectOption>
|
||||
</Select>
|
||||
<Select
|
||||
v-model:value="config.dataFormat"
|
||||
placeholder="请选择数据格式"
|
||||
:options="[
|
||||
{ label: 'JSON', value: 'JSON' },
|
||||
{ label: 'TEXT', value: 'TEXT' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem :name="['config', 'reconnectIntervalMs']" label="重连间隔(ms)">
|
||||
<InputNumber
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { FormItem, Select, SelectOption } from 'antdv-next';
|
||||
import { FormItem, Select } from 'antdv-next';
|
||||
|
||||
import { getSimpleAlertConfigList } from '#/api/iot/alert/config';
|
||||
|
||||
|
|
@ -49,20 +49,15 @@ onMounted(() => {
|
|||
<FormItem label="告警配置" required>
|
||||
<Select
|
||||
v-model:value="localValue"
|
||||
:options="alertConfigs"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
placeholder="请选择告警配置"
|
||||
show-search
|
||||
allow-clear
|
||||
@change="handleChange"
|
||||
class="w-full"
|
||||
:loading="loading"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="config in alertConfigs"
|
||||
:key="config.id"
|
||||
:label="config.name"
|
||||
:value="config.id"
|
||||
/>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
} from '@vben/constants';
|
||||
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { Col, FormItem, Row, Select, SelectOption } from 'antdv-next';
|
||||
import { Col, FormItem, Row, Select } from 'antdv-next';
|
||||
|
||||
import ValueInput from '../inputs/value-input.vue';
|
||||
import DeviceSelector from '../selectors/device-selector.vue';
|
||||
|
|
@ -163,6 +163,7 @@ function handleOperatorChange() {
|
|||
<FormItem label="条件类型" required>
|
||||
<Select
|
||||
:value="condition.type"
|
||||
:options="getConditionTypeOptions()"
|
||||
@change="
|
||||
(value: any) => {
|
||||
updateConditionField('type', value);
|
||||
|
|
@ -171,15 +172,7 @@ function handleOperatorChange() {
|
|||
"
|
||||
placeholder="请选择条件类型"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="option in getConditionTypeOptions()"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
@ -225,18 +218,11 @@ function handleOperatorChange() {
|
|||
<FormItem label="操作符" required>
|
||||
<Select
|
||||
:value="condition.operator"
|
||||
:options="statusOperatorOptions"
|
||||
@change="(value: any) => updateConditionField('operator', value)"
|
||||
placeholder="请选择操作符"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="option in statusOperatorOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
|
||||
|
|
@ -245,18 +231,11 @@ function handleOperatorChange() {
|
|||
<FormItem label="设备状态" required>
|
||||
<Select
|
||||
:value="condition.param"
|
||||
:options="deviceStatusOptions"
|
||||
@change="(value: any) => updateConditionField('param', value)"
|
||||
placeholder="请选择设备状态"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="option in deviceStatusOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
} from '@vben/constants';
|
||||
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { Col, FormItem, Input, Row, Select, SelectOption } from 'antdv-next';
|
||||
import { Col, FormItem, Input, Row, Select } from 'antdv-next';
|
||||
|
||||
import JsonParamsInput from '../inputs/json-params-input.vue';
|
||||
import ValueInput from '../inputs/value-input.vue';
|
||||
|
|
@ -171,18 +171,11 @@ function handlePropertyChange(propertyInfo: any) {
|
|||
<FormItem label="触发事件类型" required>
|
||||
<Select
|
||||
:value="triggerType"
|
||||
:options="triggerTypeOptions"
|
||||
@change="(value: any) => handleTriggerTypeChange(value)"
|
||||
placeholder="请选择触发事件类型"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="option in triggerTypeOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<!-- 设备属性条件配置 -->
|
||||
|
|
@ -317,38 +310,31 @@ function handlePropertyChange(propertyInfo: any) {
|
|||
<FormItem label="操作符" required>
|
||||
<Select
|
||||
:value="condition.operator"
|
||||
:options="[
|
||||
{
|
||||
label:
|
||||
IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS
|
||||
.name,
|
||||
value:
|
||||
IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS
|
||||
.value,
|
||||
},
|
||||
]"
|
||||
@change="(value: any) => updateConditionField('operator', value)"
|
||||
placeholder="请选择操作符"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
:value="
|
||||
IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.value
|
||||
"
|
||||
>
|
||||
{{
|
||||
IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.name
|
||||
}}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="6">
|
||||
<FormItem label="参数" required>
|
||||
<Select
|
||||
:value="condition.value"
|
||||
:options="deviceStatusChangeOptions"
|
||||
@change="(value: any) => updateConditionField('value', value)"
|
||||
placeholder="请选择操作符"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="option in deviceStatusChangeOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import {
|
|||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
SelectOption,
|
||||
Tag,
|
||||
Tooltip,
|
||||
} from 'antdv-next';
|
||||
|
|
@ -209,10 +208,11 @@ watch(
|
|||
v-model:value="localValue"
|
||||
placeholder="请选择布尔值"
|
||||
class="w-full!"
|
||||
>
|
||||
<SelectOption value="true">真 (true)</SelectOption>
|
||||
<SelectOption value="false">假 (false)</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
{ label: '真 (true)', value: 'true' },
|
||||
{ label: '假 (false)', value: 'false' },
|
||||
]"
|
||||
/>
|
||||
|
||||
<!-- 枚举值选择 -->
|
||||
<Select
|
||||
|
|
@ -220,17 +220,10 @@ watch(
|
|||
propertyType === IoTDataSpecsDataTypeEnum.ENUM && enumOptions.length > 0
|
||||
"
|
||||
v-model:value="localValue"
|
||||
:options="enumOptions"
|
||||
placeholder="请选择枚举值"
|
||||
class="w-full!"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="option in enumOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
|
||||
<!-- 范围输入 (between 操作符) -->
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -11,15 +11,7 @@ import { IconifyIcon } from '@vben/icons';
|
|||
import { getStableObjectKey } from '@vben/utils';
|
||||
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Empty,
|
||||
FormItem,
|
||||
Select,
|
||||
SelectOption,
|
||||
Tag,
|
||||
} from 'antdv-next';
|
||||
import { Button, Card, Empty, FormItem, Select, Tag } from 'antdv-next';
|
||||
|
||||
import AlertConfig from '../configs/alert-config.vue';
|
||||
import DeviceControlConfig from '../configs/device-control-config.vue';
|
||||
|
|
@ -239,18 +231,11 @@ function onActionTypeChange(action: RuleSceneApi.Action, type: number) {
|
|||
<FormItem label="执行类型" required>
|
||||
<Select
|
||||
:value="action.type"
|
||||
:options="getActionTypeOptions()"
|
||||
@change="(value) => updateActionType(index, value as number)"
|
||||
placeholder="请选择执行类型"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="option in getActionTypeOptions()"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
<!-- 属性详情弹出层 -->
|
||||
|
|
|
|||
|
|
@ -122,23 +122,21 @@ function normalizeFormData(result: any): RuleSceneApi.SceneRule {
|
|||
}
|
||||
|
||||
/** 触发器校验 */
|
||||
function validateTriggers(_rule: any, value: any, callback: any) {
|
||||
function validateTriggers(_rule: any, value: any) {
|
||||
const error = validateSceneRuleTriggers(value);
|
||||
if (error) {
|
||||
callback(new Error(error));
|
||||
return;
|
||||
return Promise.reject(new Error(error));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/** 执行器校验 */
|
||||
function validateActions(_rule: any, value: any, callback: any) {
|
||||
function validateActions(_rule: any, value: any) {
|
||||
const error = validateSceneRuleActions(value);
|
||||
if (error) {
|
||||
callback(new Error(error));
|
||||
return;
|
||||
return Promise.reject(new Error(error));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const formRules = reactive({
|
||||
|
|
|
|||
|
|
@ -35,51 +35,44 @@ function deleteEnum(index: number) {
|
|||
}
|
||||
|
||||
/** 校验单项枚举值:必填、数字、不重复 */
|
||||
function validateEnumValue(_rule: any, value: any, callback: any) {
|
||||
function validateEnumValue(_rule: any, value: any) {
|
||||
if (isEmpty(value)) {
|
||||
callback(new Error('枚举值不能为空'));
|
||||
return;
|
||||
return Promise.reject(new Error('枚举值不能为空'));
|
||||
}
|
||||
if (Number.isNaN(Number(value))) {
|
||||
callback(new Error('枚举值必须是数字'));
|
||||
return;
|
||||
return Promise.reject(new Error('枚举值必须是数字'));
|
||||
}
|
||||
const sameCount = dataSpecsList.value.filter(
|
||||
(it) => it.value === value,
|
||||
).length;
|
||||
if (sameCount > 1) {
|
||||
callback(new Error('枚举值不能重复'));
|
||||
return;
|
||||
return Promise.reject(new Error('枚举值不能重复'));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/** 校验整个枚举列表:非空、无空项、无非法数字、无重复 */
|
||||
function validateEnumList(_rule: any, _value: any, callback: any) {
|
||||
function validateEnumList(_rule: any, _value: any) {
|
||||
if (isEmpty(dataSpecsList.value)) {
|
||||
callback(new Error('请至少添加一个枚举项'));
|
||||
return;
|
||||
return Promise.reject(new Error('请至少添加一个枚举项'));
|
||||
}
|
||||
const hasEmpty = dataSpecsList.value.some(
|
||||
(item) => isEmpty(item.value) || isEmpty(item.name),
|
||||
);
|
||||
if (hasEmpty) {
|
||||
callback(new Error('存在未填写的枚举值或描述'));
|
||||
return;
|
||||
return Promise.reject(new Error('存在未填写的枚举值或描述'));
|
||||
}
|
||||
const hasInvalidNumber = dataSpecsList.value.some((item) =>
|
||||
Number.isNaN(Number(item.value)),
|
||||
);
|
||||
if (hasInvalidNumber) {
|
||||
callback(new Error('存在非数字的枚举值'));
|
||||
return;
|
||||
return Promise.reject(new Error('存在非数字的枚举值'));
|
||||
}
|
||||
const values = dataSpecsList.value.map((item) => item.value);
|
||||
if (new Set(values).size !== values.length) {
|
||||
callback(new Error('存在重复的枚举值'));
|
||||
return;
|
||||
return Promise.reject(new Error('存在重复的枚举值'));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { DICT_TYPE } from '@vben/constants';
|
|||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { FormItem, Input, Select, SelectOption } from 'antdv-next';
|
||||
import { FormItem, Input, Select } from 'antdv-next';
|
||||
|
||||
const props = defineProps<{ modelValue: any }>();
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
|
@ -36,53 +36,46 @@ function unitChange(unitSpecs: any) {
|
|||
}
|
||||
|
||||
/** 校验最小值 */
|
||||
function validateMin(_rule: any, _value: any, callback: any) {
|
||||
function validateMin(_rule: any, _value: any) {
|
||||
const min = Number(dataSpecs.value.min);
|
||||
const max = Number(dataSpecs.value.max);
|
||||
if (Number.isNaN(min)) {
|
||||
callback(new Error('请输入有效的数值'));
|
||||
return;
|
||||
return Promise.reject(new Error('请输入有效的数值'));
|
||||
}
|
||||
if (!Number.isNaN(max) && min >= max) {
|
||||
callback(new Error('最小值必须小于最大值'));
|
||||
return;
|
||||
return Promise.reject(new Error('最小值必须小于最大值'));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/** 校验最大值 */
|
||||
function validateMax(_rule: any, _value: any, callback: any) {
|
||||
function validateMax(_rule: any, _value: any) {
|
||||
const min = Number(dataSpecs.value.min);
|
||||
const max = Number(dataSpecs.value.max);
|
||||
if (Number.isNaN(max)) {
|
||||
callback(new Error('请输入有效的数值'));
|
||||
return;
|
||||
return Promise.reject(new Error('请输入有效的数值'));
|
||||
}
|
||||
if (!Number.isNaN(min) && max <= min) {
|
||||
callback(new Error('最大值必须大于最小值'));
|
||||
return;
|
||||
return Promise.reject(new Error('最大值必须大于最小值'));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/** 校验步长 */
|
||||
function validateStep(_rule: any, _value: any, callback: any) {
|
||||
function validateStep(_rule: any, _value: any) {
|
||||
const step = Number(dataSpecs.value.step);
|
||||
if (Number.isNaN(step)) {
|
||||
callback(new Error('请输入有效的数值'));
|
||||
return;
|
||||
return Promise.reject(new Error('请输入有效的数值'));
|
||||
}
|
||||
if (step <= 0) {
|
||||
callback(new Error('步长必须大于 0'));
|
||||
return;
|
||||
return Promise.reject(new Error('步长必须大于 0'));
|
||||
}
|
||||
const min = Number(dataSpecs.value.min);
|
||||
const max = Number(dataSpecs.value.max);
|
||||
if (!Number.isNaN(min) && !Number.isNaN(max) && step > max - min) {
|
||||
callback(new Error('步长不能大于最大值与最小值的差值'));
|
||||
return;
|
||||
return Promise.reject(new Error('步长不能大于最大值与最小值的差值'));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -133,15 +126,13 @@ function validateStep(_rule: any, _value: any, callback: any) {
|
|||
placeholder="请选择单位"
|
||||
class="w-full"
|
||||
@change="unitChange"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, index) in unitOptions"
|
||||
:key="index"
|
||||
:value="`${item.label}-${item.value}`"
|
||||
>
|
||||
{{ `${item.label}-${item.value}` }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...unitOptions.map((item) => ({
|
||||
label: `{{ \`${item.label}-${item.value}\` }}`,
|
||||
value: `${item.label}-${item.value}`,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -33,12 +33,11 @@ const structFormRef = ref();
|
|||
const formData = ref<any>(buildEmptyFormData());
|
||||
|
||||
/** 校验结构体属性对象非空 */
|
||||
function validateStructSpecsList(_rule: any, _value: any, callback: any) {
|
||||
function validateStructSpecsList(_rule: any, _value: any) {
|
||||
if (isEmpty(dataSpecsList.value)) {
|
||||
callback(new Error('请至少添加一个结构体属性对象'));
|
||||
return;
|
||||
return Promise.reject(new Error('请至少添加一个结构体属性对象'));
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
|
|
|
|||
|
|
@ -14,14 +14,7 @@ import {
|
|||
import { isEmpty } from '@vben/utils';
|
||||
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import {
|
||||
FormItem,
|
||||
Input,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Select,
|
||||
SelectOption,
|
||||
} from 'antdv-next';
|
||||
import { FormItem, Input, Radio, RadioGroup, Select } from 'antdv-next';
|
||||
|
||||
import { ThingModelFormRules, validateBoolName } from '#/api/iot/thingmodel';
|
||||
|
||||
|
|
@ -118,16 +111,13 @@ if (!props.isStructDataSpecs && !props.isParams) {
|
|||
v-model:value="property.dataType"
|
||||
placeholder="请选择数据类型"
|
||||
@change="handleChange"
|
||||
>
|
||||
<!-- ARRAY 和 STRUCT 类型数据相互嵌套时,最多支持递归嵌套 2 层(父和子) -->
|
||||
<SelectOption
|
||||
v-for="option in dataTypeOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ `${option.value}(${option.label})` }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="[
|
||||
...dataTypeOptions.map((option) => ({
|
||||
label: `{{ \`${option.value}(${option.label})\` }}`,
|
||||
value: option.value,
|
||||
})),
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<!-- 数值型配置 -->
|
||||
<ThingModelNumberDataSpecs
|
||||
|
|
|
|||
|
|
@ -204,15 +204,9 @@ async function getAttributeOptions(propertyId: number) {
|
|||
@blur="handleInputConfirm(index, attribute.id)"
|
||||
@change="handleInputConfirm(index, attribute.id)"
|
||||
@keyup.enter="handleInputConfirm(index, attribute.id)"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item2 in attributeOptions"
|
||||
:key="item2.id"
|
||||
:value="item2.name"
|
||||
>
|
||||
{{ item2.name }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
:options="attributeOptions"
|
||||
:field-names="{ label: 'name', value: 'name' }"
|
||||
/>
|
||||
<Tag
|
||||
v-show="!inputVisible(index)"
|
||||
@click="showInput(index)"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { ref, watch } from 'vue';
|
|||
import { IconifyIcon } from '@vben/icons';
|
||||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import { Collapse } from 'antdv-next';
|
||||
import { Collapse, CollapsePanel } from 'antdv-next';
|
||||
import draggable from 'vuedraggable';
|
||||
|
||||
import { componentConfigs } from './mobile/index';
|
||||
|
|
|
|||
|
|
@ -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="默认颜色">
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { IconifyIcon } from '@vben/icons';
|
|||
import { useAccessStore } from '@vben/stores';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
|
||||
import { message, RadioGroup } from 'antdv-next';
|
||||
import { message, RadioButton, RadioGroup } from 'antdv-next';
|
||||
|
||||
import { updateDiyPageProperty } from '#/api/mall/promotion/diy/page';
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -54,8 +54,17 @@ function handleSearch(value: string) {
|
|||
}
|
||||
|
||||
/** 处理节点点击:支持点击同一节点取消选中 */
|
||||
function handleSelect(_selectedKeys: any[], info: any) {
|
||||
const row = info.node.dataRef as MesMdItemTypeApi.ItemType;
|
||||
function handleSelect(selectedNodeKeys: any[], info: any) {
|
||||
const selectedKey = selectedNodeKeys[0] ?? info.node?.id ?? info.node?.key;
|
||||
const row = itemTypeList.value.find(
|
||||
(item) => String(item.id) === String(selectedKey),
|
||||
);
|
||||
if (!row) {
|
||||
currentNodeId.value = undefined;
|
||||
selectedKeys.value = [];
|
||||
emit('nodeClick', undefined);
|
||||
return;
|
||||
}
|
||||
if (currentNodeId.value === row.id) {
|
||||
currentNodeId.value = undefined;
|
||||
selectedKeys.value = [];
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue