Compare commits

...

4 Commits

1
.gitignore vendored

@ -20,6 +20,7 @@ coverage
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
!.vscode/settings.json
.idea .idea
*.suo *.suo
*.ntvs* *.ntvs*

@ -0,0 +1,35 @@
{
//
"editor.formatOnType": false,
"editor.formatOnSave": true,
"editor.formatOnPaste": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
//
"editor.tabSize": 2,
"editor.snippetSuggestions": "top",
"editor.guides.bracketPairs": "active",
"editor.suggestSelection": "first",
"editor.acceptSuggestionOnCommitCharacter": false,
"editor.quickSuggestions": {
"other": true,
"comments": true,
"strings": true
},
//
"editor.wordWrap": "on",
"editor.wordWrapColumn": 180,
"editor.rulers": [180],
//
"files.autoSave": "off",
// Git
"git.confirmSync": false,
//
"workbench.startupEditor": "newUntitledFile"
}

20
package-lock.json generated

@ -8,6 +8,7 @@
"name": "smdoceditor", "name": "smdoceditor",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@iconify/vue": "^4.3.0",
"axios": "^1.8.4", "axios": "^1.8.4",
"element-plus": "^2.9.7", "element-plus": "^2.9.7",
"pinia": "^3.0.1", "pinia": "^3.0.1",
@ -1337,6 +1338,25 @@
"url": "https://github.com/sponsors/nzakas" "url": "https://github.com/sponsors/nzakas"
} }
}, },
"node_modules/@iconify/types": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="
},
"node_modules/@iconify/vue": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@iconify/vue/-/vue-4.3.0.tgz",
"integrity": "sha512-Xq0h6zMrHBbrW8jXJ9fISi+x8oDQllg5hTDkDuxnWiskJ63rpJu9CvJshj8VniHVTbsxCg9fVoPAaNp3RQI5OQ==",
"dependencies": {
"@iconify/types": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/cyberalien"
},
"peerDependencies": {
"vue": ">=3"
}
},
"node_modules/@isaacs/cliui": { "node_modules/@isaacs/cliui": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",

@ -14,6 +14,7 @@
"format": "prettier --write src/" "format": "prettier --write src/"
}, },
"dependencies": { "dependencies": {
"@iconify/vue": "^4.3.0",
"axios": "^1.8.4", "axios": "^1.8.4",
"element-plus": "^2.9.7", "element-plus": "^2.9.7",
"pinia": "^3.0.1", "pinia": "^3.0.1",

@ -1,15 +1,22 @@
<!-- eslint-disable @typescript-eslint/no-explicit-any -->
<script setup lang="ts"> <script setup lang="ts">
import { inject, defineProps } from 'vue' import { inject } from 'vue'
import type { IWidget } from '@/types/widgetsConfigInterface' import { addTableRow, addTableCol, mergeRightCol, mergeRow, cancelMergeCol, } from "@/utils/table";
import { handleFindDomById } from "@/utils/index";
import type { IWidget, IWidgetJson } from '@/types/widgetsConfigInterface'
import RendererDom from '@/components/RendererDom.vue' import RendererDom from '@/components/RendererDom.vue'
const props = defineProps<{ const props = defineProps<{
itemData: IWidget itemData: IWidget,
rowIndex: number,
colIndex: number,
colLength: number
}>() }>()
// //
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const docForm = inject('docForm') as any const docForm = inject('docForm') as any
const widgetJson = inject('widgetJson') as IWidgetJson
// //
if (props['itemData']['category'] == 'formItem') { if (props['itemData']['category'] == 'formItem') {
@ -19,24 +26,135 @@ if (props['itemData']['category'] == 'formItem') {
docForm[props['itemData']['options']['name']] = props['itemData']['options']['defaultValue'] || 0 docForm[props['itemData']['options']['name']] = props['itemData']['options']['defaultValue'] || 0
} }
} }
//
const handleSelectDom = (activeId: any) => {
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 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'] = ""
}
}
// ID
const handleDelDom = (node: any = widgetJson) => {
if (!node || !Array.isArray(node.subDomList)) return;
const deleteFromList = (list: any) => {
for (let i = list.length - 1; i >= 0; i--) {
const item = list[i];
if (Array.isArray(item)) {
//
deleteFromList(item);
//
if (item.length === 0) {
list.splice(i, 1);
}
} else if (item && item.id === widgetJson['activeConfig']['activeId']) {
//
list.splice(i, 1);
} else if (item && item.subDomList) {
//
handleDelDom(item);
}
}
};
deleteFromList(node.subDomList);
}
</script> </script>
<template> <template>
<!-- 针对表格容器 --> <!-- 针对表格容器 -->
<template v-if="itemData['type'] == 'table'"> <template v-if="itemData['type'] == 'table'">
<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"> <table border="1" cellpadding="10" cellspacing="0" class="w-full">
<template v-for="(row, rowIndex) in itemData['rows']" :key="rowIndex"> <template v-for="(row, rowIndex) in itemData['subDomList']" :key="rowIndex">
<tr> <tr>
<RendererDom v-for="subData in row['cols']" :key="subData['id']" :itemData="subData" /> <RendererDom v-for="(subData, index) in row" :key="subData['id']" :itemData="subData" :rowIndex="rowIndex"
:colIndex="index" :colLength="Array.isArray(row) ? row['length'] : 1" />
</tr> </tr>
</template> </template>
</table> </table>
<template v-if="itemData['id'] == widgetJson['activeConfig']['activeId']">
<div class="activeDomWrapper">{{ widgetJson['activeConfig']['activeDisplayName'] }}</div>
<div class="fnArea">
<div @click.stop="handleFindLastDom" title="选中父组件">
<Icon icon="ion:return-up-back" />
</div>
<div @click.stop="addTableRow(widgetJson, rowIndex, true, false, 'table-cell')" title="插入新行">
<Icon icon="iconoir:arrow-separate-vertical" />
</div>
<div @click.stop="addTableCol(widgetJson, colIndex, true, false, 'table-cell')" title="插入新列">
<Icon icon="iconoir:arrow-separate" />
</div>
<div @click.stop="() => { debugger; handleDelDom() }" title="移除组件">
<Icon icon="mi:delete" />
</div>
</div>
</template>
</div>
</template> </template>
<!-- 针对表格列 --> <!-- 针对表格列 -->
<template v-if="itemData['type'] == 'table-cell'"> <template v-if="itemData['type'] == 'table-cell'">
<td v-if="!itemData['merged']" :colspan="itemData['options']['colspan']" :rowspan="itemData['options']['rowspan']"> <td v-if="!itemData['options']['merged']" :colspan="itemData['options']['colspan']"
<RendererDom v-for="subData in itemData['widgetList']" :key="subData['id']" :itemData="subData" /> :rowspan="itemData['options']['rowspan']" :data-id="itemData['id']"
:class="{ activeDom: widgetJson['activeConfig']['activeId'] == itemData['id'] }"
@click.stop="handleSelectDom(itemData['id'])">
<RendererDom v-for="(subData, index) in itemData['subDomList'] as IWidget[]" :key="subData['id']"
:itemData="subData" :rowIndex="rowIndex" :colIndex="index" :colLength="1" />
<template v-if="itemData['id'] == widgetJson['activeConfig']['activeId']">
<div class="activeDomWrapper">{{ widgetJson['activeConfig']['activeDisplayName'] }}</div>
<div class="fnArea">
<div @click.stop="handleFindLastDom" title="选中父组件">
<Icon icon="ion:return-up-back" />
</div>
<div class="moreBox">
<Icon icon="hugeicons:menu-square" />
<div class="actionBox">
<ul>
<li @click.stop="addTableCol(widgetJson, colIndex, false, true, 'table-cell')">插入左侧列</li>
<li @click.stop="addTableCol(widgetJson, colIndex, true, true, 'table-cell')">插入右侧列</li>
<li @click.stop="addTableRow(widgetJson, rowIndex, false, true, 'table-cell')">插入上方行</li>
<li @click.stop="addTableRow(widgetJson, rowIndex, true, true, 'table-cell')">插入下方行</li>
<el-divider />
<li :class="{ readOnly: colIndex == colLength - 1 || itemData['options']['colspan'] == colLength }"
@click="mergeRightCol(widgetJson, rowIndex, colIndex)">合并右侧单元格</li>
<li :class="{ readOnly: colLength == 1 || colIndex > 0 || itemData['options']['colspan'] == colLength }"
@click="mergeRow(widgetJson, rowIndex)">合并整行
</li>
<el-divider />
<li>合并下方单元格</li>
<li>合并整列</li>
<el-divider />
<li :class="{ readOnly: itemData['options']['colspan'] && itemData['options']['colspan'] == 1 }"
@click="cancelMergeCol(widgetJson, rowIndex, colIndex)">撤销行合并
</li>
<li>撤销列合并</li>
<el-divider />
<li>删除整列</li>
<li>删除整行</li>
</ul>
</div>
</div>
</div>
</template>
</td> </td>
</template> </template>

