diff --git a/admin/src/api/first_visit.ts b/admin/src/api/first_visit.ts index adb5f43f6..68452db28 100644 --- a/admin/src/api/first_visit.ts +++ b/admin/src/api/first_visit.ts @@ -168,11 +168,12 @@ export interface FirstVisitConversionFanDetailRow { wecom_userid: string wecom_staff_name: string add_time: string | null - is_deleted: boolean - delete_time: string | null + is_deleted?: boolean + delete_time?: string | null } -export interface FirstVisitConversionFansDetailResult { +export interface FirstVisitConversionFansDetailResult { + can_view_deleted_fans?: boolean lists?: FirstVisitConversionFanDetailRow[] rows?: FirstVisitConversionFanDetailRow[] total?: number @@ -237,6 +238,36 @@ export function wecomPromotionOverview() { export function wecomPromotionSavePool(params: Record) { return request.post({ url: '/firstvisit.wecomPromotion/savePool', params, timeout: 120000 }) } + +export interface WecomPromotionTagGroup { + group_id: string + group_name: string + tag: Array<{ id: string; name: string }> +} + +export function wecomPromotionTagOptions() { + return request.get<{ tag_groups: WecomPromotionTagGroup[] }>({ + url: '/firstvisit.wecomPromotion/tagOptions' + }) +} + +export function wecomPromotionCreateTag(params: { name: string }) { + return request.post<{ tag: { id: string; name: string }; group_id: string; group_name: string; reused: boolean }>({ + url: '/firstvisit.wecomPromotion/createTag', params, timeout: 30000 + }, { ignoreCancelToken: true }) +} + +export function wecomPromotionUploadWelcomeMedia(file: File, type: 'image' | 'video' | 'file') { + const data = new FormData() + data.append('file', file) + data.append('type', type) + return request.post<{ asset_id: string; name: string; type: string }>({ + url: '/firstvisit.wecomPromotion/uploadWelcomeMedia', + data, + headers: { 'Content-Type': 'multipart/form-data' }, + timeout: 120000 + }, { ignoreCancelToken: true }) +} export function wecomPromotionSaveWidget(params: Record) { return request.post({ url: '/firstvisit.wecomPromotion/saveWidget', params }) diff --git a/admin/src/api/tcm.ts b/admin/src/api/tcm.ts index fb32bc976..4b5b9d99b 100644 --- a/admin/src/api/tcm.ts +++ b/admin/src/api/tcm.ts @@ -578,6 +578,11 @@ export function prescriptionOrderAddLog(params: { return request.post({ url: '/tcm.prescriptionOrder/addLog', params }) } +/** 移除单笔收款关联,总金额不变,同步更新已付金额和需代收 */ +export function prescriptionOrderUnlinkPayOrder(params: { id: number; pay_order_id: number }) { + return request.post({ url: '/tcm.prescriptionOrder/unlinkPayOrder', params }) +} + /** 修改订单金额 */ export function prescriptionOrderUpdateAmount(params: { id: number; amount: number }) { return request.post({ url: '/tcm.prescriptionOrder/updateAmount', params }) diff --git a/admin/src/views/consumer/prescription/components/PrescriptionOrderDetailDrawer.vue b/admin/src/views/consumer/prescription/components/PrescriptionOrderDetailDrawer.vue index 35c628472..d6a5e0b5a 100644 --- a/admin/src/views/consumer/prescription/components/PrescriptionOrderDetailDrawer.vue +++ b/admin/src/views/consumer/prescription/components/PrescriptionOrderDetailDrawer.vue @@ -541,6 +541,19 @@ {{ formatOrderTime(row.create_time) }} + + + @@ -1014,6 +1027,7 @@ import { prescriptionOrderLogisticsTrace, prescriptionOrderLogisticsJdUpdate, prescriptionOrderPaidPayOrders, + prescriptionOrderUnlinkPayOrder, prescriptionOrderPatchPrescriptionUsage } from '@/api/tcm' import { getDictData } from '@/api/app' @@ -1107,6 +1121,38 @@ const detailLoading = ref(false) const detailData = ref | null>(null) const detailLogs = ref([]) const detailUnlinkedPayOrders = ref([]) +const unlinkPayOrderId = ref(null) +const canUnlinkPayOrder = computed(() => + !props.readonly && canUpdateAmount(detailData.value) && hasPerm('tcm.prescriptionOrder/unlinkPayOrder') +) + +async function confirmUnlinkPayOrder(row: { id: number; order_no?: string; amount: number | string; status: number | string }) { + const orderId = Number(detailData.value?.id) + const payOrderId = Number(row.id) + if (!canUnlinkPayOrder.value || !orderId || !payOrderId || unlinkPayOrderId.value !== null) return + if (![2, 5].includes(Number(row.status))) return + + unlinkPayOrderId.value = payOrderId + try { + await feedback.confirm( + `确定移除收款记录「${row.order_no || '#' + payOrderId}」(¥${formatMoney(row.amount)})?` + + '仅解除关联,原收款记录保留;订单总金额不变,已付金额与需代收金额按剩余关联收款重新计算。' + ) + if (!detailVisible.value || Number(detailData.value?.id) !== orderId) return + const res: any = await prescriptionOrderUnlinkPayOrder({ id: orderId, pay_order_id: payOrderId }) + const updated = res?.data ?? res + if (Number(detailData.value?.id) === orderId && Number(updated?.id) === orderId) { + Object.assign(detailData.value!, updated) + } + feedback.msgSuccess('收款关联已移除,金额已同步更新') + emit('detail-changed') + await refreshIfCurrent(orderId) + } catch { + /* 取消不提交;接口错误由拦截器提示,保留当前收款记录 */ + } finally { + unlinkPayOrderId.value = null + } +} const detailPrescription = computed(() => { const p = detailData.value?.prescription diff --git a/admin/src/views/consumer/prescription/components/prescription-order-utils.ts b/admin/src/views/consumer/prescription/components/prescription-order-utils.ts index 7b9342690..e2ca64c0c 100644 --- a/admin/src/views/consumer/prescription/components/prescription-order-utils.ts +++ b/admin/src/views/consumer/prescription/components/prescription-order-utils.ts @@ -168,6 +168,7 @@ export function logActionText(act: string) { ship: '确认发货', withdraw: '撤销', link_pay_order: '关联支付单', + unlink_pay_order: '移除收款关联', completion_request: '完单申请', auto_complete: '自动完成', revoke_rx_audit: '撤回处方审核', diff --git a/admin/src/views/first_visit/conversion/index.vue b/admin/src/views/first_visit/conversion/index.vue index 33477569b..f21aec6e6 100644 --- a/admin/src/views/first_visit/conversion/index.vue +++ b/admin/src/views/first_visit/conversion/index.vue @@ -108,7 +108,7 @@ (-{{ formatNumber(dashboard.summary.deleted_fans_count) }}) - {{ metric.hint }} + {{ metric.hint }} @@ -154,7 +154,7 @@

明细数据列表

-

展开部门可查看人员明细;加粉=总进线=区间新增加粉(按员工+客户去重,包含区间内添加后已删客户,剔除继承客户、扫一扫/搜手机号/名片分享添加及区间前已加过的重加),(-N)表示加粉总数中已删除;挂号=已支付且实收低于 10 元的订单,预约=有效预约记录;开口率=开口/加粉,挂号率=挂号/加粉,面诊率=面诊/挂号(看挂号后流失),预约率=面诊/预约(看预约后未面诊),面诊接诊率=接诊诊单/面诊,接诊率=接诊诊单/总进线

+

展开部门可查看人员明细;加粉=总进线=区间新增加粉(按员工+客户去重,包含区间内添加后已删客户,剔除继承客户、扫一扫/搜手机号/名片分享添加及区间前已加过的重加);挂号=已支付且实收低于 10 元的订单,预约=有效预约记录;开口率=开口/加粉,挂号率=挂号/加粉,面诊率=面诊/挂号(看挂号后流失),预约率=面诊/预约(看预约后未面诊),面诊接诊率=接诊诊单/面诊,接诊率=接诊诊单/总进线

{{ dashboard.rows.length }} 个顶层节点
@@ -339,7 +339,7 @@ - +