style: 全局 UI 主题皮肤重构与样式模块化
引入 Element Plus 主题定制和主题皮肤 composable,将全局 样式拆分为组件级独立 CSS 文件(侧边栏、顶栏、工作台等), 统一色彩变量和间距规范,重构所有视图和组件样式以适配新 主题系统,优化图表和知识图谱组件视觉表现,提取审计和差 旅报销相关子组件。
This commit is contained in:
@@ -357,7 +357,7 @@ function observeResize() {
|
||||
}
|
||||
|
||||
.graph-eyebrow {
|
||||
color: #0f766e;
|
||||
color: var(--theme-primary-active);
|
||||
font-size: 12px;
|
||||
font-weight: 850;
|
||||
}
|
||||
@@ -515,7 +515,7 @@ function observeResize() {
|
||||
}
|
||||
|
||||
.inspector-title span {
|
||||
color: #0f766e;
|
||||
color: var(--theme-primary-active);
|
||||
font-size: 12px;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<div class="node-detail-panel">
|
||||
<section class="detail-section">
|
||||
<div class="detail-section-head">
|
||||
<strong>节点说明</strong>
|
||||
<span>{{ safeNode.type || '实体' }}</span>
|
||||
<strong>节点摘要</strong>
|
||||
<span>{{ safeNode.type || '未知类型' }}</span>
|
||||
</div>
|
||||
<div v-if="descriptionItems.length" class="description-list">
|
||||
<p v-for="(description, index) in descriptionItems" :key="index">
|
||||
@@ -11,7 +11,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div v-else class="detail-empty compact">
|
||||
当前节点暂无 LightRAG 描述,完成新的归集后会从图谱属性中补充。
|
||||
暂无描述,LightRAG 还没有为该节点生成可展示的摘要。
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
</div>
|
||||
<dl class="property-grid">
|
||||
<div>
|
||||
<dt>类型</dt>
|
||||
<dd>{{ safeNode.type || '实体' }}</dd>
|
||||
<dt>节点类型</dt>
|
||||
<dd>{{ safeNode.type || '未知类型' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>关系数</dt>
|
||||
<dt>关联度</dt>
|
||||
<dd>{{ safeNode.degree || 0 }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
<section class="detail-section relation-section">
|
||||
<div class="detail-section-head">
|
||||
<strong>关系语境</strong>
|
||||
<strong>关联关系</strong>
|
||||
<span>{{ relationRows.length }} 条</span>
|
||||
</div>
|
||||
<div v-if="relationRows.length" class="relation-detail-list">
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="detail-empty compact">暂无关联关系。</div>
|
||||
<div v-else class="detail-empty compact">暂无关联关系</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
@@ -139,7 +139,7 @@ const relationRows = computed(() => {
|
||||
raw: relation,
|
||||
peerName,
|
||||
type: String(relation.type || '关联').trim(),
|
||||
directionLabel: isOutgoing ? '指向' : '来自',
|
||||
directionLabel: isOutgoing ? '指向' : '来源',
|
||||
description: String(relation.description || '').trim(),
|
||||
keywords: dedupeTextItems(relation.keywords).slice(0, 6)
|
||||
}
|
||||
@@ -224,7 +224,7 @@ function formatPropertyKey(key) {
|
||||
.description-list p {
|
||||
margin: 0;
|
||||
padding: 9px;
|
||||
border: 1px solid #dbeafe;
|
||||
border: 1px solid color-mix(in srgb, var(--theme-primary) 18%, #ffffff);
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
color: #1e293b;
|
||||
@@ -288,8 +288,8 @@ function formatPropertyKey(key) {
|
||||
align-items: center;
|
||||
padding: 0 7px;
|
||||
border-radius: 999px;
|
||||
background: #e0f2fe;
|
||||
color: #075985;
|
||||
background: var(--theme-primary-light-9);
|
||||
color: var(--theme-primary-active);
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
}
|
||||
@@ -317,7 +317,7 @@ function formatPropertyKey(key) {
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
gap: 7px;
|
||||
padding: 9px;
|
||||
border: 1px solid #dbeafe;
|
||||
border: 1px solid color-mix(in srgb, var(--theme-primary) 18%, #ffffff);
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
@@ -326,8 +326,8 @@ function formatPropertyKey(key) {
|
||||
}
|
||||
|
||||
.relation-detail-list button:hover {
|
||||
border-color: #60a5fa;
|
||||
background: #eff6ff;
|
||||
border-color: color-mix(in srgb, var(--theme-primary) 38%, #ffffff);
|
||||
background: var(--theme-primary-light-9);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ function formatPropertyKey(key) {
|
||||
}
|
||||
|
||||
.relation-detail-list strong {
|
||||
color: #1d4ed8;
|
||||
color: var(--theme-primary-active);
|
||||
}
|
||||
|
||||
.relation-detail-list p,
|
||||
@@ -359,8 +359,8 @@ function formatPropertyKey(key) {
|
||||
}
|
||||
|
||||
.keyword-row span {
|
||||
background: #ecfdf5;
|
||||
color: #047857;
|
||||
background: var(--theme-primary-light-9);
|
||||
color: var(--theme-primary-active);
|
||||
}
|
||||
|
||||
.detail-empty {
|
||||
|
||||
@@ -107,7 +107,7 @@ function formatElapsed(startedAt, finishedAt) {
|
||||
}
|
||||
|
||||
.info-title span {
|
||||
color: #0f766e;
|
||||
color: var(--theme-primary-active);
|
||||
font-size: 12px;
|
||||
font-weight: 850;
|
||||
}
|
||||
@@ -129,8 +129,8 @@ function formatElapsed(startedAt, finishedAt) {
|
||||
}
|
||||
|
||||
.info-title > strong.success {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
background: var(--success-soft);
|
||||
color: var(--success-active);
|
||||
}
|
||||
|
||||
.info-title > strong.warning {
|
||||
|
||||
@@ -57,7 +57,7 @@ const model = computed(() => buildKnowledgeIngestLogModel(props.run))
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
color: #0f766e;
|
||||
color: var(--theme-primary-active);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ const model = computed(() => buildKnowledgeIngestLogModel(props.run))
|
||||
display: block;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(90deg, #0f766e, #2563eb);
|
||||
background: linear-gradient(90deg, var(--theme-primary-active), var(--theme-secondary));
|
||||
transition: width 0.24s ease;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import { useThemeColors } from '../../composables/useThemeColors.js'
|
||||
|
||||
const MAX_VISIBLE_NODES = 72
|
||||
const MAX_VISIBLE_EDGES = 180
|
||||
const MAX_RELATION_PREVIEW = 40
|
||||
|
||||
const NODE_TONES = {
|
||||
const BASE_NODE_TONES = {
|
||||
hub: {
|
||||
fill: '#2563eb',
|
||||
stroke: '#dbeafe',
|
||||
@@ -12,10 +14,10 @@ const NODE_TONES = {
|
||||
shadow: 'rgba(37, 99, 235, 0.20)'
|
||||
},
|
||||
strong: {
|
||||
fill: '#0f766e',
|
||||
stroke: '#ccfbf1',
|
||||
halo: '#5eead4',
|
||||
shadow: 'rgba(15, 118, 110, 0.18)'
|
||||
fill: '#3a7ca5',
|
||||
stroke: '#eaf4fa',
|
||||
halo: '#d4e8f3',
|
||||
shadow: 'rgba(58, 124, 165, 0.18)'
|
||||
},
|
||||
accent: {
|
||||
fill: '#d97706',
|
||||
@@ -40,8 +42,18 @@ const NODE_TONES = {
|
||||
export function useKnowledgeIngestGraph(props) {
|
||||
const graphQuery = ref('')
|
||||
const activeNodeId = ref('')
|
||||
const themeColors = useThemeColors()
|
||||
|
||||
const allRelations = computed(() => normalizeRelations(props.graph?.relations))
|
||||
const nodeTones = computed(() => ({
|
||||
...BASE_NODE_TONES,
|
||||
strong: {
|
||||
fill: themeColors.value.chartPrimary,
|
||||
stroke: themeColors.value.primarySoft,
|
||||
halo: themeColors.value.primarySoft,
|
||||
shadow: toRgba(themeColors.value.chartPrimary, 0.18)
|
||||
}
|
||||
}))
|
||||
const rankedNodes = computed(() => buildRankedNodes(props.graph, allRelations.value))
|
||||
const visibleNodes = computed(() => {
|
||||
const query = graphQuery.value.toLowerCase()
|
||||
@@ -83,7 +95,7 @@ export function useKnowledgeIngestGraph(props) {
|
||||
selectedNodeRelations.value.filter((relation) => relation.source === selectedNode.value?.name)
|
||||
)
|
||||
const graphData = computed(() => ({
|
||||
nodes: visibleNodes.value.map((node) => toG6Node(node)),
|
||||
nodes: visibleNodes.value.map((node) => toG6Node(node, nodeTones.value)),
|
||||
edges: visibleRelations.value
|
||||
.map((relation, index) => toG6Edge(relation, index, nodeIdByName.value))
|
||||
.filter(Boolean)
|
||||
@@ -179,9 +191,9 @@ function buildRankedNodes(graph, relations) {
|
||||
})
|
||||
}
|
||||
|
||||
function toG6Node(node) {
|
||||
function toG6Node(node, nodeTones) {
|
||||
const tone = resolveNodeTone(node)
|
||||
const palette = NODE_TONES[tone]
|
||||
const palette = nodeTones[tone] || nodeTones.normal || BASE_NODE_TONES.normal
|
||||
const size = clamp(34 + Math.sqrt(Math.max(node.degree, 1)) * 13, 38, node.rank === 1 ? 82 : 70)
|
||||
const opacity = node.matchesQuery ? 1 : 0.24
|
||||
|
||||
@@ -241,6 +253,20 @@ function toG6Node(node) {
|
||||
}
|
||||
}
|
||||
|
||||
function toRgba(hexColor, alpha) {
|
||||
const normalized = String(hexColor || '').trim()
|
||||
const matched = normalized.match(/^#([0-9a-f]{6})$/i)
|
||||
if (!matched) {
|
||||
return `rgba(58, 124, 165, ${alpha})`
|
||||
}
|
||||
|
||||
const value = matched[1]
|
||||
const red = parseInt(value.slice(0, 2), 16)
|
||||
const middle = parseInt(value.slice(2, 4), 16)
|
||||
const blue = parseInt(value.slice(4, 6), 16)
|
||||
return `rgba(${red}, ${middle}, ${blue}, ${alpha})`
|
||||
}
|
||||
|
||||
function toG6Edge(relation, index, nodeIdByName) {
|
||||
const sourceId = nodeIdByName.get(relation.source)
|
||||
const targetId = nodeIdByName.get(relation.target)
|
||||
|
||||
Reference in New Issue
Block a user