<!--
|
文档归档
|
|
数据来自 getProjectAttachments(id),返回按模块分组的附件:
|
[{ moduleName, files: [{ name, url }] }]
|
(对齐管理端 components/ArchiveDialog.vue 的 res.data 结构)
|
|
附件下载复用 utils/file.js 的 openFile:request.ts 未透传 responseType,
|
只能 downloadFile 到临时文件再 openDocument。
|
-->
|
<template>
|
<view class="project-archive">
|
<PageHeader :title="pageTitle"
|
@back="goBack" />
|
|
<view v-if="loading"
|
class="state-text">
|
<text>加载中...</text>
|
</view>
|
|
<view v-else-if="groups.length === 0"
|
class="state-text">
|
<text>暂无归档附件</text>
|
</view>
|
|
<view v-else
|
class="archive-list">
|
<view v-for="(group, index) in groups"
|
:key="index"
|
class="archive-group">
|
<view class="group-header"
|
@click="toggleGroup(index)">
|
<text class="group-title">
|
{{ group.moduleName || "未命名模块" }}({{ group.files.length }})
|
</text>
|
<up-icon :name="isOpen(index) ? 'arrow-up' : 'arrow-down'"
|
size="16"
|
color="#909399"></up-icon>
|
</view>
|
|
<view v-if="isOpen(index)"
|
class="file-list">
|
<view v-for="(file, fileIndex) in group.files"
|
:key="fileIndex"
|
class="file-item"
|
@click="openFile(file.url)">
|
<up-icon name="attach"
|
size="18"
|
color="#5b8ff9"></up-icon>
|
<text class="file-name">{{ file.name || "未命名文件" }}</text>
|
<up-icon name="download"
|
size="18"
|
color="#909399"></up-icon>
|
</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
import { computed, ref } from "vue";
|
import { onLoad } from "@dcloudio/uni-app";
|
import PageHeader from "@/components/PageHeader.vue";
|
import { openFile } from "@/utils/file";
|
import { getProjectAttachments } from "@/api/projectManagement/project";
|
|
const loading = ref(true);
|
const projectId = ref("");
|
const projectName = ref("");
|
const groups = ref([]);
|
const openIndex = ref([]);
|
|
const pageTitle = computed(() =>
|
projectName.value ? `文档归档 - ${projectName.value}` : "文档归档"
|
);
|
|
const goBack = () => uni.navigateBack();
|
|
const isOpen = index => openIndex.value.includes(index);
|
|
const toggleGroup = index => {
|
openIndex.value = isOpen(index)
|
? openIndex.value.filter(item => item !== index)
|
: [...openIndex.value, index];
|
};
|
|
// 后端可能回 files 为 null,统一成数组,避免模板里取 length 报错
|
const normalize = list =>
|
(Array.isArray(list) ? list : []).map(group => ({
|
moduleName: group?.moduleName ?? "",
|
files: (Array.isArray(group?.files) ? group.files : []).map(file => ({
|
name: file?.name ?? "",
|
url: file?.url ?? "",
|
})),
|
}));
|
|
const loadAttachments = async () => {
|
try {
|
const res = await getProjectAttachments(projectId.value);
|
const raw = res?.data?.data ?? res?.data ?? res;
|
groups.value = normalize(raw);
|
// 全部分组默认展开,与管理端 ArchiveDialog 的 activeNames 行为一致
|
openIndex.value = groups.value.map((_, index) => index);
|
} catch (error) {
|
groups.value = [];
|
uni.showToast({ title: "获取归档附件失败", icon: "none" });
|
} finally {
|
loading.value = false;
|
}
|
};
|
|
onLoad(options => {
|
projectId.value = options?.id || "";
|
projectName.value = options?.name ? decodeURIComponent(options.name) : "";
|
if (projectId.value) {
|
loadAttachments();
|
} else {
|
loading.value = false;
|
}
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.project-archive {
|
min-height: 100vh;
|
background: #f5f6fa;
|
}
|
|
.state-text {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
padding: 80px 0;
|
font-size: 14px;
|
color: #909399;
|
}
|
|
.archive-list {
|
padding: 12px;
|
display: flex;
|
flex-direction: column;
|
gap: 12px;
|
}
|
|
.archive-group {
|
background: #ffffff;
|
border-radius: 12px;
|
overflow: hidden;
|
box-shadow: 0 2px 10px rgba(15, 35, 95, 0.05);
|
}
|
|
.group-header {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 14px 16px;
|
background: #f8fbff;
|
}
|
|
.group-title {
|
font-size: 15px;
|
font-weight: 600;
|
color: #22324d;
|
}
|
|
.file-list {
|
display: flex;
|
flex-direction: column;
|
}
|
|
.file-item {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
padding: 12px 16px;
|
border-top: 1px solid #f0f2f5;
|
}
|
|
.file-name {
|
flex: 1;
|
font-size: 14px;
|
color: #606266;
|
word-break: break-all;
|
}
|
</style>
|