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