|
|
@@ -252,3 +252,487 @@ export const fetchElderStructure = async (
|
|
|
campusStats: Array.isArray(data?.campusStats) ? data.campusStats : []
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/* ============================== 入住运营二期:退住原因与平均入住时长 ============================== */
|
|
|
+
|
|
|
+export interface MoveoutReasonBucket {
|
|
|
+ reasonValue: string | number | null
|
|
|
+ reasonLabel: string
|
|
|
+ count: number
|
|
|
+ avgStayDays: number | null
|
|
|
+}
|
|
|
+
|
|
|
+export interface MoveoutMonthTrend {
|
|
|
+ month: string
|
|
|
+ total: number
|
|
|
+ reasonMap: Record<string, number>
|
|
|
+}
|
|
|
+
|
|
|
+export interface MoveoutCampusStat {
|
|
|
+ tenantId: number
|
|
|
+ tenantName: string
|
|
|
+ total: number
|
|
|
+ avgStayDays: number | null
|
|
|
+ reasonBuckets: MoveoutReasonBucket[]
|
|
|
+}
|
|
|
+
|
|
|
+export interface MoveoutAnalysisData {
|
|
|
+ totalMoveOut: number
|
|
|
+ avgStayDays: number | null
|
|
|
+ reasonBuckets: MoveoutReasonBucket[]
|
|
|
+ monthTrend: MoveoutMonthTrend[]
|
|
|
+ campusStats: MoveoutCampusStat[]
|
|
|
+}
|
|
|
+
|
|
|
+export interface MoveoutAnalysisParams {
|
|
|
+ tenantIds?: number[]
|
|
|
+ startDate?: string
|
|
|
+ endDate?: string
|
|
|
+}
|
|
|
+
|
|
|
+/** 接口 E:退住原因与平均入住时长聚合 */
|
|
|
+export const fetchMoveoutAnalysis = async (
|
|
|
+ params: MoveoutAnalysisParams = {}
|
|
|
+): Promise<MoveoutAnalysisData> => {
|
|
|
+ try {
|
|
|
+ const data = await request.get({
|
|
|
+ url: `/operation-analysis/overview/moveout-analysis${buildQuery({ orgType: 1, ...params })}`
|
|
|
+ })
|
|
|
+ return {
|
|
|
+ totalMoveOut: Number(data?.totalMoveOut) || 0,
|
|
|
+ avgStayDays: data?.avgStayDays ?? null,
|
|
|
+ reasonBuckets: Array.isArray(data?.reasonBuckets) ? data.reasonBuckets : [],
|
|
|
+ monthTrend: Array.isArray(data?.monthTrend) ? data.monthTrend : [],
|
|
|
+ campusStats: Array.isArray(data?.campusStats) ? data.campusStats : []
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('[moveout-analysis] 后端接口尚未上线,返回演示数据', e)
|
|
|
+ // MOCK:后端接口上线后删除以下数据
|
|
|
+ return getMockMoveoutAnalysisData()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* ============================== 入住运营二期:试住转化分析 ============================== */
|
|
|
+
|
|
|
+export type TrialStatus = 'converted' | 'not_converted' | 'in_trial'
|
|
|
+
|
|
|
+export interface TrialStatusBucket {
|
|
|
+ status: TrialStatus
|
|
|
+ label: string
|
|
|
+ count: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface TrialMonthTrend {
|
|
|
+ month: string
|
|
|
+ trialCount: number
|
|
|
+ convertedCount: number
|
|
|
+ notConvertedCount: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface TrialCampusStat {
|
|
|
+ tenantId: number
|
|
|
+ tenantName: string
|
|
|
+ totalTrial: number
|
|
|
+ convertedCount: number
|
|
|
+ notConvertedCount: number
|
|
|
+ conversionRate: number | null
|
|
|
+ avgTrialDays: number | null
|
|
|
+}
|
|
|
+
|
|
|
+export interface TrialElderItem {
|
|
|
+ elderId: number
|
|
|
+ elderName: string
|
|
|
+ gender: number | string
|
|
|
+ age: number | null
|
|
|
+ tenantId: number
|
|
|
+ tenantName: string
|
|
|
+ trialStartDate: string
|
|
|
+ convertedDate: string | null
|
|
|
+ status: TrialStatus
|
|
|
+ trialDays: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface TrialConversionData {
|
|
|
+ totalTrial: number
|
|
|
+ convertedCount: number
|
|
|
+ notConvertedCount: number
|
|
|
+ inTrialCount: number
|
|
|
+ conversionRate: number | null
|
|
|
+ avgTrialDays: number | null
|
|
|
+ statusBuckets: TrialStatusBucket[]
|
|
|
+ monthTrend: TrialMonthTrend[]
|
|
|
+ campusStats: TrialCampusStat[]
|
|
|
+ list: TrialElderItem[]
|
|
|
+}
|
|
|
+
|
|
|
+export interface TrialConversionParams {
|
|
|
+ tenantIds?: number[]
|
|
|
+ startDate?: string
|
|
|
+ endDate?: string
|
|
|
+}
|
|
|
+
|
|
|
+/** 接口 F:试住转化分析聚合 */
|
|
|
+export const fetchTrialConversion = async (
|
|
|
+ params: TrialConversionParams = {}
|
|
|
+): Promise<TrialConversionData> => {
|
|
|
+ try {
|
|
|
+ const data = await request.get({
|
|
|
+ url: `/operation-analysis/overview/trial-conversion${buildQuery({ orgType: 1, ...params })}`
|
|
|
+ })
|
|
|
+ return {
|
|
|
+ totalTrial: Number(data?.totalTrial) || 0,
|
|
|
+ convertedCount: Number(data?.convertedCount) || 0,
|
|
|
+ notConvertedCount: Number(data?.notConvertedCount) || 0,
|
|
|
+ inTrialCount: Number(data?.inTrialCount) || 0,
|
|
|
+ conversionRate: data?.conversionRate ?? null,
|
|
|
+ avgTrialDays: data?.avgTrialDays ?? null,
|
|
|
+ statusBuckets: Array.isArray(data?.statusBuckets) ? data.statusBuckets : [],
|
|
|
+ monthTrend: Array.isArray(data?.monthTrend) ? data.monthTrend : [],
|
|
|
+ campusStats: Array.isArray(data?.campusStats) ? data.campusStats : [],
|
|
|
+ list: Array.isArray(data?.list) ? data.list : []
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('[trial-conversion] 后端接口尚未上线,返回演示数据', e)
|
|
|
+ return getMockTrialConversionData()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* ============================== 入住运营二期:轮候长者分析 ============================== */
|
|
|
+
|
|
|
+export interface WaitingNurseLevelBucket {
|
|
|
+ nurseLevelId: number
|
|
|
+ nurseLevelName: string
|
|
|
+ count: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface WaitingDaysBucket {
|
|
|
+ label: string
|
|
|
+ count: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface WaitingMonthTrend {
|
|
|
+ month: string
|
|
|
+ newCount: number
|
|
|
+ convertedCount: number
|
|
|
+ canceledCount: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface WaitingCampusStat {
|
|
|
+ tenantId: number
|
|
|
+ tenantName: string
|
|
|
+ totalWaiting: number
|
|
|
+ avgWaitingDays: number | null
|
|
|
+ maxWaitingDays: number | null
|
|
|
+}
|
|
|
+
|
|
|
+export interface WaitingElderItem {
|
|
|
+ id: number
|
|
|
+ elderId: number
|
|
|
+ elderName: string
|
|
|
+ gender: number | string
|
|
|
+ age: number | null
|
|
|
+ tenantId: number
|
|
|
+ tenantName: string
|
|
|
+ nurseLevelId: number | null
|
|
|
+ nurseLevelName: string | null
|
|
|
+ waitingDays: number
|
|
|
+ registerDate: string
|
|
|
+ priority: string | null
|
|
|
+ status: string | null
|
|
|
+ contactName: string | null
|
|
|
+ contactPhone: string | null
|
|
|
+}
|
|
|
+
|
|
|
+export interface WaitingAnalysisData {
|
|
|
+ totalWaiting: number
|
|
|
+ avgWaitingDays: number | null
|
|
|
+ maxWaitingDays: number | null
|
|
|
+ nursingLevelBuckets: WaitingNurseLevelBucket[]
|
|
|
+ waitingDaysBuckets: WaitingDaysBucket[]
|
|
|
+ monthTrend: WaitingMonthTrend[]
|
|
|
+ campusStats: WaitingCampusStat[]
|
|
|
+ list: WaitingElderItem[]
|
|
|
+}
|
|
|
+
|
|
|
+/** 接口 G:轮候长者分析聚合 */
|
|
|
+export const fetchWaitingAnalysis = async (
|
|
|
+ tenantIds: number[] = []
|
|
|
+): Promise<WaitingAnalysisData> => {
|
|
|
+ try {
|
|
|
+ const data = await request.get({
|
|
|
+ url: `/operation-analysis/overview/waiting-analysis${buildQuery({ orgType: 1, tenantIds })}`
|
|
|
+ })
|
|
|
+ return {
|
|
|
+ totalWaiting: Number(data?.totalWaiting) || 0,
|
|
|
+ avgWaitingDays: data?.avgWaitingDays ?? null,
|
|
|
+ maxWaitingDays: data?.maxWaitingDays ?? null,
|
|
|
+ nursingLevelBuckets: Array.isArray(data?.nursingLevelBuckets) ? data.nursingLevelBuckets : [],
|
|
|
+ waitingDaysBuckets: Array.isArray(data?.waitingDaysBuckets) ? data.waitingDaysBuckets : [],
|
|
|
+ monthTrend: Array.isArray(data?.monthTrend) ? data.monthTrend : [],
|
|
|
+ campusStats: Array.isArray(data?.campusStats) ? data.campusStats : [],
|
|
|
+ list: Array.isArray(data?.list) ? data.list : []
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('[waiting-analysis] 后端接口尚未上线,返回演示数据', e)
|
|
|
+ return getMockWaitingAnalysisData()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* ============================== 入住运营二期:合同到期预警 ============================== */
|
|
|
+
|
|
|
+export interface ContractExpirySummary {
|
|
|
+ days: number
|
|
|
+ expiringCount: number
|
|
|
+ expiredCount: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface ContractExpiryBucket {
|
|
|
+ label: string
|
|
|
+ count: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface ContractExpiryMonthTrend {
|
|
|
+ month: string
|
|
|
+ count: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface ContractExpiryItem {
|
|
|
+ elderId: number
|
|
|
+ elderName: string
|
|
|
+ gender: number | string
|
|
|
+ age: number | null
|
|
|
+ tenantId: number
|
|
|
+ tenantName: string
|
|
|
+ bedName: string | null
|
|
|
+ contractStartDate: string
|
|
|
+ contractEndDate: string
|
|
|
+ remainDays: number
|
|
|
+ contactName: string | null
|
|
|
+ contactPhone: string | null
|
|
|
+}
|
|
|
+
|
|
|
+export interface ContractExpiryData {
|
|
|
+ summary: ContractExpirySummary[]
|
|
|
+ distributionBuckets: ContractExpiryBucket[]
|
|
|
+ monthTrend: ContractExpiryMonthTrend[]
|
|
|
+ list: ContractExpiryItem[]
|
|
|
+}
|
|
|
+
|
|
|
+export interface ContractExpiryParams {
|
|
|
+ tenantIds?: number[]
|
|
|
+ days?: number[]
|
|
|
+}
|
|
|
+
|
|
|
+/** 接口 H:合同到期预警聚合 */
|
|
|
+export const fetchContractExpiry = async (
|
|
|
+ params: ContractExpiryParams = {}
|
|
|
+): Promise<ContractExpiryData> => {
|
|
|
+ try {
|
|
|
+ const data = await request.get({
|
|
|
+ url: `/operation-analysis/overview/contract-expiry${buildQuery({ orgType: 1, ...params })}`
|
|
|
+ })
|
|
|
+ return {
|
|
|
+ summary: Array.isArray(data?.summary) ? data.summary : [],
|
|
|
+ distributionBuckets: Array.isArray(data?.distributionBuckets) ? data.distributionBuckets : [],
|
|
|
+ monthTrend: Array.isArray(data?.monthTrend) ? data.monthTrend : [],
|
|
|
+ list: Array.isArray(data?.list) ? data.list : []
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('[contract-expiry] 后端接口尚未上线,返回演示数据', e)
|
|
|
+ return getMockContractExpiryData()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* ============================== 二期演示数据(后端接口上线后删除) ============================== */
|
|
|
+
|
|
|
+const getMockMoveoutAnalysisData = (): MoveoutAnalysisData => {
|
|
|
+ const reasonBuckets: MoveoutReasonBucket[] = [
|
|
|
+ { reasonValue: '1', reasonLabel: '正常退住', count: 42, avgStayDays: 386 },
|
|
|
+ { reasonValue: '2', reasonLabel: '死亡退住', count: 18, avgStayDays: 521 },
|
|
|
+ { reasonValue: '3', reasonLabel: '转院退住', count: 8, avgStayDays: 156 },
|
|
|
+ { reasonValue: '4', reasonLabel: '试住结束', count: 15, avgStayDays: 32 },
|
|
|
+ { reasonValue: '5', reasonLabel: '居家照护', count: 5, avgStayDays: 240 },
|
|
|
+ { reasonValue: '9', reasonLabel: '其他', count: 3, avgStayDays: 180 }
|
|
|
+ ]
|
|
|
+ const totalMoveOut = reasonBuckets.reduce((s, i) => s + i.count, 0)
|
|
|
+ const avgStayDays =
|
|
|
+ reasonBuckets.reduce((s, i) => s + i.count * (i.avgStayDays || 0), 0) / totalMoveOut
|
|
|
+ return {
|
|
|
+ totalMoveOut,
|
|
|
+ avgStayDays: Number(avgStayDays.toFixed(1)),
|
|
|
+ reasonBuckets,
|
|
|
+ monthTrend: [
|
|
|
+ { month: '2025-10', total: 8, reasonMap: { '1': 4, '2': 2, '4': 2 } },
|
|
|
+ { month: '2025-11', total: 6, reasonMap: { '1': 3, '2': 1, '3': 2 } },
|
|
|
+ { month: '2025-12', total: 9, reasonMap: { '1': 5, '2': 2, '4': 2 } },
|
|
|
+ { month: '2026-01', total: 7, reasonMap: { '1': 4, '2': 2, '9': 1 } },
|
|
|
+ { month: '2026-02', total: 5, reasonMap: { '1': 2, '2': 1, '3': 2 } },
|
|
|
+ { month: '2026-03', total: 10, reasonMap: { '1': 6, '2': 2, '4': 2 } },
|
|
|
+ { month: '2026-04', total: 8, reasonMap: { '1': 4, '2': 2, '5': 2 } },
|
|
|
+ { month: '2026-05', total: 12, reasonMap: { '1': 7, '2': 3, '4': 2 } },
|
|
|
+ { month: '2026-06', total: 9, reasonMap: { '1': 5, '2': 2, '3': 2 } },
|
|
|
+ { month: '2026-07', total: 11, reasonMap: { '1': 6, '2': 3, '4': 2 } },
|
|
|
+ { month: '2026-08', total: 7, reasonMap: { '1': 4, '2': 2, '9': 1 } },
|
|
|
+ { month: '2026-09', total: 6, reasonMap: { '1': 3, '2': 2, '4': 1 } }
|
|
|
+ ],
|
|
|
+ campusStats: [
|
|
|
+ { tenantId: 196, tenantName: '颐年东涌养老院', total: 24, avgStayDays: 320, reasonBuckets: reasonBuckets.map((r) => ({ ...r, count: Math.round(r.count / 4) })) },
|
|
|
+ { tenantId: 197, tenantName: '颐年五心养老院', total: 18, avgStayDays: 410, reasonBuckets: reasonBuckets.map((r) => ({ ...r, count: Math.round(r.count / 5) })) },
|
|
|
+ { tenantId: 198, tenantName: '其他院区', total: 52, avgStayDays: 365, reasonBuckets: reasonBuckets.map((r) => ({ ...r, count: Math.round(r.count / 2) })) }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getMockTrialConversionData = (): TrialConversionData => {
|
|
|
+ const totalTrial = 86
|
|
|
+ const convertedCount = 58
|
|
|
+ const notConvertedCount = 20
|
|
|
+ const inTrialCount = 8
|
|
|
+ const conversionRate = Number(((convertedCount / (convertedCount + notConvertedCount)) * 100).toFixed(1))
|
|
|
+ return {
|
|
|
+ totalTrial,
|
|
|
+ convertedCount,
|
|
|
+ notConvertedCount,
|
|
|
+ inTrialCount,
|
|
|
+ conversionRate,
|
|
|
+ avgTrialDays: 18.5,
|
|
|
+ statusBuckets: [
|
|
|
+ { status: 'converted', label: '已转化', count: convertedCount },
|
|
|
+ { status: 'not_converted', label: '未转化', count: notConvertedCount },
|
|
|
+ { status: 'in_trial', label: '试住中', count: inTrialCount }
|
|
|
+ ],
|
|
|
+ monthTrend: [
|
|
|
+ { month: '2025-10', trialCount: 6, convertedCount: 4, notConvertedCount: 2 },
|
|
|
+ { month: '2025-11', trialCount: 5, convertedCount: 3, notConvertedCount: 1 },
|
|
|
+ { month: '2025-12', trialCount: 8, convertedCount: 5, notConvertedCount: 2 },
|
|
|
+ { month: '2026-01', trialCount: 7, convertedCount: 5, notConvertedCount: 1 },
|
|
|
+ { month: '2026-02', trialCount: 6, convertedCount: 4, notConvertedCount: 1 },
|
|
|
+ { month: '2026-03', trialCount: 9, convertedCount: 6, notConvertedCount: 2 },
|
|
|
+ { month: '2026-04', trialCount: 8, convertedCount: 6, notConvertedCount: 2 },
|
|
|
+ { month: '2026-05', trialCount: 10, convertedCount: 7, notConvertedCount: 2 },
|
|
|
+ { month: '2026-06', trialCount: 7, convertedCount: 5, notConvertedCount: 2 },
|
|
|
+ { month: '2026-07', trialCount: 9, convertedCount: 6, notConvertedCount: 2 },
|
|
|
+ { month: '2026-08', trialCount: 8, convertedCount: 6, notConvertedCount: 1 },
|
|
|
+ { month: '2026-09', trialCount: 3, convertedCount: 1, notConvertedCount: 2 }
|
|
|
+ ],
|
|
|
+ campusStats: [
|
|
|
+ { tenantId: 196, tenantName: '颐年东涌养老院', totalTrial: 20, convertedCount: 15, notConvertedCount: 3, conversionRate: 83.3, avgTrialDays: 16 },
|
|
|
+ { tenantId: 197, tenantName: '颐年五心养老院', totalTrial: 18, convertedCount: 12, notConvertedCount: 4, conversionRate: 75.0, avgTrialDays: 21 },
|
|
|
+ { tenantId: 198, tenantName: '其他院区', totalTrial: 48, convertedCount: 31, notConvertedCount: 13, conversionRate: 70.5, avgTrialDays: 19 }
|
|
|
+ ],
|
|
|
+ list: Array.from({ length: 12 }).map((_, i) => ({
|
|
|
+ elderId: 1000 + i,
|
|
|
+ elderName: `试住长者${i + 1}`,
|
|
|
+ gender: i % 2 === 0 ? 1 : 2,
|
|
|
+ age: 75 + (i % 15),
|
|
|
+ tenantId: 196 + (i % 3),
|
|
|
+ tenantName: ['颐年东涌养老院', '颐年五心养老院', '其他院区'][i % 3],
|
|
|
+ trialStartDate: `2026-09-${String(10 + i).padStart(2, '0')}`,
|
|
|
+ convertedDate: i < 8 ? `2026-09-${String(20 + i).padStart(2, '0')}` : null,
|
|
|
+ status: (i < 8 ? 'converted' : i < 10 ? 'in_trial' : 'not_converted') as TrialStatus,
|
|
|
+ trialDays: 12 + i * 2
|
|
|
+ }))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getMockWaitingAnalysisData = (): WaitingAnalysisData => {
|
|
|
+ return {
|
|
|
+ totalWaiting: 55,
|
|
|
+ avgWaitingDays: 23.5,
|
|
|
+ maxWaitingDays: 96,
|
|
|
+ nursingLevelBuckets: [
|
|
|
+ { nurseLevelId: 1, nurseLevelName: '一级护理', count: 22 },
|
|
|
+ { nurseLevelId: 2, nurseLevelName: '二级护理', count: 18 },
|
|
|
+ { nurseLevelId: 3, nurseLevelName: '三级护理', count: 8 },
|
|
|
+ { nurseLevelId: 4, nurseLevelName: '特级护理', count: 5 },
|
|
|
+ { nurseLevelId: 0, nurseLevelName: '未设置', count: 2 }
|
|
|
+ ],
|
|
|
+ waitingDaysBuckets: [
|
|
|
+ { label: '0-7天', count: 12 },
|
|
|
+ { label: '8-30天', count: 20 },
|
|
|
+ { label: '31-90天', count: 15 },
|
|
|
+ { label: '90天以上', count: 8 }
|
|
|
+ ],
|
|
|
+ monthTrend: [
|
|
|
+ { month: '2025-10', newCount: 8, convertedCount: 5, canceledCount: 1 },
|
|
|
+ { month: '2025-11', newCount: 6, convertedCount: 4, canceledCount: 0 },
|
|
|
+ { month: '2025-12', newCount: 9, convertedCount: 6, canceledCount: 1 },
|
|
|
+ { month: '2026-01', newCount: 7, convertedCount: 5, canceledCount: 1 },
|
|
|
+ { month: '2026-02', newCount: 5, convertedCount: 3, canceledCount: 1 },
|
|
|
+ { month: '2026-03', newCount: 10, convertedCount: 7, canceledCount: 1 },
|
|
|
+ { month: '2026-04', newCount: 8, convertedCount: 5, canceledCount: 1 },
|
|
|
+ { month: '2026-05', newCount: 12, convertedCount: 8, canceledCount: 1 },
|
|
|
+ { month: '2026-06', newCount: 9, convertedCount: 6, canceledCount: 1 },
|
|
|
+ { month: '2026-07', newCount: 11, convertedCount: 7, canceledCount: 1 },
|
|
|
+ { month: '2026-08', newCount: 7, convertedCount: 5, canceledCount: 1 },
|
|
|
+ { month: '2026-09', newCount: 6, convertedCount: 4, canceledCount: 0 }
|
|
|
+ ],
|
|
|
+ campusStats: [
|
|
|
+ { tenantId: 196, tenantName: '颐年东涌养老院', totalWaiting: 18, avgWaitingDays: 20, maxWaitingDays: 78 },
|
|
|
+ { tenantId: 197, tenantName: '颐年五心养老院', totalWaiting: 15, avgWaitingDays: 28, maxWaitingDays: 96 },
|
|
|
+ { tenantId: 198, tenantName: '其他院区', totalWaiting: 22, avgWaitingDays: 22, maxWaitingDays: 85 }
|
|
|
+ ],
|
|
|
+ list: Array.from({ length: 12 }).map((_, i) => ({
|
|
|
+ id: 2000 + i,
|
|
|
+ elderId: 3000 + i,
|
|
|
+ elderName: `轮候长者${i + 1}`,
|
|
|
+ gender: i % 2 === 0 ? 2 : 1,
|
|
|
+ age: 78 + (i % 12),
|
|
|
+ tenantId: 196 + (i % 3),
|
|
|
+ tenantName: ['颐年东涌养老院', '颐年五心养老院', '其他院区'][i % 3],
|
|
|
+ nurseLevelId: [1, 2, 3, 4, 0][i % 5],
|
|
|
+ nurseLevelName: ['一级护理', '二级护理', '三级护理', '特级护理', '未设置'][i % 5],
|
|
|
+ waitingDays: 5 + i * 7,
|
|
|
+ registerDate: `2026-09-${String(1 + i).padStart(2, '0')}`,
|
|
|
+ priority: ['高', '中', '低'][i % 3],
|
|
|
+ status: '排队中',
|
|
|
+ contactName: `家属${i + 1}`,
|
|
|
+ contactPhone: `1380013${String(1000 + i).slice(1)}`
|
|
|
+ }))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getMockContractExpiryData = (): ContractExpiryData => {
|
|
|
+ return {
|
|
|
+ summary: [
|
|
|
+ { days: 30, expiringCount: 18, expiredCount: 3 },
|
|
|
+ { days: 60, expiringCount: 45, expiredCount: 3 },
|
|
|
+ { days: 90, expiringCount: 72, expiredCount: 3 }
|
|
|
+ ],
|
|
|
+ distributionBuckets: [
|
|
|
+ { label: '已过期', count: 3 },
|
|
|
+ { label: '0-30天', count: 15 },
|
|
|
+ { label: '31-60天', count: 27 },
|
|
|
+ { label: '61-90天', count: 27 },
|
|
|
+ { label: '90天以上', count: 18 }
|
|
|
+ ],
|
|
|
+ monthTrend: [
|
|
|
+ { month: '2026-10', count: 22 },
|
|
|
+ { month: '2026-11', count: 18 },
|
|
|
+ { month: '2026-12', count: 15 },
|
|
|
+ { month: '2027-01', count: 12 },
|
|
|
+ { month: '2027-02', count: 10 },
|
|
|
+ { month: '2027-03', count: 14 },
|
|
|
+ { month: '2027-04', count: 16 },
|
|
|
+ { month: '2027-05', count: 11 },
|
|
|
+ { month: '2027-06', count: 9 },
|
|
|
+ { month: '2027-07', count: 13 },
|
|
|
+ { month: '2027-08', count: 17 },
|
|
|
+ { month: '2027-09', count: 20 }
|
|
|
+ ],
|
|
|
+ list: Array.from({ length: 15 }).map((_, i) => ({
|
|
|
+ elderId: 4000 + i,
|
|
|
+ elderName: `合同长者${i + 1}`,
|
|
|
+ gender: i % 2 === 0 ? 1 : 2,
|
|
|
+ age: 80 + (i % 10),
|
|
|
+ tenantId: 196 + (i % 3),
|
|
|
+ tenantName: ['颐年东涌养老院', '颐年五心养老院', '其他院区'][i % 3],
|
|
|
+ bedName: `1栋-${101 + i}床`,
|
|
|
+ contractStartDate: '2025-10-01',
|
|
|
+ contractEndDate: `2026-${String(10 + (i % 3)).padStart(2, '0')}-${String(1 + (i % 28)).padStart(2, '0')}`,
|
|
|
+ remainDays: -5 + i * 8,
|
|
|
+ contactName: `家属${i + 1}`,
|
|
|
+ contactPhone: `1390013${String(1000 + i).slice(1)}`
|
|
|
+ }))
|
|
|
+ }
|
|
|
+}
|