feat: 批量识别结果导出CSV
- 新增导出CSV文件功能,包含文本、判定结果、置信度、风险关键词 - 新增复制CSV内容到剪贴板功能 - CSV字段:文本、判定结果、置信度、垃圾概率、正常概率、风险关键词 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -79,5 +79,83 @@ Page({
|
|||||||
showCancel: false,
|
showCancel: false,
|
||||||
confirmText: '关闭'
|
confirmText: '关闭'
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
generateCSV() {
|
||||||
|
const items = this.data.items
|
||||||
|
if (!items.length) return ''
|
||||||
|
|
||||||
|
const headers = ['文本', '判定结果', '置信度', '垃圾概率', '正常概率', '风险关键词']
|
||||||
|
const rows = items.map((item) => {
|
||||||
|
const prediction = item.prediction === 'spam' ? '垃圾信息' : '正常信息'
|
||||||
|
const confidence = item.confidence_text || '0%'
|
||||||
|
const spamProb = this.formatPercent(item.spam_probability, 4)
|
||||||
|
const hamProb = this.formatPercent(item.ham_probability, 4)
|
||||||
|
const tokens = (item.reason_tokens || []).map((t) => t.token || t).join('; ')
|
||||||
|
// CSV 转义:文本中的逗号和换行需要处理
|
||||||
|
const text = (item.text || '').replace(/"/g, '""')
|
||||||
|
const tokensEscaped = tokens.replace(/"/g, '""')
|
||||||
|
return `"${text}","${prediction}","${confidence}","${spamProb}","${hamProb}","${tokensEscaped}"`
|
||||||
|
})
|
||||||
|
|
||||||
|
return [headers.join(','), ...rows].join('\n')
|
||||||
|
},
|
||||||
|
|
||||||
|
exportCSV() {
|
||||||
|
const items = this.data.items
|
||||||
|
if (!items.length) {
|
||||||
|
wx.showToast({ title: '暂无识别结果可导出', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const csvContent = this.generateCSV()
|
||||||
|
const timestamp = new Date().toISOString().slice(0, 19).replace(/[T:]/g, '-')
|
||||||
|
const filename = `batch_detect_${timestamp}.csv`
|
||||||
|
|
||||||
|
// 写入临时文件
|
||||||
|
const fs = wx.getFileSystemManager()
|
||||||
|
const tempPath = `${wx.env.USER_DATA_PATH}/${filename}`
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(tempPath, csvContent, 'utf8')
|
||||||
|
wx.showModal({
|
||||||
|
title: '导出成功',
|
||||||
|
content: `CSV文件已生成,是否打开查看?\n文件名:${filename}`,
|
||||||
|
confirmText: '打开',
|
||||||
|
cancelText: '关闭',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
wx.openDocument({
|
||||||
|
filePath: tempPath,
|
||||||
|
fileType: 'csv',
|
||||||
|
showMenu: true,
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('打开文件失败', err)
|
||||||
|
wx.showToast({ title: '打开失败,请检查文件管理器', icon: 'none' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error('写入文件失败', err)
|
||||||
|
wx.showToast({ title: '导出失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
copyCSVToClipboard() {
|
||||||
|
const items = this.data.items
|
||||||
|
if (!items.length) {
|
||||||
|
wx.showToast({ title: '暂无识别结果可复制', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const csvContent = this.generateCSV()
|
||||||
|
wx.setClipboardData({
|
||||||
|
data: csvContent,
|
||||||
|
success: () => {
|
||||||
|
wx.showToast({ title: 'CSV内容已复制到剪贴板', icon: 'success' })
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -56,6 +56,10 @@
|
|||||||
|
|
||||||
<view class="card fade-up fade-up-delay-3" wx:if="{{items.length}}">
|
<view class="card fade-up fade-up-delay-3" wx:if="{{items.length}}">
|
||||||
<view class="card-title">明细结果</view>
|
<view class="card-title">明细结果</view>
|
||||||
|
<view class="btn-row" style="margin-bottom: 12rpx;">
|
||||||
|
<button class="btn btn-ghost" bindtap="exportCSV">导出CSV文件</button>
|
||||||
|
<button class="btn btn-ghost" bindtap="copyCSVToClipboard">复制CSV内容</button>
|
||||||
|
</view>
|
||||||
<view class="list-item" wx:for="{{items}}" wx:key="index">
|
<view class="list-item" wx:for="{{items}}" wx:key="index">
|
||||||
<view class="item-title">{{item.text}}</view>
|
<view class="item-title">{{item.text}}</view>
|
||||||
<view class="row">
|
<view class="row">
|
||||||
|
|||||||
Reference in New Issue
Block a user