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

- 打卡记录照片按类型分组展示与上传,每个类型各自最多 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"
:underline="false"
@click="openWaybillPunchRecordEdit(record)"
>编辑</el-link
>{{ record.punched ? '编辑打卡' : '补卡' }}</el-link
>
</div>
<template v-if="record.punched">
@@ -2245,22 +2245,12 @@
>
备注:{{ record.remark }}
</div>
<div
<punch-record-photos
v-if="record.photos && record.photos.length"
class="waybill-manage-page__punch-record-photos"
>
<div
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>
:photos="record.photos"
lazy
@preview="previewDriverUploadPhoto(record.photos, $event)"
/>
</template>
<div v-else class="waybill-manage-page__punch-record-line">暂未打卡</div>
</div>
@@ -2787,138 +2777,99 @@
>
<section-card title="打卡信息">
<el-row :gutter="18">
<el-col :span="12">
<el-col :span="24">
<el-form-item label="过程节点">
<span class="waybill-manage-page__punch-edit-readonly">{{
waybillPunchEditRecord.nodeName || waybillPunchEditRecord.nodeCode || '-'
}}</span>
</el-form-item>
</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-alert
type="info"
:closable="false"
show-icon
title="该节点过程配置为不打卡,标记已打卡仅作数据补录"
title="该节点过程配置为不打卡,保存仅作数据补录"
/>
</el-col>
<el-col v-if="!waybillPunchEditForm.punched" :span="24">
<el-alert
type="warning"
:closable="false"
show-icon
title="保存后该节点的打卡流水将被清除,时间轴上显示为「未打卡」"
/>
<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="打卡照片">
<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>
<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>
</section-card>
</el-form>
@@ -3679,6 +3630,7 @@ import {
trackVehicle,
} from '@/api/business/waybill-manage';
import VoucherExecutionDetail from './voucher-execution-detail.vue';
import PunchRecordPhotos from './punch-record-photos.vue';
import AddressMapPicker from '@/components/address-map-picker/main.vue';
import { getList as getDriverList } from '@/api/transportCapacity/driver';
import {
@@ -3886,6 +3838,7 @@ export default {
ElImageViewer,
OpenFileViewer,
VoucherExecutionDetail,
PunchRecordPhotos,
AddressMapPicker,
},
name: 'WaybillManagePage',
@@ -3983,7 +3936,6 @@ export default {
},
waybillPunchEditRecord: {},
waybillPunchEditForm: {
punched: true,
punchTime: '',
address: '',
longitude: '',
@@ -4386,11 +4338,21 @@ export default {
return this.waybillPunchEditNode?.punch === false;
},
waybillPunchEditRules() {
if (!this.waybillPunchEditForm.punched) return {};
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 = {}) {
if (!this.detailRow?.id || !record) return;
this.waybillPunchEditRecord = { ...record };
// 弹窗不提供打卡状态开关:未打卡的记录点「补卡」进来,保存即补出一条打卡流水
this.waybillPunchEditForm = {
punched: record.punched !== false,
punchTime: record.punchTime || this.$dayjs().format('YYYY-MM-DD HH:mm:ss'),
address: record.address || '',
longitude: record.longitude || '',
@@ -5914,26 +5876,40 @@ export default {
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);
if (!validType) {
this.$message.error('仅支持 JPG、PNG、BMP 图片');
return false;
}
if (file.size / 1024 / 1024 >= 5) {
this.$message.error('图片大小不能超过 5MB');
// 单张 20MB 与司机端一致:凭证要原图,5MB 会把手机拍的正常凭证挡在门外
if (file.size / 1024 / 1024 >= 20) {
this.$message.error('图片大小不能超过 20MB');
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;
},
handleWaybillPunchPhotoSuccess(res) {
handleWaybillPunchPhotoSuccess(res, type = '') {
if (res.code !== 200 || !res.data) {
this.$message.error(res.msg || '上传失败');
return;
}
const url = res.data.link || res.data.url || res.data.domain || '';
if (!url) return;
const photos = [...this.waybillPunchEditForm.photos, { url, type: '' }];
// 照片落进点开的那一组并带上该类型的名字:类型写空,保存时会被按下标回推成别的类型
const photos = [...this.waybillPunchEditForm.photos, { url, type }];
// 在途打卡表只有一个照片字段(单张),多传的保存时会被丢掉,这里先只留一张
this.waybillPunchEditForm.photos =
this.waybillPunchEditRecord.type === 'enroute' ? photos.slice(0, 1) : photos;
@@ -5950,7 +5926,6 @@ export default {
await this.$refs.waybillPunchRecordEditFormRef.validate();
const record = this.waybillPunchEditRecord;
const form = this.waybillPunchEditForm;
const keepDetail = form.punched;
const submit =
typeof this.api?.savePunchRecord === 'function' ? this.api.savePunchRecord : savePunchRecord;
this.waybillPunchRecordEditDialog.submitting = true;
@@ -5960,17 +5935,17 @@ export default {
type: record.type === 'enroute' ? 'enroute' : 'node',
nodeCode: record.nodeCode || '',
nodeName: record.nodeName || '',
punched: form.punched,
// 未打卡时后端会清空该节点流水,明细项不再上传
punchTime: keepDetail ? form.punchTime : '',
address: keepDetail ? form.address : '',
longitude: keepDetail ? form.longitude : '',
latitude: keepDetail ? form.latitude : '',
weight: keepDetail ? form.weight : '',
volume: keepDetail ? form.volume : '',
quantity: keepDetail ? form.quantity : '',
remark: keepDetail ? form.remark : '',
photos: keepDetail ? form.photos.map(photo => ({ url: photo.url, type: photo.type })) : [],
// 弹窗没有打卡状态开关:无论「补卡」还是「编辑打卡」,保存都落一条已打卡流水
punched: true,
punchTime: form.punchTime,
address: form.address,
longitude: form.longitude,
latitude: form.latitude,
weight: form.weight,
volume: form.volume,
quantity: form.quantity,
remark: form.remark,
photos: form.photos.map(photo => ({ url: photo.url, type: photo.type })),
});
this.$message.success('保存成功');
this.waybillPunchRecordEditDialog.visible = false;
@@ -11606,50 +11581,6 @@ export default {
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 {
margin-top: 4px;
font-size: 12px;
@@ -11665,7 +11596,6 @@ export default {
}
}
&__punch-record-photos,
&__driver-upload-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
+7 -16
View File
@@ -694,22 +694,12 @@
>
备注:{{ record.remark }}
</div>
<div
<punch-record-photos
v-if="record.photos && record.photos.length"
class="loading-detail__punch-record-photos"
>
<div
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>
:photos="record.photos"
lazy
@preview="previewDriverUploadPhoto(record.photos, $event)"
/>
</template>
<div v-else class="loading-detail__punch-record-line">暂未打卡</div>
</div>
@@ -1715,6 +1705,7 @@ import {
getVoucherImages as getProcessConfigVoucherImages,
} from '@/api/business/process-config';
import VoucherExecutionDetail from './components/voucher-execution-detail.vue';
import PunchRecordPhotos from './components/punch-record-photos.vue';
import {
carrierTypeOptions,
loadingManageConfig,
@@ -1925,6 +1916,7 @@ export default {
Rank,
Search,
VoucherExecutionDetail,
PunchRecordPhotos,
},
data() {
return {
@@ -4921,7 +4913,6 @@ export default {
margin-left: 12px;
}
}
.loading-detail__punch-record-photos,
.loading-detail__driver-upload-grid,
.loading-detail__voucher-image-grid {
display: grid;