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

@@ -0,0 +1,334 @@
<template>
<article class="skill-list panel">
<nav class="status-tabs" aria-label="能力类型">
<button
v-for="tab in tabs"
:key="tab.id"
type="button"
:class="{ active: activeType === tab.id }"
@click="emit('update:activeType', tab.id)"
>
{{ tab.label }}
</button>
</nav>
<div class="list-toolbar">
<div class="filter-set">
<label class="search-filter">
<i class="mdi mdi-magnify"></i>
<input
:value="keyword"
type="search"
:placeholder="searchPlaceholder"
@input="emit('update:keyword', $event.target.value)"
/>
</label>
<AuditPickerFilter
id="domain"
title="选择业务域"
close-label="关闭业务域选择"
:active-filter-popover="activeFilterPopover"
:label="selectedDomainLabel"
:options="domainOptions"
:selected-value="selectedDomain"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('domain', $event)"
/>
<AuditPickerFilter
v-if="showOwnerFilter"
id="owner"
title="选择负责人"
close-label="关闭负责人选择"
:active-filter-popover="activeFilterPopover"
:label="selectedOwnerLabel"
:options="ownerOptions"
:selected-value="selectedOwner"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('owner', $event)"
/>
<AuditPickerFilter
v-if="showRiskLevelFilter"
id="riskLevel"
title="选择风险等级"
close-label="关闭风险等级选择"
:active-filter-popover="activeFilterPopover"
:label="selectedRiskLevelLabel"
:options="riskLevelOptions"
:selected-value="selectedRiskLevel"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('riskLevel', $event)"
/>
<AuditPickerFilter
v-if="showRiskScenarioFilter"
id="riskScenario"
title="选择使用场景"
close-label="关闭使用场景选择"
:active-filter-popover="activeFilterPopover"
:label="selectedRiskScenarioLabel"
:options="riskScenarioOptions"
:selected-value="selectedRiskScenario"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('riskScenario', $event)"
/>
<AuditPickerFilter
v-if="showOnlineFilter"
id="online"
title="选择上线状态"
close-label="关闭上线状态选择"
:active-filter-popover="activeFilterPopover"
:label="selectedOnlineStateLabel"
:options="onlineStateOptions"
:selected-value="selectedOnlineState"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('online', $event)"
/>
<AuditPickerFilter
v-if="showEnabledFilter"
id="enabled"
title="选择启用状态"
close-label="关闭启用状态选择"
:active-filter-popover="activeFilterPopover"
:label="selectedEnabledStateLabel"
:options="enabledStateOptions"
:selected-value="selectedEnabledState"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('enabled', $event)"
/>
<AuditPickerFilter
v-if="showStatusFilter"
id="status"
title="选择状态"
close-label="关闭状态选择"
:active-filter-popover="activeFilterPopover"
:label="selectedStatusLabel"
:options="statusOptions"
:selected-value="selectedStatus"
@toggle="emit('toggle-filter-popover', $event)"
@close="emit('close-filter-popover')"
@select="selectFilter('status', $event)"
/>
</div>
<div class="toolbar-actions">
<button
v-if="activeFilterTokens.length"
class="ghost-filter-btn"
type="button"
@click="emit('reset-filters')"
>
<i class="mdi mdi-filter-remove-outline"></i>
<span>清空筛选</span>
</button>
<button
class="create-btn"
type="button"
:disabled="!canCreateRiskRule"
@click="emit('create-risk-rule')"
>
<i class="mdi mdi-plus"></i>
<span>{{ createButtonLabel }}</span>
</button>
</div>
</div>
<p class="hint"><i class="mdi mdi-information-outline"></i> {{ hintText }}</p>
<div v-if="activeFilterTokens.length" class="active-filter-strip">
<span v-for="token in activeFilterTokens" :key="token" class="active-filter-chip">
{{ token }}
</span>
</div>
<div
class="table-wrap"
:class="{ 'is-empty': !loading && !errorMessage && !visibleSkills.length }"
>
<div v-if="loading" class="table-state">
<TableLoadingState
variant="panel"
:title="`${activeTabLabel}资产同步中`"
:message="`正在加载${activeTabLabel}资产`"
icon="mdi mdi-view-list-outline"
/>
</div>
<div v-else-if="errorMessage" class="table-state error">
<i class="mdi mdi-alert-circle-outline"></i>
<p>{{ errorMessage }}</p>
</div>
<TableEmptyState
v-else-if="!visibleSkills.length"
:eyebrow="auditEmptyState.eyebrow"
:title="auditEmptyState.title"
:description="auditEmptyState.desc"
:icon="auditEmptyState.icon"
:action-label="auditEmptyState.actionLabel"
:action-icon="auditEmptyState.actionIcon"
:tone="auditEmptyState.tone"
:art-label="auditEmptyState.artLabel"
:tips="auditEmptyState.tips"
@action="emit('empty-action')"
/>
<table v-else>
<thead>
<tr>
<th>{{ tableColumns.name }}</th>
<th>{{ tableColumns.category }}</th>
<th>{{ tableColumns.owner }}</th>
<th>{{ tableColumns.scope }}</th>
<th v-if="showRuntimeColumn">{{ tableColumns.runtime }}</th>
<th v-if="showVersionColumn">{{ tableColumns.version }}</th>
<th v-if="showStatusColumn">{{ tableColumns.status || '状态' }}</th>
<th v-if="showMetricColumn">{{ tableColumns.metric }}</th>
<th v-if="showOnlineColumn">是否上线</th>
<th v-if="showEnabledColumn">是否启用</th>
<th>{{ tableColumns.updatedAt || '最近更新' }}</th>
</tr>
</thead>
<tbody>
<tr
v-for="skill in visibleSkills"
:key="skill.id"
:class="{ 'is-disabled': skill.usesJsonRiskRule && skill.statusValue === 'generating' }"
@click="emit('open-asset-detail', skill)"
>
<td>
<div class="skill-name-cell">
<span class="skill-avatar" :class="skill.badgeTone">{{ skill.short }}</span>
<div>
<strong>{{ skill.name }}</strong>
<span class="skill-list-subtitle">{{ skill.listSubtitle || skill.summary }}</span>
</div>
</div>
</td>
<td>{{ skill.category }}</td>
<td>
<span
v-if="skill.usesJsonRiskRule"
class="json-risk-meta-badge"
:class="skill.riskLevelTone"
>
{{ skill.riskLevelLabel || '-' }}
</span>
<template v-else>{{ skill.owner }}</template>
</td>
<td><span class="scope-pill">{{ skill.scope }}</span></td>
<td v-if="showRuntimeColumn">{{ skill.model }}</td>
<td v-if="showVersionColumn">{{ skill.versionDisplay || skill.version }}</td>
<td v-if="showStatusColumn">
<span class="status-pill" :class="skill.statusTone">{{ skill.status }}</span>
</td>
<td v-if="showMetricColumn">{{ skill.hitRate }}</td>
<td v-if="showOnlineColumn">
<span class="status-pill" :class="skill.isOnlineTone">{{ skill.isOnlineLabel }}</span>
</td>
<td v-if="showEnabledColumn">
<span class="status-pill" :class="skill.isEnabledTone">{{ skill.isEnabledLabel }}</span>
</td>
<td>{{ skill.updatedAt }}</td>
</tr>
</tbody>
</table>
</div>
<footer v-if="!loading && !errorMessage && visibleSkills.length" class="list-foot">
<span class="page-summary">当前展示 {{ visibleSkills.length }} 条资产</span>
</footer>
</article>
</template>
<script setup>
import AuditPickerFilter from './AuditPickerFilter.vue'
import TableEmptyState from '../shared/TableEmptyState.vue'
import TableLoadingState from '../shared/TableLoadingState.vue'
defineOptions({
name: 'AuditAssetList'
})
defineProps({
tabs: { type: Array, default: () => [] },
activeType: { type: String, default: '' },
activeTabLabel: { type: String, default: '' },
keyword: { type: String, default: '' },
searchPlaceholder: { type: String, default: '' },
createButtonLabel: { type: String, default: '' },
hintText: { type: String, default: '' },
tableColumns: { type: Object, default: () => ({}) },
showRuntimeColumn: { type: Boolean, default: false },
showVersionColumn: { type: Boolean, default: false },
showMetricColumn: { type: Boolean, default: false },
showStatusColumn: { type: Boolean, default: false },
showOnlineColumn: { type: Boolean, default: false },
showEnabledColumn: { type: Boolean, default: false },
visibleSkills: { type: Array, default: () => [] },
auditEmptyState: { type: Object, default: () => ({}) },
loading: { type: Boolean, default: false },
errorMessage: { type: String, default: '' },
selectedDomain: { type: String, default: '' },
selectedOwner: { type: String, default: '' },
selectedRiskLevel: { type: String, default: '' },
selectedStatus: { type: String, default: '' },
selectedRiskScenario: { type: String, default: '' },
selectedOnlineState: { type: String, default: '' },
selectedEnabledState: { type: String, default: '' },
selectedDomainLabel: { type: String, default: '' },
selectedOwnerLabel: { type: String, default: '' },
selectedRiskLevelLabel: { type: String, default: '' },
selectedStatusLabel: { type: String, default: '' },
selectedRiskScenarioLabel: { type: String, default: '' },
selectedOnlineStateLabel: { type: String, default: '' },
selectedEnabledStateLabel: { type: String, default: '' },
showRiskScenarioFilter: { type: Boolean, default: false },
showOwnerFilter: { type: Boolean, default: false },
showRiskLevelFilter: { type: Boolean, default: false },
showStatusFilter: { type: Boolean, default: false },
showOnlineFilter: { type: Boolean, default: false },
showEnabledFilter: { type: Boolean, default: false },
domainOptions: { type: Array, default: () => [] },
ownerOptions: { type: Array, default: () => [] },
riskLevelOptions: { type: Array, default: () => [] },
statusOptions: { type: Array, default: () => [] },
riskScenarioOptions: { type: Array, default: () => [] },
onlineStateOptions: { type: Array, default: () => [] },
enabledStateOptions: { type: Array, default: () => [] },
activeFilterPopover: { type: String, default: '' },
activeFilterTokens: { type: Array, default: () => [] },
canCreateRiskRule: { type: Boolean, default: false }
})
const emit = defineEmits([
'update:activeType',
'update:keyword',
'toggle-filter-popover',
'close-filter-popover',
'select-filter',
'reset-filters',
'create-risk-rule',
'empty-action',
'open-asset-detail'
])
function selectFilter(type, value) {
emit('select-filter', type, value)
}
</script>
<style scoped src="../../assets/styles/views/audit-view.css"></style>
<style scoped src="../../assets/styles/views/audit-view-part2.css"></style>