m2pool_web_frontend/mining-pool/src/views/documentManagement/modifyDocument/index.vue

918 lines
21 KiB
Vue
Raw Normal View History

2025-07-18 08:22:28 +00:00
<template>
<div class="editor-page" v-loading="addLoading">
<!-- 页面头部 -->
<div class="page-header">
<div class="header-left">
<div class="title-section">
<i class="el-icon-edit-outline title-icon"></i>
<h1 class="page-title">wangeditor 富文本文档编辑器</h1>
</div>
<p class="page-description">
使用富文本编辑器创建和管理文档支持文本格式化链接列表等功能
</p>
</div>
<div class="header-actions">
<el-button type="info" icon="el-icon-view" @click="handlePreview">
预览文档
</el-button>
<el-button type="success" icon="el-icon-document" @click="handleSave">
保存文档
</el-button>
<el-button
type="warning"
icon="el-icon-upload2"
@click="handelAddDocument"
>
修改文档
</el-button>
</div>
</div>
<!-- 文档配置区域 -->
<div class="config-section">
<div class="section-header">
<i class="el-icon-setting"></i>
<span>文档配置</span>
</div>
<div class="config-content">
<div class="config-row">
<div class="config-item">
<label>导航标题</label>
<el-input
v-model="addParams.title"
placeholder="请输入文档标题"
@input="handleAutoSave"
size="medium"
/>
2025-07-18 08:22:28 +00:00
</div>
<div class="config-item">
<label class="required">文档类型</label>
<el-cascader
:options="TypeList"
v-model="typeArray"
@change="handleAutoSaveType(typeArray)"
clearable
placeholder="请选择文档类型"
:props="{
label: 'label',
value: 'value',
children: 'children',
}"
/>
2025-07-18 08:22:28 +00:00
</div>
</div>
2025-07-18 08:22:28 +00:00
</div>
<div class="config-content">
<div class="config-row">
<div class="config-item">
<label>标题图片地址</label>
<el-input
v-model="addParams.titleUrl"
placeholder="请输入图片地址"
@input="handleAutoSave"
size="medium"
/>
2025-07-18 08:22:28 +00:00
</div>
<div class="config-item">
<label>文章访问地址</label>
<el-input
v-model="addParams.articleUrl"
placeholder="请输入访问地址"
@input="handleAutoSave"
size="medium"
/>
2025-07-18 08:22:28 +00:00
</div>
</div>
2025-07-18 08:22:28 +00:00
</div>
</div>
2025-07-18 08:22:28 +00:00
<!-- 编辑器区域 -->
<div class="editor-section">
<div class="section-header">
<i class="el-icon-edit-outline"></i>
<span>富文本编辑器</span>
</div>
<div class="editor-container">
<Toolbar
2025-07-18 08:22:28 +00:00
:editor="editor"
:defaultConfig="toolbarConfig"
:mode="mode"
style="border-bottom: 1px solid #ccc"
/>
<Editor
style="height: 600px; overflow-y: hidden"
v-model="addParams.content"
2025-07-18 08:22:28 +00:00
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="onCreated"
@onChange="handleEditorChange"
/>
</div>
</div>
<!-- 预览文档对话框 -->
<el-dialog
title="文档预览"
:visible.sync="previewVisible"
width="80%"
:before-close="handlePreviewClose"
class="preview-dialog wangeditor-preview"
>
<div class="preview-content">
<div v-html="addParams.content" class="dynamic-content" ></div>
<div v-if="!addParams.content" class="preview-empty">
<i class="el-icon-document"></i>
<p>暂无内容</p>
</div>
2025-07-18 08:22:28 +00:00
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="previewVisible = false">关闭</el-button>
<el-button type="primary" @click="handlePrintPreview"
>打印预览</el-button
>
</span>
</el-dialog>
2025-07-18 08:22:28 +00:00
</div>
</template>
<script>
import Vue from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import { updateDocument } from "../../../api/documentManagement";
import Index from "./index";
2025-07-18 08:22:28 +00:00
export default Vue.extend({
components: { Editor, Toolbar },
mixins: [Index],
data() {
return {
editor: null,
html: "<p>hello</p>",
2025-07-18 08:22:28 +00:00
toolbarConfig: {
excludeKeys: [],
insertKeys: {
index: 0,
keys: [
"bold",
"italic",
"underline",
"through",
"code",
"sub",
"sup",
"clearStyle",
],
},
2025-07-18 08:22:28 +00:00
},
editorConfig: {
placeholder: "请在此输入文档内容...",
MENU_CONF: {},
2025-07-18 08:22:28 +00:00
},
mode: "default",
addParams: {
title: "",
content: "",
type: "",
lang: "",
titleUrl: "",
articleUrl: "",
childType:""
2025-07-18 08:22:28 +00:00
},
lastSaveTime: "",
2025-07-18 08:22:28 +00:00
autoSaveTimer: null,
isSaving: false, // 新增:控制自动保存提示的显示
previewVisible: false, // 新增:控制预览对话框的显示
currentTime: "", // 新增:记录预览时间
TypeList: [
// {
// value: "1",
// label: "服务条款",
// },
// {
// value: "2",
// label: "api文档",
// },
// {
// value: "3",
// label: "挖矿教程",
// },
// {
// value: "0",
// label: "其他",
// },
],
addLoading: false,
typeArray: [],
};
},
mounted() {
2025-07-18 08:22:28 +00:00
try {
this.TypeList = JSON.parse(localStorage.getItem("TypeList"));
this.TypeList = this.TypeList.map((item) => ({ // 翻译label
...item,
label: this.$t(item.label),
children: item.children
? item.children.map((child) => ({
...child,
label: this.$t(child.label),
}))
: undefined,
}));
} catch (error) {
console.log(error);
2025-07-18 08:22:28 +00:00
}
// 先恢复数据
this.loadFromLocalStorage();
// 添加页面卸载前的保存保护
window.addEventListener("beforeunload", this.handleBeforeUnload);
// 延迟设置默认内容,避免覆盖恢复的数据
setTimeout(() => {
if (!this.addParams.content) {
this.html = "<p>模拟 Ajax 异步设置内容 HTML</p>";
}
}, 1500);
2025-07-18 08:22:28 +00:00
},
methods: {
async fetchUpdateDocument(params) {
this.setLoading("addLoading", true);
const res = await updateDocument(params);
if (res && res.code == 200) {
this.$message.success(this.$t("backendSystem.updateSuccess"));
this.$router.push({ path: `/${this.$i18n.locale}/documentManagement` });
}
this.setLoading("addLoading", false);
},
2025-07-18 08:22:28 +00:00
onCreated(editor) {
this.editor = Object.seal(editor);
2025-07-18 08:22:28 +00:00
// 编辑器创建后,如果有恢复的内容,设置到编辑器中
if (this.addParams.content) {
this.$nextTick(() => {
editor.setHtml(this.addParams.content);
});
}
2025-07-18 08:22:28 +00:00
console.log(editor.getAllMenuKeys(),"Editor.getAllMenuKe就啊哈啊ys()");
},
2025-07-18 08:22:28 +00:00
handelAddDocument() {
if (!this.addParams.type) {
this.$message({
message: "请填写文档类型",
type: "warning",
duration: 4000,
showClose: true,
});
return;
}
2025-07-18 08:22:28 +00:00
// 确保获取最新的编辑器内容
if (this.editor) {
this.addParams.content = this.editor.getHtml();
}
this.fetchUpdateDocument(this.addParams);
},
handlePreview() {
this.previewVisible = true;
this.currentTime = new Date().toLocaleString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
},
handlePreviewClose() {
this.previewVisible = false;
},
handlePrintPreview() {
const printWindow = window.open("", "_blank");
if (printWindow) {
printWindow.document.write(`
2025-07-18 08:22:28 +00:00
<html>
<head>
<title>${this.addParams.title || "文档预览"}</title>
2025-07-18 08:22:28 +00:00
<style>
body {
font-family: 'Arial', sans-serif;
line-height: 1.6;
color: #333;
margin: 20px;
}
.preview-empty {
text-align: center;
padding: 50px 0;
color: #95a5a6;
}
.preview-empty i {
font-size: 40px;
margin-bottom: 15px;
}
</style>
</head>
<body>
${
this.addParams.content ||
'<div class="preview-empty"><i>📄</i><p>暂无内容</p></div>'
}
2025-07-18 08:22:28 +00:00
</body>
</html>
`);
printWindow.document.close();
printWindow.print();
printWindow.close();
}
},
handleSave() {
this.manualSave();
this.$message({
message: "内容已保存到本地,请尽快发布,关闭页面可能丢失内容",
type: "success",
duration: 4000,
showClose: true,
});
},
handleEditorChange() {
this.handleAutoSave();
},
handleAutoSaveType(type) {
try {
let item = this.TypeList.find((item) => item.value === type[0]);
if (item.articleUrl) {
this.addParams.articleUrl = item.articleUrl;
} else {
this.addParams.articleUrl = "";
}
this.addParams.type = type[0];
this.addParams.childType= type[1];
} catch (error) {
console.log(error, "error");
}
2025-07-18 08:22:28 +00:00
// 显示正在保存状态
this.isSaving = true;
2025-07-18 08:22:28 +00:00
if (this.autoSaveTimer) {
clearTimeout(this.autoSaveTimer);
}
2025-07-18 08:22:28 +00:00
this.autoSaveTimer = setTimeout(() => {
this.saveToLocalStorage();
}, 1500); // 减少到1.5秒,提高响应速度
},
handleAutoSave() {
// 显示正在保存状态
this.isSaving = true;
if (this.autoSaveTimer) {
clearTimeout(this.autoSaveTimer);
}
this.autoSaveTimer = setTimeout(() => {
this.saveToLocalStorage();
}, 1500); // 减少到1.5秒,提高响应速度
if (this.addParams.type == "2") {
//常见问题
this.addParams.articleUrl = `commonProblem`;
} else if (this.addParams.type == "3") {
//公告中心
this.addParams.articleUrl = `announcementDetails`;
}
},
saveToLocalStorage() {
try {
// 确保获取最新的编辑器内容
if (this.editor) {
this.addParams.content = this.editor.getHtml();
}
const saveData = {
title: this.addParams.title,
content: this.addParams.content,
type: this.addParams.type,
imageUrl: this.addParams.imageUrl,
timestamp: new Date().toLocaleString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}),
lastModified: Date.now(),
};
localStorage.setItem("editor_draft", JSON.stringify(saveData));
this.lastSaveTime = saveData.timestamp;
this.isSaving = false;
} catch (error) {
console.error("自动保存失败:", error);
this.isSaving = false;
}
},
loadFromLocalStorage() {
try {
const savedData = localStorage.getItem("editor_draft");
if (savedData) {
const data = JSON.parse(savedData);
// 检查数据是否过期7天
const isExpired =
Date.now() - (data.lastModified || 0) > 7 * 24 * 60 * 60 * 1000;
if (isExpired) {
console.log("本地数据已过期,清除缓存");
localStorage.removeItem("editor_draft");
return;
2025-07-18 08:22:28 +00:00
}
// 使用 $nextTick 确保 DOM 更新后再设置内容
this.$nextTick(() => {
this.addParams.title = data.title || "";
this.addParams.content = data.content || "";
this.addParams.type = data.type || "1";
this.addParams.imageUrl = data.imageUrl || "";
this.lastSaveTime = data.timestamp || "";
// 如果编辑器已经创建,直接设置内容
if (this.editor) {
this.editor.setHtml(data.content || "");
}
});
}
} catch (error) {
console.error("恢复数据失败:", error);
}
},
clearDraft() {
localStorage.removeItem("editor_draft");
this.lastSaveTime = "";
this.isSaving = false;
},
// 新增:手动保存方法
manualSave() {
2025-07-18 08:22:28 +00:00
// 确保获取最新的编辑器内容
if (this.editor) {
this.addParams.content = this.editor.getHtml();
2025-07-18 08:22:28 +00:00
}
this.saveToLocalStorage();
},
// 页面卸载前处理
handleBeforeUnload(event) {
// 自动保存,不提示用户
if (this.addParams.title || this.addParams.content) {
// 确保获取最新的编辑器内容
if (this.editor) {
this.addParams.content = this.editor.getHtml();
}
this.saveToLocalStorage();
2025-07-18 08:22:28 +00:00
}
},
// 新增:获取文档类型名称
getDocumentTypeName(type) {
switch (type) {
case "1":
return "服务条款";
case "2":
return "api文档";
case "3":
return "挖矿教程";
case "0":
return "其他";
default:
return "未知类型";
}
},
2025-07-18 08:22:28 +00:00
},
beforeDestroy() {
// 确保获取最新的编辑器内容
if (this.editor) {
this.addParams.content = this.editor.getHtml();
}
this.saveToLocalStorage();
// 移除事件监听器
window.removeEventListener("beforeunload", this.handleBeforeUnload);
if (this.autoSaveTimer) {
clearTimeout(this.autoSaveTimer);
}
const editor = this.editor;
if (editor == null) return;
editor.destroy();
},
2025-07-18 08:22:28 +00:00
beforeRouteLeave(to, from, next) {
// 确保获取最新的编辑器内容
if (this.editor) {
this.addParams.content = this.editor.getHtml();
}
this.saveToLocalStorage();
next();
},
});
2025-07-18 08:22:28 +00:00
</script>
<style lang="scss" scoped>
2025-07-18 08:22:28 +00:00
/* 页面整体样式 */
.editor-page {
width: 100%;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
min-height: 60vh;
overflow-y: auto;
}
/* 页面头部样式 */
.page-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
border-radius: 12px;
margin-bottom: 24px;
display: flex;
justify-content: space-between;
align-items: flex-start;
box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
}
.header-left {
flex: 1;
}
.title-section {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.title-icon {
font-size: 28px;
margin-right: 12px;
color: #ffd700;
}
.page-title {
font-size: 28px;
font-weight: 700;
margin: 0;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
2025-07-18 08:22:28 +00:00
}
.page-description {
font-size: 16px;
opacity: 0.9;
margin: 0;
line-height: 1.6;
}
.header-actions {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.header-actions .el-button {
border-radius: 8px;
font-weight: 500;
padding: 12px 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
2025-07-18 08:22:28 +00:00
transition: all 0.3s ease;
}
.header-actions .el-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
2025-07-18 08:22:28 +00:00
}
/* 配置区域样式 */
.config-section {
background: white;
border-radius: 12px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
2025-07-18 08:22:28 +00:00
border: 1px solid #e9ecef;
}
.section-header {
display: flex;
align-items: center;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 2px solid #f8f9fa;
color: #2c3e50;
font-weight: 600;
font-size: 18px;
}
.section-header i {
margin-right: 10px;
color: #667eea;
font-size: 20px;
}
.config-content {
display: flex;
flex-direction: column;
gap: 20px;
margin-top: 18px;
2025-07-18 08:22:28 +00:00
}
.config-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
.config-item {
display: flex;
flex-direction: column;
}
.config-item label {
margin-bottom: 8px;
font-weight: 600;
color: #495057;
font-size: 14px;
}
.required::after {
content: "*";
2025-07-18 08:22:28 +00:00
color: #e74c3c;
margin-left: 4px;
font-weight: bold;
}
/* 编辑器区域样式 */
.editor-section {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
2025-07-18 08:22:28 +00:00
border: 1px solid #e9ecef;
}
.editor-container {
border: 1px solid #dcdfe6;
border-radius: 8px;
overflow: hidden;
background: white;
min-height: 650px;
}
/* 设置编辑器高度 */
.editor-container .w-e-text-container {
height: 600px !important;
min-height: 600px !important;
}
/* 编辑器内容区域样式 */
.editor-container .w-e-text {
height: 600px !important;
min-height: 600px !important;
}
/* 保存状态样式 */
.save-status {
position: fixed;
bottom: 20px;
right: 20px;
background: #67c23a;
color: white;
padding: 12px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3);
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
z-index: 1000;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
2025-07-18 08:22:28 +00:00
}
to {
transform: translateX(0);
opacity: 1;
2025-07-18 08:22:28 +00:00
}
}
/* 预览对话框样式 */
.preview-dialog .el-dialog__body {
padding: 20px;
max-height: 70vh;
overflow-y: auto;
}
.preview-dialog .el-dialog__header {
background-color: #f8f9fa;
border-bottom: 1px solid #ebeef5;
}
.preview-dialog .el-dialog__header .el-dialog__title {
color: #333;
font-weight: 600;
}
.preview-dialog .el-dialog__footer {
background-color: #f8f9fa;
border-top: 1px solid #ebeef5;
padding: 15px 20px;
}
.preview-dialog .el-dialog__footer .dialog-footer {
text-align: right;
}
.preview-dialog .el-dialog__footer .dialog-footer .el-button {
border-radius: 6px;
padding: 8px 15px;
font-weight: 500;
}
.preview-dialog .el-dialog__footer .dialog-footer .el-button:first-child {
margin-right: 10px;
}
.preview-dialog .el-dialog__footer .dialog-footer .el-button:last-child {
background-color: #667eea;
border-color: #667eea;
}
.preview-dialog .el-dialog__footer .dialog-footer .el-button:last-child:hover {
background-color: #5a67d8;
border-color: #5a67d8;
}
/* 预览内容样式 - 简化版本 */
.preview-content {
background: white;
padding: 20px;
}
.preview-empty {
text-align: center;
padding: 50px 20px;
color: #95a5a6;
}
.preview-empty i {
font-size: 48px;
margin-bottom: 15px;
display: block;
}
.preview-empty p {
margin: 0;
font-size: 16px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.editor-page {
padding: 10px;
2025-07-18 08:22:28 +00:00
}
2025-07-18 08:22:28 +00:00
.page-header {
flex-direction: column;
gap: 20px;
padding: 20px;
2025-07-18 08:22:28 +00:00
}
2025-07-18 08:22:28 +00:00
.header-actions {
width: 100%;
justify-content: center;
2025-07-18 08:22:28 +00:00
}
2025-07-18 08:22:28 +00:00
.config-row {
grid-template-columns: 1fr;
gap: 16px;
2025-07-18 08:22:28 +00:00
}
2025-07-18 08:22:28 +00:00
.page-title {
font-size: 24px;
2025-07-18 08:22:28 +00:00
}
2025-07-18 08:22:28 +00:00
.save-status {
bottom: 10px;
right: 10px;
left: 10px;
text-align: center;
justify-content: center;
2025-07-18 08:22:28 +00:00
}
}
/* Element UI 组件样式优化 */
.el-input,
.el-select {
2025-07-18 08:22:28 +00:00
border-radius: 8px;
}
.el-input__inner,
.el-select .el-input__inner {
2025-07-18 08:22:28 +00:00
border-radius: 8px;
border: 1px solid #dcdfe6;
transition: all 0.3s ease;
}
.el-input__inner:focus,
.el-select .el-input__inner:focus {
2025-07-18 08:22:28 +00:00
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
}
.el-tag {
border-radius: 6px;
font-weight: 500;
}
/* -------富文本样式-------------- */
.dynamic-content {
width: 100%;
margin: 0 auto;
:deep(table) {
border-collapse: collapse;
width: 100%;
margin: 16px 0;
}
:deep(th), :deep(td) {
border: 1px solid #d1d5db;
padding: 8px 12px;
text-align: left;
}
:deep(th) {
background: #f3f4f6;
font-weight: bold;
}
:deep(strong), :deep(b) {
font-weight: bold !important;
color: inherit !important;
font-style: normal !important;
}
:deep(em), :deep(i) {
font-style: italic !important;
color: inherit !important;
}
:deep(a) {
color: #007bff !important;
text-decoration: none !important;
&:hover {
text-decoration: underline !important;
}
}
}
2025-07-18 08:22:28 +00:00
</style>
<style src="@wangeditor/editor/dist/css/style.css"></style>