@ -1,5 +1,7 @@
/* eslint-disable vue/multi-word-component-names */
import { createApp } from 'vue' import { createApp } from 'vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import { Icon } from '@iconify/vue'
import ElementPlus from 'element-plus' import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css' import 'element-plus/dist/index.css'
import './assets/css/main.css' import './assets/css/main.css'
@ -8,6 +10,7 @@ import router from './router'
const app = createApp(App) const app = createApp(App)
app.use(ElementPlus) app.use(ElementPlus)
app.component('Icon', Icon)
app.use(createPinia()) app.use(createPinia())
app.use(router) app.use(router)

@ -2,7 +2,7 @@ export interface IOption {
// 元素唯一标识符 // 元素唯一标识符
name: string name: string
// 隐藏状态 // 隐藏状态
hidden: boolean hidden?: boolean
// 自定义类名 // 自定义类名
customClass?: string customClass?: string
// 标签名称 // 标签名称
@ -17,13 +17,15 @@ export interface IOption {
colspan?: number colspan?: number
// 列合并 // 列合并
rowspan?: number rowspan?: number
// 单元格是否被合并
merged?: boolean
} }
export type ICategory = 'container' | 'formItem' export type ICategory = 'container' | 'formItem'
export interface IWidget { export interface IWidget {
// 元素唯一标识符 // 元素唯一标识符
key?: number key?: string
// 元素id // 元素id
id?: string id?: string
// 类别 // 类别
@ -34,16 +36,25 @@ export interface IWidget {
displayName: string displayName: string
// 图标 // 图标
icon: string icon: string
// 子类
rows?: [{ cols: [IWidget] }]
// 属性 // 属性
options: IOption options: IOption
// 子结构 // 子结构
widgetList?: [] subDomList?: IWidget[] | IWidget[][]
// 单元格是否被合并 // X轴坐标
merged?: boolean axisX?: number
// Y轴坐标
axisY?: number
} }
export interface IWidgetJson { export interface IWidgetJson {
widgetList: IWidget[] // 页面结构
subDomList: IWidget[]
// 选中元素配置
activeConfig: {
// 当前选中元素Id
activeId: string
// 当前选中元素名称
activeDisplayName: string
}
} }

@ -1,16 +1,93 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* 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 * @param widgetJson
*/ */
export const initJson = (widgetJson: any) => { export const initJson = (widgetJson: any) => {
widgetJson['widgetList'] = [] widgetJson['subDomList'] = []
// widgetJson['formConfig'] = {} widgetJson['activeConfig']['activeId'] = ""
widgetJson['activeConfig']['activeDisplayName'] = ""
} }
export const addItem = (fatherId = "", widgetJson: any) => { /**
if (fatherId) { } else { *
widgetJson['widgetList'] = [melonJson] * @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 widgetJson JSON
* @param addItemTypeName
* @param fatherId id
*/
export const addItem = (widgetJson: any, addItemTypeName: any) => {
const { addItemData, activeId, activeDisplayName } = formatAddItem(addItemTypeName)
if (widgetJson['activeConfig']['activeId']) { } else {
widgetJson['subDomList'].push(addItemData)
}
widgetJson['activeConfig']['activeId'] = activeId
widgetJson['activeDisplayName'] = activeDisplayName
} }
/**
* id
* @param subDomList widgetJson['subDomList']
* @param returnParent
*/
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
}
// 针对 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
} }

