Files
tms-erp-web/src/views/business/project-change.vue
T

367 lines
11 KiB
Vue

<template>
<basic-container class="project-change-page">
<avue-crud
:option="tableOption"
:table-loading="loading"
:data="data"
v-model:page="page"
v-model="tableForm"
ref="crud"
:permission="permissionList"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template #menu-left>
<el-button
type="danger"
icon="el-icon-delete"
plain
v-if="hasPermission(`${config.permission}_delete`)"
@click="handleDelete"
>
批量删除
</el-button>
</template>
<template #approvalStatus="{ row }">
{{ row.approvalStatusName || statusLabel(row.approvalStatus) }}
</template>
<template #syncStatus="{ row }">
<el-tag v-if="row.syncStatus" :type="syncTagType(row.syncStatus)" class="status-text">
{{ syncLabel(row.syncStatus) }}
</el-tag>
<span v-else>-</span>
</template>
<template #menu="{ row }">
<el-link
type="primary"
v-if="canView(row)"
@click="openForm('view', row)"
>
查看
</el-link>
<el-link type="primary" v-if="canEdit(row)" @click="openForm('edit', row)">
编辑
</el-link>
<el-link
type="danger"
v-if="canDelete(row)"
@click="rowDel(row)"
>
删除
</el-link>
<el-link
v-for="operation in customOperations(row)"
:key="operation.action"
:type="operation.type === 'warning' ? 'warning' : operation.type === 'success' ? 'success' : 'primary'"
@click="handleCustomOperation(operation, row)"
>
{{ operation.label }}
</el-link>
</template>
</avue-crud>
</basic-container>
</template>
<script>
import * as api from '@/api/business/project-change';
import { getMKApprovalUrl } from '@/api/system/business-process';
import { config, option } from '@/option/business/project-change';
import { approvalStatusOptions } from '@/option/business/common';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
import { isAdministrator } from '@/utils/auth';
import { mapGetters } from 'vuex';
const projectChangeFormRoute = '/business/project-change/form';
const projectChangeListRefreshKey = 'project-change-list-refresh';
export default {
name: 'ProjectChange',
data() {
return {
api,
config,
tableForm: {},
tableOption: this.buildTableOption(),
loading: true,
data: [],
page: {
pageSize: 10,
currentPage: 1,
total: 0,
},
query: {},
selectionList: [],
mkEnabled: false,
};
},
computed: {
...mapGetters(['permission', 'userInfo', 'website']),
permissionList() {
return {
addBtn: false,
viewBtn: false,
delBtn: false,
editBtn: false,
};
},
isAdmin() {
return isAdministrator(this.userInfo);
},
ids() {
return this.selectionList.map(item => item.id).join(',');
},
},
created() {
loadMkEnabled()
.then(enabled => {
this.mkEnabled = enabled;
})
.catch(() => {
this.mkEnabled = false;
});
},
activated() {
if (sessionStorage.getItem(projectChangeListRefreshKey)) {
sessionStorage.removeItem(projectChangeListRefreshKey);
this.onLoad(this.page, this.query);
}
},
methods: {
buildTableOption() {
return {
...option,
addBtn: false,
viewBtn: false,
editBtn: false,
delBtn: false,
menuWidth: 320,
column: (option.column || []).map(column => ({
...column,
display: false,
align: 'center',
headerAlign: 'center',
})),
};
},
hasPermission(code) {
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
},
statusLabel(status) {
return approvalStatusOptions.find(item => item.value === status)?.label || status || '-';
},
syncLabel(value) {
return { unsynced: '待推送', synced: '已推送', failed: '推送失败' }[value] || value;
},
syncTagType(value) {
return { unsynced: 'info', synced: 'success', failed: 'danger' }[value] || 'info';
},
inStatus(row, statusList) {
if (!statusList || !statusList.length) return true;
return statusList.includes(row.approvalStatus);
},
canEdit(row) {
return (
this.hasPermission(`${this.config.permission}_edit`) &&
!row.readonly &&
this.inStatus(row, this.config.editStatus)
);
},
canDelete(row) {
return (
this.hasPermission(`${this.config.permission}_delete`) &&
!row.readonly &&
this.inStatus(row, this.config.deleteStatus)
);
},
canView(row) {
return (
this.hasPermission(`${this.config.permission}_view`) &&
this.inStatus(row, this.config.viewStatus)
);
},
canOperation(operation, row) {
if (operation.permission && !this.hasPermission(operation.permission)) return false;
if (row.readonly && ['submitApproval', 'withdraw', 'approve', 'reject'].includes(operation.action)) {
return false;
}
return this.inStatus(row, operation.status);
},
customOperations(row) {
return (this.config.operations || []).filter(operation => {
if (operation.mkOnly && !this.mkEnabled) return false;
if (operation.localOnly && this.mkEnabled) return false;
return this.canOperation(operation, row);
});
},
openForm(mode, row = {}) {
const query = { mode, id: row.id };
if (mode === 'edit') query.name = '编辑项目变更';
else if (mode === 'view') query.name = '查看项目变更';
this.$router.push({ path: projectChangeFormRoute, query });
},
handleCustomOperation(operation, row) {
if (operation.action === 'mkAudit') {
this.handleMkAudit(row);
return;
}
const run = () => {
const request = () => this.api[operation.action](row.id);
// 先本地提交再调 MK,避免 MK 回写 reviewing 后本地 submitApproval 被编辑校验拦住。
const afterMk =
operation.action === 'submitApproval'
? request().then(() =>
submitMkApprovalFlow({
bizType: 'project-change',
bizId: row.id,
subjectName: row.projectName || '',
approvalStatus: row.approvalStatus || '',
mkEnabled: this.mkEnabled,
tempVarData: { projectType: row.projectType || '' },
})
)
: request();
const loading =
operation.action === 'approve'
? this.$loading({
lock: true,
text: '正在推送合规,请稍候…',
background: 'rgba(255, 255, 255, 0.6)',
})
: null;
afterMk
.then(res => {
if (operation.action === 'approve' && res.data?.data === false) {
this.$message.warning(res.data?.msg);
this.onLoad(this.page, this.query);
return;
}
if (res.data?.success === false) {
this.$message.error(res.data?.msg || `${operation.label}失败,请联系管理员`);
return;
}
this.$message.success(`${operation.label}成功`);
this.onLoad(this.page, this.query);
})
.finally(() => loading && loading.close());
};
this.$confirm(operation.confirm || `确定${operation.label}该${this.config.title}?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => run());
},
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('获取审核跳转地址失败');
});
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
searchChange(params, done) {
this.query = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
selectionChange(list) {
this.selectionList = list;
},
selectionClear() {
this.selectionList = [];
this.$refs.crud?.toggleSelection?.();
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.query);
},
rowDel(row) {
this.$confirm('确定将选择数据删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.api.remove(row.id).then(() => {
this.$message.success('删除成功!');
this.onLoad(this.page, this.query);
});
});
},
handleDelete() {
if (!this.selectionList.length) {
this.$message.warning('请选择至少一条数据');
return;
}
this.$confirm('确定将选择数据删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.api.remove(this.ids).then(() => {
this.$message.success('删除成功!');
this.selectionClear();
this.onLoad(this.page, this.query);
});
});
},
onLoad(page, params = {}) {
this.loading = true;
const values = {
...this.query,
...params,
};
this.api
.getList(page.currentPage, page.pageSize, values)
.then(res => {
const data = res.data.data || {};
this.page.total = data.total || 0;
this.data = data.records || [];
this.loading = false;
this.selectionClear();
})
.catch(() => {
this.loading = false;
});
},
},
};
</script>
<style scoped lang="scss">
.project-change-page {
:deep(.avue-crud__menu) {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
}
</style>