阅读器版本
5
keybindings.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"key": "ctrl+alt+v", // 避免与Vue快捷键冲突
|
||||||
|
"command": "thief-book.toggle",
|
||||||
|
"when": "editorTextFocus"
|
||||||
|
}
|
||||||
5522
public/schemas/brex.xsd
Normal file
32
src/App.vue
@@ -1,31 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app" ref="app" :style="{ height: windowHeightApp + 'px' }">
|
||||||
<!-- <img alt="Vue logo" src="./assets/logo.png"> -->
|
<!-- <img alt="Vue logo" src="./assets/logo.png"> -->
|
||||||
<!-- <HelloWorld2 msg="Welcome to Your Vue.js App"/> -->
|
<!-- <HelloWorld2 msg="Welcome to Your Vue.js App"/> -->
|
||||||
<HelloWorld style="display:none" msg="Welcome to Your Vue.js App"/>
|
<!-- <HelloWorld style="display:none" msg="Welcome to Your Vue.js App"/> -->
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// import HelloWorld from './components/vditor2Bd.vue'
|
// import HelloWorld from './components/wang.vue'
|
||||||
// import HelloWorld from './components/homePage3.vue'
|
|
||||||
// import HelloWorld from './components/quill.vue'
|
|
||||||
// import HelloWorld from './components/tinyMCE.vue'
|
|
||||||
// import HelloWorld from './components/vditor.vue'
|
|
||||||
import HelloWorld from './components/wang.vue'
|
|
||||||
// import HelloWorld2 from './components/wangTest.vue'
|
|
||||||
// import HelloWorld from './components/wangG.vue'
|
|
||||||
// import HelloWorld2 from './components/wangITEM.vue'
|
|
||||||
// import HelloWorld from './components/XMLwangS1000D.vue'
|
|
||||||
// import HelloWorld from './components/XMLwang.vue'
|
|
||||||
// import HelloWorld from './components/vditor2.vue'
|
|
||||||
// import HelloWorld from './components/ueditor.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
HelloWorld,
|
// HelloWorld,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
screenHeightApp: 0, // 屏幕总高度
|
||||||
|
windowHeightApp: 0 // 可视窗口高度
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.screenHeightApp = window.screen.height;
|
||||||
|
// 可视窗口高度(浏览器窗口高度)
|
||||||
|
this.windowHeightApp = window.innerHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -39,4 +38,5 @@ export default {
|
|||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
/* margin-top: 60px; */
|
/* margin-top: 60px; */
|
||||||
}
|
}
|
||||||
|
body{margin:0 !important;}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
5522
src/assets/brex.xsd
Normal file
1142
src/assets/css/style.css
Normal file
BIN
src/assets/img/-Video-File.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
src/assets/img/Music.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
src/assets/img/addIcon.png
Normal file
|
After Width: | Height: | Size: 359 B |
BIN
src/assets/img/bangdingziyuanchi.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
src/assets/img/bianji.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
src/assets/img/bianji1.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/img/br.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
src/assets/img/br2.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
src/assets/img/danwei.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
src/assets/img/delIcon.png
Normal file
|
After Width: | Height: | Size: 823 B |
BIN
src/assets/img/dinggao.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/assets/img/duoyuyan.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
src/assets/img/editIcon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/img/fabushijian.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
src/assets/img/fenlei.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/assets/img/file.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
src/assets/img/gaojisousuo.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
src/assets/img/hezuodanwei.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
src/assets/img/history.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
src/assets/img/jgIcon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/img/jiazaiwenjian.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
src/assets/img/jinggao.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
src/assets/img/jingshi.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/img/jsIcon.png
Normal file
|
After Width: | Height: | Size: 952 B |
BIN
src/assets/img/jurassic_version.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
src/assets/img/leixing.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
src/assets/img/miji.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/img/mp.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
src/assets/img/mp1.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
src/assets/img/quanxian.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
src/assets/img/save-3-fill.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
src/assets/img/saveIcon.png
Normal file
|
After Width: | Height: | Size: 403 B |
BIN
src/assets/img/shanchu-copy.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
src/assets/img/shanchu.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/img/shanchu1.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
src/assets/img/shanchu2.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
src/assets/img/unknown-file.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/assets/img/video.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/assets/img/wenjian.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/img/xinjian.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/assets/img/xinjian1.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/assets/img/xinxi.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
src/assets/img/xml.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
src/assets/img/yongtu.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
src/assets/img/yulan.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
src/assets/img/zhuti.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
@@ -1,95 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div ref="editor" style="text-align:left"></div>
|
|
||||||
<button @click="exportToXml">导出为XML</button>
|
|
||||||
<input type="file" accept=".xml" @change="handleXmlUpload">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import E from 'wangeditor';
|
|
||||||
import { js2xml, xml2js } from 'xml-js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
editor: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.editor = new E(this.$refs.editor);
|
|
||||||
this.editor.create();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
convertHtmlToXml(htmlContent) {
|
|
||||||
const xmlObject = {
|
|
||||||
declaration: {
|
|
||||||
attributes: {
|
|
||||||
version: '1.0',
|
|
||||||
encoding: 'UTF-8'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
elements: [{
|
|
||||||
type: 'element',
|
|
||||||
name: 'richText',
|
|
||||||
elements: [{
|
|
||||||
type: 'text',
|
|
||||||
text: htmlContent
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
return js2xml(xmlObject, { compact: false, spaces: 4 });
|
|
||||||
},
|
|
||||||
|
|
||||||
downloadXml(content, fileName = 'content.xml') {
|
|
||||||
const blob = new Blob([content], { type: 'application/xml' });
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(blob);
|
|
||||||
link.download = fileName;
|
|
||||||
link.click();
|
|
||||||
URL.revokeObjectURL(link.href);
|
|
||||||
},
|
|
||||||
|
|
||||||
exportToXml() {
|
|
||||||
const html = this.editor.txt.html();
|
|
||||||
const xml = this.convertHtmlToXml(html);
|
|
||||||
this.downloadXml(xml);
|
|
||||||
},
|
|
||||||
|
|
||||||
parseXmlToHtml(xmlString) {
|
|
||||||
try {
|
|
||||||
const result = xml2js(xmlString, { compact: false });
|
|
||||||
const richTextElement = result.elements.find(el => el.name === 'richText');
|
|
||||||
if (richTextElement && richTextElement.elements && richTextElement.elements.length > 0) {
|
|
||||||
return richTextElement.elements[0].text;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
} catch (e) {
|
|
||||||
console.error('XML解析错误:', e);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleXmlUpload(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.parseXmlToHtml(xmlString);
|
|
||||||
if (html) {
|
|
||||||
this.editor.txt.html(html);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
reader.readAsText(file);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
if (this.editor) {
|
|
||||||
this.editor.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,425 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="editor-container">
|
|
||||||
<div class="toolbar">
|
|
||||||
<button @click="insertImage">上传图片</button>
|
|
||||||
<button @click="insertVideo">上传视频</button>
|
|
||||||
<button @click="loadAllContent">加载所有内容</button>
|
|
||||||
<button @click="generateTOC">生成目录</button>
|
|
||||||
<button @click="deleteAllVideos">删除所有视频</button>
|
|
||||||
<button @click="loadFwb">模拟加载</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="main" class="content-preview"></div>
|
|
||||||
<div id="nav" class="toc-container"></div>
|
|
||||||
<div id="editor" ref="editor"></div>
|
|
||||||
<!-- <div id="editor2" ref="editor2"></div> -->
|
|
||||||
<!-- <button @click="callWpfMethod">调用WPF方法</button> -->
|
|
||||||
<input type="text" v-model="val1">
|
|
||||||
<input type="text" v-model="val2">
|
|
||||||
<button @click="sendMessageToHost('李四','张三')">传递</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import WangEditor from 'wangeditor'
|
|
||||||
// import axios from 'axios';
|
|
||||||
// window.handleMessageFromDotNet = function(msg) {
|
|
||||||
// alert("Received message from C#: " + msg);
|
|
||||||
// }
|
|
||||||
export default {
|
|
||||||
name: 'RichTextEditor',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
val1:'',
|
|
||||||
val2:'',
|
|
||||||
wpfData: '',
|
|
||||||
message: '',
|
|
||||||
editor: null,
|
|
||||||
editor2:null,
|
|
||||||
editorContent: '',
|
|
||||||
editorContent2:'',
|
|
||||||
hasVideoSelected: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.initEditor()
|
|
||||||
// this.initEditor2();
|
|
||||||
// window.receiveMessageFromWpf = this.receiveMessageFromWpf;
|
|
||||||
|
|
||||||
console.log('chrome对象是否存在:', !!window.chrome);
|
|
||||||
console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|
||||||
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
// 销毁编辑器
|
|
||||||
if (this.editor) {
|
|
||||||
this.editor.destroy()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
sendMessageToHost() {
|
|
||||||
this.$sendToDotNet(this.val1,this.val2);
|
|
||||||
|
|
||||||
},
|
|
||||||
loadFwb() {
|
|
||||||
this.editor2.txt.html(this.editorContent)
|
|
||||||
|
|
||||||
// 创建编辑器
|
|
||||||
|
|
||||||
},
|
|
||||||
initEditor() {
|
|
||||||
this.editor = new WangEditor(this.$refs.editor)
|
|
||||||
|
|
||||||
// 配置编辑器
|
|
||||||
this.editor.config.uploadImgShowBase64 = true // 使用 base64 保存图片
|
|
||||||
this.editor.config.onchange = (html) => {
|
|
||||||
this.editorContent = html
|
|
||||||
}
|
|
||||||
let dm = this.editor.config.menus;
|
|
||||||
console.log("默认菜单",dm)
|
|
||||||
|
|
||||||
// 完全自定义菜单
|
|
||||||
this.editor.config.menus = [
|
|
||||||
'head', // 标题
|
|
||||||
'bold', // 粗体
|
|
||||||
// 'fontSize',//字号
|
|
||||||
// 'fontName',//字体
|
|
||||||
'italic', // 斜体
|
|
||||||
'underline', // 下划线
|
|
||||||
'strikeThrough', // 删除线
|
|
||||||
// 'line',//行高
|
|
||||||
'lineHeight',//
|
|
||||||
// 'foreColor', // 文字颜色
|
|
||||||
// 'backColor', // 背景颜色
|
|
||||||
// 'link', // 链接
|
|
||||||
'list', // 列表
|
|
||||||
// 'todo',//
|
|
||||||
'justify', // 对齐方式
|
|
||||||
// 'quote', // 引用
|
|
||||||
// 'emoticon',//表情
|
|
||||||
'image', // 图片
|
|
||||||
'video', // 视频
|
|
||||||
'table', // 表格
|
|
||||||
// 'code', // 代码
|
|
||||||
'splitLine',//分割线
|
|
||||||
'undo', // 撤销
|
|
||||||
'redo' // 重做
|
|
||||||
]
|
|
||||||
|
|
||||||
// 创建编辑器
|
|
||||||
this.editor.create();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 上传图片
|
|
||||||
insertImage() {
|
|
||||||
const imgUrl = 'http://youneed.top:10017/uploads/1.jpg'
|
|
||||||
this.editor.cmd.do('insertHTML', `<img src="${imgUrl}" style="max-width: 100%;" alt="图片">`)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 插入视频(可靠版本)
|
|
||||||
insertVideo() {
|
|
||||||
const videoUrl = 'http://youneed.top:10017/uploads/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">
|
|
||||||
<span class="video-delete" data-video-id="${videoId}">× 删除</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p><br></p>
|
|
||||||
`
|
|
||||||
|
|
||||||
// 使用编辑器命令插入
|
|
||||||
this.editor.cmd.do('insertHTML', videoHtml)
|
|
||||||
|
|
||||||
// 添加删除事件监听
|
|
||||||
this.bindDel();
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 根据ID删除视频(修正版)
|
|
||||||
deleteVideoById(videoId) {
|
|
||||||
console.log("ID",videoId)
|
|
||||||
const videoWrapper = document.querySelector(`[data-video-id="${videoId}"]`)
|
|
||||||
if (videoWrapper) {
|
|
||||||
// 获取视频所在段落
|
|
||||||
let parent = videoWrapper.parentElement
|
|
||||||
while (parent && parent !== this.$refs.editor) {
|
|
||||||
if (parent.tagName === 'P') {
|
|
||||||
// 如果是空段落,一并删除
|
|
||||||
if (parent.textContent.trim() === '') {
|
|
||||||
parent.remove()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
parent = parent.parentElement
|
|
||||||
}
|
|
||||||
|
|
||||||
videoWrapper.remove()
|
|
||||||
|
|
||||||
// 修正:使用正确的方式通知内容变更
|
|
||||||
if (this.editor.txt) {
|
|
||||||
this.editor.txt.html(this.editor.txt.html()) // 强制更新编辑器内容
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
bindDel(){
|
|
||||||
this.$nextTick(() => {
|
|
||||||
const deleteButtons = document.querySelectorAll('.video-delete')
|
|
||||||
console.log("所有绑定:",deleteButtons)
|
|
||||||
deleteButtons.forEach(btn => {
|
|
||||||
btn.onclick = (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
const videoId = btn.getAttribute('data-video-id')
|
|
||||||
this.deleteVideoById(videoId)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
deleteAllVideos() {
|
|
||||||
const videoWrappers = document.querySelectorAll('.video-wrapper')
|
|
||||||
if (videoWrappers.length === 0) {
|
|
||||||
alert('没有找到可删除的视频')
|
|
||||||
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()}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 生成带锚点的目录
|
|
||||||
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')
|
|
||||||
|
|
||||||
if (headings.length === 0) {
|
|
||||||
navContainer.innerHTML = '<p>没有找到标题元素,无法生成目录。</p>'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const tocList = document.createElement('ul')
|
|
||||||
tocList.style.listStyleType = 'none'
|
|
||||||
tocList.style.paddingLeft = '0'
|
|
||||||
|
|
||||||
headings.forEach(heading => {
|
|
||||||
const level = parseInt(heading.tagName.substring(1))
|
|
||||||
const listItem = document.createElement('li')
|
|
||||||
listItem.style.marginLeft = `${(level - 1) * 15}px`
|
|
||||||
listItem.style.marginBottom = '5px'
|
|
||||||
|
|
||||||
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)
|
|
||||||
tocList.appendChild(listItem)
|
|
||||||
})
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
// 方式1:直接访问全局变量
|
|
||||||
this.$watch(
|
|
||||||
() => this.$dotNetMessage,
|
|
||||||
(newMsg) => {
|
|
||||||
console.log('收到消息:', newMsg)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 方式2:监听全局事件
|
|
||||||
window.addEventListener('dotnet-message', (e) => {
|
|
||||||
console.log('通过事件收到:', e.detail)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.editor-container {
|
|
||||||
width: 80%;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#editor,#editor2 {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
min-height: 300px;
|
|
||||||
padding: 10px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-preview {
|
|
||||||
margin-top: 20px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
padding: 15px;
|
|
||||||
background: #fafafa;
|
|
||||||
}
|
|
||||||
#main th,
|
|
||||||
#main td {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
#main th {
|
|
||||||
background-color: #f2f2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 视频容器样式 */
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
657
src/components/wangG2.vue
Normal file
@@ -0,0 +1,657 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- <el-button @click='getNet'>获取</el-button>
|
||||||
|
<input type="text"> -->
|
||||||
|
<el-descriptions class="margin-top" v-if='viewXml' title="章节信息" :column="3" border>
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button @click='dmcTable' type="primary" size="small">{{dmcHint?"收起":"展开"}}</el-button>
|
||||||
|
</template>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
章节名称
|
||||||
|
</template>
|
||||||
|
{{rdfDescriptions[0].children[0].text}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
创作单位
|
||||||
|
</template>
|
||||||
|
{{rdfDescriptions[0].children[1].text}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
权限
|
||||||
|
</template>
|
||||||
|
{{formatOther('permission',rdfDescriptions[0].children[2].text).label}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
合作单位
|
||||||
|
</template>
|
||||||
|
{{rdfDescriptions[0].children[5].text}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
版本号
|
||||||
|
</template>
|
||||||
|
{{rdfDescriptions[0].children[6].text}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
密级
|
||||||
|
</template>
|
||||||
|
<!-- {{rdfDescriptions[0].children[7].text}} -->
|
||||||
|
{{formatOther('level',rdfDescriptions[0].children[7].text).label}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
发布日期
|
||||||
|
</template>
|
||||||
|
{{formatDate(rdfDescriptions[0].children[8].text)}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
适用性信息
|
||||||
|
</template>
|
||||||
|
{{rdfDescriptions[0].children[9].text}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
质量验证
|
||||||
|
</template>
|
||||||
|
{{rdfDescriptions[0].children[10].text}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if='dmcHint'>
|
||||||
|
<template slot="label">
|
||||||
|
语言
|
||||||
|
</template>
|
||||||
|
{{rdfDescriptions[0].children[14].text}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<div class="loadContent" style="width:100%;" v-html="neirong">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
optionsPermission:[
|
||||||
|
{label:'一级',value:'1'},
|
||||||
|
{label:'二级',value:'2'},
|
||||||
|
{label:'三级',value:'3'},
|
||||||
|
{label:'四级',value:'4'},
|
||||||
|
{label:'五级',value:'5'},
|
||||||
|
{label:'六级',value:'6'},
|
||||||
|
{label:'七级',value:'7'},
|
||||||
|
{label:'八级',value:'8'},
|
||||||
|
{label:'九级',value:'9'},
|
||||||
|
{label:'十级',value:'10'},
|
||||||
|
],
|
||||||
|
optionsLevel: [
|
||||||
|
{value: '1',label: '公开'},
|
||||||
|
{value: '2',label: '秘密'},
|
||||||
|
{value: '3',label: '机密'},
|
||||||
|
{value: '4',label: '绝密'}
|
||||||
|
],
|
||||||
|
dmcHint:false,
|
||||||
|
rdfDescriptions:[],
|
||||||
|
pmTitleContent:{},
|
||||||
|
viewLis:'',
|
||||||
|
viewXml:'',
|
||||||
|
neirong:'',
|
||||||
|
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',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
console.log('G2页面2222 chrome对象是否存在:', !!window.chrome);
|
||||||
|
console.log('G2页面2222 webview对象是否存在:', !!window.chrome?.webview);
|
||||||
|
this.neirong = '';
|
||||||
|
this.getNet()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
dmcTable() {
|
||||||
|
if(this.dmcHint){
|
||||||
|
this.dmcHint = false;
|
||||||
|
}else{
|
||||||
|
this.dmcHint = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatOther(type,data){
|
||||||
|
let textStr = null;
|
||||||
|
console.log("内容",type,data)
|
||||||
|
switch(type){
|
||||||
|
case "level":
|
||||||
|
textStr = this.optionsLevel.find(item => {
|
||||||
|
return item.value === data;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "permission":
|
||||||
|
textStr = this.optionsPermission.find(item => {
|
||||||
|
return item.value === data;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.log('内容',data,textStr)
|
||||||
|
return textStr;
|
||||||
|
},
|
||||||
|
formatDate(dateStr) {
|
||||||
|
if (!dateStr) return "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 将字符串转换为Date对象
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
|
||||||
|
// 获取年、月、日
|
||||||
|
const year = date.getFullYear();
|
||||||
|
// 月份从0开始,需要+1,并用padStart补零确保两位数
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||||
|
// 日期补零确保两位数
|
||||||
|
const day = String(date.getDate()).padStart(2, "0");
|
||||||
|
|
||||||
|
// 返回格式化后的字符串(格式:YYYY-MM-DD)
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("日期格式化失败:", error);
|
||||||
|
return "无效日期";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getNet() {
|
||||||
|
console.log("发送请求",this.rdfDescriptions)
|
||||||
|
this.$sendToDotNet('DMPreview','HistoryDMPreview')
|
||||||
|
this.$sendToDotNet('DMPreview','UserLocalDMPreview')
|
||||||
|
},
|
||||||
|
parseRdfDescriptions() {
|
||||||
|
try {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const xmlDoc = parser.parseFromString(this.viewXml, 'text/xml');
|
||||||
|
|
||||||
|
// 定义RDF命名空间(关键:必须与XML中的命名空间一致)
|
||||||
|
const nsResolver = (prefix) => {
|
||||||
|
const ns = {
|
||||||
|
'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
|
||||||
|
// 可以添加其他需要的命名空间
|
||||||
|
};
|
||||||
|
return ns[prefix] || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用XPath查询所有rdf:Description元素
|
||||||
|
const xpathResult = xmlDoc.evaluate(
|
||||||
|
'//rdf:Description',
|
||||||
|
xmlDoc,
|
||||||
|
nsResolver,
|
||||||
|
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
this.rdfDescriptions = [];
|
||||||
|
let currentNode;
|
||||||
|
|
||||||
|
// 遍历所有找到的rdf:Description节点
|
||||||
|
while ((currentNode = xpathResult.iterateNext())) {
|
||||||
|
// 获取节点内的所有内容(包括子节点和文本)
|
||||||
|
const content = {
|
||||||
|
attributes: {},
|
||||||
|
children: [],
|
||||||
|
innerXML: currentNode.innerHTML // 完整的内部XML
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取所有属性
|
||||||
|
if (currentNode.attributes) {
|
||||||
|
for (let i = 0; i < currentNode.attributes.length; i++) {
|
||||||
|
const attr = currentNode.attributes[i];
|
||||||
|
content.attributes[attr.name] = attr.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有子节点文本内容
|
||||||
|
if (currentNode.children && currentNode.children.length > 0) {
|
||||||
|
for (let i = 0; i < currentNode.children.length; i++) {
|
||||||
|
const child = currentNode.children[i];
|
||||||
|
content.children.push({
|
||||||
|
tag: child.tagName,
|
||||||
|
text: child.textContent,
|
||||||
|
attributes: Array.from(child.attributes).reduce((attrs, attr) => {
|
||||||
|
attrs[attr.name] = attr.value;
|
||||||
|
return attrs;
|
||||||
|
}, {})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没有子节点,直接获取文本内容
|
||||||
|
content.text = currentNode.textContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rdfDescriptions.push(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('所有rdf:Description内容:', this.rdfDescriptions);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析XML出错:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//解析新建目录时候的DMC文件
|
||||||
|
parseXmlDmc_loadFile() {
|
||||||
|
try {
|
||||||
|
// 解析XML字符串
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const xmlDoc = parser.parseFromString(this.viewXml, "text/xml");
|
||||||
|
console.log("得到的1",xmlDoc)
|
||||||
|
// 检查解析错误
|
||||||
|
const errorNode = xmlDoc.querySelector("parsererror");
|
||||||
|
console.log("得到的2",errorNode)
|
||||||
|
|
||||||
|
|
||||||
|
// if (errorNode) {
|
||||||
|
// throw new Error("XML解析错误: " + errorNode.textContent);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 提取content标签内容
|
||||||
|
this.extractContent2(xmlDoc);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("解析失败:", error);
|
||||||
|
// alert("解析XML失败: " + error.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 提取新建目录时候的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(contentHtml.trim())
|
||||||
|
// this.contentResult = contentHtml.trim();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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+'/';
|
||||||
|
// }
|
||||||
|
// console.log("jc")
|
||||||
|
// 替换为:src="基础URL+文件名"
|
||||||
|
// 这里假设原路径可能包含DM_Material目录,如果不需要可以去掉
|
||||||
|
let html_2 = this.tempDivData1.innerHTML.replace(reg2, `$1${jc}$3$4`);
|
||||||
|
// html_2 = this.modelToImg(html_2);
|
||||||
|
console.log("大豪科技等哈时间",this.viewLis)
|
||||||
|
console.log('加载还原地址:',html_2)
|
||||||
|
|
||||||
|
let processedL = this.hintFun(html_2)
|
||||||
|
|
||||||
|
this.neirong = this.modelToImg(processedL);
|
||||||
|
}else{
|
||||||
|
console.log("空白文档")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
hintFun(html_2) {
|
||||||
|
|
||||||
|
let html = html_2;
|
||||||
|
|
||||||
|
// 定义需要替换的class映射关系
|
||||||
|
const classMappings = {
|
||||||
|
'teshu': 'testBo',
|
||||||
|
'teshu2': 'testBo2'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用正则表达式匹配class属性并替换相应的值
|
||||||
|
// 这个正则表达式会匹配class属性中的各个class名称
|
||||||
|
html = html.replace(/class="([^"]*)"/g, (match, classValues) => {
|
||||||
|
let newClassValues = classValues;
|
||||||
|
|
||||||
|
// 遍历替换映射,替换每个需要替换的class
|
||||||
|
Object.keys(classMappings).forEach(oldClass => {
|
||||||
|
const newClass = classMappings[oldClass];
|
||||||
|
// 使用单词边界确保只替换完整的class名称
|
||||||
|
const regex = new RegExp(`\\b${oldClass}\\b`, 'g');
|
||||||
|
newClassValues = newClassValues.replace(regex, newClass);
|
||||||
|
});
|
||||||
|
|
||||||
|
return `class="${newClassValues}"`;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const regex = /(<div\s+[^>]*class="[^"]*(testBo2|testBo)[^"]*"[^>]*>)(.*?)(<\/div>)/gis;
|
||||||
|
|
||||||
|
html = html.replace(regex, (match, startTag, className, content, endTag) => {
|
||||||
|
// 验证捕获的className
|
||||||
|
console.log('捕获的class名称:', className);
|
||||||
|
|
||||||
|
// 定义不同class对应的边框样式
|
||||||
|
let borderStyle;
|
||||||
|
if (className === 'testBo2') {
|
||||||
|
borderStyle = '3px solid #ffcc00'; // testBo的边框样式(深黄色)
|
||||||
|
} else if (className === 'testBo') {
|
||||||
|
borderStyle = '3px solid red'; // testBo2的边框样式(红色)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 包裹div的完整样式
|
||||||
|
const wrapperStyle = `background:#fff;padding: 10px;border: ${borderStyle};border-radius: 8px;`;
|
||||||
|
// 包裹内容
|
||||||
|
const wrappedContent = `<div style='${wrapperStyle}'>${content}</div>`;
|
||||||
|
// 返回处理后的标签
|
||||||
|
return `${startTag}${wrappedContent}${endTag}`;
|
||||||
|
});
|
||||||
|
return html;
|
||||||
|
},
|
||||||
|
//解析3d资源标识
|
||||||
|
modelToImg(htmlString) {
|
||||||
|
// 匹配<model3d>标签且class中包含model3d
|
||||||
|
const regex = /<model3d([^>]*?)class="([^"]*?)model3d([^"]*?)"([^>]*?)>/gi;
|
||||||
|
|
||||||
|
// 替换标签名,保留所有属性
|
||||||
|
return htmlString.replace(regex, '<img$1class="$2model3d$3"$4>');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动修复XML中的常见语法错误,重点处理属性构造问题
|
||||||
|
* @param {string} xmlString - 包含错误的XML字符串
|
||||||
|
* @returns {Object} { fixedXml: 修复后的XML, errors: 修复过程中发现的错误 }
|
||||||
|
*/
|
||||||
|
fixXml(xmlString) {
|
||||||
|
if (typeof xmlString !== 'string') {
|
||||||
|
this.$emit('xml-error', '输入必须是字符串类型');
|
||||||
|
return { fixedXml: '', errors: ['输入必须是字符串类型'] };
|
||||||
|
}
|
||||||
|
|
||||||
|
let fixedXml = xmlString;
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 修复属性构造错误 - 处理未闭合的引号和非法字符
|
||||||
|
fixedXml = this.fixAttributeIssues(fixedXml, errors);
|
||||||
|
|
||||||
|
// 2. 修复自闭合标签 - 将空的成对标签转换为自闭合标签
|
||||||
|
const emptyTagRegex = /<(\w+:[^\s>]+|\w+)(\s+[^>]*?)?>\s*<\/\1\s*>/g;
|
||||||
|
const emptyTagMatches = fixedXml.match(emptyTagRegex);
|
||||||
|
if (emptyTagMatches && emptyTagMatches.length) {
|
||||||
|
errors.push(`发现并修复了 ${emptyTagMatches.length} 个空成对标签`);
|
||||||
|
fixedXml = fixedXml.replace(emptyTagRegex, '<$1$2/>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 修复常见的单标签错误
|
||||||
|
const selfClosingTags = ['br', 'img', 'input', 'audio', 'video', 'pbr'];
|
||||||
|
selfClosingTags.forEach(tag => {
|
||||||
|
const regex = new RegExp(`<(${tag})(\\s+[^>]*?)?>(\\s*</\\1\\s*>)?`, 'g');
|
||||||
|
const tagMatches = fixedXml.match(regex);
|
||||||
|
if (tagMatches && tagMatches.length) {
|
||||||
|
errors.push(`发现并修复了 ${tagMatches.length} 个${tag}标签`);
|
||||||
|
fixedXml = fixedXml.replace(regex, '<$1$2/>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. 修复dc命名空间的空标签
|
||||||
|
const dcTagsRegex = /<(dc:[^\s>]+)(\s+[^>]*?)?>\s*<\/\1\s*>/g;
|
||||||
|
const dcTagMatches = fixedXml.match(dcTagsRegex);
|
||||||
|
if (dcTagMatches && dcTagMatches.length) {
|
||||||
|
errors.push(`发现并修复了 ${dcTagMatches.length} 个dc命名空间标签`);
|
||||||
|
fixedXml = fixedXml.replace(dcTagsRegex, '<$1$2/>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 修复表格相关的空标签
|
||||||
|
const tableTags = ['th', 'td'];
|
||||||
|
tableTags.forEach(tag => {
|
||||||
|
const regex = new RegExp(`<(${tag})(\\s+[^>]*?)?>(\\s*</\\1\\s*>)?`, 'g');
|
||||||
|
fixedXml = fixedXml.replace(regex, (match, tagName, attrs) => {
|
||||||
|
// 检查标签内是否有实际内容
|
||||||
|
const contentStart = match.indexOf(`<${tagName}`) + tagName.length + 1;
|
||||||
|
const contentEnd = match.indexOf(`</${tagName}>`);
|
||||||
|
const hasContent = contentEnd > contentStart &&
|
||||||
|
match.substring(contentStart, contentEnd).trim().length > 0;
|
||||||
|
|
||||||
|
if (!hasContent) {
|
||||||
|
errors.push(`修复了空的${tagName}标签`);
|
||||||
|
return `<${tagName}${attrs || ''}/>`;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6. 去除多余的闭合标签
|
||||||
|
const redundantClosingRegex = /<(\w+:[^\s>]+|\w+)(\s+[^>]*?)?\/>\s*<\/\1\s*>/g;
|
||||||
|
fixedXml = fixedXml.replace(redundantClosingRegex, '<$1$2/>');
|
||||||
|
|
||||||
|
// 验证修复结果
|
||||||
|
const validation = this.validateXml(fixedXml);
|
||||||
|
if (!validation.valid) {
|
||||||
|
errors.push(...validation.errors);
|
||||||
|
|
||||||
|
// 尝试定位错误位置
|
||||||
|
const lineNumber = validation.errors[0].match(/line (\d+)/);
|
||||||
|
if (lineNumber && lineNumber[1]) {
|
||||||
|
errors.push(`错误大致位置:第 ${lineNumber[1]} 行`);
|
||||||
|
// 提取错误行附近的内容帮助调试
|
||||||
|
const lines = fixedXml.split('\n');
|
||||||
|
const errorLineNum = parseInt(lineNumber[1]) - 1; // 转换为0基索引
|
||||||
|
if (lines[errorLineNum]) {
|
||||||
|
errors.push(`错误行内容:${lines[errorLineNum].substring(0, 100)}...`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errors.push('XML修复成功,格式验证通过');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { fixedXml, errors };
|
||||||
|
} catch (err) {
|
||||||
|
this.$emit('xml-error', `修复过程出错: ${err.message}`);
|
||||||
|
return { fixedXml: xmlString, errors: [`修复过程出错: ${err.message}`] };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 专门修复属性构造错误
|
||||||
|
* @param {string} xmlString - XML字符串
|
||||||
|
* @param {Array} errors - 错误信息数组
|
||||||
|
* @returns {string} 修复后的XML字符串
|
||||||
|
*/
|
||||||
|
fixAttributeIssues(xmlString, errors) {
|
||||||
|
let fixed = xmlString;
|
||||||
|
|
||||||
|
// 1. 修复未闭合的属性引号
|
||||||
|
// 匹配模式:属性名="值但没有闭合引号
|
||||||
|
fixed = fixed.replace(/(\s\w+)=("|')([^"']*)(?=\s|>)/g, (match, attrName, quote, value) => {
|
||||||
|
errors.push(`修复了未闭合的${attrName}属性引号`);
|
||||||
|
return `${attrName}=${quote}${value}${quote}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 修复属性值中的非法字符
|
||||||
|
fixed = fixed.replace(/(\s\w+)=("|')([^"']*)(?=\s|>)/g, (match, attrName, quote, value) => {
|
||||||
|
// 替换属性值中的非法字符
|
||||||
|
const sanitizedValue = value
|
||||||
|
.replace(/&/g, '&') // & 必须转义
|
||||||
|
.replace(/</g, '<') // < 必须转义
|
||||||
|
.replace(/>/g, '>') // > 必须转义
|
||||||
|
.replace(/"/g, '"') // " 必须转义
|
||||||
|
.replace(/'/g, '''); // ' 必须转义
|
||||||
|
|
||||||
|
if (sanitizedValue !== value) {
|
||||||
|
errors.push(`修复了${attrName}属性中的非法字符`);
|
||||||
|
return `${attrName}=${quote}${sanitizedValue}${quote}`;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. 修复重复的属性
|
||||||
|
fixed = fixed.replace(/<(\w+)(\s+[^>]+?)>/g, (match, tagName, attrsStr) => {
|
||||||
|
const attrs = {};
|
||||||
|
const attrRegex = /(\w+)=("|')([^"']*)\2/g;
|
||||||
|
let cleanedAttrs = '';
|
||||||
|
|
||||||
|
attrsStr.replace(attrRegex, (attrMatch, name, quote, value) => {
|
||||||
|
if (!attrs[name]) {
|
||||||
|
attrs[name] = true;
|
||||||
|
cleanedAttrs += ` ${name}=${quote}${value}${quote}`;
|
||||||
|
} else {
|
||||||
|
errors.push(`移除了${tagName}标签中重复的${name}属性`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return `<${tagName}${cleanedAttrs}>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return fixed;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证XML是否合法
|
||||||
|
* @param {string} xmlString - 要验证的XML字符串
|
||||||
|
* @returns {Object} { valid: 是否有效, errors: 错误信息数组 }
|
||||||
|
*/
|
||||||
|
validateXml(xmlString) {
|
||||||
|
try {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(xmlString, 'text/xml');
|
||||||
|
const errorNode = doc.querySelector('parsererror');
|
||||||
|
|
||||||
|
if (errorNode) {
|
||||||
|
const errorText = errorNode.textContent || 'XML格式错误';
|
||||||
|
return { valid: false, errors: [errorText] };
|
||||||
|
}
|
||||||
|
return { valid: true, errors: [] };
|
||||||
|
} catch (err) {
|
||||||
|
return { valid: false, errors: [`验证出错: ${err.message}`] };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定位并显示错误行内容,帮助调试
|
||||||
|
* @param {string} xmlString - XML字符串
|
||||||
|
* @param {number} lineNumber - 错误行号
|
||||||
|
* @returns {string} 错误行附近的内容
|
||||||
|
*/
|
||||||
|
showErrorLine(xmlString, lineNumber) {
|
||||||
|
const lines = xmlString.split('\n');
|
||||||
|
const start = Math.max(0, lineNumber - 2);
|
||||||
|
const end = Math.min(lines.length, lineNumber + 2);
|
||||||
|
|
||||||
|
let errorContext = `错误位置:第 ${lineNumber} 行\n`;
|
||||||
|
for (let i = start; i < end; i++) {
|
||||||
|
errorContext += `${i + 1}: ${lines[i] || ''}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
window.addEventListener('handreContent', (e) => {
|
||||||
|
if(e){
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
console.log("得到的0",this.viewXml)
|
||||||
|
this.parseRdfDescriptions(this.viewXml)
|
||||||
|
this.parseXmlDmc_loadFile();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener('handPrePath', (e) => {
|
||||||
|
console.log("返回handPrePath",e.detail)
|
||||||
|
if(e){
|
||||||
|
this.viewLis = e.detail;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener('handleMessageNotice', () => {
|
||||||
|
this.getNet();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 为表格添加边框 */
|
||||||
|
table {
|
||||||
|
border-collapse: collapse; /* 合并边框 */
|
||||||
|
width: 100%; /* 可选:设置表格宽度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 为表格单元格添加边框 */
|
||||||
|
table, th, td {
|
||||||
|
border: 1px solid #ddd; /* 灰色边框 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 为表头添加背景色 */
|
||||||
|
th {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
padding: 8px; /* 单元格内边距 */
|
||||||
|
text-align: left; /* 文本左对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 为单元格添加内边距 */
|
||||||
|
td {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
430
src/components/wangG3.vue
Normal file
@@ -1,207 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="s1000d-container">
|
|
||||||
<button @click="exportToS1000D">导出为S1000D XML</button>
|
|
||||||
<div ref="editor"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import E from 'wangeditor'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
editor: null,
|
|
||||||
content: '<p>这是初始内容</p>'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.initEditor()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initEditor() {
|
|
||||||
this.editor = new E(this.$refs.editor)
|
|
||||||
this.editor.config.onchange = (html) => {
|
|
||||||
this.content = html
|
|
||||||
}
|
|
||||||
this.editor.create()
|
|
||||||
this.editor.txt.html(this.content)
|
|
||||||
},
|
|
||||||
|
|
||||||
exportToS1000D() {
|
|
||||||
// 1. 获取富文本内容
|
|
||||||
const htmlContent = this.content || this.editor.txt.html()
|
|
||||||
|
|
||||||
// 2. 转换为S1000D XML结构
|
|
||||||
const s1000dXml = this.convertToS1000D(htmlContent)
|
|
||||||
|
|
||||||
// 3. 下载XML文件
|
|
||||||
this.downloadXML(s1000dXml, 's1000d-data-module.xml')
|
|
||||||
},
|
|
||||||
|
|
||||||
convertToS1000D(html) {
|
|
||||||
// 基本S1000D XML结构
|
|
||||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE dmodule [
|
|
||||||
<!ENTITY % ISOEntities PUBLIC "ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN" "ISOEntities">
|
|
||||||
%ISOEntities;
|
|
||||||
]>
|
|
||||||
<dmodule xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
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>AVE</avee>
|
|
||||||
<modelic>MODEL</modelic>
|
|
||||||
<sdc>A</sdc>
|
|
||||||
<chapnum>00</chapnum>
|
|
||||||
<section>0</section>
|
|
||||||
<subsect>0</subsect>
|
|
||||||
<subject>SUB</subject>
|
|
||||||
<discode>DISC</discode>
|
|
||||||
<discodev>001</discodev>
|
|
||||||
<incode>INC</incode>
|
|
||||||
<incodev>001</incodev>
|
|
||||||
<itemloc>ITEM</itemloc>
|
|
||||||
</dmc>
|
|
||||||
<issue inwork="01" issno="001" type="new"/>
|
|
||||||
<language language-iso="zh-CN"/>
|
|
||||||
<dmtitle>
|
|
||||||
<techname>技术文档标题</techname>
|
|
||||||
<infoname>信息名称</infoname>
|
|
||||||
</dmtitle>
|
|
||||||
</dmaddress>
|
|
||||||
</idstatus>
|
|
||||||
|
|
||||||
<!-- 内容部分 -->
|
|
||||||
<content>
|
|
||||||
<description>
|
|
||||||
${this.htmlToS1000D(html)}
|
|
||||||
</description>
|
|
||||||
</content>
|
|
||||||
</dmodule>`
|
|
||||||
},
|
|
||||||
|
|
||||||
// 将HTML转换为S1000D兼容的XML
|
|
||||||
htmlToS1000D(html) {
|
|
||||||
// 创建临时div来解析HTML
|
|
||||||
const tempDiv = document.createElement('div')
|
|
||||||
tempDiv.innerHTML = html
|
|
||||||
|
|
||||||
// 递归处理DOM节点
|
|
||||||
const processNode = (node) => {
|
|
||||||
if (node.nodeType === Node.TEXT_NODE) {
|
|
||||||
return this.escapeXml(node.textContent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.nodeType !== Node.ELEMENT_NODE) return ''
|
|
||||||
|
|
||||||
const tag = node.tagName.toLowerCase()
|
|
||||||
const children = Array.from(node.childNodes).map(processNode).join('')
|
|
||||||
const attrs = Array.from(node.attributes)
|
|
||||||
.map(attr => ` ${attr.name}="${this.escapeXml(attr.value)}"`)
|
|
||||||
.join('')
|
|
||||||
|
|
||||||
// 将HTML标签映射到S1000D元素
|
|
||||||
switch(tag) {
|
|
||||||
case 'p':
|
|
||||||
return `<para${attrs}>${children}</para>`
|
|
||||||
case 'b':
|
|
||||||
case 'strong':
|
|
||||||
return `<emphasis role="bold">${children}</emphasis>`
|
|
||||||
case 'i':
|
|
||||||
case 'em':
|
|
||||||
return `<emphasis role="italic">${children}</emphasis>`
|
|
||||||
case 'u':
|
|
||||||
return `<emphasis role="underline">${children}</emphasis>`
|
|
||||||
case 'ol':
|
|
||||||
return `<orderedlist${attrs}>${children}</orderedlist>`
|
|
||||||
case 'ul':
|
|
||||||
return `<itemizedlist${attrs}>${children}</itemizedlist>`
|
|
||||||
case 'li':
|
|
||||||
return `<listitem>${children}</listitem>`
|
|
||||||
case 'img': {
|
|
||||||
const figureId = `FIG_${Date.now()}`
|
|
||||||
return `<figure><graphic infoentityid="${figureId}"${attrs}/></figure>`
|
|
||||||
}
|
|
||||||
case 'table':
|
|
||||||
return `<table${attrs}>${children}</table>`
|
|
||||||
case 'tr':
|
|
||||||
return `<row>${children}</row>`
|
|
||||||
case 'td':
|
|
||||||
return `<entry>${children}</entry>`
|
|
||||||
case 'th':
|
|
||||||
return `<entry thead="yes">${children}</entry>`
|
|
||||||
case 'h1':
|
|
||||||
case 'h2':
|
|
||||||
case 'h3':
|
|
||||||
case 'h4':
|
|
||||||
case 'h5':
|
|
||||||
case 'h6': {
|
|
||||||
const level = parseInt(tag.substring(1))
|
|
||||||
return `<title level="${level}">${children}</title>`
|
|
||||||
}
|
|
||||||
case 'a':
|
|
||||||
return `<xref xrefid="${node.getAttribute('href') || ''}">${children}</xref>`
|
|
||||||
default:
|
|
||||||
return children // 忽略不支持的标签但保留内容
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from(tempDiv.childNodes).map(processNode).join('')
|
|
||||||
},
|
|
||||||
|
|
||||||
// XML特殊字符转义
|
|
||||||
escapeXml(text) {
|
|
||||||
return text
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, ''')
|
|
||||||
},
|
|
||||||
|
|
||||||
// 下载XML文件
|
|
||||||
downloadXML(xmlString, fileName) {
|
|
||||||
const blob = new Blob([xmlString], { type: 'application/xml' })
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = URL.createObjectURL(blob)
|
|
||||||
link.download = fileName
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
document.body.removeChild(link)
|
|
||||||
URL.revokeObjectURL(link.href)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
if (this.editor) {
|
|
||||||
this.editor.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.s1000d-container {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
padding: 8px 16px;
|
|
||||||
background: #0066cc;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #0052a3;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
88
src/directives/draggable.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
export default {
|
||||||
|
bind(el) {
|
||||||
|
// 获取对话框头部和主体元素
|
||||||
|
const dialogHeaderEl = el.querySelector('.el-dialog__header');
|
||||||
|
const dragDom = el.querySelector('.el-dialog');
|
||||||
|
|
||||||
|
if (!dialogHeaderEl || !dragDom) {
|
||||||
|
console.warn('未找到对话框头部或主体元素,拖拽功能无法启用');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置头部元素可拖动样式
|
||||||
|
dialogHeaderEl.style.cursor = 'move';
|
||||||
|
|
||||||
|
// 初始化对话框位置(关键:消除默认margin居中的影响)
|
||||||
|
const initPosition = () => {
|
||||||
|
// 如果是首次加载且未设置过left/top,强制计算初始位置
|
||||||
|
if (!dragDom.style.left || !dragDom.style.top) {
|
||||||
|
// 获取视口和对话框尺寸
|
||||||
|
const viewWidth = window.innerWidth;
|
||||||
|
const viewHeight = window.innerHeight;
|
||||||
|
const dialogWidth = dragDom.offsetWidth;
|
||||||
|
const dialogHeight = dragDom.offsetHeight;
|
||||||
|
|
||||||
|
// 计算居中位置(替代默认的margin居中)
|
||||||
|
const initLeft = (viewWidth - dialogWidth) / 2;
|
||||||
|
const initTop = Math.max(50, (viewHeight - dialogHeight) / 3); // 稍微靠上一点
|
||||||
|
|
||||||
|
// 应用初始位置,同时清除默认margin
|
||||||
|
dragDom.style.left = `${initLeft}px`;
|
||||||
|
dragDom.style.top = `${initTop}px`;
|
||||||
|
dragDom.style.margin = '0'; // 关键:移除默认margin
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化位置
|
||||||
|
initPosition();
|
||||||
|
|
||||||
|
// 鼠标按下事件
|
||||||
|
dialogHeaderEl.onmousedown = (e) => {
|
||||||
|
// 防止拖动时选中文本
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// 获取鼠标在对话框头部的相对位置(关键:使用getBoundingClientRect计算绝对位置)
|
||||||
|
const dialogRect = dragDom.getBoundingClientRect();
|
||||||
|
const disX = e.clientX - dialogRect.left;
|
||||||
|
const disY = e.clientY - dialogRect.top;
|
||||||
|
|
||||||
|
// 鼠标移动事件
|
||||||
|
const handleMouseMove = (moveE) => {
|
||||||
|
moveE.preventDefault();
|
||||||
|
|
||||||
|
// 计算新位置(基于视口的绝对位置)
|
||||||
|
const newLeft = moveE.clientX - disX;
|
||||||
|
const newTop = moveE.clientY - disY;
|
||||||
|
|
||||||
|
// 限制在视口内
|
||||||
|
const maxLeft = window.innerWidth - dragDom.offsetWidth;
|
||||||
|
const maxTop = window.innerHeight - dragDom.offsetHeight;
|
||||||
|
|
||||||
|
// 应用新位置
|
||||||
|
dragDom.style.left = `${Math.max(0, Math.min(newLeft, maxLeft))}px`;
|
||||||
|
dragDom.style.top = `${Math.max(0, Math.min(newTop, maxTop))}px`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 鼠标释放事件
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
document.removeEventListener('mousemove', handleMouseMove);
|
||||||
|
document.removeEventListener('mouseup', handleMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 绑定事件
|
||||||
|
document.addEventListener('mousemove', handleMouseMove);
|
||||||
|
document.addEventListener('mouseup', handleMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 窗口大小改变时重新居中
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
// 只有在未被拖拽过的情况下才重新居中
|
||||||
|
if (!dragDom.dataset.dragged) {
|
||||||
|
initPosition();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 标记是否被拖拽过
|
||||||
|
dragDom.dataset.dragged = 'false';
|
||||||
|
}
|
||||||
|
};
|
||||||
213
src/main - 副本.js
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import axios from 'axios';
|
||||||
|
import router from './router' // 引入路由配置
|
||||||
|
import ElementUI from 'element-ui';
|
||||||
|
import 'element-ui/lib/theme-chalk/index.css';
|
||||||
|
// 导入拖拽指令
|
||||||
|
import draggable from './directives/draggable';
|
||||||
|
|
||||||
|
|
||||||
|
// 全局注册指令
|
||||||
|
Vue.directive('draggable', draggable);
|
||||||
|
Vue.use(ElementUI);
|
||||||
|
|
||||||
|
Vue.prototype.$axios = axios;
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
// axios.defaults.baseURL = 'http://localhost:3000';
|
||||||
|
axios.defaults.baseURL = 'http://192.168.31.181:3000';
|
||||||
|
// axios.defaults.baseURL = 'https://api.example.com';
|
||||||
|
|
||||||
|
// 可以在此处配置请求头、超时等
|
||||||
|
axios.defaults.headers.common['Authorization'] = 'Bearer token';
|
||||||
|
axios.defaults.timeout = 10000;
|
||||||
|
|
||||||
|
|
||||||
|
window.handleMessageFromDotNet = function(msg) {//窗口界面测试传递
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
const event = new CustomEvent('dotnet-message', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.SelectFilePathSend = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN SelectFilePathSend",msg)
|
||||||
|
const event = new CustomEvent('SelectFilePathSend', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// window.frontLoadProject = function(msg) {//接收后端返回路径
|
||||||
|
// Vue.prototype.$dotNetMessage = msg
|
||||||
|
// // 可以触发全局事件
|
||||||
|
// console.log("MAIN FrontLoadProject 2222",msg)
|
||||||
|
// const event = new CustomEvent('frontLoadProject', { detail: msg })
|
||||||
|
// window.dispatchEvent(event)
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// window.FrontLoadProject = function(msg) {//接收后端返回路径
|
||||||
|
// Vue.prototype.$dotNetMessage = msg
|
||||||
|
// // 可以触发全局事件
|
||||||
|
// // console.log("MAIN FrontLoadProject 2222",msg)
|
||||||
|
// const event = new CustomEvent('FrontLoadProject', { detail: msg })
|
||||||
|
// window.dispatchEvent(event)
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
let funAll = ['FrontLoadProject'];
|
||||||
|
for(let i =0;i<funAll.length;i++){
|
||||||
|
window[funAll[i]] = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN FrontLoadProject 2222",msg)
|
||||||
|
const event = new CustomEvent(funAll[i], { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.aaa = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN FrontLoadProject 2222",msg)
|
||||||
|
const event = new CustomEvent('aaa', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.FrontLoadDM = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN FrontLoadDM",msg)
|
||||||
|
const event = new CustomEvent('FrontLoadDM', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.GetDMDta = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN GetDMDta",msg)
|
||||||
|
const event = new CustomEvent('GetDMDta', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.SendResourcePath = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN SendResourcePath",msg)
|
||||||
|
const event = new CustomEvent('SendResourcePath', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.SendLisentPath = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
console.log("监听")
|
||||||
|
const event = new CustomEvent('SendLisentPath', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
window.SendProjectName = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN SendProjectName",msg)
|
||||||
|
const event = new CustomEvent('SendProjectName', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.SendNewVersionDMFileName = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN SendNewVersionDMFileName",msg)
|
||||||
|
const event = new CustomEvent('SendNewVersionDMFileName', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.GetHistoryVesions = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN GetHistoryVesions",msg)
|
||||||
|
const event = new CustomEvent('GetHistoryVesions', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.GetDMHistoryContent = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN GetDMHistoryContent",msg)
|
||||||
|
const event = new CustomEvent('GetDMHistoryContent', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.GetHistoryDMContent = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN GetHistoryDMContent",msg)
|
||||||
|
const event = new CustomEvent('GetHistoryDMContent', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.GetExistenceDMContent = function(msg) {
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
const event = new CustomEvent('GetExistenceDMContent', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
window.handPrePath = function(msg) {
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
const event = new CustomEvent('handPrePath1', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.handreContent = function(msg) {//接收后端返回路径
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
// console.log("MAIN handreContent",msg)
|
||||||
|
const event = new CustomEvent('handreContent1', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const sendToDotNet = (type, payload,id,content,lagreVersion) => {//和后端进行通讯
|
||||||
|
if (!window.chrome?.webview?.postMessage) {
|
||||||
|
console.error('WebView2 环境未就绪!当前环境:', window.chrome ? '有chrome对象' : '无chrome对象');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// const message = JSON.stringify(type, payload)
|
||||||
|
// console.log("开始给C#发送的信息",message)
|
||||||
|
console.log('接收到的参数:', { type, payload, id, content,lagreVersion });
|
||||||
|
let message = {
|
||||||
|
// GetFilePath、SaveFile 取,存。
|
||||||
|
// LoadDM 加载DM 富文本。
|
||||||
|
// DragMaterial 拖拽时候告诉后端
|
||||||
|
// 当Type为SaveFile时,
|
||||||
|
// Data1为类型(PMC或者DMC) 目录PMC,富文本DMC
|
||||||
|
// Data2为文件编号、
|
||||||
|
// Data3为文件内容
|
||||||
|
'Type':type||'',// AddSource
|
||||||
|
'Data1':payload||'',
|
||||||
|
'Data2':id||'',
|
||||||
|
'Data3':content||'',
|
||||||
|
'Data4':lagreVersion||'',
|
||||||
|
'Data5':'',
|
||||||
|
'Data6':'',
|
||||||
|
}
|
||||||
|
console.log("传参",message,JSON.stringify(message))
|
||||||
|
window.chrome.webview.postMessage(message)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vue.prototype.$sendToDotNet = sendToDotNet
|
||||||
|
new Vue({
|
||||||
|
router, // 注册路由
|
||||||
|
render: h => h(App),
|
||||||
|
}).$mount('#app')
|
||||||
60
src/main copy.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import axios from 'axios';
|
||||||
|
import ElementUI from 'element-ui';
|
||||||
|
import 'element-ui/lib/theme-chalk/index.css';
|
||||||
|
|
||||||
|
Vue.use(ElementUI);
|
||||||
|
|
||||||
|
Vue.prototype.$axios = axios;
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
// axios.defaults.baseURL = 'http://localhost:3000';
|
||||||
|
axios.defaults.baseURL = 'http://192.168.31.181:3000';
|
||||||
|
// axios.defaults.baseURL = 'https://api.example.com';
|
||||||
|
|
||||||
|
// 可以在此处配置请求头、超时等
|
||||||
|
axios.defaults.headers.common['Authorization'] = 'Bearer token';
|
||||||
|
axios.defaults.timeout = 10000;
|
||||||
|
|
||||||
|
|
||||||
|
window.handleMessageFromDotNet = function(msg) {
|
||||||
|
Vue.prototype.$dotNetMessage = msg
|
||||||
|
// 可以触发全局事件
|
||||||
|
console.log("MAIN收到信息",msg)
|
||||||
|
const event = new CustomEvent('dotnet-message', { detail: msg })
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const sendToDotNet = (type, payload) => {
|
||||||
|
if (!window.chrome?.webview?.postMessage) {
|
||||||
|
console.error('WebView2 环境未就绪!当前环境:', window.chrome ? '有chrome对象' : '无chrome对象');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// const message = JSON.stringify(type, payload)
|
||||||
|
// console.log("开始给C#发送的信息",message)
|
||||||
|
|
||||||
|
let message = {
|
||||||
|
// 'Type':'',
|
||||||
|
'Payload':payload
|
||||||
|
}
|
||||||
|
console.log("传参",message,JSON.stringify(message))
|
||||||
|
window.chrome.webview.postMessage(message)
|
||||||
|
// if (window.chrome?.webview?.postMessage) {
|
||||||
|
// const message = JSON.stringify({ type, payload })
|
||||||
|
// console.log("开始给C#发送的信息",message)
|
||||||
|
// window.chrome.webview.postMessage(message)
|
||||||
|
// } else {
|
||||||
|
// console.warn('WebView2环境未就绪')
|
||||||
|
// // 可选:开发环境模拟
|
||||||
|
// if (process.env.NODE_ENV === 'development') {
|
||||||
|
// console.log('[模拟发送]', { type, payload })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.prototype.$sendToDotNet = sendToDotNet
|
||||||
|
new Vue({
|
||||||
|
render: h => h(App),
|
||||||
|
}).$mount('#app')
|
||||||
@@ -42,7 +42,8 @@ let funAll = [
|
|||||||
'handPrePath',
|
'handPrePath',
|
||||||
'handreContent',
|
'handreContent',
|
||||||
'SelectFilePathSend',
|
'SelectFilePathSend',
|
||||||
'handleMessageFromDotNet'
|
'handleMessageFromDotNet',
|
||||||
|
'handleMessageNotice',
|
||||||
];
|
];
|
||||||
for(let i =0;i<funAll.length;i++){
|
for(let i =0;i<funAll.length;i++){
|
||||||
window[funAll[i]] = function(msg) {//接收后端返回路径
|
window[funAll[i]] = function(msg) {//接收后端返回路径
|
||||||
@@ -57,7 +58,7 @@ for(let i =0;i<funAll.length;i++){
|
|||||||
window.FrontLoadProject = function(msg) {//接收后端返回路径
|
window.FrontLoadProject = function(msg) {//接收后端返回路径
|
||||||
Vue.prototype.$dotNetMessage = msg
|
Vue.prototype.$dotNetMessage = msg
|
||||||
// 可以触发全局事件
|
// 可以触发全局事件
|
||||||
console.log("MAIN FrontLoadProject 2222",msg)
|
// console.log("MAIN FrontLoadProject 2222",msg)
|
||||||
const event = new CustomEvent('FrontLoadProjectNew', { detail: msg })
|
const event = new CustomEvent('FrontLoadProjectNew', { detail: msg })
|
||||||
window.dispatchEvent(event)
|
window.dispatchEvent(event)
|
||||||
}
|
}
|
||||||
@@ -69,7 +70,7 @@ export const sendToDotNet = (type, payload,id,content,lagreVersion) => {//和后
|
|||||||
}
|
}
|
||||||
// const message = JSON.stringify(type, payload)
|
// const message = JSON.stringify(type, payload)
|
||||||
// console.log("开始给C#发送的信息",message)
|
// console.log("开始给C#发送的信息",message)
|
||||||
console.log('接收到的参数:', { type, payload, id, content,lagreVersion });
|
// console.log('接收到的参数:', { type, payload, id, content,lagreVersion });
|
||||||
let message = {
|
let message = {
|
||||||
// GetFilePath、SaveFile 取,存。
|
// GetFilePath、SaveFile 取,存。
|
||||||
// LoadDM 加载DM 富文本。
|
// LoadDM 加载DM 富文本。
|
||||||
|
|||||||
60
src/router/index.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// 引入依赖
|
||||||
|
import Vue from 'vue'
|
||||||
|
import VueRouter from 'vue-router'
|
||||||
|
|
||||||
|
// 确保组件路径和文件名正确
|
||||||
|
import Wang from '@/components/wang.vue'
|
||||||
|
import WangG2 from '@/components/wangG2.vue' // 注意组件文件名是否正确
|
||||||
|
// import WangG3 from '@/components/wangYZ3.vue' // 注意组件文件名是否正确
|
||||||
|
import WangG3 from '@/components/wangG3.vue' // 注意组件文件名是否正确
|
||||||
|
import WangG4 from '@/components/wangG4.vue' // 注意组件文件名是否正确
|
||||||
|
// import WangG3 from '@/components/wangYZ2.vue' // 注意组件文件名是否正确
|
||||||
|
|
||||||
|
// 注册路由
|
||||||
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
|
// 路由规则
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/wang'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/wang',
|
||||||
|
name: 'Wang',
|
||||||
|
component: Wang
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/wangG2', // 路径必须与router-link的to属性完全一致
|
||||||
|
name: 'WangG2',
|
||||||
|
component: WangG2 // 确保组件正确引入
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/WangG3',
|
||||||
|
name: 'WangG3',
|
||||||
|
component: WangG3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/WangG4',
|
||||||
|
name: 'WangG4',
|
||||||
|
component: WangG4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '*',
|
||||||
|
redirect: '/wang' // 未匹配的路径重定向到首页
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = new VueRouter({
|
||||||
|
// mode: 'history',
|
||||||
|
base: process.env.BASE_URL,
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
// 调试用:路由跳转时打印信息
|
||||||
|
router.afterEach((to, from) => {
|
||||||
|
console.log('路由跳转:', from.path, '->', to.path)
|
||||||
|
console.log('对应的组件:', to.matched[0]?.components.default?.name)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
@@ -2,9 +2,12 @@ const { defineConfig } = require('@vue/cli-service')
|
|||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
transpileDependencies: true
|
transpileDependencies: true
|
||||||
})
|
})
|
||||||
|
console.log('加载自定义配置...'); // 启动时会在终端输出这句话
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
devServer: {
|
devServer: {
|
||||||
|
port: 10022, // 自定义端口号(例如8088,可根据需要修改)
|
||||||
|
open: true, // 可选:启动后自动打开浏览器
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://youneed.top:10017',
|
target: 'http://youneed.top:10017',
|
||||||
|
|||||||