commit
099d480ff4
|
|
@ -1,11 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
|
import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
|
||||||
|
import type { SystemAreaApi } from '#/api/system/area';
|
||||||
|
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
import { getDictLabel } from '@vben/hooks';
|
import { getDictLabel, getDictOptions } from '@vben/hooks';
|
||||||
import { useUserStore } from '@vben/stores';
|
import { useUserStore } from '@vben/stores';
|
||||||
import { formatDate } from '@vben/utils';
|
import { formatDate } from '@vben/utils';
|
||||||
|
|
||||||
|
|
@ -14,15 +15,45 @@ import { Button } from 'ant-design-vue';
|
||||||
import vPrint from 'vue3-print-nb';
|
import vPrint from 'vue3-print-nb';
|
||||||
|
|
||||||
import { getProcessInstancePrintData } from '#/api/bpm/processInstance';
|
import { getProcessInstancePrintData } from '#/api/bpm/processInstance';
|
||||||
|
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 { decodeFields } from '#/components/form-create';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
interface FormFieldItem {
|
||||||
|
html: string;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormFieldOption {
|
||||||
|
label?: string;
|
||||||
|
value?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormFieldRule = Record<string, unknown> & {
|
||||||
|
field?: string;
|
||||||
|
options?: FormFieldOption[];
|
||||||
|
props?: Record<string, unknown>;
|
||||||
|
title?: string;
|
||||||
|
type?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PrintableRecord = Record<string, unknown>;
|
||||||
|
|
||||||
|
interface PrintLookupMaps {
|
||||||
|
areaMap: Map<string, string>;
|
||||||
|
deptMap: Map<string, string>;
|
||||||
|
userMap: Map<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
const printData = ref<BpmProcessInstanceApi.ProcessPrintDataRespVO>();
|
const printData = ref<BpmProcessInstanceApi.ProcessPrintDataRespVO>();
|
||||||
const userName = computed(() => userStore.userInfo?.nickname ?? '');
|
const userName = computed(() => userStore.userInfo?.nickname ?? '');
|
||||||
const printTime = ref(formatDate(new Date(), 'YYYY-MM-DD HH:mm'));
|
const printTime = ref(formatDate(new Date(), 'YYYY-MM-DD HH:mm'));
|
||||||
const formFields = ref<any[]>([]);
|
const formFields = ref<FormFieldItem[]>([]);
|
||||||
const printDataMap = ref<Record<string, any>>({});
|
const printDataMap = ref<Record<string, string>>({});
|
||||||
|
|
||||||
/** 打印配置 */
|
/** 打印配置 */
|
||||||
const printObj = ref({
|
const printObj = ref({
|
||||||
|
|
@ -60,57 +91,26 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
async function fetchPrintData(id: string) {
|
async function fetchPrintData(id: string) {
|
||||||
printData.value = await getProcessInstancePrintData(id);
|
printData.value = await getProcessInstancePrintData(id);
|
||||||
initPrintDataMap();
|
initPrintDataMap();
|
||||||
parseFormFields();
|
await parseFormFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 解析表单字段 */
|
/** 解析表单字段 */
|
||||||
function parseFormFields() {
|
async function parseFormFields() {
|
||||||
if (!printData.value) return;
|
if (!printData.value) return;
|
||||||
|
|
||||||
const formFieldsObj = decodeFields(
|
const formFieldsObj = decodeFields(
|
||||||
printData.value.processInstance.processDefinition?.formFields || [],
|
printData.value.processInstance.processDefinition?.formFields || [],
|
||||||
);
|
) as unknown as FormFieldRule[];
|
||||||
const processVariables = printData.value.processInstance.formVariables;
|
const processVariables = printData.value.processInstance.formVariables ?? {};
|
||||||
const res: any = [];
|
const lookupMaps = await loadPrintLookupMaps(formFieldsObj);
|
||||||
|
const res: FormFieldItem[] = [];
|
||||||
|
|
||||||
for (const item of formFieldsObj) {
|
for (const item of formFieldsObj) {
|
||||||
const id = item.field;
|
const fieldKey = String(item.field ?? '');
|
||||||
const name = item.title;
|
const id = fieldKey;
|
||||||
const fieldKey = item.field as string;
|
const name = String(item.title ?? fieldKey);
|
||||||
const variable = processVariables[fieldKey];
|
const variable = processVariables[fieldKey];
|
||||||
let html = variable;
|
const html = formatPrintField(item, variable, lookupMaps);
|
||||||
|
|
||||||
switch (item.type) {
|
|
||||||
case 'checkbox':
|
|
||||||
case 'radio':
|
|
||||||
case 'select': {
|
|
||||||
const options = item.options;
|
|
||||||
const temp: any = [];
|
|
||||||
if (Array.isArray(options)) {
|
|
||||||
if (Array.isArray(variable)) {
|
|
||||||
const labels = options
|
|
||||||
.filter((o: any) => variable.includes(o.value))
|
|
||||||
.map((o: any) => o.label);
|
|
||||||
temp.push(...labels);
|
|
||||||
} else {
|
|
||||||
const opt = options.find((o: any) => o.value === variable);
|
|
||||||
if (opt) {
|
|
||||||
temp.push(opt.label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
html = temp.join(',');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'UploadImg': {
|
|
||||||
const imgEl = document.createElement('img');
|
|
||||||
imgEl.setAttribute('src', variable);
|
|
||||||
imgEl.setAttribute('style', 'max-width: 600px;');
|
|
||||||
html = imgEl.outerHTML;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// TODO 更多表单打印展示
|
|
||||||
}
|
|
||||||
|
|
||||||
printDataMap.value[fieldKey] = html;
|
printDataMap.value[fieldKey] = html;
|
||||||
res.push({ id, name, html });
|
res.push({ id, name, html });
|
||||||
|
|
@ -119,6 +119,308 @@ function parseFormFields() {
|
||||||
formFields.value = res;
|
formFields.value = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRuleProp(rule: FormFieldRule, key: string) {
|
||||||
|
return rule?.[key] ?? rule?.props?.[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPrintableRecord(value: unknown): value is PrintableRecord {
|
||||||
|
return typeof value === 'object' && value !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRecordValue(record: PrintableRecord, key: string) {
|
||||||
|
return record[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNotEmptyString(value: string) {
|
||||||
|
return value.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmptyValue(value: unknown) {
|
||||||
|
return value === undefined || value === null || value === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function toValueArray(value: unknown) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (isEmptyValue(value)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return [value];
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryFormatDate(value: unknown) {
|
||||||
|
if (isEmptyValue(value)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const formatted = formatDate(value as Date | number | string);
|
||||||
|
return formatted === 'Invalid Date' ? String(value) : formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateValue(value: unknown) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map((item) => tryFormatDate(item)).join(' ~ ');
|
||||||
|
}
|
||||||
|
return tryFormatDate(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPrimitiveValue(value: unknown): string {
|
||||||
|
if (isEmptyValue(value)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value
|
||||||
|
.map((item) => formatPrimitiveValue(item))
|
||||||
|
.filter((s) => isNotEmptyString(s))
|
||||||
|
.join(', ');
|
||||||
|
}
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
return value ? '是' : '否';
|
||||||
|
}
|
||||||
|
if (isPrintableRecord(value)) {
|
||||||
|
const record = value;
|
||||||
|
const displayValue =
|
||||||
|
getRecordValue(record, 'label') ??
|
||||||
|
getRecordValue(record, 'name') ??
|
||||||
|
getRecordValue(record, 'url') ??
|
||||||
|
getRecordValue(record, 'value') ??
|
||||||
|
JSON.stringify(value);
|
||||||
|
return String(displayValue);
|
||||||
|
}
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createImageHtml(url: string) {
|
||||||
|
const imgEl = document.createElement('img');
|
||||||
|
imgEl.setAttribute('src', url);
|
||||||
|
imgEl.setAttribute('style', 'max-width: 600px; max-height: 300px;');
|
||||||
|
return imgEl.outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderImageListHtml(value: unknown) {
|
||||||
|
return toValueArray(value)
|
||||||
|
.map((item) => {
|
||||||
|
let url: string | undefined;
|
||||||
|
if (typeof item === 'string') {
|
||||||
|
url = item;
|
||||||
|
} else if (isPrintableRecord(item)) {
|
||||||
|
const recordUrl = getRecordValue(item, 'url');
|
||||||
|
url = recordUrl ? String(recordUrl) : undefined;
|
||||||
|
}
|
||||||
|
return url ? createImageHtml(String(url)) : '';
|
||||||
|
})
|
||||||
|
.filter((s) => isNotEmptyString(s))
|
||||||
|
.join('<br/>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFileLinkHtml(file: unknown) {
|
||||||
|
const record = isPrintableRecord(file) ? file : undefined;
|
||||||
|
const recordUrl = record ? getRecordValue(record, 'url') : undefined;
|
||||||
|
const url = typeof file === 'string' ? file : String(recordUrl ?? '');
|
||||||
|
if (!url) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const linkEl = document.createElement('a');
|
||||||
|
linkEl.setAttribute('href', String(url));
|
||||||
|
linkEl.setAttribute('target', '_blank');
|
||||||
|
linkEl.setAttribute('rel', 'noopener noreferrer');
|
||||||
|
const fallbackName = String(url).slice(Math.max(0, String(url).lastIndexOf('/') + 1)) || String(url);
|
||||||
|
const recordName = record ? getRecordValue(record, 'name') : undefined;
|
||||||
|
linkEl.textContent = recordName ? String(recordName) : fallbackName;
|
||||||
|
return linkEl.outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFileListHtml(value: unknown) {
|
||||||
|
return toValueArray(value)
|
||||||
|
.map((item) => createFileLinkHtml(item))
|
||||||
|
.filter((s) => isNotEmptyString(s))
|
||||||
|
.join('<br/>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapValuesWithOptions(
|
||||||
|
value: unknown,
|
||||||
|
options: FormFieldOption[] = [],
|
||||||
|
) {
|
||||||
|
const values = toValueArray(value);
|
||||||
|
const labels = values
|
||||||
|
.map((item) => {
|
||||||
|
const matched = options.find(
|
||||||
|
(option) =>
|
||||||
|
option?.value === item || String(option?.value ?? '') === String(item),
|
||||||
|
);
|
||||||
|
return matched?.label ?? String(item);
|
||||||
|
})
|
||||||
|
.filter((s) => isNotEmptyString(s));
|
||||||
|
return labels.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenAreaTree(
|
||||||
|
list: Array<SystemAreaApi.Area & { children?: SystemAreaApi.Area[] }> = [],
|
||||||
|
map: Map<string, string> = new Map(),
|
||||||
|
) {
|
||||||
|
list.forEach((item) => {
|
||||||
|
if (item.id !== undefined) {
|
||||||
|
map.set(String(item.id), item.name);
|
||||||
|
}
|
||||||
|
if (Array.isArray(item.children) && item.children.length > 0) {
|
||||||
|
flattenAreaTree(item.children, map);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapValueWithLabelMap(
|
||||||
|
value: unknown,
|
||||||
|
labelMap: Map<string, string>,
|
||||||
|
separator = ', ',
|
||||||
|
) {
|
||||||
|
const values = toValueArray(value);
|
||||||
|
const labels = values
|
||||||
|
.map((item) => labelMap.get(String(item)) ?? String(item))
|
||||||
|
.filter((s) => isNotEmptyString(s));
|
||||||
|
return labels.length > 0 ? labels.join(separator) : formatPrimitiveValue(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按当前表单字段类型按需加载打印所需的关联数据,并构建查找映射表。
|
||||||
|
*
|
||||||
|
* @param formFieldsObj - 已解码的表单字段规则列表
|
||||||
|
* @returns 打印展示时使用的区域、部门、用户名称映射
|
||||||
|
*/
|
||||||
|
async function loadPrintLookupMaps(formFieldsObj: FormFieldRule[]) {
|
||||||
|
const hasAreaSelect = formFieldsObj.some((item) => item.type === 'AreaSelect');
|
||||||
|
const hasUserSelect = formFieldsObj.some((item) => item.type === 'UserSelect');
|
||||||
|
const hasDeptSelect = formFieldsObj.some((item) => item.type === 'DeptSelect');
|
||||||
|
|
||||||
|
const [areaList, userList, deptList] = await Promise.all([
|
||||||
|
hasAreaSelect ? getAreaTree() : Promise.resolve([]),
|
||||||
|
hasUserSelect ? getSimpleUserList() : Promise.resolve([]),
|
||||||
|
hasDeptSelect ? getSimpleDeptList() : Promise.resolve([]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
areaMap: flattenAreaTree(areaList as Array<SystemAreaApi.Area>),
|
||||||
|
deptMap: new Map(
|
||||||
|
(deptList ?? []).map((item) => [String(item.id), item.name] as const),
|
||||||
|
),
|
||||||
|
userMap: new Map(
|
||||||
|
(userList ?? []).map(
|
||||||
|
(item) => [String(item.id), item.nickname ?? item.username] as const,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
} satisfies PrintLookupMaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据表单字段规则和字段值,格式化为适合打印展示的 HTML 字符串。
|
||||||
|
*
|
||||||
|
* @param rule - 表单字段规则,包含字段类型、选项、props 等配置信息
|
||||||
|
* @param value - 该字段在流程实例中的实际值
|
||||||
|
* @param lookupMaps - 预加载的查找映射表,包含区域、部门、用户的 id→name 映射
|
||||||
|
* @returns 格式化后的 HTML 字符串(纯文本或富文本,取决于字段类型)
|
||||||
|
*/
|
||||||
|
function formatPrintField(
|
||||||
|
rule: FormFieldRule,
|
||||||
|
value: unknown,
|
||||||
|
lookupMaps: PrintLookupMaps,
|
||||||
|
){
|
||||||
|
const type = String(rule.type ?? '');
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'AreaSelect': {
|
||||||
|
const separator = String(getRuleProp(rule, 'separator') || '/');
|
||||||
|
return mapValueWithLabelMap(value, lookupMaps.areaMap, separator);
|
||||||
|
}
|
||||||
|
case 'cascader':
|
||||||
|
case 'checkbox':
|
||||||
|
case 'radio':
|
||||||
|
case 'select':
|
||||||
|
case 'treeSelect': {
|
||||||
|
const options = getRuleProp(rule, 'options');
|
||||||
|
return Array.isArray(options) && options.length > 0
|
||||||
|
? mapValuesWithOptions(value, options as FormFieldOption[])
|
||||||
|
: formatPrimitiveValue(value);
|
||||||
|
}
|
||||||
|
case 'date':
|
||||||
|
case 'DatePicker':
|
||||||
|
case 'datePicker':
|
||||||
|
case 'daterange':
|
||||||
|
case 'datetime':
|
||||||
|
case 'datetimerange':
|
||||||
|
case 'month':
|
||||||
|
case 'monthrange':
|
||||||
|
case 'RangePicker':
|
||||||
|
case 'rangePicker':
|
||||||
|
case 'TimePicker':
|
||||||
|
case 'timePicker':
|
||||||
|
case 'TimeRangePicker':
|
||||||
|
case 'timeRangePicker': {
|
||||||
|
return formatDateValue(value);
|
||||||
|
}
|
||||||
|
case 'DeptSelect': {
|
||||||
|
if (String(getRuleProp(rule, 'returnType')) === 'name') {
|
||||||
|
return formatPrimitiveValue(value);
|
||||||
|
}
|
||||||
|
return mapValueWithLabelMap(value, lookupMaps.deptMap);
|
||||||
|
}
|
||||||
|
case 'DictSelect': {
|
||||||
|
const dictType = getRuleProp(rule, 'dictType');
|
||||||
|
if (typeof dictType !== 'string' || !dictType) {
|
||||||
|
return formatPrimitiveValue(value);
|
||||||
|
}
|
||||||
|
const valueTypeMap = {
|
||||||
|
bool: 'boolean',
|
||||||
|
int: 'number',
|
||||||
|
str: 'string',
|
||||||
|
} as const;
|
||||||
|
const rawValueType = String(getRuleProp(rule, 'valueType') ?? '');
|
||||||
|
const valueType =
|
||||||
|
(valueTypeMap as Record<string, 'boolean' | 'number' | 'string'>)[rawValueType] ??
|
||||||
|
'string';
|
||||||
|
const options = getDictOptions(dictType, valueType);
|
||||||
|
return mapValuesWithOptions(value, options);
|
||||||
|
}
|
||||||
|
case 'FileUpload': {
|
||||||
|
return renderFileListHtml(value);
|
||||||
|
}
|
||||||
|
case 'IframeComponent': {
|
||||||
|
const propsObj = rule.props;
|
||||||
|
const propsUrl =
|
||||||
|
isPrintableRecord(propsObj)
|
||||||
|
? String(getRecordValue(propsObj, 'url') ?? '')
|
||||||
|
: '';
|
||||||
|
const iframeUrl = isEmptyValue(value) ? propsUrl : String(value ?? '');
|
||||||
|
return iframeUrl ? createFileLinkHtml(iframeUrl) : '';
|
||||||
|
}
|
||||||
|
case 'ImagesUpload':
|
||||||
|
case 'ImageUpload':
|
||||||
|
case 'UploadImg': {
|
||||||
|
return renderImageListHtml(value);
|
||||||
|
}
|
||||||
|
case 'switch': {
|
||||||
|
if (isEmptyValue(value)) return '否';
|
||||||
|
const checkedVal = getRuleProp(rule, 'checkedValue');
|
||||||
|
const isChecked =
|
||||||
|
checkedVal !== undefined && checkedVal !== null
|
||||||
|
? value === checkedVal
|
||||||
|
: Boolean(value);
|
||||||
|
return isChecked ? '是' : '否';
|
||||||
|
}
|
||||||
|
case 'Tinymce': {
|
||||||
|
return isEmptyValue(value) ? '' : String(value);
|
||||||
|
}
|
||||||
|
case 'UserSelect': {
|
||||||
|
if (String(getRuleProp(rule, 'returnType')) === 'name') {
|
||||||
|
return formatPrimitiveValue(value);
|
||||||
|
}
|
||||||
|
return mapValueWithLabelMap(value, lookupMaps.userMap);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return formatPrimitiveValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 初始化打印数据映射 */
|
/** 初始化打印数据映射 */
|
||||||
function initPrintDataMap() {
|
function initPrintDataMap() {
|
||||||
if (!printData.value) return;
|
if (!printData.value) return;
|
||||||
|
|
@ -128,16 +430,18 @@ function initPrintDataMap() {
|
||||||
printDataMap.value.startUserDept =
|
printDataMap.value.startUserDept =
|
||||||
printData.value.processInstance.startUser?.deptName || '';
|
printData.value.processInstance.startUser?.deptName || '';
|
||||||
printDataMap.value.processName = printData.value.processInstance.name;
|
printDataMap.value.processName = printData.value.processInstance.name;
|
||||||
printDataMap.value.processNum = printData.value.processInstance.id;
|
printDataMap.value.processNum = String(printData.value.processInstance.id ?? '');
|
||||||
printDataMap.value.startTime = formatDate(
|
printDataMap.value.startTime = formatDate(
|
||||||
printData.value.processInstance.startTime,
|
printData.value.processInstance.startTime,
|
||||||
);
|
);
|
||||||
printDataMap.value.endTime = formatDate(
|
printDataMap.value.endTime = formatDate(
|
||||||
printData.value.processInstance.endTime,
|
printData.value.processInstance.endTime,
|
||||||
);
|
);
|
||||||
printDataMap.value.processStatus = getDictLabel(
|
printDataMap.value.processStatus = String(
|
||||||
DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS,
|
getDictLabel(
|
||||||
printData.value.processInstance.status,
|
DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS,
|
||||||
|
printData.value.processInstance.status,
|
||||||
|
) ?? '',
|
||||||
);
|
);
|
||||||
printDataMap.value.printUser = userName.value;
|
printDataMap.value.printUser = userName.value;
|
||||||
printDataMap.value.printTime = printTime.value;
|
printDataMap.value.printTime = printTime.value;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { DictSelectProps } from '../typing';
|
import type { DictSelectProps } from '../typing';
|
||||||
|
|
||||||
import { computed, useAttrs } from 'vue';
|
import { computed, ref, useAttrs, watch } from 'vue';
|
||||||
|
|
||||||
import { getDictOptions } from '@vben/hooks';
|
import { getDictOptions } from '@vben/hooks';
|
||||||
|
|
||||||
|
|
@ -22,8 +22,24 @@ const props = withDefaults(defineProps<DictSelectProps>(), {
|
||||||
selectType: 'select',
|
selectType: 'select',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:modelValue', value: any): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
const attrs = useAttrs();
|
const attrs = useAttrs();
|
||||||
|
|
||||||
|
/** 内部选中值 */
|
||||||
|
const selectedValue = ref<any>();
|
||||||
|
|
||||||
|
/** 同步 modelValue 到内部选中值 */
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(newVal) => {
|
||||||
|
selectedValue.value = newVal;
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
/** 获得字典配置 */
|
/** 获得字典配置 */
|
||||||
const getDictOption = computed(() => {
|
const getDictOption = computed(() => {
|
||||||
switch (props.valueType) {
|
switch (props.valueType) {
|
||||||
|
|
@ -31,20 +47,30 @@ const getDictOption = computed(() => {
|
||||||
return getDictOptions(props.dictType, 'boolean');
|
return getDictOptions(props.dictType, 'boolean');
|
||||||
}
|
}
|
||||||
case 'int': {
|
case 'int': {
|
||||||
return getDictOptions(props.dictType);
|
return getDictOptions(props.dictType, 'number');
|
||||||
}
|
}
|
||||||
case 'str': {
|
case 'str': {
|
||||||
return getDictOptions(props.dictType);
|
return getDictOptions(props.dictType, 'string');
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleChange(value: any) {
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElSelect v-if="selectType === 'select'" class="w-1/1" v-bind="attrs">
|
<ElSelect
|
||||||
|
v-if="selectType === 'select'"
|
||||||
|
v-model="selectedValue"
|
||||||
|
class="w-full"
|
||||||
|
v-bind="attrs"
|
||||||
|
@change="handleChange"
|
||||||
|
>
|
||||||
<ElOption
|
<ElOption
|
||||||
v-for="(dict, index) in getDictOption"
|
v-for="(dict, index) in getDictOption"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
|
@ -52,24 +78,32 @@ const getDictOption = computed(() => {
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
/>
|
/>
|
||||||
</ElSelect>
|
</ElSelect>
|
||||||
<ElRadioGroup v-if="selectType === 'radio'" class="w-1/1" v-bind="attrs">
|
<ElRadioGroup
|
||||||
|
v-if="selectType === 'radio'"
|
||||||
|
v-model="selectedValue"
|
||||||
|
class="w-full"
|
||||||
|
v-bind="attrs"
|
||||||
|
@change="handleChange"
|
||||||
|
>
|
||||||
<ElRadio
|
<ElRadio
|
||||||
v-for="(dict, index) in getDictOption"
|
v-for="(dict, index) in getDictOption"
|
||||||
:key="index"
|
:key="index"
|
||||||
:label="dict.value"
|
:value="dict.value"
|
||||||
>
|
>
|
||||||
{{ dict.label }}
|
{{ dict.label }}
|
||||||
</ElRadio>
|
</ElRadio>
|
||||||
</ElRadioGroup>
|
</ElRadioGroup>
|
||||||
<ElCheckboxGroup
|
<ElCheckboxGroup
|
||||||
v-if="selectType === 'checkbox'"
|
v-if="selectType === 'checkbox'"
|
||||||
class="w-1/1"
|
v-model="selectedValue"
|
||||||
|
class="w-full"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
|
@change="handleChange"
|
||||||
>
|
>
|
||||||
<ElCheckbox
|
<ElCheckbox
|
||||||
v-for="(dict, index) in getDictOption"
|
v-for="(dict, index) in getDictOption"
|
||||||
:key="index"
|
:key="index"
|
||||||
:label="dict.value"
|
:value="dict.value"
|
||||||
>
|
>
|
||||||
{{ dict.label }}
|
{{ dict.label }}
|
||||||
</ElCheckbox>
|
</ElCheckbox>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ export interface DictSelectProps {
|
||||||
dictType: string; // 字典类型
|
dictType: string; // 字典类型
|
||||||
valueType?: 'bool' | 'int' | 'str'; // 字典值类型
|
valueType?: 'bool' | 'int' | 'str'; // 字典值类型
|
||||||
selectType?: 'checkbox' | 'radio' | 'select'; // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
|
selectType?: 'checkbox' | 'radio' | 'select'; // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
|
||||||
|
modelValue?: any; // 选中值,由 form-create 通过 modelField 绑定
|
||||||
formCreateInject?: any;
|
formCreateInject?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -399,6 +399,7 @@ onBeforeUnmount(() => {
|
||||||
ref="treeRef"
|
ref="treeRef"
|
||||||
v-model="userTaskForm.candidateParam"
|
v-model="userTaskForm.candidateParam"
|
||||||
:data="deptTreeOptions"
|
:data="deptTreeOptions"
|
||||||
|
node-key="id"
|
||||||
:props="defaultProps"
|
:props="defaultProps"
|
||||||
placeholder="加载中,请稍后"
|
placeholder="加载中,请稍后"
|
||||||
multiple
|
multiple
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,13 @@ const deptLevelLabel = computed(() => {
|
||||||
return label;
|
return label;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 定义 TreeSelect 的默认属性映射
|
||||||
|
const defaultProps = {
|
||||||
|
children: 'children',
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
};
|
||||||
|
|
||||||
// 抽屉配置
|
// 抽屉配置
|
||||||
const [Drawer, drawerApi] = useVbenDrawer({
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
header: true,
|
header: true,
|
||||||
|
|
@ -282,12 +289,8 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
||||||
<ElTreeSelect
|
<ElTreeSelect
|
||||||
v-model="configForm.deptIds"
|
v-model="configForm.deptIds"
|
||||||
:data="deptTreeOptions"
|
:data="deptTreeOptions"
|
||||||
:props="{
|
node-key="id"
|
||||||
label: 'name',
|
:props="defaultProps"
|
||||||
// @ts-expect-error: dynamic node config access is narrower than runtime shape
|
|
||||||
value: 'id',
|
|
||||||
children: 'children',
|
|
||||||
}"
|
|
||||||
empty-text="加载中,请稍候"
|
empty-text="加载中,请稍候"
|
||||||
multiple
|
multiple
|
||||||
:check-strictly="true"
|
:check-strictly="true"
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,13 @@ const emits = defineEmits<{
|
||||||
findReturnTaskNodes: [nodeList: SimpleFlowNode[]];
|
findReturnTaskNodes: [nodeList: SimpleFlowNode[]];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
// 定义 TreeSelect 的默认属性映射
|
||||||
|
const defaultProps = {
|
||||||
|
children: 'children',
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
};
|
||||||
|
|
||||||
const deptLevelLabel = computed(() => {
|
const deptLevelLabel = computed(() => {
|
||||||
let label = '部门负责人来源';
|
let label = '部门负责人来源';
|
||||||
if (
|
if (
|
||||||
|
|
@ -694,12 +701,9 @@ onMounted(() => {
|
||||||
>
|
>
|
||||||
<ElTreeSelect
|
<ElTreeSelect
|
||||||
v-model="configForm.deptIds"
|
v-model="configForm.deptIds"
|
||||||
:tree-data="deptTreeOptions"
|
:data="deptTreeOptions"
|
||||||
:field-names="{
|
node-key="id"
|
||||||
label: 'name',
|
:props="defaultProps"
|
||||||
value: 'id',
|
|
||||||
children: 'children',
|
|
||||||
}"
|
|
||||||
empty-text="加载中,请稍等..."
|
empty-text="加载中,请稍等..."
|
||||||
multiple
|
multiple
|
||||||
:check-strictly="true"
|
:check-strictly="true"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue