style: 全局 UI 主题皮肤重构与样式模块化
引入 Element Plus 主题定制和主题皮肤 composable,将全局 样式拆分为组件级独立 CSS 文件(侧边栏、顶栏、工作台等), 统一色彩变量和间距规范,重构所有视图和组件样式以适配新 主题系统,优化图表和知识图谱组件视觉表现,提取审计和差 旅报销相关子组件。
This commit is contained in:
@@ -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;
|
||||
|
||||
72
web/src/components/shared/EnterpriseSelect.vue
Normal file
72
web/src/components/shared/EnterpriseSelect.vue
Normal 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>
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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: '' }
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user