| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- <template>
- <view class="detail-container">
- <view class="header-card">
- <view class="title">{{ projectStore.projectDetailData.projectName || '未知名称' }}</view>
- <view class="meta-row">
- <view class="leader" v-if="projectStore.projectDetailData.projectLeaderName || projectStore.projectDetailData.manager">
- 项目负责人:{{ projectStore.projectDetailData.projectLeaderName || projectStore.projectDetailData.manager }}
- </view>
- <view class="tags">
- <text class="tag bg-blue">{{ getProjectTypeName(projectType) }}</text>
- </view>
- </view>
- </view>
- <view class="tabs-container">
- <uv-tabs :list="tabList" :current="currentTab" @click="handleTabClick" lineColor="#1c9bfd" activeColor="#1c9bfd"
- inactiveColor="#666" :scrollable="true"></uv-tabs>
- </view>
- <scroll-view class="content-area" scroll-y>
- <view class="component-wrapper">
- <component
- :is="currentComponentName"
- :projectId="projectId"
- :projectType="projectType"
- :projectData="projectStore.projectDetailData"
- ></component>
- </view>
- </scroll-view>
- </view>
- </template>
- <script setup lang="ts">
- import { onLoad } from '@dcloudio/uni-app';
- import { ref, computed } from 'vue';
- import { useProjectStore } from '@/store/modules/project';
- import { getProjectTypeName } from '@/constants';
- // 引入全部模块组件
- import ProjectSetup from './components/detail/ProjectSetup.vue';
- import ProjectMembers from './components/detail/ProjectMembers.vue';
- import ProjectCooperates from './components/detail/ProjectCooperates.vue';
- import ProjectBudget from './components/detail/ProjectBudget.vue';
- import ProjectDocs from './components/detail/ProjectDocs.vue';
- import ProjectProcess from './components/detail/ProjectProcess.vue';
- import ProjectChanges from './components/detail/ProjectChanges.vue';
- import ProjectMidterm from './components/detail/ProjectMidterm.vue';
- import ProjectClosing from './components/detail/ProjectClosing.vue';
- import ProjectFunding from './components/detail/ProjectFunding.vue';
- import ProjectAchievements from './components/detail/ProjectAchievements.vue';
- import ProjectInspection from './components/detail/ProjectInspection.vue';
- import ProjectApproval from './components/detail/ProjectApproval.vue';
- import ProjectEthical from './components/detail/ProjectEthical.vue';
- // 注册动态组件
- const componentsMap: Record<string, any> = {
- ProjectSetup,
- ProjectMembers,
- ProjectCooperates,
- ProjectBudget,
- ProjectDocs,
- ProjectProcess,
- ProjectChanges,
- ProjectMidterm,
- ProjectClosing,
- ProjectFunding,
- ProjectAchievements,
- ProjectInspection,
- ProjectApproval,
- ProjectEthical
- };
- const projectType = ref('');
- const projectId = ref(0);
- const projectStore = useProjectStore();
- // 标签页当前激活项索引
- const currentTab = ref(0);
- /**
- * 标签页配置
- * 根据项目类型动态调整需要显示的模块 Tab
- */
- const tabList = computed(() => {
- const tabs = [
- { name: '立项信息', component: 'ProjectSetup' }
- ];
- if (projectType.value !== 'horizontal') {
- tabs.push({ name: '项目预算', component: 'ProjectBudget' });
- }
- tabs.push({ name: '项目成员', component: 'ProjectMembers' });
- if (projectType.value === 'horizontal') {
- tabs.push({ name: '合作单位', component: 'ProjectCooperates' });
- }
-
- tabs.push(
- { name: '项目文档', component: 'ProjectDocs' },
- { name: '科研成果', component: 'ProjectAchievements' },
- { name: '经费信息', component: 'ProjectFunding' },
- { name: '中检信息', component: 'ProjectInspection' },
- { name: '结题信息', component: 'ProjectClosing' },
- { name: '变更信息', component: 'ProjectChanges' },
- { name: '审批信息', component: 'ProjectApproval' },
- { name: '伦理信息', component: 'ProjectEthical' }
- );
-
- return tabs;
- });
- /**
- * 动态组件计算
- * 根据当前 Tab 索引返回对应的 Vue 组件定义
- */
- const currentComponentName = computed(() => {
- const comp = tabList.value[currentTab.value];
- if (!comp) return '';
- return componentsMap[comp.component];
- });
- /**
- * 页面加载生命周期
- * 1. 解析路由参数(项目类型、ID)
- * 2. 调用 store 获取项目详细信息数据
- */
- onLoad((options: any) => {
- if (options.type) projectType.value = options.type;
- if (options.id) projectId.value = Number(options.id);
- if (projectId.value && projectType.value) {
- uni.showLoading({ title: '加载中...' });
- // 业务逻辑交由 Store 处理,页面只负责触发
- projectStore.fetchProjectDetailData(projectId.value, projectType.value)
- .finally(() => {
- uni.hideLoading();
- });
- }
- });
- /**
- * 标签切换点击处理
- */
- const handleTabClick = (item: any) => {
- currentTab.value = item.index;
- };
- /**
- * 通用动作处理 (占位)
- */
- const handleAction = () => {
- uni.showToast({ title: '功能开发中', icon: 'none' });
- };
- </script>
- <style lang="scss" scoped>
- .detail-container {
- height: 100vh;
- display: flex;
- flex-direction: column;
- background-color: #f5f7fa;
- box-sizing: border-box;
- }
- .header-card {
- flex-shrink: 0;
- background: linear-gradient(135deg, #1c9bfd 0%, #15a982 100%);
- padding: 40rpx 30rpx 80rpx;
- color: #fff;
- border-bottom-left-radius: 40rpx;
- border-bottom-right-radius: 40rpx;
- box-shadow: 0 10rpx 20rpx rgba(28, 155, 253, 0.2);
- .title {
- font-size: 40rpx;
- font-weight: bold;
- margin-bottom: 20rpx;
- line-height: 1.4;
- }
- .meta-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 20rpx;
- }
- .leader {
- font-size: 28rpx;
- opacity: 0.9;
- }
- .tags {
- display: flex;
- flex-wrap: wrap;
- gap: 16rpx;
- .tag {
- font-size: 24rpx;
- padding: 6rpx 20rpx;
- border-radius: 30rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.4);
- background: rgba(255, 255, 255, 0.1);
- backdrop-filter: blur(4px);
- }
- }
- }
- .tabs-container {
- flex-shrink: 0;
- margin: -40rpx 30rpx 20rpx;
- background-color: #fff;
- border-radius: 16rpx;
- padding: 10rpx 0;
- box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
- position: relative;
- z-index: 10;
- }
- .content-area {
- flex: 1;
- height: 0;
- /* 必须设置高度以便 scroll-view 撑开 */
- }
- .component-wrapper {
- margin: 0 30rpx;
- }
- .bottom-bar {
- position: fixed;
- bottom: 0;
- left: 0;
- width: 100%;
- background-color: #fff;
- padding: 20rpx 30rpx;
- box-sizing: border-box;
- box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05);
- z-index: 99;
- }
- </style>
|