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

421 lines
10 KiB
Vue
Raw Normal View History

2025-07-25 13:28:47 +08:00
<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>
<button @click='test'>测试</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: {
test() {
},
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 = [
'image', // 图片
'video', // 视频
'head', // 标题
'bold', // 粗体
// 'fontSize',//字号
// 'fontName',//字体
'italic', // 斜体
'underline', // 下划线
'strikeThrough', // 删除线
// 'line',//行高
'lineHeight',//
// 'foreColor', // 文字颜色
// 'backColor', // 背景颜色
// 'link', // 链接
'list', // 列表
// 'todo',//
'justify', // 对齐方式
// 'quote', // 引用
// 'emoticon',//表情
'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" style='display:none;'>
<span class="video-delete" data-video-id="${videoId}">× 删除</span>
</div>
</div>
<p><br></p>
`
// 使用编辑器命令插入
this.editor.cmd.do('insertHTML', videoHtml)
// 添加删除事件监听
this.$nextTick(() => {
const btn = document.querySelector(`button[data-video-id="${videoId}"]`)
if (btn) {
btn.onclick = (e) => {
e.preventDefault()
this.deleteVideoById(videoId)
}
}
})
},
// 根据ID删除视频修正版
deleteVideoById(videoId) {
const container = document.querySelector(`.video-container[data-video-id="${videoId}"]`)
if (container) {
container.remove()
this.editor.txt.html(this.editor.txt.html()) // 刷新编辑器
}
},
bindDel(){
},
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;
}
/* 禁用菜单项样式 */
.disabled-menu-item {
opacity: 0.5 !important;
cursor: not-allowed !important;
pointer-events: none !important;
}
/* 如果需要工具提示也禁用 */
.w-e-toolbar .w-e-menu:nth-child(1),
.w-e-toolbar .w-e-menu:nth-child(2) {
/* background-color:#096; */
display: none !important;
}
</style>