@ -5,9 +5,8 @@
"type": "table", "type": "table",
"displayName": "表格", "displayName": "表格",
"icon": "Monitor", "icon": "Monitor",
"rows": [ "subDomList": [
{ [
"cols": [
{ {
"key": 47005, "key": 47005,
"id": "table-cell-12332", "id": "table-cell-12332",
@ -16,7 +15,7 @@
"displayName": "子单元格", "displayName": "子单元格",
"icon": "Monitor", "icon": "Monitor",
"merged": false, "merged": false,
"widgetList": [ "subDomList": [
{ {
"key": 47016, "key": 47016,
"id": "input-12355", "id": "input-12355",
@ -30,7 +29,6 @@
"keyName": "", "keyName": "",
"label": "名称", "label": "名称",
"labelAlign": "", "labelAlign": "",
"type": "text",
"defaultValue": "", "defaultValue": "",
"hidden": false, "hidden": false,
"customClass": "", "customClass": "",
@ -38,7 +36,6 @@
} }
} }
], ],
"rows": [],
"options": { "options": {
"name": "table-cell-12332", "name": "table-cell-12332",
"cellWidth": "", "cellWidth": "",
@ -57,7 +54,7 @@
"displayName": "子单元格", "displayName": "子单元格",
"icon": "Monitor", "icon": "Monitor",
"merged": false, "merged": false,
"widgetList": [ "subDomList": [
{ {
"key": 47016, "key": 47016,
"id": "input-12366", "id": "input-12366",
@ -71,14 +68,12 @@
"keyName": "", "keyName": "",
"label": "年龄", "label": "年龄",
"labelAlign": "", "labelAlign": "",
"type": "number",
"defaultValue": "", "defaultValue": "",
"hidden": false, "hidden": false,
"customClass": "" "customClass": ""
} }
} }
], ],
"rows": [],
"options": { "options": {
"name": "table-cell-12344", "name": "table-cell-12344",
"cellWidth": "", "cellWidth": "",
@ -89,10 +84,8 @@
"customClass": "" "customClass": ""
} }
} }
] ],
}, [
{
"cols": [
{ {
"key": 47015, "key": 47015,
"id": "table-cell-12344", "id": "table-cell-12344",
@ -101,7 +94,7 @@
"displayName": "子单元格", "displayName": "子单元格",
"icon": "Monitor", "icon": "Monitor",
"merged": false, "merged": false,
"widgetList": [ "subDomList": [
{ {
"key": 47016, "key": 47016,
"id": "radio-12377", "id": "radio-12377",
@ -115,7 +108,6 @@
"keyName": "", "keyName": "",
"label": "性别", "label": "性别",
"labelAlign": "", "labelAlign": "",
"type": "text",
"defaultValue": "1", "defaultValue": "1",
"hidden": false, "hidden": false,
"customClass": "", "customClass": "",
@ -132,7 +124,6 @@
} }
} }
], ],
"rows": [],
"options": { "options": {
"name": "table-cell-12344", "name": "table-cell-12344",
"cellWidth": "", "cellWidth": "",
@ -152,7 +143,6 @@
"icon": "Monitor", "icon": "Monitor",
"merged": true, "merged": true,
"widgetList": [], "widgetList": [],
"rows": [],
"options": { "options": {
"name": "table-cell-12399", "name": "table-cell-12399",
"cellWidth": "", "cellWidth": "",
@ -164,7 +154,6 @@
} }
} }
] ]
}
], ],
"options": { "options": {
"name": "", "name": "",

@ -0,0 +1,180 @@
/* 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'] = tableCol['activeId']
widgetJson['activeConfig']['activeDisplayName'] = tableCol['activeDisplayName']
// widgetJson['activeConfig']['activeId'] = activeId
// widgetJson['activeConfig']['activeDisplayName'] = activeDisplayName
}
/**
*
* @param widgetJson
* @param addItemTypeName
* @param fatherId
*/
export const addTableRow = (widgetJson: IWidgetJson, rowIndex: number, isPush: boolean, returnParent: boolean, addItemTypeName = 'table-cell') => {
const tableJson = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], returnParent);
if (tableJson) {
const newRow: IWidget[] = []
for (let index = 0; index < tableJson['subDomList'][0]['length']; index++) {
const { addItemData } = formatAddItem(addItemTypeName);
if (addItemData) {
newRow.push(addItemData);
}
}
if (isPush) {
(tableJson['subDomList'] as IWidget[][]).splice(rowIndex + 1, 0, newRow)
} else {
(tableJson['subDomList'] as IWidget[][]).splice(rowIndex, 0, newRow)
}
}
}
/**
*
* @param widgetJson
* @param colIndex
* @param isPush
* @param returnParent
* @param addItemTypeName
*/
export const addTableCol = (widgetJson: IWidgetJson, colIndex: number, isPush: boolean, returnParent: boolean, addItemTypeName = 'table-cell') => {
const tableJson = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], returnParent);
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];
if (isPush) {
row.splice(colIndex + 1, 0, addItemData);
} else {
row.splice(colIndex, 0, addItemData);
}
}
}
}
}
/**
*
* @param widgetJson
* @param rowIndex
* @param colIndex
*/
export const mergeRightCol = (widgetJson: IWidgetJson, rowIndex: number, colIndex: number) => {
const tableJson = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], true);
if (tableJson && tableJson['subDomList']) {
let isStartmerge = false;
let mergeIndexArr: number[] = [];
let targetItem = { colspan: 0 };
const rowData = tableJson['subDomList'][rowIndex];
for (let index = 0; index < rowData['length']; index++) {
if (isStartmerge) {
if (mergeIndexArr.includes(index)) {
const nextItem = rowData[index]['options'] as any;
if (nextItem['merged']) {
targetItem['colspan'] = targetItem['colspan'] + nextItem['colspan']
}
nextItem['colspan'] = 1;
nextItem['merged'] = true;
}
}
if (colIndex == index) {
isStartmerge = true;
targetItem = rowData[index]['options'];
rowData[index]['options']['colspan']++;
mergeIndexArr = Array.from({ length: rowData[index]['options']['colspan'] }, (_, i) => index + i);
}
}
}
}
/**
*
* @param widgetJson
* @param rowIndex
*/
export const mergeRow = (widgetJson: IWidgetJson, rowIndex: number) => {
const tableJson = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], true);
if (tableJson && tableJson['subDomList']) {
const rowData = tableJson['subDomList'][rowIndex];
for (let index = 0; index < rowData['length']; index++) {
if (index == 0) {
rowData[index]['options']['colspan'] = rowData['length'];
} else {
const nextItem = rowData[index]['options'] as any;
nextItem['colspan'] = 1;
nextItem['merged'] = true;
}
}
}
}
/**
*
* @param widgetJson
* @param rowIndex
* @param colIndex
*/
export const cancelMergeCol = (widgetJson: IWidgetJson, rowIndex: number, colIndex: number) => {
const tableJson = handleFindDomById(widgetJson['subDomList'], widgetJson['activeConfig']['activeId'], true);
if (tableJson && tableJson['subDomList']) {
let isStartmerge = false;
let mergeIndexArr: number[] = [];
const rowData = tableJson['subDomList'][rowIndex];
for (let index = 0; index < rowData['length']; index++) {
if (isStartmerge) {
if (mergeIndexArr.includes(index)) {
(rowData[index]['options'] as any)['merged'] = false;
(rowData[index]['options'] as any)['colspan'] = 1;
}
}
if (colIndex == index) {
isStartmerge = true;
mergeIndexArr = Array.from({ length: rowData[index]['options']['colspan'] }, (_, i) => index + i);
(rowData[index]['options'] as any)['colspan'] = 1;
}
}
}
}

