更新
This commit is contained in:
+20
-1
@@ -110,6 +110,13 @@ const COMMISSION_SETTLEMENT_TIMEOUT_MS = 120000
|
||||
/** 提成结算业绩(独立于业绩看板 yejiStats) */
|
||||
export function commissionSettlementOverview(params: {
|
||||
settlement_month: string
|
||||
/** 与 tcm.prescriptionOrder/lists 同源:create_time between */
|
||||
start_time?: string
|
||||
end_time?: string
|
||||
/** 默认 3=履约完成,与列表 fulfillment_status 一致 */
|
||||
fulfillment_status?: number
|
||||
/** 传 1 时仅统计 is_system_auto=1;显式时段下默认不传(含手动) */
|
||||
require_system_auto_prescription?: 0 | 1
|
||||
dept_ids?: number[] | string
|
||||
channel_code?: string
|
||||
}) {
|
||||
@@ -127,14 +134,21 @@ export function commissionSettlementChannelOptions() {
|
||||
return request.get({ url: '/stats.commissionSettlement/channelOptions' })
|
||||
}
|
||||
|
||||
/** 提成核对:订单明细分页 bucket: 空|current|deferred */
|
||||
/** 提成核对:订单明细分页 bucket: 空|current|deferred;appt_channel_value 可与 assistant_id/doctor_id 组合(0=未匹配挂号渠道) */
|
||||
export function commissionSettlementOrderLines(params: {
|
||||
settlement_month: string
|
||||
start_time?: string
|
||||
end_time?: string
|
||||
fulfillment_status?: number
|
||||
require_system_auto_prescription?: 0 | 1
|
||||
dept_ids?: number[] | string
|
||||
channel_code?: string
|
||||
page?: number
|
||||
page_size?: number
|
||||
bucket?: string
|
||||
assistant_id?: number
|
||||
doctor_id?: number
|
||||
appt_channel_value?: number
|
||||
}) {
|
||||
return request.get(
|
||||
{ url: '/stats.commissionSettlement/orderLines', params, timeout: COMMISSION_SETTLEMENT_TIMEOUT_MS },
|
||||
@@ -160,3 +174,8 @@ export function commissionSettlementConfirmFinalize(params: Record<string, any>)
|
||||
{ ignoreCancelToken: true }
|
||||
)
|
||||
}
|
||||
|
||||
/** 撤回「确定本期业绩」:清除顺延结转,状态变为可再次核对/确定 */
|
||||
export function commissionSettlementConfirmRevoke(params: Record<string, any>) {
|
||||
return request.post({ url: '/stats.commissionSettlement/confirmRevoke', params })
|
||||
}
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
</span>
|
||||
<h1 class="cs-masthead__title">提成结算业绩</h1>
|
||||
<p class="cs-masthead__desc">
|
||||
系统代开处方对应的已完成业务订单;按结算月 7 日截止拆分本期提成与顺延下期;可按部门汇总或下发至业绩归属医助与处方开方医生
|
||||
订单池默认与<strong>处方订单列表</strong>同源:<strong>创建时间 start_time~end_time</strong> +
|
||||
<strong>fulfillment_status</strong>(默认完成态 3),账号<strong>列表可见性/数据域</strong>一并生效。
|
||||
选结算月<strong>仍可</strong>套用「物流签收<strong>与</strong>尾款支付<strong>均须在</strong><strong>结算月 7 日 24 点</strong>前」拆分「本期提成 / 顺延下期」
|
||||
(仅有签收在范围内、尾款晚于截止仍会顺延);
|
||||
展示部门/渠道下拉与列表不同,勾选后会与列表<strong>计数不同</strong>。
|
||||
</p>
|
||||
</div>
|
||||
<dl v-if="meta.settlement_month || meta.cutoff_end" class="cs-masthead__stats">
|
||||
@@ -17,9 +21,9 @@
|
||||
<dt class="cs-stat__label">结算月</dt>
|
||||
<dd class="cs-stat__value">{{ meta.settlement_month || settlementMonth }}</dd>
|
||||
</div>
|
||||
<div v-if="meta.order_month" class="cs-stat">
|
||||
<dt class="cs-stat__label">订单创建月</dt>
|
||||
<dd class="cs-stat__value">{{ meta.order_month }}</dd>
|
||||
<div v-if="meta.order_month || meta.order_start_time" class="cs-stat">
|
||||
<dt class="cs-stat__label">订单时间窗</dt>
|
||||
<dd class="cs-stat__value cs-stat__value--xs">{{ orderWindowLabel }}</dd>
|
||||
</div>
|
||||
<div v-if="meta.cutoff_end" class="cs-stat">
|
||||
<dt class="cs-stat__label">截止时刻</dt>
|
||||
@@ -42,6 +46,21 @@
|
||||
class="filter-month"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-field filter-field--rangewrap">
|
||||
<span class="lbl">订单创建</span>
|
||||
<el-date-picker
|
||||
v-model="orderTimeRange"
|
||||
type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
range-separator="至"
|
||||
start-placeholder="列表 start_time"
|
||||
end-placeholder="列表 end_time"
|
||||
class="filter-datetimerange"
|
||||
/>
|
||||
<el-checkbox v-model="requireSystemAutoOnly" class="filter-sa-check">
|
||||
仅系统代开处方
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<div class="filter-field">
|
||||
<span class="lbl">渠道来源</span>
|
||||
<el-select
|
||||
@@ -117,17 +136,26 @@
|
||||
<div class="cs-alert__body">
|
||||
<p v-if="meta.rule_note">{{ meta.rule_note }}</p>
|
||||
<p v-else>
|
||||
订单创建月为<strong>结算月的上一自然月</strong>;仅<strong>系统代开处方</strong>(完成挂号等自动生成)对应的<strong>履约已完成</strong>业务订单。
|
||||
<strong>签收</strong>时间取物流签收时间;<strong>尾款支付</strong>取关联支付单已支付时间(优先尾款/全部费用类型)。
|
||||
签收与尾款均在<strong>结算月 7 日 24:00 前</strong>完成的计入「本期提成」,其余计入「顺延下期」。
|
||||
请选择结算月并设置<strong>订单创建时间范围</strong>(与处方订单列表 URL 参数
|
||||
<code>start_time</code>/<code>end_time</code>/<code>fulfillment_status</code>
|
||||
一致);切换结算月会自动带出<strong>上一自然月整段</strong>作为默认时段,可按业务修改(例如 2026-04-02~04-30)。
|
||||
「仅系统代开处方」勾选后与列表在未限制开方类型时的条数会不一致。
|
||||
</p>
|
||||
<p v-if="meta.cutoff_end" class="cs-alert__meta">
|
||||
当前结算月:<strong>{{ meta.settlement_month }}</strong>
|
||||
· 订单创建月:<strong>{{ meta.order_month }}</strong>
|
||||
· 订单时间:<strong>{{ orderWindowLabel }}</strong>
|
||||
· 履约状态:<strong>{{ meta.fulfillment_status ?? 3 }}</strong>
|
||||
· 截止时刻:<strong>{{ meta.cutoff_end }}</strong>
|
||||
<template v-if="meta.channel_name">
|
||||
· 渠道:<strong>{{ meta.channel_name }}</strong>
|
||||
</template>
|
||||
<template v-if="meta.settlement_month">
|
||||
· 处方池:<strong>{{
|
||||
meta.require_system_auto_prescription ? '仅系统代开' : '含手动与系统'
|
||||
}}</strong>
|
||||
<template v-if="meta.explicit_order_time_window">
|
||||
· 列表对齐:<strong>是</strong></template>
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
</el-alert>
|
||||
@@ -136,27 +164,43 @@
|
||||
<div class="cs-reconcile">
|
||||
<div class="cs-reconcile__head">
|
||||
<div class="cs-reconcile__tags">
|
||||
<el-tag v-if="confirmInfo?.status === 1" type="success" effect="dark">已确定业绩</el-tag>
|
||||
<el-tag v-else type="warning" effect="plain">待确定业绩</el-tag>
|
||||
<el-tag v-if="settlementConfirmedLocked" type="success" effect="dark">已确定业绩</el-tag>
|
||||
<el-tag v-else-if="confirmInfo?.status === 2" type="warning" effect="plain">已撤回确定</el-tag>
|
||||
<el-tag v-else type="info" effect="plain">待确定业绩</el-tag>
|
||||
<span
|
||||
v-if="confirmInfo?.status === 1 && confirmInfo.confirmed_at_text"
|
||||
v-if="settlementConfirmedLocked && confirmInfo?.confirmed_at_text"
|
||||
class="cs-reconcile__meta"
|
||||
>
|
||||
{{ confirmInfo.confirmed_admin_name || '—' }} · {{ confirmInfo.confirmed_at_text }}
|
||||
</span>
|
||||
<span
|
||||
v-else-if="confirmInfo?.status === 2 && confirmInfo.revoked_at_text"
|
||||
class="cs-reconcile__meta"
|
||||
>
|
||||
撤回:{{ confirmInfo.revoked_admin_name || '—' }} · {{ confirmInfo.revoked_at_text }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="cs-reconcile__toolbar">
|
||||
<el-button type="primary" plain @click="openReconcileDialog">业绩明细核对</el-button>
|
||||
<el-button :disabled="confirmInfo?.status === 1 || reconcileSaving" @click="handleSaveReconcile">
|
||||
<el-button :disabled="settlementConfirmedLocked || reconcileSaving" @click="handleSaveReconcile">
|
||||
保存核对备注
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
:disabled="confirmInfo?.status === 1 || confirmSubmitting"
|
||||
:disabled="settlementConfirmedLocked || confirmSubmitting"
|
||||
@click="handleConfirmFinalize"
|
||||
>
|
||||
确定本期业绩
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="settlementConfirmedLocked"
|
||||
type="danger"
|
||||
plain
|
||||
:loading="confirmRevoking"
|
||||
@click="handleConfirmRevoke"
|
||||
>
|
||||
撤回确定
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
@@ -166,7 +210,7 @@
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
placeholder="核对备注(可与财务线下对齐后填写;确定业绩前可反复保存)"
|
||||
:disabled="confirmInfo?.status === 1"
|
||||
:disabled="settlementConfirmedLocked"
|
||||
class="cs-reconcile__note"
|
||||
/>
|
||||
<p v-if="Number(totalAgg.carry_in_order_count) > 0" class="cs-reconcile__carry">
|
||||
@@ -245,6 +289,8 @@
|
||||
按诊单业绩归属医助 <strong>×</strong> 挂号渠道(doctor_appointment.channels)拆分到行;
|
||||
<strong>「复诊2」「复诊3」「自媒体1」等保持各自独立</strong>,未关联挂号统一归到「未匹配挂号渠道」。
|
||||
同一医助多行<strong>自动合并「医助 / 归属中心」单元格</strong>,「顺延汇入」为上期结转进入本期订单池的单数。
|
||||
<strong>展示部门</strong>有勾选时,本表仅列出<strong>可归入该部门上下文</strong>的业绩医助(与人事部门落在所选部门范围内的账号一致)。
|
||||
<strong>点击挂号渠道</strong>:查看该行对应的业务订单明细(与上方部门筛选一致)。
|
||||
</span>
|
||||
</div>
|
||||
<el-table
|
||||
@@ -264,9 +310,10 @@
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
size="small"
|
||||
class="cs-channel-tag"
|
||||
class="cs-channel-tag cs-channel-tag--clickable"
|
||||
:class="channelTagClass(row.appt_channel_name)"
|
||||
effect="plain"
|
||||
@click.stop="openOrdersForAssistantChannel(row)"
|
||||
>
|
||||
{{ row.appt_channel_name }}
|
||||
</el-tag>
|
||||
@@ -293,6 +340,8 @@
|
||||
<span class="cs-table-head__hint">
|
||||
按处方创建账号(开方人) <strong>×</strong> 挂号渠道拆分;
|
||||
可与医助维度交叉核对;同一医生多行自动合并「医生 / 归属中心」单元格。
|
||||
<strong>展示部门</strong>有勾选时,与医助表相同,仅统计<strong>该部门上下文内</strong>订单对应的开方医生。
|
||||
<strong>点击挂号渠道</strong>:查看该行对应的业务订单明细。
|
||||
</span>
|
||||
</div>
|
||||
<el-table
|
||||
@@ -312,9 +361,10 @@
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
size="small"
|
||||
class="cs-channel-tag"
|
||||
class="cs-channel-tag cs-channel-tag--clickable"
|
||||
:class="channelTagClass(row.appt_channel_name)"
|
||||
effect="plain"
|
||||
@click.stop="openOrdersForDoctorChannel(row)"
|
||||
>
|
||||
{{ row.appt_channel_name }}
|
||||
</el-tag>
|
||||
@@ -341,7 +391,7 @@
|
||||
<el-dialog
|
||||
v-model="reconcileDialogVisible"
|
||||
title="业绩明细核对"
|
||||
width="1040px"
|
||||
width="1280px"
|
||||
destroy-on-close
|
||||
class="cs-dialog"
|
||||
@open="onReconcileDialogOpen"
|
||||
@@ -354,7 +404,26 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-table v-loading="linesLoading" :data="linesRows" border stripe size="small" max-height="440">
|
||||
<el-table-column prop="order_no" label="业务订单号" min-width="116" show-overflow-tooltip />
|
||||
<el-table-column prop="order_no" label="业务订单号" min-width="128" show-overflow-tooltip />
|
||||
<el-table-column label="快递单号" min-width="132" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.tracking_number || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="挂号日期" min-width="124" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.appointment_date_text || '—' }}</span>
|
||||
<el-tag
|
||||
v-if="row.appointment_status && Number(row.appointment_status) !== 3"
|
||||
class="cs-tag-appt-muted"
|
||||
type="info"
|
||||
size="small"
|
||||
effect="plain"
|
||||
>未完结挂号</el-tag
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="挂号渠道" min-width="124" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.appt_channel_name || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="assistant_name" label="业绩医助" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="doctor_name" label="开方医生" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="primary_dept_name" label="归属部门" min-width="100" show-overflow-tooltip />
|
||||
@@ -373,8 +442,20 @@
|
||||
<span v-else class="cs-muted">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sign_time_text" label="签收时间" min-width="152" show-overflow-tooltip />
|
||||
<el-table-column prop="pay_time_text" label="尾款支付" min-width="152" show-overflow-tooltip />
|
||||
<el-table-column label="顺延说明" min-width="200" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.bucket === 'deferred' && row.bucket_defer_text" class="cs-muted">{{
|
||||
row.bucket_defer_text
|
||||
}}</span>
|
||||
<span v-else class="cs-muted">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物流签收" min-width="152" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.sign_time_text || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pay_time_text" label="尾款支付" min-width="152" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.pay_time_text || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time_text" label="订单创建" width="152" />
|
||||
</el-table>
|
||||
<div class="cs-dialog__pager">
|
||||
@@ -390,8 +471,8 @@
|
||||
|
||||
<el-dialog
|
||||
v-model="leafOrdersVisible"
|
||||
:title="leafOrdersDept ? `已完成业绩订单 · ${leafOrdersDept.name}` : '已完成业绩订单'"
|
||||
width="1040px"
|
||||
:title="leafOrdersDialogTitle"
|
||||
width="1280px"
|
||||
destroy-on-close
|
||||
class="cs-dialog"
|
||||
>
|
||||
@@ -403,12 +484,33 @@
|
||||
</el-radio-group>
|
||||
<span class="cs-dialog__hint">
|
||||
结算月:<strong>{{ meta.settlement_month || settlementMonth }}</strong>
|
||||
<template v-if="meta.order_month"> · 订单创建月:<strong>{{ meta.order_month }}</strong></template>
|
||||
<template v-if="orderWindowLabel && orderWindowLabel !== '—'"
|
||||
> · 订单创建:<strong>{{ orderWindowLabel }}</strong></template
|
||||
>
|
||||
<template v-if="meta.channel_name"> · 渠道:<strong>{{ meta.channel_name }}</strong></template>
|
||||
</span>
|
||||
</div>
|
||||
<el-table v-loading="leafOrdersLoading" :data="leafOrdersRows" border stripe size="small" max-height="440">
|
||||
<el-table-column prop="order_no" label="业务订单号" min-width="116" show-overflow-tooltip />
|
||||
<el-table-column prop="order_no" label="业务订单号" min-width="128" show-overflow-tooltip />
|
||||
<el-table-column label="快递单号" min-width="132" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.tracking_number || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="挂号日期" min-width="124" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.appointment_date_text || '—' }}</span>
|
||||
<el-tag
|
||||
v-if="row.appointment_status && Number(row.appointment_status) !== 3"
|
||||
class="cs-tag-appt-muted"
|
||||
type="info"
|
||||
size="small"
|
||||
effect="plain"
|
||||
>未完结挂号</el-tag
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="挂号渠道" min-width="124" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.appt_channel_name || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="assistant_name" label="业绩医助" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="doctor_name" label="开方医生" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="primary_dept_name" label="归属部门" min-width="120" show-overflow-tooltip />
|
||||
@@ -427,8 +529,20 @@
|
||||
<span v-else class="cs-muted">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sign_time_text" label="签收时间" min-width="152" show-overflow-tooltip />
|
||||
<el-table-column prop="pay_time_text" label="尾款支付" min-width="152" show-overflow-tooltip />
|
||||
<el-table-column label="顺延说明" min-width="200" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.bucket === 'deferred' && row.bucket_defer_text" class="cs-muted">{{
|
||||
row.bucket_defer_text
|
||||
}}</span>
|
||||
<span v-else class="cs-muted">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物流签收" min-width="152" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.sign_time_text || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pay_time_text" label="尾款支付" min-width="152" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.pay_time_text || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time_text" label="订单创建" width="152" />
|
||||
</el-table>
|
||||
<div class="cs-dialog__pager">
|
||||
@@ -445,12 +559,13 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { ArrowRight, ArrowUp, RefreshRight, Search, Tickets } from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
commissionSettlementChannelOptions,
|
||||
commissionSettlementConfirmFinalize,
|
||||
commissionSettlementConfirmRevoke,
|
||||
commissionSettlementDeptOptions,
|
||||
commissionSettlementOrderLines,
|
||||
commissionSettlementOverview,
|
||||
@@ -565,6 +680,16 @@ interface CsDoctorRow {
|
||||
deferred_period_amount: number
|
||||
}
|
||||
|
||||
type LeafOrderDrillDept = { mode: 'dept'; dept_id: number; dept_name: string }
|
||||
type LeafOrderDrillChannel = {
|
||||
mode: 'channel'
|
||||
title: string
|
||||
assistant_id?: number
|
||||
doctor_id?: number
|
||||
appt_channel_value: number
|
||||
}
|
||||
type LeafOrderDrill = LeafOrderDrillDept | LeafOrderDrillChannel
|
||||
|
||||
interface CsAssistChannelRow extends CsAssistRow {
|
||||
appt_channel_value: number
|
||||
appt_channel_name: string
|
||||
@@ -584,7 +709,27 @@ function defaultSettlementMonth(): string {
|
||||
return `${y}-${m}`
|
||||
}
|
||||
|
||||
function pad2(n: number): string {
|
||||
return String(n).padStart(2, '0')
|
||||
}
|
||||
|
||||
function formatDateTime(d: Date): string {
|
||||
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`
|
||||
}
|
||||
|
||||
/** 订单池默认:结算月「上一自然月」整段(与未传 start/end 的后端行为一致) */
|
||||
function defaultOrderTimeRangeForSettlementMonth(sm: string): [string, string] {
|
||||
const m = /^(\d{4})-(\d{2})$/.exec(sm)
|
||||
const y = m ? parseInt(m[1], 10) : new Date().getFullYear()
|
||||
const monthNum = m ? parseInt(m[2], 10) : new Date().getMonth() + 1
|
||||
const start = new Date(y, monthNum - 2, 1, 0, 0, 0)
|
||||
const end = new Date(y, monthNum - 1, 0, 23, 59, 59)
|
||||
return [formatDateTime(start), formatDateTime(end)]
|
||||
}
|
||||
|
||||
const settlementMonth = ref<string>(defaultSettlementMonth())
|
||||
const orderTimeRange = ref<[string, string] | null>(defaultOrderTimeRangeForSettlementMonth(settlementMonth.value))
|
||||
const requireSystemAutoOnly = ref(false)
|
||||
const loading = ref(false)
|
||||
const deptOptions = ref<DeptOption[]>([])
|
||||
const deptOptionsLoading = ref(false)
|
||||
@@ -612,9 +757,14 @@ const totalAgg = ref<{
|
||||
const meta = reactive({
|
||||
settlement_month: '',
|
||||
order_month: '',
|
||||
order_start_time: '',
|
||||
order_end_time: '',
|
||||
fulfillment_status: 3,
|
||||
explicit_order_time_window: false,
|
||||
cutoff_end: '',
|
||||
rule_note: '',
|
||||
channel_name: '',
|
||||
require_system_auto_prescription: false,
|
||||
})
|
||||
|
||||
const confirmInfo = ref<{
|
||||
@@ -622,10 +772,17 @@ const confirmInfo = ref<{
|
||||
reconcile_note?: string
|
||||
confirmed_admin_name?: string
|
||||
confirmed_at_text?: string
|
||||
revoked_admin_name?: string
|
||||
revoked_at_text?: string
|
||||
is_confirmed_locked?: boolean
|
||||
} | null>(null)
|
||||
const reconcileNote = ref('')
|
||||
const reconcileSaving = ref(false)
|
||||
const confirmSubmitting = ref(false)
|
||||
const confirmRevoking = ref(false)
|
||||
|
||||
/** 仅 status=1 时锁定,禁止保存备注 / 重复确定;status=2 已撤回可再操作 */
|
||||
const settlementConfirmedLocked = computed(() => Number(confirmInfo.value?.status) === 1)
|
||||
|
||||
const reconcileDialogVisible = ref(false)
|
||||
const linesLoading = ref(false)
|
||||
@@ -636,7 +793,7 @@ const linesPage = ref(1)
|
||||
const linesPageSize = ref(20)
|
||||
|
||||
const leafOrdersVisible = ref(false)
|
||||
const leafOrdersDept = ref<{ id: number; name: string } | null>(null)
|
||||
const leafOrderDrill = ref<LeafOrderDrill | null>(null)
|
||||
const leafOrdersBucket = ref<string>('all')
|
||||
const leafOrdersRows = ref<Record<string, any>[]>([])
|
||||
const leafOrdersTotal = ref(0)
|
||||
@@ -644,6 +801,20 @@ const leafOrdersPage = ref(1)
|
||||
const leafOrdersPageSize = ref(20)
|
||||
const leafOrdersLoading = ref(false)
|
||||
|
||||
const leafOrdersDialogTitle = computed(() => {
|
||||
const d = leafOrderDrill.value
|
||||
if (!d) return '已完成业绩订单'
|
||||
if (d.mode === 'dept') return `已完成业绩订单 · ${d.dept_name}`
|
||||
return d.title
|
||||
})
|
||||
|
||||
const orderWindowLabel = computed(() => {
|
||||
if (meta.order_start_time && meta.order_end_time) {
|
||||
return `${meta.order_start_time}~${meta.order_end_time}`
|
||||
}
|
||||
return meta.order_month || '—'
|
||||
})
|
||||
|
||||
const deptChildrenMap = computed(() => {
|
||||
const map = new Map<number, number[]>()
|
||||
for (const d of deptOptions.value) {
|
||||
@@ -718,7 +889,45 @@ function openOrderLinesForDept(deptId: number, deptName: string) {
|
||||
ElMessage.warning('请选择结算月')
|
||||
return
|
||||
}
|
||||
leafOrdersDept.value = { id: deptId, name: deptName }
|
||||
leafOrderDrill.value = { mode: 'dept', dept_id: deptId, dept_name: deptName }
|
||||
leafOrdersBucket.value = 'all'
|
||||
leafOrdersPage.value = 1
|
||||
leafOrdersRows.value = []
|
||||
leafOrdersTotal.value = 0
|
||||
leafOrdersVisible.value = true
|
||||
loadLeafOrders(1)
|
||||
}
|
||||
|
||||
function openOrdersForAssistantChannel(row: CsAssistChannelRow) {
|
||||
if (!settlementMonth.value) {
|
||||
ElMessage.warning('请选择结算月')
|
||||
return
|
||||
}
|
||||
leafOrderDrill.value = {
|
||||
mode: 'channel',
|
||||
title: `业务订单 · ${row.appt_channel_name} · 医助 ${row.assistant_name}`,
|
||||
assistant_id: row.assistant_id,
|
||||
appt_channel_value: Number(row.appt_channel_value ?? 0),
|
||||
}
|
||||
leafOrdersBucket.value = 'all'
|
||||
leafOrdersPage.value = 1
|
||||
leafOrdersRows.value = []
|
||||
leafOrdersTotal.value = 0
|
||||
leafOrdersVisible.value = true
|
||||
loadLeafOrders(1)
|
||||
}
|
||||
|
||||
function openOrdersForDoctorChannel(row: CsDoctorChannelRow) {
|
||||
if (!settlementMonth.value) {
|
||||
ElMessage.warning('请选择结算月')
|
||||
return
|
||||
}
|
||||
leafOrderDrill.value = {
|
||||
mode: 'channel',
|
||||
title: `业务订单 · ${row.appt_channel_name} · 医生 ${row.doctor_name}`,
|
||||
doctor_id: row.doctor_id,
|
||||
appt_channel_value: Number(row.appt_channel_value ?? 0),
|
||||
}
|
||||
leafOrdersBucket.value = 'all'
|
||||
leafOrdersPage.value = 1
|
||||
leafOrdersRows.value = []
|
||||
@@ -728,18 +937,30 @@ function openOrderLinesForDept(deptId: number, deptName: string) {
|
||||
}
|
||||
|
||||
async function loadLeafOrders(page: number) {
|
||||
if (!settlementMonth.value || !leafOrdersDept.value) return
|
||||
if (!settlementMonth.value || !leafOrderDrill.value) return
|
||||
leafOrdersLoading.value = true
|
||||
leafOrdersPage.value = page
|
||||
try {
|
||||
const d = leafOrderDrill.value
|
||||
const p: Record<string, any> = {
|
||||
settlement_month: settlementMonth.value,
|
||||
dept_ids: String(leafOrdersDept.value.id),
|
||||
page,
|
||||
page_size: leafOrdersPageSize.value,
|
||||
}
|
||||
if (selectedChannel.value) {
|
||||
p.channel_code = selectedChannel.value
|
||||
if (d.mode === 'dept') {
|
||||
p.dept_ids = String(d.dept_id)
|
||||
if (selectedChannel.value) {
|
||||
p.channel_code = selectedChannel.value
|
||||
}
|
||||
} else {
|
||||
Object.assign(p, buildQueryParams())
|
||||
p.appt_channel_value = d.appt_channel_value
|
||||
if (d.assistant_id != null) {
|
||||
p.assistant_id = d.assistant_id
|
||||
}
|
||||
if (d.doctor_id != null) {
|
||||
p.doctor_id = d.doctor_id
|
||||
}
|
||||
}
|
||||
if (leafOrdersBucket.value && leafOrdersBucket.value !== 'all') {
|
||||
p.bucket = leafOrdersBucket.value
|
||||
@@ -796,6 +1017,11 @@ async function loadOverview() {
|
||||
ElMessage.warning('请选择结算月')
|
||||
return
|
||||
}
|
||||
const ot = orderTimeRange.value
|
||||
if (!ot || !ot[0] || !ot[1]) {
|
||||
ElMessage.warning('请设置订单创建时间范围')
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
const res: any = await commissionSettlementOverview(buildQueryParams() as any)
|
||||
@@ -807,9 +1033,21 @@ async function loadOverview() {
|
||||
totalAgg.value = res?.total || {}
|
||||
meta.settlement_month = res?.settlement_month || sm
|
||||
meta.order_month = res?.order_month || ''
|
||||
meta.order_start_time = res?.order_start_time ? String(res.order_start_time) : ''
|
||||
meta.order_end_time = res?.order_end_time ? String(res.order_end_time) : ''
|
||||
meta.fulfillment_status =
|
||||
res?.fulfillment_status != null ? Number(res.fulfillment_status) : 3
|
||||
meta.explicit_order_time_window =
|
||||
typeof res?.explicit_order_time_window === 'boolean'
|
||||
? res.explicit_order_time_window
|
||||
: !!(meta.order_start_time && meta.order_end_time)
|
||||
meta.cutoff_end = res?.cutoff_end || ''
|
||||
meta.rule_note = res?.rule_note || ''
|
||||
meta.channel_name = res?.channel_name || ''
|
||||
meta.require_system_auto_prescription =
|
||||
typeof res?.require_system_auto_prescription === 'boolean'
|
||||
? res.require_system_auto_prescription
|
||||
: false
|
||||
confirmInfo.value = res?.confirm ?? null
|
||||
reconcileNote.value =
|
||||
res?.confirm?.reconcile_note != null ? String(res.confirm.reconcile_note) : ''
|
||||
@@ -829,7 +1067,18 @@ async function loadOverview() {
|
||||
|
||||
function buildQueryParams(): Record<string, any> {
|
||||
const sm = settlementMonth.value
|
||||
const p: Record<string, any> = { settlement_month: sm }
|
||||
const p: Record<string, any> = {
|
||||
settlement_month: sm,
|
||||
fulfillment_status: 3,
|
||||
}
|
||||
const ot = orderTimeRange.value
|
||||
if (ot && ot[0] && ot[1]) {
|
||||
p.start_time = ot[0]
|
||||
p.end_time = ot[1]
|
||||
}
|
||||
if (requireSystemAutoOnly.value) {
|
||||
p.require_system_auto_prescription = 1
|
||||
}
|
||||
if (selectedDeptIds.value.length > 0) {
|
||||
p.dept_ids = selectedDeptIds.value.join(',')
|
||||
}
|
||||
@@ -882,8 +1131,8 @@ async function handleSaveReconcile() {
|
||||
ElMessage.warning('请选择结算月')
|
||||
return
|
||||
}
|
||||
if (confirmInfo.value?.status === 1) {
|
||||
ElMessage.warning('已确定业绩不可修改备注')
|
||||
if (settlementConfirmedLocked.value) {
|
||||
ElMessage.warning('已确定业绩不可修改备注;请先撤回确定')
|
||||
return
|
||||
}
|
||||
reconcileSaving.value = true
|
||||
@@ -908,7 +1157,7 @@ async function handleConfirmFinalize() {
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'确定后将为「顺延下期」订单写入结转记录;下一结算月在相同渠道与部门筛选下自动并入统计。已确定后不可在线撤销,请与财务核对后再操作。',
|
||||
'确定后将为「顺延下期」订单写入结转记录,下一结算月在相同渠道与部门筛选下自动并入统计。确定后仍可「撤回确定」以清除结转并重新核对(与财务对齐后再操作)。',
|
||||
'确定本期业绩',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
@@ -936,8 +1185,40 @@ async function handleConfirmFinalize() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleConfirmRevoke() {
|
||||
if (!settlementMonth.value) {
|
||||
ElMessage.warning('请选择结算月')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'将撤回本次「确定业绩」状态,并删除已写入的顺延结转记录(下期将不再汇入这些单)。确定后继续?',
|
||||
'撤回确定业绩',
|
||||
{
|
||||
confirmButtonText: '撤回',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
confirmRevoking.value = true
|
||||
try {
|
||||
await commissionSettlementConfirmRevoke(buildQueryParams() as any)
|
||||
ElMessage.success('已撤回确定;可重新保存备注并再次确定本期业绩')
|
||||
await loadOverview()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || '撤回失败')
|
||||
} finally {
|
||||
confirmRevoking.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
settlementMonth.value = defaultSettlementMonth()
|
||||
orderTimeRange.value = defaultOrderTimeRangeForSettlementMonth(settlementMonth.value)
|
||||
requireSystemAutoOnly.value = false
|
||||
selectedDeptIds.value = []
|
||||
selectedChannel.value = ''
|
||||
loadOverview()
|
||||
@@ -1053,6 +1334,12 @@ function getSummaries(param: { columns: Array<{ property?: string }>; data: CsRo
|
||||
return sums
|
||||
}
|
||||
|
||||
watch(settlementMonth, (v) => {
|
||||
if (v) {
|
||||
orderTimeRange.value = defaultOrderTimeRangeForSettlementMonth(v)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadDeptOptions(), loadChannelOptions()])
|
||||
await loadOverview()
|
||||
@@ -1249,7 +1536,24 @@ onMounted(async () => {
|
||||
flex: 1 1 280px;
|
||||
min-width: 240px;
|
||||
}
|
||||
.filter-field--rangewrap {
|
||||
flex: 1 1 440px;
|
||||
min-width: 300px;
|
||||
}
|
||||
.filter-month { width: 168px; }
|
||||
.filter-datetimerange {
|
||||
width: 100%;
|
||||
max-width: 520px;
|
||||
}
|
||||
.filter-sa-check {
|
||||
margin-top: 6px;
|
||||
font-weight: 500;
|
||||
color: var(--cs-text);
|
||||
}
|
||||
.cs-tag-appt-muted {
|
||||
margin-left: 6px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.filter-select--channel { width: 220px; }
|
||||
.filter-dept-tree-select { width: 100%; min-width: 240px; }
|
||||
.filter-actions {
|
||||
@@ -1581,6 +1885,12 @@ onMounted(async () => {
|
||||
border-color: #cbd5e1 !important;
|
||||
color: #64748b !important;
|
||||
}
|
||||
&--clickable {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
filter: brightness(0.96);
|
||||
}
|
||||
}
|
||||
&--neutral {
|
||||
background: #f0fdfa !important;
|
||||
border-color: #99f6e4 !important;
|
||||
|
||||
Reference in New Issue
Block a user