Files
tms-erp-web/src/views/business/components/voucher-execution-detail.vue
T
b2894lxlx f3a33b1a04 ✨ 完善运单导入编辑回填、分批校验与运费录入
导入编辑进入独立页面并回填数据,确认导入按每批100条校验后统一导出失败明细,完整录入运费信息按数量单位联动单价单位。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-27 22:54:33 +08:00

275 lines
8.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="voucher-folder-page voucher-folder-page--embedded">
<section class="voucher-folder-page__search">
<el-form :model="query" label-position="right" label-width="160px">
<div class="voucher-folder-page__search-grid">
<el-form-item label="车牌号">
<el-input v-model="query.plateNo" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="是否关联运单">
<el-select v-model="query.matched" clearable placeholder="全部">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
<div class="voucher-folder-page__search-actions">
<el-button type="primary" @click="search">查询</el-button>
<el-button @click="reset">重置</el-button>
</div>
</div>
</el-form>
</section>
<section class="voucher-folder-page__table-wrap">
<div class="voucher-folder-page__title">{{ batchTitle }}</div>
<el-table v-loading="loading" :data="pageRows" border class="voucher-folder-page__table">
<el-table-column type="index" label="序号" width="70" :index="rowIndex" />
<el-table-column prop="voucherNo" label="执行凭证号" min-width="220">
<template #default="{ row }">
<el-link type="primary" @click="openViewer(row)">{{ row.voucherNo }}</el-link>
</template>
</el-table-column>
<el-table-column prop="plateNo" label="车牌号" min-width="150" />
<el-table-column prop="folderName" label="文件夹名称" min-width="160" />
<el-table-column prop="matched" label="是否关联运单" width="130">
<template #default="{ row }">{{ row.matched === 1 ? '是' : '否' }}</template>
</el-table-column>
<el-table-column prop="processStatus" label="处理状态" width="130" />
<el-table-column prop="createTime" label="创建时间" min-width="180" />
<el-table-column prop="updateTime" label="更新时间" min-width="180" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<div class="voucher-folder-page__actions">
<el-link type="primary" @click="openViewer(row)">查看</el-link>
</div>
</template>
</el-table-column>
</el-table>
<div class="voucher-folder-page__pagination">
<el-pagination
v-model:current-page="page.current"
v-model:page-size="page.size"
:total="filteredRows.length"
:page-sizes="[10, 20, 50]"
layout="total, prev, pager, next, sizes"
@size-change="page.current = 1"
/>
</div>
</section>
<el-dialog v-model="viewerVisible" title="执行凭证详情" width="960px" destroy-on-close>
<div v-loading="viewerLoading" class="voucher-folder-page__viewer">
<div class="voucher-folder-page__viewer-info">
<span>执行凭证号:{{ viewer.voucherNo || '-' }}</span>
<span>文件夹名称:{{ viewer.folderName || '-' }}</span>
<span>是否关联运单:{{ viewer.matched === 1 ? '是' : '否' }}</span>
<span>
配载单号:
<el-link v-if="viewer.loadingNo" type="primary" @click="goToLoading(viewer.loadingNo)">
{{ viewer.loadingNo }}
</el-link>
<template v-else>-</template>
</span>
</div>
<div v-if="viewerFiles.length" class="voucher-folder-page__images">
<el-image
v-for="(file, index) in viewerFiles"
:key="file.id || file.url || index"
:src="file.url"
:preview-src-list="viewerFiles.map(item => item.url).filter(Boolean)"
:initial-index="index"
fit="cover"
/>
</div>
<el-empty v-else description="暂无凭证文件" />
</div>
</el-dialog>
</div>
</template>
<script setup>
import { computed, reactive, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { getVoucherFolderDetail, getVoucherFolders } from '@/api/business/process-config';
const props = defineProps({
waybillId: {
type: [String, Number],
default: '',
},
});
const router = useRouter();
const loading = ref(false);
const rows = ref([]);
const page = reactive({ current: 1, size: 10 });
const query = reactive({ plateNo: '', matched: undefined });
const appliedQuery = reactive({ plateNo: '', matched: undefined });
const viewerVisible = ref(false);
const viewerLoading = ref(false);
const viewer = ref({});
const viewerFiles = ref([]);
const batchTitle = computed(() => {
const batchNos = [...new Set(rows.value.map(item => item.voucherBatchNo).filter(Boolean))];
return batchNos.length === 1 ? batchNos[0] : '执行凭证详情';
});
const filteredRows = computed(() => {
const plateNo = String(appliedQuery.plateNo || '').trim();
return rows.value.filter(item => {
const plateMatched = !plateNo || String(item.plateNo || '').includes(plateNo);
const relatedMatched =
appliedQuery.matched === undefined || appliedQuery.matched === null || appliedQuery.matched === ''
? true
: item.matched === appliedQuery.matched;
return plateMatched && relatedMatched;
});
});
const pageRows = computed(() => {
const start = (page.current - 1) * page.size;
return filteredRows.value.slice(start, start + page.size);
});
const rowIndex = index => (page.current - 1) * page.size + index + 1;
const load = async () => {
if (!props.waybillId) {
rows.value = [];
return;
}
loading.value = true;
try {
const res = await getVoucherFolders(props.waybillId);
rows.value = res.data?.data || [];
} catch (error) {
rows.value = [];
ElMessage.error(error.message || '获取执行凭证失败');
} finally {
loading.value = false;
}
};
const search = () => {
appliedQuery.plateNo = query.plateNo;
appliedQuery.matched = query.matched;
page.current = 1;
};
const reset = () => {
query.plateNo = '';
query.matched = undefined;
search();
};
const openViewer = async row => {
viewerVisible.value = true;
viewerLoading.value = true;
viewer.value = row;
viewerFiles.value = [];
try {
const res = await getVoucherFolderDetail(row.voucherId, row.plateNo);
viewer.value = res.data?.data || row;
viewerFiles.value = viewer.value.files || [];
} catch (error) {
ElMessage.error(error.message || '获取凭证详情失败');
} finally {
viewerLoading.value = false;
}
};
const goToLoading = loadingNo => {
router.push({ path: '/business/loading-manage', query: { loadingNo } });
};
watch(
() => props.waybillId,
() => {
page.current = 1;
load();
},
{ immediate: true }
);
</script>
<style scoped lang="scss">
.voucher-folder-page {
&__search {
padding: 12px 12px 4px;
margin-bottom: 8px;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
&__search-grid {
display: grid;
grid-template-columns: 360px 360px 1fr;
gap: 8px 24px;
}
&__search-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
&__table-wrap {
padding: 12px;
background: #fff;
}
&__title {
padding-bottom: 12px;
color: #303133;
font-size: 16px;
font-weight: 600;
}
&__actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
&__pagination {
display: flex;
justify-content: flex-end;
padding-top: 12px;
}
&__viewer {
min-height: 360px;
}
&__viewer-info {
display: flex;
flex-wrap: wrap;
gap: 28px;
padding-bottom: 20px;
color: #303133;
}
&__images {
display: grid;
grid-template-columns: repeat(5, 140px);
gap: 20px;
}
&__images :deep(.el-image) {
width: 140px;
height: 100px;
border: 6px solid #cfcfcf;
cursor: zoom-in;
}
:deep(.el-table) {
--el-table-border-color: #eff1f7;
}
:deep(.el-table__body tr:nth-child(even) > td) {
background: #fafafa;
}
:deep(.el-form-item) {
margin-bottom: 8px;
}
:deep(.el-form-item__label) {
white-space: nowrap;
}
:deep(.el-input),
:deep(.el-select) {
width: 100%;
}
}
@media (max-width: 900px) {
.voucher-folder-page__search-grid {
grid-template-columns: 1fr 1fr;
}
.voucher-folder-page__search-actions {
grid-column: 1 / -1;
}
}
</style>