优化编辑器和前端内容页显示

This commit is contained in:
李志强 2026-01-28 14:36:40 +08:00
parent da378a01be
commit b714df5f43
3 changed files with 945 additions and 482 deletions

View File

@ -5,15 +5,15 @@
// body 样式 // body 样式
body { body {
background-color: var(--el-bg-color-page); background-color: #f5f7fa;
color: var(--el-text-color-primary); color: #303133;
transition: background-color 0.3s ease, color 0.3s ease; transition: background-color 0.3s ease, color 0.3s ease;
} }
// container-box 样式 // container-box 样式
.container-box { .container-box {
background-color: var(--el-bg-color); background-color: #ffffff;
border: 1px solid var(--el-border-color-lighter); border: 1px solid #ebeef5;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.04); box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.04);
border-radius: 8px; border-radius: 8px;
padding: 24px; padding: 24px;
@ -48,6 +48,382 @@ body {
.toolbar-container{ .toolbar-container{
border-bottom: 1px solid #dcdfe6 !important; border-bottom: 1px solid #dcdfe6 !important;
} }
.editor-container {
background-color: #ffffff !important;
}
:deep(.w-e-text),
:deep(.w-e-text-container) {
background-color: transparent !important;
* {
color: #1a1a2e !important;
}
p {
color: #1a1a2e !important;
margin: 8px 0 !important;
line-height: 1.8 !important;
font-size: 14px !important;
text-indent: 0 !important;
}
span {
color: #1a1a2e !important;
font-size: 14px !important;
line-height: 1.8 !important;
}
strong, b {
font-weight: 600 !important;
color: #1a1a2e !important;
}
em, i {
font-style: italic !important;
}
u {
text-decoration: underline !important;
}
s, del {
text-decoration: line-through !important;
}
h1 {
color: #1a1a2e !important;
font-size: 28px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h2 {
color: #1a1a2e !important;
font-size: 24px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h3 {
color: #1a1a2e !important;
font-size: 20px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h4 {
color: #1a1a2e !important;
font-size: 18px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h5 {
color: #1a1a2e !important;
font-size: 16px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h6 {
color: #1a1a2e !important;
font-size: 14px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
a {
color: #3973ff !important;
text-decoration: underline !important;
&:hover {
color: #3973ff !important;
opacity: 0.8 !important;
}
}
code {
background-color: #f5f7fa !important;
color: #1a1a2e !important;
border: 1px solid #e4e7ed !important;
border-radius: 3px !important;
padding: 2px 6px !important;
font-family: 'Consolas', 'Monaco', monospace !important;
font-size: 13px !important;
}
pre {
background-color: #f5f7fa !important;
border: 1px solid #e4e7ed !important;
border-radius: 4px !important;
color: #1a1a2e !important;
padding: 12px 16px !important;
margin: 12px 0 !important;
overflow-x: auto;
code {
background-color: transparent !important;
border: none !important;
padding: 0 !important;
color: #1a1a2e !important;
font-size: 13px !important;
line-height: 1.6 !important;
}
}
blockquote {
border-left: 4px solid #3973ff !important;
background-color: #f5f7fa !important;
color: #606266 !important;
padding: 8px 16px !important;
margin: 12px 0 !important;
}
table {
border-collapse: collapse !important;
border: 1px solid #e4e7ed !important;
width: 100% !important;
th, td {
border: 1px solid #e4e7ed !important;
background-color: #ffffff !important;
color: #1a1a2e !important;
padding: 8px 12px !important;
min-width: 60px;
}
th {
background-color: #f5f7fa !important;
font-weight: 600 !important;
}
}
ul {
list-style-type: disc !important;
color: #1a1a2e !important;
padding-left: 24px !important;
margin: 8px 0 !important;
}
ol {
list-style-type: decimal !important;
color: #1a1a2e !important;
padding-left: 24px !important;
margin: 8px 0 !important;
}
li {
color: #1a1a2e !important;
line-height: 1.8 !important;
margin: 4px 0 !important;
}
hr {
border-top: 1px solid #e4e7ed !important;
margin: 16px 0 !important;
}
img {
max-width: 100% !important;
border-radius: 4px !important;
margin: 8px 0 !important;
}
video {
max-width: 100% !important;
border-radius: 4px !important;
margin: 8px 0 !important;
}
.w-e-panel-tab-content {
color: #1a1a2e !important;
}
}
}
html.dark {
.wang-editor-wrapper {
border-color: #3d3d3d !important;
background-color: #1a1a1a !important;
.toolbar-container {
background-color: #2d2d2d !important;
border-color: #3d3d3d !important;
}
.editor-container {
background-color: #1a1a1a !important;
&::-webkit-scrollbar-thumb {
background: #4d4d4d !important;
}
&::-webkit-scrollbar-track {
background: #2d2d2d !important;
}
}
:deep(.w-e-text),
:deep(.w-e-text-container) {
background-color: transparent !important;
* {
color: #e0e0e0 !important;
}
p {
color: #e0e0e0 !important;
margin: 8px 0 !important;
line-height: 1.8 !important;
font-size: 14px !important;
text-indent: 0 !important;
}
span {
color: #e0e0e0 !important;
font-size: 14px !important;
line-height: 1.8 !important;
}
strong, b {
font-weight: 600 !important;
color: #e0e0e0 !important;
}
em, i {
font-style: italic !important;
}
u {
text-decoration: underline !important;
}
s, del {
text-decoration: line-through !important;
}
h1 {
color: #e0e0e0 !important;
font-size: 28px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h2 {
color: #e0e0e0 !important;
font-size: 24px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h3 {
color: #e0e0e0 !important;
font-size: 20px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h4 {
color: #e0e0e0 !important;
font-size: 18px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h5 {
color: #e0e0e0 !important;
font-size: 16px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
h6 {
color: #e0e0e0 !important;
font-size: 14px !important;
font-weight: 600 !important;
margin: 16px 0 12px !important;
line-height: 1.4 !important;
}
a {
color: #4f84ff !important;
text-decoration: underline !important;
&:hover {
color: #4f84ff !important;
opacity: 0.8 !important;
}
}
code {
background-color: #2d2d2d !important;
color: #e0e0e0 !important;
border-color: #3d3d3d !important;
}
pre {
background-color: #2d2d2d !important;
border-color: #3d3d3d !important;
color: #e0e0e0 !important;
code {
background-color: transparent !important;
border: none !important;
color: #e0e0e0 !important;
}
}
blockquote {
border-left-color: #4f84ff !important;
background-color: #2d2d2d !important;
color: #b0b0b0 !important;
}
table {
border-color: #3d3d3d !important;
th, td {
border-color: #3d3d3d !important;
background-color: #1a1a1a !important;
color: #e0e0e0 !important;
}
th {
background-color: #2d2d2d !important;
}
}
ul, ol, li {
color: #e0e0e0 !important;
}
hr {
border-top-color: #3d3d3d !important;
}
img, video {
max-width: 100% !important;
border-radius: 4px !important;
}
.w-e-panel-tab-content {
color: #e0e0e0 !important;
}
}
}
} }
.el-form-item__label{ .el-form-item__label{
min-width: 80px !important; min-width: 80px !important;

View File

@ -1,5 +1,10 @@
<template> <template>
<el-drawer v-model="visible" :title="isEdit ? '编辑文章' : '新增文章'" size="60%" :before-close="handleBeforeClose"> <el-drawer
v-model="visible"
:title="isEdit ? '编辑文章' : '新增文章'"
size="60%"
:before-close="handleBeforeClose"
>
<el-form :model="form" :rules="rules" ref="formRef" label-width="80px"> <el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
<el-form-item label="标题" prop="title"> <el-form-item label="标题" prop="title">
<el-input v-model="form.title" placeholder="请输入文章标题" /> <el-input v-model="form.title" placeholder="请输入文章标题" />
@ -25,37 +30,71 @@
</el-row> </el-row>
<el-form-item label="简介" prop="desc"> <el-form-item label="简介" prop="desc">
<el-input v-model="form.desc" :rows="4" type="textarea" placeholder="请输入简介" /> <el-input
v-model="form.desc"
:rows="4"
type="textarea"
placeholder="请输入简介"
/>
</el-form-item> </el-form-item>
<el-form-item label="封面图片" prop="image"> <el-form-item label="封面图片" prop="image">
<div class="uploads"> <div class="uploads">
<!-- 已有图片显示 --> <!-- 已有图片显示 -->
<div v-if="form.image && fileList.length === 0" class="existing-image"> <div
<img :src="API_BASE_URL + form.image.replace(/\\\//g, '/')" alt="已有图片" /> v-if="form.image && fileList.length === 0"
class="existing-image"
>
<img
:src="API_BASE_URL + form.image.replace(/\\\//g, '/')"
alt="已有图片"
/>
<div class="image-actions"> <div class="image-actions">
<el-button type="primary" size="small" @click="previewExistingImage">预览</el-button> <el-button
<el-button type="danger" size="small" @click="removeExistingImage">删除</el-button> type="primary"
size="small"
@click="previewExistingImage"
>预览</el-button
>
<el-button type="danger" size="small" @click="removeExistingImage"
>删除</el-button
>
</div> </div>
</div> </div>
<!-- 上传组件 --> <!-- 上传组件 -->
<el-upload v-model:file-list="fileList" :auto-upload="false" :before-upload="beforeImgUpload" <el-upload
list-type="picture-card" :limit="1" :show-file-list="fileList.length > 0"> v-model:file-list="fileList"
:auto-upload="false"
:before-upload="beforeImgUpload"
list-type="picture-card"
:limit="1"
:show-file-list="fileList.length > 0"
>
<el-icon> <el-icon>
<Plus /> <Plus />
</el-icon> </el-icon>
<template #file="{ file }"> <template #file="{ file }">
<div> <div>
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" /> <img
class="el-upload-list__item-thumbnail"
:src="file.url"
alt=""
/>
<span class="el-upload-list__item-actions"> <span class="el-upload-list__item-actions">
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)"> <span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<el-icon> <el-icon>
<ZoomIn /> <ZoomIn />
</el-icon> </el-icon>
</span> </span>
<span class="el-upload-list__item-delete" @click="handleRemove(file)"> <span
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<el-icon> <el-icon>
<Delete /> <Delete />
</el-icon> </el-icon>
@ -66,9 +105,18 @@
</el-upload> </el-upload>
<el-drawer v-model="drawerVisible" width="60%" center> <el-drawer v-model="drawerVisible" width="60%" center>
<div style="display: flex; justify-content: center;align-items:center;"> <div
<img :src="drawerImageUrl" alt="Preview Image" style="
style="max-width: 100%; max-height: 70vh; object-fit: contain;" /> display: flex;
justify-content: center;
align-items: center;
"
>
<img
:src="drawerImageUrl"
alt="Preview Image"
style="max-width: 100%; max-height: 70vh; object-fit: contain"
/>
</div> </div>
</el-drawer> </el-drawer>
@ -102,36 +150,47 @@
<el-divider></el-divider> <el-divider></el-divider>
<span class="drawer-footer"> <span class="drawer-footer">
<el-button @click="handleCancel">取消</el-button> <el-button @click="handleCancel">取消</el-button>
<el-button v-if="!isEdit" type="warning" @click="handleConfirm(0)" :loading="submitLoading">草稿</el-button> <el-button
<el-button type="primary" @click="handleConfirm(1)" :loading="submitLoading">提交</el-button> v-if="!isEdit"
type="warning"
@click="handleConfirm(0)"
:loading="submitLoading"
>草稿</el-button
>
<el-button
type="primary"
@click="handleConfirm(1)"
:loading="submitLoading"
>提交</el-button
>
</span> </span>
</template> </template>
</el-drawer> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, watch, nextTick, onMounted, computed } from 'vue'; import { ref, reactive, watch, nextTick, onMounted, computed } from "vue";
import { ElMessage, ElUpload } from 'element-plus' import { ElMessage, ElUpload } from "element-plus";
import WangEditor from '@/views/components/WangEditor.vue'; import WangEditor from "@/views/components/WangEditor.vue";
import { createArticle, editArticle, listCategories } from '@/api/article.js'; import { createArticle, editArticle, listCategories } from "@/api/article.js";
import { uploadFile } from '@/api/file.js'; import { uploadFile } from "@/api/file.js";
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
isEdit: { isEdit: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
model: { model: {
type: Object, type: Object,
default: () => null default: () => null,
} },
}); });
const emit = defineEmits(['update:modelValue', 'saved']); const emit = defineEmits(["update:modelValue", "saved"]);
const visible = ref(false); const visible = ref(false);
const submitLoading = ref(false); const submitLoading = ref(false);
@ -145,28 +204,31 @@ const handleBeforeClose = (done: () => void) => {
const cateOptions = ref([]); const cateOptions = ref([]);
const form = reactive({ const form = reactive({
title: '', title: "",
author: '云泽网', author: "云泽网",
cate: '', cate: "",
content: '', content: "",
image: '', image: "",
desc: '', desc: "",
is_trans: 0, is_trans: 0,
transurl: null transurl: null,
}); });
const rules = { const rules = {
title: [ title: [
{ required: true, message: '请输入文章标题', trigger: 'blur' }, { required: true, message: "请输入文章标题", trigger: "blur" },
{ min: 2, max: 200, message: '标题长度在 2 到 200 个字符', trigger: 'blur' } {
min: 2,
max: 200,
message: "标题长度在 2 到 200 个字符",
trigger: "blur",
},
], ],
author: [ author: [
{ required: true, message: '请输入作者', trigger: 'blur' }, { required: true, message: "请输入作者", trigger: "blur" },
{ max: 50, message: '作者姓名不能超过50个字符', trigger: 'blur' } { max: 50, message: "作者姓名不能超过50个字符", trigger: "blur" },
],
content: [
{ required: true, message: '请输入文章内容', trigger: 'blur' }
], ],
content: [{ required: true, message: "请输入文章内容", trigger: "blur" }],
}; };
// //
@ -179,88 +241,91 @@ async function fetchCategories() {
cateOptions.value = buildCategoryTree(categories); cateOptions.value = buildCategoryTree(categories);
} }
} catch (error) { } catch (error) {
console.error('获取分类失败:', error); console.error("获取分类失败:", error);
} }
} }
// //
function buildCategoryTree(categories, parentCid = 0) { function buildCategoryTree(categories, parentCid = 0) {
return categories return categories
.filter(cate => Number(cate.cid) === Number(parentCid)) .filter((cate) => Number(cate.cid) === Number(parentCid))
.map(cate => { .map((cate) => {
const children = buildCategoryTree(categories, cate.id); const children = buildCategoryTree(categories, cate.id);
return { return {
...cate, ...cate,
label: cate.name, label: cate.name,
value: cate.id, value: cate.id,
children: children.length > 0 ? children : undefined children: children.length > 0 ? children : undefined,
}; };
}); });
} }
// //
const fileList = ref<any[]>([]) const fileList = ref<any[]>([]);
const drawerVisible = ref(false) const drawerVisible = ref(false);
const drawerImageUrl = ref('') const drawerImageUrl = ref("");
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
function beforeImgUpload(file: File) { function beforeImgUpload(file: File) {
const isImage = file.type.startsWith('image/') const isImage = file.type.startsWith("image/");
const isLt10M = file.size / 1024 / 1024 < 10 const isLt10M = file.size / 1024 / 1024 < 10;
if (!isImage) ElMessage.error('仅支持图片格式') if (!isImage) ElMessage.error("仅支持图片格式");
if (!isLt10M) ElMessage.error('图片大小不能超过10MB') if (!isLt10M) ElMessage.error("图片大小不能超过10MB");
return isImage && isLt10M return isImage && isLt10M;
} }
function handlePictureCardPreview(file: any) { function handlePictureCardPreview(file: any) {
drawerImageUrl.value = file.url drawerImageUrl.value = file.url;
drawerVisible.value = true drawerVisible.value = true;
} }
function handleRemove(file: any) { function handleRemove(file: any) {
fileList.value = [] fileList.value = [];
form.image = '' form.image = "";
} }
function removeExistingImage() { function removeExistingImage() {
form.image = '' form.image = "";
} }
function previewExistingImage() { function previewExistingImage() {
const imagePath = form.image.replace(/\\\//g, '/'); const imagePath = form.image.replace(/\\\//g, "/");
drawerImageUrl.value = API_BASE_URL + imagePath; drawerImageUrl.value = API_BASE_URL + imagePath;
drawerVisible.value = true; drawerVisible.value = true;
} }
// //
watch(() => props.modelValue, (newVal) => { watch(
visible.value = newVal; () => props.modelValue,
if (newVal) { (newVal) => {
if (props.isEdit && props.model) { visible.value = newVal;
// if (newVal) {
const modelData = props.model._raw || props.model; if (props.isEdit && props.model) {
nextTick(() => { //
Object.assign(form, { const modelData = props.model._raw || props.model;
title: modelData.title || '', nextTick(() => {
author: modelData.author || '', Object.assign(form, {
cate: modelData.cate || '', title: modelData.title || "",
content: modelData.content || '', author: modelData.author || "",
desc: modelData.desc || '', cate: modelData.cate || "",
is_trans: modelData.is_trans || 0, content: modelData.content || "",
transurl: modelData.transurl || null, desc: modelData.desc || "",
image: modelData.image || '' is_trans: modelData.is_trans || 0,
transurl: modelData.transurl || null,
image: modelData.image || "",
});
fileList.value = []; //
}); });
fileList.value = []; // } else {
}); //
} else { resetForm();
// }
resetForm();
} }
} },
}); );
function handleCancel() { function handleCancel() {
emit('update:modelValue', false); emit("update:modelValue", false);
resetForm(); resetForm();
} }
@ -274,16 +339,20 @@ async function handleConfirm(status = 1) {
// //
if (fileList.value.length > 0 && fileList.value[0].raw) { if (fileList.value.length > 0 && fileList.value[0].raw) {
const uploadFormData = new FormData() const uploadFormData = new FormData();
uploadFormData.append('file', fileList.value[0].raw) uploadFormData.append("file", fileList.value[0].raw);
uploadFormData.append('cate', 'article') uploadFormData.append("cate", "article");
const uploadRes = await uploadFile(uploadFormData); const uploadRes = await uploadFile(uploadFormData);
// 200=201=使 // 200=201=使
if ((uploadRes.code === 200 || uploadRes.code === 201) && uploadRes.data && uploadRes.data.url) { if (
(uploadRes.code === 200 || uploadRes.code === 201) &&
uploadRes.data &&
uploadRes.data.url
) {
imageUrl = uploadRes.data.url; imageUrl = uploadRes.data.url;
} else { } else {
ElMessage.error('图片上传失败:' + (uploadRes.msg || '未知错误')); ElMessage.error("图片上传失败:" + (uploadRes.msg || "未知错误"));
return; return;
} }
} }
@ -298,7 +367,7 @@ async function handleConfirm(status = 1) {
image: imageUrl, image: imageUrl,
is_trans: form.is_trans, is_trans: form.is_trans,
transurl: form.is_trans === 1 ? form.transurl : null, transurl: form.is_trans === 1 ? form.transurl : null,
status: status // 0稿1 status: status, // 0稿1
}; };
// /API // /API
@ -311,38 +380,49 @@ async function handleConfirm(status = 1) {
const createArticleWithCheck = async (ignoreSimilarity = false) => { const createArticleWithCheck = async (ignoreSimilarity = false) => {
const data = { const data = {
...submitData, ...submitData,
...(ignoreSimilarity && { ignore_similarity: 1 }) ...(ignoreSimilarity && { ignore_similarity: 1 }),
}; };
return await createArticle(data); return await createArticle(data);
}; };
res = await createArticleWithCheck(); res = await createArticleWithCheck();
resp = (res && typeof res.code !== 'undefined') ? res : (res && res.data ? res.data : res); resp =
res && typeof res.code !== "undefined"
? res
: res && res.data
? res.data
: res;
// //
if (resp && resp.code === 409) { if (resp && resp.code === 409) {
const similarArticles = resp.data?.similar_articles || []; const similarArticles = resp.data?.similar_articles || [];
// //
const similarArticlesList = similarArticles.map((article: any) => `\n<div style="margin: 8px 0; padding: 8px; background: #f5f7fa; border-radius: 4px;">\n<div style="font-weight: bold; color: #303133;">${article.title}</div>\n<div style="color: #606266; font-size: 14px; margin-top: 4px;">相似度:${article.similarity}%</div>\n</div>\n`).join(''); const similarArticlesList = similarArticles
.map(
(article: any) =>
`\n<div style="margin: 8px 0; padding: 8px; background: #f5f7fa; border-radius: 4px;">\n<div style="font-weight: bold; color: #303133;">${article.title}</div>\n<div style="color: #606266; font-size: 14px; margin-top: 4px;">相似度:${article.similarity}%</div>\n</div>\n`,
)
.join("");
const confirmMessage = `\n<div>\n\n${similarArticlesList} \n<p style="margin-top: 16px; color: #303133;">是否继续创建?</p>\n</div>\n`; const confirmMessage = `\n<div>\n\n${similarArticlesList} \n<p style="margin-top: 16px; color: #303133;">是否继续创建?</p>\n</div>\n`;
// 使Element Plusconfirm // 使Element Plusconfirm
const { ElMessageBox } = await import('element-plus'); const { ElMessageBox } = await import("element-plus");
try { try {
await ElMessageBox.confirm( await ElMessageBox.confirm(confirmMessage, "检测到相似标题", {
confirmMessage, confirmButtonText: "继续创建",
'检测到相似标题', cancelButtonText: "取消",
{ type: "warning",
confirmButtonText: '继续创建', dangerouslyUseHTMLString: true,
cancelButtonText: '取消', });
type: 'warning',
dangerouslyUseHTMLString: true,
}
);
// //
res = await createArticleWithCheck(true); res = await createArticleWithCheck(true);
resp = (res && typeof res.code !== 'undefined') ? res : (res && res.data ? res.data : res); resp =
res && typeof res.code !== "undefined"
? res
: res && res.data
? res.data
: res;
} catch (error) { } catch (error) {
// //
return; return;
@ -353,30 +433,59 @@ async function handleConfirm(status = 1) {
// ID // ID
const modelData = props.model._raw || props.model; const modelData = props.model._raw || props.model;
const articleId = modelData.id; const articleId = modelData.id;
if (!articleId) { if (!articleId) {
ElMessage.error('文章ID不存在无法更新'); ElMessage.error("文章ID不存在无法更新");
return; return;
} }
// IDIDURL // IDIDURL
const editData = { const editData = {
...submitData ...submitData,
}; };
res = await editArticle(articleId, editData); res = await editArticle(articleId, editData);
resp = (res && typeof res.code !== 'undefined') ? res : (res && res.data ? res.data : res); resp =
res && typeof res.code !== "undefined"
? res
: res && res.data
? res.data
: res;
} }
if (resp && resp.code === 200 && (resp.message === 'success' || resp.msg === 'success')) { if (
ElMessage.success(status === 0 ? '保存草稿成功' : (props.isEdit ? '更新成功' : '创建成功')); resp &&
emit('update:modelValue', false); resp.code === 200 &&
emit('saved'); (resp.message === "success" || resp.msg === "success")
) {
ElMessage.success(
status === 0
? "保存草稿成功"
: props.isEdit
? "更新成功"
: "创建成功",
);
emit("update:modelValue", false);
emit("saved");
} else { } else {
ElMessage.error((resp && resp.message) || (resp && resp.msg) || (status === 0 ? '保存草稿失败' : (props.isEdit ? '更新失败' : '创建失败'))); ElMessage.error(
(resp && resp.message) ||
(resp && resp.msg) ||
(status === 0
? "保存草稿失败"
: props.isEdit
? "更新失败"
: "创建失败"),
);
} }
} catch (error) { } catch (error) {
ElMessage.error(status === 0 ? '保存草稿失败' : (props.isEdit ? '更新失败' : '创建失败')); ElMessage.error(
status === 0
? "保存草稿失败"
: props.isEdit
? "更新失败"
: "创建失败",
);
} finally { } finally {
submitLoading.value = false; submitLoading.value = false;
} }
@ -393,14 +502,14 @@ function resetForm() {
fileList.value = []; fileList.value = [];
// //
Object.assign(form, { Object.assign(form, {
title: '', title: "",
author: '云泽网', author: "云泽网",
cate: '', cate: "",
content: '', content: "",
image: '', image: "",
desc: '', desc: "",
is_trans: 0, is_trans: 0,
transurl: null transurl: null,
}); });
} }
@ -408,12 +517,12 @@ function resetForm() {
defineExpose({ defineExpose({
resetForm: () => { resetForm: () => {
formRef.value?.resetFields(); formRef.value?.resetFields();
} },
}); });
onMounted(() => { onMounted(() => {
fetchCategories(); fetchCategories();
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -421,6 +530,106 @@ onMounted(() => {
border: 1px solid #dcdfe6; border: 1px solid #dcdfe6;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
&:focus {
outline: none !important;
}
:deep(.w-e-text),
:deep(.w-e-text-container) {
p {
color: #1a1a2e !important;
margin: 0.5em 0 !important;
}
h1, h2, h3, h4, h5, h6 {
color: #1a1a2e !important;
font-weight: 600 !important;
}
a {
color: #3973ff !important;
text-decoration: underline !important;
&:hover {
color: #3973ff !important;
opacity: 0.8 !important;
}
}
code {
background-color: #f5f7fa !important;
color: #1a1a2e !important;
border: 1px solid #e4e7ed !important;
padding: 2px 6px !important;
border-radius: 3px !important;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 13px;
}
pre {
background-color: #f5f7fa !important;
border: 1px solid #e4e7ed !important;
color: #1a1a2e !important;
border-radius: 4px !important;
padding: 12px 16px;
margin: 12px 0;
overflow-x: auto;
code {
background-color: transparent !important;
border: none !important;
padding: 0 !important;
}
}
blockquote {
border-left: 4px solid #3973ff !important;
background-color: #f5f7fa !important;
color: #606266 !important;
padding: 0.6em 1.2em !important;
margin: 1em 0 !important;
}
table {
border-collapse: collapse !important;
border: 1px solid #e4e7ed !important;
th, td {
border: 1px solid #e4e7ed !important;
background-color: #ffffff !important;
color: #1a1a2e !important;
padding: 8px 12px;
}
th {
background-color: #f5f7fa !important;
}
}
ul, ol {
color: #1a1a2e !important;
padding-left: 24px;
margin: 8px 0;
}
li {
color: #1a1a2e !important;
line-height: 1.8;
margin: 4px 0;
}
hr {
border-top: 1px solid #e4e7ed !important;
margin: 16px 0;
}
img {
max-width: 100%;
border-radius: 4px;
margin: 8px 0;
}
}
} }
.drawer-footer { .drawer-footer {

View File

@ -325,380 +325,258 @@ onBeforeUnmount(() => {
}); });
</script> </script>
<style scoped lang="less"> <style lang="less">
.wang-editor-wrapper { .wang-editor-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 650px; // height: 650px;
border: 1px solid var(--border-color); border: 1px solid #dcdfe6;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
background-color: var(--fill-color-blank); background: #ffffff;
.toolbar-container { .toolbar-container {--fill-color-light
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 10; z-index: 10;
background-color: var(--fill-color-light); background: #f5f7fa;
border-bottom: 1px solid var(--border-color-lighter); border-bottom: 1px solid #dcdfe6;
flex-shrink: 0;
} }
.editor-container { .editor-container {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 10px; overflow-x: hidden;
padding: 0;
&::-webkit-scrollbar {
width: 6px;
height: 6px;
}
&::-webkit-scrollbar-thumb {
background: #c0c4cc;
border-radius: 3px;
}
&::-webkit-scrollbar-track {
background: #f5f7fa;
border-radius: 3px;
}
}
:deep(.w-e-text),
:deep(.w-e-text-container) {
p {
color: #1a1a2e !important;
margin: 8px 0 !important;
line-height: 1.8 !important;
}
h1, h2, h3, h4, h5, h6 {
color: #1a1a2e !important;
font-weight: 600 !important;
margin: 16px 0 8px !important;
}
a {
color: #3973ff !important;
text-decoration: underline !important;
}
code {
background: #f5f7fa !important;
color: #1a1a2e !important;
border: 1px solid #e4e7ed !important;
border-radius: 3px;
padding: 2px 6px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 13px;
}
pre {
background: #f5f7fa !important;
border: 1px solid #e4e7ed !important;
border-radius: 4px;
padding: 12px 16px;
margin: 12px 0;
overflow-x: auto;
color: #1a1a2e !important;
code {
background: transparent !important;
border: none !important;
padding: 0;
font-size: 13px;
line-height: 1.6;
color: #1a1a2e !important;
}
}
blockquote {
border-left: 4px solid #3973ff !important;
background: #f5f7fa !important;
color: #606266 !important;
padding: 8px 16px;
margin: 12px 0;
}
table {
border-collapse: collapse !important;
border: 1px solid #e4e7ed !important;
width: 100% !important;
margin: 12px 0;
th, td {
border: 1px solid #e4e7ed !important;
padding: 8px 12px !important;
min-width: 60px;
}
th {
background: #f5f7fa !important;
font-weight: 600;
}
}
ul, ol {
color: #1a1a2e !important;
padding-left: 24px !important;
margin: 8px 0;
}
li {
color: #1a1a2e !important;
line-height: 1.8 !important;
margin: 4px 0;
}
hr {
border-top: 1px solid #e4e7ed !important;
margin: 16px 0 !important;
}
img {
max-width: 100% !important;
border-radius: 4px !important;
margin: 8px 0;
}
}
}
html.dark {
.wang-editor-wrapper {
border-color: #3d3d3d;
background: #1a1a1a;
.toolbar-container {
background: #2d2d2d;
border-color: #3d3d3d;
}
.editor-container {
&::-webkit-scrollbar {
width: 6px;
height: 6px;
}
&::-webkit-scrollbar-thumb {
background: #4d4d4d;
border-radius: 3px;
}
&::-webkit-scrollbar-track {
background: #2d2d2d;
border-radius: 3px;
}
}
:deep(.w-e-text),
:deep(.w-e-text-container) {
background: transparent !important;
p, h1, h2, h3, h4, h5, h6 {
color: #e0e0e0 !important;
}
a {
color: #4f84ff !important;
text-decoration: underline !important;
}
code {
background: #2d2d2d !important;
color: #e0e0e0 !important;
border-color: #3d3d3d !important;
}
pre {
background: #2d2d2d !important;
border-color: #3d3d3d !important;
color: #e0e0e0 !important;
code {
background: transparent !important;
border: none !important;
color: #e0e0e0 !important;
}
}
blockquote {
border-color: #4f84ff !important;
background: #2d2d2d !important;
color: #b0b0b0 !important;
}
table {
border-color: #3d3d3d !important;
th, td {
border-color: #3d3d3d !important;
background: #1a1a1a !important;
color: #e0e0e0 !important;
}
th {
background: #2d2d2d !important;
}
}
ul, ol {
color: #e0e0e0 !important;
padding-left: 24px !important;
margin: 8px 0;
}
li {
color: #e0e0e0 !important;
line-height: 1.8 !important;
margin: 4px 0;
}
hr {
border-color: #3d3d3d !important;
}
img {
max-width: 100% !important;
border-radius: 4px !important;
margin: 8px 0;
}
}
} }
} }
.toolbar-container { .toolbar-container {
border-bottom: 1px solid var(--border-color-lighter); border-bottom: 1px solid #dcdfe6;
background-color: var(--fill-color-light); background-color: #f5f7fa;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.editor-container { .editor-container {
min-height: 400px; min-height: 400px;
background-color: var(--fill-color-blank); background-color: #ffffff;
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
} }
</style> </style>
<style lang="less">
// WangEditor
.wang-editor-wrapper {
//
:deep(.w-e-toolbar) {
background-color: var(--fill-color-light) !important;
border-bottom-color: var(--border-color-lighter) !important;
border-bottom: 1px solid var(--border-color-lighter) !important;
//
.w-e-bar-item {
button {
color: var(--text-color-primary) !important;
background-color: transparent !important;
border: none !important;
transition: all 0.2s ease !important;
&:hover:not(.disabled) {
background-color: var(--fill-color) !important;
color: var(--primary-color) !important;
}
&:active:not(.disabled) {
background-color: var(--fill-color-dark) !important;
}
&.active {
background-color: var(--fill-color-dark) !important;
color: var(--primary-color) !important;
}
&.disabled {
color: var(--text-color-disabled) !important;
cursor: not-allowed !important;
opacity: 0.5 !important;
}
}
// 线
&::after {
background-color: var(--border-color-lighter) !important;
}
}
// 线
.w-e-bar-divider {
background-color: var(--border-color-lighter) !important;
}
}
//
:deep(.w-e-text-container) {
background-color: var(--el-bg-color) !important;
color: var(--text-color-primary) !important;
border: none !important;
//
.w-e-text {
color: var(--text-color-primary) !important;
background-color: transparent !important;
min-height: 400px !important;
//
&:focus {
outline: none !important;
}
//
p {
color: var(--text-color-primary) !important;
margin: 0.5em 0 !important;
}
//
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--text-color-primary) !important;
font-weight: 600 !important;
}
//
a {
color: var(--primary-color) !important;
text-decoration: underline !important;
&:hover {
color: var(--primary-color) !important;
opacity: 0.8 !important;
}
}
//
code {
background-color: var(--fill-color-light) !important;
color: var(--text-color-primary) !important;
border: 1px solid var(--border-color-lighter) !important;
padding: 2px 6px !important;
border-radius: 3px !important;
}
//
pre {
background-color: var(--fill-color-light) !important;
border: 1px solid var(--border-color-lighter) !important;
color: var(--text-color-primary) !important;
border-radius: 4px !important;
code {
background-color: transparent !important;
border: none !important;
padding: 0 !important;
}
}
//
blockquote {
border-left: 4px solid var(--border-color) !important;
background-color: var(--fill-color-light) !important;
color: var(--text-color-primary) !important;
padding: 0.6em 1.2em !important;
margin: 1em 0 !important;
}
//
table {
border-collapse: collapse !important;
border: 1px solid var(--border-color-lighter) !important;
th,
td {
border: 1px solid var(--border-color-lighter) !important;
background-color: var(--fill-color-blank) !important;
color: var(--text-color-primary) !important;
}
th {
background-color: var(--fill-color-light) !important;
}
}
//
ul,
ol {
color: var(--text-color-primary) !important;
}
li {
color: var(--text-color-primary) !important;
}
}
//
.placeholder {
color: var(--text-color-placeholder) !important;
}
}
//
:deep(.w-e-drop-panel) {
background-color: var(--bg-color-overlay) !important;
border: 1px solid var(--border-color) !important;
box-shadow: var(--box-shadow) !important;
color: var(--text-color-primary) !important;
.w-e-list-item {
color: var(--text-color-primary) !important;
transition: all 0.2s ease !important;
&:hover {
background-color: var(--fill-color-light) !important;
color: var(--text-color-primary) !important;
}
&.selected {
background-color: var(--fill-color-light) !important;
color: var(--primary-color) !important;
}
&.disabled {
color: var(--text-color-disabled) !important;
cursor: not-allowed !important;
&:hover {
background-color: transparent !important;
}
}
}
// 线
.w-e-drop-panel-divider {
background-color: var(--border-color-lighter) !important;
}
}
//
:deep(.w-e-toolbar-menu) {
background-color: var(--bg-color-overlay) !important;
border: 1px solid var(--border-color) !important;
box-shadow: var(--box-shadow) !important;
.w-e-menu-item {
color: var(--text-color-primary) !important;
&:hover {
background-color: var(--fill-color-light) !important;
color: var(--text-color-primary) !important;
}
&.active {
background-color: var(--fill-color-light) !important;
color: var(--primary-color) !important;
}
}
}
//
:deep(.w-e-modal) {
background-color: var(--bg-color-overlay) !important;
border: 1px solid var(--border-color) !important;
box-shadow: var(--box-shadow) !important;
.w-e-modal-header {
border-bottom: 1px solid var(--border-color-lighter) !important;
color: var(--text-color-primary) !important;
}
.w-e-modal-body {
background-color: var(--bg-color-overlay) !important;
color: var(--text-color-primary) !important;
input,
textarea,
select {
background-color: var(--fill-color-blank) !important;
border-color: var(--border-color) !important;
color: var(--text-color-primary) !important;
&:focus {
border-color: var(--primary-color) !important;
}
}
}
.w-e-modal-footer {
border-top: 1px solid var(--border-color-lighter) !important;
button {
background-color: var(--fill-color-blank) !important;
border-color: var(--border-color) !important;
color: var(--text-color-primary) !important;
&:hover {
background-color: var(--fill-color-light) !important;
border-color: var(--primary-color) !important;
color: var(--primary-color) !important;
}
}
}
}
//
:deep(.w-e-bar-item svg),
:deep(.w-e-bar-item .w-e-icon) {
fill: var(--text-color-primary) !important;
color: var(--text-color-primary) !important;
transition:
fill 0.2s ease,
color 0.2s ease !important;
}
:deep(.w-e-bar-item:hover:not(.disabled) svg),
:deep(.w-e-bar-item:hover:not(.disabled) .w-e-icon),
:deep(.w-e-bar-item.active svg),
:deep(.w-e-bar-item.active .w-e-icon) {
fill: var(--primary-color) !important;
color: var(--primary-color) !important;
}
//
:deep(.w-e-bar-divider) {
background-color: var(--border-color-lighter) !important;
}
//
&.focused {
border-color: var(--primary-color) !important;
box-shadow: 0 0 0 1px var(--primary-color) inset !important;
}
}
:deep(.w-e-text-container) {
background-color: var(--el-bg-color) !important;
}
//
[data-theme="dark"] {
.wang-editor-wrapper {
background-color: var(--fill-color-darker) !important;
border-color: var(--border-color) !important;
:deep(.w-e-text-container) {
background-color: var(--fill-color-darker) !important;
}
:deep(.w-e-text-container .w-e-text) {
background-color: var(--fill-color-darker) !important;
color: var(--text-color-primary) !important;
//
img {
border: 1px solid var(--border-color-lighter) !important;
border-radius: 4px !important;
}
// 线
hr {
border-top-color: var(--border-color-lighter) !important;
}
//
table tr:nth-child(even) {
background-color: var(--fill-color-extra-light) !important;
}
}
}
}
// Fix z-index for full-screen/maximize mode
:deep(.w-e-fullscreen),
:deep(.w-e-fullscreen *),
:deep(.w-e-modal),
:deep(.w-e-drop-panel) {
z-index: 99999 !important;
}
</style>