mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-26 14:23:48 +00:00
refactor(frontend): 重构前端样式体系,统一资源路径与代码规范
1. 重构SCSS样式结构,将原有分散的样式文件整合到custom目录下,统一使用@use替代@import 2. 删除冗余的全局样式文件,将样式逻辑内联到对应组件或集中到core目录 3. 新增DOMPurify依赖,对富文本内容添加XSS防护 4. 修复metrics端点暴露的兼容性问题,优化启动banner样式 5. 移除无用的权限校验代码,重构工作流组件路径与命名 6. 优化样式混合宏与响应式布局,修复部分组件样式冲突
This commit is contained in:
@@ -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 ?? "";
|
||||
|
||||
Reference in New Issue
Block a user