树状结构支持数据导入
parent
d61c8dd12a
commit
eeff8ef4f4
|
|
@ -33,9 +33,11 @@
|
||||||
@input="updateLabelOrValue('value', label, value, $event.target.value)"
|
@input="updateLabelOrValue('value', label, value, $event.target.value)"
|
||||||
class="options-value"
|
class="options-value"
|
||||||
/>
|
/>
|
||||||
|
<Tooltip title="添加子级选项" placement="top">
|
||||||
<a class="options-delete" @click="addTreeNode(value)">
|
<a class="options-delete" @click="addTreeNode(value)">
|
||||||
<Icon icon="ant-design:folder-add-outlined" />
|
<Icon icon="ant-design:folder-add-outlined" />
|
||||||
</a>
|
</a>
|
||||||
|
</Tooltip>
|
||||||
<a class="options-delete" @click="deleteTreeNode(value)">
|
<a class="options-delete" @click="deleteTreeNode(value)">
|
||||||
<Icon icon="ant-design:delete-outlined" />
|
<Icon icon="ant-design:delete-outlined" />
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -46,6 +48,10 @@
|
||||||
<Icon icon="ant-design:file-add-outlined" />
|
<Icon icon="ant-design:file-add-outlined" />
|
||||||
添加父级选项
|
添加父级选项
|
||||||
</a>
|
</a>
|
||||||
|
<a @click="showModal">
|
||||||
|
<Icon icon="ant-design:import-outlined" />
|
||||||
|
导入数据
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="['Transfer'].includes(formConfig.currentItem!.component)">
|
<div v-else-if="['Transfer'].includes(formConfig.currentItem!.component)">
|
||||||
<div v-for="(item, index) of formConfig.currentItem!.componentProps![key]" :key="index">
|
<div v-for="(item, index) of formConfig.currentItem!.componentProps![key]" :key="index">
|
||||||
|
|
@ -92,30 +98,64 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Modal
|
||||||
|
title="树状结构数据导入"
|
||||||
|
:open="visible"
|
||||||
|
@ok="handleImportJson"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
cancelText="关闭"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
wrapClassName="v-code-modal"
|
||||||
|
style="top: 20px"
|
||||||
|
:width="850"
|
||||||
|
>
|
||||||
|
<p class="hint-box">导入格式如下:</p>
|
||||||
|
<div class="v-json-box">
|
||||||
|
<CodeEditor v-model:value="json" ref="myEditor" :mode="MODE.JSON" />
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<a-button @click="handleCancel">取消</a-button>
|
||||||
|
<Upload
|
||||||
|
class="upload-button"
|
||||||
|
:beforeUpload="beforeUpload"
|
||||||
|
:showUploadList="false"
|
||||||
|
accept="application/json"
|
||||||
|
>
|
||||||
|
<a-button type="primary">导入json文件</a-button>
|
||||||
|
</Upload>
|
||||||
|
<a-button type="primary" @click="handleImportJson">确定</a-button>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, toRefs, ref, unref, watch } from 'vue';
|
import { defineComponent, reactive, toRefs, ref, unref, watch } from 'vue';
|
||||||
import { useFormDesignState } from '../../../hooks/useFormDesignState';
|
import { useFormDesignState } from '../../../hooks/useFormDesignState';
|
||||||
import { BasicTree, TreeItem, TreeActionType } from '@/components/Tree';
|
import { BasicTree, TreeItem, TreeActionType } from '@/components/Tree';
|
||||||
import { remove } from '../../../utils';
|
import { remove, formItemsForEach, generateKey } from '../../../utils';
|
||||||
import message from '../../../utils/message';
|
import { CodeEditor, MODE } from '@/components/CodeEditor';
|
||||||
import { Input } from 'ant-design-vue';
|
import { Upload, Modal, Input, Tooltip } from 'ant-design-vue';
|
||||||
|
import { options_json, treeData_json } from '../../VFormDesign/config/formItemPropsScript';
|
||||||
import Icon from '@/components/Icon/Icon.vue';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import Icon from '@/components/Icon/Icon.vue';
|
||||||
|
import message from '../../../utils/message';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'FormOptions',
|
name: 'FormOptions',
|
||||||
components: { Input, Icon, BasicTree },
|
components: { Input, Icon, BasicTree, CodeEditor, Upload, Modal, Tooltip },
|
||||||
// props: {},
|
// props: {},
|
||||||
setup() {
|
setup() {
|
||||||
const state = reactive({});
|
|
||||||
const { formConfig } = useFormDesignState();
|
const { formConfig } = useFormDesignState();
|
||||||
|
|
||||||
let key: string = '';
|
let key: string = '';
|
||||||
|
|
||||||
// 树的ref
|
// 导入窗口
|
||||||
|
const state = reactive({
|
||||||
|
visible: false,
|
||||||
|
json: ``,
|
||||||
|
});
|
||||||
|
// 树的值
|
||||||
const treeDataAndOptions = ref<TreeItem[]>([]);
|
const treeDataAndOptions = ref<TreeItem[]>([]);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
@ -124,10 +164,12 @@
|
||||||
if (formConfig.value.currentItem?.component == 'TreeSelect') {
|
if (formConfig.value.currentItem?.component == 'TreeSelect') {
|
||||||
// 树形选择
|
// 树形选择
|
||||||
treeDataAndOptions.value = formConfig.value.currentItem?.componentProps?.treeData;
|
treeDataAndOptions.value = formConfig.value.currentItem?.componentProps?.treeData;
|
||||||
|
state.json = treeData_json;
|
||||||
key = 'treeData';
|
key = 'treeData';
|
||||||
} else if (formConfig.value.currentItem?.component == 'Cascader') {
|
} else if (formConfig.value.currentItem?.component == 'Cascader') {
|
||||||
// 级联选择
|
// 级联选择
|
||||||
treeDataAndOptions.value = formConfig.value.currentItem?.componentProps?.options;
|
treeDataAndOptions.value = formConfig.value.currentItem?.componentProps?.options;
|
||||||
|
state.json = options_json;
|
||||||
key = 'options';
|
key = 'options';
|
||||||
} else if (formConfig.value.currentItem?.component == 'Transfer') {
|
} else if (formConfig.value.currentItem?.component == 'Transfer') {
|
||||||
// 穿梭框
|
// 穿梭框
|
||||||
|
|
@ -142,11 +184,11 @@
|
||||||
|
|
||||||
// 获取树
|
// 获取树
|
||||||
const treeDataRef = ref<Nullable<TreeActionType>>(null);
|
const treeDataRef = ref<Nullable<TreeActionType>>(null);
|
||||||
function getTree(): any {
|
const getTree = (): any => {
|
||||||
return unref(treeDataRef);
|
return unref(treeDataRef);
|
||||||
}
|
};
|
||||||
// 添加树的父节点、子节点
|
// 添加树的父节点、子节点
|
||||||
function addTreeNode(value) {
|
const addTreeNode = (value) => {
|
||||||
let length = getLength(1, treeDataAndOptions.value);
|
let length = getLength(1, treeDataAndOptions.value);
|
||||||
getTree().insertNodeByKey({
|
getTree().insertNodeByKey({
|
||||||
parentKey: value,
|
parentKey: value,
|
||||||
|
|
@ -158,14 +200,14 @@
|
||||||
push: 'push',
|
push: 'push',
|
||||||
});
|
});
|
||||||
refresh();
|
refresh();
|
||||||
}
|
};
|
||||||
// 删除树的节点
|
// 删除树的节点
|
||||||
function deleteTreeNode(value) {
|
const deleteTreeNode = (value) => {
|
||||||
getTree().deleteNodeByKey(value);
|
getTree().deleteNodeByKey(value);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
};
|
||||||
// 获取树的节点的不重复的默认值
|
// 获取树的节点的不重复的默认值
|
||||||
function getLength(length, treeDataOrOptions) {
|
const getLength = (length, treeDataOrOptions) => {
|
||||||
treeDataOrOptions?.forEach((to) => {
|
treeDataOrOptions?.forEach((to) => {
|
||||||
if (to.children) {
|
if (to.children) {
|
||||||
length = getLength(length, to.children);
|
length = getLength(length, to.children);
|
||||||
|
|
@ -176,24 +218,84 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return length;
|
return length;
|
||||||
}
|
};
|
||||||
// 修改树的label或者value
|
// 修改树的label或者value
|
||||||
function updateLabelOrValue(type, label, value, newLabelOrValue) {
|
const updateLabelOrValue = (type, label, value, newLabelOrValue) => {
|
||||||
if (type == 'label') {
|
if (type == 'label') {
|
||||||
getTree().updateNodeByKey(value, { label: newLabelOrValue, value: value });
|
getTree().updateNodeByKey(value, { label: newLabelOrValue, value: value });
|
||||||
}
|
}
|
||||||
if (type == 'value') {
|
if (type == 'value') {
|
||||||
|
if (checkRepeat(true, getTree().getTreeData(), newLabelOrValue)) {
|
||||||
getTree().updateNodeByKey(value, { label: label, value: newLabelOrValue });
|
getTree().updateNodeByKey(value, { label: label, value: newLabelOrValue });
|
||||||
|
} else {
|
||||||
|
message.warning('不能赋给重复的值');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
refresh();
|
refresh();
|
||||||
// console.log(getTree().getTreeData());
|
};
|
||||||
|
// 修改树的label或者value-检查是否有重复值
|
||||||
|
const checkRepeat = (flag, treeData, newValue) => {
|
||||||
|
treeData.forEach((tree) => {
|
||||||
|
if (tree.value == newValue) {
|
||||||
|
flag = false;
|
||||||
|
} else if (tree.children) {
|
||||||
|
return checkRepeat(flag, tree.children, newValue);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
return flag;
|
||||||
|
};
|
||||||
// 刷新树的值
|
// 刷新树的值
|
||||||
function refresh() {
|
const refresh = () => {
|
||||||
getTree().expandAll(true);
|
getTree().expandAll(true);
|
||||||
treeDataAndOptions.value = getTree().getTreeData();
|
formConfig.value.currentItem.componentProps[key] = treeDataAndOptions.value =
|
||||||
formConfig.value.currentItem.componentProps[key] = treeDataAndOptions.value;
|
getTree().getTreeData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树的导入窗口关闭
|
||||||
|
const handleCancel = () => {
|
||||||
|
state.visible = false;
|
||||||
|
};
|
||||||
|
// 树的导入窗口开启
|
||||||
|
const showModal = () => {
|
||||||
|
state.visible = true;
|
||||||
|
};
|
||||||
|
// 树的导入JSON
|
||||||
|
const handleImportJson = () => {
|
||||||
|
try {
|
||||||
|
const editorJsonData = JSON.parse(state.json);
|
||||||
|
editorJsonData[key] &&
|
||||||
|
formItemsForEach(editorJsonData[key], (formItem) => {
|
||||||
|
generateKey(formItem);
|
||||||
|
});
|
||||||
|
// 删除旧的树
|
||||||
|
cloneDeep(getTree().getTreeData())?.forEach((item) => {
|
||||||
|
deleteTreeNode(item.value);
|
||||||
|
});
|
||||||
|
// 导入新数据
|
||||||
|
editorJsonData[key]?.forEach((item) => {
|
||||||
|
getTree().insertNodeByKey({
|
||||||
|
parentKey: null,
|
||||||
|
node: item,
|
||||||
|
push: 'push',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
refresh();
|
||||||
|
handleCancel();
|
||||||
|
message.success('导入成功');
|
||||||
|
} catch {
|
||||||
|
message.error('导入失败,数据格式不对');
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
// 通过json文件导入
|
||||||
|
const beforeUpload = (e: File) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsText(e);
|
||||||
|
reader.onload = function () {
|
||||||
|
state.json = this.result as string;
|
||||||
|
handleImportJson();
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
const addOptions = () => {
|
const addOptions = () => {
|
||||||
if (!formConfig.value.currentItem?.componentProps?.[key])
|
if (!formConfig.value.currentItem?.componentProps?.[key])
|
||||||
|
|
@ -230,7 +332,6 @@
|
||||||
const deleteOptions = (index: number) => {
|
const deleteOptions = (index: number) => {
|
||||||
remove(formConfig.value.currentItem?.componentProps?.[key], index);
|
remove(formConfig.value.currentItem?.componentProps?.[key], index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addGridOptions = () => {
|
const addGridOptions = () => {
|
||||||
formConfig.value.currentItem?.['columns']?.push({
|
formConfig.value.currentItem?.['columns']?.push({
|
||||||
span: 12,
|
span: 12,
|
||||||
|
|
@ -242,7 +343,6 @@
|
||||||
|
|
||||||
remove(formConfig.value.currentItem!['columns']!, index);
|
remove(formConfig.value.currentItem!['columns']!, index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateTransferDisabled = (index: number, flag: boolean) => {
|
const updateTransferDisabled = (index: number, flag: boolean) => {
|
||||||
formConfig.value.currentItem.componentProps[key][index].disabled = flag;
|
formConfig.value.currentItem.componentProps[key][index].disabled = flag;
|
||||||
};
|
};
|
||||||
|
|
@ -260,6 +360,11 @@
|
||||||
deleteTreeNode,
|
deleteTreeNode,
|
||||||
updateLabelOrValue,
|
updateLabelOrValue,
|
||||||
updateTransferDisabled,
|
updateTransferDisabled,
|
||||||
|
handleImportJson,
|
||||||
|
beforeUpload,
|
||||||
|
handleCancel,
|
||||||
|
showModal,
|
||||||
|
MODE,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -290,4 +395,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upload-button {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -58,3 +58,42 @@ url:api地址, params:参数
|
||||||
let resPut = await utils.httpPut(url, params);
|
let resPut = await utils.httpPut(url, params);
|
||||||
url:api地址, params:参数
|
url:api地址, params:参数
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
export const options_json = `{
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "选项1",
|
||||||
|
"value": "1",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"label": "选项3",
|
||||||
|
"value": "3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "选项2",
|
||||||
|
"value": "2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const treeData_json = `{
|
||||||
|
"treeData": [
|
||||||
|
{
|
||||||
|
"label": "选项1",
|
||||||
|
"value": "1",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"label": "选项3",
|
||||||
|
"value": "3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "选项2",
|
||||||
|
"value": "2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`;
|
||||||
Loading…
Reference in New Issue