✨ 角色权限联动与临时额度/项目备案/司机车辆审核MK
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,7 +7,8 @@ import {
|
|||||||
|
|
||||||
export const changeTypeOptions = [
|
export const changeTypeOptions = [
|
||||||
{ label: '项目变更', value: '项目变更' },
|
{ label: '项目变更', value: '项目变更' },
|
||||||
{ label: '项目备案调整', value: '项目备案调整' },
|
{ label: '项目备案流程(本部流程)', value: '项目备案流程(本部流程)' },
|
||||||
|
{ label: '项目备案流程(运输部流程)', value: '项目备案流程(运输部流程)' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
|||||||
@@ -85,13 +85,6 @@ export const config = {
|
|||||||
status: ['draft', 'withdrawn', 'rejected'],
|
status: ['draft', 'withdrawn', 'rejected'],
|
||||||
permission: 'temporary_credit_limit_submit',
|
permission: 'temporary_credit_limit_submit',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
action: 'flow',
|
|
||||||
label: '流程',
|
|
||||||
statusProp: 'approvalStatus',
|
|
||||||
status: ['reviewing'],
|
|
||||||
permission: 'temporary_credit_limit_view',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
action: 'withdraw',
|
action: 'withdraw',
|
||||||
label: '撤回',
|
label: '撤回',
|
||||||
@@ -99,6 +92,14 @@ export const config = {
|
|||||||
status: ['reviewing'],
|
status: ['reviewing'],
|
||||||
permission: 'temporary_credit_limit_withdraw',
|
permission: 'temporary_credit_limit_withdraw',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
action: 'mkAudit',
|
||||||
|
label: '流程',
|
||||||
|
statusProp: 'approvalStatus',
|
||||||
|
status: ['reviewing'],
|
||||||
|
permission: 'temporary_credit_limit_approve',
|
||||||
|
mkOnly: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
action: 'approve',
|
action: 'approve',
|
||||||
label: '审批通过',
|
label: '审批通过',
|
||||||
@@ -106,6 +107,7 @@ export const config = {
|
|||||||
statusProp: 'approvalStatus',
|
statusProp: 'approvalStatus',
|
||||||
status: ['reviewing'],
|
status: ['reviewing'],
|
||||||
permission: 'temporary_credit_limit_approve',
|
permission: 'temporary_credit_limit_approve',
|
||||||
|
localOnly: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'reject',
|
action: 'reject',
|
||||||
@@ -114,6 +116,7 @@ export const config = {
|
|||||||
statusProp: 'approvalStatus',
|
statusProp: 'approvalStatus',
|
||||||
status: ['reviewing'],
|
status: ['reviewing'],
|
||||||
permission: 'temporary_credit_limit_reject',
|
permission: 'temporary_credit_limit_reject',
|
||||||
|
localOnly: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -106,6 +106,17 @@ export default [
|
|||||||
bizType: 'project-change',
|
bizType: 'project-change',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/business/project-record-change/public-view',
|
||||||
|
name: '查看项目备案信息',
|
||||||
|
component: () => import('@/views/business/project-apply-public-view.vue'),
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
isTab: false,
|
||||||
|
isAuth: false,
|
||||||
|
bizType: 'project-record-change',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/business/contract-manage/public-view',
|
path: '/business/contract-manage/public-view',
|
||||||
name: '查看合同信息',
|
name: '查看合同信息',
|
||||||
@@ -194,6 +205,39 @@ export default [
|
|||||||
bizType: 'bill-payment',
|
bizType: 'bill-payment',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/business/temporary-credit-limit/public-view',
|
||||||
|
name: '查看临时额度申请信息',
|
||||||
|
component: () => import('@/views/business/temporary-credit-limit-public-view.vue'),
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
isTab: false,
|
||||||
|
isAuth: false,
|
||||||
|
bizType: 'temporary-credit-limit',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/transportCapacity/cert-audit-driver/public-view',
|
||||||
|
name: '查看司机认证审核信息',
|
||||||
|
component: () => import('@/views/transportCapacity/cert-audit-driver-public-view.vue'),
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
isTab: false,
|
||||||
|
isAuth: false,
|
||||||
|
bizType: 'cert-audit-driver',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/transportCapacity/cert-audit-vehicle/public-view',
|
||||||
|
name: '查看车辆认证审核信息',
|
||||||
|
component: () => import('@/views/transportCapacity/cert-audit-vehicle-public-view.vue'),
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
isTab: false,
|
||||||
|
isAuth: false,
|
||||||
|
bizType: 'cert-audit-vehicle',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: '主页',
|
name: '主页',
|
||||||
|
|||||||
@@ -21,6 +21,19 @@ export const MK_BIZ = {
|
|||||||
rejected: ['rejected', 'withdrawn'],
|
rejected: ['rejected', 'withdrawn'],
|
||||||
approved: ['approved'],
|
approved: ['approved'],
|
||||||
},
|
},
|
||||||
|
/** 项目备案流程(本部/运输部)共用 */
|
||||||
|
'project-record-change': {
|
||||||
|
dictName: '提交项目备案审核流',
|
||||||
|
subjectPrefix: '项目备案审批',
|
||||||
|
rejected: ['rejected', 'withdrawn'],
|
||||||
|
approved: ['approved'],
|
||||||
|
},
|
||||||
|
'temporary-credit-limit': {
|
||||||
|
dictName: '提交临时额度审核流',
|
||||||
|
subjectPrefix: '临时额度审批',
|
||||||
|
rejected: ['rejected', 'withdrawn'],
|
||||||
|
approved: ['approved'],
|
||||||
|
},
|
||||||
'contract-manage': {
|
'contract-manage': {
|
||||||
dictName: '提交合同审核流',
|
dictName: '提交合同审核流',
|
||||||
subjectPrefix: '合同审批',
|
subjectPrefix: '合同审批',
|
||||||
@@ -69,10 +82,42 @@ export const MK_BIZ = {
|
|||||||
rejected: ['returned'],
|
rejected: ['returned'],
|
||||||
approved: ['approved'],
|
approved: ['approved'],
|
||||||
},
|
},
|
||||||
|
'cert-audit-driver': {
|
||||||
|
dictName: '提交司机审核流',
|
||||||
|
subjectPrefix: '司机认证审批',
|
||||||
|
// 认证记录 status:0审核中 1已通过 2已驳回
|
||||||
|
rejected: ['2'],
|
||||||
|
approved: ['1'],
|
||||||
|
},
|
||||||
|
'cert-audit-vehicle': {
|
||||||
|
dictName: '提交车辆审核流',
|
||||||
|
subjectPrefix: '车辆认证审批',
|
||||||
|
rejected: ['2'],
|
||||||
|
approved: ['1'],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 按认证对象类型解析 MK 业务类型 */
|
||||||
|
export function resolveCertAuditMkBizType(bizType) {
|
||||||
|
return String(bizType || '') === 'vehicle' ? 'cert-audit-vehicle' : 'cert-audit-driver';
|
||||||
|
}
|
||||||
|
|
||||||
const MK_SWITCH_NAME = '开启MK';
|
const MK_SWITCH_NAME = '开启MK';
|
||||||
|
|
||||||
|
/** 项目备案类变更类型(含历史「项目备案调整」) */
|
||||||
|
export const PROJECT_RECORD_CHANGE_TYPES = [
|
||||||
|
'项目备案流程(本部流程)',
|
||||||
|
'项目备案流程(运输部流程)',
|
||||||
|
'项目备案调整',
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 按变更类型选择项目变更 / 项目备案 MK 业务类型 */
|
||||||
|
export function resolveProjectChangeMkBizType(changeType) {
|
||||||
|
return PROJECT_RECORD_CHANGE_TYPES.includes(String(changeType || '').trim())
|
||||||
|
? 'project-record-change'
|
||||||
|
: 'project-change';
|
||||||
|
}
|
||||||
|
|
||||||
/** 进程内缓存,避免每次提交都打字典;登录后同一会话开关一般不变 */
|
/** 进程内缓存,避免每次提交都打字典;登录后同一会话开关一般不变 */
|
||||||
let mkEnabledCache = null;
|
let mkEnabledCache = null;
|
||||||
let mkEnabledPromise = null;
|
let mkEnabledPromise = null;
|
||||||
|
|||||||
@@ -92,6 +92,7 @@
|
|||||||
ref="treeMenu"
|
ref="treeMenu"
|
||||||
:default-checked-keys="menuTreeObj"
|
:default-checked-keys="menuTreeObj"
|
||||||
:props="props"
|
:props="props"
|
||||||
|
@check="handleMenuCheck"
|
||||||
>
|
>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
@@ -102,7 +103,7 @@
|
|||||||
style="margin-bottom: 12px; background: #f5f7fa; padding: 6px 10px; border-radius: 4px"
|
style="margin-bottom: 12px; background: #f5f7fa; padding: 6px 10px; border-radius: 4px"
|
||||||
>
|
>
|
||||||
<span style="color: #606266; font-size: 13px"
|
<span style="color: #606266; font-size: 13px"
|
||||||
>仅展示菜单权限已勾选的菜单,已配置的数据权限会保留。勾选多个菜单后点击「设置」可批量配置</span
|
>菜单权限勾选/反选会自动同步到此页;仅展示已勾选菜单,已配置的数据权限会保留。可再调整勾选后点击「设置」批量配置</span
|
||||||
>
|
>
|
||||||
<el-button type="primary" size="small" @click="openDataScopeSetting">设置</el-button>
|
<el-button type="primary" size="small" @click="openDataScopeSetting">设置</el-button>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -191,20 +192,8 @@
|
|||||||
<el-radio :value="1">所在部门及下级</el-radio>
|
<el-radio :value="1">所在部门及下级</el-radio>
|
||||||
<el-radio :value="2">全部组织</el-radio>
|
<el-radio :value="2">全部组织</el-radio>
|
||||||
<el-radio :value="3">本人创建</el-radio>
|
<el-radio :value="3">本人创建</el-radio>
|
||||||
<el-radio :value="4">自定义组织</el-radio>
|
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="Number(dataScopeSetting.scopeType) === 4" label="组织">
|
|
||||||
<el-tree
|
|
||||||
ref="treeCustomDept"
|
|
||||||
:data="deptTree"
|
|
||||||
show-checkbox
|
|
||||||
node-key="id"
|
|
||||||
default-expand-all
|
|
||||||
:props="{ label: 'title', children: 'children' }"
|
|
||||||
style="width: 100%; max-height: 320px; overflow: auto; border: 1px solid #ebeef5; padding: 8px"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
@@ -228,7 +217,6 @@ import {
|
|||||||
update,
|
update,
|
||||||
} from '@/api/system/role';
|
} from '@/api/system/role';
|
||||||
import { getRoleMenuDataScopeTree, submitRoleMenuDataScope } from '@/api/system/roleDataScope';
|
import { getRoleMenuDataScopeTree, submitRoleMenuDataScope } from '@/api/system/roleDataScope';
|
||||||
import { getDeptTree } from '@/api/system/dept';
|
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import website from '@/config/website';
|
import website from '@/config/website';
|
||||||
|
|
||||||
@@ -253,7 +241,6 @@ export default {
|
|||||||
currentRoleName: '',
|
currentRoleName: '',
|
||||||
roleDataScopeMenuTree: [],
|
roleDataScopeMenuTree: [],
|
||||||
roleDataScopeMap: {},
|
roleDataScopeMap: {},
|
||||||
deptTree: [],
|
|
||||||
dataScopeSetting: {
|
dataScopeSetting: {
|
||||||
roleName: '',
|
roleName: '',
|
||||||
scopeType: 2,
|
scopeType: 2,
|
||||||
@@ -430,6 +417,11 @@ export default {
|
|||||||
if (!tree) return;
|
if (!tree) return;
|
||||||
const allKeys = this.getAllNodeKeys(dataList);
|
const allKeys = this.getAllNodeKeys(dataList);
|
||||||
tree.setCheckedKeys(allKeys);
|
tree.setCheckedKeys(allKeys);
|
||||||
|
if (treeRef === 'treeMenu') {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.syncDataScopeTreeFromMenu();
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
handleInvertSelect(treeRef, dataList, isLinked) {
|
handleInvertSelect(treeRef, dataList, isLinked) {
|
||||||
const tree = this.$refs[treeRef];
|
const tree = this.$refs[treeRef];
|
||||||
@@ -444,6 +436,11 @@ export default {
|
|||||||
const invertedKeys = allKeys.filter(key => !checkedKeys.has(key));
|
const invertedKeys = allKeys.filter(key => !checkedKeys.has(key));
|
||||||
tree.setCheckedKeys(invertedKeys);
|
tree.setCheckedKeys(invertedKeys);
|
||||||
}
|
}
|
||||||
|
if (treeRef === 'treeMenu') {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.syncDataScopeTreeFromMenu();
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
handleLinkedChange(treeRef) {
|
handleLinkedChange(treeRef) {
|
||||||
const tree = this.$refs[treeRef];
|
const tree = this.$refs[treeRef];
|
||||||
@@ -452,6 +449,9 @@ export default {
|
|||||||
const halfCheckedKeys = tree.getHalfCheckedKeys();
|
const halfCheckedKeys = tree.getHalfCheckedKeys();
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
tree.setCheckedKeys([...checkedKeys, ...halfCheckedKeys]);
|
tree.setCheckedKeys([...checkedKeys, ...halfCheckedKeys]);
|
||||||
|
if (treeRef === 'treeMenu') {
|
||||||
|
this.syncDataScopeTreeFromMenu();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// el-tree 会把 setCheckedKeys 的入参记录为默认勾选集并在树数据重载时回放,
|
// el-tree 会把 setCheckedKeys 的入参记录为默认勾选集并在树数据重载时回放,
|
||||||
@@ -461,6 +461,50 @@ export default {
|
|||||||
if (!tree) return;
|
if (!tree) return;
|
||||||
tree.setCheckedKeys(keys);
|
tree.setCheckedKeys(keys);
|
||||||
},
|
},
|
||||||
|
handleMenuCheck() {
|
||||||
|
this.syncDataScopeTreeFromMenu();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 按当前菜单权限勾选结果过滤数据权限树,并同步勾选状态。
|
||||||
|
* 取消勾选的菜单从数据权限树移除;已配置的数据权限标签仍保留在 roleDataScopeMap。
|
||||||
|
*/
|
||||||
|
syncDataScopeTreeFromMenu() {
|
||||||
|
const treeMenu = this.$refs.treeMenu;
|
||||||
|
if (!treeMenu) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const checkedKeys = treeMenu.getCheckedKeys() || [];
|
||||||
|
const checkedKeySet = new Set(checkedKeys);
|
||||||
|
this.roleDataScopeMenuTree = this.filterMenuTreeByChecked(this.menuGrantList, checkedKeySet);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const treeDataScope = this.$refs.treeRoleDataScope;
|
||||||
|
if (!treeDataScope) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const visibleKeys = new Set(this.getAllNodeKeys(this.roleDataScopeMenuTree));
|
||||||
|
const syncKeys = checkedKeys.filter(key => visibleKeys.has(key));
|
||||||
|
treeDataScope.setCheckedKeys(syncKeys);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 仅保留已勾选菜单及其祖先节点,保持树层级。
|
||||||
|
*/
|
||||||
|
filterMenuTreeByChecked(nodes, checkedKeySet) {
|
||||||
|
if (!nodes || nodes.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const result = [];
|
||||||
|
nodes.forEach(node => {
|
||||||
|
const children = this.filterMenuTreeByChecked(node.children || [], checkedKeySet);
|
||||||
|
if (checkedKeySet.has(node.id) || children.length > 0) {
|
||||||
|
result.push({
|
||||||
|
...node,
|
||||||
|
children,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
},
|
||||||
submit() {
|
submit() {
|
||||||
const menuList = this.$refs.treeMenu.getCheckedKeys();
|
const menuList = this.$refs.treeMenu.getCheckedKeys();
|
||||||
const apiScopeList = this.$refs.treeApiScope.getCheckedKeys();
|
const apiScopeList = this.$refs.treeApiScope.getCheckedKeys();
|
||||||
@@ -484,7 +528,7 @@ export default {
|
|||||||
return getRoleMenuDataScopeTree(roleId).then(res => {
|
return getRoleMenuDataScopeTree(roleId).then(res => {
|
||||||
const data = res.data.data || {};
|
const data = res.data.data || {};
|
||||||
this.currentRoleName = data.roleName || '';
|
this.currentRoleName = data.roleName || '';
|
||||||
this.roleDataScopeMenuTree = data.menuTree || [];
|
// 菜单树由菜单权限勾选实时联动生成;这里只加载已配置的数据权限标签
|
||||||
const scopeMap = {};
|
const scopeMap = {};
|
||||||
(data.scopes || []).forEach(item => {
|
(data.scopes || []).forEach(item => {
|
||||||
scopeMap[String(item.menuId)] = item;
|
scopeMap[String(item.menuId)] = item;
|
||||||
@@ -507,20 +551,7 @@ export default {
|
|||||||
scopeType: 2,
|
scopeType: 2,
|
||||||
menuIds,
|
menuIds,
|
||||||
};
|
};
|
||||||
const loadDept =
|
this.dataScopeSettingBox = true;
|
||||||
this.deptTree && this.deptTree.length
|
|
||||||
? Promise.resolve()
|
|
||||||
: getDeptTree(this.userInfo.tenantId).then(res => {
|
|
||||||
this.deptTree = res.data.data || [];
|
|
||||||
});
|
|
||||||
loadDept.then(() => {
|
|
||||||
this.dataScopeSettingBox = true;
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.$refs.treeCustomDept) {
|
|
||||||
this.$refs.treeCustomDept.setCheckedKeys([]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
submitDataScopeSetting() {
|
submitDataScopeSetting() {
|
||||||
const roleId = this.currentRoleIds && this.currentRoleIds[0];
|
const roleId = this.currentRoleIds && this.currentRoleIds[0];
|
||||||
@@ -529,27 +560,17 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const scopeType = Number(this.dataScopeSetting.scopeType || 2);
|
const scopeType = Number(this.dataScopeSetting.scopeType || 2);
|
||||||
let deptIds = [];
|
|
||||||
if (scopeType === 4) {
|
|
||||||
deptIds = this.$refs.treeCustomDept ? this.$refs.treeCustomDept.getCheckedKeys() : [];
|
|
||||||
if (!deptIds.length) {
|
|
||||||
this.$message.warning('自定义组织时请勾选组织');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
submitRoleMenuDataScope({
|
submitRoleMenuDataScope({
|
||||||
roleId,
|
roleId,
|
||||||
menuIds: this.dataScopeSetting.menuIds,
|
menuIds: this.dataScopeSetting.menuIds,
|
||||||
scopeType,
|
scopeType,
|
||||||
deptIds,
|
deptIds: [],
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$message.success('数据权限设置成功');
|
this.$message.success('数据权限设置成功');
|
||||||
this.dataScopeSettingBox = false;
|
this.dataScopeSettingBox = false;
|
||||||
this.loadRoleDataScopeTree(roleId).then(() => {
|
this.loadRoleDataScopeTree(roleId).then(() => {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this.$refs.treeRoleDataScope) {
|
this.syncDataScopeTreeFromMenu();
|
||||||
this.$refs.treeRoleDataScope.setCheckedKeys([]);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -663,6 +684,7 @@ export default {
|
|||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.applyCheckedKeys('treeMenu', this.menuTreeObj);
|
this.applyCheckedKeys('treeMenu', this.menuTreeObj);
|
||||||
this.applyCheckedKeys('treeApiScope', this.apiScopeTreeObj);
|
this.applyCheckedKeys('treeApiScope', this.apiScopeTreeObj);
|
||||||
|
this.syncDataScopeTreeFromMenu();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -682,6 +704,7 @@ export default {
|
|||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.applyCheckedKeys('treeMenu', this.menuTreeObj);
|
this.applyCheckedKeys('treeMenu', this.menuTreeObj);
|
||||||
this.applyCheckedKeys('treeApiScope', this.apiScopeTreeObj);
|
this.applyCheckedKeys('treeApiScope', this.apiScopeTreeObj);
|
||||||
|
this.syncDataScopeTreeFromMenu();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1248,7 +1248,11 @@ import { getList as getUserList } from '@/api/system/user';
|
|||||||
import { getToken, isAdministrator } from '@/utils/auth';
|
import { getToken, isAdministrator } from '@/utils/auth';
|
||||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||||
import { getMKApprovalUrl } from '@/api/system/business-process';
|
import { getMKApprovalUrl } from '@/api/system/business-process';
|
||||||
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
|
import {
|
||||||
|
loadMkEnabled,
|
||||||
|
resolveProjectChangeMkBizType,
|
||||||
|
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';
|
||||||
@@ -1353,7 +1357,8 @@ const optionalSentinelFields = [
|
|||||||
|
|
||||||
const changeTypeOptions = [
|
const changeTypeOptions = [
|
||||||
{ label: '项目变更', value: '项目变更' },
|
{ label: '项目变更', value: '项目变更' },
|
||||||
{ label: '项目备案调整', value: '项目备案调整' },
|
{ label: '项目备案流程(本部流程)', value: '项目备案流程(本部流程)' },
|
||||||
|
{ label: '项目备案流程(运输部流程)', value: '项目备案流程(运输部流程)' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const attachmentViewerPlugins = [
|
const attachmentViewerPlugins = [
|
||||||
@@ -1756,12 +1761,16 @@ export default {
|
|||||||
isPublicViewPage() {
|
isPublicViewPage() {
|
||||||
return (
|
return (
|
||||||
this.$route.path === '/business/project-apply/public-view' ||
|
this.$route.path === '/business/project-apply/public-view' ||
|
||||||
this.$route.path === '/business/project-change/public-view'
|
this.$route.path === '/business/project-change/public-view' ||
|
||||||
|
this.$route.path === '/business/project-record-change/public-view'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
publicBizType() {
|
publicBizType() {
|
||||||
return this.$route.meta?.bizType || 'project-apply';
|
return this.$route.meta?.bizType || 'project-apply';
|
||||||
},
|
},
|
||||||
|
isPublicProjectChangeView() {
|
||||||
|
return ['project-change', 'project-record-change'].includes(this.publicBizType);
|
||||||
|
},
|
||||||
projectFormContainer() {
|
projectFormContainer() {
|
||||||
// 表单页实例始终用页面容器,避免 keep-alive 失活时误切弹窗
|
// 表单页实例始终用页面容器,避免 keep-alive 失活时误切弹窗
|
||||||
return this.isFormPageInstance ? 'div' : 'el-dialog';
|
return this.isFormPageInstance ? 'div' : 'el-dialog';
|
||||||
@@ -1900,7 +1909,7 @@ export default {
|
|||||||
},
|
},
|
||||||
openPublicProjectForm() {
|
openPublicProjectForm() {
|
||||||
const id = this.$route.query.id;
|
const id = this.$route.query.id;
|
||||||
this.dialogType = this.publicBizType === 'project-change' ? 'change' : 'view';
|
this.dialogType = this.isPublicProjectChangeView ? 'change' : 'view';
|
||||||
this.dialogReadonly = true;
|
this.dialogReadonly = true;
|
||||||
this.projectBox = true;
|
this.projectBox = true;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
@@ -2535,10 +2544,20 @@ export default {
|
|||||||
},
|
},
|
||||||
resolveChangeType(row = {}) {
|
resolveChangeType(row = {}) {
|
||||||
if (row.changeType) return row.changeType;
|
if (row.changeType) return row.changeType;
|
||||||
const node = row.currentNode || '';
|
const node = String(row.currentNode || '');
|
||||||
if (String(node).includes('备案')) return '项目备案调整';
|
if (node.includes('运输部')) return '项目备案流程(运输部流程)';
|
||||||
|
if (node.includes('本部') || node.includes('备案')) return '项目备案流程(本部流程)';
|
||||||
return '项目变更';
|
return '项目变更';
|
||||||
},
|
},
|
||||||
|
buildProjectChangeMkTempVarData(row = {}) {
|
||||||
|
const changeType = this.resolveChangeType(row);
|
||||||
|
return {
|
||||||
|
projectType: row.projectType || this.form.projectType || '',
|
||||||
|
// MK 临时变量:变更类型(本部/运输部备案分流)
|
||||||
|
changeType,
|
||||||
|
变更类型: changeType,
|
||||||
|
};
|
||||||
|
},
|
||||||
submitProject() {
|
submitProject() {
|
||||||
this.$refs.projectForm.validate(valid => {
|
this.$refs.projectForm.validate(valid => {
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
@@ -2681,6 +2700,7 @@ export default {
|
|||||||
if (this.isProjectChangeModule) {
|
if (this.isProjectChangeModule) {
|
||||||
const payload = this.normalizeSubmitForm({ includeChangeType: true });
|
const payload = this.normalizeSubmitForm({ includeChangeType: true });
|
||||||
const approvalStatus = this.form.approvalStatus || '';
|
const approvalStatus = this.form.approvalStatus || '';
|
||||||
|
const changeType = this.resolveChangeType(this.form);
|
||||||
projectChangeApi
|
projectChangeApi
|
||||||
.submit(payload)
|
.submit(payload)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
@@ -2690,12 +2710,15 @@ export default {
|
|||||||
}
|
}
|
||||||
const id = this.form.id;
|
const id = this.form.id;
|
||||||
return submitMkApprovalFlow({
|
return submitMkApprovalFlow({
|
||||||
bizType: 'project-change',
|
bizType: resolveProjectChangeMkBizType(changeType),
|
||||||
bizId: id,
|
bizId: id,
|
||||||
subjectName: this.form.projectName || '',
|
subjectName: this.form.projectName || '',
|
||||||
approvalStatus,
|
approvalStatus,
|
||||||
mkEnabled: this.mkEnabled,
|
mkEnabled: this.mkEnabled,
|
||||||
tempVarData: { projectType: this.form.projectType || '' },
|
tempVarData: this.buildProjectChangeMkTempVarData({
|
||||||
|
...this.form,
|
||||||
|
changeType,
|
||||||
|
}),
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$message.success('提交成功!');
|
this.$message.success('提交成功!');
|
||||||
this.closeProjectForm();
|
this.closeProjectForm();
|
||||||
@@ -2715,13 +2738,17 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const id = this.form.id;
|
const id = this.form.id;
|
||||||
|
const changeType = this.resolveChangeType(this.form);
|
||||||
return submitMkApprovalFlow({
|
return submitMkApprovalFlow({
|
||||||
bizType: 'project-apply',
|
bizType: 'project-apply',
|
||||||
bizId: id,
|
bizId: id,
|
||||||
subjectName: this.form.projectName || '',
|
subjectName: this.form.projectName || '',
|
||||||
approvalStatus: this.form.approvalStatus || 'change_rejected',
|
approvalStatus: this.form.approvalStatus || 'change_rejected',
|
||||||
mkEnabled: this.mkEnabled,
|
mkEnabled: this.mkEnabled,
|
||||||
tempVarData: { projectType: this.form.projectType || '' },
|
tempVarData: this.buildProjectChangeMkTempVarData({
|
||||||
|
...this.form,
|
||||||
|
changeType,
|
||||||
|
}),
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$message.success('提交成功!');
|
this.$message.success('提交成功!');
|
||||||
this.closeProjectForm();
|
this.closeProjectForm();
|
||||||
|
|||||||
@@ -75,7 +75,11 @@ import * as api from '@/api/business/project-change';
|
|||||||
import { getMKApprovalUrl } from '@/api/system/business-process';
|
import { getMKApprovalUrl } from '@/api/system/business-process';
|
||||||
import { config, option } from '@/option/business/project-change';
|
import { config, option } from '@/option/business/project-change';
|
||||||
import { approvalStatusOptions } from '@/option/business/common';
|
import { approvalStatusOptions } from '@/option/business/common';
|
||||||
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
|
import {
|
||||||
|
loadMkEnabled,
|
||||||
|
resolveProjectChangeMkBizType,
|
||||||
|
submitMkApprovalFlow,
|
||||||
|
} from '@/utils/mk-approval';
|
||||||
import { isAdministrator } from '@/utils/auth';
|
import { isAdministrator } from '@/utils/auth';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
@@ -217,16 +221,21 @@ export default {
|
|||||||
// 先本地提交再调 MK,避免 MK 回写 reviewing 后本地 submitApproval 被编辑校验拦住。
|
// 先本地提交再调 MK,避免 MK 回写 reviewing 后本地 submitApproval 被编辑校验拦住。
|
||||||
const afterMk =
|
const afterMk =
|
||||||
operation.action === 'submitApproval'
|
operation.action === 'submitApproval'
|
||||||
? request().then(() =>
|
? request().then(() => {
|
||||||
submitMkApprovalFlow({
|
const changeType = row.changeType || '项目变更';
|
||||||
bizType: 'project-change',
|
return submitMkApprovalFlow({
|
||||||
|
bizType: resolveProjectChangeMkBizType(changeType),
|
||||||
bizId: row.id,
|
bizId: row.id,
|
||||||
subjectName: row.projectName || '',
|
subjectName: row.projectName || '',
|
||||||
approvalStatus: row.approvalStatus || '',
|
approvalStatus: row.approvalStatus || '',
|
||||||
mkEnabled: this.mkEnabled,
|
mkEnabled: this.mkEnabled,
|
||||||
tempVarData: { projectType: row.projectType || '' },
|
tempVarData: {
|
||||||
})
|
projectType: row.projectType || '',
|
||||||
)
|
changeType,
|
||||||
|
变更类型: changeType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
: request();
|
: request();
|
||||||
const loading =
|
const loading =
|
||||||
operation.action === 'approve'
|
operation.action === 'approve'
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
<template>
|
||||||
|
<mk-public-shell biz-type="temporary-credit-limit" :get-form="getForm">
|
||||||
|
<div class="temporary-credit-limit-public-view">
|
||||||
|
<div class="temporary-credit-limit-public-view__title">查看临时额度申请</div>
|
||||||
|
<template v-if="detail">
|
||||||
|
<div class="temporary-credit-limit-public-view__section-title">项目额度信息</div>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="项目名称">{{ detail.projectName || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="项目编号">{{ detail.projectCode || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="承办部门">{{ detail.undertakeDeptName || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="项目资金使用额度(万元)">
|
||||||
|
{{ formatAmount(detail.projectFundLimit) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="已使用项目资金使用额度(万元)">
|
||||||
|
{{ formatAmount(detail.usedFundLimit) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="剩余项目资金使用额度(万元)">
|
||||||
|
{{ formatAmount(detail.remainingFundLimit) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="temporary-credit-limit-public-view__section-title">临时额度信息</div>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="申请单号">{{ detail.applicationNo || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="申请临时额度(万元)">
|
||||||
|
{{ formatAmount(detail.applyLimit) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="申请有效期至">{{ detail.validUntil || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="审批状态">
|
||||||
|
{{ detail.approvalStatusName || detail.approvalStatus || '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="申请人">{{ detail.applicantName || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="申请部门">{{ detail.applyDeptName || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="备注" :span="2">{{ detail.remark || '-' }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</template>
|
||||||
|
<el-empty v-else description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
</mk-public-shell>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getMkPublicDetail } from '@/api/mk-process';
|
||||||
|
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TemporaryCreditLimitPublicView',
|
||||||
|
components: { MkPublicShell },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
detail: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadDetail();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadDetail() {
|
||||||
|
const id = this.$route.query.id;
|
||||||
|
if (!id) return;
|
||||||
|
getMkPublicDetail('temporary-credit-limit', id).then(res => {
|
||||||
|
this.detail = res.data?.data || null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatAmount(value) {
|
||||||
|
if (value === undefined || value === null || value === '' || Number(value) === -1) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
getForm() {
|
||||||
|
const detail = this.detail || {};
|
||||||
|
return {
|
||||||
|
...detail,
|
||||||
|
subject: detail.projectName || detail.applicationNo || '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.temporary-credit-limit-public-view {
|
||||||
|
padding: 16px 20px 24px;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__section-title {
|
||||||
|
position: relative;
|
||||||
|
margin: 16px 0 12px;
|
||||||
|
padding-left: 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
width: 4px;
|
||||||
|
height: 14px;
|
||||||
|
margin-top: -7px;
|
||||||
|
background: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -329,8 +329,10 @@ import {
|
|||||||
getDetail as getProjectDetail,
|
getDetail as getProjectDetail,
|
||||||
getList as getProjectList,
|
getList as getProjectList,
|
||||||
} from '@/api/business/project-apply';
|
} from '@/api/business/project-apply';
|
||||||
|
import { getMKApprovalUrl } from '@/api/system/business-process';
|
||||||
import { config, option } from '@/option/business/temporary-credit-limit';
|
import { config, option } from '@/option/business/temporary-credit-limit';
|
||||||
import { getToken, isAdministrator } from '@/utils/auth';
|
import { getToken, isAdministrator } from '@/utils/auth';
|
||||||
|
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
|
||||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||||
import { ElImageViewer } from 'element-plus';
|
import { ElImageViewer } from 'element-plus';
|
||||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||||
@@ -426,6 +428,7 @@ export default {
|
|||||||
flowBox: false,
|
flowBox: false,
|
||||||
flowUrl: '',
|
flowUrl: '',
|
||||||
processInstanceId: '',
|
processInstanceId: '',
|
||||||
|
mkEnabled: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -455,6 +458,13 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadProjectOptions();
|
this.loadProjectOptions();
|
||||||
|
loadMkEnabled()
|
||||||
|
.then(enabled => {
|
||||||
|
this.mkEnabled = !!enabled;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.mkEnabled = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
buildTableOption() {
|
buildTableOption() {
|
||||||
@@ -543,11 +553,19 @@ export default {
|
|||||||
},
|
},
|
||||||
customOperations(row) {
|
customOperations(row) {
|
||||||
return (this.config.operations || []).filter(operation => {
|
return (this.config.operations || []).filter(operation => {
|
||||||
|
if (operation.mkOnly && !this.mkEnabled) return false;
|
||||||
|
if (operation.localOnly && this.mkEnabled) return false;
|
||||||
const permission = operation.permission || `${this.config.permission}_${operation.action}`;
|
const permission = operation.permission || `${this.config.permission}_${operation.action}`;
|
||||||
const statusProp = operation.statusProp || this.config.statusProp || 'status';
|
const statusProp = operation.statusProp || this.config.statusProp || 'status';
|
||||||
|
// MK「流程」入口允许只读行查看审核页;本地审批/提交/撤回仍禁止只读
|
||||||
|
if (
|
||||||
|
row.readonly &&
|
||||||
|
['submitApproval', 'withdraw', 'approve', 'reject'].includes(operation.action)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
this.hasPermission(permission) &&
|
this.hasPermission(permission) &&
|
||||||
!row.readonly &&
|
|
||||||
(!operation.status?.length || operation.status.includes(row[statusProp]))
|
(!operation.status?.length || operation.status.includes(row[statusProp]))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -736,6 +754,10 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleOperation(operation, row) {
|
handleOperation(operation, row) {
|
||||||
|
if (operation.action === 'mkAudit') {
|
||||||
|
this.handleMkAudit(row);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (operation.action === 'flow') {
|
if (operation.action === 'flow') {
|
||||||
this.openFlow(row);
|
this.openFlow(row);
|
||||||
return;
|
return;
|
||||||
@@ -744,12 +766,49 @@ export default {
|
|||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
})
|
}).then(() => {
|
||||||
.then(() => this.api[operation.action](row.id))
|
const previousApprovalStatus = row.approvalStatus || '';
|
||||||
.then(() => {
|
const request = () => this.api[operation.action](row.id);
|
||||||
|
// 先本地提交再调 MK,避免 MK 回写 reviewing 后本地 submitApproval 被编辑校验拦住
|
||||||
|
const afterMk =
|
||||||
|
operation.action === 'submitApproval'
|
||||||
|
? request().then(() =>
|
||||||
|
submitMkApprovalFlow({
|
||||||
|
bizType: 'temporary-credit-limit',
|
||||||
|
bizId: row.id,
|
||||||
|
subjectName: row.projectName || row.applicationNo || '',
|
||||||
|
approvalStatus: previousApprovalStatus,
|
||||||
|
mkEnabled: this.mkEnabled,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
: request();
|
||||||
|
return afterMk.then(res => {
|
||||||
|
if (res?.data?.success === false) {
|
||||||
|
this.$message.error(res.data?.msg || `${operation.label}失败,请联系管理员`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.$message.success(`${operation.label}成功`);
|
this.$message.success(`${operation.label}成功`);
|
||||||
this.onLoad(this.page, this.query);
|
this.onLoad(this.page, this.query);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleMkAudit(row) {
|
||||||
|
if (!row?.id) {
|
||||||
|
this.$message.warning('临时额度申请ID为空,无法打开审核页');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getMKApprovalUrl({ bizId: String(row.id) })
|
||||||
|
.then(res => {
|
||||||
|
const url = res?.data?.data;
|
||||||
|
if (!url) {
|
||||||
|
this.$message.error('获取审核跳转地址失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.open(url, '_blank');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$message.error('获取审核跳转地址失败');
|
||||||
|
});
|
||||||
},
|
},
|
||||||
openFlow(row, loaded = false) {
|
openFlow(row, loaded = false) {
|
||||||
const processInstanceId = row.processInstanceId || row.processInstanceID || row.procInstId;
|
const processInstanceId = row.processInstanceId || row.processInstanceID || row.procInstId;
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<mk-public-shell biz-type="cert-audit-driver" :get-form="getForm">
|
||||||
|
<cert-audit-public-content ref="page" biz-type="driver" />
|
||||||
|
</mk-public-shell>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
|
||||||
|
import CertAuditPublicContent from './components/cert-audit-public-content/main.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CertAuditDriverPublicView',
|
||||||
|
components: { MkPublicShell, CertAuditPublicContent },
|
||||||
|
methods: {
|
||||||
|
getForm() {
|
||||||
|
return this.$refs.page?.getForm?.() || {};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<mk-public-shell biz-type="cert-audit-vehicle" :get-form="getForm">
|
||||||
|
<cert-audit-public-content ref="page" biz-type="vehicle" />
|
||||||
|
</mk-public-shell>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MkPublicShell from '@/views/mk/mk-public-shell.vue';
|
||||||
|
import CertAuditPublicContent from './components/cert-audit-public-content/main.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CertAuditVehiclePublicView',
|
||||||
|
components: { MkPublicShell, CertAuditPublicContent },
|
||||||
|
methods: {
|
||||||
|
getForm() {
|
||||||
|
return this.$refs.page?.getForm?.() || {};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -61,6 +61,14 @@
|
|||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<section-card title="审核意见">
|
<section-card title="审核意见">
|
||||||
|
<el-alert
|
||||||
|
v-if="mkEnabled && auditable"
|
||||||
|
type="info"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
title="已开启 MK 审核流:请在列表点击「提交 / 流程」完成审批,此处仅可查看"
|
||||||
|
class="cert-audit-detail__notice"
|
||||||
|
/>
|
||||||
<el-form label-position="right" label-width="90px" class="cert-audit-detail__form">
|
<el-form label-position="right" label-width="90px" class="cert-audit-detail__form">
|
||||||
<el-form-item label="驳回原因">
|
<el-form-item label="驳回原因">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -69,7 +77,7 @@
|
|||||||
:rows="3"
|
:rows="3"
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
:disabled="!auditable"
|
:disabled="!localAuditable"
|
||||||
placeholder="驳回时必填,不超过 200 字;通过时无需填写"
|
placeholder="驳回时必填,不超过 200 字;通过时无需填写"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -78,18 +86,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="visible = false">取消</el-button>
|
<el-button @click="visible = false">{{ mkEnabled ? '关闭' : '取消' }}</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
|
v-if="!mkEnabled"
|
||||||
type="danger"
|
type="danger"
|
||||||
:disabled="!auditable || loadFailed"
|
:disabled="!localAuditable || loadFailed"
|
||||||
:loading="rejectLoading"
|
:loading="rejectLoading"
|
||||||
@click="handleReject"
|
@click="handleReject"
|
||||||
>
|
>
|
||||||
驳回
|
驳回
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
|
v-if="!mkEnabled"
|
||||||
type="primary"
|
type="primary"
|
||||||
:disabled="!auditable || loadFailed"
|
:disabled="!localAuditable || loadFailed"
|
||||||
:loading="approveLoading"
|
:loading="approveLoading"
|
||||||
@click="handleApprove"
|
@click="handleApprove"
|
||||||
>
|
>
|
||||||
@@ -124,6 +134,11 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'driver',
|
default: 'driver',
|
||||||
},
|
},
|
||||||
|
// 开启 MK 后详情页仅查看,本地通过/驳回隐藏
|
||||||
|
mkEnabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'success'],
|
emits: ['update:modelValue', 'success'],
|
||||||
data() {
|
data() {
|
||||||
@@ -166,6 +181,9 @@ export default {
|
|||||||
auditable() {
|
auditable() {
|
||||||
return String(this.detail.status) === String(CERT_RECORD_STATUS.PENDING);
|
return String(this.detail.status) === String(CERT_RECORD_STATUS.PENDING);
|
||||||
},
|
},
|
||||||
|
localAuditable() {
|
||||||
|
return this.auditable && !this.mkEnabled;
|
||||||
|
},
|
||||||
diffKey() {
|
diffKey() {
|
||||||
return `${this.detail.id || this.recordId || ''}-${this.detail.submitTime || ''}`;
|
return `${this.detail.id || this.recordId || ''}-${this.detail.submitTime || ''}`;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,7 +39,21 @@
|
|||||||
<template #menu="{ row }">
|
<template #menu="{ row }">
|
||||||
<!-- 只有审核中的记录能审;已通过/已驳回只能查看,弹窗内也会禁用审核按钮 -->
|
<!-- 只有审核中的记录能审;已通过/已驳回只能查看,弹窗内也会禁用审核按钮 -->
|
||||||
<el-link type="primary" v-if="canAudit" @click="openDetail(row)">
|
<el-link type="primary" v-if="canAudit" @click="openDetail(row)">
|
||||||
{{ isPending(row) ? '审核' : '查看' }}
|
{{ isPending(row) ? (mkEnabled ? '查看' : '审核') : '查看' }}
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="canAudit && mkEnabled && isPending(row)"
|
||||||
|
type="primary"
|
||||||
|
@click="handleMkSubmit(row)"
|
||||||
|
>
|
||||||
|
提交
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="canAudit && mkEnabled && isPending(row)"
|
||||||
|
type="primary"
|
||||||
|
@click="handleMkAudit(row)"
|
||||||
|
>
|
||||||
|
流程
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
</avue-crud>
|
</avue-crud>
|
||||||
@@ -48,6 +62,7 @@
|
|||||||
v-model="detailVisible"
|
v-model="detailVisible"
|
||||||
:record-id="detailRecordId"
|
:record-id="detailRecordId"
|
||||||
:biz-type="bizType"
|
:biz-type="bizType"
|
||||||
|
:mk-enabled="mkEnabled"
|
||||||
@success="handleAuditSuccess"
|
@success="handleAuditSuccess"
|
||||||
/>
|
/>
|
||||||
</basic-container>
|
</basic-container>
|
||||||
@@ -56,8 +71,14 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { getPendingPage } from '@/api/transportCapacity/certification-audit';
|
import { getPendingPage } from '@/api/transportCapacity/certification-audit';
|
||||||
|
import { getMKApprovalUrl } from '@/api/system/business-process';
|
||||||
import { CERT_RECORD_STATUS, certStatusTag, formatCertDateTime } from '@/utils/cert-audit';
|
import { CERT_RECORD_STATUS, certStatusTag, formatCertDateTime } from '@/utils/cert-audit';
|
||||||
import { isAdministrator } from '@/utils/auth';
|
import { isAdministrator } from '@/utils/auth';
|
||||||
|
import {
|
||||||
|
loadMkEnabled,
|
||||||
|
resolveCertAuditMkBizType,
|
||||||
|
submitMkApprovalFlow,
|
||||||
|
} from '@/utils/mk-approval';
|
||||||
import CertAuditDetail from '../cert-audit-detail/main.vue';
|
import CertAuditDetail from '../cert-audit-detail/main.vue';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,6 +132,7 @@ export default {
|
|||||||
],
|
],
|
||||||
detailVisible: false,
|
detailVisible: false,
|
||||||
detailRecordId: '',
|
detailRecordId: '',
|
||||||
|
mkEnabled: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -122,12 +144,65 @@ export default {
|
|||||||
return this.isAdmin || this.permission?.[this.auditPermissionCode] === true;
|
return this.isAdmin || this.permission?.[this.auditPermissionCode] === true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
loadMkEnabled()
|
||||||
|
.then(enabled => {
|
||||||
|
this.mkEnabled = !!enabled;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.mkEnabled = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
certStatusTag,
|
certStatusTag,
|
||||||
formatCertDateTime,
|
formatCertDateTime,
|
||||||
isPending(row) {
|
isPending(row) {
|
||||||
return String(row.status) === String(CERT_RECORD_STATUS.PENDING);
|
return String(row.status) === String(CERT_RECORD_STATUS.PENDING);
|
||||||
},
|
},
|
||||||
|
handleMkSubmit(row) {
|
||||||
|
if (!row?.id) {
|
||||||
|
this.$message.warning('认证记录ID为空,无法提交审核流');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$confirm('确定提交该认证记录到 MK 审核流?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() =>
|
||||||
|
submitMkApprovalFlow({
|
||||||
|
bizType: resolveCertAuditMkBizType(this.bizType),
|
||||||
|
bizId: row.id,
|
||||||
|
subjectName: row.bizKey || row.driverName || '',
|
||||||
|
approvalStatus: String(row.status ?? ''),
|
||||||
|
mkEnabled: this.mkEnabled,
|
||||||
|
tempVarData: {
|
||||||
|
bizType: this.bizType,
|
||||||
|
bizKey: row.bizKey || '',
|
||||||
|
},
|
||||||
|
}).then(() => {
|
||||||
|
this.$message.success('提交成功');
|
||||||
|
this.onLoad(this.page, this.query);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
handleMkAudit(row) {
|
||||||
|
if (!row?.id) {
|
||||||
|
this.$message.warning('认证记录ID为空,无法打开审核页');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getMKApprovalUrl({ bizId: String(row.id) })
|
||||||
|
.then(res => {
|
||||||
|
const url = res?.data?.data;
|
||||||
|
if (!url) {
|
||||||
|
this.$message.error('获取审核跳转地址失败,请先提交审核流');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.open(url, '_blank');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$message.error('获取审核跳转地址失败');
|
||||||
|
});
|
||||||
|
},
|
||||||
onLoad(page, params = {}) {
|
onLoad(page, params = {}) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
getPendingPage(this.bizType, page.currentPage, page.pageSize, {
|
getPendingPage(this.bizType, page.currentPage, page.pageSize, {
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cert-audit-public-content">
|
||||||
|
<div class="cert-audit-public-content__title">{{ pageTitle }}</div>
|
||||||
|
<template v-if="detail">
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="认证对象">{{ detail.bizKey || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="审核状态">
|
||||||
|
{{ detail.statusText || certStatusTag(detail.status).label }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="提交人">{{ detail.submitUserName || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="提交时间">
|
||||||
|
{{ formatCertDateTime(detail.submitTime) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="认证类型">{{ bizTypeLabel }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="是否已认证">
|
||||||
|
{{ detail.certified ? '已认证' : '未认证' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="detail.rejectReason" label="驳回原因" :span="2">
|
||||||
|
{{ detail.rejectReason }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="cert-audit-public-content__section-title">待审内容 vs 当前生效值</div>
|
||||||
|
<cert-audit-diff
|
||||||
|
:key="diffKey"
|
||||||
|
:biz-type="bizType"
|
||||||
|
:pending-json="detail.pendingSnapshotJson"
|
||||||
|
:effective-json="detail.effectiveSnapshotJson"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<el-empty v-else description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getMkPublicDetail } from '@/api/mk-process';
|
||||||
|
import { certStatusTag, formatCertDateTime } from '@/utils/cert-audit';
|
||||||
|
import CertAuditDiff from '../cert-audit-diff/main.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CertAuditPublicContent',
|
||||||
|
components: { CertAuditDiff },
|
||||||
|
props: {
|
||||||
|
bizType: {
|
||||||
|
type: String,
|
||||||
|
default: 'driver',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
detail: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
bizTypeLabel() {
|
||||||
|
return this.bizType === 'vehicle' ? '车辆认证' : '司机认证';
|
||||||
|
},
|
||||||
|
pageTitle() {
|
||||||
|
const key = this.detail?.bizKey ? `:${this.detail.bizKey}` : '';
|
||||||
|
return `${this.bizTypeLabel}审核${key}`;
|
||||||
|
},
|
||||||
|
mkBizType() {
|
||||||
|
return this.bizType === 'vehicle' ? 'cert-audit-vehicle' : 'cert-audit-driver';
|
||||||
|
},
|
||||||
|
diffKey() {
|
||||||
|
return `${this.detail?.id || ''}-${this.detail?.submitTime || ''}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadDetail();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
certStatusTag,
|
||||||
|
formatCertDateTime,
|
||||||
|
loadDetail() {
|
||||||
|
const id = this.$route.query.id;
|
||||||
|
if (!id) return;
|
||||||
|
getMkPublicDetail(this.mkBizType, id).then(res => {
|
||||||
|
this.detail = res.data?.data || null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getForm() {
|
||||||
|
const detail = this.detail || {};
|
||||||
|
return {
|
||||||
|
...detail,
|
||||||
|
subject: detail.bizKey || '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cert-audit-public-content {
|
||||||
|
padding: 16px 20px 24px;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__section-title {
|
||||||
|
position: relative;
|
||||||
|
margin: 16px 0 12px;
|
||||||
|
padding-left: 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
width: 4px;
|
||||||
|
height: 14px;
|
||||||
|
margin-top: -7px;
|
||||||
|
background: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user