@ -3,22 +3,27 @@ import type { IWidget } from "@/types/widgetsConfigInterface"
export const containers: IWidget[] = [ export const containers: IWidget[] = [
{ {
category: 'container', category: 'container',
type: 'el-table', type: 'table',
displayName: '表格', displayName: '表格',
icon: 'Monitor', icon: 'Monitor',
options: { options: {
name: '', name: '',
hidden: false, hidden: false,
customClass: ""
} }
}, },
{ {
category: 'container', category: 'container',
type: 'el-table-column', type: 'table-cell',
displayName: '子单元格', displayName: '子单元格',
icon: 'Monitor', icon: 'Monitor',
options: { options: {
name: '', name: '',
merged: false,
hidden: false, hidden: false,
colspan: 1,
rowspan: 1,
customClass: ""
} }
} }
] ]
@ -30,29 +35,20 @@ export const basicFields: IWidget[] = [
type: 'el-input', type: 'el-input',
displayName: '输入框', displayName: '输入框',
icon: 'Folder', icon: 'Folder',
options: { options: { name: '' }
name: '',
hidden: false,
},
}, },
{ {
category: 'formItem', category: 'formItem',
type: 'el-input-number', type: 'el-input-number',
displayName: '数值输入框', displayName: '数值输入框',
icon: 'Folder', icon: 'Folder',
options: { options: { name: '' }
name: '',
hidden: false,
},
}, },
{ {
category: 'formItem', category: 'formItem',
type: 'el-redio', type: 'el-redio',
displayName: ' 单选项', displayName: ' 单选项',
icon: 'Folder', icon: 'Folder',
options: { options: { name: '' }
name: '',
hidden: false,
},
}, },
] ]

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

