Files
IETM-web/src/components/wang.vue

5928 lines
194 KiB
Vue
Raw Normal View History

2025-07-25 13:28:47 +08:00
<template>
<div class="editor-container">
2025-09-15 09:58:52 +08:00
<!-- 左侧操作框 -->
<div class="leftDiv" v-if='isLoadXml' :style="{'maxHeight':leftHeight+'px','minHeight':'100px'}" >
<div id='navMain'>
<!-- 操作栏 -->
<div class="operation-buttons">
<!-- 节点搜索框 -->
<div style="margin:10px auto;">
<el-input
placeholder="输入节点名称搜索"
v-model="searchText"
class="search-input"
clearable
@clear="handleSearchClear"
@keyup.enter="handleSearch">
<el-button slot="append" icon="el-icon-search" @click="handleSearch"></el-button>
</el-input>
</div>
<!-- 操作栏按钮 -->
<img class='iconImg' title='新建' src="../assets/addIcon.png" @click="showAddDialog('create')" v-if='isLoadXml' alt="">
<img class='iconImg' title='编辑' src="../assets/editIcon.png" @click="showAddDialog('edit')" v-if='currentNode' alt="">
<img class='iconImg' title='保存' src="../assets/saveIcon.png" @click="saveNowDmcClick" v-if='currentNode' alt="">
<img class='iconImg' title='删除' src="../assets/delIcon.png" @click="deleteDialog" v-if='currentNode' alt="">
<!-- <el-button title='新建' class='el-icon-circle-plus-outline' type="primary" size="medium" :disabled='!isLoadXml' @click="showAddDialog('create')"></el-button>
<el-button title='编辑' class='el-icon-edit' type="primary" size="medium" @click="showAddDialog('edit')" :disabled="!currentNode"></el-button>
<el-button title='保存' class='el-icon-files' type="primary" size="medium" @click="saveNowDmcClick" :disabled="!currentNode"></el-button>
<el-button title='删除' class='el-icon-delete' type="danger" size="medium" @click="deleteDialog" :disabled="!currentNode"></el-button> -->
<!-- <el-button type="primary" @click="draggableVis = true">打开可拖拽对话框</el-button> -->
2025-07-28 17:01:13 +08:00
2025-09-15 09:58:52 +08:00
<!-- <el-button type="danger" @click="queryMaxId">查询当前最大ID</el-button> -->
</div>
<div class="tree-container">
<!-- <router-link to="/wangG3">Wang2 2页面</router-link> -->
<!-- <button
@click="openWangG2InNewWindow"
class="bg-primary text-white px-4 py-2 rounded-md hover:bg-primary/90 transition-colors"
>
打开wangG2页面
</button> -->
<!-- 1. 添加触发按钮 -->
<!-- <button
class="check-version-btn"
@click="handleCheckVersion"
>
检查版本并显示修改按钮
</button> -->
<!-- <button @click="GetPreviewDataFun">
全屏的蒙版
</button> -->
<div style=" text-align: left;
font-size: 20px;
font-weight: bold;" @click='clearNodeState' >目录</div>
<!-- <el-button @click="test">替1换</el-button> -->
<!-- <el-tree
class="elTree"
ref="tree"
:data="treeData"
node-key="id"
highlight-current
:props="defaultProps"
:filter-node-method="filterNode"
@node-click="handleNodeClick"
@node-contextmenu="handleRightClick"
:expand-on-click-node="false">
<span class="custom-tree-node" slot-scope="{ node, data }">
<span :class="{ 'highlight': isHighlight(data) }">{{ node.label }}</span>
</span>
</el-tree> -->
<!-- :props="defaultProps" -->
<!-- :default-checked-keys="[5]" -->
<!-- <el-tree
class="elTree"
ref="tree"
:data="treeData"
node-key="id"
icon-class="el-icon-arrow-right"
:default-expand-all="true"
:props="defaultProps"
:filter-node-method="filterNode"
@node-click="handleNodeClick"
@node-contextmenu="handleNodeContextMenu"
:expand-on-click-node="false"
:highlight-current="true"
>
<span slot-scope="{ data }">
<template v-if="data.children">
<div v-if="data.children.length > 0" >
<i class="el-icon-folder" :style="'font-size: 14px; padding: 0 5px 0 5px'"/>
<span :title='data.id+"-"+data.name' :class="{ 'highlight': isHighlight(data) }">
{{data.name }}</span>
</div>
<div v-else>
<i class="leaf-node-line"></i>
<i class="el-icon-document" :style="'padding: 0 5px 0 5px'"/>
<span :title='data.id+"-"+data.name' :class="{ 'highlight': isHighlight(data) }">
{{data.name }}</span>
</div>
</template>
<template v-else>
<div style="margin-left: 0px;">
<i class="leaf-node-line"></i>
<i class="el-icon-document" :style="'padding: 0 5px 0 5px'"></i>
<i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="deployment-unit" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M888.3 693.2c-42.5-24.6-94.3-18-129.2 12.8l-53-30.7V523.6c0-15.7-8.4-30.3-22-38.1l-136-78.3v-67.1c44.2-15 76-56.8 76-106.1 0-61.9-50.1-112-112-112s-112 50.1-112 112c0 49.3 31.8 91.1 76 106.1v67.1l-136 78.3c-13.6 7.8-22 22.4-22 38.1v151.6l-53 30.7c-34.9-30.8-86.8-37.4-129.2-12.8-53.5 31-71.7 99.4-41 152.9 30.8 53.5 98.9 71.9 152.2 41 42.5-24.6 62.7-73 53.6-118.8l48.7-28.3 140.6 81c6.8 3.9 14.4 5.9 22 5.9s15.2-2 22-5.9L674.5 740l48.7 28.3c-9.1 45.7 11.2 94.2 53.6 118.8 53.3 30.9 121.5 12.6 152.2-41 30.8-53.6 12.6-122-40.7-152.9zm-673 138.4a47.6 47.6 0 0 1-65.2-17.6c-13.2-22.9-5.4-52.3 17.5-65.5a47.6 47.6 0 0 1 65.2 17.6c13.2 22.9 5.4 52.3-17.5 65.5zM522 463.8zM464 234a48.01 48.01 0 0 1 96 0 48.01 48.01 0 0 1-96 0zm170 446.2l-122 70.3-122-70.3V539.8l122-70.3 122 70.3v140.4zm239.9 133.9c-13.2 22.9-42.4 30.8-65.2 17.6-22.8-13.2-30.7-42.6-17.5-65.5s42.4-30.8 65.2-17.6c22.9 13.2 30.7 42.5 17.5 65.5z"></path></svg></i>
<span :title='data.id+"-"+data.name' :class="{ 'highlight': isHighlight(data) }">
{{ data.name }}</span>
</div>
</template>
</span>
</el-tree>-->
<!-- 2. 树组件修改节点渲染逻辑 -->
<el-tree
class="elTree"
ref="tree"
:data="treeData"
node-key="id"
icon-class="el-icon-arrow-right"
:default-expand-all="true"
:props="defaultProps"
:filter-node-method="filterNode"
@node-click="handleNodeClick"
@node-contextmenu="handleNodeContextMenu"
:expand-on-click-node="false"
:highlight-current="true"
>
<!-- 节点自定义渲染插槽 -->
<span slot-scope="{ data }">
<!-- 1原有节点图标+名称渲染逻辑 -->
<template v-if="data.children && data.children.length > 0">
<!-- 父节点有子节点 -->
<div>
<i class="el-icon-folder" :style="'font-size: 14px; padding: 0 5px 0 5px'"/>
<i class="el-icon-warning changeState" @click.stop="handleModify(data)" v-if="showModifyBtn && hasMinorVersion01(data)" :style="'font-size: 14px; padding: 0 5px 0 5px'"/>
<span :title="data.id + '-' + data.name" :class="{ 'highlight': isHighlight(data) }">
{{ data.name }}
</span>
<!-- 关键条件显示修改检查状态开启 + 当前节点/子节点有minorVersion:01 -->
<!-- <span
class="modify-btn"
v-if="showModifyBtn && hasMinorVersion01(data)"
@click.stop="handleModify(data)"
>
修改
</span> -->
</div>
</template>
<template v-else>
<!-- 叶子节点无子节点 -->
<div style="margin-left: 0px;">
<i class="leaf-node-line"></i>
<i class="el-icon-document" :style="'padding: 0 5px 0 5px'"></i>
<!-- <svg viewBox="64 64 896 896" data-icon="deployment-unit" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
<path d="M888.3 693.2c-42.5-24.6-94.3-18-129.2 12.8l-53-30.7V523.6c0-15.7-8.4-30.3-22-38.1l-136-78.3v-67.1c44.2-15 76-56.8 76-106.1 0-61.9-50.1-112-112-112s-112 50.1-112 112c0 49.3 31.8 91.1 76 106.1v67.1l-136 78.3c-13.6 7.8-22 22.4-22 38.1v151.6l-53 30.7c-34.9-30.8-86.8-37.4-129.2-12.8-53.5 31-71.7 99.4-41 152.9 30.8 53.5 98.9 71.9 152.2 41 42.5-24.6 62.7-73 53.6-118.8l48.7-28.3 140.6 81c6.8 3.9 14.4 5.9 22 5.9s15.2-2 22-5.9L674.5 740l48.7 28.3c-9.1 45.7 11.2 94.2 53.6 118.8 53.3 30.9 121.5 12.6 152.2-41 30.8-53.6 12.6-122-40.7-152.9zm-673 138.4a47.6 47.6 0 0 1-65.2-17.6c-13.2-22.9-5.4-52.3 17.5-65.5a47.6 47.6 0 0 1 65.2 17.6c13.2 22.9 5.4 52.3-17.5 65.5zM522 463.8zM464 234a48.01 48.01 0 0 1 96 0 48.01 48.01 0 0 1-96 0zm170 446.2l-122 70.3-122-70.3V539.8l122-70.3 122 70.3v140.4zm239.9 133.9c-13.2 22.9-42.4 30.8-65.2 17.6-22.8-13.2-30.7-42.6-17.5-65.5s42.4-30.8 65.2-17.6c22.9 13.2 30.7 42.5 17.5 65.5z"></path>
</svg> -->
<!-- <i :style="'font-size: 13px; padding: 0 5px 0 5px'">
</i> -->
<i class="el-icon-warning changeState" @click.stop="handleModify(data)" v-if="showModifyBtn && hasMinorVersion01(data)" :style="'font-size: 14px; padding: 0 5px 0 5px'"/>
<span :title="data.id + '-' + data.name" :class="{ 'highlight': isHighlight(data) }">
{{ data.name }}
</span>
<!-- 关键叶子节点同样条件显示修改 -->
<!-- <span
class="modify-btn"
v-if="showModifyBtn && hasMinorVersion01(data)"
@click.stop="handleModify(data)"
>
修改
</span> -->
</div>
</template>
</span>
</el-tree>
<div class="mask" v-if='dmcChangeState'></div>
</div>
</div>
<!-- <div id='nav'>
<nav-temp />
</div> -->
</div>
<div class="rightDiv">
<div class="toolbar" style="display:none;" >
<button @click='dmcIsChange'>判断富文本内容是否更改</button>
<button @click="ceshi()">测试拼接地址</button>
<button @click='jzml'>加载目录</button>
<button @click='catalogueAnalysis(treeData)'>存目录</button>
<!-- <button @click='catalogueAnalysis(treeData)'>拼接目录字符串</button> -->
<input type="text" v-model="val1">
<button @click='loadFWBFile'> 读取富文本</button>
<button @click="insertImage()">上传图片4</button>
<button @click="insertVideo()">上传视频</button>
<button @click="insertAudio()">上传音频</button>
<button @click="loadAllContent">加载所有内容</button>
<button @click="processHeadingIds">生成目录</button>
<button @click="openDialog">查看图片</button>
<!-- generateTOC -->
<button @click="enableContextMenu">启用右键菜单功能</button>
<button @click="logTocItems">打印目录数据</button>
<!-- <input type="color" v-model="colors"> -->
<!-- <button @click="deleteAllVideos">删除所有视频</button> -->
<button @click="loadFwb">加载到富文本框内</button>
<!-- <button @click='test'>测试</button> -->
2025-07-28 17:01:13 +08:00
</div>
2025-09-15 09:58:52 +08:00
<div class="button-group" style="display:none;">
<button @click="generateXml(false)">保存内容到XML</button>
<button @click="navXml">保存目录到XML</button>
<button @click="loadXml">Load XML</button>
<input type="file" ref="fileInput" @change="handleFileUpload" style="display: none" accept=".xml" />
</div>
<div class="s1000d-editor-container">
<!-- 工具栏 -->
<div class="toolbar" style="display:none;">
<button @click="exportToS1000D" class="tool-btn">导出S1000D XML</button>
<label for="xml-upload" class="tool-btn">导入S1000D XML</label>
<input
id="xml-upload"
type="file"
accept=".xml"
@change="handleS1000DUpload"
style="display: none"
/>
<button @click="validateCurrentXml" class="tool-btn">验证XML</button>
</div>
<!-- 编辑器区域 -->
<div class="editor-area" style="display:none;">
<div ref="editor" class="editor"></div>
<div class="xml-preview" style="display: none">
<h3>XML预览</h3>
<pre>{{ formattedXmlPreview }}</pre>
</div>
</div>
</div>
<div id="main" class="content-preview" style="display:none;"> {{editorHeight}}</div>
<!-- v-show='currentNode' -->
<div id="editor" v-show='currentNode' :style="{height:editorHeight+'px'}" ref="editor" :class="{ editor2Opacity: dialogVisibleNav||dialogVisibleNavDel|| dialogVisible||dialogVisibleImg||dialogVisibleRight||dialogVisibleTips||dialogVisibleBland||dialogVisibleHistory||dialogVisibleTips2 }"></div>
<div v-show="!currentNode && isLoadXml">请打开所要编辑的的章节目录</div>
<div v-show="!isLoadXml">请加载所要编辑的文档目录</div>
<!-- <div>{{tempDivData.innerHTML}}</div>
<div id="editor2" ref="editor2"></div> -->
<!-- <input type="text" v-model="val2"> -->
<!-- <button @click="sendMessageToHost('李四','张三')">传递</button> -->
</div>
<el-dialog
title="弹窗操作"
:visible.sync="dialogVisibleRight"
width='580px'
class="custom-dialog"
:before-close="handleClose">
<div class="attribute-selection">
<el-button @click="showAddDialog('edit')">编辑目录</el-button>
<el-button @click='showBand'>绑定资源</el-button>
<el-button @click="showHistory">历史版本</el-button>
<el-button @click="deleteDialog" type="danger">删除目录</el-button>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisibleRight = false">关闭</el-button>
<!-- <el-button type="primary" @click="catalogueAnalysis(treeData)">选择绑定资源</el-button> -->
</span>
</el-dialog>
<el-dialog
title="历史版本"
:visible.sync="dialogVisibleHistory"
width='80%'
class="custom-dialog"
:before-close="handleClose">
<div class="attribute-selection" v-if='historyData'>
<div class='zyBox' style="text-align: left;" v-for="(item,index) in historyData" :key="index">
<p>版本名称{{item.Name}}</p>
<p>修改时间{{timeChange(item.CreationTime)}}</p>
<p>文件路径{{item.Path}}</p>
<el-button @click='lookHistoryFile(item.Path)'>查看历史版本</el-button>
2025-07-28 17:01:13 +08:00
</div>
2025-09-15 09:58:52 +08:00
</div>
<div class="attribute-selection" v-if='historyData.length == 0'>
暂无历史版本
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisibleHistory = false">关闭</el-button>
<!-- <el-button type="primary" @click="catalogueAnalysis(treeData)">选择绑定资源</el-button> -->
</span>
</el-dialog>
<!-- 右键菜单弹窗 -->
<el-dialog
title="绑定资源"
class="custom-dialog"
:visible.sync="dialogVisibleBland"
width="50%"
:before-close="handleClose">
<div class="attribute-selection" v-if="currentNode&&currentNode.attributes.length > 0">
<p>已绑资源</p>
<!-- 属性展示区域 -->
<div v-if='currentNode' class='bindMain'>
<div class="zyBox" v-for="(item,index) in currentNode.attributes" :key="index">
<p>{{item}}</p>
<el-button type="danger" size="mini" @click="deleteAttr(item,index)" :disabled="!currentNode">删除</el-button>
</div>
</div>
</div>
<div v-else>暂未绑定资源</div>
<span slot="footer" class="dialog-footer">
<el-button @click='saveAttributes'>选择绑定资源</el-button>
<el-button @click="dialogVisibleBland = false">关闭</el-button>
<!-- <el-button type="primary" @click="catalogueAnalysis(treeData)">选择绑定资源</el-button> -->
</span>
</el-dialog>
<!-- <div
v-if="contextMenu.visible"
class="context-menu"
:style="{
left: `${contextMenu.x}px`,
top: `${contextMenu.y}px`
}"
@click.stop
>
<div
v-for="option in menuOptions"
:key="option"
class="menu-item"
:class="{ active: contextMenu.selectedParam === option }"
@click="selectMenuOption(option)"
>
{{ option }}
</div>
</div> -->
<el-dialog
title="自定义标题"
:visible.sync="dialogVisible"
class="custom-dialog"
width="50%">
<div>
<h3>自定义内容</h3>
2025-07-28 17:01:13 +08:00
</div>
2025-09-15 09:58:52 +08:00
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">关闭</el-button>
</span>
</el-dialog>
<el-dialog
:visible.sync="dialogVisibleImg"
fullscreen
class="custom-dialog"
custom-class="image-preview-dialog"
:show-close="false"
>
<!-- 图片容器 -->
<div class="image-container" :style="{height: containerHeight}">
<img
ref="previewImage"
:src="currentImage"
:style="{
transform: `scale(${scale}) rotate(${rotation}deg)`,
cursor: 'pointer',
transition: 'transform 0.3s ease',
maxWidth: '100%',
width: '50%'
}"
@mousedown="startDrag"
>
</div>
<!-- 固定控制栏 -->
<div class="control-bar">
<button @click="zoomIn" class="action-btn">
<i class="el-icon-zoom-in"></i>
<span>放大</span>
<div class="liquid-effect"></div>
</button>
<button @click="zoomOut" class="action-btn">
<i class="el-icon-zoom-out"></i>
<span>缩小</span>
<div class="liquid-effect"></div>
</button>
<button @click="rotate" class="action-btn">
<i class="el-icon-zoom-out"></i>
<span>旋转图片</span>
<div class="liquid-effect"></div>
</button>
<button @click="reset" class="action-btn">
<i class="el-icon-zoom-out"></i>
<span>重置图片</span>
<div class="liquid-effect"></div>
</button>
<button @click="dialogVisibleImg = false" class="action-btn">
<i class="el-icon-zoom-out"></i>
<span>关闭</span>
<div class="liquid-effect"></div>
</button>
<div class="scale-display">
缩放: {{ (scale * 100).toFixed(0) }}%
</div>
</div>
</el-dialog>
<el-dialog
:title="navIsAdd=='create'?'新建目录':'编辑目录'"
:visible.sync="dialogVisibleNav"
width="80%"
class="custom-dialog"
:before-close="handleClose"
>
<el-form :model="form" :rules="rules" ref="form">
<el-button type="primary" v-if='navIsAdd == "create"' @click="loadFileBtn">加载文件</el-button>
<el-button type="primary" v-if='showView' @click="viewLoadDMFile">预览文件</el-button>
<el-row :gutter="20">
<el-form-item label="目录名称" prop="name">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="责任合作单位">
<el-input v-model="form.contributor" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="创作单位">
<el-input v-model="form.creator" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="适用性信息">
<el-input v-model="form.applicabilityInformation" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="质量验证">
<el-input v-model="form.qualityVerification" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="权限">
<el-input v-model="form.permission" autocomplete="off"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="版本信息">
<el-input v-model="form.version" disabled autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="发布时间">
<el-input v-model="form.date" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="语言">
<el-input v-model="form.language" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="密级">
<el-input v-model="form.level" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="是否使用加载原文档数据" v-if='showView'>
<el-radio v-model="isLoadOldDmc" label="1">使用</el-radio>
<el-radio v-model="isLoadOldDmc" label="2">不使用</el-radio>
</el-form-item>
</el-col>
</el-row>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisibleNav = false"> </el-button>
<el-button type="primary" v-if='isLoadOldDmc == "1"' @click="confirmAdd(false)">加载确定</el-button>
<el-button v-if='navIsAdd == "create"&&isLoadOldDmc == "2"' type="primary" @click="confirmAdd(true)"> </el-button>
<el-button v-if='navIsAdd == "edit"' type="primary" @click="confirmEdit()"> </el-button>
</span>
</el-dialog>
<el-dialog
title="删除章节"
:visible.sync="dialogVisibleNavDel"
width="30%"
class="custom-dialog"
>
<div>
是否确定删除{{currentNode?currentNode.name:''}}?
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisibleNavDel = false"> </el-button>
<el-button type="primary" @click="deleteNode"> </el-button>
</span>
</el-dialog>
<el-dialog
title="删除资源"
:visible.sync="dialogVisibleAttrDel"
width="30%"
class="custom-dialog"
>
<div>
是否确定删除{{nowAttr.name}}的绑定?
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisibleAttrDel = false"> </el-button>
<el-button type="primary" @click="deleteAttrSure"> </el-button>
</span>
</el-dialog>
<el-dialog
title="提示"
:visible.sync="dialogVisibleTips"
width="30%"
class="custom-dialog"
:before-close="handleClose2">
<span>是否保存当前编辑内容1</span>
<span slot="footer" class="dialog-footer">
<el-button @click="saveNowDmc('noSave')">不保存</el-button>
<el-button type="primary" @click="saveNowDmc('save')">保存</el-button>
</span>
</el-dialog>
<el-dialog
title="提示"
:visible.sync="dialogVisibleTips2"
width="30%"
class="custom-dialog"
:before-close="saveNowDmc2">
<span>是否保存当前编辑内容2</span>
<span slot="footer" class="dialog-footer">
<el-button @click="saveNowDmc2('noSave')">不保存</el-button>
<el-button type="primary" @click="saveNowDmc2('save')">保存</el-button>
</span>
</el-dialog>
<div
v-if="contextMenuVisible"
class="custom-context-menu"
:style="{ left: contextMenuLeft + 'px', top: contextMenuTop + 'px' }"
>
<div class="menu-item" @click="handleMenuClick('a')">编辑目录</div>
<div class="menu-item" @click="handleMenuClick('b')">绑定资源</div>
<div class="menu-item" @click="handleMenuClick('c')">版本定稿</div>
<div class="menu-item" @click="handleMenuClick('d')">历史版本</div>
<div class="menu-item" @click="handleMenuClick('e')">删除目录</div>
<div class="menu-divider"></div>
<div class="menu-item" @click="contextMenuVisible = false">关闭</div>
</div>
<el-dialog
title="文件预览"
:visible.sync="maskViewShow"
width="90%"
class="custom-dialog viewDialog"
:before-close="handleCloseMask"
>
<div class="loadContent" v-html="neirong">
</div>
</el-dialog>
<!-- <div class="maskView" v-if='maskViewShow' :style="{width:windowW+'px',height:windowH+'px','backgroundColor':'grba(0,0,0,.5)'}">
<div>
screen高度{{screenHeight}}
screen宽度{{screenWidth}}
</div>
<div>
window高度{{windowH}}
window宽度{{windowW}}
</div>
<el-button @click='maskViewShow = false' style="right: 0px;position: absolute;top: 0;">关闭</el-button>
</div> -->
2025-07-25 13:28:47 +08:00
</div>
</template>
<script>
2025-09-15 09:58:52 +08:00
import WangEditor from 'wangeditor';
// import icon1 from '@/assets/jgIcon.png' // 第一个按钮的图标
// import icon2 from '@/assets/jsIcon.png' // 第二个按钮的图标
// import navTemp from './navTemp.vue';
2025-07-28 17:01:13 +08:00
// import '@yaireo/colorpicker/dist/colorpicker.min.css';
2025-09-15 09:58:52 +08:00
// import '@yaireo/colorpickehandleMessageFromDotNetr'
2025-07-25 13:28:47 +08:00
// import axios from 'axios';
// window.handleMessageFromDotNet = function(msg) {
// alert("Received message from C#: " + msg);
// }
export default {
name: 'RichTextEditor',
2025-09-15 09:58:52 +08:00
components: {
// navTemp
},
2025-07-25 13:28:47 +08:00
data() {
return {
2025-09-15 09:58:52 +08:00
imgUrlJg: require('../assets/jgIcon.png'),
imgUrlJs: require('../assets/jsIcon.png'),
editorHeight:'',
historyVsPath:'',
leftHeight:'0',
isLoadXml:false,//是否加载DMC
neirong:'',//预览内容
isLoadOldDmc: "1", // 初始值需与 options 中的 value 类型一致
options: [
{ value: 1, label: '使用' },
{ value: 2, label: '不使用' }
],
showView:false,//是否显加载原文档
tempDivData1:'',
viewLis:'',
viewXml:'',
viewXmlContent:'',
maskViewShow:false,
// 屏幕整体尺寸(设备屏幕尺寸)
screenWidth: 0,
screenHeight: 0,
// 浏览器窗口尺寸
windowW: 0,
windowH: 0,
// 用于防抖的定时器
resizeTimer: null,
// 要传递的数据
dataToSend: {
id: 1001,
name: '测试数据',
timestamp: new Date().getTime(),
content: '这是从主页面传递到wangG2.vue的数据',
status: 'active',
details: {
author: '系统管理员',
priority: 'high'
}
},
// 保存对新窗口的引用
wangG2Window: null,
// 用于检查窗口状态的定时器
checkWindowTimer: null,
draggableVis:false,
showModifyBtn: false, // 控制是否显示【修改】的开关按钮点击后设为true
mId:'',
isClick:true,
loadFileDMC:'',//加载的DMC文件里面的content内容
loadFileXML:'',//加载的DMC文件内容
historyData:[],//历史版本列表信息
contextMenuVisible: false, // 右键菜单是否可见
contextMenuLeft: 0, // 右键菜单left位置
contextMenuTop: 0, // 右键菜单top位置
contextMenuNode: null, // 右键点击的节点数据
dmcChangeState:false,
windowHeight:'',
windowWidth:'',
nowAttr:{//当前操作的资源
name:'',
index:''
},
dialogVisibleAttrDel:false,//删除弹窗
dialogVisibleTips2:false,//
dialogVisibleTips:false,//删除提示弹窗
contentXmlStr:'',//当前富文本字符串
getDmName:'',//当前DM文件名。
isNavEdit:false,//是否在目录编辑状态。
allImgType://暂定当前的图片类型
new Set([
"bmp", "jpg", "jpeg", "png", "tif", "gif", "pcx", "tga", "exif", "fpx", "svg", "psd", "cdr", "pcd", "dxf", "ufo", "eps", "ai", "raw", "WMF", "webp", "avif", "apng"
]),
allMusicType:new Set([//暂定当前的音频类型
"mp3", "aac", "wav", "flac", "alac", "wma"
]),
allVideoType:new Set([//暂定当前的视频类型
"mp4", "avi", "mkv", "mov", "flv", "wmv", "webm", "mpeg"
]),
otherType:new Set(['wrl','jltf']),
lisenPath:'',//当前内置服务器端口
//目录
domNameStr:[//拼接文档名
'DMC-','test2','-A-P4-20-00-00A-','A-A_000_00','_zh_cn'
],
lastClick:'',//上一次点击的目录id
nowClick:'',//当前点击的目录id
nowMaxId:'',//最大文档id
catalogueString:'',//目录字符串
navIsAdd:'',//判断当前目录操作类型
searchText:'',//搜索目录章节
dialogVisibleNav:false,//目录弹窗
dialogVisibleRight:false,//右键绑定弹窗
dialogVisibleBland:false,//绑定源弹窗
dialogVisibleHistory:false,//历史版本弹窗
dialogVisibleNavDel:false,//删除弹窗
selectedAttributes: [], // 当前选中的属性
treeData: [//基础目录数据
// { id: '00001', name: '根节点1根节点1根节点1根节点1根节点1根节点1', children: [
// {
// id: '00003', name: '根节点3根节点3根节点3根节点3根节点3', children: [{
// id: '00004', name: '根节点4根节点4根节点4根节点4根节点4', children: [],
// attributes: [] // 示例:已绑定的属性
// }]
// }
// ], attributes: [] // 示例:已绑定的属性
// },
// { id:'00002', name: '根节点2根节点2根节点2根节点2根节点2', children: [
// {id: '00005', name: '根节点5根节点5根节点5根节点5根节点5', children: [ { id:'00002', name: '根节点2根节点2根节点2根节点2根节点2', children: [
// {id: '00005', name: '根节点5根节点5根节点5根节点5根节点5', children: [ { id:'00002', name: '根节点2根节点2根节点2根节点2根节点2', children: [
// {id: '00005', name: '根节点5根节点5根节点5根节点5根节点5', children: [ { id:'00002', name: '根节点2根节点2根节点2根节点2根节点2', children: [
// {id: '00005', name: '根节点5根节点5根节点5根节点5根节点5', children: []}
// ] }]}
// ] }]}
// ] }]}
// ] }
],
treeData2:[
{
id: '00001',
name: '装备概况',
attributes: ['金', '木', '水'],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: [
{
id: '00002',
name: '方舱概述',
attributes: [],
lagreVersion: '001',
minorVersion: '01',
children: [
{
id: '00003',
name: '功能介绍',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00004',
name: '规格介绍',
attributes: ['水'],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
}
]
},
{
id: '00005',
name: '舱体',
attributes: ['火', '土'],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00006',
name: '机身转运小车',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00007',
name: '无人机部件存储装置',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00008',
name: '机身转运小车',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00009',
name: '附件及工具',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: [
{
id: '00010',
name: '灭火器',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00011',
name: '应急灯',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00012',
name: '土木工具',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00013',
name: '机械工具',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
}
]
}
]
},
{
id: '00013',
name: '使用说明',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: [
{
id: '00014',
name: '无人机出舱',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00015',
name: '无人机入舱',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
},
{
id: '00016',
name: '灭火器使用',
attributes: [],
formMes: {},
lagreVersion: '001',
minorVersion: '01',
children: []
}
]
}
],
defaultProps: {//树结构
children: 'children',
label: 'name'
},
currentNode: null,//当前节点
form: {//默认目录信息
name: '',
version:'000',
date:'',
language:'',
level:'',
contributor:'',
creator:'',
applicabilityInformation:'',
qualityVerification:'',
permission:''
// version:'001',
// date:'2046-12-12',
// language:'zn_cn',
// level:'公开',
// contributor:'责任合作单位',
// creator:'创作单位',
// applicabilityInformation:'适用性信息',
// qualityVerification:'质量验证',
// permission:'10'
},
rules: {
name: [
{ required: true, message: '请输入节点名称', trigger: 'blur' },
{ min: 1, max: 40, message: '长度在 1 到 40 个字符', trigger: 'blur' }
]
},
//弹窗
dialogVisible:false,//自定义弹窗
idLength:10,//暂无用
contextMenu: {//暂无用
visible: false,
x: 0,
y: 0,
selectedElement: null,
selectedParam: ''
},
isContextMenuEnabled: false,//暂无用
itemParams: {}, //暂无用
// 存储元素的参数 {elementId: param}
// 模拟弹框参数
//查看图片begin
dialogVisibleImg:false,//暂无用
images: [//暂无用
'https://ww2.sinaimg.cn/mw690/007ut4Uhly1hx4v37mpxcj30u017cgrv.jpg',
'https://example.com/image2.jpg'
],
currentImage: '',//暂无用
scale: 1,//暂无用
rotation: 0,//暂无用
isDragging: false,//暂无用
startX: 0,//暂无用
startY: 0,//暂无用
translateX: 0,//暂无用
translateY: 0,//暂无用
containerHeight: '90vh', //暂无用 动态控制高度
//查看图片end
oldSelector:[//基础标签模块
'div','code','hr','br','h1,h2,h3,h4','p','img','table','a','ul','ol','blockquote','pre',
],
newSelector:[//替换后的标签模块
'pDiv','pCode','pHr','pBr','pTitle','pDiv','pImg','pTable','pA','pUl','pOl','pBlockquote','pPre',
],
tempDivData:{//当前富文本str
innerHTML:'五'
},
colors:'',//暂无用
val1:'',//暂无用
val2:'',//暂无用
message: '',//暂无用
2025-07-25 13:28:47 +08:00
editor: null,
editor2:null,
2025-09-15 09:58:52 +08:00
editorContent: '',//富文本内容
editorContent2:'',
xmlPreview: '',
2025-07-28 17:01:13 +08:00
validationResult: {
valid: false,
message: '',
details: ''
},
// 简单的S1000D模板配置
dmConfig: {
dmc: {
modelIdentCode: 'AAA',
systemDiffCode: 'BBB',
systemCode: 'CCC',
subSystemCode: 'DDD',
subSubSystemCode: 'EEE',
assyCode: 'FFF',
disassyCode: 'GGG',
disassyCodeVariant: 'HHH',
infoCode: 'III',
infoCodeVariant: 'JJJ',
itemLocationCode: 'KKK'
},
issueInfo: {
2025-09-15 09:58:52 +08:00
issueNumber: '000',
inWork: '00',
2025-07-28 17:01:13 +08:00
issueDate: new Date().toISOString().split('T')[0]
},
language: 'zh-CN'
}
}
},
2025-09-15 09:58:52 +08:00
computed: {
2025-07-28 17:01:13 +08:00
formattedXmlPreview() {
if (!this.xmlPreview) return '暂无XML预览';
// 简单格式化XML显示
return this.xmlPreview
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br>')
.replace(/\s/g, '&nbsp;');
2025-07-25 13:28:47 +08:00
}
},
mounted() {
2025-09-15 09:58:52 +08:00
// 初始化尺寸
this.getWindowSize()
// 绑定 resize 事件
window.addEventListener('resize', this.handleResize)
// 确保DOM已加载
this.$nextTick(() => {
this.initEditor()
})
// this.initEditor()
2025-07-25 13:28:47 +08:00
// this.initEditor2();
// window.receiveMessageFromWpf = this.receiveMessageFromWpf;
console.log('chrome对象是否存在:', !!window.chrome);
console.log('webview对象是否存在:', !!window.chrome?.webview);
2025-09-15 09:58:52 +08:00
window.removeEventListener('SelectFilePathSend', this.handleFilePathSend);
// 再绑定新的监听器
window.addEventListener('SelectFilePathSend', this.handleFilePathSend);
window.addEventListener('SelectFilePathSend', this.SendLisentPathFun);
2025-07-25 13:28:47 +08:00
},
beforeDestroy() {
// 销毁编辑器
if (this.editor) {
this.editor.destroy()
}
2025-09-15 09:58:52 +08:00
// 清理定时器
if (this.checkWindowTimer) {
clearInterval(this.checkWindowTimer)
}
console.log('监听器开始移除:', Date.now()) // 打印移除时间戳
window.removeEventListener('resize', this.handleResize)
window.removeEventListener('FrontLoadDM', this.FrontLoadDM_g1)
window.removeEventListener('handleMessageFromDotNet', this.fun1)
window.removeEventListener('SelectFilePathSend', this.SendLisentPathFun)
2025-07-25 13:28:47 +08:00
},
2025-09-15 09:58:52 +08:00
methods: {
//清除节点选中
clearNodeState() {
// 通过ref获取el-tree实例
const tree = this.$refs.tree;
if (tree) {
// 取消当前选中状态
tree.setCurrentKey(null);
// 如果需要清除所有节点的选中状态(包括多选情况)
// 先获取所有选中的节点
const selectedNodes = tree.getCheckedNodes();
// 取消所有节点的选中状态
selectedNodes.forEach(node => {
tree.setChecked(node.id, false);
});
// 可选触发节点点击事件的回调传递null表示无选中节点
// this.handleNodeClick(null);
}
},
// 测试接收数据
fun1(e) {
console.log("方法1",e,e.detail)
},
// 获取服务端口地址
SendLisentPathFun(e) {
console.log('服务端口地址信息1:',e,e.detail)
this.lisenPath = e.detail;
},
//接收富文本内容信息
FrontLoadDM_g1(e) {
console.log("加载文档数据 1111")
console.log('FrontLoadDM 参数接收111:',e,e.detail)
// this.editorShow = true;
let xmlContent = e.detail.trim();
if (xmlContent.startsWith('"')) {
xmlContent = xmlContent.substring(1);
}
// 检查并去除结尾的双引号
if (xmlContent.endsWith('"')) {
xmlContent = xmlContent.substring(0, xmlContent.length - 1);
}
// console.log("xmlContent",xmlContent)
const parser = new DOMParser()
const xmlDoc = parser.parseFromString(xmlContent, "text/xml")
console.log("xmlDoc",xmlDoc)
const contentNodes = xmlDoc.getElementsByTagName('content')[0];
console.log("xmlDoc.getElementsByTagName('content')",xmlDoc.getElementsByTagName('content'))
console.log("contentNodes",contentNodes)
const rdfDescription = xmlDoc.querySelector('rdf\\:Description, Description');
if (rdfDescription) {
// 填充表单数据 - 映射关系form字段 -> XML标签
this.form = {
name: this.getNodeValue(rdfDescription, 'dc\\:title, title'),
version: this.getNodeValue(rdfDescription, 'dc\\:version, version'),
date: this.getNodeValue(rdfDescription, 'dc\\:date, date'),
language: this.getNodeValue(rdfDescription, 'dc\\:language, language'),
level: this.getNodeValue(rdfDescription, 'dc\\:level, level'),
contributor: this.getNodeValue(rdfDescription, 'dc\\:contributor, contributor'),
creator: this.getNodeValue(rdfDescription, 'dc\\:creator, creator'),
applicabilityInformation: this.getNodeValue(rdfDescription, 'dc\\:applicabilityInformation, applicabilityInformation'),
qualityVerification: this.getNodeValue(rdfDescription, 'dc\\:qualityVerification, qualityVerification'),
permission: this.getNodeValue(rdfDescription, 'dc\\:permission, permission')
};
console.log('XML解析成功', this.form);
}else{
this.form = {
name: '',
version:'',
date:'',
language:'',
level:'',
contributor:'',
creator:'',
applicabilityInformation:'',
qualityVerification:'',
};
console.log("目录解析不成功 置空",this.form)
}
if (contentNodes) {
console.log("处理前",contentNodes.innerHTML)
let htmlContent = this.convertXmlContentToHtml(contentNodes.innerHTML)
let htmlContent2 = this.completeModel3DTags(htmlContent)
console.log("处理后",htmlContent2)
const htmlContent3 = this.replaceModel3dToImg(htmlContent2);
// this.editor.txt.html(htmlContent)
//处理过后再导入
// this.restoreContent(htmlContent)
this.restoreContentXh(htmlContent3)
}
},
2025-07-25 13:28:47 +08:00
test() {
2025-09-15 09:58:52 +08:00
let a = '<pdiv id="divid"><p id="id"><pimg id="imgid"><img src="4.jpg" style="max-width: 100%;" alt="图片" id="imgid_1"></img></pimg><pimg id="imgid_2"><img src="zw.jpg" class="model3d" alt="图片" style="max-width: 100%;" id="imgid_3"></img></pimg></p></pdiv>';
let b = this.replacemodel3dImages(a);
console.log("替换的结果",b)
2025-07-25 13:28:47 +08:00
},
2025-09-15 09:58:52 +08:00
//测试发送给后端信息
2025-07-25 13:28:47 +08:00
sendMessageToHost() {
2025-09-15 09:58:52 +08:00
//调用后端 sendToDotNet
2025-07-25 13:28:47 +08:00
this.$sendToDotNet(this.val1,this.val2);
},
2025-09-15 09:58:52 +08:00
//加载富文本
loadFwb() {
2025-07-25 13:28:47 +08:00
this.editor2.txt.html(this.editorContent)
},
2025-09-15 09:58:52 +08:00
initEditor() {//初始化编辑器配置
2025-07-25 13:28:47 +08:00
this.editor = new WangEditor(this.$refs.editor)
// 配置编辑器
this.editor.config.uploadImgShowBase64 = true // 使用 base64 保存图片
this.editor.config.onchange = (html) => {
2025-09-15 09:58:52 +08:00
this.editorContent = html;
2025-07-25 13:28:47 +08:00
}
let dm = this.editor.config.menus;
console.log("默认菜单",dm)
2025-09-15 09:58:52 +08:00
2025-07-25 13:28:47 +08:00
2025-07-28 17:01:13 +08:00
// 启用颜色选择功能
this.editor.config.colors = [
'#000000', '#ffffff', '#eeeef1',
'#ff0000', '#ff5e5e', '#ffbbbb',
'#0033ff', '#0055ff', '#3d7eff',
'red',"#096","#9cf"
]
2025-07-25 13:28:47 +08:00
// 完全自定义菜单
this.editor.config.menus = [
'image', // 图片
'video', // 视频
2025-09-15 09:58:52 +08:00
// 'head', // 标题
2025-07-25 13:28:47 +08:00
'bold', // 粗体
2025-09-15 09:58:52 +08:00
'fontSize',//字号
2025-07-25 13:28:47 +08:00
// 'fontName',//字体
'italic', // 斜体
'underline', // 下划线
'strikeThrough', // 删除线
// 'line',//行高
'lineHeight',//
2025-07-28 17:01:13 +08:00
'foreColor', // 文字颜色
'backColor', // 背景颜色
2025-09-15 09:58:52 +08:00
'link', // 链接
2025-07-25 13:28:47 +08:00
'list', // 列表
// 'todo',//
'justify', // 对齐方式
2025-09-15 09:58:52 +08:00
'quote', // 引用
2025-07-25 13:28:47 +08:00
// 'emoticon',//表情
'table', // 表格
2025-09-15 09:58:52 +08:00
'code', // 代码
2025-07-25 13:28:47 +08:00
'splitLine',//分割线
'undo', // 撤销
2025-07-28 17:01:13 +08:00
'redo', // 重做
2025-07-25 13:28:47 +08:00
]
2025-09-15 09:58:52 +08:00
console.log("自定义菜单",this.editor.config)
// 自定义处理拖拽事件
this.editor.config.customUpload = true
// 监听拖拽事件
this.editor.config.uploadImgShowBase64 = false
this.editor.config.uploadVideoShowBase64 = false
this.editor.config.uploadAudioShowBase64 = false
// 设置自定义拖拽处理
// this.setupDragHandler()
// 保存原始的创建方法
const originalCreate = this.editor.create
// 重写创建方法,在创建完成后立即添加按钮
this.editor.create = () => {
// 执行原始创建方法
originalCreate.call(this.editor)
// 尝试获取工具栏
// 📌🔖
//
this.addCustomButton('警示', '<img src="'+this.imgUrlJs+'" alt="按钮图标" style="width: 20px; height: 20px; object-fit: contain;">',2) // 第一个按钮弹出123使用图钉图标
this.addCustomButton('警告', '<img src="'+this.imgUrlJg+'" alt="按钮图标" style="width: 20px; height: 20px; object-fit: contain;">',2) // 第一个按钮弹出123使用图钉图标
}
2025-07-25 13:28:47 +08:00
// 创建编辑器
this.editor.create();
2025-09-15 09:58:52 +08:00
// 特别注意:需要在编辑器创建完成后绑定事件
this.$nextTick(() => {
console.log("初始化 setupDragHandler")
this.setupDragHandler()
})
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// this.editor2 = new WangEditor(this.$refs.editor2)
// // 配置编辑器
// this.editor2.config.uploadImgShowBase64 = true // 使用 base64 保存图片
// this.editor2.config.onchange = (html) => {
// console.log("22222",html)
// // this.editorContent2 = html
// }
// this.editor2.create()
2025-07-25 13:28:47 +08:00
},
2025-09-15 09:58:52 +08:00
// 通用的添加自定义按钮方法,接收两个参数:提示内容和图标
addCustomButton(alertContent, icon,type) {
let toolbarElem = null
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// 方式1: 通过编辑器内部属性获取工具栏
if (this.editor.$toolbarElem && this.editor.$toolbarElem[0]) {
toolbarElem = this.editor.$toolbarElem[0]
console.log('通过$toolbarElem获取到工具栏')
}
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// 方式2: 直接通过容器查找
if (!toolbarElem) {
toolbarElem = this.$refs.editor.querySelector('.w-e-toolbar')
if (toolbarElem) {
console.log('通过容器查询获取到工具栏')
}
}
// 方式3: 全局查找
if (!toolbarElem) {
toolbarElem = document.querySelector('.w-e-toolbar')
if (toolbarElem) {
console.log('通过全局查询获取到工具栏')
}
}
// 如果还是找不到工具栏,直接创建一个简单的按钮放在编辑器上方
if (!toolbarElem) {
console.warn('仍然未找到工具栏,创建独立按钮')
this.createFallbackButton(alertContent, icon,type)
return
}
// 查找或创建菜单容器
let menuBar = toolbarElem.querySelector('.w-e-menu-bar')
if (!menuBar) {
menuBar = document.createElement('div')
menuBar.className = 'w-e-menu-bar'
menuBar.style.cssText = 'padding: 5px; border-bottom: 1px solid #e8e8e8;'
toolbarElem.appendChild(menuBar)
}
// 创建自定义按钮
const customBtn = document.createElement('div')
customBtn.className = 'w-e-menu'
customBtn.style.cssText = `
width: 32px;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
margin: 0 2px;
background: #fff;
border-radius: 2px;
color: #333;
font-size: 18px;
`
customBtn.innerHTML = icon
customBtn.title = `${alertContent}`
// 绑定点击事件,使用传入的提示内容
customBtn.onclick = () => {
console.log("点击了111",type)
switch (type){
case 1:
case '1':
this.editor.cmd.do('insertHTML', `<p data-we-empty-p><div style='margin: 50px auto;
padding: 20px;
border: 12px solid;
border-image: linear-gradient(45deg,
#ffcc00 0%, #ffcc00 10%,
#000000 10%, #000000 20%,
#ffcc00 20%, #ffcc00 30%,
#000000 30%, #000000 40%,
#ffcc00 40%, #ffcc00 50%,
#000000 50%, #000000 60%,
#ffcc00 60%, #ffcc00 70%,
#000000 70%, #000000 80%,
#ffcc00 80%, #ffcc00 90%,
#000000 90%, #000000 100%
) 1;'></div></p>`)
break;
case 2:
case '2':
this.editor.cmd.do('insertHTML', `<p data-we-empty-p><div style="margin: 50px auto;
padding: 20px;
border: 12px solid;
border-image: linear-gradient(45deg,
#ff0000 0%, #ff0000 10%,
#000000 10%, #000000 20%,
#ff0000 20%, #ff0000 30%,
#000000 30%, #000000 40%,
#ff0000 40%, #ff0000 50%,
#000000 50%, #000000 60%,
#ff0000 60%, #ff0000 70%,
#000000 70%, #000000 80%,
#ff0000 80%, #ff0000 90%,
#000000 90%, #000000 100%
) 1;"></div></p>`)
break;
2025-07-25 13:28:47 +08:00
}
2025-09-15 09:58:52 +08:00
}
// 添加悬停效果
customBtn.onmouseover = () => {
customBtn.style.background = '#f0f0f0'
}
customBtn.onmouseout = () => {
customBtn.style.background = '#fff'
}
// 添加到菜单最前面
menuBar.insertBefore(customBtn, menuBar.firstChild)
console.log(`自定义按钮(弹出${alertContent})已成功添加到菜单栏`)
},
// 备用方案:如果找不到工具栏,直接在编辑器上方创建一个按钮
createFallbackButton(alertContent, icon,type) {
const fallbackBtn = document.createElement('div')
fallbackBtn.style.cssText = `
padding: 8px 15px;
background: #409eff;
color: white;
border-radius: 4px;
cursor: pointer;
display: inline-block;
margin: 0 5px 10px 0;
`
fallbackBtn.innerHTML = `${icon} 点击弹出${alertContent}`
fallbackBtn.onclick = () => {
console.log("点击了222",type)
switch (type){
case 1:
this.editor.cmd.do('insertHTML', `<p class="fine-stripe-border1"></p>`)
break;
case 2:
this.editor.cmd.do('insertHTML', `<p class="fine-stripe-border2"></p>`)
break;
2025-07-25 13:28:47 +08:00
}
2025-09-15 09:58:52 +08:00
}
// 添加到编辑器容器的最前面
this.$refs.editor.insertBefore(fallbackBtn, this.$refs.editor.firstChild)
console.log(`已创建备用按钮(弹出${alertContent})`)
},
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
setupDragHandler() {
console.log("进入setupDragHandler")
const editor = this.editor
const editorContainer = editor.$textElem.elems[0]
let that = this;
// console.log("进入参数",editor,editorContainer)
// // 阻止默认拖拽行为
editorContainer.addEventListener('dragover', (e) => {
// console.log("进入 dragover",e)
e.preventDefault()
e.stopPropagation()
2025-07-25 13:28:47 +08:00
})
2025-09-15 09:58:52 +08:00
editorContainer.addEventListener('drop', (e) => {
// console.log("进入 drop",)
e.preventDefault()
e.stopPropagation()
that.$sendToDotNet('GetMaterialPath');
// // 检查拖拽的文件类型
// const items = e.dataTransfer.items
// for (let i = 0; i < items.length; i++) {
// const item = items[i]
// if (item.kind === 'file') {
// const file = item.getAsFile()
// const fileType = file.type.split('/')[0]
// // 根据文件类型显示不同的提示
// switch(fileType) {
// case 'image':
// alert('检测到图片文件,已阻止上传')
// break
// case 'video':
// alert('检测到视频文件,已阻止上传')
// break
// case 'audio':
// alert('检测到音频文件,已阻止上传')
// break
// default:
// alert(`检测到${fileType}类型文件,已阻止上传`)
// }
// }
// }
2025-07-25 13:28:47 +08:00
})
2025-09-15 09:58:52 +08:00
},
// 上传图片
insertImage(src) {
// src = 'http://localhost:54610/a1.jpg';
// const imgUrl = 'http://youneed.top:10017/uploads/1.jpg'
// const imgUrl = 'https://ww2.sinaimg.cn/mw690/007ut4Uhly1hx4v37mpxcj30u017cgrv.jpg';
const imgUrl = src?src:'http://localhost:5432/DM_Material/a1.jpg';
console.log("地址",src,imgUrl)
this.editor.cmd.do('insertHTML', `<img src="${imgUrl}" style="max-width: 100%;" alt="图片">`)
},
// 上传3D资源模拟占位
insert3d(imgUrl) {
this.editor.cmd.do('insertHTML', `<img src="${imgUrl}" class='model3d' style="max-width: 100%;" alt="图片">`)
// this.editor.cmd.do('insertHTML', `<div>3D模型<model3d src="http://www.baidu.com" controls="controls" style="max-width: 100%"></model3d></div>`)
},
//上传3D资源模设置标识
replacemodel3dImages(htmlString) {
if (!htmlString) return '';
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
const regex = /<img([^>]*?)class="model3d"([^>]*?)\/?>/gi;
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
return htmlString.replace(regex, (match, prefix, suffix) => {
const attributes = (prefix || '') + (suffix || '');
return `<model3d class="model3d" ${attributes}></model3d>`;
});
2025-07-25 13:28:47 +08:00
},
2025-09-15 09:58:52 +08:00
//解析3d资源标识
modelToImg(htmlString) {
// 匹配<model3d>标签且class中包含model3d
const regex = /<model3d([^>]*?)class="([^"]*?)model3d([^"]*?)"([^>]*?)>/gi;
// 替换标签名,保留所有属性
return htmlString.replace(regex, '<img$1class="$2model3d$3"$4>');
},
// // 插入视频
// insertVideo() {
// // const videoUrl = 'http://youneed.top:10017/uploads/video.mp4'
// const videoUrl = 'https://r1.realme.net/general/20250530/17485780109181ed6767541a64e7d90626e0a3fd1aaae.mp4?type=video/mp4';
// const videoId = `video-${Date.now()}`
// // 创建视频HTML
// const videoHtml = `
// <div class="video-wrapper" data-video-id="${videoId}">
// <video controls width="50%" style='margin:auto' data-video-id="${videoId}">
// <source src="${videoUrl}" type="video/mp4">
// </video>
// <div class="video-controls" style='display:none;'>
// <span class="video-delete" data-video-id="${videoId}">× 删除</span>
// </div>
// </div>
// <p><br></p>
// `
// // 使用编辑器命令插入
// this.editor.cmd.do('insertHTML', videoHtml)
// // 添加删除事件监听
// this.$nextTick(() => {
// const btn = document.querySelector(`button[data-video-id="${videoId}"]`)
// if (btn) {
// btn.onclick = (e) => {
// e.preventDefault()
// this.deleteVideoById(videoId)
// }
// }
// })
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// },
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// // 根据ID删除视频修正版
// deleteVideoById(videoId) {
// const container = document.querySelector(`.video-container[data-video-id="${videoId}"]`)
// if (container) {
// container.remove()
// this.editor.txt.html(this.editor.txt.html()) // 刷新编辑器
// }
// },
// 插入视频
insertVideo(src) {
// const videoUrl = src?src:'https://r1.realme.net/general/20250530/17485780109181ed6767541a64e7d90626e0a3fd1aaae.mp4?type=video/mp4'
const videoUrl = src?src:'http://localhost:5432/DM_Material/video.mp4'
// 使用 editor.cmd.do 执行插入操作,支持撤销/重做
this.editor.cmd.do('insertHTML', `<div><video src="${videoUrl}" controls="controls" style="max-width: 100%"></video></div>`)
// 或者使用更简单的插入方式
// this.editor.txt.append(`<video src="${videoUrl}" controls="controls" style="max-width: 100%"></video>`)
2025-07-25 13:28:47 +08:00
},
2025-09-15 09:58:52 +08:00
// 插入音频
insertAudio(src) {
// let audioUrl = 'https://m801.music.126.net/20250805103535/df94bdfa3bc8c3a07de64531c4925895/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/14096444542/bafc/a068/39f8/9a9e06e5634410b5e7e81df24749e656.mp3?vuutv=6WGmbU7+iXWmpSMnJs8FQEYWEieNRcA9S4YcUYOjmx36NJRHK0ASi7jhIJz0R9gxcmMcAUDn9rf1k08lFtqC9TQYcOlc8ZofqvCCJho3eeBGH/hwnjUlaQHe2GQFyJ32rhYAydM3bCBfIg+ZnpfZTw==';
let audioUrl =src?src:'http://localhost:5432/DM_Material/music.mp3';
// 使用预设音频URL
// const audioHtml = `
// <div class="audio-container" style="margin: 10px 0;">
// <audio controls src="${audioUrl}" style="width: 100%;"></audio>
// <div style="text-align: center; color: #999; font-size: 12px;">音频文件</div>
// </div>
// `;
const audioHtml = `
<audio controls src="${audioUrl}" style="width: 100%;"></audio>
`;
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// 插入到编辑器
this.editor.cmd.do('insertHTML', audioHtml);
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// 或者使用更简单的方式
// this.editor.txt.append(audioHtml)
},
//循环处理标签
xh(oldSt,newSt,type,saveId){
let times = oldSt.length;
// const ht1 = this.tempDivData.innerHTML;
// console.log("3d转换之后2",ht1)
for(let i = 0;i<times;i++){
const headings = this.tempDivData.querySelectorAll(oldSt[i])
headings.forEach(heading => {
const dmTitle = document.createElement(newSt[i])
heading.parentNode.insertBefore(dmTitle, heading)
console.log("内容替换",dmTitle, heading)
dmTitle.appendChild(heading)
})
}
this.createXmlSt(type,saveId);
},
// 先清除已有的ID确保干净的基础
removeAllIds(htmlString) {
// 创建临时容器解析HTML
const tempContainer = document.createElement('div');
tempContainer.innerHTML = htmlString;
// 递归移除所有元素的id属性
const removeIdsFromElement = (element) => {
if (!element || element.nodeType !== 1) return;
// 移除当前元素的id
element.removeAttribute('id');
// 递归处理子元素
Array.from(element.children).forEach(child => {
removeIdsFromElement(child);
});
};
// 处理所有子元素
Array.from(tempContainer.children).forEach(child => {
removeIdsFromElement(child);
});
return tempContainer.innerHTML;
},
// 删除Colgroup
removeColgroup(str) {
// 1. 校验入参防止传入null/undefined导致报错
if (!str || typeof str !== 'string') {
console.warn('传入的内容不是有效字符串');
return str; // 入参无效时,直接返回原内容
}
// 2. 定义正则匹配colgroup标签不区分大小写、全局匹配
const colgroupReg = /<colgroup[\s\S]*?<\/colgroup>/gi;
// 3. 判断是否包含colgroup标签
const hasColgroupTag = colgroupReg.test(str);
// 4. 处理逻辑:包含则移除,不包含则返回原字符串
let processedStr;
if (hasColgroupTag) {
processedStr = str.replace(colgroupReg, '');
console.log('检测到colgroup标签已移除');
} else {
processedStr = str; // 关键不包含时返回原字符串而非null
console.log('未检测到colgroup标签返回原字符串');
}
// 5. 打印日志(便于调试)
console.log('解析前:', str);
console.log('解析后:', processedStr);
return processedStr;
},
/**
* 为HTML字符串中的所有标签添加唯一ID
* @param {string} htmlString - 原始HTML字符串
* @returns {string} 处理后的HTML字符串
*/
addUniqueIds(htmlString) {
// 先清除已有的ID确保干净的基础
const cleanHtml = this.removeAllIds(htmlString);
console.log("当前的所有html内容2",cleanHtml)
const tempContainer = document.createElement('div');
tempContainer.innerHTML = cleanHtml;
const usedIds = new Set();
// 生成唯一ID
const generateUniqueId = (tagName, content = '') => {
const pureTagName = tagName.toLowerCase().replace(/^p/, '');
const semanticSuffix = content.trim()
.replace(/\s+/g, '_')
.replace(/[^a-z0-9-]/g, '')
.slice(0, 10);
console.log('循环数据semanticSuffix',semanticSuffix)
let baseId = pureTagName + (semanticSuffix ? `id_${semanticSuffix}` : 'id');
let finalId = baseId;
let counter = 1;
console.log('循环数据counter',counter)
console.log('循环数据baseId',baseId)
while (usedIds.has(finalId)) {
finalId = `${baseId}_${counter}`;
counter++;
}
usedIds.add(finalId);
return finalId;
};
// 递归添加ID
const addIdsToElement = (element) => {
if (!element || element.nodeType !== 1) return;
const elementText = element.textContent || '';
const elementId = generateUniqueId(element.tagName, elementText);
element.setAttribute('id', elementId);
Array.from(element.children).forEach(child => {
addIdsToElement(child);
});
};
// 处理所有元素
Array.from(tempContainer.children).forEach(child => {
addIdsToElement(child);
});
return tempContainer.innerHTML;
},
/**
* 完整处理流程清除现有ID -> 添加新的唯一ID
* @param {string} originalHtml - 原始HTML字符串
*/
processHtmlWithIds(originalHtml) {
console.log("当前的所有html内容1",originalHtml)
let processedHtml = this.addUniqueIds(originalHtml);
console.log("添加完的富文本",processedHtml)
return processedHtml;
},
// 创建XML字符串
createXmlSt(type,saveId) {
let desStr = this.form;
// if(type||this.isNavEdit){
// desStr = this.form;
// this.isNavEdit = false;
// }else{
// desStr = '';
// }
console.log("当前",this.navIsAdd)
// const ht3 = this.tempDivData.innerHTML;
// console.log("3d转换之后3",ht3)
switch (this.navIsAdd){
case "create":
this.tempDivData.innerHTML = '';
break;
case "edit":
// this.tempDivData.innerHTML = this.tempDivData.innerHTML;
break;
case "other":
// this.tempDivData.innerHTML = this.tempDivData.innerHTML;
this.navIsAdd = 'create';
break;
default:
// this.tempDivData.innerHTML = '';
break;
}
// this.tempDivData.innerHTML = this.navIsAdd?"":this.tempDivData.innerHTML;
this.tempDivData.innerHTML = this.clearNbsp(this.tempDivData.innerHTML)
// const ht4 = this.tempDivData.innerHTML;
// console.log("3d转换之后4",ht4)
console.log("当前的所有html内容3",this.tempDivData.innerHTML)
let newDST = this.processHtmlWithIds(this.tempDivData.innerHTML)
// const ht5 = this.tempDivData.innerHTML;
// console.log("3d转换之后5",ht5)
console.log("结果cleanedStr 然后赋值",this.tempDivData.innerHTML)
console.log("加ID后的",newDST)
//去掉table里面的东西
newDST = this.removeColgroup(newDST)
// const ht6 = this.tempDivData.innerHTML;
// console.log("3d转换之后6",ht6)
if(this.loadFileDMC){
newDST = this.loadFileDMC;
}
console.log("解析出来后的",newDST)
const xmlString = `<?xml version="1.0" encoding="UTF-8"?>
<dmodule xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<rdf:Description>
<dc:title>`+desStr.name+`</dc:title>
<dc:creator>`+desStr.creator+`</dc:creator>
<dc:permission>`+desStr.permission+`</dc:permission>
<dc:subject/>
<dc:publisher>`+desStr.creator+`</dc:publisher>
<dc:contributor>`+desStr.contributor+`</dc:contributor>
<dc:version>`+desStr.version+`</dc:version>
<dc:level>`+desStr.level+`</dc:level>
<dc:date>`+desStr.date+`</dc:date>
<dc:applicabilityInformation>`+desStr.applicabilityInformation+`</dc:applicabilityInformation>
<dc:qualityVerification>`+desStr.qualityVerification+`</dc:qualityVerification>
<dc:type>text</dc:type>
<dc:format>text/xml</dc:format>
<dc:identifier>`+this.getDmName+`</dc:identifier>
<dc:language>`+desStr.language+`</dc:language>
<dc:rights>01</dc:rights>
</rdf:Description>
<identAndStatusSection>
<dmAddress>
<dmIdent>
<dmCode
modelIdentCode="EXAMPLE01"
systemDiffCode="A1"
systemCode="SYS"
subSystemCode="A"
subSubSystemCode="B"
assyCode="AS01"
disassyCode="DS"
disassyCodeVariant="001"
infoCode="INF"
infoCodeVariant="1"
itemLocationCode="A"
/>
<language languageIsoCode="en" countryIsoCode="US"/>
<issueInfo issueNumber="001" inWork="01"/>
</dmIdent>
<dmAddressItems>
<issueDate year="2023" month="06" day="15"/>
<dmTitle>
<techName>Example Technical Document</techName>
</dmTitle>
</dmAddressItems>
</dmAddress>
<dmStatus>
<security securityClassification="01"/>
<responsiblePartnerCompany enterpriseCode="COMP001">
<enterpriseName>Example Company</enterpriseName>
</responsiblePartnerCompany>
<originator>John Doe</originator>
<brexDmRef>BREX001</brexDmRef>
<qualityAssurance>QA passed</qualityAssurance>
</dmStatus>
</identAndStatusSection>
<content>`
+newDST+
`</content>
</dmodule>`
// this.tempDivData.innerHTML
// ${this.convertHtmlToXmlContent(this.editorContent)}
console.log("${tempDiv.innerHTML}",this.tempDivData.innerHTML)
console.log("${tempDiv}",this.tempDivData)
let xmlData = this.fixHrTagsInContent(this.fixBrTagsInContent(this.fixImgTagsInContent(xmlString, false), false),false);
//存富文本信息
// const ht7 = this.tempDivData.innerHTML;
// console.log("3d转换之后7",ht7)
// const ht8 = xmlData;
// console.log("3d转换之后8",ht8)
// xmlData = this.replacemodel3dImages(xmlData);
if(type){
console.log("存富文本内容时候1",type,this.nowMaxId,xmlData)
this.$sendToDotNet('SaveFile','DMC',this.nowMaxId,xmlData,'000');
}else{
console.log("存富文本内容时候2",type,saveId,this.currentNode,xmlData)
this.$sendToDotNet('SaveFile','DMC',saveId?saveId:this.currentNode.id,xmlData,this.currentNode.lagreVersion);
this.lastClick = this.currentNode.id;
}
if(this.navIsAdd == 'create'){
// this.currentNode = '';
// this.getDmName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+this.nowMaxId+this.domNameStr[3]+this.domNameStr[4];
// console.log("新建目录以后调到新的DMC",this.getDmName)
// this.loadFWBFile(this.getDmName);
}
// this.successTips('目录保存完成')
// 处理content标签内的img标签
// this.xmlDownload(xmlData)
// this.xmlDownload(this.fixImgTagsInContent(xmlString, false))
},
// 创建XML文件然后下载
xmlDownload(xmlString) {
// Create download link
const blob = new Blob([xmlString], { type: 'text/xml' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'document.xml'
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
},
// // XML文件标签剔除
// restoreContent(html) {
// console.log("XML初始内容",html)
// if (html) {
// const tempDiv = document.createElement('div')
// tempDiv.innerHTML = html
// // 处理所有dmTitle标签
// const dmTitles = tempDiv.querySelectorAll('dmTitle')
// dmTitles.forEach(dmTitle => {
// // 获取dmTitle的子节点
// const children = Array.from(dmTitle.childNodes)
// // 在dmTitle之前插入所有子节点
// children.forEach(child => {
// dmTitle.parentNode.insertBefore(child.cloneNode(true), dmTitle)
// })
// // 移除dmTitle
// dmTitle.parentNode.removeChild(dmTitle)
// })
// console.log('内容还原完成dmTitle标签已被移除')
// console.log("tempDiv.innerHTML",tempDiv.innerHTML)
// this.editor.txt.html(tempDiv.innerHTML)
// }else{
// console.log("空白文档")
// this.editor.txt.html('')
// }
// this.editor.txt.html(tempDiv.innerHTML)
// },
//删除视频
deleteAllVideos() {
const videoWrappers = document.querySelectorAll('.video-wrapper')
if (videoWrappers.length === 0) {
console.log('没有找到可删除的视频')
return
}
videoWrappers.forEach(wrapper => {
wrapper.remove()
})
// 删除可能残留的空段落
const editor = this.$refs.editor
const paragraphs = editor.querySelectorAll('p')
paragraphs.forEach(p => {
if (p.textContent.trim() === '' && p.children.length === 0) {
p.remove()
}
})
// 使用正确的方式通知内容变更
if (this.editor.txt) {
this.editor.txt.html(this.editor.txt.html()) // 强制更新编辑器内容
}
console.log(`已删除 ${videoWrappers.length} 个视频`)
},
// 加载所有内容到 main 容器
loadAllContent() {
const mainContainer = document.getElementById('main')
mainContainer.innerHTML = this.editorContent;
console.log("this.editorContent",this.editorContent)
// 为main容器中的标题添加ID
this.addHeadingIds(mainContainer)
},
// 为标题元素添加ID
addHeadingIds(container) {
const headings = container.querySelectorAll('h1, h2, h3, h4, h5, h6')
headings.forEach((heading, index) => {
if (!heading.id) {
heading.id = `heading-${index}-${Date.now()}`
console.log("Date.now",Date.now())
}
})
},
// 生成带锚点的目录
generateTOC() {
const navContainer = document.getElementById('nav')
navContainer.innerHTML = '' // 清空原有目录
// 确保main容器已加载内容
// if (!document.getElementById('main').innerHTML) {
this.loadAllContent()
// }
const mainContainer = document.getElementById('main')
const headings = mainContainer.querySelectorAll('h1, h2, h3, h4, h5, h6')
console.log("headings.length",headings.length)
if (headings.length === 0) {
2025-07-25 13:28:47 +08:00
navContainer.innerHTML = '<p>没有找到标题元素,无法生成目录。</p>'
return
}
const tocList = document.createElement('ul')
tocList.style.listStyleType = 'none'
tocList.style.paddingLeft = '0'
headings.forEach(heading => {
2025-09-15 09:58:52 +08:00
console.log("heading",)
2025-07-25 13:28:47 +08:00
const level = parseInt(heading.tagName.substring(1))
const listItem = document.createElement('li')
2025-09-15 09:58:52 +08:00
listItem.style.marginLeft = `${(level - 1) *1.2}rem`;
listItem.style.marginBottom = '5px';
let fontSize = '12px'
switch(level){
case 1:
fontSize = '30px'
break;
case 2:
fontSize = '26px'
break;
case 3:
fontSize = '22px'
break;
case 4:
fontSize = '18px'
break;
case 5:
fontSize = '20px'
break;
}
listItem.style.fontSize = fontSize;
2025-07-25 13:28:47 +08:00
const link = document.createElement('a')
link.href = `#${heading.id}`
link.textContent = heading.textContent
link.style.textDecoration = 'none'
link.style.color = '#333'
// 添加平滑滚动效果
link.addEventListener('click', (e) => {
e.preventDefault()
document.getElementById(heading.id).scrollIntoView({
behavior: 'smooth'
})
})
listItem.appendChild(link)
2025-09-15 09:58:52 +08:00
tocList.appendChild(listItem)
2025-07-25 13:28:47 +08:00
})
const tocContainer = document.createElement('div')
tocContainer.style.border = '1px solid #ddd'
tocContainer.style.padding = '15px'
tocContainer.style.marginBottom = '20px'
tocContainer.style.background = '#f9f9f9'
tocContainer.style.borderRadius = '4px'
const tocTitle = document.createElement('h2')
tocTitle.textContent = '目录'
tocTitle.style.marginTop = '0'
tocContainer.appendChild(tocTitle)
tocContainer.appendChild(tocList)
navContainer.appendChild(tocContainer)
2025-07-28 17:01:13 +08:00
},
2025-09-15 09:58:52 +08:00
// 处理所有标题元素的ID
processHeadingIds() {
if (!this.editor) return
// 获取当前时间戳简化版只取后8位
const timestamp = Date.now().toString().slice(-8);
let timestamps = parseInt(Date.now().toString().slice(-8));
console.log("时间戳",timestamp,timestamps)
// 获取编辑器内容
const editorHtml = this.editor.txt.html()
if (!editorHtml) return
// 创建一个临时div来操作DOM
const tempDiv = document.createElement('div')
tempDiv.innerHTML = editorHtml
// 获取所有标题元素
const headings = tempDiv.querySelectorAll('h1, h2, h3, h4, h5, h6')
headings.forEach((heading, index) => {
console.log("index",index)
// 生成3个随机字母 + 时间戳后8位
// const newId = `heading-${randomLetters}-${timestamp}`
const randomLetters = this.getRandomLetters(this.idLength)
// const newId = `${heading.id}${randomLetters}${timestamps}`
timestamps++;
console.log("timestamp",timestamps)
const newId = `${randomLetters}`
if(heading.id.length<this.idLength){
heading.id = newId
}
})
// 将修改后的HTML设置回编辑器
this.editor.txt.html(tempDiv.innerHTML);
setTimeout(() => {
this.generateTOC()
}, 200);
},
// 生成随机字母
getRandomLetter() {
const letters = 'abcdefghijklmnopqrstuvwxyz1234567890'
return letters.charAt(Math.floor(Math.random() * letters.length))
},
// 生成随机字母组合
getRandomLetters(length) {
let result = ''
for (let i = 0; i < length; i++) {
result += this.getRandomLetter()
}
return result
},
2025-07-28 17:01:13 +08:00
// 生成S1000D XML模板
generateS1000DTemplate(content) {
const { dmc, issueInfo, language } = this.dmConfig;
return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dmodule [
<!ENTITY % ISOEntities PUBLIC "ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML" "http://www.s1000d.org/S1000D_4-1/ent/ISOEntities">
%ISOEntities;
]>
<dmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.s1000d.org/S1000D_4-1/xml_schema_flat/descript.xsd">
<idstatus>
<dmaddress>
<dmc>
<avee>${dmc.modelIdentCode}-${dmc.systemDiffCode}-${dmc.systemCode}-${dmc.subSystemCode}-${dmc.subSubSystemCode}-${dmc.assyCode}-${dmc.disassyCode}-${dmc.disassyCodeVariant}</avee>
<avee>${dmc.infoCode}-${dmc.infoCodeVariant}-${dmc.itemLocationCode}</avee>
</dmc>
</dmaddress>
<issueinfo>
<issue number="${issueInfo.issueNumber}" inwork="${issueInfo.inWork}" date="${issueInfo.issueDate}"/>
<language country="${language.split('-')[1]}" language="${language.split('-')[0]}"/>
</issueinfo>
</idstatus>
<content>
<description>
${this.escapeXml(content)}
</description>
</content>
</dmodule>`;
},
// XML特殊字符转义
escapeXml(unsafe) {
return unsafe.replace(/[<>&'"]/g, (c) => {
switch (c) {
case '<': return '&lt;';
case '>': return '&gt;';
case '&': return '&amp;';
case '\'': return '&apos;';
case '"': return '&quot;';
default: return c;
}
});
},
// 更新XML预览
updateXmlPreview(html) {
try {
this.xmlPreview = this.generateS1000DTemplate(html);
} catch (e) {
console.error('生成XML预览失败:', e);
this.xmlPreview = `生成XML预览时出错: ${e.message}`;
}
},
// 导出为S1000D XML文件
exportToS1000D() {
const html = this.editor.txt.html();
const xmlContent = this.generateS1000DTemplate(html);
// 下载文件
const blob = new Blob([xmlContent], { type: 'application/xml' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = `s1000d-${this.dmConfig.dmc.modelIdentCode}-${this.dmConfig.issueInfo.issueNumber}.xml`;
link.click();
URL.revokeObjectURL(link.href);
},
// 从S1000D XML导入
handleS1000DUpload(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
const xmlString = e.target.result;
const html = this.extractContentFromS1000D(xmlString);
2025-09-15 09:58:52 +08:00
// const parser = new DOMParser();
// const xmlDoc = parser.parseFromString(html, 'text/xml');
// 获取 content 元素
const contentElement = html.querySelector('content');
console.log("contentElement",contentElement)
if (contentElement) {
// 提取 content 内部的所有子节点并转换为字符串
let innerHtml = '';
Array.from(contentElement.childNodes).forEach(node => {
innerHtml += new XMLSerializer().serializeToString(node);
});
console.log("innerHtml",innerHtml)
this.editor.txt.html(innerHtml)
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
// if (html) {
// this.editor.txt.html(html);
// // this.updateXmlPreview(html);
// }
2025-07-28 17:01:13 +08:00
// 重置input值允许重复选择同一文件
event.target.value = '';
};
reader.readAsText(file);
},
// 从S1000D XML提取内容
extractContentFromS1000D(xmlString) {
try {
// 简单提取description内容
const descriptionMatch = xmlString.match(/<description>([\s\S]*?)<\/description>/i);
if (!descriptionMatch || !descriptionMatch[1]) {
throw new Error('未找到description内容');
}
// 反转义XML特殊字符
let content = descriptionMatch[1]
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&')
.replace(/&apos;/g, "'")
.replace(/&quot;/g, '"');
// 移除可能的多余空格和换行
content = content.trim();
return content;
} catch (e) {
console.error('S1000D导入错误:', e);
alert(`导入S1000D XML失败: ${e.message}`);
return '';
}
},
2025-09-15 09:58:52 +08:00
rgbToHex(r, g, b) {//色号转换
console.log("colors",this.colors)
return `#${[r, g, b].map(x => x.toString(16).padStart(2, '0')).join('')}`;
},
2025-07-28 17:01:13 +08:00
// 验证当前XML
validateCurrentXml() {
2025-09-15 09:58:52 +08:00
2025-07-28 17:01:13 +08:00
const validationResult = this.validateS1000DXml(this.xmlPreview);
this.validationResult = validationResult;
},
// 基本S1000D XML验证
validateS1000DXml(xmlString) {
try {
// 检查基本结构
const hasDmodule = xmlString.includes('<dmodule');
const hasIdstatus = xmlString.includes('<idstatus');
const hasContent = xmlString.includes('<content');
if (!hasDmodule) {
return {
valid: false,
message: '无效的S1000D文档: 缺少dmodule根元素'
};
}
if (!hasIdstatus) {
return {
valid: false,
message: '缺少必需的S1000D元素: idstatus'
};
}
if (!hasContent) {
return {
valid: false,
message: '缺少必需的S1000D元素: content'
};
}
// 检查dmc结构
const hasDmc = xmlString.includes('<dmc>');
const hasAvee = xmlString.includes('<avee>');
if (!hasDmc || !hasAvee) {
return {
valid: false,
message: '无效的dmc结构: 缺少dmc或avee元素'
};
}
return {
valid: true,
message: 'XML文档符合S1000D基本结构要求'
};
} catch (e) {
return {
valid: false,
message: `验证过程中发生错误: ${e.message}`
};
}
2025-09-15 09:58:52 +08:00
},
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
//保存内容到XML
generateXml(type,saveId) {
this.dataXmlProcessing(type,saveId);
},
//测试 保存目录到XML
navXml() {
const xmlString = `<?xml version="1.0" encoding="UTF-8"?>
<dmodule xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<identAndStatusSection>
<dmAddress>
<dmIdent>
<dmCode
modelIdentCode="EXAMPLE01"
systemDiffCode="A1"
systemCode="SYS"
subSystemCode="A"
subSubSystemCode="B"
assyCode="AS01"
disassyCode="DS"
disassyCodeVariant="001"
infoCode="INF"
infoCodeVariant="1"
itemLocationCode="A"
/>
<language languageIsoCode="en" countryIsoCode="US"/>
<issueInfo issueNumber="001" inWork="01"/>
</dmIdent>
<dmAddressItems>
<issueDate year="2023" month="06" day="15"/>
<dmTitle>
<techName>Example Technical Document</techName>
</dmTitle>
</dmAddressItems>
</dmAddress>
<dmStatus>
<security securityClassification="01"/>
<responsiblePartnerCompany enterpriseCode="COMP001">
<enterpriseName>Example Company</enterpriseName>
</responsiblePartnerCompany>
<originator>John Doe</originator>
<brexDmRef>BREX001</brexDmRef>
<qualityAssurance>QA passed</qualityAssurance>
</dmStatus>
</identAndStatusSection>
<content><infoCode>`
+JSON.stringify(this.treeData)+
`</infoCode></content>
</dmodule>`;
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
const blob = new Blob([xmlString], { type: 'text/xml' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'mulu.xml'
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
},
//测试 加载XML
loadXml() {
this.$refs.fileInput.click()
},
//测试 选择文件
handleFileUpload(event) {
const file = event.target.files[0]
if (!file) return
const reader = new FileReader()
reader.onload = (e) => {
const xmlContent = e.target.result
// console.log("xmlContent",xmlContent)
const parser = new DOMParser()
const xmlDoc = parser.parseFromString(xmlContent, "text/xml")
console.log("xmlDoc",xmlDoc)
const contentNodes = xmlDoc.getElementsByTagName('content')[0];
console.log("xmlDoc.getElementsByTagName('content')",xmlDoc.getElementsByTagName('content'))
console.log("contentNodes",contentNodes)
if (contentNodes) {
const htmlContent = this.convertXmlContentToHtml(contentNodes.innerHTML)
// this.editor.txt.html(htmlContent)
//处理过后再导入
// this.restoreContent(htmlContent)
this.restoreContentXh(htmlContent)
}
2025-07-25 13:28:47 +08:00
}
2025-09-15 09:58:52 +08:00
reader.readAsText(file)
},
completeModel3DTags(htmlString) {
// 处理自闭合的model3d标签替换为完整的闭合标签
return htmlString.replace(/<model3d([^>]*?)\/>/g, (match, attributes) => {
// 返回带有相同属性的完整闭合标签
return `<model3d${attributes}></model3d>`;
});
},
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// 解析xml里面 content内容
convertXmlContentToHtml(xmlContent) {
return xmlContent
.replace(/<para>/g, '<p>')
.replace(/<\/para>/g, '</p>')
.replace(/<emphasis emphasisType="bold">/g, '<strong>')
.replace(/<\/emphasis>/g, '</strong>')
.replace(/<emphasis emphasisType="italic">/g, '<em>')
},
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
/**
* 修复content标签内的img标签闭合符
* @param {string} inputStr 输入HTML
* @param {boolean} useSlash 是否使用自闭合
* @returns {string} 修正后的HTML
*/
fixImgTagsInContent(inputStr, useSlash) {
const contentRegex = /<content>([\s\S]*?)<\/content>/g
return inputStr.replace(contentRegex, (contentMatch, innerContent) => {
const fixedInnerContent = innerContent.replace(
/<img\b([^>]*)>/g,
(imgMatch, imgAttributes) => {
// 已有闭合符则不处理
if (imgMatch.endsWith('/>') || imgMatch.includes('</img>')) {
return imgMatch
}
// 根据参数决定闭合方式
return useSlash
? `<img${imgAttributes} />`
: `<img${imgAttributes}></img>`
}
)
return `<content>${fixedInnerContent}</content>`
})
},
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// 检测hr标签是否闭合
fixHrTagsInContent(inputStr, useSlash) {
const contentRegex = /<content>([\s\S]*?)<\/content>/g
return inputStr.replace(contentRegex, (contentMatch, innerContent) => {
const fixedInnerContent = innerContent.replace(
/<hr\b([^>]*)>/g,
(imgMatch) => {
// 已有闭合符则不处理
if (imgMatch.endsWith('/>') || imgMatch.includes('</hr>')) {
return imgMatch
}
// 根据参数决定闭合方式
return useSlash
? `<hr/>`
: `<hr></hr>`
}
)
return `<content>${fixedInnerContent}</content>`
})
},
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// 检测br标签是否闭合
fixBrTagsInContent(inputStr, useSlash) {
const contentRegex = /<content>([\s\S]*?)<\/content>/g
return inputStr.replace(contentRegex, (contentMatch, innerContent) => {
const fixedInnerContent = innerContent.replace(
/<br\b([^>]*)>/g,
(imgMatch, imgAttributes) => {
// 已有闭合符则不处理
if (imgMatch.endsWith('/>') || imgMatch.includes('</br>')) {
return imgMatch
}
// 根据参数决定闭合方式
return useSlash
? `<br${imgAttributes} />`
: `<br${imgAttributes}></br>`
}
)
return `<content>${fixedInnerContent}</content>`
})
},
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
//富文本内容打标签
dataXmlProcessing(type,saveId) {
let html = this.editor.txt.html()
console.log('保存内容',html);
// 正则表达式匹配img、audio、video标签的src属性
// 支持三种标签:<img ...>、<audio ...>、<video ...>
// 捕获组1: 标签名和src="之前的部分
// 捕获组2: 提取文件名(最后一个/后面的内容)
// 捕获组3: 保留"及之后的部分
const reg = /(<(img|audio|video)[^>]+src=")[^"]+\/([^"]+)("[^>]*>)/gi;
// 替换为src="文件名"
let html_1 = html.replace(reg, '$1$3$4');
console.log('解析地址:',html_1)
2025-07-25 13:28:47 +08:00
2025-09-15 09:58:52 +08:00
// const reg2 = /(<(img|audio|video)[^>]+src=")(?!http:\/\/)([^"]+)("[^>]*>)/gi;
// let jc = 'http://localhost:5432/';
// // 替换为src="基础URL+文件名"
// // 这里假设原路径可能包含DM_Material目录如果不需要可以去掉
// let html_2 = html_1.replace(reg2, `$1${jc}$3$4`);
// console.log('还原地址:',html_2)
this.tempDivData = document.createElement('div')
html_1 = this.replacemodel3dImages(html_1);
// const ht1 = html_1;
// console.log("3d转换之后1",ht1)
this.tempDivData.innerHTML = html_1;
this.xh(this.oldSelector,this.newSelector,type,saveId);
},
// XML文件标签剔除
restoreContentXh(html) {
console.log("XML初始内容",html)
if (html) {
this.tempDivData = document.createElement('div')
this.tempDivData.innerHTML = html
let times = this.oldSelector.length;
for(let i =0;i<times;i++){
// 处理所有dmTitle标签
const dmTitles = this.tempDivData.querySelectorAll(this.newSelector[i])
dmTitles.forEach(dmTitle => {
// 获取dmTitle的子节点
const children = Array.from(dmTitle.childNodes)
console.log("剔除标签:",dmTitles,children)
// 在dmTitle之前插入所有子节点
children.forEach(child => {
dmTitle.parentNode.insertBefore(child.cloneNode(true), dmTitle)
})
// 移除dmTitle
dmTitle.parentNode.removeChild(dmTitle)
console.log("剔除标签移除:",dmTitle,'替换成',dmTitle)
})
}
console.log('内容还原完成dmTitle标签已被移除')
console.log("tempDiv.innerHTML",this.tempDivData.innerHTML)
const reg2 = /(<(img|audio|video)[^>]+src=")(?!http:\/\/)([^"]+)("[^>]*>)/gi;
let jc = this.lisenPath;
// 检查并去除开头的双引号
if (jc.startsWith('"')) {
jc = jc.substring(1);
}
// 检查并去除结尾的双引号
if (jc.endsWith('"')) {
jc = jc.substring(0, jc.length - 1);
}
// 替换为src="基础URL+文件名"
// 这里假设原路径可能包含DM_Material目录如果不需要可以去掉
let html_2 = this.tempDivData.innerHTML.replace(reg2, `$1${jc}$3$4`);
// html_2 = this.modelToImg(html_2);
console.log('还原地址:',html_2)
this.editor.txt.html(html_2)
this.contentXmlStr = this.editor.txt.html();
}else{
console.log("空白文档")
this.editor.txt.html('')
this.contentXmlStr = this.editor.txt.html();
}
},
// XML文件标签剔除
restoreContentXh2(html) {
console.log("XML初始内容",html)
if (html) {
this.tempDivData1 = document.createElement('div')
this.tempDivData1.innerHTML = html
let times = this.oldSelector.length;
for(let i =0;i<times;i++){
// 处理所有dmTitle标签
const dmTitles = this.tempDivData1.querySelectorAll(this.newSelector[i])
dmTitles.forEach(dmTitle => {
// 获取dmTitle的子节点
const children = Array.from(dmTitle.childNodes)
console.log("剔除标签:",dmTitles,children)
// 在dmTitle之前插入所有子节点
children.forEach(child => {
dmTitle.parentNode.insertBefore(child.cloneNode(true), dmTitle)
})
// 移除dmTitle
dmTitle.parentNode.removeChild(dmTitle)
console.log("剔除标签移除:",dmTitle,'替换成',dmTitle)
})
}
console.log('内容还原完成dmTitle标签已被移除')
console.log("tempDiv.innerHTML",this.tempDivData1.innerHTML)
const reg2 = /(<(model3d|img|audio|video)[^>]+src=")(?!http:\/\/)([^"]+)("[^>]*>)/gi;
let jc = this.viewLis;
// 检查并去除开头的双引号
if (jc.startsWith('"')) {
jc = jc.substring(1);
}
// 检查并去除结尾的双引号
if (jc.endsWith('"')) {
jc = jc.substring(0, jc.length - 1);
}
if(!this.dialogVisibleNav){
jc = jc+this.historyVsPath+'/';
}
// 替换为src="基础URL+文件名"
// 这里假设原路径可能包含DM_Material目录如果不需要可以去掉
const html_2 = this.tempDivData1.innerHTML.replace(reg2, `$1${jc}$3$4`);
// html_2 = this.modelToImg(html_2);
console.log('加载还原地址:',html_2)
this.neirong = this.modelToImg(html_2);
// document.getElementsByClassName("loadContent")[0].innerHTML=html_2;
// document.getElementsByClassName("loadContent1")[0].innerHTML=html_2;
console.log('加载还原地址:',html_2)
// this.editor.txt.html(html_2)
// this.contentXmlStr = this.editor.txt.html();
}else{
console.log("空白文档")
// this.editor.txt.html('')
// this.contentXmlStr = this.editor.txt.html();
}
},
//处理 3d模型标签
replaceModel3dToImg(htmlString) {
// 创建临时DOM元素处理HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlString;
// 查找所有model3d标签
const model3dElements = tempDiv.querySelectorAll('model3d');
model3dElements.forEach(model3d => {
// 创建新的img元素
const img = document.createElement('img');
// 复制所有属性
Array.from(model3d.attributes).forEach(attr => {
img.setAttribute(attr.name, attr.value);
});
// 替换元素
model3d.parentNode.replaceChild(img, model3d);
});
return tempDiv.innerHTML;
},
//判断富文本内容是否更改
dmcIsChange() {
console.log("this.editor.txt.html()",this.editor.txt.html())
console.log("this.contentXmlStr",this.contentXmlStr)
console.log("验证结果",this.editor.txt.html() == this.contentXmlStr)
return this.editor.txt.html() == this.contentXmlStr;
},
//测试 启用右键功能
enableContextMenu() {
console.log("进入isContextMenuEnabled",this.isContextMenuEnabled)
if (this.isContextMenuEnabled) return;
this.isContextMenuEnabled = true;
// 获取#nav下的所有li元素
const tocItems = document.querySelectorAll('.elTree .el-tree-node__content');
// 为每个li元素绑定右键事件
tocItems.forEach((item, index) => {
// 为元素设置唯一ID
const elementId = `toc-item-${index}`;
item.id = elementId;
// 初始化参数存储
if (!this.itemParams[elementId]) {
this.$set(this.itemParams, elementId, '');
}
item.addEventListener('contextmenu', (e) => {
e.preventDefault();
this.showContextMenu(e, item);
});
item.addEventListener('click', (e) => {
console.log("左键",e,item)
this.dialogVisible = true;
});
});
// alert('右键菜单功能已启用!');
},
//测试 显示右键菜单
showContextMenu(e, element) {
console.log("ID",element)
// 获取该 <a> 元素
// const linkElement = element.querySelector('#toc-item-0 a');
// 提取 # 后面的参数
// const hashValue = linkElement.getAttribute('href');
// const hashValue2 = hashValue.split('#')[1];
// console.log(hashValue,hashValue2);
if (!this.isContextMenuEnabled) return;
// 设置右键菜单位置和当前选中元素
this.contextMenu = {
visible: true,
x: e.clientX,
y: e.clientY,
selectedElement: element,
selectedParam: this.itemParams[element.id] || ''
}
// 点击其他地方关闭菜单
document.addEventListener('click', this.closeContextMenu, { once: true });
},
//测试 关闭右键菜单
closeContextMenu() {
this.contextMenu.visible = false;
},
//测试 选择右键菜单选项
selectMenuOption(option) {
if (this.contextMenu.selectedElement) {
const element = this.contextMenu.selectedElement;
// 更新元素的参数
this.$set(this.itemParams, element.id, option);
// 在元素上显示参数(可选)
const paramSpan = element.querySelector('.param-display');
if (paramSpan) {
paramSpan.textContent = `(${option})`;
} else {
const span = document.createElement('span');
span.className = 'param-display';
span.style.cssText = 'color: #f00; margin-left: 10px;';
span.textContent = `(${option})`;
element.appendChild(span);
}
this.closeContextMenu();
}
},
// 新增方法打印nav内容
logTocItems() {
const navElement = document.getElementById('nav');
if (!navElement) {
console.error('未找到ID为"nav"的容器');
return;
}
// 获取nav内的HTML内容
const navHtml = navElement.innerHTML;
// 获取nav内的文本内容
const navText = navElement.innerText || navElement.textContent;
// 获取结构化数据(包括参数)
const structuredData = this.getStructuredNavData();
console.log('====== nav的HTML内容 ======');
console.log(navHtml);
console.log('====== nav的文本内容 ======');
console.log(navText);
console.log('====== nav的结构化数据 ======');
console.log(structuredData);
},
// 获取结构化数据
getStructuredNavData() {
const items = document.querySelectorAll('#nav ul li');
return Array.from(items).map(item => {
return {
id: item.id,
text: item.innerText.trim(),
html: item.innerHTML,
param: this.itemParams[item.id] || '未设置'
};
});
},
//测试 查看图片
openDialog() {
// this.imageStyle();
const imgItems = document.querySelectorAll('#main img');
console.log("imgItems",imgItems)
imgItems.forEach((item, index) => {
item.addEventListener('click', (e) => {
console.log("左键",e,item,index,item.src)
// 通过选择器获取
// let imgSrc = e.src;
this.scale = 1;
this.rotation = 0;
this.currentImage = item.src;
this.dialogVisibleImg = true;
});
});
// this.currentImage = this.images[index]
// this.dialogVisible = true
// this.reset() // 每次打开重置状态
},
//测试 放大
zoomIn() {
console.log("放大")
this.scale = 1.1*this.scale
},
//测试 缩小
zoomOut() {
if (this.scale > 0.5) {
this.scale -= 0.2
}
},
//测试 旋转
rotate() {
this.rotation += 90
},
//测试 重置
reset() {
this.scale = 1
this.rotation = 0
this.translateX = 0
this.translateY = 0
},
//测试 开始拖动
startDrag(e) {
this.isDragging = true
this.startX = e.type === 'mousedown' ? e.clientX : e.touches[0].clientX
this.startY = e.type === 'mousedown' ? e.clientY : e.touches[0].clientY
document.addEventListener('mousemove', this.dragImage)
document.addEventListener('touchmove', this.dragImage)
document.addEventListener('mouseup', this.endDrag)
document.addEventListener('touchend', this.endDrag)
},
//测试 拖动图片
dragImage(e) {
if (!this.isDragging) return
const clientX = e.type === 'mousemove' ? e.clientX : e.touches[0].clientX
const clientY = e.type === 'mousemove' ? e.clientY : e.touches[0].clientY
this.translateX += (clientX - this.startX) / this.scale
this.translateY += (clientY - this.startY) / this.scale
this.startX = clientX
this.startY = clientY
},
//测试 停止拖动
endDrag() {
this.isDragging = false
document.removeEventListener('mousemove', this.dragImage)
document.removeEventListener('touchmove', this.dragImage)
document.removeEventListener('mouseup', this.endDrag)
document.removeEventListener('touchend', this.endDrag)
},
//测试 图片样式
imageStyle() {
return {
transform: `
scale(${this.scale})
rotate(${this.rotation}deg)
translate(${this.translateX}px, ${this.translateY}px)
`,
cursor: this.isDragging ? 'grabbing' : 'grab'
}
},
gengxin() {
document.getElementsByClassName('w-e-text-container')[0].style.height = (window.innerHeight - 240) + 'px' ;
document.getElementsByClassName('w-e-text-container')[1].style.height = (window.innerHeight - 240) + 'px' ;
},
//获取屏幕尺寸
getWindowSize() {
// this.windowW = window.innerWidth
// this.windowH = window.innerHeigh
// t
this.leftHeight = window.innerHeight - 200;
this.editorHeight = window.innerHeight - 200;
},
// 尺寸变化时的处理函数
handleResize() {
// 使用节流优化性能(频繁触发时限制执行频率)
if (this.resizeTimer) clearTimeout(this.resizeTimer)
this.resizeTimer = setTimeout(() => {
this.getWindowSize()
console.log('窗口尺寸变化:', this.windowW, this.windowH)
// 在这里可以添加尺寸变化后的业务逻辑
// 例如:调整组件布局、重新计算元素位置等
}, 100) // 100ms 节流延迟,可根据需求调整
},
// 加载XML文件 dmc
loadFileBtn() {
this.$sendToDotNet('GetExistenceDMContent')
},
// 获取屏幕和窗口尺寸
getScreenSize() {
// 屏幕整体尺寸(设备的屏幕尺寸)
this.screenWidth = window.screen.width;
this.screenHeight = window.screen.height;
// 浏览器窗口尺寸(可视区域尺寸)
this.windowW = window.innerWidth;
this.windowH = window.innerHeight;
console.log("屏幕变化的尺寸",window.screen.width,window.screen.height,window.innerWidth,window.innerHeight)
// 可以在这里添加需要根据尺寸执行的逻辑
this.handleSizeChange();
},
// 处理窗口大小变化(使用防抖优化性能)
// handleResize() {
// // 清除之前的定时器
// if (this.resizeTimer) {
// clearTimeout(this.resizeTimer);
// }
// // 防抖50ms后再执行避免频繁触发
// this.resizeTimer = setTimeout(() => {
// this.getScreenSize();
// this.resizeTimer = null;
// }, 50);
// },
// 根据尺寸变化执行的逻辑
handleSizeChange() {
// 这里可以添加尺寸变化时需要执行的操作
console.log('尺寸变化:', {
screenWidth: this.screenWidth,
screenHeight: this.screenHeight,
windowWidth: this.windowW,
windowHeight: this.windowH
});
},
//预览加载的XML
viewLoadDMFile() {
this.maskViewShow = true;
// this.draggableVis = true;
// localStorage.setItem("fileName",'dassad')
this.$sendToDotNet('DMPreview','UserLocalDMPreview')
},
//解析加载的DMC
parseXmlDmc() {
try {
// 解析XML字符串
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(this.loadFileXML, "text/xml");
// 检查解析错误
const errorNode = xmlDoc.querySelector("parsererror");
if (errorNode) {
throw new Error("XML解析错误: " + errorNode.textContent);
}
// 提取content标签内容
this.extractContent(xmlDoc);
// 解析rdf:Description
this.parseRdfDescription(xmlDoc);
} catch (error) {
console.error("解析失败:", error);
// alert("解析XML失败: " + error.message);
}
},
//解析新建目录时候的DMC文件
parseXmlDmc_loadFile() {
try {
// 解析XML字符串
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(this.viewXml, "text/xml");
// 检查解析错误
const errorNode = xmlDoc.querySelector("parsererror");
if (errorNode) {
throw new Error("XML解析错误: " + errorNode.textContent);
}
// 提取content标签内容
this.extractContent2(xmlDoc);
} catch (error) {
console.error("解析失败:", error);
// alert("解析XML失败: " + error.message);
}
},
//验证是否修改过的dmc文件
handleCheckVersion() {
this.showModifyBtn = true;
// 可选若需高亮有01版本的节点可在此处触发树重渲染
this.$nextTick(() => {
this.$refs.tree.updateKeyChildren(); // 强制树更新节点
});
},
// 3. 递归判断当前节点或其所有子节点是否存在minorVersion: '01'
hasMinorVersion01(node) {
// 边界条件节点不存在则返回false
if (!node) return false;
// 第一步检查当前节点是否为01版本
if (node.minorVersion === '01') {
return true;
}
// 第二步:若有子节点,递归检查所有子节点
if (node.children && node.children.length > 0) {
// 只要有一个子节点满足就返回true
return node.children.some(child => this.hasMinorVersion01(child));
}
// 第三步无子孙节点且当前节点不是01版本返回false
return false;
},
// 4. 【修改】文字点击事件(可根据需求扩展修改逻辑)
handleModify(node) {
console.log('当前需要修改的节点:', node);
// 示例:可打开修改弹窗、跳转修改页面等
// this.$emit('openModifyDialog', node);
},
// 提取content标签内的内容
extractContent(xmlDoc) {
const contentNodes = xmlDoc.getElementsByTagName("content");
if (contentNodes.length === 0) {
this.contentResult = "未找到content标签";
return;
}
const contentNode = contentNodes[0];
let contentHtml = "";
// 序列化content节点的所有子节点
for (let i = 0; i < contentNode.childNodes.length; i++) {
const child = contentNode.childNodes[i];
// 忽略空文本节点
if (child.nodeType === 1 || (child.nodeType === 3 && child.textContent.trim())) {
contentHtml += new XMLSerializer().serializeToString(child);
}
}
this.loadFileDMC = contentHtml.trim();
console.log("解析出来DMC内容",contentHtml)
console.log("解析出来DMC内容",contentHtml.trim())
// this.contentResult = contentHtml.trim();
},
// 提取新建目录时候的DMC文件中content标签内的内容
extractContent2(xmlDoc) {
const contentNodes = xmlDoc.getElementsByTagName("content");
if (contentNodes.length === 0) {
this.contentResult = "未找到content标签";
return;
}
const contentNode = contentNodes[0];
let contentHtml = "";
// 序列化content节点的所有子节点
for (let i = 0; i < contentNode.childNodes.length; i++) {
const child = contentNode.childNodes[i];
// 忽略空文本节点
if (child.nodeType === 1 || (child.nodeType === 3 && child.textContent.trim())) {
contentHtml += new XMLSerializer().serializeToString(child);
}
}
this.viewXmlContent = contentHtml.trim();
console.log("加载解析出来DMC内容",contentHtml)
console.log("加载解析出来DMC内容",contentHtml.trim())
this.restoreContentXh2(this.viewXmlContent)
// this.contentResult = contentHtml.trim();
},
// 解析rdf:Description为对象
parseRdfDescription(xmlDoc) {
const rdfObj = {};
const rdfNamespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
const rdfNode = xmlDoc.getElementsByTagNameNS(rdfNamespace, "Description")[0];
if (!rdfNode) {
// this.rdfResult = { error: "未找到rdf:Description标签" };
return;
}
// 解析dc命名空间下的所有子节点
const dcNamespace = "http://purl.org/dc/elements/1.1/";
const dcNodes = rdfNode.getElementsByTagNameNS(dcNamespace, "*");
Array.from(dcNodes).forEach(node => {
const key = node.localName; // 获取不带命名空间前缀的标签名
const value = node.textContent.trim() || ""; // 处理空标签
rdfObj[key] = value;
});
console.log("解析出来DMC目录内容",rdfObj)
// this.rdfResult = rdfObj;
for(let key in this.form){
this.form[key] = rdfObj[key]?rdfObj[key]:'';
}
this.form.name = rdfObj.title;
this.form.version = '000';
},
//转换时间值
timeChange(isoTime){
const date = new Date(isoTime);
// 3. 提取时间组件(注意:月份从 0 开始,需 +1数字不足 2 位时补 0
const year = date.getFullYear(); // 年份2025
const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份09getMonth() 返回 8+1 后补 0
const day = String(date.getDate()).padStart(2, "0"); // 日期01
const hours = String(date.getHours()).padStart(2, "0"); // 小时1624小时制
const minutes = String(date.getMinutes()).padStart(2, "0"); // 分钟39
const seconds = String(date.getSeconds()).padStart(2, "0"); // 秒07
// 4. 拼接成正常时间格式
const normalTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
return normalTime;
},
// 关闭预览弹窗
handleCloseMask() {
this.maskViewShow = false;
},
//关闭弹窗节点
handleClose(type) {
console.log('点',type);
this.dialogVisibleHistory = false;
this.dialogVisibleNav = false;
this.dialogVisibleRight = false;
this.dialogVisibleTips = false;
this.dialogVisibleTips2 = false;
this.dialogVisibleBland = false;
this.dialogVisibleBland = false;
this.currentNode = null;
this.clearNodeState();
},
handleClose2() {
this.dialogVisibleHistory = false;
this.dialogVisibleNav = false;
this.dialogVisibleRight = false;
this.dialogVisibleTips = false;
this.dialogVisibleTips2 = false;
this.dialogVisibleBland = false;
this.dialogVisibleBland = false;
},
//测试 保存当前目录信息
saveNowDmcClick() {
const targetNode = this.findNodeById(this.treeData,this.currentNode.id);
targetNode.minorVersion = '01';
const aaa = this.editor.txt.html();
console.log("当前的所有html内容4",aaa)
this.catalogueAnalysis(this.treeData);
this.generateXml(false,this.currentNode.id);
// this.$sendToDotNet('RefreshTree');
this.contentXmlStr = this.editor.txt.html();
},
//保存当前DMC信息
saveNowDmc2(type) {
console.log("保存时候点击",this.lastClick)
if(type == 'save'){
console.log("保存时候点击 this.currentNode",this.currentNode,this.currentNode.minorVersion)
// if(this.currentNode.minorVersion == '00'){
const targetNode = this.findNodeById(this.treeData,this.lastClick);
targetNode.minorVersion = '01';
// }
console.log("保存时候点击 treedata",this.treeData)
this.catalogueAnalysis(this.treeData);
this.generateXml(false,this.lastClick);
this.contentXmlStr = this.editor.txt.html();
// const targetNode = this.findNodeById(this.treeData, this.lastClick);
// if (targetNode) {
// targetNode.minorVersion = '01';
// }
// this.catalogueAnalysis(this.treeData);
}else if(type == 'noSave'){
this.getDmName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+this.lastClick+this.domNameStr[3]+this.domNameStr[4];
console.log("加载富文本的文件名",this.getDmName)
this.loadFWBFile(this.getDmName);
console.log("不保存")
}
this.dialogVisibleTips2 = false;
console.log("saveNowDmc2",type)
},
//保存当前DMC信息
saveNowDmc(type){
if(type == 'save'){
this.navIsAdd = 'other'
this.generateXml(false);
this.dialogVisibleTips = false;
this.dialogVisibleNav = true;
}else if(type == 'noSave'){
this.getDmName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+this.lastClick+this.domNameStr[3]+this.domNameStr[4];
this.loadFWBFile(this.getDmName);
this.dialogVisibleTips = false;
this.dialogVisibleNav = true;
}
this.clearFormData();
this.showView = false;
this.$sendToDotNet('CancelUserHistoy')
},
//测试 绑定资源弹窗显示
showBand() {
this.dialogVisibleRight = false;
this.dialogVisibleBland = true;
},
//测试 历史版本展示
showHistory() {
this.dialogVisibleRight = false;
this.dialogVisibleHistory = true;
this.getDmName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+this.currentNode.id+this.domNameStr[3]+this.domNameStr[4];
console.log("历史版本查询时候的名字",this.getDmName)
console.log("历史版本查询时候的domNameStr",this.domNameStr)
console.log("历史版本查询时候的currentNode",this.currentNode)
this.$sendToDotNet("GetHistoryVesions",this.getDmName)
},
//版本定稿
finalVersion() {
console.log("定版时候的参数",this.currentNode)
const newStr = 'A-A_'+this.currentNode.lagreVersion+'_00';
// let versionName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+this.currentNode.id+this.domNameStr[3]+this.domNameStr[4]+'.xml';
const versionName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+this.currentNode.id+newStr+this.domNameStr[4]+'.xml';
// this.currentNode.minorVersion = '00';
const targetNode = this.findNodeById(this.treeData,this.currentNode.id);
targetNode.minorVersion = '00';
this.catalogueAnalysis(this.treeData);
console.log(versionName)
this.$sendToDotNet("CreatVersion",versionName)
},
//显示新建弹窗
showAddDialog(type) {
console.log("点击新建",type)
this.$sendToDotNet("CancelPriview")
this.navIsAdd = type;
if(type == "create"){
this.form.name = '';
this.isLoadOldDmc = '2',
this.clearFormData();
}else{
this.form.name = this.currentNode.name;
}
if(this.dmcIsChange()){
this.dialogVisibleNav = true;
this.showView = false;
}else{
this.dialogVisibleTips = true;
}
// this.$sendToDotNet('CancelUserHistoy')
this.dialogVisibleRight = false;
},
//截取路径信息
removeQuotationMarks(data) {
const result = data.replace(/^"|"$/g, '');
return result;
},
//目录节点点击
handleNodeClick(data) {
this.currentNode = data;
this.navIsAdd = '';
this.gengxin();
console.log("这一次点击的节点信息",data);
console.log("上一次点击的时候的ID",this.lastClick)
console.log("这一次点击的时候的ID",this.nowClick)
if(this.lastClick == this.nowClick.id){
this.nowClick = data;
this.lastClick = data.id;
// this.generateXml(false);
// this.catalogueAnalysis(this.treeData);
}else{
console.log("中中中中中",this.lastClick,this.dmcIsChange())
if(this.lastClick&&!this.dmcIsChange()){
this.dialogVisibleTips2 = true;
console.log("dialogVisibleTips2出现")
console.log("恢复成上一次点击",this.lastClick)
// this.$refs.tree.setCheckedKeys([this.lastClick]);
// 确保DOM更新后再重置状态
this.$nextTick(() => {
this.$refs.tree.setCheckedKeys([this.lastClick]);
this.$refs.tree.setCurrentKey(this.lastClick);
});
}else{
const target = this.domNameStr[3];
// 假设需要替换的格式是 "前缀_xxx_yy"其中xxx是3位yy是2位
// 先找到最后两个下划线的位置
const lastUnderlineIndex = target.lastIndexOf('_');
const secondLastUnderlineIndex = target.lastIndexOf('_', lastUnderlineIndex - 1);
if (lastUnderlineIndex > -1 && secondLastUnderlineIndex > -1) {
// 截取前缀部分(到第二个下划线)
const prefix = target.substring(0, secondLastUnderlineIndex + 1);
// 截取中间部分3位和末尾部分2位并用新值替换
// const newValue = `${prefix}${data.lagreVersion}_${data.minorVersion}`;
const newValue = `${prefix}${data.lagreVersion}_00`;
// 更新数组
this.domNameStr.splice(3, 1, newValue);
this.replaced = true;
}
console.log("点击时候的节点data",data.id)
this.getDmName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+data.id+this.domNameStr[3]+this.domNameStr[4];
console.log("加载富文本的文件名",this.getDmName)
this.loadFWBFile(this.getDmName);
this.lastClick = data.id;
}
// if(this.lastClick){
// if(!this.dmcIsChange()){
// this.navIsAdd = 'edit';
// this.generateXml(false,this.lastClick);
// }
// }
// this.getDmName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+data.id+this.domNameStr[3]+this.domNameStr[4];
// console.log("加载富文本的文件名",this.getDmName)
// this.loadFWBFile(this.getDmName);
}
console.log("当前节点点击",data);
// this.$sendToDotNet('GetFilePath','PMC',data.id,'');
},
//确认新建目录
confirmAdd(type) {
if(type){
this.loadFileDMC = '';
}else{
this.$sendToDotNet('CopyToWorkDire');
}
this.$refs.form.validate((valid) => {
if (!valid) return
// if(!this.dmcIsChange()){
// }else{
// }
const newNode = {
id: this.queryMaxId(),
maxId:this.mId,
name: this.form.name,
attributes: [],
formMes:this.form,
lagreVersion:this.form.version,//大版本
minorVersion:'00',//临时版本
children: []
}
console.log("新建章节目录时候的参数",newNode)
this.nowMaxId = newNode.id;
if (this.currentNode) {
if (!this.currentNode.children) {
this.$set(this.currentNode, 'children', [])
}
this.currentNode.children.push(newNode)
} else {
this.treeData.push(newNode)
}
this.dialogVisibleNav = false
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(newNode.id)
this.currentNode = newNode;
this.catalogueAnalysis(this.treeData);
// this.$sendToDotNet('SaveFile','PMC',newNode.id,JSON.stringify(this.currentNode));
//创建目录时候同时创建一个空DM文件
this.generateXml(true)
})
})
},
//编辑目录时候点击确认进行保存
confirmEdit() {
this.currentNode.name = this.form.name;
console.log("点击编辑确定",this.currentNode,this.treeData)
this.currentNode.minorVersion = '01';
this.catalogueAnalysis(this.treeData);
this.isNavEdit = true;
this.dialogVisibleNav = false;
this.generateXml(false,this.treeData.id)
// let targetNode = this.findNodeById(this.treeData,this.currentNode.id);
// if (targetNode) {
// // 替换attributes
// // targetNode.name = ...this.currentNode.name];
// // this.hasReplaced = true;
// console.log('替换成功,新的节点数据:', targetNode);
// } else {
// console.warn('未找到节点');
// }
// console.log('获取当前节点',this.currentNode)
},
// 删除时候的二次确认弹窗
deleteDialog() {
console.log("currentNode.children",this.currentNode)
this.dialogVisibleNavDel = true;
},
// 删除绑定资源按钮
deleteAttr(name,index) {
this.nowAttr ={
name:name,
index:index,
}
this.dialogVisibleAttrDel = true;
},
// 删除绑定资源二次确定
deleteAttrSure() {
this.currentNode.attributes.splice(this.nowAttr.index, 1);
this.catalogueAnalysis(this.treeData);
this.dialogVisibleAttrDel = false;
},
//删除节点时候判断是否有子节点。
deleteNode() {
if (this.currentNode.children && this.currentNode.children.length > 0) {
this.$message({
type: 'warning',
message: '该节点包含子节点,不能删除!'
})
}else{
let isDel = this.deleteNodeFromTree(this.treeData, this.currentNode.id)
console.log("删除是否成功",isDel)
// this.currentNode = null;
const fileId = this.currentNode.id;
this.currentNode = '';
this.catalogueAnalysis(this.treeData);
this.dialogVisibleNavDel = false;
// this.$sendToDotNet('RefreshTree');
console.log('删除RMDMfile fileId',fileId)
this.$sendToDotNet('RMDMfile','DMC',fileId)
// this.$message({
// type: 'success',
// message: '删除成功!'
// })
}
},
// 更新指定节点的版本号
updateNodeVersion(id,vs) {
const targetNode = this.findNodeById(this.treeData2, id);
if (targetNode) {
targetNode.lagreVersion = vs;
}
},
//删除节点
deleteNodeFromTree(nodes, id) {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].id === id) {
nodes.splice(i, 1)
return true
}
if (nodes[i].children && nodes[i].children.length > 0) {
if (this.deleteNodeFromTree(nodes[i].children, id)) {
return true
}
}
}
return false
},
//重置输入框
resetForm() {
this.$refs.form.resetFields()
},
// 搜索相关方法
handleSearch() {
if (!this.searchText.trim()) {
this.$refs.tree.filter(this.searchText);
// 搜索内容为空时,重置所有节点的可见性
this.resetTreeVisibility(this.treeData)
}else{
this.$refs.tree.filter(this.searchText)
}
},
//清除搜索
handleSearchClear() {
this.searchText = '';
// 清空搜索时重置所有节点的可见性
this.$refs.tree.filter('');
this.resetTreeVisibility(this.treeData)
},
//重置目录节点信息
resetTreeVisibility(nodes) {
nodes.forEach(node => {
// 重置节点的可见性
node.visible = true
if (node.children && node.children.length > 0) {
this.resetTreeVisibility(node.children)
}
})
// 强制刷新树组件
this.$refs.tree.updateKeyChildren()
},
//目录节点信息
filterNode(value, data) {
if (!value) return true; // 空值时显示所有节点
// 这里假设你要搜索的是节点的name属性
return data.name.toLowerCase().includes(value.toLowerCase());
},
//筛选后高亮class
isHighlight(data) {
return this.searchText && data.name.toLowerCase().includes(this.searchText.toLowerCase())
},
//右键
// 处理右键点击事件
handleRightClick(event, data, node, component) {
console.log("node, component",node, component)
event.preventDefault(); // 阻止默认右键菜单
this.currentNode = data;
// this.getDmName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+this.lastClick+this.domNameStr[3]+this.domNameStr[4];
// console.log("删除的DM名字",this.getDmName)
// return;
this.dialogVisibleRight = true;
// 初始化已选属性
// this.selectedAttributes = data.attributes ? [...data.attributes] : [];
// // 显示弹窗
// this.$sendToDotNet('GetFilePath','PMC',data.id,'');
},
// 保存属性
saveAttributes() {
console.log("this.currentNode.id",this.currentNode.id)
this.$sendToDotNet('GetFilePath','PMC',this.currentNode.id,'');
// if (this.currentNode) {
// // 保存选中的属性到当前节点
// this.currentNode.attributes = [...this.selectedAttributes];
// // 刷新树结构
// this.$refs.tree.setCurrentKey(this.currentNode.id);
// }
// this.dialogVisibleRight = false;
},
//查询最大ID值
queryMaxId() {
// maxId = '00001'
console.log("初始化ID",this.treeData)
// if(this.treeData[0]){
let maxId = this.mId?this.mId:'00001';
const length = maxId.length;
// 将字符串转换为数字并加1
const number = parseInt(maxId, 10) + 1;
// 格式化数字为指定长度的字符串,不足的前面补零
maxId = number.toString().padStart(length, '0');
// }
this.mId = maxId;
return maxId;
// let maxId = 0;
// let treeData = this.treeData;
// // 递归遍历函数
// const traverse = (nodes) => {
// if (!nodes || nodes.length === 0) return;
// nodes.forEach(node => {
// // 比较当前节点ID与最大值
// if (node.id > maxId) {
// maxId = node.id;
// }
// // 递归处理子节点
// traverse(node.children);
// });
// };
// // 开始遍历
// traverse(treeData);
// console.log("当前数据最大ID",maxId)
// const length = maxId.length;
// // 将字符串转换为数字并加1
// const number = parseInt(maxId, 10) + 1;
// // 格式化数字为指定长度的字符串,不足的前面补零
// maxId = number.toString().padStart(length, '0');
// if(maxId == 1){
// maxId = '00001'
// }
// return maxId;
},
//解析XML目录暂时不用
extractDmCode(xmlString) {
// 1. 解析 XML 字符串
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, "text/xml");
// 2. 获取所有 dmCode 节点
const dmCodeNodes = xmlDoc.getElementsByTagName("dmCode");
const issueInfoNodes = xmlDoc.getElementsByTagName("issueInfo");
const languageNodes = xmlDoc.getElementsByTagName("language");
// 3. 遍历节点,提取属性
let dmCodeList = Array.from(dmCodeNodes).map(node => {
const attributes = {};
// 遍历节点的所有属性
for (let i = 0; i < node.attributes.length; i++) {
const attr = node.attributes[i];
attributes[attr.name] = attr.value;
}
return attributes;
});
let issueInfoList = Array.from(issueInfoNodes).map(node => {
const attributes = {};
// 遍历节点的所有属性
for (let i = 0; i < node.attributes.length; i++) {
const attr = node.attributes[i];
attributes[attr.name] = attr.value;
}
return attributes;
});
let languageList = Array.from(languageNodes).map(node => {
const attributes = {};
// 遍历节点的所有属性
for (let i = 0; i < node.attributes.length; i++) {
const attr = node.attributes[i];
attributes[attr.name] = attr.value;
}
return attributes;
});
// 处理函数:将对象按指定字段集拼接成字符串
let dmCodeTitle = ['modelIdentCode','systemDiffCode','systemCode','subSystemCode','subSubSystemCode','disassyCode','disassyCodeVariant','infoCode','infoCodeVariant','itemLocationCode']
let issueInfoTitle = ['issueNumber','inWork'];
let languageTitle = ['languageIsoCode','countryIsoCode']
// 处理三组数据
let dmCodeResult = this.joinFields(dmCodeList, dmCodeTitle,'-');
let issueInfoResult = this.joinFields(issueInfoList, issueInfoTitle,'_');
let languageResult = this.joinFields(languageList, languageTitle,'_');
console.log("参数 dmCodeResult",dmCodeResult)
console.log("参数 issueInfoResult",issueInfoResult)
console.log("参数 languageResult",languageResult)
// let dmCodeResult2 = [];
let newList = [];
for(let i = 0;i<dmCodeResult.length;i++){
// let strA = dmCodeResult[0]+'_'+dmCodeResult[0]
let str = dmCodeResult[i]+"-"+issueInfoResult[i]+"-"+languageResult[i]
newList.push(str);
}
console.log("结果",newList)
},
//解析目录节点数据
generateTreeData(xmlContent) {
console.log("加载XML解析内容 初始化内容",xmlContent)
// 1. 解析XML
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlContent, "text/xml");
console.log("加载XML解析内容 初始化XML",xmlDoc)
// 2. 获取根节点content下的所有pmEntry
const contentNode = xmlDoc.querySelector("content");
console.log("加载XML解析内容 content",contentNode)
const rootPmEntries = contentNode ? Array.from(contentNode.children).filter(node => node.tagName === "pmEntry") : [];
// 3. 递归处理节点,构建树形结构
this.treeData = this.parsePmEntries(rootPmEntries);
this.handleCheckVersion();
// console.log("")
console.log("重新解析结果",this.treeData)
// if(this.treeData.length>0){
// this.clearNodeState();
// }
// this.enableContextMenu();//绑定右键
// 获取最大ID
const simpleParaNode = xmlDoc.querySelector("simplePara");
console.log("加载XML解析内容 id",simpleParaNode)
const maxId = simpleParaNode.getAttribute("maxId") || "00001";
console.log("当前目录最大ID",maxId)
this.mId = maxId;
},
/**
* 递归解析pmEntry节点列表生成树形数据
* @param {NodeList} pmEntries - pmEntry节点列表
* @param {Object} parent - 父节点用于处理嵌套关系
* @returns {Array} 树形结构数组
*/
parsePmEntries(pmEntries, parent = null) {
const treeNodes = [];
console.log("parent",parent)
pmEntries.forEach(entry => {
// const lagreVersion = parent.getAttribute("lagreVersion") || "";
// const minorVersion = parent.getAttribute("minorVersion") || "";
// console.log("数循环结果 大版本",lagreVersion)
// console.log("数循环结果 小版本",minorVersion)
// a. 提取当前节点的id、name、attributes
const titleNode = entry.querySelector("pmEntryTitle") || entry.querySelector("techName");
if (!titleNode) return; // 跳过无标题的节点
const id = titleNode.getAttribute("id");
const name = titleNode.textContent.trim();
const attributesStr = titleNode.getAttribute("attributes") || "";
const lagreVersion = titleNode.getAttribute("lagreVersion") || "000";
const minorVersion = titleNode.getAttribute("minorVersion") || "00";
// const maxID = titleNode.getAttribute("maxId") || "00001";
const attributes = attributesStr ? attributesStr.split(",").map(item => item.trim()) : [];
// b. 创建节点基本结构
const node = {
id,
name,
// maxId:maxID,
attributes,
formMes: {}, // 固定空对象
children: [],
lagreVersion:lagreVersion,//大版本
minorVersion:minorVersion,//临时版本
};
// c. 处理子节点当前pmEntry下的子pmEntry
const childEntries = Array.from(entry.children).filter(child => child.tagName === "pmEntry");
if (childEntries.length > 0) {
node.children = this.parsePmEntries(childEntries, node);
}
treeNodes.push(node);
});
return treeNodes;
},
//测试 解析xml 暂未使用
parseXml(xmlInput) {
this.errorMessage = '';
this.parsedData = [];
try {
// 创建DOMParser实例
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlInput, "text/xml");
console.log("xmlDoc",xmlDoc)
// 检查XML解析错误
const parserError = xmlDoc.getElementsByTagName("parsererror")[0];
if (parserError) {
throw new Error("XML格式错误: " + parserError.textContent);
}
// 获取所有顶级pmEntry节点
const pmEntries = xmlDoc.querySelectorAll('content > pmEntry');
let parsedData = [];
// 解析每个pmEntry
pmEntries.forEach(pmEntry => {
const entryData = this.parsePmEntry(pmEntry);
if (entryData) {
parsedData.push(entryData);
}
});
parsedData.map((res)=>{
if(res.infoCode){
return res.is = res.infoCode,res.name = res.title;
}
})
this.treeData = parsedData;
console.log("parsedData",parsedData)
} catch (error) {
this.errorMessage = "解析失败: " + error.message;
console.error("XML解析错误:", error);
}
},
/**
* 获取指定节点的值
* @param {Element} parentNode - 父节点
* @param {string} selector - 节点选择器
* @returns {string} 节点值
*/
getNodeValue(parentNode, selector) {
const node = parentNode.querySelector(selector);
return node ? node.textContent.trim() : '';
},
//格式化数据提取节点内容
parsePmEntry(pmEntryElement) {
// 获取pmEntryTitle
const titleElement = pmEntryElement.querySelector('pmEntryTitle');
if (!titleElement) return null;
const title = titleElement.textContent.trim();
const result = {
title,
children: [],
subItems: []
};
// 获取当前pmEntry下的所有子节点
const childNodes = Array.from(pmEntryElement.childNodes);
// 查找子pmEntry和dmRef节点
childNodes.forEach(node => {
if (node.nodeType !== 1) return; // 只处理元素节点
if (node.tagName === 'pmEntry') {
// 递归解析子pmEntry
const childData = this.parsePmEntry(node);
if (childData) {
result.children.push(childData);
}
} else if (node.tagName === 'dmRef') {
// 解析dmRef中的infoCode
const dmCode = node.querySelector('dmRefIdent > dmCode');
if (dmCode && dmCode.hasAttribute('infoCode')) {
result.subItems.push({
infoCode: dmCode.getAttribute('infoCode')
});
}
}
});
// 如果没有子节点但有subItems设置当前节点的infoCode为第一个subItem的infoCode
if (result.children.length === 0 && result.subItems.length > 0) {
result.infoCode = result.subItems[0].infoCode;
}
return result;
},
// 处理数据信息
joinFields(dataList, fields) {
return dataList.map(item => {
return fields.map(field => item[field]).join('_');
});
},
//处理目录节点信息
catalogueAnalysis(treeData) {
console.log("处理每个节点信息 treeData",treeData)
// 递归处理每个节点跟踪路径用于生成infoName
const processNode = (node, path = [], level = 0) => {
let nodeStr = '';
console.log("处理每个节点信息 node",node)
// 每个节点都用pmEntry标签包裹
nodeStr += '<pmEntry>\n';
// 计算当前节点的路径用于生成infoName
const currentPath = [...path, node.name];
// 如果有子节点添加pmEntryTitle标签
if (node.children && node.children.length > 0) {
nodeStr += `\t<pmEntryTitle attributes='${node.attributes}' lagreVersion="${node.lagreVersion}" minorVersion="${node.minorVersion}" id="${node.id}">${node.name}</pmEntryTitle>\n`;
// 递归处理子节点
node.children.forEach((child, index) => {
console.log("index",index)
// 递归处理子节点传入当前路径移除了未使用的infoCode和issueDate参数
nodeStr += processNode(child, currentPath, level + 1);
});
} else {
// 没有子节点的节点用dmRef标签包裹
nodeStr += '\t<dmRef>\n';
// 添加dmRefIdent部分包含动态生成的dmCode
nodeStr += `\t\t<dmRefIdent>\n`;
nodeStr += `\t\t\t<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="${getInfoCode(level)}" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="${getSubSubSystemCode(level)}" subSystemCode="${getSubSystemCode(level)}" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>\n`;
nodeStr += `\t\t\t<issueInfo issueNumber="001" inWork="01"/>\n`;
nodeStr += `\t\t\t<language languageIsoCode="zh" countryIsoCode="CN"/>\n`;
nodeStr += `\t\t</dmRefIdent>\n`;
// 添加dmRefAddressItems标签
nodeStr += `\t\t<dmRefAddressItems>\n`;
// 添加dmTitle标签包含techName和infoName
nodeStr += `\t\t\t<dmTitle>\n`;
nodeStr += `\t\t\t\t<techName attributes='${node.attributes}' lagreVersion="${node.lagreVersion}" minorVersion="${node.minorVersion}" id="${node.id}">${node.name}</techName>\n`;
nodeStr += `\t\t\t\t<infoName>${currentPath.join('-')}</infoName>\n`;
nodeStr += `\t\t\t</dmTitle>\n`;
// 添加issueDate标签
nodeStr += `\t\t\t<issueDate year="2024" month="06" day="${getIssueDay(level)}"/>\n`;
// 闭合dmRefAddressItems标签
nodeStr += `\t\t</dmRefAddressItems>\n`;
// 闭合dmRef标签
nodeStr += `\t</dmRef>\n`;
}
// 闭合当前节点的pmEntry标签
nodeStr += '</pmEntry>\n';
return nodeStr;
};
// 根据层级和索引生成infoCode
const getInfoCode = (level, index = 0) => {
if (level === 1 && index === 0) return "042";
if (level === 1 && index === 1) return "041";
return "040";
};
// 根据层级生成subSystemCode
const getSubSystemCode = (level) => {
if (level === 0) return "0";
if (level === 1) return "1";
if (level === 2) return "3";
return "0";
};
// 根据层级生成subSubSystemCode
const getSubSubSystemCode = (level, index = 0) => {
if (level === 1) return "1";
if (level === 2) return index + 1;
return "0";
};
// 根据层级生成issueDay
const getIssueDay = (level) => {
return level === 1 ? "13" : "12";
};
// 处理所有根节点
let result = '';
treeData.forEach(node => {
result += processNode(node);
});
console.log('catalogueString 目录字符串',result);
this.catalogueString = result;
this.saveNav();
// console.log(this.treeData)
},
//生成目录节点XML信息 并保存目录pmc
saveNav() {
let xmlStr = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE pm [
]>
<pm xsi:noNamespaceSchemaLocation="http://www.s1000d.org/S1000D_4-1/xml_schema_flat/pm.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://www.purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink">
<identAndStatusSection>
<pmAddress>
<pmIdent>
<pmCode modelIdentCode="YSC001" pmIssuer="732380192B" pmNumber="00001" pmVolume="00"/>
<language countryIsoCode="CN" languageIsoCode="zh"/>
<issueInfo issueNumber="000" inWork="01"/>
</pmIdent>
<pmAddressItems>
<issueDate year="2024" month="06" day="24"/>
<pmTitle>无人机系统地面运输方舱使用维护说明书</pmTitle>
</pmAddressItems>
</pmAddress>
<pmStatus issueType="new">
<security securityClassification="01"/>
<responsiblePartnerCompany enterpriseCode=" "/>
<originator/>
<applic>
<displayText>
<simplePara maxId='`+this.mId+`'>A0304-1A无人机系统地面运输方舱</simplePara>
</displayText>
</applic>
<brexDmRef>
<dmRef xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace" xlink:href="URN:S1000D:DMC-S1000D-E-04-10-0301-00A-022A-D">
<dmRefIdent>
<dmCode modelIdentCode="YSC001" systemDiffCode="A" systemCode="P4" subSystemCode="0" subSubSystemCode="0" assyCode="00" disassyCode="00" disassyCodeVariant="A" infoCode="022" infoCodeVariant="A" itemLocationCode="A"/>
</dmRefIdent>
</dmRef>
</brexDmRef>
<qualityAssurance>
<unverified/>
</qualityAssurance>
</pmStatus>
</identAndStatusSection>
<content>`
+this.catalogueString+
`</content>
</pm>`
console.log("传递字符串",this.catalogueString)
console.log("存时候",xmlStr)
this.$sendToDotNet('SaveFile','PMC','',xmlStr);
},
//测试 加载目录
jzml() {
let a = `<!DOCTYPE pm [
]>
<pm xsi:noNamespaceSchemaLocation="http://www.s1000d.org/S1000D_4-1/xml_schema_flat/pm.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://www.purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink">
<identAndStatusSection>
<pmAddress>
<pmIdent>
<pmCode modelIdentCode="YSC001" pmIssuer="732380192B" pmNumber="00001" pmVolume="00"/>
<language countryIsoCode="CN" languageIsoCode="zh"/>
<issueInfo issueNumber="000" inWork="01"/>
</pmIdent>
<pmAddressItems>
<issueDate year="2024" month="06" day="24"/>
<pmTitle>无人机系统地面运输方舱使用维护说明书</pmTitle>
</pmAddressItems>
</pmAddress>
<pmStatus issueType="new">
<security securityClassification="01"/>
<responsiblePartnerCompany enterpriseCode=" "/>
<originator/>
<applic>
<displayText>
<simplePara>A0304-1A无人机系统地面运输方舱</simplePara>
</displayText>
</applic>
<brexDmRef>
<dmRef xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace" xlink:href="URN:S1000D:DMC-S1000D-E-04-10-0301-00A-022A-D">
<dmRefIdent>
<dmCode modelIdentCode="YSC001" systemDiffCode="A" systemCode="P4" subSystemCode="0" subSubSystemCode="0" assyCode="00" disassyCode="00" disassyCodeVariant="A" infoCode="022" infoCodeVariant="A" itemLocationCode="A"/>
</dmRefIdent>
</dmRef>
</brexDmRef>
<qualityAssurance>
<unverified/>
</qualityAssurance>
</pmStatus>
</identAndStatusSection>
<content><pmEntry>
<pmEntryTitle>装备概况</pmEntryTitle>
<pmEntry>
<pmEntryTitle>方舱概述</pmEntryTitle>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="040" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="3" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00003">无人机系统</techName>
<infoName>装备概况-方舱概述-功能介绍</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="12"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="040" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="3" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00004">无人机系统</techName>
<infoName>装备概况-方舱概述-规格介绍</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="12"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="042" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="1" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes='["金","木","水"]' id="00005">无人机系统</techName>
<infoName>装备概况-舱体</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="13"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="042" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="1" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00006">无人机系统</techName>
<infoName>装备概况-机身转运小车</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="13"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="042" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="1" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00007">无人机系统</techName>
<infoName>装备概况-无人机部件存储装置</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="13"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="042" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="1" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00008">无人机系统</techName>
<infoName>装备概况-机身转运小车</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="13"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<pmEntryTitle>附件及工具</pmEntryTitle>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="040" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="3" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00010">无人机系统</techName>
<infoName>装备概况-附件及工具-灭火器</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="12"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="040" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="3" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00011">无人机系统</techName>
<infoName>装备概况-附件及工具-应急灯</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="12"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="040" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="3" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00012">无人机系统</techName>
<infoName>装备概况-附件及工具-土木工具</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="12"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="040" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="3" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00013">无人机系统</techName>
<infoName>装备概况-附件及工具-机械工具</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="12"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
</pmEntry>
</pmEntry>
<pmEntry>
<pmEntryTitle>使用说明</pmEntryTitle>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="042" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="1" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00014">无人机系统</techName>
<infoName>使用说明-无人机出舱</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="13"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="042" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="1" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00015">无人机系统</techName>
<infoName>使用说明-无人机入舱</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="13"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
<pmEntry>
<dmRef>
<dmRefIdent>
<dmCode itemLocationCode="A" infoCodeVariant="A" infoCode="042" disassyCodeVariant="A" disassyCode="00" assyCode="00" subSubSystemCode="1" subSystemCode="1" systemCode="P4" systemDiffCode="A" modelIdentCode="YSC001"/>
<issueInfo issueNumber="000" inWork="01"/>
<language languageIsoCode="zh" countryIsoCode="CN"/>
</dmRefIdent>
<dmRefAddressItems>
<dmTitle>
<techName attributes="[]" id="00016">无人机系统</techName>
<infoName>使用说明-灭火器使用</infoName>
</dmTitle>
<issueDate year="2024" month="06" day="13"/>
</dmRefAddressItems>
</dmRef>
</pmEntry>
</pmEntry>
</content>
</pm>`
this.parseToTreeData(a);
},
//清除新建目录弹窗内容信息。
clearFormData() {
this.form = {
name: '',
version:'000',
date:'',
language:'',
level:'',
contributor:'',
creator:'',
applicabilityInformation:'',
qualityVerification:'',
permission:''
}
},
//测试 格式化目录信息
parseToTreeData(xmlString) {
// 创建DOMParser实例
const parser = new DOMParser();
// 解析XML字符串
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
// 获取根节点下的所有pmEntry节点顶级节点
const rootPmEntries = xmlDoc.querySelectorAll('content > pmEntry');
// 递归解析节点的函数
const parseNode = (xmlNode) => {
// 检查是否有pmEntryTitle子节点
const titleNode = xmlNode.querySelector('pmEntryTitle');
if (titleNode) {
// 有标题节点,说明是父节点
const node = {
id: '', // 后续会从子节点中查找合适的id
name: titleNode.textContent.trim(),
children: [],
attributes: []
};
// 修复选择器错误:使用更兼容的方式获取直接子节点
const childNodes = xmlNode.childNodes;
const childPmEntries = [];
// 遍历所有子节点筛选出直接子pmEntry元素
for (let i = 0; i < childNodes.length; i++) {
const child = childNodes[i];
// 检查是否是元素节点且标签名为pmEntry
if (child.nodeType === 1 && child.tagName === 'pmEntry') {
childPmEntries.push(child);
}
}
// 递归解析每个子节点
childPmEntries.forEach(childEntry => {
const childNode = parseNode(childEntry);
if (childNode) {
node.children.push(childNode);
}
});
// 为父节点生成合适的id
if (!node.id && node.children.length > 0) {
// 取第一个子节点id的前缀并生成父节点id
const firstChildId = node.children[0].id;
// 简单处理取前面的数字部分并减1根据实际情况调整
node.id = (parseInt(firstChildId, 10) - 1).toString().padStart(firstChildId.length, '0');
}
return node;
} else {
// 没有标题节点说明是叶子节点从dmRef中获取信息
const dmRef = xmlNode.querySelector('dmRef');
if (!dmRef) return null;
// 获取techName节点中的id和内容
const techNameNode = dmRef.querySelector('techName');
const id = techNameNode.getAttribute('id');
const attributes = techNameNode.getAttribute('attributes');
// 从infoName获取名称路径
const infoNameNode = dmRef.querySelector('infoName');
const infoName = infoNameNode.textContent.trim();
// 取最后一部分作为名称
const nameParts = infoName.split('-');
const name = nameParts[nameParts.length - 1];
return {
id,
name,
children: [],
attributes: attributes
};
}
};
// 解析所有顶级节点
const treeData = [];
rootPmEntries.forEach(entry => {
const node = parseNode(entry);
if (node) {
treeData.push(node);
}
});
console.log("打印",treeData)
console.log("打印String",JSON.stringify(treeData))
this.treeData = treeData;
},
//和后台交互 加载需要读取的dmc
loadFWBFile(dmName) {
// this.$sendToDotNet('LoadDM','DMC--A-P4-20-00-00A-00001A-A_000_01_zh_cn');
console.log("需要读取的富文本:",dmName)
this.$sendToDotNet('LoadDM','DMC',dmName);
// this.$sendToDotNet('GetFilePath','PMC',data.id,'');
},
//和后台交互 查看历史版本
lookHistoryFile(path) {
let newPath = path+'\\'+this.getLastPartOfPath(path)+'.xml';
this.historyVsPath = path;
console.log("发送路径",newPath)
this.$sendToDotNet('DMPreview','HistoryDMPreview',newPath)
// this.$sendToDotNet('GetHistoryDMContent',newPath);
// localStorage.setItem('filePath',newPath);
// const url = this.$router.resolve({ name: 'WangG2' }).href
// // 打开新窗口,可指定窗口大小和位置
// window.open(
// url,
// '_blank',
// 'width=800,height=600,top=100,left=100'
// )
},
//清除 NBSP str
clearNbsp(originalStr) {
const cleanedStr = originalStr.replace(/&nbsp;/g, '');
console.log("结果cleanedStr",cleanedStr)
return cleanedStr;
},
//测试 测试拼接地址;
ceshi() {
this.lisenPath = '"http://localhost:61980/"';
let e = {
detail:'"\\1.jpg"'
}
const parsedA = this.lisenPath.replace(/"/g, '');
const parsedB = e.detail.replace(/"/g, '');
// 2. 处理转义字符(如斜杠)
const processedA = parsedA.replace(/\\/g, '');
const processedB = parsedB.replace(/\\/g, '');
// 3. 确保基础URL以斜杠结尾
let baseUrl = processedA;
if (baseUrl && !baseUrl.endsWith('/')) {
baseUrl += '/';
}
this.insertImage( baseUrl + processedB)
},
//判断拖拽进富文本的文件类型;
isImageSuffix(a) {
let type = '';
// 检查参数是否为字符串
if (typeof a !== 'string') {
return false;
}
// 转换为小写后判断(忽略大小写)
const lowerCaseA = a.toLowerCase();
if(this.allImgType.has(lowerCaseA)){
type = 'img';
}else if(this.allMusicType.has(lowerCaseA)){
type = 'music';
}else if(this.allVideoType.has(lowerCaseA)){
type = 'video';
}else if(this.otherType.has(lowerCaseA)){
type = '3d'
}else {
type = 'other'
}
return type;
},
//解析字符串信息
parseSpecialString(str) {
return str.replace(/['"\\]/g, '')
// 处理步骤:移除引号 → 替换转义反斜杠 → 解析Unicode
// return str
// .replace(/^"|"$/g, '')
// .replace(/\\\\/g, '\\')
// .replace(/\\\\/g, '\\')
// .replace(/""/g, '')
// .replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
},
//根据ID查询节点 已经需要替换的内容信息
replaceAttributes() {
// 找到treeData中id为xx节点
const targetNode = this.findNodeById(this.treeData,this.currentNode.id);
if (targetNode) {
// 替换attributes
targetNode.attributes = [...this.currentNode.attributes];
// this.hasReplaced = true;
console.log('替换成功新的attributes:', targetNode.attributes);
}
this.catalogueAnalysis(this.treeData);
},
/**
* 查找树形结构中指定id的节点
* @param {Array} nodes - 节点数组
* @param {string} id - 要查找的节点id
* @returns {Object|null} 找到的节点或null
*/
findNodeById(nodes, id) {
// 遍历节点数组
for (const node of nodes) {
// 找到目标节点
if (node.id === id) {
return node;
}
// 递归查找子节点
if (node.children && node.children.length > 0) {
const found = this.findNodeById(node.children, id);
if (found) {
return found;
}
}
}
return null;
},
//成功信息 暂未使用
successTips(msg) {
this.$message({
message: msg,
type: 'success'
});
},
// 处理节点右键点击
// 处理节点右键点击
handleNodeContextMenu(event, data, node, component) {
event.preventDefault();
event.stopPropagation();
console.log("node",node)
console.log("component",component)
this.navIsAdd = '';
this.contextMenuNode = data;
this.currentNode = data;
console.log("右键以后赋值:",this.currentNode,data)
this.getDmName = this.domNameStr[0]+this.domNameStr[1]+this.domNameStr[2]+data.id+this.domNameStr[3]+this.domNameStr[4];
console.log("删除的DM名字",this.getDmName)
// return;
// 获取鼠标位置
let clientX = event.clientX;
let clientY = event.clientY;
// 预估菜单高度(可根据实际菜单内容调整)
const menuHeight = 200; // 包含4个选项+分隔线+关闭按钮的大概高度
const windowHeight = window.innerHeight;
// 计算菜单底部位置
const menuBottom = clientY + menuHeight;
// 如果菜单底部超出窗口高度,向上调整位置
if (menuBottom > windowHeight) {
// 调整后的位置 = 鼠标Y - 菜单高度(确保菜单顶部不超出窗口顶部)
clientY = Math.max(0, clientY - menuHeight);
}
// 设置调整后的菜单位置
this.contextMenuLeft = clientX;
this.contextMenuTop = clientY;
this.contextMenuVisible = true;
// 点击其他区域关闭菜单 - 修复版
const handleClickOutside = (e) => {
// 先判断菜单元素是否存在
const menuElement = document.querySelector('.custom-context-menu');
if (!menuElement) {
// 元素不存在时直接移除监听
document.removeEventListener('click', handleClickOutside);
return;
}
// 元素存在再判断点击位置
if (!menuElement.contains(e.target)) {
this.contextMenuVisible = false;
document.removeEventListener('click', handleClickOutside);
}
};
// 确保移除之前可能存在的监听,避免重复绑定
setTimeout(() => {
document.removeEventListener('click', handleClickOutside);
document.addEventListener('click', handleClickOutside);
}, 0);
}
,
// 处理菜单选项点击
handleMenuClick(option) {
console.log('选择了选项:', option, ',节点数据:', this.contextMenuNode);
// 根据选项执行对应操作
switch(option) {
case 'a':
this.showAddDialog('edit');
// 处理选项a逻辑
break;
case 'b':
this.showBand();
// 处理选项b逻辑
break;
case 'c':
this.finalVersion();
// 处理选项b逻辑
break;
case 'd':
this.showHistory();
// 处理选项c逻辑
break;
case 'e':
// 处理选项d逻辑
this.deleteDialog();
break;
}
// 关闭菜单
this.contextMenuVisible = false;
},
// 复用路径截取函数
getLastPartOfPath(path) {
if (!path || typeof path !== 'string') return '';
const lastBackslashIndex = path.lastIndexOf('\\');
return lastBackslashIndex === -1 ? path : path.slice(lastBackslashIndex + 1);
},
//转译 多个反斜杠
fixJsonEscapes(str) {
// 步骤1先将所有单个\转义为\\(基础转义修复)
let fixed = str.replace(/(?<!\\)\\/g, '\\\\');
// // 步骤2将所有实际换行符\n替换为字符串\\n
// fixed = fixed.replace(/\n/g, '\\n');
// // 步骤3处理可能存在的\r回车符
// fixed = fixed.replace(/\r/g, '\\r');
return fixed;
},
//测试 新开一个浏览器窗口
openWangG2InNewWindow(sendStr) {
let str = "[{\"Path\":\"C:\\Users\\sw\\Desktop\\net8.0-windows7.0\\DM_Material\\Version\\DMC-testNew2-A-P4-20-00-00A-00001A-A\\DMC-testNew2-A-P4-20-00-00A-00001A-A_000_00_zh_cn\",\"Name\":\"DMC-testNew2-A-P4-20-00-00A-00001A-A_000_00_zh_cn\",\"CreationTime\":\"2025-09-01T16:39:07.0578892+08:00\"},{\"Path\":\"C:\\Users\\sw\\Desktop\\net8.0-windows7.0\\DM_Material\\Version\\DMC-testNew2-A-P4-20-00-00A-00001A-A\\DMC-testNew2-A-P4-20-00-00A-00001A-A_001_00_zh_cn\",\"Name\":\"DMC-testNew2-A-P4-20-00-00A-00001A-A_001_00_zh_cn\",\"CreationTime\":\"2025-09-01T16:41:46.4181541+08:00\"},{\"Path\":\"C:\\Users\\sw\\Desktop\\net8.0-windows7.0\\DM_Material\\Version\\DMC-testNew2-A-P4-20-00-00A-00001A-A\\DMC-testNew2-A-P4-20-00-00A-00001A-A_002_00_zh_cn\",\"Name\":\"DMC-testNew2-A-P4-20-00-00A-00001A-A_002_00_zh_cn\",\"CreationTime\":\"2025-09-01T16:42:06.7153697+08:00\"},{\"Path\":\"C:\\Users\\sw\\Desktop\\net8.0-windows7.0\\DM_Material\\Version\\DMC-testNew2-A-P4-20-00-00A-00001A-A\\DMC-testNew2-A-P4-20-00-00A-00001A-A_003_00_zh_cn\",\"Name\":\"DMC-testNew2-A-P4-20-00-00A-00001A-A_003_00_zh_cn\",\"CreationTime\":\"2025-09-01T16:42:25.8314261+08:00\"},{\"Path\":\"C:\\Users\\sw\\Desktop\\net8.0-windows7.0\\DM_Material\\Version\\DMC-testNew2-A-P4-20-00-00A-00001A-A\\DMC-testNew2-A-P4-20-00-00A-00001A-A_004_00_zh_cn\",\"Name\":\"DMC-testNew2-A-P4-20-00-00A-00001A-A_004_00_zh_cn\",\"CreationTime\":\"2025-09-01T16:51:02.3778949+08:00\"},{\"Path\":\"C:\\Users\\sw\\Desktop\\net8.0-windows7.0\\DM_Material\\Version\\DMC-testNew2-A-P4-20-00-00A-00001A-A\\DMC-testNew2-A-P4-20-00-00A-00001A-A_005_00_zh_cn\",\"Name\":\"DMC-testNew2-A-P4-20-00-00A-00001A-A_005_00_zh_cn\",\"CreationTime\":\"2025-09-02T15:59:19.5476416+08:00\"}]"
// let strb = JSON.parse(str);
console.log("解析的结果",str)
// // 获取wangG2页面的路由路径
const wangG2Url = this.$router.resolve({ name: 'WangG2' }).href
// // 打开新窗口,可指定窗口大小和位置
// window.open(
// url,
// '_blank',
// 'width=800,height=600,top=100,left=100'
// )
// 打开新窗口假设wangG2.vue的路由路径是/wangG2
// const wangG2Url = this.$router.resolve({ path: '/wangG2' }).href;
// 打开新窗口并设置尺寸
this.wangG2Window = window.open(
wangG2Url,
'WangG2Window',
'width=800,height=600,left=200,top=100'
);
// 检查窗口是否被浏览器拦截
if (!this.wangG2Window) {
this.$message.error('窗口被浏览器拦截,请允许弹出窗口');
return;
}
// 定时检查窗口是否加载完成并发送数据
this.checkWindowTimer = setInterval(() => {
// 窗口已关闭
if (this.wangG2Window.closed) {
clearInterval(this.checkWindowTimer);
this.$message.info('wangG2窗口已关闭');
return;
}
try {
// 尝试发送消息,确保窗口已加载
this.wangG2Window.postMessage(
{
type: 'INIT_DATA',
// payload: this.dataToSend,
payload: sendStr,
sender: 'MainPage'
},
window.location.origin // 限制只发送到同源地址
);
// 发送成功后清除定时器
clearInterval(this.checkWindowTimer);
this.$message.success('数据已发送到wangG2窗口');
} catch (error) {
// 窗口未加载完成时会抛出错误,继续等待
console.log('等待wangG2窗口加载...');
}
}, 100);
},
// 将处理逻辑提取为单独的方法,便于绑定和解绑
handleFilePathSend(e) {
const newStr = this.parseSpecialString(e.detail);
console.log('路径接收:', this.currentNode, e, e.detail, newStr);
// 访问 currentNode 前先判断是否存在,避免 null 错误
if (this.currentNode && this.currentNode.attributes) {
this.currentNode.attributes.push(newStr);
this.replaceAttributes();
console.log("当前节点", this.currentNode, this.treeData);
} else {
console.warn('currentNode 不存在或无 attributes 属性');
}
},
},
watch: {
// 监听新建目录的弹窗状态
dialogVisibleNav(newVal, oldVal) {
console.log("弹窗",newVal,oldVal)
if(newVal == true){
this.loadFileDMC = '';
}
}
},
created() {
// let that = this;
// 方式1直接访问全局变量
this.$watch(
() => this.$dotNetMessage,
(newMsg) => {
console.log('收到消息:', newMsg)
}
)
//和后台交互 获取对应的DMC的历史版本列表
window.addEventListener('GetHistoryVesions', (e) => {
console.log('接收历史版本信息:', e.detail)
if(e.detail){
const aa = this.fixJsonEscapes(e.detail);
console.log("1第一次接收历史版本信息this.historyData", e.detail,aa)
this.historyData = JSON.parse(aa);
console.log("1第二次接收历史版本信息this.historyData", this.historyData)
}
})
//和后台交互 暂未使用
window.addEventListener('GetHistoryDMContent', (e) => {
console.log('GetHistoryDMContent接收历史版本信息XML:', e.detail)
})
//和后台交互 新建章节目录时候点击加载DMC文件
window.addEventListener('GetExistenceDMContent', (e) => {
// console.log('接收历史版本DMC的XML内容:',e, e.detail)
if(e.detail){
this.isLoadOldDmc = '2';
this.showView = true;
this.loadFileXML = e.detail;
if (this.loadFileXML.startsWith('"')) {
this.loadFileXML = this.loadFileXML.substring(1);
}
// 检查并去除结尾的双引号
if (this.loadFileXML.endsWith('"')) {
this.loadFileXML = this.loadFileXML.substring(0, this.loadFileXML.length - 1);
}
this.parseXmlDmc();
}
})
//和后台交互 接收目录XML
window.addEventListener('FrontLoadProjectNew', (e) => {
// 加载目录
console.log('FrontLoadProjectNew 参数接收:')
this.isLoadXml = true;
this.currentNode = '';
//获取目录字段拼接,暂不用
// this.extractDmCode(e.detail)
// 去除首尾的双引号
let processed = e.detail.trim();
// 检查并去除开头的双引号
if (processed.startsWith('"')) {
processed = processed.substring(1);
}
// 检查并去除结尾的双引号
if (processed.endsWith('"')) {
processed = processed.substring(0, processed.length - 1);
}
// this.outputXml = processed;
this.generateTreeData(processed);
// this.parseXml(processed);
// this.parseToTreeData(processed)
})
//和后台交互 单独获取DM的文文件(暂未使用)
window.addEventListener('GetDMDta', (e) => {
// 加载目录
console.log('GetDMDta 参数接收:',e,e.detail)
})
//和后台交互 接收富文本XML
window.addEventListener('FrontLoadDM', this.FrontLoadDM_g1)
//和后台交互 获取文件地址
window.addEventListener('SendResourcePath', (e) => {
console.log('拖拽文件地址:',e,e.detail)
// 1. 去除双引号
const parsedA = this.lisenPath.replace(/"/g, '');
const parsedB = e.detail.replace(/"/g, '');
// 2. 处理转义字符(如斜杠)
const processedA = parsedA.replace(/\\/g, '');
const processedB = parsedB.replace(/\\/g, '');
// 3. 确保基础URL以斜杠结尾
let baseUrl = processedA;
if (baseUrl && !baseUrl.endsWith('/')) {
baseUrl += '/';
}
const cleanedFile = parsedB.replace(/\\/g, '');
// 2. 提取后缀名
const lastDotIndex = cleanedFile.lastIndexOf('.');
const suffix = cleanedFile.substring(lastDotIndex + 1).toLowerCase();
// allImgType allMusicType allVideoType
console.log("后缀",suffix)
switch (this.isImageSuffix(suffix)){
case 'img':
this.insertImage( baseUrl + processedB)
// this.insertImage( baseUrl + '/zw.jpg')
break;
case 'music':
this.insertAudio( baseUrl + processedB)
break;
case 'video':
this.insertVideo( baseUrl + processedB)
break;
case '3d':
this.insert3d(baseUrl + '/zw.jpg')
break;
default:
this.$message({
message: '当前文件不支持',
type: 'warning'
});
break;
}
})
console.log('监听器开始注册:', Date.now()) // 打印注册时间戳
//和后台交互 获取文件地址
window.addEventListener('SendLisentPath', this.SendLisentPathFun)
//和后台交互 获取项目名
window.addEventListener('SendProjectName', (e) => {
this.domNameStr[1] = this.removeQuotationMarks(e.detail);
console.log('获取项目文件夹名子:',this.domNameStr,e,e.detail)
})
//和后台交互 点击请求需要获取哪个DMC的历史版本列表
window.addEventListener('GetHistoryVesions', (e) => {
console.log('查询历史版本内容',e.detail)
})
//和后台交互 定稿之后获取最新的dm名称
window.addEventListener('SendNewVersionDMFileName', (e) => {
console.log("定稿以后的信息",e.detail)
let str = this.removeQuotationMarks(e.detail);
console.log("定稿以后的 str",str)
const firstUnderline = str.indexOf('_');
console.log("定稿以后的 firstUnderline",firstUnderline)
const secondUnderline = str.indexOf('_', firstUnderline + 1);
console.log("节点属性",this.currentNode)
console.log("定稿以后的secondUnderline",secondUnderline)
if(this.currentNode){
this.currentNode.lagreVersion =str.substring(
firstUnderline + 1,
secondUnderline
);
this.updateNodeVersion(this.currentNode.id,this.currentNode.lagreVersion)
console.log("定稿以后的currentNode",this.currentNode)
console.log("定稿以后的树",this.treeData)
this.catalogueAnalysis(this.treeData);
}
})
//和后台交互 获取加载时候的DMC文件预览服务器端口号
window.addEventListener('handPrePath', (e) => {
console.log('handPrePath111111111',e.detail)
if(e){
this.viewLis = e.detail;
}
})
//和后台交互 获取加载时候的DMC文件具体内容
window.addEventListener('handreContent', (e) => {
console.log('打印handreContent111111111111',e.detail)
if(e){
this.maskViewShow = true;
this.viewXml = e.detail;
if (this.viewLis.startsWith('"')) {
this.viewXml = this.viewXml.substring(1);
}
// 检查并去除结尾的双引号
if (this.viewXml.endsWith('"')) {
this.viewXml = this.viewXml.substring(0, this.viewXml.length - 1);
}
this.parseXmlDmc_loadFile();
}
})
// window.addEventListener('handleMessageFromDotNet', this.fun1)
},
}
</script>
<style>
.editor-container {
width: 100%;
margin: 0 auto;
box-sizing: border-box;
display: flex;
}
.leftDiv {
width: 350px; /* 左边宽度 30% */
/* background: lightblue; */
padding-top:25px;
}
.rightDiv {
width: calc( 100% - 350px); /* 右边宽度 70% */
/* background: lightcoral; */
height:600px;
overflow: hidden auto;
padding:20px;
padding-top:35px;
}
.toolbar {
margin-bottom: 10px;
}
.toolbar button {
margin-right: 10px;
padding: 5px 10px;
background: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.toolbar button:hover {
background: #66b1ff;
}
.editor2Opacity{
opacity:.5;
}
#editor,#editor2 {
/* border: 1px solid #ddd; */
min-height: 300px;
padding: 10px;
padding-top:0;
text-align: left;
}
#nav{
text-align: left;
}
.content-preview {
margin-top: 20px;
border: 1px solid #eee;
padding: 15px;
background: #fafafa;
text-align: left;
}
#main th,
#main td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
#main th {
background-color: #f2f2f2;
}
#main{
height:70%;
overflow: hidden;
overflow-y: auto;
}
/* 视频容器样式 */
.video-wrapper {
position: relative;
margin: 15px 0;
/* border: 1px solid #ddd; */
border-radius: 4px;
overflow: hidden;
}
.video-controls {
position: absolute;
top: 5px;
right: 5px;
z-index: 10;
}
.video-delete {
display: inline-block;
padding: 2px 8px;
background: rgba(255, 0, 0, 0.7);
color: white;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
user-select: none;
}
.video-delete:hover {
background: rgba(255, 0, 0, 0.9);
}
/* 确保视频响应式 */
video {
max-width: 100%;
display: block;
background: #000;
}
/* 禁用菜单项样式 */
.disabled-menu-item {
opacity: 0.5 !important;
cursor: not-allowed !important;
pointer-events: none !important;
}
/* 如果需要工具提示也禁用 */
.w-e-toolbar .w-e-menu:nth-child(1),
.w-e-toolbar .w-e-menu:nth-child(2),
.w-e-toolbar .w-e-menu:nth-child(18),
.w-e-toolbar .w-e-menu:nth-child(19),
.w-e-toolbar .w-e-menu:last-child {
/* display: none !important; */
}
.s1000d-editor-container {
display: flex;
flex-direction: column;
/*height: 100vh;*/
/* padding: 20px; */
box-sizing: border-box;
}
.toolbar {
margin-bottom: 15px;
}
.tool-btn {
padding: 8px 15px;
margin-right: 10px;
background-color: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.tool-btn:hover {
background-color: #66b1ff;
}
.editor-area {
display: flex;
flex: 1;
gap: 20px;
}
.editor {
flex: 1;
border: 1px solid #dcdfe6;
border-radius: 4px;
overflow: hidden;
}
.xml-preview {
flex: 1;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 10px;
overflow: auto;
background-color: #f5f7fa;
}
.xml-preview pre {
white-space: pre-wrap;
font-family: Consolas, Monaco, monospace;
margin: 0;
}
.modal {
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 5px;
width: 60%;
max-height: 80%;
overflow: auto;
}
.close {
float: right;
font-size: 24px;
cursor: pointer;
}
.valid {
color: #67c23a;
}
.invalid {
color: #f56c6c;
}
/deep/ .editor .w-e-text{
text-align: left;
}
#nav{
overflow: hidden;
height: 300px;
overflow-y: auto;
}
.context-menu {
position: fixed;
background: white;
border: 1px solid #ddd;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 1000;
padding: 5px 0;
min-width: 100px;
}
.menu-item {
padding: 8px 15px;
cursor: pointer;
}
.menu-item:hover {
background-color: #f0f0f0;
}
.menu-item.active {
background-color: #e6f7ff;
color: #1890ff;
font-weight: bold;
}
button {
margin: 10px 10px 0 0;
padding: 8px 15px;
background: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #40a9ff;
}
</style>
<style scoped>
/* 修改后的样式 */
.image-preview-dialog {
display: flex;
flex-direction: column;
background: rgba(0,0,0,0.9);
}
.image-container {
width: 100%;
overflow: auto;
display: flex;
align-items: center;
justify-content: center;
/* 默认高度会被JS覆盖 */
height: 80vh;
/* 添加边界保护 */
min-height: 500px;
max-height: calc(100vh - 150px);
}
.control-bar {
height: 80px; /* 固定高度 */
flex-shrink: 0;
background: rgba(0,0,0,0.7);
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
}
/* 大屏适配 */
@media (min-height: 800px) {
.image-container {
height: 85vh;
}
}
.control-bar {
padding: 15px;
background: rgba(0,0,0,0.8);
border-radius: 10px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.3);
display: flex;
gap: 10px;
justify-content: center;
}
/* 操作按钮样式 */
.action-btn {
width: 100px;
height: 50px;
font-size: 16px;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.action-btn i {
font-size: 20px;
margin-bottom: 5px;
}
.action-btn span {
font-weight: bold;
}
/* 按钮颜色定制 */
.el-button--primary {
background: #409EFF;
border-color: #409EFF;
}
.el-button--primary:hover {
background: #66b1ff;
border-color: #66b1ff;
}
/* 旋转按钮特殊样式 */
.rotate-btn {
background: #67C23A;
border-color: #67C23A;
}
.rotate-btn:hover {
background: #85ce61;
border-color: #85ce61;
}
.floating-controls {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 20px;
z-index: 999;
2025-07-25 13:28:47 +08:00
}
2025-09-15 09:58:52 +08:00
/* 悬浮按钮 */
.floating-btn {
width: 60px;
height: 60px;
border-radius: 50%;
border: none;
background: white;
color: #4a6cf7;
display: flex;
align-items: center;
justify-content: center;
2025-07-25 13:28:47 +08:00
cursor: pointer;
2025-09-15 09:58:52 +08:00
position: relative;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.floating-btn .icon-container {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.floating-btn .tooltip {
position: absolute;
top: -40px;
background: #4a6cf7;
color: white;
padding: 5px 10px;
border-radius: 6px;
2025-07-25 13:28:47 +08:00
font-size: 12px;
2025-09-15 09:58:52 +08:00
opacity: 0;
pointer-events: none;
transition: all 0.3s;
2025-07-25 13:28:47 +08:00
}
2025-09-15 09:58:52 +08:00
/* 按钮颜色 */
.zoom-in { background: #4a6cf7; color: white; }
.zoom-out { background: #f68084; color: white; }
.rotate-btn { background: #5ee7df; color: white; }
.reset-btn { background: #f093fb; color: white; }
.close-btn { background: #ff7676; color: white; }
/* 悬停动画 */
.floating-btn:hover {
transform: translateY(-5px) scale(1.05);
box-shadow: 0 8px 25px rgba(0,0,0,0.2);
2025-07-25 13:28:47 +08:00
}
2025-09-15 09:58:52 +08:00
.floating-btn:hover .tooltip {
opacity: 1;
transform: translateY(-5px);
2025-07-25 13:28:47 +08:00
}
2025-09-15 09:58:52 +08:00
.floating-btn:hover .icon-container {
transform: rotate(15deg) scale(1.1);
2025-07-25 13:28:47 +08:00
}
2025-09-15 09:58:52 +08:00
/* 点击效果 */
.floating-btn:active {
transform: scale(0.95);
2025-07-25 13:28:47 +08:00
}
2025-09-15 09:58:52 +08:00
.el-dialog__wrapper{
z-index:10010 !important;
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
.highlight {
color: #ff4d4f;
font-weight: bold;
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
.operation-buttons {
text-align: left;
}
/*
.tree-manager {
padding: 20px;
}
.search-input {
width: 300px;
2025-07-28 17:01:13 +08:00
margin-right: 10px;
2025-09-15 09:58:52 +08:00
}
.tree-container {
border: 1px solid #ebeef5;
2025-07-28 17:01:13 +08:00
border-radius: 4px;
2025-09-15 09:58:52 +08:00
padding: 10px;
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
.el-tree {
min-height: 200px;
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
.highlight {
color: #ff4d4f;
font-weight: bold;
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
.custom-tree-node {
2025-07-28 17:01:13 +08:00
flex: 1;
2025-09-15 09:58:52 +08:00
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
} */
.tree-container {
overflow-x: auto;
max-width: 280px;
margin: 30px auto;
margin-top:10px;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
position: relative;
}
.el-tree-node__children{
overflow:auto;
}
.el-tree-node__content {
max-width: 280px;
height: 30px;
overflow: auto;
}
.el-tree-node__content span div{
/* overflow: auto; */
/* width:150px; */
text-align: left;
}
/* .bindMain{
text-align: left;
2025-07-28 17:01:13 +08:00
overflow: hidden;
2025-09-15 09:58:52 +08:00
overflow-y: auto;
}
.bindMain > div{
margin-top:10px;
} */
.zyBox{
text-align: left;
border: 1px dashed deepskyblue;
padding: 10px;
padding-top: 0;
border-radius: 4px;
margin-bottom: 20px;
}
.mask{
position: absolute;
top:0;
left: 0;
width:100%;
height:100%;
background-color:#9cf;
opacity:0.1;
}
.maskView{
position: fixed;
top:0;
left: 0;
z-index:10010;
background-color:rgba(0,0,0,.5);
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
</style>
2025-07-28 17:01:13 +08:00
2025-09-15 09:58:52 +08:00
<style scoped>
/* 设置树形组件节点的定位和左内边距 */
.tree-container /deep/ .el-tree-node {
position: relative;
padding-left: 13px;
}
/* 设置树形组件节点的 before 伪类的样式*/
.tree-container /deep/ .el-tree-node:before {
width: 1px;
height: 100%;
content: '';
position: absolute;
top: -38px;
bottom: 0;
left: 0;
right: auto;
border-width: 1px;
border-left: 1px solid #b8b9bb;
}
/* // 设置树形组件节点的 after 伪类的样式 */
.tree-container /deep/ .el-tree-node:after {
width: 13px;
height: 13px;
content: '';
position: absolute;
left: 0;
right: auto;
top: 12px;
bottom: auto;
border-width: 1px;
border-top: 1px solid #b8b9bb;
}
/* // 设置树形组件首节点的左边框不显示 */
.tree-container /deep/ .el-tree > .el-tree-node:before {
border-left: none;
}
/* // 设置树形组件首节点的顶部边框不显示 */
.tree-container /deep/ .el-tree > .el-tree-node:after {
border-top: none;
}
/* // 设置树形组件末节点的 before 伪类的高度 */
.tree-container /deep/ .el-tree .el-tree-node:last-child:before {
height: 50px;
}
/* // 设置树形组件节点字体大小、以及取消左内边距 */
.tree-container /deep/ .el-tree .el-tree-node__content {
/* overflow: auto; */
color: #000;
font-size: 14px;
padding-left: 0 !important;
height: 20px;
}
/* // 设置树形组件孩子节点左内边距 */
.tree-container /deep/ .el-tree .el-tree-node__children {
padding-left: 11.5px;
}
/* // 设置树形组件复选框左右外边距 */
.tree-container /deep/ .el-tree .el-tree-node__content > label.el-checkbox {
margin: 0 5px 0 5px !important;
}
/* // 设置树形组件展开图标定位、图层、内边距 */
.tree-container /deep/ .el-tree .el-tree-node__expand-icon {
position: relative;
z-index: 99;
}
/* // 设置树形组件叶子节点的默认图标不显示 */
.tree-container /deep/ .el-tree .el-tree-node__expand-icon.is-leaf {
display: none;
}
/* // 设置树形组件叶子节点的横线 */
.tree-container /deep/ .el-tree .leaf-node-line {
width: 23px;
height: 13px;
content: '';
position: absolute;
left: 13px;
right: auto;
top: 12px;
bottom: auto;
border-width: 1px;
border-top: 1px solid #b8b9bb;
}
/* // 设置树形组件有叶子节点的左外边距 */
.tree-container /deep/ .el-tree .el-tree-node__content:has(.is-leaf){
color: #096;
margin-left: 24px !important;
height:36px;
/* overflow:auto; */
}
.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content {
background-color: #ccffdd;
}
/deep/ .el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content{
background-color: #99ccff6b;
}
.w-e-tooltip-up .w-e-tooltip-item-wrapper:nth-child(1){
display:none;
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
/* 针对 el-tree 容器的滚动条优化 */
.elTree {
/* 设置最大高度以触发滚动 */
max-height: calc(100vh - 200px);
overflow-y: auto;
padding-right: 5px; /* 预留滚动条空间 */
padding-bottom: 5px; /* 预留横向滚动条空间 */
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
/* 滚动条轨道 - 区分横向和纵向 */
.elTree::-webkit-scrollbar {
width: 6px; /* 纵向滚动条宽度 */
height: 4px; /* 横向滚动条高度(变细) */
}
/* 滚动条轨道背景 */
.elTree::-webkit-scrollbar-track {
background: rgba(241, 241, 241, 0.5); /* 轨道颜色更透明 */
border-radius: 3px;
}
/* 滚动条滑块 - 区分横向和纵向 */
.elTree::-webkit-scrollbar-thumb {
background: rgba(193, 193, 193, 0.6); /* 滑块颜色更透明 */
border-radius: 3px;
transition: background 0.3s ease;
}
/* 滚动条滑块悬停状态 */
.elTree::-webkit-scrollbar-thumb:hover {
background: rgba(168, 168, 168, 0.7); /* 悬停时透明度降低 */
}
/* 滚动条滑块激活状态 */
.elTree::-webkit-scrollbar-thumb:active {
background: rgba(136, 136, 136, 0.8); /* 激活时更不透明 */
}
/* 隐藏角落的交汇处 */
.elTree::-webkit-scrollbar-corner {
background: transparent;
}
/* Firefox 滚动条样式适配 */
.elTree {
scrollbar-width: thin;
scrollbar-color: rgba(193, 193, 193, 0.6) rgba(241, 241, 241, 0.5);
}
</style>
<style scoped>
.custom-context-menu {
2025-07-28 17:01:13 +08:00
position: fixed;
2025-09-15 09:58:52 +08:00
width: 120px;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
2025-07-28 17:01:13 +08:00
z-index: 1000;
2025-09-15 09:58:52 +08:00
padding: 5px 0;
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
.menu-item {
padding: 6px 15px;
cursor: pointer;
font-size: 14px;
}
/deep/ .el-dialog{
border-radius: 10px;
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
.loadContent1{
/* height:50px; */
background-color: #fff;
width: 90%;
/* height: 90%; */
/* position: absolute;
top: 5%;
left: 5%;
padding: 20px;
overflow: auto;
box-sizing: border-box; */
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
/deep/ .loadContent table {
border-collapse: collapse; /* 合并边框 */
width: 100%;
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
/deep/ .loadContent table,/deep/ .loadContent td {
border: 1px solid #ddd; /* 表格、表头、单元格都添加边框 */
2025-07-28 17:01:13 +08:00
}
2025-09-15 09:58:52 +08:00
/deep/ .loadContent th{
background-color: #f1f1f1;
border: 1px solid #ddd; /* 表格、表头、单元格都添加边框 */
}
/deep/ .loadContent th,/deep/ .loadContent td {
padding: 8px 12px; /* 添点内边距,让内容不贴边 */
2025-07-28 17:01:13 +08:00
text-align: left;
}
2025-09-15 09:58:52 +08:00
.changeState{
color: #d25d1f;
margin-left: -24px;
font-size: 8px !important;
}
/* 树形组件样式 */
.tree-container .el-tree {
white-space: nowrap;
min-width: 100%;
position: static; /* 避免定位冲突 */
}
/* 解决节点展开后被遮挡的问题 */
.tree-container .el-tree-node {
overflow: visible !important; /* 允许子节点溢出显示 */
position: relative; /* 确保层级正确 */
}
/* 修复连接线和内容容器 */
.tree-container .el-tree-node__children {
overflow: visible !important;
position: relative;
}
/* 确保节点内容不被裁剪 */
.tree-container .el-tree-node__content {
overflow: visible !important;
}
/* 在 wang.vue 的样式部分添加 */
.elTree {
/* 设置固定高度,超出部分显示滚动条 */
max-height: calc(100vh - 200px); /* 根据页面布局调整具体数值 */
overflow-y: auto; /* 纵向溢出时显示滚动条 */
overflow-x: auto; /* 横向溢出时显示滚动条(防止节点名称过长) */
padding-right: 8px; /* 预留滚动条空间,避免内容被遮挡 */
}
/* 优化滚动条样式(可选) */
.elTree::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.elTree::-webkit-scrollbar-thumb {
background-color: #ccc;
border-radius: 3px;
}
.elTree::-webkit-scrollbar-track {
background-color: #f5f5f5;
}
/* 针对树形组件容器添加横向滚动支持 */
.tree-container {
/* 限制容器最大宽度(可根据实际布局调整) */
max-width: 100%;
overflow-x: auto; /* 横向溢出时显示滚动条 */
padding-bottom: 8px; /* 预留底部空间,避免滚动条遮挡内容 */
}
/* 确保树形组件本身不限制宽度,让内容能撑开容器 */
.elTree {
min-width: 100%; /* 至少占满容器宽度 */
display: inline-block; /* 让树组件宽度由内容决定 */
white-space: nowrap; /* 防止节点内容自动换行 */
}
/* 优化节点文本显示,避免过长文本被截断 */
.custom-tree-node span,
.el-tree span {
max-width: none; /* 取消最大宽度限制 */
overflow: visible; /* 允许内容溢出显示 */
text-overflow: clip; /* 取消省略号,完整显示文本 */
}
</style>
<style scoped>
/* 限制弹窗内容区域高度并添加滚动条 */
/deep/ .custom-dialog .el-dialog__body {
max-height: 55vh; /* 固定最大高度,可根据需求调整 */
overflow-y: auto; /* 内容超出时显示纵向滚动条 */
padding: 15px; /* 调整内边距,避免内容紧贴边框 */
}
/deep/ .viewDialog .el-dialog__body{
max-height: 65vh; /* 固定最大高度,可根据需求调整 */
height: 65vh; /* 固定最大高度,可根据需求调整 */
}
/* 可选:美化滚动条样式 */
/deep/ .custom-dialog .el-dialog__body::-webkit-scrollbar {
width: 6px; /* 滚动条宽度 */
}
/deep/ .custom-dialog .el-dialog__body::-webkit-scrollbar-thumb {
background: #ccc; /* 滚动条滑块颜色 */
border-radius: 3px; /* 滑块圆角 */
}
/deep/ .custom-dialog .el-dialog__body::-webkit-scrollbar-track {
background: #f5f5f5; /* 滚动条轨道颜色 */
}
</style>
<style scoped>
/deep/ .fine-stripe-border1 {
margin: 50px auto;
padding: 20px;
border: 12px solid;
border-image: linear-gradient(45deg,
#ffcc00 0%, #ffcc00 10%,
#000000 10%, #000000 20%,
#ffcc00 20%, #ffcc00 30%,
#000000 30%, #000000 40%,
#ffcc00 40%, #ffcc00 50%,
#000000 50%, #000000 60%,
#ffcc00 60%, #ffcc00 70%,
#000000 70%, #000000 80%,
#ffcc00 80%, #ffcc00 90%,
#000000 90%, #000000 100%
) 1;
/* 内容居中 */
/* display: flex;
align-items: center;
justify-content: center;
font-family: Arial, sans-serif;
font-size: 18px; */
}
/deep/ .fine-stripe-border2 {
margin: 50px auto;
padding: 20px;
border: 12px solid;
border-image: linear-gradient(45deg,
#ff0000 0%, #ff0000 10%,
#000000 10%, #000000 20%,
#ff0000 20%, #ff0000 30%,
#000000 30%, #000000 40%,
#ff0000 40%, #ff0000 50%,
#000000 50%, #000000 60%,
#ff0000 60%, #ff0000 70%,
#000000 70%, #000000 80%,
#ff0000 80%, #ff0000 90%,
#000000 90%, #000000 100%
) 1;
/* 内容居中 */
/* display: flex;
align-items: center;
justify-content: center;
font-family: Arial, sans-serif;
font-size: 18px; */
}
.iconImg{
height: 20px;
cursor: pointer;
margin-right: 20px;
margin-top: 10px;
}
2025-07-25 13:28:47 +08:00
</style>