Compare commits
4 Commits
815810f841
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6212d68a77 | |||
| ce1ce02551 | |||
| 3a8442e522 | |||
| ac87208ba9 |
@@ -24,7 +24,7 @@ export const syncKingdeeBatch = ids =>
|
|||||||
export const paymentTypeOptions = [
|
export const paymentTypeOptions = [
|
||||||
{ label: '项目预付', value: 'project_advance' },
|
{ label: '项目预付', value: 'project_advance' },
|
||||||
{ label: '进度预付', value: 'progress_advance' },
|
{ label: '进度预付', value: 'progress_advance' },
|
||||||
{ label: '结算付款', value: 'settlement_payment' },
|
{ label: '尾款付款', value: 'settlement_payment' },
|
||||||
];
|
];
|
||||||
export const approvalStatusOptions = [
|
export const approvalStatusOptions = [
|
||||||
{ label: '草稿', value: 'draft' },
|
{ label: '草稿', value: 'draft' },
|
||||||
|
|||||||
@@ -48,6 +48,15 @@ export const syncIamOrganizations = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const clearNonTopDept = signal => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/dept/clear-non-top',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const syncOaCompany = (current = 1, size = 20, signal) => {
|
export const syncOaCompany = (current = 1, size = 20, signal) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/blade-system/dept/sync-oa-company',
|
url: '/blade-system/dept/sync-oa-company',
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
longitude: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
latitude: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'confirm'],
|
emits: ['update:modelValue', 'confirm'],
|
||||||
data() {
|
data() {
|
||||||
@@ -66,9 +74,11 @@ export default {
|
|||||||
this.visible = value;
|
this.visible = value;
|
||||||
if (value) {
|
if (value) {
|
||||||
this.keyword = this.address || '';
|
this.keyword = this.address || '';
|
||||||
this.selected = {};
|
|
||||||
this.status = '可搜索地址或点击地图选点';
|
|
||||||
this.searchResults = [];
|
this.searchResults = [];
|
||||||
|
this.selected = this.buildInitialSelection();
|
||||||
|
this.status = this.selected.longitude
|
||||||
|
? this.selected.address || '已选点,可确认回填'
|
||||||
|
: '可搜索地址或点击地图选点';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -82,6 +92,18 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
buildInitialSelection() {
|
||||||
|
const longitude = Number(this.longitude);
|
||||||
|
const latitude = Number(this.latitude);
|
||||||
|
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
longitude,
|
||||||
|
latitude,
|
||||||
|
address: this.address || '',
|
||||||
|
};
|
||||||
|
},
|
||||||
loadAmap() {
|
loadAmap() {
|
||||||
if (window.AMap && window.AMap.Map) {
|
if (window.AMap && window.AMap.Map) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@@ -110,15 +132,27 @@ export default {
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (!this.$refs.map) return resolve(null);
|
if (!this.$refs.map) return resolve(null);
|
||||||
|
const initial = this.buildInitialSelection();
|
||||||
|
const center =
|
||||||
|
Number.isFinite(initial.longitude) && Number.isFinite(initial.latitude)
|
||||||
|
? [initial.longitude, initial.latitude]
|
||||||
|
: [116.40769, 39.89945];
|
||||||
if (!this.amap) {
|
if (!this.amap) {
|
||||||
this.amap = new window.AMap.Map(this.$refs.map, {
|
this.amap = new window.AMap.Map(this.$refs.map, {
|
||||||
center: [116.40769, 39.89945],
|
center,
|
||||||
zoom: 11,
|
zoom: initial.longitude ? 14 : 11,
|
||||||
});
|
});
|
||||||
this.amap.on('click', event => this.pickPoint(event.lnglat));
|
this.amap.on('click', event => this.pickPoint(event.lnglat));
|
||||||
} else {
|
} else {
|
||||||
this.amap.resize();
|
this.amap.resize();
|
||||||
this.clearMarker();
|
this.clearMarker();
|
||||||
|
this.amap.setZoomAndCenter(initial.longitude ? 14 : 11, center);
|
||||||
|
}
|
||||||
|
if (initial.longitude) {
|
||||||
|
const point = new window.AMap.LngLat(initial.longitude, initial.latitude);
|
||||||
|
this.renderMarker(point);
|
||||||
|
this.selected = { ...initial };
|
||||||
|
this.status = initial.address || '已选点,可确认回填';
|
||||||
}
|
}
|
||||||
resolve(this.amap);
|
resolve(this.amap);
|
||||||
});
|
});
|
||||||
@@ -254,11 +288,19 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirm() {
|
confirm() {
|
||||||
|
if (!this.selected.longitude || !this.selected.latitude) {
|
||||||
|
this.$message.warning('请先搜索或点击地图完成选点');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.selected.address) {
|
if (!this.selected.address) {
|
||||||
this.$message.warning('请等待地址解析完成后再确认');
|
this.$message.warning('请等待地址解析完成后再确认');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$emit('confirm', this.selected.address);
|
this.$emit('confirm', {
|
||||||
|
address: this.selected.address,
|
||||||
|
longitude: this.selected.longitude,
|
||||||
|
latitude: this.selected.latitude,
|
||||||
|
});
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="modelValue"
|
||||||
|
title="变更记录详情"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
align-center
|
||||||
|
width="1100px"
|
||||||
|
class="change-record-detail-dialog"
|
||||||
|
@update:model-value="$emit('update:modelValue', $event)"
|
||||||
|
@open="resetPage"
|
||||||
|
>
|
||||||
|
<el-table
|
||||||
|
:data="pageRows"
|
||||||
|
border
|
||||||
|
max-height="60vh"
|
||||||
|
:show-overflow-tooltip="false"
|
||||||
|
>
|
||||||
|
<el-table-column prop="field" label="变更字段" min-width="180" />
|
||||||
|
<el-table-column
|
||||||
|
prop="before"
|
||||||
|
label="变更前"
|
||||||
|
min-width="360"
|
||||||
|
class-name="change-record-detail-value"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="after"
|
||||||
|
label="变更后"
|
||||||
|
min-width="500"
|
||||||
|
class-name="change-record-detail-value"
|
||||||
|
/>
|
||||||
|
</el-table>
|
||||||
|
<el-empty v-if="!total" description="暂无变更内容" :image-size="60" />
|
||||||
|
<div class="change-record-detail-dialog__pager">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
background
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="$emit('update:modelValue', false)">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ChangeRecordDetailDialog',
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
rows: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
total() {
|
||||||
|
return this.rows.length;
|
||||||
|
},
|
||||||
|
pageRows() {
|
||||||
|
const start = (this.currentPage - 1) * this.pageSize;
|
||||||
|
return this.rows.slice(start, start + this.pageSize);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
rows() {
|
||||||
|
const maxPage = Math.max(1, Math.ceil(this.total / this.pageSize) || 1);
|
||||||
|
if (this.currentPage > maxPage) this.currentPage = 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
resetPage() {
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.pageSize = 10;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.change-record-detail-dialog__pager {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.change-record-detail-value .cell) {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow: visible;
|
||||||
|
text-overflow: clip;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -42,6 +42,7 @@ export const createOption = () => ({
|
|||||||
label: '计量单位编码',
|
label: '计量单位编码',
|
||||||
prop: 'unitCode',
|
prop: 'unitCode',
|
||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
|
span: 24,
|
||||||
search: true,
|
search: true,
|
||||||
searchOrder: 4,
|
searchOrder: 4,
|
||||||
searchSpan: 6,
|
searchSpan: 6,
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ export const option = {
|
|||||||
},
|
},
|
||||||
...auditColumns.map(column => ({ ...column, hide: true })),
|
...auditColumns.map(column => ({ ...column, hide: true })),
|
||||||
])),
|
])),
|
||||||
dialogWidth: '96%',
|
dialogWidth: '1100px',
|
||||||
menuFixed: 'right',
|
menuFixed: 'right',
|
||||||
menuWidth: 320,
|
menuWidth: 320,
|
||||||
index: false,
|
index: false,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export const GRANT_TYPE_DIC = [
|
|||||||
{ label: '社交登录', value: 'social' },
|
{ label: '社交登录', value: 'social' },
|
||||||
{ label: '客户端凭证', value: 'client_credentials' },
|
{ label: '客户端凭证', value: 'client_credentials' },
|
||||||
{ label: '刷新令牌', value: 'refresh_token' },
|
{ label: '刷新令牌', value: 'refresh_token' },
|
||||||
|
{ label: '退出登录', value: 'logout' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const authLogOption = {
|
export const authLogOption = {
|
||||||
@@ -38,7 +39,7 @@ export const authLogOption = {
|
|||||||
prop: 'realName',
|
prop: 'realName',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '授权类型',
|
label: '操作类型',
|
||||||
prop: 'grantType',
|
prop: 'grantType',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
search: true,
|
search: true,
|
||||||
@@ -80,7 +81,7 @@ export const authLogOption = {
|
|||||||
width: 120,
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '登录时间',
|
label: '操作时间',
|
||||||
prop: 'loginTime',
|
prop: 'loginTime',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
span: 24,
|
span: 24,
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { getDeptLazyTree } from '@/api/system/dept';
|
import { getDeptLazyTree } from '@/api/system/dept';
|
||||||
|
import { validateLoginPassword } from '@/utils/validate';
|
||||||
|
|
||||||
export const userOption = safe => {
|
export const userOption = safe => {
|
||||||
const validatePass = (rule, value, callback) => {
|
const validatePass = (rule, value, callback) => {
|
||||||
if (value === '') {
|
validateLoginPassword(rule, value, callback);
|
||||||
callback(new Error('请输入密码'));
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
const validatePass2 = (rule, value, callback) => {
|
const validatePass2 = (rule, value, callback) => {
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
-->
|
-->
|
||||||
<userLogin v-if="activeName === 'user'"></userLogin>
|
<userLogin v-if="activeName === 'user'"></userLogin>
|
||||||
<registerLogin v-else-if="activeName === 'register'"></registerLogin>
|
<registerLogin v-else-if="activeName === 'register'"></registerLogin>
|
||||||
|
<!-- 暂时默认账号密码登录,IAM 选择入口先隐藏
|
||||||
<div v-else class="iam-login">
|
<div v-else class="iam-login">
|
||||||
<el-button type="primary" class="login-submit" @click.prevent="handleIamLogin">
|
<el-button type="primary" class="login-submit" @click.prevent="handleIamLogin">
|
||||||
IAM统一身份认证
|
IAM统一身份认证
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
账号密码登录
|
账号密码登录
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -61,7 +63,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
website: website,
|
website: website,
|
||||||
time: '',
|
time: '',
|
||||||
activeName: 'iam',
|
activeName: 'user',
|
||||||
socialForm: {
|
socialForm: {
|
||||||
tenantId: '000000',
|
tenantId: '000000',
|
||||||
source: '',
|
source: '',
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: '/business/project-apply/public-view',
|
path: '/business/project-apply/public-view',
|
||||||
name: '查看项目信息',
|
name: '查看项目信息',
|
||||||
component: () => import('@/views/mk/public-biz-view.vue'),
|
component: () => import('@/views/business/project-apply-public-view.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
isTab: false,
|
isTab: false,
|
||||||
@@ -98,7 +98,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: '/business/contract-manage/public-view',
|
path: '/business/contract-manage/public-view',
|
||||||
name: '查看合同信息',
|
name: '查看合同信息',
|
||||||
component: () => import('@/views/mk/public-biz-view.vue'),
|
component: () => import('@/views/business/contract-manage-public-view.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
isTab: false,
|
isTab: false,
|
||||||
@@ -109,7 +109,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: '/business/waybill-manage/public-view',
|
path: '/business/waybill-manage/public-view',
|
||||||
name: '查看运单信息',
|
name: '查看运单信息',
|
||||||
component: () => import('@/views/mk/public-biz-view.vue'),
|
component: () => import('@/views/business/waybill-manage-public-view.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
isTab: false,
|
isTab: false,
|
||||||
@@ -120,7 +120,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: '/settlement/pre-settlement/public-view',
|
path: '/settlement/pre-settlement/public-view',
|
||||||
name: '查看预结算信息',
|
name: '查看预结算信息',
|
||||||
component: () => import('@/views/mk/public-biz-view.vue'),
|
component: () => import('@/views/settlement/pre-settlement-public-view.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
isTab: false,
|
isTab: false,
|
||||||
@@ -131,7 +131,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: '/settlement/formal-settlement/public-view',
|
path: '/settlement/formal-settlement/public-view',
|
||||||
name: '查看正式结算信息',
|
name: '查看正式结算信息',
|
||||||
component: () => import('@/views/mk/public-biz-view.vue'),
|
component: () => import('@/views/settlement/formal-settlement-public-view.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
isTab: false,
|
isTab: false,
|
||||||
@@ -142,7 +142,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: '/payment/payment-application/public-view',
|
path: '/payment/payment-application/public-view',
|
||||||
name: '查看付款申请信息',
|
name: '查看付款申请信息',
|
||||||
component: () => import('@/views/mk/public-biz-view.vue'),
|
component: () => import('@/views/payment/payment-application-public-view.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
isTab: false,
|
isTab: false,
|
||||||
|
|||||||
@@ -40,10 +40,22 @@ export const MK_BIZ = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function resolveMkTemplateCode(dictName) {
|
const MK_SWITCH_NAME = '开启MK';
|
||||||
|
|
||||||
|
async function loadMkTemplateList() {
|
||||||
const res = await getDictionary({ code: 'mk_template' });
|
const res = await getDictionary({ code: 'mk_template' });
|
||||||
const list = res?.data?.data || [];
|
return res?.data?.data || [];
|
||||||
const matched = list.find(item => String(item.dictValue || '').trim() === dictName);
|
}
|
||||||
|
|
||||||
|
/** 业务字典 mk_template:名称「开启MK」且键值为 1 时才请求 MK */
|
||||||
|
export function isMkEnabled(list = []) {
|
||||||
|
const matched = list.find(item => String(item.dictValue || '').trim() === MK_SWITCH_NAME);
|
||||||
|
return String(matched?.dictKey ?? '').trim() === '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resolveMkTemplateCode(dictName, list) {
|
||||||
|
const dictList = list || (await loadMkTemplateList());
|
||||||
|
const matched = dictList.find(item => String(item.dictValue || '').trim() === dictName);
|
||||||
const templateCode = matched?.dictKey;
|
const templateCode = matched?.dictKey;
|
||||||
if (!templateCode) {
|
if (!templateCode) {
|
||||||
ElMessage.warning(`未配置业务字典 mk_template「${dictName}」,无法提交审核流`);
|
ElMessage.warning(`未配置业务字典 mk_template「${dictName}」,无法提交审核流`);
|
||||||
@@ -62,10 +74,14 @@ export async function submitMkApprovalFlow({
|
|||||||
if (!conf) {
|
if (!conf) {
|
||||||
return Promise.reject(new Error(`不支持的MK业务类型:${bizType}`));
|
return Promise.reject(new Error(`不支持的MK业务类型:${bizType}`));
|
||||||
}
|
}
|
||||||
|
const list = await loadMkTemplateList();
|
||||||
|
if (!isMkEnabled(list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ((conf.rejected || []).includes(approvalStatus)) {
|
if ((conf.rejected || []).includes(approvalStatus)) {
|
||||||
await processDelete({ formInstanceId: String(formInstanceId) });
|
await processDelete({ formInstanceId: String(formInstanceId) });
|
||||||
}
|
}
|
||||||
const templateCode = await resolveMkTemplateCode(conf.dictName);
|
const templateCode = await resolveMkTemplateCode(conf.dictName, list);
|
||||||
const subject = subjectName
|
const subject = subjectName
|
||||||
? `${conf.subjectPrefix}:${subjectName}`
|
? `${conf.subjectPrefix}:${subjectName}`
|
||||||
: `${conf.subjectPrefix}:${formInstanceId}`;
|
: `${conf.subjectPrefix}:${formInstanceId}`;
|
||||||
|
|||||||
@@ -283,3 +283,31 @@ export function validatejson(val) {
|
|||||||
// 非对象、非数组、非字符串,或者字符串不是 JSON
|
// 非对象、非数组、非字符串,或者字符串不是 JSON
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 登录密码规则提示 */
|
||||||
|
export const LOGIN_PASSWORD_RULE_MESSAGE =
|
||||||
|
'密码须大于8位,且同时包含字母、数字和特殊字符(.!@#$%^&*)';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验登录密码强度:大于8位,同时包含字母、数字、特殊字符(.!@#$%^&*)
|
||||||
|
* @param {string} password
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function isValidLoginPassword(password) {
|
||||||
|
return /^(?=.*[A-Za-z])(?=.*\d)(?=.*[.!@#$%^&*]).{9,}$/.test(String(password || ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element Plus / Avue 表单校验器:登录密码强度
|
||||||
|
*/
|
||||||
|
export function validateLoginPassword(rule, value, callback) {
|
||||||
|
if (value === undefined || value === null || String(value).trim() === '') {
|
||||||
|
callback(new Error('请输入登录密码'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isValidLoginPassword(value)) {
|
||||||
|
callback(new Error(LOGIN_PASSWORD_RULE_MESSAGE));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|||||||
@@ -94,6 +94,17 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
|
<template #detailAddress-form>
|
||||||
|
<el-input
|
||||||
|
v-model="form.detailAddress"
|
||||||
|
class="detail-address-input"
|
||||||
|
readonly
|
||||||
|
maxlength="255"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="点击选择地图地址"
|
||||||
|
@click="openAddressMapPicker"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<template #longitude-form>
|
<template #longitude-form>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.longitude"
|
v-model="form.longitude"
|
||||||
@@ -121,6 +132,13 @@
|
|||||||
</template>
|
</template>
|
||||||
</avue-form>
|
</avue-form>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<address-map-picker
|
||||||
|
v-model="addressMapPickerVisible"
|
||||||
|
:address="form.detailAddress"
|
||||||
|
:longitude="form.longitude"
|
||||||
|
:latitude="form.latitude"
|
||||||
|
@confirm="handleAddressMapConfirm"
|
||||||
|
/>
|
||||||
</basic-container>
|
</basic-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -134,6 +152,7 @@ import { openImportDialog } from '@/utils/import-excel';
|
|||||||
import { formatUpdateUserName } from '@/utils/audit';
|
import { formatUpdateUserName } from '@/utils/audit';
|
||||||
import { getToken } from '@/utils/auth';
|
import { getToken } from '@/utils/auth';
|
||||||
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
||||||
|
import AddressMapPicker from '@/components/address-map-picker/main.vue';
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
|
|
||||||
@@ -181,6 +200,9 @@ const addExcelRequiredHeaderMarks = async blob => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
AddressMapPicker,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
const validateIataCode = (rule, value, callback) => {
|
const validateIataCode = (rule, value, callback) => {
|
||||||
if (!/^[A-Z]{3}$/.test(String(value || '').toUpperCase())) {
|
if (!/^[A-Z]{3}$/.test(String(value || '').toUpperCase())) {
|
||||||
@@ -220,6 +242,7 @@ export default {
|
|||||||
loading: true,
|
loading: true,
|
||||||
data: [],
|
data: [],
|
||||||
excelBox: false,
|
excelBox: false,
|
||||||
|
addressMapPickerVisible: false,
|
||||||
excelForm: {},
|
excelForm: {},
|
||||||
provinceOptions: [],
|
provinceOptions: [],
|
||||||
cityOptions: [],
|
cityOptions: [],
|
||||||
@@ -368,15 +391,14 @@ export default {
|
|||||||
{
|
{
|
||||||
label: '详细地址',
|
label: '详细地址',
|
||||||
prop: 'detailAddress',
|
prop: 'detailAddress',
|
||||||
type: 'textarea',
|
formslot: true,
|
||||||
minRows: 2,
|
|
||||||
span: 24,
|
span: 24,
|
||||||
minWidth: 220,
|
minWidth: 220,
|
||||||
overHidden: false,
|
overHidden: false,
|
||||||
maxlength: 255,
|
maxlength: 255,
|
||||||
showWordLimit: true,
|
showWordLimit: true,
|
||||||
rules: [
|
rules: [
|
||||||
{ required: true, message: '请输入详细地址', trigger: 'blur' },
|
{ required: true, message: '请选择详细地址', trigger: 'change' },
|
||||||
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -669,6 +691,29 @@ export default {
|
|||||||
handleCoordinateInput(prop, value) {
|
handleCoordinateInput(prop, value) {
|
||||||
this.form[prop] = normalizeCoordinateInput(value);
|
this.form[prop] = normalizeCoordinateInput(value);
|
||||||
},
|
},
|
||||||
|
openAddressMapPicker() {
|
||||||
|
this.addressMapPickerVisible = true;
|
||||||
|
},
|
||||||
|
handleAddressMapConfirm(payload) {
|
||||||
|
const selection = typeof payload === 'string' ? { address: payload } : payload || {};
|
||||||
|
if (selection.address) {
|
||||||
|
this.form.detailAddress = selection.address;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
selection.longitude !== undefined &&
|
||||||
|
selection.longitude !== null &&
|
||||||
|
selection.longitude !== ''
|
||||||
|
) {
|
||||||
|
this.form.longitude = Number(selection.longitude).toFixed(6);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
selection.latitude !== undefined &&
|
||||||
|
selection.latitude !== null &&
|
||||||
|
selection.latitude !== ''
|
||||||
|
) {
|
||||||
|
this.form.latitude = Number(selection.latitude).toFixed(6);
|
||||||
|
}
|
||||||
|
},
|
||||||
normalizeRow(row) {
|
normalizeRow(row) {
|
||||||
row.code = row.iataCode ? `JC-${row.iataCode}` : row.code;
|
row.code = row.iataCode ? `JC-${row.iataCode}` : row.code;
|
||||||
row.regionCode = String(row.regionCode || '').trim();
|
row.regionCode = String(row.regionCode || '').trim();
|
||||||
@@ -917,4 +962,13 @@ export default {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-address-input {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
:deep(.el-input__wrapper),
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -194,10 +194,11 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="form.detailAddress"
|
v-model="form.detailAddress"
|
||||||
class="detail-address-input"
|
class="detail-address-input"
|
||||||
clearable
|
readonly
|
||||||
maxlength="255"
|
maxlength="255"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
placeholder="请输入"
|
placeholder="点击选择地图地址"
|
||||||
|
@click="openAddressMapPicker"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #remark-form>
|
<template #remark-form>
|
||||||
@@ -244,6 +245,13 @@
|
|||||||
</template>
|
</template>
|
||||||
</avue-form>
|
</avue-form>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<address-map-picker
|
||||||
|
v-model="addressMapPickerVisible"
|
||||||
|
:address="form.detailAddress"
|
||||||
|
:longitude="form.longitude"
|
||||||
|
:latitude="form.latitude"
|
||||||
|
@confirm="handleAddressMapConfirm"
|
||||||
|
/>
|
||||||
</basic-container>
|
</basic-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -264,12 +272,16 @@ import { openImportDialog } from '@/utils/import-excel';
|
|||||||
import { formatUpdateUserName } from '@/utils/audit';
|
import { formatUpdateUserName } from '@/utils/audit';
|
||||||
import { getToken } from '@/utils/auth';
|
import { getToken } from '@/utils/auth';
|
||||||
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
||||||
|
import AddressMapPicker from '@/components/address-map-picker/main.vue';
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
|
|
||||||
const DEFAULT_COUNTRY_CODE = '+86';
|
const DEFAULT_COUNTRY_CODE = '+86';
|
||||||
const newLocal = '请选择类型';
|
const newLocal = '请选择类型';
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
AddressMapPicker,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
const validateCode = (rule, value, callback) => {
|
const validateCode = (rule, value, callback) => {
|
||||||
const code = String(value || '').toUpperCase();
|
const code = String(value || '').toUpperCase();
|
||||||
@@ -315,6 +327,7 @@ export default {
|
|||||||
loading: true,
|
loading: true,
|
||||||
data: [],
|
data: [],
|
||||||
excelBox: false,
|
excelBox: false,
|
||||||
|
addressMapPickerVisible: false,
|
||||||
excelForm: {},
|
excelForm: {},
|
||||||
portOptions: [],
|
portOptions: [],
|
||||||
countryOptions: [],
|
countryOptions: [],
|
||||||
@@ -572,11 +585,11 @@ export default {
|
|||||||
minWidth: 220,
|
minWidth: 220,
|
||||||
overHidden: false,
|
overHidden: false,
|
||||||
order: 85,
|
order: 85,
|
||||||
placeholder: '请输入',
|
placeholder: '点击选择地图地址',
|
||||||
maxlength: 255,
|
maxlength: 255,
|
||||||
showWordLimit: true,
|
showWordLimit: true,
|
||||||
rules: [
|
rules: [
|
||||||
{ required: true, message: '请输入详细地址', trigger: 'blur' },
|
{ required: true, message: '请选择详细地址', trigger: 'change' },
|
||||||
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -1040,6 +1053,21 @@ export default {
|
|||||||
handleCoordinateInput(prop, value) {
|
handleCoordinateInput(prop, value) {
|
||||||
this.form[prop] = normalizeCoordinateInput(value);
|
this.form[prop] = normalizeCoordinateInput(value);
|
||||||
},
|
},
|
||||||
|
openAddressMapPicker() {
|
||||||
|
this.addressMapPickerVisible = true;
|
||||||
|
},
|
||||||
|
handleAddressMapConfirm(payload) {
|
||||||
|
const selection = typeof payload === 'string' ? { address: payload } : payload || {};
|
||||||
|
if (selection.address) {
|
||||||
|
this.form.detailAddress = selection.address;
|
||||||
|
}
|
||||||
|
if (selection.longitude !== undefined && selection.longitude !== null && selection.longitude !== '') {
|
||||||
|
this.form.longitude = Number(selection.longitude).toFixed(6);
|
||||||
|
}
|
||||||
|
if (selection.latitude !== undefined && selection.latitude !== null && selection.latitude !== '') {
|
||||||
|
this.form.latitude = Number(selection.latitude).toFixed(6);
|
||||||
|
}
|
||||||
|
},
|
||||||
resolveCountryCode(country) {
|
resolveCountryCode(country) {
|
||||||
if (!country) {
|
if (!country) {
|
||||||
return Promise.resolve('');
|
return Promise.resolve('');
|
||||||
@@ -1107,7 +1135,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isBlank(row.detailAddress)) {
|
if (isBlank(row.detailAddress)) {
|
||||||
this.$message.warning('请输入详细地址');
|
this.$message.warning('请选择详细地址');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isBlank(row.longitude)) {
|
if (isBlank(row.longitude)) {
|
||||||
@@ -1399,6 +1427,15 @@ export default {
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-address-input {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
:deep(.el-input__wrapper),
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -94,6 +94,17 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
|
<template #detailAddress-form>
|
||||||
|
<el-input
|
||||||
|
v-model="form.detailAddress"
|
||||||
|
class="detail-address-input"
|
||||||
|
readonly
|
||||||
|
maxlength="255"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="点击选择地图地址"
|
||||||
|
@click="openAddressMapPicker"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<template #longitude-form>
|
<template #longitude-form>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.longitude"
|
v-model="form.longitude"
|
||||||
@@ -121,6 +132,13 @@
|
|||||||
</template>
|
</template>
|
||||||
</avue-form>
|
</avue-form>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<address-map-picker
|
||||||
|
v-model="addressMapPickerVisible"
|
||||||
|
:address="form.detailAddress"
|
||||||
|
:longitude="form.longitude"
|
||||||
|
:latitude="form.latitude"
|
||||||
|
@confirm="handleAddressMapConfirm"
|
||||||
|
/>
|
||||||
</basic-container>
|
</basic-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -134,12 +152,16 @@ import { openImportDialog } from '@/utils/import-excel';
|
|||||||
import { formatUpdateUserName } from '@/utils/audit';
|
import { formatUpdateUserName } from '@/utils/audit';
|
||||||
import { getToken } from '@/utils/auth';
|
import { getToken } from '@/utils/auth';
|
||||||
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
||||||
|
import AddressMapPicker from '@/components/address-map-picker/main.vue';
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
|
|
||||||
const DEFAULT_COUNTRY_CODE = '+86';
|
const DEFAULT_COUNTRY_CODE = '+86';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
AddressMapPicker,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
const validateTmisCode = (rule, value, callback) => {
|
const validateTmisCode = (rule, value, callback) => {
|
||||||
if (!/^\d{5}$/.test(value || '')) {
|
if (!/^\d{5}$/.test(value || '')) {
|
||||||
@@ -185,6 +207,7 @@ export default {
|
|||||||
loading: true,
|
loading: true,
|
||||||
data: [],
|
data: [],
|
||||||
excelBox: false,
|
excelBox: false,
|
||||||
|
addressMapPickerVisible: false,
|
||||||
excelForm: {},
|
excelForm: {},
|
||||||
provinceOptions: [],
|
provinceOptions: [],
|
||||||
cityOptions: [],
|
cityOptions: [],
|
||||||
@@ -340,15 +363,14 @@ export default {
|
|||||||
{
|
{
|
||||||
label: '详细地址',
|
label: '详细地址',
|
||||||
prop: 'detailAddress',
|
prop: 'detailAddress',
|
||||||
type: 'textarea',
|
formslot: true,
|
||||||
minRows: 2,
|
|
||||||
span: 24,
|
span: 24,
|
||||||
minWidth: 220,
|
minWidth: 220,
|
||||||
overHidden: false,
|
overHidden: false,
|
||||||
maxlength: 255,
|
maxlength: 255,
|
||||||
showWordLimit: true,
|
showWordLimit: true,
|
||||||
rules: [
|
rules: [
|
||||||
{ required: true, message: '请输入详细地址', trigger: 'blur' },
|
{ required: true, message: '请选择详细地址', trigger: 'change' },
|
||||||
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -652,6 +674,29 @@ export default {
|
|||||||
handleCoordinateInput(prop, value) {
|
handleCoordinateInput(prop, value) {
|
||||||
this.form[prop] = normalizeCoordinateInput(value);
|
this.form[prop] = normalizeCoordinateInput(value);
|
||||||
},
|
},
|
||||||
|
openAddressMapPicker() {
|
||||||
|
this.addressMapPickerVisible = true;
|
||||||
|
},
|
||||||
|
handleAddressMapConfirm(payload) {
|
||||||
|
const selection = typeof payload === 'string' ? { address: payload } : payload || {};
|
||||||
|
if (selection.address) {
|
||||||
|
this.form.detailAddress = selection.address;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
selection.longitude !== undefined &&
|
||||||
|
selection.longitude !== null &&
|
||||||
|
selection.longitude !== ''
|
||||||
|
) {
|
||||||
|
this.form.longitude = Number(selection.longitude).toFixed(6);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
selection.latitude !== undefined &&
|
||||||
|
selection.latitude !== null &&
|
||||||
|
selection.latitude !== ''
|
||||||
|
) {
|
||||||
|
this.form.latitude = Number(selection.latitude).toFixed(6);
|
||||||
|
}
|
||||||
|
},
|
||||||
normalizeRow(row) {
|
normalizeRow(row) {
|
||||||
row.code = row.tmisCode ? `TL${row.tmisCode}` : row.code;
|
row.code = row.tmisCode ? `TL${row.tmisCode}` : row.code;
|
||||||
row.regionCode = String(row.regionCode || '').trim();
|
row.regionCode = String(row.regionCode || '').trim();
|
||||||
@@ -910,9 +955,16 @@ export default {
|
|||||||
:deep(.el-table td .cell) {
|
:deep(.el-table td .cell) {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-address-input {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
:deep(.el-input__wrapper),
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -162,9 +162,9 @@
|
|||||||
v-model="row.billingUnit"
|
v-model="row.billingUnit"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
:disabled="!row.billingElement"
|
:disabled="!canSelectBillingUnit(row)"
|
||||||
:loading="unitLoading"
|
:loading="unitLoading"
|
||||||
:placeholder="row.billingElement ? '请选择' : '请先选择计费要素'"
|
:placeholder="billingUnitPlaceholder(row)"
|
||||||
><el-option
|
><el-option
|
||||||
v-for="item in unitOptionsFor(row)"
|
v-for="item in unitOptionsFor(row)"
|
||||||
:key="item.id || item.value"
|
:key="item.id || item.value"
|
||||||
@@ -381,11 +381,18 @@ 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 BILLING_ELEMENT_DIMENSION_MAP = {
|
||||||
按重量: '重量',
|
按重量: '重量',
|
||||||
按体积: '体积',
|
按体积: '体积',
|
||||||
按车辆: '数量',
|
按数量: '数量',
|
||||||
|
};
|
||||||
|
const BILLING_UNIT_SELECTABLE_ELEMENTS = Object.keys(BILLING_ELEMENT_DIMENSION_MAP);
|
||||||
|
/** 固定计费单位(不可编辑) */
|
||||||
|
const FIXED_BILLING_UNIT_MAP = {
|
||||||
|
按里程: '公里',
|
||||||
|
'按吨·公里': '吨公里',
|
||||||
|
'固定金额(整单一口价)': '单',
|
||||||
};
|
};
|
||||||
const defaultRule = () => ({
|
const defaultRule = () => ({
|
||||||
feeType: '',
|
feeType: '',
|
||||||
@@ -436,7 +443,6 @@ export default {
|
|||||||
billingElements: [
|
billingElements: [
|
||||||
'按重量',
|
'按重量',
|
||||||
'按体积',
|
'按体积',
|
||||||
'按车辆',
|
|
||||||
'按里程',
|
'按里程',
|
||||||
'按吨·公里',
|
'按吨·公里',
|
||||||
'固定金额(整单一口价)',
|
'固定金额(整单一口价)',
|
||||||
@@ -445,7 +451,6 @@ export default {
|
|||||||
typeMap: {
|
typeMap: {
|
||||||
按重量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
按重量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||||
按体积: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
按体积: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||||
按车辆: ['固定单价'],
|
|
||||||
按里程: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
按里程: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||||
'按吨·公里': ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
'按吨·公里': ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'],
|
||||||
'固定金额(整单一口价)': ['固定一口价'],
|
'固定金额(整单一口价)': ['固定一口价'],
|
||||||
@@ -560,6 +565,7 @@ export default {
|
|||||||
next.limitRanges = next.limitRanges.map(item => ({ ...item, minimumBillingWeight: '' }));
|
next.limitRanges = next.limitRanges.map(item => ({ ...item, minimumBillingWeight: '' }));
|
||||||
this.syncLegacyLimit(next);
|
this.syncLegacyLimit(next);
|
||||||
next.matchCondition = { ...defaultRule().matchCondition, ...(rule.matchCondition || {}) };
|
next.matchCondition = { ...defaultRule().matchCondition, ...(rule.matchCondition || {}) };
|
||||||
|
this.syncBillingUnit(next);
|
||||||
return next;
|
return next;
|
||||||
},
|
},
|
||||||
normalizeRanges(row) {
|
normalizeRanges(row) {
|
||||||
@@ -651,7 +657,26 @@ export default {
|
|||||||
measurementDimension(billingElement) {
|
measurementDimension(billingElement) {
|
||||||
return BILLING_ELEMENT_DIMENSION_MAP[billingElement] || '';
|
return BILLING_ELEMENT_DIMENSION_MAP[billingElement] || '';
|
||||||
},
|
},
|
||||||
|
fixedBillingUnit(billingElement) {
|
||||||
|
return FIXED_BILLING_UNIT_MAP[String(billingElement || '').trim()] || '';
|
||||||
|
},
|
||||||
|
canSelectBillingUnit(row) {
|
||||||
|
return BILLING_UNIT_SELECTABLE_ELEMENTS.includes(String(row?.billingElement || '').trim());
|
||||||
|
},
|
||||||
|
billingUnitPlaceholder(row) {
|
||||||
|
if (!row?.billingElement) return '请先选择计费要素';
|
||||||
|
if (this.fixedBillingUnit(row.billingElement)) return this.fixedBillingUnit(row.billingElement);
|
||||||
|
if (!this.canSelectBillingUnit(row)) return '当前计费要素无需选择';
|
||||||
|
return '请选择';
|
||||||
|
},
|
||||||
unitOptionsFor(row) {
|
unitOptionsFor(row) {
|
||||||
|
const fixedUnit = this.fixedBillingUnit(row?.billingElement);
|
||||||
|
if (fixedUnit) {
|
||||||
|
return [{ id: fixedUnit, label: fixedUnit, value: fixedUnit }];
|
||||||
|
}
|
||||||
|
if (!this.canSelectBillingUnit(row)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const dimension = this.measurementDimension(row?.billingElement);
|
const dimension = this.measurementDimension(row?.billingElement);
|
||||||
if (dimension) {
|
if (dimension) {
|
||||||
return this.measurementUnits
|
return this.measurementUnits
|
||||||
@@ -663,15 +688,18 @@ export default {
|
|||||||
}))
|
}))
|
||||||
.filter(item => item.value);
|
.filter(item => item.value);
|
||||||
}
|
}
|
||||||
return (this.unitOptions || [])
|
return [];
|
||||||
.map(item => ({
|
|
||||||
id: item.id || item.dictKey || item.dictValue,
|
|
||||||
label: item.dictValue,
|
|
||||||
value: item.dictValue,
|
|
||||||
}))
|
|
||||||
.filter(item => item.value);
|
|
||||||
},
|
},
|
||||||
syncBillingUnit(row) {
|
syncBillingUnit(row) {
|
||||||
|
const fixedUnit = this.fixedBillingUnit(row?.billingElement);
|
||||||
|
if (fixedUnit) {
|
||||||
|
row.billingUnit = fixedUnit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.canSelectBillingUnit(row)) {
|
||||||
|
row.billingUnit = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
const options = this.unitOptionsFor(row);
|
const options = this.unitOptionsFor(row);
|
||||||
if (!options.some(item => String(item.value) === String(row.billingUnit || ''))) {
|
if (!options.some(item => String(item.value) === String(row.billingUnit || ''))) {
|
||||||
row.billingUnit = '';
|
row.billingUnit = '';
|
||||||
@@ -892,7 +920,6 @@ export default {
|
|||||||
['taxRate', '税率'],
|
['taxRate', '税率'],
|
||||||
['billingElement', '计费要素'],
|
['billingElement', '计费要素'],
|
||||||
['billingType', '计费类型'],
|
['billingType', '计费类型'],
|
||||||
['billingUnit', '计费单位'],
|
|
||||||
];
|
];
|
||||||
for (const [i, row] of this.draft.rules.entries()) {
|
for (const [i, row] of this.draft.rules.entries()) {
|
||||||
const empty = required.find(
|
const empty = required.find(
|
||||||
@@ -902,6 +929,15 @@ export default {
|
|||||||
this.$message.warning(`第${i + 1}行${empty[1]}不能为空`);
|
this.$message.warning(`第${i + 1}行${empty[1]}不能为空`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
this.canSelectBillingUnit(row) &&
|
||||||
|
(row.billingUnit === undefined ||
|
||||||
|
row.billingUnit === null ||
|
||||||
|
String(row.billingUnit).trim() === '')
|
||||||
|
) {
|
||||||
|
this.$message.warning(`第${i + 1}行计费单位不能为空`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const feeItem = String(row.feeItem).trim();
|
const feeItem = String(row.feeItem).trim();
|
||||||
if (feeItemSet.has(feeItem)) {
|
if (feeItemSet.has(feeItem)) {
|
||||||
this.$message.warning(`费用项“${feeItem}”不能重复`);
|
this.$message.warning(`费用项“${feeItem}”不能重复`);
|
||||||
|
|||||||
@@ -1899,11 +1899,11 @@
|
|||||||
label-width="auto"
|
label-width="auto"
|
||||||
class="business-crud-page__module-form business-crud-page__module-form--three"
|
class="business-crud-page__module-form business-crud-page__module-form--three"
|
||||||
>
|
>
|
||||||
<el-form-item label="账单起始日期" required :error="settlementRuleErrors.billStartDate">
|
<el-form-item label="账单起始日" required :error="settlementRuleErrors.billStartDate">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="settlementRuleForm.billStartDate"
|
v-model="settlementRuleForm.billStartDate"
|
||||||
type="date"
|
type="date"
|
||||||
placeholder="请选择账单起始日期"
|
placeholder="请选择账单起始日"
|
||||||
format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
:disabled="dialogReadonly"
|
:disabled="dialogReadonly"
|
||||||
@@ -7784,8 +7784,8 @@ export default {
|
|||||||
const rule = this.normalizeSettlementRule(source);
|
const rule = this.normalizeSettlementRule(source);
|
||||||
this.settlementRuleErrors = {};
|
this.settlementRuleErrors = {};
|
||||||
if (this.isBillingFieldEmpty(rule.billStartDate)) {
|
if (this.isBillingFieldEmpty(rule.billStartDate)) {
|
||||||
this.settlementRuleErrors = { billStartDate: '请选择账单起始日期' };
|
this.settlementRuleErrors = { billStartDate: '请选择账单起始日' };
|
||||||
this.$message.warning(`${label}${label ? ':' : ''}请选择账单起始日期`);
|
this.$message.warning(`${label}${label ? ':' : ''}请选择账单起始日`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.isBillingFieldEmpty(rule.settlementType)) {
|
if (this.isBillingFieldEmpty(rule.settlementType)) {
|
||||||
|
|||||||
@@ -103,8 +103,13 @@
|
|||||||
<div class="contract-attachment-upload-dialog__body">
|
<div class="contract-attachment-upload-dialog__body">
|
||||||
<div class="contract-attachment-upload-dialog__field">
|
<div class="contract-attachment-upload-dialog__field">
|
||||||
<span class="contract-attachment-upload-dialog__label">附件位置</span>
|
<span class="contract-attachment-upload-dialog__label">附件位置</span>
|
||||||
<el-select :model-value="attachmentLocation" disabled style="width: 100%">
|
<el-select v-model="uploadDialogLocation" placeholder="请选择附件位置" style="width: 100%">
|
||||||
<el-option :label="attachmentLocation" :value="attachmentLocation" />
|
<el-option
|
||||||
|
v-for="item in resolvedLocationOptions"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item"
|
||||||
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="contract-attachment-upload-dialog__field">
|
<div class="contract-attachment-upload-dialog__field">
|
||||||
@@ -273,15 +278,17 @@ export default {
|
|||||||
markApprovedOnUpload: Boolean,
|
markApprovedOnUpload: Boolean,
|
||||||
useUploadDialog: Boolean,
|
useUploadDialog: Boolean,
|
||||||
attachmentLocation: { type: String, default: '合同文件' },
|
attachmentLocation: { type: String, default: '合同文件' },
|
||||||
|
attachmentLocationOptions: { type: Array, default: null },
|
||||||
preview: { type: Function, default: null },
|
preview: { type: Function, default: null },
|
||||||
},
|
},
|
||||||
emits: ['update:rows'],
|
emits: ['update:rows', 'upload-to-location'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selected: [],
|
selected: [],
|
||||||
attachmentFileTypes,
|
attachmentFileTypes,
|
||||||
contractAttachmentTypeOptions: [...defaultContractAttachmentTypeOptions],
|
contractAttachmentTypeOptions: [...defaultContractAttachmentTypeOptions],
|
||||||
uploadDialogVisible: false,
|
uploadDialogVisible: false,
|
||||||
|
uploadDialogLocation: '合同文件',
|
||||||
uploadDialogType: resolveOtherAttachmentType(defaultContractAttachmentTypeOptions),
|
uploadDialogType: resolveOtherAttachmentType(defaultContractAttachmentTypeOptions),
|
||||||
uploadDialogFiles: [],
|
uploadDialogFiles: [],
|
||||||
uploadDialogFileList: [],
|
uploadDialogFileList: [],
|
||||||
@@ -300,6 +307,11 @@ export default {
|
|||||||
defaultAttachmentType() {
|
defaultAttachmentType() {
|
||||||
return resolveOtherAttachmentType(this.contractAttachmentTypeOptions);
|
return resolveOtherAttachmentType(this.contractAttachmentTypeOptions);
|
||||||
},
|
},
|
||||||
|
resolvedLocationOptions() {
|
||||||
|
const options = (this.attachmentLocationOptions || []).filter(Boolean);
|
||||||
|
if (options.length) return options;
|
||||||
|
return ['合同文件', '其它附件'];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadAttachmentTypeOptions();
|
this.loadAttachmentTypeOptions();
|
||||||
@@ -340,6 +352,10 @@ export default {
|
|||||||
this.update([...this.rows]);
|
this.update([...this.rows]);
|
||||||
},
|
},
|
||||||
openUploadDialog() {
|
openUploadDialog() {
|
||||||
|
const options = this.resolvedLocationOptions;
|
||||||
|
this.uploadDialogLocation = options.includes(this.attachmentLocation)
|
||||||
|
? this.attachmentLocation
|
||||||
|
: options[0] || this.attachmentLocation;
|
||||||
this.uploadDialogType = this.defaultAttachmentType;
|
this.uploadDialogType = this.defaultAttachmentType;
|
||||||
this.uploadDialogFiles = [];
|
this.uploadDialogFiles = [];
|
||||||
this.uploadDialogFileList = [];
|
this.uploadDialogFileList = [];
|
||||||
@@ -348,6 +364,7 @@ export default {
|
|||||||
resetUploadDialog() {
|
resetUploadDialog() {
|
||||||
this.uploadDialogFiles = [];
|
this.uploadDialogFiles = [];
|
||||||
this.uploadDialogFileList = [];
|
this.uploadDialogFileList = [];
|
||||||
|
this.uploadDialogLocation = this.attachmentLocation;
|
||||||
this.uploadDialogType = this.defaultAttachmentType;
|
this.uploadDialogType = this.defaultAttachmentType;
|
||||||
},
|
},
|
||||||
beforeUpload(file) {
|
beforeUpload(file) {
|
||||||
@@ -416,6 +433,10 @@ export default {
|
|||||||
this.$message.warning('请先上传附件');
|
this.$message.warning('请先上传附件');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!this.uploadDialogLocation) {
|
||||||
|
this.$message.warning('请选择附件位置');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.uploadDialogType) {
|
if (!this.uploadDialogType) {
|
||||||
this.$message.warning('请选择附件类型');
|
this.$message.warning('请选择附件类型');
|
||||||
return;
|
return;
|
||||||
@@ -430,7 +451,14 @@ export default {
|
|||||||
uploadTime: row.uploadTime || uploadTime,
|
uploadTime: row.uploadTime || uploadTime,
|
||||||
...(this.markApprovedOnUpload ? { approved: true } : {}),
|
...(this.markApprovedOnUpload ? { approved: true } : {}),
|
||||||
}));
|
}));
|
||||||
this.update([...(this.rows || []), ...appended]);
|
if (this.uploadDialogLocation === this.attachmentLocation) {
|
||||||
|
this.update([...(this.rows || []), ...appended]);
|
||||||
|
} else {
|
||||||
|
this.$emit('upload-to-location', {
|
||||||
|
location: this.uploadDialogLocation,
|
||||||
|
rows: appended,
|
||||||
|
});
|
||||||
|
}
|
||||||
this.uploadDialogVisible = false;
|
this.uploadDialogVisible = false;
|
||||||
},
|
},
|
||||||
handleChange(rows) {
|
handleChange(rows) {
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="loading" class="master-detail">
|
<div v-loading="loading" class="master-detail">
|
||||||
<section v-if="master" class="detail-overview">
|
<section v-if="master" class="detail-overview">
|
||||||
<div class="detail-heading"><div><h2>多联总单详情 <span>|</span> {{ master.masterNo }}</h2><el-tag :type="statusType(master.businessStatus)" class="status-text">{{ statusName(master.businessStatus) }}</el-tag><el-tag v-if="transportFlowLabel" type="info" class="transport-flow-tag">{{ transportFlowLabel }}</el-tag></div></div>
|
<div class="detail-heading">
|
||||||
|
<div>
|
||||||
|
<h2>多联总单详情 <span>|</span> {{ master.masterNo }}</h2>
|
||||||
|
<el-tag :type="statusType(master.businessStatus)">{{
|
||||||
|
statusName(master.businessStatus)
|
||||||
|
}}</el-tag>
|
||||||
|
<el-tag v-if="transportFlowLabel" type="primary" class="transport-flow-tag">{{
|
||||||
|
transportFlowLabel
|
||||||
|
}}</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<dl class="detail-meta">
|
<dl class="detail-meta">
|
||||||
<div>
|
<div>
|
||||||
<dt>客户</dt>
|
<dt>客户</dt>
|
||||||
@@ -132,6 +142,7 @@ export default {
|
|||||||
dateRange() { return this.master?.planStartTime && this.master?.planEndTime ? `${String(this.master.planStartTime).slice(0, 10)} ~ ${String(this.master.planEndTime).slice(0, 10)}` : '-'; },
|
dateRange() { return this.master?.planStartTime && this.master?.planEndTime ? `${String(this.master.planStartTime).slice(0, 10)} ~ ${String(this.master.planEndTime).slice(0, 10)}` : '-'; },
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
if (!this.id) return;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const res = await api.getDetail(this.id);
|
const res = await api.getDetail(this.id);
|
||||||
@@ -282,8 +293,36 @@ export default {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.master-detail { padding-bottom: 20px; color: #303133; }
|
.master-detail { padding-bottom: 20px; color: #303133; }
|
||||||
.detail-overview, .execution-detail-card { margin-bottom: 8px; border: 1px solid #eff1f7; background: #fff; }
|
.detail-overview, .execution-detail-card { margin-bottom: 8px; border: 1px solid #eff1f7; background: #fff; }
|
||||||
.detail-heading { display: flex; align-items: center; justify-content: space-between; padding: 18px 24px; h2 { display: inline-block; margin: 0 16px 0 0; font-size: 20px; } h2 span { margin: 0 8px; color: #909399; font-weight: 400; } }
|
.detail-heading {
|
||||||
.transport-flow-tag { margin-left: 8px; }
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 18px 24px;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 16px 0 0;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 span {
|
||||||
|
margin: 0 8px;
|
||||||
|
color: #909399;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tag) {
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 12px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.transport-flow-tag {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
.detail-meta { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px 24px; margin: 0; padding: 0 24px 20px; dt { margin-bottom: 6px; color: #909399; font-size: 13px; } dd { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 0; color: #303133; font-size: 14px; word-break: break-all; } :deep(.el-link) { font-size: 14px; } }
|
.detail-meta { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px 24px; margin: 0; padding: 0 24px 20px; dt { margin-bottom: 6px; color: #909399; font-size: 13px; } dd { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 0; color: #303133; font-size: 14px; word-break: break-all; } :deep(.el-link) { font-size: 14px; } }
|
||||||
.route-map { display: flex; align-items: flex-start; padding: 16px 24px 20px; border-top: 1px solid #eff1f7; overflow-x: auto; }
|
.route-map { display: flex; align-items: flex-start; padding: 16px 24px 20px; border-top: 1px solid #eff1f7; overflow-x: auto; }
|
||||||
.route-map__node { display: grid; flex: 0 0 150px; justify-items: center; gap: 6px; text-align: center; strong { font-size: 16px; white-space: nowrap; } small { color: #606266; white-space: nowrap; } }
|
.route-map__node { display: grid; flex: 0 0 150px; justify-items: center; gap: 6px; text-align: center; strong { font-size: 16px; white-space: nowrap; } small { color: #606266; white-space: nowrap; } }
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async load() {
|
async load() {
|
||||||
|
if (!this.id) return;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const res = await api.getDetail(this.id);
|
const res = await api.getDetail(this.id);
|
||||||
|
|||||||
@@ -113,6 +113,12 @@
|
|||||||
label="导入方式"
|
label="导入方式"
|
||||||
width="100"
|
width="100"
|
||||||
/>
|
/>
|
||||||
|
<el-table-column
|
||||||
|
v-if="columnVisible.statusName"
|
||||||
|
prop="statusName"
|
||||||
|
label="状态"
|
||||||
|
width="100"
|
||||||
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-if="columnVisible.createUserName"
|
v-if="columnVisible.createUserName"
|
||||||
prop="createUserName"
|
prop="createUserName"
|
||||||
@@ -121,12 +127,6 @@
|
|||||||
>
|
>
|
||||||
<template #default="{ row }">{{ row.createUserName || '-' }}</template>
|
<template #default="{ row }">{{ row.createUserName || '-' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
|
||||||
v-if="columnVisible.statusName"
|
|
||||||
prop="statusName"
|
|
||||||
label="状态"
|
|
||||||
width="100"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-if="columnVisible.createTime"
|
v-if="columnVisible.createTime"
|
||||||
prop="createTime"
|
prop="createTime"
|
||||||
@@ -621,8 +621,8 @@ const columnOptions = [
|
|||||||
{ prop: 'carrierType', label: '承运类型' },
|
{ prop: 'carrierType', label: '承运类型' },
|
||||||
{ prop: 'waybillCount', label: '运单数' },
|
{ prop: 'waybillCount', label: '运单数' },
|
||||||
{ prop: 'importTypeName', label: '导入方式' },
|
{ prop: 'importTypeName', label: '导入方式' },
|
||||||
{ prop: 'createUserName', label: '创建人' },
|
|
||||||
{ prop: 'statusName', label: '状态' },
|
{ prop: 'statusName', label: '状态' },
|
||||||
|
{ prop: 'createUserName', label: '创建人' },
|
||||||
{ prop: 'createTime', label: '创建时间' },
|
{ prop: 'createTime', label: '创建时间' },
|
||||||
{ prop: 'updateTime', label: '更新时间' },
|
{ prop: 'updateTime', label: '更新时间' },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1774,12 +1774,17 @@
|
|||||||
<div class="waybill-manage-page__waybill-heading">
|
<div class="waybill-manage-page__waybill-heading">
|
||||||
<strong>运单详情</strong>
|
<strong>运单详情</strong>
|
||||||
<span>{{ detailRow.waybillNo || '-' }}</span>
|
<span>{{ detailRow.waybillNo || '-' }}</span>
|
||||||
<span class="detail-status-text status-text-success">{{
|
<el-tag :type="statusTagType(detailRow.businessStatus)">
|
||||||
displayStatus(detailRow, 'businessStatus')
|
{{ displayStatus(detailRow, 'businessStatus') }}
|
||||||
}}</span>
|
</el-tag>
|
||||||
<span class="detail-status-text detail-transport-type">
|
<el-tag type="primary">
|
||||||
{{ getTransportTypeLabel(waybillTransportMode(detailRow)) || '-' }}
|
{{
|
||||||
</span>
|
detailRow.transportTypeName ||
|
||||||
|
getTransportTypeLabel(detailRow.transportType) ||
|
||||||
|
waybillTransportMode(detailRow) ||
|
||||||
|
'-'
|
||||||
|
}}
|
||||||
|
</el-tag>
|
||||||
<el-link
|
<el-link
|
||||||
v-if="detailRow.id"
|
v-if="detailRow.id"
|
||||||
class="waybill-manage-page__waybill-route-change-link"
|
class="waybill-manage-page__waybill-route-change-link"
|
||||||
@@ -1927,7 +1932,8 @@
|
|||||||
v-if="!waybillIsCarrier(detailRow)"
|
v-if="!waybillIsCarrier(detailRow)"
|
||||||
class="waybill-manage-page__waybill-detail-field"
|
class="waybill-manage-page__waybill-detail-field"
|
||||||
>
|
>
|
||||||
<span>里程(km)</span><strong>{{ detailRow.mileage || '-' }}</strong>
|
<span>里程(km)</span
|
||||||
|
><strong>{{ normalizeMileageValue(detailRow.mileage) || '-' }}</strong>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="waybillIsNonRoadTransport(detailRow)">
|
<template v-else-if="waybillIsNonRoadTransport(detailRow)">
|
||||||
@@ -2183,7 +2189,11 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="司机上传" name="driverUpload">
|
<el-tab-pane
|
||||||
|
v-if="waybillDriverUploads.length"
|
||||||
|
label="司机上传"
|
||||||
|
name="driverUpload"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-loading="waybillPunchRecordsLoading"
|
v-loading="waybillPunchRecordsLoading"
|
||||||
class="waybill-manage-page__driver-upload-grid"
|
class="waybill-manage-page__driver-upload-grid"
|
||||||
@@ -2200,11 +2210,6 @@
|
|||||||
{{ photo.label || '凭证' }}
|
{{ photo.label || '凭证' }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-empty
|
|
||||||
v-if="!waybillPunchRecordsLoading && !waybillDriverUploads.length"
|
|
||||||
description="暂无司机上传数据"
|
|
||||||
:image-size="50"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
@@ -2315,7 +2320,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="detailPageLocked" class="waybill-manage-page__detail-footer">
|
<div v-if="detailPageLocked && !isPublicWaybillView" class="waybill-manage-page__detail-footer">
|
||||||
<el-button type="primary" @click="closeDetail">关闭</el-button>
|
<el-button type="primary" @click="closeDetail">关闭</el-button>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="!detailPageLocked" #footer>
|
<template v-if="!detailPageLocked" #footer>
|
||||||
@@ -3122,6 +3127,7 @@ import {
|
|||||||
getVoucherImages as getProcessConfigVoucherImages,
|
getVoucherImages as getProcessConfigVoucherImages,
|
||||||
} from '@/api/business/process-config';
|
} from '@/api/business/process-config';
|
||||||
import { getPunchRecords as getWaybillPunchRecords, locateVehicle, trackVehicle } from '@/api/business/waybill-manage';
|
import { getPunchRecords as getWaybillPunchRecords, locateVehicle, trackVehicle } from '@/api/business/waybill-manage';
|
||||||
|
import { getMkPublicDetail } from '@/api/mk-process';
|
||||||
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';
|
||||||
@@ -3646,6 +3652,7 @@ export default {
|
|||||||
this.formPageLocked = this.isStandaloneWaybillFormPage;
|
this.formPageLocked = this.isStandaloneWaybillFormPage;
|
||||||
this.detailPageLocked = this.isStandaloneWaybillDetailPage;
|
this.detailPageLocked = this.isStandaloneWaybillDetailPage;
|
||||||
this.formModeLocked = this.$route.query.mode || '';
|
this.formModeLocked = this.$route.query.mode || '';
|
||||||
|
if (this.isPublicWaybillView) return;
|
||||||
if (this.config.enableAllDept && this.isAdmin) {
|
if (this.config.enableAllDept && this.isAdmin) {
|
||||||
this.allDept = 1;
|
this.allDept = 1;
|
||||||
}
|
}
|
||||||
@@ -3709,8 +3716,14 @@ export default {
|
|||||||
isStandaloneWaybillFormPage() {
|
isStandaloneWaybillFormPage() {
|
||||||
return this.isStandaloneWaybillPage && ['add', 'edit'].includes(this.$route.query.mode);
|
return this.isStandaloneWaybillPage && ['add', 'edit'].includes(this.$route.query.mode);
|
||||||
},
|
},
|
||||||
|
isPublicWaybillView() {
|
||||||
|
return this.$route.path === '/business/waybill-manage/public-view';
|
||||||
|
},
|
||||||
isStandaloneWaybillDetailPage() {
|
isStandaloneWaybillDetailPage() {
|
||||||
return this.standaloneDetailPage && this.$route.path === standaloneWaybillDetailRoute;
|
return (
|
||||||
|
(this.standaloneDetailPage && this.$route.path === standaloneWaybillDetailRoute) ||
|
||||||
|
this.isPublicWaybillView
|
||||||
|
);
|
||||||
},
|
},
|
||||||
// 容器形态读实例创建时锁定的标志,不随 $route 翻转(详见 data 中 formPageLocked 的说明)
|
// 容器形态读实例创建时锁定的标志,不随 $route 翻转(详见 data 中 formPageLocked 的说明)
|
||||||
// 当前路由是否仍属于本实例(tab.js 按 fullPath 建实例,一个实例只对应一个 path)。
|
// 当前路由是否仍属于本实例(tab.js 按 fullPath 建实例,一个实例只对应一个 path)。
|
||||||
@@ -4591,14 +4604,19 @@ export default {
|
|||||||
this.waybillVoucherFolder = null;
|
this.waybillVoucherFolder = null;
|
||||||
this.waybillVoucherFolderView = false;
|
this.waybillVoucherFolderView = false;
|
||||||
this.waybillVoucherImagesLoaded = false;
|
this.waybillVoucherImagesLoaded = false;
|
||||||
const request =
|
const request = this.isPublicWaybillView
|
||||||
typeof this.api.getDetail === 'function'
|
? getMkPublicDetail('waybill-manage', row.id)
|
||||||
|
: typeof this.api.getDetail === 'function'
|
||||||
? this.api.getDetail(row.id)
|
? this.api.getDetail(row.id)
|
||||||
: Promise.resolve({ data: { data: row } });
|
: Promise.resolve({ data: { data: row } });
|
||||||
request
|
request
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const detail = res?.data?.data || res?.data || row;
|
const detail = res?.data?.data || res?.data || row;
|
||||||
this.detailRow = detail;
|
this.detailRow = detail;
|
||||||
|
if (this.isPublicWaybillView) {
|
||||||
|
this.applyFormDetail(detail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.loadWaybillPunchRecords();
|
this.loadWaybillPunchRecords();
|
||||||
this.loadWaybillVoucherImages();
|
this.loadWaybillVoucherImages();
|
||||||
if (detail.contractId) {
|
if (detail.contractId) {
|
||||||
@@ -5251,7 +5269,9 @@ export default {
|
|||||||
: defaultText;
|
: defaultText;
|
||||||
},
|
},
|
||||||
statusTagType(status) {
|
statusTagType(status) {
|
||||||
if ([1, 'pending', 'processing', 'approved', 'change_approved'].includes(status)) {
|
if (
|
||||||
|
[1, 'pending', 'processing', 'running', 'approved', 'change_approved'].includes(status)
|
||||||
|
) {
|
||||||
return 'success';
|
return 'success';
|
||||||
}
|
}
|
||||||
if ([2, 'cancelled', 'withdrawn', 'draft'].includes(status)) {
|
if ([2, 'cancelled', 'withdrawn', 'draft'].includes(status)) {
|
||||||
@@ -8560,7 +8580,7 @@ export default {
|
|||||||
};
|
};
|
||||||
this.mileageForm = {
|
this.mileageForm = {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
mileage: row.mileage === null || row.mileage === undefined ? '' : String(row.mileage),
|
mileage: this.normalizeMileageValue(row.mileage),
|
||||||
mileageRemark: row.mileageRemark || '',
|
mileageRemark: row.mileageRemark || '',
|
||||||
};
|
};
|
||||||
this.$nextTick(() => this.$refs.mileageFormRef?.clearValidate());
|
this.$nextTick(() => this.$refs.mileageFormRef?.clearValidate());
|
||||||
@@ -9749,6 +9769,15 @@ export default {
|
|||||||
strong {
|
strong {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-tag) {
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 12px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__waybill-route-change-link {
|
&__waybill-route-change-link {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
type="date"
|
type="date"
|
||||||
format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="YYYY-MM-DD"
|
placeholder="请输入"
|
||||||
/>
|
/>
|
||||||
<span>至</span>
|
<span>至</span>
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
type="date"
|
type="date"
|
||||||
format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="YYYY-MM-DD"
|
placeholder="请输入"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -92,12 +92,12 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="合同金额">
|
<el-form-item label="合同金额">
|
||||||
<el-input-number
|
<el-input
|
||||||
v-model="form.contractAmount"
|
:model-value="contractAmountDisplay"
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:controls="false"
|
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
@input="handleContractAmountInput"
|
||||||
|
@clear="form.contractAmount = null"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="是否范本">
|
<el-form-item label="是否范本">
|
||||||
@@ -190,9 +190,11 @@
|
|||||||
description
|
description
|
||||||
attachment-type
|
attachment-type
|
||||||
use-upload-dialog
|
use-upload-dialog
|
||||||
|
:attachment-location-options="changeAttachmentLocationOptions"
|
||||||
:rows="contractFileRows"
|
:rows="contractFileRows"
|
||||||
:preview="previewAttachment"
|
:preview="previewAttachment"
|
||||||
@update:rows="contractFileRows = $event"
|
@update:rows="contractFileRows = $event"
|
||||||
|
@upload-to-location="handleAttachmentUploadToLocation"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section class="change-section">
|
<section class="change-section">
|
||||||
@@ -218,7 +220,7 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<el-form v-if="settlementRule.autoGenerate === 1" :model="settlementRule" label-position="right" label-width="auto" class="settlement-form">
|
<el-form v-if="settlementRule.autoGenerate === 1" :model="settlementRule" label-position="right" label-width="auto" class="settlement-form">
|
||||||
<el-form-item label="账单起始日期" required><el-date-picker v-model="settlementRule.billStartDate" type="date" placeholder="请选择账单起始日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item>
|
<el-form-item label="账单起始日" required><el-date-picker v-model="settlementRule.billStartDate" type="date" placeholder="请选择账单起始日" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item>
|
||||||
<el-form-item label="结算类型" required><el-select v-model="settlementRule.settlementType" placeholder="请选择结算类型" @change="handleSettlementTypeChange"><el-option v-for="item in settlementTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
|
<el-form-item label="结算类型" required><el-select v-model="settlementRule.settlementType" placeholder="请选择结算类型" @change="handleSettlementTypeChange"><el-option v-for="item in settlementTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
|
||||||
<el-form-item v-if="showSettlementBillCycleType" label="结算周期" required><el-select v-model="settlementRule.billCycleType" placeholder="请选择结算周期" @change="handleCycleTypeChange"><el-option v-for="item in billCycleTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
|
<el-form-item v-if="showSettlementBillCycleType" label="结算周期" required><el-select v-model="settlementRule.billCycleType" placeholder="请选择结算周期" @change="handleCycleTypeChange"><el-option v-for="item in billCycleTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
|
||||||
<el-form-item v-if="showSettlementBillCutoffDay" label="账单截单日" required><el-select v-model="settlementRule.billCutoffDay" placeholder="请选择账单截单日"><el-option v-for="item in billCutoffDayOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
|
<el-form-item v-if="showSettlementBillCutoffDay" label="账单截单日" required><el-select v-model="settlementRule.billCutoffDay" placeholder="请选择账单截单日"><el-option v-for="item in billCutoffDayOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
|
||||||
@@ -258,9 +260,11 @@
|
|||||||
attachment-type
|
attachment-type
|
||||||
use-upload-dialog
|
use-upload-dialog
|
||||||
attachment-location="其它附件"
|
attachment-location="其它附件"
|
||||||
|
:attachment-location-options="changeAttachmentLocationOptions"
|
||||||
:rows="attachments"
|
:rows="attachments"
|
||||||
:preview="previewAttachment"
|
:preview="previewAttachment"
|
||||||
@update:rows="attachments = $event"
|
@update:rows="attachments = $event"
|
||||||
|
@upload-to-location="handleAttachmentUploadToLocation"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section class="change-section change-reason-section">
|
<section class="change-section change-reason-section">
|
||||||
@@ -283,9 +287,11 @@
|
|||||||
attachment-type
|
attachment-type
|
||||||
use-upload-dialog
|
use-upload-dialog
|
||||||
attachment-location="变更材料"
|
attachment-location="变更材料"
|
||||||
|
:attachment-location-options="changeAttachmentLocationOptions"
|
||||||
:rows="changeMaterials"
|
:rows="changeMaterials"
|
||||||
:preview="previewAttachment"
|
:preview="previewAttachment"
|
||||||
@update:rows="changeMaterials = $event"
|
@update:rows="changeMaterials = $event"
|
||||||
|
@upload-to-location="handleAttachmentUploadToLocation"
|
||||||
/>
|
/>
|
||||||
<div class="page-footer">
|
<div class="page-footer">
|
||||||
<el-button @click="$router.back()">取消</el-button>
|
<el-button @click="$router.back()">取消</el-button>
|
||||||
@@ -343,7 +349,15 @@ const normalizeOptionalPositiveInteger = value => {
|
|||||||
return Number.isInteger(number) && number > 0 ? number : null;
|
return Number.isInteger(number) && number > 0 ? number : null;
|
||||||
};
|
};
|
||||||
const normalizeOptionalAmount = value => {
|
const normalizeOptionalAmount = value => {
|
||||||
if (value === undefined || value === null || value === '') return null;
|
if (
|
||||||
|
value === undefined ||
|
||||||
|
value === null ||
|
||||||
|
value === '' ||
|
||||||
|
Number(value) === -1 ||
|
||||||
|
String(value) === '-1'
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const number = Number(value);
|
const number = Number(value);
|
||||||
return Number.isFinite(number) && number >= 0 ? Number(number.toFixed(2)) : null;
|
return Number.isFinite(number) && number >= 0 ? Number(number.toFixed(2)) : null;
|
||||||
};
|
};
|
||||||
@@ -421,6 +435,22 @@ export default {
|
|||||||
pageTitle() {
|
pageTitle() {
|
||||||
return this.$route.query.name || '合同变更';
|
return this.$route.query.name || '合同变更';
|
||||||
},
|
},
|
||||||
|
changeAttachmentLocationOptions() {
|
||||||
|
return ['合同文件', '其它附件', '变更材料'];
|
||||||
|
},
|
||||||
|
contractAmountDisplay() {
|
||||||
|
const value = this.form.contractAmount;
|
||||||
|
if (
|
||||||
|
value === undefined ||
|
||||||
|
value === null ||
|
||||||
|
value === '' ||
|
||||||
|
Number(value) === -1 ||
|
||||||
|
String(value) === '-1'
|
||||||
|
) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
contractCategoryOptions() {
|
contractCategoryOptions() {
|
||||||
return this.contractCategoryDictOptions.length
|
return this.contractCategoryDictOptions.length
|
||||||
? this.contractCategoryDictOptions
|
? this.contractCategoryDictOptions
|
||||||
@@ -464,6 +494,13 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleContractAmountInput(value) {
|
||||||
|
const text = String(value || '').replace(/[^\d.]/g, '');
|
||||||
|
const parts = text.split('.');
|
||||||
|
const normalized =
|
||||||
|
parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0];
|
||||||
|
this.form.contractAmount = normalized === '' ? null : normalized;
|
||||||
|
},
|
||||||
loadDictionaries() {
|
loadDictionaries() {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
getSystemDictionary({ code: 'currency_type' }),
|
getSystemDictionary({ code: 'currency_type' }),
|
||||||
@@ -619,6 +656,20 @@ export default {
|
|||||||
attachmentExtension(row = {}) { const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0]; const index = source.lastIndexOf('.'); return index > -1 ? source.slice(index + 1).toLowerCase() : ''; },
|
attachmentExtension(row = {}) { const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0]; const index = source.lastIndexOf('.'); return index > -1 ? source.slice(index + 1).toLowerCase() : ''; },
|
||||||
isAttachmentImage(row) { return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(this.attachmentExtension(row)); },
|
isAttachmentImage(row) { return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(this.attachmentExtension(row)); },
|
||||||
previewAttachment(row, rows = this.attachments) { const url = this.attachmentUrl(row); if (!url) { this.$message.warning('附件地址为空,无法预览'); return; } if (this.isAttachmentImage(row)) { this.imagePreviewUrls = (rows || []).filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item)).map(item => this.attachmentUrl(item)); this.imagePreviewIndex = Math.max(this.imagePreviewUrls.indexOf(url), 0); this.imagePreviewVisible = true; return; } this.previewFile = { name: this.attachmentName(row), url, mimeType: row.mimeType || row.contentType || '' }; this.documentPreviewVisible = true; },
|
previewAttachment(row, rows = this.attachments) { const url = this.attachmentUrl(row); if (!url) { this.$message.warning('附件地址为空,无法预览'); return; } if (this.isAttachmentImage(row)) { this.imagePreviewUrls = (rows || []).filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item)).map(item => this.attachmentUrl(item)); this.imagePreviewIndex = Math.max(this.imagePreviewUrls.indexOf(url), 0); this.imagePreviewVisible = true; return; } this.previewFile = { name: this.attachmentName(row), url, mimeType: row.mimeType || row.contentType || '' }; this.documentPreviewVisible = true; },
|
||||||
|
handleAttachmentUploadToLocation({ location, rows = [] }) {
|
||||||
|
if (!rows.length) return;
|
||||||
|
if (location === '合同文件') {
|
||||||
|
this.contractFileRows = [...(this.contractFileRows || []), ...rows];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (location === '其它附件') {
|
||||||
|
this.attachments = [...(this.attachments || []), ...rows];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (location === '变更材料') {
|
||||||
|
this.changeMaterials = [...(this.changeMaterials || []), ...rows];
|
||||||
|
}
|
||||||
|
},
|
||||||
handlePreviewUnsupported() { this.$message.warning('当前文件暂不支持在线预览'); },
|
handlePreviewUnsupported() { this.$message.warning('当前文件暂不支持在线预览'); },
|
||||||
handlePreviewError() { this.$message.error('附件预览失败'); },
|
handlePreviewError() { this.$message.error('附件预览失败'); },
|
||||||
formatFileSize(value) { const size = Number(value || 0); return size > 1024 * 1024 ? `${(size / 1024 / 1024).toFixed(2)}MB` : `${Math.max(1, Math.ceil(size / 1024))}KB`; },
|
formatFileSize(value) { const size = Number(value || 0); return size > 1024 * 1024 ? `${(size / 1024 / 1024).toFixed(2)}MB` : `${Math.max(1, Math.ceil(size / 1024))}KB`; },
|
||||||
@@ -629,7 +680,7 @@ export default {
|
|||||||
addCustomPeriodRow() { const periods = normalizeCustomPeriods(this.settlementRule.customPeriods || []); periods.push({ startDay: '', endDay: '' }); this.settlementRule.customPeriods = periods; },
|
addCustomPeriodRow() { const periods = normalizeCustomPeriods(this.settlementRule.customPeriods || []); periods.push({ startDay: '', endDay: '' }); this.settlementRule.customPeriods = periods; },
|
||||||
removeCustomPeriodRow(index) { if (index <= 0) return; const periods = [...(this.settlementRule.customPeriods || [])]; periods.splice(index, 1); this.settlementRule.customPeriods = normalizeCustomPeriods(periods); },
|
removeCustomPeriodRow(index) { if (index <= 0) return; const periods = [...(this.settlementRule.customPeriods || [])]; periods.splice(index, 1); this.settlementRule.customPeriods = normalizeCustomPeriods(periods); },
|
||||||
validateCustomPeriods(periods = [], label) { const rows = normalizeCustomPeriods(periods); if (!rows.length) { this.$message.warning(`${label}:请至少配置一段自定义周期`); return false; } for (let index = 0; index < rows.length; index += 1) { const row = rows[index]; const startDay = Number(row.startDay); const endDay = Number(row.endDay); if (!Number.isFinite(startDay) || startDay < 1 || startDay > 31) { this.$message.warning(`${label}:请选择第${index + 1}行运单区间开始日`); return false; } if (!Number.isFinite(endDay) || endDay < 1 || endDay > 31) { this.$message.warning(`${label}:请选择第${index + 1}行运单区间结束日`); return false; } if (endDay < startDay) { this.$message.warning(`${label}:第${index + 1}行结束日不能早于开始日`); return false; } if (index > 0 && startDay !== Number(rows[index - 1].endDay) + 1) { this.$message.warning(`${label}:自定义多周期区间必须连续,不允许重叠或存在日期缺口`); return false; } } return true; },
|
validateCustomPeriods(periods = [], label) { const rows = normalizeCustomPeriods(periods); if (!rows.length) { this.$message.warning(`${label}:请至少配置一段自定义周期`); return false; } for (let index = 0; index < rows.length; index += 1) { const row = rows[index]; const startDay = Number(row.startDay); const endDay = Number(row.endDay); if (!Number.isFinite(startDay) || startDay < 1 || startDay > 31) { this.$message.warning(`${label}:请选择第${index + 1}行运单区间开始日`); return false; } if (!Number.isFinite(endDay) || endDay < 1 || endDay > 31) { this.$message.warning(`${label}:请选择第${index + 1}行运单区间结束日`); return false; } if (endDay < startDay) { this.$message.warning(`${label}:第${index + 1}行结束日不能早于开始日`); return false; } if (index > 0 && startDay !== Number(rows[index - 1].endDay) + 1) { this.$message.warning(`${label}:自定义多周期区间必须连续,不允许重叠或存在日期缺口`); return false; } } return true; },
|
||||||
validateSettlementRule(rule, label) { if (Number(rule.autoGenerate) !== 1) return true; if (!rule.billStartDate || !rule.settlementType) { this.$message.warning(`${label}:请完整填写账单起始日期和结算类型`); return false; } if (rule.settlementType === '月结' && !rule.billCycleType) { this.$message.warning(`${label}:请选择结算周期`); return false; } if (rule.settlementType === '月结' && rule.billCycleType === '固定截单日' && !rule.billCutoffDay) { this.$message.warning(`${label}:请选择账单截单日`); return false; } if (rule.settlementType === '月结' && rule.billCycleType === '自定义多周期') return this.validateCustomPeriods(rule.customPeriods, label); if (rule.settlementType === '固定天数周期结算' && !rule.cycleDays) { this.$message.warning(`${label}:请选择周期天数`); return false; } return true; },
|
validateSettlementRule(rule, label) { if (Number(rule.autoGenerate) !== 1) return true; if (!rule.billStartDate || !rule.settlementType) { this.$message.warning(`${label}:请完整填写账单起始日和结算类型`); return false; } if (rule.settlementType === '月结' && !rule.billCycleType) { this.$message.warning(`${label}:请选择结算周期`); return false; } if (rule.settlementType === '月结' && rule.billCycleType === '固定截单日' && !rule.billCutoffDay) { this.$message.warning(`${label}:请选择账单截单日`); return false; } if (rule.settlementType === '月结' && rule.billCycleType === '自定义多周期') return this.validateCustomPeriods(rule.customPeriods, label); if (rule.settlementType === '固定天数周期结算' && !rule.cycleDays) { this.$message.warning(`${label}:请选择周期天数`); return false; } return true; },
|
||||||
async submit() { await this.$refs.formRef.validate(); const total = this.paymentRatioRows.reduce((sum, row) => sum + Number(row.ratioLimit || 0), 0); if (this.paymentRatioRows.length && Math.abs(total - 100) > 0.0001) { this.$message.warning('付款比例上限合计必须等于100%'); return; } if (this.settlementConfigTab === 'pre') this.preSettlementConfig = { ...this.settlementRule }; else this.formalSettlementConfig = { ...this.settlementRule }; if (!this.validateSettlementRule(this.preSettlementConfig, '预结算配置') || !this.validateSettlementRule(this.formalSettlementConfig, '正式结算配置')) return; const settlementRule = { preSettlementConfig: this.preSettlementConfig, formalSettlementConfig: this.formalSettlementConfig }; await api.submitChange({ ...this.form, settlementCurrency: String(this.form.settlementCurrency || '').trim() || 'RMB', copyCount: normalizeOptionalPositiveInteger(this.form.copyCount), invoiceCycle: normalizeOptionalPositiveInteger(this.form.invoiceCycle), paymentDays: normalizeOptionalPositiveInteger(this.form.paymentDays), contractAmount: normalizeOptionalAmount(this.form.contractAmount), startDate: this.period[0], endDate: this.period[1], feeGenerationMode: this.feeGenerationMode, billingEnabled: this.feeGenerationMode === 'system' ? 1 : 0, billingPlanJson: JSON.stringify(this.plans), settlementRuleJson: JSON.stringify(settlementRule), preSettlementConfigJson: JSON.stringify(this.preSettlementConfig), formalSettlementConfigJson: JSON.stringify(this.formalSettlementConfig), paymentRatioJson: JSON.stringify(this.paymentRatioRows), contractFileJson: JSON.stringify(this.contractFileRows), attachmentsJson: JSON.stringify(this.attachments), changeContent: this.form.changeContent, changeReason: this.form.changeReason, changeAttachmentsJson: JSON.stringify(this.changeMaterials) }); await submitMkApprovalFlow({ bizType: 'contract-manage', formInstanceId: this.form.id, subjectName: this.form.contractName || '', approvalStatus: this.form.approvalStatus || 'change_rejected' }); this.$message.success('变更已提交'); this.$router.back(); },
|
async submit() { await this.$refs.formRef.validate(); const total = this.paymentRatioRows.reduce((sum, row) => sum + Number(row.ratioLimit || 0), 0); if (this.paymentRatioRows.length && Math.abs(total - 100) > 0.0001) { this.$message.warning('付款比例上限合计必须等于100%'); return; } if (this.settlementConfigTab === 'pre') this.preSettlementConfig = { ...this.settlementRule }; else this.formalSettlementConfig = { ...this.settlementRule }; if (!this.validateSettlementRule(this.preSettlementConfig, '预结算配置') || !this.validateSettlementRule(this.formalSettlementConfig, '正式结算配置')) return; const settlementRule = { preSettlementConfig: this.preSettlementConfig, formalSettlementConfig: this.formalSettlementConfig }; await api.submitChange({ ...this.form, settlementCurrency: String(this.form.settlementCurrency || '').trim() || 'RMB', copyCount: normalizeOptionalPositiveInteger(this.form.copyCount), invoiceCycle: normalizeOptionalPositiveInteger(this.form.invoiceCycle), paymentDays: normalizeOptionalPositiveInteger(this.form.paymentDays), contractAmount: normalizeOptionalAmount(this.form.contractAmount), startDate: this.period[0], endDate: this.period[1], feeGenerationMode: this.feeGenerationMode, billingEnabled: this.feeGenerationMode === 'system' ? 1 : 0, billingPlanJson: JSON.stringify(this.plans), settlementRuleJson: JSON.stringify(settlementRule), preSettlementConfigJson: JSON.stringify(this.preSettlementConfig), formalSettlementConfigJson: JSON.stringify(this.formalSettlementConfig), paymentRatioJson: JSON.stringify(this.paymentRatioRows), contractFileJson: JSON.stringify(this.contractFileRows), attachmentsJson: JSON.stringify(this.attachments), changeContent: this.form.changeContent, changeReason: this.form.changeReason, changeAttachmentsJson: JSON.stringify(this.changeMaterials) }); await submitMkApprovalFlow({ bizType: 'contract-manage', formInstanceId: this.form.id, subjectName: this.form.contractName || '', approvalStatus: this.form.approvalStatus || 'change_rejected' }); this.$message.success('变更已提交'); this.$router.back(); },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -654,6 +705,14 @@ export default {
|
|||||||
.contract-basic-section :deep(.el-input),
|
.contract-basic-section :deep(.el-input),
|
||||||
.contract-basic-section :deep(.el-select),
|
.contract-basic-section :deep(.el-select),
|
||||||
.contract-basic-section :deep(.el-date-editor) { width: 360px; max-width: 100%; }
|
.contract-basic-section :deep(.el-date-editor) { width: 360px; max-width: 100%; }
|
||||||
|
.contract-basic-section :deep(.el-input-number) {
|
||||||
|
width: 360px;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
.dialog-section-title { margin-bottom: 18px; font-size: 16px; font-weight: 600; }
|
.dialog-section-title { margin-bottom: 18px; font-size: 16px; font-weight: 600; }
|
||||||
.dialog-section-title::before { display: inline-block; width: 4px; height: 16px; margin-right: 8px; vertical-align: -2px; background: #409eff; content: ''; }
|
.dialog-section-title::before { display: inline-block; width: 4px; height: 16px; margin-right: 8px; vertical-align: -2px; background: #409eff; content: ''; }
|
||||||
.section-head { display: flex; align-items: center; justify-content: space-between; }
|
.section-head { display: flex; align-items: center; justify-content: space-between; }
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<mk-public-shell biz-type="contract-manage" :get-form="getForm">
|
||||||
|
<contract-manage ref="page" />
|
||||||
|
</mk-public-shell>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
|
||||||
|
import ContractManage from '@/views/business/contract-manage.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ContractManagePublicView',
|
||||||
|
components: { MkPublicShell, ContractManage },
|
||||||
|
methods: {
|
||||||
|
getForm() {
|
||||||
|
const row = this.$refs.page?.detailRow || {};
|
||||||
|
return { ...row, subject: row.contractName || row.contractNo || '' };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -222,7 +222,7 @@
|
|||||||
Number(detailSettlementRule.autoGenerate) === 1 ? '开启' : '关闭'
|
Number(detailSettlementRule.autoGenerate) === 1 ? '开启' : '关闭'
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
<template v-if="Number(detailSettlementRule.autoGenerate) === 1">
|
<template v-if="Number(detailSettlementRule.autoGenerate) === 1">
|
||||||
<el-descriptions-item label="账单起始日期">{{
|
<el-descriptions-item label="账单起始日">{{
|
||||||
displayValue(detailSettlementRule.billStartDate)
|
displayValue(detailSettlementRule.billStartDate)
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item label="结算类型">{{
|
<el-descriptions-item label="结算类型">{{
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
}}</el-descriptions-item
|
}}</el-descriptions-item
|
||||||
>
|
>
|
||||||
<el-descriptions-item
|
<el-descriptions-item
|
||||||
v-if="detailSettlementRule.settlementType === '固定天数周期结算'"
|
v-if="isFixedCycleSettlementType(detailSettlementRule.settlementType)"
|
||||||
label="周期天数"
|
label="周期天数"
|
||||||
>{{
|
>{{
|
||||||
detailObjectUnitValue(detailSettlementRule, 'cycleDays', '天')
|
detailObjectUnitValue(detailSettlementRule, 'cycleDays', '天')
|
||||||
@@ -345,7 +345,7 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="contract-manage-page__footer">
|
<div class="contract-manage-page__footer">
|
||||||
<el-button @click="closeDetail">关闭</el-button>
|
<el-button v-if="!isPublicViewPage" @click="closeDetail">关闭</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -478,7 +478,7 @@
|
|||||||
type="date"
|
type="date"
|
||||||
format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="YYYY-MM-DD"
|
placeholder="请输入"
|
||||||
/>
|
/>
|
||||||
<span>至</span>
|
<span>至</span>
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
@@ -486,7 +486,7 @@
|
|||||||
type="date"
|
type="date"
|
||||||
format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="YYYY-MM-DD"
|
placeholder="请输入"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -498,12 +498,12 @@
|
|||||||
><template #suffix>天</template></el-input>
|
><template #suffix>天</template></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="合同金额">
|
<el-form-item label="合同金额">
|
||||||
<el-input-number
|
<el-input
|
||||||
v-model="form.contractAmount"
|
:model-value="contractAmountDisplay"
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:controls="false"
|
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
|
clearable
|
||||||
|
@input="handleContractAmountInput"
|
||||||
|
@clear="form.contractAmount = null"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="是否范本">
|
<el-form-item label="是否范本">
|
||||||
@@ -593,11 +593,13 @@
|
|||||||
description
|
description
|
||||||
attachment-type
|
attachment-type
|
||||||
use-upload-dialog
|
use-upload-dialog
|
||||||
|
:attachment-location-options="contractAttachmentLocationOptions"
|
||||||
:lock-approved="isAttachmentUploadMode || hasApprovedContractFiles"
|
:lock-approved="isAttachmentUploadMode || hasApprovedContractFiles"
|
||||||
:mark-approved-on-upload="isAttachmentUploadMode"
|
:mark-approved-on-upload="isAttachmentUploadMode"
|
||||||
:rows="contractFileRows"
|
:rows="contractFileRows"
|
||||||
:preview="previewAttachment"
|
:preview="previewAttachment"
|
||||||
@update:rows="contractFileRows = $event"
|
@update:rows="contractFileRows = $event"
|
||||||
|
@upload-to-location="handleAttachmentUploadToLocation"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section class="contract-manage-form__section contract-manage-form__section--panel">
|
<section class="contract-manage-form__section contract-manage-form__section--panel">
|
||||||
@@ -646,7 +648,7 @@
|
|||||||
content="开启时,系统根据配置规则归集运单,定时生成结算单"
|
content="开启时,系统根据配置规则归集运单,定时生成结算单"
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<el-icon class="settlement-switch-tip"><QuestionFilled /></el-icon>
|
<el-icon class="contract-manage-form__fee-mode-tip"><QuestionFilled /></el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-radio :label="0">关闭</el-radio>
|
<el-radio :label="0">关闭</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
@@ -655,13 +657,13 @@
|
|||||||
v-if="settlementRuleEnabled"
|
v-if="settlementRuleEnabled"
|
||||||
class="contract-manage-form__grid contract-manage-form__settlement-grid"
|
class="contract-manage-form__grid contract-manage-form__settlement-grid"
|
||||||
>
|
>
|
||||||
<el-form-item label="账单起始日期" required>
|
<el-form-item label="账单起始日" required>
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="settlementRuleForm.billStartDate"
|
v-model="settlementRuleForm.billStartDate"
|
||||||
type="date"
|
type="date"
|
||||||
format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="请选择账单起始日期"
|
placeholder="请选择账单起始日"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="结算类型" required>
|
<el-form-item label="结算类型" required>
|
||||||
@@ -813,10 +815,12 @@
|
|||||||
attachment-type
|
attachment-type
|
||||||
use-upload-dialog
|
use-upload-dialog
|
||||||
attachment-location="其它附件"
|
attachment-location="其它附件"
|
||||||
|
:attachment-location-options="contractAttachmentLocationOptions"
|
||||||
:readonly="isAttachmentUploadMode"
|
:readonly="isAttachmentUploadMode"
|
||||||
:rows="attachmentRows"
|
:rows="attachmentRows"
|
||||||
:preview="previewAttachment"
|
:preview="previewAttachment"
|
||||||
@update:rows="attachmentRows = $event"
|
@update:rows="attachmentRows = $event"
|
||||||
|
@upload-to-location="handleAttachmentUploadToLocation"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
@@ -962,44 +966,10 @@
|
|||||||
@close="attachmentImagePreviewVisible = false"
|
@close="attachmentImagePreviewVisible = false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<el-dialog
|
<change-record-detail-dialog
|
||||||
v-model="detailChangeRecordVisible"
|
v-model="detailChangeRecordVisible"
|
||||||
title="变更记录详情"
|
:rows="detailChangeRecordDetailRows"
|
||||||
append-to-body
|
/>
|
||||||
destroy-on-close
|
|
||||||
width="1100px"
|
|
||||||
top="10px"
|
|
||||||
class="contract-change-record-detail-dialog"
|
|
||||||
>
|
|
||||||
<div v-if="detailChangeRecord" class="contract-change-record-detail-meta">
|
|
||||||
<span>经办人:{{ detailChangeRecord.handlerUserName || '-' }}</span>
|
|
||||||
<span>变更类型:{{ detailChangeRecord.changeType || '-' }}</span>
|
|
||||||
<span>状态:{{ detailChangeRecord.statusName || detailChangeRecord.status || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
<el-table :data="detailChangeRecordDetailRows" border :show-overflow-tooltip="false">
|
|
||||||
<el-table-column prop="field" label="变更字段" min-width="180" />
|
|
||||||
<el-table-column
|
|
||||||
prop="before"
|
|
||||||
label="变更前"
|
|
||||||
min-width="360"
|
|
||||||
class-name="contract-change-record-detail-value"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
prop="after"
|
|
||||||
label="变更后"
|
|
||||||
min-width="500"
|
|
||||||
class-name="contract-change-record-detail-value"
|
|
||||||
/>
|
|
||||||
</el-table>
|
|
||||||
<el-empty
|
|
||||||
v-if="!detailChangeRecordDetailRows.length"
|
|
||||||
description="暂无变更内容"
|
|
||||||
:image-size="60"
|
|
||||||
/>
|
|
||||||
<template #footer>
|
|
||||||
<el-button type="primary" @click="detailChangeRecordVisible = false">关闭</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<billing-plan-editor
|
<billing-plan-editor
|
||||||
v-model="detailBillingPlanBox"
|
v-model="detailBillingPlanBox"
|
||||||
@@ -1027,6 +997,7 @@ import NProgress from 'nprogress';
|
|||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import { exportBlob } from '@/api/common';
|
import { exportBlob } from '@/api/common';
|
||||||
import * as api from '@/api/business/contract-manage';
|
import * as api from '@/api/business/contract-manage';
|
||||||
|
import { getMkPublicDetail } from '@/api/mk-process';
|
||||||
import { getList as getProjectList } from '@/api/business/project-apply';
|
import { getList as getProjectList } from '@/api/business/project-apply';
|
||||||
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
|
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
|
||||||
import { getDeptTree } from '@/api/system/dept';
|
import { getDeptTree } from '@/api/system/dept';
|
||||||
@@ -1037,6 +1008,7 @@ import { getToken } from '@/utils/auth';
|
|||||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||||
import { submitMkApprovalFlow } from '@/utils/mk-approval';
|
import { submitMkApprovalFlow } from '@/utils/mk-approval';
|
||||||
import BillingPlanEditor from './components/billing-plan-editor.vue';
|
import BillingPlanEditor from './components/billing-plan-editor.vue';
|
||||||
|
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
|
||||||
import ContractAttachmentSection, {
|
import ContractAttachmentSection, {
|
||||||
attachmentName as sharedAttachmentName,
|
attachmentName as sharedAttachmentName,
|
||||||
attachmentUrl as sharedAttachmentUrl,
|
attachmentUrl as sharedAttachmentUrl,
|
||||||
@@ -1058,7 +1030,15 @@ const normalizeOptionalInteger = (value, emptyValue = null) => {
|
|||||||
return Number.isFinite(number) ? Math.trunc(number) : emptyValue;
|
return Number.isFinite(number) ? Math.trunc(number) : emptyValue;
|
||||||
};
|
};
|
||||||
const normalizeOptionalAmount = (value, emptyValue = null) => {
|
const normalizeOptionalAmount = (value, emptyValue = null) => {
|
||||||
if (value === undefined || value === null || value === '') return emptyValue;
|
if (
|
||||||
|
value === undefined ||
|
||||||
|
value === null ||
|
||||||
|
value === '' ||
|
||||||
|
Number(value) === -1 ||
|
||||||
|
String(value) === '-1'
|
||||||
|
) {
|
||||||
|
return emptyValue;
|
||||||
|
}
|
||||||
const number = Number(value);
|
const number = Number(value);
|
||||||
return Number.isFinite(number) && number >= 0 ? Number(number.toFixed(2)) : emptyValue;
|
return Number.isFinite(number) && number >= 0 ? Number(number.toFixed(2)) : emptyValue;
|
||||||
};
|
};
|
||||||
@@ -1100,7 +1080,7 @@ const defaultForm = () => ({
|
|||||||
settlementCurrency: '',
|
settlementCurrency: '',
|
||||||
invoiceCycle: '',
|
invoiceCycle: '',
|
||||||
paymentDays: '',
|
paymentDays: '',
|
||||||
contractAmount: '',
|
contractAmount: null,
|
||||||
templateFlag: 0,
|
templateFlag: 0,
|
||||||
originalContractNo: '',
|
originalContractNo: '',
|
||||||
electronicSealFlag: 0,
|
electronicSealFlag: 0,
|
||||||
@@ -1181,7 +1161,14 @@ const attachmentViewerPlugins = [
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ContractManage',
|
name: 'ContractManage',
|
||||||
components: { ContractAttachmentSection, BillingPlanEditor, ElImageViewer, OpenFileViewer, PdfPreview },
|
components: {
|
||||||
|
ContractAttachmentSection,
|
||||||
|
BillingPlanEditor,
|
||||||
|
ChangeRecordDetailDialog,
|
||||||
|
ElImageViewer,
|
||||||
|
OpenFileViewer,
|
||||||
|
PdfPreview,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
api,
|
api,
|
||||||
@@ -1272,7 +1259,7 @@ export default {
|
|||||||
formalSettlementRuleForm: defaultSettlementRule(),
|
formalSettlementRuleForm: defaultSettlementRule(),
|
||||||
paymentRatioRows: [],
|
paymentRatioRows: [],
|
||||||
changeRecordRows: [],
|
changeRecordRows: [],
|
||||||
settlementTypeOptions: ['月结', '日结', '周结', '半月结', '固定天数周期结算'],
|
settlementTypeOptions: ['现结', '日结', '周结', '月结', '按固定天数周期'],
|
||||||
billCycleTypeOptions: ['固定截单日', '自然月', '自定义多周期'],
|
billCycleTypeOptions: ['固定截单日', '自然月', '自定义多周期'],
|
||||||
detailLoading: false,
|
detailLoading: false,
|
||||||
detailRow: {},
|
detailRow: {},
|
||||||
@@ -1316,7 +1303,12 @@ export default {
|
|||||||
return this.$route.path === '/business/contract-manage/form';
|
return this.$route.path === '/business/contract-manage/form';
|
||||||
},
|
},
|
||||||
isDetailPage() {
|
isDetailPage() {
|
||||||
return this.$route.path === '/business/contract-manage/detail';
|
return (
|
||||||
|
this.$route.path === '/business/contract-manage/detail' || this.isPublicViewPage
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isPublicViewPage() {
|
||||||
|
return this.$route.path === '/business/contract-manage/public-view';
|
||||||
},
|
},
|
||||||
formMode() {
|
formMode() {
|
||||||
return this.$route.query.mode === 'edit' ? 'edit' : 'add';
|
return this.$route.query.mode === 'edit' ? 'edit' : 'add';
|
||||||
@@ -1331,6 +1323,23 @@ export default {
|
|||||||
isAttachmentUploadMode() {
|
isAttachmentUploadMode() {
|
||||||
return this.$route.query.attachmentUpload === '1';
|
return this.$route.query.attachmentUpload === '1';
|
||||||
},
|
},
|
||||||
|
contractAttachmentLocationOptions() {
|
||||||
|
if (this.isAttachmentUploadMode) return ['合同文件'];
|
||||||
|
return ['合同文件', '其它附件'];
|
||||||
|
},
|
||||||
|
contractAmountDisplay() {
|
||||||
|
const value = this.form.contractAmount;
|
||||||
|
if (
|
||||||
|
value === undefined ||
|
||||||
|
value === null ||
|
||||||
|
value === '' ||
|
||||||
|
Number(value) === -1 ||
|
||||||
|
String(value) === '-1'
|
||||||
|
) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
hasApprovedContractFiles() {
|
hasApprovedContractFiles() {
|
||||||
return (this.contractFileRows || []).some(
|
return (this.contractFileRows || []).some(
|
||||||
row =>
|
row =>
|
||||||
@@ -1412,7 +1421,7 @@ export default {
|
|||||||
return this.showBillCycleType && this.settlementRuleForm.billCycleType === '自定义多周期';
|
return this.showBillCycleType && this.settlementRuleForm.billCycleType === '自定义多周期';
|
||||||
},
|
},
|
||||||
showCycleDays() {
|
showCycleDays() {
|
||||||
return this.settlementRuleForm.settlementType === '固定天数周期结算';
|
return this.isFixedCycleSettlementType(this.settlementRuleForm.settlementType);
|
||||||
},
|
},
|
||||||
billCutoffDayOptions() {
|
billCutoffDayOptions() {
|
||||||
return Array.from({ length: 31 }, (_, index) => ({
|
return Array.from({ length: 31 }, (_, index) => ({
|
||||||
@@ -1473,9 +1482,11 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadProjectOptions();
|
if (!this.isPublicViewPage) {
|
||||||
this.loadSettlementDictionaries();
|
this.loadProjectOptions();
|
||||||
this.loadOrganizationOptions();
|
this.loadSettlementDictionaries();
|
||||||
|
this.loadOrganizationOptions();
|
||||||
|
}
|
||||||
if (this.isFormPage) this.initFormPage();
|
if (this.isFormPage) this.initFormPage();
|
||||||
if (this.isDetailPage) this.initDetailPage();
|
if (this.isDetailPage) this.initDetailPage();
|
||||||
},
|
},
|
||||||
@@ -1675,7 +1686,7 @@ export default {
|
|||||||
copyCount: normalizeOptionalInteger(detail.copyCount, ''),
|
copyCount: normalizeOptionalInteger(detail.copyCount, ''),
|
||||||
invoiceCycle: normalizeOptionalInteger(detail.invoiceCycle, ''),
|
invoiceCycle: normalizeOptionalInteger(detail.invoiceCycle, ''),
|
||||||
paymentDays: normalizeOptionalInteger(detail.paymentDays, ''),
|
paymentDays: normalizeOptionalInteger(detail.paymentDays, ''),
|
||||||
contractAmount: normalizeOptionalAmount(detail.contractAmount, ''),
|
contractAmount: normalizeOptionalAmount(detail.contractAmount, null),
|
||||||
archiveStatus: detail.archiveStatus || '未归档',
|
archiveStatus: detail.archiveStatus || '未归档',
|
||||||
feeGenerationMode:
|
feeGenerationMode:
|
||||||
detail.feeGenerationMode || (Number(detail.billingEnabled) === 0 ? 'manual' : 'system'),
|
detail.feeGenerationMode || (Number(detail.billingEnabled) === 0 ? 'manual' : 'system'),
|
||||||
@@ -2118,6 +2129,13 @@ export default {
|
|||||||
integerInput(prop, value) {
|
integerInput(prop, value) {
|
||||||
this.form[prop] = String(value || '').replace(/\D/g, '');
|
this.form[prop] = String(value || '').replace(/\D/g, '');
|
||||||
},
|
},
|
||||||
|
handleContractAmountInput(value) {
|
||||||
|
const text = String(value || '').replace(/[^\d.]/g, '');
|
||||||
|
const parts = text.split('.');
|
||||||
|
const normalized =
|
||||||
|
parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0];
|
||||||
|
this.form.contractAmount = normalized === '' ? null : normalized;
|
||||||
|
},
|
||||||
positiveIntegerInput(prop, value) {
|
positiveIntegerInput(prop, value) {
|
||||||
const normalized = String(value || '').replace(/\D/g, '').replace(/^0+/, '');
|
const normalized = String(value || '').replace(/\D/g, '').replace(/^0+/, '');
|
||||||
this.form[prop] = normalized;
|
this.form[prop] = normalized;
|
||||||
@@ -2148,6 +2166,16 @@ export default {
|
|||||||
};
|
};
|
||||||
this.attachmentDocumentPreviewVisible = true;
|
this.attachmentDocumentPreviewVisible = true;
|
||||||
},
|
},
|
||||||
|
handleAttachmentUploadToLocation({ location, rows = [] }) {
|
||||||
|
if (!rows.length) return;
|
||||||
|
if (location === '合同文件') {
|
||||||
|
this.contractFileRows = [...(this.contractFileRows || []), ...rows];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (location === '其它附件') {
|
||||||
|
this.attachmentRows = [...(this.attachmentRows || []), ...rows];
|
||||||
|
}
|
||||||
|
},
|
||||||
handleAttachmentPreviewUnsupported() {
|
handleAttachmentPreviewUnsupported() {
|
||||||
this.$message.warning('当前文件暂不支持在线预览');
|
this.$message.warning('当前文件暂不支持在线预览');
|
||||||
},
|
},
|
||||||
@@ -2206,8 +2234,12 @@ export default {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
isFixedCycleSettlementType(value) {
|
||||||
|
return value === '按固定天数周期' || value === '固定天数周期结算';
|
||||||
|
},
|
||||||
normalizeSettlementRule(rule = {}) {
|
normalizeSettlementRule(rule = {}) {
|
||||||
const next = { ...defaultSettlementRule(), ...rule };
|
const next = { ...defaultSettlementRule(), ...rule };
|
||||||
|
if (next.settlementType === '固定天数周期结算') next.settlementType = '按固定天数周期';
|
||||||
if (next.settlementType !== '月结') {
|
if (next.settlementType !== '月结') {
|
||||||
next.billCycleType = '';
|
next.billCycleType = '';
|
||||||
next.billCutoffDay = '';
|
next.billCutoffDay = '';
|
||||||
@@ -2222,7 +2254,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
next.customPeriods = [];
|
next.customPeriods = [];
|
||||||
}
|
}
|
||||||
if (next.settlementType !== '固定天数周期结算') next.cycleDays = '';
|
if (!this.isFixedCycleSettlementType(next.settlementType)) next.cycleDays = '';
|
||||||
return next;
|
return next;
|
||||||
},
|
},
|
||||||
syncSettlementConfig() {
|
syncSettlementConfig() {
|
||||||
@@ -2245,7 +2277,7 @@ export default {
|
|||||||
this.settlementRuleForm.billCycleType = '';
|
this.settlementRuleForm.billCycleType = '';
|
||||||
this.settlementRuleForm.billCutoffDay = '';
|
this.settlementRuleForm.billCutoffDay = '';
|
||||||
this.settlementRuleForm.customPeriods = [];
|
this.settlementRuleForm.customPeriods = [];
|
||||||
if (value !== '固定天数周期结算') this.settlementRuleForm.cycleDays = '';
|
if (!this.isFixedCycleSettlementType(value)) this.settlementRuleForm.cycleDays = '';
|
||||||
},
|
},
|
||||||
handleBillCycleTypeChange(value) {
|
handleBillCycleTypeChange(value) {
|
||||||
if (value !== '固定截单日') this.settlementRuleForm.billCutoffDay = '';
|
if (value !== '固定截单日') this.settlementRuleForm.billCutoffDay = '';
|
||||||
@@ -2336,7 +2368,7 @@ export default {
|
|||||||
validateSettlementRule(rule, label) {
|
validateSettlementRule(rule, label) {
|
||||||
if (Number(rule.autoGenerate) !== 1) return true;
|
if (Number(rule.autoGenerate) !== 1) return true;
|
||||||
if (!rule.billStartDate || !rule.settlementType) {
|
if (!rule.billStartDate || !rule.settlementType) {
|
||||||
this.$message.warning(`${label}:请完整填写账单起始日期和结算类型`);
|
this.$message.warning(`${label}:请完整填写账单起始日和结算类型`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (rule.settlementType === '月结' && !rule.billCycleType) {
|
if (rule.settlementType === '月结' && !rule.billCycleType) {
|
||||||
@@ -2354,7 +2386,7 @@ export default {
|
|||||||
if (rule.settlementType === '月结' && rule.billCycleType === '自定义多周期') {
|
if (rule.settlementType === '月结' && rule.billCycleType === '自定义多周期') {
|
||||||
return this.validateCustomPeriods(rule.customPeriods, label);
|
return this.validateCustomPeriods(rule.customPeriods, label);
|
||||||
}
|
}
|
||||||
if (rule.settlementType === '固定天数周期结算' && !rule.cycleDays) {
|
if (this.isFixedCycleSettlementType(rule.settlementType) && !rule.cycleDays) {
|
||||||
this.$message.warning(`${label}:请选择周期天数`);
|
this.$message.warning(`${label}:请选择周期天数`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2406,8 +2438,10 @@ export default {
|
|||||||
if (!id) return;
|
if (!id) return;
|
||||||
this.detailLoading = true;
|
this.detailLoading = true;
|
||||||
this.applyDetailState({});
|
this.applyDetailState({});
|
||||||
this.api
|
const request = this.isPublicViewPage
|
||||||
.getDetail(id)
|
? getMkPublicDetail('contract-manage', id)
|
||||||
|
: this.api.getDetail(id);
|
||||||
|
request
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.applyDetailState(res.data?.data || {});
|
this.applyDetailState(res.data?.data || {});
|
||||||
})
|
})
|
||||||
@@ -2576,7 +2610,7 @@ export default {
|
|||||||
.join(',')}`
|
.join(',')}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (config.billStartDate) parts.push(`账单起始日期:${config.billStartDate}`);
|
if (config.billStartDate) parts.push(`账单起始日:${config.billStartDate}`);
|
||||||
return parts.join(',');
|
return parts.join(',');
|
||||||
};
|
};
|
||||||
if (normalized.preSettlementConfig || normalized.formalSettlementConfig) {
|
if (normalized.preSettlementConfig || normalized.formalSettlementConfig) {
|
||||||
@@ -2672,6 +2706,15 @@ export default {
|
|||||||
},
|
},
|
||||||
detailValue(prop) {
|
detailValue(prop) {
|
||||||
const value = this.detailRow[prop];
|
const value = this.detailRow[prop];
|
||||||
|
if (prop === 'contractAmount') {
|
||||||
|
return value === undefined ||
|
||||||
|
value === null ||
|
||||||
|
value === '' ||
|
||||||
|
Number(value) === -1 ||
|
||||||
|
String(value) === '-1'
|
||||||
|
? '-'
|
||||||
|
: value;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
prop === 'contractStage' ||
|
prop === 'contractStage' ||
|
||||||
prop === 'approvalStatus' ||
|
prop === 'approvalStatus' ||
|
||||||
@@ -2900,25 +2943,6 @@ export default {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contract-change-record-detail-meta {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px 32px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
color: #606266;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.contract-change-record-detail-dialog .el-dialog__body) {
|
|
||||||
max-height: 65vh;
|
|
||||||
overflow: auto;
|
|
||||||
padding-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.contract-change-record-detail-dialog .contract-change-record-detail-value .cell) {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.avue--collapse .contract-manage-page__footer) {
|
:global(.avue--collapse .contract-manage-page__footer) {
|
||||||
left: 60px;
|
left: 60px;
|
||||||
}
|
}
|
||||||
@@ -3018,13 +3042,6 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
||||||
.settlement-switch-tip {
|
|
||||||
margin: 0 4px;
|
|
||||||
color: #a8abb2;
|
|
||||||
cursor: help;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__settlement-grid {
|
&__settlement-grid {
|
||||||
@@ -3082,11 +3099,19 @@ export default {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-input-number) {
|
||||||
|
width: 360px;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-input),
|
:deep(.el-input),
|
||||||
:deep(.el-select),
|
:deep(.el-select),
|
||||||
:deep(.el-cascader),
|
:deep(.el-cascader),
|
||||||
:deep(.el-tree-select),
|
:deep(.el-tree-select),
|
||||||
:deep(.el-input-number),
|
|
||||||
:deep(.el-date-editor) {
|
:deep(.el-date-editor) {
|
||||||
width: 360px;
|
width: 360px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|||||||
@@ -395,14 +395,8 @@
|
|||||||
<div class="loading-detail__summary">
|
<div class="loading-detail__summary">
|
||||||
<div class="loading-detail__heading">配载单详情</div>
|
<div class="loading-detail__heading">配载单详情</div>
|
||||||
<div>{{ dialogForm.loadingNo || '-' }}</div>
|
<div>{{ dialogForm.loadingNo || '-' }}</div>
|
||||||
<div>
|
<el-tag :type="detailStatusType">{{ statusText(dialogForm) }}</el-tag>
|
||||||
<el-tag :type="detailStatusType" class="status-text">{{
|
<el-tag type="primary">{{ transportTypeLabel(dialogForm.transportType) }}</el-tag>
|
||||||
statusText(dialogForm)
|
|
||||||
}}</el-tag>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-tag type="warning">{{ transportTypeLabel(dialogForm.transportType) }}</el-tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="loading-detail__task-row">
|
<div class="loading-detail__task-row">
|
||||||
<div class="loading-detail__field">
|
<div class="loading-detail__field">
|
||||||
@@ -1683,11 +1677,12 @@ export default {
|
|||||||
const typeMap = {
|
const typeMap = {
|
||||||
pending: 'success',
|
pending: 'success',
|
||||||
running: 'success',
|
running: 'success',
|
||||||
completed: 'info',
|
processing: 'success',
|
||||||
|
completed: 'primary',
|
||||||
cancelled: 'info',
|
cancelled: 'info',
|
||||||
draft: 'warning',
|
draft: 'info',
|
||||||
};
|
};
|
||||||
return typeMap[this.dialogForm.businessStatus] || 'info';
|
return typeMap[this.dialogForm.businessStatus] || 'warning';
|
||||||
},
|
},
|
||||||
taskSummary() {
|
taskSummary() {
|
||||||
return (
|
return (
|
||||||
@@ -3604,8 +3599,17 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 24px;
|
gap: 12px;
|
||||||
padding: 4px 0 16px;
|
padding: 4px 0 16px;
|
||||||
|
|
||||||
|
:deep(.el-tag) {
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 12px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.loading-detail__heading {
|
.loading-detail__heading {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|||||||
@@ -132,7 +132,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<master-order-dispatch v-else-if="mode === 'dispatch'" :id="routeId" @back="goList" />
|
<master-order-dispatch v-else-if="mode === 'dispatch'" :id="routeId" @back="goList" />
|
||||||
<master-order-detail v-else :id="routeId" />
|
<master-order-detail v-else-if="mode === 'detail'" :id="routeId" />
|
||||||
<el-dialog v-model="confirm.visible" title="提示" width="400px"
|
<el-dialog v-model="confirm.visible" title="提示" width="400px"
|
||||||
><span>{{ confirm.message }}</span
|
><span>{{ confirm.message }}</span
|
||||||
><template #footer
|
><template #footer
|
||||||
@@ -153,6 +153,10 @@ export default {
|
|||||||
components: { MasterOrderEditor, MasterOrderDispatch, MasterOrderDetail },
|
components: { MasterOrderEditor, MasterOrderDispatch, MasterOrderDetail },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
// 实例创建时锁定所属路由。keep-alive 缓存后 $route 会变成其它页面,
|
||||||
|
// 不能再用全局 query.mode / query.id 决定本页形态,否则打开「新增项目管理」
|
||||||
|
//(mode=add 且无 id)会落入详情分支并请求缺少 id 的详情接口。
|
||||||
|
routePathLocked: this.$route.path,
|
||||||
searchExpanded: false,
|
searchExpanded: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
records: [],
|
records: [],
|
||||||
@@ -180,10 +184,15 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isOwnedRoute() {
|
||||||
|
return this.$route.path === this.routePathLocked;
|
||||||
|
},
|
||||||
mode() {
|
mode() {
|
||||||
|
if (!this.isOwnedRoute) return 'list';
|
||||||
return this.$route.query.mode || 'list';
|
return this.$route.query.mode || 'list';
|
||||||
},
|
},
|
||||||
routeId() {
|
routeId() {
|
||||||
|
if (!this.isOwnedRoute) return '';
|
||||||
return this.$route.query.id;
|
return this.$route.query.id;
|
||||||
},
|
},
|
||||||
masterEditorTitle() {
|
masterEditorTitle() {
|
||||||
@@ -194,6 +203,7 @@ export default {
|
|||||||
'$route.query': {
|
'$route.query': {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
handler() {
|
handler() {
|
||||||
|
if (!this.isOwnedRoute) return;
|
||||||
this.syncTagTitle();
|
this.syncTagTitle();
|
||||||
if (this.mode === 'list') this.load();
|
if (this.mode === 'list') this.load();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="page" class="project-apply-public-view">
|
||||||
|
<project-apply ref="project" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ProjectApply from './project-apply.vue';
|
||||||
|
import { postMkPublicProcessMessage } from '@/api/mk-process';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProjectApplyPublicView',
|
||||||
|
components: {
|
||||||
|
ProjectApply,
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.handleIframeHeight = () => this.sendIframeHeight();
|
||||||
|
this.handleProcessMessage = event => this.onProcessMessage(event);
|
||||||
|
document.addEventListener('DOMContentLoaded', this.handleIframeHeight, false);
|
||||||
|
window.addEventListener('message', this.handleProcessMessage);
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
window.removeEventListener('message', this.handleProcessMessage);
|
||||||
|
(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(() => {
|
||||||
|
window.parent.postMessage({ height: document.body.clientHeight }, '*');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onProcessMessage(event) {
|
||||||
|
const data = event && event.data;
|
||||||
|
if (!data || typeof data !== 'object') return;
|
||||||
|
if (data.height && !data.status && !data.type) return;
|
||||||
|
if (data.type === 'formValues' || data.type === 'afterSubmit') return;
|
||||||
|
const formValues = data.formValues;
|
||||||
|
if (data.status === 'submit') {
|
||||||
|
this.submitData(formValues);
|
||||||
|
} else if (data.status === 'save') {
|
||||||
|
this.saveData(formValues);
|
||||||
|
}
|
||||||
|
if (data.type === 'getFormValues') {
|
||||||
|
window.parent.postMessage({ type: 'formValues', formData: this.buildFormData() }, '*');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submitData(lbpmFormValues) {
|
||||||
|
const formData = this.buildFormData();
|
||||||
|
this.postFormData('submit', lbpmFormValues, formData);
|
||||||
|
if (!lbpmFormValues) return;
|
||||||
|
const parameters = Object.assign({}, lbpmFormValues, {
|
||||||
|
loginName: this.getLoginName(lbpmFormValues),
|
||||||
|
formInstanceId: this.getFormId(),
|
||||||
|
subject: formData.subject,
|
||||||
|
});
|
||||||
|
window.parent.postMessage({ type: 'afterSubmit', success: true, parameters }, '*');
|
||||||
|
},
|
||||||
|
saveData(lbpmFormValues) {
|
||||||
|
this.postFormData('save', lbpmFormValues, this.buildFormData());
|
||||||
|
},
|
||||||
|
postFormData(status, formValues, formData) {
|
||||||
|
postMkPublicProcessMessage('project-apply', {
|
||||||
|
status,
|
||||||
|
formValues: formValues || {},
|
||||||
|
formData,
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('项目公开页提交流程数据失败:', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
buildFormData() {
|
||||||
|
const form = this.getProjectForm();
|
||||||
|
return {
|
||||||
|
...form,
|
||||||
|
subject: form.projectName || form.projectShortName || '',
|
||||||
|
formInstanceId: this.getFormId(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getProjectForm() {
|
||||||
|
const form = this.$refs.project && this.$refs.project.form;
|
||||||
|
if (!form) return {};
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(form));
|
||||||
|
} catch (error) {
|
||||||
|
return { ...form };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getFormId() {
|
||||||
|
return this.$route.query.id || this.getProjectForm().id || '';
|
||||||
|
},
|
||||||
|
getLoginName(formValues = {}) {
|
||||||
|
return (
|
||||||
|
formValues.loginName ||
|
||||||
|
this.$route.query.loginName ||
|
||||||
|
this.$route.query.submitIdentity ||
|
||||||
|
''
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</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>
|
||||||
|
.project-apply-public-view {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 12px 0 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #f0f2f5;
|
||||||
|
|
||||||
|
:deep(.basic-container) {
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<basic-container class="project-apply-page">
|
<basic-container class="project-apply-page" :class="{ 'is-public-view': isPublicViewPage }">
|
||||||
<avue-crud
|
<avue-crud
|
||||||
v-if="!isProjectFormPage"
|
v-if="!isProjectFormPage"
|
||||||
:option="tableOption"
|
:option="tableOption"
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
<el-form-item label="项目编号" prop="projectCode">
|
<el-form-item label="项目编号" prop="projectCode">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.projectCode"
|
v-model="form.projectCode"
|
||||||
placeholder="请输入"
|
:placeholder="dialogType === 'add' ? '若不填写,系统自动生成' : '请输入'"
|
||||||
:disabled="dialogType === 'edit'"
|
:disabled="dialogType === 'edit'"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -703,6 +703,7 @@
|
|||||||
label="变更内容"
|
label="变更内容"
|
||||||
min-width="180"
|
min-width="180"
|
||||||
align="center"
|
align="center"
|
||||||
|
:show-overflow-tooltip="false"
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tooltip placement="top" :show-after="200">
|
<el-tooltip placement="top" :show-after="200">
|
||||||
@@ -730,43 +731,10 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<el-dialog
|
<change-record-detail-dialog
|
||||||
v-model="changeRecordDetailVisible"
|
v-model="changeRecordDetailVisible"
|
||||||
title="变更记录详情"
|
:rows="changeRecordDetailRows"
|
||||||
append-to-body
|
/>
|
||||||
destroy-on-close
|
|
||||||
width="1100px"
|
|
||||||
top="10px"
|
|
||||||
class="project-change-record-detail-dialog"
|
|
||||||
>
|
|
||||||
<div v-if="changeRecordDetail" class="project-change-record-detail-meta">
|
|
||||||
<span>变更日期:{{ changeRecordDetail.changeDate || '-' }}</span>
|
|
||||||
<span>变更账号:{{ changeRecordDetail.handler || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
<el-table :data="changeRecordDetailRows" border :show-overflow-tooltip="false">
|
|
||||||
<el-table-column prop="field" label="变更字段" min-width="180" />
|
|
||||||
<el-table-column
|
|
||||||
prop="before"
|
|
||||||
label="变更前"
|
|
||||||
min-width="360"
|
|
||||||
class-name="project-change-record-detail-value"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
prop="after"
|
|
||||||
label="变更后"
|
|
||||||
min-width="500"
|
|
||||||
class-name="project-change-record-detail-value"
|
|
||||||
/>
|
|
||||||
</el-table>
|
|
||||||
<el-empty
|
|
||||||
v-if="!changeRecordDetailRows.length"
|
|
||||||
description="暂无变更内容"
|
|
||||||
:image-size="60"
|
|
||||||
/>
|
|
||||||
<template #footer>
|
|
||||||
<el-button type="primary" @click="changeRecordDetailVisible = false">关闭</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<template v-if="isChangeDialog">
|
<template v-if="isChangeDialog">
|
||||||
<div class="dialog-section-title">变更原因</div>
|
<div class="dialog-section-title">变更原因</div>
|
||||||
@@ -784,6 +752,7 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
v-if="!isPublicViewPage"
|
||||||
class="project-apply-dialog__footer"
|
class="project-apply-dialog__footer"
|
||||||
:class="{ 'project-apply-dialog__footer--change': isChangeDialog }"
|
:class="{ 'project-apply-dialog__footer--change': isChangeDialog }"
|
||||||
>
|
>
|
||||||
@@ -909,12 +878,29 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="publicCustomerVisible"
|
||||||
|
title="查看客商档案"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
width="92%"
|
||||||
|
top="4vh"
|
||||||
|
class="project-apply-public-customer-dialog"
|
||||||
|
>
|
||||||
|
<customer-archive
|
||||||
|
v-if="publicCustomerVisible"
|
||||||
|
:embedded-public-id="publicCustomerId"
|
||||||
|
/>
|
||||||
|
</el-dialog>
|
||||||
</basic-container>
|
</basic-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { exportBlob } from '@/api/common';
|
import { exportBlob } from '@/api/common';
|
||||||
|
import { defineAsyncComponent } from 'vue';
|
||||||
import * as api from '@/api/business/project-apply';
|
import * as api from '@/api/business/project-apply';
|
||||||
|
import { getMkPublicDetail } from '@/api/mk-process';
|
||||||
import {
|
import {
|
||||||
getList as getCustomerArchiveList,
|
getList as getCustomerArchiveList,
|
||||||
getDetail as getCustomerArchiveDetail,
|
getDetail as getCustomerArchiveDetail,
|
||||||
@@ -929,6 +915,7 @@ import { submitMkApprovalFlow } from '@/utils/mk-approval';
|
|||||||
import { ElImageViewer } from 'element-plus';
|
import { ElImageViewer } from 'element-plus';
|
||||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||||
|
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
|
||||||
import {
|
import {
|
||||||
fallbackPlugin,
|
fallbackPlugin,
|
||||||
imagePlugin,
|
imagePlugin,
|
||||||
@@ -1045,7 +1032,10 @@ const majorProjectAttachmentTypeOptions = [
|
|||||||
].map(item => ({ label: item, value: item })).concat(otherAttachmentType);
|
].map(item => ({ label: item, value: item })).concat(otherAttachmentType);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
name: 'ProjectApply',
|
||||||
components: {
|
components: {
|
||||||
|
CustomerArchive: defineAsyncComponent(() => import('@/views/vehicle/customer-archive.vue')),
|
||||||
|
ChangeRecordDetailDialog,
|
||||||
ElImageViewer,
|
ElImageViewer,
|
||||||
OpenFileViewer,
|
OpenFileViewer,
|
||||||
PdfPreview,
|
PdfPreview,
|
||||||
@@ -1239,6 +1229,8 @@ export default {
|
|||||||
changeRecordDetail: null,
|
changeRecordDetail: null,
|
||||||
changeRecordDetailRows: [],
|
changeRecordDetailRows: [],
|
||||||
userBox: false,
|
userBox: false,
|
||||||
|
publicCustomerVisible: false,
|
||||||
|
publicCustomerId: '',
|
||||||
userPickType: '',
|
userPickType: '',
|
||||||
userLoading: false,
|
userLoading: false,
|
||||||
userData: [],
|
userData: [],
|
||||||
@@ -1315,6 +1307,9 @@ export default {
|
|||||||
isProjectFormPage() {
|
isProjectFormPage() {
|
||||||
return this.isFormPageInstance;
|
return this.isFormPageInstance;
|
||||||
},
|
},
|
||||||
|
isPublicViewPage() {
|
||||||
|
return this.$route.path === '/business/project-apply/public-view';
|
||||||
|
},
|
||||||
projectFormContainer() {
|
projectFormContainer() {
|
||||||
// 表单页实例始终用页面容器,避免 keep-alive 失活时误切弹窗
|
// 表单页实例始终用页面容器,避免 keep-alive 失活时误切弹窗
|
||||||
return this.isFormPageInstance ? 'div' : 'el-dialog';
|
return this.isFormPageInstance ? 'div' : 'el-dialog';
|
||||||
@@ -1361,6 +1356,11 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
if (this.isPublicViewPage) {
|
||||||
|
this.isFormPageInstance = true;
|
||||||
|
this.openPublicProjectForm();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.loadDeptOptions();
|
this.loadDeptOptions();
|
||||||
this.loadCargoTypeOptions();
|
this.loadCargoTypeOptions();
|
||||||
this.loadTransportTypeOptions();
|
this.loadTransportTypeOptions();
|
||||||
@@ -1384,6 +1384,7 @@ export default {
|
|||||||
this.attachmentDocumentPreviewVisible = false;
|
this.attachmentDocumentPreviewVisible = false;
|
||||||
this.attachmentImagePreviewVisible = false;
|
this.attachmentImagePreviewVisible = false;
|
||||||
this.userBox = false;
|
this.userBox = false;
|
||||||
|
this.publicCustomerVisible = false;
|
||||||
},
|
},
|
||||||
buildTableOption() {
|
buildTableOption() {
|
||||||
return {
|
return {
|
||||||
@@ -1406,6 +1407,75 @@ export default {
|
|||||||
hasPermission(code) {
|
hasPermission(code) {
|
||||||
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
|
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
|
||||||
},
|
},
|
||||||
|
openPublicProjectForm() {
|
||||||
|
const id = this.$route.query.id;
|
||||||
|
this.dialogType = 'view';
|
||||||
|
this.dialogReadonly = true;
|
||||||
|
this.projectBox = true;
|
||||||
|
if (!id) {
|
||||||
|
this.$message.error('缺少项目ID');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getMkPublicDetail('project-apply', id)
|
||||||
|
.then(res => {
|
||||||
|
const detail = res.data?.data || {};
|
||||||
|
this.applyPublicDictOptions(detail);
|
||||||
|
const displayDetail = { ...detail };
|
||||||
|
delete displayDetail.cargoTypeOptions;
|
||||||
|
delete displayDetail.transportTypeOptions;
|
||||||
|
delete displayDetail.settlementModeOptions;
|
||||||
|
this.applyProjectDetail(displayDetail);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$message.error('项目信息加载失败');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
applyPublicDictOptions(detail = {}) {
|
||||||
|
this.cargoTypeOptions = this.resolvePublicDictOptions(
|
||||||
|
detail.cargoTypeOptions,
|
||||||
|
detail.cargoType
|
||||||
|
);
|
||||||
|
this.transportTypeOptions = this.resolvePublicDictOptions(
|
||||||
|
detail.transportTypeOptions,
|
||||||
|
detail.transportType
|
||||||
|
);
|
||||||
|
this.settlementModeOptions = this.resolvePublicDictOptions(
|
||||||
|
detail.settlementModeOptions,
|
||||||
|
detail.settlementMode
|
||||||
|
);
|
||||||
|
if (detail.businessDeptId) {
|
||||||
|
const label = detail.businessDeptName || String(detail.businessDeptId);
|
||||||
|
this.businessDeptTreeOptions = [{ label, value: detail.businessDeptId }];
|
||||||
|
this.deptOptions = [{ label, rawLabel: label, value: detail.businessDeptId }];
|
||||||
|
}
|
||||||
|
if (detail.undertakeDeptId) {
|
||||||
|
this.platformCompanyOptions = [
|
||||||
|
{
|
||||||
|
label: detail.undertakeDeptName || String(detail.undertakeDeptId),
|
||||||
|
value: detail.undertakeDeptId,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolvePublicDictOptions(options, currentValue) {
|
||||||
|
const list = Array.isArray(options)
|
||||||
|
? options
|
||||||
|
.filter(item => item && item.value !== undefined && item.value !== null && item.value !== '')
|
||||||
|
.map(item => ({
|
||||||
|
label: item.label || String(item.value),
|
||||||
|
value: item.value,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
if (
|
||||||
|
currentValue !== undefined &&
|
||||||
|
currentValue !== null &&
|
||||||
|
currentValue !== '' &&
|
||||||
|
!list.some(item => String(item.value) === String(currentValue))
|
||||||
|
) {
|
||||||
|
list.unshift({ label: String(currentValue), value: currentValue });
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
},
|
||||||
openProjectFormPage() {
|
openProjectFormPage() {
|
||||||
const type = this.$route.query.mode || 'add';
|
const type = this.$route.query.mode || 'add';
|
||||||
const id = this.$route.query.id;
|
const id = this.$route.query.id;
|
||||||
@@ -1420,6 +1490,7 @@ export default {
|
|||||||
this.fillDefaultUsers();
|
this.fillDefaultUsers();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!id) return;
|
||||||
this.api.getDetail(id).then(res => {
|
this.api.getDetail(id).then(res => {
|
||||||
const detail = res.data.data || {};
|
const detail = res.data.data || {};
|
||||||
const isChange = type === 'change';
|
const isChange = type === 'change';
|
||||||
@@ -2012,6 +2083,11 @@ export default {
|
|||||||
this.$message.warning('客商档案 ID 为空,无法打开');
|
this.$message.warning('客商档案 ID 为空,无法打开');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.isPublicViewPage) {
|
||||||
|
this.publicCustomerId = String(id);
|
||||||
|
this.publicCustomerVisible = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
path: '/vehicle/customer-archive/form',
|
path: '/vehicle/customer-archive/form',
|
||||||
query: { id: String(id), name: '查看客商档案', view: '1' },
|
query: { id: String(id), name: '查看客商档案', view: '1' },
|
||||||
@@ -2594,6 +2670,12 @@ export default {
|
|||||||
this.$message.warning('项目ID为空,无法查看变更详情');
|
this.$message.warning('项目ID为空,无法查看变更详情');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.isPublicViewPage) {
|
||||||
|
this.changeRecordDetail = { ...row };
|
||||||
|
this.changeRecordDetailRows = this.buildChangeRecordDetailRows(this.changeRecordDetail);
|
||||||
|
this.changeRecordDetailVisible = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (!this.transportTypeOptions.length) {
|
if (!this.transportTypeOptions.length) {
|
||||||
await this.loadTransportTypeOptions();
|
await this.loadTransportTypeOptions();
|
||||||
@@ -2650,6 +2732,12 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.project-apply-page.is-public-view {
|
||||||
|
:deep(.el-link) {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.project-apply-form {
|
.project-apply-form {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
@@ -2882,27 +2970,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.project-change-record-detail-dialog .el-dialog__body) {
|
|
||||||
max-height: 65vh;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.project-change-record-detail-dialog .project-change-record-detail-value .cell) {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-word;
|
|
||||||
overflow: visible;
|
|
||||||
text-overflow: clip;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-change-record-detail-meta {
|
|
||||||
display: flex;
|
|
||||||
gap: 32px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
color: #606266;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.project-apply-dialog .el-dialog__body) {
|
:global(.project-apply-dialog .el-dialog__body) {
|
||||||
max-height: 76vh;
|
max-height: 76vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -3019,3 +3086,11 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.project-apply-public-customer-dialog .el-dialog__body {
|
||||||
|
max-height: 78vh;
|
||||||
|
overflow: auto;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -99,13 +99,25 @@
|
|||||||
<el-table :data="attachmentRows" border @selection-change="selectedAttachments = $event">
|
<el-table :data="attachmentRows" border @selection-change="selectedAttachments = $event">
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
<el-table-column label="文件名" min-width="240" show-overflow-tooltip>
|
<el-table-column label="文件名称" min-width="240" show-overflow-tooltip>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link type="primary" @click="previewAttachment(row)">
|
<el-link type="primary" @click="previewAttachment(row)">
|
||||||
{{ attachmentName(row) }}
|
{{ attachmentName(row) }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="附件描述" min-width="220">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="dialogReadonly">{{ row.description || '-' }}</span>
|
||||||
|
<el-input
|
||||||
|
v-else
|
||||||
|
v-model="row.description"
|
||||||
|
maxlength="200"
|
||||||
|
placeholder="请输入"
|
||||||
|
@input="syncAttachmentsJson"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="文件大小" width="120" align="center">
|
<el-table-column label="文件大小" width="120" align="center">
|
||||||
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
|
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -172,7 +184,7 @@
|
|||||||
title="查看临时额度申请"
|
title="查看临时额度申请"
|
||||||
append-to-body
|
append-to-body
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
width="96%"
|
width="1100px"
|
||||||
class="temporary-credit-limit-dialog temporary-credit-limit-detail-dialog"
|
class="temporary-credit-limit-dialog temporary-credit-limit-detail-dialog"
|
||||||
>
|
>
|
||||||
<div v-loading="detailLoading" class="business-crud-page__detail-content">
|
<div v-loading="detailLoading" class="business-crud-page__detail-content">
|
||||||
@@ -204,13 +216,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-table :data="attachmentRows" empty-text="暂无附件">
|
<el-table :data="attachmentRows" empty-text="暂无附件">
|
||||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
<el-table-column label="文件名" min-width="240" show-overflow-tooltip>
|
<el-table-column label="文件名称" min-width="240" show-overflow-tooltip>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link type="primary" @click="previewAttachment(row)">
|
<el-link type="primary" @click="previewAttachment(row)">
|
||||||
{{ attachmentName(row) }}
|
{{ attachmentName(row) }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="附件描述" min-width="220" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">{{ row.description || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="文件大小" width="120" align="center">
|
<el-table-column label="文件大小" width="120" align="center">
|
||||||
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
|
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -595,7 +610,12 @@ export default {
|
|||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
applyDetail(detail) {
|
applyDetail(detail) {
|
||||||
this.form = { ...detail };
|
this.form = {
|
||||||
|
...detail,
|
||||||
|
projectFundLimit: this.blankSentinelAmount(detail.projectFundLimit),
|
||||||
|
usedFundLimit: this.blankSentinelAmount(detail.usedFundLimit),
|
||||||
|
remainingFundLimit: this.blankSentinelAmount(detail.remainingFundLimit),
|
||||||
|
};
|
||||||
this.selectedProjectId = detail.projectId || '';
|
this.selectedProjectId = detail.projectId || '';
|
||||||
this.attachmentRows = this.parseJsonArray(detail.attachmentsJson);
|
this.attachmentRows = this.parseJsonArray(detail.attachmentsJson);
|
||||||
this.selectedAttachments = [];
|
this.selectedAttachments = [];
|
||||||
@@ -605,6 +625,9 @@ export default {
|
|||||||
const { projectQuotaInfoTitle, temporaryCreditInfoTitle, ...payload } = row || {};
|
const { projectQuotaInfoTitle, temporaryCreditInfoTitle, ...payload } = row || {};
|
||||||
return {
|
return {
|
||||||
...payload,
|
...payload,
|
||||||
|
projectFundLimit: this.toOptionalAmount(payload.projectFundLimit),
|
||||||
|
usedFundLimit: this.toOptionalAmount(payload.usedFundLimit),
|
||||||
|
remainingFundLimit: this.toOptionalAmount(payload.remainingFundLimit),
|
||||||
applyLimit: payload.applyLimit === '' ? '' : Number(payload.applyLimit),
|
applyLimit: payload.applyLimit === '' ? '' : Number(payload.applyLimit),
|
||||||
attachmentsJson: JSON.stringify(this.attachmentRows),
|
attachmentsJson: JSON.stringify(this.attachmentRows),
|
||||||
};
|
};
|
||||||
@@ -750,8 +773,8 @@ export default {
|
|||||||
projectName: project.projectName || '',
|
projectName: project.projectName || '',
|
||||||
projectCode: project.projectCode || '',
|
projectCode: project.projectCode || '',
|
||||||
undertakeDeptName: project.undertakeDeptName || '',
|
undertakeDeptName: project.undertakeDeptName || '',
|
||||||
projectFundLimit: project.fundLimit || project.projectFundLimit || 0,
|
projectFundLimit: this.blankSentinelAmount(project.fundLimit ?? project.projectFundLimit),
|
||||||
usedFundLimit: project.usedFundLimit || 0,
|
usedFundLimit: this.blankSentinelAmount(project.usedFundLimit),
|
||||||
});
|
});
|
||||||
this.form.remainingFundLimit = this.calculateRemainingFundLimit();
|
this.form.remainingFundLimit = this.calculateRemainingFundLimit();
|
||||||
getProjectDetail(project.id).then(res => {
|
getProjectDetail(project.id).then(res => {
|
||||||
@@ -759,18 +782,32 @@ export default {
|
|||||||
Object.assign(this.form, {
|
Object.assign(this.form, {
|
||||||
projectCode: detail.projectCode || this.form.projectCode,
|
projectCode: detail.projectCode || this.form.projectCode,
|
||||||
undertakeDeptName: detail.undertakeDeptName || this.form.undertakeDeptName,
|
undertakeDeptName: detail.undertakeDeptName || this.form.undertakeDeptName,
|
||||||
projectFundLimit:
|
projectFundLimit: this.blankSentinelAmount(
|
||||||
detail.fundLimit ?? detail.projectFundLimit ?? this.form.projectFundLimit,
|
detail.fundLimit ?? detail.projectFundLimit ?? this.form.projectFundLimit
|
||||||
usedFundLimit: detail.usedFundLimit ?? this.form.usedFundLimit,
|
),
|
||||||
|
usedFundLimit: this.blankSentinelAmount(
|
||||||
|
detail.usedFundLimit ?? this.form.usedFundLimit
|
||||||
|
),
|
||||||
});
|
});
|
||||||
this.form.remainingFundLimit = this.calculateRemainingFundLimit();
|
this.form.remainingFundLimit = this.calculateRemainingFundLimit();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
blankSentinelAmount(value) {
|
||||||
|
if (value === undefined || value === null || value === '') return '';
|
||||||
|
return Number(value) === -1 ? '' : value;
|
||||||
|
},
|
||||||
|
toOptionalAmount(value) {
|
||||||
|
const amount = this.blankSentinelAmount(value);
|
||||||
|
return amount === '' ? null : amount;
|
||||||
|
},
|
||||||
calculateRemainingFundLimit() {
|
calculateRemainingFundLimit() {
|
||||||
const total = Number(this.form.projectFundLimit);
|
const total = this.blankSentinelAmount(this.form.projectFundLimit);
|
||||||
const used = Number(this.form.usedFundLimit);
|
const used = this.blankSentinelAmount(this.form.usedFundLimit);
|
||||||
if (!Number.isFinite(total) || !Number.isFinite(used)) return '';
|
if (total === '' || used === '') return '';
|
||||||
return Math.round((total - used + Number.EPSILON) * 100) / 100;
|
const totalAmount = Number(total);
|
||||||
|
const usedAmount = Number(used);
|
||||||
|
if (!Number.isFinite(totalAmount) || !Number.isFinite(usedAmount)) return '';
|
||||||
|
return Math.round((totalAmount - usedAmount + Number.EPSILON) * 100) / 100;
|
||||||
},
|
},
|
||||||
handleAmountInput(value) {
|
handleAmountInput(value) {
|
||||||
const text = String(value || '').replace(/[^\d.]/g, '');
|
const text = String(value || '').replace(/[^\d.]/g, '');
|
||||||
@@ -783,24 +820,37 @@ export default {
|
|||||||
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
this.attachmentRows = (list || []).map(item => ({
|
this.attachmentRows = (list || []).map(item => ({
|
||||||
...item,
|
...item,
|
||||||
|
description: item.description || '',
|
||||||
uploadUserName: item.uploadUserName || uploadUserName,
|
uploadUserName: item.uploadUserName || uploadUserName,
|
||||||
uploadTime: item.uploadTime || uploadTime,
|
uploadTime: item.uploadTime || uploadTime,
|
||||||
}));
|
}));
|
||||||
this.form.attachmentsJson = JSON.stringify(this.attachmentRows);
|
this.syncAttachmentsJson();
|
||||||
},
|
},
|
||||||
removeAttachment(index) {
|
removeAttachment(index) {
|
||||||
this.attachmentRows.splice(index, 1);
|
this.attachmentRows.splice(index, 1);
|
||||||
this.form.attachmentsJson = JSON.stringify(this.attachmentRows);
|
this.syncAttachmentsJson();
|
||||||
|
},
|
||||||
|
syncAttachmentsJson() {
|
||||||
|
this.form.attachmentsJson = JSON.stringify(this.attachmentRows || []);
|
||||||
},
|
},
|
||||||
parseJsonArray(value) {
|
parseJsonArray(value) {
|
||||||
if (Array.isArray(value)) return value;
|
let list = [];
|
||||||
if (!value) return [];
|
if (Array.isArray(value)) {
|
||||||
try {
|
list = value;
|
||||||
const data = JSON.parse(value);
|
} else if (!value) {
|
||||||
return Array.isArray(data) ? data : [];
|
|
||||||
} catch (error) {
|
|
||||||
return [];
|
return [];
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(value);
|
||||||
|
list = Array.isArray(data) ? data : [];
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return list.map(item => ({
|
||||||
|
...item,
|
||||||
|
description: item?.description || '',
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
attachmentName(row = {}) {
|
attachmentName(row = {}) {
|
||||||
return row.originalName || row.name || row.fileName || '附件';
|
return row.originalName || row.name || row.fileName || '附件';
|
||||||
@@ -907,6 +957,7 @@ export default {
|
|||||||
.temporary-credit-limit-page {
|
.temporary-credit-limit-page {
|
||||||
&__field {
|
&__field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__attachment-head {
|
&__attachment-head {
|
||||||
@@ -993,7 +1044,7 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 14px 16px 4px;
|
padding: 14px 0 4px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
@@ -1086,9 +1137,36 @@ export default {
|
|||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
padding: 0 !important;
|
padding: 0 16px 8px !important;
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增/编辑:控件限宽,避免宽屏下撑满列宽贴到弹窗右边缘
|
||||||
|
&:not(.temporary-credit-limit-detail-dialog) {
|
||||||
|
.el-form-item__content {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item__content > .el-input,
|
||||||
|
.el-form-item__content > .el-select,
|
||||||
|
.el-form-item__content > .el-date-editor,
|
||||||
|
.el-form-item__content > .el-cascader,
|
||||||
|
.el-form-item__content > .el-textarea,
|
||||||
|
.temporary-credit-limit-page__field {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item__content > .el-textarea {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-date-editor.el-input,
|
||||||
|
.el-date-editor.el-input__wrapper {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 240px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.temporary-credit-limit-dialog .business-crud-page__detail-content .el-descriptions__label {
|
.temporary-credit-limit-dialog .business-crud-page__detail-content .el-descriptions__label {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<mk-public-shell biz-type="waybill-manage" :get-form="getForm">
|
||||||
|
<waybill-manage-page
|
||||||
|
ref="page"
|
||||||
|
:api="api"
|
||||||
|
:config="config"
|
||||||
|
:crud-option="option"
|
||||||
|
:detail-id="detailId"
|
||||||
|
standalone-detail-page
|
||||||
|
/>
|
||||||
|
</mk-public-shell>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
|
||||||
|
import WaybillManagePage from '@/views/business/components/waybill-manage-page.vue';
|
||||||
|
import * as api from '@/api/business/waybill-manage';
|
||||||
|
import { config, option } from '@/option/business/waybill-manage';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WaybillManagePublicView',
|
||||||
|
components: { MkPublicShell, WaybillManagePage },
|
||||||
|
data() {
|
||||||
|
return { api, config, option };
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
detailId() {
|
||||||
|
return this.$route.query.id || '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getForm() {
|
||||||
|
const row = this.$refs.page?.detailRow || {};
|
||||||
|
return { ...row, subject: row.waybillNo || '' };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="page" class="mk-public-shell">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { postMkPublicProcessMessage } from '@/api/mk-process';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MkPublicShell',
|
||||||
|
props: {
|
||||||
|
bizType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
getForm: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.handleIframeHeight = () => this.sendIframeHeight();
|
||||||
|
this.handleProcessMessage = event => this.onProcessMessage(event);
|
||||||
|
document.addEventListener('DOMContentLoaded', this.handleIframeHeight, false);
|
||||||
|
window.addEventListener('message', this.handleProcessMessage);
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
window.removeEventListener('message', this.handleProcessMessage);
|
||||||
|
(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(() => {
|
||||||
|
window.parent.postMessage({ height: document.body.clientHeight }, '*');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onProcessMessage(event) {
|
||||||
|
const data = event && event.data;
|
||||||
|
if (!data || typeof data !== 'object') return;
|
||||||
|
if (data.height && !data.status && !data.type) return;
|
||||||
|
if (data.type === 'formValues' || data.type === 'afterSubmit') return;
|
||||||
|
const formValues = data.formValues;
|
||||||
|
if (data.status === 'submit') {
|
||||||
|
this.submitData(formValues);
|
||||||
|
} else if (data.status === 'save') {
|
||||||
|
this.saveData(formValues);
|
||||||
|
}
|
||||||
|
if (data.type === 'getFormValues') {
|
||||||
|
window.parent.postMessage({ type: 'formValues', formData: this.buildFormData() }, '*');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submitData(lbpmFormValues) {
|
||||||
|
const formData = this.buildFormData();
|
||||||
|
this.postFormData('submit', lbpmFormValues, formData);
|
||||||
|
if (!lbpmFormValues) return;
|
||||||
|
const parameters = Object.assign({}, lbpmFormValues, {
|
||||||
|
loginName: this.getLoginName(lbpmFormValues),
|
||||||
|
formInstanceId: this.getFormId(),
|
||||||
|
subject: formData.subject,
|
||||||
|
});
|
||||||
|
window.parent.postMessage({ type: 'afterSubmit', success: true, parameters }, '*');
|
||||||
|
},
|
||||||
|
saveData(lbpmFormValues) {
|
||||||
|
this.postFormData('save', lbpmFormValues, this.buildFormData());
|
||||||
|
},
|
||||||
|
postFormData(status, formValues, formData) {
|
||||||
|
postMkPublicProcessMessage(this.bizType, {
|
||||||
|
status,
|
||||||
|
formValues: formValues || {},
|
||||||
|
formData,
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('公开页提交流程数据失败:', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
buildFormData() {
|
||||||
|
const form = this.readForm();
|
||||||
|
return {
|
||||||
|
...form,
|
||||||
|
subject: form.subject || '',
|
||||||
|
formInstanceId: this.getFormId(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
readForm() {
|
||||||
|
const form = typeof this.getForm === 'function' ? this.getForm() : {};
|
||||||
|
if (!form) return {};
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(form));
|
||||||
|
} catch (error) {
|
||||||
|
return { ...form };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getFormId() {
|
||||||
|
return this.$route.query.id || this.readForm().id || '';
|
||||||
|
},
|
||||||
|
getLoginName(formValues = {}) {
|
||||||
|
return (
|
||||||
|
formValues.loginName ||
|
||||||
|
this.$route.query.loginName ||
|
||||||
|
this.$route.query.submitIdentity ||
|
||||||
|
''
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</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>
|
||||||
|
.mk-public-shell {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 12px 0 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #f0f2f5;
|
||||||
|
|
||||||
|
:deep(.basic-container) {
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<mk-public-shell biz-type="payment-application" :get-form="getForm">
|
||||||
|
<payment-application-form ref="page" />
|
||||||
|
</mk-public-shell>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
|
||||||
|
import PaymentApplicationForm from '@/views/payment/payment-application-form.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PaymentApplicationPublicView',
|
||||||
|
components: { MkPublicShell, PaymentApplicationForm },
|
||||||
|
methods: {
|
||||||
|
getForm() {
|
||||||
|
const form = this.$refs.page?.form || {};
|
||||||
|
return { ...form, subject: form.paymentNo || form.projectName || '' };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -326,7 +326,7 @@ export default {
|
|||||||
openProjectAdvance() {
|
openProjectAdvance() {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
path: '/payment/payment-application/form',
|
path: '/payment/payment-application/form',
|
||||||
query: { mode: 'add', paymentType: 'project_advance' },
|
query: { mode: 'add', paymentType: 'progress_advance' },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
openEdit(row) {
|
openEdit(row) {
|
||||||
|
|||||||
@@ -211,8 +211,15 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-table v-show="!detailCollapsed" :data="filteredDetails" border>
|
<el-table v-show="!detailCollapsed" :data="pagedDetails" border>
|
||||||
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
|
<el-table-column
|
||||||
|
type="index"
|
||||||
|
label="序号"
|
||||||
|
width="64"
|
||||||
|
fixed="left"
|
||||||
|
align="center"
|
||||||
|
:index="detailIndexMethod"
|
||||||
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="column in detailTableColumns"
|
v-for="column in detailTableColumns"
|
||||||
:key="column.prop"
|
:key="column.prop"
|
||||||
@@ -245,6 +252,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<div v-show="!detailCollapsed" class="formal-editor__pagination">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="detailPage.current"
|
||||||
|
v-model:page-size="detailPage.size"
|
||||||
|
:total="filteredDetails.length"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleDetailPageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<section-card>
|
<section-card>
|
||||||
@@ -623,7 +640,7 @@
|
|||||||
>提交</el-button
|
>提交</el-button
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="pageMode" class="formal-editor__page-actions">
|
<div v-if="pageMode && !publicMode" class="formal-editor__page-actions">
|
||||||
<el-button @click="visible = false">取消</el-button>
|
<el-button @click="visible = false">取消</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="editable && !recordId"
|
v-if="editable && !recordId"
|
||||||
@@ -929,6 +946,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { getMkPublicDetail } from '@/api/mk-process';
|
||||||
import {
|
import {
|
||||||
adjustDetail,
|
adjustDetail,
|
||||||
getCandidateDetails,
|
getCandidateDetails,
|
||||||
@@ -977,6 +995,10 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
publicMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'success'],
|
emits: ['update:modelValue', 'success'],
|
||||||
data() {
|
data() {
|
||||||
@@ -1088,6 +1110,10 @@ export default {
|
|||||||
batchNo: '',
|
batchNo: '',
|
||||||
cargoName: '',
|
cargoName: '',
|
||||||
},
|
},
|
||||||
|
detailPage: {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -1135,6 +1161,13 @@ export default {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
pagedDetails() {
|
||||||
|
const list = this.filteredDetails;
|
||||||
|
const size = Number(this.detailPage.size) || 10;
|
||||||
|
const current = Math.max(Number(this.detailPage.current) || 1, 1);
|
||||||
|
const start = (current - 1) * size;
|
||||||
|
return list.slice(start, start + size);
|
||||||
|
},
|
||||||
missingAttachmentTypeText() {
|
missingAttachmentTypeText() {
|
||||||
const missing = this.getMissingAttachmentTypes();
|
const missing = this.getMissingAttachmentTypes();
|
||||||
return missing.length ? `未上传:${missing.join('、')}` : '';
|
return missing.length ? `未上传:${missing.join('、')}` : '';
|
||||||
@@ -1159,6 +1192,16 @@ export default {
|
|||||||
if (value) this.initialize();
|
if (value) this.initialize();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
filteredDetails: {
|
||||||
|
handler(list) {
|
||||||
|
const size = Number(this.detailPage.size) || 10;
|
||||||
|
const maxPage = Math.max(1, Math.ceil((list?.length || 0) / size) || 1);
|
||||||
|
if (this.detailPage.current > maxPage) {
|
||||||
|
this.detailPage.current = maxPage;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
summaryTotal(value) {
|
summaryTotal(value) {
|
||||||
this.form.settlementAmount = Number(value || 0).toFixed(2);
|
this.form.settlementAmount = Number(value || 0).toFixed(2);
|
||||||
this.form.localSettlementAmount = (
|
this.form.localSettlementAmount = (
|
||||||
@@ -1173,7 +1216,9 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadFormalDetail(id) {
|
async loadFormalDetail(id) {
|
||||||
const response = await getDetail(id);
|
const response = this.publicMode
|
||||||
|
? await getMkPublicDetail('formal-settlement', id)
|
||||||
|
: await getDetail(id);
|
||||||
const data = this.unwrapData(response);
|
const data = this.unwrapData(response);
|
||||||
this.form = {
|
this.form = {
|
||||||
...createFormalSettlementForm(),
|
...createFormalSettlementForm(),
|
||||||
@@ -1185,12 +1230,13 @@ export default {
|
|||||||
};
|
};
|
||||||
this.sources = data.sources || [];
|
this.sources = data.sources || [];
|
||||||
this.details = data.details || [];
|
this.details = data.details || [];
|
||||||
|
this.detailPage.current = 1;
|
||||||
this.summaryFees = data.summaryFees || [];
|
this.summaryFees = data.summaryFees || [];
|
||||||
// 服务端数据已刷新,费用快照与暂存调整全部作废。
|
// 服务端数据已刷新,费用快照与暂存调整全部作废。
|
||||||
this.detailFeeSnapshots = {};
|
this.detailFeeSnapshots = {};
|
||||||
this.pendingAdjustments = {};
|
this.pendingAdjustments = {};
|
||||||
this.paymentApplications = data.paymentApplications || [];
|
this.paymentApplications = data.paymentApplications || [];
|
||||||
if (this.readonly && this.form.settlementType === 'receivable') {
|
if (this.readonly && this.form.settlementType === 'receivable' && !this.publicMode) {
|
||||||
this.receiptClaims = this.unwrapData(await getReceiptClaims(id)) || [];
|
this.receiptClaims = this.unwrapData(await getReceiptClaims(id)) || [];
|
||||||
}
|
}
|
||||||
this.adjustments = data.adjustments || [];
|
this.adjustments = data.adjustments || [];
|
||||||
@@ -1237,14 +1283,17 @@ export default {
|
|||||||
rule: null,
|
rule: null,
|
||||||
};
|
};
|
||||||
this.detailCollapsed = false;
|
this.detailCollapsed = false;
|
||||||
|
this.detailPage = { current: 1, size: 10 };
|
||||||
this.resetDetailQuery(false);
|
this.resetDetailQuery(false);
|
||||||
await Promise.all([
|
if (!this.publicMode) {
|
||||||
this.loadAllContracts(),
|
await Promise.all([
|
||||||
this.loadFeeOptions(),
|
this.loadAllContracts(),
|
||||||
this.loadFeeCategoryOptions(),
|
this.loadFeeOptions(),
|
||||||
this.loadAttachmentTypeOptions(),
|
this.loadFeeCategoryOptions(),
|
||||||
this.loadTransportTypeOptions(),
|
this.loadAttachmentTypeOptions(),
|
||||||
]);
|
this.loadTransportTypeOptions(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
if (!this.recordId) {
|
if (!this.recordId) {
|
||||||
this.form.exchangeRateDate = this.$dayjs().format('YYYY-MM-DD');
|
this.form.exchangeRateDate = this.$dayjs().format('YYYY-MM-DD');
|
||||||
if (this.initialData) await this.applyInitialData();
|
if (this.initialData) await this.applyInitialData();
|
||||||
@@ -1328,6 +1377,7 @@ export default {
|
|||||||
settlementAmountTax:
|
settlementAmountTax:
|
||||||
row.settlementAmountTax ?? row.totalAmount ?? row.afterAmount ?? row.settlementAmount,
|
row.settlementAmountTax ?? row.totalAmount ?? row.afterAmount ?? row.settlementAmount,
|
||||||
}));
|
}));
|
||||||
|
this.detailPage.current = 1;
|
||||||
this.form.sourceDetailIds = this.details.map(item => item.sourceDetailId);
|
this.form.sourceDetailIds = this.details.map(item => item.sourceDetailId);
|
||||||
await this.refreshSummaryFees();
|
await this.refreshSummaryFees();
|
||||||
},
|
},
|
||||||
@@ -1373,6 +1423,7 @@ export default {
|
|||||||
sourcePreSettlementId: settlement.id,
|
sourcePreSettlementId: settlement.id,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
this.detailPage.current = 1;
|
||||||
const summaryMap = new Map();
|
const summaryMap = new Map();
|
||||||
settlements
|
settlements
|
||||||
.flatMap(settlement => settlement.summaryFees || [])
|
.flatMap(settlement => settlement.summaryFees || [])
|
||||||
@@ -1601,6 +1652,7 @@ export default {
|
|||||||
...this.details.filter(item => item.formalSettlementId),
|
...this.details.filter(item => item.formalSettlementId),
|
||||||
...existing.values(),
|
...existing.values(),
|
||||||
];
|
];
|
||||||
|
this.detailPage.current = 1;
|
||||||
this.form.sourceDetailIds = [...existing.values()].map(item => item.sourceDetailId);
|
this.form.sourceDetailIds = [...existing.values()].map(item => item.sourceDetailId);
|
||||||
this.detailCandidate.loading = true;
|
this.detailCandidate.loading = true;
|
||||||
try {
|
try {
|
||||||
@@ -1819,6 +1871,9 @@ export default {
|
|||||||
return sourceDetailId ? `src:${sourceDetailId}` : '';
|
return sourceDetailId ? `src:${sourceDetailId}` : '';
|
||||||
},
|
},
|
||||||
async loadDetailFeeRows(detail) {
|
async loadDetailFeeRows(detail) {
|
||||||
|
if (this.publicMode) {
|
||||||
|
return [this.normalizeAdjustRow(detail)];
|
||||||
|
}
|
||||||
if (detail.formalSettlementId && detail.id) {
|
if (detail.formalSettlementId && detail.id) {
|
||||||
const rows = this.unwrapData(await getDetailFees(detail.id)) || [];
|
const rows = this.unwrapData(await getDetailFees(detail.id)) || [];
|
||||||
return rows.map(item => this.normalizeAdjustRow(item));
|
return rows.map(item => this.normalizeAdjustRow(item));
|
||||||
@@ -1920,12 +1975,19 @@ export default {
|
|||||||
},
|
},
|
||||||
handleDetailQuery() {
|
handleDetailQuery() {
|
||||||
this.appliedDetailQuery = { ...this.detailQuery };
|
this.appliedDetailQuery = { ...this.detailQuery };
|
||||||
|
this.detailPage.current = 1;
|
||||||
},
|
},
|
||||||
resetDetailQuery(apply = true) {
|
resetDetailQuery(apply = true) {
|
||||||
this.detailQuery = { documentNo: '', waybillNo: '', batchNo: '', cargoName: '' };
|
this.detailQuery = { documentNo: '', waybillNo: '', batchNo: '', cargoName: '' };
|
||||||
if (apply) this.handleDetailQuery();
|
if (apply) this.handleDetailQuery();
|
||||||
else this.appliedDetailQuery = { ...this.detailQuery };
|
else this.appliedDetailQuery = { ...this.detailQuery };
|
||||||
},
|
},
|
||||||
|
handleDetailPageSizeChange() {
|
||||||
|
this.detailPage.current = 1;
|
||||||
|
},
|
||||||
|
detailIndexMethod(index) {
|
||||||
|
return (this.detailPage.current - 1) * this.detailPage.size + index + 1;
|
||||||
|
},
|
||||||
exportDetails() {
|
exportDetails() {
|
||||||
if (!this.details.length) return this.$message.warning('暂无可导出的结算明细');
|
if (!this.details.length) return this.$message.warning('暂无可导出的结算明细');
|
||||||
const rows = this.details.map((row, index) => {
|
const rows = this.details.map((row, index) => {
|
||||||
|
|||||||
@@ -66,6 +66,12 @@
|
|||||||
<span v-else-if="field.prop === 'settlementType'" class="form-readonly">
|
<span v-else-if="field.prop === 'settlementType'" class="form-readonly">
|
||||||
{{ settlementTypeName }}
|
{{ settlementTypeName }}
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else-if="field.prop === 'preSettlementNo'" class="form-readonly">
|
||||||
|
{{ form.preSettlementNo || (!form.id ? '系统自动生成' : '-') }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="field.prop === 'createTime'" class="form-readonly">
|
||||||
|
{{ formatCreateDate(form.createTime) }}
|
||||||
|
</span>
|
||||||
<span v-else-if="field.money" class="form-readonly">
|
<span v-else-if="field.money" class="form-readonly">
|
||||||
{{ formatMoney(form[field.prop], moneyCurrency(field.prop)) }}
|
{{ formatMoney(form[field.prop], moneyCurrency(field.prop)) }}
|
||||||
</span>
|
</span>
|
||||||
@@ -184,7 +190,6 @@
|
|||||||
</template>
|
</template>
|
||||||
<el-form
|
<el-form
|
||||||
:model="detailQuery"
|
:model="detailQuery"
|
||||||
inline
|
|
||||||
label-position="right"
|
label-position="right"
|
||||||
label-width="auto"
|
label-width="auto"
|
||||||
class="pre-settlement-editor__detail-filter"
|
class="pre-settlement-editor__detail-filter"
|
||||||
@@ -228,8 +233,15 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div v-show="!detailCollapsed">
|
<div v-show="!detailCollapsed">
|
||||||
<el-table :data="filteredDetails" border class="pre-settlement-editor__detail-table">
|
<el-table :data="pagedDetails" border class="pre-settlement-editor__detail-table">
|
||||||
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
|
<el-table-column
|
||||||
|
type="index"
|
||||||
|
label="序号"
|
||||||
|
width="64"
|
||||||
|
fixed="left"
|
||||||
|
align="center"
|
||||||
|
:index="detailIndexMethod"
|
||||||
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="column in visibleDetailColumns"
|
v-for="column in visibleDetailColumns"
|
||||||
:key="column.prop"
|
:key="column.prop"
|
||||||
@@ -300,6 +312,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<div class="pre-settlement-editor__pagination">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="detailPage.current"
|
||||||
|
v-model:page-size="detailPage.size"
|
||||||
|
:total="filteredDetails.length"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleDetailPageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
@@ -313,13 +335,43 @@
|
|||||||
<el-table :data="attachments" border @selection-change="handleAttachmentSelectionChange">
|
<el-table :data="attachments" border @selection-change="handleAttachmentSelectionChange">
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
<el-table-column label="文件名" min-width="260" show-overflow-tooltip>
|
<el-table-column label="附件类型" min-width="180" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-select
|
||||||
|
v-if="editable"
|
||||||
|
v-model="row.type"
|
||||||
|
filterable
|
||||||
|
placeholder="请选择"
|
||||||
|
@change="sortAttachments"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in attachmentTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<span v-else>{{ attachmentTypeName(row.type) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="文件名称" min-width="260" show-overflow-tooltip>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link type="primary" @click="previewAttachment(row)">
|
<el-link type="primary" @click="previewAttachment(row)">
|
||||||
{{ attachmentName(row) }}
|
{{ attachmentName(row) }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="附件描述" min-width="220">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="!editable">{{ row.description || '-' }}</span>
|
||||||
|
<el-input
|
||||||
|
v-else
|
||||||
|
v-model="row.description"
|
||||||
|
maxlength="200"
|
||||||
|
placeholder="请输入"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="文件大小" width="120" align="center">
|
<el-table-column label="文件大小" width="120" align="center">
|
||||||
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
|
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -425,7 +477,7 @@
|
|||||||
提交
|
提交
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="pageMode" class="pre-settlement-editor__page-actions">
|
<div v-if="pageMode && !publicMode" class="pre-settlement-editor__page-actions">
|
||||||
<el-button @click="visible = false">取消</el-button>
|
<el-button @click="visible = false">取消</el-button>
|
||||||
<el-button v-if="editable" type="primary" plain :loading="saving" @click="saveDraft(false)"
|
<el-button v-if="editable" type="primary" plain :loading="saving" @click="saveDraft(false)"
|
||||||
>保存</el-button
|
>保存</el-button
|
||||||
@@ -681,134 +733,113 @@
|
|||||||
append-to-body
|
append-to-body
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
>
|
>
|
||||||
<el-tabs v-model="adjustDialog.activeTab" class="pre-settlement-editor__adjust-tabs">
|
<el-table v-loading="adjustDialog.loading" :data="adjustRows" border>
|
||||||
<el-tab-pane label="结算明细调整" name="adjust">
|
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
|
||||||
<el-table v-loading="adjustDialog.loading" :data="adjustRows" border>
|
<el-table-column prop="cargoName" label="货物名称" min-width="140" align="center" />
|
||||||
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
|
<el-table-column prop="cargoType" label="货物类型" min-width="130" align="center" />
|
||||||
<el-table-column prop="cargoName" label="货物名称" min-width="140" align="center" />
|
<el-table-column label="计费规则" min-width="190" align="center">
|
||||||
<el-table-column prop="cargoType" label="货物类型" min-width="130" align="center" />
|
<template #default="{ row }">
|
||||||
<el-table-column label="计费规则" min-width="190" align="center">
|
<div class="pre-settlement-editor__billing-rule-cell">
|
||||||
<template #default="{ row }">
|
<span>{{ displayValue(billingRuleName(row)) }}</span>
|
||||||
<div class="pre-settlement-editor__billing-rule-cell">
|
<el-icon
|
||||||
<span>{{ displayValue(billingRuleName(row)) }}</span>
|
class="pre-settlement-editor__billing-rule-icon"
|
||||||
<el-icon
|
title="查看计费规则详情"
|
||||||
class="pre-settlement-editor__billing-rule-icon"
|
@click="openBillingRuleInfo(row)"
|
||||||
title="查看计费规则详情"
|
>
|
||||||
@click="openBillingRuleInfo(row)"
|
<InfoFilled />
|
||||||
>
|
</el-icon>
|
||||||
<InfoFilled />
|
</div>
|
||||||
</el-icon>
|
</template>
|
||||||
</div>
|
</el-table-column>
|
||||||
</template>
|
<el-table-column label="运输总量" min-width="150" align="center">
|
||||||
</el-table-column>
|
<template #default="{ row }">
|
||||||
<el-table-column label="运输总量" min-width="150" align="center">
|
<span v-if="adjustDialog.readonly">{{ formatQuantity(row) }}</span>
|
||||||
<template #default="{ row }">
|
<el-input-number
|
||||||
<span v-if="adjustDialog.readonly">{{ formatQuantity(row) }}</span>
|
v-else
|
||||||
<el-input-number
|
v-model="row.transportQuantity"
|
||||||
v-else
|
:min="0"
|
||||||
v-model="row.transportQuantity"
|
:precision="2"
|
||||||
:min="0"
|
:controls="false"
|
||||||
:precision="2"
|
@change="recalculateAdjustRow(row, 'transportQuantity')"
|
||||||
:controls="false"
|
|
||||||
@change="recalculateAdjustRow(row, 'transportQuantity')"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="里程(KM)" min-width="140" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.mileage"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:controls="false"
|
|
||||||
:disabled="adjustDialog.readonly"
|
|
||||||
@change="recalculateAdjustRow(row, 'mileage')"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="运输单价" min-width="140" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.unitPrice"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:controls="false"
|
|
||||||
:disabled="adjustDialog.readonly"
|
|
||||||
@change="recalculateAdjustRow(row, 'unitPrice')"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="feeItem in adjustFeeItemNames"
|
|
||||||
:key="feeItem"
|
|
||||||
:label="feeItem"
|
|
||||||
min-width="140"
|
|
||||||
align="center"
|
|
||||||
>
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.feeItems[feeItem]"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:controls="false"
|
|
||||||
:disabled="adjustDialog.readonly"
|
|
||||||
@change="recalculateAdjustRow(row, feeItem)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="结算金额(含税)" min-width="170" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.settlementAmountTax"
|
|
||||||
:precision="2"
|
|
||||||
:controls="false"
|
|
||||||
:disabled="adjustDialog.readonly"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="备注" min-width="200" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input v-model="row.remark" maxlength="200" :disabled="adjustDialog.readonly" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<el-form
|
|
||||||
v-if="!adjustDialog.readonly"
|
|
||||||
:model="adjustDialog"
|
|
||||||
label-position="right"
|
|
||||||
label-width="auto"
|
|
||||||
class="pre-settlement-editor__adjust-reason"
|
|
||||||
>
|
|
||||||
<el-form-item label="调整原因">
|
|
||||||
<el-input v-model="adjustDialog.reason" maxlength="200" show-word-limit />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="变更记录" name="records">
|
|
||||||
<el-table :data="changeRecords" border>
|
|
||||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
|
||||||
<el-table-column prop="changeTime" label="变更日期" min-width="170" align="center" />
|
|
||||||
<el-table-column prop="operatorName" label="经办人" min-width="140" align="center" />
|
|
||||||
<el-table-column prop="changeType" label="变更类型" min-width="160" align="center" />
|
|
||||||
<el-table-column
|
|
||||||
prop="changeReason"
|
|
||||||
label="变更原因"
|
|
||||||
min-width="240"
|
|
||||||
align="center"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/>
|
/>
|
||||||
<el-table-column label="状态" min-width="120" align="center">
|
</template>
|
||||||
<template #default>已生效</template>
|
</el-table-column>
|
||||||
</el-table-column>
|
<el-table-column label="里程(KM)" min-width="140" align="center">
|
||||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
<template #default="{ row }">
|
||||||
<template #default="{ row }">
|
<el-input-number
|
||||||
<el-link type="primary" @click="openAdjustChangeRecord(row)">查看详情</el-link>
|
v-model="row.mileage"
|
||||||
</template>
|
:min="0"
|
||||||
</el-table-column>
|
:precision="2"
|
||||||
</el-table>
|
:controls="false"
|
||||||
<el-empty v-if="!adjustChangeRecords.length" description="暂无变更记录" :image-size="60" />
|
:disabled="adjustDialog.readonly"
|
||||||
</el-tab-pane>
|
@change="recalculateAdjustRow(row, 'mileage')"
|
||||||
</el-tabs>
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="运输单价" min-width="140" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.unitPrice"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:controls="false"
|
||||||
|
:disabled="adjustDialog.readonly"
|
||||||
|
@change="recalculateAdjustRow(row, 'unitPrice')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
v-for="feeItem in adjustFeeItemNames"
|
||||||
|
:key="feeItem"
|
||||||
|
:label="feeItem"
|
||||||
|
min-width="140"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.feeItems[feeItem]"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
:controls="false"
|
||||||
|
:disabled="adjustDialog.readonly"
|
||||||
|
@change="recalculateAdjustRow(row, feeItem)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="结算金额(含税)" min-width="170" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input-number
|
||||||
|
v-model="row.settlementAmountTax"
|
||||||
|
:precision="2"
|
||||||
|
:controls="false"
|
||||||
|
:disabled="adjustDialog.readonly"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="备注" min-width="200" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.remark" maxlength="200" :disabled="adjustDialog.readonly" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-form
|
||||||
|
v-if="!adjustDialog.readonly"
|
||||||
|
:model="adjustDialog"
|
||||||
|
label-position="right"
|
||||||
|
label-width="auto"
|
||||||
|
class="pre-settlement-editor__adjust-reason"
|
||||||
|
>
|
||||||
|
<el-form-item label="调整原因">
|
||||||
|
<el-input
|
||||||
|
v-model="adjustDialog.reason"
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
maxlength="200"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="请输入"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="adjustDialog.visible = false">取消</el-button>
|
<el-button @click="adjustDialog.visible = false">取消</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
@@ -822,35 +853,10 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog
|
<change-record-detail-dialog
|
||||||
v-model="adjustChangeRecordVisible"
|
v-model="adjustChangeRecordVisible"
|
||||||
title="变更记录详情"
|
:rows="adjustChangeRecordDetailRows"
|
||||||
append-to-body
|
/>
|
||||||
destroy-on-close
|
|
||||||
width="1100px"
|
|
||||||
top="10px"
|
|
||||||
class="pre-settlement-change-record-detail-dialog"
|
|
||||||
>
|
|
||||||
<div v-if="adjustChangeRecord" class="pre-settlement-change-record-detail-meta">
|
|
||||||
<span>变更日期:{{ adjustChangeRecord.changeTime || '-' }}</span>
|
|
||||||
<span>经办人:{{ adjustChangeRecord.operatorName || '-' }}</span>
|
|
||||||
<span>变更类型:{{ adjustChangeRecord.changeType || '-' }}</span>
|
|
||||||
<span>状态:已生效</span>
|
|
||||||
</div>
|
|
||||||
<el-table :data="adjustChangeRecordDetailRows" border :show-overflow-tooltip="false">
|
|
||||||
<el-table-column prop="field" label="变更字段" min-width="220" />
|
|
||||||
<el-table-column prop="before" label="变更前" min-width="330" />
|
|
||||||
<el-table-column prop="after" label="变更后" min-width="420" />
|
|
||||||
</el-table>
|
|
||||||
<el-empty
|
|
||||||
v-if="!adjustChangeRecordDetailRows.length"
|
|
||||||
description="暂无变更内容"
|
|
||||||
:image-size="60"
|
|
||||||
/>
|
|
||||||
<template #footer>
|
|
||||||
<el-button type="primary" @click="adjustChangeRecordVisible = false">关闭</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="billingRuleDialog.visible"
|
v-model="billingRuleDialog.visible"
|
||||||
@@ -922,6 +928,8 @@
|
|||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { InfoFilled } from '@element-plus/icons-vue';
|
import { InfoFilled } from '@element-plus/icons-vue';
|
||||||
|
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
|
||||||
|
import { getMkPublicDetail } from '@/api/mk-process';
|
||||||
import {
|
import {
|
||||||
adjustDetail,
|
adjustDetail,
|
||||||
getCandidateDetails,
|
getCandidateDetails,
|
||||||
@@ -960,7 +968,7 @@ import * as XLSX from 'xlsx';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PreSettlementEditor',
|
name: 'PreSettlementEditor',
|
||||||
components: { InfoFilled },
|
components: { InfoFilled, ChangeRecordDetailDialog },
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -982,6 +990,10 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
publicMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'success'],
|
emits: ['update:modelValue', 'success'],
|
||||||
data() {
|
data() {
|
||||||
@@ -1054,9 +1066,14 @@ export default {
|
|||||||
batchNo: '',
|
batchNo: '',
|
||||||
cargoName: '',
|
cargoName: '',
|
||||||
},
|
},
|
||||||
|
detailPage: {
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
},
|
||||||
advances: [],
|
advances: [],
|
||||||
changeRecords: [],
|
changeRecords: [],
|
||||||
attachments: [],
|
attachments: [],
|
||||||
|
attachmentTypeOptions: [],
|
||||||
selectedAttachmentRows: [],
|
selectedAttachmentRows: [],
|
||||||
candidateDialog: {
|
candidateDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -1079,7 +1096,6 @@ export default {
|
|||||||
loading: false,
|
loading: false,
|
||||||
saving: false,
|
saving: false,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
activeTab: 'adjust',
|
|
||||||
detailId: '',
|
detailId: '',
|
||||||
sourceDetailId: '',
|
sourceDetailId: '',
|
||||||
detailLineNo: '',
|
detailLineNo: '',
|
||||||
@@ -1206,6 +1222,13 @@ export default {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
pagedDetails() {
|
||||||
|
const list = this.filteredDetails;
|
||||||
|
const size = Number(this.detailPage.size) || 10;
|
||||||
|
const current = Math.max(Number(this.detailPage.current) || 1, 1);
|
||||||
|
const start = (current - 1) * size;
|
||||||
|
return list.slice(start, start + size);
|
||||||
|
},
|
||||||
adjustFeeItemNames() {
|
adjustFeeItemNames() {
|
||||||
const names = new Set();
|
const names = new Set();
|
||||||
this.adjustRows.forEach(row => {
|
this.adjustRows.forEach(row => {
|
||||||
@@ -1232,6 +1255,16 @@ export default {
|
|||||||
this.form.settlementAmount = Number(value || 0).toFixed(2);
|
this.form.settlementAmount = Number(value || 0).toFixed(2);
|
||||||
this.recalculateLocalAmount();
|
this.recalculateLocalAmount();
|
||||||
},
|
},
|
||||||
|
filteredDetails: {
|
||||||
|
handler(list) {
|
||||||
|
const size = Number(this.detailPage.size) || 10;
|
||||||
|
const maxPage = Math.max(1, Math.ceil((list?.length || 0) / size) || 1);
|
||||||
|
if (this.detailPage.current > maxPage) {
|
||||||
|
this.detailPage.current = maxPage;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
hasPermission(code) {
|
hasPermission(code) {
|
||||||
@@ -1239,9 +1272,12 @@ export default {
|
|||||||
},
|
},
|
||||||
async initialize() {
|
async initialize() {
|
||||||
this.resetEditor();
|
this.resetEditor();
|
||||||
await this.loadFeeOptions();
|
if (!this.publicMode) {
|
||||||
await this.loadFeeCategoryOptions();
|
await this.loadFeeOptions();
|
||||||
await this.loadTransportTypeOptions();
|
await this.loadFeeCategoryOptions();
|
||||||
|
await this.loadTransportTypeOptions();
|
||||||
|
await this.loadAttachmentTypeOptions();
|
||||||
|
}
|
||||||
if (this.recordId) await this.loadDetail();
|
if (this.recordId) await this.loadDetail();
|
||||||
else if (this.initialData) await this.applyInitialData();
|
else if (this.initialData) await this.applyInitialData();
|
||||||
},
|
},
|
||||||
@@ -1281,6 +1317,7 @@ export default {
|
|||||||
localCurrency: first.localCurrency || 'RMB',
|
localCurrency: first.localCurrency || 'RMB',
|
||||||
});
|
});
|
||||||
this.details = rows.map(row => this.normalizeSourceDetail(row));
|
this.details = rows.map(row => this.normalizeSourceDetail(row));
|
||||||
|
this.detailPage.current = 1;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
await this.ensureSourceFeeSnapshots();
|
await this.ensureSourceFeeSnapshots();
|
||||||
@@ -1342,6 +1379,7 @@ export default {
|
|||||||
cargoName: '',
|
cargoName: '',
|
||||||
};
|
};
|
||||||
this.appliedDetailQuery = { ...this.detailQuery };
|
this.appliedDetailQuery = { ...this.detailQuery };
|
||||||
|
this.detailPage = { current: 1, size: 10 };
|
||||||
this.advances = [];
|
this.advances = [];
|
||||||
this.changeRecords = [];
|
this.changeRecords = [];
|
||||||
this.attachments = [];
|
this.attachments = [];
|
||||||
@@ -1350,7 +1388,6 @@ export default {
|
|||||||
this.pendingAdjustments = {};
|
this.pendingAdjustments = {};
|
||||||
this.sourceFeeSnapshots = {};
|
this.sourceFeeSnapshots = {};
|
||||||
this.detailFeeCache = {};
|
this.detailFeeCache = {};
|
||||||
this.adjustDialog.activeTab = 'adjust';
|
|
||||||
this.adjustDialog.detailId = '';
|
this.adjustDialog.detailId = '';
|
||||||
this.adjustDialog.sourceDetailId = '';
|
this.adjustDialog.sourceDetailId = '';
|
||||||
this.adjustDialog.detailLineNo = '';
|
this.adjustDialog.detailLineNo = '';
|
||||||
@@ -1370,11 +1407,14 @@ export default {
|
|||||||
async loadDetail() {
|
async loadDetail() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const { data } = await getDetail(this.form.id || this.recordId);
|
const response = this.publicMode
|
||||||
const detail = data?.data || {};
|
? await getMkPublicDetail('pre-settlement', this.form.id || this.recordId)
|
||||||
|
: await getDetail(this.form.id || this.recordId);
|
||||||
|
const detail = response.data?.data || {};
|
||||||
this.form = { ...emptyPreSettlementForm(), ...detail };
|
this.form = { ...emptyPreSettlementForm(), ...detail };
|
||||||
this.summaryFees = (detail.summaryFees || []).map(row => ({ ...row }));
|
this.summaryFees = (detail.summaryFees || []).map(row => ({ ...row }));
|
||||||
this.details = (detail.details || []).map(row => this.normalizeSourceDetail(row));
|
this.details = (detail.details || []).map(row => this.normalizeSourceDetail(row));
|
||||||
|
this.detailPage.current = 1;
|
||||||
this.advances = (detail.advances || []).map(row => ({
|
this.advances = (detail.advances || []).map(row => ({
|
||||||
...row,
|
...row,
|
||||||
createUserName: row.createUserName || detail.createUserName,
|
createUserName: row.createUserName || detail.createUserName,
|
||||||
@@ -1382,6 +1422,7 @@ export default {
|
|||||||
}));
|
}));
|
||||||
this.changeRecords = detail.changeRecords || [];
|
this.changeRecords = detail.changeRecords || [];
|
||||||
this.attachments = this.parseAttachments(detail.attachmentsJson);
|
this.attachments = this.parseAttachments(detail.attachmentsJson);
|
||||||
|
this.sortAttachments();
|
||||||
this.selectedAttachmentRows = [];
|
this.selectedAttachmentRows = [];
|
||||||
if (
|
if (
|
||||||
detail.contractId &&
|
detail.contractId &&
|
||||||
@@ -1399,8 +1440,15 @@ export default {
|
|||||||
partyB: detail.payeeName,
|
partyB: detail.payeeName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 预加载所有明细的费用明细数据,用于拼接货物名称和类型
|
if (this.publicMode && detail.detailFees) {
|
||||||
await this.loadAllDetailFees();
|
Object.entries(detail.detailFees).forEach(([detailId, feeRows]) => {
|
||||||
|
if (Array.isArray(feeRows) && feeRows.length) {
|
||||||
|
this.detailFeeCache[detailId] = feeRows;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.loadAllDetailFees();
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
@@ -1566,6 +1614,7 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$message.success('保存成功');
|
this.$message.success('保存成功');
|
||||||
|
this.visible = false;
|
||||||
this.$emit('success', this.form.id);
|
this.$emit('success', this.form.id);
|
||||||
} finally {
|
} finally {
|
||||||
this[stateKey] = false;
|
this[stateKey] = false;
|
||||||
@@ -1895,6 +1944,7 @@ export default {
|
|||||||
},
|
},
|
||||||
handleDetailQuery() {
|
handleDetailQuery() {
|
||||||
this.appliedDetailQuery = { ...this.detailQuery };
|
this.appliedDetailQuery = { ...this.detailQuery };
|
||||||
|
this.detailPage.current = 1;
|
||||||
},
|
},
|
||||||
resetDetailQuery() {
|
resetDetailQuery() {
|
||||||
this.detailQuery = {
|
this.detailQuery = {
|
||||||
@@ -1904,6 +1954,13 @@ export default {
|
|||||||
cargoName: '',
|
cargoName: '',
|
||||||
};
|
};
|
||||||
this.appliedDetailQuery = { ...this.detailQuery };
|
this.appliedDetailQuery = { ...this.detailQuery };
|
||||||
|
this.detailPage.current = 1;
|
||||||
|
},
|
||||||
|
handleDetailPageSizeChange() {
|
||||||
|
this.detailPage.current = 1;
|
||||||
|
},
|
||||||
|
detailIndexMethod(index) {
|
||||||
|
return (this.detailPage.current - 1) * this.detailPage.size + index + 1;
|
||||||
},
|
},
|
||||||
normalizeAdjustRow(item, feeItemNames = []) {
|
normalizeAdjustRow(item, feeItemNames = []) {
|
||||||
const rawFeeItems =
|
const rawFeeItems =
|
||||||
@@ -2026,7 +2083,6 @@ export default {
|
|||||||
this.adjustDialog.visible = true;
|
this.adjustDialog.visible = true;
|
||||||
this.adjustDialog.loading = true;
|
this.adjustDialog.loading = true;
|
||||||
this.adjustDialog.readonly = readonly;
|
this.adjustDialog.readonly = readonly;
|
||||||
this.adjustDialog.activeTab = 'adjust';
|
|
||||||
this.adjustDialog.detailId = detailRow.id;
|
this.adjustDialog.detailId = detailRow.id;
|
||||||
this.adjustDialog.detailLineNo = detailRow.lineNo;
|
this.adjustDialog.detailLineNo = detailRow.lineNo;
|
||||||
this.adjustDialog.sourceDetailId =
|
this.adjustDialog.sourceDetailId =
|
||||||
@@ -2042,6 +2098,11 @@ export default {
|
|||||||
this.adjustRows = this.cloneAdjustRows(pending.rows);
|
this.adjustRows = this.cloneAdjustRows(pending.rows);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.publicMode) {
|
||||||
|
const cachedFees = this.detailFeeCache[detailRow.id] || [];
|
||||||
|
this.adjustRows = cachedFees.map(item => this.normalizeAdjustRow(item));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (sourceDetailId) {
|
if (sourceDetailId) {
|
||||||
if (!this.sourceFeeSnapshots[sourceDetailId]) {
|
if (!this.sourceFeeSnapshots[sourceDetailId]) {
|
||||||
this.sourceFeeSnapshots[sourceDetailId] = await this.loadSourceFeeRows(sourceDetailId);
|
this.sourceFeeSnapshots[sourceDetailId] = await this.loadSourceFeeRows(sourceDetailId);
|
||||||
@@ -2049,6 +2110,11 @@ export default {
|
|||||||
this.adjustRows = this.cloneAdjustRows(this.sourceFeeSnapshots[sourceDetailId]);
|
this.adjustRows = this.cloneAdjustRows(this.sourceFeeSnapshots[sourceDetailId]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const cachedFees = this.detailFeeCache[detailRow.id];
|
||||||
|
if (this.publicMode && Array.isArray(cachedFees)) {
|
||||||
|
this.adjustRows = cachedFees.map(item => this.normalizeAdjustRow(item));
|
||||||
|
return;
|
||||||
|
}
|
||||||
const response = await getDetailFees(detailRow.id);
|
const response = await getDetailFees(detailRow.id);
|
||||||
const rows = response.data?.data || response.data || [];
|
const rows = response.data?.data || response.data || [];
|
||||||
this.adjustRows = rows.map(item => this.normalizeAdjustRow(item));
|
this.adjustRows = rows.map(item => this.normalizeAdjustRow(item));
|
||||||
@@ -2237,10 +2303,6 @@ export default {
|
|||||||
return String(name || '').includes('运费') || String(name || '').includes('运输费');
|
return String(name || '').includes('运费') || String(name || '').includes('运输费');
|
||||||
},
|
},
|
||||||
async saveAdjustment() {
|
async saveAdjustment() {
|
||||||
if (!this.adjustDialog.reason.trim()) {
|
|
||||||
this.$message.warning('请输入调整原因');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.adjustRows.some(row => row.calculating)) {
|
if (this.adjustRows.some(row => row.calculating)) {
|
||||||
this.$message.warning('费用正在重新计算,请稍候');
|
this.$message.warning('费用正在重新计算,请稍候');
|
||||||
return;
|
return;
|
||||||
@@ -2419,13 +2481,92 @@ export default {
|
|||||||
handleAttachmentChange(list) {
|
handleAttachmentChange(list) {
|
||||||
const uploadUserName = this.userInfo?.realName || this.userInfo?.userName || '';
|
const uploadUserName = this.userInfo?.realName || this.userInfo?.userName || '';
|
||||||
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
this.attachments = (list || []).map(file => ({
|
const existingRows = this.attachments || [];
|
||||||
...file,
|
this.attachments = (list || []).map(file => {
|
||||||
uploadUserName: file.uploadUserName || uploadUserName,
|
const fileUrl = this.attachmentUrl(file);
|
||||||
uploadTime: file.uploadTime || uploadTime,
|
const existing = existingRows.find(item => {
|
||||||
}));
|
const sameUid = file.uid && item.uid && String(file.uid) === String(item.uid);
|
||||||
|
const sameUrl = fileUrl && this.attachmentUrl(item) === fileUrl;
|
||||||
|
return sameUid || sameUrl;
|
||||||
|
});
|
||||||
|
const originalName = this.attachmentName(file);
|
||||||
|
return {
|
||||||
|
...file,
|
||||||
|
type: existing?.type || file.type || this.resolveAttachmentType(originalName),
|
||||||
|
description: existing?.description || file.description || '',
|
||||||
|
uploadUserName: existing?.uploadUserName || file.uploadUserName || uploadUserName,
|
||||||
|
uploadTime: existing?.uploadTime || file.uploadTime || uploadTime,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.sortAttachments();
|
||||||
this.selectedAttachmentRows = [];
|
this.selectedAttachmentRows = [];
|
||||||
},
|
},
|
||||||
|
async loadAttachmentTypeOptions() {
|
||||||
|
try {
|
||||||
|
const response = await getDictionary({ code: 'pre_settlement_attachment_type' });
|
||||||
|
const data = response?.data?.data || [];
|
||||||
|
this.attachmentTypeOptions = data
|
||||||
|
.map(item => ({
|
||||||
|
label: item.dictValue,
|
||||||
|
value: item.dictValue,
|
||||||
|
}))
|
||||||
|
.filter(item => item.label && item.value);
|
||||||
|
} catch (error) {
|
||||||
|
this.attachmentTypeOptions = [];
|
||||||
|
}
|
||||||
|
this.sortAttachments();
|
||||||
|
},
|
||||||
|
resolveAttachmentType(fileName) {
|
||||||
|
const normalizedName = String(fileName || '').toLocaleLowerCase();
|
||||||
|
const matchedType = [...this.attachmentTypeOptions]
|
||||||
|
.filter(item => item?.label || item?.value)
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
String(b.label || b.value || '').length - String(a.label || a.value || '').length
|
||||||
|
)
|
||||||
|
.find(item => {
|
||||||
|
const typeText = String(item.label || item.value || '').toLocaleLowerCase();
|
||||||
|
return typeText && normalizedName.includes(typeText);
|
||||||
|
});
|
||||||
|
if (matchedType?.value) return matchedType.value;
|
||||||
|
const otherType = this.attachmentTypeOptions.find(item => this.isOtherAttachmentType(item));
|
||||||
|
if (otherType?.value) return otherType.value;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
isOtherAttachmentType(item) {
|
||||||
|
const typeText = String(item?.label || item?.value || '').trim();
|
||||||
|
return (
|
||||||
|
typeText === '其他' ||
|
||||||
|
typeText === '其它' ||
|
||||||
|
typeText === '其他附件' ||
|
||||||
|
typeText === '其它附件'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getAttachmentTypeOrder(type) {
|
||||||
|
const normalizedType = String(type || '').trim();
|
||||||
|
const index = this.attachmentTypeOptions.findIndex(
|
||||||
|
item =>
|
||||||
|
String(item.value || '').trim() === normalizedType ||
|
||||||
|
String(item.label || '').trim() === normalizedType
|
||||||
|
);
|
||||||
|
return index === -1 ? Number.MAX_SAFE_INTEGER : index;
|
||||||
|
},
|
||||||
|
sortAttachments() {
|
||||||
|
this.attachments = (this.attachments || [])
|
||||||
|
.map((file, index) => ({ file, index }))
|
||||||
|
.sort((a, b) => {
|
||||||
|
const orderDifference =
|
||||||
|
this.getAttachmentTypeOrder(a.file.type) - this.getAttachmentTypeOrder(b.file.type);
|
||||||
|
return orderDifference || a.index - b.index;
|
||||||
|
})
|
||||||
|
.map(item => item.file);
|
||||||
|
},
|
||||||
|
attachmentTypeName(type) {
|
||||||
|
const option = this.attachmentTypeOptions.find(
|
||||||
|
item => String(item.value) === String(type) || String(item.label) === String(type)
|
||||||
|
);
|
||||||
|
return option?.label || type || '-';
|
||||||
|
},
|
||||||
handleAttachmentSelectionChange(rows) {
|
handleAttachmentSelectionChange(rows) {
|
||||||
this.selectedAttachmentRows = rows || [];
|
this.selectedAttachmentRows = rows || [];
|
||||||
},
|
},
|
||||||
@@ -2469,13 +2610,25 @@ export default {
|
|||||||
},
|
},
|
||||||
parseAttachments(value) {
|
parseAttachments(value) {
|
||||||
if (!value) return [];
|
if (!value) return [];
|
||||||
if (Array.isArray(value)) return value;
|
let list = [];
|
||||||
try {
|
if (Array.isArray(value)) {
|
||||||
const parsed = JSON.parse(value);
|
list = value;
|
||||||
return Array.isArray(parsed) ? parsed : [];
|
} else {
|
||||||
} catch (error) {
|
try {
|
||||||
return [];
|
const parsed = JSON.parse(value);
|
||||||
|
list = Array.isArray(parsed) ? parsed : [];
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return list.map(file => {
|
||||||
|
const originalName = this.attachmentName(file);
|
||||||
|
return {
|
||||||
|
...file,
|
||||||
|
type: file?.type || this.resolveAttachmentType(originalName),
|
||||||
|
description: file?.description || '',
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
parseFeeItems(value) {
|
parseFeeItems(value) {
|
||||||
if (!value) return {};
|
if (!value) return {};
|
||||||
@@ -2512,6 +2665,11 @@ export default {
|
|||||||
if (!Number.isFinite(amount)) return '-';
|
if (!Number.isFinite(amount)) return '-';
|
||||||
return `${amount.toFixed(2)} ${currency || 'RMB'}`;
|
return `${amount.toFixed(2)} ${currency || 'RMB'}`;
|
||||||
},
|
},
|
||||||
|
formatCreateDate(value) {
|
||||||
|
if (value === undefined || value === null || value === '') return '-';
|
||||||
|
const parsed = this.$dayjs(value);
|
||||||
|
return parsed.isValid() ? parsed.format('YYYY-MM-DD') : this.displayValue(value);
|
||||||
|
},
|
||||||
formatQuantity(row) {
|
formatQuantity(row) {
|
||||||
const value = row.transportQuantity;
|
const value = row.transportQuantity;
|
||||||
if (value === undefined || value === null || value === '') return '-';
|
if (value === undefined || value === null || value === '') return '-';
|
||||||
@@ -2662,22 +2820,43 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__detail-filter {
|
&__detail-filter {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-wrap: wrap;
|
grid-template-columns: repeat(4, minmax(0, 1fr)) auto;
|
||||||
gap: 0 24px;
|
align-items: center;
|
||||||
|
gap: 8px 16px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
padding: 12px 12px 4px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
|
|
||||||
:deep(.el-form-item) {
|
:deep(.el-form-item) {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
margin-right: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item__content) {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-input) {
|
:deep(.el-input) {
|
||||||
width: 220px;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__detail-filter-actions {
|
&__detail-filter-actions {
|
||||||
margin-left: auto;
|
width: auto;
|
||||||
|
margin-left: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
justify-self: end;
|
||||||
|
|
||||||
|
:deep(.el-form-item__content) {
|
||||||
|
flex: none;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__contract-filter {
|
&__contract-filter {
|
||||||
@@ -2790,23 +2969,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pre-settlement-change-record-detail-meta {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px 32px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
color: #606266;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.pre-settlement-change-record-detail-dialog .el-dialog__body) {
|
|
||||||
padding-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.pre-settlement-change-record-detail-dialog .el-table .cell) {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.pre-settlement-editor .el-dialog__body) {
|
:deep(.pre-settlement-editor .el-dialog__body) {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
goBack() {
|
goBack() {
|
||||||
removeSettlementTransfer(this.$route.query.transferToken);
|
removeSettlementTransfer(this.$route.query.transferToken);
|
||||||
|
this.$router.$avueRouter.closeTag();
|
||||||
this.$router.push('/settlement/formal-settlement');
|
this.$router.push('/settlement/formal-settlement');
|
||||||
},
|
},
|
||||||
syncTagTitle() {
|
syncTagTitle() {
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<mk-public-shell biz-type="formal-settlement" :get-form="getForm">
|
||||||
|
<basic-container>
|
||||||
|
<div class="mk-public-form-title">查看正式结算</div>
|
||||||
|
<formal-settlement-editor
|
||||||
|
ref="page"
|
||||||
|
:model-value="true"
|
||||||
|
page-mode
|
||||||
|
readonly
|
||||||
|
public-mode
|
||||||
|
:record-id="recordId"
|
||||||
|
/>
|
||||||
|
</basic-container>
|
||||||
|
</mk-public-shell>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
|
||||||
|
import FormalSettlementEditor from '@/views/settlement/components/formal-settlement-editor.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FormalSettlementPublicView',
|
||||||
|
components: { MkPublicShell, FormalSettlementEditor },
|
||||||
|
computed: {
|
||||||
|
recordId() {
|
||||||
|
return this.$route.query.id || '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getForm() {
|
||||||
|
const form = this.$refs.page?.form || {};
|
||||||
|
return { ...form, subject: form.formalSettlementNo || form.contractName || '' };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mk-public-form-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
width: 4px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 8px;
|
||||||
|
background: #409eff;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -50,6 +50,7 @@ export default {
|
|||||||
},
|
},
|
||||||
goBack() {
|
goBack() {
|
||||||
removeSettlementTransfer(this.$route.query.transferToken);
|
removeSettlementTransfer(this.$route.query.transferToken);
|
||||||
|
this.$router.$avueRouter.closeTag();
|
||||||
this.$router.push('/settlement/pre-settlement');
|
this.$router.push('/settlement/pre-settlement');
|
||||||
},
|
},
|
||||||
syncTagTitle() {
|
syncTagTitle() {
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<mk-public-shell biz-type="pre-settlement" :get-form="getForm">
|
||||||
|
<basic-container>
|
||||||
|
<div class="mk-public-form-title">查看预结算</div>
|
||||||
|
<pre-settlement-editor
|
||||||
|
ref="page"
|
||||||
|
:model-value="true"
|
||||||
|
page-mode
|
||||||
|
readonly
|
||||||
|
public-mode
|
||||||
|
:record-id="recordId"
|
||||||
|
/>
|
||||||
|
</basic-container>
|
||||||
|
</mk-public-shell>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
|
||||||
|
import PreSettlementEditor from '@/views/settlement/components/pre-settlement-editor.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PreSettlementPublicView',
|
||||||
|
components: { MkPublicShell, PreSettlementEditor },
|
||||||
|
computed: {
|
||||||
|
recordId() {
|
||||||
|
return this.$route.query.id || '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getForm() {
|
||||||
|
const form = this.$refs.page?.form || {};
|
||||||
|
return { ...form, subject: form.preSettlementNo || form.contractName || '' };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mk-public-form-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
width: 4px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 8px;
|
||||||
|
background: #409eff;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -497,6 +497,7 @@ export default {
|
|||||||
path: '/payment/payment-application/form',
|
path: '/payment/payment-application/form',
|
||||||
query: {
|
query: {
|
||||||
mode: 'add',
|
mode: 'add',
|
||||||
|
paymentType: 'progress_advance',
|
||||||
transferToken,
|
transferToken,
|
||||||
preSettlementIds: sourcePreSettlements
|
preSettlementIds: sourcePreSettlements
|
||||||
.map(row => row.preSettlementId)
|
.map(row => row.preSettlementId)
|
||||||
|
|||||||
@@ -183,6 +183,7 @@
|
|||||||
title="变更记录详情"
|
title="变更记录详情"
|
||||||
width="88%"
|
width="88%"
|
||||||
append-to-body
|
append-to-body
|
||||||
|
align-center
|
||||||
>
|
>
|
||||||
<el-table v-loading="changeRecordsDialog.loading" :data="changeRows" border>
|
<el-table v-loading="changeRecordsDialog.loading" :data="changeRows" border>
|
||||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||||
@@ -1153,28 +1154,16 @@ export default {
|
|||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
align: 'right',
|
align: 'right',
|
||||||
};
|
};
|
||||||
const dynamicColumns = this.isPayable
|
const dynamicColumns = [
|
||||||
? [
|
{
|
||||||
{
|
label: '运输费',
|
||||||
label: '运输费',
|
prop: 'tableFreightAmount',
|
||||||
prop: 'tableFreightAmount',
|
feeSummaryType: 'freight',
|
||||||
feeSummaryType: 'freight',
|
minWidth: 130,
|
||||||
minWidth: 130,
|
align: 'right',
|
||||||
align: 'right',
|
},
|
||||||
},
|
otherFeeColumn,
|
||||||
otherFeeColumn,
|
];
|
||||||
]
|
|
||||||
: [
|
|
||||||
...this.tableFeeItemNames.map((name, index) => ({
|
|
||||||
label: name,
|
|
||||||
prop: `tableFeeItem${index}`,
|
|
||||||
feeItemName: name,
|
|
||||||
dynamic: true,
|
|
||||||
minWidth: 130,
|
|
||||||
align: 'right',
|
|
||||||
})),
|
|
||||||
otherFeeColumn,
|
|
||||||
];
|
|
||||||
if (totalIndex < 0) return [...columns, ...dynamicColumns];
|
if (totalIndex < 0) return [...columns, ...dynamicColumns];
|
||||||
columns.splice(totalIndex, 0, ...dynamicColumns);
|
columns.splice(totalIndex, 0, ...dynamicColumns);
|
||||||
return columns;
|
return columns;
|
||||||
@@ -1209,10 +1198,24 @@ export default {
|
|||||||
await this.loadTable();
|
await this.loadTable();
|
||||||
await this.openRouteDetail();
|
await this.openRouteDetail();
|
||||||
},
|
},
|
||||||
|
// 标签切走(keep-alive 缓存)或销毁时,收起 teleport/append-to-body 弹层,避免残留盖住其它页面
|
||||||
|
deactivated() {
|
||||||
|
this.closeInnerDialogs();
|
||||||
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.clearAdjustCalculations();
|
this.closeInnerDialogs();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
closeInnerDialogs() {
|
||||||
|
this.closeDetailPanel();
|
||||||
|
this.closeAdjustPanel();
|
||||||
|
this.changeRecordsDialog.visible = false;
|
||||||
|
this.updateFeeDialog.visible = false;
|
||||||
|
this.transferDialog.visible = false;
|
||||||
|
this.generateDialog.visible = false;
|
||||||
|
this.previewDialog.visible = false;
|
||||||
|
this.generateContractDialog.visible = false;
|
||||||
|
},
|
||||||
contractCategoryName(value) {
|
contractCategoryName(value) {
|
||||||
return (
|
return (
|
||||||
this.contractCategoryOptions.find(item => String(item.value) === String(value))?.label ||
|
this.contractCategoryOptions.find(item => String(item.value) === String(value))?.label ||
|
||||||
@@ -1498,7 +1501,7 @@ export default {
|
|||||||
const res = await api.getList(this.page.current, this.page.size, params);
|
const res = await api.getList(this.page.current, this.page.size, params);
|
||||||
const data = this.unwrapPage(res);
|
const data = this.unwrapPage(res);
|
||||||
this.rows = (data.records || []).map(this.decorateRow);
|
this.rows = (data.records || []).map(this.decorateRow);
|
||||||
this.tableFeeItemNames = this.isPayable ? [] : this.collectFeeItemNames(this.rows);
|
this.tableFeeItemNames = [];
|
||||||
this.page.total = data.total || 0;
|
this.page.total = data.total || 0;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
@@ -1738,7 +1741,7 @@ export default {
|
|||||||
});
|
});
|
||||||
const adjusted = {
|
const adjusted = {
|
||||||
...item,
|
...item,
|
||||||
transportQuantity: Number(item.transportQuantity || 0),
|
transportQuantity: this.normalizeAdjustTransportQuantity(item),
|
||||||
mileage:
|
mileage:
|
||||||
item.mileage === null || item.mileage === undefined || Number(item.mileage) === -1
|
item.mileage === null || item.mileage === undefined || Number(item.mileage) === -1
|
||||||
? null
|
? null
|
||||||
@@ -1767,6 +1770,21 @@ export default {
|
|||||||
feeSourceLabel(value) {
|
feeSourceLabel(value) {
|
||||||
return ['手动录入', '手动添加', '手工录入'].includes(value) ? '手动录入' : '自动生成';
|
return ['手动录入', '手动添加', '手工录入'].includes(value) ? '手动录入' : '自动生成';
|
||||||
},
|
},
|
||||||
|
normalizeAdjustTransportQuantity(item = {}) {
|
||||||
|
const value = item.transportQuantity;
|
||||||
|
if (value === undefined || value === null || value === '' || Number(value) === -1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const element = item.billingFactor;
|
||||||
|
// 按车辆/固定金额生成时运输量会被写成占位 1,调整弹窗无实际运输量时不展示
|
||||||
|
if (
|
||||||
|
(element === '按车辆' || element === '固定金额(整单一口价)') &&
|
||||||
|
Number(value) === 1
|
||||||
|
) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return Number(value);
|
||||||
|
},
|
||||||
adjustBillingTypes(row) {
|
adjustBillingTypes(row) {
|
||||||
return ADJUST_BILLING_TYPE_MAP[row.billingFactor] || [];
|
return ADJUST_BILLING_TYPE_MAP[row.billingFactor] || [];
|
||||||
},
|
},
|
||||||
@@ -1894,7 +1912,12 @@ export default {
|
|||||||
model: row.model,
|
model: row.model,
|
||||||
billingFactor: row.billingFactor,
|
billingFactor: row.billingFactor,
|
||||||
billingType: row.billingType,
|
billingType: row.billingType,
|
||||||
transportQuantity: row.transportQuantity,
|
transportQuantity:
|
||||||
|
row.transportQuantity === '' ||
|
||||||
|
row.transportQuantity === null ||
|
||||||
|
row.transportQuantity === undefined
|
||||||
|
? null
|
||||||
|
: Number(row.transportQuantity),
|
||||||
priceUnit: row.priceUnit,
|
priceUnit: row.priceUnit,
|
||||||
unitPrice: row.unitPrice,
|
unitPrice: row.unitPrice,
|
||||||
mileage: row.mileage,
|
mileage: row.mileage,
|
||||||
|
|||||||
+46
-18
@@ -143,7 +143,8 @@
|
|||||||
:status="oaSyncProgressStatus"
|
:status="oaSyncProgressStatus"
|
||||||
:stroke-width="12"
|
:stroke-width="12"
|
||||||
/>
|
/>
|
||||||
<div class="oa-sync-meta">
|
<div class="oa-sync-meta" v-if="oaSyncStage === 'clear'">当前阶段:{{ oaSyncStageLabel }}</div>
|
||||||
|
<div class="oa-sync-meta" v-else>
|
||||||
当前阶段:{{ oaSyncStageLabel }},第 {{ oaSyncProgress.current || 0 }} / {{ oaSyncTotalPage }} 页
|
当前阶段:{{ oaSyncStageLabel }},第 {{ oaSyncProgress.current || 0 }} / {{ oaSyncTotalPage }} 页
|
||||||
</div>
|
</div>
|
||||||
<div class="oa-sync-stats">
|
<div class="oa-sync-stats">
|
||||||
@@ -177,6 +178,7 @@ import {
|
|||||||
getDeptTree,
|
getDeptTree,
|
||||||
syncOaCompany,
|
syncOaCompany,
|
||||||
syncOaDepartment,
|
syncOaDepartment,
|
||||||
|
clearNonTopDept,
|
||||||
} 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';
|
||||||
@@ -215,6 +217,7 @@ export default {
|
|||||||
oaSyncCancelled: false,
|
oaSyncCancelled: false,
|
||||||
oaSyncStatus: 'running',
|
oaSyncStatus: 'running',
|
||||||
oaSyncStage: 'company',
|
oaSyncStage: 'company',
|
||||||
|
oaSyncClearNonTop: false,
|
||||||
oaSyncController: null,
|
oaSyncController: null,
|
||||||
oaSyncProgress: {
|
oaSyncProgress: {
|
||||||
current: 0,
|
current: 0,
|
||||||
@@ -287,8 +290,9 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const parentId = this.normalizeParentId(this.form?.parentId);
|
const parentId = this.normalizeParentId(this.form?.parentId);
|
||||||
|
// 顶级组织不选择上级时,编码不依赖上级编码
|
||||||
if (!parentId) {
|
if (!parentId) {
|
||||||
callback(new Error('请先选择上级组织'));
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.parentDept || String(this.parentDept.id) !== String(parentId)) {
|
if (!this.parentDept || String(this.parentDept.id) !== String(parentId)) {
|
||||||
@@ -429,13 +433,6 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
label: 'title',
|
label: 'title',
|
||||||
},
|
},
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择上级组织',
|
|
||||||
trigger: 'click',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '所属租户',
|
label: '所属租户',
|
||||||
@@ -520,6 +517,9 @@ export default {
|
|||||||
return '自动同步组织';
|
return '自动同步组织';
|
||||||
},
|
},
|
||||||
oaSyncStageLabel() {
|
oaSyncStageLabel() {
|
||||||
|
if (this.oaSyncStage === 'clear') {
|
||||||
|
return '清除非顶级组织';
|
||||||
|
}
|
||||||
return this.oaSyncStage === 'department' ? '同步部门' : '同步公司';
|
return this.oaSyncStage === 'department' ? '同步部门' : '同步公司';
|
||||||
},
|
},
|
||||||
oaSyncTotalPage() {
|
oaSyncTotalPage() {
|
||||||
@@ -555,21 +555,34 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleOaOrgSync() {
|
handleOaOrgSync() {
|
||||||
this.$confirm('确定从OA先同步公司、再同步部门?', '提示', {
|
this.$confirm(
|
||||||
confirmButtonText: '确定',
|
'是否清除非顶级组织?选择“是”将先删除顶级以外的组织后再同步;选择“否”则直接同步。',
|
||||||
cancelButtonText: '取消',
|
'提示',
|
||||||
type: 'warning',
|
{
|
||||||
}).then(() => {
|
confirmButtonText: '是',
|
||||||
this.startOaOrgSync();
|
cancelButtonText: '否',
|
||||||
});
|
distinguishCancelAndClose: true,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.startOaOrgSync(true);
|
||||||
|
})
|
||||||
|
.catch(action => {
|
||||||
|
if (action === 'cancel') {
|
||||||
|
this.startOaOrgSync(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
startOaOrgSync() {
|
startOaOrgSync(clearNonTop) {
|
||||||
this.oaSyncLoading = true;
|
this.oaSyncLoading = true;
|
||||||
this.oaSyncVisible = true;
|
this.oaSyncVisible = true;
|
||||||
this.oaSyncRunning = true;
|
this.oaSyncRunning = true;
|
||||||
this.oaSyncCancelled = false;
|
this.oaSyncCancelled = false;
|
||||||
this.oaSyncStatus = 'running';
|
this.oaSyncStatus = 'running';
|
||||||
this.oaSyncStage = 'company';
|
this.oaSyncClearNonTop = !!clearNonTop;
|
||||||
|
this.oaSyncStage = clearNonTop ? 'clear' : 'company';
|
||||||
this.oaSyncController = new AbortController();
|
this.oaSyncController = new AbortController();
|
||||||
this.oaSyncProgress = {
|
this.oaSyncProgress = {
|
||||||
current: 0,
|
current: 0,
|
||||||
@@ -607,6 +620,14 @@ export default {
|
|||||||
},
|
},
|
||||||
async runOaOrgSyncPages() {
|
async runOaOrgSyncPages() {
|
||||||
try {
|
try {
|
||||||
|
if (this.oaSyncClearNonTop) {
|
||||||
|
this.oaSyncStage = 'clear';
|
||||||
|
await clearNonTopDept(this.oaSyncController?.signal);
|
||||||
|
if (this.oaSyncCancelled) {
|
||||||
|
this.oaSyncStatus = 'cancelled';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
this.oaSyncStage = 'company';
|
this.oaSyncStage = 'company';
|
||||||
await this.runOaOrgSyncStage(syncOaCompany);
|
await this.runOaOrgSyncStage(syncOaCompany);
|
||||||
if (this.oaSyncCancelled) {
|
if (this.oaSyncCancelled) {
|
||||||
@@ -821,6 +842,11 @@ export default {
|
|||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
return result.length ? result.join('、') : '-';
|
return result.length ? result.join('、') : '-';
|
||||||
},
|
},
|
||||||
|
normalizeTopParent(row) {
|
||||||
|
if (!this.normalizeParentId(row?.parentId)) {
|
||||||
|
row.parentId = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
isRootDept(row) {
|
isRootDept(row) {
|
||||||
return row && (row.parentId === 0 || String(row.parentId) === '0');
|
return row && (row.parentId === 0 || String(row.parentId) === '0');
|
||||||
},
|
},
|
||||||
@@ -836,6 +862,7 @@ export default {
|
|||||||
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
|
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
|
||||||
row.deptCode = String(row.deptCode || '').trim();
|
row.deptCode = String(row.deptCode || '').trim();
|
||||||
row.leaderId = func.join(row.leaderId);
|
row.leaderId = func.join(row.leaderId);
|
||||||
|
this.normalizeTopParent(row);
|
||||||
if (Number(row.deptCategory) === 6 && !row.carrierCustomerId) {
|
if (Number(row.deptCategory) === 6 && !row.carrierCustomerId) {
|
||||||
this.$message.warning('请选择承运商');
|
this.$message.warning('请选择承运商');
|
||||||
loading();
|
loading();
|
||||||
@@ -865,6 +892,7 @@ export default {
|
|||||||
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
|
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
|
||||||
row.deptCode = String(row.deptCode || '').trim();
|
row.deptCode = String(row.deptCode || '').trim();
|
||||||
row.leaderId = func.join(row.leaderId);
|
row.leaderId = func.join(row.leaderId);
|
||||||
|
this.normalizeTopParent(row);
|
||||||
if (Number(row.deptCategory) === 6 && !row.carrierCustomerId) {
|
if (Number(row.deptCategory) === 6 && !row.carrierCustomerId) {
|
||||||
this.$message.warning('请选择承运商');
|
this.$message.warning('请选择承运商');
|
||||||
loading();
|
loading();
|
||||||
|
|||||||
@@ -205,7 +205,7 @@
|
|||||||
type="password"
|
type="password"
|
||||||
show-password
|
show-password
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
placeholder="请输入登录密码"
|
placeholder="大于8位,含字母、数字、特殊字符.!@#$%^&*"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -332,7 +332,12 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="新密码" prop="password">
|
<el-form-item label="新密码" prop="password">
|
||||||
<el-input v-model="passwordForm.password" type="password" show-password />
|
<el-input
|
||||||
|
v-model="passwordForm.password"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
placeholder="大于8位,含字母、数字、特殊字符.!@#$%^&*"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
@@ -424,6 +429,7 @@ import { mapGetters } from 'vuex';
|
|||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
import { getToken } from '@/utils/auth';
|
import { getToken } from '@/utils/auth';
|
||||||
import { downloadXls } from '@/utils/util';
|
import { downloadXls } from '@/utils/util';
|
||||||
|
import { validateLoginPassword } from '@/utils/validate';
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import func from '@/utils/func';
|
import func from '@/utils/func';
|
||||||
@@ -487,8 +493,7 @@ export default {
|
|||||||
passwordForm: {},
|
passwordForm: {},
|
||||||
passwordRules: {
|
passwordRules: {
|
||||||
password: [
|
password: [
|
||||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
{ required: true, validator: validateLoginPassword, trigger: 'blur' },
|
||||||
{ min: 6, max: 32, message: '密码长度在6到32个字符', trigger: 'blur' },
|
|
||||||
],
|
],
|
||||||
password2: [
|
password2: [
|
||||||
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
|
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
|
||||||
@@ -497,6 +502,7 @@ export default {
|
|||||||
},
|
},
|
||||||
userRules: {
|
userRules: {
|
||||||
account: [{ required: true, message: '请输入账号名', trigger: 'blur' }],
|
account: [{ required: true, message: '请输入账号名', trigger: 'blur' }],
|
||||||
|
password: [{ required: true, validator: validateLoginPassword, trigger: 'blur' }],
|
||||||
realName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
realName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||||
phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
|
phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
|
||||||
deptId: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
|
deptId: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="passwordForm.newPassword"
|
v-model="passwordForm.newPassword"
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="请输入新密码"
|
placeholder="大于8位,含字母、数字、特殊字符.!@#$%^&*"
|
||||||
show-password
|
show-password
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
@@ -143,6 +143,7 @@
|
|||||||
import { getUserInfo, updateInfo, updatePassword } from '@/api/system/user';
|
import { getUserInfo, updateInfo, updatePassword } from '@/api/system/user';
|
||||||
import md5 from 'js-md5';
|
import md5 from 'js-md5';
|
||||||
import { sensitive } from '@/utils/sensitive';
|
import { sensitive } from '@/utils/sensitive';
|
||||||
|
import { isValidLoginPassword, LOGIN_PASSWORD_RULE_MESSAGE } from '@/utils/validate';
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
Bell,
|
Bell,
|
||||||
@@ -259,6 +260,18 @@ export default {
|
|||||||
},
|
},
|
||||||
// 提交密码修改
|
// 提交密码修改
|
||||||
submitPassword() {
|
submitPassword() {
|
||||||
|
if (!this.passwordForm.newPassword || !this.passwordForm.newPassword1) {
|
||||||
|
this.$message.warning('请输入新密码和确认密码');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.passwordForm.newPassword !== this.passwordForm.newPassword1) {
|
||||||
|
this.$message.warning('两次输入的新密码不一致');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isValidLoginPassword(this.passwordForm.newPassword)) {
|
||||||
|
this.$message.warning(LOGIN_PASSWORD_RULE_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
updatePassword(
|
updatePassword(
|
||||||
md5(this.passwordForm.oldPassword),
|
md5(this.passwordForm.oldPassword),
|
||||||
|
|||||||
@@ -129,7 +129,7 @@
|
|||||||
>
|
>
|
||||||
<el-form
|
<el-form
|
||||||
label-position="right"
|
label-position="right"
|
||||||
label-width="calc(7em + 24px)"
|
label-width="calc(4em + 12px)"
|
||||||
class="certification-audit archive-form"
|
class="certification-audit archive-form"
|
||||||
>
|
>
|
||||||
<section-card title="基础信息">
|
<section-card title="基础信息">
|
||||||
@@ -145,19 +145,25 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="外廓尺寸(毫米)">
|
<el-form-item label="外廓尺寸">
|
||||||
<div class="dimension-group">
|
<div class="dimension-group">
|
||||||
<div class="dimension-group__item">
|
<div class="dimension-group__item">
|
||||||
<span class="dimension-group__label">长</span>
|
<span class="dimension-group__label">长</span>
|
||||||
<span class="certification-audit__value">{{ auditValue('outerLength') }}</span>
|
<span class="certification-audit__value">
|
||||||
|
{{ auditValueWithUnit('outerLength', 'mm') }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dimension-group__item">
|
<div class="dimension-group__item">
|
||||||
<span class="dimension-group__label">宽</span>
|
<span class="dimension-group__label">宽</span>
|
||||||
<span class="certification-audit__value">{{ auditValue('outerWidth') }}</span>
|
<span class="certification-audit__value">
|
||||||
|
{{ auditValueWithUnit('outerWidth', 'mm') }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dimension-group__item">
|
<div class="dimension-group__item">
|
||||||
<span class="dimension-group__label">高</span>
|
<span class="dimension-group__label">高</span>
|
||||||
<span class="certification-audit__value">{{ auditValue('outerHeight') }}</span>
|
<span class="certification-audit__value">
|
||||||
|
{{ auditValueWithUnit('outerHeight', 'mm') }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -165,13 +171,17 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="18">
|
<el-row :gutter="18">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="核定载质量(KG)">
|
<el-form-item label="核定载质量">
|
||||||
<span class="certification-audit__value">{{ auditValue('approvedLoadKg') }}</span>
|
<span class="certification-audit__value">
|
||||||
|
{{ auditValueWithUnit('approvedLoadKg', 'KG') }}
|
||||||
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="准牵引总质量(KG)">
|
<el-form-item label="准牵引总质量">
|
||||||
<span class="certification-audit__value">{{ auditValue('tractionMassKg') }}</span>
|
<span class="certification-audit__value">
|
||||||
|
{{ auditValueWithUnit('tractionMassKg', 'KG') }}
|
||||||
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
@@ -357,7 +367,7 @@
|
|||||||
v-if="showRejectReason"
|
v-if="showRejectReason"
|
||||||
class="certification-audit__reason"
|
class="certification-audit__reason"
|
||||||
label-position="right"
|
label-position="right"
|
||||||
label-width="calc(7em + 24px)"
|
label-width="calc(4em + 24px)"
|
||||||
>
|
>
|
||||||
<el-form-item label="驳回原因" required>
|
<el-form-item label="驳回原因" required>
|
||||||
<el-input
|
<el-input
|
||||||
@@ -427,7 +437,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="外廓尺寸(毫米)">
|
<el-form-item label="外廓尺寸">
|
||||||
<div class="dimension-group">
|
<div class="dimension-group">
|
||||||
<div class="dimension-group__item">
|
<div class="dimension-group__item">
|
||||||
<span class="dimension-group__label">长</span>
|
<span class="dimension-group__label">长</span>
|
||||||
@@ -436,7 +446,9 @@
|
|||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@input="vehicleForm.outerLength = digitsOnly($event)"
|
@input="vehicleForm.outerLength = digitsOnly($event)"
|
||||||
/>
|
>
|
||||||
|
<template #suffix>mm</template>
|
||||||
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="dimension-group__item">
|
<div class="dimension-group__item">
|
||||||
<span class="dimension-group__label">宽</span>
|
<span class="dimension-group__label">宽</span>
|
||||||
@@ -445,7 +457,9 @@
|
|||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@input="vehicleForm.outerWidth = digitsOnly($event)"
|
@input="vehicleForm.outerWidth = digitsOnly($event)"
|
||||||
/>
|
>
|
||||||
|
<template #suffix>mm</template>
|
||||||
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="dimension-group__item">
|
<div class="dimension-group__item">
|
||||||
<span class="dimension-group__label">高</span>
|
<span class="dimension-group__label">高</span>
|
||||||
@@ -454,7 +468,9 @@
|
|||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@input="vehicleForm.outerHeight = digitsOnly($event)"
|
@input="vehicleForm.outerHeight = digitsOnly($event)"
|
||||||
/>
|
>
|
||||||
|
<template #suffix>mm</template>
|
||||||
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -462,23 +478,27 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="18">
|
<el-row :gutter="18">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="核定载质量(KG)" prop="approvedLoadKg">
|
<el-form-item label="核定载质量" prop="approvedLoadKg">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="vehicleForm.approvedLoadKg"
|
v-model="vehicleForm.approvedLoadKg"
|
||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@input="vehicleForm.approvedLoadKg = digitsOnly($event)"
|
@input="vehicleForm.approvedLoadKg = digitsOnly($event)"
|
||||||
/>
|
>
|
||||||
|
<template #suffix>KG</template>
|
||||||
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="准牵引总质量(KG)" prop="tractionMassKg">
|
<el-form-item label="准牵引总质量" prop="tractionMassKg">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="vehicleForm.tractionMassKg"
|
v-model="vehicleForm.tractionMassKg"
|
||||||
inputmode="numeric"
|
inputmode="numeric"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@input="vehicleForm.tractionMassKg = digitsOnly($event)"
|
@input="vehicleForm.tractionMassKg = digitsOnly($event)"
|
||||||
/>
|
>
|
||||||
|
<template #suffix>KG</template>
|
||||||
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
@@ -896,6 +916,7 @@ export default {
|
|||||||
energyTypeOptions: ['柴油', '汽油', '新能源', '天然气'],
|
energyTypeOptions: ['柴油', '汽油', '新能源', '天然气'],
|
||||||
baseRules: {
|
baseRules: {
|
||||||
organizationName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
|
organizationName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
|
||||||
|
useDepartment: [{ required: true, message: '请选择使用部门', trigger: 'change' }],
|
||||||
plateNo: [
|
plateNo: [
|
||||||
{ required: true, message: '请输入车牌号', trigger: 'change' },
|
{ required: true, message: '请输入车牌号', trigger: 'change' },
|
||||||
{ validator: this.validatePlateNo, trigger: 'change' },
|
{ validator: this.validatePlateNo, trigger: 'change' },
|
||||||
@@ -986,6 +1007,9 @@ export default {
|
|||||||
if (this.vehicleBox && !this.vehicleForm.id && !this.vehicleForm.organizationName) {
|
if (this.vehicleBox && !this.vehicleForm.id && !this.vehicleForm.organizationName) {
|
||||||
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
|
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
|
||||||
}
|
}
|
||||||
|
if (this.vehicleBox && !this.vehicleForm.useDepartment) {
|
||||||
|
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
flattenDeptOptions(tree = [], level = 0) {
|
flattenDeptOptions(tree = [], level = 0) {
|
||||||
@@ -1061,6 +1085,7 @@ export default {
|
|||||||
if (!row?.id) {
|
if (!row?.id) {
|
||||||
this.vehicleForm = emptyForm();
|
this.vehicleForm = emptyForm();
|
||||||
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
|
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
|
||||||
|
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
|
||||||
this.syncPlateNo(false);
|
this.syncPlateNo(false);
|
||||||
this.vehicleBox = true;
|
this.vehicleBox = true;
|
||||||
return;
|
return;
|
||||||
@@ -1070,6 +1095,9 @@ export default {
|
|||||||
...emptyForm(),
|
...emptyForm(),
|
||||||
...(res.data.data || {}),
|
...(res.data.data || {}),
|
||||||
};
|
};
|
||||||
|
if (!this.vehicleForm.useDepartment) {
|
||||||
|
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
|
||||||
|
}
|
||||||
this.splitPlateNo();
|
this.splitPlateNo();
|
||||||
this.syncPlateNo(false);
|
this.syncPlateNo(false);
|
||||||
this.vehicleBox = true;
|
this.vehicleBox = true;
|
||||||
@@ -1102,6 +1130,10 @@ export default {
|
|||||||
const value = this.certificationAuditForm[prop];
|
const value = this.certificationAuditForm[prop];
|
||||||
return value === undefined || value === null || value === '' ? '-' : value;
|
return value === undefined || value === null || value === '' ? '-' : value;
|
||||||
},
|
},
|
||||||
|
auditValueWithUnit(prop, unit) {
|
||||||
|
const value = this.auditValue(prop);
|
||||||
|
return value === '-' ? value : `${value}${unit}`;
|
||||||
|
},
|
||||||
handleCertificationApprove() {
|
handleCertificationApprove() {
|
||||||
auditCertification(this.certificationAuditForm.id, 1).then(() => {
|
auditCertification(this.certificationAuditForm.id, 1).then(() => {
|
||||||
this.$message.success('认证已通过');
|
this.$message.success('认证已通过');
|
||||||
@@ -1693,11 +1725,19 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.certification-audit,
|
.certification-audit {
|
||||||
|
:deep(.el-form-item__label) {
|
||||||
|
flex: 0 0 calc(4em + 12px) !important;
|
||||||
|
width: calc(4em + 12px) !important;
|
||||||
|
max-width: calc(4em + 12px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.certification-audit__reason {
|
.certification-audit__reason {
|
||||||
:deep(.el-form-item__label) {
|
:deep(.el-form-item__label) {
|
||||||
flex: 0 0 calc(7em + 24px) !important;
|
flex: 0 0 calc(4em + 24px) !important;
|
||||||
width: calc(7em + 24px) !important;
|
width: calc(4em + 24px) !important;
|
||||||
|
max-width: calc(4em + 24px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -144,9 +144,24 @@
|
|||||||
>
|
>
|
||||||
撤回
|
撤回
|
||||||
</el-link>
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
v-if="
|
||||||
|
hasPermission('customer_type_re_cert') &&
|
||||||
|
row.approvalStatus === 'reviewing' &&
|
||||||
|
row.reviewStatus !== '已完成'
|
||||||
|
"
|
||||||
|
@click="openReview(row)"
|
||||||
|
>
|
||||||
|
复评
|
||||||
|
</el-link>
|
||||||
<el-link
|
<el-link
|
||||||
type="success"
|
type="success"
|
||||||
v-if="hasPermission('customer_archive_approve') && row.approvalStatus === 'reviewing'"
|
v-if="
|
||||||
|
hasPermission('customer_archive_approve') &&
|
||||||
|
row.approvalStatus === 'reviewing' &&
|
||||||
|
row.reviewStatus === '已完成'
|
||||||
|
"
|
||||||
@click="handleApprove(row)"
|
@click="handleApprove(row)"
|
||||||
>
|
>
|
||||||
审核通过
|
审核通过
|
||||||
@@ -517,7 +532,7 @@
|
|||||||
<span>{{ formatScoreValue(row.selfScore) }}</span>
|
<span>{{ formatScoreValue(row.selfScore) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="复评得分" min-width="145" align="center">
|
<el-table-column v-if="archiveForm.id" label="复评得分" min-width="145" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span>{{ formatScoreValue(row.reviewScore) }}</span>
|
<span>{{ formatScoreValue(row.reviewScore) }}</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -553,11 +568,18 @@
|
|||||||
<span>{{ row.reviewStatus }}</span>
|
<span>{{ row.reviewStatus }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="135" fixed="right" align="center">
|
<el-table-column label="操作" width="160" fixed="right" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link type="primary" @click="openScore(row.__raw, row.__rawIndex)">
|
<el-link type="primary" @click="openScore(row.__raw, row.__rawIndex)">
|
||||||
详情
|
详情
|
||||||
</el-link>
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="showScoreDelete"
|
||||||
|
type="danger"
|
||||||
|
@click="deleteScore(row.__rawIndex)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -624,6 +646,7 @@
|
|||||||
<el-table-column label="开户人姓名" prop="accountHolderName" min-width="150" />
|
<el-table-column label="开户人姓名" prop="accountHolderName" min-width="150" />
|
||||||
<el-table-column label="收款账号" prop="bankAccount" min-width="180" />
|
<el-table-column label="收款账号" prop="bankAccount" min-width="180" />
|
||||||
<el-table-column label="开户行" prop="bankName" min-width="180" />
|
<el-table-column label="开户行" prop="bankName" min-width="180" />
|
||||||
|
<el-table-column label="联行号" prop="cnapsCode" min-width="160" />
|
||||||
<el-table-column label="备注" prop="remark" min-width="180" />
|
<el-table-column label="备注" prop="remark" min-width="180" />
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="操作"
|
label="操作"
|
||||||
@@ -875,43 +898,10 @@
|
|||||||
/>
|
/>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<el-dialog
|
<change-record-detail-dialog
|
||||||
v-model="changeRecordDetailVisible"
|
v-model="changeRecordDetailVisible"
|
||||||
title="变更记录详情"
|
:rows="changeRecordDetailRows"
|
||||||
append-to-body
|
/>
|
||||||
destroy-on-close
|
|
||||||
width="1100px"
|
|
||||||
top="10px"
|
|
||||||
class="change-record-detail-dialog"
|
|
||||||
>
|
|
||||||
<div v-if="changeRecordDetail" class="change-record-detail-meta">
|
|
||||||
<span>变更日期:{{ changeRecordDetail.changeTime || '-' }}</span>
|
|
||||||
<span>变更账号:{{ changeRecordDetail.changeUserName || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
<el-table :data="changeRecordDetailRows" border :show-overflow-tooltip="false">
|
|
||||||
<el-table-column prop="field" label="变更字段" min-width="180" />
|
|
||||||
<el-table-column
|
|
||||||
prop="before"
|
|
||||||
label="变更前"
|
|
||||||
min-width="360"
|
|
||||||
class-name="change-record-detail-value"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
prop="after"
|
|
||||||
label="变更后"
|
|
||||||
min-width="500"
|
|
||||||
class-name="change-record-detail-value"
|
|
||||||
/>
|
|
||||||
</el-table>
|
|
||||||
<el-empty
|
|
||||||
v-if="!changeRecordDetailRows.length"
|
|
||||||
description="暂无变更内容"
|
|
||||||
:image-size="60"
|
|
||||||
/>
|
|
||||||
<template #footer>
|
|
||||||
<el-button type="primary" @click="changeRecordDetailVisible = false">关闭</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<div class="archive-form__footer" v-if="!isPublicViewPage">
|
<div class="archive-form__footer" v-if="!isPublicViewPage">
|
||||||
<!-- 次要:独立页返回 / 只读关闭 / 弹窗取消 -->
|
<!-- 次要:独立页返回 / 只读关闭 / 弹窗取消 -->
|
||||||
@@ -1022,6 +1012,9 @@
|
|||||||
<el-form-item label="开户行" prop="bankName">
|
<el-form-item label="开户行" prop="bankName">
|
||||||
<el-input v-model="receiptForm.bankName" maxlength="100" />
|
<el-input v-model="receiptForm.bankName" maxlength="100" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="联行号" prop="cnapsCode">
|
||||||
|
<el-input v-model="receiptForm.cnapsCode" maxlength="32" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="收款账号" prop="bankAccount">
|
<el-form-item label="收款账号" prop="bankAccount">
|
||||||
<el-input v-model="receiptForm.bankAccount" maxlength="50" />
|
<el-input v-model="receiptForm.bankAccount" maxlength="50" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -1319,7 +1312,25 @@
|
|||||||
class="score-detail-table score-detail-table--inline"
|
class="score-detail-table score-detail-table--inline"
|
||||||
max-height="520"
|
max-height="520"
|
||||||
>
|
>
|
||||||
<el-table-column label="评分项目" prop="itemName" width="150" align="center" />
|
<el-table-column
|
||||||
|
label="评分项目"
|
||||||
|
width="150"
|
||||||
|
align="center"
|
||||||
|
:show-overflow-tooltip="false"
|
||||||
|
>
|
||||||
|
<template #default="{ row: detail }">
|
||||||
|
<el-tooltip
|
||||||
|
:content="detail.itemName"
|
||||||
|
placement="top"
|
||||||
|
effect="dark"
|
||||||
|
append-to="body"
|
||||||
|
:show-after="200"
|
||||||
|
:disabled="!detail.itemName"
|
||||||
|
>
|
||||||
|
<span class="score-item-name">{{ detail.itemName }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="评分标准" min-width="430">
|
<el-table-column label="评分标准" min-width="430">
|
||||||
<template #default="{ row: detail }">
|
<template #default="{ row: detail }">
|
||||||
<div class="score-standard-cell">
|
<div class="score-standard-cell">
|
||||||
@@ -1329,6 +1340,7 @@
|
|||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
:disabled="readonly"
|
:disabled="readonly"
|
||||||
@input="value => handleScoreValueInput(detail, value)"
|
@input="value => handleScoreValueInput(detail, value)"
|
||||||
|
@blur="handleScoreValueBlur(detail)"
|
||||||
>
|
>
|
||||||
<template #append>{{ getScoreRule(detail)?.changeUnit || '' }}</template>
|
<template #append>{{ getScoreRule(detail)?.changeUnit || '' }}</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
@@ -1358,13 +1370,15 @@
|
|||||||
<span>{{ formatScoreValue(getSignedScore(detail, 'selfScore')) }}分</span>
|
<span>{{ formatScoreValue(getSignedScore(detail, 'selfScore')) }}分</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="复评得分" width="150" align="center">
|
<el-table-column v-if="!isNewScore" label="复评得分" width="150" align="center">
|
||||||
<template #default="{ row: detail }">
|
<template #default="{ row: detail }">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="detail.reviewScore"
|
v-model="detail.reviewScore"
|
||||||
maxlength="10"
|
maxlength="10"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
:disabled="readonly || !hasPermission('customer_type_re_cert')"
|
:disabled="
|
||||||
|
(!reviewMode && readonly) || !hasPermission('customer_type_re_cert')
|
||||||
|
"
|
||||||
@input="value => handleScoreDetailScoreInput(detail, 'reviewScore', value)"
|
@input="value => handleScoreDetailScoreInput(detail, 'reviewScore', value)"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
@@ -1385,7 +1399,7 @@
|
|||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="score-category-table__name">{{ row.categoryName }}</span>
|
<span class="score-category-table__name">{{ row.categoryName }}</span>
|
||||||
<span>自评:{{ getSignedScore(row, 'selfScore') }}分</span>
|
<span>自评:{{ getSignedScore(row, 'selfScore') }}分</span>
|
||||||
<span>复评:{{ getSignedScore(row, 'reviewScore') }}分</span>
|
<span v-if="!isNewScore">复评:{{ getSignedScore(row, 'reviewScore') }}分</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column width="60" align="center">
|
<el-table-column width="60" align="center">
|
||||||
@@ -1459,12 +1473,15 @@
|
|||||||
<div class="score-detail-dialog__total">
|
<div class="score-detail-dialog__total">
|
||||||
<span>总分合计</span>
|
<span>总分合计</span>
|
||||||
<span>自评:{{ formatScoreValue(currentScore.selfScore) }}分</span>
|
<span>自评:{{ formatScoreValue(currentScore.selfScore) }}分</span>
|
||||||
<span>复评:{{ formatScoreValue(currentScore.reviewScore) }}分</span>
|
<span v-if="!isNewScore">复评:{{ formatScoreValue(currentScore.reviewScore) }}分</span>
|
||||||
</div>
|
</div>
|
||||||
<el-button @click="scoreBox = false">取消</el-button>
|
<el-button @click="scoreBox = false">取消</el-button>
|
||||||
<el-button type="primary" plain v-if="!readonly" @click="saveScoreDetail">保存</el-button>
|
<el-button v-if="isNewScore" type="primary" @click="submitSelfScore">提交自评</el-button>
|
||||||
|
<el-button type="primary" plain v-if="!readonly && !isNewScore" @click="saveScoreDetail">
|
||||||
|
保存
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="!readonly && hasPermission('customer_type_re_cert')"
|
v-if="!isNewScore && (!readonly || reviewMode) && hasPermission('customer_type_re_cert')"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="confirmReviewScore"
|
@click="confirmReviewScore"
|
||||||
>
|
>
|
||||||
@@ -1500,7 +1517,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 { processDelete, processSubmit } from '@/api/system/business-process';
|
import { submitMkApprovalFlow } from '@/utils/mk-approval';
|
||||||
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';
|
||||||
@@ -1519,6 +1536,7 @@ import NProgress from 'nprogress';
|
|||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import SectionCard from '@/components/section-card/main.vue';
|
import SectionCard from '@/components/section-card/main.vue';
|
||||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||||
|
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
|
||||||
|
|
||||||
const createTimeRangeMap = {
|
const createTimeRangeMap = {
|
||||||
createTimeRange: ['createTimeStart', 'createTimeEnd'],
|
createTimeRange: ['createTimeStart', 'createTimeEnd'],
|
||||||
@@ -1534,11 +1552,18 @@ const qualificationViewerPlugins = [
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SectionCard,
|
SectionCard,
|
||||||
|
ChangeRecordDetailDialog,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
ElImageViewer,
|
ElImageViewer,
|
||||||
OpenFileViewer,
|
OpenFileViewer,
|
||||||
PdfPreview,
|
PdfPreview,
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
embeddedPublicId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
const nonNegativeLabelMap = {
|
const nonNegativeLabelMap = {
|
||||||
invoiceTaxRate: '开票税点',
|
invoiceTaxRate: '开票税点',
|
||||||
@@ -1621,6 +1646,8 @@ export default {
|
|||||||
receiptBox: false,
|
receiptBox: false,
|
||||||
invoiceBox: false,
|
invoiceBox: false,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
|
reviewMode: false,
|
||||||
|
listActivated: false,
|
||||||
originalAccessType: '',
|
originalAccessType: '',
|
||||||
activeTab: 'scores',
|
activeTab: 'scores',
|
||||||
archiveForm: this.emptyArchive(),
|
archiveForm: this.emptyArchive(),
|
||||||
@@ -1779,6 +1806,7 @@ export default {
|
|||||||
accountName: [{ required: true, message: '请输入收款单位名称', trigger: 'blur' }],
|
accountName: [{ required: true, message: '请输入收款单位名称', trigger: 'blur' }],
|
||||||
accountHolderName: [{ required: true, message: '请输入开户人姓名', trigger: 'blur' }],
|
accountHolderName: [{ required: true, message: '请输入开户人姓名', trigger: 'blur' }],
|
||||||
bankName: [{ required: true, message: '请输入开户行', trigger: 'blur' }],
|
bankName: [{ required: true, message: '请输入开户行', trigger: 'blur' }],
|
||||||
|
cnapsCode: [{ required: true, message: '请输入联行号', trigger: 'blur' }],
|
||||||
bankAccount: [{ required: true, message: '请输入收款账号', trigger: 'blur' }],
|
bankAccount: [{ required: true, message: '请输入收款账号', trigger: 'blur' }],
|
||||||
remark: [{ max: 200, message: '备注最多200个字符', trigger: 'blur' }],
|
remark: [{ max: 200, message: '备注最多200个字符', trigger: 'blur' }],
|
||||||
},
|
},
|
||||||
@@ -1814,7 +1842,7 @@ export default {
|
|||||||
editBtn: false,
|
editBtn: false,
|
||||||
selection: true,
|
selection: true,
|
||||||
dialogClickModal: false,
|
dialogClickModal: false,
|
||||||
menuWidth: 280,
|
menuWidth: 320,
|
||||||
column: [
|
column: [
|
||||||
{
|
{
|
||||||
label: '客商编号',
|
label: '客商编号',
|
||||||
@@ -1994,7 +2022,7 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
if (this.isPublicViewPage) {
|
if (this.isPublicViewPage) {
|
||||||
this.readonly = true;
|
this.readonly = true;
|
||||||
const id = this.$route.query.id;
|
const id = this.embeddedPublicId || this.$route.query.id;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
this.archiveBox = true;
|
this.archiveBox = true;
|
||||||
this.$message.error('缺少客商ID');
|
this.$message.error('缺少客商ID');
|
||||||
@@ -2008,10 +2036,20 @@ export default {
|
|||||||
this.initBusinessDictionaries();
|
this.initBusinessDictionaries();
|
||||||
this.initScoreQuantificationOptions();
|
this.initScoreQuantificationOptions();
|
||||||
if (this.isArchivePage) {
|
if (this.isArchivePage) {
|
||||||
const readonly = this.$route.query.view === '1' || this.$route.query.view === 'true';
|
this.reviewMode = this.$route.query.review === '1';
|
||||||
|
const readonly =
|
||||||
|
this.$route.query.view === '1' || this.$route.query.view === 'true' || this.reviewMode;
|
||||||
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);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
activated() {
|
||||||
|
if (this.isArchivePage || this.isPublicViewPage) return;
|
||||||
|
if (!this.listActivated) {
|
||||||
|
this.listActivated = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.onLoad(this.page, this.query);
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['permission', 'userInfo']),
|
...mapGetters(['permission', 'userInfo']),
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
@@ -2029,7 +2067,9 @@ export default {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
isPublicViewPage() {
|
isPublicViewPage() {
|
||||||
return this.$route.path === '/vehicle/customer-archive/public-view';
|
return (
|
||||||
|
this.$route.path === '/vehicle/customer-archive/public-view' || !!this.embeddedPublicId
|
||||||
|
);
|
||||||
},
|
},
|
||||||
archiveContainer() {
|
archiveContainer() {
|
||||||
return 'div';
|
return 'div';
|
||||||
@@ -2095,6 +2135,7 @@ export default {
|
|||||||
return ids.join(',');
|
return ids.join(',');
|
||||||
},
|
},
|
||||||
dialogTitle() {
|
dialogTitle() {
|
||||||
|
if (this.reviewMode) return '客商复评';
|
||||||
if (this.readonly) return '查看客商档案';
|
if (this.readonly) return '查看客商档案';
|
||||||
return this.archiveForm.id ? '编辑客商档案' : '新增客商档案';
|
return this.archiveForm.id ? '编辑客商档案' : '新增客商档案';
|
||||||
},
|
},
|
||||||
@@ -2138,6 +2179,14 @@ export default {
|
|||||||
scoreList() {
|
scoreList() {
|
||||||
return this.archiveForm.scores || [];
|
return this.archiveForm.scores || [];
|
||||||
},
|
},
|
||||||
|
showScoreDelete() {
|
||||||
|
if (this.readonly) return false;
|
||||||
|
const status = this.archiveForm.approvalStatus || 'draft';
|
||||||
|
return status === 'draft' || status === 'rejected';
|
||||||
|
},
|
||||||
|
isNewScore() {
|
||||||
|
return this.scoreRecordIndex < 0 && !this.reviewMode;
|
||||||
|
},
|
||||||
scorePageCount() {
|
scorePageCount() {
|
||||||
return Math.max(Math.ceil(this.scoreList.length / this.scorePage.pageSize), 1);
|
return Math.max(Math.ceil(this.scoreList.length / this.scorePage.pageSize), 1);
|
||||||
},
|
},
|
||||||
@@ -2695,6 +2744,7 @@ export default {
|
|||||||
accountName: '',
|
accountName: '',
|
||||||
accountHolderName: '',
|
accountHolderName: '',
|
||||||
bankName: '',
|
bankName: '',
|
||||||
|
cnapsCode: '',
|
||||||
bankAccount: '',
|
bankAccount: '',
|
||||||
registeredPhone: '',
|
registeredPhone: '',
|
||||||
registeredAddress: '',
|
registeredAddress: '',
|
||||||
@@ -3948,6 +3998,12 @@ export default {
|
|||||||
query: { id: row.id, name: '查看客商档案', view: '1' },
|
query: { id: row.id, name: '查看客商档案', view: '1' },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
openReview(row) {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/vehicle/customer-archive/form',
|
||||||
|
query: { id: row.id, name: '客商复评', review: '1' },
|
||||||
|
});
|
||||||
|
},
|
||||||
closeArchive() {
|
closeArchive() {
|
||||||
if (this.isPublicViewPage) return;
|
if (this.isPublicViewPage) return;
|
||||||
this.archiveBox = false;
|
this.archiveBox = false;
|
||||||
@@ -3979,6 +4035,7 @@ export default {
|
|||||||
if (this.isPublicViewPage) this.ensurePublicViewOptions(this.archiveForm);
|
if (this.isPublicViewPage) this.ensurePublicViewOptions(this.archiveForm);
|
||||||
this.archiveBox = true;
|
this.archiveBox = true;
|
||||||
this.loadChangeRecords();
|
this.loadChangeRecords();
|
||||||
|
if (this.reviewMode) this.openReviewScore();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.archiveBox = true;
|
this.archiveBox = true;
|
||||||
@@ -4020,14 +4077,15 @@ export default {
|
|||||||
this.invoiceForm = this.emptyInvoice();
|
this.invoiceForm = this.emptyInvoice();
|
||||||
this.$refs.archiveForm?.clearValidate();
|
this.$refs.archiveForm?.clearValidate();
|
||||||
},
|
},
|
||||||
// 客商材料不做提交校验,仅在页面上提示未上传项
|
// 客商材料不做提交校验,仅在页面上提示未上传项。提交审批前必须完成自评,且评分记录只能有一条。
|
||||||
validateFormalForApproval(archive) {
|
validateScoreForApproval(archive) {
|
||||||
if (archive.accessType !== 'formal') return true;
|
const scores = archive.scores || [];
|
||||||
const hasCompletedScore = (archive.scores || []).some(
|
if (scores.length > 1) {
|
||||||
item => item.selfStatus === '已完成' || item.reviewStatus === '已完成' || item.finalScore
|
this.$message.warning('评分记录只能有一条');
|
||||||
);
|
return false;
|
||||||
if (!hasCompletedScore) {
|
}
|
||||||
this.$message.warning('正式客商提交审核前必须完成信用评分');
|
if (scores.length !== 1 || scores[0].selfStatus !== '已完成') {
|
||||||
|
this.$message.warning('提交审批前必须完成自评');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -4079,7 +4137,7 @@ export default {
|
|||||||
.filter(item => item.contactName || item.contactPhone);
|
.filter(item => item.contactName || item.contactPhone);
|
||||||
archive.receiptAccounts = (archive.receiptAccounts || [])
|
archive.receiptAccounts = (archive.receiptAccounts || [])
|
||||||
.map(item => this.normalizeReceipt(item))
|
.map(item => this.normalizeReceipt(item))
|
||||||
.filter(item => item.accountName || item.accountHolderName || item.bankAccount);
|
.filter(item => item.accountName || item.accountHolderName || item.bankAccount || item.cnapsCode);
|
||||||
archive.invoices = (archive.invoices || [])
|
archive.invoices = (archive.invoices || [])
|
||||||
.map(item => {
|
.map(item => {
|
||||||
const invoice = this.normalizeInvoice(item);
|
const invoice = this.normalizeInvoice(item);
|
||||||
@@ -4137,7 +4195,7 @@ export default {
|
|||||||
const archive = this.normalizeArchive();
|
const archive = this.normalizeArchive();
|
||||||
// 仅提交时落变更记录;保存不记录
|
// 仅提交时落变更记录;保存不记录
|
||||||
archive.recordChange = true;
|
archive.recordChange = true;
|
||||||
if (!this.validateFormalForApproval(archive)) return;
|
if (!this.validateScoreForApproval(archive)) return;
|
||||||
submit(archive).then(res => {
|
submit(archive).then(res => {
|
||||||
const data = res.data.data;
|
const data = res.data.data;
|
||||||
const id = (data && typeof data === 'object' ? data.id : data) || this.archiveForm.id;
|
const id = (data && typeof data === 'object' ? data.id : data) || this.archiveForm.id;
|
||||||
@@ -4165,44 +4223,31 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 列表 / 新增 / 编辑提交共用:提交 MK 审核流并更新客商审批状态
|
* 列表 / 新增 / 编辑提交共用:开启 MK 时提交审核流,并更新客商审批状态
|
||||||
*/
|
*/
|
||||||
submitCustomerApproval(id, fullName = '', approvalStatus = '') {
|
submitCustomerApproval(id, fullName = '', approvalStatus = '') {
|
||||||
return this.submitMkApprovalFlow(id, fullName, approvalStatus).then(() => submitApproval(id));
|
return submitMkApprovalFlow({
|
||||||
|
bizType: 'customer-archive',
|
||||||
|
formInstanceId: id,
|
||||||
|
subjectName: fullName,
|
||||||
|
approvalStatus,
|
||||||
|
}).then(() => submitApproval(id));
|
||||||
},
|
},
|
||||||
/**
|
openReviewScore() {
|
||||||
* 提交 MK 审核流:templateCode 取业务字典 mk_template 中「提交客商审核流」的键值
|
const scores = this.archiveForm.scores || [];
|
||||||
* 提交人手机号由后端从用户表读取真实值(前端接口会脱敏)
|
if (!scores.length) {
|
||||||
* MK 驳回后再提交时,先删除旧流程再创建新流程
|
this.$message.warning('暂无评分记录,无法复评');
|
||||||
*/
|
return;
|
||||||
async submitMkApprovalFlow(formInstanceId, subjectName = '', approvalStatus = '') {
|
|
||||||
if (approvalStatus === 'rejected') {
|
|
||||||
await processDelete({ formInstanceId: String(formInstanceId) });
|
|
||||||
}
|
}
|
||||||
const templateCode = await this.resolveMkTemplateCode('提交客商审核流');
|
this.$nextTick(() => this.openScore(scores[0], 0));
|
||||||
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() {
|
||||||
|
if ((this.archiveForm.scores || []).length) {
|
||||||
|
this.$message.warning('请勿重复添加');
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.scoreRecordIndex = -1;
|
this.scoreRecordIndex = -1;
|
||||||
this.currentScore = this.normalizeScore({});
|
this.currentScore = this.normalizeScore({ applyCreditLimit: '' });
|
||||||
this.scoreAttachmentFiles = [];
|
this.scoreAttachmentFiles = [];
|
||||||
this.expandedScoreCategoryKeys = [];
|
this.expandedScoreCategoryKeys = [];
|
||||||
this.scoreBox = true;
|
this.scoreBox = true;
|
||||||
@@ -4284,7 +4329,7 @@ export default {
|
|||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
(item.options || []).map(option => ({
|
(item.options || []).map(option => ({
|
||||||
label: option.label || option.optionName || option.value,
|
label: option.label || option.optionName || option.value,
|
||||||
value: option.value || option.optionName || option.label,
|
value: option.value ?? option.optionName ?? option.label,
|
||||||
score: option.score,
|
score: option.score,
|
||||||
changeType: option.changeType,
|
changeType: option.changeType,
|
||||||
changeValue: option.changeValue,
|
changeValue: option.changeValue,
|
||||||
@@ -4374,20 +4419,21 @@ export default {
|
|||||||
const isScoreOption = item.changeType || item.changeValue || item.changeUnit;
|
const isScoreOption = item.changeType || item.changeValue || item.changeUnit;
|
||||||
const changeLabel = item.changeType === 'decrease' ? '每减少' : '每增加';
|
const changeLabel = item.changeType === 'decrease' ? '每减少' : '每增加';
|
||||||
const scoreLabel = item.scoreType === 'subtract' ? '减' : '加';
|
const scoreLabel = item.scoreType === 'subtract' ? '减' : '加';
|
||||||
|
const rawScore = item.score;
|
||||||
const generatedLabel = isScoreOption
|
const generatedLabel = isScoreOption
|
||||||
? `${changeLabel}${item.changeValue || ''}${item.changeUnit || ''}${scoreLabel}${
|
? `${changeLabel}${item.changeValue || ''}${item.changeUnit || ''}${scoreLabel}${
|
||||||
item.score || ''
|
this.isBlankScore(rawScore) ? '' : rawScore
|
||||||
}分`
|
}分`
|
||||||
: '';
|
: '';
|
||||||
const label = item.label || item.optionName || item.value || generatedLabel;
|
const label = item.label || item.optionName || item.value || generatedLabel;
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
label,
|
label,
|
||||||
value: item.value || item.optionName || item.label || generatedLabel,
|
value: item.value ?? item.optionName ?? item.label ?? generatedLabel,
|
||||||
score:
|
score:
|
||||||
item.scoreType === 'subtract' && item.score !== undefined
|
item.scoreType === 'subtract' && !this.isBlankScore(rawScore)
|
||||||
? -Math.abs(Number(item.score))
|
? -Math.abs(Number(rawScore))
|
||||||
: item.score,
|
: rawScore,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
if (Array.isArray(value)) return normalize(value);
|
if (Array.isArray(value)) return normalize(value);
|
||||||
@@ -4451,23 +4497,35 @@ export default {
|
|||||||
const valid = increase ? input >= base : input <= base;
|
const valid = increase ? input >= base : input <= base;
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
detail.selfScore = '';
|
detail.selfScore = '';
|
||||||
this.$message.warning(`输入值${increase ? '不能小于' : '不能大于'}基准数值${base}`);
|
|
||||||
this.calculateScore(this.currentScore);
|
this.calculateScore(this.currentScore);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
detail.selfScore = this.getScoreTypeCalculatedScore(detail);
|
detail.selfScore = this.getScoreTypeCalculatedScore(detail);
|
||||||
this.calculateScore(this.currentScore);
|
this.calculateScore(this.currentScore);
|
||||||
},
|
},
|
||||||
|
handleScoreValueBlur(detail) {
|
||||||
|
const normalized = String(detail.scoreInput ?? '').trim();
|
||||||
|
if (!normalized || normalized === '-' || normalized === '.') return;
|
||||||
|
const rule = this.getScoreRule(detail);
|
||||||
|
const base = Number(detail.baseValue);
|
||||||
|
const input = Number(normalized);
|
||||||
|
if (!rule || !Number.isFinite(base) || !Number.isFinite(input)) return;
|
||||||
|
const increase = rule.changeType !== 'decrease';
|
||||||
|
const valid = increase ? input >= base : input <= base;
|
||||||
|
if (!valid) {
|
||||||
|
this.$message.warning(`输入值${increase ? '不能小于' : '不能大于'}基准数值${base}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
scoreOptionChange(detail, optionValue) {
|
scoreOptionChange(detail, optionValue) {
|
||||||
const option = this.parseScoreOptions(detail.optionsJson).find(
|
const option = this.parseScoreOptions(detail.optionsJson).find(
|
||||||
item => item.value === optionValue
|
item => item.value === optionValue
|
||||||
);
|
);
|
||||||
const score = Number(option?.score || 0);
|
const rawScore = option?.score;
|
||||||
detail.selfScore = score;
|
detail.selfScore = this.isBlankScore(rawScore) ? 0 : Number(rawScore);
|
||||||
this.calculateScore(this.currentScore);
|
this.calculateScore(this.currentScore);
|
||||||
},
|
},
|
||||||
handleScoreDetailScoreInput(detail, prop, value) {
|
handleScoreDetailScoreInput(detail, prop, value) {
|
||||||
const normalized = String(value || '')
|
const normalized = String(value ?? '')
|
||||||
.replace(/[^\d.]/g, '')
|
.replace(/[^\d.]/g, '')
|
||||||
.replace(/^\./, '')
|
.replace(/^\./, '')
|
||||||
.replace(/(\..*)\./g, '$1')
|
.replace(/(\..*)\./g, '$1')
|
||||||
@@ -4599,8 +4657,7 @@ export default {
|
|||||||
sumScore(plusDetails, 'reviewScore') -
|
sumScore(plusDetails, 'reviewScore') -
|
||||||
sumScore(minusDetails, 'reviewScore');
|
sumScore(minusDetails, 'reviewScore');
|
||||||
const hasReviewScore = details.some(
|
const hasReviewScore = details.some(
|
||||||
item =>
|
item => !this.isBlankScore(item.reviewScore)
|
||||||
item.reviewScore !== undefined && item.reviewScore !== null && item.reviewScore !== ''
|
|
||||||
);
|
);
|
||||||
const finalScore = hasReviewScore ? reviewScore : selfScore;
|
const finalScore = hasReviewScore ? reviewScore : selfScore;
|
||||||
const fullMark = this.getScoreFullMark(details);
|
const fullMark = this.getScoreFullMark(details);
|
||||||
@@ -4620,13 +4677,17 @@ export default {
|
|||||||
}
|
}
|
||||||
return score;
|
return score;
|
||||||
},
|
},
|
||||||
|
isBlankScore(value) {
|
||||||
|
return value === undefined || value === null || String(value).trim() === '';
|
||||||
|
},
|
||||||
hasIncompleteBasicScoreDetail(details = []) {
|
hasIncompleteBasicScoreDetail(details = []) {
|
||||||
return this.getScoreCategoryDetailsByList(details, 'basic').some(item => {
|
return this.getScoreCategoryDetailsByList(details, 'basic').some(item => {
|
||||||
if (this.isScoreTypeDetail(item)) {
|
if (this.isScoreTypeDetail(item)) {
|
||||||
const input = String(item.scoreInput ?? '').trim();
|
const input = String(item.scoreInput ?? '').trim();
|
||||||
return !input || !Number.isFinite(Number(input)) || item.selfScore === '';
|
const inputBlank = input === '' || input === '-' || input === '.';
|
||||||
|
return inputBlank || !Number.isFinite(Number(input)) || this.isBlankScore(item.selfScore);
|
||||||
}
|
}
|
||||||
return !item.selectedOption;
|
return this.isBlankScore(item.selectedOption);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
finishScore(score) {
|
finishScore(score) {
|
||||||
@@ -4643,10 +4704,13 @@ export default {
|
|||||||
score.reviewStatus = '已完成';
|
score.reviewStatus = '已完成';
|
||||||
this.$message.success('评分已完成');
|
this.$message.success('评分已完成');
|
||||||
},
|
},
|
||||||
saveScoreDetail() {
|
saveScoreDetail(successMessage) {
|
||||||
if (!this.validateScoreForm(false)) return;
|
if (!this.validateScoreForm(false)) return;
|
||||||
this.currentScore.attachments = JSON.parse(JSON.stringify(this.scoreAttachmentFiles || []));
|
this.currentScore.attachments = JSON.parse(JSON.stringify(this.scoreAttachmentFiles || []));
|
||||||
this.currentScore.proofAttachments = this.stringifyAttachments(this.scoreAttachmentFiles);
|
this.currentScore.proofAttachments = this.stringifyAttachments(this.scoreAttachmentFiles);
|
||||||
|
this.currentScore.selfStatus = this.hasIncompleteBasicScoreDetail(this.currentScore.details)
|
||||||
|
? '未完成'
|
||||||
|
: '已完成';
|
||||||
this.calculateScore(this.currentScore);
|
this.calculateScore(this.currentScore);
|
||||||
const score = JSON.parse(JSON.stringify(this.currentScore));
|
const score = JSON.parse(JSON.stringify(this.currentScore));
|
||||||
if (this.scoreRecordIndex > -1) {
|
if (this.scoreRecordIndex > -1) {
|
||||||
@@ -4658,23 +4722,62 @@ export default {
|
|||||||
this.syncArchiveCreditFromScores();
|
this.syncArchiveCreditFromScores();
|
||||||
this.activeTab = 'scores';
|
this.activeTab = 'scores';
|
||||||
this.scoreBox = false;
|
this.scoreBox = false;
|
||||||
this.saveScoreArchive();
|
const pending = this.saveScoreArchive(successMessage);
|
||||||
},
|
if (this.reviewMode && successMessage && pending && pending.then) {
|
||||||
saveScoreArchive() {
|
pending.then(() => this.closeArchive());
|
||||||
if (!this.archiveForm.id) {
|
|
||||||
this.$message.success('评分记录已添加,请保存客商档案后生效');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
submit(this.normalizeArchive()).then(() => {
|
return pending;
|
||||||
this.$message.success('评分记录已保存');
|
},
|
||||||
|
deleteScore(index) {
|
||||||
|
this.$confirm('确定删除该评分记录?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
if (!Array.isArray(this.archiveForm.scores)) this.archiveForm.scores = [];
|
||||||
|
this.archiveForm.scores.splice(index, 1);
|
||||||
|
if (this.scoreRecordIndex === index) {
|
||||||
|
this.scoreBox = false;
|
||||||
|
} else if (this.scoreRecordIndex > index) {
|
||||||
|
this.scoreRecordIndex -= 1;
|
||||||
|
}
|
||||||
|
const latestScore = this.getLatestCreditScore(this.archiveForm.scores);
|
||||||
|
if (latestScore) {
|
||||||
|
this.syncArchiveCreditFromScores();
|
||||||
|
} else {
|
||||||
|
this.archiveForm.customerLevel = '';
|
||||||
|
this.archiveForm.maxCreditLimit = '';
|
||||||
|
this.archiveForm.applyCreditLimit = '';
|
||||||
|
}
|
||||||
|
this.handleScoreCurrentChange(this.scorePage.currentPage);
|
||||||
|
this.saveScoreArchive('评分记录已删除', '评分记录已删除,请保存客商档案后生效');
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
},
|
||||||
|
saveScoreArchive(
|
||||||
|
successMessage = '评分记录已保存',
|
||||||
|
draftMessage = '评分记录已添加,请保存客商档案后生效'
|
||||||
|
) {
|
||||||
|
if (!this.archiveForm.id) {
|
||||||
|
this.$message.success(draftMessage);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return submit(this.normalizeArchive()).then(() => {
|
||||||
|
this.$message.success(successMessage);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
submitSelfScore() {
|
||||||
|
if (!this.validateScoreForm(true)) return;
|
||||||
|
this.currentScore.selfStatus = '已完成';
|
||||||
|
this.currentScore.reviewStatus = '未完成';
|
||||||
|
this.saveScoreDetail('自评已提交');
|
||||||
|
},
|
||||||
confirmReviewScore() {
|
confirmReviewScore() {
|
||||||
if (!this.validateScoreForm(true)) return;
|
if (!this.validateScoreForm(true)) return;
|
||||||
this.currentScore.selfStatus = '已完成';
|
this.currentScore.selfStatus = '已完成';
|
||||||
this.currentScore.reviewStatus = '已完成';
|
this.currentScore.reviewStatus = '已完成';
|
||||||
this.saveScoreDetail();
|
this.saveScoreDetail('复评确认成功');
|
||||||
this.$message.success('复评确认成功');
|
|
||||||
},
|
},
|
||||||
validateScoreForm(requireComplete) {
|
validateScoreForm(requireComplete) {
|
||||||
if (!this.currentScore.scoreDate) {
|
if (!this.currentScore.scoreDate) {
|
||||||
@@ -4712,7 +4815,7 @@ export default {
|
|||||||
if (
|
if (
|
||||||
item.reviewScore === undefined ||
|
item.reviewScore === undefined ||
|
||||||
item.reviewScore === null ||
|
item.reviewScore === null ||
|
||||||
item.reviewScore === ''
|
String(item.reviewScore).trim() === ''
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -4729,7 +4832,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (requireComplete && this.hasIncompleteBasicScoreDetail(this.currentScore.details)) {
|
if (requireComplete && this.hasIncompleteBasicScoreDetail(this.currentScore.details)) {
|
||||||
this.$message.warning('请完整填写基础得分项评分明细');
|
this.$message.warning('请完整填写所有基础项目后再提交自评');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -4784,7 +4887,7 @@ export default {
|
|||||||
getDetail(row.id).then(res => {
|
getDetail(row.id).then(res => {
|
||||||
const archive = this.normalizeDetail(res.data.data);
|
const archive = this.normalizeDetail(res.data.data);
|
||||||
archive.qualificationAttachments = archive.qualificationAttachments || '';
|
archive.qualificationAttachments = archive.qualificationAttachments || '';
|
||||||
if (!this.validateFormalForApproval(archive)) return;
|
if (!this.validateScoreForApproval(archive)) return;
|
||||||
this.$confirm('是否提交客商准入审批?', '提示', {
|
this.$confirm('是否提交客商准入审批?', '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
@@ -5271,10 +5374,16 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-table__body td) {
|
:deep(.el-table__body td) {
|
||||||
//min-height: 112px;
|
|
||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.score-item-name) {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-select),
|
:deep(.el-select),
|
||||||
:deep(.el-input),
|
:deep(.el-input),
|
||||||
:deep(.el-select) {
|
:deep(.el-select) {
|
||||||
@@ -5309,6 +5418,16 @@ export default {
|
|||||||
|
|
||||||
.invoice-form {
|
.invoice-form {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
|
|
||||||
|
:deep(.el-input),
|
||||||
|
:deep(.el-cascader),
|
||||||
|
:deep(.el-select) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-cascader .el-input) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.invoice-contact-table {
|
.invoice-contact-table {
|
||||||
@@ -5447,27 +5566,6 @@ export default {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.change-record-detail-dialog .el-dialog__body) {
|
|
||||||
max-height: 65vh;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.change-record-detail-dialog .change-record-detail-value .cell) {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-word;
|
|
||||||
overflow: visible;
|
|
||||||
text-overflow: clip;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.change-record-detail-meta {
|
|
||||||
display: flex;
|
|
||||||
gap: 32px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
color: #606266;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.customer-archive-page.is-public-view {
|
.customer-archive-page.is-public-view {
|
||||||
:deep(.archive-page-form .archive-form__footer) {
|
:deep(.archive-page-form .archive-form__footer) {
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|||||||
@@ -564,8 +564,8 @@ export default {
|
|||||||
this.addressMapPickerVisible = true;
|
this.addressMapPickerVisible = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleAddressMapConfirm(address) {
|
handleAddressMapConfirm(payload) {
|
||||||
this.form.address = address;
|
this.form.address = typeof payload === 'string' ? payload : payload?.address || '';
|
||||||
},
|
},
|
||||||
hasPermission(code) {
|
hasPermission(code) {
|
||||||
return this.isAdmin || this.validData(this.permission[code], false);
|
return this.isAdmin || this.validData(this.permission[code], false);
|
||||||
|
|||||||
@@ -545,8 +545,8 @@ export default {
|
|||||||
this.addressMapPickerVisible = true;
|
this.addressMapPickerVisible = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleAddressMapConfirm(address) {
|
handleAddressMapConfirm(payload) {
|
||||||
this.form.address = address;
|
this.form.address = typeof payload === 'string' ? payload : payload?.address || '';
|
||||||
},
|
},
|
||||||
hasPermission(code) {
|
hasPermission(code) {
|
||||||
return this.isAdmin || this.validData(this.permission[code], false);
|
return this.isAdmin || this.validData(this.permission[code], false);
|
||||||
|
|||||||
@@ -288,8 +288,8 @@ export default {
|
|||||||
this.locationMapPickerVisible = true;
|
this.locationMapPickerVisible = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleLocationMapConfirm(address) {
|
handleLocationMapConfirm(payload) {
|
||||||
this.form.location = address;
|
this.form.location = typeof payload === 'string' ? payload : payload?.address || '';
|
||||||
},
|
},
|
||||||
hasPermission(code) {
|
hasPermission(code) {
|
||||||
return this.isAdmin || this.validData(this.permission[code], false);
|
return this.isAdmin || this.validData(this.permission[code], false);
|
||||||
|
|||||||
+7
-4
@@ -570,19 +570,22 @@
|
|||||||
|
|
||||||
- 功能点:
|
- 功能点:
|
||||||
- 任意状态的运单均支持复制。
|
- 任意状态的运单均支持复制。
|
||||||
- 复制后生成新的草稿运单,运单号重新生成。
|
- 复制后运单号重新生成,业务状态与原运单保持一致。
|
||||||
- 复制时保留运单基础信息、货物信息、承运信息和过程配置相关信息,状态与过程记录重新初始化。
|
- 复制时保留运单基础信息、货物信息、承运信息和过程配置相关信息;过程打卡记录重新初始化。
|
||||||
- 复制成功后跳转编辑页面。
|
- 若原运单为已完成,复制保存后按合同系统计费规则自动生成对应应收、应付明细。
|
||||||
|
- 复制成功后跳转编辑页面(独立表单模式)或刷新列表(弹窗模式)。
|
||||||
|
|
||||||
- 异常与边界:
|
- 异常与边界:
|
||||||
- 原运单不存在时提示数据不存在。
|
- 原运单不存在时提示数据不存在。
|
||||||
- 复制后名称、编号或关联字段触发唯一性校验时应重新处理。
|
- 复制后名称、编号或关联字段触发唯一性校验时应重新处理。
|
||||||
- 复制失败时不影响原运单数据。
|
- 复制失败时不影响原运单数据。
|
||||||
|
- 合同未开启系统计费、无匹配计费方案或自有运输等场景下,按既有规则跳过对应应收/应付生成。
|
||||||
|
|
||||||
- 反例:
|
- 反例:
|
||||||
- 不允许复制后沿用原运单号。
|
- 不允许复制后沿用原运单号。
|
||||||
- 不允许复制后直接生成进行中或已完成状态。
|
- 不允许复制后状态与原运单不一致。
|
||||||
- 不允许复制后保留原过程节点完成记录。
|
- 不允许复制后保留原过程节点完成记录。
|
||||||
|
- 不允许已完成运单复制成功后遗漏按规则应生成的应收应付明细。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user