diff --git a/admin/src/api/first_visit.ts b/admin/src/api/first_visit.ts index 9e677b3bf..ef3f66669 100644 --- a/admin/src/api/first_visit.ts +++ b/admin/src/api/first_visit.ts @@ -126,7 +126,7 @@ export function myPatientCancelAppointment(params: { id: number }) { } export interface FirstVisitConversionParams { - time_type: 'today' | 'week' | 'month' | 'quarter' | 'year' + time_type: 'today' | 'yesterday' | 'week' | 'month' | 'quarter' | 'year' dept_id?: number assistant_id?: number } @@ -173,14 +173,6 @@ export function wecomPromotionOverview() { return request.get({ url: '/firstvisit.wecomPromotion/overview' }) } -export function wecomPromotionAuthorizationUrl() { - return request.post({ url: '/firstvisit.wecomPromotion/authorizationUrl' }) -} - -export function wecomPromotionVerifyAccount(params: { id: number }) { - return request.post({ url: '/firstvisit.wecomPromotion/verifyAccount', params }) -} - export function wecomPromotionSavePool(params: Record) { return request.post({ url: '/firstvisit.wecomPromotion/savePool', params }) } @@ -193,6 +185,22 @@ export function wecomPromotionSaveLink(params: Record) { return request.post({ url: '/firstvisit.wecomPromotion/saveLink', params }) } +export function wecomPromotionCheckApiPermission() { + return request.post({ url: '/firstvisit.wecomPromotion/checkApiPermission' }) +} + +export function wecomPromotionSyncRemoteLinks(params: { pool_id: number }) { + return request.post({ url: '/firstvisit.wecomPromotion/syncRemoteLinks', params, timeout: 120000 }) +} + +export function wecomPromotionRemoteLinkDetail(params: { id: number }) { + return request.get({ url: '/firstvisit.wecomPromotion/remoteLinkDetail', params }) +} + +export function wecomPromotionDeleteRemoteLink(params: { id: number }) { + return request.post({ url: '/firstvisit.wecomPromotion/deleteRemoteLink', params }) +} + export function wecomPromotionToggleLink(params: { id: number; status: number }) { return request.post({ url: '/firstvisit.wecomPromotion/toggleLink', params }) } @@ -200,3 +208,61 @@ export function wecomPromotionToggleLink(params: { id: number; status: number }) export function wecomPromotionDeleteLink(params: { id: number }) { return request.post({ url: '/firstvisit.wecomPromotion/deleteLink', params }) } + +export type WecomPromotionCustomerChatStatus = '' | 'messaged' | 'silent' | 'unknown' + +export interface WecomPromotionCustomerStatsParams { + page_no: number + page_size: number + promotion_link_id?: number + userid?: string + chat_status?: 0 | 1 | 2 +} + +export interface WecomPromotionCustomerStatsSummary { + customer_count: number + messaged_customer_count: number + message_customer_rate: number + received_message_count: number + message_count_known_count: number +} + +export interface WecomPromotionCustomerStatRow { + id?: number + external_userid_masked?: string + customer_id_masked?: string + customer_name_masked?: string + link_id?: number | string + link_name?: string + member_id?: number + member_name?: string + department_name?: string + dept_name?: string + has_messaged?: boolean | number + chat_status?: 0 | 1 | 2 + message_count_known?: boolean | number + received_message_count?: number + last_synced_at?: string + last_message_at?: string +} + +export interface WecomPromotionCustomerStatsResult { + summary?: Partial + lists?: WecomPromotionCustomerStatRow[] + total?: number + link_options?: Array<{ id: number | string; name: string }> + member_options?: Array<{ id: number; name: string; department_name?: string; dept_name?: string }> + meta?: { last_synced_at?: string } +} + +/** 获客客户消息统计:服务端继续按当前角色和部门数据范围收窄。 */ +export function wecomPromotionCustomerStats(params: WecomPromotionCustomerStatsParams) { + return request.get( + { url: '/firstvisit.wecomPromotion/customerStatistics', params, timeout: 120000 }, + { ignoreCancelToken: true } + ) +} + +export function wecomPromotionSyncCustomers(params: { promotion_link_id?: number } = {}) { + return request.post({ url: '/firstvisit.wecomPromotion/syncCustomers', params, timeout: 120000 }) +} diff --git a/admin/src/views/first_visit/conversion/index.vue b/admin/src/views/first_visit/conversion/index.vue index c2e125965..27123a093 100644 --- a/admin/src/views/first_visit/conversion/index.vue +++ b/admin/src/views/first_visit/conversion/index.vue @@ -67,7 +67,7 @@

部门订单量占比

-

当前范围内审核通过的接诊诊单

+

双审通过,并排除取消、拒收及退款

单位:单
@@ -85,7 +85,7 @@

部门金额占比

-

与诊单金额指标保持同一审核口径

+

仅统计未取消、未拒收且未退款的有效金额

单位:元
@@ -231,6 +231,7 @@ const query = reactive({ time_type: 'today', dept_id const deptTreeProps = { value: 'id', label: 'name', children: 'children' } const timeOptions = [ { label: '今日', value: 'today' }, + { label: '昨天', value: 'yesterday' }, { label: '本周', value: 'week' }, { label: '本月', value: 'month' }, { label: '本季度', value: 'quarter' }, @@ -240,8 +241,8 @@ const metricCards: Array<{ key: string; label: string; type: MetricType; hint: s { key: 'add_fans_count', label: '加粉数', type: 'count', hint: '企微新增客户' }, { key: 'total_open_count', label: '开口数', type: 'count', hint: '来源于个人业绩录入' }, { key: 'interview_count', label: '面诊', type: 'count', hint: '已完成挂号' }, - { key: 'completed_order_count', label: '接诊诊单', type: 'count', hint: '处方与支付审核通过' }, - { key: 'completed_order_amount', label: '诊单金额', type: 'money', hint: '与接诊诊单同口径' }, + { key: 'completed_order_count', label: '接诊诊单', type: 'count', hint: '双审通过,排除取消、拒收及退款' }, + { key: 'completed_order_amount', label: '诊单金额', type: 'money', hint: '仅统计有效诊单金额' }, { key: 'avg_unit_price', label: '平均客单价', type: 'money', hint: '诊单金额 / 接诊诊单' }, { key: 'account_cost', label: '现金成本', type: 'money', hint: '当前范围内实际投放成本' }, { key: 'roi', label: 'ROI', type: 'ratio', hint: '诊单金额 / 投放成本' } diff --git a/admin/src/views/first_visit/my_patients/components/OrderPanel.vue b/admin/src/views/first_visit/my_patients/components/OrderPanel.vue index 469df149f..6b24d6d51 100644 --- a/admin/src/views/first_visit/my_patients/components/OrderPanel.vue +++ b/admin/src/views/first_visit/my_patients/components/OrderPanel.vue @@ -52,9 +52,9 @@
- 订单金额 + 有效订单金额 ¥{{ money(summary.amount) }} - 当前筛选 + 排除取消、拒收及退款
待审核 @@ -98,8 +98,11 @@
- - + +