更新bug

This commit is contained in:
Your Name
2026-06-02 10:16:03 +08:00
parent b0a8bd83bc
commit 707e4888ad
6 changed files with 316 additions and 18 deletions
+6 -1
View File
@@ -482,11 +482,16 @@ export function prescriptionOrderRequestCompletion(params: { id: number }) {
return request.post({ url: '/tcm.prescriptionOrder/requestCompletion', params }) 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 })
} }
/** 业务订单退款(须填写原因;关联收款单标记已退款、已付总额清零) */
export function prescriptionOrderRefund(params: { id: number; reason: string }) {
return request.post({ url: '/tcm.prescriptionOrder/refund', params })
}
/** 甘草药管家:中药处方下单(服务端 CTM_PREVIEW → CTM_SUBMIT_RECIPEL */ /** 甘草药管家:中药处方下单(服务端 CTM_PREVIEW → CTM_SUBMIT_RECIPEL */
export function prescriptionOrderSubmitGancaoRecipel(params: { id: number }) { export function prescriptionOrderSubmitGancaoRecipel(params: { id: number }) {
return request.post({ url: '/tcm.prescriptionOrder/submitGancaoRecipel', params }) return request.post({ url: '/tcm.prescriptionOrder/submitGancaoRecipel', params })
@@ -530,6 +530,13 @@
link link
@click="confirmComplete(row)" @click="confirmComplete(row)"
>完成订单</el-button> >完成订单</el-button>
<el-button
v-if="canRefundRow(row)"
v-perms="['tcm.prescriptionOrder/refund']"
type="danger"
link
@click="openRefundOrder(row)"
>退款</el-button>
<el-button <el-button
v-if="canWithdrawRow(row)" v-if="canWithdrawRow(row)"
v-perms="['tcm.prescriptionOrder/withdraw']" v-perms="['tcm.prescriptionOrder/withdraw']"
@@ -672,6 +679,16 @@
> >
完成订单 完成订单
</el-button> </el-button>
<el-button
v-if="canRefundRow(detailData)"
v-perms="['tcm.prescriptionOrder/refund']"
type="danger"
size="small"
plain
@click="openRefundOrder(detailData)"
>
退款
</el-button>
</div> </div>
</div> </div>
</template> </template>
@@ -2103,6 +2120,41 @@
</el-button> </el-button>
</template> </template>
</el-dialog> </el-dialog>
<!-- 订单退款须填写原因 -->
<el-dialog
v-model="refundOrderDialogVisible"
title="订单退款"
width="480px"
:close-on-click-modal="false"
destroy-on-close
@closed="onRefundOrderDialogClosed"
>
<el-alert
type="warning"
:closable="false"
show-icon
class="mb-4 !items-start"
title="确认退款后,关联收款记录将全部标记为「已退款」,已付总额清零,且不可撤销。"
/>
<el-form ref="refundOrderFormRef" :model="refundOrderForm" :rules="refundOrderRules" label-width="88px">
<el-form-item label="退款原因" prop="reason">
<el-input
v-model="refundOrderForm.reason"
type="textarea"
:rows="4"
maxlength="500"
show-word-limit
placeholder="请填写退款原因(必填)"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="refundOrderDialogVisible = false">取消</el-button>
<el-button type="danger" :loading="refundOrderSubmitting" @click="submitRefundOrder">
确认退款
</el-button>
</template>
</el-dialog>
<!-- 补齐支付单 Dialog --> <!-- 补齐支付单 Dialog -->
<el-dialog <el-dialog
v-model="addPayOrderVisible" v-model="addPayOrderVisible"
@@ -2727,6 +2779,7 @@ import {
prescriptionOrderAddPayOrder, prescriptionOrderAddPayOrder,
prescriptionOrderRequestCompletion, prescriptionOrderRequestCompletion,
prescriptionOrderComplete, prescriptionOrderComplete,
prescriptionOrderRefund,
prescriptionOrderRevokeRxAudit, prescriptionOrderRevokeRxAudit,
prescriptionOrderRevokePayAudit, prescriptionOrderRevokePayAudit,
prescriptionOrderPatchPrescriptionPatient, prescriptionOrderPatchPrescriptionPatient,
@@ -3686,6 +3739,10 @@ function canCompleteRow(row: { fulfillment_status?: number; payment_slip_audit_s
return (fs === 5 || fs === 6) && Number(row.payment_slip_audit_status) === 1 return (fs === 5 || fs === 6) && Number(row.payment_slip_audit_status) === 1
} }
function canRefundRow(row: { fulfillment_status?: number; payment_slip_audit_status?: number }) {
return canCompleteRow(row)
}
function canQuickTrackRow(row: { fulfillment_status?: number }) { function canQuickTrackRow(row: { fulfillment_status?: number }) {
// (5) (6) // (5) (6)
return Number(row.fulfillment_status) === 5 return Number(row.fulfillment_status) === 5
@@ -4087,7 +4144,8 @@ function logActionText(act: string) {
gancao_submit: '甘草下单', gancao_submit: '甘草下单',
patch_rx_patient: '处方患者信息', patch_rx_patient: '处方患者信息',
update_amount: '修改订单金额', update_amount: '修改订单金额',
complete: '完成订单' complete: '完成订单',
refund: '退款'
} }
return m[act] || act return m[act] || act
} }
@@ -5193,7 +5251,6 @@ const completeOrderStatusOptions = [
{ value: 7, label: '进行中' }, { value: 7, label: '进行中' },
{ value: 8, label: '暂不制药' }, { value: 8, label: '暂不制药' },
{ value: 9, label: '拒收' }, { value: 9, label: '拒收' },
{ value: 10, label: '退款' },
{ value: 11, label: '保留药方' }, { value: 11, label: '保留药方' },
{ value: 12, label: '制药缓发' } { value: 12, label: '制药缓发' }
] as const ] as const
@@ -5269,6 +5326,54 @@ async function submitCompleteOrder() {
} }
} }
// 退/ +
const refundOrderDialogVisible = ref(false)
const refundOrderId = ref(0)
const refundOrderSubmitting = ref(false)
const refundOrderFormRef = ref<FormInstance>()
const refundOrderForm = reactive({ reason: '' })
const refundOrderRules: FormRules = {
reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }]
}
function onRefundOrderDialogClosed() {
refundOrderId.value = 0
refundOrderForm.reason = ''
refundOrderFormRef.value?.clearValidate()
}
function openRefundOrder(row: { id: number }) {
refundOrderId.value = row.id
refundOrderForm.reason = ''
refundOrderDialogVisible.value = true
}
async function submitRefundOrder() {
const id = refundOrderId.value
if (!id) return
const valid = await refundOrderFormRef.value?.validate().catch(() => false)
if (!valid) return
refundOrderSubmitting.value = true
try {
await prescriptionOrderRefund({ id, reason: refundOrderForm.reason.trim() })
feedback.msgSuccess('退款成功')
refundOrderDialogVisible.value = false
getLists()
if (detailVisible.value && detailData.value?.id === id) {
try {
const r: any = await prescriptionOrderDetail({ id })
const nd = r?.data ?? r ?? null
if (nd) detailData.value = nd
await fetchLogs(id)
} catch { /* 静默 */ }
}
} catch {
/* 拦截器已提示 */
} finally {
refundOrderSubmitting.value = false
}
}
// //
const addPayOrderVisible = ref(false) const addPayOrderVisible = ref(false)
const addPayOrderSaving = ref(false) const addPayOrderSaving = ref(false)
@@ -498,6 +498,9 @@
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item v-if="canAddPayOrderRow(row)" command="addPayOrder">补齐支付单</el-dropdown-item> <el-dropdown-item v-if="canAddPayOrderRow(row)" command="addPayOrder">补齐支付单</el-dropdown-item>
<el-dropdown-item v-if="canCompleteRow(row)" command="complete">完成订单</el-dropdown-item> <el-dropdown-item v-if="canCompleteRow(row)" command="complete">完成订单</el-dropdown-item>
<el-dropdown-item v-if="canRefundRow(row)" command="refund">
<span class="text-red-500">退款</span>
</el-dropdown-item>
<el-dropdown-item v-if="canUploadGancaoRow(row)" command="submitGancao">上传药方</el-dropdown-item> <el-dropdown-item v-if="canUploadGancaoRow(row)" command="submitGancao">上传药方</el-dropdown-item>
<el-dropdown-item v-if="canWithdrawRow(row)" command="withdraw" divided> <el-dropdown-item v-if="canWithdrawRow(row)" command="withdraw" divided>
<span class="text-red-500">撤回订单</span> <span class="text-red-500">撤回订单</span>
@@ -612,6 +615,16 @@
> >
完成订单 完成订单
</el-button> </el-button>
<el-button
v-if="canRefundRow(detailData)"
v-perms="['tcm.prescriptionOrder/refund']"
type="danger"
size="small"
plain
@click="openRefundOrder(detailData)"
>
退款
</el-button>
</div> </div>
</div> </div>
</template> </template>
@@ -1854,6 +1867,42 @@
</el-button> </el-button>
</template> </template>
</el-dialog> </el-dialog>
<!-- 订单退款须填写原因 -->
<el-dialog
v-model="refundOrderDialogVisible"
title="订单退款"
width="92%"
:close-on-click-modal="false"
destroy-on-close
class="po-h5-dialog"
@closed="onRefundOrderDialogClosed"
>
<el-alert
type="warning"
:closable="false"
show-icon
class="mb-4 !items-start"
title="确认退款后,关联收款记录将全部标记为「已退款」,已付总额清零,且不可撤销。"
/>
<el-form ref="refundOrderFormRef" :model="refundOrderForm" :rules="refundOrderRules" label-width="88px">
<el-form-item label="退款原因" prop="reason">
<el-input
v-model="refundOrderForm.reason"
type="textarea"
:rows="4"
maxlength="500"
show-word-limit
placeholder="请填写退款原因(必填)"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="refundOrderDialogVisible = false">取消</el-button>
<el-button type="danger" :loading="refundOrderSubmitting" @click="submitRefundOrder">
确认退款
</el-button>
</template>
</el-dialog>
<!-- 补齐支付单 Dialog --> <!-- 补齐支付单 Dialog -->
<el-dialog <el-dialog
v-model="addPayOrderVisible" v-model="addPayOrderVisible"
@@ -2416,6 +2465,7 @@ import {
prescriptionOrderUpdateAmount, prescriptionOrderUpdateAmount,
prescriptionOrderAddPayOrder, prescriptionOrderAddPayOrder,
prescriptionOrderComplete, prescriptionOrderComplete,
prescriptionOrderRefund,
prescriptionOrderRevokeRxAudit, prescriptionOrderRevokeRxAudit,
prescriptionOrderRevokePayAudit, prescriptionOrderRevokePayAudit,
prescriptionOrderPatchPrescriptionPatient, prescriptionOrderPatchPrescriptionPatient,
@@ -3237,6 +3287,10 @@ function canCompleteRow(row: { fulfillment_status?: number; payment_slip_audit_s
return (fs === 5 || fs === 6) && Number(row.payment_slip_audit_status) === 1 return (fs === 5 || fs === 6) && Number(row.payment_slip_audit_status) === 1
} }
function canRefundRow(row: { fulfillment_status?: number; payment_slip_audit_status?: number }) {
return canCompleteRow(row)
}
function canQuickTrackRow(row: { fulfillment_status?: number }) { function canQuickTrackRow(row: { fulfillment_status?: number }) {
return Number(row.fulfillment_status) === 5 return Number(row.fulfillment_status) === 5
} }
@@ -3286,13 +3340,14 @@ const h5ActiveFilterCount = computed(() => {
// H5: // H5:
function hasMoreCardActions(row: Record<string, any>) { function hasMoreCardActions(row: Record<string, any>) {
return canAddPayOrderRow(row) || canCompleteRow(row) || canUploadGancaoRow(row) || canWithdrawRow(row) return canAddPayOrderRow(row) || canCompleteRow(row) || canRefundRow(row) || canUploadGancaoRow(row) || canWithdrawRow(row)
} }
// H5: // H5:
function handleCardMoreCommand(cmd: string, row: Record<string, any>) { function handleCardMoreCommand(cmd: string, row: Record<string, any>) {
if (cmd === 'addPayOrder') openAddPayOrder(row as any) if (cmd === 'addPayOrder') openAddPayOrder(row as any)
else if (cmd === 'complete') confirmComplete(row as any) else if (cmd === 'complete') confirmComplete(row as any)
else if (cmd === 'refund') openRefundOrder(row as any)
else if (cmd === 'submitGancao') confirmSubmitGancaoRecipel(row as any) else if (cmd === 'submitGancao') confirmSubmitGancaoRecipel(row as any)
else if (cmd === 'withdraw') confirmWithdraw(row as any) else if (cmd === 'withdraw') confirmWithdraw(row as any)
} }
@@ -3607,7 +3662,8 @@ function logActionText(act: string) {
gancao_submit: '甘草下单', gancao_submit: '甘草下单',
patch_rx_patient: '处方患者信息', patch_rx_patient: '处方患者信息',
update_amount: '修改订单金额', update_amount: '修改订单金额',
complete: '完成订单' complete: '完成订单',
refund: '退款'
} }
return m[act] || act return m[act] || act
} }
@@ -4553,7 +4609,6 @@ const completeOrderStatusOptions = [
{ value: 7, label: '进行中' }, { value: 7, label: '进行中' },
{ value: 8, label: '暂不制药' }, { value: 8, label: '暂不制药' },
{ value: 9, label: '拒收' }, { value: 9, label: '拒收' },
{ value: 10, label: '退款' },
{ value: 11, label: '保留药方' }, { value: 11, label: '保留药方' },
{ value: 12, label: '制药缓发' } { value: 12, label: '制药缓发' }
] as const ] as const
@@ -4586,6 +4641,53 @@ async function submitCompleteOrder() {
} }
} }
const refundOrderDialogVisible = ref(false)
const refundOrderId = ref(0)
const refundOrderSubmitting = ref(false)
const refundOrderFormRef = ref<FormInstance>()
const refundOrderForm = reactive({ reason: '' })
const refundOrderRules: FormRules = {
reason: [{ required: true, message: '请填写退款原因', trigger: 'blur' }]
}
function onRefundOrderDialogClosed() {
refundOrderId.value = 0
refundOrderForm.reason = ''
refundOrderFormRef.value?.clearValidate()
}
function openRefundOrder(row: { id: number }) {
refundOrderId.value = row.id
refundOrderForm.reason = ''
refundOrderDialogVisible.value = true
}
async function submitRefundOrder() {
const id = refundOrderId.value
if (!id) return
const valid = await refundOrderFormRef.value?.validate().catch(() => false)
if (!valid) return
refundOrderSubmitting.value = true
try {
await prescriptionOrderRefund({ id, reason: refundOrderForm.reason.trim() })
feedback.msgSuccess('退款成功')
refundOrderDialogVisible.value = false
getLists()
if (detailVisible.value && detailData.value?.id === id) {
try {
const r: any = await prescriptionOrderDetail({ id })
const nd = r?.data ?? r ?? null
if (nd) detailData.value = nd
await fetchLogs(id)
} catch { /* 静默 */ }
}
} catch {
/* 拦截器已提示 */
} finally {
refundOrderSubmitting.value = false
}
}
// //
const addPayOrderVisible = ref(false) const addPayOrderVisible = ref(false)
const addPayOrderSaving = ref(false) const addPayOrderSaving = ref(false)
@@ -345,6 +345,25 @@ class PrescriptionOrderController extends BaseAdminController
return $this->success('操作成功', $result); return $this->success('操作成功', $result);
} }
/**
* 业务订单退款(须填写原因;关联收款单标记已退款)
*/
public function refund()
{
$params = (new PrescriptionOrderValidate())->post()->goCheck('refund');
$result = PrescriptionOrderLogic::refund(
(int) $params['id'],
(string) ($params['reason'] ?? ''),
$this->adminId,
$this->adminInfo
);
if ($result === false) {
return $this->fail(PrescriptionOrderLogic::getError());
}
return $this->success('退款成功', $result);
}
/** /**
* 甘草药管家:处方下单(CTM_PREVIEW → CTM_SUBMIT_RECIPEL * 甘草药管家:处方下单(CTM_PREVIEW → CTM_SUBMIT_RECIPEL
* *
@@ -2534,8 +2534,8 @@ class PrescriptionOrderLogic
return $out; return $out;
} }
/** 完成订单时可选的履约终态:3=已完成,7-12=业务结案类状态 */ /** 完成订单时可选的履约终态:3=已完成,7-12 业务结案类状态(退款 10 走独立 refund 接口) */
private const COMPLETE_FULFILLMENT_TARGETS = [3, 7, 8, 9, 10, 11, 12]; private const COMPLETE_FULFILLMENT_TARGETS = [3, 7, 8, 9, 11, 12];
/** 完成时是否按「实付=关联支付单金额」重算并清空诊单医助(与 clearDiagnosisAssistantOnComplete 的终态判断一致) */ /** 完成时是否按「实付=关联支付单金额」重算并清空诊单医助(与 clearDiagnosisAssistantOnComplete 的终态判断一致) */
private const COMPLETE_FULFILLMENT_TERMINAL = [3, 8, 9, 10, 11, 12]; private const COMPLETE_FULFILLMENT_TERMINAL = [3, 8, 9, 10, 11, 12];
@@ -2592,13 +2592,7 @@ class PrescriptionOrderLogic
$order->fulfillment_status = $targetFulfillmentStatus; $order->fulfillment_status = $targetFulfillmentStatus;
} }
$refundedPayCount = 0;
try { try {
if ($targetFulfillmentStatus === 10 && $linkedPayOrderIds !== []) {
$refundedPayCount = Order::whereIn('id', $linkedPayOrderIds)
->whereNull('delete_time')
->update(['status' => 4]);
}
$order->save(); $order->save();
} catch (\Throwable $e) { } catch (\Throwable $e) {
self::$error = $e->getMessage(); self::$error = $e->getMessage();
@@ -2614,9 +2608,6 @@ class PrescriptionOrderLogic
$logLine = '订单完成,状态变更为「' . $label . '」,实付金额更新为 ¥' . $linkedPayPaidTotal . $unassignSummary; $logLine = '订单完成,状态变更为「' . $label . '」,实付金额更新为 ¥' . $linkedPayPaidTotal . $unassignSummary;
} else { } else {
$logLine = '订单处理完成,状态变更为「' . $label . '」' . $unassignSummary; $logLine = '订单处理完成,状态变更为「' . $label . '」' . $unassignSummary;
if ($targetFulfillmentStatus === 10 && $refundedPayCount > 0) {
$logLine .= ',关联支付单 ' . $refundedPayCount . ' 笔已标记为已退款';
}
} }
self::writeLog( self::writeLog(
$id, $id,
@@ -2634,6 +2625,79 @@ class PrescriptionOrderLogic
return $out; return $out;
} }
/**
* 业务订单退款:须填写原因;关联收款单标记为已退款,实付清零。
*
* @return array<string,mixed>|false
*/
public static function refund(int $id, string $reason, int $adminId, array $adminInfo)
{
self::$error = '';
$reason = trim($reason);
if ($reason === '') {
self::$error = '请填写退款原因';
return false;
}
$order = PrescriptionOrder::where('id', $id)->whereNull('delete_time')->find();
if (!$order) {
self::$error = '订单不存在';
return false;
}
if (!self::canAccessOrder($order, $adminId, $adminInfo)) {
self::$error = '无权限操作';
return false;
}
$curFs = (int) $order->fulfillment_status;
if ($curFs !== 5 && $curFs !== 6) {
self::$error = '仅「已发货」或「已签收」状态可退款';
return false;
}
if ((int) $order->payment_slip_audit_status !== 1) {
self::$error = '支付单审核尚未通过,不可退款';
return false;
}
$linkedPayOrderIds = self::linkedPayOrderIdList($id);
$refundedPayCount = 0;
try {
if ($linkedPayOrderIds !== []) {
$refundedPayCount = Order::whereIn('id', $linkedPayOrderIds)
->whereNull('delete_time')
->update(['status' => 4]);
}
$order->fulfillment_status = 10;
$order->paid = 0;
$order->save();
} catch (\Throwable $e) {
self::$error = $e->getMessage();
return false;
}
$unassignSummary = self::clearDiagnosisAssistantOnComplete((int) $order->diagnosis_id);
$logLine = '订单退款,状态变更为「退款」;原因:' . $reason;
if ($refundedPayCount > 0) {
$logLine .= ';关联支付单 ' . $refundedPayCount . ' 笔已标记为已退款';
}
$logLine .= ';已付总额清零' . $unassignSummary;
self::writeLog($id, $adminId, $adminInfo, 'refund', $logLine);
$out = $order->toArray();
self::maskInternalCostIfNeeded($out, $adminInfo);
self::maskRemarkExtraIfNeeded($out, $adminInfo);
self::attachLinkedPayOrders($out);
return $out;
}
public static function fulfillmentStatusLabel(int $fs): string public static function fulfillmentStatusLabel(int $fs): string
{ {
$m = [ $m = [
@@ -32,7 +32,8 @@ class PrescriptionOrderValidate extends BaseValidate
'remark_assistant' => 'max:500', 'remark_assistant' => 'max:500',
'action' => 'require|in:approve,reject', 'action' => 'require|in:approve,reject',
'remark' => 'max:500', 'remark' => 'max:500',
'fulfillment_status' => 'require|integer|in:3,7,8,9,10,11,12', 'fulfillment_status' => 'require|integer|in:3,7,8,9,11,12',
'reason' => 'require|max:500',
'patient_name' => 'require|max:50', 'patient_name' => 'require|max:50',
'phone' => 'require|max:20', 'phone' => 'require|max:20',
'phone_tail' => 'max:20|regex:/^\\d*$/', 'phone_tail' => 'max:20|regex:/^\\d*$/',
@@ -50,6 +51,7 @@ class PrescriptionOrderValidate extends BaseValidate
'patient_name.require' => '请输入患者姓名', 'patient_name.require' => '请输入患者姓名',
'phone.require' => '请输入手机号', 'phone.require' => '请输入手机号',
'phone_tail.regex' => '手机后四位仅支持数字', 'phone_tail.regex' => '手机后四位仅支持数字',
'reason.require' => '请填写退款原因',
]; ];
protected $scene = [ protected $scene = [
@@ -75,6 +77,7 @@ class PrescriptionOrderValidate extends BaseValidate
'linkPayOrder' => ['id', 'pay_order_id'], 'linkPayOrder' => ['id', 'pay_order_id'],
'requestCompletion' => ['id'], 'requestCompletion' => ['id'],
'complete' => ['id', 'fulfillment_status'], 'complete' => ['id', 'fulfillment_status'],
'refund' => ['id', 'reason'],
'submitGancaoRecipel' => ['id'], 'submitGancaoRecipel' => ['id'],
'previewGancaoRecipel' => ['id'], 'previewGancaoRecipel' => ['id'],
'patchPrescriptionPatient' => ['id', 'patient_name', 'phone'], 'patchPrescriptionPatient' => ['id', 'patient_name', 'phone'],