表单设计-级联选择,第二级的选项无法配置的bug

zzq
滕嵩 2024-06-19 10:01:07 +08:00
parent 336786356c
commit c7ea9deb0c
7 changed files with 347 additions and 212 deletions

View File

@ -178,7 +178,7 @@
watch(
() => formConfig.value,
() => {
console.log("formConfig",formConfig.value);
console.log('formConfig', formConfig.value);
if (formConfig.value.currentItem) {
formConfig.value.currentItem.itemProps = formConfig.value.currentItem.itemProps || {};
formConfig.value.currentItem.itemProps.labelCol =
@ -187,11 +187,13 @@
formConfig.value.currentItem.itemProps.wrapperCol || {};
if (formConfig.value.currentItem.component === 'MapGeom') {
//
formConfig.value.currentItem.mapSetData = formConfig.value.currentItem.mapSetData ? formConfig.value.currentItem.mapSetData : {
chooseLayer: '',
isAllowEditPolygon: false,
isEnablePostionJump: false,
};
formConfig.value.currentItem.mapSetData = formConfig.value.currentItem.mapSetData
? formConfig.value.currentItem.mapSetData
: {
chooseLayer: '',
isAllowEditPolygon: false,
isEnablePostionJump: false,
};
//
if (shpLayerSourceOptions.value.length == 0) {
getShpLayerSourceOptions();

View File

@ -14,6 +14,40 @@
添加栅格
</a>
</div>
<div v-if="['TreeSelect', 'Cascader'].includes(formConfig.currentItem!.component)">
<BasicTree
ref="treeDataRef"
:treeData="treeDataAndOptions"
:fieldNames="{ key: 'value', title: 'label' }"
:clickRowToExpand="false"
:defaultExpandAll="true"
>
<template #title="{ label, value }">
<div class="options-box">
<Input
:value="label"
@blur="updateLabelOrValue('label', label, value, $event.target.value)"
/>
<Input
:value="value"
@blur="updateLabelOrValue('value', label, value, $event.target.value)"
class="options-value"
/>
<!-- {{ key }}-{{ label }}-{{ value }} -->
<a class="options-delete" @click="addTreeNode(value)">
<Icon icon="ant-design:folder-add-outlined" />
</a>
<a class="options-delete" @click="deleteTreeNode(value)">
<Icon icon="ant-design:delete-outlined" />
</a>
</div>
</template>
</BasicTree>
<a @click="addTreeNode(null)">
<Icon icon="ant-design:file-add-outlined" />
添加父级选项
</a>
</div>
<div v-else>
<div v-for="(item, index) of formConfig.currentItem!.componentProps![key]" :key="index">
<div class="options-box">
@ -33,22 +67,105 @@
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { defineComponent, reactive, toRefs, ref, unref, watch } from 'vue';
import { useFormDesignState } from '../../../hooks/useFormDesignState';
import { BasicTree, TreeItem, TreeActionType } from '@/components/Tree';
import { remove } from '../../../utils';
import message from '../../../utils/message';
import { Input } from 'ant-design-vue';
import Icon from '@/components/Icon/Icon.vue';
import { v4 as uuidv4 } from 'uuid';
export default defineComponent({
name: 'FormOptions',
components: { Input, Icon },
components: { Input, Icon, BasicTree },
// props: {},
setup() {
const state = reactive({});
const { formConfig } = useFormDesignState();
const key = formConfig.value.currentItem?.component === 'TreeSelect' ? 'treeData' : 'options';
let key: string = '';
// ref
const treeDataAndOptions = ref<TreeItem[]>([]);
//
let len: number = 1;
watch(
() => formConfig.value.currentItem?.component,
() => {
if (formConfig.value.currentItem?.component == 'Select') {
treeDataAndOptions.value = [];
key = 'options';
} else if (formConfig.value.currentItem?.component == 'TreeSelect') {
treeDataAndOptions.value = formConfig.value.currentItem?.componentProps?.treeData;
key = 'treeData';
} else if (formConfig.value.currentItem?.component == 'Cascader') {
treeDataAndOptions.value = formConfig.value.currentItem?.componentProps?.options;
key = 'options';
} else if (formConfig.value.currentItem?.component == 'Transfer') {
treeDataAndOptions.value = [];
key = 'dataSource';
}
},
{ deep: true, immediate: true },
);
//
const treeDataRef = ref<Nullable<TreeActionType>>(null);
function getTree(): any {
return unref(treeDataRef);
}
//
function addTreeNode(value) {
len = 1;
getLength(treeDataAndOptions.value);
getTree().insertNodeByKey({
parentKey: value,
node: {
label: `选项${len}`,
value: len,
},
//
push: 'push',
});
refresh();
}
//
function deleteTreeNode(value) {
getTree().deleteNodeByKey(value);
refresh();
}
//
function getLength(treeDataOrOptions) {
treeDataOrOptions.forEach((to) => {
if (to.value) {
len++;
}
if (to.children) {
getLength(to.children);
}
});
}
// labelvalue
function updateLabelOrValue(type, label, value, newLabelOrValue) {
if (type == 'label') {
getTree().updateNodeByKey(value, { label: newLabelOrValue, value: value });
}
if (type == 'value') {
getTree().updateNodeByKey(value, { label: label, value: newLabelOrValue });
}
refresh();
// console.log(getTree().getTreeData());
}
//
function refresh() {
getTree().expandAll(true);
treeDataAndOptions.value = getTree().getTreeData();
formConfig.value.currentItem.componentProps[key] = treeDataAndOptions.value;
}
const addOptions = () => {
if (!formConfig.value.currentItem?.componentProps?.[key])
formConfig.value.currentItem!.componentProps![key] = [];
@ -60,7 +177,7 @@
value: '' + len,
children: [],
});
}else if (['CardGroup'].includes(formConfig.value.currentItem!.component)) {
} else if (['CardGroup'].includes(formConfig.value.currentItem!.component)) {
formConfig.value.currentItem!.componentProps![key].push({
label: `卡片${len}`,
value: '' + len,
@ -97,6 +214,11 @@
key,
deleteGridOptions,
addGridOptions,
treeDataRef,
treeDataAndOptions,
addTreeNode,
deleteTreeNode,
updateLabelOrValue,
};
},
});

View File

@ -1,5 +1,5 @@
import { IAnyObject } from '../../../typings/base-type';
import { baseComponents, customComponents } from '../../../core/formItemConfig';
import { baseComponents, commonComponents } from '../../../core/formItemConfig';
import { Input, Select, RadioGroup, Slider } from 'ant-design-vue';
import { Component } from 'vue';
@ -185,7 +185,7 @@ export const baseFormItemProps: IBaseFormAttrs[] = [
label: '控件-FormItem',
component: Select,
componentProps: {
options: baseComponents.concat(customComponents[1]).map((item) => ({
options: commonComponents.concat(baseComponents).map((item) => ({
value: item.component,
label: item.label,
key: item.component + '===' + item.label,

View File

@ -18,6 +18,14 @@
breakpoint="md"
>
<div class="collapseItem-box">
<CollapseContainer title="常用控件" isTitleBold="true">
<CollapseItem
:list="commonComponents"
:handleListPush="handleListPushDrag"
@add-attrs="handleAddAttrs"
@handle-list-push="handleListPush"
/>
</CollapseContainer>
<CollapseContainer title="基础控件" isTitleBold="true">
<CollapseItem
:list="baseComponents"
@ -114,7 +122,12 @@
import { IVFormComponent, IFormConfig, PropsTabKey } from '../../typings/v-form-component';
import { formItemsForEach, generateKey, removeAttrs } from '../../utils';
import { cloneDeep } from 'lodash-es';
import { baseComponents, customComponents, layoutComponents } from '../../core/formItemConfig';
import {
commonComponents,
baseComponents,
customComponents,
layoutComponents,
} from '../../core/formItemConfig';
import { useRefHistory, UseRefHistoryReturn } from '@vueuse/core';
import { globalConfigState } from './config/formItemPropsConfig';
import { IFormDesignMethods, IPropsPanel, IToolbarMethods } from '../../typings/form-type';

View File

@ -82,35 +82,6 @@ export function setFormDesignComponents(config: IVFormComponent | IVFormComponen
//外部设置的自定义控件
export const customComponents: IVFormComponent[] = [
{
component: 'Tabs',
label: '选项卡',
icon: 'ant-design:database-outlined',
colProps: { span: 24 },
field: 'Tabs',
componentProps: {
options: [
{
label: '选项卡1',
value: '1',
children: [],
},
{
label: '选项卡2',
value: '2',
children: [],
},
],
},
},
{
component: 'InputGuid',
label: 'GUID主键',
icon: 'bi:braces-asterisk',
field: '',
colProps: { span: 24 },
componentProps: {},
},
{
component: 'TreeSelect',
label: '职级选择',
@ -163,85 +134,20 @@ export const customComponents: IVFormComponent[] = [
colProps: { span: 24 },
componentProps: {},
},
{
field: '',
component: 'Grid',
label: '设计子表',
icon: 'bi:list-ul',
type: 'subTable',
componentProps: {},
columns: [
{
span: 24,
children: [],
},
],
colProps: { span: 24 },
options: {
gutter: 0,
},
},
];
// 左侧控件列表与初始化的控件属性
// props.slotName,会在formitem级别生成一个slot,并绑定当前record值
// 属性props类型为对象不能为undefined或是null。
export const baseComponents: IVFormComponent[] = [
export const commonComponents: IVFormComponent[] = [
{
component: 'InputCountDown',
label: '倒计时输入',
icon: 'ant-design:hourglass-outlined',
colProps: { span: 24 },
component: 'InputGuid',
label: 'GUID主键',
icon: 'bi:braces-asterisk',
field: '',
colProps: { span: 24 },
componentProps: {},
},
{
component: 'IconPicker',
label: '图标选择器',
icon: 'bi:grid',
colProps: { span: 24 },
field: '',
componentProps: {},
},
{
component: 'StrengthMeter',
label: '密码强度',
icon: 'wpf:password1',
colProps: { span: 24 },
field: '',
componentProps: {},
},
{
component: 'AutoComplete',
label: '自动完成',
icon: 'bi:check2-circle',
colProps: { span: 24 },
field: '',
componentProps: {
placeholder: '请输入正则表达式',
options: [
{
value: '/^(?:(?:\\+|00)86)?1[3-9]\\d{9}$/',
label: '手机号码',
},
{
value: '/^((ht|f)tps?:\\/\\/)?[\\w-]+(\\.[\\w-]+)+:\\d{1,5}\\/?$/',
label: '网址带端口号',
},
],
},
},
{
component: 'Divider',
label: '分割线',
icon: 'radix-icons:divider-horizontal',
colProps: { span: 24 },
field: '',
componentProps: {
orientation: 'center',
dashed: true,
},
},
{
component: 'Input',
label: '输入框',
@ -301,6 +207,7 @@ export const baseComponents: IVFormComponent[] = [
icon: 'bi:check-square',
colProps: { span: 24 },
field: '',
componentProps: {},
},
{
component: 'CheckboxGroup',
@ -357,22 +264,6 @@ export const baseComponents: IVFormComponent[] = [
colProps: { span: 24 },
componentProps: {},
},
{
component: 'Slider',
label: '滑动输入条',
icon: 'vaadin:slider',
field: '',
colProps: { span: 24 },
componentProps: {},
},
{
component: 'Rate',
label: '评分',
icon: 'ic:outline-star-rate',
field: '',
colProps: { span: 24 },
componentProps: {},
},
{
component: 'Switch',
label: '开关',
@ -428,8 +319,8 @@ export const baseComponents: IVFormComponent[] = [
value: '1',
children: [
{
label: '选项',
value: '1-1',
label: '选项3',
value: '3',
},
],
},
@ -440,16 +331,6 @@ export const baseComponents: IVFormComponent[] = [
],
},
},
// {
// component: 'Upload',
// label: '上传',
// icon: 'ant-design:upload-outlined',
// field: '',
// colProps: { span: 24 },
// componentProps: {
// api: () => 1,
// },
// },
{
component: 'Cascader',
label: '级联选择',
@ -463,8 +344,8 @@ export const baseComponents: IVFormComponent[] = [
value: '1',
children: [
{
label: '选项',
value: '1-1',
label: '选项3',
value: '3',
},
],
},
@ -475,6 +356,112 @@ export const baseComponents: IVFormComponent[] = [
],
},
},
{
component: 'Transfer',
label: '穿梭框',
icon: 'bx:bx-transfer-alt',
field: '',
colProps: { span: 24 },
componentProps: {
render: (item) => item.title,
dataSource: [
{
key: 'key-1',
title: '标题1',
description: '描述',
disabled: false,
chosen: true,
},
{
key: 'key-2',
title: 'title2',
description: 'description2',
disabled: true,
},
{
key: 'key-3',
title: '标题3',
description: '描述3',
disabled: false,
chosen: true,
},
],
},
},
{
component: 'Divider',
label: '分割线',
icon: 'radix-icons:divider-horizontal',
colProps: { span: 24 },
field: '',
componentProps: {
orientation: 'center',
dashed: true,
},
},
];
export const baseComponents: IVFormComponent[] = [
{
component: 'InputCountDown',
label: '倒计时输入',
icon: 'ant-design:hourglass-outlined',
colProps: { span: 24 },
field: '',
componentProps: {},
},
{
component: 'IconPicker',
label: '图标选择器',
icon: 'bi:grid',
colProps: { span: 24 },
field: '',
componentProps: {},
},
{
component: 'StrengthMeter',
label: '密码强度',
icon: 'wpf:password1',
colProps: { span: 24 },
field: '',
componentProps: {},
},
{
component: 'AutoComplete',
label: '自动完成',
icon: 'bi:check2-circle',
colProps: { span: 24 },
field: '',
componentProps: {
placeholder: '请输入正则表达式',
options: [
{
value: '/^(?:(?:\\+|00)86)?1[3-9]\\d{9}$/',
label: '手机号码',
},
{
value: '/^((ht|f)tps?:\\/\\/)?[\\w-]+(\\.[\\w-]+)+:\\d{1,5}\\/?$/',
label: '网址带端口号',
},
],
},
},
{
component: 'Slider',
label: '滑动输入条',
icon: 'vaadin:slider',
field: '',
colProps: { span: 24 },
componentProps: {},
},
{
component: 'Rate',
label: '评分',
icon: 'ic:outline-star-rate',
field: '',
colProps: { span: 24 },
componentProps: {},
},
// {
// component: 'ColorPicker',
// label: '颜色选择器',
@ -486,16 +473,6 @@ export const baseComponents: IVFormComponent[] = [
// value: '',
// },
// },
{
component: 'slot',
label: '插槽',
icon: 'bi:inboxes',
field: '',
colProps: { span: 24 },
componentProps: {
slotName: 'slotName',
},
},
{
component: 'CreateUser',
type: 'createuser',
@ -579,6 +556,16 @@ export const baseComponents: IVFormComponent[] = [
server: 'http://192.168.10.102:9023',
},
},
{
component: 'slot',
label: '插槽',
icon: 'bi:inboxes',
field: '',
colProps: { span: 24 },
componentProps: {
slotName: 'slotName',
},
},
{
component: 'Location',
label: '获取位置',
@ -594,48 +581,39 @@ export const baseComponents: IVFormComponent[] = [
},
];
// https://next.antdv.com/components/transfer-cn
const transferControl = {
component: 'Transfer',
label: '穿梭框',
icon: 'bx:bx-transfer-alt',
field: '',
colProps: { span: 24 },
componentProps: {
render: (item) => item.title,
dataSource: [
{
key: 'key-1',
title: '标题1',
description: '描述',
disabled: false,
chosen: true,
},
{
key: 'key-2',
title: 'title2',
description: 'description2',
disabled: true,
},
{
key: 'key-3',
title: '标题3',
description: '描述3',
disabled: false,
chosen: true,
},
],
},
};
// // https://next.antdv.com/components/transfer-cn
// const transferControl = ;
baseComponents.push(transferControl);
// baseComponents.push(transferControl);
export const layoutComponents: IVFormComponent[] = [
{
component: 'Tabs',
label: '选项卡',
icon: 'ant-design:database-outlined',
colProps: { span: 24 },
field: 'Tabs',
componentProps: {
options: [
{
label: '选项卡1',
value: '1',
children: [],
},
{
label: '选项卡2',
value: '2',
children: [],
},
],
},
},
{
field: '',
component: 'Grid',
label: '栅格布局',
icon: 'bi:border-all',
label: '设计子表',
icon: 'bi:list-ul',
type: 'subTable',
componentProps: {},
columns: [
{
@ -688,4 +666,21 @@ export const layoutComponents: IVFormComponent[] = [
],
},
},
{
field: '',
component: 'Grid',
label: '栅格布局',
icon: 'bi:border-all',
componentProps: {},
columns: [
{
span: 24,
children: [],
},
],
colProps: { span: 24 },
options: {
gutter: 0,
},
},
];

View File

@ -14,6 +14,8 @@ export interface IState {
// 语言
locale: any;
// 公用组件
commonComponents: IVFormComponent[];
// 公用组件
baseComponents: IVFormComponent[];
// 自定义组件
customComponents: IVFormComponent[];

View File

@ -10,31 +10,30 @@
@ok="dataBaseClick"
>
<BasicTable @register="registerImportTable" />
</BasicModal>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:canFullscreen="false"
:defaultFullscreen="false"
:maskClosable="false"
:width="800"
title="数据对象选择"
@ok="modalSuReClick"
>
<BasicTable @register="registerDataTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'stateSlot'">
<div>
<span class="tbBtnBox" @click="tbBtnClick(record)"></span>
</div>
</template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:canFullscreen="false"
:defaultFullscreen="false"
:maskClosable="false"
:width="800"
title="数据对象选择"
@ok="modalSuReClick"
>
<BasicTable @register="registerDataTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'stateSlot'">
<div>
<span class="tbBtnBox" @click="tbBtnClick(record)"></span>
</div>
</template>
</BasicTable>
<template #centerFooter>
<a-button type="success" @click="handleAddForm"> </a-button>
</template>
</BasicModal>
</BasicTable>
<template #centerFooter>
<a-button type="success" @click="handleAddForm"> </a-button>
</template>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
@ -103,6 +102,7 @@
api: getImportBaseTableList,
formConfig: {
labelWidth: 80,
schemas: searchFormSchema,
},
columns: importColumns,
size: 'small',
@ -111,16 +111,17 @@
type: 'checkbox',
// type: 'radio',
},
useSearchForm: false,
useSearchForm: true,
showTableSetting: false,
canResize: false,
bordered: true,
pagination: {
pageSize: 10,
},
beforeFetch: () => {
beforeFetch: (data) => {
var temp = {
code: receiceDbCode.value,
key: data.key,
};
return temp;
},