refactor(frontend): 重构前端样式体系,统一资源路径与代码规范

1.  重构SCSS样式结构,将原有分散的样式文件整合到custom目录下,统一使用@use替代@import
2.  删除冗余的全局样式文件,将样式逻辑内联到对应组件或集中到core目录
3.  新增DOMPurify依赖,对富文本内容添加XSS防护
4.  修复metrics端点暴露的兼容性问题,优化启动banner样式
5.  移除无用的权限校验代码,重构工作流组件路径与命名
6.  优化样式混合宏与响应式布局,修复部分组件样式冲突
This commit is contained in:
zhangtao
2026-06-01 00:58:21 +08:00
parent c2135bb456
commit c0733d9861
50 changed files with 1880 additions and 1792 deletions
@@ -670,144 +670,6 @@ async function submitForget() {
}
</script>
<style scoped>
@import "@styles/fa-login.css";
</style>
<style lang="scss" scoped>
.login-page-panel {
:deep(.btn) {
border-radius: 8px;
}
}
html.dark .login-page-panel {
:deep(.text-g-800) {
color: rgb(255 255 255 / 78%) !important;
}
}
/* 副标题与表单块间距(login-page-form 在子组件内,须 :deep) */
:deep(.login-page-form) {
margin-top: 1rem;
}
:deep(.login-page-form .el-form-item) {
margin-bottom: 1.1rem;
}
:deep(.el-select__wrapper) {
min-height: 42px !important;
}
/* 仅账号登录底部「手机号 / 扫码」双列按钮需要 42px;手机号登录页的返回钮与主按钮同为 h-11,勿全局压高度 */
:deep(.login-secondary-actions .login-secondary-btn) {
height: 42px;
border-radius: 8px;
}
html.dark {
:deep(.login-page-form .el-input__wrapper) {
background-color: rgb(255 255 255 / 6%) !important;
box-shadow: 0 0 0 1px rgb(255 255 255 / 10%) inset !important;
}
:deep(.login-page-form .el-input__inner) {
color: rgb(255 255 255 / 92%);
}
:deep(.login-page-form .el-input__inner::placeholder) {
color: rgb(255 255 255 / 35%);
}
:deep(.login-page-form .el-select .el-select__wrapper) {
background-color: rgb(255 255 255 / 6%) !important;
box-shadow: 0 0 0 1px rgb(255 255 255 / 10%) inset !important;
}
:deep(.login-page-form .el-select__placeholder) {
color: rgb(255 255 255 / 40%);
}
:deep(.login-page-form .el-select__selected-item) {
color: rgb(255 255 255 / 92%);
}
:deep(.login-remember .el-checkbox__label) {
color: rgb(255 255 255 / 65%);
}
:deep(.login-secondary-btn) {
color: rgb(255 255 255 / 85%) !important;
background: transparent !important;
border-color: rgb(255 255 255 / 22%) !important;
border-radius: 8px;
&:hover {
color: var(--el-color-primary) !important;
background: var(--el-color-primary-light-9) !important;
border-color: var(--el-color-primary) !important;
}
}
:deep(.login-secondary-actions .login-secondary-btn) {
height: 42px;
}
}
.login-page-footer {
margin-top: 0;
text-align: center;
border-top: 1px solid var(--el-border-color-lighter);
.login-footer-text {
line-height: 1.65;
color: var(--el-text-color-secondary) !important;
}
.login-page-footer__link {
color: var(--el-text-color-regular);
text-decoration: none;
transition: color 0.2s ease;
&:hover {
color: var(--el-color-primary);
}
}
.login-page-footer__sep {
margin: 0 0.35rem;
color: var(--el-border-color);
}
.login-page-footer__record {
display: inline-block;
margin-left: 0.35rem;
color: var(--el-text-color-placeholder);
}
}
html.dark .login-page-footer {
border-top-color: rgb(255 255 255 / 8%);
.login-footer-text {
color: rgb(255 255 255 / 42%) !important;
}
.login-page-footer__link {
color: rgb(255 255 255 / 48%);
&:hover {
color: var(--el-color-primary);
}
}
.login-page-footer__sep {
color: rgb(255 255 255 / 25%);
}
.login-page-footer__record {
color: rgb(255 255 255 / 35%);
}
}
<style scoped lang="scss">
@use "@styles/custom/fa-login";
</style>
@@ -170,6 +170,7 @@ import FaUserTableSelect from "@/components/forms/fa-search-bar/FaUserTableSelec
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
import FaForm from "@/components/forms/fa-form/index.vue";
import { ElTag, ElMessage } from "element-plus";
import DOMPurify from "dompurify";
defineOptions({
name: "Notice",
@@ -307,9 +308,12 @@ const noticeDetailItems: import("@/components/others/fa-descriptions/index.vue")
{ label: "更新时间", prop: "updated_time" },
];
/** 详情富文本 HTML(用于预览) */
/** 详情富文本 HTML(用于预览,已做 XSS 净化) */
const detailContentHtml = computed({
get: () => detailFormData.value.notice_content ?? "",
get: () => {
const raw = detailFormData.value.notice_content ?? "";
return DOMPurify.sanitize(raw);
},
set: (v: string) => {
detailFormData.value.notice_content = v;
},
@@ -112,7 +112,7 @@
</template>
<template #reply_content>
<div v-if="detailFormData.reply" class="ticket-html-preview">
<div v-html="detailFormData.reply" />
<div v-html="sanitizedReply" />
</div>
<p v-else class="ticket-html-empty">暂无回复</p>
</template>
@@ -217,6 +217,7 @@ import FaForm from "@/components/forms/fa-form/index.vue";
import FaWangEditor from "@/components/forms/fa-wang-editor/index.vue";
import { ElTag, ElMessage, ElSelect, ElOption, ElRadioGroup, ElRadio } from "element-plus";
import { h } from "vue";
import DOMPurify from "dompurify";
defineOptions({
name: "Ticket",
@@ -384,14 +385,23 @@ const ticketDetailItems: import("@/components/others/fa-descriptions/index.vue")
{ label: "更新时间", prop: "updated_time" },
];
/** 详情富文本 HTML(用于预览) */
/** 详情富文本 HTML(用于预览,已做 XSS 净化) */
const detailContentHtml = computed({
get: () => detailFormData.value.ticket_content ?? "",
get: () => {
const raw = detailFormData.value.ticket_content ?? "";
return DOMPurify.sanitize(raw);
},
set: (v: string) => {
detailFormData.value.ticket_content = v;
},
});
/** 回复富文本 HTML(已做 XSS 净化) */
const sanitizedReply = computed(() => {
const raw = detailFormData.value.reply ?? "";
return raw ? DOMPurify.sanitize(raw) : "";
});
/** 详情是否有可视文本 */
const detailHasRenderableContent = computed(() => {
const raw = detailFormData.value.ticket_content ?? "";