@ -1,7 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, inject } from 'vue' import { ref, inject } from 'vue'
import { containers, basicFields } from '@/utils/widgetsConfig' 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' import UseIcon from '@/components/UseIcon.vue'
// //
@ -23,6 +24,15 @@ const collapseList = ref([
]) ])
// //
const widgetJson = inject('widgetJson') const widgetJson = inject('widgetJson')
//
const onAddItem = (typeName: string) => {
if (typeName == 'table') {
addTable(widgetJson, typeName)
} else {
addItem(widgetJson, typeName)
}
}
</script> </script>
<template> <template>
@ -35,9 +45,7 @@ const widgetJson = inject('widgetJson')
<el-collapse-item :title="group['display']" :name="group['activeName']"> <el-collapse-item :title="group['display']" :name="group['activeName']">
<div class="flex flex-wrap justify-between content-center px-2"> <div class="flex flex-wrap justify-between content-center px-2">
<template v-for="item in group['subItem']" :key="item['displayName']"> <template v-for="item in group['subItem']" :key="item['displayName']">
<div <div class="itemBox" @click="onAddItem(item['type'])">
class="basis-2/5 flex justify-between items-center border border-solid border-[#c1c1c1] px-2 mb-4"
@click="addItem('', widgetJson)">
<UseIcon :iconName="item['icon']"></UseIcon> <UseIcon :iconName="item['icon']"></UseIcon>
<p>{{ item['displayName'] }}</p> <p>{{ item['displayName'] }}</p>
</div> </div>
@ -60,5 +68,26 @@ section {
padding-left: 8px; padding-left: 8px;
} }
} }
.itemBox {
flex-basis: 40%;
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #c1c1c1;
padding: 0 8px;
margin-bottom: 16px;
cursor: pointer;
&:hover {
border-color: var(--el-color-primary);
background-color: #F1F2F3;
:deep(.el-icon) {
color: var(--el-color-primary);
}
}
}
} }
</style> </style>

