fix(vben): 修复多端迁移差异与组件适配问题
- 同步 IoT 物模型 Event dataType 类型,并修复 antdv-next 模拟器事件类型展示 - 修复 antdv-next 物模型选项模板字符串和校验规则写法 - 补齐 antdv-next BPM 审批附件上传限制与流程打印业务表单渲染 - 修复 antdv-next 缺失的组件导入、垂直按钮组导出,并删除未引用的部门树遗留文件 - 对齐 BPM 流程配置中 antd/antdv-next 的组件属性写法 - 修复 ele 流程创建跳转、好友拒绝文本框、客服回车发送、公告栏颜色表单项和部门选择弹窗选中状态 - 调整 BPM 流程实例 API 字段顺序,减少三端无意义差异
This commit is contained in:
@@ -52,6 +52,7 @@ export namespace ThingModelApi {
|
||||
name?: string;
|
||||
required?: boolean;
|
||||
type?: string;
|
||||
dataType?: string;
|
||||
description?: string;
|
||||
outputParams?: Param[];
|
||||
method?: string;
|
||||
|
||||
@@ -264,7 +264,6 @@ watch(
|
||||
class="mr-2 mt-0.5"
|
||||
v-model:value="timeDuration"
|
||||
:min="1"
|
||||
controls-position="right"
|
||||
@change="
|
||||
() => {
|
||||
updateTimeModdle();
|
||||
|
||||
@@ -687,7 +687,6 @@ onMounted(async () => {
|
||||
class="w-24"
|
||||
v-model:value="configForm.timeDuration"
|
||||
:min="1"
|
||||
controls-position="right"
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
|
||||
@@ -264,7 +264,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
>
|
||||
<Select
|
||||
v-model:value="configForm.roleIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
@@ -311,7 +311,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
>
|
||||
<Select
|
||||
v-model:value="configForm.postIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
@@ -331,7 +331,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
>
|
||||
<Select
|
||||
v-model:value="configForm.userIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
@@ -353,7 +353,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
>
|
||||
<Select
|
||||
v-model:value="configForm.userGroups"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
@@ -373,7 +373,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
label="表单内用户字段"
|
||||
name="formUser"
|
||||
>
|
||||
<Select v-model:value="configForm.formUser" clearable>
|
||||
<Select v-model:value="configForm.formUser" allow-clear>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in userFieldOnFormOptions"
|
||||
:key="idx"
|
||||
@@ -393,7 +393,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
label="表单内部门字段"
|
||||
name="formDept"
|
||||
>
|
||||
<Select v-model:value="configForm.formDept" clearable>
|
||||
<Select v-model:value="configForm.formDept" allow-clear>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in deptFieldOnFormOptions"
|
||||
:key="idx"
|
||||
@@ -419,7 +419,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
:label="deptLevelLabel!"
|
||||
name="deptLevel"
|
||||
>
|
||||
<Select v-model:value="configForm.deptLevel" clearable>
|
||||
<Select v-model:value="configForm.deptLevel" allow-clear>
|
||||
<SelectOption
|
||||
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
||||
:key="index"
|
||||
@@ -437,7 +437,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
label="流程表达式"
|
||||
name="expression"
|
||||
>
|
||||
<Textarea v-model:value="configForm.expression" clearable />
|
||||
<Textarea v-model:value="configForm.expression" allow-clear />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
@@ -698,7 +698,7 @@ onMounted(() => {
|
||||
>
|
||||
<Select
|
||||
v-model:value="configForm.roleIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
@@ -745,7 +745,7 @@ onMounted(() => {
|
||||
>
|
||||
<Select
|
||||
v-model:value="configForm.postIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
@@ -765,7 +765,7 @@ onMounted(() => {
|
||||
>
|
||||
<Select
|
||||
v-model:value="configForm.userIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
@@ -787,7 +787,7 @@ onMounted(() => {
|
||||
>
|
||||
<Select
|
||||
v-model:value="configForm.userGroups"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
@@ -807,7 +807,7 @@ onMounted(() => {
|
||||
label="表单内用户字段"
|
||||
name="formUser"
|
||||
>
|
||||
<Select v-model:value="configForm.formUser" clearable>
|
||||
<Select v-model:value="configForm.formUser" allow-clear>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in userFieldOnFormOptions"
|
||||
:key="idx"
|
||||
@@ -827,7 +827,7 @@ onMounted(() => {
|
||||
label="表单内部门字段"
|
||||
name="formDept"
|
||||
>
|
||||
<Select v-model:value="configForm.formDept" clearable>
|
||||
<Select v-model:value="configForm.formDept" allow-clear>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in deptFieldOnFormOptions"
|
||||
:key="idx"
|
||||
@@ -853,7 +853,7 @@ onMounted(() => {
|
||||
:label="deptLevelLabel!"
|
||||
name="deptLevel"
|
||||
>
|
||||
<Select v-model:value="configForm.deptLevel" clearable>
|
||||
<Select v-model:value="configForm.deptLevel" allow-clear>
|
||||
<SelectOption
|
||||
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
||||
:key="index"
|
||||
@@ -944,7 +944,7 @@ onMounted(() => {
|
||||
label="驳回节点"
|
||||
name="returnNodeId"
|
||||
>
|
||||
<Select v-model:value="configForm.returnNodeId" clearable>
|
||||
<Select v-model:value="configForm.returnNodeId" allow-clear>
|
||||
<SelectOption
|
||||
v-for="item in returnTaskList"
|
||||
:key="item.id"
|
||||
@@ -1015,7 +1015,6 @@ onMounted(() => {
|
||||
class="mr-2 mt-0.5"
|
||||
v-model:value="configForm.timeDuration"
|
||||
:min="1"
|
||||
controls-position="right"
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
@@ -1088,7 +1087,7 @@ onMounted(() => {
|
||||
>
|
||||
<Select
|
||||
v-model:value="configForm.assignEmptyHandlerUserIds"
|
||||
clearable
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
>
|
||||
<SelectOption
|
||||
|
||||
@@ -79,11 +79,11 @@ export namespace BpmProcessInstanceApi {
|
||||
export interface ApprovalTaskInfo {
|
||||
id: number;
|
||||
assigneeUser: User;
|
||||
attachments?: string[];
|
||||
ownerUser: User;
|
||||
reason: string;
|
||||
signPicUrl: string;
|
||||
status: number;
|
||||
attachments?: string[];
|
||||
}
|
||||
|
||||
/** 抄送流程实例 */
|
||||
|
||||
@@ -54,6 +54,7 @@ export namespace ThingModelApi {
|
||||
name?: string;
|
||||
required?: boolean;
|
||||
type?: string;
|
||||
dataType?: string;
|
||||
description?: string;
|
||||
outputParams?: Param[];
|
||||
method?: string;
|
||||
@@ -113,37 +114,41 @@ export namespace ThingModelApi {
|
||||
|
||||
/** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */
|
||||
function buildRequiredNumberValidator(label: string) {
|
||||
return (_rule: any, value: any) => {
|
||||
return (_rule: any, value: any, callback: any) => {
|
||||
if (isEmpty(value)) {
|
||||
return Promise.reject(new Error(`${label}不能为空`));
|
||||
callback(new Error(`${label}不能为空`));
|
||||
return;
|
||||
}
|
||||
if (Number.isNaN(Number(value))) {
|
||||
return Promise.reject(new Error(`${label}必须是数字`));
|
||||
callback(new Error(`${label}必须是数字`));
|
||||
return;
|
||||
}
|
||||
return Promise.resolve();
|
||||
callback();
|
||||
};
|
||||
}
|
||||
|
||||
/** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */
|
||||
export function buildIdentifierLikeNameValidator(label: string) {
|
||||
return (_rule: any, value: string) => {
|
||||
return (_rule: any, value: string, callback: any) => {
|
||||
if (isEmpty(value)) {
|
||||
return Promise.reject(new Error(`${label}不能为空`));
|
||||
callback(new Error(`${label}不能为空`));
|
||||
return;
|
||||
}
|
||||
if (!/^[一-龥A-Za-z0-9]/.test(value)) {
|
||||
return Promise.reject(
|
||||
new Error(`${label}必须以中文、英文字母或数字开头`),
|
||||
);
|
||||
callback(new Error(`${label}必须以中文、英文字母或数字开头`));
|
||||
return;
|
||||
}
|
||||
if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) {
|
||||
return Promise.reject(
|
||||
callback(
|
||||
new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (value.length > 20) {
|
||||
return Promise.reject(new Error(`${label}长度不能超过 20 个字符`));
|
||||
callback(new Error(`${label}长度不能超过 20 个字符`));
|
||||
return;
|
||||
}
|
||||
return Promise.resolve();
|
||||
callback();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -167,7 +172,7 @@ export const ThingModelFormRules: Record<string, FormItemRule[]> = {
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
validator: (_rule: any, value: string) => {
|
||||
validator: (_rule: any, value: string, callback: any) => {
|
||||
const reservedKeywords = [
|
||||
'set',
|
||||
'get',
|
||||
@@ -178,16 +183,18 @@ export const ThingModelFormRules: Record<string, FormItemRule[]> = {
|
||||
'value',
|
||||
];
|
||||
if (reservedKeywords.includes(value)) {
|
||||
return Promise.reject(
|
||||
callback(
|
||||
new Error(
|
||||
'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (/^\d+$/.test(value)) {
|
||||
return Promise.reject(new Error('标识符不能是纯数字'));
|
||||
callback(new Error('标识符不能是纯数字'));
|
||||
return;
|
||||
}
|
||||
return Promise.resolve();
|
||||
callback();
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
|
||||
@@ -263,7 +263,6 @@ watch(
|
||||
class="mr-2 mt-0.5"
|
||||
v-model:value="timeDuration"
|
||||
:min="1"
|
||||
controls-position="right"
|
||||
@change="
|
||||
() => {
|
||||
updateTimeModdle();
|
||||
|
||||
@@ -673,7 +673,6 @@ onMounted(async () => {
|
||||
class="w-24"
|
||||
v-model:value="configForm.timeDuration"
|
||||
:min="1"
|
||||
controls-position="right"
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
|
||||
@@ -402,7 +402,7 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||
label="流程表达式"
|
||||
name="expression"
|
||||
>
|
||||
<TextArea v-model:value="configForm.expression" clearable />
|
||||
<TextArea v-model:value="configForm.expression" allow-clear />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
@@ -980,7 +980,6 @@ onMounted(() => {
|
||||
class="mr-2 mt-0.5"
|
||||
v-model:value="configForm.timeDuration"
|
||||
:min="1"
|
||||
controls-position="right"
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
|
||||
@@ -94,6 +94,24 @@ const popOverVisible: any = ref({
|
||||
deleteSign: false,
|
||||
}); // 气泡卡是否展示
|
||||
const returnList = ref([] as any); // 退回节点
|
||||
const APPROVAL_ATTACHMENT_FILE_TYPES = [
|
||||
'doc',
|
||||
'docx',
|
||||
'xls',
|
||||
'xlsx',
|
||||
'ppt',
|
||||
'pptx',
|
||||
'txt',
|
||||
'pdf',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'png',
|
||||
'gif',
|
||||
'bmp',
|
||||
'webp',
|
||||
];
|
||||
const APPROVAL_ATTACHMENT_FILE_SIZE = 5;
|
||||
const APPROVAL_ATTACHMENT_DIRECTORY = 'bpm/task-attachment';
|
||||
|
||||
/** 创建流程表达式 */
|
||||
function openSignatureModal() {
|
||||
@@ -897,8 +915,12 @@ defineExpose({ loadTodoTask });
|
||||
<FormItem label="上传附件/图片" name="attachments">
|
||||
<FileUpload
|
||||
v-model:value="approveReasonForm.attachments"
|
||||
:accept="APPROVAL_ATTACHMENT_FILE_TYPES"
|
||||
:directory="APPROVAL_ATTACHMENT_DIRECTORY"
|
||||
:max-number="10"
|
||||
:max-size="APPROVAL_ATTACHMENT_FILE_SIZE"
|
||||
:multiple="true"
|
||||
:show-description="true"
|
||||
:show-download-icon="false"
|
||||
help-text="支持多文件/图片上传"
|
||||
@preview="handleFilePreview"
|
||||
@@ -964,8 +986,12 @@ defineExpose({ loadTodoTask });
|
||||
<FormItem label="上传附件/图片" name="attachments">
|
||||
<FileUpload
|
||||
v-model:value="rejectReasonForm.attachments"
|
||||
:accept="APPROVAL_ATTACHMENT_FILE_TYPES"
|
||||
:directory="APPROVAL_ATTACHMENT_DIRECTORY"
|
||||
:max-number="10"
|
||||
:max-size="APPROVAL_ATTACHMENT_FILE_SIZE"
|
||||
:multiple="true"
|
||||
:show-description="true"
|
||||
help-text="支持多文件/图片上传"
|
||||
@preview="handleFilePreview"
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
|
||||
import type { SystemAreaApi } from '#/api/system/area';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref, shallowRef } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
@@ -19,6 +19,7 @@ import { getAreaTree } from '#/api/system/area';
|
||||
import { getSimpleDeptList } from '#/api/system/dept';
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { decodeFields } from '#/components/form-create';
|
||||
import { registerComponent } from '#/utils';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
@@ -54,6 +55,7 @@ const userName = computed(() => userStore.userInfo?.nickname ?? '');
|
||||
const printTime = ref(formatDate(new Date(), 'YYYY-MM-DD HH:mm'));
|
||||
const formFields = ref<FormFieldItem[]>([]);
|
||||
const printDataMap = ref<Record<string, string>>({});
|
||||
const BusinessFormComponent = shallowRef<any>();
|
||||
|
||||
/** 打印配置 */
|
||||
const printObj = ref({
|
||||
@@ -93,6 +95,17 @@ async function fetchPrintData(id: string) {
|
||||
printTime.value = formatDate(new Date(), 'YYYY-MM-DD HH:mm');
|
||||
initPrintDataMap();
|
||||
await parseFormFields();
|
||||
initBusinessFormComponent();
|
||||
}
|
||||
|
||||
/** 初始化业务表单组件 */
|
||||
function initBusinessFormComponent() {
|
||||
const businessFormPath =
|
||||
printData.value?.processInstance.processDefinition?.formCustomViewPath ||
|
||||
'';
|
||||
BusinessFormComponent.value = businessFormPath
|
||||
? registerComponent(businessFormPath)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/** 解析表单字段 */
|
||||
@@ -595,6 +608,22 @@ function getPrintTemplateHTML() {
|
||||
<div v-html="item.html"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- 业务表单:独立成块渲染,不嵌入表格单元格,避免宽度与分页受限 -->
|
||||
<div
|
||||
v-if="BusinessFormComponent && formFields.length === 0"
|
||||
class="mt-3"
|
||||
>
|
||||
<component
|
||||
:is="BusinessFormComponent"
|
||||
:id="printData.processInstance.businessKey"
|
||||
:readonly="true"
|
||||
:print-mode="true"
|
||||
/>
|
||||
</div>
|
||||
<table class="mt-3 w-full border-collapse">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
class="w-full border border-black p-1.5 text-center"
|
||||
|
||||
@@ -587,7 +587,7 @@ watch([activeTab, upstreamTab, downstreamTab], () => {
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dataType'">
|
||||
{{ record.dataType ?? '-' }}
|
||||
{{ record.event?.dataType ?? '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'dataDefinition'">
|
||||
<DataDefinition :data="record" />
|
||||
|
||||
@@ -128,7 +128,7 @@ function validateStep(_rule: any, _value: any) {
|
||||
@change="unitChange"
|
||||
:options="[
|
||||
...unitOptions.map((item) => ({
|
||||
label: `{{ \`${item.label}-${item.value}\` }}`,
|
||||
label: `${item.label}-${item.value}`,
|
||||
value: `${item.label}-${item.value}`,
|
||||
})),
|
||||
]"
|
||||
|
||||
@@ -113,7 +113,7 @@ if (!props.isStructDataSpecs && !props.isParams) {
|
||||
@change="handleChange"
|
||||
:options="[
|
||||
...dataTypeOptions.map((option) => ({
|
||||
label: `{{ \`${option.value}(${option.label})\` }}`,
|
||||
label: `${option.value}(${option.label})`,
|
||||
value: option.value,
|
||||
})),
|
||||
]"
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { Button } from 'antdv-next';
|
||||
|
||||
import { VerticalSpace } from '#/views/mall/promotion/components';
|
||||
import { VerticalButtonGroup } from '#/views/mall/promotion/components';
|
||||
|
||||
import { components } from './mobile';
|
||||
|
||||
@@ -109,7 +109,7 @@ const handleDeleteComponent = () => {
|
||||
class="component-toolbar"
|
||||
v-if="showToolbar && component.name && active"
|
||||
>
|
||||
<VerticalSpace size="small">
|
||||
<VerticalButtonGroup size="small">
|
||||
<Button
|
||||
:disabled="!canMoveUp"
|
||||
type="primary"
|
||||
@@ -164,7 +164,7 @@ const handleDeleteComponent = () => {
|
||||
>
|
||||
<IconifyIcon icon="lucide:trash-2" />
|
||||
</Button>
|
||||
</VerticalSpace>
|
||||
</VerticalButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { cloneDeep, isEmpty, isString } from '@vben/utils';
|
||||
|
||||
import { Button, Card, Col, QRCode, Row, Tag, Tooltip } from 'antdv-next';
|
||||
import { Button, Card, Col, QRCode, Row, Space, Tag, Tooltip } from 'antdv-next';
|
||||
import draggable from 'vuedraggable';
|
||||
|
||||
import statusBarImg from '#/assets/imgs/diy/statusBar.png';
|
||||
|
||||
@@ -7,4 +7,4 @@ export { default as Draggable } from './draggable/index.vue';
|
||||
export { default as InputWithColor } from './input-with-color/index.vue';
|
||||
|
||||
export { default as MagicCubeEditor } from './magic-cube-editor/index.vue';
|
||||
export { default as VerticalSpace } from './vertical-button-group/index.vue';
|
||||
export { default as VerticalButtonGroup } from './vertical-button-group/index.vue';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { SpaceCompact } from 'antdv-next';
|
||||
import { Space, SpaceCompact } from 'antdv-next';
|
||||
|
||||
/**
|
||||
* 垂直按钮组
|
||||
* Ant Design Vue 的按钮组,通过 Space 实现垂直布局
|
||||
*/
|
||||
defineOptions({ name: 'VerticalSpace' });
|
||||
defineOptions({ name: 'VerticalButtonGroup' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -6,7 +6,16 @@ import { computed, ref } from 'vue';
|
||||
import { confirm } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { Button, Col, Input, Layout, Row, TextArea } from 'antdv-next';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Input,
|
||||
Layout,
|
||||
LayoutContent,
|
||||
LayoutSider,
|
||||
Row,
|
||||
TextArea,
|
||||
} from 'antdv-next';
|
||||
|
||||
import { createEmptyNewsItem } from '#/api/mp/draft';
|
||||
import { Tinymce as RichTextArea } from '#/components/tinymce';
|
||||
|
||||
@@ -13,7 +13,15 @@ import {
|
||||
import { useTabs } from '@vben/hooks';
|
||||
import { fenToYuan, formatDate } from '@vben/utils';
|
||||
|
||||
import { Button, Card, Descriptions, Input, message, QRCode } from 'antdv-next';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Descriptions,
|
||||
DescriptionsItem,
|
||||
Input,
|
||||
message,
|
||||
QRCode,
|
||||
} from 'antdv-next';
|
||||
|
||||
import { getOrder, submitOrder } from '#/api/pay/order';
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { SystemDeptApi } from '#/api/system/dept';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { Input, Spin, Tree } from 'antdv-next';
|
||||
|
||||
import { getSimpleDeptList } from '#/api/system/dept';
|
||||
|
||||
const emit = defineEmits(['select']);
|
||||
const deptList = ref<SystemDeptApi.Dept[]>([]); // 部门列表
|
||||
const deptTree = ref<any[]>([]); // 部门树
|
||||
const expandedKeys = ref<number[]>([]); // 展开的节点
|
||||
const loading = ref(false); // 加载状态
|
||||
const searchValue = ref(''); // 搜索值
|
||||
|
||||
/** 处理搜索逻辑 */
|
||||
function handleSearch(e: any) {
|
||||
const value = e.target.value;
|
||||
searchValue.value = value;
|
||||
const filteredList = value
|
||||
? deptList.value.filter((item) =>
|
||||
item.name.toLowerCase().includes(value.toLowerCase()),
|
||||
)
|
||||
: deptList.value;
|
||||
deptTree.value = handleTree(filteredList);
|
||||
// 展开所有节点
|
||||
expandedKeys.value = deptTree.value.map((node) => node.id!);
|
||||
}
|
||||
|
||||
/** 选中部门 */
|
||||
function handleSelect(selectedNodeKeys: any[], info: any) {
|
||||
const selectedKey = selectedNodeKeys[0] ?? info.node?.id ?? info.node?.key;
|
||||
const dept = info.selected
|
||||
? deptList.value.find((item) => String(item.id) === String(selectedKey))
|
||||
: undefined;
|
||||
emit('select', dept);
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const data = await getSimpleDeptList();
|
||||
deptList.value = data;
|
||||
deptTree.value = handleTree(data);
|
||||
} catch (error) {
|
||||
console.error('获取部门数据失败', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Input
|
||||
placeholder="搜索部门"
|
||||
allow-clear
|
||||
v-model:value="searchValue"
|
||||
@change="handleSearch"
|
||||
class="w-full"
|
||||
>
|
||||
<template #prefix>
|
||||
<IconifyIcon icon="lucide:search" class="size-4" />
|
||||
</template>
|
||||
</Input>
|
||||
<Spin :spinning="loading" :classes="{ root: 'w-full' }">
|
||||
<Tree
|
||||
@select="handleSelect"
|
||||
v-if="deptTree.length > 0"
|
||||
class="pt-2"
|
||||
:tree-data="deptTree"
|
||||
:default-expand-all="true"
|
||||
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
||||
/>
|
||||
<div v-else-if="!loading" class="py-4 text-center text-gray-500">
|
||||
暂无数据
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
</template>
|
||||
@@ -52,6 +52,7 @@ export namespace ThingModelApi {
|
||||
name?: string;
|
||||
required?: boolean;
|
||||
type?: string;
|
||||
dataType?: string;
|
||||
description?: string;
|
||||
outputParams?: Param[];
|
||||
method?: string;
|
||||
|
||||
@@ -177,8 +177,6 @@ async function initProcessInfo(row: any, formVariables?: any) {
|
||||
await router.push({
|
||||
path: row.formCustomCreatePath,
|
||||
});
|
||||
// 返回选择流程
|
||||
emit('cancel');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,8 @@ async function handleRefuse() {
|
||||
clearable: true,
|
||||
maxlength: 255,
|
||||
placeholder: '不填则不告知对方原因',
|
||||
rows: 3
|
||||
rows: 3,
|
||||
type: 'textarea'
|
||||
},
|
||||
content: '',
|
||||
defaultValue: '',
|
||||
|
||||
@@ -41,7 +41,7 @@ const rules = {
|
||||
<ElFormItem label="背景颜色" prop="backgroundColor">
|
||||
<ColorInput v-model="formData.backgroundColor" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="文字颜色" prop="文字颜色">
|
||||
<ElFormItem label="文字颜色" prop="textColor">
|
||||
<ColorInput v-model="formData.textColor" />
|
||||
</ElFormItem>
|
||||
<ElCard header="公告内容" class="property-group" shadow="never">
|
||||
|
||||
@@ -415,7 +415,7 @@ function showTime(item: MallKefuMessageApi.Message, index: number) {
|
||||
:rows="4"
|
||||
class="border-none p-2"
|
||||
placeholder="输入消息,Enter发送,Shift+Enter换行"
|
||||
@keyup.enter="handleSendMessage"
|
||||
@keydown.enter.exact.prevent="handleSendMessage"
|
||||
:input-style="{ boxShadow: 'none' }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -40,10 +40,9 @@ const treeRef = ref(); // Tree 组件引用
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
// 获取选中的部门ID
|
||||
const selectedIds: number[] = props.checkStrictly
|
||||
? treeRef.value?.getCheckedKeys() || []
|
||||
: selectedDeptIds.value;
|
||||
// 获取选中的部门ID(selectedDeptIds 由 handleCheck 实时维护,
|
||||
// 严格/非严格模式下均与树勾选状态一致,无需依赖 treeRef 是否已挂载)
|
||||
const selectedIds: number[] = selectedDeptIds.value;
|
||||
|
||||
const deptArray = deptData.value.filter((dept) =>
|
||||
selectedIds.includes(dept.id!),
|
||||
@@ -102,6 +101,9 @@ function handleCheck(
|
||||
if (lastSelectedId) {
|
||||
selectedDeptIds.value = [lastSelectedId];
|
||||
treeRef.value?.setCheckedKeys([lastSelectedId]);
|
||||
} else {
|
||||
selectedDeptIds.value = [];
|
||||
treeRef.value?.setCheckedKeys([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user