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 = [ ...@@ -67,7 +67,10 @@ export const INLINE_ELEMENTS = [
'CON', 'CON',
'NCON', 'NCON',
'REVST', 'REVST',
'REVEND' 'REVEND',
'EXPD',
'EXPDNAME',
'ITEMNBR'
] ]
// 段落类节点标签(PARA 为英文段落,PARAC 为中文段落) // 段落类节点标签(PARA 为英文段落,PARAC 为中文段落)
...@@ -149,7 +152,8 @@ export const COMPOSITE_CONTAINER_MAP: Record<string, string[]> = { ...@@ -149,7 +152,8 @@ export const COMPOSITE_CONTAINER_MAP: Record<string, string[]> = {
TED: ['TOOLNAME', 'TOOLNBR'], TED: ['TOOLNAME', 'TOOLNBR'],
CON: ['CONNAME', 'CONNBR'], CON: ['CONNAME', 'CONNBR'],
GRAPHIC: ['TITLE'], GRAPHIC: ['TITLE'],
EINDATA: ['EIN'] EINDATA: ['EIN'],
EXPD: ['EXPDNAME', 'CSN', 'ITEMNBR']
} }
// 动态派生出所有复合容器节点标签集 // 动态派生出所有复合容器节点标签集
......
...@@ -226,14 +226,21 @@ export function getMinChildCount(parentTagName: string, childTagName: string): n ...@@ -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 (model.type === 'elementRef' && model.name === childTagName) {
if (isRepeatable) {
return model.occurrence === 'oneOrMore' || model.occurrence === 'once'
? 'oneOrMore'
: 'zeroOrMore'
}
return model.occurrence return model.occurrence
} }
if (model.children) { if (model.children) {
for (const child of model.children) { for (const child of model.children) {
const result = findChildOccurrence(child, childTagName) const result = findChildOccurrence(child, childTagName, isRepeatable)
if (result !== 'unknown') return result if (result !== 'unknown') return result
} }
} }
......
import type { XmlNode } from '@/types/xmlNode' import type { XmlNode } from '@/types/xmlNode'
import { useEditorStore, nodeSelectedRefs } from '@/store/editor' import { useEditorStore, nodeSelectedRefs } from '@/store/editor'
import { LIST_ITEM_TAGS, HEADER_TAGS, ROMAN_LOOKUP } from '../constants' 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' import { useI18n } from 'vue-i18n'
// 警示块级节点集合(WARNING / CAUTION / NOTE),用于注入 insideAlert 上下文 // 警示块级节点集合(WARNING / CAUTION / NOTE),用于注入 insideAlert 上下文
const ALERT_ROOT_TAGS = new Set(ALERT_BLOCK_TAGS) 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 }) { export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | null; isInline?: boolean; insideRefBlock?: boolean }) {
const editorStore = useEditorStore() const editorStore = useEditorStore()
...@@ -55,10 +56,14 @@ export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | nu ...@@ -55,10 +56,14 @@ export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | nu
provide('insideChinese', true) provide('insideChinese', true)
} }
const renderInline = computed(() => {
return props.isInline || INLINE_ELEMENTS_SET.has(props.node.tagName)
})
// 判断是否是列表容器 (需要排除表格、主题、选项以及通过复合 Map 特殊渲染的各种复合父节点/容器) // 判断是否是列表容器 (需要排除表格、主题、选项以及通过复合 Map 特殊渲染的各种复合父节点/容器)
const isContainer = computed(() => { const isContainer = computed(() => {
return ( return (
!props.isInline && !renderInline.value &&
props.node.children && props.node.children &&
props.node.children.length > 0 && props.node.children.length > 0 &&
props.node.tagName !== 'TABLE' && props.node.tagName !== 'TABLE' &&
...@@ -365,6 +370,7 @@ export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | nu ...@@ -365,6 +370,7 @@ export function useDocNodeRenderer(props: { node: XmlNode; parent?: XmlNode | nu
editorStore, editorStore,
isSelected, isSelected,
isContainer, isContainer,
renderInline,
isListItem, isListItem,
isHeaderTag, isHeaderTag,
isInsideAlert, isInsideAlert,
......
...@@ -1145,6 +1145,7 @@ export function useNodeTree( ...@@ -1145,6 +1145,7 @@ export function useNodeTree(
editorStore.setSelectedNodeId(parent.id) editorStore.setSelectedNodeId(parent.id)
editorStore.rebuildNodeMap() editorStore.rebuildNodeMap()
} else { } else {
editorStore.setSelectedNodeId(realNodeId)
editorStore.deleteSelectedNode() editorStore.deleteSelectedNode()
} }
window.$message?.success('删除成功') window.$message?.success('删除成功')
......
...@@ -1699,6 +1699,12 @@ export function useTableEditor(props: { node: XmlNode }) { ...@@ -1699,6 +1699,12 @@ export function useTableEditor(props: { node: XmlNode }) {
return styles 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 { return {
structure, structure,
selectedCellIds, selectedCellIds,
...@@ -1739,6 +1745,7 @@ export function useTableEditor(props: { node: XmlNode }) { ...@@ -1739,6 +1745,7 @@ export function useTableEditor(props: { node: XmlNode }) {
splitCell, splitCell,
insertedRowIds, insertedRowIds,
insertedCellIds, insertedCellIds,
getRowNonEntryChildren,
// 样式计算 // 样式计算
colWidthStyles, colWidthStyles,
tableFrameClass, tableFrameClass,
......
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