feat: 完成表格扩展功能

master
shengwen.chen 2 months ago
parent ad14e6d7ee
commit 70f4d52912

@ -1,6 +1,8 @@
<!-- eslint-disable @typescript-eslint/no-explicit-any -->
<script setup lang="ts">
import { inject } from 'vue'
import { addTableRow, addTableCol } from "@/utils/table";
import { handleFindDomById } from "@/utils/index";
import type { IWidget, IWidgetJson } from '@/types/widgetsConfigInterface'
import RendererDom from '@/components/RendererDom.vue'
@ -22,62 +24,39 @@ if (props['itemData']['category'] == 'formItem') {
}
}
// id
const handleFindDomById = (data: any, returnParent = false, parent: any) => {
if (Array.isArray(data)) {
for (const item of data) {
const found: any = handleFindDomById(item, returnParent, data);
if (found) return found;
}
} else {
if (data.id === widgetJson['activeId']) {
console.log(parent);
console.log(data);
return returnParent ? parent : data;
}
//
if (data.subDomList) {
const result: any = handleFindDomById(data.subDomList, returnParent, data);
if (result) return result;
}
}
return null;
}
//
const handleSelectDom = (activeId: any) => {
widgetJson['activeId'] = activeId
const fatherDom = handleFindDomById(widgetJson['subDomList'], activeId, false)
if (fatherDom) {
widgetJson['activeConfig']['activeId'] = fatherDom['id']
widgetJson['activeConfig']['activeDisplayName'] = fatherDom['displayName']
} else {
widgetJson['activeConfig']['activeId'] = ""
widgetJson['activeConfig']['activeDisplayName'] = ""
}
}
//
const handleFindLastDom = () => {
}
//
const handleAddRow = () => {
// widgetJson['activeId'] = 'input-12366'
const res = handleFindDomById(widgetJson['subDomList'], true, null);
console.log(res);
}
//
const handleAddCol = () => {
const fatherDom = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], true)
if (fatherDom) {
widgetJson['activeConfig']['activeId'] = fatherDom['id']
widgetJson['activeConfig']['activeDisplayName'] = fatherDom['displayName']
} else {
widgetJson['activeConfig']['activeId'] = ""
widgetJson['activeConfig']['activeDisplayName'] = ""
}
}
//
const handleDelDom = () => {
}
</script>
<template>
<!-- 针对表格容器 -->
<template v-if="itemData['type'] == 'table'">
<div class="table-container" :class="{ activeDom: itemData['id'] == widgetJson['activeId'] }"
<div class="table-container" :class="{ activeDom: itemData['id'] == widgetJson['activeConfig']['activeId'] }"
:data-id="itemData['id']" @click.stop="handleSelectDom(itemData['id'])">
<table border="1" cellpadding="10" cellspacing="0" class="w-full">
<template v-for="(row, rowIndex) in itemData['subDomList']" :key="rowIndex">
@ -86,19 +65,19 @@ const handleDelDom = () => {
</tr>
</template>
</table>
<template v-if="itemData['id'] == widgetJson['activeId']">
<div class="activeDomWrapper">{{ widgetJson['activeDomName'] }}</div>
<template v-if="itemData['id'] == widgetJson['activeConfig']['activeId']">
<div class="activeDomWrapper">{{ widgetJson['activeConfig']['activeDisplayName'] }}</div>
<div class="fnArea">
<div @click="handleFindLastDom" title="选中父组件">
<div @click.stop="handleFindLastDom" title="选中父组件">
<Icon icon="ion:return-up-back" />
</div>
<div @click="handleAddRow" title="插入新行">
<div @click.stop="addTableRow(widgetJson, 'table-cell')" title="插入新行">
<Icon icon="iconoir:arrow-separate-vertical" />
</div>
<div @click="handleAddCol" title="插入新列">
<div @click.stop="addTableCol(widgetJson, 'table-cell')" title="插入新列">
<Icon icon="iconoir:arrow-separate" />
</div>
<div @click="handleDelDom" title="移除组件">
<div @click.stop="handleDelDom" title="移除组件">
<Icon icon="mi:delete" />
</div>
</div>
@ -109,9 +88,9 @@ const handleDelDom = () => {
<!-- 针对表格列 -->
<template v-if="itemData['type'] == 'table-cell'">
<td v-if="!itemData['merged']" :colspan="itemData['options']['colspan']" :rowspan="itemData['options']['rowspan']"
:data-id="itemData['id']" :class="{ activeDom: widgetJson['activeId'] == itemData['id'] }"
:data-id="itemData['id']" :class="{ activeDom: widgetJson['activeConfig']['activeId'] == itemData['id'] }"
@click.stop="handleSelectDom(itemData['id'])">
<RendererDom v-for="subData in itemData['subDomList']" :key="subData['id']" :itemData="subData" />
<RendererDom v-for="subData in itemData['subDomList'] as IWidget[]" :key="subData['id']" :itemData="subData" />
</td>
</template>
@ -140,4 +119,4 @@ const handleDelDom = () => {
</el-radio-group>
</el-form-item>
</template>
</template>
</template>

@ -37,7 +37,7 @@ export interface IWidget {
// 属性
options: IOption
// 子结构
subDomList?: [IWidget] | [[IWidget]]
subDomList?: IWidget[] | IWidget[][]
// 单元格是否被合并
merged?: boolean
// X轴坐标
@ -49,8 +49,12 @@ export interface IWidget {
export interface IWidgetJson {
// 页面结构
subDomList: IWidget[]
// 当前选中元素ID
activeId: string
// 当前选中元素名称
activeDomName: string
// 选中元素配置
activeConfig: {
// 当前选中元素Id
activeId: string
// 当前选中元素名称
activeDisplayName: string
}
}

@ -1,76 +1,93 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// import melonjson from "./melon.json";
import { containers, basicFields } from "@/utils/widgetsConfig";
import type { IWidget } from '@/types/widgetsConfigInterface'
/**
*
* @param widgetJson
*/
export const initJson = (widgetJson: any) => {
widgetJson['subDomList'] = []
widgetJson['activeId'] = ""
widgetJson['activeDomName'] = ""
// widgetJson['formConfig'] = {}
widgetJson['activeConfig']['activeId'] = ""
widgetJson['activeConfig']['activeDisplayName'] = ""
}
/**
*
* @param addItemDataOrigin
*/
export const formatAddItem = (addItemTypeName: string) => {
let addItemData = null;
let activeId = ""
let activeDisplayName = ""
const allWidgetList = [...containers, ...basicFields];
const targetItem = allWidgetList.filter(item => {
return item['type'] == addItemTypeName
})
if (targetItem['length']) {
addItemData = structuredClone(targetItem[0]);
addItemData['key'] = `${Date.now().toString().slice(-6)}${Math.floor(Math.random() * 900) + 100}`;
addItemData['id'] = `${addItemData['type']}-${addItemData['key']}`
addItemData['subDomList'] = [];
addItemData['options']['name'] = addItemData['id']
activeId = addItemData['id']
activeDisplayName = addItemData['displayName']
}
return {
addItemData,
activeId,
activeDisplayName
}
}
/**
*
* @param addItemDataOrigin
* @param widgetJson JSON
* @param addItemTypeName
* @param fatherId id
* @param widgetJson wJSON
*/
export const addItem = (addItemDataOrigin: any, fatherId = "", widgetJson: any) => {
const { addItemData, activeId, activeDomName } = formatAddItem(addItemDataOrigin)
if (fatherId) { } else {
widgetJson['subDomList'] = [addItemData]
export const addItem = (widgetJson: any, addItemTypeName: any) => {
const { addItemData, activeId, activeDisplayName } = formatAddItem(addItemTypeName)
if (widgetJson['activeConfig']['activeId']) { } else {
widgetJson['subDomList'].push(addItemData)
}
widgetJson['activeId'] = activeId
widgetJson['activeDomName'] = activeDomName
widgetJson['activeConfig']['activeId'] = activeId
widgetJson['activeDisplayName'] = activeDisplayName
}
/**
*
* @param addItemDataOrigin
* id
* @param subDomList widgetJson['subDomList']
* @param returnParent
*/
const formatAddItem = (addItemDataOrigin: any) => {
const addItemData = JSON.parse(JSON.stringify(addItemDataOrigin));
let activeId = '';
let activeDomName = '';
addItemData['key'] = Date.now().toString().slice(-6);
addItemData['id'] = `${addItemData['type']}-${addItemData['key']}`;
activeId = addItemData['id']
activeDomName = addItemData['displayName']
addItemData['options'] = {
name: `table-${addItemData['key']}`,
hidden: false,
customClass: ''
};
// 针对表格 需要初始化子单元格
if (addItemData['type'] == 'table') {
const key = addItemData['key'].slice(-1) + addItemData['key'].slice(0, -1);
// 子单元格
const colItem = {
key: key,
id: `table-cell-${key}`,
category: "container",
type: "table-cell",
displayName: "子单元格",
icon: "Monitor",
merged: false,
subDomList: [],
options: {
name: `table-cell-${key}`,
hidden: false,
colspan: 1,
rowspan: 1,
customClass: ''
export const handleFindDomById = (subDomList: any, activeKey: string, returnParent = false) => {
let found: IWidget | null = null
function dfs(nodes: IWidget[], parent: IWidget | null): boolean {
for (const node of nodes) {
if (node['id'] === activeKey) {
found = returnParent ? parent?.category === 'container' ? parent : null : node
return true
}
};
addItemData['subDomList'] = [[colItem]]
// activeId = colItem['id']
// activeDomName = colItem['displayName']
}
return {
addItemData,
activeId,
activeDomName
// 针对 subDomList 为二维数组或一维数组都做兼容处理
const children = node.subDomList
if (Array.isArray(children)) {
if (Array.isArray(children[0])) {
// 二维数组情况
for (const row of children as IWidget[][]) {
if (dfs(row, node)) return true
}
} else {
// 一维数组情况
if (dfs(children as IWidget[], node)) return true
}
}
}
return false
}
}
dfs(subDomList, null)
return found
}

@ -0,0 +1,83 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { formatAddItem, handleFindDomById } from "@/utils/index";
import type { IWidget, IWidgetJson } from '@/types/widgetsConfigInterface'
/**
*
* @param widgetJson
* @param addItemTypeName
* @param fatherId
*/
export const addTable = (widgetJson: IWidgetJson, addItemTypeName: any) => {
const tableCol = formatAddItem('table-cell');
const { addItemData, activeId, activeDisplayName } = formatAddItem(addItemTypeName);
if (addItemData && tableCol['addItemData']) {
addItemData['subDomList'] = [[tableCol['addItemData']]];
}
if (widgetJson['activeConfig']['activeId']) {
if (widgetJson['activeConfig']['activeId'].startsWith('table-cell')) {
// 单元格里面直接加
const tableJson = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], false);
if (tableJson && addItemData) {
(tableJson['subDomList'] as IWidget[]).push(addItemData)
}
} else if (widgetJson['activeConfig']['activeId'].startsWith('table')) {
// 表格后面加
if (addItemData) {
widgetJson['subDomList'].push(addItemData)
}
} else {
// 寻找上一层单元格加
const tableJson = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], true);
if (tableJson && addItemData) {
(tableJson['subDomList'] as IWidget[]).push(addItemData)
}
}
} else {
if (addItemData) {
widgetJson['subDomList'].push(addItemData)
}
}
widgetJson['activeConfig']['activeId'] = activeId
widgetJson['activeConfig']['activeDisplayName'] = activeDisplayName
}
/**
*
* @param widgetJson
* @param addItemTypeName
* @param fatherId
*/
export const addTableRow = (widgetJson: IWidgetJson, addItemTypeName = 'table-cell') => {
const tableJson = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], false);
if (tableJson) {
const newRow: IWidget[] = []
for (let index = 0; index < tableJson['subDomList'][0]['length']; index++) {
const { addItemData } = formatAddItem(addItemTypeName);
if (addItemData) {
newRow.push(addItemData);
}
}
(tableJson['subDomList'] as IWidget[][]).push(newRow)
}
}
/**
*
* @param widgetJson
* @param addItemTypeName
* @param fatherId
*/
export const addTableCol = (widgetJson: IWidgetJson, addItemTypeName = 'table-cell') => {
const tableJson = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], false);
if (tableJson && tableJson['subDomList']) {
for (let index = 0; index < tableJson['subDomList']['length']; index++) {
const { addItemData } = formatAddItem(addItemTypeName);
if (addItemData) {
const row: IWidget[] = tableJson['subDomList'][index];
row.push(addItemData);
}
}
}
}

@ -6,14 +6,25 @@ export const containers: IWidget[] = [
type: 'table',
displayName: '表格',
icon: 'Monitor',
options: { name: '' }
options: {
name: '',
hidden: false,
customClass: ""
}
},
{
category: 'container',
type: 'table-cell',
displayName: '子单元格',
icon: 'Monitor',
options: { name: '' }
merged: false,
options: {
name: '',
hidden: false,
colspan: 1,
rowspan: 1,
customClass: ""
}
}
]

@ -1,13 +1,17 @@
<script setup lang="ts">
import { provide, reactive } from 'vue'
import { initJson } from '@/utils'
import LeftSide from './subCom/LeftSide.vue'
import NavBar from './subCom/NavBar.vue'
import MainWrapper from './subCom/MainWrapper.vue'
import RightSide from './subCom/RightSide.vue'
const widgetJson = reactive({})
initJson(widgetJson)
const widgetJson = reactive({
subDomList: [],
activeConfig: {
activeId: "",
activeDisplayName: ""
}
})
provide('widgetJson', widgetJson)
</script>

@ -1,7 +1,8 @@
<script setup lang="ts">
import { ref, inject } from 'vue'
import { containers, basicFields } from '@/utils/widgetsConfig'
import { addItem } from '@/utils'
import { addItem } from '@/utils/index'
import { addTable } from '@/utils/table'
import UseIcon from '@/components/UseIcon.vue'
//
@ -23,6 +24,15 @@ const collapseList = ref([
])
//
const widgetJson = inject('widgetJson')
//
const onAddItem = (typeName: string) => {
if (typeName == 'table') {
addTable(widgetJson, typeName)
} else {
addItem(widgetJson, typeName)
}
}
</script>
<template>
@ -35,7 +45,7 @@ const widgetJson = inject('widgetJson')
<el-collapse-item :title="group['display']" :name="group['activeName']">
<div class="flex flex-wrap justify-between content-center px-2">
<template v-for="item in group['subItem']" :key="item['displayName']">
<div class="itemBox" @click="addItem(item, '', widgetJson)">
<div class="itemBox" @click="onAddItem(item['type'])">
<UseIcon :iconName="item['icon']"></UseIcon>
<p>{{ item['displayName'] }}</p>
</div>

Loading…
Cancel
Save