feat: 引入 ECharts 统一图表并完善员工画像标签分页

后端优化员工行为画像服务和辅助函数,完善系统设置模型和
配置持久化,前端引入 ECharts 替换所有图表组件实现统一
渲染,新增员工画像标签分页器和数字员工工作记录组件,优
化工作台响应式布局和登录页过渡动画,完善预算中心和数字
员工页面样式细节。
This commit is contained in:
caoxiaozhu
2026-05-28 16:24:59 +08:00
parent 8a4a777be7
commit e384318046
53 changed files with 4698 additions and 2468 deletions

View File

@@ -13,6 +13,8 @@ import { setRuntimeApiBaseUrl } from '../services/api.js'
import { checkBackendHealth } from './useBackendHealth.js'
import { resolveDefaultAuthorizedRoute } from '../utils/accessControl.js'
import { useToast } from './useToast.js'
import { fetchSettings } from '../services/settings.js'
import { setThemeSkin } from './useThemeSkin.js'
const AUTH_STORAGE_KEY = 'x-financial-authenticated'
const AUTH_USERNAME_KEY = 'x-financial-auth-username'
@@ -86,68 +88,68 @@ function readStoredUsername() {
}
function buildAnonymousUser() {
return {
username: '',
name: '',
role: '',
department: '',
departmentName: '',
position: '',
grade: '',
employeeNo: '',
managerName: '',
location: '',
costCenter: '',
financeOwnerName: '',
riskProfile: {},
roleCodes: [],
email: '',
avatar: '',
isAdmin: false
return {
username: '',
name: '',
role: '',
department: '',
departmentName: '',
position: '',
grade: '',
employeeNo: '',
managerName: '',
location: '',
costCenter: '',
financeOwnerName: '',
riskProfile: {},
roleCodes: [],
email: '',
avatar: '',
isAdmin: false
}
}
function buildLegacyAdminUser(username = '') {
function buildLegacyAdminUser(username = '') {
const normalized = String(username || '').trim()
const name = normalized || DEFAULT_USER_NAME
return {
username: normalized,
name,
role: DEFAULT_USER_ROLE,
department: '',
departmentName: '',
position: DEFAULT_USER_ROLE,
grade: '',
employeeNo: '',
managerName: '',
location: '',
costCenter: '',
financeOwnerName: '',
riskProfile: {},
roleCodes: ['manager'],
email: '',
avatar: name.slice(0, 1).toUpperCase(),
isAdmin: true
}
}
function resolvePlatformAdminFlag(payload, roleCodes = []) {
const username = String(payload?.username || payload?.account || '').trim().toLowerCase()
const role = String(payload?.role || '').trim().toLowerCase()
const normalizedRoleCodes = roleCodes.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean)
return (
Boolean(payload?.isAdmin)
|| username === 'admin'
|| role === 'admin'
|| role === '管理员'
|| role === '系统管理员'
|| normalizedRoleCodes.includes('admin')
)
}
function readStoredUser() {
return {
username: normalized,
name,
role: DEFAULT_USER_ROLE,
department: '',
departmentName: '',
position: DEFAULT_USER_ROLE,
grade: '',
employeeNo: '',
managerName: '',
location: '',
costCenter: '',
financeOwnerName: '',
riskProfile: {},
roleCodes: ['manager'],
email: '',
avatar: name.slice(0, 1).toUpperCase(),
isAdmin: true
}
}
function resolvePlatformAdminFlag(payload, roleCodes = []) {
const username = String(payload?.username || payload?.account || '').trim().toLowerCase()
const role = String(payload?.role || '').trim().toLowerCase()
const normalizedRoleCodes = roleCodes.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean)
return (
Boolean(payload?.isAdmin)
|| username === 'admin'
|| role === 'admin'
|| role === '管理员'
|| role === '系统管理员'
|| normalizedRoleCodes.includes('admin')
)
}
function readStoredUser() {
if (typeof window === 'undefined') {
return buildAnonymousUser()
}
@@ -162,26 +164,26 @@ function readStoredUser() {
const name = String(payload.name || username || DEFAULT_USER_NAME).trim()
const roleCodes = Array.isArray(payload.roleCodes) ? payload.roleCodes.filter(Boolean) : []
return {
username,
name,
role: String(payload.role || DEFAULT_USER_ROLE),
department: String(payload.department || payload.departmentName || ''),
departmentName: String(payload.departmentName || payload.department || ''),
position: String(payload.position || ''),
grade: String(payload.grade || ''),
employeeNo: String(payload.employeeNo || payload.employee_no || ''),
managerName: String(payload.managerName || payload.manager_name || ''),
location: String(payload.location || ''),
costCenter: String(payload.costCenter || payload.cost_center || ''),
financeOwnerName: String(payload.financeOwnerName || payload.finance_owner_name || ''),
riskProfile: payload.riskProfile && typeof payload.riskProfile === 'object' ? payload.riskProfile : {},
roleCodes,
email: String(payload.email || ''),
avatar: String(payload.avatar || name.slice(0, 1).toUpperCase()),
isAdmin: resolvePlatformAdminFlag(payload, roleCodes)
}
}
return {
username,
name,
role: String(payload.role || DEFAULT_USER_ROLE),
department: String(payload.department || payload.departmentName || ''),
departmentName: String(payload.departmentName || payload.department || ''),
position: String(payload.position || ''),
grade: String(payload.grade || ''),
employeeNo: String(payload.employeeNo || payload.employee_no || ''),
managerName: String(payload.managerName || payload.manager_name || ''),
location: String(payload.location || ''),
costCenter: String(payload.costCenter || payload.cost_center || ''),
financeOwnerName: String(payload.financeOwnerName || payload.finance_owner_name || ''),
riskProfile: payload.riskProfile && typeof payload.riskProfile === 'object' ? payload.riskProfile : {},
roleCodes,
email: String(payload.email || ''),
avatar: String(payload.avatar || name.slice(0, 1).toUpperCase()),
isAdmin: resolvePlatformAdminFlag(payload, roleCodes)
}
}
} catch {
return buildLegacyAdminUser(readStoredUsername())
}
@@ -359,6 +361,15 @@ export function installSessionNavigation(router) {
.then((state) => {
applyBootstrapState(state)
setRuntimeApiBaseUrl(resolveBrowserApiBaseUrl(state))
fetchSettings()
.then((snapshot) => {
if (snapshot?.appearanceForm?.themeSkin) {
setThemeSkin(snapshot.appearanceForm.themeSkin)
}
})
.catch((error) => {
console.warn('Failed to load remote theme settings:', error)
})
router.isReady().then(() => reconcileEntryRoute(router))
})
.catch(() => {
@@ -624,14 +635,14 @@ async function handleLogin(credentials) {
password: credentials.password
})
const responseUser = response?.user || buildAnonymousUser()
const responseRoleCodes = Array.isArray(responseUser.roleCodes) ? responseUser.roleCodes.filter(Boolean) : []
const user = {
...responseUser,
roleCodes: responseRoleCodes,
isAdmin: resolvePlatformAdminFlag(responseUser, responseRoleCodes)
}
loggedIn.value = true
const responseUser = response?.user || buildAnonymousUser()
const responseRoleCodes = Array.isArray(responseUser.roleCodes) ? responseUser.roleCodes.filter(Boolean) : []
const user = {
...responseUser,
roleCodes: responseRoleCodes,
isAdmin: resolvePlatformAdminFlag(responseUser, responseRoleCodes)
}
loggedIn.value = true
persistAuthState(true, user)
currentUser.value = user
touchAuthActivity(true)