树状结构支持数据导入
parent
d61c8dd12a
commit
eeff8ef4f4
|
|
@ -33,9 +33,11 @@
|
|||
@input="updateLabelOrValue('value', label, value, $event.target.value)"
|
||||
class="options-value"
|
||||
/>
|
||||
<a class="options-delete" @click="addTreeNode(value)">
|
||||
<Icon icon="ant-design:folder-add-outlined" />
|
||||
</a>
|
||||
<Tooltip title="添加子级选项" placement="top">
|
||||
<a class="options-delete" @click="addTreeNode(value)">
|
||||
<Icon icon="ant-design:folder-add-outlined" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
<a class="options-delete" @click="deleteTreeNode(value)">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
</a>
|
||||
|
|
@ -46,6 +48,10 @@
|
|||
<Icon icon="ant-design:file-add-outlined" />
|
||||
添加父级选项
|
||||
</a>
|
||||
<a @click="showModal">
|
||||
<Icon icon="ant-design:import-outlined" />
|
||||
导入数据
|
||||
</a>
|
||||
</div>
|
||||
<div v-else-if="['Transfer'].includes(formConfig.currentItem!.component)">
|
||||
<div v-for="(item, index) of formConfig.currentItem!.componentProps![key]" :key="index">
|
||||
|
|
@ -92,30 +98,64 @@
|
|||
</a>
|
||||
</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>
|
||||
|
||||
<script lang="ts">
|
||||
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 { remove, formItemsForEach, generateKey } from '../../../utils';
|
||||
import { CodeEditor, MODE } from '@/components/CodeEditor';
|
||||
import { Upload, Modal, Input, Tooltip } from 'ant-design-vue';
|
||||
import { options_json, treeData_json } from '../../VFormDesign/config/formItemPropsScript';
|
||||
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({
|
||||
name: 'FormOptions',
|
||||
components: { Input, Icon, BasicTree },
|
||||
components: { Input, Icon, BasicTree, CodeEditor, Upload, Modal, Tooltip },
|
||||
// props: {},
|
||||
setup() {
|
||||
const state = reactive({});
|
||||
const { formConfig } = useFormDesignState();
|
||||
|
||||
let key: string = '';
|
||||
|
||||
// 树的ref
|
||||
// 导入窗口
|
||||
const state = reactive({
|
||||
visible: false,
|
||||
json: ``,
|
||||
});
|
||||
// 树的值
|
||||
const treeDataAndOptions = ref<TreeItem[]>([]);
|
||||
|
||||
watch(
|
||||
|
|
@ -124,10 +164,12 @@
|
|||
if (formConfig.value.currentItem?.component == 'TreeSelect') {
|
||||
// 树形选择
|
||||
treeDataAndOptions.value = formConfig.value.currentItem?.componentProps?.treeData;
|
||||
state.json = treeData_json;
|
||||
key = 'treeData';
|
||||
} else if (formConfig.value.currentItem?.component == 'Cascader') {
|
||||
// 级联选择
|
||||
treeDataAndOptions.value = formConfig.value.currentItem?.componentProps?.options;
|
||||
state.json = options_json;
|
||||
key = 'options';
|
||||
} else if (formConfig.value.currentItem?.component == 'Transfer') {
|
||||
// 穿梭框
|
||||
|
|
@ -142,11 +184,11 @@
|
|||
|
||||
// 获取树
|
||||
const treeDataRef = ref<Nullable<TreeActionType>>(null);
|
||||
function getTree(): any {
|
||||
const getTree = (): any => {
|
||||
return unref(treeDataRef);
|
||||
}
|
||||
};
|
||||
// 添加树的父节点、子节点
|
||||
function addTreeNode(value) {
|
||||
const addTreeNode = (value) => {
|
||||
let length = getLength(1, treeDataAndOptions.value);
|
||||
getTree().insertNodeByKey({
|
||||
parentKey: value,
|
||||
|
|
@ -158,14 +200,14 @@
|
|||
push: 'push',
|
||||
});
|
||||
refresh();
|
||||
}
|
||||
};
|
||||
// 删除树的节点
|
||||
function deleteTreeNode(value) {
|
||||
const deleteTreeNode = (value) => {
|
||||
getTree().deleteNodeByKey(value);
|
||||
refresh();
|
||||
}
|
||||
};
|
||||
// 获取树的节点的不重复的默认值
|
||||
function getLength(length, treeDataOrOptions) {
|
||||
const getLength = (length, treeDataOrOptions) => {
|
||||
treeDataOrOptions?.forEach((to) => {
|
||||
if (to.children) {
|
||||
length = getLength(length, to.children);
|
||||
|
|
@ -176,24 +218,84 @@
|
|||
}
|
||||
});
|
||||
return length;
|
||||
}
|
||||
};
|
||||
// 修改树的label或者value
|
||||
function updateLabelOrValue(type, label, value, newLabelOrValue) {
|
||||
const 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 });
|
||||
if (checkRepeat(true, getTree().getTreeData(), newLabelOrValue)) {
|
||||
getTree().updateNodeByKey(value, { label: label, value: newLabelOrValue });
|
||||
} else {
|
||||
message.warning('不能赋给重复的值');
|
||||
}
|
||||
}
|
||||
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);
|
||||
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 = () => {
|
||||
if (!formConfig.value.currentItem?.componentProps?.[key])
|
||||
|
|
@ -230,7 +332,6 @@
|
|||
const deleteOptions = (index: number) => {
|
||||
remove(formConfig.value.currentItem?.componentProps?.[key], index);
|
||||
};
|
||||
|
||||
const addGridOptions = () => {
|
||||
formConfig.value.currentItem?.['columns']?.push({
|
||||
span: 12,
|
||||
|
|
@ -242,7 +343,6 @@
|
|||
|
||||
remove(formConfig.value.currentItem!['columns']!, index);
|
||||
};
|
||||
|
||||
const updateTransferDisabled = (index: number, flag: boolean) => {
|
||||
formConfig.value.currentItem.componentProps[key][index].disabled = flag;
|
||||
};
|
||||
|
|
@ -260,6 +360,11 @@
|
|||
deleteTreeNode,
|
||||
updateLabelOrValue,
|
||||
updateTransferDisabled,
|
||||
handleImportJson,
|
||||
beforeUpload,
|
||||
handleCancel,
|
||||
showModal,
|
||||
MODE,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
@ -290,4 +395,8 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-button {
|
||||
margin: 0 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -58,3 +58,42 @@ url:api地址, params:参数
|
|||
let resPut = await utils.httpPut(url, 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