✨ 打卡照片按凭证类型分组,编辑弹窗去掉打卡状态开关

- 打卡记录照片按类型分组展示与上传,每个类型各自最多 9 张,组内第一张标「主图」
- 打卡照片是每个打卡节点必有的类型,不由过程配置决定,与各凭证类型并列且互不替代
- 附件区抽出 punch-record-photos 组件,运单详情与配载单详情共用;编辑态沿用 96px 固定方图
- 弹窗上传体积上限由 5MB 提到 20MB,与小程序一致
- 弹窗不再提供「打卡状态」开关:未打卡的记录点「补卡」进来,保存即补出一条已打卡流水
This commit is contained in:
2026-10-07 16:57:46 +08:00
parent 8ad53894b9
commit 3320155416
3 changed files with 443 additions and 217 deletions
@@ -0,0 +1,305 @@
<template>
<div v-if="groups.length" class="punch-record-photos">
<section v-for="group in groups" :key="group.type" class="punch-record-photos__group">
<div class="punch-record-photos__head">
<span class="punch-record-photos__title">{{ group.type }}</span>
<span v-if="editable" class="punch-record-photos__count">{{ countText(group) }}</span>
</div>
<div
class="punch-record-photos__grid"
:class="{ 'punch-record-photos__grid--edit': editable }"
>
<div
v-for="(item, groupIndex) in group.items"
:key="`${item.photo.url}-${item.index}`"
class="punch-record-photos__item"
@click="handlePreview(item.index)"
>
<el-image
:src="item.photo.url"
:preview-src-list="nativePreview ? previewUrls : []"
:initial-index="nativePreview ? item.index : 0"
:preview-teleported="nativePreview"
:lazy="lazy"
fit="cover"
/>
<!-- 主图 = 该类型的第一张,按上传顺序取;没有独立字段,也不可切换 -->
<div v-if="groupIndex === 0" class="punch-record-photos__badge">主图</div>
<el-link
v-if="editable"
class="punch-record-photos__remove"
type="danger"
:underline="false"
@click.stop="$emit('remove', item.index)"
>删除</el-link
>
</div>
<el-upload
v-if="editable && !group.full"
class="punch-record-photos__upload"
:action="uploadAction"
name="file"
:headers="uploadHeaders"
:show-file-list="false"
:before-upload="file => handleBeforeUpload(file, group.type)"
:on-success="res => $emit('upload-success', res, group.type)"
:on-error="error => $emit('upload-error', error, group.type)"
>
<div class="punch-record-photos__add">+ 添加</div>
</el-upload>
<div v-else-if="editable" class="punch-record-photos__add punch-record-photos__add--full">
已满 {{ maxPerType }} 张
</div>
</div>
</section>
</div>
</template>
<script setup>
import { computed } from 'vue';
// 打卡照片是每个打卡节点必有的一个照片类型,不由过程配置的凭证类型决定;
// 它与各凭证类型各自最多 9 张,同一类型下第一张即界面上的「主图」。
const PUNCH_PHOTO_TYPE = '打卡照片';
// 照片没带类型时的兜底名称,与后端落库时的兜底口径一致
const PHOTO_TYPE_FALLBACK = '凭证';
const MAX_PHOTOS_PER_TYPE = 9;
const props = defineProps({
// 扁平照片数组:管理端回显用 voucherType,司机端回显与旧数据用 type
photos: {
type: Array,
default: () => [],
},
// 恒定展示的照片类型:「打卡照片」+ 该节点过程配置勾选的凭证类型。
// 留空则只按照片自身带的类型分组(只读回显用)。
groupTypes: {
type: Array,
default: () => [],
},
// 编辑态:每个类型自带「添加」格与删除链接,并显示已选/上限
editable: {
type: Boolean,
default: false,
},
// 每个类型的张数上限;0 表示不限制(在途打卡只有一个照片字段,张数由「只保留第一张」约束)
maxPerType: {
type: Number,
default: MAX_PHOTOS_PER_TYPE,
},
// 编辑态用 el-image 自带预览;只读态不发预览事件,把点击交给调用方开自己的看图器
nativePreview: {
type: Boolean,
default: false,
},
lazy: {
type: Boolean,
default: false,
},
uploadAction: {
type: String,
default: '',
},
uploadHeaders: {
type: Object,
default: () => ({}),
},
// 返回 false 即中止本次上传;类型、体积、张数校验都在调用方
beforeUpload: {
type: Function,
default: null,
},
});
const emit = defineEmits(['preview', 'remove', 'upload-success', 'upload-error']);
const photoType = photo =>
String(photo?.voucherType || photo?.type || '').trim() || PHOTO_TYPE_FALLBACK;
const groups = computed(() => {
const photos = Array.isArray(props.photos) ? props.photos : [];
const names = [];
const buckets = new Map();
const ensure = name => {
if (!buckets.has(name)) {
buckets.set(name, []);
names.push(name);
}
return buckets.get(name);
};
// 恒定展示的类型先占位:一张照片都没有的类型也要出现,否则编辑态下没有添加入口
props.groupTypes.forEach(type => {
const name = String(type || '').trim();
if (name) ensure(name);
});
// 按上传顺序入组,组间顺序即类型首次出现的顺序,不重排照片
photos.forEach((photo, index) => {
if (!photo?.url) return;
ensure(photoType(photo)).push({ photo, index });
});
const list = names.map(type => {
const items = buckets.get(type);
return {
type,
items,
full: props.editable && props.maxPerType > 0 && items.length >= props.maxPerType,
};
});
// 编辑态至少要有一个分组,否则记录里还没有照片时就没有添加入口
if (props.editable && !list.length) {
return [{ type: props.groupTypes[0] || PUNCH_PHOTO_TYPE, items: [], full: false }];
}
return list;
});
// 预览列表按原始下标对齐,删除/分组都不改变点击某张时打开的是哪一张
const previewUrls = computed(() =>
(Array.isArray(props.photos) ? props.photos : []).map(photo => photo?.url || '')
);
const handlePreview = index => {
if (props.nativePreview) return;
emit('preview', index);
};
const handleBeforeUpload = (file, type) => {
if (typeof props.beforeUpload !== 'function') return true;
return props.beforeUpload(file, type);
};
const countText = group =>
props.maxPerType > 0 ? `${group.items.length}/${props.maxPerType}` : String(group.items.length);
</script>
<style lang="scss" scoped>
.punch-record-photos {
// 弹窗里本组件挂在 el-form-item__content 下,那是个 flex 容器:
// 不给宽度就会收缩成内容宽,里面的网格跟着塌成一小块
width: 100%;
display: flex;
flex-direction: column;
gap: 12px;
margin-top: 4px;
&__group {
width: 100%;
display: flex;
flex-direction: column;
gap: 6px;
}
&__head {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
line-height: 20px;
}
&__title {
font-weight: 600;
color: #303133;
word-break: break-all;
}
&__count {
font-size: 12px;
color: #909399;
}
// 只读回显:撑满容器宽度的五列网格
&__grid {
width: 100%;
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 10px;
}
// 编辑态沿用弹窗原本的固定 96px 方图 + 自动换行:尺寸不随容器宽窄变化
&__grid--edit {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 10px;
}
&__item {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
cursor: pointer;
.el-image {
display: block;
width: 100%;
aspect-ratio: 1;
border: 1px solid #eff1f7;
border-radius: 4px;
overflow: hidden;
background: #f5f7fa;
}
}
&__grid--edit &__item {
width: 96px;
.el-image {
width: 96px;
height: 96px;
aspect-ratio: auto;
}
}
&__badge {
position: absolute;
top: 0;
left: 0;
padding: 0 6px;
border-radius: 4px 0 4px 0;
background: rgba(64, 158, 255, 0.9);
font-size: 12px;
line-height: 18px;
color: #fff;
}
&__remove {
align-self: center;
font-size: 12px;
}
&__upload {
width: 100%;
}
&__grid--edit &__upload {
width: 96px;
}
&__add {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 100%;
aspect-ratio: 1;
border: 1px dashed #dcdfe6;
border-radius: 4px;
font-size: 13px;
color: #909399;
cursor: pointer;
}
&__grid--edit &__add {
width: 96px;
height: 96px;
aspect-ratio: auto;
}
&__add--full {
color: #c0c4cc;
cursor: not-allowed;
}
}
</style>
@@ -2221,7 +2221,7 @@
type="primary" type="primary"
:underline="false" :underline="false"
@click="openWaybillPunchRecordEdit(record)" @click="openWaybillPunchRecordEdit(record)"
>编辑</el-link >{{ record.punched ? '编辑打卡' : '补卡' }}</el-link
> >
</div> </div>
<template v-if="record.punched"> <template v-if="record.punched">
@@ -2245,22 +2245,12 @@
> >
备注:{{ record.remark }} 备注:{{ record.remark }}
</div> </div>
<div <punch-record-photos
v-if="record.photos && record.photos.length" v-if="record.photos && record.photos.length"
class="waybill-manage-page__punch-record-photos" :photos="record.photos"
> lazy
<div @preview="previewDriverUploadPhoto(record.photos, $event)"
v-for="(photo, pi) in record.photos" />
:key="`${record.nodeCode || index}-${pi}`"
class="waybill-manage-page__driver-upload-item"
@click="previewDriverUploadPhoto(record.photos, pi)"
>
<el-image :src="photo.url" fit="cover" lazy />
<div class="waybill-manage-page__driver-upload-label">
{{ photo.label || `${record.nodeName}-凭证` }}
</div>
</div>
</div>
</template> </template>
<div v-else class="waybill-manage-page__punch-record-line">暂未打卡</div> <div v-else class="waybill-manage-page__punch-record-line">暂未打卡</div>
</div> </div>
@@ -2787,138 +2777,99 @@
> >
<section-card title="打卡信息"> <section-card title="打卡信息">
<el-row :gutter="18"> <el-row :gutter="18">
<el-col :span="12"> <el-col :span="24">
<el-form-item label="过程节点"> <el-form-item label="过程节点">
<span class="waybill-manage-page__punch-edit-readonly">{{ <span class="waybill-manage-page__punch-edit-readonly">{{
waybillPunchEditRecord.nodeName || waybillPunchEditRecord.nodeCode || '-' waybillPunchEditRecord.nodeName || waybillPunchEditRecord.nodeCode || '-'
}}</span> }}</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12">
<el-form-item label="打卡状态" prop="punched">
<el-radio-group v-model="waybillPunchEditForm.punched">
<el-radio :label="true">已打卡</el-radio>
<el-radio :label="false">未打卡</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col v-if="waybillPunchEditPunchDisabled" :span="24"> <el-col v-if="waybillPunchEditPunchDisabled" :span="24">
<el-alert <el-alert
type="info" type="info"
:closable="false" :closable="false"
show-icon show-icon
title="该节点过程配置为不打卡,标记已打卡仅作数据补录" title="该节点过程配置为不打卡,保存仅作数据补录"
/> />
</el-col> </el-col>
<el-col v-if="!waybillPunchEditForm.punched" :span="24"> <el-col :span="12">
<el-alert <el-form-item label="打卡时间" prop="punchTime">
type="warning" <el-date-picker
:closable="false" v-model="waybillPunchEditForm.punchTime"
show-icon type="datetime"
title="保存后该节点的打卡流水将被清除,时间轴上显示为「未打卡」" format="YYYY-MM-DD HH:mm:ss"
/> value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择打卡时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col v-if="waybillPunchEditFields.location" :span="24">
<el-form-item label="打卡地址">
<el-input
v-model="waybillPunchEditForm.address"
readonly
maxlength="500"
placeholder="点击选择地图地址"
:suffix-icon="Location"
@click="waybillPunchAddressMapVisible = true"
/>
</el-form-item>
</el-col>
<el-col v-if="waybillPunchEditFields.location" :span="12">
<el-form-item label="经度">
<el-input v-model="waybillPunchEditForm.longitude" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col v-if="waybillPunchEditFields.location" :span="12">
<el-form-item label="纬度">
<el-input v-model="waybillPunchEditForm.latitude" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col v-for="item in waybillPunchEditFields.cargoFields" :key="item.prop" :span="8">
<el-form-item :label="item.label">
<el-input
v-model="waybillPunchEditForm[item.prop]"
maxlength="32"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input
v-model="waybillPunchEditForm.remark"
type="textarea"
:rows="2"
maxlength="500"
show-word-limit
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="打卡照片">
<punch-record-photos
:photos="waybillPunchEditForm.photos"
:group-types="waybillPunchEditPhotoTypes"
:max-per-type="waybillPunchEditPhotoLimit"
editable
native-preview
upload-action="/api/blade-resource/oss/endpoint/put-file"
:upload-headers="waybillExceptionUploadHeaders"
:before-upload="beforeWaybillPunchPhotoUpload"
@remove="removeWaybillPunchEditPhoto"
@upload-success="handleWaybillPunchPhotoSuccess"
@upload-error="handleWaybillPunchPhotoError"
/>
<div
v-if="waybillPunchEditRecord.type === 'enroute'"
class="waybill-manage-page__punch-edit-photo-tip"
>
在途打卡只保留 1 张照片
</div>
</el-form-item>
</el-col> </el-col>
<template v-else>
<el-col :span="12">
<el-form-item label="打卡时间" prop="punchTime">
<el-date-picker
v-model="waybillPunchEditForm.punchTime"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择打卡时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col v-if="waybillPunchEditFields.location" :span="24">
<el-form-item label="打卡地址">
<el-input
v-model="waybillPunchEditForm.address"
readonly
maxlength="500"
placeholder="点击选择地图地址"
:suffix-icon="Location"
@click="waybillPunchAddressMapVisible = true"
/>
</el-form-item>
</el-col>
<el-col v-if="waybillPunchEditFields.location" :span="12">
<el-form-item label="经度">
<el-input v-model="waybillPunchEditForm.longitude" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col v-if="waybillPunchEditFields.location" :span="12">
<el-form-item label="纬度">
<el-input v-model="waybillPunchEditForm.latitude" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col v-for="item in waybillPunchEditFields.cargoFields" :key="item.prop" :span="8">
<el-form-item :label="item.label">
<el-input
v-model="waybillPunchEditForm[item.prop]"
maxlength="32"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input
v-model="waybillPunchEditForm.remark"
type="textarea"
:rows="2"
maxlength="500"
show-word-limit
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="打卡照片">
<div class="waybill-manage-page__punch-edit-photos">
<div
v-for="(photo, index) in waybillPunchEditForm.photos"
:key="`${photo.url}-${index}`"
class="waybill-manage-page__punch-edit-photo"
>
<el-image
:src="photo.url"
:preview-src-list="waybillPunchEditPhotoUrls"
:initial-index="index"
fit="cover"
/>
<div class="waybill-manage-page__punch-edit-photo-label">
{{ photo.type || '凭证' }}
</div>
<el-link
type="danger"
:underline="false"
@click="removeWaybillPunchEditPhoto(index)"
>删除</el-link
>
</div>
<el-upload
action="/api/blade-resource/oss/endpoint/put-file"
name="file"
:headers="waybillExceptionUploadHeaders"
:show-file-list="false"
:before-upload="beforeWaybillPunchPhotoUpload"
:on-success="handleWaybillPunchPhotoSuccess"
:on-error="handleWaybillPunchPhotoError"
>
<div class="waybill-manage-page__punch-edit-photo-add">+ 添加</div>
</el-upload>
</div>
<div
v-if="waybillPunchEditRecord.type === 'enroute'"
class="waybill-manage-page__punch-edit-photo-tip"
>
在途打卡只保留 1 张照片
</div>
</el-form-item>
</el-col>
</template>
</el-row> </el-row>
</section-card> </section-card>
</el-form> </el-form>
@@ -3679,6 +3630,7 @@ import {
trackVehicle, trackVehicle,
} from '@/api/business/waybill-manage'; } from '@/api/business/waybill-manage';
import VoucherExecutionDetail from './voucher-execution-detail.vue'; import VoucherExecutionDetail from './voucher-execution-detail.vue';
import PunchRecordPhotos from './punch-record-photos.vue';
import AddressMapPicker from '@/components/address-map-picker/main.vue'; import AddressMapPicker from '@/components/address-map-picker/main.vue';
import { getList as getDriverList } from '@/api/transportCapacity/driver'; import { getList as getDriverList } from '@/api/transportCapacity/driver';
import { import {
@@ -3886,6 +3838,7 @@ export default {
ElImageViewer, ElImageViewer,
OpenFileViewer, OpenFileViewer,
VoucherExecutionDetail, VoucherExecutionDetail,
PunchRecordPhotos,
AddressMapPicker, AddressMapPicker,
}, },
name: 'WaybillManagePage', name: 'WaybillManagePage',
@@ -3983,7 +3936,6 @@ export default {
}, },
waybillPunchEditRecord: {}, waybillPunchEditRecord: {},
waybillPunchEditForm: { waybillPunchEditForm: {
punched: true,
punchTime: '', punchTime: '',
address: '', address: '',
longitude: '', longitude: '',
@@ -4386,11 +4338,21 @@ export default {
return this.waybillPunchEditNode?.punch === false; return this.waybillPunchEditNode?.punch === false;
}, },
waybillPunchEditRules() { waybillPunchEditRules() {
if (!this.waybillPunchEditForm.punched) return {};
return { punchTime: [{ required: true, message: '请选择打卡时间', trigger: 'change' }] }; return { punchTime: [{ required: true, message: '请选择打卡时间', trigger: 'change' }] };
}, },
waybillPunchEditPhotoUrls() { // 照片类型只有两个来源:固定的「打卡照片」,和该节点过程配置勾选的凭证类型;
return (this.waybillPunchEditForm.photos || []).map(item => item.url).filter(Boolean); // 不接受配置外的类型。在途打卡表只有一个照片字段、也没有类型列,不按凭证类型分组
waybillPunchEditPhotoTypes() {
if (this.waybillPunchEditRecord.type === 'enroute') return [];
const voucherTypes = Array.isArray(this.waybillPunchEditNode?.voucherTypes)
? this.waybillPunchEditNode.voucherTypes
: [];
return ['打卡照片', ...voucherTypes];
},
// 打卡照片与各凭证类型各自最多 9 张,不另设节点总张数上限;
// 在途打卡用 0 表示不限制——它保存时只保留第一张,张数由那一步约束
waybillPunchEditPhotoLimit() {
return this.waybillPunchEditRecord.type === 'enroute' ? 0 : 9;
}, },
// 打卡时间轴按打卡时间先后展示:已打卡记录按打卡时间升序在前, // 打卡时间轴按打卡时间先后展示:已打卡记录按打卡时间升序在前,
// 未打卡记录保持流程节点原顺序排在其后。 // 未打卡记录保持流程节点原顺序排在其后。
@@ -5876,8 +5838,8 @@ export default {
openWaybillPunchRecordEdit(record = {}) { openWaybillPunchRecordEdit(record = {}) {
if (!this.detailRow?.id || !record) return; if (!this.detailRow?.id || !record) return;
this.waybillPunchEditRecord = { ...record }; this.waybillPunchEditRecord = { ...record };
// 弹窗不提供打卡状态开关:未打卡的记录点「补卡」进来,保存即补出一条打卡流水
this.waybillPunchEditForm = { this.waybillPunchEditForm = {
punched: record.punched !== false,
punchTime: record.punchTime || this.$dayjs().format('YYYY-MM-DD HH:mm:ss'), punchTime: record.punchTime || this.$dayjs().format('YYYY-MM-DD HH:mm:ss'),
address: record.address || '', address: record.address || '',
longitude: record.longitude || '', longitude: record.longitude || '',
@@ -5914,26 +5876,40 @@ export default {
this.waybillPunchEditForm.latitude = Number(selection.latitude).toFixed(6); this.waybillPunchEditForm.latitude = Number(selection.latitude).toFixed(6);
} }
}, },
beforeWaybillPunchPhotoUpload(file) { beforeWaybillPunchPhotoUpload(file, type = '') {
const validType = ['image/jpeg', 'image/png', 'image/jpg', 'image/bmp'].includes(file.type); const validType = ['image/jpeg', 'image/png', 'image/jpg', 'image/bmp'].includes(file.type);
if (!validType) { if (!validType) {
this.$message.error('仅支持 JPG、PNG、BMP 图片'); this.$message.error('仅支持 JPG、PNG、BMP 图片');
return false; return false;
} }
if (file.size / 1024 / 1024 >= 5) { // 单张 20MB 与司机端一致:凭证要原图,5MB 会把手机拍的正常凭证挡在门外
this.$message.error('图片大小不能超过 5MB'); if (file.size / 1024 / 1024 >= 20) {
this.$message.error('图片大小不能超过 20MB');
return false; return false;
} }
// 每个照片类型各自最多 9 张。界面已经不给满员的类型「添加」入口,这里再兜一层。
// 张数按界面上的分组口径统计:没带类型的照片算进兜底类型「凭证」
const limit = this.waybillPunchEditPhotoLimit;
if (limit > 0 && type) {
const used = (this.waybillPunchEditForm.photos || []).filter(
photo => (String(photo?.type || '').trim() || '凭证') === type
).length;
if (used >= limit) {
this.$message.error(`${type}最多上传 ${limit} 张`);
return false;
}
}
return true; return true;
}, },
handleWaybillPunchPhotoSuccess(res) { handleWaybillPunchPhotoSuccess(res, type = '') {
if (res.code !== 200 || !res.data) { if (res.code !== 200 || !res.data) {
this.$message.error(res.msg || '上传失败'); this.$message.error(res.msg || '上传失败');
return; return;
} }
const url = res.data.link || res.data.url || res.data.domain || ''; const url = res.data.link || res.data.url || res.data.domain || '';
if (!url) return; if (!url) return;
const photos = [...this.waybillPunchEditForm.photos, { url, type: '' }]; // 照片落进点开的那一组并带上该类型的名字:类型写空,保存时会被按下标回推成别的类型
const photos = [...this.waybillPunchEditForm.photos, { url, type }];
// 在途打卡表只有一个照片字段(单张),多传的保存时会被丢掉,这里先只留一张 // 在途打卡表只有一个照片字段(单张),多传的保存时会被丢掉,这里先只留一张
this.waybillPunchEditForm.photos = this.waybillPunchEditForm.photos =
this.waybillPunchEditRecord.type === 'enroute' ? photos.slice(0, 1) : photos; this.waybillPunchEditRecord.type === 'enroute' ? photos.slice(0, 1) : photos;
@@ -5950,7 +5926,6 @@ export default {
await this.$refs.waybillPunchRecordEditFormRef.validate(); await this.$refs.waybillPunchRecordEditFormRef.validate();
const record = this.waybillPunchEditRecord; const record = this.waybillPunchEditRecord;
const form = this.waybillPunchEditForm; const form = this.waybillPunchEditForm;
const keepDetail = form.punched;
const submit = const submit =
typeof this.api?.savePunchRecord === 'function' ? this.api.savePunchRecord : savePunchRecord; typeof this.api?.savePunchRecord === 'function' ? this.api.savePunchRecord : savePunchRecord;
this.waybillPunchRecordEditDialog.submitting = true; this.waybillPunchRecordEditDialog.submitting = true;
@@ -5960,17 +5935,17 @@ export default {
type: record.type === 'enroute' ? 'enroute' : 'node', type: record.type === 'enroute' ? 'enroute' : 'node',
nodeCode: record.nodeCode || '', nodeCode: record.nodeCode || '',
nodeName: record.nodeName || '', nodeName: record.nodeName || '',
punched: form.punched, // 弹窗没有打卡状态开关:无论「补卡」还是「编辑打卡」,保存都落一条已打卡流水
// 未打卡时后端会清空该节点流水,明细项不再上传 punched: true,
punchTime: keepDetail ? form.punchTime : '', punchTime: form.punchTime,
address: keepDetail ? form.address : '', address: form.address,
longitude: keepDetail ? form.longitude : '', longitude: form.longitude,
latitude: keepDetail ? form.latitude : '', latitude: form.latitude,
weight: keepDetail ? form.weight : '', weight: form.weight,
volume: keepDetail ? form.volume : '', volume: form.volume,
quantity: keepDetail ? form.quantity : '', quantity: form.quantity,
remark: keepDetail ? form.remark : '', remark: form.remark,
photos: keepDetail ? form.photos.map(photo => ({ url: photo.url, type: photo.type })) : [], photos: form.photos.map(photo => ({ url: photo.url, type: photo.type })),
}); });
this.$message.success('保存成功'); this.$message.success('保存成功');
this.waybillPunchRecordEditDialog.visible = false; this.waybillPunchRecordEditDialog.visible = false;
@@ -11606,50 +11581,6 @@ export default {
color: #303133; color: #303133;
} }
&__punch-edit-photos {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
&__punch-edit-photo {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
width: 96px;
.el-image {
display: block;
width: 96px;
height: 96px;
border: 1px solid #eff1f7;
border-radius: 4px;
overflow: hidden;
}
}
&__punch-edit-photo-label {
font-size: 12px;
line-height: 1.4;
color: #606266;
text-align: center;
word-break: break-all;
}
&__punch-edit-photo-add {
display: flex;
align-items: center;
justify-content: center;
width: 96px;
height: 96px;
border: 1px dashed #dcdfe6;
border-radius: 4px;
font-size: 13px;
color: #909399;
cursor: pointer;
}
&__punch-edit-photo-tip { &__punch-edit-photo-tip {
margin-top: 4px; margin-top: 4px;
font-size: 12px; font-size: 12px;
@@ -11665,7 +11596,6 @@ export default {
} }
} }
&__punch-record-photos,
&__driver-upload-grid { &__driver-upload-grid {
display: grid; display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr)); grid-template-columns: repeat(5, minmax(0, 1fr));
+7 -16
View File
@@ -694,22 +694,12 @@
> >
备注:{{ record.remark }} 备注:{{ record.remark }}
</div> </div>
<div <punch-record-photos
v-if="record.photos && record.photos.length" v-if="record.photos && record.photos.length"
class="loading-detail__punch-record-photos" :photos="record.photos"
> lazy
<div @preview="previewDriverUploadPhoto(record.photos, $event)"
v-for="(photo, pi) in record.photos" />
:key="`${record.nodeCode || index}-${pi}`"
class="loading-detail__driver-upload-item"
@click="previewDriverUploadPhoto(record.photos, pi)"
>
<el-image :src="photo.url" fit="cover" lazy />
<div class="loading-detail__driver-upload-label">
{{ photo.label || `${record.nodeName}-凭证` }}
</div>
</div>
</div>
</template> </template>
<div v-else class="loading-detail__punch-record-line">暂未打卡</div> <div v-else class="loading-detail__punch-record-line">暂未打卡</div>
</div> </div>
@@ -1715,6 +1705,7 @@ import {
getVoucherImages as getProcessConfigVoucherImages, getVoucherImages as getProcessConfigVoucherImages,
} from '@/api/business/process-config'; } from '@/api/business/process-config';
import VoucherExecutionDetail from './components/voucher-execution-detail.vue'; import VoucherExecutionDetail from './components/voucher-execution-detail.vue';
import PunchRecordPhotos from './components/punch-record-photos.vue';
import { import {
carrierTypeOptions, carrierTypeOptions,
loadingManageConfig, loadingManageConfig,
@@ -1925,6 +1916,7 @@ export default {
Rank, Rank,
Search, Search,
VoucherExecutionDetail, VoucherExecutionDetail,
PunchRecordPhotos,
}, },
data() { data() {
return { return {
@@ -4921,7 +4913,6 @@ export default {
margin-left: 12px; margin-left: 12px;
} }
} }
.loading-detail__punch-record-photos,
.loading-detail__driver-upload-grid, .loading-detail__driver-upload-grid,
.loading-detail__voucher-image-grid { .loading-detail__voucher-image-grid {
display: grid; display: grid;