调整 iam
This commit is contained in:
+215
-111
@@ -1,57 +1,58 @@
|
||||
<template>
|
||||
<basic-container class="master-order-page">
|
||||
<template v-if="mode === 'list'">
|
||||
<el-form :model="query" class="master-search" label-width="160px" @submit.prevent>
|
||||
<el-row :gutter="16">
|
||||
<el-col v-for="field in primaryFields" :key="field.prop" :span="6"
|
||||
><el-form-item :label="field.label"
|
||||
><el-input v-model="query[field.prop]" placeholder="请输入" /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="6"
|
||||
><el-form-item label="状态"
|
||||
><el-select v-model="query.businessStatus" placeholder="全部"
|
||||
><el-option label="全部" value="" /><el-option
|
||||
<div class="master-order-page__search">
|
||||
<el-form :model="query" label-position="right" label-width="88px" @submit.prevent>
|
||||
<div class="master-order-page__search-grid">
|
||||
<el-form-item v-for="field in primaryFields" :key="field.prop" :label="field.label">
|
||||
<el-input v-model="query[field.prop]" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="query.businessStatus" clearable placeholder="全部">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option
|
||||
v-for="item in statuses"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value" /></el-select></el-form-item
|
||||
></el-col>
|
||||
<template v-if="searchExpanded">
|
||||
<el-col v-for="field in secondaryFields" :key="field.prop" :span="6"
|
||||
><el-form-item :label="field.label"
|
||||
><el-input v-model="query[field.prop]" placeholder="请输入" /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="6"
|
||||
><el-form-item label="计划开始日期"
|
||||
><el-date-picker
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<template v-if="searchExpanded">
|
||||
<el-form-item v-for="field in secondaryFields" :key="field.prop" :label="field.label">
|
||||
<el-input v-model="query[field.prop]" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<el-form-item label="计划开始日期">
|
||||
<el-date-picker
|
||||
v-model="query.planStartRange"
|
||||
type="datetimerange"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
start-placeholder="请选择"
|
||||
end-placeholder="请选择" /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="6"
|
||||
><el-form-item label="计划结束日期"
|
||||
><el-date-picker
|
||||
end-placeholder="请选择"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="计划结束日期">
|
||||
<el-date-picker
|
||||
v-model="query.planEndRange"
|
||||
type="datetimerange"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
start-placeholder="请选择"
|
||||
end-placeholder="请选择" /></el-form-item
|
||||
></el-col>
|
||||
</template>
|
||||
<el-col :span="24" class="search-actions"
|
||||
><el-button type="primary" @click="search">查询</el-button
|
||||
><el-button @click="reset">重置</el-button
|
||||
><el-link type="primary" @click="searchExpanded = !searchExpanded"
|
||||
><el-icon><component :is="searchExpanded ? ArrowUp : ArrowDown" /></el-icon
|
||||
>{{ searchExpanded ? '折叠' : '展开' }}</el-link
|
||||
></el-col
|
||||
>
|
||||
</el-row>
|
||||
</el-form>
|
||||
end-placeholder="请选择"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<div class="master-order-page__search-actions">
|
||||
<el-button type="primary" @click="search">查询</el-button>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
<el-link type="primary" @click="searchExpanded = !searchExpanded">{{
|
||||
searchExpanded ? '收起' : '展开'
|
||||
}}</el-link>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<section class="master-list-panel">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" @click="goCreate()">新建多联总单</el-button
|
||||
@@ -76,7 +77,13 @@
|
||||
<div class="route-point">
|
||||
<span :class="['route-node', node.type]">{{ node.text }}</span>
|
||||
<div class="route-detail">
|
||||
<el-tooltip placement="top"><template #content>{{ node.name }}</template><strong>{{ node.name }}</strong></el-tooltip>
|
||||
<el-tooltip
|
||||
:content="node.address || node.name || '-'"
|
||||
placement="top"
|
||||
:show-after="120"
|
||||
>
|
||||
<strong>{{ node.name }}</strong>
|
||||
</el-tooltip>
|
||||
<span v-for="line in node.lines" :key="line">{{ line }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -125,7 +132,7 @@
|
||||
/>
|
||||
</template>
|
||||
<master-order-dispatch v-else-if="mode === 'dispatch'" :id="routeId" @back="goList" />
|
||||
<master-order-detail v-else :id="routeId" @back="goList" />
|
||||
<master-order-detail v-else :id="routeId" />
|
||||
<el-dialog v-model="confirm.visible" title="提示" width="400px"
|
||||
><span>{{ confirm.message }}</span
|
||||
><template #footer
|
||||
@@ -138,7 +145,6 @@
|
||||
|
||||
<script>
|
||||
import * as api from '@/api/business/master-order';
|
||||
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
||||
import MasterOrderEditor from './components/master-order-editor.vue';
|
||||
import MasterOrderDispatch from './components/master-order-dispatch.vue';
|
||||
import MasterOrderDetail from './components/master-order-detail.vue';
|
||||
@@ -147,8 +153,6 @@ export default {
|
||||
components: { MasterOrderEditor, MasterOrderDispatch, MasterOrderDetail },
|
||||
data() {
|
||||
return {
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
searchExpanded: false,
|
||||
loading: false,
|
||||
records: [],
|
||||
@@ -303,7 +307,7 @@ export default {
|
||||
},
|
||||
timeRange(row) {
|
||||
return row.planStartTime && row.planEndTime
|
||||
? `${String(row.planStartTime).slice(0, 16)} - ${String(row.planEndTime).slice(0, 16)}`
|
||||
? `${String(row.planStartTime).slice(0, 10)} - ${String(row.planEndTime).slice(0, 10)}`
|
||||
: '-';
|
||||
},
|
||||
goodsSummary(row) {
|
||||
@@ -334,6 +338,19 @@ export default {
|
||||
const value = String(type || '').trim().toLowerCase();
|
||||
return value === 'road' || value.includes('公路');
|
||||
},
|
||||
isStationLikeName(value) {
|
||||
const text = String(value || '').trim();
|
||||
if (!text) return false;
|
||||
return /(?:火车站|高铁站|客运站|货运站|港口|码头|机场|空港|航站楼)$/.test(text) || /(?<!市|州|盟|区|县|旗)站$/.test(text);
|
||||
},
|
||||
isNonRoadLocation({ name, siteCode, transportType, nextTransportType } = {}) {
|
||||
const code = String(siteCode || '').trim();
|
||||
if (code && code !== '/') return true;
|
||||
if (this.isStationLikeName(name)) return true;
|
||||
if (transportType && !this.isRoadTransportType(transportType)) return true;
|
||||
if (nextTransportType && !this.isRoadTransportType(nextTransportType)) return true;
|
||||
return false;
|
||||
},
|
||||
formatRoadAddress(value) {
|
||||
const text = String(value || '').replace(/\s+/g, '');
|
||||
if (!text) return '-';
|
||||
@@ -355,10 +372,16 @@ export default {
|
||||
routeNodeName(value, address, transportType, region = {}) {
|
||||
const text = String(value || '').trim();
|
||||
if (!text) return '-';
|
||||
const siteCode = String(region.siteCode || '').trim();
|
||||
// 运输方式切换不应改变地址本身的展示模式:有站点编码时显示站点名称,
|
||||
// 没有站点编码的区域地址统一按“市 区县”格式展示。
|
||||
if (siteCode && siteCode !== '/') return text;
|
||||
if (
|
||||
this.isNonRoadLocation({
|
||||
name: text,
|
||||
siteCode: region.siteCode,
|
||||
transportType,
|
||||
nextTransportType: region.nextTransportType,
|
||||
})
|
||||
) {
|
||||
return text;
|
||||
}
|
||||
// 优先从完整的地址名称/详细地址解析,避免后端只返回“梅州”等不完整的市名称时
|
||||
// 提前返回城市,导致区县被遗漏。
|
||||
const parsedText = this.formatRoadAddress(text);
|
||||
@@ -383,86 +406,149 @@ export default {
|
||||
const source = /省|自治区|特别行政区|市|州|盟/.test(text) ? text : address || text;
|
||||
const formatted = this.formatRoadAddress(source);
|
||||
if (/(?:区|县|旗)$/.test(text) && !formatted.includes(text)) {
|
||||
const city = formatted.split(' ')[0];
|
||||
return city && city !== formatted ? `${city} ${text}` : formatted;
|
||||
const cityName = formatted.split(' ')[0];
|
||||
return cityName && cityName !== formatted ? `${cityName} ${text}` : formatted;
|
||||
}
|
||||
return formatted;
|
||||
},
|
||||
resolveRouteNodeLocation({
|
||||
name,
|
||||
address,
|
||||
transportType,
|
||||
nextTransportType,
|
||||
cityName,
|
||||
districtName,
|
||||
siteCode,
|
||||
} = {}) {
|
||||
const rawName = String(name || '').trim();
|
||||
const rawAddress = String(address || '').trim();
|
||||
const regionText = [cityName, districtName].filter(Boolean).join(' ');
|
||||
const nonRoad = this.isNonRoadLocation({
|
||||
name: rawName,
|
||||
siteCode,
|
||||
transportType,
|
||||
nextTransportType,
|
||||
});
|
||||
if (nonRoad) {
|
||||
// 非公路:展示站点名称,悬停展示详细地址。
|
||||
// 兼容历史数据把站点名称写在 address、区域信息写在 name 的情况。
|
||||
if (this.isStationLikeName(rawAddress) && !this.isStationLikeName(rawName)) {
|
||||
return {
|
||||
name: rawAddress,
|
||||
address: rawName || regionText || rawAddress,
|
||||
};
|
||||
}
|
||||
const displayName = rawName || rawAddress || '-';
|
||||
const tipAddress =
|
||||
(rawAddress && rawAddress !== displayName ? rawAddress : '') ||
|
||||
regionText ||
|
||||
rawAddress ||
|
||||
displayName;
|
||||
return { name: displayName, address: tipAddress };
|
||||
}
|
||||
return {
|
||||
name: this.routeNodeName(rawName || rawAddress, rawAddress, transportType, {
|
||||
cityName,
|
||||
districtName,
|
||||
siteCode,
|
||||
nextTransportType,
|
||||
}),
|
||||
address: rawAddress || rawName || regionText || '-',
|
||||
};
|
||||
},
|
||||
routeNodes(row = {}) {
|
||||
const routes = row.routeProgress || [];
|
||||
const total = this.routeNumber(row.totalQuantity);
|
||||
const firstTransportType = routes[0]?.transportType || row.routes?.[0]?.transportType || row.transportType || '';
|
||||
if (!routes.length) {
|
||||
const start = this.resolveRouteNodeLocation({
|
||||
name: row.departureName || row.departureAddress,
|
||||
address: row.departureAddress,
|
||||
transportType: firstTransportType,
|
||||
cityName: row.departureCityName,
|
||||
districtName: row.departureDistrictName,
|
||||
siteCode: row.departureSiteCode,
|
||||
});
|
||||
const end = this.resolveRouteNodeLocation({
|
||||
name: row.arrivalName || row.arrivalAddress,
|
||||
address: row.arrivalAddress,
|
||||
transportType: row.finalTransportType || firstTransportType,
|
||||
cityName: row.arrivalCityName,
|
||||
districtName: row.arrivalDistrictName,
|
||||
siteCode: row.arrivalSiteCode,
|
||||
});
|
||||
return [
|
||||
{
|
||||
key: 'start',
|
||||
type: 'start',
|
||||
text: '起',
|
||||
name: this.routeNodeName(
|
||||
row.departureName || row.departureAddress,
|
||||
row.departureAddress,
|
||||
firstTransportType,
|
||||
{
|
||||
cityName: row.departureCityName,
|
||||
districtName: row.departureDistrictName,
|
||||
siteCode: row.departureSiteCode,
|
||||
}
|
||||
),
|
||||
...start,
|
||||
lines: [`已调度0/${total}吨`],
|
||||
},
|
||||
{
|
||||
key: 'end',
|
||||
type: 'end',
|
||||
text: '终',
|
||||
name: this.routeNodeName(
|
||||
row.arrivalName || row.arrivalAddress,
|
||||
row.arrivalAddress,
|
||||
row.finalTransportType || firstTransportType,
|
||||
{
|
||||
cityName: row.arrivalCityName,
|
||||
districtName: row.arrivalDistrictName,
|
||||
siteCode: row.arrivalSiteCode,
|
||||
}
|
||||
),
|
||||
...end,
|
||||
lines: [`到达0/${total}吨`],
|
||||
},
|
||||
];
|
||||
}
|
||||
const start = this.resolveRouteNodeLocation({
|
||||
name: row.departureName || row.departureAddress,
|
||||
address: row.departureAddress,
|
||||
transportType: firstTransportType,
|
||||
nextTransportType: firstTransportType,
|
||||
cityName: row.departureCityName,
|
||||
districtName: row.departureDistrictName,
|
||||
siteCode: row.departureSiteCode,
|
||||
});
|
||||
return [
|
||||
{
|
||||
key: 'start',
|
||||
type: 'start',
|
||||
text: '起',
|
||||
name: this.routeNodeName(
|
||||
row.departureName || row.departureAddress,
|
||||
row.departureAddress,
|
||||
firstTransportType,
|
||||
{
|
||||
cityName: row.departureCityName,
|
||||
districtName: row.departureDistrictName,
|
||||
siteCode: row.departureSiteCode,
|
||||
}
|
||||
),
|
||||
...start,
|
||||
lines: [`已调度${this.routeNumber(routes[0]?.dispatchedQuantity)}/${total}吨`],
|
||||
},
|
||||
...routes.map((route, index) => {
|
||||
const isEnd = index === routes.length - 1;
|
||||
const arrived = this.routeNumber(this.routeArrivedQuantity(route));
|
||||
const dispatched = this.routeNumber(routes[index + 1]?.dispatchedQuantity);
|
||||
const address =
|
||||
route.arrivalAddress ||
|
||||
route.departureAddress ||
|
||||
(isEnd ? row.arrivalAddress : '') ||
|
||||
'';
|
||||
const location = this.resolveRouteNodeLocation({
|
||||
name:
|
||||
route.arrivalName ||
|
||||
route.departureName ||
|
||||
(isEnd ? row.arrivalName : '') ||
|
||||
'',
|
||||
address,
|
||||
transportType: route.transportType,
|
||||
nextTransportType: isEnd
|
||||
? row.finalTransportType || route.transportType
|
||||
: routes[index + 1]?.transportType || '',
|
||||
cityName:
|
||||
route.arrivalCityName ||
|
||||
route.departureCityName ||
|
||||
(isEnd ? row.arrivalCityName : ''),
|
||||
districtName:
|
||||
route.arrivalDistrictName ||
|
||||
route.departureDistrictName ||
|
||||
(isEnd ? row.arrivalDistrictName : ''),
|
||||
siteCode:
|
||||
route.arrivalSiteCode ||
|
||||
route.departureSiteCode ||
|
||||
(isEnd ? row.arrivalSiteCode : ''),
|
||||
});
|
||||
return {
|
||||
key: route.segmentNo || `route-${index}`,
|
||||
type: isEnd ? 'end' : 'middle',
|
||||
text: isEnd ? '终' : '经',
|
||||
name: this.routeNodeName(
|
||||
route.arrivalName || route.departureName || row.arrivalName,
|
||||
route.arrivalAddress || route.departureAddress,
|
||||
route.transportType,
|
||||
{
|
||||
cityName: route.arrivalCityName || route.departureCityName,
|
||||
districtName: route.arrivalDistrictName || route.departureDistrictName,
|
||||
siteCode: route.arrivalSiteCode || route.departureSiteCode,
|
||||
}
|
||||
),
|
||||
...location,
|
||||
lines: isEnd
|
||||
? [`到达${arrived}/${total}吨`]
|
||||
: [`到达 ${arrived}/${total}吨`, `已调度 ${dispatched}/到达${arrived}/${total}吨`],
|
||||
@@ -494,31 +580,43 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.master-search {
|
||||
margin-bottom: 12px;
|
||||
padding: 12px 12px 4px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
.el-form-item {
|
||||
.master-order-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: repeat(4, minmax(0, 1fr));
|
||||
gap: 8px 24px;
|
||||
}
|
||||
|
||||
&__search-actions {
|
||||
display: flex;
|
||||
grid-column: 1 / -1;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.el-input,
|
||||
.el-select,
|
||||
.el-date-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.search-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
min-height: 40px;
|
||||
.el-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
:deep(.el-input),
|
||||
:deep(.el-select),
|
||||
:deep(.el-date-editor.el-input),
|
||||
:deep(.el-date-editor.el-input__wrapper),
|
||||
:deep(.el-date-editor--datetimerange) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.master-list-panel {
|
||||
@@ -612,10 +710,16 @@ export default {
|
||||
line-height: 1.8;
|
||||
white-space: nowrap;
|
||||
strong {
|
||||
display: inline-block;
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
max-width: 180px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: bottom;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
.route-connector {
|
||||
|
||||
Reference in New Issue
Block a user