merge: 合并远程更新并保留签名与报表接入

This commit is contained in:
xy
2026-09-30 18:31:36 +08:00
14 changed files with 577 additions and 512 deletions
+74 -32
View File
@@ -24,7 +24,8 @@ const useMockData = mockFlags.workbenchHome
interface MockCampusOverview {
summary: WorkbenchOverviewResponse['summary']
trend: WorkbenchTrendPoint[]
/** 趋势的日均人次基数(按「院区 + 日期」播种出每天的具体值,见 createTrend) */
trendBase: number
todos: WorkbenchTodoTask[]
ranking: Array<Omit<WorkbenchDocumentRanking, 'signedCount'> & { baseCount: number }>
}
@@ -39,15 +40,7 @@ const mockCampusOverviews: Record<WorkbenchCampus, MockCampusOverview> = {
availableTemplates: 128,
coveredDepartments: 9,
},
trend: [
{ label: '8/21', value: 24 },
{ label: '8/22', value: 31 },
{ label: '8/23', value: 28 },
{ label: '8/24', value: 35 },
{ label: '8/25', value: 32 },
{ label: '8/26', value: 39 },
{ label: '8/27', value: 36 },
],
trendBase: 32,
todos: [
{
id: 'T0818003',
@@ -128,15 +121,7 @@ const mockCampusOverviews: Record<WorkbenchCampus, MockCampusOverview> = {
availableTemplates: 76,
coveredDepartments: 6,
},
trend: [
{ label: '8/21', value: 15 },
{ label: '8/22', value: 18 },
{ label: '8/23', value: 17 },
{ label: '8/24', value: 23 },
{ label: '8/25', value: 20 },
{ label: '8/26', value: 25 },
{ label: '8/27', value: 22 },
],
trendBase: 20,
todos: [
{
id: 'T0818001',
@@ -216,15 +201,7 @@ const mockCampusOverviews: Record<WorkbenchCampus, MockCampusOverview> = {
availableTemplates: 54,
coveredDepartments: 5,
},
trend: [
{ label: '8/21', value: 11 },
{ label: '8/22', value: 14 },
{ label: '8/23', value: 13 },
{ label: '8/24', value: 16 },
{ label: '8/25', value: 15 },
{ label: '8/26', value: 19 },
{ label: '8/27', value: 17 },
],
trendBase: 15,
todos: [
{
id: 'T0818021',
@@ -301,6 +278,62 @@ function getPeriodFactor(period: HomeRankingPeriod) {
return period === 7 ? 1 : period === 14 ? 2 : 3.5
}
/** 「院区 + 日期」→ 固定种子:同一天同一院区的数值不随刷新、切换时段或翻页而变 */
function hashSeed(text: string) {
let hash = 2166136261
for (let index = 0; index < text.length; index += 1) {
hash ^= text.charCodeAt(index)
hash = Math.imul(hash, 16777619)
}
return hash >>> 0
}
/** mulberry32:小、快、同种子可复现(与 api/workbench/report-analytics.ts 同一套做法) */
function createRandom(seed: number) {
let state = seed >>> 0
return () => {
state = (state + 0x6d2b79f5) >>> 0
let t = state
t = Math.imul(t ^ (t >>> 15), t | 1)
t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
/**
* 演示趋势:近 period 天(含今天)的已签署人次。
*
* 为什么不用写死的日期数组:写死 8/21–8/27 之后,演示日一过「近 7 天」窗口就全是过期日期
* (仓库的演示数据铁律:**日期相对** + **确定性**,不要 Math.random)。
* 末点取今日已签署,与指标卡同源 —— 原型里趋势最后一点就是今天已签署的任务数。
*/
function createTrend(
campus: WorkbenchCampus,
summary: WorkbenchOverviewResponse['summary'],
period: HomeRankingPeriod,
): WorkbenchTrendPoint[] {
const points: WorkbenchTrendPoint[] = []
const base = (mockCampusOverviews[campus] ?? mockCampusOverviews['本部院区']).trendBase
for (let offset = period - 1; offset >= 0; offset -= 1) {
const day = new Date()
day.setDate(day.getDate() - offset)
const label = `${day.getMonth() + 1}/${day.getDate()}`
const value =
offset === 0
? summary.todaySigned
: Math.max(base + Math.round(createRandom(hashSeed(`${campus}-${label}`))() * 12 - 5), 0)
points.push({ label, value })
}
return points
}
function createRanking(
rows: MockCampusOverview['ranking'],
period: HomeRankingPeriod,
@@ -317,7 +350,15 @@ function createRanking(
.sort((left, right) => right.signedCount - left.signedCount)
}
const TODO_LIMIT = 8
/**
* 首页「我的待办」最多几条。
*
* 原型 `renderDash()` 是 `tasks.slice(0, 5)`(最多 5 条)。
* 仓库收成 **3 条**(2026-09-30 定):趋势卡与待办卡同行,待办越多行高越高,
* 图是按宽度定高的、贴底后会在标题与图之间留大片空白 —— 3 条时两张卡高度基本齐平。
* 该上限同时作为 `todoLimit` 传给后端,避免取回一批再丢掉。
*/
const TODO_LIMIT = 3
const RANKING_LIMIT = 10
let campusIds: Map<string, string> | null = null
let campusIdsRequest: Promise<Map<string, string>> | null = null
@@ -423,11 +464,12 @@ export async function getWorkbenchOverview(
): Promise<WorkbenchOverviewResponse> {
if (useMockData) {
const overview = mockCampusOverviews[query.campus] ?? mockCampusOverviews['本部院区']
const summary = { ...overview.summary }
return {
summary: { ...overview.summary },
trend: overview.trend.map((point) => ({ ...point })),
todos: overview.todos.map((task) => ({ ...task })),
summary,
trend: createTrend(query.campus, summary, query.rankingPeriod),
todos: overview.todos.slice(0, TODO_LIMIT).map((task) => ({ ...task })),
documentRanking: createRanking(overview.ranking, query.rankingPeriod),
}
}