feat(system): 登录页重做、布局白化与首页 Bento 仪表盘(视觉体系统一落地)
- 登录页:雾灰点阵底 + 右上涟漪同心圆 + 顶部靛蓝天光;居中纸白卡片(品牌区/欢迎回来/表单);逻辑不变 - BasicLayout:侧栏深色面板改为中性灰微渐变 + 白底浮起当前项(左 2px 靛蓝条);logo 区水线;内容区雾灰点阵 - 首页:Bento Grid 仪表盘(参数 16 项/岗位 3 个为真实数据;绩效/回款/待办为业务上线前的精致空态) - AppMenu:激活项白底浮起样式,菜单变量随浅色侧栏调整 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
4c442b8cf2
commit
d7fbec1177
5 files changed
+545
-292
No files matched your search
@@ -1,49 +1,350 @@
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import * as businessRoleApi from '@/api/businessRoles'
|
||||
import * as paramsApi from '@/api/params'
|
||||
import { handledError } from '@/utils/error'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
/**
|
||||
* 首页:登录链路验证页。
|
||||
* 首页仪表盘(Bento Grid,2026-10-12 视觉方向)。
|
||||
*
|
||||
* 顶部栏(用户信息、退出登录)已由 BasicLayout 提供,本页只保留内容区;
|
||||
* 用户名等字段的展示是端到端脚本的断言对象,调整时需同步 e2e。
|
||||
* M1 阶段:计提参数与岗位配置已就绪(真实数据);绩效/回款/待办等业务卡
|
||||
* 展示空态 —— 业务数据管理上线后接入真实统计(图表用 ECharts 渲染)。
|
||||
* 空态文案遵循全局规范:空态是邀请,说明「这里将出现什么、怎么让它出现」。
|
||||
*/
|
||||
const userStore = useUserStore()
|
||||
const roleCount = ref<number | null>(null)
|
||||
const paramCount = ref<number | null>(null)
|
||||
const latestVersion = ref<string | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
// 三项统计各自容错:一张卡失败不影响其余卡片
|
||||
void (async () => {
|
||||
try {
|
||||
const page = await businessRoleApi.listBusinessRoles({ pageNum: 1, pageSize: 1 })
|
||||
roleCount.value = page.total
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
}
|
||||
})()
|
||||
void (async () => {
|
||||
try {
|
||||
const catalog = await paramsApi.getCatalog()
|
||||
paramCount.value = catalog.length
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
}
|
||||
})()
|
||||
void (async () => {
|
||||
try {
|
||||
const page = await paramsApi.listBatches({ pageNum: 1, pageSize: 1 })
|
||||
latestVersion.value = page.list[0]?.version_no ?? null
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
}
|
||||
})()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<span>登录状态验证通过</span>
|
||||
</template>
|
||||
<div>
|
||||
<div class="page-head">
|
||||
<h1 class="page-title">仪表盘</h1>
|
||||
<p class="page-desc">系统概览 · 计提参数与岗位配置就绪,业务数据管理上线后接入核算统计</p>
|
||||
<span class="waterline"></span>
|
||||
</div>
|
||||
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="用户 ID">{{ userStore.userInfo?.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用户名">{{ userStore.userInfo?.username }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">
|
||||
{{ userStore.userInfo?.real_name || '—' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="角色">
|
||||
{{ userStore.roles.join('、') || '—' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="上次登录">
|
||||
{{ formatDateTime(userStore.userInfo?.last_login_time) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="bento">
|
||||
<!-- 绩效总额:业务上线前的空态 -->
|
||||
<div class="bento-card b-2x2">
|
||||
<div class="b-eyebrow">本月绩效总额</div>
|
||||
<div class="b-empty">
|
||||
<div class="b-empty-title">暂无核算数据</div>
|
||||
<div class="b-empty-desc">回款录入并核算后,这里将显示本月绩效总额与趋势</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
class="dashboard-hint"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="这是登录链路的验证页"
|
||||
description="刷新页面可验证会话恢复(守卫会调用 /me);「系统管理」菜单已接入用户管理、角色管理与日志查询。"
|
||||
/>
|
||||
</el-card>
|
||||
<!-- 回款趋势:业务上线前的空态 -->
|
||||
<div class="bento-card b-2x2">
|
||||
<div class="b-eyebrow">近 6 个月回款</div>
|
||||
<div class="b-empty">
|
||||
<div class="b-empty-title">暂无回款数据</div>
|
||||
<div class="b-empty-desc">业务数据管理上线后,此处展示按月回款趋势</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 待办 -->
|
||||
<div class="bento-card b-2x1">
|
||||
<div class="b-eyebrow">待办</div>
|
||||
<div class="b-empty-mini">业务数据管理上线后,待核算回款会出现在这里</div>
|
||||
</div>
|
||||
|
||||
<!-- 营销人员:人员管理上线后接入 -->
|
||||
<div class="bento-card">
|
||||
<div class="b-eyebrow">营销人员</div>
|
||||
<div class="b-empty-mini">人员管理上线后同步</div>
|
||||
</div>
|
||||
|
||||
<!-- 岗位角色:真实数据 -->
|
||||
<div class="bento-card">
|
||||
<div class="b-eyebrow">岗位角色</div>
|
||||
<div class="b-num">{{ roleCount ?? '—' }}<small>个</small></div>
|
||||
<button class="b-link" type="button" @click="router.push('/system/business-roles')">
|
||||
管理 ›
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 计提参数:真实数据 -->
|
||||
<div class="bento-card">
|
||||
<div class="b-eyebrow">计提参数</div>
|
||||
<div class="b-num">{{ paramCount ?? '—' }}<small>项</small></div>
|
||||
<button class="b-link" type="button" @click="router.push('/system/params')">
|
||||
维护 ›
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 常用操作 -->
|
||||
<div class="bento-card b-2x1">
|
||||
<div class="b-eyebrow">常用操作</div>
|
||||
<div class="b-links">
|
||||
<button class="b-op" type="button" @click="router.push('/system/params')">
|
||||
计提参数
|
||||
</button>
|
||||
<button class="b-op" type="button" @click="router.push('/system/business-roles')">
|
||||
岗位角色
|
||||
</button>
|
||||
<button class="b-op" type="button" @click="router.push('/system/logs')">
|
||||
操作日志
|
||||
</button>
|
||||
<span class="b-op b-op-disabled" title="业务数据管理上线后开放">录入回款</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- T+ 同步 -->
|
||||
<div class="bento-card">
|
||||
<div class="b-eyebrow">T+ 同步</div>
|
||||
<div class="b-state">待接入</div>
|
||||
<div class="b-sub">每周一次 · 手动触发</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dashboard-hint {
|
||||
margin-top: 18px;
|
||||
.page-head {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--ys-ink);
|
||||
}
|
||||
|
||||
.page-desc {
|
||||
margin: 4px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
.waterline {
|
||||
display: block;
|
||||
width: 56px;
|
||||
height: 2px;
|
||||
margin-top: 10px;
|
||||
border-radius: 1px;
|
||||
background: linear-gradient(90deg, var(--ys-indigo), rgba(46, 75, 168, 0));
|
||||
}
|
||||
|
||||
/* Bento Grid:4 列基格,卡片按重要性混排大小 */
|
||||
.bento {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-auto-rows: 102px;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.bento-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 17px 20px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(180deg, #ffffff, #fcfcfe);
|
||||
border: 1px solid var(--ys-line);
|
||||
border-radius: 10px;
|
||||
box-shadow:
|
||||
0 1px 2px rgba(28, 35, 51, 0.04),
|
||||
0 3px 10px rgba(28, 35, 51, 0.03);
|
||||
transition:
|
||||
transform 180ms cubic-bezier(0.25, 0.1, 0.25, 1),
|
||||
box-shadow 180ms cubic-bezier(0.25, 0.1, 0.25, 1);
|
||||
}
|
||||
|
||||
.bento-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 1px 2px rgba(28, 35, 51, 0.05),
|
||||
0 6px 18px rgba(28, 35, 51, 0.07);
|
||||
}
|
||||
|
||||
.b-2x2 {
|
||||
grid-column: span 2;
|
||||
grid-row: span 2;
|
||||
}
|
||||
|
||||
.b-2x1 {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.b-eyebrow {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--ys-ink-2);
|
||||
}
|
||||
|
||||
.b-num {
|
||||
margin-top: auto;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--ys-ink);
|
||||
}
|
||||
|
||||
.b-num small {
|
||||
margin-left: 3px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
.b-sub {
|
||||
margin-top: 3px;
|
||||
font-size: 11px;
|
||||
color: var(--ys-ink-3);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.b-state {
|
||||
margin-top: auto;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--ys-ink-2);
|
||||
}
|
||||
|
||||
.b-empty {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.b-empty-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--ys-ink-2);
|
||||
}
|
||||
|
||||
.b-empty-desc {
|
||||
max-width: 260px;
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--ys-ink-3);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.b-empty-mini {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
font-size: 12px;
|
||||
color: var(--ys-ink-3);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.b-links {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.b-link {
|
||||
padding: 0;
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--ys-indigo);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.b-link:hover {
|
||||
color: var(--ys-indigo-deep);
|
||||
}
|
||||
|
||||
.b-op {
|
||||
height: 30px;
|
||||
padding: 0 13px;
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
color: var(--ys-ink);
|
||||
cursor: pointer;
|
||||
background: linear-gradient(180deg, #ffffff, #fbfcfe);
|
||||
border: 1px solid var(--ys-line);
|
||||
border-radius: 7px;
|
||||
transition:
|
||||
color 150ms cubic-bezier(0.25, 0.1, 0.25, 1),
|
||||
border-color 150ms cubic-bezier(0.25, 0.1, 0.25, 1);
|
||||
}
|
||||
|
||||
button.b-op:hover {
|
||||
color: var(--ys-indigo);
|
||||
border-color: rgba(46, 75, 168, 0.45);
|
||||
}
|
||||
|
||||
.b-op-disabled {
|
||||
color: var(--ys-ink-3);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.bento {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.bento {
|
||||
grid-template-columns: 1fr;
|
||||
grid-auto-rows: auto;
|
||||
}
|
||||
|
||||
.bento-card {
|
||||
min-height: 90px;
|
||||
}
|
||||
|
||||
.b-2x2,
|
||||
.b-2x1 {
|
||||
grid-column: span 1;
|
||||
grid-row: span 1;
|
||||
min-height: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.bento-card {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user