优化文档管理与导航体验
1、为文档增加编辑接口和前端编辑抽屉,文档列表与详情页统一按创建、更新、删除权限展示操作。 2、将文档版本上传和分发改为抽屉交互,并在详情页补充版本删除权限检查。 3、增强全局面包屑上下文和详情页标题同步,避免详情页重复或缺失导航信息。 4、按项目权限过滤快捷入口、首页费用指标和 eTMF 新建入口,并补充对应前端测试。
This commit is contained in:
@@ -17,10 +17,10 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ctms-section-actions">
|
||||
<el-button @click="goBack">
|
||||
<el-icon class="el-icon--left"><ArrowLeft /></el-icon>
|
||||
{{ TEXT.common.actions.back }}
|
||||
<div class="actions">
|
||||
<el-button v-if="canUpdateDocument" type="primary" @click="openEdit" class="header-action-btn">
|
||||
<el-icon class="el-icon--left"><Edit /></el-icon>
|
||||
{{ TEXT.common.actions.edit }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +43,7 @@
|
||||
<div class="ctms-section-header">
|
||||
<div class="ctms-section-title"> </div>
|
||||
<div class="ctms-section-actions">
|
||||
<el-button type="primary" @click="openUpload" v-if="canAction('create_version')">
|
||||
<el-button type="primary" @click="openUpload" v-if="canUpdateDocument">
|
||||
<el-icon class="el-icon--left"><Upload /></el-icon>
|
||||
{{ TEXT.modules.fileVersionManagement.actions.uploadVersion }}
|
||||
</el-button>
|
||||
@@ -79,11 +79,11 @@
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.modules.fileVersionManagement.columns.actions" width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="downloadVersion(row)" v-if="canAction('view')">
|
||||
<el-button link type="primary" size="small" @click="downloadVersion(row)" v-if="canReadDocument">
|
||||
{{ TEXT.modules.fileVersionManagement.actions.download }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="isAdmin"
|
||||
v-if="canDeleteDocument"
|
||||
link
|
||||
type="danger"
|
||||
size="small"
|
||||
@@ -101,7 +101,7 @@
|
||||
<div class="ctms-section-header">
|
||||
<div class="ctms-section-title"> </div>
|
||||
<div class="ctms-section-actions">
|
||||
<el-button type="primary" @click="openDistribute">
|
||||
<el-button type="primary" @click="openDistribute" v-if="canUpdateDocument">
|
||||
<el-icon class="el-icon--left"><Share /></el-icon>
|
||||
{{ TEXT.modules.fileVersionManagement.actions.distribute }}
|
||||
</el-button>
|
||||
@@ -132,100 +132,199 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<el-dialog append-to=".layout-main .content-wrapper" v-model="uploadVisible" :title="TEXT.modules.fileVersionManagement.dialog.uploadTitle" width="520px" destroy-on-close class="ctms-dialog">
|
||||
<el-form ref="uploadFormRef" :model="uploadForm" :rules="uploadRules" label-width="110px" class="ctms-form">
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.versionNo" prop="version_no">
|
||||
<el-input v-model="uploadForm.version_no" :placeholder="TEXT.modules.fileVersionManagement.placeholders.versionNo" />
|
||||
</el-form-item>
|
||||
<el-form-item label="版本日期" prop="version_date">
|
||||
<el-date-picker
|
||||
v-model="uploadForm.version_date"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.changeSummary">
|
||||
<el-input v-model="uploadForm.change_summary" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.parentVersion">
|
||||
<el-select v-model="uploadForm.parent_version_id" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in detail.version_timeline"
|
||||
:key="item.id"
|
||||
:label="item.version_no"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.file">
|
||||
<input type="file" @change="onFileChange" class="ctms-file-input" />
|
||||
</el-form-item>
|
||||
<el-drawer
|
||||
v-if="editorVisible"
|
||||
v-model="editorVisible"
|
||||
direction="rtl"
|
||||
size="620px"
|
||||
:close-on-click-modal="true"
|
||||
:show-close="false"
|
||||
destroy-on-close
|
||||
class="document-editor-drawer"
|
||||
>
|
||||
<template #header>
|
||||
<div class="editor-header">
|
||||
<div class="editor-title">{{ TEXT.common.actions.edit }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="editorFormRef" :model="editorForm" :rules="editorRules" label-position="top" class="ctms-form">
|
||||
<div class="form-group">
|
||||
<div class="form-group-title">
|
||||
<span class="group-dot group-dot-basic"></span>
|
||||
{{ TEXT.common.labels.basicInfo }}
|
||||
</div>
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.title" prop="title">
|
||||
<el-input v-model="editorForm.title" />
|
||||
</el-form-item>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.scope" prop="scope_type">
|
||||
<el-select v-model="editorForm.scope_type" class="full-width">
|
||||
<el-option v-for="item in scopeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.docType" prop="doc_type">
|
||||
<el-select v-model="editorForm.doc_type" class="full-width">
|
||||
<el-option v-for="item in docTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item v-if="editorForm.scope_type === 'SITE'" :label="TEXT.modules.fileVersionManagement.fields.site" prop="site_id">
|
||||
<el-select v-model="editorForm.site_id" filterable class="full-width">
|
||||
<el-option v-for="item in siteOptions" :key="item.value" :label="item.label" :value="item.value" :disabled="item.disabled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="uploadVisible = false">{{ TEXT.common.actions.cancel }}</el-button>
|
||||
<el-button type="primary" :loading="uploading" @click="submitUpload">{{ TEXT.common.actions.confirm }}</el-button>
|
||||
<div class="drawer-footer">
|
||||
<el-button @click="editorVisible = false">{{ TEXT.common.actions.cancel }}</el-button>
|
||||
<el-button type="primary" :loading="savingEditor" @click="submitEditor">{{ TEXT.common.actions.confirm }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-drawer>
|
||||
|
||||
<el-dialog append-to=".layout-main .content-wrapper" v-model="distributeVisible" :title="TEXT.modules.fileVersionManagement.dialog.distributeTitle" width="600px" destroy-on-close class="ctms-dialog">
|
||||
<el-form :model="distributeForm" label-width="100px" class="ctms-form">
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.targetList">
|
||||
<div class="target-list w-full">
|
||||
<div class="target-toolbar">
|
||||
<el-select v-model="distributeForm.target_type" class="target-type-preset" :placeholder="TEXT.common.labels.all" @change="onTargetTypeChange">
|
||||
<el-option :label="TEXT.common.labels.all" value="ALL" />
|
||||
<el-option :label="TEXT.enums.distributionTargetType.ROLE" value="ROLE" />
|
||||
<el-option :label="TEXT.enums.distributionTargetType.USER" value="USER" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="target-row">
|
||||
<template v-if="distributeForm.target_type === 'ROLE'">
|
||||
<el-select
|
||||
v-model="distributeForm.role_ids"
|
||||
multiple
|
||||
filterable
|
||||
clearable
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
class="target-select"
|
||||
>
|
||||
<el-option v-for="role in roleOptions" :key="role.value" :label="role.label" :value="role.value" />
|
||||
<el-drawer
|
||||
v-if="uploadVisible"
|
||||
v-model="uploadVisible"
|
||||
direction="rtl"
|
||||
size="620px"
|
||||
:close-on-click-modal="true"
|
||||
:show-close="false"
|
||||
destroy-on-close
|
||||
class="upload-editor-drawer"
|
||||
>
|
||||
<template #header>
|
||||
<div class="editor-header">
|
||||
<div class="editor-title">{{ TEXT.modules.fileVersionManagement.dialog.uploadTitle }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="uploadFormRef" :model="uploadForm" :rules="uploadRules" label-position="top" class="ctms-form">
|
||||
<div class="form-group">
|
||||
<div class="form-group-title">
|
||||
<span class="group-dot group-dot-basic"></span>
|
||||
{{ TEXT.modules.fileVersionManagement.actions.uploadVersion }}
|
||||
</div>
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.versionNo" prop="version_no">
|
||||
<el-input v-model="uploadForm.version_no" :placeholder="TEXT.modules.fileVersionManagement.placeholders.versionNo" />
|
||||
</el-form-item>
|
||||
<el-form-item label="版本日期" prop="version_date">
|
||||
<el-date-picker
|
||||
v-model="uploadForm.version_date"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.changeSummary">
|
||||
<el-input v-model="uploadForm.change_summary" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.parentVersion">
|
||||
<el-select v-model="uploadForm.parent_version_id" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in detail.version_timeline"
|
||||
:key="item.id"
|
||||
:label="item.version_no"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.file">
|
||||
<input type="file" @change="onFileChange" class="ctms-file-input" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="drawer-footer">
|
||||
<el-button @click="uploadVisible = false">{{ TEXT.common.actions.cancel }}</el-button>
|
||||
<el-button type="primary" :loading="uploading" @click="submitUpload">{{ TEXT.common.actions.confirm }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
<el-drawer
|
||||
v-if="distributeVisible"
|
||||
v-model="distributeVisible"
|
||||
direction="rtl"
|
||||
size="620px"
|
||||
:close-on-click-modal="true"
|
||||
:show-close="false"
|
||||
destroy-on-close
|
||||
class="distribution-editor-drawer"
|
||||
>
|
||||
<template #header>
|
||||
<div class="editor-header">
|
||||
<div class="editor-title">{{ TEXT.modules.fileVersionManagement.dialog.distributeTitle }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="distributeForm" label-position="top" class="ctms-form">
|
||||
<div class="form-group">
|
||||
<div class="form-group-title">
|
||||
<span class="group-dot group-dot-basic"></span>
|
||||
{{ TEXT.modules.fileVersionManagement.fields.targetList }}
|
||||
</div>
|
||||
<el-form-item :label="TEXT.modules.fileVersionManagement.fields.targetList">
|
||||
<div class="target-list w-full">
|
||||
<div class="target-toolbar">
|
||||
<el-select v-model="distributeForm.target_type" class="target-type-preset" :placeholder="TEXT.common.labels.all" @change="onTargetTypeChange">
|
||||
<el-option :label="TEXT.common.labels.all" value="ALL" />
|
||||
<el-option :label="TEXT.enums.distributionTargetType.ROLE" value="ROLE" />
|
||||
<el-option :label="TEXT.enums.distributionTargetType.USER" value="USER" />
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else-if="distributeForm.target_type === 'USER'">
|
||||
<el-select
|
||||
v-model="distributeForm.user_ids"
|
||||
multiple
|
||||
filterable
|
||||
clearable
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
class="target-select"
|
||||
>
|
||||
<el-option v-for="member in memberOptions" :key="member.value" :label="member.label" :value="member.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
<div v-else class="target-all">
|
||||
<el-tag effect="plain" type="info">{{ TEXT.common.labels.all }}</el-tag>
|
||||
</div>
|
||||
<div class="target-row">
|
||||
<template v-if="distributeForm.target_type === 'ROLE'">
|
||||
<el-select
|
||||
v-model="distributeForm.role_ids"
|
||||
multiple
|
||||
filterable
|
||||
clearable
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
class="target-select"
|
||||
>
|
||||
<el-option v-for="role in roleOptions" :key="role.value" :label="role.label" :value="role.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else-if="distributeForm.target_type === 'USER'">
|
||||
<el-select
|
||||
v-model="distributeForm.user_ids"
|
||||
multiple
|
||||
filterable
|
||||
clearable
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
class="target-select"
|
||||
>
|
||||
<el-option v-for="member in memberOptions" :key="member.value" :label="member.label" :value="member.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
<div v-else class="target-all">
|
||||
<el-tag effect="plain" type="info">{{ TEXT.common.labels.all }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="distributeVisible = false">{{ TEXT.common.actions.cancel }}</el-button>
|
||||
<el-button type="primary" :loading="distributing" @click="submitDistribute">{{ TEXT.common.actions.confirm }}</el-button>
|
||||
<div class="drawer-footer">
|
||||
<el-button @click="distributeVisible = false">{{ TEXT.common.actions.cancel }}</el-button>
|
||||
<el-button type="primary" :loading="distributing" @click="submitDistribute">{{ TEXT.common.actions.confirm }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-drawer>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useRoute } from "vue-router";
|
||||
import { ElMessage, ElMessageBox, FormInstance, FormRules } from "element-plus";
|
||||
import { ArrowLeft, Upload, Download, Share } from "@element-plus/icons-vue";
|
||||
import { Edit, Upload, Download, Share } from "@element-plus/icons-vue";
|
||||
import {
|
||||
createDistributions,
|
||||
deleteDocumentVersion,
|
||||
@@ -233,6 +332,7 @@ import {
|
||||
fetchDocumentDetail,
|
||||
listDistributions,
|
||||
uploadDocumentVersion,
|
||||
updateDocument,
|
||||
} from "../../api/documents";
|
||||
import { listMembers } from "../../api/members";
|
||||
import { fetchSites } from "../../api/sites";
|
||||
@@ -241,14 +341,17 @@ import type { Distribution, DocumentDetail, DocumentVersion } from "../../types/
|
||||
import type { Site, StudyMember } from "../../types/api";
|
||||
import type { UserInfo } from "../../types/api";
|
||||
import { useAuthStore } from "../../store/auth";
|
||||
import { useStudyStore } from "../../store/study";
|
||||
import { TEXT } from "../../locales";
|
||||
import { displayEnum, displayText, getUserDisplayName } from "../../utils/display";
|
||||
import { useRoleTemplateMeta } from "../../composables/useRoleTemplateMeta";
|
||||
import { usePermission } from "../../utils/permission";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const auth = useAuthStore();
|
||||
const study = useStudyStore();
|
||||
const { roleLabel, loadRoleTemplates } = useRoleTemplateMeta();
|
||||
const { can } = usePermission();
|
||||
const documentId = computed(() => route.params.id as string);
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -284,6 +387,21 @@ const siteActiveMap = computed(() => {
|
||||
});
|
||||
const isReadOnly = computed(() => !!detail.site_id && siteActiveMap.value[detail.site_id] === false);
|
||||
const isAdmin = computed(() => auth.user?.is_admin);
|
||||
const canReadDocument = computed(() => can("documents.read"));
|
||||
const canUpdateDocument = computed(() => !isReadOnly.value && can("documents.update"));
|
||||
const canDeleteDocument = computed(() => !isReadOnly.value && can("documents.delete"));
|
||||
const canReadMembers = computed(() => can("project.members.list"));
|
||||
const docTypeOptions = Object.keys(TEXT.enums.documentType).map((value) => ({
|
||||
value,
|
||||
label: TEXT.enums.documentType[value as keyof typeof TEXT.enums.documentType],
|
||||
}));
|
||||
const scopeOptions = [
|
||||
{ value: "GLOBAL", label: TEXT.enums.scopeType.GLOBAL },
|
||||
{ value: "SITE", label: TEXT.enums.scopeType.SITE },
|
||||
];
|
||||
const siteOptions = computed(() =>
|
||||
sites.value.map((site) => ({ value: site.id, label: site.name, disabled: !site.is_active }))
|
||||
);
|
||||
|
||||
const memberUserMap = computed(() =>
|
||||
members.value.reduce<Record<string, string>>((acc, member) => {
|
||||
@@ -318,6 +436,13 @@ const displaySite = (siteId?: string | null) => {
|
||||
return sites.value.find((s) => s.id === siteId)?.name || siteId;
|
||||
};
|
||||
|
||||
const syncBreadcrumbContext = () => {
|
||||
study.setViewContext({
|
||||
siteName: displaySite(detail.site_id),
|
||||
pageTitle: detail.title || TEXT.modules.fileVersionManagement.title,
|
||||
});
|
||||
};
|
||||
|
||||
const currentEffectiveVersion = computed(() => {
|
||||
if (!detail.current_effective_version_id) return null;
|
||||
return detail.version_timeline?.find((item: any) => item.id === detail.current_effective_version_id) || null;
|
||||
@@ -328,6 +453,16 @@ const distributionVersionId = computed(() => {
|
||||
return detail.version_timeline?.[0]?.id || null;
|
||||
});
|
||||
|
||||
const editorVisible = ref(false);
|
||||
const savingEditor = ref(false);
|
||||
const editorFormRef = ref<FormInstance>();
|
||||
const editorForm = reactive({
|
||||
title: "",
|
||||
scope_type: "GLOBAL",
|
||||
site_id: "",
|
||||
doc_type: "",
|
||||
});
|
||||
|
||||
const uploadVisible = ref(false);
|
||||
const uploading = ref(false);
|
||||
const uploadFormRef = ref<FormInstance>();
|
||||
@@ -361,6 +496,24 @@ const uploadRules: FormRules = {
|
||||
version_date: [{ required: true, message: TEXT.common.messages.required, trigger: "change" }],
|
||||
};
|
||||
|
||||
const editorRules: FormRules = {
|
||||
title: [{ required: true, message: TEXT.common.messages.required, trigger: "blur" }],
|
||||
scope_type: [{ required: true, message: TEXT.common.messages.required, trigger: "change" }],
|
||||
site_id: [
|
||||
{
|
||||
validator: (_rule, value, callback) => {
|
||||
if (editorForm.scope_type === "SITE" && !value) {
|
||||
callback(new Error(TEXT.common.messages.required));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
doc_type: [{ required: true, message: TEXT.common.messages.required, trigger: "change" }],
|
||||
};
|
||||
|
||||
const roleOptions = computed(() => {
|
||||
const fallbackRoles = ["CRA", "PM", "CTA", "PV", "QA"];
|
||||
const roles = new Set(members.value.map((m) => m.role_in_study).filter(Boolean));
|
||||
@@ -393,14 +546,11 @@ const displayTarget = (row: Distribution) => {
|
||||
return row.target_id;
|
||||
};
|
||||
|
||||
const canAction = (action: string) => {
|
||||
if (isReadOnly.value && action !== "view") return false;
|
||||
const list = detail.can_actions || [];
|
||||
if (list.length === 0) return true;
|
||||
return list.includes(action);
|
||||
};
|
||||
|
||||
const loadMembers = async () => {
|
||||
if (!canReadMembers.value) {
|
||||
members.value = [];
|
||||
return;
|
||||
}
|
||||
if (!detail.trial_id) {
|
||||
members.value = [];
|
||||
return;
|
||||
@@ -451,6 +601,7 @@ const loadDetail = async () => {
|
||||
if (!sites.value.length) {
|
||||
await loadSites();
|
||||
}
|
||||
syncBreadcrumbContext();
|
||||
if (activeTab.value === "distributions") {
|
||||
await loadDistributions();
|
||||
}
|
||||
@@ -483,7 +634,53 @@ const handleTabChange = async (name: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const openEdit = () => {
|
||||
if (!canUpdateDocument.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
Object.assign(editorForm, {
|
||||
title: detail.title || "",
|
||||
scope_type: detail.scope_type || "GLOBAL",
|
||||
site_id: detail.site_id || "",
|
||||
doc_type: detail.doc_type || "",
|
||||
});
|
||||
editorFormRef.value?.clearValidate();
|
||||
editorVisible.value = true;
|
||||
};
|
||||
|
||||
const submitEditor = async () => {
|
||||
if (!canUpdateDocument.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
if (!editorFormRef.value) return;
|
||||
await editorFormRef.value.validate(async (valid) => {
|
||||
if (!valid || !documentId.value) return;
|
||||
savingEditor.value = true;
|
||||
try {
|
||||
await updateDocument(documentId.value, {
|
||||
title: editorForm.title.trim(),
|
||||
scope_type: editorForm.scope_type,
|
||||
site_id: editorForm.scope_type === "SITE" ? editorForm.site_id : null,
|
||||
doc_type: editorForm.doc_type,
|
||||
});
|
||||
editorVisible.value = false;
|
||||
await loadDetail();
|
||||
ElMessage.success(TEXT.common.messages.updateSuccess);
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.response?.data?.message || TEXT.common.messages.saveFailed);
|
||||
} finally {
|
||||
savingEditor.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const openUpload = () => {
|
||||
if (!canUpdateDocument.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
uploadForm.version_no = "";
|
||||
uploadForm.version_date = "";
|
||||
uploadForm.change_summary = "";
|
||||
@@ -500,6 +697,10 @@ const onFileChange = (event: Event) => {
|
||||
};
|
||||
|
||||
const submitUpload = async () => {
|
||||
if (!canUpdateDocument.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
if (!uploadFormRef.value) return;
|
||||
await uploadFormRef.value.validate(async (valid) => {
|
||||
if (!valid || !documentId.value || !uploadFile.value) {
|
||||
@@ -564,6 +765,10 @@ const downloadVersion = async (version: DocumentVersion) => {
|
||||
|
||||
const confirmDeleteVersion = async (version: DocumentVersion) => {
|
||||
if (!version?.id) return;
|
||||
if (!canDeleteDocument.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
const ok = await ElMessageBox.confirm(TEXT.common.confirm.delete, TEXT.common.labels.tips).catch(() => null);
|
||||
if (!ok) return;
|
||||
try {
|
||||
@@ -576,6 +781,10 @@ const confirmDeleteVersion = async (version: DocumentVersion) => {
|
||||
};
|
||||
|
||||
const openDistribute = () => {
|
||||
if (!canUpdateDocument.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
distributeForm.target_type = "ALL";
|
||||
distributeForm.role_ids = [];
|
||||
distributeForm.user_ids = [];
|
||||
@@ -591,6 +800,10 @@ const onTargetTypeChange = () => {
|
||||
};
|
||||
|
||||
const submitDistribute = async () => {
|
||||
if (!canUpdateDocument.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
if (!distributionVersionId.value) {
|
||||
ElMessage.warning(TEXT.modules.fileVersionManagement.messages.noEffective);
|
||||
return;
|
||||
@@ -639,14 +852,6 @@ const submitDistribute = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
if (detail.trial_id) {
|
||||
router.push(`/trial/${detail.trial_id}/documents`);
|
||||
} else {
|
||||
router.back();
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (value?: string | null) => {
|
||||
if (!value) return TEXT.common.fallback;
|
||||
return value.replace("T", " ").replace("Z", "").split(".")[0];
|
||||
@@ -741,6 +946,47 @@ watch(
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.editor-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.editor-title {
|
||||
color: var(--ctms-text-main);
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.form-group-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
color: var(--ctms-text-main);
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.group-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.group-dot-basic {
|
||||
background: var(--ctms-primary);
|
||||
}
|
||||
|
||||
.drawer-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.target-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user