Files
tms-erp-web/src/views/business/transport-plan-dispatch.vue
T

954 lines
30 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>
<basic-container class="transport-plan-dispatch-page">
<div v-loading="loading" class="transport-plan-dispatch-page__shell">
<!-- 顶部信息区:对齐运单详情汇总样式 -->
<section-card class="transport-plan-dispatch-page__summary">
<div class="transport-plan-dispatch-page__heading">
<el-button icon="el-icon-arrow-left" text @click="handleBack">返回</el-button>
<strong>计划调度</strong>
<span>{{ planData.planNo || planData.loadingNo || '-' }}</span>
<span class="detail-status-text" :class="statusTextClass">
{{ planData.businessStatusName || '-' }}
</span>
<span class="detail-status-text detail-transport-type">
{{ transportTypeLabel }}
</span>
</div>
<div class="transport-plan-dispatch-page__summary-grid">
<div class="transport-plan-dispatch-page__summary-item">
<span class="transport-plan-dispatch-page__summary-label">客户</span>
<span class="transport-plan-dispatch-page__summary-value">
{{ planData.customerName || '-' }}
</span>
</div>
<div class="transport-plan-dispatch-page__summary-item">
<span class="transport-plan-dispatch-page__summary-label">合同编号</span>
<span class="transport-plan-dispatch-page__summary-value is-link">
{{ planData.contractNo || '-' }}
</span>
</div>
<div class="transport-plan-dispatch-page__summary-item">
<span class="transport-plan-dispatch-page__summary-label">项目</span>
<span class="transport-plan-dispatch-page__summary-value">
{{ planData.projectName || '-' }}
</span>
</div>
<div class="transport-plan-dispatch-page__summary-item">
<span class="transport-plan-dispatch-page__summary-label">计划执行时间</span>
<span class="transport-plan-dispatch-page__summary-value">
{{ formatDateRange(planData.planStartDate, planData.planEndDate) }}
</span>
</div>
<div class="transport-plan-dispatch-page__summary-item is-goods">
<span class="transport-plan-dispatch-page__summary-label">货物信息</span>
<span class="transport-plan-dispatch-page__summary-value is-wrap">
{{ planData.goodsInfo || formatGoodsInfo(planData) }}
</span>
</div>
<div class="transport-plan-dispatch-page__route">
<div class="transport-plan-dispatch-page__route-item is-start">
<div class="transport-plan-dispatch-page__route-marker">起</div>
<div class="transport-plan-dispatch-page__route-detail">
<strong class="transport-plan-dispatch-page__route-name">
{{
formatRouteCityDistrict(planData.departureAddress || planData.departureName)
}}
</strong>
<el-tooltip
:content="planData.departureAddress || planData.departureName || '-'"
placement="top"
>
<span class="transport-plan-dispatch-page__route-addr">
{{ planData.departureAddress || planData.departureName || '-' }}
</span>
</el-tooltip>
<div class="transport-plan-dispatch-page__route-contact">
<span>联系人:{{ planData.departureContact || '-' }}</span>
<span>联系方式:{{ planData.departurePhone || '-' }}</span>
</div>
</div>
</div>
<span class="transport-plan-dispatch-page__route-line" aria-hidden="true" />
<div class="transport-plan-dispatch-page__route-item is-end">
<div class="transport-plan-dispatch-page__route-marker">终</div>
<div class="transport-plan-dispatch-page__route-detail">
<strong class="transport-plan-dispatch-page__route-name">
{{ formatRouteCityDistrict(planData.arrivalAddress || planData.arrivalName) }}
</strong>
<el-tooltip
:content="planData.arrivalAddress || planData.arrivalName || '-'"
placement="top"
>
<span class="transport-plan-dispatch-page__route-addr">
{{ planData.arrivalAddress || planData.arrivalName || '-' }}
</span>
</el-tooltip>
<div class="transport-plan-dispatch-page__route-contact">
<span>联系人:{{ planData.arrivalContact || '-' }}</span>
<span>联系方式:{{ planData.arrivalPhone || '-' }}</span>
</div>
</div>
</div>
</div>
<div class="transport-plan-dispatch-page__attachments">
<span class="transport-plan-dispatch-page__summary-label">附件</span>
<div>
<el-link
v-for="(item, index) in attachmentList"
:key="index"
type="primary"
:href="item.url"
:underline="false"
target="_blank"
>
{{ item.name || item.originalName || '附件' }}
</el-link>
<span v-if="!attachmentList.length">-</span>
</div>
</div>
</div>
</section-card>
<!-- 调度列表 -->
<section-card class="transport-plan-dispatch-page__list-card">
<template #title>
<span>待调度列表</span>
<span class="transport-plan-dispatch-page__list-summary">
{{ summaryText }}
</span>
</template>
<template #extra>
<el-button type="primary" :disabled="!canAddDispatch" @click="handleAdd">
新增
</el-button>
</template>
<el-table
:data="pagedDispatchList"
border
height="100%"
row-key="id"
class="transport-plan-dispatch-page__table"
>
<el-table-column label="序号" width="70" align="center" fixed="left">
<template #default="{ $index }">
{{ dispatchRowIndex($index) }}
</template>
</el-table-column>
<el-table-column label="运输方式" min-width="180" align="center" show-overflow-tooltip>
<template #default="{ row }">
{{ formatTransportType(row.transportTypeName || row.transportType) }}
</template>
</el-table-column>
<el-table-column prop="carrierType" label="承运类型" min-width="150" align="center" show-overflow-tooltip />
<el-table-column prop="carrierName" label="承运商" min-width="150" align="center" show-overflow-tooltip />
<el-table-column prop="driverName" label="司机" min-width="260" align="center" show-overflow-tooltip>
<template #default="{ row }">
<span>{{ formatDriver(row) }}</span>
</template>
</el-table-column>
<el-table-column prop="vehicleNo" label="车牌号" min-width="150" align="center" show-overflow-tooltip />
<el-table-column prop="cargoType" label="货物类型" min-width="170" align="center" show-overflow-tooltip />
<el-table-column prop="cargoInfo" label="货物信息" min-width="280" align="center" show-overflow-tooltip>
<template #default="{ row }">
{{ row.cargoInfo || formatCargoInfo(row) }}
</template>
</el-table-column>
<el-table-column label="发货地址" min-width="220" align="center" show-overflow-tooltip>
<template #default="{ row }">
{{ formatAddress(row.departureAddress) }}
</template>
</el-table-column>
<el-table-column label="收货地址" min-width="220" align="center" show-overflow-tooltip>
<template #default="{ row }">
{{ formatAddress(row.arrivalAddress) }}
</template>
</el-table-column>
<el-table-column prop="quantity" label="数量" min-width="120" align="center">
<template #default="{ row }">
{{ row.quantity ? `${row.quantity}${row.quantityUnit || '吨'}` : '-' }}
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="单价" min-width="120" align="center" />
<el-table-column prop="freight" label="运费" min-width="120" align="center" />
<el-table-column prop="otherFeeTotal" label="其他费用" min-width="120" align="center" />
<el-table-column prop="freightTotal" label="运费合计" min-width="120" align="center" />
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="{ row, $index }">
<div class="transport-plan-dispatch-page__actions">
<el-link type="primary" @click="handleEdit(row, $index)">编辑</el-link>
<el-link type="danger" @click="handleDelete($index)">删除</el-link>
</div>
</template>
</el-table-column>
</el-table>
<div class="transport-plan-dispatch-page__pagination">
<el-pagination
v-model:current-page="dispatchPage.currentPage"
v-model:page-size="dispatchPage.pageSize"
:page-sizes="dispatchPage.pageSizes"
layout="total, sizes, prev, pager, next"
:total="dispatchList.length"
@current-change="handleDispatchCurrentChange"
@size-change="handleDispatchSizeChange"
/>
</div>
</section-card>
<!-- 底部按钮 -->
<div class="transport-plan-dispatch-page__footer">
<el-button @click="handleBack">关闭</el-button>
<el-button type="primary" :loading="saving" @click="handleSubmit">
确认生成
</el-button>
</div>
</div>
<!-- 调度明细编辑弹窗 - 将使用原组件的弹窗 -->
<transport-plan-page
v-if="transportPlanPageVisible"
ref="transportPlanPageRef"
:api="api"
:config="config"
:crud-option="option"
style="display: none"
/>
</basic-container>
</template>
<script>
import { getDetail, dispatch } from '@/api/business/transport-plan';
import { getDictionary } from '@/api/system/dictbiz';
import SectionCard from '@/components/section-card/main.vue';
import TransportPlanPage from './components/transport-plan-page.vue';
import * as api from '@/api/business/transport-plan';
import { config, option } from '@/option/business/transport-plan';
const TRANSPORT_PLAN_QUANTITY_UNIT = '吨';
const normalizeDictOptions = (list = []) =>
(Array.isArray(list) ? list : []).map(item => ({
label: item.dictValue || item.label || '',
value: item.dictKey ?? item.value ?? '',
}));
export default {
name: 'TransportPlanDispatch',
components: {
SectionCard,
TransportPlanPage,
},
data() {
return {
loading: false,
saving: false,
planData: {},
dispatchList: [],
attachmentList: [],
transportPlanPageVisible: false,
transportTypeOptions: [],
dispatchPage: {
currentPage: 1,
pageSize: 10,
pageSizes: [10, 20, 50, 100],
},
api,
config,
option,
};
},
computed: {
planId() {
return this.$route.query.planId;
},
transportTypeLabel() {
return this.formatTransportType(
this.planData.transportTypeName || this.planData.transportType
);
},
pagedDispatchList() {
const start = (this.dispatchPage.currentPage - 1) * this.dispatchPage.pageSize;
return this.dispatchList.slice(start, start + this.dispatchPage.pageSize);
},
statusTextClass() {
const status = this.planData.businessStatus;
if (status === 2 || status === '2') return 'status-text-success';
if (status === 1 || status === '1') return 'is-dispatching';
if (status === 3 || status === '3') return 'is-danger';
return 'is-info';
},
summaryText() {
const goodsList = this.parseGoodsList(this.planData.goodsJson);
if (!goodsList.length) {
const total = Number(this.planData.totalQuantity || 0);
const dispatched = this.calculateDispatchedQuantity();
if (total <= 0) {
return `已调度 ${dispatched.toFixed(2)}吨`;
}
const remaining = Math.max(total - dispatched, 0);
return `共${total}吨 | 已调度 ${dispatched.toFixed(2)}吨,剩余${remaining.toFixed(2)}吨`;
}
const summary = {};
goodsList.forEach(goods => {
const unit = goods.quantityUnit || TRANSPORT_PLAN_QUANTITY_UNIT;
if (!summary[unit]) {
summary[unit] = { total: 0, dispatched: 0 };
}
summary[unit].total += Number(goods.quantity || 0);
});
this.dispatchList.forEach(item => {
const unit = item.quantityUnit || TRANSPORT_PLAN_QUANTITY_UNIT;
if (!summary[unit]) {
summary[unit] = { total: 0, dispatched: 0 };
}
summary[unit].dispatched += Number(item.quantity || 0);
});
const parts = Object.entries(summary).map(([unit, data]) => {
if (data.total <= 0) {
return `已调度 ${data.dispatched.toFixed(2)}${unit}`;
}
const remaining = Math.max(data.total - data.dispatched, 0);
return `${data.total}${unit} | 已调度 ${data.dispatched.toFixed(2)}${unit},剩余${remaining.toFixed(
2
)}${unit}`;
});
return parts.join(' | ');
},
canAddDispatch() {
const goodsList = this.parseGoodsList(this.planData.goodsJson);
if (!goodsList.length) {
const total = Number(this.planData.totalQuantity || 0);
if (total <= 0) return true;
const dispatched = this.calculateDispatchedQuantity();
return dispatched < total;
}
const hasPositiveTotal = goodsList.some(
goods => Number(goods.quantity || 0) > 0
);
if (!hasPositiveTotal) return true;
return goodsList.some(goods => {
const unit = goods.quantityUnit || TRANSPORT_PLAN_QUANTITY_UNIT;
const totalQty = Number(goods.quantity || 0);
if (totalQty <= 0) return true;
const dispatchedQty = this.dispatchList
.filter(item => item.quantityUnit === unit)
.reduce((sum, item) => sum + Number(item.quantity || 0), 0);
return dispatchedQty < totalQty;
});
},
},
created() {
this.loadTransportTypeOptions().finally(() => {
this.loadPlanData();
});
},
mounted() {
// 初始化 transport-plan-page 组件用于打开弹窗
this.transportPlanPageVisible = true;
},
methods: {
loadTransportTypeOptions() {
return getDictionary({ code: 'transport_type' })
.then(res => {
this.transportTypeOptions = normalizeDictOptions(res.data?.data || []);
})
.catch(() => {
this.transportTypeOptions = [];
});
},
formatTransportType(value) {
const transportType = String(value || '').trim();
if (!transportType) return '-';
// 已是中文标签时直接展示
if (/[\u4e00-\u9fff]/.test(transportType)) return transportType;
const item = this.transportTypeOptions.find(
option => String(option.value ?? '').toLowerCase() === transportType.toLowerCase()
);
return item?.label || transportType;
},
loadPlanData() {
if (!this.planId) {
this.$message.error('缺少计划ID参数');
this.handleBack();
return;
}
this.loading = true;
getDetail(this.planId)
.then(res => {
if (res.data?.success) {
this.planData = res.data.data || {};
this.attachmentList = this.parseAttachments(this.planData.attachmentsJson);
this.dispatchList = this.parseDispatchList(this.planData);
this.dispatchPage.currentPage = 1;
} else {
this.$message.error(res.data?.msg || '加载数据失败');
}
})
.catch(err => {
console.error('加载计划数据失败:', err);
this.$message.error('加载数据失败');
})
.finally(() => {
this.loading = false;
});
},
parseAttachments(json) {
try {
const data = JSON.parse(json || '[]');
return Array.isArray(data) ? data : [];
} catch {
return [];
}
},
parseDispatchList(planData) {
try {
const sources = [
planData.waybillList,
planData.dispatchRows,
planData.dispatchRecordList,
planData.dispatchList,
];
for (const source of sources) {
if (Array.isArray(source) && source.length) {
return source.map((item, index) => ({
...item,
id: item.id || `temp-${index}`,
}));
}
}
return [];
} catch {
return [];
}
},
parseGoodsList(json) {
try {
const data = JSON.parse(json || '[]');
return Array.isArray(data) ? data : [];
} catch {
return [];
}
},
calculateDispatchedQuantity() {
return this.dispatchList.reduce((sum, item) => sum + Number(item.quantity || 0), 0);
},
formatDateRange(startDate, endDate) {
const dates = [startDate, endDate].filter(Boolean);
return dates.length ? dates.join(' ~ ') : '-';
},
formatAddress(address) {
if (!address) return '-';
const match = address.match(/^(.*?省)?(.*?市)?(.*?区|.*?县)?/);
return match ? match[0] || address : address;
},
formatProvinceCityDistrict(value) {
const text = String(value || '').trim();
if (!text) return '-';
const districtMatch = text.match(/(?:自治县|自治旗|林区|矿区|新区|开发区|区|县|旗)/);
if (districtMatch) {
return text.slice(0, districtMatch.index + districtMatch[0].length);
}
const cityMatch = text.match(/市/);
return cityMatch ? text.slice(0, cityMatch.index + 1) : text;
},
formatRouteCityDistrict(value) {
const text = String(value || '').trim();
if (!text) return '-';
const provinceMatch = text.match(/^.+?(?:省|自治区|特别行政区)/);
const cityRegion = provinceMatch ? text.slice(provinceMatch[0].length) : text;
const cityMatch = cityRegion.match(/^(.+?市)/);
if (!cityMatch) return this.formatProvinceCityDistrict(text);
const city = cityMatch[1];
const remainder = cityRegion.slice(city.length);
const districtMatch = remainder.match(
/^(.+?(?:自治县|自治旗|林区|矿区|新区|开发区|区|县|旗){1,2})/
);
const district = districtMatch ? districtMatch[1] : '';
return [city, district].filter(Boolean).join(' ');
},
formatDriver(row) {
const parts = [row.driverName, row.driverPhone].filter(Boolean);
return parts.length ? parts.join(' / ') : '-';
},
formatGoodsInfo(data) {
const goodsList = this.parseGoodsList(data.goodsJson);
if (!goodsList.length) {
return data.cargoName || '-';
}
return goodsList
.map(goods => {
const parts = [goods.cargoName || goods.goodsName];
if (goods.quantity) {
parts.push(`${goods.quantity}${goods.quantityUnit || TRANSPORT_PLAN_QUANTITY_UNIT}`);
}
return parts.join(' ');
})
.join(';');
},
formatCargoInfo(row) {
const parts = [row.cargoName];
if (row.specification) parts.push(row.specification);
if (row.model) parts.push(row.model);
return parts.filter(Boolean).join(' / ') || '-';
},
handleBack() {
this.$router.back();
},
handleAdd() {
if (!this.canAddDispatch) {
this.$message.warning('待调度列表货物总量已达到计划总量,不能新增调度明细');
return;
}
this.openPageDispatchItemDialog(-1);
},
dispatchRowIndex(index) {
return (this.dispatchPage.currentPage - 1) * this.dispatchPage.pageSize + index + 1;
},
resolveDispatchGlobalIndex(pageIndex) {
return (this.dispatchPage.currentPage - 1) * this.dispatchPage.pageSize + pageIndex;
},
syncDispatchPage() {
const maxPage = Math.max(
1,
Math.ceil(this.dispatchList.length / this.dispatchPage.pageSize) || 1
);
if (this.dispatchPage.currentPage > maxPage) {
this.dispatchPage.currentPage = maxPage;
}
},
handleDispatchCurrentChange(currentPage) {
this.dispatchPage.currentPage = currentPage;
},
handleDispatchSizeChange(pageSize) {
this.dispatchPage.pageSize = pageSize;
this.dispatchPage.currentPage = 1;
},
handleEdit(row, index) {
this.openPageDispatchItemDialog(this.resolveDispatchGlobalIndex(index), row);
},
openPageDispatchItemDialog(index = -1, row) {
this.$nextTick(() => {
const component = this.$refs.transportPlanPageRef;
if (!component) return;
component.dispatchRow = this.planData;
component.dispatchRows = [...this.dispatchList];
this.bindDispatchItemSaveSync(component);
if (index >= 0) component.openDispatchItemDialog(index, row);
else component.openDispatchItemDialog(-1);
});
},
bindDispatchItemSaveSync(component) {
if (component.__dispatchListSaveSynced) return;
const originalSave = component.saveDispatchItem;
component.saveDispatchItem = (...args) => {
originalSave.apply(component, args);
if (!component.dispatchItemBox) {
this.dispatchList = [...(component.dispatchRows || [])];
this.syncDispatchPage();
}
};
component.__dispatchListSaveSynced = true;
},
handleDelete(index) {
this.$confirm('确定删除该调度明细?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.dispatchList.splice(this.resolveDispatchGlobalIndex(index), 1);
this.syncDispatchPage();
this.$message.success('删除成功');
});
},
handleSubmit() {
if (!this.dispatchList.length) {
this.$message.warning('请先添加调度明细');
return;
}
for (const [index, row] of this.dispatchList.entries()) {
if (!row.carrierName) {
this.$message.warning(`第${index + 1}条调度明细缺少承运商信息`);
return;
}
// 司机是否必填由调度明细弹窗按承运类型判定(承运商承运时司机选填),此处不再重复校验
if (!row.vehicleNo) {
this.$message.warning(`第${index + 1}条调度明细缺少车牌号`);
return;
}
if (!this.resolveDispatchQuantityUnit(row)) {
this.$message.warning(`第${index + 1}条调度明细缺少数量单位`);
return;
}
}
this.$confirm(
`请确认调度信息,将生成 <span style="color: #409eff; font-size: 20px; padding: 0 4px;">${this.dispatchList.length}</span> 条运单,是否继续?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true,
}
).then(() => {
this.submitDispatch();
});
},
resolveDispatchQuantityUnit(row = {}) {
const topUnit = String(row.quantityUnit || '').trim();
if (topUnit) return topUnit;
const goodsList = this.parseGoodsList(row.goodsJson);
for (const goods of goodsList) {
const unit = String(
goods?.quantityUnit || goods?.goodsQuantityUnit || goods?.unit || ''
).trim();
if (unit) return unit;
}
return TRANSPORT_PLAN_QUANTITY_UNIT;
},
enrichDispatchGoodsJson(row = {}, quantityUnit) {
const goodsList = this.parseGoodsList(row.goodsJson);
if (goodsList.length) {
const nextGoods = goodsList.map(goods => ({
...goods,
quantityUnit: String(goods.quantityUnit || quantityUnit || '').trim() || quantityUnit,
}));
return JSON.stringify(nextGoods);
}
return JSON.stringify([
{
cargoName: row.cargoName,
cargoType: row.cargoType,
quantity: row.quantity,
quantityUnit,
specification: row.specification,
model: row.model,
packageType: row.packageType,
brand: row.brand,
},
]);
},
submitDispatch() {
this.saving = true;
const payload = {
id: this.planId,
mode: 'submit',
waybills: this.dispatchList.map(row => {
const quantityUnit = this.resolveDispatchQuantityUnit(row);
return {
...row,
quantityUnit,
taskEntryMode: row.taskEntryMode || 'simple',
taskRemark: row.remark || '',
estimatedStartTime: row.estimatedStartTime || this.planData.planStartDate || '',
estimatedEndTime: row.estimatedEndTime || this.planData.planEndDate || '',
goodsJson: this.enrichDispatchGoodsJson(row, quantityUnit),
};
}),
};
dispatch(payload)
.then(res => {
if (res.data?.success) {
this.$message.success('调度成功,运单已生成');
this.handleBack();
} else {
this.$message.error(res.data?.msg || '调度失败');
}
})
.catch(err => {
console.error('调度失败:', err);
this.$message.error(err?.response?.data?.msg || err?.message || '调度失败');
})
.finally(() => {
this.saving = false;
});
},
},
};
</script>
<style lang="scss" scoped>
.transport-plan-dispatch-page {
height: 100%;
display: flex;
flex-direction: column;
&__shell {
display: flex;
flex-direction: column;
gap: 16px;
height: 100%;
}
&__summary {
:deep(.el-card__body) {
padding: 18px 22px;
}
}
&__heading {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 18px;
strong {
font-size: 20px;
}
.detail-status-text {
display: inline-flex;
align-items: center;
height: 28px;
padding: 0 12px;
border-radius: 8px;
font-size: 13px;
line-height: 28px;
}
.status-text-success,
.is-dispatching {
color: #67c23a;
background: #e1f3d8;
}
.detail-transport-type {
color: #409eff;
background: #ecf5ff;
}
.is-danger {
color: #f56c6c;
background: #fef0f0;
}
.is-info {
color: #909399;
background: #f4f4f5;
}
}
&__summary-grid {
display: grid;
grid-template-columns: repeat(4, minmax(120px, 1fr)) minmax(360px, 1.5fr);
gap: 16px 24px;
}
&__summary-item {
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
&.is-goods {
grid-column: 1 / 5;
}
}
&__summary-label {
color: #909399;
}
&__summary-value {
overflow: hidden;
color: #303133;
font-weight: 500;
text-overflow: ellipsis;
white-space: nowrap;
&.is-link {
color: #409eff;
}
&.is-wrap {
white-space: normal;
word-break: break-word;
}
}
&__route {
display: grid;
grid-template-columns: minmax(100px, 1fr) minmax(36px, 0.7fr) minmax(100px, 1fr);
grid-row: 1 / span 3;
grid-column: 5;
align-items: start;
min-width: 0;
padding: 4px 0;
}
&__route-item {
display: flex;
min-width: 0;
flex-direction: column;
align-items: center;
text-align: center;
}
&__route-marker {
display: inline-flex;
width: 36px;
height: 36px;
align-items: center;
justify-content: center;
color: #fff;
border-radius: 8px;
background: #409eff;
font-size: 18px;
font-weight: 600;
.is-end & {
background: #67c23a;
}
}
&__route-line {
width: 100%;
height: 6px;
margin-top: 15px;
border-radius: 1px;
background: #e4e7ed;
}
&__route-detail {
display: flex;
min-width: 0;
flex-direction: column;
align-items: center;
gap: 5px;
margin-top: 8px;
width: 100%;
}
&__route-name {
max-width: 100%;
overflow: hidden;
color: #303133;
font-size: 18px;
font-weight: 600;
line-height: 1.35;
text-overflow: ellipsis;
white-space: nowrap;
}
&__route-addr {
display: block;
max-width: 100%;
overflow: hidden;
color: #606266;
font-size: 12px;
line-height: 1.5;
text-overflow: ellipsis;
white-space: nowrap;
}
&__route-contact {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 2px 8px;
max-width: 100%;
color: #909399;
font-size: 12px;
line-height: 1.5;
}
&__attachments {
grid-column: 1 / 5;
display: flex;
flex-direction: column;
gap: 8px;
div {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
}
&__list-card {
flex: 1;
display: flex;
flex-direction: column;
}
&__list-summary {
margin-left: 12px;
font-size: 14px;
color: #606266;
font-weight: normal;
}
&__table {
flex: 1;
}
&__pagination {
display: flex;
justify-content: flex-end;
padding-top: 12px;
}
&__actions {
display: flex;
gap: 8px;
justify-content: center;
}
&__footer {
display: flex;
justify-content: flex-end;
gap: 12px;
padding: 16px;
background: #fff;
border-radius: 4px;
}
@media (max-width: 1200px) {
&__summary-grid {
grid-template-columns: repeat(4, minmax(130px, 1fr));
}
&__summary-item.is-goods,
&__attachments {
grid-column: 1 / -1;
}
&__route {
grid-column: 1 / -1;
grid-row: auto;
}
}
@media (max-width: 800px) {
&__summary-grid {
grid-template-columns: 1fr;
}
&__route {
grid-template-columns: minmax(0, 1fr);
gap: 16px;
}
&__route-line {
width: 10px;
height: 32px;
margin: 0 auto;
}
&__summary-item.is-goods,
&__attachments {
grid-column: auto;
}
}
}
</style>