|
@@ -4,7 +4,15 @@
|
|
|
*/
|
|
*/
|
|
|
import { del, get, post, put } from '@/utils/request'
|
|
import { del, get, post, put } from '@/utils/request'
|
|
|
import { getAppConfig } from '@/utils/appConfig'
|
|
import { getAppConfig } from '@/utils/appConfig'
|
|
|
-import { buildDashboardByRole } from '@/utils/buildDashboard'
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ FLOW_STAGES,
|
|
|
|
|
+ getBarChartTitle,
|
|
|
|
|
+ getDashboardScopeLabel,
|
|
|
|
|
+ LEADER_WORKBENCH_TIPS,
|
|
|
|
|
+ SOCIAL_WORKBENCH_TIPS,
|
|
|
|
|
+ TOWN_REVIEW_TIPS,
|
|
|
|
|
+ VILLAGE_WORKBENCH_TIPS,
|
|
|
|
|
+} from '@/utils/buildDashboard'
|
|
|
import type {
|
|
import type {
|
|
|
AccountItem,
|
|
AccountItem,
|
|
|
AlertItem,
|
|
AlertItem,
|
|
@@ -156,7 +164,183 @@ async function buildTownVillageChartsFromApi(townId: string, payMonth: string) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** —— 工作台 —— */
|
|
|
|
|
|
|
+function emptyDashboard(role: ReturnType<typeof getAppConfig>['roles'][0], payMonth: string): DashboardData {
|
|
|
|
|
+ const isDistrict = Boolean(role.allData)
|
|
|
|
|
+ return {
|
|
|
|
|
+ stats: { normal: 0, pending: 0, alert: 0, amount: 0 },
|
|
|
|
|
+ batchInfo: {
|
|
|
|
|
+ batchNo: payMonth,
|
|
|
|
|
+ scope: getDashboardScopeLabel(role),
|
|
|
|
|
+ peopleCount: 0,
|
|
|
|
|
+ amount: 0,
|
|
|
|
|
+ todoCount: 0,
|
|
|
|
|
+ alertCount: 0,
|
|
|
|
|
+ },
|
|
|
|
|
+ barChartTitle: getBarChartTitle(role),
|
|
|
|
|
+ workflow: [...FLOW_STAGES],
|
|
|
|
|
+ workflowActive: 0,
|
|
|
|
|
+ townAmountChart: [],
|
|
|
|
|
+ batchCompareChart: [],
|
|
|
|
|
+ statusPieMonth: [],
|
|
|
|
|
+ statusPieYear: [],
|
|
|
|
|
+ todos: [],
|
|
|
|
|
+ recentAlerts: [],
|
|
|
|
|
+ villageTips: role.village ? VILLAGE_WORKBENCH_TIPS : undefined,
|
|
|
|
|
+ districtOverview: isDistrict
|
|
|
|
|
+ ? {
|
|
|
|
|
+ focus: role.key === 'agri' ? 'qualify' : 'amount',
|
|
|
|
|
+ townCount: 0,
|
|
|
|
|
+ villageCount: 0,
|
|
|
|
|
+ peopleCount: 0,
|
|
|
|
|
+ amount: 0,
|
|
|
|
|
+ withLandCount: 0,
|
|
|
|
|
+ withoutLandCount: 0,
|
|
|
|
|
+ qualifiedCount: 0,
|
|
|
|
|
+ townPeopleChart: [],
|
|
|
|
|
+ townAmountChart: [],
|
|
|
|
|
+ townWithLandChart: [],
|
|
|
|
|
+ townWithoutLandChart: [],
|
|
|
|
|
+ qualifyPie: [],
|
|
|
|
|
+ halfYearPeopleChart: [],
|
|
|
|
|
+ halfYearAmountChart: [],
|
|
|
|
|
+ townSummary: [],
|
|
|
|
|
+ }
|
|
|
|
|
+ : undefined,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type WorkbenchApi = {
|
|
|
|
|
+ summary?: Record<string, unknown> & {
|
|
|
|
|
+ totalPeople?: number
|
|
|
|
|
+ totalAmount?: number | string
|
|
|
|
|
+ pendingAlertCount?: number
|
|
|
|
|
+ townStats?: BatchStatRow[]
|
|
|
|
|
+ }
|
|
|
|
|
+ region?: RegionDashRow
|
|
|
|
|
+ personnelCount?: number
|
|
|
|
|
+ withLandCount?: number
|
|
|
|
|
+ withoutLandCount?: number
|
|
|
|
|
+ qualifiedCount?: number
|
|
|
|
|
+ pendingTownApproveCount?: number
|
|
|
|
|
+ pendingLeaderApproveCount?: number
|
|
|
|
|
+ monthBatchCount?: number
|
|
|
|
|
+ monthPeopleCount?: number
|
|
|
|
|
+ monthAmount?: number | string
|
|
|
|
|
+ townQualify?: Array<{
|
|
|
|
|
+ townId?: string
|
|
|
|
|
+ peopleCount?: number
|
|
|
|
|
+ villageCount?: number
|
|
|
|
|
+ withLandCount?: number
|
|
|
|
|
+ withoutLandCount?: number
|
|
|
|
|
+ qualifiedCount?: number
|
|
|
|
|
+ }>
|
|
|
|
|
+ villageCharts?: Array<{ villageId?: string; peopleCount?: number; amount?: number | string }>
|
|
|
|
|
+ batchStatusCounts?: Record<string, number>
|
|
|
|
|
+ recentAlerts?: Record<string, unknown>[]
|
|
|
|
|
+ monthBatches?: Record<string, unknown>[]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 线上旧包无 /workbench 时,用已有 summary + region-stats 拼装 */
|
|
|
|
|
+async function fetchWorkbenchLegacy(
|
|
|
|
|
+ payMonth: string,
|
|
|
|
|
+ townId?: string,
|
|
|
|
|
+ villageId?: string,
|
|
|
|
|
+ months = 6,
|
|
|
|
|
+): Promise<WorkbenchApi | null> {
|
|
|
|
|
+ const [summary, region] = await Promise.all([
|
|
|
|
|
+ get<WorkbenchApi['summary']>(
|
|
|
|
|
+ '/dashboard/summary',
|
|
|
|
|
+ { payMonth, townId: townId || undefined },
|
|
|
|
|
+ { silent: true },
|
|
|
|
|
+ ).catch(() => null),
|
|
|
|
|
+ get<RegionDashRow>(
|
|
|
|
|
+ '/dashboard/region-stats',
|
|
|
|
|
+ {
|
|
|
|
|
+ payMonth,
|
|
|
|
|
+ townId: townId || undefined,
|
|
|
|
|
+ villageId: villageId || undefined,
|
|
|
|
|
+ months,
|
|
|
|
|
+ },
|
|
|
|
|
+ { silent: true },
|
|
|
|
|
+ ).catch(() => null),
|
|
|
|
|
+ ])
|
|
|
|
|
+ if (!summary && !region) return null
|
|
|
|
|
+
|
|
|
|
|
+ const batchRes = await post<PageResult<Record<string, unknown>>>(
|
|
|
|
|
+ '/batches/page',
|
|
|
|
|
+ {
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 80,
|
|
|
|
|
+ payMonth,
|
|
|
|
|
+ townId: townId || undefined,
|
|
|
|
|
+ villageId: villageId || undefined,
|
|
|
|
|
+ batchLevel: villageId ? 'VILLAGE' : townId ? 'VILLAGE' : undefined,
|
|
|
|
|
+ },
|
|
|
|
|
+ { silent: true },
|
|
|
|
|
+ ).catch(() => null)
|
|
|
|
|
+ const monthBatches = batchRes?.list || []
|
|
|
|
|
+ const batchStatusCounts: Record<string, number> = {}
|
|
|
|
|
+ for (const b of monthBatches) {
|
|
|
|
|
+ const st = String(b.status || '')
|
|
|
|
|
+ if (!st) continue
|
|
|
|
|
+ batchStatusCounts[st] = (batchStatusCounts[st] || 0) + 1
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const alertRes = await post<PageResult<Record<string, unknown>>>(
|
|
|
|
|
+ '/alerts/page',
|
|
|
|
|
+ { pageNum: 1, pageSize: 8, townId: townId || undefined, villageId: villageId || undefined },
|
|
|
|
|
+ { silent: true },
|
|
|
|
|
+ ).catch(() => null)
|
|
|
|
|
+
|
|
|
|
|
+ let villageCharts: WorkbenchApi['villageCharts']
|
|
|
|
|
+ if (townId && !villageId) {
|
|
|
|
|
+ const charts = await buildTownVillageChartsFromApi(townId, payMonth).catch(() => null)
|
|
|
|
|
+ if (charts) {
|
|
|
|
|
+ villageCharts = charts.people.map((p, idx) => ({
|
|
|
|
|
+ villageId: p.name,
|
|
|
|
|
+ peopleCount: p.value,
|
|
|
|
|
+ amount: charts.amount[idx]?.value || 0,
|
|
|
|
|
+ }))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ summary: summary || undefined,
|
|
|
|
|
+ region: region || undefined,
|
|
|
|
|
+ personnelCount: Number(region?.personnelCount || 0),
|
|
|
|
|
+ monthPeopleCount: Number(region?.currentMonth?.peopleCount || 0),
|
|
|
|
|
+ monthAmount: region?.currentMonth?.totalAmount,
|
|
|
|
|
+ monthBatchCount: monthBatches.length,
|
|
|
|
|
+ pendingTownApproveCount: Number(batchStatusCounts.PENDING_TOWN_APPROVE || 0),
|
|
|
|
|
+ pendingLeaderApproveCount: Number(batchStatusCounts.PENDING_LEADER_APPROVE || 0),
|
|
|
|
|
+ batchStatusCounts,
|
|
|
|
|
+ monthBatches,
|
|
|
|
|
+ recentAlerts: alertRes?.list || [],
|
|
|
|
|
+ villageCharts,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function loadWorkbenchApi(
|
|
|
|
|
+ payMonth: string,
|
|
|
|
|
+ townId?: string,
|
|
|
|
|
+ villageId?: string,
|
|
|
|
|
+ months = 6,
|
|
|
|
|
+): Promise<WorkbenchApi | null> {
|
|
|
|
|
+ const wb = await get<WorkbenchApi>(
|
|
|
|
|
+ '/dashboard/workbench',
|
|
|
|
|
+ {
|
|
|
|
|
+ payMonth,
|
|
|
|
|
+ townId: townId || undefined,
|
|
|
|
|
+ villageId: villageId || undefined,
|
|
|
|
|
+ months,
|
|
|
|
|
+ },
|
|
|
|
|
+ { silent: true },
|
|
|
|
|
+ ).catch(() => null)
|
|
|
|
|
+ if (wb) return wb
|
|
|
|
|
+ return fetchWorkbenchLegacy(payMonth, townId, villageId, months)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** —— 工作台:优先单接口聚合;旧后台无 workbench 时自动降级 —— */
|
|
|
export async function fetchDashboard(params: {
|
|
export async function fetchDashboard(params: {
|
|
|
roleKey: string
|
|
roleKey: string
|
|
|
currentMonth: string
|
|
currentMonth: string
|
|
@@ -167,8 +351,7 @@ export async function fetchDashboard(params: {
|
|
|
const { townId, villageId } = await resolveScopeIds(role.town, role.village)
|
|
const { townId, villageId } = await resolveScopeIds(role.town, role.village)
|
|
|
if (townId) await ensureVillages(townId)
|
|
if (townId) await ensureVillages(townId)
|
|
|
const payMonth = monthToPayMonth(params.currentMonth)
|
|
const payMonth = monthToPayMonth(params.currentMonth)
|
|
|
- const base = buildDashboardByRole(role, params.currentMonth)
|
|
|
|
|
- base.batchInfo.batchNo = payMonth
|
|
|
|
|
|
|
+ const base = emptyDashboard(role, payMonth)
|
|
|
base.batchInfo.scope =
|
|
base.batchInfo.scope =
|
|
|
role.village && role.town
|
|
role.village && role.town
|
|
|
? `${role.town}/${role.village}`
|
|
? `${role.town}/${role.village}`
|
|
@@ -180,143 +363,212 @@ export async function fetchDashboard(params: {
|
|
|
: '全区'
|
|
: '全区'
|
|
|
: base.batchInfo.scope
|
|
: base.batchInfo.scope
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- const [summary, region] = await Promise.all([
|
|
|
|
|
- get<Record<string, unknown>>('/dashboard/summary', {
|
|
|
|
|
- payMonth,
|
|
|
|
|
- townId: townId || undefined,
|
|
|
|
|
- }).catch(() => null),
|
|
|
|
|
- get<RegionDashRow>('/dashboard/region-stats', {
|
|
|
|
|
- payMonth,
|
|
|
|
|
- townId: townId || undefined,
|
|
|
|
|
- villageId: villageId || undefined,
|
|
|
|
|
- months: 6,
|
|
|
|
|
- }).catch(() => null),
|
|
|
|
|
- ])
|
|
|
|
|
-
|
|
|
|
|
- const halfYear = seriesToHalfYearCharts(region?.series, 6)
|
|
|
|
|
- const cur = region?.currentMonth
|
|
|
|
|
- let regionPeople = Number(cur?.peopleCount ?? 0)
|
|
|
|
|
- let regionAmount = Number(cur?.totalAmount ?? 0)
|
|
|
|
|
- const pendingAlert = Number(summary?.pendingAlertCount || 0)
|
|
|
|
|
-
|
|
|
|
|
- // 批次有人数但金额未回填时,用本月村/镇批次再算一遍(避免金额柱图全 0)
|
|
|
|
|
- if ((regionPeople > 0 && regionAmount <= 0) || (villageId && regionPeople <= 0)) {
|
|
|
|
|
- try {
|
|
|
|
|
- const monthBatches = await pageAll<Record<string, unknown>>((pageNum, pageSize) =>
|
|
|
|
|
- post<PageResult<Record<string, unknown>>>('/batches/page', {
|
|
|
|
|
- pageNum,
|
|
|
|
|
- pageSize,
|
|
|
|
|
- payMonth,
|
|
|
|
|
- townId: townId || undefined,
|
|
|
|
|
- villageId: villageId || undefined,
|
|
|
|
|
- batchLevel: villageId ? 'VILLAGE' : undefined,
|
|
|
|
|
- }),
|
|
|
|
|
- )
|
|
|
|
|
- let p = 0
|
|
|
|
|
- let a = 0
|
|
|
|
|
- for (const b of monthBatches) {
|
|
|
|
|
- if (String(b.status || '') === 'ARCHIVED') continue
|
|
|
|
|
- p += Number(b.peopleCount || 0)
|
|
|
|
|
- a += Number(b.totalAmount || 0)
|
|
|
|
|
- }
|
|
|
|
|
- if (p > 0) regionPeople = p
|
|
|
|
|
- if (a > 0) regionAmount = a
|
|
|
|
|
- } catch {
|
|
|
|
|
- /* ignore */
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (regionPeople <= 0) regionPeople = Number(summary?.totalPeople || 0)
|
|
|
|
|
- if (regionAmount <= 0) regionAmount = Number(summary?.totalAmount || 0)
|
|
|
|
|
|
|
+ const wb = await loadWorkbenchApi(payMonth, townId || undefined, villageId || undefined, 6)
|
|
|
|
|
+
|
|
|
|
|
+ if (!wb) return base
|
|
|
|
|
+
|
|
|
|
|
+ const summary = wb.summary
|
|
|
|
|
+ const region = wb.region
|
|
|
|
|
+ const halfYear = seriesToHalfYearCharts(region?.series, 6)
|
|
|
|
|
+ let regionPeople = Number(region?.currentMonth?.peopleCount ?? wb.monthPeopleCount ?? 0)
|
|
|
|
|
+ let regionAmount = Number(region?.currentMonth?.totalAmount ?? wb.monthAmount ?? 0)
|
|
|
|
|
+ if (regionPeople <= 0) regionPeople = Number(summary?.totalPeople || 0)
|
|
|
|
|
+ if (regionAmount <= 0) regionAmount = Number(summary?.totalAmount || 0)
|
|
|
|
|
+ if (halfYear.people.length) {
|
|
|
|
|
+ const lastIdx = halfYear.people.length - 1
|
|
|
|
|
+ if (regionPeople > 0) halfYear.people[lastIdx].value = regionPeople
|
|
|
|
|
+ if (regionAmount > 0) halfYear.amount[lastIdx].value = regionAmount
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 把本月校正写回近半年序列最后一柱
|
|
|
|
|
- if (halfYear.people.length) {
|
|
|
|
|
- const lastIdx = halfYear.people.length - 1
|
|
|
|
|
- if (regionPeople > 0) halfYear.people[lastIdx].value = regionPeople
|
|
|
|
|
- if (regionAmount > 0) halfYear.amount[lastIdx].value = regionAmount
|
|
|
|
|
|
|
+ const pendingAlert = Number(summary?.pendingAlertCount || 0)
|
|
|
|
|
+ const pendingTown = Number(wb.pendingTownApproveCount || 0)
|
|
|
|
|
+ const pendingLeader = Number(wb.pendingLeaderApproveCount || 0)
|
|
|
|
|
+ const pending =
|
|
|
|
|
+ role.key === 'town' ? pendingTown : role.key === 'leader' ? pendingLeader : pendingTown + pendingLeader
|
|
|
|
|
+
|
|
|
|
|
+ base.stats.amount = regionAmount
|
|
|
|
|
+ base.stats.alert = pendingAlert
|
|
|
|
|
+ base.stats.normal = Number(region?.personnelCount ?? wb.personnelCount ?? regionPeople)
|
|
|
|
|
+ base.stats.pending = pending
|
|
|
|
|
+ base.batchInfo.peopleCount = regionPeople
|
|
|
|
|
+ base.batchInfo.amount = regionAmount
|
|
|
|
|
+ base.batchInfo.alertCount = pendingAlert
|
|
|
|
|
+ base.batchInfo.todoCount = role.allData ? 0 : pending
|
|
|
|
|
+
|
|
|
|
|
+ const monthBatches = (wb.monthBatches || []).map(mapVillageBatch)
|
|
|
|
|
+ const statusCounts = wb.batchStatusCounts || {}
|
|
|
|
|
+ base.statusPieMonth = Object.entries(statusCounts)
|
|
|
|
|
+ .map(([name, value]) => ({ name, value: Number(value) || 0 }))
|
|
|
|
|
+ .filter((i) => i.value > 0)
|
|
|
|
|
+ base.statusPieYear = base.statusPieMonth
|
|
|
|
|
+ base.recentAlerts = (wb.recentAlerts || []).map(mapAlert).slice(0, 8)
|
|
|
|
|
+ base.todos = role.allData
|
|
|
|
|
+ ? []
|
|
|
|
|
+ : monthBatches.slice(0, 12).map((b) => ({
|
|
|
|
|
+ type: '批次流转',
|
|
|
|
|
+ title: `${b.batchNo} ${b.town}${b.village}月度批次`,
|
|
|
|
|
+ node: b.stage,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ let workflowActive = FLOW_STAGES.length - 1
|
|
|
|
|
+ for (let i = 0; i < FLOW_STAGES.length; i++) {
|
|
|
|
|
+ if (monthBatches.some((b) => b.stage.includes(FLOW_STAGES[i]) || (i === 0 && b.stage === '退回修改'))) {
|
|
|
|
|
+ workflowActive = i
|
|
|
|
|
+ break
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
+ base.workflowActive = workflowActive
|
|
|
|
|
+
|
|
|
|
|
+ if (role.village || role.key === 'village') {
|
|
|
|
|
+ base.villageHalfYearPeopleChart = halfYear.people
|
|
|
|
|
+ base.villageHalfYearAmountChart = halfYear.amount
|
|
|
|
|
+ base.townAmountChart = halfYear.amount
|
|
|
|
|
+ base.batchCompareChart = halfYear.amount.map((i, idx) => ({
|
|
|
|
|
+ ...i,
|
|
|
|
|
+ count: halfYear.people[idx]?.value,
|
|
|
|
|
+ }))
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 汇总卡片:优先区划统计(含村级),保证切换镇/村后数字一致
|
|
|
|
|
- base.stats.amount = regionAmount
|
|
|
|
|
- base.stats.alert = pendingAlert
|
|
|
|
|
- base.stats.normal = Number(region?.personnelCount ?? regionPeople)
|
|
|
|
|
- base.batchInfo.peopleCount = regionPeople
|
|
|
|
|
- base.batchInfo.amount = regionAmount
|
|
|
|
|
- base.batchInfo.alertCount = pendingAlert
|
|
|
|
|
-
|
|
|
|
|
- // 村合作社:近半年图表用真实序列(不再用 mock 骨架)
|
|
|
|
|
- if (role.village || role.key === 'village') {
|
|
|
|
|
- base.villageHalfYearPeopleChart = halfYear.people
|
|
|
|
|
- base.villageHalfYearAmountChart = halfYear.amount
|
|
|
|
|
- base.townAmountChart = halfYear.amount
|
|
|
|
|
|
|
+ if (townId && !villageId && (role.key === 'town' || role.key === 'social' || role.key === 'leader')) {
|
|
|
|
|
+ const villageCharts = {
|
|
|
|
|
+ people: (wb.villageCharts || []).map((v) => ({
|
|
|
|
|
+ name: regionName(String(v.villageId || '')) || String(v.villageId || '未知村'),
|
|
|
|
|
+ value: Number(v.peopleCount || 0),
|
|
|
|
|
+ })),
|
|
|
|
|
+ amount: (wb.villageCharts || []).map((v) => ({
|
|
|
|
|
+ name: regionName(String(v.villageId || '')) || String(v.villageId || '未知村'),
|
|
|
|
|
+ value: Number(v.amount || 0),
|
|
|
|
|
+ })),
|
|
|
|
|
+ }
|
|
|
|
|
+ base.townVillagePeopleChart = villageCharts.people
|
|
|
|
|
+ base.townVillageAmountChart = villageCharts.amount
|
|
|
|
|
+ base.townAmountChart = villageCharts.amount
|
|
|
|
|
+ if (halfYear.people.length) {
|
|
|
base.batchCompareChart = halfYear.amount.map((i, idx) => ({
|
|
base.batchCompareChart = halfYear.amount.map((i, idx) => ({
|
|
|
...i,
|
|
...i,
|
|
|
count: halfYear.people[idx]?.value,
|
|
count: halfYear.people[idx]?.value,
|
|
|
}))
|
|
}))
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 镇级:本批次各村柱状图
|
|
|
|
|
- if (townId && !villageId && (role.key === 'town' || role.key === 'social' || role.key === 'leader')) {
|
|
|
|
|
- try {
|
|
|
|
|
- const villageCharts = await buildTownVillageChartsFromApi(townId, payMonth)
|
|
|
|
|
- base.townVillagePeopleChart = villageCharts.people
|
|
|
|
|
- base.townVillageAmountChart = villageCharts.amount
|
|
|
|
|
- base.townAmountChart = villageCharts.amount
|
|
|
|
|
- } catch {
|
|
|
|
|
- /* 保留骨架 */
|
|
|
|
|
- }
|
|
|
|
|
- // 镇级也可看近半年趋势(右侧/其他图用到时)
|
|
|
|
|
- if (halfYear.people.length) {
|
|
|
|
|
- base.batchCompareChart = halfYear.amount.map((i, idx) => ({
|
|
|
|
|
- ...i,
|
|
|
|
|
- count: halfYear.people[idx]?.value,
|
|
|
|
|
- }))
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const withLand = Number(wb.withLandCount || 0)
|
|
|
|
|
+ const withoutLand = Number(wb.withoutLandCount || 0)
|
|
|
|
|
+ if (role.key === 'town') {
|
|
|
|
|
+ base.townReview = {
|
|
|
|
|
+ pendingCount: pendingTown,
|
|
|
|
|
+ withLandCount: withLand,
|
|
|
|
|
+ withoutLandCount: withoutLand,
|
|
|
|
|
+ pendingVillageCount: new Set(monthBatches.filter((b) => b.statusCode === 'PENDING_TOWN_APPROVE').map((b) => b.village)).size,
|
|
|
|
|
+ qualifyPie: [
|
|
|
|
|
+ { name: '有土地退养', value: withLand },
|
|
|
|
|
+ { name: '无土地有资格', value: withoutLand },
|
|
|
|
|
+ ].filter((i) => i.value > 0),
|
|
|
|
|
+ villageReviews: (wb.villageCharts || []).map((v) => ({
|
|
|
|
|
+ village: regionName(String(v.villageId || '')) || String(v.villageId || '未知村'),
|
|
|
|
|
+ total: Number(v.peopleCount || 0),
|
|
|
|
|
+ pending: 0,
|
|
|
|
|
+ withLand: 0,
|
|
|
|
|
+ withoutLand: 0,
|
|
|
|
|
+ })),
|
|
|
|
|
+ pendingPeople: [],
|
|
|
|
|
+ tips: TOWN_REVIEW_TIPS,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (role.key === 'social') {
|
|
|
|
|
+ base.socialWorkbench = {
|
|
|
|
|
+ matchPendingCount: Number(statusCounts.PENDING_INSURANCE_MATCH || statusCounts.INSURANCE_MATCHING || 0),
|
|
|
|
|
+ compareReadyCount: Number(statusCounts.READY_EXPORT || 0),
|
|
|
|
|
+ diskPendingCount:
|
|
|
|
|
+ Number(statusCounts.READY_EXPORT || 0) + Number(statusCounts.EXPORTED || 0),
|
|
|
|
|
+ alertCount: pendingAlert,
|
|
|
|
|
+ tips: SOCIAL_WORKBENCH_TIPS,
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 区级:近半年 + 各街镇(来自 summary.townStats)
|
|
|
|
|
- if (role.allData && base.districtOverview) {
|
|
|
|
|
- base.districtOverview.halfYearPeopleChart = halfYear.people
|
|
|
|
|
- base.districtOverview.halfYearAmountChart = halfYear.amount
|
|
|
|
|
- base.districtOverview.peopleCount = regionPeople
|
|
|
|
|
- base.districtOverview.amount = regionAmount
|
|
|
|
|
- const townStats = (summary?.townStats as BatchStatRow[] | undefined) || []
|
|
|
|
|
- if (townStats.length) {
|
|
|
|
|
- const byTown = new Map<string, { people: number; amount: number }>()
|
|
|
|
|
- for (const s of townStats) {
|
|
|
|
|
- const name = regionName(String(s.townId || '')) || String(s.townId || '未知')
|
|
|
|
|
- const curT = byTown.get(name) || { people: 0, amount: 0 }
|
|
|
|
|
- curT.people += Number(s.shouldPayCount || 0)
|
|
|
|
|
- curT.amount += Number(s.shouldPayAmount || 0)
|
|
|
|
|
- byTown.set(name, curT)
|
|
|
|
|
- }
|
|
|
|
|
- const names = Array.from(byTown.keys()).sort((a, b) => a.localeCompare(b, 'zh-CN'))
|
|
|
|
|
- base.districtOverview.townCount = names.length
|
|
|
|
|
- base.districtOverview.townPeopleChart = names.map((n) => ({
|
|
|
|
|
- name: n,
|
|
|
|
|
- value: byTown.get(n)?.people || 0,
|
|
|
|
|
- }))
|
|
|
|
|
- base.districtOverview.townAmountChart = names.map((n) => ({
|
|
|
|
|
- name: n,
|
|
|
|
|
- value: byTown.get(n)?.amount || 0,
|
|
|
|
|
- }))
|
|
|
|
|
- base.districtOverview.townSummary = names.map((n) => ({
|
|
|
|
|
- town: n,
|
|
|
|
|
- villageCount: 0,
|
|
|
|
|
- peopleCount: byTown.get(n)?.people || 0,
|
|
|
|
|
- amount: byTown.get(n)?.amount || 0,
|
|
|
|
|
- withLandCount: 0,
|
|
|
|
|
- withoutLandCount: 0,
|
|
|
|
|
- qualifiedCount: byTown.get(n)?.people || 0,
|
|
|
|
|
- }))
|
|
|
|
|
- base.townAmountChart = base.districtOverview.townAmountChart
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (role.key === 'leader') {
|
|
|
|
|
+ base.leaderWorkbench = {
|
|
|
|
|
+ pendingApprovalCount: pendingLeader,
|
|
|
|
|
+ specialPendingCount: 0,
|
|
|
|
|
+ issueReadyCount: Number(statusCounts.READY_EXPORT || 0),
|
|
|
|
|
+ alertCount: pendingAlert,
|
|
|
|
|
+ tips: LEADER_WORKBENCH_TIPS,
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return base
|
|
|
|
|
- } catch {
|
|
|
|
|
- return base
|
|
|
|
|
|
|
+ if (role.allData && base.districtOverview) {
|
|
|
|
|
+ const townStats = (summary?.townStats as BatchStatRow[] | undefined) || []
|
|
|
|
|
+ const byTownPay = new Map<string, { people: number; amount: number }>()
|
|
|
|
|
+ for (const s of townStats) {
|
|
|
|
|
+ const name = regionName(String(s.townId || '')) || String(s.townId || '未知')
|
|
|
|
|
+ const curT = byTownPay.get(name) || { people: 0, amount: 0 }
|
|
|
|
|
+ curT.people += Number(s.shouldPayCount || 0)
|
|
|
|
|
+ curT.amount += Number(s.shouldPayAmount || 0)
|
|
|
|
|
+ byTownPay.set(name, curT)
|
|
|
|
|
+ }
|
|
|
|
|
+ const qRows = wb.townQualify || []
|
|
|
|
|
+ const names = Array.from(
|
|
|
|
|
+ new Set([
|
|
|
|
|
+ ...byTownPay.keys(),
|
|
|
|
|
+ ...qRows.map((r) => regionName(String(r.townId || '')) || String(r.townId || '未知')),
|
|
|
|
|
+ ]),
|
|
|
|
|
+ ).sort((a, b) => a.localeCompare(b, 'zh-CN'))
|
|
|
|
|
+ const qByName = new Map(
|
|
|
|
|
+ qRows.map((r) => [
|
|
|
|
|
+ regionName(String(r.townId || '')) || String(r.townId || '未知'),
|
|
|
|
|
+ r,
|
|
|
|
|
+ ]),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ base.districtOverview.halfYearPeopleChart = halfYear.people
|
|
|
|
|
+ base.districtOverview.halfYearAmountChart = halfYear.amount
|
|
|
|
|
+ base.districtOverview.peopleCount = regionPeople
|
|
|
|
|
+ base.districtOverview.amount = regionAmount
|
|
|
|
|
+ base.districtOverview.withLandCount = withLand
|
|
|
|
|
+ base.districtOverview.withoutLandCount = withoutLand
|
|
|
|
|
+ base.districtOverview.qualifiedCount = Number(wb.qualifiedCount || 0)
|
|
|
|
|
+ base.districtOverview.townCount = names.length
|
|
|
|
|
+ base.districtOverview.villageCount = qRows.reduce((s, r) => s + Number(r.villageCount || 0), 0)
|
|
|
|
|
+ base.districtOverview.townPeopleChart = names.map((n) => ({
|
|
|
|
|
+ name: n,
|
|
|
|
|
+ value: byTownPay.get(n)?.people || Number(qByName.get(n)?.peopleCount || 0),
|
|
|
|
|
+ }))
|
|
|
|
|
+ base.districtOverview.townAmountChart = names.map((n) => ({
|
|
|
|
|
+ name: n,
|
|
|
|
|
+ value: byTownPay.get(n)?.amount || 0,
|
|
|
|
|
+ }))
|
|
|
|
|
+ base.districtOverview.townWithLandChart = names.map((n) => ({
|
|
|
|
|
+ name: n,
|
|
|
|
|
+ value: Number(qByName.get(n)?.withLandCount || 0),
|
|
|
|
|
+ }))
|
|
|
|
|
+ base.districtOverview.townWithoutLandChart = names.map((n) => ({
|
|
|
|
|
+ name: n,
|
|
|
|
|
+ value: Number(qByName.get(n)?.withoutLandCount || 0),
|
|
|
|
|
+ }))
|
|
|
|
|
+ base.districtOverview.qualifyPie = [
|
|
|
|
|
+ { name: '有土地退养', value: withLand },
|
|
|
|
|
+ { name: '无土地有资格', value: withoutLand },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '未标注资格',
|
|
|
|
|
+ value: Math.max(0, Number(wb.personnelCount || 0) - withLand - withoutLand),
|
|
|
|
|
+ },
|
|
|
|
|
+ ].filter((i) => i.value > 0)
|
|
|
|
|
+ base.districtOverview.townSummary = names.map((n) => {
|
|
|
|
|
+ const q = qByName.get(n)
|
|
|
|
|
+ return {
|
|
|
|
|
+ town: n,
|
|
|
|
|
+ villageCount: Number(q?.villageCount || 0),
|
|
|
|
|
+ peopleCount: byTownPay.get(n)?.people || Number(q?.peopleCount || 0),
|
|
|
|
|
+ amount: byTownPay.get(n)?.amount || 0,
|
|
|
|
|
+ withLandCount: Number(q?.withLandCount || 0),
|
|
|
|
|
+ withoutLandCount: Number(q?.withoutLandCount || 0),
|
|
|
|
|
+ qualifiedCount: Number(q?.qualifiedCount || 0),
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ base.townAmountChart = base.districtOverview.townAmountChart
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return base
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/** —— 人员 / 村批 —— */
|
|
/** —— 人员 / 村批 —— */
|
|
@@ -802,6 +1054,8 @@ export async function reviewPersonQualify(payload: {
|
|
|
}) {
|
|
}) {
|
|
|
const id = personIdStr(payload.personId)
|
|
const id = personIdStr(payload.personId)
|
|
|
const detail = await get<Record<string, unknown>>(`/personnel/${encodeURIComponent(id)}`)
|
|
const detail = await get<Record<string, unknown>>(`/personnel/${encodeURIComponent(id)}`)
|
|
|
|
|
+ const code =
|
|
|
|
|
+ payload.retireQualifyType === '无土地有资格' ? 'WITHOUT_LAND' : 'WITH_LAND'
|
|
|
const tip =
|
|
const tip =
|
|
|
payload.retireQualifyType === '无土地有资格'
|
|
payload.retireQualifyType === '无土地有资格'
|
|
|
? '无承包地,按政策认定具备退养补助资格;资格已核验'
|
|
? '无承包地,按政策认定具备退养补助资格;资格已核验'
|
|
@@ -818,6 +1072,7 @@ export async function reviewPersonQualify(payload: {
|
|
|
monthlyStandard: detail?.monthlyStandard,
|
|
monthlyStandard: detail?.monthlyStandard,
|
|
|
enjoyStartMonth: detail?.enjoyStartMonth,
|
|
enjoyStartMonth: detail?.enjoyStartMonth,
|
|
|
source: detail?.source,
|
|
source: detail?.source,
|
|
|
|
|
+ retireQualifyType: code,
|
|
|
remarks: [detail?.remarks, tip].filter(Boolean).join(';'),
|
|
remarks: [detail?.remarks, tip].filter(Boolean).join(';'),
|
|
|
})
|
|
})
|
|
|
return { ok: true }
|
|
return { ok: true }
|
|
@@ -1093,19 +1348,17 @@ export async function createSpecialBiz(payload: {
|
|
|
return { ok: true, id: String(created?.id || '') }
|
|
return { ok: true, id: String(created?.id || '') }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-export async function exportSpecialSummary(type?: string) {
|
|
|
|
|
- return {
|
|
|
|
|
- ok: true,
|
|
|
|
|
- fileName: type
|
|
|
|
|
- ? `${currentPayMonth()}-${type}汇总文件.xlsx`
|
|
|
|
|
- : '特殊业务汇总文件.xlsx',
|
|
|
|
|
- }
|
|
|
|
|
|
|
+export async function exportSpecialSummary(_type?: string) {
|
|
|
|
|
+ return { ok: false as const, fileName: '', message: '特殊业务汇总导出未对接后端接口' }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export async function triggerAdjustReissue(town?: string) {
|
|
export async function triggerAdjustReissue(town?: string) {
|
|
|
const cfg = getAppConfig()
|
|
const cfg = getAppConfig()
|
|
|
const role = cfg.roles.find((r) => r.key === cfg.currentRole)
|
|
const role = cfg.roles.find((r) => r.key === cfg.currentRole)
|
|
|
- const townName = town || role?.town || '叶榭镇'
|
|
|
|
|
|
|
+ const townName = town || role?.town
|
|
|
|
|
+ if (!townName) {
|
|
|
|
|
+ return { ok: false, message: '请指定街镇', batchNo: currentPayMonth(), count: 0, villageCount: 0 }
|
|
|
|
|
+ }
|
|
|
const { townId } = await resolveScopeIds(townName)
|
|
const { townId } = await resolveScopeIds(townName)
|
|
|
if (!townId) return { ok: false, message: '未识别街镇', batchNo: currentPayMonth(), count: 0, villageCount: 0 }
|
|
if (!townId) return { ok: false, message: '未识别街镇', batchNo: currentPayMonth(), count: 0, villageCount: 0 }
|
|
|
const created = await post<Record<string, unknown>>('/special-biz', {
|
|
const created = await post<Record<string, unknown>>('/special-biz', {
|