573 lines
14 KiB
Vue
573 lines
14 KiB
Vue
<template>
|
||
<div class="app-container dpc-detail-container">
|
||
<!-- 原页面头部 (已隐藏,使用UploadData组件的头部) -->
|
||
<div class="detail-header">
|
||
<div class="header-left">
|
||
<el-button size="small" icon="el-icon-back" @click="handleBack"
|
||
>返回</el-button
|
||
>
|
||
<div class="title-section">
|
||
<div class="page-title">
|
||
<h2>
|
||
{{ projectInfo.projectName }}
|
||
</h2>
|
||
<el-tag
|
||
:type="getStatusType(projectInfo.projectStatus)"
|
||
size="small"
|
||
>
|
||
{{ getStatusLabel(projectInfo.projectStatus) }}
|
||
</el-tag>
|
||
</div>
|
||
|
||
<p class="update-time">
|
||
最后更新时间:{{ formatUpdateTime(projectInfo.updateTime) }}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="header-right">
|
||
<el-menu
|
||
:default-active="activeTab"
|
||
mode="horizontal"
|
||
@select="handleMenuSelect"
|
||
class="nav-menu"
|
||
>
|
||
<el-menu-item index="upload">上传数据</el-menu-item>
|
||
<el-menu-item index="config">参数配置</el-menu-item>
|
||
<el-menu-item index="overview">结果总览</el-menu-item>
|
||
<el-menu-item index="special">专项排查</el-menu-item>
|
||
<el-menu-item index="detail">流水明细查询</el-menu-item>
|
||
</el-menu>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 动态组件渲染区域 -->
|
||
<component
|
||
:is="currentComponent"
|
||
:project-id="projectId"
|
||
:project-info="projectInfo"
|
||
@menu-change="handleMenuChange"
|
||
@data-uploaded="handleDataUploaded"
|
||
@name-selected="handleNameSelected"
|
||
@generate-report="handleGenerateReport"
|
||
@fetch-bank-info="handleFetchBankInfo"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import UploadData from "./components/detail/UploadData";
|
||
import ParamConfig from "./components/detail/ParamConfig";
|
||
import PreliminaryCheck from "./components/detail/PreliminaryCheck";
|
||
import SpecialCheck from "./components/detail/SpecialCheck";
|
||
import DetailQuery from "./components/detail/DetailQuery";
|
||
import {getProject} from "@/api/ccdiProject";
|
||
|
||
export default {
|
||
name: "ProjectDetail",
|
||
components: {
|
||
UploadData,
|
||
ParamConfig,
|
||
PreliminaryCheck,
|
||
SpecialCheck,
|
||
DetailQuery,
|
||
},
|
||
data() {
|
||
return {
|
||
// 当前激活的菜单项索引
|
||
activeTab: "upload",
|
||
// 当前显示的组件名称
|
||
currentComponent: "UploadData",
|
||
// 项目ID
|
||
projectId: this.$route.params.projectId,
|
||
// 项目信息
|
||
projectInfo: {
|
||
projectId: this.$route.params.projectId,
|
||
projectName: "",
|
||
projectDesc: "",
|
||
createTime: "",
|
||
updateTime: "",
|
||
startDate: "",
|
||
endDate: "",
|
||
targetCount: 0,
|
||
warningCount: 0,
|
||
warningThreshold: 60,
|
||
projectStatus: "0",
|
||
},
|
||
};
|
||
},
|
||
watch: {
|
||
"$route.params.projectId"(newId) {
|
||
if (newId) {
|
||
this.projectId = newId;
|
||
this.projectInfo.projectId = newId;
|
||
this.initActiveTabFromRoute();
|
||
this.initPageData();
|
||
}
|
||
},
|
||
"$route.query.tab"() {
|
||
this.initActiveTabFromRoute();
|
||
},
|
||
},
|
||
created() {
|
||
// 初始化页面数据
|
||
this.initActiveTabFromRoute();
|
||
this.initPageData();
|
||
},
|
||
methods: {
|
||
initActiveTabFromRoute() {
|
||
const tab = (this.$route.query && this.$route.query.tab) || "";
|
||
const validTabs = ["upload", "config", "overview", "special", "detail"];
|
||
const targetTab = validTabs.includes(tab) ? tab : "upload";
|
||
this.setActiveTab(targetTab);
|
||
},
|
||
setActiveTab(index) {
|
||
this.activeTab = index;
|
||
const componentMap = {
|
||
upload: "UploadData",
|
||
config: "ParamConfig",
|
||
overview: "PreliminaryCheck",
|
||
special: "SpecialCheck",
|
||
detail: "DetailQuery",
|
||
};
|
||
this.currentComponent = componentMap[index] || "UploadData";
|
||
},
|
||
/** 初始化页面数据 */
|
||
initPageData() {
|
||
// 这里应该从API获取项目详细信息
|
||
if (!this.projectId) {
|
||
return;
|
||
}
|
||
this.projectInfo.projectName = "";
|
||
this.updatePageTitle();
|
||
getProject(this.projectId)
|
||
.then((res) => {
|
||
const data = res.data || {};
|
||
this.projectInfo = {
|
||
...this.projectInfo,
|
||
...data,
|
||
projectId: data.projectId || this.projectId,
|
||
projectName: data.projectName || "",
|
||
projectDesc: data.projectDesc || data.description || "",
|
||
projectStatus: String(
|
||
data.projectStatus !== undefined && data.projectStatus !== null
|
||
? data.projectStatus
|
||
: data.status !== undefined && data.status !== null
|
||
? data.status
|
||
: this.projectInfo.projectStatus
|
||
),
|
||
};
|
||
this.updatePageTitle();
|
||
})
|
||
.catch(() => {
|
||
this.$message.error("Failed to load project details");
|
||
this.updatePageTitle();
|
||
});
|
||
},
|
||
updatePageTitle() {
|
||
const title = this.projectInfo.projectName || `ProjectDetail-${this.projectId}`;
|
||
this.$route.meta.title = title;
|
||
this.$store.dispatch("settings/setTitle", title);
|
||
this.$store.dispatch("tagsView/updateVisitedView", {
|
||
path: this.$route.path,
|
||
title,
|
||
});
|
||
},
|
||
/** 格式化更新时间 */
|
||
formatUpdateTime(time) {
|
||
if (!time) return "-";
|
||
const date = new Date(time);
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||
const day = String(date.getDate()).padStart(2, "0");
|
||
const hours = String(date.getHours()).padStart(2, "0");
|
||
const minutes = String(date.getMinutes()).padStart(2, "0");
|
||
const seconds = String(date.getSeconds()).padStart(2, "0");
|
||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||
},
|
||
/** 模拟项目信息 */
|
||
mockProjectInfo() {
|
||
// 模拟数据,实际应该调用API
|
||
this.projectInfo = {
|
||
projectId: this.projectId,
|
||
projectName: "2024年Q1初核项目",
|
||
projectDesc: "第一季度员工异常行为排查",
|
||
createTime: "2024-01-01 10:00:00",
|
||
updateTime: new Date().toISOString(),
|
||
startDate: "2024-01-01",
|
||
endDate: "2024-03-31",
|
||
targetCount: 500,
|
||
warningCount: 15,
|
||
warningThreshold: 60,
|
||
projectStatus: "0",
|
||
};
|
||
},
|
||
/** 获取状态类型 */
|
||
getStatusType(status) {
|
||
const statusMap = {
|
||
0: "primary", // 进行中
|
||
1: "success", // 已完成
|
||
2: "info", // 已归档
|
||
};
|
||
return statusMap[status] || "info";
|
||
},
|
||
/** 获取状态标签 */
|
||
getStatusLabel(status) {
|
||
const statusMap = {
|
||
0: "进行中",
|
||
1: "已完成",
|
||
2: "已归档",
|
||
};
|
||
return statusMap[status] || "未知";
|
||
},
|
||
/** 标签页切换 */
|
||
handleTabChange(tab) {
|
||
console.log("切换到标签页:", tab.name);
|
||
},
|
||
/** 返回列表页 */
|
||
handleBack() {
|
||
this.$router.push("/ccdiProject");
|
||
},
|
||
/** 菜单选择事件 */
|
||
handleMenuSelect(index) {
|
||
console.log("菜单选择:", index);
|
||
this.setActiveTab(index);
|
||
},
|
||
/** UploadData 组件:菜单切换 */
|
||
handleMenuChange({ key, route }) {
|
||
console.log("切换到菜单:", key, route);
|
||
// 直接触发菜单选择
|
||
this.handleMenuSelect(route);
|
||
},
|
||
/** UploadData 组件:数据上传完成 */
|
||
handleDataUploaded({ type }) {
|
||
console.log("数据上传完成:", type);
|
||
this.$message.success(`${type} 数据上传成功`);
|
||
},
|
||
/** UploadData 组件:名单选择完成 */
|
||
handleNameSelected(nameList) {
|
||
console.log("名单选择完成:", nameList);
|
||
this.$message.success("名单选择成功");
|
||
},
|
||
/** UploadData 组件:生成报告 */
|
||
handleGenerateReport() {
|
||
console.log("生成报告");
|
||
// this.$message.info("生成报告功能开发中");
|
||
},
|
||
/** UploadData 组件:拉取本行信息 */
|
||
handleFetchBankInfo() {
|
||
console.log("拉取本行信息");
|
||
this.$message.info("拉取本行信息功能开发中");
|
||
},
|
||
/** 数据上传完成 */
|
||
handleDataUploaded() {
|
||
console.log("数据上传完成");
|
||
this.$message.success("数据上传成功");
|
||
},
|
||
/** 刷新页面 */
|
||
handleRefresh() {
|
||
this.initPageData();
|
||
this.$message.success("刷新成功");
|
||
},
|
||
/** 导出报告 */
|
||
handleExport() {
|
||
console.log("导出报告");
|
||
this.$message.info("报告导出功能开发中");
|
||
},
|
||
/** 完成项目 */
|
||
handleComplete() {
|
||
this.$confirm("确定要完成当前项目吗?", "提示", {
|
||
confirmButtonText: "确定",
|
||
cancelButtonText: "取消",
|
||
type: "warning",
|
||
})
|
||
.then(() => {
|
||
this.$message.success("项目已完成");
|
||
this.projectInfo.projectStatus = "1";
|
||
})
|
||
.catch(() => {
|
||
this.$message.info("已取消");
|
||
});
|
||
},
|
||
/** 归档项目 */
|
||
handleArchive() {
|
||
this.$confirm(
|
||
"确定要归档当前项目吗?归档后将不能进行修改操作。",
|
||
"警告",
|
||
{
|
||
confirmButtonText: "确定",
|
||
cancelButtonText: "取消",
|
||
type: "warning",
|
||
}
|
||
)
|
||
.then(() => {
|
||
this.$message.success("项目已归档");
|
||
this.projectInfo.projectStatus = "2";
|
||
})
|
||
.catch(() => {
|
||
this.$message.info("已取消");
|
||
});
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.dpc-detail-container {
|
||
padding: 16px;
|
||
background: #f0f2f5;
|
||
min-height: calc(100vh - 84px);
|
||
}
|
||
|
||
.detail-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
padding: 16px 20px;
|
||
background: #ffffff;
|
||
border-radius: 4px;
|
||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||
|
||
.header-left {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
|
||
.el-button {
|
||
padding: 8px 12px;
|
||
font-size: 13px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.title-section {
|
||
.page-title {
|
||
display: flex;
|
||
align-items: center;
|
||
h2 {
|
||
margin: 0 0 4px 0;
|
||
font-size: 20px;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
margin-right: 5px;
|
||
}
|
||
}
|
||
|
||
.update-time {
|
||
margin: 0;
|
||
font-size: 13px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
}
|
||
|
||
.header-right {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.nav-menu {
|
||
// 移除默认背景色和边框
|
||
background-color: transparent;
|
||
border-bottom: none;
|
||
|
||
// 菜单项基础样式
|
||
.el-menu-item,
|
||
.el-submenu__title {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
padding: 0 16px;
|
||
height: 40px;
|
||
line-height: 40px;
|
||
border-bottom: 2px solid transparent;
|
||
|
||
&:hover {
|
||
background-color: #f5f7fa;
|
||
color: #303133;
|
||
}
|
||
}
|
||
|
||
// 子菜单容器高度统一
|
||
.el-submenu {
|
||
height: 40px;
|
||
line-height: 40px;
|
||
}
|
||
|
||
// 激活状态:底部下划线 + 蓝色文字
|
||
.el-menu-item.is-active {
|
||
color: #1890ff;
|
||
border-bottom: 2px solid #1890ff;
|
||
background-color: transparent;
|
||
}
|
||
|
||
// 下拉菜单激活状态
|
||
.el-submenu.is-active > .el-submenu__title {
|
||
color: #1890ff;
|
||
border-bottom: 2px solid #1890ff;
|
||
}
|
||
|
||
// 下拉菜单图标
|
||
.el-submenu__icon-arrow {
|
||
margin-left: 4px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.info-card {
|
||
margin-bottom: 16px;
|
||
|
||
:deep(.el-card__body) {
|
||
padding: 20px;
|
||
}
|
||
}
|
||
|
||
.info-header {
|
||
margin-bottom: 16px;
|
||
|
||
h3 {
|
||
margin: 0;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
}
|
||
}
|
||
|
||
.info-content {
|
||
.info-row {
|
||
display: flex;
|
||
gap: 32px;
|
||
margin-bottom: 12px;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 14px;
|
||
|
||
.label {
|
||
color: #606266;
|
||
min-width: 100px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.value {
|
||
color: #303133;
|
||
font-weight: 400;
|
||
|
||
&.warning-count {
|
||
color: #e6a23c;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.content-card {
|
||
margin-bottom: 16px;
|
||
|
||
:deep(.el-card__body) {
|
||
padding: 0;
|
||
|
||
.el-tabs {
|
||
height: 100%;
|
||
|
||
.el-tabs__content {
|
||
padding: 20px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.action-bar {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
padding: 16px;
|
||
background: #ffffff;
|
||
border-radius: 4px;
|
||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||
|
||
.el-button {
|
||
padding: 10px 20px;
|
||
|
||
i {
|
||
margin-right: 4px;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 响应式设计
|
||
@media (max-width: 768px) {
|
||
.dpc-detail-container {
|
||
padding: 8px;
|
||
}
|
||
|
||
.detail-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
|
||
.header-right {
|
||
width: 100%;
|
||
margin-top: 12px;
|
||
|
||
.nav-menu {
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
|
||
.el-menu-item,
|
||
.el-submenu {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 0 8px;
|
||
font-size: 13px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.info-content {
|
||
.info-row {
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
|
||
.info-item {
|
||
.label {
|
||
min-width: auto;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.action-bar {
|
||
flex-wrap: wrap;
|
||
|
||
.el-button {
|
||
width: 100%;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 下拉菜单弹窗样式
|
||
::v-deep .el-menu--popup {
|
||
min-width: 140px;
|
||
|
||
.el-menu-item {
|
||
font-size: 14px;
|
||
|
||
&:hover {
|
||
background-color: #f5f7fa;
|
||
}
|
||
|
||
&.is-active {
|
||
color: #1890ff;
|
||
background-color: #e6f7ff;
|
||
}
|
||
}
|
||
}
|
||
</style>
|