style: 全局 UI 主题皮肤重构与样式模块化

引入 Element Plus 主题定制和主题皮肤 composable,将全局
样式拆分为组件级独立 CSS 文件(侧边栏、顶栏、工作台等),
统一色彩变量和间距规范,重构所有视图和组件样式以适配新
主题系统,优化图表和知识图谱组件视觉表现,提取审计和差
旅报销相关子组件。
This commit is contained in:
caoxiaozhu
2026-05-27 09:17:57 +08:00
parent df49103f23
commit 2dcc72102d
112 changed files with 10983 additions and 8996 deletions

View File

@@ -111,18 +111,18 @@ function handleCancel() {
-webkit-backdrop-filter: blur(10px);
}
.shared-confirm-card {
width: min(480px, 100%);
display: grid;
gap: 14px;
padding: 24px;
border: 1px solid rgba(16, 185, 129, 0.14);
border-radius: 24px;
background:
radial-gradient(circle at top left, rgba(16, 185, 129, 0.12), transparent 36%),
linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(247, 250, 252, 0.98));
box-shadow: 0 28px 56px rgba(15, 23, 42, 0.18);
}
.shared-confirm-card {
width: min(480px, 100%);
display: grid;
gap: 14px;
padding: 24px;
border: 1px solid rgba(var(--theme-primary-rgb, 58, 124, 165), 0.14);
border-radius: 8px;
background:
radial-gradient(circle at top left, rgba(var(--theme-primary-rgb, 58, 124, 165), 0.10), transparent 36%),
linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(247, 250, 252, 0.98));
box-shadow: 0 28px 56px rgba(15, 23, 42, 0.18);
}
.shared-confirm-badge {
display: inline-flex;
@@ -135,20 +135,20 @@ function handleCancel() {
font-weight: 800;
}
.shared-confirm-badge.info {
background: rgba(59, 130, 246, 0.12);
color: #1d4ed8;
}
.shared-confirm-badge.info {
background: var(--theme-primary-soft);
color: var(--theme-primary-active);
}
.shared-confirm-badge.warning {
background: rgba(245, 158, 11, 0.14);
color: #b45309;
}
.shared-confirm-badge.danger {
background: rgba(239, 68, 68, 0.12);
color: #dc2626;
}
.shared-confirm-badge.warning {
background: var(--warning-soft);
color: var(--warning-active);
}
.shared-confirm-badge.danger {
background: var(--danger-soft);
color: var(--danger-hover);
}
.shared-confirm-card h4 {
margin: 0;
@@ -197,7 +197,7 @@ function handleCancel() {
justify-content: center;
gap: 8px;
padding: 0 16px;
border-radius: 14px;
border-radius: 6px;
font-size: 14px;
font-weight: 800;
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease, background 160ms ease;
@@ -214,20 +214,20 @@ function handleCancel() {
color: #fff;
}
.shared-confirm-btn.confirm.primary {
background: linear-gradient(135deg, #10b981, #059669);
box-shadow: 0 12px 24px rgba(5, 150, 105, 0.22);
}
.shared-confirm-btn.confirm.danger {
background: linear-gradient(135deg, #ef4444, #dc2626);
box-shadow: 0 12px 24px rgba(220, 38, 38, 0.22);
}
.shared-confirm-btn.cancel:hover:not(:disabled) {
border-color: rgba(16, 185, 129, 0.3);
color: #047857;
}
.shared-confirm-btn.confirm.primary {
background: linear-gradient(135deg, var(--theme-primary), var(--theme-primary-active));
box-shadow: 0 12px 24px rgba(var(--theme-primary-rgb, 58, 124, 165), 0.20);
}
.shared-confirm-btn.confirm.danger {
background: linear-gradient(135deg, var(--danger), var(--danger-hover));
box-shadow: 0 12px 24px rgba(var(--danger-rgb), 0.22);
}
.shared-confirm-btn.cancel:hover:not(:disabled) {
border-color: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.3);
color: var(--theme-primary-active);
}
.shared-confirm-btn.confirm:hover:not(:disabled) {
transform: translateY(-1px);
@@ -259,7 +259,7 @@ function handleCancel() {
width: min(360px, 100%);
gap: 8px;
padding: 16px;
border-radius: 12px;
border-radius: 6px;
background: #fff;
}
@@ -281,7 +281,7 @@ function handleCancel() {
min-width: 76px;
min-height: 30px;
padding: 0 10px;
border-radius: 7px;
border-radius: 4px;
font-size: 12px;
}
@@ -290,10 +290,10 @@ function handleCancel() {
padding: 18px;
}
.shared-confirm-card {
padding: 20px;
border-radius: 20px;
}
.shared-confirm-card {
padding: 20px;
border-radius: 8px;
}
.shared-confirm-card h4 {
font-size: 19px;

View File

@@ -0,0 +1,72 @@
<template>
<ElSelect
class="enterprise-select"
popper-class="enterprise-select-popper"
:model-value="modelValue"
:placeholder="placeholder"
:disabled="disabled"
:clearable="clearable"
:filterable="filterable"
:size="size"
:teleported="teleported"
@change="handleChange"
>
<ElOption
v-for="option in normalizedOptions"
:key="option.key"
:label="option.label"
:value="option.value"
:disabled="option.disabled"
/>
</ElSelect>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
modelValue: { type: [String, Number, Boolean], default: '' },
options: { type: Array, required: true },
placeholder: { type: String, default: '请选择' },
disabled: { type: Boolean, default: false },
clearable: { type: Boolean, default: false },
filterable: { type: Boolean, default: false },
size: { type: String, default: 'default' },
teleported: { type: Boolean, default: true }
})
const emit = defineEmits(['update:modelValue', 'change'])
const normalizedOptions = computed(() =>
props.options.map((option, index) => {
if (option && typeof option === 'object') {
const value = option.value ?? option.label ?? ''
return {
key: `${value}-${index}`,
label: String(option.label ?? value),
value,
disabled: Boolean(option.disabled)
}
}
return {
key: `${option}-${index}`,
label: String(option),
value: option,
disabled: false
}
})
)
function handleChange(value) {
emit('update:modelValue', value)
emit('change', value)
}
</script>
<style scoped>
.enterprise-select {
width: 100%;
}
</style>

View File

@@ -54,7 +54,7 @@ const props = defineProps({
artLabel: { type: String, default: 'EMPTY' },
actionLabel: { type: String, default: '' },
actionIcon: { type: String, default: '' },
tone: { type: String, default: 'emerald' },
tone: { type: String, default: 'theme' },
tips: {
type: Array,
default: () => []
@@ -66,11 +66,11 @@ defineEmits(['action'])
<style scoped>
.table-empty-state {
--accent: #10b981;
--accent-deep: #059669;
--accent-soft: rgba(16, 185, 129, 0.16);
--accent-line: rgba(16, 185, 129, 0.24);
--accent-mist: rgba(16, 185, 129, 0.08);
--accent: var(--theme-primary);
--accent-deep: var(--theme-primary-active);
--accent-soft: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.16);
--accent-line: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.24);
--accent-mist: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
display: flex;
flex-direction: column;
align-items: center;
@@ -82,20 +82,21 @@ defineEmits(['action'])
width: 100%;
}
.table-empty-state.theme,
.table-empty-state.sky {
--accent: #0ea5e9;
--accent-deep: #0284c7;
--accent-soft: rgba(14, 165, 233, 0.16);
--accent-line: rgba(14, 165, 233, 0.24);
--accent-mist: rgba(14, 165, 233, 0.08);
--accent: var(--theme-primary);
--accent-deep: var(--theme-primary-active);
--accent-soft: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.16);
--accent-line: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.24);
--accent-mist: rgba(var(--theme-primary-rgb, 58, 124, 165), 0.08);
}
.table-empty-state.amber {
--accent: #f59e0b;
--accent-deep: #d97706;
--accent-soft: rgba(245, 158, 11, 0.16);
--accent-line: rgba(245, 158, 11, 0.24);
--accent-mist: rgba(245, 158, 11, 0.08);
--accent: var(--warning);
--accent-deep: var(--warning-hover);
--accent-soft: rgba(var(--warning-rgb), 0.16);
--accent-line: rgba(var(--warning-rgb), 0.24);
--accent-mist: rgba(var(--warning-rgb), 0.08);
}
.table-empty-state.slate {
@@ -106,6 +107,14 @@ defineEmits(['action'])
--accent-mist: rgba(100, 116, 139, 0.08);
}
.table-empty-state.success {
--accent: var(--success);
--accent-deep: var(--success-hover);
--accent-soft: rgba(var(--success-rgb), 0.16);
--accent-line: rgba(var(--success-rgb), 0.24);
--accent-mist: rgba(var(--success-rgb), 0.08);
}
.table-empty-state__art {
position: relative;
width: min(240px, 100%);
@@ -144,7 +153,7 @@ defineEmits(['action'])
height: 82px;
padding: 14px 14px 12px;
border: 1px solid rgba(255, 255, 255, 0.88);
border-radius: 18px;
border-radius: 8px;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(245, 248, 250, 0.94));
box-shadow: 0 18px 30px rgba(15, 23, 42, 0.08);
@@ -208,7 +217,7 @@ defineEmits(['action'])
display: grid;
place-items: center;
border: 1px solid rgba(255, 255, 255, 0.86);
border-radius: 24px;
border-radius: 10px;
background:
radial-gradient(circle at 30% 20%, rgba(255, 255, 255, 0.88), rgba(255, 255, 255, 0.3) 42%),
linear-gradient(135deg, var(--accent), var(--accent-deep));
@@ -287,7 +296,7 @@ defineEmits(['action'])
gap: 8px;
padding: 0 16px;
border: 1px solid transparent;
border-radius: 12px;
border-radius: 6px;
background: linear-gradient(135deg, var(--accent), var(--accent-deep));
color: #fff;
font-size: 13px;
@@ -316,13 +325,13 @@ defineEmits(['action'])
.table-empty-state__sheet {
width: 110px;
height: 74px;
border-radius: 16px;
border-radius: 8px;
}
.table-empty-state__badge {
width: 60px;
height: 60px;
border-radius: 20px;
border-radius: 10px;
}
.table-empty-state__badge i {

View File

@@ -28,8 +28,8 @@ const props = defineProps({
},
tone: {
type: String,
default: 'emerald',
validator: (value) => ['emerald', 'sky'].includes(value)
default: 'theme',
validator: (value) => ['theme', 'sky', 'success'].includes(value)
},
title: { type: String, default: '' },
message: { type: String, default: '' },
@@ -45,15 +45,21 @@ const ariaLabel = computed(() => [props.title, props.message].filter(Boolean).jo
<style scoped>
.table-loading {
--accent: #10b981;
--accent-deep: #059669;
--accent: var(--theme-primary);
--accent-deep: var(--theme-primary-active);
width: 100%;
color: #64748b;
}
.table-loading.theme,
.table-loading.sky {
--accent: #0ea5e9;
--accent-deep: #0284c7;
--accent: var(--theme-primary);
--accent-deep: var(--theme-primary-active);
}
.table-loading.success {
--accent: var(--success);
--accent-deep: var(--success-hover);
}
.table-loading.panel {
@@ -96,7 +102,7 @@ const ariaLabel = computed(() => [props.title, props.message].filter(Boolean).jo
gap: 8px;
min-height: 0;
padding: 0;
color: #0369a1;
color: #255b7d;
}
.table-loading__spinner {

View File

@@ -19,7 +19,7 @@ import { workbenchIconMap } from '../../utils/workbenchIconAssets.js'
const props = defineProps({
iconKey: { type: String, required: true },
color: { type: String, default: '#10b981' },
color: { type: String, default: 'var(--theme-primary)' },
accent: { type: String, default: '' }
})