feat(web): 更新侧边栏组件和个人工作台组件,增强导航和业务展示功能

This commit is contained in:
caoxiaozhu
2026-05-14 15:43:28 +00:00
parent 056d6dbe22
commit 98f68c47b0
2 changed files with 158 additions and 32 deletions

View File

@@ -59,11 +59,11 @@
</div> </div>
</article> </article>
<div class="workbench-grid"> <div class="workbench-grid" :class="{ 'finance-grid': isFinanceUser }">
<article class="panel list-panel"> <article class="panel list-panel">
<div class="section-head"> <div class="section-head">
<div class="title-with-badge"> <div class="title-with-badge">
<h3>今日待办</h3> <h3>报销待办</h3>
<span class="alert-badge">{{ todoAlertCount }}</span> <span class="alert-badge">{{ todoAlertCount }}</span>
</div> </div>
<button type="button" class="link-action">查看全部 <i class="mdi mdi-chevron-right"></i></button> <button type="button" class="link-action">查看全部 <i class="mdi mdi-chevron-right"></i></button>
@@ -113,6 +113,58 @@
</div> </div>
</div> </div>
</article> </article>
<article v-if="isFinanceUser" class="panel list-panel">
<div class="section-head">
<div class="title-with-badge">
<h3>应收管理</h3>
<span class="alert-badge">{{ receivableAlertCount }}</span>
</div>
<button type="button" class="link-action">查看全部 <i class="mdi mdi-chevron-right"></i></button>
</div>
<div class="list-body">
<div v-for="item in receivableItems" :key="item.id" class="progress-row">
<div class="todo-icon" :style="{ '--icon-color': item.color }">
<i :class="item.icon"></i>
</div>
<div class="todo-copy progress-copy">
<strong>{{ item.title }}</strong>
<p>{{ item.date }}</p>
</div>
<strong class="progress-amount">{{ item.amount }}</strong>
<span class="progress-status" :class="item.tone">{{ item.status }}</span>
</div>
</div>
</article>
<article v-if="isFinanceUser" class="panel list-panel">
<div class="section-head">
<div class="title-with-badge">
<h3>应付管理</h3>
<span class="alert-badge">{{ payableAlertCount }}</span>
</div>
<button type="button" class="link-action">查看全部 <i class="mdi mdi-chevron-right"></i></button>
</div>
<div class="list-body">
<div v-for="item in payableItems" :key="item.id" class="progress-row">
<div class="todo-icon" :style="{ '--icon-color': item.color }">
<i :class="item.icon"></i>
</div>
<div class="todo-copy progress-copy">
<strong>{{ item.title }}</strong>
<p>{{ item.date }}</p>
</div>
<strong class="progress-amount">{{ item.amount }}</strong>
<span class="progress-status" :class="item.tone">{{ item.status }}</span>
</div>
</div>
</article>
</div> </div>
<article class="panel policy-panel"> <article class="panel policy-panel">
@@ -154,6 +206,13 @@ const props = defineProps({
const emit = defineEmits(['openAssistant']) const emit = defineEmits(['openAssistant'])
const { currentUser } = useSystemState() const { currentUser } = useSystemState()
// 判断是否为财务人员
const isFinanceUser = computed(() => {
const user = currentUser.value || {}
const roleCodes = Array.isArray(user.roleCodes) ? user.roleCodes : []
return roleCodes.includes('finance') || roleCodes.includes('accountant') || user.isAdmin
})
const { toast } = useToast() const { toast } = useToast()
const assistantDraft = ref('') const assistantDraft = ref('')
const fileInputRef = ref(null) const fileInputRef = ref(null)
@@ -233,7 +292,9 @@ function emitAssistant(payload) {
} }
async function loadLatestConversation() { async function loadLatestConversation() {
const payload = await fetchLatestConversation(resolveCurrentUserId(), SESSION_TYPE_EXPENSE) const payload = await fetchLatestConversation(resolveCurrentUserId(), SESSION_TYPE_EXPENSE, {
preferRecoverable: true
})
return payload?.found ? payload.conversation || null : null return payload?.found ? payload.conversation || null : null
} }
@@ -361,6 +422,76 @@ const progressItems = [
const progressAlertCount = progressItems.filter((item) => item.status !== '已到账').length const progressAlertCount = progressItems.filter((item) => item.status !== '已到账').length
const receivableItems = [
{
id: 'receivable-1',
title: '客户服务费收入',
amount: '¥15,800',
date: '2026-05-10',
status: '待收款',
tone: 'info',
icon: 'mdi mdi-account-cash-outline',
color: '#f59e0b'
},
{
id: 'receivable-2',
title: '项目进度款',
amount: '¥42,600',
date: '2026-05-08',
status: '部分收款',
tone: 'success',
icon: 'mdi mdi-cash-multiple',
color: '#10b981'
},
{
id: 'receivable-3',
title: '咨询服务费',
amount: '¥8,900',
date: '2026-05-05',
status: '逾期提醒',
tone: 'info',
icon: 'mdi mdi-bell-alert',
color: '#ef4444'
}
]
const receivableAlertCount = receivableItems.filter((item) => item.status === '逾期提醒' || item.status === '待收款').length
const payableItems = [
{
id: 'payable-1',
title: '供应商采购款',
amount: '¥28,500',
date: '2026-05-12',
status: '待付款',
tone: 'info',
icon: 'mdi mdi-truck-outline',
color: '#3b82f6'
},
{
id: 'payable-2',
title: '房租物业费',
amount: '¥12,800',
date: '2026-05-15',
status: '待审批',
tone: 'success',
icon: 'mdi mdi-office-building',
color: '#6366f1'
},
{
id: 'payable-3',
title: '软件服务费',
amount: '¥3,600',
date: '2026-05-01',
status: '已付款',
tone: 'mint',
icon: 'mdi mdi-server',
color: '#10b981'
}
]
const payableAlertCount = payableItems.filter((item) => item.status === '待付款' || item.status === '待审批').length
const policyItems = [ const policyItems = [
{ {
name: '差旅报销管理办法2026版', name: '差旅报销管理办法2026版',
@@ -725,6 +856,11 @@ watch(
gap: 20px; gap: 20px;
} }
.workbench-grid.finance-grid {
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.list-panel, .list-panel,
.policy-panel { .policy-panel {
padding: 20px 22px; padding: 20px 22px;
@@ -974,6 +1110,11 @@ watch(
.policy-row { .policy-row {
grid-template-columns: 1.8fr 1.8fr 1fr; grid-template-columns: 1.8fr 1.8fr 1fr;
} }
.workbench-grid.finance-grid {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
} }
@media (max-width: 1440px) { @media (max-width: 1440px) {
@@ -1040,7 +1181,8 @@ watch(
width: 176px; width: 176px;
} }
.workbench-grid { .workbench-grid,
.workbench-grid.finance-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
@@ -1107,6 +1249,14 @@ watch(
justify-self: start; justify-self: start;
} }
.workbench-grid.finance-grid {
gap: 16px;
}
.workbench-grid.finance-grid .list-panel {
padding: 16px 18px;
}
.policy-table { .policy-table {
border: 0; border: 0;
border-radius: 0; border-radius: 0;
@@ -1130,4 +1280,3 @@ watch(
} }
</style> </style>

View File

@@ -8,9 +8,6 @@
</svg> </svg>
</div> </div>
<strong class="brand-name">{{ displayCompanyName }}</strong> <strong class="brand-name">{{ displayCompanyName }}</strong>
<button class="brand-toggle" type="button" aria-label="打开 AI 助手" @click="emit('openChat')">
<i class="mdi mdi-chevron-double-left"></i>
</button>
</div> </div>
<nav class="rail-nav" aria-label="功能导航"> <nav class="rail-nav" aria-label="功能导航">
@@ -115,10 +112,10 @@ const displayCompanyName = computed(() => props.companyName || 'X-Financial')
.rail-brand { .rail-brand {
min-height: 86px; min-height: 86px;
display: grid; display: flex;
grid-template-columns: 32px minmax(0, 1fr) 28px;
align-items: center; align-items: center;
gap: 10px; justify-content: center;
gap: 12px;
padding: 22px 20px 18px; padding: 22px 20px 18px;
} }
@@ -137,31 +134,11 @@ const displayCompanyName = computed(() => props.companyName || 'X-Financial')
} }
.brand-name { .brand-name {
min-width: 0;
color: #0f172a; color: #0f172a;
font-size: 16px; font-size: 16px;
font-weight: 800; font-weight: 800;
letter-spacing: 0; letter-spacing: 0;
white-space: nowrap; white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.brand-toggle {
width: 28px;
height: 28px;
display: grid;
place-items: center;
border: 0;
border-radius: 7px;
background: transparent;
color: #718096;
transition: background 180ms var(--ease), color 180ms var(--ease);
}
.brand-toggle:hover {
background: #eef7f4;
color: #07936f;
} }
.rail-nav { .rail-nav {