Commit b6df8dc3 by pangchong

feat(editor): 优化文档节点渲染及表格编辑器功能

- 新增 EXPD、EXPDNAME、ITEMNBR 标签及其复合容器映射
- 优化 dtdManager 子元素出现约束判断,支持父节点重复属性传递
- DocNodeRenderer 组件中新增 renderInline 计算属性替代 isInline
- 增强引用(REFINT、REFEXT、GRPHCREF)内容的混合渲染和编辑功能
- 为 EXPD 标签定制统一配置化渲染方案
- TableEditor 组件支持表头及表体行中非 ENTRY 子节点展示
- 调整行选中样式及操作按钮位置,提升行选择和删除体验
- TableEditor functionals 新增获取行非 ENTRY 子节点方法,优化逻辑复用
- NodeTree 组件删除节点时修正选中节点行为,防止选中错误
parent feaf36ba
......@@ -67,7 +67,10 @@ export const INLINE_ELEMENTS = [
'CON',
'NCON',
'REVST',
'REVEND'
'REVEND',
'EXPD',
'EXPDNAME',
'ITEMNBR'
]
// 段落类节点标签(PARA 为英文段落,PARAC 为中文段落)
......@@ -149,7 +152,8 @@ export const COMPOSITE_CONTAINER_MAP: Record<string, string[]> = {
TED: ['TOOLNAME', 'TOOLNBR'],
CON: ['CONNAME', 'CONNBR'],
GRAPHIC: ['TITLE'],
EINDATA: ['EIN']
EINDATA: ['EIN'],
EXPD: ['EXPDNAME', 'CSN', 'ITEMNBR']
}
// 动态派生出所有复合容器节点标签集
......
......@@ -226,14 +226,21 @@ export function getMinChildCount(parentTagName: string, childTagName: string): n
/**
* 在内容模型树中查找子元素的出现约束
*/
function findChildOccurrence(model: DtdContentModel, childTagName: string): string {
function findChildOccurrence(model: DtdContentModel, childTagName: string, parentRepeatable = false): string {
const isRepeatable = parentRepeatable || model.occurrence === 'zeroOrMore' || model.occurrence === 'oneOrMore'
if (model.type === 'elementRef' && model.name === childTagName) {
if (isRepeatable) {
return model.occurrence === 'oneOrMore' || model.occurrence === 'once'
? 'oneOrMore'
: 'zeroOrMore'
}
return model.occurrence
}
if (model.children) {
for (const child of model.children) {
const result = findChildOccurrence(child, childTagName)
const result = findChildOccurrence(child, childTagName, isRepeatable)
if (result !== 'unknown') return result
}
}
......
import type { XmlNode } from '@/types/xmlNode'
import { useEditorStore, nodeSelectedRefs } from '@/store/editor'
import { LIST_ITEM_TAGS, HEADER_TAGS, ROMAN_LOOKUP } from '../constants'
import { ALERT_AND_EFF_TAGS, ALERT_BLOCK_TAGS, COMPOSITE_CONTAINER_MAP } from '@/configs/xmlTags'
import { ALERT_AND_EFF_TAGS, ALERT_BLOCK_TAGS, COMPOSITE_CONTAINER_MAP, INLINE_ELEMENTS } from '@/configs/xmlTags'
import { useI18n } from 'vue-i18n'
// 警示块级节点集合(WARNING / CAUTION / NOTE),用于注入 insideAlert 上下文
const ALERT_ROOT_TAGS = new Set(ALERT_BLOCK_TAGS)
const INLINE_ELEMENTS_SET = new Set(INLINE_ELEMENTS)
export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | null; isInline?: boolean; insideRefBlock?: boolean }) {
const editorStore = useEditorStore()
......@@ -55,10 +56,14 @@ export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | nu
provide('insideChinese', true)
}
const renderInline = computed(() => {
return props.isInline || INLINE_ELEMENTS_SET.has(props.node.tagName)
})
// 判断是否是列表容器 (需要排除表格、主题、选项以及通过复合 Map 特殊渲染的各种复合父节点/容器)
const isContainer = computed(() => {
return (
!props.isInline &&
!renderInline.value &&
props.node.children &&
props.node.children.length > 0 &&
props.node.tagName !== 'TABLE' &&
......@@ -365,6 +370,7 @@ export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | nu
editorStore,
isSelected,
isContainer,
renderInline,
isListItem,
isHeaderTag,
isInsideAlert,
......
......@@ -1145,6 +1145,7 @@ export function useNodeTree(
editorStore.setSelectedNodeId(parent.id)
editorStore.rebuildNodeMap()
} else {
editorStore.setSelectedNodeId(realNodeId)
editorStore.deleteSelectedNode()
}
window.$message?.success('删除成功')
......
......@@ -1699,6 +1699,12 @@ export function useTableEditor(props: { node: XmlNode }) {
return styles
}
const getRowNonEntryChildren = (rowId: string): XmlNode[] => {
const rowNodeObj = store.nodeMap.get(rowId)
if (!rowNodeObj || !rowNodeObj.node.children) return []
return rowNodeObj.node.children.filter((c) => c.tagName !== 'ENTRY')
}
return {
structure,
selectedCellIds,
......@@ -1739,6 +1745,7 @@ export function useTableEditor(props: { node: XmlNode }) {
splitCell,
insertedRowIds,
insertedCellIds,
getRowNonEntryChildren,
// 样式计算
colWidthStyles,
tableFrameClass,
......
......@@ -28,9 +28,23 @@
class="transition-all"
:class="[isNodeSelected(structure.theadId) ? 'outline outline-2 outline-primary outline-offset-[-2px] bg-primary/5' : '']"
>
<template v-for="(row, rIdx) in structure.theadRows" :key="row.id">
<!-- 如果 ROW 拥有非 ENTRY 子节点 (如 EFFECT, CONEFFECT, REVST, REVEND) -->
<tr v-if="getRowNonEntryChildren(row.id).length > 0" class="bg-fill-1">
<th class="p-1 border border-divider bg-fill-4"></th>
<th :colspan="structure.cols" class="p-1 border border-divider text-left align-middle space-x-1 font-normal">
<DocNodeRenderer
v-for="child in getRowNonEntryChildren(row.id)"
:key="child.id"
:node="child"
:parent="editorStore.nodeMap.get(row.id)?.node"
is-inline
/>
</th>
<th class="p-1 border border-divider bg-fill-4"></th>
</tr>
<tr
v-for="(row, rIdx) in structure.theadRows"
:key="row.id"
:data-node-id="row.id"
class="border-b border-divider hover:bg-fill-3 group transition-colors cursor-pointer transition-all"
:class="[
......@@ -90,6 +104,7 @@
</CommonButton>
</th>
</tr>
</template>
</thead>
<!-- TBODY 渲染 -->
......@@ -99,9 +114,23 @@
class="transition-all"
:class="[isNodeSelected(structure.tbodyId) ? 'outline outline-2 outline-primary outline-offset-[-2px] bg-primary/5' : '']"
>
<template v-for="(row, rIdx) in structure.tbodyRows" :key="row.id">
<!-- 如果 ROW 拥有非 ENTRY 子节点 (如 EFFECT, CONEFFECT, REVST, REVEND) -->
<tr v-if="getRowNonEntryChildren(row.id).length > 0" class="bg-fill-1">
<td class="p-1 border border-divider bg-fill-4"></td>
<td :colspan="structure.cols" class="p-1 border border-divider text-left align-middle space-x-1">
<DocNodeRenderer
v-for="child in getRowNonEntryChildren(row.id)"
:key="child.id"
:node="child"
:parent="editorStore.nodeMap.get(row.id)?.node"
is-inline
/>
</td>
<td class="p-1 border border-divider"></td>
</tr>
<tr
v-for="(row, rIdx) in structure.tbodyRows"
:key="row.id"
:data-node-id="row.id"
class="border-b border-divider hover:bg-fill-3 group transition-colors cursor-pointer transition-all"
:class="[
......@@ -161,6 +190,7 @@
</CommonButton>
</td>
</tr>
</template>
<!-- 列删除快捷按钮行 -->
<tr class="hover:bg-transparent">
......@@ -240,6 +270,7 @@ const {
batchDeleteColumns,
insertedRowIds,
insertedCellIds,
getRowNonEntryChildren,
colWidthStyles,
tableFrameClass,
getCellStyle
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment