项目详情页--初步更新
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<el-card class="mb-12">
|
||||
<div class="header">
|
||||
<div>
|
||||
<h3>项目详情</h3>
|
||||
<p class="sub" v-if="project">项目:{{ project.code }} - {{ project.name }}</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<el-button type="primary" @click="enterProject" :disabled="!project">进入项目</el-button>
|
||||
<el-button @click="goMembers" :disabled="!project">项目成员配置</el-button>
|
||||
<el-button @click="goSites" :disabled="!project">中心管理</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mb-12">
|
||||
<h4 class="section-title">项目基本信息</h4>
|
||||
<el-descriptions v-if="project" :column="2" border>
|
||||
<el-descriptions-item label="项目编号">{{ project.code }}</el-descriptions-item>
|
||||
<el-descriptions-item label="项目名称">{{ project.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申办方">{{ project.sponsor || "—" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{ statusLabel(project.status) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ project.created_at || "—" }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-skeleton v-else :rows="4" animated />
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<h4 class="section-title">项目通用文件</h4>
|
||||
<AttachmentList
|
||||
v-if="project"
|
||||
:study-id="project.id"
|
||||
entity-type="project"
|
||||
:entity-id="project.id"
|
||||
:show-uploader="true"
|
||||
/>
|
||||
<el-skeleton v-else :rows="3" animated />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { fetchStudyDetail } from "../../api/studies";
|
||||
import type { Study } from "../../types/api";
|
||||
import AttachmentList from "../../components/attachments/AttachmentList.vue";
|
||||
import { useStudyStore } from "../../store/study";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const studyStore = useStudyStore();
|
||||
|
||||
const project = ref<Study | null>(null);
|
||||
|
||||
const loadProject = async () => {
|
||||
try {
|
||||
const { data } = await fetchStudyDetail(route.params.projectId as string);
|
||||
project.value = data as Study;
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.response?.data?.message || "项目加载失败");
|
||||
}
|
||||
};
|
||||
|
||||
const statusLabel = (status: string) =>
|
||||
({
|
||||
DRAFT: "草稿",
|
||||
ACTIVE: "进行中",
|
||||
CLOSED: "已关闭",
|
||||
}[status] || status);
|
||||
|
||||
const enterProject = () => {
|
||||
if (!project.value) return;
|
||||
studyStore.setCurrentStudy({ ...project.value, role_in_study: project.value.role_in_study || "PM" } as Study);
|
||||
router.push("/study/home");
|
||||
};
|
||||
|
||||
const goMembers = () => {
|
||||
if (!project.value) return;
|
||||
router.push(`/admin/projects/${project.value.id}/members`);
|
||||
};
|
||||
|
||||
const goSites = () => {
|
||||
if (!project.value) return;
|
||||
router.push(`/admin/projects/${project.value.id}/sites`);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadProject();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
padding: 16px;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.sub {
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.section-title {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.mb-12 {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -16,21 +16,29 @@
|
||||
<el-tag>{{ scope.row.role_in_study }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="120">
|
||||
<el-table-column label="状态" width="160">
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.is_active">启用</el-tag>
|
||||
<el-tag type="danger" v-else>已禁用</el-tag>
|
||||
<el-tooltip
|
||||
v-if="scope.row.effectiveStatus === 'DISABLED_GLOBAL'"
|
||||
content="请前往【账号治理】启用该账号"
|
||||
placement="top"
|
||||
>
|
||||
<el-tag type="danger">全局已禁用</el-tag>
|
||||
</el-tooltip>
|
||||
<el-tag v-else :type="scope.row.is_active ? 'success' : 'danger'">
|
||||
{{ scope.row.is_active ? "启用" : "已禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="added_at" label="加入时间" width="200" />
|
||||
<el-table-column label="操作" width="320" fixed="right">
|
||||
<el-table-column label="操作" width="360" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
v-model="scope.row.role_in_study"
|
||||
size="small"
|
||||
style="width: 120px"
|
||||
@change="(val) => updateRole(scope.row.id, val)"
|
||||
:disabled="!scope.row.is_active"
|
||||
:disabled="!scope.row.is_active || scope.row.effectiveStatus === 'DISABLED_GLOBAL'"
|
||||
>
|
||||
<el-option label="PM" value="PM" />
|
||||
<el-option label="CRA" value="CRA" />
|
||||
@@ -38,11 +46,13 @@
|
||||
<el-option label="IMP" value="IMP" />
|
||||
<el-option label="ADMIN" value="ADMIN" />
|
||||
</el-select>
|
||||
<span v-if="scope.row.effectiveStatus === 'DISABLED_GLOBAL'" class="hint">该账号已在系统级被禁用,无法在任何项目中使用</span>
|
||||
<el-button link type="danger" size="small" @click="onDelete(scope.row)">删除</el-button>
|
||||
<el-button
|
||||
link
|
||||
:type="scope.row.is_active ? 'danger' : 'primary'"
|
||||
size="small"
|
||||
:disabled="scope.row.effectiveStatus === 'DISABLED_GLOBAL'"
|
||||
@click="toggleActive(scope.row)"
|
||||
>
|
||||
{{ scope.row.is_active ? "停用" : "启用" }}
|
||||
@@ -151,9 +161,11 @@ const loadUsers = async () => {
|
||||
const memberRows = computed(() =>
|
||||
members.value.map((m) => {
|
||||
const user = users.value.find((u) => u.id === m.user_id);
|
||||
const effectiveStatus = user && user.is_active === false ? "DISABLED_GLOBAL" : m.is_active ? "ACTIVE" : "DISABLED";
|
||||
return {
|
||||
...m,
|
||||
username: user?.username || m.user_id,
|
||||
effectiveStatus,
|
||||
};
|
||||
})
|
||||
);
|
||||
@@ -388,4 +400,9 @@ onMounted(async () => {
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.hint {
|
||||
color: #999;
|
||||
margin: 0 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,8 +9,16 @@
|
||||
<el-button type="primary" @click="openCreate">新建项目</el-button>
|
||||
</div>
|
||||
<el-table :data="projects" v-loading="loading" stripe>
|
||||
<el-table-column prop="code" label="项目编号" width="140" />
|
||||
<el-table-column prop="name" label="项目名称" min-width="200" />
|
||||
<el-table-column prop="code" label="项目编号" width="140">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" @click="goDetail(scope.row)">{{ scope.row.code }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="项目名称" min-width="200">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" @click="goDetail(scope.row)">{{ scope.row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sponsor" label="申办方" min-width="160" />
|
||||
<el-table-column prop="status" label="状态" width="120">
|
||||
<template #default="scope">
|
||||
@@ -84,6 +92,10 @@ const enterStudy = (row: Study) => {
|
||||
router.push("/study/home");
|
||||
};
|
||||
|
||||
const goDetail = (row: Study) => {
|
||||
router.push(`/projects/${row.id}`);
|
||||
};
|
||||
|
||||
const statusLabel = (status: string) =>
|
||||
({
|
||||
DRAFT: "草稿",
|
||||
|
||||
Reference in New Issue
Block a user