@ -13,12 +13,118 @@ provide('docForm', docForm)
</script> </script>
<template> <template>
<section> <section class="editWrapper">
<h1 class="text-sky-300">{{ widgetJson }}</h1> <h1 class="text-sky-300">{{ widgetJson }}</h1>
<el-form :model="docForm" label-width="80px"> <el-form :model="docForm" label-width="80px">
<template v-for="item in widgetJson['widgetList']" :key="item['id']"> <template v-for="(item, index) in widgetJson['subDomList']" :key="item['id']">
<RendererDom :itemData="item" /> <RendererDom :itemData="item" :rowIndex="index" :colIndex="index" :colLength="1" />
</template> </template>
</el-form> </el-form>
</section> </section>
</template> </template>
<style lang="scss" scoped>
.editWrapper {
--active-color: #409EFF;
:deep(.table-container) {
box-sizing: border-box;
padding: 8px;
border: 1px dashed var(--el-color-primary);
table {
border: none;
td {
border: 1px dashed var(--el-color-primary);
position: relative;
.moreBox {
position: relative;
&:hover {
.actionBox {
display: block;
}
}
.actionBox {
// display: none;
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
padding-top: 12px;
z-index: 10;
ul {
list-style: none;
padding-left: 0;
background-color: #fff;
border: 1px solid #c1c1c1;
padding-bottom: 2px;
li {
list-style: none;
white-space: nowrap;
color: #666;
font-size: 12px;
padding: 4px 8px;
&:not(.readOnly):hover {
background-color: #c9effd;
color: var(--el-color-primary);
}
&.readOnly {
color: #c0c4cc;
cursor: not-allowed;
}
}
}
}
}
}
}
&.activeDom,
.activeDom {
outline: 2px solid var(--active-color);
padding: 16px;
position: relative;
}
&.activeDomWrapper,
.activeDomWrapper {
position: absolute;
top: 0;
left: 0;
padding: 0px 12px;
background-color: var(--active-color);
color: #fff;
font-size: 13px;
}
&.fnArea,
.fnArea {
position: absolute;
bottom: 0;
right: 0;
padding: 2px 4px;
background-color: var(--active-color);
color: #fff;
font-size: 18px;
display: flex;
align-items: center;
div {
margin: 0 4px;
cursor: pointer;
}
}
}
}
</style>

Loading…
Cancel
Save