feat(staffchat): 入口改造(StaffList/CustomerList微信行切新页)+useStaffProfile缓存+StatsPane固定列/金额格式+vitest依赖化

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
浅念
2026-08-25 19:56:22 +08:00
parent 4c3bc3ee93
commit 50635d0df8
9 changed files with 489 additions and 46 deletions
+15 -18
View File
@@ -4,11 +4,12 @@
根元素必须保持单 div——父组件 StaffChat 靠 attrs fallthrough 挂
sc-root__pane 列宽。 -->
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { computed, ref, toRef, watch } from 'vue'
import { useRouter } from 'vue-router'
import { getAccounts, getStaffStatsCounts, getStaffStatsDetail } from '@/api'
import { getStaffStatsCounts, getStaffStatsDetail } from '@/api'
import type { StaffStatsKind } from '@/api'
import type { Account, StaffStatsCounts } from '@/api/types'
import type { StaffStatsCounts } from '@/api/types'
import { useStaffProfile } from '@/composables/useStaffProfile'
import {
callDirectionLabel,
formatCallDuration,
@@ -74,6 +75,8 @@ interface Col {
label: string
/** link=客户/群点击跳聊天;staff=员工微信(资料卡);缺省按 prop 特判文本。 */
kind?: 'link' | 'staff'
/** 操作列固定右侧(对齐云客,防横向滚动时被挤出视口)。 */
fixed?: 'right'
}
const KIND_COLS: Partial<Record<KindKey, Col[]>> = {
@@ -94,7 +97,7 @@ const KIND_COLS: Partial<Record<KindKey, Col[]>> = {
{ prop: 'staff', label: '员工微信', kind: 'staff' },
{ prop: 'amount', label: '金额' },
{ prop: 'time', label: '发生时间' },
{ prop: '_action', label: '操作' },
{ prop: '_action', label: '操作', fixed: 'right' },
],
del_customers: [
{ prop: 'nickname', label: '客户微信', kind: 'link' },
@@ -137,18 +140,10 @@ function windowParams(): { start?: number; end?: number } {
}
// ---- 员工微信列数据: 后端明细行无 staff 字段——数据本就是该员工的统计,
// 用资料卡信息渲染(nickname/avatar,与 StaffChat 同款 getAccounts 手法) ----
// 用资料卡信息渲染(nickname/avatar;useStaffProfile 与 StaffChat 资料卡
// 共享模块级缓存,全 app 一份请求) ----
const profile = ref<Account | null>(null)
async function loadProfile(): Promise<void> {
try {
const res = await getAccounts({ channel: 'wechat', page: 1, page_size: 100 })
profile.value = res.items.find(a => a.account_id === props.ownerId) ?? null
} catch {
profile.value = null // 后端未起: 员工列退化为 ownerId 兜底
}
}
const { profile } = useStaffProfile(toRef(props, 'ownerId'))
const staffName = computed(() => profile.value?.nickname || props.ownerId)
@@ -231,7 +226,6 @@ watch(
() => props.ownerId,
() => {
dateRange.value = defaultRange()
void loadProfile()
void loadCounts()
void loadDetail()
},
@@ -431,7 +425,8 @@ function playRecord(row: StatsRow): void {
v-for="col in activeCols"
:key="col.prop"
:label="col.label"
:min-width="col.kind === 'link' || col.kind === 'staff' ? 150 : col.prop === '_action' ? 110 : 110"
:fixed="col.fixed"
:min-width="col.kind === 'link' || col.kind === 'staff' ? 150 : 110"
>
<template #default="{ row }">
<!-- link 列: 客户/群 → 跳本员工聊天窗 -->
@@ -473,7 +468,9 @@ function playRecord(row: StatsRow): void {
<!-- 时间/金额/操作列特判,其余纯文本 -->
<template v-else-if="col.prop === 'time'">{{ formatTime(row.time) }}</template>
<template v-else-if="col.prop === 'amount'">{{ row.amount ?? '—' }}</template>
<template v-else-if="col.prop === 'amount'">{{
row.amount != null ? '¥' + row.amount.toFixed(2) : '—'
}}</template>
<template v-else-if="col.prop === '_action'">
<el-button link type="primary" size="small" @click="goChat(row, true)">
查看聊天记录