Commit 36594141 by 李宁

1

1 parent 32c48fee
...@@ -121,18 +121,19 @@ const fetchDetail = async () => { ...@@ -121,18 +121,19 @@ const fetchDetail = async () => {
} }
} }
// 辅助函数:格式化时长 // 辅助函数:格式化时长(参数单位:毫秒)
const formatDuration = (seconds: number): string => { const formatDuration = (milliseconds: number): string => {
if (!seconds) return '0秒' if (!milliseconds) return '0秒'
const hours = Math.floor(seconds / 3600) const totalSeconds = Math.floor(milliseconds / 1000) // 将毫秒转换为秒
const minutes = Math.floor((seconds % 3600) / 60) const hours = Math.floor(totalSeconds / 3600)
const secs = seconds % 60 const minutes = Math.floor((totalSeconds % 3600) / 60)
const secs = totalSeconds % 60
const parts = [] const parts = []
if (hours > 0) parts.push(`${hours}小时`) if (hours > 0) parts.push(`${hours}小时`)
if (minutes > 0) parts.push(`${minutes}分`) if (minutes > 0) parts.push(`${minutes}分`)
if (secs > 0 || parts.length === 0) parts.push(`${secs}秒`) if (secs > 0 || parts.length === 0) parts.push(`${secs}秒`)
return parts.join('') return parts.join('')
} }
...@@ -157,39 +158,39 @@ const showEnvImages = () => { ...@@ -157,39 +158,39 @@ const showEnvImages = () => {
} }
} }
const playVideo = async (recordFile: string) => { const playVideo = (recordFile: string) => {
// 检查 recordFile 是否存在 // 检查 recordFile 是否存在
if (!recordFile) { if (!recordFile) {
ElMessage.warning('暂不支持播放') ElMessage.warning('暂不支持播放')
return return
} }
try { // 直接播放视频
// 调用接口获取视频播放地址 currentVideoUrl.value = recordFile
const response = await qualityApi.getVideoUrl({ videoDialogVisible.value = true
url: recordFile
}) as ApiResponse<any>
if (response.code === 200 || response.code === 0) {
// 假设接口返回的视频地址在 data 字段中
const videoUrl = response.data || recordFile
currentVideoUrl.value = videoUrl
videoDialogVisible.value = true
}
} catch (error) {
console.error('获取视频地址失败:', error)
// 错误提示已在 request.ts 中统一处理
}
} }
const downloadVideo = (url: string) => { const downloadVideo = async (url: string) => {
try {
// 获取视频文件
const response = await fetch(url)
const blob = await response.blob()
// 创建下载链接
const blobUrl = window.URL.createObjectURL(blob)
const link = document.createElement('a') const link = document.createElement('a')
link.href = url link.href = blobUrl
link.target = '_blank' link.download = `video_${Date.now()}.mp4`
link.download = 'video.mp4'
document.body.appendChild(link) document.body.appendChild(link)
link.click() link.click()
document.body.removeChild(link) document.body.removeChild(link)
window.URL.revokeObjectURL(blobUrl)
ElMessage.success('下载成功')
} catch (error) {
console.error('下载视频失败:', error)
ElMessage.error('下载失败')
}
} }
const downloadAllVideos = async () => { const downloadAllVideos = async () => {
......
...@@ -106,7 +106,7 @@ const fetchData = async () => { ...@@ -106,7 +106,7 @@ const fetchData = async () => {
params.cheatNumType = queryForm.envAbnormalCountMin params.cheatNumType = queryForm.envAbnormalCountMin
} }
// 是否异常 -> isCheat // 疑似作弊 -> isCheat
if (queryForm.isAbnormal === 'abnormal') { if (queryForm.isAbnormal === 'abnormal') {
params.isCheat = 1 params.isCheat = 1
} else if (queryForm.isAbnormal === 'normal') { } else if (queryForm.isAbnormal === 'normal') {
...@@ -117,28 +117,34 @@ const fetchData = async () => { ...@@ -117,28 +117,34 @@ const fetchData = async () => {
if (response.code === 200 || response.code === 0) { if (response.code === 200 || response.code === 0) {
const data = response.data || { records: [], total: 0 } const data = response.data || { records: [], total: 0 }
// 将后端数据映射到前端 Order 类型 // 将后端数据映射到前端 Order 类型
tableData.value = (data.records || []).map((item: any) => ({ tableData.value = (data.records || []).map((item: any) => {
id: item.applyId, // 使用 applyId 作为唯一 id // 根据 areaType 在 cities 中匹配对应的 name
applyId: item.applyId, const cityInfo = cities.find(c => c.code === item.areaType)
workerId: item.campaignId, const cityName = cityInfo ? cityInfo.name : item.areaName || ''
businessAccount: item.accNbr,
orderIds: [], // 工单ID列表需要单独查询或从其他字段获取 return {
city: item.areaName, id: item.applyId, // 使用 applyId 作为唯一 id
status: item.checkStatusDesc || getStatusText(item.checkStatus), applyId: item.applyId,
cannotQcReason: item.failReason || '', workerId: item.campaignId,
startTime: item.startTime || '', businessAccount: item.accNbr,
endTime: item.endTime || '', orderIds: [], // 工单ID列表需要单独查询或从其他字段获取
noPhotoCount: item.noShowNum || 0, city: cityName,
manualInputCount: item.manualInputNum || 0, status: item.checkStatusDesc || getStatusText(item.checkStatus),
envAbnormalCount: item.cheatNum || 0, cannotQcReason: item.failReason || '',
isCheating: item.isCheat === 1, startTime: item.startTime || '',
cheatingReason: '', endTime: item.endTime || '',
cheatingRemark: '', noPhotoCount: item.noShowNum || 0,
cheatingTime: '' manualInputCount: item.manualInputNum || 0,
})) envAbnormalCount: item.cheatNum || 0,
isCheating: item.isCheat === 1,
cheatingReason: '',
cheatingRemark: '',
cheatingTime: ''
}
})
total.value = data.total || 0 total.value = data.total || 0
} }
} catch (error) { } catch (error) {
...@@ -305,20 +311,26 @@ const openIdsDialog = async (row: Order) => { ...@@ -305,20 +311,26 @@ const openIdsDialog = async (row: Order) => {
const openDetailsDialog = async (row: Order, type: 'noPhoto' | 'manual' | 'env') => { const openDetailsDialog = async (row: Order, type: 'noPhoto' | 'manual' | 'env') => {
currentOrder.value = row currentOrder.value = row
detailsType.value = type detailsType.value = type
let detailType = 0
// 设置标题 // 设置标题
if (type === 'noPhoto') { if (type === 'noPhoto') {
detailsTitle.value = '无法拍摄明细' detailsTitle.value = '无法拍摄明细'
detailType = 1
} else if (type === 'manual') { } else if (type === 'manual') {
detailsTitle.value = '手动输入明细' detailsTitle.value = '手动输入明细'
detailType = 2
} else { } else {
detailsTitle.value = '环境异常明细' detailsTitle.value = '环境异常明细'
detailType = 3
} }
try { try {
// 调用接口获取流程明细列表 // 调用接口获取流程明细列表
const response = await qualityApi.getProcessDetailList({ const response = await qualityApi.getProcessDetailList({
applyId: row.applyId applyId: row.applyId,
detailType: detailType
}) as ApiResponse<any[]> }) as ApiResponse<any[]>
if (response.code === 200 || response.code === 0) { if (response.code === 200 || response.code === 0) {
...@@ -529,23 +541,30 @@ fetchData() ...@@ -529,23 +541,30 @@ fetchData()
start-placeholder="开始时间" start-placeholder="开始时间"
end-placeholder="结束时间" end-placeholder="结束时间"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
class="!w-80" class="!w-80"
/> />
</el-form-item> </el-form-item>
<el-form-item label="无法拍摄"> <el-form-item label="无法拍摄">
<el-input-number v-model="queryForm.noPhotoCountMin" :min="1" controls-position="right" class="!w-32" placeholder="请输入" /> <el-input-number v-model="queryForm.noPhotoCountMin" :min="1" controls-position="right" class="!w-32" placeholder="请输入">
<template #prefix></template>
</el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="手动输入"> <el-form-item label="手动输入">
<el-input-number v-model="queryForm.manualInputCountMin" :min="1" controls-position="right" class="!w-32" placeholder="请输入" /> <el-input-number v-model="queryForm.manualInputCountMin" :min="1" controls-position="right" class="!w-32" placeholder="请输入">
<template #prefix></template>
</el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="环境异常"> <el-form-item label="环境异常">
<el-input-number v-model="queryForm.envAbnormalCountMin" :min="1" controls-position="right" class="!w-32" placeholder="请输入" /> <el-input-number v-model="queryForm.envAbnormalCountMin" :min="1" controls-position="right" class="!w-32" placeholder="请输入">
<template #prefix></template>
</el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="是否异常"> <el-form-item label="疑似作弊">
<el-select v-model="queryForm.isAbnormal" placeholder="请选择" clearable class="!w-32"> <el-select v-model="queryForm.isAbnormal" placeholder="请选择" clearable class="!w-32">
<el-option label="全部" value="" /> <el-option label="全部" value="" />
<el-option label="异常" value="abnormal" /> <el-option label="" value="abnormal" />
<el-option label="正常" value="normal" /> <el-option label="" value="normal" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item class="ml-auto"> <el-form-item class="ml-auto">
...@@ -652,11 +671,11 @@ fetchData() ...@@ -652,11 +671,11 @@ fetchData()
</el-dialog> </el-dialog>
<!-- Dialog: Numeric Details (NoPhoto/Manual/Env) --> <!-- Dialog: Numeric Details (NoPhoto/Manual/Env) -->
<el-dialog v-model="dialogVisible.details" :title="detailsTitle" width="600px"> <el-dialog v-model="dialogVisible.details" :title="detailsTitle" width="800px">
<el-table :data="detailsData" border stripe> <el-table :data="detailsData" border stripe>
<el-table-column type="index" label="序号" width="60" align="center" /> <el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="process" label="流程" /> <el-table-column prop="process" label="流程" />
<el-table-column prop="reason" label="原因" v-if="detailsType !== 'manual'" /> <el-table-column prop="reason" label="原因" v-if="detailsType !== 'manual'" show-overflow-tooltip/>
<el-table-column prop="time" label="提交时间" /> <el-table-column prop="time" label="提交时间" />
</el-table> </el-table>
</el-dialog> </el-dialog>
......
...@@ -286,6 +286,7 @@ onMounted(() => { ...@@ -286,6 +286,7 @@ onMounted(() => {
range-separator="至" range-separator="至"
start-placeholder="开始时间" start-placeholder="开始时间"
end-placeholder="结束时间" end-placeholder="结束时间"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
...@@ -350,6 +351,7 @@ onMounted(() => { ...@@ -350,6 +351,7 @@ onMounted(() => {
range-separator="至" range-separator="至"
start-placeholder="开始时间" start-placeholder="开始时间"
end-placeholder="结束时间" end-placeholder="结束时间"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
style="width: 100%" style="width: 100%"
/> />
</el-form-item> </el-form-item>
...@@ -387,6 +389,7 @@ onMounted(() => { ...@@ -387,6 +389,7 @@ onMounted(() => {
range-separator="至" range-separator="至"
start-placeholder="开始时间" start-placeholder="开始时间"
end-placeholder="结束时间" end-placeholder="结束时间"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
/> />
</el-form-item> </el-form-item>
<el-form-item label="Apply ID"> <el-form-item label="Apply ID">
......
...@@ -266,6 +266,7 @@ onMounted(() => { ...@@ -266,6 +266,7 @@ onMounted(() => {
range-separator="至" range-separator="至"
start-placeholder="开始时间" start-placeholder="开始时间"
end-placeholder="结束时间" end-placeholder="结束时间"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
/> />
</el-form-item> </el-form-item>
<el-form-item label="地区"> <el-form-item label="地区">
......
...@@ -510,6 +510,7 @@ onMounted(() => { ...@@ -510,6 +510,7 @@ onMounted(() => {
range-separator="至" range-separator="至"
start-placeholder="开始时间" start-placeholder="开始时间"
end-placeholder="结束时间" end-placeholder="结束时间"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
/> />
</el-form-item> </el-form-item>
<el-form-item label="地区"> <el-form-item label="地区">
...@@ -525,41 +526,45 @@ onMounted(() => { ...@@ -525,41 +526,45 @@ onMounted(() => {
</el-card> </el-card>
<!-- Cards --> <!-- Cards -->
<div class="grid grid-cols-2 gap-4 mb-4"> <div class="grid grid-cols-4 gap-4 mb-4">
<el-card shadow="never" class="bg-blue-50"> <el-card shadow="never" class="bg-blue-50">
<div class="text-gray-500 mb-2">工单总数</div> <div class="text-gray-500 mb-2">工单总数</div>
<div class="text-3xl font-bold mb-3">{{ stats.totalOrders }}</div> <div class="text-3xl font-bold text-blue-600">{{ stats.totalOrders }}</div>
<div class="flex justify-between text-sm">
<span class="text-gray-600">已完成工单数</span>
<span class="font-bold text-blue-600">{{ stats.finishOrders }}</span>
</div>
</el-card> </el-card>
<el-card shadow="never" class="bg-blue-50">
<div class="text-gray-500 mb-2">已完成工单数</div>
<div class="text-3xl font-bold text-blue-600">{{ stats.finishOrders }}</div>
</el-card>
<el-card shadow="never" class="bg-green-50"> <el-card shadow="never" class="bg-green-50">
<div class="text-gray-500 mb-2">质检工单总数</div> <div class="text-gray-500 mb-2">质检工单总数</div>
<div class="text-3xl font-bold text-green-600 mb-3">{{ stats.resultCount }}</div> <div class="text-3xl font-bold text-green-600">{{ stats.resultCount }}</div>
<div class="flex justify-between text-sm">
<span class="text-gray-600">已完成质检工单数</span>
<span class="font-bold text-green-600">{{ stats.finishResult }}</span>
</div>
</el-card> </el-card>
<el-card shadow="never" class="bg-green-50">
<div class="text-gray-500 mb-2">已完成质检工单数</div>
<div class="text-3xl font-bold text-green-600">{{ stats.finishResult }}</div>
</el-card>
<el-card shadow="never" class="bg-orange-50"> <el-card shadow="never" class="bg-orange-50">
<div class="text-gray-500 mb-2">投诉工单总数</div> <div class="text-gray-500 mb-2">投诉工单总数</div>
<div class="text-3xl font-bold text-orange-600 mb-3">{{ stats.complaintTotal }}</div> <div class="text-3xl font-bold text-orange-600">{{ stats.complaintTotal }}</div>
<div class="flex justify-between text-sm">
<span class="text-gray-600">已完成投诉工单数</span>
<span class="font-bold text-orange-600">{{ stats.complaintFinish }}</span>
</div>
</el-card> </el-card>
<el-card shadow="never" class="bg-orange-50">
<div class="text-gray-500 mb-2">已完成投诉工单数</div>
<div class="text-3xl font-bold text-orange-600">{{ stats.complaintFinish }}</div>
</el-card>
<el-card shadow="never" class="bg-purple-50"> <el-card shadow="never" class="bg-purple-50">
<div class="text-gray-500 mb-2">无法质检数</div> <div class="text-gray-500 mb-2">无法质检数</div>
<div class="text-3xl font-bold text-purple-600 mb-3">{{ stats.noShowCount }}</div> <div class="text-3xl font-bold text-purple-600">{{ stats.noShowCount }}</div>
<div class="flex justify-between text-sm"> </el-card>
<span class="text-gray-600">平均耗时</span>
<span class="font-bold text-purple-600">{{ stats.averageDuration }}</span> <el-card shadow="never" class="bg-purple-50">
</div> <div class="text-gray-500 mb-2">平均耗时(秒)</div>
<div class="text-3xl font-bold text-purple-600">{{ stats.averageDuration }}</div>
</el-card> </el-card>
</div> </div>
......
...@@ -387,6 +387,7 @@ onMounted(() => { ...@@ -387,6 +387,7 @@ onMounted(() => {
range-separator="至" range-separator="至"
start-placeholder="开始时间" start-placeholder="开始时间"
end-placeholder="结束时间" end-placeholder="结束时间"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
...@@ -418,7 +419,7 @@ onMounted(() => { ...@@ -418,7 +419,7 @@ onMounted(() => {
</el-card> </el-card>
<el-card shadow="never" class="bg-indigo-50"> <el-card shadow="never" class="bg-indigo-50">
<div class="text-gray-500 mb-2">平均耗时</div> <div class="text-gray-500 mb-2">环节平均耗时</div>
<div class="text-4xl font-bold text-indigo-600 mb-3">{{ summary.avgDuration }}<span class="text-xl">s</span></div> <div class="text-4xl font-bold text-indigo-600 mb-3">{{ summary.avgDuration }}<span class="text-xl">s</span></div>
</el-card> </el-card>
...@@ -489,7 +490,7 @@ onMounted(() => { ...@@ -489,7 +490,7 @@ onMounted(() => {
<template #default="{ row }">{{ row.autoRecognizeRate }}%</template> <template #default="{ row }">{{ row.autoRecognizeRate }}%</template>
</el-table-column> </el-table-column>
<el-table-column prop="avgRecognizeTimes" label="平均识别次数" sortable /> <el-table-column prop="avgRecognizeTimes" label="平均识别次数" sortable />
<el-table-column prop="avgDuration" label="平均耗时" sortable> <el-table-column prop="avgDuration" label="环节平均耗时" sortable>
<template #default="{ row }">{{ row.avgDuration }}s</template> <template #default="{ row }">{{ row.avgDuration }}s</template>
</el-table-column> </el-table-column>
</el-table> </el-table>
...@@ -526,7 +527,7 @@ onMounted(() => { ...@@ -526,7 +527,7 @@ onMounted(() => {
<template #default="{ row }">{{ row.autoRecognizeRate }}%</template> <template #default="{ row }">{{ row.autoRecognizeRate }}%</template>
</el-table-column> </el-table-column>
<el-table-column prop="avgRecognizeTimes" label="平均识别次数" sortable /> <el-table-column prop="avgRecognizeTimes" label="平均识别次数" sortable />
<el-table-column prop="avgDuration" label="平均耗时" sortable> <el-table-column prop="avgDuration" label="环节平均耗时" sortable>
<template #default="{ row }">{{ row.avgDuration }}s</template> <template #default="{ row }">{{ row.avgDuration }}s</template>
</el-table-column> </el-table-column>
</el-table> </el-table>
......
...@@ -374,7 +374,7 @@ ...@@ -374,7 +374,7 @@
<script src="js/vue.min.js"></script> <script src="js/vue.min.js"></script>
<script src="js/vant.min.js"></script> <script src="js/vant.min.js"></script>
<script src="js/demo.js?90910"></script> <script src="js/demo.js?121222"></script>
</body> </body>
</html> </html>
\ No newline at end of file \ No newline at end of file
...@@ -390,7 +390,7 @@ function snInputShow() { ...@@ -390,7 +390,7 @@ function snInputShow() {
} }
$('#snTimeOut').click(function () { $('#snTimeOut').click(function () {
if (snTimeNum > 0) { if (snTimeNum>0 && window.timeOutFlag) {
return return
} }
...@@ -398,6 +398,11 @@ $('#snTimeOut').click(function () { ...@@ -398,6 +398,11 @@ $('#snTimeOut').click(function () {
snInputShow() snInputShow()
}) })
function cheatShowMethod() { function cheatShowMethod() {
if(!window.timeOutFlag){
$('#cheatClose').text('重新拍摄')
return
}
$('#cheatClose').addClass('gray') $('#cheatClose').addClass('gray')
cheatTimeStr = setTimeout(() => { cheatTimeStr = setTimeout(() => {
cheatTimeNum -= 1 cheatTimeNum -= 1
...@@ -412,6 +417,10 @@ function cheatShowMethod() { ...@@ -412,6 +417,10 @@ function cheatShowMethod() {
}, 1000) }, 1000)
} }
function noShootShowMethod() { function noShootShowMethod() {
if(!window.timeOutFlag){
return
}
$('#noShootTime').addClass('grayNoShoot') $('#noShootTime').addClass('grayNoShoot')
$('#noShootTime').removeClass('clickButt') $('#noShootTime').removeClass('clickButt')
if (!noShootTimeStr) { if (!noShootTimeStr) {
...@@ -432,6 +441,11 @@ function noShootShowMethod() { ...@@ -432,6 +441,11 @@ function noShootShowMethod() {
}, 1000) }, 1000)
} }
function snShowMethod() { function snShowMethod() {
if(!window.timeOutFlag){
$('#snTimeOut').text('手动输入')
return
}
$('#snTimeOut').addClass('graySn') $('#snTimeOut').addClass('graySn')
snTimeStr = setTimeout(() => { snTimeStr = setTimeout(() => {
snTimeNum -= 1 snTimeNum -= 1
...@@ -856,7 +870,7 @@ $('.clickButt').click(async (e) => { ...@@ -856,7 +870,7 @@ $('.clickButt').click(async (e) => {
} }
if (key == 'bunengpai') { if (key == 'bunengpai') {
if($('#noShootTime').hasClass('grayNoShoot')){ if($('#noShootTime').hasClass('grayNoShoot') && window.timeOutFlag){
return return
}else{ }else{
noShootTimeIndex += 1 noShootTimeIndex += 1
...@@ -1275,7 +1289,7 @@ $('.continueTest').click(() => { ...@@ -1275,7 +1289,7 @@ $('.continueTest').click(() => {
window.location.reload() window.location.reload()
}) })
$('#cheatClose').click(() => { $('#cheatClose').click(() => {
if (cheatTimeNum >= 1) { if (cheatTimeNum>=1 && window.timeOutFlag) {
return return
} }
......
...@@ -21,3 +21,10 @@ if(pa.areaType && cheatArea.includes(pa.areaType)){ ...@@ -21,3 +21,10 @@ if(pa.areaType && cheatArea.includes(pa.areaType)){
window.accountInputNum = 5 window.accountInputNum = 5
} }
//控制页面上的倒计时限制是否开启
let timeOutArea = '3208'
window.timeOutFlag = false
if(pa.areaType && timeOutArea.includes(pa.areaType)){
window.timeOutFlag = true
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!