This commit is contained in:
Your Name
2026-05-22 16:57:34 +08:00
parent 7e94c5dfd7
commit 7f7b8f0a64
+126 -18
View File
@@ -1156,9 +1156,19 @@
class="yeji-leadlines-dialog"
>
<template #header>
<div class="yeji-unassigned-dialog__head">
<span class="yeji-unassigned-dialog__title">进线数据明细</span>
<p class="yeji-unassigned-dialog__sub">{{ leadLinesSubtitle }}</p>
<div class="yeji-unassigned-dialog__head yeji-leadlines-dialog__head">
<div class="yeji-leadlines-dialog__head-main">
<span class="yeji-unassigned-dialog__title">进线数据明细</span>
<p class="yeji-unassigned-dialog__sub">{{ leadLinesSubtitle }}</p>
</div>
<el-button
size="small"
:loading="leadLinesExporting"
:disabled="leadLinesLoading || leadLinesExporting || leadLinesCount <= 0"
@click="exportLeadLines"
>
导出
</el-button>
</div>
</template>
<p v-if="leadLinesApiNote" class="yeji-unassigned-dialog__note">{{ leadLinesApiNote }}</p>
@@ -1630,6 +1640,7 @@ const revisitBreakdownMeta = ref<{ start: string; end: string; revisitSlot: numb
const leadLinesDialogVisible = ref(false)
const leadLinesLoading = ref(false)
const leadLinesExporting = ref(false)
const leadLinesSubtitle = ref('')
const leadLinesApiNote = ref('')
const leadLinesRows = ref<YejiLeadLineRow[]>([])
@@ -3128,28 +3139,79 @@ function onLeadCountCellClick(ev: MouseEvent, tb: YejiTable, row: YejiRow) {
void openLeadLinesDialog(tb, row)
}
const LEAD_LINES_EXPORT_COLUMNS: { key: keyof YejiLeadLineRow; label: string }[] = [
{ key: 'event_time_text', label: '进线时间' },
{ key: 'reception_admin_name', label: '接待' },
{ key: 'external_contact_name', label: '客户' },
{ key: 'external_userid', label: '外部联系人ID' },
{ key: 'user_id', label: '企微成员ID' },
{ key: 'state', label: '渠道参数' },
]
function buildLeadLinesRequestParams(
ctx: { tb: YejiTable; row: YejiRow },
page: number,
pageSize: number
): Record<string, string | number> {
const { tb, row } = ctx
const p: Record<string, string | number> = {
start_date: tb.start_date,
end_date: tb.end_date,
dept_id: row.dept_id,
page,
page_size: pageSize,
}
if (selectedDeptIds.value.length > 0) {
p.dept_ids = selectedDeptIds.value.join(',')
}
if (selectedChannel.value) {
p.channel_code = selectedChannel.value
}
return p
}
function escapeLeadLinesCsvCell(value: unknown): string {
const s = value == null ? '' : String(value)
if (/[",\n\r]/.test(s)) {
return `"${s.replace(/"/g, '""')}"`
}
return s
}
function buildLeadLinesCsv(rows: YejiLeadLineRow[]): string {
const header = LEAD_LINES_EXPORT_COLUMNS.map((c) => escapeLeadLinesCsvCell(c.label)).join(',')
const body = rows
.map((row) => LEAD_LINES_EXPORT_COLUMNS.map((c) => escapeLeadLinesCsvCell(row[c.key])).join(','))
.join('\n')
return `\uFEFF${header}\n${body}`
}
function downloadLeadLinesCsv(filename: string, content: string) {
const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' })
const url = URL.createObjectURL(blob)
const anchor = document.createElement('a')
anchor.href = url
anchor.download = filename
anchor.click()
URL.revokeObjectURL(url)
}
function buildLeadLinesExportFilename(ctx: { tb: YejiTable; row: YejiRow }): string {
const { tb, row } = ctx
const safeDept = (row.dept_name || `dept${row.dept_id}`).replace(/[\\/:*?"<>|]/g, '_')
return `进线数据明细_${safeDept}_${tb.start_date}_${tb.end_date}.csv`
}
async function fetchLeadLinesPage() {
const ctx = leadLinesContext.value
if (!ctx) {
return
}
const { tb, row } = ctx
leadLinesLoading.value = true
try {
const p: Record<string, string | number> = {
start_date: tb.start_date,
end_date: tb.end_date,
dept_id: row.dept_id,
page: leadLinesPage.value,
page_size: leadLinesPageSize.value,
}
if (selectedDeptIds.value.length > 0) {
p.dept_ids = selectedDeptIds.value.join(',')
}
if (selectedChannel.value) {
p.channel_code = selectedChannel.value
}
const res: any = await yejiStatsLeadLines(p as any)
const res: any = await yejiStatsLeadLines(
buildLeadLinesRequestParams(ctx, leadLinesPage.value, leadLinesPageSize.value) as any
)
leadLinesRows.value = Array.isArray(res?.lists) ? res.lists : []
leadLinesCount.value = Number(res?.count ?? 0)
leadLinesApiNote.value = typeof res?.note === 'string' ? res.note : ''
@@ -3188,6 +3250,40 @@ function onLeadLinesPageSizeChange(size: number) {
void fetchLeadLinesPage()
}
async function exportLeadLines() {
const ctx = leadLinesContext.value
if (!ctx || leadLinesExporting.value) {
return
}
leadLinesExporting.value = true
try {
const pageSize = 100
const firstRes: any = await yejiStatsLeadLines(buildLeadLinesRequestParams(ctx, 1, pageSize) as any)
const total = Number(firstRes?.count ?? 0)
if (total <= 0) {
ElMessage.warning('暂无进线明细可导出')
return
}
const allRows: YejiLeadLineRow[] = Array.isArray(firstRes?.lists) ? [...firstRes.lists] : []
const totalPages = Math.ceil(total / pageSize)
for (let page = 2; page <= totalPages; page++) {
const res: any = await yejiStatsLeadLines(buildLeadLinesRequestParams(ctx, page, pageSize) as any)
const lists = Array.isArray(res?.lists) ? res.lists : []
allRows.push(...lists)
}
downloadLeadLinesCsv(buildLeadLinesExportFilename(ctx), buildLeadLinesCsv(allRows))
ElMessage.success(`已导出 ${allRows.length} 条进线明细`)
} catch (e: unknown) {
if (axios.isCancel(e)) {
return
}
const any = e as any
ElMessage.error(any?.msg || any?.message || '导出进线明细失败')
} finally {
leadLinesExporting.value = false
}
}
function appointmentLinesIndexMethod(index: number) {
return (appointmentLinesPage.value - 1) * appointmentLinesPageSize.value + index + 1
}
@@ -4914,6 +5010,18 @@ tbody tr.total-row:hover .yeji-lead-cell--link {
color: color-mix(in srgb, var(--yj-brand, #2563eb) 88%, #000);
}
.yeji-leadlines-dialog__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
}
.yeji-leadlines-dialog__head-main {
flex: 1;
min-width: 0;
}
.yeji-leadlines-dialog__pager {
margin-top: 14px;
display: flex;