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

1045 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="editor-page" v-loading="addLoading" :class="{ 'no-scroll': addLoading }">
<h2 style="margin-bottom: 20px;">{{ $t('backendSystem.modifyDocument') || '修改文档' }}</h2>
<!-- 页面头部 -->
<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">{{ $t('backendSystem.wangeditor') || 'wangeditor 富文本文档编辑器' }}</h1>
</div>
</div>
<div class="header-actions">
<el-button
type="primary"
icon="el-icon-link"
@click="handleInsertAnchor"
>
{{ $t('backendSystem.insertAnchor') || '插入锚点' }}
</el-button>
<el-button type="info" icon="el-icon-view" @click="handlePreview">
{{ $t('backendSystem.previewDocument') || '预览文档' }}
</el-button>
<el-button type="success" icon="el-icon-document" @click="handleSave">
{{ $t('backendSystem.saveDocument') || '保存文档' }}
</el-button>
<el-button
type="warning"
icon="el-icon-upload2"
@click="handelAddDocument"
>
{{ $t('backendSystem.modifyDocument') || '修改文档' }}
</el-button>
</div>
</div>
<!-- 文档配置区域 -->
<div class="config-section">
<div class="section-header">
<i class="el-icon-setting"></i>
<span>{{ $t('backendSystem.documentConfiguration') || '文档配置' }}</span>
</div>
<div class="config-content">
<div class="config-row">
<div class="config-item">
<label>{{ $t('backendSystem.navigationTitle') || '导航标题' }}</label>
<el-input
v-model="addParams.title"
:placeholder="$t('backendSystem.pleaseInputDocumentTitle') || '请输入文档标题'"
@input="handleAutoSave"
size="medium"
/>
</div>
<div class="config-item">
<label class="required">{{ $t('backendSystem.documentType') || '文档类型' }}</label>
<el-cascader
:options="TypeList"
v-model="typeArray"
@change="handleAutoSaveType(typeArray)"
clearable
:placeholder="$t('backendSystem.pleaseSelectDocumentType') || '请选择文档类型'"
:props="{
label: 'label',
value: 'value',
children: 'children',
}"
/>
</div>
</div>
</div>
<div class="config-content">
<div class="config-row">
<div class="config-item">
<label>{{ $t('backendSystem.titleImageAddress') || '标题图片地址' }}</label>
<el-input
v-model="addParams.titleUrl"
:placeholder="$t('backendSystem.pleaseInputImageAddress') || '请输入图片地址'"
@input="handleAutoSave"
size="medium"
/>
</div>
<div class="config-item">
<label>{{ $t('backendSystem.articleAccessAddress') || '文章访问地址' }}</label>
<el-input
v-model="addParams.articleUrl"
:placeholder="$t('backendSystem.pleaseInputAccessAddress') || '请输入访问地址'"
@input="handleAutoSave"
size="medium"
/>
</div>
</div>
</div>
<div class="config-content">
<div class="config-row">
<div class="config-item">
<label >{{
$t("backendSystem.coin2") || "币种"
}}</label>
<el-select
class="input"
size="middle"
ref="screen"
@change="changeScreen(screenCurrency)"
v-model="screenCurrency"
:placeholder="$t(`backendSystem.selectCurrency`)"
>
<el-option
v-for="item in currencyList"
:key="item.value"
:label="item.label"
:value="item.value"
>
<div style="display: flex; align-items: center">
<img :src="item.imgUrl" style="float: left; width: 20px" />
<span style="float: left; margin-left: 5px">
{{ item.label }}</span
>
</div>
</el-option>
</el-select>
</div>
</div>
</div>
</div>
<!-- 编辑器区域 -->
<div class="editor-section">
<div class="section-header">
<i class="el-icon-edit-outline"></i>
<span>{{ $t('backendSystem.richTextEditor') || '富文本编辑器' }}</span>
</div>
<div class="editor-container">
<div ref="editor" style="height: 600px"></div>
</div>
</div>
<!-- 预览文档对话框 -->
<el-dialog
:title="$t('backendSystem.documentPreview') || '文档预览'"
: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>{{ $t('backendSystem.noContent') || '暂无内容' }}</p>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="previewVisible = false">{{ $t('backendSystem.close') || '关闭' }}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Vue from "vue";
import E from "wangeditor";
import { updateDocument } from "../../../api/documentManagement";
import Index from "./index";
export default Vue.extend({
mixins: [Index],
data() {
return {
editor: null,
html: "", // 编辑器内容
hasSetEditorContent: false, // 避免重复 set 内容
mode: "default",
addParams: {
title: "",
content: "",
type: "",
lang: "",
titleUrl: "",
articleUrl: "",
childType: "",
coin: "",
},
lastSaveTime: "",
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() {
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);
}
// 先恢复数据
this.loadFromLocalStorage();
// 添加页面卸载前的保存保护
window.addEventListener("beforeunload", this.handleBeforeUnload);
// 延迟设置默认内容,避免覆盖恢复的数据
setTimeout(() => {
if (!this.addParams.content) {
this.html = "<p>模拟 Ajax 异步设置内容 HTML</p>";
}
}, 1500);
this.editor = new E(this.$refs.editor);
this.editor.config.height = 600;
this.editor.config.onchange = (html) => {
this.html = html;
this.addParams.content = html;
};
//配置颜色
this.editor.config.colors = [
`#651FFF`,
`#FF4081`,
`#FF9900`,
`#FFC107`,
`#FF5722`,
`#9C27B0`,
`#673AB7`,
`#3F51B5`,
"#000000", // 黑色
"#ffffff", // 白色
"#eeece0", // 浅米色
"#1c487f", // 深蓝
"#4d80bf", // 蓝色
"#c24f4a", // 红色
"#8baa4a", // 绿色
"#a96b59", // 棕色
"#2b2b2b", // 深灰
"#b6975a", // 金色
"#5b9bd5", // 天蓝
"#70ad47", // 草绿
"#ed7d31", // 橙色
"#ffc000", // 黄色
"#7030a0", // 紫色
"#f4b084", // 浅橙
"#bdd7ee", // 浅蓝
"#a9d08e", // 浅绿
"#ffe699", // 浅黄
"#d9d9d9", // 浅灰
"#f8cbad", // 浅棕
"#e2efda", // 淡绿
"#fff2cc", // 淡黄
"#dbe5f1", // 淡蓝
"#e4dfec", // 淡紫
"#fbeee6", // 淡橙
"#f6f6f6", // 极浅灰
"#ff0000", // 纯红
"#00b050", // 纯绿
"#0070c0", // 纯蓝
"#7030a0", // 纯紫
"#ff9900", // 纯橙
"#808080", // 中灰
];
//配置字体
this.editor.config.fontNames = [
// 对象形式 v4.6.16
{name:"黑体",value:"黑体"},
{name:"绝绝字体",value:"Times New Roman"},
// 字符串形式
'黑体',
'仿宋',
'楷体',
'标楷体',
'华文仿宋',
'华文楷体',
'宋体',
'微软雅黑',
'Arial',
'Tahoma',
'Verdana',
'Times New Roman',
'Courier New',
],
// 不自定义菜单,使用官方默认菜单栏
this.editor.create();
// 只在初始化时 set 一次内容
if (this.addParams.content && !this.hasSetEditorContent) {
this.editor.txt.html(this.addParams.content);
this.html = this.addParams.content;
this.hasSetEditorContent = true;
}
},
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);
},
/**
* 编辑器创建后回调,确保 this.editor 正确赋值
* @param {Object} editor wangEditor 实例
*/
onCreated(editor) {
this.editor = editor;
console.log("编辑器已初始化", this.editor);
// 编辑器创建后,如果有恢复的内容,设置到编辑器中
if (this.addParams.content) {
this.$nextTick(() => {
this.editor.txt.html(this.addParams.content);
});
}
},
handelAddDocument() {
if (!this.addParams.type || this.addParams.type == "0") {
this.$message({
message: this.$t('backendSystem.pleaseInputType') || '请填写文档类型',
type: "warning",
duration: 4000,
showClose: true,
});
return;
}
console.log(this.screenCurrency, "this.screenCurrency",this.addParams.type);
if (this.addParams.type == "1" && !this.screenCurrency) {
this.$message({
message:
this.$t("backendSystem.pleaseSelectCurrency2") ||
"添加挖矿教程,请选择币种",
type: "warning",
duration: 4000,
showClose: true,
});
return;
}
if (this.addParams.content) {
//锚点定位的A标签 去掉target="_blank"
this.addParams.content = this.addParams.content.replace(
/<a([^>]*href="#[^"]*")[^>]*>/gi,
function (match) {
return match.replace(/\s*target="_blank"/gi, "");
}
);
}
this.addParams.coin = this.screenCurrency
console.log(this.addParams.content, "this.addParams.content");
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;
},
handleSave() {
// 保存前彻底去除所有锚点链接的 target="_blank"
this.addParams.content = this.addParams.content.replace(
/<a([^>]*href="#([^"]*)")[^>]*>/gi,
function (match) {
// 去掉 target="_blank"
return match.replace(/\s*target="_blank"/gi, "");
}
);
this.manualSave();
this.$message({
message: this.$t('backendSystem.contentSaved') || '内容已保存到本地,请尽快发布,关闭页面可能丢失内容',
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");
}
// 显示正在保存状态
this.isSaving = true;
if (this.autoSaveTimer) {
clearTimeout(this.autoSaveTimer);
}
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.txt.html();
}
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;
}
// 使用 $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.txt.html(data.content || "");
}
});
}
} catch (error) {
console.error("恢复数据失败:", error);
}
},
clearDraft() {
localStorage.removeItem("editor_draft");
this.lastSaveTime = "";
this.isSaving = false;
},
// 新增:手动保存方法
manualSave() {
// 确保获取最新的编辑器内容
if (this.editor) {
this.addParams.content = this.editor.txt.html();
}
this.saveToLocalStorage();
},
// 页面卸载前处理
handleBeforeUnload(event) {
// 自动保存,不提示用户
if (this.addParams.title || this.addParams.content) {
// 确保获取最新的编辑器内容
if (this.editor) {
this.addParams.content = this.editor.txt.html();
}
this.saveToLocalStorage();
}
},
// 新增:获取文档类型名称
getDocumentTypeName(type) {
switch (type) {
case "1":
return "home.serviceTerms";//服务条款
case "2":
return "home.APIfile";//api文档
case "3":
return "home.miningTutorial";//挖矿教程
case "0":
return "home.other";//其他
default:
return "home.unknownType";//未知类型
}
},
/**
* 插入锚点源码模式下插入HTML
*/
handleInsertAnchor() {
if (!this.editor) {
this.$message.warning("编辑器尚未初始化");
return;
}
this.$prompt(this.$t('backendSystem.pleaseInputAnchorName') || '请输入锚点名称(不能重复)', this.$t('backendSystem.insertAnchor') || '插入锚点', {
confirmButtonText: this.$t('backendSystem.confirm') || '确定',
cancelButtonText: this.$t('backendSystem.cancel') || '取消',
inputPattern: /^[a-zA-Z0-9_-]+$/,
inputErrorMessage: this.$t('backendSystem.anchorNameErrorMessage') || '锚点名只能包含字母、数字、下划线和中划线',
})
.then(({ value }) => {
this.editor.cmd.do("insertHTML", `<span id="${value}"></span>`);
this.html = this.editor.txt.html();
this.addParams.content = this.html;
this.$message.success(this.$t('backendSystem.anchorInserted') || '锚点已插入');
})
.catch(() => {});
},
/**
* 给选中的标题添加id属性作为锚点
*/
handleAddHeadingId() {
if (!this.editor) {
this.$message.warning("编辑器尚未初始化");
return;
}
// 获取当前选中的标题节点
const headers = this.editor.getElemsByTypePrefix("header");
if (!headers || headers.length === 0) {
this.$message.warning(this.$t('backendSystem.pleaseSelectHeader') || '请先选中一个标题h1~h6');
return;
}
const node = headers[0];
this.$prompt(this.$t('backendSystem.pleaseInputAnchorID') || '请输入锚点ID不能重复', this.$t('backendSystem.titleAnchor') || '标题锚点', {
confirmButtonText: this.$t('backendSystem.confirm') || '确定',
cancelButtonText: this.$t('backendSystem.cancel') || '取消',
inputPattern: /^[a-zA-Z0-9_-]+$/,
inputErrorMessage: this.$t('backendSystem.anchorIDErrorMessage') || 'ID只能包含字母、数字、下划线和中划线',
})
.then(({ value }) => {
this.editor.setElemAttribute(node, "id", value);
this.$message.success(this.$t('backendSystem.anchorIDAdded') || '锚点ID已添加');
})
.catch(() => {});
},
},
beforeDestroy() {
if (this.editor) {
this.editor.destroy();
}
},
beforeRouteLeave(to, from, next) {
// 确保获取最新的编辑器内容
if (this.editor) {
this.addParams.content = this.editor.txt.html();
}
this.saveToLocalStorage();
next();
},
});
</script>
<style lang="scss" scoped>
/* 页面整体样式 */
.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);
}
.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);
transition: all 0.3s ease;
}
.header-actions .el-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
}
/* 配置区域样式 */
.config-section {
background: white;
border-radius: 12px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
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;
}
.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: "*";
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);
border: 1px solid #e9ecef;
}
.editor-container {
border: 1px solid #dcdfe6;
border-radius: 8px;
overflow: hidden;
background: white;
min-height: 650px;
position: relative;
z-index: 1;
}
/* 设置编辑器高度 */
::v-deep .editor-container .w-e-text-container {
height: 600px !important;
min-height: 600px !important;
z-index: 1 !important;
position: relative;
}
::v-deep .w-e-toolbar {
z-index: 10 !important;
position: relative;
}
/* 编辑器内容区域样式 */
.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;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* 预览对话框样式 */
.preview-dialog .el-dialog__body {
padding: 20px;
max-height: 70vh;
overflow-y: auto;
z-index: 9999;
}
.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;
}
.page-header {
flex-direction: column;
gap: 20px;
padding: 20px;
}
.header-actions {
width: 100%;
justify-content: center;
}
.config-row {
grid-template-columns: 1fr;
gap: 16px;
}
.page-title {
font-size: 24px;
}
.save-status {
bottom: 10px;
right: 10px;
left: 10px;
text-align: center;
justify-content: center;
}
}
/* Element UI 组件样式优化 */
.el-input,
.el-select {
border-radius: 8px;
}
.el-input__inner,
.el-select .el-input__inner {
border-radius: 8px;
border: 1px solid #dcdfe6;
transition: all 0.3s ease;
}
.el-input__inner:focus,
.el-select .el-input__inner:focus {
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;
}
}
}
.no-scroll {
overflow: hidden !important;
}
</style>