This commit is contained in:
gr
2026-10-15 11:03:54 +08:00
parent 70ddc1a70a
commit bb28d93cec
64 changed files with 12747 additions and 420 deletions
+195
View File
@@ -0,0 +1,195 @@
import request from '@/utils/request'
/**
* 复诊到期弹窗(需求 4)接口:/adminapi/tcm.revisitDue/<action>
* 契约见 docs/plans/assistant-revisit-reach-reminder-2026-10-12.md §3.3。
* 写接口后端 show=1,成功提示由 utils/request 统一弹出,调用方不要重复 ElMessage。
*/
/** 弹窗条目(RevisitDueLogic::formatItem) */
export interface RevisitDueToastItem {
id: number
diagnosis_id: number
assistant_id: number
patient_name: string
current_slot: number
next_slot: number
medication_days: number
/** 'YYYY-MM-DD' 或 '' */
due_date: string
overdue_days: number | null
/** overdue | missed(弹窗只含这两态) */
state: string
state_text: string
severe: 0 | 1
missed_count: number
snooze_count: number
/** 0~3,3 = 居中模态 */
escalation_level: number
contact_count: number
promised_date: string
next_remind_at: number
next_remind_at_text: string
/** 下一次点「延迟」会顺延的秒数(按钮文案用) */
next_snooze_seconds: number
appointment_date: string
closed_state: number
}
/** 汇总计数(RevisitDueLogic::buildSummary) */
export interface RevisitDueToastSummary {
enabled: boolean
due_total: number
max_overdue_days: number
warning_total: number
promise_max_days: number
contact_max_per_cycle: number
work_hours: [string, string]
}
export interface RevisitDuePendingResult {
items: RevisitDueToastItem[]
summary: RevisitDueToastSummary
}
/** 写接口返回处理后的当前行(幂等:被他人处理时也返回当前行) */
export type RevisitDueWriteResult = RevisitDueToastItem
export interface RevisitDueSnoozeAllResult {
items: RevisitDueToastItem[]
snoozed: number
skipped: number
message: string
}
/** 轮询接口不走统一 code 处理:权限不足 / 开关关闭时不要每分钟弹一次错误提示 */
const POLL_OPTIONS = {
isTransformResponse: false,
ignoreCancelToken: true,
isOpenRetry: false,
retryCount: 0
}
/**
* 弹窗待办(60s 轮询):本人到期未挂号 / 过号且已到提醒时刻的项,每次返回全集。
* 非 code=1 一律 reject(调用方静默处理),避免轮询刷错误提示。
*/
export async function revisitDuePending(): Promise<RevisitDuePendingResult> {
const res = await request.get({ url: '/tcm.revisitDue/pending' }, POLL_OPTIONS)
if (res && typeof res === 'object' && Number(res.code) === 1) {
return res.data as RevisitDuePendingResult
}
return Promise.reject(res?.msg ?? 'revisit due pending failed')
}
/** 仅计数,不更新弹出时间 */
export function revisitDueSummary(): Promise<RevisitDueToastSummary> {
return request.get({ url: '/tcm.revisitDue/summary' })
}
/** 延迟一次(幂等) */
export function revisitDueSnooze(params: { id: number }): Promise<RevisitDueWriteResult> {
return request.post({ url: '/tcm.revisitDue/snooze', params })
}
/** 批量延迟(仅 escalation_level<=1 的项) */
export function revisitDueSnoozeAll(params: {
ids: number[]
}): Promise<RevisitDueSnoozeAllResult> {
return request.post({ url: '/tcm.revisitDue/snoozeAll', params })
}
/** 已联系:记录患者承诺复诊日 */
export function revisitDueContact(params: {
id: number
promised_date: string
remark?: string
}): Promise<RevisitDueWriteResult> {
return request.post({ url: '/tcm.revisitDue/contact', params })
}
/** 人工关闭(reason lost|other,remark 必填 ≤200) */
export function revisitDueClose(params: {
id: number
reason: 'lost' | 'other'
remark: string
}): Promise<RevisitDueWriteResult> {
return request.post({ url: '/tcm.revisitDue/close', params })
}
/** 主管视图列表行(RevisitDueLists,按角色 data_scope 收窄) */
export interface RevisitDueListRow {
id: number
diagnosis_id: number
patient_name: string
assistant_id: number
assistant_name: string
dept_name: string
current_slot: number
next_slot: number
medication_days: number
/** 14/21/28/30 或 0=其它 */
rule_days: number
anchor_date: string
due_date: string
overdue_days: number | null
days_left: number | null
state: string
state_text: string
severe: 0 | 1
appointment_state: number
appointment_date: string
snooze_count: number
escalation_level: number
contact_count: number
promised_date: string
missed_count: number
closed_state: number
closed_state_text: string
handle_reason: string
handle_remark: string
first_shown_at_text: string
last_snoozed_at_text: string
computed_at_text: string
}
/** 主管视图筛选参数(state:due|warning|booked|visited|contacted|no_days|pending|closed;days_bucket:14|21|28|30|other) */
export interface RevisitDueListsParams {
assistant_id?: number | ''
dept_id?: number | ''
state?: string
days_bucket?: string
min_snooze?: number | ''
keyword?: string
page_no?: number
page_size?: number
/** 1 = 导出(export-data 组件追加) */
export?: number
[key: string]: any
}
/** 主管视图:复诊到期跟进分页列表(lists/count/page_no/page_size/extend 包络,供 usePaging / export-data 消费) */
export function revisitDueLists(params: RevisitDueListsParams) {
return request.get({ url: '/tcm.revisitDue/lists', params })
}
/** 事件流水行(RevisitDueLogic::logs) */
export interface RevisitDueLogRow {
id: number
/** 1 延迟 2 已联系 3 人工关闭 4 自动关闭-已挂号 5 自动关闭-已面诊 6 周期重置 7 重新打开 8 企微推送 9 医助变更 10 形态升级 11 首次弹出 */
event: number
event_text: string
snooze_count_after: number
level_after: number
overdue_days: number
/** payload JSON 解析后的对象(next_remind_at / promised_date / appointment_id / order_id / remark / from_assistant 等) */
payload_arr: Record<string, any>
operator_id: number
operator_name: string
create_time_text: string
}
/** 主管视图:某条提醒的事件流水(按 due_id,时间倒序由后端决定) */
export function revisitDueLogs(params: { due_id: number }) {
return request.get({ url: '/tcm.revisitDue/logs', params })
}
+50 -4
View File
@@ -204,8 +204,26 @@ export function commissionSettlementConfirmRevoke(params: Record<string, any>) {
return request.post({ url: '/stats.commissionSettlement/confirmRevoke', params })
}
/** 复诊接诊率(按月):当月 N 诊接诊率,部门 → 医助分组 + 合计;剔除「继承」指派 */
export function revisitRateOverview(params: { month?: string; dept_ids?: string }) {
/**
* 复诊接诊率口径:
* - slot(默认,后端 config project.revisit_rate.default_inherit_mode):继承按继承时诊次计入每列独立分母(visit{N}_den = 新指派数 + 起算诊次 ≤ N 的继承数)
* - exclude:继承不计分母(旧口径,assigned_count / visit{N}_count / visit{N}_rate 与 2026-10 前一致)
*/
export type RevisitRateInheritMode = 'exclude' | 'slot'
/** 被指派明细行类型:new 新指派 / inherit 继承 / all 两者(默认) */
export type RevisitRateAssignKind = 'new' | 'inherit' | 'all'
/**
* 复诊接诊率(按月):部门 → 医助分组 + 合计。
* 返回 inherit_mode、slots,行含 assigned_count(新指派)、inherit_count、assigned_total_count、
* visit{N}_count/_den/_rate、visit_all_count/_den/_rate、visit2_rate_new_assign(旧口径二诊率)
*/
export function revisitRateOverview(params: {
month?: string
dept_ids?: string
inherit_mode?: RevisitRateInheritMode
}) {
return request.get({ url: '/stats.revisitRate/overview', params })
}
@@ -214,23 +232,29 @@ export function revisitRateDeptOptions() {
return request.get({ url: '/stats.revisitRate/deptOptions' })
}
/** 复诊接诊率:「被指派数」点击下钻(按诊单聚合明细) */
/**
* 复诊接诊率:「新指派数 / 继承数 / 被指派合计」点击下钻(按诊单聚合明细)。
* 行含 assign_type(new|inherit)、inherit_visit_slot(继承时诊次 k)、entry_slot(起算诊次)
*/
export function revisitRateAssignLines(params: {
month?: string
dept_ids?: string
assistant_id?: number
dept_id?: number
inherit_mode?: RevisitRateInheritMode
kind?: RevisitRateAssignKind
}) {
return request.get({ url: '/stats.revisitRate/assignLines', params })
}
/** 复诊接诊率:「N 诊单数」点击下钻(具体订单明细) */
/** 复诊接诊率:「N 诊单数」点击下钻(具体订单明细);slot=0 为全部 N≥2(复诊合计单数),行含 visit_slot */
export function revisitRateVisitOrderLines(params: {
month?: string
slot: number
dept_ids?: string
assistant_id?: number
dept_id?: number
inherit_mode?: RevisitRateInheritMode
}) {
return request.get({ url: '/stats.revisitRate/visitOrderLines', params })
}
@@ -253,3 +277,25 @@ export function assistantPerformanceOverview(params: {
}) {
return request.get({ url: '/stats.assistantPerformance/overview', params })
}
/** 「我的复诊接诊率」卡片返回(RevisitRateLogic::assistantOverview,仅本人行) */
export interface AssistantRevisitRateResult {
month: string
inherit_mode: RevisitRateInheritMode
/** 诊次列范围(2~N) */
slots: number[]
/** false = 本人不属于二中心子树(复诊接诊率仅统计二中心医助) */
in_scope: boolean
/** 本人行:assigned_count / inherit_count / assigned_total_count / visit{N}_count|_den|_rate / visit_all_*;无数据为 null */
row: Record<string, any> | null
}
/**
* 医助个人业绩:我的复诊接诊率(按月,仅本人行;口径与复诊接诊率页一致,
* 继承患者按继承时诊次从第 k+1 诊起计入分母)
*/
export function assistantPerformanceRevisitRate(params: {
month?: string
}): Promise<AssistantRevisitRateResult> {
return request.get({ url: '/stats.assistantPerformance/revisitRate', params })
}