2025-12-23 10:58:15 +08:00
|
|
|
<template>
|
|
|
|
|
<a-spin :spinning="loading">
|
|
|
|
|
<div class="container2">
|
|
|
|
|
<a-row>
|
|
|
|
|
<a-col :span="24">
|
|
|
|
|
<a-table :dataSource="tableData" :columns="columns" :pagination="false" bordered size="small"
|
|
|
|
|
:rowClassName="setRowClassName">
|
|
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
|
|
<!-- 旧数据 -->
|
|
|
|
|
<template v-if="column.dataIndex === 'oldValue'">
|
|
|
|
|
<template v-if="isImageField(record, column.dataIndex) && record.oldValue">
|
|
|
|
|
<JImageUpload :value="getFieldValue(record, column.dataIndex)" disabled />
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else-if="record.fieldKey === 'mp3File' && record.oldValue">
|
|
|
|
|
<audio controls style="width: 200px; height: 30px;">
|
|
|
|
|
<source :src="getFileAccessHttpUrl(getFullUrl(record.oldValue))" type="audio/mpeg">
|
|
|
|
|
</audio>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else-if="record.fieldKey === 'mp4File' && record.oldValue">
|
|
|
|
|
<video controls style="width: 200px; max-height: 150px;">
|
|
|
|
|
<source :src="getFileAccessHttpUrl(getFullUrl(record.oldValue))" type="video/mp4">
|
|
|
|
|
</video>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
2026-01-21 15:06:26 +08:00
|
|
|
<span>{{ record[column.dataIndex] != null ? record[column.dataIndex] : '-' }}</span>
|
2025-12-23 10:58:15 +08:00
|
|
|
</template>
|
|
|
|
|
</template>
|
|
|
|
|
<!-- 新数据 -->
|
|
|
|
|
<template v-else-if="column.dataIndex === 'newValue'">
|
|
|
|
|
<template v-if="isImageField(record, column.dataIndex) && record.newValue">
|
|
|
|
|
<JImageUpload :value="getFieldValue(record, column.dataIndex)" disabled />
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else-if="record.fieldKey === 'mp3File' && record.newValue">
|
|
|
|
|
<audio controls style="width: 200px; height: 30px;">
|
|
|
|
|
<source :src="getFileAccessHttpUrl(getFullUrl(record.newValue))" type="audio/mpeg">
|
|
|
|
|
</audio>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else-if="record.fieldKey === 'mp4File' && record.newValue">
|
|
|
|
|
<video controls style="width: 200px; max-height: 150px;">
|
|
|
|
|
<source :src="getFileAccessHttpUrl(getFullUrl(record.newValue))" type="video/mp4">
|
|
|
|
|
</video>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
2026-01-21 15:06:26 +08:00
|
|
|
<span>{{ record[column.dataIndex] != null ? record[column.dataIndex] : '-' }}</span>
|
2025-12-23 10:58:15 +08:00
|
|
|
</template>
|
|
|
|
|
</template>
|
|
|
|
|
</template>
|
|
|
|
|
</a-table>
|
|
|
|
|
</a-col>
|
|
|
|
|
</a-row>
|
|
|
|
|
</div>
|
|
|
|
|
</a-spin>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
import { ref, computed, onMounted } from 'vue';
|
|
|
|
|
import { Tag } from 'ant-design-vue';
|
|
|
|
|
import JImageUpload from '/@/components/Form/src/jeecg/components/JImageUpload.vue';
|
|
|
|
|
import { list } from '../DirectiveOpeLogInfo.api';
|
|
|
|
|
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
|
|
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
const oldData = ref<Record<string, any>>({});
|
|
|
|
|
const newData = ref<Record<string, any>>({});
|
|
|
|
|
|
|
|
|
|
// 字段映射,用于显示中文名称
|
|
|
|
|
const fieldLabels: Record<string, string> = {
|
|
|
|
|
instructionTagName: '分类标签',
|
|
|
|
|
categoryName: '服务类别',
|
|
|
|
|
typeName: '服务类型',
|
|
|
|
|
directiveName: '服务指令',
|
|
|
|
|
tollPrice: '收费价格(元)',
|
|
|
|
|
comPrice: '提成价格(元)',
|
2026-01-27 15:34:15 +08:00
|
|
|
serviceContent: '服务指令描述',
|
2025-12-23 10:58:15 +08:00
|
|
|
serviceDuration: '服务时长(分钟)',
|
2026-01-21 15:06:26 +08:00
|
|
|
timeoutDuration: '超时时长(分钟)',
|
2025-12-23 10:58:15 +08:00
|
|
|
mp3File: '语音文件',
|
|
|
|
|
mp4File: '视频文件',
|
2026-01-27 15:34:15 +08:00
|
|
|
previewFile: '服务指令图片',
|
|
|
|
|
// previewFileSmall: '预览图片(小)',
|
|
|
|
|
immediateFile: '指令样式(选中)',
|
|
|
|
|
immediateFileFocus: '指令样式(选中)',
|
2025-12-23 10:58:15 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 需要显示的字段顺序
|
|
|
|
|
const displayFields = [
|
|
|
|
|
'instructionTagName',
|
|
|
|
|
'categoryName',
|
|
|
|
|
'typeName',
|
|
|
|
|
'directiveName',
|
|
|
|
|
'tollPrice',
|
|
|
|
|
'comPrice',
|
|
|
|
|
'serviceDuration',
|
2026-01-21 15:06:26 +08:00
|
|
|
'timeoutDuration',
|
2025-12-23 10:58:15 +08:00
|
|
|
'previewFile',
|
2026-01-27 15:34:15 +08:00
|
|
|
// 'previewFileSmall',
|
2025-12-23 10:58:15 +08:00
|
|
|
'mp3File',
|
|
|
|
|
'mp4File',
|
|
|
|
|
'immediateFile',
|
|
|
|
|
'immediateFileFocus',
|
|
|
|
|
'serviceContent',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 表格列定义
|
|
|
|
|
const columns = computed(() => [
|
|
|
|
|
{
|
|
|
|
|
title: '字段',
|
|
|
|
|
dataIndex: 'field',
|
|
|
|
|
key: 'field',
|
|
|
|
|
ellipsis: false
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-01-27 15:34:15 +08:00
|
|
|
title: '变更前',
|
2025-12-23 10:58:15 +08:00
|
|
|
dataIndex: 'oldValue',
|
|
|
|
|
key: 'oldValue',
|
2026-01-27 15:34:15 +08:00
|
|
|
width: '42%',
|
2025-12-23 10:58:15 +08:00
|
|
|
ellipsis: false
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-01-27 15:34:15 +08:00
|
|
|
title: '变更后',
|
2025-12-23 10:58:15 +08:00
|
|
|
dataIndex: 'newValue',
|
|
|
|
|
key: 'newValue',
|
2026-01-27 15:34:15 +08:00
|
|
|
width: '42%',
|
2025-12-23 10:58:15 +08:00
|
|
|
ellipsis: false
|
|
|
|
|
}
|
|
|
|
|
]);
|
2026-01-21 15:06:26 +08:00
|
|
|
setTimeout(()=>{
|
|
|
|
|
console.log("🌊 ~ tableData:", tableData)
|
|
|
|
|
},3000)
|
2025-12-23 10:58:15 +08:00
|
|
|
// 表格数据
|
|
|
|
|
const tableData = computed(() => {
|
|
|
|
|
return displayFields.map(field => {
|
|
|
|
|
// 修正:正确处理字段值
|
|
|
|
|
let oldValue = oldData.value[field];
|
|
|
|
|
let newValue = newData.value[field];
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
key: field,
|
|
|
|
|
field: fieldLabels[field] || field,
|
|
|
|
|
fieldKey: field,
|
|
|
|
|
oldValue: oldValue,
|
|
|
|
|
newValue: newValue,
|
|
|
|
|
// 为了在模板中方便访问,添加原始字段
|
|
|
|
|
[field]: newValue,
|
|
|
|
|
// 添加原始数据的引用
|
|
|
|
|
oldDataRef: oldData,
|
|
|
|
|
newDataRef: newData
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 判断是否为图片字段
|
|
|
|
|
const isImageField = (record: any, columnDataIndex: string) => {
|
|
|
|
|
const imageFields = [
|
|
|
|
|
'previewFile',
|
|
|
|
|
'previewFileSmall',
|
|
|
|
|
'immediateFile',
|
|
|
|
|
'immediateFileFocus'
|
|
|
|
|
];
|
|
|
|
|
if (columnDataIndex == 'field') {
|
|
|
|
|
return false
|
|
|
|
|
} else {
|
|
|
|
|
return imageFields.includes(record.fieldKey);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取字段值
|
|
|
|
|
const getFieldValue = (record: any, columnDataIndex: string) => {
|
|
|
|
|
if (columnDataIndex === 'oldValue') {
|
|
|
|
|
return record.oldValue;
|
|
|
|
|
} else if (columnDataIndex === 'newValue') {
|
|
|
|
|
return record.newValue;
|
|
|
|
|
} else if (columnDataIndex === 'field') {
|
|
|
|
|
return record.field;
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取完整的文件URL
|
|
|
|
|
const getFullUrl = (url: string) => {
|
|
|
|
|
if (!url) return '';
|
|
|
|
|
if (url.startsWith('http')) {
|
|
|
|
|
return url;
|
|
|
|
|
}
|
|
|
|
|
const baseUrl = '';
|
|
|
|
|
return baseUrl + url;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 设置行类名,高亮显示有差异的行
|
|
|
|
|
const setRowClassName = (record: any) => {
|
|
|
|
|
if (record.oldValue !== record.newValue && record.fieldKey !== 'createTime') {
|
|
|
|
|
return 'highlight-row';
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function init(record_: any) {
|
|
|
|
|
try {
|
|
|
|
|
loading.value = true;
|
|
|
|
|
const data = await list({ 'pkId': record_.id });
|
|
|
|
|
|
|
|
|
|
if (data && data.records && data.records.length >= 2) {
|
|
|
|
|
const oldRecord = data.records.find((item: any) => item.dataType === '1');
|
|
|
|
|
const newRecord = data.records.find((item: any) => item.dataType === '2');
|
|
|
|
|
|
|
|
|
|
if (oldRecord) {
|
|
|
|
|
oldData.value = { ...oldRecord };
|
|
|
|
|
}
|
|
|
|
|
if (newRecord) {
|
|
|
|
|
newData.value = { ...newRecord };
|
|
|
|
|
}
|
|
|
|
|
} else if (data && data.records && data.records.length === 1) {
|
|
|
|
|
const record = data.records[0];
|
|
|
|
|
if (record.dataType === '1') {
|
|
|
|
|
oldData.value = { ...record };
|
|
|
|
|
newData.value = {};
|
|
|
|
|
} else if (record.dataType === '2') {
|
|
|
|
|
oldData.value = {};
|
|
|
|
|
newData.value = { ...record };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defineExpose({
|
|
|
|
|
init
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
|
|
.container2 {
|
|
|
|
|
padding: 16px;
|
|
|
|
|
background: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.ant-table-cell) {
|
|
|
|
|
padding: 8px 12px !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 高亮行样式
|
|
|
|
|
:deep(.highlight-row) {
|
|
|
|
|
background-color: #f0f9ff !important; // 浅蓝色背景,表示有变更
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
background-color: #e6f7ff !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 差异高亮样式
|
|
|
|
|
:deep(.ant-table-row) {
|
|
|
|
|
.ant-table-cell {
|
|
|
|
|
|
|
|
|
|
&:nth-child(2),
|
|
|
|
|
// 旧数据列
|
|
|
|
|
&:nth-child(3) {
|
|
|
|
|
|
|
|
|
|
// 新数据列
|
|
|
|
|
.ant-image,
|
|
|
|
|
.ant-upload-list-item {
|
|
|
|
|
border: 1px solid #e8e8e8;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.highlight-row {
|
|
|
|
|
.ant-table-cell {
|
|
|
|
|
|
|
|
|
|
&:nth-child(2),
|
|
|
|
|
// 旧数据列
|
|
|
|
|
&:nth-child(3) {
|
|
|
|
|
// 新数据列
|
|
|
|
|
background-color: #fff1f0 !important; // 差异数据浅红色背景
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 媒体文件样式调整
|
|
|
|
|
audio,
|
|
|
|
|
video {
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
audio {
|
|
|
|
|
min-width: 200px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
video {
|
|
|
|
|
min-width: 200px;
|
|
|
|
|
min-height: 100px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 标签样式
|
|
|
|
|
.ant-tag {
|
|
|
|
|
margin: 0;
|
|
|
|
|
border: none;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|