调试mk
This commit is contained in:
@@ -33,6 +33,25 @@ export const getPunchRecords = waybillId =>
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params: { waybillId },
|
params: { waybillId },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 运单车辆实时定位 */
|
||||||
|
export const locateVehicle = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/locate`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 运单车辆历史轨迹 */
|
||||||
|
export const trackVehicle = (id, startDate, endDate) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/track`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id, startDate, endDate },
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
export const roadLoading = ids =>
|
export const roadLoading = ids =>
|
||||||
request({
|
request({
|
||||||
url: `${baseUrl}/road-loading`,
|
url: `${baseUrl}/road-loading`,
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用 MK processSubmit 提交审核流
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {string} data.templateCode 模板编码
|
||||||
|
* @param {string} data.submitIdentity 提交人(手机号)
|
||||||
|
* @param {string} data.loginName 登录账号(手机号)
|
||||||
|
* @param {string} data.formInstanceId 业务表单实例 id
|
||||||
|
* @param {string} [data.subject] 流程标题
|
||||||
|
*/
|
||||||
|
export const processSubmit = data => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/businessProcess/processSubmit',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -48,6 +48,32 @@ export const syncIamOrganizations = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const syncOaCompany = (current = 1, size = 20, signal) => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/dept/sync-oa-company',
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
timeout: 60000,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const syncOaDepartment = (current = 1, size = 20, signal) => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/dept/sync-oa-department',
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
timeout: 60000,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const update = row => {
|
export const update = row => {
|
||||||
return request({
|
return request({
|
||||||
url: '/blade-system/dept/submit',
|
url: '/blade-system/dept/submit',
|
||||||
|
|||||||
@@ -22,6 +22,19 @@ export const getDetail = id => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getPublicDetail = id => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/customer-archive/public/detail',
|
||||||
|
method: 'get',
|
||||||
|
meta: {
|
||||||
|
isToken: false,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const getChangeRecordList = (customerId, current, size) => {
|
export const getChangeRecordList = (customerId, current, size) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/blade-transport/customer-archive/change-record/list',
|
url: '/blade-transport/customer-archive/change-record/list',
|
||||||
@@ -34,6 +47,21 @@ export const getChangeRecordList = (customerId, current, size) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getPublicChangeRecordList = (customerId, current, size) => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/customer-archive/public/change-record/list',
|
||||||
|
method: 'get',
|
||||||
|
meta: {
|
||||||
|
isToken: false,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
customerId,
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const submit = row => {
|
export const submit = row => {
|
||||||
return request({
|
return request({
|
||||||
url: '/blade-transport/customer-archive/submit',
|
url: '/blade-transport/customer-archive/submit',
|
||||||
|
|||||||
@@ -38,6 +38,20 @@ export const createOption = () => ({
|
|||||||
menuWidth: 240,
|
menuWidth: 240,
|
||||||
menuFixed: 'right',
|
menuFixed: 'right',
|
||||||
column: [
|
column: [
|
||||||
|
{
|
||||||
|
label: '计量单位编码',
|
||||||
|
prop: 'unitCode',
|
||||||
|
minWidth: 150,
|
||||||
|
search: true,
|
||||||
|
searchOrder: 4,
|
||||||
|
searchSpan: 6,
|
||||||
|
maxlength: 50,
|
||||||
|
showWordLimit: true,
|
||||||
|
rules: [
|
||||||
|
{ required: true, message: '请输入计量单位编码', trigger: 'blur' },
|
||||||
|
{ max: 50, message: '计量单位编码不能超过50个字', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '计量单位',
|
label: '计量单位',
|
||||||
prop: 'unitName',
|
prop: 'unitName',
|
||||||
|
|||||||
@@ -74,6 +74,16 @@ export default [
|
|||||||
isAuth: false,
|
isAuth: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/vehicle/customer-archive/public-view',
|
||||||
|
name: '查看客商信息',
|
||||||
|
component: () => import('@/views/vehicle/customer-archive-public-view.vue'),
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
isTab: false,
|
||||||
|
isAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: '主页',
|
name: '主页',
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ export default {
|
|||||||
return this.isAdmin || this.permission?.[code] === true;
|
return this.isAdmin || this.permission?.[code] === true;
|
||||||
},
|
},
|
||||||
normalizeRow(row) {
|
normalizeRow(row) {
|
||||||
|
row.unitCode = String(row.unitCode || '').trim();
|
||||||
row.unitName = String(row.unitName || '').trim();
|
row.unitName = String(row.unitName || '').trim();
|
||||||
row.dimension = String(row.dimension || '').trim();
|
row.dimension = String(row.dimension || '').trim();
|
||||||
row.remark = String(row.remark || '').trim();
|
row.remark = String(row.remark || '').trim();
|
||||||
|
|||||||
@@ -157,12 +157,19 @@
|
|||||||
<el-table-column label="计费单位" width="150"
|
<el-table-column label="计费单位" width="150"
|
||||||
><template #default="{ row }"
|
><template #default="{ row }"
|
||||||
><span v-if="readonly">{{ displayValue(row.billingUnit) }}</span
|
><span v-if="readonly">{{ displayValue(row.billingUnit) }}</span
|
||||||
><el-select v-else v-model="row.billingUnit" clearable filterable :loading="unitLoading"
|
><el-select
|
||||||
|
v-else
|
||||||
|
v-model="row.billingUnit"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:disabled="!row.billingElement"
|
||||||
|
:loading="unitLoading"
|
||||||
|
:placeholder="row.billingElement ? '请选择' : '请先选择计费要素'"
|
||||||
><el-option
|
><el-option
|
||||||
v-for="item in unitOptions"
|
v-for="item in unitOptionsFor(row)"
|
||||||
:key="item.id || item.dictKey || item.dictValue"
|
:key="item.id || item.value"
|
||||||
:label="item.dictValue"
|
:label="item.label"
|
||||||
:value="item.dictValue" /></el-select></template
|
:value="item.value" /></el-select></template
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<el-table-column label="单价(元)" width="180"
|
<el-table-column label="单价(元)" width="180"
|
||||||
><template #default="{ row }"
|
><template #default="{ row }"
|
||||||
@@ -367,12 +374,19 @@
|
|||||||
<script>
|
<script>
|
||||||
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
||||||
import { getList as getFeeItemList } from '@/api/base/fee-item';
|
import { getList as getFeeItemList } from '@/api/base/fee-item';
|
||||||
|
import { getList as getMeasurementUnitList } from '@/api/base/measurement-unit';
|
||||||
import { getLazyTree as getRegionLazyTree } from '@/api/base/region';
|
import { getLazyTree as getRegionLazyTree } from '@/api/base/region';
|
||||||
import { InfoFilled } from '@element-plus/icons-vue';
|
import { InfoFilled } from '@element-plus/icons-vue';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import SectionCard from '@/components/section-card/main.vue';
|
import SectionCard from '@/components/section-card/main.vue';
|
||||||
|
|
||||||
const clone = value => JSON.parse(JSON.stringify(value));
|
const clone = value => JSON.parse(JSON.stringify(value));
|
||||||
|
/** 计费要素与计量单位维度的对应关系 */
|
||||||
|
const BILLING_ELEMENT_DIMENSION_MAP = {
|
||||||
|
按重量: '重量',
|
||||||
|
按体积: '体积',
|
||||||
|
按车辆: '数量',
|
||||||
|
};
|
||||||
const defaultRule = () => ({
|
const defaultRule = () => ({
|
||||||
feeType: '',
|
feeType: '',
|
||||||
feeItem: '',
|
feeItem: '',
|
||||||
@@ -417,6 +431,7 @@ export default {
|
|||||||
feeItems: {},
|
feeItems: {},
|
||||||
feeItemLoadingMap: {},
|
feeItemLoadingMap: {},
|
||||||
unitOptions: [],
|
unitOptions: [],
|
||||||
|
measurementUnits: [],
|
||||||
unitLoading: false,
|
unitLoading: false,
|
||||||
billingElements: [
|
billingElements: [
|
||||||
'按重量',
|
'按重量',
|
||||||
@@ -614,15 +629,54 @@ export default {
|
|||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.feeCategoryLoading = false;
|
this.feeCategoryLoading = false;
|
||||||
});
|
});
|
||||||
|
this.loadUnitOptions();
|
||||||
|
},
|
||||||
|
loadUnitOptions() {
|
||||||
this.unitLoading = true;
|
this.unitLoading = true;
|
||||||
getDictionary({ code: 'unit_fee' })
|
Promise.all([
|
||||||
.then(res => {
|
getDictionary({ code: 'unit_fee' }).then(res => {
|
||||||
this.unitOptions = res.data?.data || [];
|
this.unitOptions = res.data?.data || [];
|
||||||
})
|
}),
|
||||||
.finally(() => {
|
getMeasurementUnitList(1, 9999, { status: 1 }).then(res => {
|
||||||
|
const data = res?.data?.data || res?.data || {};
|
||||||
|
const records = Array.isArray(data) ? data : data.records || [];
|
||||||
|
this.measurementUnits = records.filter(
|
||||||
|
item => item.status === undefined || item.status === null || Number(item.status) === 1
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
]).finally(() => {
|
||||||
this.unitLoading = false;
|
this.unitLoading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
measurementDimension(billingElement) {
|
||||||
|
return BILLING_ELEMENT_DIMENSION_MAP[billingElement] || '';
|
||||||
|
},
|
||||||
|
unitOptionsFor(row) {
|
||||||
|
const dimension = this.measurementDimension(row?.billingElement);
|
||||||
|
if (dimension) {
|
||||||
|
return this.measurementUnits
|
||||||
|
.filter(item => String(item.dimension || '').trim() === dimension)
|
||||||
|
.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
label: item.unitName,
|
||||||
|
value: item.unitName,
|
||||||
|
}))
|
||||||
|
.filter(item => item.value);
|
||||||
|
}
|
||||||
|
return (this.unitOptions || [])
|
||||||
|
.map(item => ({
|
||||||
|
id: item.id || item.dictKey || item.dictValue,
|
||||||
|
label: item.dictValue,
|
||||||
|
value: item.dictValue,
|
||||||
|
}))
|
||||||
|
.filter(item => item.value);
|
||||||
|
},
|
||||||
|
syncBillingUnit(row) {
|
||||||
|
const options = this.unitOptionsFor(row);
|
||||||
|
if (!options.some(item => String(item.value) === String(row.billingUnit || ''))) {
|
||||||
|
row.billingUnit = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
feeTypeKey(row) {
|
feeTypeKey(row) {
|
||||||
const option = this.feeCategories.find(
|
const option = this.feeCategories.find(
|
||||||
item =>
|
item =>
|
||||||
@@ -699,7 +753,14 @@ export default {
|
|||||||
return this.typeMap[row.billingElement] || [];
|
return this.typeMap[row.billingElement] || [];
|
||||||
},
|
},
|
||||||
handleElementChange(row) {
|
handleElementChange(row) {
|
||||||
if (!this.billingTypes(row).includes(row.billingType)) row.billingType = '';
|
const billingTypes = this.billingTypes(row);
|
||||||
|
if (!billingTypes.includes(row.billingType)) {
|
||||||
|
row.billingType =
|
||||||
|
row.billingElement === '固定金额(整单一口价)' && billingTypes.includes('固定一口价')
|
||||||
|
? '固定一口价'
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
this.syncBillingUnit(row);
|
||||||
if (!this.canEditMinimum(row)) {
|
if (!this.canEditMinimum(row)) {
|
||||||
row.minimumBillingWeight = '';
|
row.minimumBillingWeight = '';
|
||||||
row.limitRanges = (row.limitRanges || []).map(item => ({
|
row.limitRanges = (row.limitRanges || []).map(item => ({
|
||||||
|
|||||||
@@ -2212,13 +2212,105 @@
|
|||||||
<section-card title="物流轨迹">
|
<section-card title="物流轨迹">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<div class="waybill-manage-page__waybill-track-actions">
|
<div class="waybill-manage-page__waybill-track-actions">
|
||||||
<el-button plain @click="handleWaybillTrackAction('playback')"
|
<el-button
|
||||||
|
plain
|
||||||
|
:type="waybillTrackMode === 'playback' ? 'primary' : undefined"
|
||||||
|
:loading="waybillTrackLoading"
|
||||||
|
@click="handleWaybillTrackAction('playback')"
|
||||||
>轨迹回放</el-button
|
>轨迹回放</el-button
|
||||||
>
|
>
|
||||||
<el-button plain @click="handleWaybillTrackAction('locate')">实时定位</el-button>
|
<el-button
|
||||||
|
plain
|
||||||
|
:type="waybillTrackMode === 'locate' ? 'primary' : undefined"
|
||||||
|
:loading="waybillLocateLoading"
|
||||||
|
@click="handleWaybillTrackAction('locate')"
|
||||||
|
>实时定位</el-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="waybill-manage-page__waybill-map-empty">暂无数据</div>
|
<div
|
||||||
|
v-if="waybillTrackMode === 'playback'"
|
||||||
|
class="waybill-manage-page__waybill-track-toolbar"
|
||||||
|
>
|
||||||
|
<span>时间段</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="waybillTrackDateRange"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
:clearable="false"
|
||||||
|
:disabled="waybillTrackLoading"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:loading="waybillTrackLoading"
|
||||||
|
@click="loadWaybillTrack"
|
||||||
|
>查询</el-button
|
||||||
|
>
|
||||||
|
<span class="waybill-manage-page__waybill-track-total"
|
||||||
|
>轨迹点 {{ waybillTrackInfo?.total || 0 }} 个</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="waybillTrackMode === 'locate'"
|
||||||
|
class="waybill-manage-page__waybill-locate"
|
||||||
|
>
|
||||||
|
<div v-if="waybillLocateInfo" class="waybill-manage-page__waybill-locate-meta">
|
||||||
|
<div>
|
||||||
|
<span>车牌号</span><strong>{{ waybillLocateInfo.vehicleNo || '-' }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>定位时间</span><strong>{{ waybillLocateInfo.locateTime || '-' }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>速度</span><strong>{{ waybillLocateInfo.speed || '-' }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>方向</span><strong>{{ waybillLocateInfo.direction || '-' }}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="waybill-manage-page__waybill-locate-address">
|
||||||
|
<span>地址</span><strong>{{ waybillLocateInfo.address || '-' }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>坐标</span
|
||||||
|
><strong
|
||||||
|
>{{
|
||||||
|
waybillLocateInfo.longitude != null && waybillLocateInfo.latitude != null
|
||||||
|
? `${waybillLocateInfo.longitude}, ${waybillLocateInfo.latitude}`
|
||||||
|
: '-'
|
||||||
|
}}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ref="waybillLocateMap"
|
||||||
|
class="waybill-manage-page__waybill-locate-map"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="!waybillLocateInfo"
|
||||||
|
class="waybill-manage-page__waybill-map-tip"
|
||||||
|
>
|
||||||
|
{{ waybillLocateHint || '暂无数据' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="waybillTrackMode === 'playback'"
|
||||||
|
class="waybill-manage-page__waybill-locate"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref="waybillTrackMap"
|
||||||
|
class="waybill-manage-page__waybill-locate-map waybill-manage-page__waybill-locate-map--track"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="!(waybillTrackInfo?.points || []).length"
|
||||||
|
class="waybill-manage-page__waybill-map-tip"
|
||||||
|
>
|
||||||
|
{{ waybillTrackHint || '暂无数据' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="waybill-manage-page__waybill-map-empty">暂无数据</div>
|
||||||
</section-card>
|
</section-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -3029,7 +3121,7 @@ import {
|
|||||||
getList as getProcessConfigList,
|
getList as getProcessConfigList,
|
||||||
getVoucherImages as getProcessConfigVoucherImages,
|
getVoucherImages as getProcessConfigVoucherImages,
|
||||||
} from '@/api/business/process-config';
|
} from '@/api/business/process-config';
|
||||||
import { getPunchRecords as getWaybillPunchRecords } from '@/api/business/waybill-manage';
|
import { getPunchRecords as getWaybillPunchRecords, locateVehicle, trackVehicle } from '@/api/business/waybill-manage';
|
||||||
import { getList as getDriverList } from '@/api/transportCapacity/driver';
|
import { getList as getDriverList } from '@/api/transportCapacity/driver';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
||||||
@@ -3292,6 +3384,21 @@ export default {
|
|||||||
waybillRouteChangeRecords: [],
|
waybillRouteChangeRecords: [],
|
||||||
waybillRouteChangeDragIndex: -1,
|
waybillRouteChangeDragIndex: -1,
|
||||||
waybillRouteChangeSaving: false,
|
waybillRouteChangeSaving: false,
|
||||||
|
waybillLocateLoading: false,
|
||||||
|
waybillLocateInfo: null,
|
||||||
|
waybillLocateHint: '',
|
||||||
|
waybillLocateMapInstance: null,
|
||||||
|
waybillLocateMarker: null,
|
||||||
|
waybillLocateInfoWindow: null,
|
||||||
|
waybillTrackMode: '',
|
||||||
|
waybillTrackLoading: false,
|
||||||
|
waybillTrackInfo: null,
|
||||||
|
waybillTrackHint: '',
|
||||||
|
waybillTrackDateRange: [],
|
||||||
|
waybillTrackMapInstance: null,
|
||||||
|
waybillTrackPolyline: null,
|
||||||
|
waybillTrackStartMarker: null,
|
||||||
|
waybillTrackEndMarker: null,
|
||||||
mileageDialog: {
|
mileageDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
submitting: false,
|
submitting: false,
|
||||||
@@ -4523,6 +4630,13 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
closeDetail() {
|
closeDetail() {
|
||||||
|
this.destroyWaybillLocateMap();
|
||||||
|
this.destroyWaybillTrackMap();
|
||||||
|
this.waybillTrackMode = '';
|
||||||
|
this.waybillLocateInfo = null;
|
||||||
|
this.waybillLocateHint = '';
|
||||||
|
this.waybillTrackInfo = null;
|
||||||
|
this.waybillTrackHint = '';
|
||||||
if (this.isStandaloneWaybillDetailPage) {
|
if (this.isStandaloneWaybillDetailPage) {
|
||||||
this.$router.$avueRouter?.closeTag?.();
|
this.$router.$avueRouter?.closeTag?.();
|
||||||
this.$router.push({ path: '/business/waybill-manage', query: {} });
|
this.$router.push({ path: '/business/waybill-manage', query: {} });
|
||||||
@@ -4867,7 +4981,262 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleWaybillTrackAction(action) {
|
handleWaybillTrackAction(action) {
|
||||||
this.$message.info(action === 'playback' ? '暂无可回放的物流轨迹' : '暂无车辆实时定位数据');
|
if (action === 'playback') {
|
||||||
|
this.destroyWaybillLocateMap();
|
||||||
|
this.waybillTrackMode = 'playback';
|
||||||
|
this.waybillLocateInfo = null;
|
||||||
|
this.waybillLocateHint = '';
|
||||||
|
if (!Array.isArray(this.waybillTrackDateRange) || this.waybillTrackDateRange.length !== 2) {
|
||||||
|
this.waybillTrackDateRange = this.buildDefaultTrackDateRange();
|
||||||
|
}
|
||||||
|
this.loadWaybillTrack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.destroyWaybillTrackMap();
|
||||||
|
this.waybillTrackMode = 'locate';
|
||||||
|
this.waybillTrackInfo = null;
|
||||||
|
this.waybillTrackHint = '';
|
||||||
|
this.loadWaybillLocate();
|
||||||
|
},
|
||||||
|
buildDefaultTrackDateRange() {
|
||||||
|
const pad = value => String(value).padStart(2, '0');
|
||||||
|
const formatDate = date =>
|
||||||
|
`${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
||||||
|
const today = new Date();
|
||||||
|
const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
|
||||||
|
return [formatDate(yesterday), formatDate(today)];
|
||||||
|
},
|
||||||
|
destroyWaybillLocateMap() {
|
||||||
|
if (this.waybillLocateMarker) {
|
||||||
|
this.waybillLocateMarker.setMap(null);
|
||||||
|
this.waybillLocateMarker = null;
|
||||||
|
}
|
||||||
|
if (this.waybillLocateInfoWindow) {
|
||||||
|
this.waybillLocateInfoWindow.close();
|
||||||
|
this.waybillLocateInfoWindow = null;
|
||||||
|
}
|
||||||
|
if (this.waybillLocateMapInstance) {
|
||||||
|
this.waybillLocateMapInstance.destroy();
|
||||||
|
this.waybillLocateMapInstance = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyWaybillTrackMap() {
|
||||||
|
if (this.waybillTrackPolyline) {
|
||||||
|
this.waybillTrackPolyline.setMap(null);
|
||||||
|
this.waybillTrackPolyline = null;
|
||||||
|
}
|
||||||
|
if (this.waybillTrackStartMarker) {
|
||||||
|
this.waybillTrackStartMarker.setMap(null);
|
||||||
|
this.waybillTrackStartMarker = null;
|
||||||
|
}
|
||||||
|
if (this.waybillTrackEndMarker) {
|
||||||
|
this.waybillTrackEndMarker.setMap(null);
|
||||||
|
this.waybillTrackEndMarker = null;
|
||||||
|
}
|
||||||
|
if (this.waybillTrackMapInstance) {
|
||||||
|
this.waybillTrackMapInstance.destroy();
|
||||||
|
this.waybillTrackMapInstance = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadWaybillLocate() {
|
||||||
|
const waybillId = this.detailRow?.id;
|
||||||
|
if (!waybillId) {
|
||||||
|
this.$message.warning('运单信息不完整');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.detailRow.vehicleNo) {
|
||||||
|
this.$message.warning('运单未绑定车牌号,无法实时定位');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.waybillLocateLoading = true;
|
||||||
|
this.waybillLocateHint = '正在获取车辆实时定位...';
|
||||||
|
this.waybillLocateInfo = null;
|
||||||
|
try {
|
||||||
|
const locateApi =
|
||||||
|
typeof this.api.locateVehicle === 'function' ? this.api.locateVehicle : locateVehicle;
|
||||||
|
const res = await locateApi(waybillId);
|
||||||
|
const data = res?.data?.data || null;
|
||||||
|
if (!data || data.longitude == null || data.latitude == null) {
|
||||||
|
this.waybillLocateInfo = null;
|
||||||
|
this.waybillLocateHint = '暂无车辆实时定位数据';
|
||||||
|
this.$message.warning('暂无车辆实时定位数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.waybillLocateInfo = data;
|
||||||
|
this.waybillLocateHint = '';
|
||||||
|
await this.$nextTick();
|
||||||
|
await this.renderWaybillLocateMap(data);
|
||||||
|
} catch (error) {
|
||||||
|
this.waybillLocateInfo = null;
|
||||||
|
this.waybillLocateHint = '实时定位获取失败';
|
||||||
|
this.$message.error(error?.message || '实时定位获取失败');
|
||||||
|
} finally {
|
||||||
|
this.waybillLocateLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadWaybillTrack() {
|
||||||
|
const waybillId = this.detailRow?.id;
|
||||||
|
if (!waybillId) {
|
||||||
|
this.$message.warning('运单信息不完整');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.detailRow.vehicleNo) {
|
||||||
|
this.$message.warning('运单未绑定车牌号,无法查询历史轨迹');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(this.waybillTrackDateRange) || this.waybillTrackDateRange.length !== 2) {
|
||||||
|
this.waybillTrackDateRange = this.buildDefaultTrackDateRange();
|
||||||
|
}
|
||||||
|
const [startDate, endDate] = this.waybillTrackDateRange;
|
||||||
|
this.waybillTrackLoading = true;
|
||||||
|
this.waybillTrackHint = '正在获取历史轨迹...';
|
||||||
|
this.destroyWaybillTrackMap();
|
||||||
|
try {
|
||||||
|
const trackApi =
|
||||||
|
typeof this.api.trackVehicle === 'function' ? this.api.trackVehicle : trackVehicle;
|
||||||
|
const res = await trackApi(waybillId, startDate, endDate);
|
||||||
|
const data = res?.data?.data || null;
|
||||||
|
const points = Array.isArray(data?.points) ? data.points : [];
|
||||||
|
if (!data || points.length === 0) {
|
||||||
|
this.waybillTrackInfo = data || { total: 0, points: [] };
|
||||||
|
this.waybillTrackHint = '暂无可回放的物流轨迹';
|
||||||
|
this.$message.warning('暂无可回放的物流轨迹');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.waybillTrackInfo = data;
|
||||||
|
this.waybillTrackHint = '';
|
||||||
|
await this.$nextTick();
|
||||||
|
await this.renderWaybillTrackMap(points);
|
||||||
|
} catch (error) {
|
||||||
|
this.waybillTrackInfo = null;
|
||||||
|
this.waybillTrackHint = '历史轨迹获取失败';
|
||||||
|
this.$message.error(error?.message || '历史轨迹获取失败');
|
||||||
|
} finally {
|
||||||
|
this.waybillTrackLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async renderWaybillLocateMap(locateInfo) {
|
||||||
|
const longitude = Number(locateInfo?.longitude);
|
||||||
|
const latitude = Number(locateInfo?.latitude);
|
||||||
|
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) {
|
||||||
|
this.$message.warning('定位坐标无效,无法在地图上展示');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.loadAmap();
|
||||||
|
await this.$nextTick();
|
||||||
|
const container = this.$refs.waybillLocateMap;
|
||||||
|
if (!container || !window.AMap) {
|
||||||
|
this.$message.warning('高德地图容器未就绪');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.destroyWaybillLocateMap();
|
||||||
|
this.waybillLocateMapInstance = new window.AMap.Map(container, {
|
||||||
|
zoom: 15,
|
||||||
|
center: [longitude, latitude],
|
||||||
|
viewMode: '2D',
|
||||||
|
resizeEnable: true,
|
||||||
|
});
|
||||||
|
const content = [
|
||||||
|
`<div style="padding:4px 2px;line-height:1.6;font-size:12px;">`,
|
||||||
|
`<div><b>${locateInfo.vehicleNo || '车辆位置'}</b></div>`,
|
||||||
|
locateInfo.locateTime ? `<div>时间:${locateInfo.locateTime}</div>` : '',
|
||||||
|
locateInfo.address ? `<div>地址:${locateInfo.address}</div>` : '',
|
||||||
|
`<div>坐标:${longitude}, ${latitude}</div>`,
|
||||||
|
`</div>`,
|
||||||
|
].join('');
|
||||||
|
this.waybillLocateInfoWindow = new window.AMap.InfoWindow({
|
||||||
|
content,
|
||||||
|
offset: new window.AMap.Pixel(0, -30),
|
||||||
|
});
|
||||||
|
this.waybillLocateMarker = new window.AMap.Marker({
|
||||||
|
position: [longitude, latitude],
|
||||||
|
title: locateInfo.vehicleNo || '车辆位置',
|
||||||
|
anchor: 'bottom-center',
|
||||||
|
});
|
||||||
|
this.waybillLocateMarker.on('click', () => {
|
||||||
|
this.waybillLocateInfoWindow.open(this.waybillLocateMapInstance, [longitude, latitude]);
|
||||||
|
});
|
||||||
|
this.waybillLocateMapInstance.add(this.waybillLocateMarker);
|
||||||
|
this.waybillLocateInfoWindow.open(this.waybillLocateMapInstance, [longitude, latitude]);
|
||||||
|
this.waybillLocateMapInstance.setFitView([this.waybillLocateMarker], false, [40, 40, 40, 40]);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.waybillLocateMapInstance?.resize?.();
|
||||||
|
}, 80);
|
||||||
|
} catch (error) {
|
||||||
|
window.console.warn('实时定位地图渲染失败', error);
|
||||||
|
this.$message.error('高德地图渲染失败,请稍后重试');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async renderWaybillTrackMap(points) {
|
||||||
|
const path = (points || [])
|
||||||
|
.map(item => [Number(item.longitude), Number(item.latitude)])
|
||||||
|
.filter(item => Number.isFinite(item[0]) && Number.isFinite(item[1]));
|
||||||
|
if (!path.length) {
|
||||||
|
this.$message.warning('轨迹坐标无效,无法在地图上展示');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.loadAmap();
|
||||||
|
await this.$nextTick();
|
||||||
|
const container = this.$refs.waybillTrackMap;
|
||||||
|
if (!container || !window.AMap) {
|
||||||
|
this.$message.warning('高德地图容器未就绪');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.destroyWaybillTrackMap();
|
||||||
|
this.waybillTrackMapInstance = new window.AMap.Map(container, {
|
||||||
|
zoom: 12,
|
||||||
|
center: path[0],
|
||||||
|
viewMode: '2D',
|
||||||
|
resizeEnable: true,
|
||||||
|
});
|
||||||
|
this.waybillTrackPolyline = new window.AMap.Polyline({
|
||||||
|
path,
|
||||||
|
strokeColor: '#409eff',
|
||||||
|
strokeWeight: 6,
|
||||||
|
strokeOpacity: 0.9,
|
||||||
|
lineJoin: 'round',
|
||||||
|
lineCap: 'round',
|
||||||
|
showDir: path.length > 1,
|
||||||
|
});
|
||||||
|
this.waybillTrackStartMarker = new window.AMap.Marker({
|
||||||
|
position: path[0],
|
||||||
|
title: '起点',
|
||||||
|
anchor: 'bottom-center',
|
||||||
|
label: {
|
||||||
|
content: '起',
|
||||||
|
direction: 'top',
|
||||||
|
offset: new window.AMap.Pixel(0, -6),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.waybillTrackEndMarker = new window.AMap.Marker({
|
||||||
|
position: path[path.length - 1],
|
||||||
|
title: '终点',
|
||||||
|
anchor: 'bottom-center',
|
||||||
|
label: {
|
||||||
|
content: '终',
|
||||||
|
direction: 'top',
|
||||||
|
offset: new window.AMap.Pixel(0, -6),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.waybillTrackMapInstance.add([
|
||||||
|
this.waybillTrackPolyline,
|
||||||
|
this.waybillTrackStartMarker,
|
||||||
|
this.waybillTrackEndMarker,
|
||||||
|
]);
|
||||||
|
this.waybillTrackMapInstance.setFitView(
|
||||||
|
[this.waybillTrackPolyline, this.waybillTrackStartMarker, this.waybillTrackEndMarker],
|
||||||
|
false,
|
||||||
|
[48, 48, 48, 48]
|
||||||
|
);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.waybillTrackMapInstance?.resize?.();
|
||||||
|
}, 80);
|
||||||
|
} catch (error) {
|
||||||
|
window.console.warn('历史轨迹地图渲染失败', error);
|
||||||
|
this.$message.error('高德地图渲染失败,请稍后重试');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
displayStatus(row, prop) {
|
displayStatus(row, prop) {
|
||||||
const formatStatus = this.config.formatStatus;
|
const formatStatus = this.config.formatStatus;
|
||||||
@@ -9553,6 +9922,81 @@ export default {
|
|||||||
border: 1px solid #eff1f7;
|
border: 1px solid #eff1f7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__waybill-locate {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__waybill-locate-meta {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 8px 16px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid #eff1f7;
|
||||||
|
background: #fafafa;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__waybill-locate-address {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__waybill-locate-map {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 320px;
|
||||||
|
height: 320px;
|
||||||
|
border: 1px solid #eff1f7;
|
||||||
|
background: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__waybill-locate-map--track {
|
||||||
|
min-height: 420px;
|
||||||
|
height: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__waybill-map-tip {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
z-index: 2;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.92);
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||||
|
color: #909399;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__waybill-track-toolbar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__waybill-track-total {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
&__waybill-track-actions {
|
&__waybill-track-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|||||||
@@ -1116,6 +1116,10 @@ export default {
|
|||||||
total: 0,
|
total: 0,
|
||||||
},
|
},
|
||||||
selectionList: [],
|
selectionList: [],
|
||||||
|
// 表单页实例标记:创建时按路由落定,不随 keep-alive 切走后的 $route 变化翻转。
|
||||||
|
// 否则 isProjectFormPage 变 false 后容器会从 div 切成 el-dialog(append-to-body),
|
||||||
|
// 盖住后续打开的页面(如客商档案)。
|
||||||
|
isFormPageInstance: false,
|
||||||
projectBox: false,
|
projectBox: false,
|
||||||
dialogType: 'add',
|
dialogType: 'add',
|
||||||
dialogReadonly: false,
|
dialogReadonly: false,
|
||||||
@@ -1308,13 +1312,14 @@ export default {
|
|||||||
return ['add', 'majorSupplement'].includes(this.dialogType);
|
return ['add', 'majorSupplement'].includes(this.dialogType);
|
||||||
},
|
},
|
||||||
isProjectFormPage() {
|
isProjectFormPage() {
|
||||||
return this.$route.path === '/business/project-apply/form';
|
return this.isFormPageInstance;
|
||||||
},
|
},
|
||||||
projectFormContainer() {
|
projectFormContainer() {
|
||||||
return this.isProjectFormPage ? 'div' : 'el-dialog';
|
// 表单页实例始终用页面容器,避免 keep-alive 失活时误切弹窗
|
||||||
|
return this.isFormPageInstance ? 'div' : 'el-dialog';
|
||||||
},
|
},
|
||||||
projectFormContainerProps() {
|
projectFormContainerProps() {
|
||||||
if (this.isProjectFormPage) return { class: 'project-apply-page-form' };
|
if (this.isFormPageInstance) return { class: 'project-apply-page-form' };
|
||||||
return {
|
return {
|
||||||
modelValue: this.projectBox,
|
modelValue: this.projectBox,
|
||||||
title: this.projectDialogTitle,
|
title: this.projectDialogTitle,
|
||||||
@@ -1359,11 +1364,26 @@ export default {
|
|||||||
this.loadCargoTypeOptions();
|
this.loadCargoTypeOptions();
|
||||||
this.loadTransportTypeOptions();
|
this.loadTransportTypeOptions();
|
||||||
this.loadSettlementModeOptions();
|
this.loadSettlementModeOptions();
|
||||||
if (this.isProjectFormPage) {
|
if (this.$route.path === '/business/project-apply/form') {
|
||||||
|
this.isFormPageInstance = true;
|
||||||
this.openProjectFormPage();
|
this.openProjectFormPage();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 标签切走(keep-alive 缓存)或销毁时,收起 append-to-body 的内层弹窗,
|
||||||
|
// 避免 Teleport 出去的 DOM 继续盖住后续页面。
|
||||||
|
deactivated() {
|
||||||
|
this.closeInnerDialogs();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.closeInnerDialogs();
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
closeInnerDialogs() {
|
||||||
|
this.changeRecordDetailVisible = false;
|
||||||
|
this.attachmentDocumentPreviewVisible = false;
|
||||||
|
this.attachmentImagePreviewVisible = false;
|
||||||
|
this.userBox = false;
|
||||||
|
},
|
||||||
buildTableOption() {
|
buildTableOption() {
|
||||||
return {
|
return {
|
||||||
...option,
|
...option,
|
||||||
|
|||||||
@@ -30,6 +30,14 @@
|
|||||||
@click="handleDelete"
|
@click="handleDelete"
|
||||||
>删除
|
>删除
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
:loading="oaSyncLoading"
|
||||||
|
v-if="userInfo.authority.includes('admin')"
|
||||||
|
@click="handleOaOrgSync"
|
||||||
|
>自动同步组织
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template #menu="scope">
|
<template #menu="scope">
|
||||||
<el-link
|
<el-link
|
||||||
@@ -116,6 +124,46 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<el-dialog
|
||||||
|
v-model="oaSyncVisible"
|
||||||
|
width="480px"
|
||||||
|
append-to-body
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="!oaSyncRunning"
|
||||||
|
:show-close="!oaSyncRunning"
|
||||||
|
class="oa-sync-dialog"
|
||||||
|
@close="closeOaSyncDialog"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<span class="dialog-title">{{ oaSyncDialogTitle }}</span>
|
||||||
|
</template>
|
||||||
|
<div class="oa-sync-body">
|
||||||
|
<el-progress
|
||||||
|
:percentage="oaSyncPercent"
|
||||||
|
:status="oaSyncProgressStatus"
|
||||||
|
:stroke-width="12"
|
||||||
|
/>
|
||||||
|
<div class="oa-sync-meta">
|
||||||
|
当前阶段:{{ oaSyncStageLabel }},第 {{ oaSyncProgress.current || 0 }} / {{ oaSyncTotalPage }} 页
|
||||||
|
</div>
|
||||||
|
<div class="oa-sync-stats">
|
||||||
|
<div class="oa-sync-stat">
|
||||||
|
<span class="oa-sync-stat__label">同步成功</span>
|
||||||
|
<span class="oa-sync-stat__value oa-sync-stat__value--success">{{ oaSyncProgress.synced }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="oa-sync-stat">
|
||||||
|
<span class="oa-sync-stat__label">跳过</span>
|
||||||
|
<span class="oa-sync-stat__value oa-sync-stat__value--skip">{{ oaSyncProgress.skipped }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button v-if="oaSyncRunning" @click="cancelOaSync">取消</el-button>
|
||||||
|
<el-button v-else type="primary" @click="closeOaSyncDialog">关闭</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</basic-container>
|
</basic-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -127,6 +175,8 @@ import {
|
|||||||
add,
|
add,
|
||||||
getDept,
|
getDept,
|
||||||
getDeptTree,
|
getDeptTree,
|
||||||
|
syncOaCompany,
|
||||||
|
syncOaDepartment,
|
||||||
} from '@/api/system/dept';
|
} from '@/api/system/dept';
|
||||||
import { getLeaderList } from '@/api/system/user';
|
import { getLeaderList } from '@/api/system/user';
|
||||||
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
|
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
|
||||||
@@ -159,6 +209,20 @@ export default {
|
|||||||
selectionList: [],
|
selectionList: [],
|
||||||
query: {},
|
query: {},
|
||||||
loading: true,
|
loading: true,
|
||||||
|
oaSyncLoading: false,
|
||||||
|
oaSyncVisible: false,
|
||||||
|
oaSyncRunning: false,
|
||||||
|
oaSyncCancelled: false,
|
||||||
|
oaSyncStatus: 'running',
|
||||||
|
oaSyncStage: 'company',
|
||||||
|
oaSyncController: null,
|
||||||
|
oaSyncProgress: {
|
||||||
|
current: 0,
|
||||||
|
size: 20,
|
||||||
|
total: 0,
|
||||||
|
synced: 0,
|
||||||
|
skipped: 0,
|
||||||
|
},
|
||||||
parentId: 0,
|
parentId: 0,
|
||||||
page: {
|
page: {
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@@ -443,8 +507,153 @@ export default {
|
|||||||
});
|
});
|
||||||
return ids.join(',');
|
return ids.join(',');
|
||||||
},
|
},
|
||||||
|
oaSyncDialogTitle() {
|
||||||
|
if (this.oaSyncStatus === 'done') {
|
||||||
|
return '同步完成';
|
||||||
|
}
|
||||||
|
if (this.oaSyncStatus === 'cancelled') {
|
||||||
|
return '已取消同步';
|
||||||
|
}
|
||||||
|
if (this.oaSyncStatus === 'error') {
|
||||||
|
return '同步失败';
|
||||||
|
}
|
||||||
|
return '自动同步组织';
|
||||||
|
},
|
||||||
|
oaSyncStageLabel() {
|
||||||
|
return this.oaSyncStage === 'department' ? '同步部门' : '同步公司';
|
||||||
|
},
|
||||||
|
oaSyncTotalPage() {
|
||||||
|
const total = Number(this.oaSyncProgress.total) || 0;
|
||||||
|
const size = Number(this.oaSyncProgress.size) || 20;
|
||||||
|
if (total <= 0) {
|
||||||
|
return this.oaSyncProgress.current || 0;
|
||||||
|
}
|
||||||
|
return Math.max(1, Math.ceil(total / size));
|
||||||
|
},
|
||||||
|
oaSyncPercent() {
|
||||||
|
if (this.oaSyncStatus === 'done') {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
const totalPage = this.oaSyncTotalPage;
|
||||||
|
const current = Number(this.oaSyncProgress.current) || 0;
|
||||||
|
const stageBase = this.oaSyncStage === 'department' ? 50 : 0;
|
||||||
|
if (totalPage <= 0) {
|
||||||
|
return stageBase;
|
||||||
|
}
|
||||||
|
const stagePercent = Math.min(50, Math.round((current / totalPage) * 50));
|
||||||
|
return Math.min(99, stageBase + stagePercent);
|
||||||
|
},
|
||||||
|
oaSyncProgressStatus() {
|
||||||
|
if (this.oaSyncStatus === 'done') {
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
if (this.oaSyncStatus === 'error') {
|
||||||
|
return 'exception';
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleOaOrgSync() {
|
||||||
|
this.$confirm('确定从OA先同步公司、再同步部门?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
this.startOaOrgSync();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
startOaOrgSync() {
|
||||||
|
this.oaSyncLoading = true;
|
||||||
|
this.oaSyncVisible = true;
|
||||||
|
this.oaSyncRunning = true;
|
||||||
|
this.oaSyncCancelled = false;
|
||||||
|
this.oaSyncStatus = 'running';
|
||||||
|
this.oaSyncStage = 'company';
|
||||||
|
this.oaSyncController = new AbortController();
|
||||||
|
this.oaSyncProgress = {
|
||||||
|
current: 0,
|
||||||
|
size: 20,
|
||||||
|
total: 0,
|
||||||
|
synced: 0,
|
||||||
|
skipped: 0,
|
||||||
|
};
|
||||||
|
this.runOaOrgSyncPages();
|
||||||
|
},
|
||||||
|
isOaSyncCanceledError(error) {
|
||||||
|
return (
|
||||||
|
this.oaSyncCancelled ||
|
||||||
|
error?.code === 'ERR_CANCELED' ||
|
||||||
|
error?.name === 'CanceledError' ||
|
||||||
|
error?.name === 'AbortError'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
async runOaOrgSyncStage(syncApi) {
|
||||||
|
const size = 20;
|
||||||
|
let current = 1;
|
||||||
|
while (!this.oaSyncCancelled) {
|
||||||
|
const res = await syncApi(current, size, this.oaSyncController?.signal);
|
||||||
|
const page = res?.data?.data || {};
|
||||||
|
this.oaSyncProgress.current = page.current || current;
|
||||||
|
this.oaSyncProgress.size = page.size || size;
|
||||||
|
this.oaSyncProgress.total = page.total || 0;
|
||||||
|
this.oaSyncProgress.synced += page.syncedCount || 0;
|
||||||
|
this.oaSyncProgress.skipped += page.skippedCount || 0;
|
||||||
|
if (page.finished) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current += 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async runOaOrgSyncPages() {
|
||||||
|
try {
|
||||||
|
this.oaSyncStage = 'company';
|
||||||
|
await this.runOaOrgSyncStage(syncOaCompany);
|
||||||
|
if (this.oaSyncCancelled) {
|
||||||
|
this.oaSyncStatus = 'cancelled';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.oaSyncStage = 'department';
|
||||||
|
this.oaSyncProgress.current = 0;
|
||||||
|
this.oaSyncProgress.total = 0;
|
||||||
|
await this.runOaOrgSyncStage(syncOaDepartment);
|
||||||
|
if (this.oaSyncCancelled) {
|
||||||
|
this.oaSyncStatus = 'cancelled';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.oaSyncStatus = 'done';
|
||||||
|
} catch (error) {
|
||||||
|
if (this.isOaSyncCanceledError(error)) {
|
||||||
|
this.oaSyncStatus = 'cancelled';
|
||||||
|
} else {
|
||||||
|
this.oaSyncStatus = 'error';
|
||||||
|
this.$message.error(error?.message || 'OA组织同步失败');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.oaSyncRunning = false;
|
||||||
|
this.oaSyncLoading = false;
|
||||||
|
if (this.oaSyncStatus === 'done' || this.oaSyncStatus === 'cancelled') {
|
||||||
|
this.parentId = 0;
|
||||||
|
this.data = [];
|
||||||
|
this.$refs.crud?.refreshTable?.();
|
||||||
|
this.onLoad(this.page, this.query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancelOaSync() {
|
||||||
|
if (!this.oaSyncRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.oaSyncCancelled = true;
|
||||||
|
this.oaSyncController?.abort();
|
||||||
|
},
|
||||||
|
closeOaSyncDialog() {
|
||||||
|
if (this.oaSyncRunning) {
|
||||||
|
this.cancelOaSync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.oaSyncVisible = false;
|
||||||
|
},
|
||||||
initData(tenantId) {
|
initData(tenantId) {
|
||||||
getDeptTree(tenantId).then(res => {
|
getDeptTree(tenantId).then(res => {
|
||||||
const column = this.findColumn(this.option.column, 'parentId');
|
const column = this.findColumn(this.option.column, 'parentId');
|
||||||
@@ -865,3 +1074,66 @@ export default {
|
|||||||
color: var(--el-color-danger-light-3);
|
color: var(--el-color-danger-light-3);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.oa-sync-dialog .dialog-title {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oa-sync-dialog .dialog-title::before {
|
||||||
|
width: 4px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 8px;
|
||||||
|
background: #409eff;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
.oa-sync-body {
|
||||||
|
padding: 8px 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oa-sync-meta {
|
||||||
|
margin-top: 12px;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oa-sync-stats {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oa-sync-stat {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: #fafafa;
|
||||||
|
border: 1px solid #eff1f7;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oa-sync-stat__label {
|
||||||
|
display: block;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oa-sync-stat__value {
|
||||||
|
display: block;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oa-sync-stat__value--success {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oa-sync-stat__value--skip {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="page" class="customer-archive-public-view">
|
||||||
|
<customer-archive />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CustomerArchive from './customer-archive.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CustomerArchivePublicView',
|
||||||
|
components: {
|
||||||
|
CustomerArchive,
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.handleIframeHeight = () => this.sendIframeHeight();
|
||||||
|
document.addEventListener('DOMContentLoaded', this.handleIframeHeight, false);
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.documentElement.classList.add('mk-iframe-page');
|
||||||
|
document.body.classList.add('mk-iframe-page');
|
||||||
|
const app = document.getElementById('app');
|
||||||
|
if (app) app.classList.add('mk-iframe-page');
|
||||||
|
this.handleIframeHeight();
|
||||||
|
window.addEventListener('load', this.handleIframeHeight);
|
||||||
|
this.mkHeightTimers = [300, 800, 1600].map(delay =>
|
||||||
|
setTimeout(this.handleIframeHeight, delay)
|
||||||
|
);
|
||||||
|
if (typeof ResizeObserver === 'undefined') return;
|
||||||
|
this.mkHeightObserver = new ResizeObserver(() => this.handleIframeHeight());
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.page) this.mkHeightObserver.observe(this.$refs.page);
|
||||||
|
this.mkHeightObserver.observe(document.body);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
document.removeEventListener('DOMContentLoaded', this.handleIframeHeight, false);
|
||||||
|
window.removeEventListener('load', this.handleIframeHeight);
|
||||||
|
(this.mkHeightTimers || []).forEach(timer => clearTimeout(timer));
|
||||||
|
if (this.mkHeightObserver) {
|
||||||
|
this.mkHeightObserver.disconnect();
|
||||||
|
this.mkHeightObserver = null;
|
||||||
|
}
|
||||||
|
document.documentElement.classList.remove('mk-iframe-page');
|
||||||
|
document.body.classList.remove('mk-iframe-page');
|
||||||
|
const app = document.getElementById('app');
|
||||||
|
if (app) app.classList.remove('mk-iframe-page');
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
sendIframeHeight() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
var tbody = document.body;
|
||||||
|
var height = tbody.clientHeight;
|
||||||
|
// 如需动态改变表单高度,可以直接发送此postMessage
|
||||||
|
window.parent.postMessage({ height: height }, '*');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
html.mk-iframe-page,
|
||||||
|
html.mk-iframe-page body,
|
||||||
|
html.mk-iframe-page #app,
|
||||||
|
html.mk-iframe-page #app.mk-iframe-page {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 100%;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.customer-archive-public-view {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 12px 0 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #f0f2f5;
|
||||||
|
|
||||||
|
:deep(.basic-container) {
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.archive-page-form .archive-form__footer) {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<basic-container class="customer-archive-page">
|
<basic-container class="customer-archive-page" :class="{ 'is-public-view': isPublicViewPage }">
|
||||||
<avue-crud
|
<avue-crud
|
||||||
v-if="!isArchivePage"
|
v-if="!isArchivePage"
|
||||||
:option="option"
|
:option="option"
|
||||||
@@ -913,7 +913,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<div class="archive-form__footer">
|
<div class="archive-form__footer" v-if="!isPublicViewPage">
|
||||||
<!-- 次要:独立页返回 / 只读关闭 / 弹窗取消 -->
|
<!-- 次要:独立页返回 / 只读关闭 / 弹窗取消 -->
|
||||||
<el-button @click="closeArchive">{{ readonly ? '关闭' : '取消' }}</el-button>
|
<el-button @click="closeArchive">{{ readonly ? '关闭' : '取消' }}</el-button>
|
||||||
<el-button type="primary" plain v-if="!readonly" @click="saveArchive">保存</el-button>
|
<el-button type="primary" plain v-if="!readonly" @click="saveArchive">保存</el-button>
|
||||||
@@ -1482,7 +1482,9 @@ import { ElCascader } from 'element-plus';
|
|||||||
import {
|
import {
|
||||||
getList,
|
getList,
|
||||||
getDetail,
|
getDetail,
|
||||||
|
getPublicDetail,
|
||||||
getChangeRecordList,
|
getChangeRecordList,
|
||||||
|
getPublicChangeRecordList,
|
||||||
submit,
|
submit,
|
||||||
submitApproval,
|
submitApproval,
|
||||||
withdrawApproval,
|
withdrawApproval,
|
||||||
@@ -1498,6 +1500,7 @@ import {
|
|||||||
getDetail as getCreditScoreQuantificationDetail,
|
getDetail as getCreditScoreQuantificationDetail,
|
||||||
} from '@/api/vehicle/credit-score-quantification';
|
} from '@/api/vehicle/credit-score-quantification';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
|
import { processSubmit } from '@/api/system/business-process';
|
||||||
import { getDeptTree } from '@/api/system/dept';
|
import { getDeptTree } from '@/api/system/dept';
|
||||||
import { getLazyTree } from '@/api/base/region';
|
import { getLazyTree } from '@/api/base/region';
|
||||||
import { exportBlob } from '@/api/common';
|
import { exportBlob } from '@/api/common';
|
||||||
@@ -1989,6 +1992,18 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
if (this.isPublicViewPage) {
|
||||||
|
this.readonly = true;
|
||||||
|
const id = this.$route.query.id;
|
||||||
|
if (!id) {
|
||||||
|
this.archiveBox = true;
|
||||||
|
this.$message.error('缺少客商ID');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.openArchive({ id }, true);
|
||||||
|
this.bindMkParentHeight();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.initDeptTree();
|
this.initDeptTree();
|
||||||
this.initRegionOptions();
|
this.initRegionOptions();
|
||||||
this.initBusinessDictionaries();
|
this.initBusinessDictionaries();
|
||||||
@@ -1997,6 +2012,10 @@ export default {
|
|||||||
const readonly = this.$route.query.view === '1' || this.$route.query.view === 'true';
|
const readonly = this.$route.query.view === '1' || this.$route.query.view === 'true';
|
||||||
this.openArchive(this.$route.query.id ? { id: this.$route.query.id } : null, readonly);
|
this.openArchive(this.$route.query.id ? { id: this.$route.query.id } : null, readonly);
|
||||||
}
|
}
|
||||||
|
this.bindMkParentHeight();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.notifyMkParentHeight();
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['permission', 'userInfo']),
|
...mapGetters(['permission', 'userInfo']),
|
||||||
@@ -2010,7 +2029,18 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
isArchivePage() {
|
isArchivePage() {
|
||||||
return this.$route.path === '/vehicle/customer-archive/form';
|
return (
|
||||||
|
this.$route.path === '/vehicle/customer-archive/form' || this.isPublicViewPage
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isPublicViewPage() {
|
||||||
|
return this.$route.path === '/vehicle/customer-archive/public-view';
|
||||||
|
},
|
||||||
|
isMkEmbed() {
|
||||||
|
const isMk = this.$route.query.isMk;
|
||||||
|
return (
|
||||||
|
this.isPublicViewPage || isMk === '1' || isMk === 1 || isMk === 'true'
|
||||||
|
);
|
||||||
},
|
},
|
||||||
archiveContainer() {
|
archiveContainer() {
|
||||||
return 'div';
|
return 'div';
|
||||||
@@ -2190,6 +2220,67 @@ export default {
|
|||||||
hasPermission(code) {
|
hasPermission(code) {
|
||||||
return this.isAdmin || this.validData(this.permission[code], false);
|
return this.isAdmin || this.validData(this.permission[code], false);
|
||||||
},
|
},
|
||||||
|
bindMkParentHeight() {
|
||||||
|
if (!this.isMkEmbed) return;
|
||||||
|
document.addEventListener(
|
||||||
|
'DOMContentLoaded',
|
||||||
|
function () {
|
||||||
|
var tbody = document.body;
|
||||||
|
var height = tbody.clientHeight;
|
||||||
|
// 如需动态改变表单高度,可以直接发送此postMessage
|
||||||
|
window.parent.postMessage({ height: height }, '*');
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
},
|
||||||
|
notifyMkParentHeight() {
|
||||||
|
if (!this.isMkEmbed) return;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
var tbody = document.body;
|
||||||
|
var height = tbody.clientHeight;
|
||||||
|
// 如需动态改变表单高度,可以直接发送此postMessage
|
||||||
|
window.parent.postMessage({ height: height }, '*');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
mergeSelectOptions(target, values) {
|
||||||
|
const list = Array.isArray(values) ? values : values ? [values] : [];
|
||||||
|
list.forEach(value => {
|
||||||
|
if (value === undefined || value === null || value === '') return;
|
||||||
|
if (!this[target].some(item => String(item.value) === String(value))) {
|
||||||
|
this[target].push({ label: String(value), value });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
ensurePublicViewOptions(archive = {}) {
|
||||||
|
this.mergeSelectOptions('customerNatureOptions', archive.customerNature);
|
||||||
|
this.mergeSelectOptions('customerTypeOptions', archive.customerType);
|
||||||
|
this.mergeSelectOptions('businessScopeOptions', archive.businessScope);
|
||||||
|
this.mergeSelectOptions(
|
||||||
|
'qualificationTypeOptions',
|
||||||
|
(this.qualificationFiles || []).map(item => item.type)
|
||||||
|
);
|
||||||
|
const deptId = Array.isArray(archive.deptId) ? archive.deptId[0] : archive.deptId;
|
||||||
|
if (deptId && archive.deptName) {
|
||||||
|
this.deptTree = [
|
||||||
|
{
|
||||||
|
label: archive.deptName,
|
||||||
|
value: String(deptId),
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
this.deptOptions = this.flattenDept(this.deptTree);
|
||||||
|
}
|
||||||
|
(archive.scores || []).forEach(score => {
|
||||||
|
if (!score.quantificationId) return;
|
||||||
|
const id = String(score.quantificationId);
|
||||||
|
if (!this.scoreQuantificationOptions.some(item => String(item.id) === id)) {
|
||||||
|
this.scoreQuantificationOptions.push({
|
||||||
|
id,
|
||||||
|
name: score.quantificationName || score.name || id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
// 检测输入框文本是否溢出,仅溢出时才允许 tooltip 显示完整地址
|
// 检测输入框文本是否溢出,仅溢出时才允许 tooltip 显示完整地址
|
||||||
checkOverflow(refName, flag) {
|
checkOverflow(refName, flag) {
|
||||||
const inst = this.$refs[refName];
|
const inst = this.$refs[refName];
|
||||||
@@ -2278,7 +2369,8 @@ export default {
|
|||||||
loadChangeRecords() {
|
loadChangeRecords() {
|
||||||
if (!this.archiveForm.id) return;
|
if (!this.archiveForm.id) return;
|
||||||
const page = this.changeRecordPage;
|
const page = this.changeRecordPage;
|
||||||
getChangeRecordList(this.archiveForm.id, page.currentPage, page.pageSize).then(res => {
|
const requestList = this.isPublicViewPage ? getPublicChangeRecordList : getChangeRecordList;
|
||||||
|
requestList(this.archiveForm.id, page.currentPage, page.pageSize).then(res => {
|
||||||
const data = res.data.data || {};
|
const data = res.data.data || {};
|
||||||
page.records = this.replaceNegativeOneWithBlank(data.records || []);
|
page.records = this.replaceNegativeOneWithBlank(data.records || []);
|
||||||
page.total = Number(data.total || 0);
|
page.total = Number(data.total || 0);
|
||||||
@@ -3890,6 +3982,7 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
closeArchive() {
|
closeArchive() {
|
||||||
|
if (this.isPublicViewPage) return;
|
||||||
this.$router.push('/vehicle/customer-archive');
|
this.$router.push('/vehicle/customer-archive');
|
||||||
},
|
},
|
||||||
openArchive(row, readonly = false) {
|
openArchive(row, readonly = false) {
|
||||||
@@ -3898,7 +3991,9 @@ export default {
|
|||||||
this.resetDetailPagination();
|
this.resetDetailPagination();
|
||||||
this.resetChangeRecordPage();
|
this.resetChangeRecordPage();
|
||||||
if (row && row.id) {
|
if (row && row.id) {
|
||||||
getDetail(row.id).then(res => {
|
const requestDetail = this.isPublicViewPage ? getPublicDetail : getDetail;
|
||||||
|
requestDetail(row.id)
|
||||||
|
.then(res => {
|
||||||
const archive = this.normalizeDetail(res.data.data);
|
const archive = this.normalizeDetail(res.data.data);
|
||||||
this.archiveForm = readonly ? this.replaceNegativeOneWithBlank(archive) : archive;
|
this.archiveForm = readonly ? this.replaceNegativeOneWithBlank(archive) : archive;
|
||||||
this.originalAccessType = this.archiveForm.accessType || '';
|
this.originalAccessType = this.archiveForm.accessType || '';
|
||||||
@@ -3912,8 +4007,14 @@ export default {
|
|||||||
this.qualificationUploadFiles = [];
|
this.qualificationUploadFiles = [];
|
||||||
this.ocrQualificationUploadFiles = [];
|
this.ocrQualificationUploadFiles = [];
|
||||||
this.selectedQualificationFiles = [];
|
this.selectedQualificationFiles = [];
|
||||||
|
if (this.isPublicViewPage) this.ensurePublicViewOptions(this.archiveForm);
|
||||||
this.archiveBox = true;
|
this.archiveBox = true;
|
||||||
|
this.notifyMkParentHeight();
|
||||||
this.loadChangeRecords();
|
this.loadChangeRecords();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.archiveBox = true;
|
||||||
|
if (this.isPublicViewPage) this.$message.error('客商信息加载失败');
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3924,6 +4025,7 @@ export default {
|
|||||||
this.ocrQualificationUploadFiles = [];
|
this.ocrQualificationUploadFiles = [];
|
||||||
this.selectedQualificationFiles = [];
|
this.selectedQualificationFiles = [];
|
||||||
this.archiveBox = true;
|
this.archiveBox = true;
|
||||||
|
this.notifyMkParentHeight();
|
||||||
},
|
},
|
||||||
resetArchive() {
|
resetArchive() {
|
||||||
this.readonly = false;
|
this.readonly = false;
|
||||||
@@ -4078,7 +4180,14 @@ export default {
|
|||||||
this.$message({ type: 'success', message: '保存成功,请在列表提交审批' });
|
this.$message({ type: 'success', message: '保存成功,请在列表提交审批' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
submitApproval(id).then(() => {
|
const fullName =
|
||||||
|
(data && typeof data === 'object' ? data.fullName : '') ||
|
||||||
|
archive.fullName ||
|
||||||
|
this.archiveForm.fullName ||
|
||||||
|
'';
|
||||||
|
this.submitMkApprovalFlow(id, fullName)
|
||||||
|
.then(() => submitApproval(id))
|
||||||
|
.then(() => {
|
||||||
this.closeArchive();
|
this.closeArchive();
|
||||||
this.onLoad(this.page);
|
this.onLoad(this.page);
|
||||||
this.$message({ type: 'success', message: '提交成功!' });
|
this.$message({ type: 'success', message: '提交成功!' });
|
||||||
@@ -4086,6 +4195,32 @@ export default {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 提交 MK 审核流:templateCode 取业务字典 mk_template 中「提交审核流」的键值
|
||||||
|
* 提交人手机号由后端从用户表读取真实值(前端接口会脱敏)
|
||||||
|
*/
|
||||||
|
async submitMkApprovalFlow(formInstanceId, subjectName = '') {
|
||||||
|
const templateCode = await this.resolveMkTemplateCode('提交审核流');
|
||||||
|
const subject = subjectName
|
||||||
|
? `客商准入审批:${subjectName}`
|
||||||
|
: `客商准入审批:${formInstanceId}`;
|
||||||
|
return processSubmit({
|
||||||
|
templateCode,
|
||||||
|
formInstanceId: String(formInstanceId),
|
||||||
|
subject,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async resolveMkTemplateCode(dictName = '提交审核流') {
|
||||||
|
const res = await getDictionary({ code: 'mk_template' });
|
||||||
|
const list = res?.data?.data || [];
|
||||||
|
const matched = list.find(item => String(item.dictValue || '').trim() === dictName);
|
||||||
|
const templateCode = matched?.dictKey;
|
||||||
|
if (!templateCode) {
|
||||||
|
this.$message.warning(`未配置业务字典 mk_template「${dictName}」,无法提交审核流`);
|
||||||
|
return Promise.reject(new Error(`未配置业务字典 mk_template「${dictName}」`));
|
||||||
|
}
|
||||||
|
return String(templateCode);
|
||||||
|
},
|
||||||
addScore() {
|
addScore() {
|
||||||
this.scoreRecordIndex = -1;
|
this.scoreRecordIndex = -1;
|
||||||
this.currentScore = this.normalizeScore({});
|
this.currentScore = this.normalizeScore({});
|
||||||
@@ -4676,6 +4811,7 @@ export default {
|
|||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
})
|
})
|
||||||
|
.then(() => this.submitMkApprovalFlow(row.id, archive.fullName || row.fullName))
|
||||||
.then(() => submitApproval(row.id))
|
.then(() => submitApproval(row.id))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.onLoad(this.page);
|
this.onLoad(this.page);
|
||||||
@@ -5348,6 +5484,12 @@ export default {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.customer-archive-page.is-public-view {
|
||||||
|
:deep(.archive-page-form .archive-form__footer) {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.archive-page-form {
|
.archive-page-form {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
margin-bottom: 60px;
|
margin-bottom: 60px;
|
||||||
|
|||||||
+13
-13
@@ -47,26 +47,26 @@ export default ({ mode, command }) => {
|
|||||||
__VUE_I18N_LEGACY_API__: true,
|
__VUE_I18N_LEGACY_API__: true,
|
||||||
__INTLIFY_PROD_DEVTOOLS__: false,
|
__INTLIFY_PROD_DEVTOOLS__: false,
|
||||||
},
|
},
|
||||||
// server: {
|
|
||||||
// port: 2888,
|
|
||||||
// proxy: {
|
|
||||||
// '/api': {
|
|
||||||
// target: 'http://localhost',
|
|
||||||
// //target: 'https://saber3.bladex.cn/api',
|
|
||||||
// changeOrigin: true,
|
|
||||||
// rewrite: path => path.replace(/^\/api/, ''),
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
server: {
|
server: {
|
||||||
port: 2889,
|
port: 2888,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://172.16.203.228:8000',
|
target: 'http://localhost',
|
||||||
|
//target: 'https://saber3.bladex.cn/api',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
rewrite: path => path.replace(/^\/api/, ''),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// server: {
|
||||||
|
// port: 2889,
|
||||||
|
// proxy: {
|
||||||
|
// '/api': {
|
||||||
|
// target: 'http://172.16.203.228:8000',
|
||||||
|
// changeOrigin: true,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'~': resolve(__dirname, './'),
|
'~': resolve(__dirname, './'),
|
||||||
|
|||||||
Reference in New Issue
Block a user