This commit is contained in:
zlwu committed 2026-05-17 08:36:37 +08:00
1 parent 246ad2228e
commit 8e2dd8b9f0
186 files changed
+38821 -2571

No files matched your search

+5 -1
View File
@@ -2,11 +2,15 @@
* 分页循环请求直至拿全数据(接口需返回 { list, total })。
* 用于分类、单位、用户等列表接口,避免只取第一页导致下拉选项不全。
*/
/** 与后端 `/web/admin/*` 列表普遍 `Query(..., le=100)` 一致,超出会 422 */
export const MAX_ADMIN_LIST_PAGE_SIZE = 100
export async function fetchAllPagedRecords<T>(
fetchPage: (page: number, pageSize: number) => Promise<{ list?: T[]; total?: number }>,
options?: { pageSize?: number; maxPages?: number }
): Promise<T[]> {
const pageSize = options?.pageSize ?? 100
const requested = options?.pageSize ?? MAX_ADMIN_LIST_PAGE_SIZE
const pageSize = Math.min(Math.max(1, requested), MAX_ADMIN_LIST_PAGE_SIZE)
const maxPages = options?.maxPages ?? 50
let page = 1
const all: T[] = []
+1 -1
View File
@@ -23,7 +23,7 @@ import { BaseResponse } from '@/types'
import { decryptJson, deriveKey, encryptJson, randomNonce } from './crypto'
/** 请求配置常量 */
const REQUEST_TIMEOUT = 15000
const REQUEST_TIMEOUT = 60000
const LOGOUT_DELAY = 500
const RETRY_DELAY = 1000
/** 幂等请求在网络抖动、唤醒后首包失败时自动重试次数 */
+5
View File
@@ -17,6 +17,7 @@
import { AppRouteRecord } from '@/types/router'
import { router } from '@/router'
import { isNavigableMenuItem } from './route'
import { useSettingStore } from '@/store/modules/setting'
// 打开外部链接
export const openExternalLink = (link: string) => {
@@ -38,6 +39,10 @@ export const handleMenuJump = (item: AppRouteRecord, jumpToFirst: boolean = fals
// 如果不需要跳转到第一个子菜单,或者没有子菜单,直接跳转当前路径
if (!jumpToFirst || !item.children?.length) {
// 已经在当前页面时,刷新页面内容以重新加载列表数据
if (router.currentRoute.value.path === item.path) {
return useSettingStore().reload()
}
return router.push(item.path)
}
+4 -1
View File
@@ -2,6 +2,8 @@
* 新增时默认排序号:当前列表中最大 sortOrder + 1;无任何数据时为 0。
* 依赖列表接口按 sort_order 升序分页(最后一页含最大排序号)。
*/
import { MAX_ADMIN_LIST_PAGE_SIZE } from '@/utils/fetch-paged-list'
export async function fetchNextSortOrder(
fetchList: (params: {
page: number
@@ -11,7 +13,8 @@ export async function fetchNextSortOrder(
}) => Promise<{ list: { sortOrder?: number }[]; total: number }>,
options?: { pageSize?: number }
): Promise<number> {
const pageSize = options?.pageSize ?? 100
const requested = options?.pageSize ?? MAX_ADMIN_LIST_PAGE_SIZE
const pageSize = Math.min(Math.max(1, requested), MAX_ADMIN_LIST_PAGE_SIZE)
const first = await fetchList({ page: 1, pageSize })
const total = first.total ?? 0
if (total === 0) return 0
+13 -4
View File
@@ -11,8 +11,16 @@ export type TreePathFlatNode = Record<string, unknown>
export function buildTreePathLabelFromFlat(
nodeId: string | null | undefined,
flatList: TreePathFlatNode[] | null | undefined,
separator: string = DEFAULT_TREE_PATH_SEPARATOR
separatorOrOpts?: string | { separator?: string; nameKey?: string }
): string {
const separator =
typeof separatorOrOpts === 'string'
? separatorOrOpts
: separatorOrOpts?.separator ?? DEFAULT_TREE_PATH_SEPARATOR
const nameKey =
typeof separatorOrOpts === 'string'
? 'name'
: separatorOrOpts?.nameKey ?? 'name'
const cid = String(nodeId ?? '').trim()
if (!cid) return ''
const byId = new Map<string, TreePathFlatNode>()
@@ -26,7 +34,7 @@ export function buildTreePathLabelFromFlat(
while (cur && byId.has(cur) && !seen.has(cur)) {
seen.add(cur)
const cat: TreePathFlatNode = byId.get(cur)!
const nm = String(cat?.name ?? '').trim()
const nm = String(cat?.[nameKey] ?? '').trim()
if (nm) names.unshift(nm)
const pid: string = String(cat?.parentId ?? cat?.parent_id ?? '').trim()
cur = pid || null
@@ -70,12 +78,13 @@ export function resolveTreePathDisplay(
flatList: TreePathFlatNode[] | null | undefined,
fallbackName?: unknown,
separator: string = DEFAULT_TREE_PATH_SEPARATOR,
emptyPlaceholder = '—'
emptyPlaceholder = '—',
nameKey?: string
): string {
const path = buildTreePathLabelFromFlat(
String(nodeId ?? '').trim() || undefined,
flatList,
separator
{ separator, nameKey }
)
if (path) return path
const fb = String(fallbackName ?? '').trim()
+10 -2
View File
@@ -13,13 +13,16 @@ import {
CircleClose,
Close,
CloseBold,
CollectionTag,
Connection,
DArrowRight,
DataAnalysis,
DataBoard,
Delete,
Document,
Edit,
Expand,
Fold,
FolderOpened,
FullScreen,
Grid,
HomeFilled,
@@ -109,7 +112,12 @@ export const epIconMap: Record<string, Component> = {
smile: ChatDotRound,
brush: Brush,
// Status
warning: Warning
warning: Warning,
// Business
'folder-opened': FolderOpened,
'collection-tag': CollectionTag,
workflow: Connection,
'data-board': DataBoard
}
export function resolveEpIcon(name: string | undefined): Component | undefined {
+4 -4
View File
@@ -1,12 +1,12 @@
/** 未设置头像时统一使用中科兴皖 logo(与登录页、侧栏 ArtLogo 一致) */
export const DEFAULT_USER_AVATAR_URL = '/logo.png'
/** 未设置头像时使用 `public/avatart.jpg`(根路径 `/avatart.jpg`) */
export const DEFAULT_USER_AVATAR_URL = '/avatart.jpg'
/** @deprecated 各角色默认已统一为 logo,保留参数仅为兼容旧调用 */
/** @deprecated 各角色默认已统一为默认头像图,保留参数仅为兼容旧调用 */
export function defaultAvatarPathByRole(_role: string | undefined): string {
return DEFAULT_USER_AVATAR_URL
}
/** 展示用头像:有自定义 URL 用自定义,否则用中科兴皖 logo */
/** 展示用头像:`avatarUrl` 非空则用其值,否则用默认图 */
export function resolveUserAvatarUrl(avatarUrl: string | null | undefined, role: string | undefined): string {
const u = avatarUrl != null ? String(avatarUrl).trim() : ''
if (u) return u