修改
This commit is contained in:
81
package-lock.json
generated
81
package-lock.json
generated
@@ -13,6 +13,7 @@
|
|||||||
"@bytemd/vue": "^1.22.0",
|
"@bytemd/vue": "^1.22.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^1.0.2",
|
"@wangeditor/editor-for-vue": "^1.0.2",
|
||||||
|
"@xmldom/xmldom": "^0.9.8",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
@@ -23,7 +24,9 @@
|
|||||||
"vditor": "^3.11.1",
|
"vditor": "^3.11.1",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
"vue-quill-editor": "^3.0.6",
|
"vue-quill-editor": "^3.0.6",
|
||||||
"wangeditor": "^4.7.15"
|
"wangeditor": "^4.7.15",
|
||||||
|
"xml-js": "^1.6.11",
|
||||||
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.16",
|
"@babel/core": "^7.12.16",
|
||||||
@@ -3261,6 +3264,14 @@
|
|||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@xmldom/xmldom": {
|
||||||
|
"version": "0.9.8",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.9.8.tgz",
|
||||||
|
"integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xtuc/ieee754": {
|
"node_modules/@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
@@ -11232,6 +11243,11 @@
|
|||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
|
||||||
|
},
|
||||||
"node_modules/schema-utils": {
|
"node_modules/schema-utils": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
|
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
|
||||||
@@ -13458,6 +13474,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xml-js": {
|
||||||
|
"version": "1.6.11",
|
||||||
|
"resolved": "https://registry.npmmirror.com/xml-js/-/xml-js-1.6.11.tgz",
|
||||||
|
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"xml-js": "bin/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xml2js": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/xml2js/-/xml2js-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
||||||
@@ -16022,6 +16069,11 @@
|
|||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@xmldom/xmldom": {
|
||||||
|
"version": "0.9.8",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.9.8.tgz",
|
||||||
|
"integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A=="
|
||||||
|
},
|
||||||
"@xtuc/ieee754": {
|
"@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
@@ -21709,6 +21761,11 @@
|
|||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"sax": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
|
||||||
|
},
|
||||||
"schema-utils": {
|
"schema-utils": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
|
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
|
||||||
@@ -23373,6 +23430,28 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"xml-js": {
|
||||||
|
"version": "1.6.11",
|
||||||
|
"resolved": "https://registry.npmmirror.com/xml-js/-/xml-js-1.6.11.tgz",
|
||||||
|
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
|
||||||
|
"requires": {
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml2js": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/xml2js/-/xml2js-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||||
|
"requires": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
|
||||||
|
},
|
||||||
"y18n": {
|
"y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"@bytemd/vue": "^1.22.0",
|
"@bytemd/vue": "^1.22.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^1.0.2",
|
"@wangeditor/editor-for-vue": "^1.0.2",
|
||||||
|
"@xmldom/xmldom": "^0.9.8",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
@@ -24,7 +25,9 @@
|
|||||||
"vditor": "^3.11.1",
|
"vditor": "^3.11.1",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
"vue-quill-editor": "^3.0.6",
|
"vue-quill-editor": "^3.0.6",
|
||||||
"wangeditor": "^4.7.15"
|
"wangeditor": "^4.7.15",
|
||||||
|
"xml-js": "^1.6.11",
|
||||||
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.16",
|
"@babel/core": "^7.12.16",
|
||||||
|
|||||||
@@ -11,8 +11,9 @@
|
|||||||
// import HelloWorld from './components/quill.vue'
|
// import HelloWorld from './components/quill.vue'
|
||||||
// import HelloWorld from './components/tinyMCE.vue'
|
// import HelloWorld from './components/tinyMCE.vue'
|
||||||
// import HelloWorld from './components/vditor.vue'
|
// import HelloWorld from './components/vditor.vue'
|
||||||
// import HelloWorld from './components/wang.vue'
|
import HelloWorld from './components/wang.vue'
|
||||||
import HelloWorld from './components/XMLwang.vue'
|
// import HelloWorld from './components/XMLwangS1000D.vue'
|
||||||
|
// import HelloWorld from './components/XMLwang.vue'
|
||||||
// import HelloWorld from './components/vditor2.vue'
|
// import HelloWorld from './components/vditor2.vue'
|
||||||
// import HelloWorld from './components/ueditor.vue'
|
// import HelloWorld from './components/ueditor.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -1,209 +1,95 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="xml-importer">
|
<div>
|
||||||
<input type="file" @change="handleFileUpload" accept=".xml" />
|
<div ref="editor" style="text-align:left"></div>
|
||||||
<button @click="importToEditor" :disabled="!xmlData">导入到编辑器</button>
|
<button @click="exportToXml">导出为XML</button>
|
||||||
<div ref="editor"></div>
|
<input type="file" accept=".xml" @change="handleXmlUpload">
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import E from 'wangeditor'
|
import E from 'wangeditor';
|
||||||
|
import { js2xml, xml2js } from 'xml-js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'XmlImporter',
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
editor: null,
|
editor: null
|
||||||
xmlData: null,
|
};
|
||||||
parsedContent: ''
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.initEditor()
|
this.editor = new E(this.$refs.editor);
|
||||||
|
this.editor.create();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initEditor() {
|
convertHtmlToXml(htmlContent) {
|
||||||
this.editor = new E(this.$refs.editor)
|
const xmlObject = {
|
||||||
this.editor.create()
|
declaration: {
|
||||||
},
|
attributes: {
|
||||||
|
version: '1.0',
|
||||||
// 处理XML文件上传
|
encoding: 'UTF-8'
|
||||||
handleFileUpload(event) {
|
}
|
||||||
const file = event.target.files[0]
|
},
|
||||||
if (!file) return
|
elements: [{
|
||||||
|
type: 'element',
|
||||||
|
name: 'richText',
|
||||||
|
elements: [{
|
||||||
|
type: 'text',
|
||||||
|
text: htmlContent
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
const reader = new FileReader()
|
return js2xml(xmlObject, { compact: false, spaces: 4 });
|
||||||
reader.onload = (e) => {
|
|
||||||
this.xmlData = e.target.result
|
|
||||||
this.parseS1000DXml(this.xmlData)
|
|
||||||
}
|
|
||||||
reader.readAsText(file)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 解析S1000D XML
|
downloadXml(content, fileName = 'content.xml') {
|
||||||
parseS1000DXml(xmlString) {
|
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 {
|
try {
|
||||||
const parser = new DOMParser()
|
const result = xml2js(xmlString, { compact: false });
|
||||||
const xmlDoc = parser.parseFromString(xmlString, "text/xml")
|
const richTextElement = result.elements.find(el => el.name === 'richText');
|
||||||
|
if (richTextElement && richTextElement.elements && richTextElement.elements.length > 0) {
|
||||||
// 检查是否是有效的S1000D文档
|
return richTextElement.elements[0].text;
|
||||||
if (xmlDoc.getElementsByTagName('parsererror').length > 0) {
|
|
||||||
throw new Error('XML解析错误: ' + xmlDoc.getElementsByTagName('parsererror')[0].textContent)
|
|
||||||
}
|
}
|
||||||
|
return '';
|
||||||
// 提取内容部分
|
} catch (e) {
|
||||||
const contentElement = xmlDoc.querySelector('content description')
|
console.error('XML解析错误:', e);
|
||||||
if (!contentElement) {
|
return '';
|
||||||
throw new Error('未找到content/description元素')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将S1000D XML转换为HTML
|
|
||||||
this.parsedContent = this.s1000dToHtml(contentElement)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('XML解析失败:', error)
|
|
||||||
alert(`解析失败: ${error.message}`)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 将S1000D元素转换为HTML
|
handleXmlUpload(event) {
|
||||||
s1000dToHtml(s1000dElement) {
|
const file = event.target.files[0];
|
||||||
let html = ''
|
if (!file) return;
|
||||||
|
|
||||||
// 递归处理子节点
|
const reader = new FileReader();
|
||||||
const processNode = (node) => {
|
reader.onload = (e) => {
|
||||||
if (node.nodeType === Node.TEXT_NODE) {
|
const xmlString = e.target.result;
|
||||||
return node.textContent
|
const html = this.parseXmlToHtml(xmlString);
|
||||||
|
if (html) {
|
||||||
|
this.editor.txt.html(html);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
if (node.nodeType !== Node.ELEMENT_NODE) return ''
|
reader.readAsText(file);
|
||||||
|
|
||||||
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.escapeHtml(attr.value)}"`)
|
|
||||||
.join('')
|
|
||||||
|
|
||||||
// 将S1000D标签映射回HTML
|
|
||||||
switch(tag) {
|
|
||||||
case 'para': {
|
|
||||||
return `<p${attrs}>${children}</p>`
|
|
||||||
}
|
|
||||||
case 'emphasis': {
|
|
||||||
const role = node.getAttribute('role') || 'bold'
|
|
||||||
switch(role) {
|
|
||||||
case 'bold': return `<strong${attrs}>${children}</strong>`
|
|
||||||
case 'italic': return `<em${attrs}>${children}</em>`
|
|
||||||
case 'underline': return `<u${attrs}>${children}</u>`
|
|
||||||
default: return `<span class="emphasis-${role}"${attrs}>${children}</span>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'orderedlist': {
|
|
||||||
return `<ol${attrs}>${children}</ol>`
|
|
||||||
}
|
|
||||||
case 'itemizedlist': {
|
|
||||||
return `<ul${attrs}>${children}</ul>`
|
|
||||||
}
|
|
||||||
case 'listitem': {
|
|
||||||
return `<li${attrs}>${children}</li>`
|
|
||||||
}
|
|
||||||
case 'figure': {
|
|
||||||
const graphic = node.querySelector('graphic')
|
|
||||||
if (graphic) {
|
|
||||||
const imgAttrs = Array.from(graphic.attributes)
|
|
||||||
.filter(attr => attr.name !== 'infoentityid')
|
|
||||||
.map(attr => ` ${attr.name}="${this.escapeHtml(attr.value)}"`)
|
|
||||||
.join('')
|
|
||||||
return `<div class="figure"><img${imgAttrs}>${children}</div>`
|
|
||||||
}
|
|
||||||
return children
|
|
||||||
}
|
|
||||||
case 'table': {
|
|
||||||
return `<table${attrs}>${children}</table>`
|
|
||||||
}
|
|
||||||
case 'row': {
|
|
||||||
return `<tr${attrs}>${children}</tr>`
|
|
||||||
}
|
|
||||||
case 'entry': {
|
|
||||||
const isHeader = node.getAttribute('thead') === 'yes'
|
|
||||||
return isHeader
|
|
||||||
? `<th${attrs}>${children}</th>`
|
|
||||||
: `<td${attrs}>${children}</td>`
|
|
||||||
}
|
|
||||||
case 'title': {
|
|
||||||
const level = node.getAttribute('level') || '1'
|
|
||||||
return `<h${level}${attrs}>${children}</h${level}>`
|
|
||||||
}
|
|
||||||
case 'xref': {
|
|
||||||
const href = node.getAttribute('xrefid') || '#'
|
|
||||||
return `<a href="${href}"${attrs}>${children}</a>`
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
// 未知标签作为div处理
|
|
||||||
return `<div class="s1000d-${tag}"${attrs}>${children}</div>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理所有子节点
|
|
||||||
html = Array.from(s1000dElement.childNodes).map(processNode).join('')
|
|
||||||
|
|
||||||
return html
|
|
||||||
},
|
|
||||||
|
|
||||||
// HTML特殊字符转义
|
|
||||||
escapeHtml(text) {
|
|
||||||
return text
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, ''')
|
|
||||||
},
|
|
||||||
|
|
||||||
// 将解析后的内容导入编辑器
|
|
||||||
importToEditor() {
|
|
||||||
if (this.parsedContent && this.editor) {
|
|
||||||
this.editor.txt.html(this.parsedContent)
|
|
||||||
alert('内容已成功导入编辑器')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
this.editor.destroy()
|
this.editor.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.xml-importer {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.xml-importer input[type="file"] {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.xml-importer button {
|
|
||||||
padding: 8px 16px;
|
|
||||||
background: #42b983;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.xml-importer button:disabled {
|
|
||||||
background: #cccccc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.figure {
|
|
||||||
margin: 15px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -5,11 +5,36 @@
|
|||||||
<button @click="insertVideo">上传视频</button>
|
<button @click="insertVideo">上传视频</button>
|
||||||
<button @click="loadAllContent">加载所有内容</button>
|
<button @click="loadAllContent">加载所有内容</button>
|
||||||
<button @click="generateTOC">生成目录</button>
|
<button @click="generateTOC">生成目录</button>
|
||||||
<button @click="deleteAllVideos">删除所有视频</button>
|
<input type="color">
|
||||||
|
<!-- <button @click="deleteAllVideos">删除所有视频</button> -->
|
||||||
<button @click="loadFwb">模拟加载</button>
|
<button @click="loadFwb">模拟加载</button>
|
||||||
<button @click='test'>测试</button>
|
<!-- <button @click='test'>测试</button> -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="s1000d-editor-container">
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<div class="toolbar">
|
||||||
|
<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"></div>
|
<div id="main" class="content-preview"></div>
|
||||||
<div id="nav" class="toc-container"></div>
|
<div id="nav" class="toc-container"></div>
|
||||||
<div id="editor" ref="editor"></div>
|
<div id="editor" ref="editor"></div>
|
||||||
@@ -23,6 +48,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import WangEditor from 'wangeditor'
|
import WangEditor from 'wangeditor'
|
||||||
|
// import '@yaireo/colorpicker/dist/colorpicker.min.css';
|
||||||
|
// import '@yaireo/colorpicker'
|
||||||
// import axios from 'axios';
|
// import axios from 'axios';
|
||||||
// window.handleMessageFromDotNet = function(msg) {
|
// window.handleMessageFromDotNet = function(msg) {
|
||||||
// alert("Received message from C#: " + msg);
|
// alert("Received message from C#: " + msg);
|
||||||
@@ -40,6 +67,47 @@ export default {
|
|||||||
editorContent: '',
|
editorContent: '',
|
||||||
editorContent2:'',
|
editorContent2:'',
|
||||||
hasVideoSelected: false,
|
hasVideoSelected: false,
|
||||||
|
xmlPreview: '',
|
||||||
|
showValidationModal: false,
|
||||||
|
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: {
|
||||||
|
issueNumber: '001',
|
||||||
|
inWork: '01',
|
||||||
|
issueDate: new Date().toISOString().split('T')[0]
|
||||||
|
},
|
||||||
|
language: 'zh-CN'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
formattedXmlPreview() {
|
||||||
|
if (!this.xmlPreview) return '暂无XML预览';
|
||||||
|
|
||||||
|
// 简单格式化XML显示
|
||||||
|
return this.xmlPreview
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/\n/g, '<br>')
|
||||||
|
.replace(/\s/g, ' ');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -82,6 +150,14 @@ console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|||||||
let dm = this.editor.config.menus;
|
let dm = this.editor.config.menus;
|
||||||
console.log("默认菜单",dm)
|
console.log("默认菜单",dm)
|
||||||
|
|
||||||
|
|
||||||
|
// 启用颜色选择功能
|
||||||
|
this.editor.config.colors = [
|
||||||
|
'#000000', '#ffffff', '#eeeef1',
|
||||||
|
'#ff0000', '#ff5e5e', '#ffbbbb',
|
||||||
|
'#0033ff', '#0055ff', '#3d7eff',
|
||||||
|
'red',"#096","#9cf"
|
||||||
|
]
|
||||||
// 完全自定义菜单
|
// 完全自定义菜单
|
||||||
this.editor.config.menus = [
|
this.editor.config.menus = [
|
||||||
'image', // 图片
|
'image', // 图片
|
||||||
@@ -95,8 +171,8 @@ console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|||||||
'strikeThrough', // 删除线
|
'strikeThrough', // 删除线
|
||||||
// 'line',//行高
|
// 'line',//行高
|
||||||
'lineHeight',//
|
'lineHeight',//
|
||||||
// 'foreColor', // 文字颜色
|
'foreColor', // 文字颜色
|
||||||
// 'backColor', // 背景颜色
|
'backColor', // 背景颜色
|
||||||
// 'link', // 链接
|
// 'link', // 链接
|
||||||
'list', // 列表
|
'list', // 列表
|
||||||
// 'todo',//
|
// 'todo',//
|
||||||
@@ -107,7 +183,7 @@ console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|||||||
// 'code', // 代码
|
// 'code', // 代码
|
||||||
'splitLine',//分割线
|
'splitLine',//分割线
|
||||||
'undo', // 撤销
|
'undo', // 撤销
|
||||||
'redo' // 重做
|
'redo', // 重做
|
||||||
]
|
]
|
||||||
|
|
||||||
// 创建编辑器
|
// 创建编辑器
|
||||||
@@ -135,7 +211,7 @@ console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|||||||
this.editor.cmd.do('insertHTML', `<img src="${imgUrl}" style="max-width: 100%;" alt="图片">`)
|
this.editor.cmd.do('insertHTML', `<img src="${imgUrl}" style="max-width: 100%;" alt="图片">`)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 插入视频(可靠版本)
|
// 插入视频
|
||||||
insertVideo() {
|
insertVideo() {
|
||||||
const videoUrl = 'http://youneed.top:10017/uploads/video.mp4'
|
const videoUrl = 'http://youneed.top:10017/uploads/video.mp4'
|
||||||
const videoId = `video-${Date.now()}`
|
const videoId = `video-${Date.now()}`
|
||||||
@@ -183,16 +259,12 @@ console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
bindDel(){
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
deleteAllVideos() {
|
deleteAllVideos() {
|
||||||
const videoWrappers = document.querySelectorAll('.video-wrapper')
|
const videoWrappers = document.querySelectorAll('.video-wrapper')
|
||||||
if (videoWrappers.length === 0) {
|
if (videoWrappers.length === 0) {
|
||||||
alert('没有找到可删除的视频')
|
console.log('没有找到可删除的视频')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +281,7 @@ console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 修正:使用正确的方式通知内容变更
|
// 使用正确的方式通知内容变更
|
||||||
if (this.editor.txt) {
|
if (this.editor.txt) {
|
||||||
this.editor.txt.html(this.editor.txt.html()) // 强制更新编辑器内容
|
this.editor.txt.html(this.editor.txt.html()) // 强制更新编辑器内容
|
||||||
}
|
}
|
||||||
@@ -251,7 +323,7 @@ console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|||||||
|
|
||||||
const mainContainer = document.getElementById('main')
|
const mainContainer = document.getElementById('main')
|
||||||
const headings = mainContainer.querySelectorAll('h1, h2, h3, h4, h5, h6')
|
const headings = mainContainer.querySelectorAll('h1, h2, h3, h4, h5, h6')
|
||||||
|
console.log("headings.length",headings.length)
|
||||||
if (headings.length === 0) {
|
if (headings.length === 0) {
|
||||||
navContainer.innerHTML = '<p>没有找到标题元素,无法生成目录。</p>'
|
navContainer.innerHTML = '<p>没有找到标题元素,无法生成目录。</p>'
|
||||||
return
|
return
|
||||||
@@ -299,6 +371,182 @@ console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|||||||
tocContainer.appendChild(tocTitle)
|
tocContainer.appendChild(tocTitle)
|
||||||
tocContainer.appendChild(tocList)
|
tocContainer.appendChild(tocList)
|
||||||
navContainer.appendChild(tocContainer)
|
navContainer.appendChild(tocContainer)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 生成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 '<';
|
||||||
|
case '>': return '>';
|
||||||
|
case '&': return '&';
|
||||||
|
case '\'': return ''';
|
||||||
|
case '"': return '"';
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (html) {
|
||||||
|
this.editor.txt.html(html);
|
||||||
|
this.updateXmlPreview(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置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(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/'/g, "'")
|
||||||
|
.replace(/"/g, '"');
|
||||||
|
|
||||||
|
// 移除可能的多余空格和换行
|
||||||
|
content = content.trim();
|
||||||
|
|
||||||
|
return content;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('S1000D导入错误:', e);
|
||||||
|
alert(`导入S1000D XML失败: ${e.message}`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 验证当前XML
|
||||||
|
validateCurrentXml() {
|
||||||
|
const validationResult = this.validateS1000DXml(this.xmlPreview);
|
||||||
|
this.validationResult = validationResult;
|
||||||
|
this.showValidationModal = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 基本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}`
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
@@ -357,6 +605,7 @@ console.log('webview对象是否存在:', !!window.chrome?.webview);
|
|||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
#main th,
|
#main th,
|
||||||
#main td {
|
#main td {
|
||||||
@@ -418,4 +667,96 @@ video {
|
|||||||
/* background-color:#096; */
|
/* background-color:#096; */
|
||||||
display: none !important;
|
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;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user