明树Git Lab

Commit d7b10ba8 authored by zhanghan's avatar zhanghan

完成项目档案库开发

parent ffdc2ddb
Pipeline #109390 passed with stage
in 21 seconds
......@@ -3,6 +3,7 @@
## 功能说明
这是一个基于 Element Plus `el-collapse` 的粘性导航组件,可以自动为折叠面板生成右侧导航,支持:
- 自动获取所有折叠面板项
- 点击导航快速定位到对应面板
- 滚动时自动高亮当前可见的面板
......@@ -42,10 +43,10 @@
</template>
<script setup>
import { ref } from 'vue';
import CollapseNavigation from '@/components/CollapseNavigation/index.vue';
import { ref } from "vue";
import CollapseNavigation from "@/components/CollapseNavigation/index.vue";
const activeCollapse = ref(['项目基本信息']);
const activeCollapse = ref(["项目基本信息"]);
const navigationItems = ref([]); // 由指令自动填充
const handleNavClick = (item) => {
......@@ -77,7 +78,7 @@ const handleNavClick = (item) => {
## 组件 Props
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| --------------- | -------------- | ------------ | ----------- |
| navItems | 导航项列表 | Array | [] |
| activeItem | 当前激活的项 | String/Array | '' |
| width | 导航宽度 | String | '200px' |
......@@ -88,7 +89,7 @@ const handleNavClick = (item) => {
## 组件事件
| 事件名 | 说明 | 参数 |
|--------|------|------|
| ----------------- | ---------------- | ---------------- |
| navClick | 导航项被点击 | item: 导航项对象 |
| update:activeItem | 激活项变化时触发 | name: 导航项名称 |
......@@ -99,6 +100,7 @@ const handleNavClick = (item) => {
自动获取 `el-collapse` 的所有子项并生成导航数据。
**用法:**
```vue
<el-collapse v-collapse-nav="navigationItems">
<!-- 折叠面板项 -->
......@@ -106,6 +108,7 @@ const handleNavClick = (item) => {
```
**参数:**
- `navigationItems`: 响应式数组,用于接收生成的导航数据
## 高级配置
......
......@@ -1069,11 +1069,6 @@ const navigationItems = ref([]);
const handleNavClick = (item) => {
console.log("Navigation clicked:", item);
// 点击导航时自动展开对应的折叠面板
if (!activeCollapse.value.includes(item.name)) {
activeCollapse.value.push(item.name);
}
// 等待 DOM 更新后滚动
nextTick(() => {
setTimeout(() => {
......
......@@ -530,6 +530,14 @@ import FinancialTable from "@/components/FinancialTable.vue";
import InvestmentRecoveryTable from "@/components/InvestmentRecoveryTable.vue";
import routerBack from "@/components/common/routerBack.vue";
import CollapseNavigation from "@/components/CollapseNavigation/index.vue";
// 接收父组件传递的 isPreview prop
const props = defineProps({
isPreview: {
type: Boolean,
default: undefined,
},
});
const transferColumns = ref([
{
prop: "njfcgbl",
......@@ -587,11 +595,6 @@ const navigationItems = ref([]);
const handleNavClick = (item) => {
console.log("Navigation clicked:", item);
// 点击导航时自动展开对应的折叠面板
if (!activeCollapse.value.includes(item.name)) {
activeCollapse.value.push(item.name);
}
// 等待 DOM 更新后滚动
nextTick(() => {
setTimeout(() => {
......@@ -1288,8 +1291,15 @@ const changeProject = (val) => {
};
// 加载状态
const loading = ref(false);
// 是否预览模式
const isPreview = ref(!!route.query.isPreview);
// 是否预览模式 - 优先使用父组件传递的 prop,否则从路由参数读取
const isPreview = computed(() => {
// 如果父组件显式传递了 isPreview prop,使用 prop 的值
if (props.isPreview !== undefined) {
return props.isPreview;
}
// 否则从路由参数读取
return !!route.query.isPreview;
});
// 项目列表数据
const projectList = ref([]);
// 当前编辑的记录ID
......
This diff is collapsed.
......@@ -259,7 +259,6 @@ const previewProject = (item) => {
name: "xmdakDetaill",
// name: "addProject",
query: {
isPreview: true,
projectId: item.id,
},
});
......
......@@ -54,7 +54,7 @@
</template>
<script setup>
import { ref, computed, markRaw } from "vue";
import { ref, computed, markRaw, onMounted } from "vue";
import { useRoute } from "vue-router";
import {
Document,
......@@ -66,6 +66,13 @@ import {
const route = useRoute();
// 初始化时自动加载第一个tab的第一个子项
onMounted(async () => {
if (projectId.value) {
await switchTab(0);
}
});
// Tab 配置
const tabs = ref([
{
......@@ -103,13 +110,13 @@ const routeChildren = {
name: "projectDraft",
title: "项目遴选",
desc: "项目初选与评估",
importFn: () => import("@/views/projectManage/projectDraft.vue"),
importFn: () => import("@/views/projectManage/addProject.vue"),
},
{
name: "projectSetUp",
title: "项目立项",
desc: "项目立项申请",
importFn: () => import("@/views/projectManage/projectSetUp.vue"),
importFn: () => import("@/views/projectManage/addProject.vue"),
},
{
name: "projectArgument",
......@@ -121,12 +128,6 @@ const routeChildren = {
name: "projectDecision",
title: "项目决策",
desc: "项目投资决策",
importFn: () => import("@/views/projectManage/projectDecision.vue"),
},
{
name: "projectAllPage",
title: "项目档案库",
desc: "项目档案管理",
importFn: () => import("@/views/projectManage/addProject.vue"),
},
],
......@@ -135,8 +136,7 @@ const routeChildren = {
name: "targetLiabilityStatement",
title: "投资目标责任书",
desc: "投资目标责任管理",
importFn: () =>
import("@/views/investingManage/addStatement.vue"),
importFn: () => import("@/views/investingManage/addStatement.vue"),
},
{
name: "targetControl",
......@@ -154,8 +154,7 @@ const routeChildren = {
name: "constructionTime",
title: "建设期投资回收",
desc: "建设期资金回收",
importFn: () =>
import("@/views/investingManage/constructionTimeAdd.vue"),
importFn: () => import("@/views/investingManage/constructionTimeAdd.vue"),
},
{
name: "construction",
......@@ -187,8 +186,7 @@ const routeChildren = {
name: "investmentCecovery",
title: "运营期投资回收",
desc: "运营期收益回收",
importFn: () =>
import("@/views/castbehind/investmentCecoveryAdd.vue"),
importFn: () => import("@/views/castbehind/investmentCecoveryAdd.vue"),
},
{
name: "runningPeriod",
......@@ -218,10 +216,17 @@ const currentTabChildren = computed(() => {
});
// 切换 Tab
const switchTab = (index) => {
const switchTab = async (index) => {
activeTab.value = index;
// 获取当前Tab的第一个子项
const children = routeChildren[tabs.value[index].key];
if (children && children.length > 0) {
await handleSubItemClick(children[0]);
} else {
activeSubItem.value = "";
currentComponent.value = null;
}
};
// 处理子项点击 - 直接加载详情组件
......@@ -255,15 +260,22 @@ const handleSubItemClick = async (item) => {
flex-direction: column;
}
// 主 Tab 切换区域 - 现代政府风格
.detail-section {
.tab-content {
padding: 16px !important;
}
::v-deep .add-project-back {
display: none;
}
}
.main-tab-wrapper {
background: white;
padding: 0;
box-shadow: 0 2px 12px rgba(0, 86, 179, 0.08);
position: sticky;
top: 0;
z-index: 100;
border-bottom: 2px solid #e8f4fd;
border-bottom: 1px solid #e8f4fd;
}
.main-tab-container {
......@@ -280,15 +292,14 @@ const handleSubItemClick = async (item) => {
display: flex;
align-items: center;
justify-content: center;
padding: 18px 24px;
padding: 10px 16px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
border-radius: 8px 8px 0 0;
margin-top: 4px;
margin-top: 0;
.tab-label {
font-size: 15px;
font-size: 14px;
font-weight: 500;
color: #5a6c7d;
letter-spacing: 0.5px;
......@@ -298,34 +309,31 @@ const handleSubItemClick = async (item) => {
}
&::before {
content: '';
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 3px;
height: 2px;
background: transparent;
transition: all 0.3s ease;
border-radius: 3px 3px 0 0;
}
&:hover {
background: linear-gradient(to bottom, #f8fbff, #ffffff);
background: #f8fbff;
.tab-label {
color: #0056b3;
}
&::before {
background: linear-gradient(to right, #4dabf7, #0056b3);
background: #4dabf7;
opacity: 0.6;
}
}
&.active {
background: white;
margin-top: 0;
padding-top: 22px;
.tab-label {
color: #0056b3;
......@@ -333,23 +341,24 @@ const handleSubItemClick = async (item) => {
}
&::before {
background: linear-gradient(to right, #4dabf7, #0056b3);
box-shadow: 0 -2px 8px rgba(0, 86, 179, 0.15);
background: #0056b3;
}
}
}
// 子项导航区域
.sub-tab-wrapper {
background: linear-gradient(to bottom, #f8fbff, #ffffff);
border-bottom: 1px solid #d4e8fc;
padding: 16px 24px;
box-shadow: 0 2px 8px rgba(0, 86, 179, 0.04);
background: #ffffff;
border-bottom: 1px solid #e8f4fd;
padding: 8px 24px;
position: sticky;
top: 46px;
z-index: 99;
}
.sub-tab-container {
display: flex;
gap: 12px;
gap: 8px;
flex-wrap: wrap;
max-width: 1400px;
margin: 0 auto;
......@@ -357,23 +366,28 @@ const handleSubItemClick = async (item) => {
}
.sub-tab-item {
padding: 12px 24px;
border-radius: 20px;
display: flex;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
background: white;
border: 1.5px solid #e8f4fd;
border: 1px solid #e8f4fd;
position: relative;
overflow: hidden;
&::before {
content: '';
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(77, 171, 247, 0.05), rgba(0, 86, 179, 0.05));
background: linear-gradient(
135deg,
rgba(77, 171, 247, 0.05),
rgba(0, 86, 179, 0.05)
);
opacity: 0;
transition: opacity 0.3s ease;
}
......@@ -391,7 +405,6 @@ const handleSubItemClick = async (item) => {
&:hover {
border-color: #4dabf7;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(77, 171, 247, 0.15);
&::before {
opacity: 1;
......@@ -403,10 +416,9 @@ const handleSubItemClick = async (item) => {
}
&.active {
background: linear-gradient(135deg, #4dabf7, #0056b3);
border-color: transparent;
box-shadow: 0 4px 16px rgba(0, 86, 179, 0.25);
transform: translateY(-2px);
background: #0056b3;
border-color: #0056b3;
transform: translateY(-1px);
&::before {
background: transparent;
......@@ -415,7 +427,6 @@ const handleSubItemClick = async (item) => {
.sub-tab-label {
color: white;
font-weight: 600;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
}
}
......@@ -472,7 +483,7 @@ const handleSubItemClick = async (item) => {
padding: 12px 16px;
.tab-label {
font-size: 13px;
font-size: 14px;
}
}
......@@ -484,7 +495,7 @@ const handleSubItemClick = async (item) => {
padding: 8px 14px;
.sub-tab-label {
font-size: 13px;
font-size: 14px;
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment