feat(ui): 所有列表页加CSV导出按钮(全量拉取+中文BOM兼容Excel)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Download } from '@element-plus/icons-vue'
|
||||
import { getSms } from '@/api'
|
||||
import type { Sms } from '@/api/types'
|
||||
import { formatTime, smsDirectionLabel } from '@/utils/format'
|
||||
import { buildSmsSections, readField } from '@/utils/smsFields'
|
||||
import { exportListToCsv } from '@/utils/csvExport'
|
||||
|
||||
const list = ref<Sms[]>([])
|
||||
const total = ref(0)
|
||||
@@ -57,6 +59,41 @@ function onDateChange(): void {
|
||||
load()
|
||||
}
|
||||
|
||||
// ---- header 导出:全量短信 CSV(含当前日期筛选) ----
|
||||
const exporting = ref(false)
|
||||
|
||||
async function exportSms(): Promise<void> {
|
||||
if (total.value === 0) {
|
||||
ElMessage.warning('当前列表为空,无数据可导出')
|
||||
return
|
||||
}
|
||||
exporting.value = true
|
||||
try {
|
||||
const result = await exportListToCsv({
|
||||
fetchPage: (p, pageSize) =>
|
||||
getSms({
|
||||
page: p,
|
||||
page_size: pageSize,
|
||||
start: dateRange.value?.[0]?.getTime(),
|
||||
end: dateRange.value?.[1]?.getTime(),
|
||||
}),
|
||||
columns: [
|
||||
{ key: 'create_time', label: '时间', format: (r) => (r.create_time ? formatTime(r.create_time) : '') },
|
||||
{ key: 'receive_name', label: '员工', format: (r) => r.receive_name ?? r.user_id ?? '' },
|
||||
{ key: 'customer_phone', label: '号码', format: (r) => r.customer_phone ?? r.sender ?? '' },
|
||||
{ key: 'direction', label: '方向', format: (r) => smsDirectionLabel(r.direction) },
|
||||
{ key: 'content', label: '内容', format: (r) => r.content ?? r.content_preview ?? '' },
|
||||
],
|
||||
filename: '短信记录',
|
||||
})
|
||||
ElMessage.success(`已导出 ${result.rows} 条`)
|
||||
} catch (e) {
|
||||
ElMessage.error(`导出失败:${(e as Error).message}`)
|
||||
} finally {
|
||||
exporting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a raw_json camelCase key off an SMS, preferring the structured
|
||||
* snake_case column when it is populated. The first argument is the row so the
|
||||
@@ -107,17 +144,22 @@ onMounted(load)
|
||||
<h2 style="margin: 0 0 4px; font-size: 16px">短信记录</h2>
|
||||
<span class="yk-sms__sub">共 {{ total }} 条</span>
|
||||
</div>
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
size="small"
|
||||
clearable
|
||||
style="width: 260px"
|
||||
@change="onDateChange"
|
||||
/>
|
||||
<div class="yk-sms__actions">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
size="small"
|
||||
clearable
|
||||
style="width: 260px"
|
||||
@change="onDateChange"
|
||||
/>
|
||||
<el-button size="small" :loading="exporting" @click="exportSms">
|
||||
<el-icon><Download /></el-icon> 导出
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
@@ -224,6 +266,13 @@ onMounted(load)
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.yk-sms__actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.yk-sms__pager {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user