Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
752eddb0ba | ||
|
|
1005859404 | ||
|
|
ea6c73c063 | ||
|
|
c9ad4ae2ed | ||
|
|
abf8026d45 | ||
|
|
26b442b5da | ||
|
|
1cb00e9fbe | ||
|
|
9208e7eaaa | ||
|
|
a1392f9491 | ||
|
|
2ea0e1d54b | ||
|
|
d96fa6f4a1 | ||
|
|
0fb51e12d1 | ||
|
|
4f9494db0b | ||
|
|
4e87b080a0 | ||
|
|
2f3038aba1 | ||
|
|
131fb7d9c7 | ||
|
|
762a724204 | ||
|
|
8090e0aab3 | ||
|
|
9df6330d41 | ||
|
|
fd63e23eaf | ||
|
|
c1143f4629 | ||
|
|
149fdffd49 | ||
|
|
439d5a3ec9 | ||
|
|
9e24d49210 | ||
|
|
90ac43fc82 | ||
|
|
8b6b709987 | ||
|
|
fb6ca9ce85 | ||
|
|
5ce331394d | ||
|
|
800c6af7f1 | ||
|
|
876ef0f8a0 | ||
|
|
fdcfa30810 | ||
|
|
4caa98af3a | ||
|
|
bb87e4fb4c | ||
|
|
039105580c | ||
|
|
f47f431bdf |
@@ -179,3 +179,12 @@ export function commissionSettlementConfirmFinalize(params: Record<string, any>)
|
|||||||
export function commissionSettlementConfirmRevoke(params: Record<string, any>) {
|
export function commissionSettlementConfirmRevoke(params: Record<string, any>) {
|
||||||
return request.post({ url: '/stats.commissionSettlement/confirmRevoke', params })
|
return request.post({ url: '/stats.commissionSettlement/confirmRevoke', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 医助个人业绩概览 */
|
||||||
|
export function assistantPerformanceOverview(params: {
|
||||||
|
time_type?: string
|
||||||
|
start_date?: string
|
||||||
|
end_date?: string
|
||||||
|
}) {
|
||||||
|
return request.get({ url: '/stats.assistantPerformance/overview', params })
|
||||||
|
}
|
||||||
|
|||||||
+17
-1
@@ -462,15 +462,26 @@ export function prescriptionOrderAddPayOrder(params: {
|
|||||||
order_type: number
|
order_type: number
|
||||||
pay_amount: number
|
pay_amount: number
|
||||||
pay_remark?: string
|
pay_remark?: string
|
||||||
|
completion_request?: number
|
||||||
}) {
|
}) {
|
||||||
return request.post({ url: '/tcm.prescriptionOrder/addPayOrder', params })
|
return request.post({ url: '/tcm.prescriptionOrder/addPayOrder', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 为「已发货」订单关联已有支付单并重置支付审核为待审核 */
|
/** 为「已发货」订单关联已有支付单并重置支付审核为待审核 */
|
||||||
export function prescriptionOrderLinkPayOrder(params: { id: number; pay_order_id: number }) {
|
export function prescriptionOrderLinkPayOrder(params: {
|
||||||
|
id: number
|
||||||
|
pay_order_id?: number
|
||||||
|
pay_order_ids?: number[]
|
||||||
|
completion_request?: number
|
||||||
|
}) {
|
||||||
return request.post({ url: '/tcm.prescriptionOrder/linkPayOrder', params })
|
return request.post({ url: '/tcm.prescriptionOrder/linkPayOrder', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 已发货/已签收:仅提交完单申请(不新增/关联支付单) */
|
||||||
|
export function prescriptionOrderRequestCompletion(params: { id: number }) {
|
||||||
|
return request.post({ url: '/tcm.prescriptionOrder/requestCompletion', params })
|
||||||
|
}
|
||||||
|
|
||||||
/** 将「已发货/已签收」且支付审核通过的订单结案(3=已完成 或 7-12 业务状态) */
|
/** 将「已发货/已签收」且支付审核通过的订单结案(3=已完成 或 7-12 业务状态) */
|
||||||
export function prescriptionOrderComplete(params: { id: number; fulfillment_status: number }) {
|
export function prescriptionOrderComplete(params: { id: number; fulfillment_status: number }) {
|
||||||
return request.post({ url: '/tcm.prescriptionOrder/complete', params })
|
return request.post({ url: '/tcm.prescriptionOrder/complete', params })
|
||||||
@@ -500,6 +511,11 @@ export function prescriptionOrderUpdateAmount(params: { id: number; amount: numb
|
|||||||
return request.post({ url: '/tcm.prescriptionOrder/updateAmount', params })
|
return request.post({ url: '/tcm.prescriptionOrder/updateAmount', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 设置发货类型:gancao 甘草药房 / direct 洛阳药房 */
|
||||||
|
export function prescriptionOrderSetShipMode(params: { id: number; ship_mode: 'gancao' | 'direct' }) {
|
||||||
|
return request.post({ url: '/tcm.prescriptionOrder/setShipMode', params })
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 处方库 ==========
|
// ========== 处方库 ==========
|
||||||
|
|
||||||
// 处方库列表
|
// 处方库列表
|
||||||
|
|||||||
@@ -252,7 +252,7 @@
|
|||||||
v-model="queryParams.assistant_id"
|
v-model="queryParams.assistant_id"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
placeholder="诊单医助"
|
placeholder="订单创建人"
|
||||||
class="!w-full"
|
class="!w-full"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
@@ -277,7 +277,7 @@
|
|||||||
:fetch-fun="prescriptionOrderExport"
|
:fetch-fun="prescriptionOrderExport"
|
||||||
:params="prescriptionOrderExportParams"
|
:params="prescriptionOrderExportParams"
|
||||||
:page-size="pager.size"
|
:page-size="pager.size"
|
||||||
export-hint="导出范围与上方筛选一致(履约状态、创建时间及其他条件均会生效)。含「自媒体渠道(挂号渠道来源)」:按诊单关联患者取最近一条挂号的渠道字典与细分。"
|
export-hint="导出范围与上方筛选一致(履约状态、创建时间及其他条件均会生效)。含「自媒体渠道(挂号渠道来源)」:优先取该单关联处方登记的挂号;无则诊单下同患者挂号取 id 最大的一条(与前台挂号选择的记录一致);业绩侧栏带渠道筛选导出时与同页列表高亮挂号同源。"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -495,7 +495,7 @@
|
|||||||
>修改单号</el-button>
|
>修改单号</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="canShipRow(row)"
|
v-if="canShipRow(row)"
|
||||||
v-perms="['tcm.prescriptionOrder/ship', 'tcm.prescriptionOrder/submitGancaoRecipel']"
|
v-perms="['tcm.prescriptionOrder/ship']"
|
||||||
type="primary"
|
type="primary"
|
||||||
link
|
link
|
||||||
@click="openShip(row)"
|
@click="openShip(row)"
|
||||||
@@ -562,6 +562,28 @@
|
|||||||
round
|
round
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
>甘草 {{ detailData.gancao_reciperl_order_no }}</el-tag>
|
>甘草 {{ detailData.gancao_reciperl_order_no }}</el-tag>
|
||||||
|
<div v-if="detailData" class="flex items-center gap-2 ml-4 shrink-0">
|
||||||
|
<span class="text-sm text-gray-500">发货类型</span>
|
||||||
|
<el-radio-group
|
||||||
|
v-perms="['tcm.prescriptionOrder/setShipMode']"
|
||||||
|
:model-value="normalizeShipMode(detailData.ship_mode)"
|
||||||
|
size="small"
|
||||||
|
:disabled="shipModeSaving || !canEditShipMode(detailData)"
|
||||||
|
@change="onDetailShipModeChange"
|
||||||
|
>
|
||||||
|
<el-radio-button label="gancao">甘草药房</el-radio-button>
|
||||||
|
<el-radio-button
|
||||||
|
label="direct"
|
||||||
|
:disabled="isShipModeLockedToGancao(detailData)"
|
||||||
|
>洛阳药房</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
<el-tag
|
||||||
|
v-if="!hasSetShipModePerm"
|
||||||
|
:type="normalizeShipMode(detailData.ship_mode) === 'direct' ? 'warning' : 'success'"
|
||||||
|
effect="plain"
|
||||||
|
round
|
||||||
|
>{{ shipModeLabel(detailData.ship_mode) }}</el-tag>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="detailData" class="flex items-center gap-2 mr-6">
|
<div v-if="detailData" class="flex items-center gap-2 mr-6">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -606,7 +628,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="canShipRow(detailData)"
|
v-if="canShipRow(detailData)"
|
||||||
v-perms="['tcm.prescriptionOrder/ship', 'tcm.prescriptionOrder/submitGancaoRecipel']"
|
v-perms="['tcm.prescriptionOrder/ship']"
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
plain
|
plain
|
||||||
@@ -1917,15 +1939,6 @@
|
|||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
>
|
>
|
||||||
<el-alert
|
<el-alert
|
||||||
v-if="shipForm.ship_mode === 'gancao'"
|
|
||||||
title="甘草药方由甘草侧履约配送,无需填写快递单号。点击下方按钮将提交处方至甘草药管家(与「上传药方」相同)。"
|
|
||||||
type="info"
|
|
||||||
:closable="false"
|
|
||||||
show-icon
|
|
||||||
class="mb-4"
|
|
||||||
/>
|
|
||||||
<el-alert
|
|
||||||
v-else
|
|
||||||
title="确认后订单状态将变更为「已发货」,请核对快递信息无误。"
|
title="确认后订单状态将变更为「已发货」,请核对快递信息无误。"
|
||||||
type="warning"
|
type="warning"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
@@ -1934,38 +1947,34 @@
|
|||||||
/>
|
/>
|
||||||
<el-form v-loading="shipSaving" label-width="90px" class="pr-2" @submit.prevent="submitShip">
|
<el-form v-loading="shipSaving" label-width="90px" class="pr-2" @submit.prevent="submitShip">
|
||||||
<el-form-item label="发货方式">
|
<el-form-item label="发货方式">
|
||||||
<el-radio-group v-model="shipForm.ship_mode">
|
<el-tag
|
||||||
<el-radio label="gancao">甘草药方发</el-radio>
|
:type="normalizeShipMode(shipForm.ship_mode) === 'direct' ? 'warning' : 'success'"
|
||||||
<el-radio label="direct">药房直发</el-radio>
|
effect="plain"
|
||||||
</el-radio-group>
|
size="default"
|
||||||
|
>{{ shipDialogModeDisplay }}</el-tag>
|
||||||
|
<span class="text-xs text-gray-400 ml-2">请在订单详情顶部「发货类型」中设置,此处不可修改</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="承运商">
|
||||||
|
<el-select v-model="shipForm.express_company" class="w-full">
|
||||||
|
<el-option label="自动识别" value="auto" />
|
||||||
|
<el-option label="顺丰速运" value="sf" />
|
||||||
|
<el-option label="京东快递" value="jd" />
|
||||||
|
<el-option label="极兔速递" value="jt" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="快递单号">
|
||||||
|
<el-input
|
||||||
|
v-model="shipForm.tracking_number"
|
||||||
|
maxlength="80"
|
||||||
|
placeholder="请输入快递单号"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<template v-if="shipForm.ship_mode === 'direct'">
|
|
||||||
<el-form-item label="承运商">
|
|
||||||
<el-select v-model="shipForm.express_company" class="w-full">
|
|
||||||
<el-option label="自动识别" value="auto" />
|
|
||||||
<el-option label="顺丰速运" value="sf" />
|
|
||||||
<el-option label="京东快递" value="jd" />
|
|
||||||
<el-option label="极兔速递" value="jt" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="快递单号">
|
|
||||||
<el-input
|
|
||||||
v-model="shipForm.tracking_number"
|
|
||||||
maxlength="80"
|
|
||||||
placeholder="请输入快递单号"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="shipVisible = false">取消</el-button>
|
<el-button @click="shipVisible = false">取消</el-button>
|
||||||
<el-button
|
<el-button type="primary" :loading="shipSaving" @click="submitShip">
|
||||||
type="primary"
|
确认发货
|
||||||
:loading="shipSaving || (shipForm.ship_mode === 'gancao' && gancaoSubmitId === shipRowId)"
|
|
||||||
@click="submitShip"
|
|
||||||
>
|
|
||||||
{{ shipForm.ship_mode === 'gancao' ? '确认并上传药方' : '确认发货' }}
|
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@@ -2043,7 +2052,7 @@
|
|||||||
@closed="addPayOrderFormRef?.clearValidate()"
|
@closed="addPayOrderFormRef?.clearValidate()"
|
||||||
>
|
>
|
||||||
<el-alert
|
<el-alert
|
||||||
title="可以手动创建新支付单,或关联已存在但未绑定的支付单。"
|
:title="addPayOrderAlertTitle"
|
||||||
type="info"
|
type="info"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
show-icon
|
show-icon
|
||||||
@@ -2061,6 +2070,7 @@
|
|||||||
<el-radio-group v-model="addPayOrderForm.add_mode">
|
<el-radio-group v-model="addPayOrderForm.add_mode">
|
||||||
<el-radio value="create">手动创建</el-radio>
|
<el-radio value="create">手动创建</el-radio>
|
||||||
<el-radio value="link">关联已有</el-radio>
|
<el-radio value="link">关联已有</el-radio>
|
||||||
|
<el-radio value="completion_only">直接完单申请</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
@@ -2081,11 +2091,11 @@
|
|||||||
<el-form-item label="支付金额" prop="pay_amount">
|
<el-form-item label="支付金额" prop="pay_amount">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="addPayOrderForm.pay_amount"
|
v-model="addPayOrderForm.pay_amount"
|
||||||
:min="0.01"
|
:min="0"
|
||||||
:step="0.01"
|
:step="0.01"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
placeholder="请输入支付金额(元)"
|
placeholder="请输入支付金额(元,可为 0)"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="pay_remark">
|
<el-form-item label="备注" prop="pay_remark">
|
||||||
@@ -2100,6 +2110,20 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 直接完单申请(不创建/关联支付单) -->
|
||||||
|
<template v-else-if="addPayOrderForm.add_mode === 'completion_only'">
|
||||||
|
<el-alert
|
||||||
|
type="warning"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
class="!mb-0"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
不新增或关联支付单,仅向审核人员提交「完成订单」申请;提交后支付审核将变为待审核,审核通过后将自动结案。
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 关联已有模式 -->
|
<!-- 关联已有模式 -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-form-item label="选择支付单" prop="link_pay_order_id">
|
<el-form-item label="选择支付单" prop="link_pay_order_id">
|
||||||
@@ -2130,8 +2154,8 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 完单申请选项 -->
|
<!-- 完单申请选项(创建/关联时可选) -->
|
||||||
<el-form-item label="完单申请">
|
<el-form-item v-if="addPayOrderForm.add_mode !== 'completion_only'" label="完单申请">
|
||||||
<el-radio-group v-model="addPayOrderForm.completion_request">
|
<el-radio-group v-model="addPayOrderForm.completion_request">
|
||||||
<el-radio :value="0">不申请</el-radio>
|
<el-radio :value="0">不申请</el-radio>
|
||||||
<el-radio :value="1">申请完成订单</el-radio>
|
<el-radio :value="1">申请完成订单</el-radio>
|
||||||
@@ -2143,7 +2167,13 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="addPayOrderVisible = false">取消</el-button>
|
<el-button @click="addPayOrderVisible = false">取消</el-button>
|
||||||
<el-button type="primary" :loading="addPayOrderSaving" @click="submitAddPayOrder">确认新增</el-button>
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:loading="addPayOrderSaving"
|
||||||
|
@click="submitAddPayOrder"
|
||||||
|
>
|
||||||
|
{{ addPayOrderForm.add_mode === 'completion_only' ? '提交申请' : '确认新增' }}
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
@@ -2586,7 +2616,9 @@ import {
|
|||||||
prescriptionOrderShip,
|
prescriptionOrderShip,
|
||||||
prescriptionOrderLogs,
|
prescriptionOrderLogs,
|
||||||
prescriptionOrderUpdateAmount,
|
prescriptionOrderUpdateAmount,
|
||||||
|
prescriptionOrderSetShipMode,
|
||||||
prescriptionOrderAddPayOrder,
|
prescriptionOrderAddPayOrder,
|
||||||
|
prescriptionOrderRequestCompletion,
|
||||||
prescriptionOrderComplete,
|
prescriptionOrderComplete,
|
||||||
prescriptionOrderRevokeRxAudit,
|
prescriptionOrderRevokeRxAudit,
|
||||||
prescriptionOrderRevokePayAudit,
|
prescriptionOrderRevokePayAudit,
|
||||||
@@ -2604,7 +2636,6 @@ import { getDictData } from '@/api/app'
|
|||||||
import { deptAll } from '@/api/org/department'
|
import { deptAll } from '@/api/org/department'
|
||||||
import { usePaging } from '@/hooks/usePaging'
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
import feedback from '@/utils/feedback'
|
import feedback from '@/utils/feedback'
|
||||||
import { hasPermission } from '@/utils/perm'
|
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElMessageBox } from 'element-plus'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
@@ -2651,6 +2682,8 @@ const prescriptionOrderDetailButtonText = computed(() =>
|
|||||||
|
|
||||||
/** 与 server/config/project.php prescription_order_finance_roles 保持一致 */
|
/** 与 server/config/project.php prescription_order_finance_roles 保持一致 */
|
||||||
const FINANCE_ROLE_IDS = [0, 3, 6]
|
const FINANCE_ROLE_IDS = [0, 3, 6]
|
||||||
|
/** 与 server/config/project.php order_edit_all_roles 一致:可绕过「双审通过后创建人禁编」 */
|
||||||
|
const ORDER_EDIT_ALL_ROLE_IDS = [0, 3]
|
||||||
|
|
||||||
/** 判断当前用户是否有查看财务字段(内部成本)的权限 */
|
/** 判断当前用户是否有查看财务字段(内部成本)的权限 */
|
||||||
const canViewFinanceFields = () => {
|
const canViewFinanceFields = () => {
|
||||||
@@ -2856,9 +2889,9 @@ const queryParams = reactive({
|
|||||||
express_company: '' as '' | 'sf' | 'jd',
|
express_company: '' as '' | 'sf' | 'jd',
|
||||||
/** 开方医生(后台:关联处方 creator_id) */
|
/** 开方医生(后台:关联处方 creator_id) */
|
||||||
doctor_id: '' as number | '',
|
doctor_id: '' as number | '',
|
||||||
/** 诊单医助(后台:关联诊单 assistant_id) */
|
/** 订单创建人(后台:业务订单 creator_id) */
|
||||||
assistant_id: '' as number | '',
|
assistant_id: '' as number | '',
|
||||||
/** 诊单医助所属部门(后台:assistant_dept_id,la_admin_dept) */
|
/** 创建人所属部门(后台:assistant_dept_id,la_admin_dept) */
|
||||||
assistant_dept_id: '' as number | '',
|
assistant_dept_id: '' as number | '',
|
||||||
fulfillment_status: '' as number | '',
|
fulfillment_status: '' as number | '',
|
||||||
prescription_audit_status: '' as number | '',
|
prescription_audit_status: '' as number | '',
|
||||||
@@ -3305,8 +3338,32 @@ function formatTime(v: unknown) {
|
|||||||
return String(v)
|
return String(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canBypassCreatorDualAuditEditLock(): boolean {
|
||||||
|
const u = userStore.userInfo
|
||||||
|
if (!u) return false
|
||||||
|
if (Number(u.root) === 1) return true
|
||||||
|
const ids = Array.isArray(u.role_ids) ? u.role_ids.map((n: unknown) => Number(n)) : []
|
||||||
|
return ids.some((id) => ORDER_EDIT_ALL_ROLE_IDS.includes(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCurrentUserOrderCreator(row: { creator_id?: number }) {
|
||||||
|
const uid = Number(userStore.userInfo?.id)
|
||||||
|
return uid > 0 && Number(row.creator_id) === uid
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处方审核 + 支付单审核均已通过 */
|
||||||
|
function isDualAuditPassed(row: {
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
return Number(row.prescription_audit_status) === 1 && Number(row.payment_slip_audit_status) === 1
|
||||||
|
}
|
||||||
|
|
||||||
function canEditRow(row: {
|
function canEditRow(row: {
|
||||||
|
creator_id?: number
|
||||||
fulfillment_status?: number
|
fulfillment_status?: number
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
gancao_reciperl_order_no?: string | null
|
gancao_reciperl_order_no?: string | null
|
||||||
gancao_submit_time?: number | null
|
gancao_submit_time?: number | null
|
||||||
}) {
|
}) {
|
||||||
@@ -3314,6 +3371,15 @@ function canEditRow(row: {
|
|||||||
// 已完成(3)、已取消(4) 不可编辑
|
// 已完成(3)、已取消(4) 不可编辑
|
||||||
if (fs === 3 || fs === 4) return false
|
if (fs === 3 || fs === 4) return false
|
||||||
|
|
||||||
|
// 双审均已通过:仅订单创建人不可再编辑(超管/全量编辑角色仍可改)
|
||||||
|
if (
|
||||||
|
isCurrentUserOrderCreator(row) &&
|
||||||
|
isDualAuditPassed(row) &&
|
||||||
|
!canBypassCreatorDualAuditEditLock()
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const gcNo = String(row.gancao_reciperl_order_no || '').trim()
|
const gcNo = String(row.gancao_reciperl_order_no || '').trim()
|
||||||
const gcTime = Number(row.gancao_submit_time || 0)
|
const gcTime = Number(row.gancao_submit_time || 0)
|
||||||
const gcLocked = gcNo !== '' || gcTime > 0
|
const gcLocked = gcNo !== '' || gcTime > 0
|
||||||
@@ -3380,6 +3446,70 @@ function canShipRow(row: { fulfillment_status?: number }) {
|
|||||||
return Number(row.fulfillment_status) === 2
|
return Number(row.fulfillment_status) === 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ShipMode = 'gancao' | 'direct'
|
||||||
|
|
||||||
|
const hasSetShipModePerm = computed(() => {
|
||||||
|
const p = userStore.perms || []
|
||||||
|
return p.includes('*') || p.includes('tcm.prescriptionOrder/setShipMode')
|
||||||
|
})
|
||||||
|
|
||||||
|
function normalizeShipMode(v: unknown): ShipMode {
|
||||||
|
return String(v || '').toLowerCase() === 'direct' ? 'direct' : 'gancao'
|
||||||
|
}
|
||||||
|
|
||||||
|
function shipModeLabel(v: unknown): string {
|
||||||
|
return normalizeShipMode(v) === 'direct' ? '洛阳药房' : '甘草药房'
|
||||||
|
}
|
||||||
|
|
||||||
|
function isShipModeLockedToGancao(row: { gancao_reciperl_order_no?: string | null }) {
|
||||||
|
return String(row.gancao_reciperl_order_no || '').trim() !== ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function canEditShipMode(row: { fulfillment_status?: number }) {
|
||||||
|
const fs = Number(row.fulfillment_status)
|
||||||
|
return fs !== 3 && fs !== 4
|
||||||
|
}
|
||||||
|
|
||||||
|
const shipModeSaving = ref(false)
|
||||||
|
|
||||||
|
function isSamePrescriptionOrderId(a: unknown, b: unknown) {
|
||||||
|
const na = Number(a)
|
||||||
|
const nb = Number(b)
|
||||||
|
return na > 0 && na === nb
|
||||||
|
}
|
||||||
|
|
||||||
|
const shipDialogModeDisplay = computed(() => {
|
||||||
|
return normalizeShipMode(shipForm.ship_mode) === 'direct' ? '洛阳药房直发' : '甘草药房直发'
|
||||||
|
})
|
||||||
|
|
||||||
|
async function onDetailShipModeChange(mode: string | number | boolean | undefined) {
|
||||||
|
const row = detailData.value
|
||||||
|
if (!row?.id) return
|
||||||
|
const next = normalizeShipMode(mode)
|
||||||
|
const prev = normalizeShipMode(row.ship_mode)
|
||||||
|
if (next === prev) return
|
||||||
|
if (!canEditShipMode(row)) {
|
||||||
|
feedback.msgWarning('当前订单状态不可修改发货类型')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
shipModeSaving.value = true
|
||||||
|
try {
|
||||||
|
await prescriptionOrderSetShipMode({ id: row.id, ship_mode: next })
|
||||||
|
if (detailData.value) detailData.value.ship_mode = next
|
||||||
|
if (shipVisible.value && isSamePrescriptionOrderId(shipRowId.value, row.id)) {
|
||||||
|
shipForm.ship_mode = next
|
||||||
|
}
|
||||||
|
await refreshCurrentPrescriptionOrderDetail()
|
||||||
|
feedback.msgSuccess('发货类型已保存')
|
||||||
|
getLists()
|
||||||
|
void fetchLogs(row.id)
|
||||||
|
} catch {
|
||||||
|
/* 拦截器已提示 */
|
||||||
|
} finally {
|
||||||
|
shipModeSaving.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function canAddPayOrderRow(row: { fulfillment_status?: number }) {
|
function canAddPayOrderRow(row: { fulfillment_status?: number }) {
|
||||||
// 已发货(5) / 已签收(6) 状态可补齐支付单
|
// 已发货(5) / 已签收(6) 状态可补齐支付单
|
||||||
const fs = Number(row.fulfillment_status)
|
const fs = Number(row.fulfillment_status)
|
||||||
@@ -3738,6 +3868,8 @@ function logActionText(act: string) {
|
|||||||
ship: '确认发货',
|
ship: '确认发货',
|
||||||
withdraw: '撤销',
|
withdraw: '撤销',
|
||||||
link_pay_order: '关联支付单',
|
link_pay_order: '关联支付单',
|
||||||
|
completion_request: '完单申请',
|
||||||
|
auto_complete: '自动完成',
|
||||||
revoke_rx_audit: '撤回处方审核',
|
revoke_rx_audit: '撤回处方审核',
|
||||||
revoke_pay_audit: '撤回支付审核',
|
revoke_pay_audit: '撤回支付审核',
|
||||||
gancao_submit: '甘草下单',
|
gancao_submit: '甘草下单',
|
||||||
@@ -4240,7 +4372,25 @@ async function goEditOrderNextStep() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openEdit(row: { id: number }) {
|
async function openEdit(row: {
|
||||||
|
id: number
|
||||||
|
creator_id?: number
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
fulfillment_status?: number
|
||||||
|
gancao_reciperl_order_no?: string | null
|
||||||
|
gancao_submit_time?: number | null
|
||||||
|
}) {
|
||||||
|
if (!canEditRow(row)) {
|
||||||
|
if (
|
||||||
|
isCurrentUserOrderCreator(row) &&
|
||||||
|
isDualAuditPassed(row) &&
|
||||||
|
!canBypassCreatorDualAuditEditLock()
|
||||||
|
) {
|
||||||
|
feedback.msgWarning('处方与支付单均已审核通过,创建人不可再编辑')
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
editOrderStep.value = 0
|
editOrderStep.value = 0
|
||||||
editOrderPrescription.value = null
|
editOrderPrescription.value = null
|
||||||
editVisible.value = true
|
editVisible.value = true
|
||||||
@@ -4761,33 +4911,32 @@ const shipForm = reactive({
|
|||||||
tracking_number: ''
|
tracking_number: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
function openShip(row: { id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown }) {
|
function resolveShipModeForRow(row: { id: number; ship_mode?: unknown }) {
|
||||||
shipRowId.value = row.id
|
if (isSamePrescriptionOrderId(detailData.value?.id, row.id)) {
|
||||||
const tn = String(row.tracking_number ?? '').trim()
|
return normalizeShipMode(detailData.value!.ship_mode)
|
||||||
const sm = String(row.ship_mode ?? '').trim()
|
|
||||||
if (sm === 'direct' || sm === 'gancao') {
|
|
||||||
shipForm.ship_mode = sm
|
|
||||||
} else if (tn) {
|
|
||||||
// 已填单号时默认走药房直发(如快捷填单号后「确认发货」)
|
|
||||||
shipForm.ship_mode = 'direct'
|
|
||||||
} else {
|
|
||||||
shipForm.ship_mode = 'gancao'
|
|
||||||
}
|
}
|
||||||
|
return normalizeShipMode(row.ship_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
function openShip(row: { id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown }) {
|
||||||
|
shipRowId.value = Number(row.id)
|
||||||
|
shipForm.ship_mode = resolveShipModeForRow(row)
|
||||||
shipForm.express_company = String(row.express_company || 'auto') || 'auto'
|
shipForm.express_company = String(row.express_company || 'auto') || 'auto'
|
||||||
shipForm.tracking_number = String(row.tracking_number || '')
|
shipForm.tracking_number = String(row.tracking_number || '')
|
||||||
shipVisible.value = true
|
shipVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitShip() {
|
watch(
|
||||||
if (shipForm.ship_mode !== 'direct') {
|
() => detailData.value?.ship_mode,
|
||||||
if (!hasPermission(['tcm.prescriptionOrder/submitGancaoRecipel'])) {
|
(v) => {
|
||||||
feedback.msgError('暂无上传甘草药方权限,请改用「药房直发」并填写快递单号,或联系管理员开通权限')
|
if (!shipVisible.value || !shipRowId.value || !isSamePrescriptionOrderId(detailData.value?.id, shipRowId.value)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
shipVisible.value = false
|
shipForm.ship_mode = normalizeShipMode(v)
|
||||||
await confirmSubmitGancaoRecipel({ id: shipRowId.value })
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
async function submitShip() {
|
||||||
if (!shipForm.tracking_number.trim()) {
|
if (!shipForm.tracking_number.trim()) {
|
||||||
feedback.msgError('请先填写快递单号再确认发货')
|
feedback.msgError('请先填写快递单号再确认发货')
|
||||||
return
|
return
|
||||||
@@ -4796,7 +4945,7 @@ async function submitShip() {
|
|||||||
try {
|
try {
|
||||||
await prescriptionOrderShip({
|
await prescriptionOrderShip({
|
||||||
id: shipRowId.value,
|
id: shipRowId.value,
|
||||||
ship_mode: 'direct',
|
ship_mode: normalizeShipMode(shipForm.ship_mode),
|
||||||
express_company: shipForm.express_company || 'auto',
|
express_company: shipForm.express_company || 'auto',
|
||||||
tracking_number: shipForm.tracking_number.trim()
|
tracking_number: shipForm.tracking_number.trim()
|
||||||
})
|
})
|
||||||
@@ -4894,8 +5043,15 @@ const addPayOrderRowId = ref(0)
|
|||||||
const addPayOrderFormRef = ref<FormInstance>()
|
const addPayOrderFormRef = ref<FormInstance>()
|
||||||
const addPayOrderAvailableLoading = ref(false)
|
const addPayOrderAvailableLoading = ref(false)
|
||||||
const addPayOrderAvailableList = ref<any[]>([])
|
const addPayOrderAvailableList = ref<any[]>([])
|
||||||
|
const addPayOrderAlertTitle = computed(() => {
|
||||||
|
if (addPayOrderForm.add_mode === 'completion_only') {
|
||||||
|
return '不创建或关联支付单,仅提交完单申请,由审核人员在支付审核时处理。'
|
||||||
|
}
|
||||||
|
return '可以手动创建新支付单,或关联已存在但未绑定的支付单。'
|
||||||
|
})
|
||||||
|
|
||||||
const addPayOrderForm = reactive({
|
const addPayOrderForm = reactive({
|
||||||
add_mode: 'create' as 'create' | 'link',
|
add_mode: 'create' as 'create' | 'link' | 'completion_only',
|
||||||
order_type: 3,
|
order_type: 3,
|
||||||
pay_amount: undefined as number | undefined,
|
pay_amount: undefined as number | undefined,
|
||||||
pay_remark: '',
|
pay_remark: '',
|
||||||
@@ -4903,38 +5059,47 @@ const addPayOrderForm = reactive({
|
|||||||
completion_request: 0
|
completion_request: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const addPayOrderRules: FormRules = {
|
const addPayOrderRules = computed<FormRules>(() => {
|
||||||
add_mode: [{ required: true, message: '请选择添加方式', trigger: 'change' }],
|
const rules: FormRules = {
|
||||||
order_type: [{ required: true, message: '请选择费用类别', trigger: 'change' }],
|
add_mode: [{ required: true, message: '请选择添加方式', trigger: 'change' }]
|
||||||
pay_amount: [
|
}
|
||||||
{ required: true, message: '请输入支付金额', trigger: 'blur' },
|
if (addPayOrderForm.add_mode === 'create') {
|
||||||
{
|
rules.order_type = [{ required: true, message: '请选择费用类别', trigger: 'change' }]
|
||||||
validator: (_rule, v, cb) => {
|
rules.pay_amount = [
|
||||||
const n = Number(v)
|
{
|
||||||
if (!Number.isFinite(n) || n <= 0) {
|
validator: (_rule, v, cb) => {
|
||||||
cb(new Error('金额须大于 0'))
|
if (v === undefined || v === null || v === '') {
|
||||||
} else {
|
cb(new Error('请输入支付金额'))
|
||||||
cb()
|
return
|
||||||
}
|
}
|
||||||
},
|
const n = Number(v)
|
||||||
trigger: 'blur'
|
if (!Number.isFinite(n) || n < 0) {
|
||||||
}
|
cb(new Error('金额不能小于 0'))
|
||||||
],
|
} else {
|
||||||
link_pay_order_ids: [
|
cb()
|
||||||
{
|
}
|
||||||
required: true,
|
},
|
||||||
message: '请至少选择一个支付单',
|
trigger: 'blur'
|
||||||
trigger: 'change',
|
}
|
||||||
validator: (_rule, v, cb) => {
|
]
|
||||||
if (!Array.isArray(v) || v.length === 0) {
|
} else if (addPayOrderForm.add_mode === 'link') {
|
||||||
cb(new Error('请至少选择一个支付单'))
|
rules.link_pay_order_ids = [
|
||||||
} else {
|
{
|
||||||
cb()
|
required: true,
|
||||||
|
message: '请至少选择一个支付单',
|
||||||
|
trigger: 'change',
|
||||||
|
validator: (_rule, v, cb) => {
|
||||||
|
if (!Array.isArray(v) || v.length === 0) {
|
||||||
|
cb(new Error('请至少选择一个支付单'))
|
||||||
|
} else {
|
||||||
|
cb()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
]
|
}
|
||||||
}
|
return rules
|
||||||
|
})
|
||||||
|
|
||||||
async function loadAddPayOrderAvailable(diagnosisId: number, currentLinkedIds: number[] = []) {
|
async function loadAddPayOrderAvailable(diagnosisId: number, currentLinkedIds: number[] = []) {
|
||||||
if (!diagnosisId) {
|
if (!diagnosisId) {
|
||||||
@@ -4982,7 +5147,10 @@ async function submitAddPayOrder() {
|
|||||||
await addPayOrderFormRef.value.validate()
|
await addPayOrderFormRef.value.validate()
|
||||||
addPayOrderSaving.value = true
|
addPayOrderSaving.value = true
|
||||||
try {
|
try {
|
||||||
if (addPayOrderForm.add_mode === 'create') {
|
if (addPayOrderForm.add_mode === 'completion_only') {
|
||||||
|
await prescriptionOrderRequestCompletion({ id: addPayOrderRowId.value })
|
||||||
|
feedback.msgSuccess('完单申请已提交,请等待支付审核')
|
||||||
|
} else if (addPayOrderForm.add_mode === 'create') {
|
||||||
// 手动创建新支付单
|
// 手动创建新支付单
|
||||||
await prescriptionOrderAddPayOrder({
|
await prescriptionOrderAddPayOrder({
|
||||||
id: addPayOrderRowId.value,
|
id: addPayOrderRowId.value,
|
||||||
|
|||||||
@@ -3047,14 +3047,48 @@ function formatTime(v: unknown) {
|
|||||||
return String(v)
|
return String(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 与 server/config/project.php order_edit_all_roles 一致 */
|
||||||
|
const ORDER_EDIT_ALL_ROLE_IDS = [0, 3]
|
||||||
|
|
||||||
|
function canBypassCreatorDualAuditEditLock(): boolean {
|
||||||
|
const u = userStore.userInfo
|
||||||
|
if (!u) return false
|
||||||
|
if (Number(u.root) === 1) return true
|
||||||
|
const ids = Array.isArray(u.role_ids) ? u.role_ids.map((n: unknown) => Number(n)) : []
|
||||||
|
return ids.some((id) => ORDER_EDIT_ALL_ROLE_IDS.includes(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCurrentUserOrderCreator(row: { creator_id?: number }) {
|
||||||
|
const uid = Number(userStore.userInfo?.id)
|
||||||
|
return uid > 0 && Number(row.creator_id) === uid
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDualAuditPassed(row: {
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
}) {
|
||||||
|
return Number(row.prescription_audit_status) === 1 && Number(row.payment_slip_audit_status) === 1
|
||||||
|
}
|
||||||
|
|
||||||
function canEditRow(row: {
|
function canEditRow(row: {
|
||||||
|
creator_id?: number
|
||||||
fulfillment_status?: number
|
fulfillment_status?: number
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
gancao_reciperl_order_no?: string | null
|
gancao_reciperl_order_no?: string | null
|
||||||
gancao_submit_time?: number | null
|
gancao_submit_time?: number | null
|
||||||
}) {
|
}) {
|
||||||
const fs = Number(row.fulfillment_status)
|
const fs = Number(row.fulfillment_status)
|
||||||
if (fs === 3 || fs === 4) return false
|
if (fs === 3 || fs === 4) return false
|
||||||
|
|
||||||
|
if (
|
||||||
|
isCurrentUserOrderCreator(row) &&
|
||||||
|
isDualAuditPassed(row) &&
|
||||||
|
!canBypassCreatorDualAuditEditLock()
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const gcNo = String(row.gancao_reciperl_order_no || '').trim()
|
const gcNo = String(row.gancao_reciperl_order_no || '').trim()
|
||||||
const gcTime = Number(row.gancao_submit_time || 0)
|
const gcTime = Number(row.gancao_submit_time || 0)
|
||||||
const gcLocked = gcNo !== '' || gcTime > 0
|
const gcLocked = gcNo !== '' || gcTime > 0
|
||||||
@@ -3889,7 +3923,25 @@ async function goEditOrderNextStep() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openEdit(row: { id: number }) {
|
async function openEdit(row: {
|
||||||
|
id: number
|
||||||
|
creator_id?: number
|
||||||
|
prescription_audit_status?: number
|
||||||
|
payment_slip_audit_status?: number
|
||||||
|
fulfillment_status?: number
|
||||||
|
gancao_reciperl_order_no?: string | null
|
||||||
|
gancao_submit_time?: number | null
|
||||||
|
}) {
|
||||||
|
if (!canEditRow(row)) {
|
||||||
|
if (
|
||||||
|
isCurrentUserOrderCreator(row) &&
|
||||||
|
isDualAuditPassed(row) &&
|
||||||
|
!canBypassCreatorDualAuditEditLock()
|
||||||
|
) {
|
||||||
|
feedback.msgWarning('处方与支付单均已审核通过,创建人不可再编辑')
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
editOrderStep.value = 0
|
editOrderStep.value = 0
|
||||||
editOrderPrescription.value = null
|
editOrderPrescription.value = null
|
||||||
editVisible.value = true
|
editVisible.value = true
|
||||||
|
|||||||
@@ -22,16 +22,25 @@
|
|||||||
<el-select
|
<el-select
|
||||||
v-model="formData.media_channel_code"
|
v-model="formData.media_channel_code"
|
||||||
placeholder="请选择自媒体渠道"
|
placeholder="请选择自媒体渠道"
|
||||||
class="w-full"
|
class="account-cost-channel-select w-full"
|
||||||
filterable
|
filterable
|
||||||
:disabled="mode === 'edit'"
|
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option-group
|
||||||
v-for="item in mediaChannelOptions"
|
v-for="g in mediaChannelGroups"
|
||||||
:key="item.code"
|
:key="g.group_name"
|
||||||
:label="item.name"
|
:label="g.group_name"
|
||||||
:value="item.code"
|
>
|
||||||
/>
|
<el-option
|
||||||
|
v-for="ch in g.channels"
|
||||||
|
:key="ch.channel_code"
|
||||||
|
:label="ch.channel_name"
|
||||||
|
:value="ch.channel_code"
|
||||||
|
>
|
||||||
|
<div class="channel-opt-row">
|
||||||
|
<span class="opt-name">{{ ch.channel_name }}</span>
|
||||||
|
</div>
|
||||||
|
</el-option>
|
||||||
|
</el-option-group>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="部门" prop="dept_id">
|
<el-form-item label="部门" prop="dept_id">
|
||||||
@@ -78,9 +87,9 @@ import type { FormInstance } from 'element-plus'
|
|||||||
import { accountCostAdd, accountCostDetail, accountCostEdit } from '@/api/finance'
|
import { accountCostAdd, accountCostDetail, accountCostEdit } from '@/api/finance'
|
||||||
import Popup from '@/components/popup/index.vue'
|
import Popup from '@/components/popup/index.vue'
|
||||||
|
|
||||||
interface MediaChannelOption {
|
interface MediaChannelGroup {
|
||||||
code: string
|
group_name: string
|
||||||
name: string
|
channels: { channel_code: string; channel_name: string }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeptOption {
|
interface DeptOption {
|
||||||
@@ -90,7 +99,7 @@ interface DeptOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
mediaChannelOptions: MediaChannelOption[]
|
mediaChannelGroups: MediaChannelGroup[]
|
||||||
deptOptions: DeptOption[]
|
deptOptions: DeptOption[]
|
||||||
defaultMediaChannelCode: string
|
defaultMediaChannelCode: string
|
||||||
}>()
|
}>()
|
||||||
@@ -197,3 +206,21 @@ defineExpose({
|
|||||||
getDetail,
|
getDetail,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.account-cost-channel-select {
|
||||||
|
:deep(.channel-opt-row) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
:deep(.opt-name) {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -27,14 +27,25 @@
|
|||||||
placeholder="筛选全部渠道"
|
placeholder="筛选全部渠道"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
class="w-[220px]"
|
class="account-cost-channel-select w-[220px]"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option label="全部" value="" />
|
||||||
v-for="item in mediaChannelOptions"
|
<el-option-group
|
||||||
:key="item.code"
|
v-for="g in mediaChannelGroups"
|
||||||
:label="item.name"
|
:key="g.group_name"
|
||||||
:value="item.code"
|
:label="g.group_name"
|
||||||
/>
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="ch in g.channels"
|
||||||
|
:key="ch.channel_code"
|
||||||
|
:label="ch.channel_name"
|
||||||
|
:value="ch.channel_code"
|
||||||
|
>
|
||||||
|
<div class="channel-opt-row">
|
||||||
|
<span class="opt-name">{{ ch.channel_name }}</span>
|
||||||
|
</div>
|
||||||
|
</el-option>
|
||||||
|
</el-option-group>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="部门">
|
<el-form-item label="部门">
|
||||||
@@ -78,7 +89,15 @@
|
|||||||
|
|
||||||
<el-table class="mt-4" size="large" v-loading="pager.loading" :data="pager.lists">
|
<el-table class="mt-4" size="large" v-loading="pager.loading" :data="pager.lists">
|
||||||
<el-table-column label="日期" prop="cost_date" min-width="120" />
|
<el-table-column label="日期" prop="cost_date" min-width="120" />
|
||||||
<el-table-column label="自媒体渠道" prop="media_channel_name" min-width="120" />
|
<el-table-column label="自媒体渠道" min-width="160">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="getChannelGroupLabel(row.media_channel_code)">
|
||||||
|
<el-tag size="small" class="mr-1">{{ getChannelGroupLabel(row.media_channel_code) }}</el-tag>
|
||||||
|
{{ row.media_channel_name }}
|
||||||
|
</span>
|
||||||
|
<span v-else>{{ row.media_channel_name }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="部门" prop="dept_name" min-width="140" />
|
<el-table-column label="部门" prop="dept_name" min-width="140" />
|
||||||
<el-table-column label="账户消耗" min-width="120">
|
<el-table-column label="账户消耗" min-width="120">
|
||||||
<template #default="{ row }">¥{{ row.amount }}</template>
|
<template #default="{ row }">¥{{ row.amount }}</template>
|
||||||
@@ -117,7 +136,7 @@
|
|||||||
<edit-popup
|
<edit-popup
|
||||||
v-if="showEdit"
|
v-if="showEdit"
|
||||||
ref="editRef"
|
ref="editRef"
|
||||||
:media-channel-options="mediaChannelOptions"
|
:media-channel-groups="mediaChannelGroups"
|
||||||
:dept-options="deptOptions"
|
:dept-options="deptOptions"
|
||||||
:default-media-channel-code="defaultMediaChannelCode"
|
:default-media-channel-code="defaultMediaChannelCode"
|
||||||
@success="getLists"
|
@success="getLists"
|
||||||
@@ -133,6 +152,11 @@ import feedback from '@/utils/feedback'
|
|||||||
|
|
||||||
import EditPopup from './edit.vue'
|
import EditPopup from './edit.vue'
|
||||||
|
|
||||||
|
interface MediaChannelGroup {
|
||||||
|
group_name: string
|
||||||
|
channels: { channel_code: string; channel_name: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
interface MediaChannelOption {
|
interface MediaChannelOption {
|
||||||
code: string
|
code: string
|
||||||
name: string
|
name: string
|
||||||
@@ -160,7 +184,7 @@ const { pager, getLists, resetPage } = usePaging({
|
|||||||
params: queryParams,
|
params: queryParams,
|
||||||
})
|
})
|
||||||
|
|
||||||
const mediaChannelOptions = computed<MediaChannelOption[]>(() => {
|
const mediaChannelOptionsFlat = computed<MediaChannelOption[]>(() => {
|
||||||
const options = pager.extend.media_channel_options
|
const options = pager.extend.media_channel_options
|
||||||
if (!Array.isArray(options)) return []
|
if (!Array.isArray(options)) return []
|
||||||
|
|
||||||
@@ -170,6 +194,26 @@ const mediaChannelOptions = computed<MediaChannelOption[]>(() => {
|
|||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const mediaChannelGroups = computed<MediaChannelGroup[]>(() => {
|
||||||
|
const groups = pager.extend.media_channel_groups
|
||||||
|
if (Array.isArray(groups) && groups.length > 0) {
|
||||||
|
return groups as MediaChannelGroup[]
|
||||||
|
}
|
||||||
|
const flat = mediaChannelOptionsFlat.value
|
||||||
|
if (!flat.length) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
group_name: '渠道',
|
||||||
|
channels: flat.map(item => ({
|
||||||
|
channel_code: item.code,
|
||||||
|
channel_name: item.name,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
const deptOptions = computed<DeptOption[]>(() => {
|
const deptOptions = computed<DeptOption[]>(() => {
|
||||||
const options = pager.extend.dept_options
|
const options = pager.extend.dept_options
|
||||||
return Array.isArray(options) ? options : []
|
return Array.isArray(options) ? options : []
|
||||||
@@ -177,6 +221,17 @@ const deptOptions = computed<DeptOption[]>(() => {
|
|||||||
|
|
||||||
const defaultMediaChannelCode = computed(() => String(pager.extend.default_media_channel_code || ''))
|
const defaultMediaChannelCode = computed(() => String(pager.extend.default_media_channel_code || ''))
|
||||||
|
|
||||||
|
const getChannelGroupLabel = (channelCode: string): string => {
|
||||||
|
if (!channelCode) return ''
|
||||||
|
for (const group of mediaChannelGroups.value) {
|
||||||
|
const channel = group.channels.find(ch => ch.channel_code === channelCode)
|
||||||
|
if (channel) {
|
||||||
|
return group.group_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
const treeProps = {
|
const treeProps = {
|
||||||
value: 'id',
|
value: 'id',
|
||||||
label: 'name',
|
label: 'name',
|
||||||
@@ -213,3 +268,21 @@ const handleReset = () => {
|
|||||||
|
|
||||||
getLists()
|
getLists()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.account-cost-channel-select {
|
||||||
|
:deep(.channel-opt-row) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
:deep(.opt-name) {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,273 @@
|
|||||||
|
<template>
|
||||||
|
<div class="assistant-performance-page">
|
||||||
|
<el-card class="!border-none" shadow="never">
|
||||||
|
<el-form :inline="true" class="stats-filter-form">
|
||||||
|
<el-form-item label="时间范围">
|
||||||
|
<el-radio-group v-model="queryParams.time_type" @change="handleTimeTypeChange">
|
||||||
|
<el-radio-button label="today">今天</el-radio-button>
|
||||||
|
<el-radio-button label="yesterday">昨天</el-radio-button>
|
||||||
|
<el-radio-button label="week">最近7天</el-radio-button>
|
||||||
|
<el-radio-button label="month">最近30天</el-radio-button>
|
||||||
|
<el-radio-button label="custom">自定义</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item v-if="queryParams.time_type === 'custom'">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
style="width: 260px"
|
||||||
|
@change="handleCustomDateChange"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="loading" @click="fetchData">查询</el-button>
|
||||||
|
<el-button @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<div class="stats-kpi-grid">
|
||||||
|
<div v-for="card in summaryCards" :key="card.key" class="stats-kpi-card" :class="card.cardClass">
|
||||||
|
<div class="stats-kpi-label">{{ card.label }}</div>
|
||||||
|
<div class="stats-kpi-value" :class="{ 'is-money': card.type === 'money' }">
|
||||||
|
{{ formatValue(card.key, card.type) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-card class="!border-none mt-4" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">业绩趋势</span>
|
||||||
|
<span class="card-hint">{{ dateRangeText }} · 履约完成业绩</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<v-charts
|
||||||
|
v-if="chartHasData"
|
||||||
|
class="stats-chart"
|
||||||
|
:option="chartOption"
|
||||||
|
autoresize
|
||||||
|
/>
|
||||||
|
<el-empty v-else description="暂无数据" />
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="assistantPerformancePage">
|
||||||
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import vCharts from 'vue-echarts'
|
||||||
|
import { assistantPerformanceOverview } from '@/api/stats'
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const dateRange = ref<string[]>([])
|
||||||
|
const queryParams = reactive({
|
||||||
|
time_type: 'month',
|
||||||
|
start_date: '',
|
||||||
|
end_date: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const overview = reactive<Record<string, any>>({
|
||||||
|
date_range: [],
|
||||||
|
summary: {
|
||||||
|
total_amount: 0,
|
||||||
|
total_count: 0
|
||||||
|
},
|
||||||
|
chart: {
|
||||||
|
dates: [],
|
||||||
|
amounts: [],
|
||||||
|
counts: []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const summaryCards = [
|
||||||
|
{ key: 'total_amount', label: '业绩', type: 'money', cardClass: '' },
|
||||||
|
{ key: 'total_count', label: '有效订单数', type: 'count', cardClass: '' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const dateRangeText = computed(() => {
|
||||||
|
if (!overview.date_range?.length) return '未选择'
|
||||||
|
return `${overview.date_range[0]} 至 ${overview.date_range[1]}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartHasData = computed(() => {
|
||||||
|
return overview.chart.dates.length > 0 && overview.chart.amounts.some((v: number) => v > 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartOption = computed(() => ({
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const p = params[0]
|
||||||
|
return `${p.axisValue}<br/>${p.marker}${p.seriesName}: ¥${Number(p.value).toFixed(2)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: { left: 60, right: 24, top: 36, bottom: 36 },
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: overview.chart.dates,
|
||||||
|
axisLabel: {
|
||||||
|
interval: overview.chart.dates.length > 15 ? 'auto' : 0,
|
||||||
|
rotate: overview.chart.dates.length > 10 ? 30 : 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '金额(元)',
|
||||||
|
axisLabel: {
|
||||||
|
formatter: (val: number) => val >= 10000 ? (val / 10000).toFixed(1) + '万' : String(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '业绩',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: true,
|
||||||
|
symbolSize: 6,
|
||||||
|
lineStyle: { width: 3, color: '#4a78ff' },
|
||||||
|
itemStyle: { color: '#4a78ff' },
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0, y: 0, x2: 0, y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(74, 120, 255, 0.25)' },
|
||||||
|
{ offset: 1, color: 'rgba(74, 120, 255, 0.02)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: overview.chart.amounts
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
const formatValue = (key: string, type: string) => {
|
||||||
|
const value = overview.summary?.[key] ?? 0
|
||||||
|
if (type === 'money') return `¥${Number(value).toFixed(2)}`
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params: Record<string, any> = { time_type: queryParams.time_type }
|
||||||
|
if (queryParams.time_type === 'custom') {
|
||||||
|
params.start_date = dateRange.value[0] || ''
|
||||||
|
params.end_date = dateRange.value[1] || ''
|
||||||
|
}
|
||||||
|
const res = await assistantPerformanceOverview(params)
|
||||||
|
Object.assign(overview, res || {})
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('获取业绩数据失败:', error)
|
||||||
|
ElMessage.error(error?.msg || '获取业绩数据失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTimeTypeChange = () => {
|
||||||
|
if (queryParams.time_type !== 'custom') {
|
||||||
|
dateRange.value = []
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCustomDateChange = () => {
|
||||||
|
if (dateRange.value?.length === 2) {
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
queryParams.time_type = 'month'
|
||||||
|
dateRange.value = []
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.assistant-performance-page {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-filter-form {
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-kpi-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-kpi-card {
|
||||||
|
background: linear-gradient(145deg, #ffffff, #f5f8ff);
|
||||||
|
border: 1px solid #ebf1ff;
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 10px 24px rgba(74, 120, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-kpi-card.is-cancelled {
|
||||||
|
background: linear-gradient(145deg, #ffffff, #fff5f5);
|
||||||
|
border-color: #ffe0e0;
|
||||||
|
box-shadow: 0 10px 24px rgba(239, 68, 68, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-kpi-label {
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-kpi-value {
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: 1.1;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-kpi-value.is-money {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-kpi-card.is-cancelled .stats-kpi-value {
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-hint {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-chart {
|
||||||
|
height: 360px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
<el-descriptions-item label="医师">{{ detailPrescription.doctor_name || '—' }}</el-descriptions-item>
|
<el-descriptions-item label="医师">{{ detailPrescription.doctor_name || '—' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="类型">{{ detailPrescription.prescription_type || '—' }}</el-descriptions-item>
|
<el-descriptions-item label="类型">{{ detailPrescription.prescription_type || '—' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="临床诊断" :span="2">{{ detailPrescription.clinical_diagnosis || '—' }}</el-descriptions-item>
|
<el-descriptions-item label="临床诊断" :span="2">{{ detailPrescription.clinical_diagnosis || '—' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="剂数 / 用法" :span="2">
|
<el-descriptions-item label="剂数 / 用法" :span="2" v-if="showPrescriptionAuditFilter">
|
||||||
{{ detailData.dose_count ?? detailPrescription.dose_count ?? '—' }} {{ detailData.dose_unit || '剂' }} ·
|
{{ detailData.dose_count ?? detailPrescription.dose_count ?? '—' }} {{ detailData.dose_unit || '剂' }} ·
|
||||||
{{ detailPrescription.usage_instruction || detailPrescription.usage_method || '—' }}
|
{{ detailPrescription.usage_instruction || detailPrescription.usage_method || '—' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
@@ -379,6 +379,22 @@ const buildParams = () => {
|
|||||||
queryParams.scene = 'diagnosis_edit'
|
queryParams.scene = 'diagnosis_edit'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 诊间医助角色(与 DiagnosisLists 等一致) */
|
||||||
|
const TCM_ASSISTANT_ROLE_ID = 2
|
||||||
|
/** 与 server/config/project.php prescription_audit_roles 默认一致,可处方审核的角色 */
|
||||||
|
const PRESCRIPTION_AUDIT_ROLE_IDS = [0, 3, 6]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否展示「处方审核」相关字段
|
||||||
|
*/
|
||||||
|
const showPrescriptionAuditFilter = computed(() => {
|
||||||
|
const u = userStore.userInfo
|
||||||
|
if (!u || Number(u.root) === 1) return true
|
||||||
|
const ids = Array.isArray(u.role_ids) ? u.role_ids.map((n: unknown) => Number(n)) : []
|
||||||
|
if (!ids.includes(TCM_ASSISTANT_ROLE_ID)) return true
|
||||||
|
return ids.some((id) => PRESCRIPTION_AUDIT_ROLE_IDS.includes(id))
|
||||||
|
})
|
||||||
|
|
||||||
// ─── 详情抽屉 ───
|
// ─── 详情抽屉 ───
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
const detailLoading = ref(false)
|
const detailLoading = ref(false)
|
||||||
|
|||||||
@@ -257,7 +257,7 @@
|
|||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="row.last_blood_record_at"
|
v-if="row.last_blood_record_at"
|
||||||
:content="`最近一次血糖打卡:${row.last_blood_record_at}`"
|
:content="`最近一次健康打卡:${row.last_blood_record_at}`"
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<span class="unserved-days" :class="unservedDaysClass(row.unserved_days)">
|
<span class="unserved-days" :class="unservedDaysClass(row.unserved_days)">
|
||||||
|
|||||||
@@ -17,14 +17,14 @@
|
|||||||
</span>
|
</span>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="lastBloodAt"
|
v-if="lastBloodAt"
|
||||||
:content="`最近一次血糖打卡:${lastBloodAt}`"
|
:content="`最近一次健康打卡:${lastBloodAt}`"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
>
|
>
|
||||||
<span :class="['unserved-badge', unservedDaysClass(unservedDays)]">
|
<span :class="['unserved-badge', unservedDaysClass(unservedDays)]">
|
||||||
未服务 {{ unservedDays ?? '—' }} 天
|
未服务 {{ unservedDays ?? '—' }} 天
|
||||||
</span>
|
</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span v-else class="unserved-badge unserved-muted">从未打卡血糖</span>
|
<span v-else class="unserved-badge unserved-muted">从未打卡</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace app\adminapi\controller\stats;
|
||||||
|
|
||||||
|
use app\adminapi\controller\BaseAdminController;
|
||||||
|
use app\adminapi\logic\stats\AssistantPerformanceLogic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 医助个人业绩
|
||||||
|
*
|
||||||
|
* - GET stats.assistantPerformance/overview 个人业绩概览
|
||||||
|
*/
|
||||||
|
class AssistantPerformanceController extends BaseAdminController
|
||||||
|
{
|
||||||
|
public function overview()
|
||||||
|
{
|
||||||
|
$params = $this->request->get();
|
||||||
|
return $this->data(AssistantPerformanceLogic::overview($params, $this->adminId, $this->adminInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
+33
@@ -124,6 +124,25 @@ class PrescriptionOrderController extends BaseAdminController
|
|||||||
return $this->success('修改成功');
|
return $this->success('修改成功');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置发货类型(甘草 / 洛阳)
|
||||||
|
*/
|
||||||
|
public function setShipMode()
|
||||||
|
{
|
||||||
|
$params = (new PrescriptionOrderValidate())->post()->goCheck('setShipMode');
|
||||||
|
$result = PrescriptionOrderLogic::setShipMode(
|
||||||
|
(int) $params['id'],
|
||||||
|
(string) ($params['ship_mode'] ?? 'gancao'),
|
||||||
|
$this->adminId,
|
||||||
|
$this->adminInfo
|
||||||
|
);
|
||||||
|
if ($result === false) {
|
||||||
|
return $this->fail(PrescriptionOrderLogic::getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success('已保存', $result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改关联处方的患者姓名与手机号(订单详情场景)
|
* 修改关联处方的患者姓名与手机号(订单详情场景)
|
||||||
*/
|
*/
|
||||||
@@ -293,6 +312,20 @@ class PrescriptionOrderController extends BaseAdminController
|
|||||||
return $this->success('关联支付单成功', $result);
|
return $this->success('关联支付单成功', $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已发货/已签收:仅提交完单申请(不新增/关联支付单),并重置支付审核为待审核
|
||||||
|
*/
|
||||||
|
public function requestCompletion()
|
||||||
|
{
|
||||||
|
$params = (new PrescriptionOrderValidate())->post()->goCheck('requestCompletion');
|
||||||
|
$result = PrescriptionOrderLogic::requestCompletion($params, $this->adminId, $this->adminInfo);
|
||||||
|
if ($result === false) {
|
||||||
|
return $this->fail(PrescriptionOrderLogic::getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success('完单申请已提交', $result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将「已发货」且支付审核已通过的订单标记为「已完成」
|
* 将「已发货」且支付审核已通过的订单标记为「已完成」
|
||||||
*/
|
*/
|
||||||
|
|||||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user