新增项目管理员角色权限控制

This commit is contained in:
wkc
2026-07-01 16:12:03 +08:00
parent 6c70149e0c
commit 64ddc362e3
33 changed files with 727 additions and 92 deletions

View File

@@ -101,10 +101,10 @@
v-if="['0', '3', '4'].includes(scope.row.status)"
size="mini"
type="text"
icon="el-icon-right"
:icon="canOperate(scope.row) ? 'el-icon-right' : 'el-icon-view'"
@click="handleEnter(scope.row)"
>
进入项目
{{ canOperate(scope.row) ? "进入项目" : "查看项目" }}
</el-button>
<template v-if="scope.row.status === '1'">
@@ -117,6 +117,7 @@
查看结果
</el-button>
<el-button
v-if="canOperate(scope.row)"
size="mini"
type="text"
icon="el-icon-refresh"
@@ -127,6 +128,7 @@
重新分析
</el-button>
<el-button
v-if="canOperate(scope.row)"
size="mini"
type="text"
icon="el-icon-folder"
@@ -228,6 +230,9 @@ export default {
handleArchive(row) {
this.$emit("archive", row);
},
canOperate(row) {
return !row || row.canOperate !== false;
},
handleSizeChange(val) {
this.$emit("pagination", {
pageNum: this.pageParams.pageNum,

View File

@@ -30,13 +30,14 @@
maxlength="500"
show-word-limit
placeholder="请填写为什么将该详情确认为证据"
:disabled="!canOperate"
/>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" :loading="submitting" @click="handleSubmit">
<el-button type="primary" :loading="submitting" :disabled="!canOperate" @click="handleSubmit">
确认入库
</el-button>
</div>
@@ -63,6 +64,10 @@ export default {
type: Object,
default: () => ({}),
},
canOperate: {
type: Boolean,
default: true,
},
},
data() {
return {
@@ -100,6 +105,10 @@ export default {
this.$emit("update:visible", false);
},
async handleSubmit() {
if (!this.canOperate) {
this.$message.warning("当前项目仅可查看,不能确认入库证据");
return;
}
if (!this.confirmReason) {
this.$message.warning("请填写确认理由/备注");
return;

View File

@@ -1,7 +1,7 @@
<template>
<div class="param-config-container" v-loading="loading" element-loading-text="加载中...">
<div v-if="isProjectTagging || isProjectArchived" class="readonly-tip">
{{ isProjectArchived ? "已归档项目暂不可修改参数" : "项目正在进行银行流水打标参数暂不可修改" }}
<div v-if="isWriteLocked" class="readonly-tip">
{{ writeLockedMessage }}
</div>
<!-- 模型参数卡片组垂直堆叠 -->
@@ -24,7 +24,7 @@
<template #default="{ row }">
<el-input
v-model="row.paramValue"
:disabled="isProjectTagging || isProjectArchived"
:disabled="isWriteLocked"
placeholder="请输入阈值"
@input="markAsModified(model.modelCode, row)"
/>
@@ -42,7 +42,7 @@
<!-- 统一保存按钮 -->
<div class="button-section" v-if="!loading && modelGroups.length > 0">
<el-button type="primary" :disabled="isProjectTagging || isProjectArchived || saving" @click="handleSaveAll" :loading="saving">
<el-button type="primary" :disabled="isWriteLocked || saving" @click="handleSaveAll" :loading="saving">
保存所有修改
</el-button>
<span v-if="modifiedCount > 0" class="modified-tip">
@@ -65,6 +65,10 @@ export default {
projectInfo: {
type: Object,
default: () => ({})
},
canOperate: {
type: Boolean,
default: true
}
},
data() {
@@ -85,6 +89,15 @@ export default {
},
isProjectArchived() {
return String(this.projectInfo.projectStatus) === "2";
},
isWriteLocked() {
return !this.canOperate || this.isProjectTagging || this.isProjectArchived;
},
writeLockedMessage() {
if (!this.canOperate) {
return "当前项目仅可查看,暂不可修改参数。";
}
return this.isProjectArchived ? "已归档项目暂不可修改参数。" : "项目正在进行银行流水打标,参数暂不可修改。";
}
},
watch: {
@@ -162,6 +175,10 @@ export default {
},
async handleSaveAll() {
if (!this.canOperate) {
this.$message.warning("当前项目仅可查看,暂不可修改参数");
return;
}
if (this.isProjectArchived) {
this.$message.warning("已归档项目暂不可修改参数");
return;

View File

@@ -58,6 +58,7 @@
:source="projectAnalysisSource"
:model-summary="projectAnalysisModelSummary"
:project-name="projectInfo.projectName"
:can-operate="canOperate"
@close="handleProjectAnalysisDialogClose"
@evidence-confirm="$emit('evidence-confirm', $event)"
/>
@@ -115,6 +116,10 @@ export default {
projectStatus: "0",
}),
},
canOperate: {
type: Boolean,
default: true,
},
},
data() {
return {

View File

@@ -112,6 +112,7 @@
:project-id="projectId"
:person="person"
:model-summary="modelSummary"
:can-operate="canOperate"
initial-graph-tab="relation"
:graph-tabs="['relation']"
/>
@@ -123,6 +124,7 @@
:project-id="projectId"
:person="person"
:model-summary="modelSummary"
:can-operate="canOperate"
/>
</el-tab-pane>
</el-tabs>
@@ -181,6 +183,10 @@ export default {
type: String,
default: "",
},
canOperate: {
type: Boolean,
default: true,
},
},
data() {
return {

View File

@@ -14,6 +14,8 @@
:show-edge-metrics="!isFundOnly"
:stacked-fund-detail-pane="isFundOnly"
hide-empty-detail-pane
:project-id="projectId"
:can-operate="canOperate"
/>
</div>
</template>
@@ -48,6 +50,10 @@ export default {
type: Array,
default: () => ["fund", "relation"],
},
canOperate: {
type: Boolean,
default: true,
},
},
computed: {
isFundOnly() {

View File

@@ -2,8 +2,8 @@
<div class="upload-data-container">
<!-- 主内容区 -->
<div class="main-content">
<div v-if="isProjectTagging || isProjectArchived" class="tagging-lock-tip">
{{ isProjectArchived ? "项目已归档暂不可上传或拉取数据" : "项目正在进行银行流水打标暂不可上传或拉取数据" }}
<div v-if="isWriteLocked" class="tagging-lock-tip">
{{ writeLockedMessage }}
</div>
<!-- 文件上传记录列表 -->
@@ -14,7 +14,7 @@
<el-button
size="small"
icon="el-icon-upload2"
:disabled="isProjectTagging || isProjectArchived"
:disabled="isWriteLocked"
@click="handleOpenBatchUploadDialog"
>
上传流水
@@ -22,7 +22,7 @@
<el-button
size="small"
icon="el-icon-download"
:disabled="isProjectTagging || isProjectArchived"
:disabled="isWriteLocked"
@click="handleFetchBankInfo"
>
拉取本行信息
@@ -30,7 +30,7 @@
<el-button
size="small"
icon="el-icon-upload2"
:disabled="isProjectTagging || isProjectArchived"
:disabled="isWriteLocked"
@click="handleGoCreditInfoPage"
>
征信导入
@@ -125,7 +125,7 @@
v-model="pullBankInfoForm.idCardText"
type="textarea"
:rows="5"
:disabled="isProjectTagging || isProjectArchived"
:disabled="isWriteLocked"
placeholder="仅支持英文逗号分隔"
/>
<div class="pull-bank-field-tip">
@@ -143,7 +143,7 @@
:show-file-list="false"
:file-list="idCardFileList"
accept=".xls,.xlsx"
:disabled="isProjectTagging || isProjectArchived"
:disabled="isWriteLocked"
:on-change="handleIdCardFileChange"
:on-remove="handleIdCardFileRemove"
>
@@ -175,7 +175,7 @@
<el-date-picker
class="pull-bank-range-picker"
v-model="pullBankInfoForm.dateRange"
:disabled="isProjectTagging || isProjectArchived"
:disabled="isWriteLocked"
type="daterange"
value-format="yyyy-MM-dd"
:picker-options="pullBankInfoDatePickerOptions"
@@ -189,7 +189,7 @@
<el-button @click="pullBankInfoDialogVisible = false">取消</el-button>
<el-button
type="primary"
:disabled="isProjectTagging || isProjectArchived"
:disabled="isWriteLocked"
:loading="pullBankInfoLoading"
@click="handleConfirmPullBankInfo"
>
@@ -210,7 +210,7 @@
drag
action="#"
multiple
:disabled="isProjectTagging || isProjectArchived"
:disabled="isWriteLocked"
:auto-upload="false"
:on-change="handleBatchFileChange"
:show-file-list="false"
@@ -250,7 +250,7 @@
<el-button @click="batchUploadDialogVisible = false">取消</el-button>
<el-button
type="primary"
:disabled="isProjectTagging || isProjectArchived || selectedFiles.length === 0"
:disabled="isWriteLocked || selectedFiles.length === 0"
:loading="uploadLoading"
@click="handleBatchUpload"
>开始上传</el-button
@@ -291,6 +291,10 @@ export default {
projectStatus: "0",
}),
},
canOperate: {
type: Boolean,
default: true,
},
},
data() {
return {
@@ -358,6 +362,17 @@ export default {
isProjectArchived() {
return String(this.projectInfo.projectStatus) === "2";
},
isWriteLocked() {
return !this.canOperate || this.isProjectTagging || this.isProjectArchived;
},
writeLockedMessage() {
if (!this.canOperate) {
return "当前项目仅可查看,暂不可上传或拉取数据。";
}
return this.isProjectArchived
? "项目已归档,暂不可上传或拉取数据。"
: "项目正在进行银行流水打标,暂不可上传或拉取数据。";
},
isReportDisabled() {
return ["0", "3", "4"].includes(String(this.projectInfo.projectStatus));
},
@@ -406,13 +421,15 @@ export default {
this.$emit("menu-change", { key: "overview", route: "overview" });
},
handleGoCreditInfoPage() {
if (this.isProjectTagging || this.isProjectArchived) {
if (this.isWriteLocked) {
this.showWriteLockedMessage();
return;
}
this.$router.push("/maintain/creditInfo");
},
handleOpenBatchUploadDialog() {
if (this.isProjectTagging || this.isProjectArchived) {
if (this.isWriteLocked) {
this.showWriteLockedMessage();
return;
}
this.batchUploadDialogVisible = true;
@@ -450,7 +467,8 @@ export default {
return Array.from(new Set(merged)).join(", ");
},
async handleIdCardFileChange(file, fileList) {
if (this.isProjectTagging || this.isProjectArchived) {
if (this.isWriteLocked) {
this.showWriteLockedMessage();
return;
}
const latestFile = (fileList || []).slice(-1);
@@ -570,12 +588,8 @@ export default {
return this.parseIdCardText(this.pullBankInfoForm.idCardText);
},
async handleConfirmPullBankInfo() {
if (this.isProjectTagging || this.isProjectArchived) {
this.$message.warning(
this.isProjectArchived
? "项目已归档,暂不可上传或拉取数据"
: "项目正在进行银行流水打标,暂不可上传或拉取数据"
);
if (this.isWriteLocked) {
this.showWriteLockedMessage();
return;
}
const idCards = this.buildFinalIdCardList();
@@ -634,12 +648,8 @@ export default {
},
/** 拉取本行信息 */
handleFetchBankInfo() {
if (this.isProjectTagging || this.isProjectArchived) {
this.$message.warning(
this.isProjectArchived
? "项目已归档,暂不可上传或拉取数据"
: "项目正在进行银行流水打标,暂不可上传或拉取数据"
);
if (this.isWriteLocked) {
this.showWriteLockedMessage();
return;
}
this.resetPullBankInfoForm();
@@ -685,6 +695,10 @@ export default {
/** 批量上传的文件选择变化 */
handleBatchFileChange(file, fileList) {
if (this.isWriteLocked) {
this.showWriteLockedMessage();
return;
}
if (fileList.length > 100) {
this.$message.warning("最多上传100个文件");
fileList = fileList.slice(0, 100);
@@ -712,17 +726,17 @@ export default {
/** 删除已选文件 */
handleRemoveFile(index) {
if (this.isWriteLocked) {
this.showWriteLockedMessage();
return;
}
this.selectedFiles.splice(index, 1);
},
/** 开始批量上传 */
async handleBatchUpload() {
if (this.isProjectTagging || this.isProjectArchived) {
this.$message.warning(
this.isProjectArchived
? "项目已归档,暂不可上传或拉取数据"
: "项目正在进行银行流水打标,暂不可上传或拉取数据"
);
if (this.isWriteLocked) {
this.showWriteLockedMessage();
return;
}
if (this.selectedFiles.length === 0) {
@@ -868,7 +882,11 @@ export default {
},
getRowAction(row) {
return getUploadFileAction(row);
const action = getUploadFileAction(row);
if (!this.canOperate && action && action.key === "delete") {
return null;
}
return action;
},
handleRowAction(row) {
@@ -895,6 +913,10 @@ export default {
},
async handleDeleteFile(row) {
if (this.isWriteLocked) {
this.showWriteLockedMessage();
return;
}
try {
await this.$confirm(
"删除该文件后将同步删除流水分析平台中的文件,并清除本系统中该文件对应的所有银行流水数据,项目内流水也将重新打标,是否继续?",
@@ -923,6 +945,9 @@ export default {
this.$message.error("删除失败:" + ((error && error.message) || "未知错误"));
}
},
showWriteLockedMessage() {
this.$message.warning(this.writeLockedMessage);
},
hasActivePollingRecords() {
return (

View File

@@ -80,6 +80,7 @@
plain
icon="el-icon-plus"
title="新增主体节点及手工资金流向边"
:disabled="!canOperate"
@click="handleManualFlowCreate"
>
新增资金流向
@@ -503,6 +504,14 @@ export default {
type: String,
default: "",
},
projectId: {
type: [String, Number],
default: null,
},
canOperate: {
type: Boolean,
default: true,
},
autoScroll: {
type: Boolean,
default: true,
@@ -818,6 +827,10 @@ export default {
this.renderChart();
},
handleManualFlowCreate() {
if (!this.canOperate) {
this.$message.warning("当前项目仅可查看,不能新增手工资金流向");
return;
}
const centerNode = this.graph.centerNode || this.selectedNode;
if (!centerNode || !centerNode.objectKey) {
this.$message.warning("请先查询或选择一个起点主体");
@@ -843,6 +856,10 @@ export default {
});
},
submitManualFlow() {
if (!this.canOperate) {
this.$message.warning("当前项目仅可查看,不能新增手工资金流向");
return;
}
if (!this.$refs.manualForm) {
return;
}
@@ -852,7 +869,10 @@ export default {
}
this.manualDialog.saving = true;
try {
await saveFundGraphManualEdge(this.manualForm);
await saveFundGraphManualEdge({
...this.manualForm,
projectId: this.projectId,
});
this.$message.success("手工资金流向已保存");
this.manualDialog.visible = false;
await this.loadGraph();
@@ -884,6 +904,7 @@ export default {
try {
const response = await getFundGraph({
...this.query,
projectId: this.projectId,
keyword: node.nodeName,
objectKey: node.objectKey,
});
@@ -923,7 +944,7 @@ export default {
}
this.graphLoading = true;
try {
const response = await getFundGraph(this.query);
const response = await getFundGraph(this.buildFundGraphQuery());
this.graph = (response && response.data) || emptyGraph();
this.expandedObjectKeys = [];
this.clearSelection();
@@ -966,7 +987,7 @@ export default {
}
this.relationGraphLoading = true;
try {
const response = await getRelationGraph(this.relationQuery);
const response = await getRelationGraph(this.buildRelationGraphQuery());
this.relationGraph = (response && response.data) || emptyGraph();
this.clearRelationSelection();
} catch (error) {
@@ -998,6 +1019,7 @@ export default {
this.suspectedEnterpriseDialog.loading = true;
try {
const response = await getRelationGraphSuspectedEnterprises({
projectId: this.projectId,
personName: node.nodeName,
certNo: node.idNumber,
limit: 10,
@@ -1751,6 +1773,7 @@ export default {
this.detailLoading = true;
try {
const response = await getFundGraphEdgeDetail({
projectId: this.projectId,
fromKey: selectedEdge.fromKey,
toKey: selectedEdge.toKey,
direction: selectedEdge.direction,
@@ -1781,6 +1804,20 @@ export default {
}
}
},
buildFundGraphQuery(extra = {}) {
return {
...this.query,
...extra,
projectId: this.projectId,
};
},
buildRelationGraphQuery(extra = {}) {
return {
...this.relationQuery,
...extra,
projectId: this.projectId,
};
},
mergeGraph(currentGraph, nextGraph) {
const nodeMap = new Map();
const edgeMap = new Map();

View File

@@ -80,6 +80,7 @@
:is="currentComponent"
:project-id="projectId"
:project-info="projectInfo"
:can-operate="canOperate"
@menu-change="handleMenuChange"
@refresh-project="handleRefreshProject"
@data-uploaded="handleDataUploaded"
@@ -94,6 +95,7 @@
<evidence-confirm-dialog
:visible.sync="evidenceConfirmVisible"
:payload="evidencePayload"
:can-operate="canOperate"
@saved="handleEvidenceSaved"
/>
<evidence-drawer
@@ -162,6 +164,7 @@ export default {
projectStatus: "0",
latestTagTaskErrorMessage: "",
latestTagTaskEndTime: "",
canOperate: true,
},
evidenceConfirmVisible: false,
evidenceDrawerVisible: false,
@@ -177,6 +180,9 @@ export default {
isProjectArchived() {
return String(this.projectInfo.projectStatus) === "2";
},
canOperate() {
return this.projectInfo.canOperate !== false;
},
isProjectTagFailed() {
return String(this.projectInfo.projectStatus) === "4";
},
@@ -200,12 +206,10 @@ export default {
this.initActiveTabFromRoute();
},
"projectInfo.projectStatus"() {
this.syncProjectStatusPolling();
const accessibleTab = this.resolveAccessibleTab(this.activeTab);
if (accessibleTab !== this.activeTab) {
this.setActiveTab(accessibleTab);
this.syncRouteTab(accessibleTab);
}
this.syncAccessibleTab();
},
canOperate() {
this.syncAccessibleTab();
},
},
created() {
@@ -219,6 +223,14 @@ export default {
this.stopProjectStatusPolling();
},
methods: {
syncAccessibleTab() {
this.syncProjectStatusPolling();
const accessibleTab = this.resolveAccessibleTab(this.activeTab);
if (accessibleTab !== this.activeTab) {
this.setActiveTab(accessibleTab);
this.syncRouteTab(accessibleTab);
}
},
initActiveTabFromRoute() {
const tab = (this.$route.query && this.$route.query.tab) || "";
const validTabs = ["upload", "config", "overview", "special", "detail"];
@@ -230,7 +242,7 @@ export default {
}
},
isArchiveLockedTab(tab) {
return this.isProjectArchived && ["upload", "config"].includes(tab);
return (this.isProjectArchived || !this.canOperate) && ["upload", "config"].includes(tab);
},
resolveAccessibleTab(tab) {
if (this.isArchiveLockedTab(tab)) {
@@ -295,6 +307,7 @@ export default {
};
this.updatePageTitle();
this.syncProjectStatusPolling();
this.syncAccessibleTab();
return this.projectInfo;
} catch (error) {
if (!silent) {
@@ -436,10 +449,12 @@ export default {
/** 菜单选择事件 */
handleMenuSelect(index) {
if (this.isArchiveLockedTab(index)) {
this.$message.warning(this.canOperate ? "已归档项目暂不可进入该页签" : "当前项目仅可查看,不能进入写入页签");
return;
}
console.log("菜单选择:", index);
this.setActiveTab(index);
this.syncRouteTab(index);
},
/** UploadData 组件:菜单切换 */
handleMenuChange({ key, route }) {
@@ -489,6 +504,10 @@ export default {
this.initPageData();
},
handleEvidenceConfirm(payload) {
if (!this.canOperate) {
this.$message.warning("当前项目仅可查看,不能确认入库证据");
return;
}
this.evidencePayload = {
projectId: this.projectId,
...(payload || {}),

View File

@@ -3,7 +3,7 @@
<!-- 页面标题 -->
<div class="page-header">
<h2 class="page-title">初核项目管理</h2>
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新建项目</el-button>
<el-button v-hasPermi="['ccdi:project:add']" type="primary" icon="el-icon-plus" @click="handleAdd">新建项目</el-button>
</div>
<!-- 搜索和操作区 -->
@@ -29,6 +29,7 @@
<!-- 快捷入口区 -->
<quick-entry
v-hasPermi="['ccdi:project:add']"
@import-history="handleImport"
@create-quarterly="handleCreateQuarterly"
@create-employee="handleCreateEmployee"
@@ -245,7 +246,8 @@ export default {
/** 进入项目 */
handleEnter(row) {
this.$router.push({
path: `ccdiProject/detail/${row.projectId}`,
path: `/ccdiProject/detail/${row.projectId}`,
query: { tab: this.canOperate(row) ? "upload" : "overview" },
});
// this.$modal.msgSuccess("进入项目: " + row.projectName);
},
@@ -258,6 +260,10 @@ export default {
},
/** 重新分析 */
async handleReAnalyze(row) {
if (!this.canOperate(row)) {
this.$modal.msgWarning("当前项目仅可查看,不能重新分析")
return
}
const projectKey = String(row.projectId)
if (this.reAnalyzeLoadingMap[projectKey]) {
return
@@ -286,6 +292,10 @@ export default {
},
/** 归档项目 */
handleArchive(row) {
if (!this.canOperate(row)) {
this.$modal.msgWarning("当前项目仅可查看,不能归档")
return
}
this.currentArchiveProject = row;
this.archiveDialogVisible = true;
},
@@ -302,6 +312,9 @@ export default {
this.$modal.msgError(message)
}
},
canOperate(row) {
return !row || row.canOperate !== false
},
},
};
</script>