|
|
@@ -30,7 +30,7 @@
|
|
|
<OfficeBuilding v-else-if="data.type === 2" />
|
|
|
<Document v-else />
|
|
|
</el-icon>
|
|
|
- <span class="node-label">{{ node.label }}</span>
|
|
|
+ <span class="node-label" @click="handleFolderClick(data)">{{ node.label }}</span>
|
|
|
<span class="node-actions">
|
|
|
<span class="menu-trigger" @click="toggleMenu($event, data)">
|
|
|
<el-icon><MoreFilled /></el-icon>
|
|
|
@@ -83,16 +83,119 @@
|
|
|
<span>{{ t('document.document.menu.folder') }}</span>
|
|
|
</li>
|
|
|
</template>
|
|
|
- <!-- 文件夹:只显示文件夹 -->
|
|
|
+ <!-- 文件夹:只显示文件夹和文档 -->
|
|
|
<template v-else-if="currentMenuData && currentMenuData.type === 0">
|
|
|
<li class="menu-item" @click="handleMenuItemClick('add:0', currentMenuData)">
|
|
|
<span>{{ t('document.document.menu.folder') }}</span>
|
|
|
</li>
|
|
|
+ <li class="menu-item" v-hasPermi="['document:document:add']" @click="handleMenuItemClick('add:document', currentMenuData)">
|
|
|
+ <span>{{ t('document.document.menu.document') }}</span>
|
|
|
+ </li>
|
|
|
</template>
|
|
|
</ul>
|
|
|
|
|
|
<div class="content-container">
|
|
|
- <el-empty :description="t('document.document.empty.description')">
|
|
|
+ <!-- 文档列表展示区域 -->
|
|
|
+ <div v-if="selectedFolder" class="document-list-container">
|
|
|
+ <!-- 搜索栏 -->
|
|
|
+ <el-form :model="documentQueryParams" :inline="true" class="search-form">
|
|
|
+ <el-form-item :label="t('document.document.documentList.fileName')">
|
|
|
+ <el-input
|
|
|
+ v-model="documentQueryParams.name"
|
|
|
+ :placeholder="t('document.document.documentList.fileNamePlaceholder')"
|
|
|
+ clearable
|
|
|
+ style="width: 240px"
|
|
|
+ @keyup.enter="handleDocumentQuery"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="Search" @click="handleDocumentQuery">{{ t('document.document.button.search') }}</el-button>
|
|
|
+ <el-button icon="Refresh" @click="handleDocumentReset">{{ t('document.document.button.reset') }}</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <!-- 文档列表 -->
|
|
|
+ <el-table v-loading="documentLoading" :data="documentList" border style="margin-top: 10px">
|
|
|
+ <el-table-column type="index" width="55" align="center" :label="t('document.document.documentList.index')" />
|
|
|
+ <el-table-column prop="name" :label="t('document.document.documentList.name')" min-width="150" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="specification" :label="t('document.document.documentList.specification')" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="planDocumentType" :label="t('document.document.documentList.planDocumentType')" width="120" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <dict-tag v-if="scope.row.planDocumentType" :options="plan_document_type" :value="scope.row.planDocumentType" />
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="submitter" :label="t('document.document.documentList.submitter')" width="120" align="center" />
|
|
|
+ <el-table-column prop="submitDeadline" :label="t('document.document.documentList.submitDeadline')" width="110" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <span v-if="scope.row.submitDeadline">{{ parseTime(scope.row.submitDeadline, '{y}-{m}-{d}') }}</span>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="submitTime" :label="t('document.document.documentList.submitTime')" width="160" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <span v-if="scope.row.submitTime">{{ parseTime(scope.row.submitTime) }}</span>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="url" :label="t('document.document.documentList.url')" min-width="200">
|
|
|
+ <template #default="scope">
|
|
|
+ <div v-if="scope.row.fileName" class="file-name-cell">
|
|
|
+ <el-icon :size="18" class="file-icon">
|
|
|
+ <Document v-if="isWordFile(scope.row.fileName)" style="color: #2b579a;" />
|
|
|
+ <Grid v-else-if="isExcelFile(scope.row.fileName)" style="color: #217346;" />
|
|
|
+ <Monitor v-else-if="isPPTFile(scope.row.fileName)" style="color: #d24726;" />
|
|
|
+ <Reading v-else-if="isPDFFile(scope.row.fileName)" style="color: #e74c3c;" />
|
|
|
+ <Document v-else style="color: #606266;" />
|
|
|
+ </el-icon>
|
|
|
+ <span class="file-name-text">{{ scope.row.fileName }}</span>
|
|
|
+ <!-- 下载按钮已注释 -->
|
|
|
+ <!-- <el-link v-if="scope.row.url" type="primary" :href="scope.row.url" :download="scope.row.fileName" target="_blank" :underline="false" class="download-btn">
|
|
|
+ <el-icon><Download /></el-icon>
|
|
|
+ </el-link> -->
|
|
|
+ </div>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="note" :label="t('document.document.documentList.note')" min-width="150" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="createTime" :label="t('document.document.documentList.createTime')" width="160" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <span v-if="scope.row.createTime">{{ parseTime(scope.row.createTime) }}</span>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="updateTime" :label="t('document.document.documentList.updateTime')" width="160" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <span v-if="scope.row.updateTime">{{ parseTime(scope.row.updateTime) }}</span>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column :label="t('document.document.documentList.action')" width="100" align="center" fixed="right">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button
|
|
|
+ v-hasPermi="['document:document:audit']"
|
|
|
+ type="primary"
|
|
|
+ link
|
|
|
+ :icon="Select"
|
|
|
+ @click="handleAudit(scope.row)"
|
|
|
+ :title="t('document.document.button.audit')"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 分页 -->
|
|
|
+ <pagination
|
|
|
+ v-show="documentTotal > 0"
|
|
|
+ v-model:page="documentQueryParams.pageNum"
|
|
|
+ v-model:limit="documentQueryParams.pageSize"
|
|
|
+ :total="documentTotal"
|
|
|
+ @pagination="getDocumentList"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 空状态 -->
|
|
|
+ <el-empty v-else :description="t('document.document.empty.description')">
|
|
|
</el-empty>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -129,18 +232,100 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 添加文档对话框 -->
|
|
|
+ <el-dialog v-model="documentDialog.visible" :title="documentDialog.title" width="700px" append-to-body>
|
|
|
+ <el-form ref="documentFormRef" :model="documentForm" :rules="documentRules" label-width="140px">
|
|
|
+ <el-form-item :label="documentForm.type === 1 ? t('document.document.documentForm.planName') : t('document.document.documentForm.name')" prop="name">
|
|
|
+ <el-input v-model="documentForm.name" :placeholder="t('document.document.documentForm.namePlaceholder')" clearable />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item :label="t('document.document.documentForm.type')" prop="type" v-if="hasAddPlanPermission">
|
|
|
+ <el-radio-group v-model="documentForm.type" @change="handleDocumentTypeChange">
|
|
|
+ <el-radio :label="0">{{ t('document.document.documentForm.normalDocument') }}</el-radio>
|
|
|
+ <el-radio :label="1">{{ t('document.document.documentForm.planDocument') }}</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item :label="t('document.document.documentForm.submitter')" prop="submitterId">
|
|
|
+ <template v-if="documentForm.type === 0">
|
|
|
+ <el-input v-model="currentUserName" disabled />
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-select
|
|
|
+ v-model="documentForm.submitterId"
|
|
|
+ filterable
|
|
|
+ remote
|
|
|
+ reserve-keyword
|
|
|
+ :placeholder="t('document.document.documentForm.submitterPlaceholder')"
|
|
|
+ :remote-method="searchSubmitters"
|
|
|
+ :loading="submitterSearchLoading"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="submitter in submitterOptions"
|
|
|
+ :key="submitter.id"
|
|
|
+ :label="`${submitter.name} / ${submitter.dept} --- ${submitter.phoneNumber}`"
|
|
|
+ :value="submitter.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item v-if="documentForm.type === 1" :label="t('document.document.documentForm.submitDeadline')" prop="submitDeadline">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="documentForm.submitDeadline"
|
|
|
+ type="date"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ :placeholder="t('document.document.documentForm.submitDeadlinePlaceholder')"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item v-if="documentForm.type === 1" :label="t('document.document.documentForm.planType')" prop="planType">
|
|
|
+ <el-select v-model="documentForm.planType" :placeholder="t('document.document.documentForm.planTypePlaceholder')" clearable style="width: 100%">
|
|
|
+ <el-option v-for="dict in plan_document_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item :label="t('document.document.documentForm.file')" :prop="documentForm.type === 0 ? 'ossId' : ''">
|
|
|
+ <fileUpload v-model="uploadedFileId" :limit="1" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item v-if="documentForm.submitTime" :label="t('document.document.documentForm.submitTime')">
|
|
|
+ <el-input v-model="documentForm.submitTime" disabled />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item :label="t('document.document.documentForm.note')" prop="note">
|
|
|
+ <el-input v-model="documentForm.note" type="textarea" :rows="4" :placeholder="t('document.document.documentForm.notePlaceholder')" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button :loading="documentButtonLoading" type="primary" @click="submitDocumentForm">{{ t('document.document.button.submit') }}</el-button>
|
|
|
+ <el-button @click="cancelDocument">{{ t('document.document.button.cancel') }}</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, reactive, onMounted, onUnmounted, nextTick, getCurrentInstance, watch } from 'vue';
|
|
|
+import { ref, reactive, onMounted, onUnmounted, nextTick, getCurrentInstance, watch, computed } from 'vue';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
import { listFolder, addFolder, delFolder, getFolder, updateFolder } from '@/api/document/folder';
|
|
|
import { FolderListVO, FolderForm } from '@/api/document/folder/types';
|
|
|
-import { Folder, Document, Edit, Delete, Plus, MoreFilled, Location, OfficeBuilding, ArrowRight } from '@element-plus/icons-vue';
|
|
|
+import { addDocument, listDocument } from '@/api/document/document';
|
|
|
+import { DocumentForm, DocumentQuery, DocumentVO } from '@/api/document/document/types';
|
|
|
+import { queryMemberNotInCenter } from '@/api/project/management';
|
|
|
+import { MemberNotInCenterVO, MemberNotInCenterQuery } from '@/api/project/management/types';
|
|
|
+import { Folder, Document, Edit, Delete, Plus, MoreFilled, Location, OfficeBuilding, ArrowRight, Download, Select, Grid, Monitor, Reading } from '@element-plus/icons-vue';
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
import type { FormInstance } from 'element-plus';
|
|
|
import type { ComponentInternalInstance } from 'vue';
|
|
|
+import { useUserStore } from '@/store/modules/user';
|
|
|
+import { checkPermi } from '@/utils/permission';
|
|
|
+import fileUpload from '@/components/FileUpload/index.vue';
|
|
|
|
|
|
interface Props {
|
|
|
projectId?: number | string;
|
|
|
@@ -154,12 +339,22 @@ const emit = defineEmits<{
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
const { t } = useI18n();
|
|
|
+const userStore = useUserStore();
|
|
|
+const { plan_document_type } = toRefs<any>(proxy?.useDict('plan_document_type'));
|
|
|
|
|
|
// 数据定义
|
|
|
const loading = ref(false);
|
|
|
const buttonLoading = ref(false);
|
|
|
const treeData = ref<FolderListVO[]>([]);
|
|
|
const folderFormRef = ref<FormInstance>();
|
|
|
+const documentFormRef = ref<FormInstance>();
|
|
|
+const documentButtonLoading = ref(false);
|
|
|
+
|
|
|
+// 检查是否有计划文档添加权限
|
|
|
+const hasAddPlanPermission = computed(() => checkPermi(['document:document:addPlan']));
|
|
|
+
|
|
|
+// 当前用户信息
|
|
|
+const currentUserName = ref(userStore.nickname || '');
|
|
|
|
|
|
// 对话框
|
|
|
const dialog = reactive({
|
|
|
@@ -168,6 +363,12 @@ const dialog = reactive({
|
|
|
isEdit: false
|
|
|
});
|
|
|
|
|
|
+// 文档对话框
|
|
|
+const documentDialog = reactive({
|
|
|
+ visible: false,
|
|
|
+ title: ''
|
|
|
+});
|
|
|
+
|
|
|
// 当前操作的节点
|
|
|
const currentNode = ref<FolderListVO | null>(null);
|
|
|
|
|
|
@@ -190,6 +391,48 @@ const initFormData: FolderForm = {
|
|
|
// 表单数据
|
|
|
const form = ref<FolderForm>({ ...initFormData });
|
|
|
|
|
|
+// 文档表单初始数据
|
|
|
+const initDocumentFormData: DocumentForm = {
|
|
|
+ id: undefined,
|
|
|
+ name: '',
|
|
|
+ type: 0,
|
|
|
+ submitterId: undefined,
|
|
|
+ folderId: undefined,
|
|
|
+ submitDeadline: undefined,
|
|
|
+ planType: undefined,
|
|
|
+ ossId: undefined,
|
|
|
+ submitTime: undefined,
|
|
|
+ note: ''
|
|
|
+};
|
|
|
+
|
|
|
+// 文件上传的ossId(字符串格式)
|
|
|
+const uploadedFileId = ref<string>('');
|
|
|
+
|
|
|
+// 文档表单数据
|
|
|
+const documentForm = ref<DocumentForm>({ ...initDocumentFormData });
|
|
|
+
|
|
|
+// 递交人搜索相关
|
|
|
+const submitterSearchLoading = ref(false);
|
|
|
+const submitterOptions = ref<MemberNotInCenterVO[]>([]);
|
|
|
+let submitterSearchTimer: NodeJS.Timeout | null = null;
|
|
|
+
|
|
|
+// ========== 文档列表相关 ==========
|
|
|
+// 文档列表数据
|
|
|
+const documentList = ref<DocumentVO[]>([]);
|
|
|
+const documentLoading = ref(false);
|
|
|
+const documentTotal = ref(0);
|
|
|
+
|
|
|
+// 当前选中的文件夹
|
|
|
+const selectedFolder = ref<FolderListVO | null>(null);
|
|
|
+
|
|
|
+// 文档查询参数
|
|
|
+const documentQueryParams = reactive<DocumentQuery>({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ name: '',
|
|
|
+ folderId: undefined
|
|
|
+});
|
|
|
+
|
|
|
// 表单验证规则
|
|
|
const rules = {
|
|
|
name: [
|
|
|
@@ -200,6 +443,19 @@ const rules = {
|
|
|
]
|
|
|
};
|
|
|
|
|
|
+// 文档表单验证规则
|
|
|
+const documentRules = {
|
|
|
+ name: [
|
|
|
+ { required: true, message: t('document.document.documentRule.nameRequired'), trigger: 'blur' }
|
|
|
+ ],
|
|
|
+ submitterId: [
|
|
|
+ { required: true, message: t('document.document.documentRule.submitterRequired'), trigger: 'change' }
|
|
|
+ ],
|
|
|
+ ossId: [
|
|
|
+ { required: true, message: t('document.document.documentRule.fileRequired'), trigger: 'change' }
|
|
|
+ ]
|
|
|
+};
|
|
|
+
|
|
|
// 树形组件配置
|
|
|
const treeProps = {
|
|
|
children: 'children',
|
|
|
@@ -314,8 +570,13 @@ const getAvailableTypes = () => {
|
|
|
// 下拉菜单命令处理
|
|
|
const handleCommand = (command: string, data: FolderListVO) => {
|
|
|
if (command.startsWith('add:')) {
|
|
|
- const type = parseInt(command.split(':')[1]);
|
|
|
- handleAddChildWithType(data, type);
|
|
|
+ const cmdPart = command.split(':')[1];
|
|
|
+ if (cmdPart === 'document') {
|
|
|
+ handleAddDocument(data);
|
|
|
+ } else {
|
|
|
+ const type = parseInt(cmdPart);
|
|
|
+ handleAddChildWithType(data, type);
|
|
|
+ }
|
|
|
} else if (command === 'edit') {
|
|
|
handleEdit(data);
|
|
|
} else if (command === 'delete') {
|
|
|
@@ -517,6 +778,222 @@ const handleMenuItemClick = (command: string, data: FolderListVO | null) => {
|
|
|
closeAllMenus();
|
|
|
};
|
|
|
|
|
|
+// ========== 文档相关函数 ==========
|
|
|
+
|
|
|
+// 重置文档表单
|
|
|
+const resetDocumentForm = () => {
|
|
|
+ documentForm.value = { ...initDocumentFormData };
|
|
|
+ uploadedFileId.value = '';
|
|
|
+ // 如果没有计划文档权限,默认为非计划文档
|
|
|
+ if (!hasAddPlanPermission.value) {
|
|
|
+ documentForm.value.type = 0;
|
|
|
+ }
|
|
|
+ documentForm.value.submitterId = userStore.userId;
|
|
|
+ currentUserName.value = userStore.nickname || '';
|
|
|
+ submitterOptions.value = [];
|
|
|
+ documentFormRef.value?.resetFields();
|
|
|
+};
|
|
|
+
|
|
|
+// 添加文档
|
|
|
+const handleAddDocument = (data: FolderListVO) => {
|
|
|
+ resetDocumentForm();
|
|
|
+ documentForm.value.folderId = data.id;
|
|
|
+ documentDialog.visible = true;
|
|
|
+ documentDialog.title = t('document.document.dialog.addDocument');
|
|
|
+};
|
|
|
+
|
|
|
+// 处理文档类型变化
|
|
|
+const handleDocumentTypeChange = (value: number) => {
|
|
|
+ if (value === 0) {
|
|
|
+ // 非计划文档,递交人为当前用户
|
|
|
+ documentForm.value.submitterId = userStore.userId;
|
|
|
+ documentForm.value.submitDeadline = undefined;
|
|
|
+ documentForm.value.planType = undefined;
|
|
|
+ } else {
|
|
|
+ // 计划文档,清空递交人
|
|
|
+ documentForm.value.submitterId = undefined;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 搜索递交人
|
|
|
+const searchSubmitters = async (query: string) => {
|
|
|
+ if (!query || query.trim() === '') {
|
|
|
+ submitterOptions.value = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清除之前的定时器
|
|
|
+ if (submitterSearchTimer) {
|
|
|
+ clearTimeout(submitterSearchTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置防抖
|
|
|
+ submitterSearchTimer = setTimeout(async () => {
|
|
|
+ submitterSearchLoading.value = true;
|
|
|
+ try {
|
|
|
+ const queryParams: MemberNotInCenterQuery = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ projectId: props.projectId || 0,
|
|
|
+ folderId: 0,
|
|
|
+ name: query
|
|
|
+ };
|
|
|
+ const res = await queryMemberNotInCenter(queryParams);
|
|
|
+ submitterOptions.value = res.rows || [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Failed to search submitters:', error);
|
|
|
+ ElMessage.error(t('document.document.message.searchSubmitterFailed'));
|
|
|
+ } finally {
|
|
|
+ submitterSearchLoading.value = false;
|
|
|
+ }
|
|
|
+ }, 300);
|
|
|
+};
|
|
|
+
|
|
|
+// 监听文件上传变化
|
|
|
+watch(uploadedFileId, (newVal) => {
|
|
|
+ if (newVal) {
|
|
|
+ // 解析文件ID(可能是逗号分隔的字符串)
|
|
|
+ const ids = newVal.split(',').filter(id => id.trim());
|
|
|
+ if (ids.length > 0) {
|
|
|
+ documentForm.value.ossId = parseInt(ids[0]);
|
|
|
+ // 自动设置递交时间为当前时间
|
|
|
+ const now = new Date();
|
|
|
+ documentForm.value.submitTime = proxy?.parseTime(now, '{y}-{m}-{d} {h}:{i}:{s}');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ documentForm.value.ossId = undefined;
|
|
|
+ documentForm.value.submitTime = undefined;
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 取消文档对话框
|
|
|
+const cancelDocument = () => {
|
|
|
+ resetDocumentForm();
|
|
|
+ documentDialog.visible = false;
|
|
|
+};
|
|
|
+
|
|
|
+// 提交文档表单
|
|
|
+const submitDocumentForm = () => {
|
|
|
+ documentFormRef.value?.validate(async (valid: boolean) => {
|
|
|
+ if (valid) {
|
|
|
+ documentButtonLoading.value = true;
|
|
|
+ try {
|
|
|
+ // 构建完整的请求数据,确保所有字段都存在(参考文件夹的实现)
|
|
|
+ const submitData: DocumentForm = {
|
|
|
+ id: documentForm.value.id || 0,
|
|
|
+ name: documentForm.value.name || '',
|
|
|
+ type: documentForm.value.type !== undefined ? documentForm.value.type : 0,
|
|
|
+ submitterId: documentForm.value.submitterId || 0,
|
|
|
+ folderId: documentForm.value.folderId || 0,
|
|
|
+ submitDeadline: documentForm.value.submitDeadline || '',
|
|
|
+ planType: documentForm.value.planType || '',
|
|
|
+ ossId: documentForm.value.ossId || null,
|
|
|
+ submitTime: documentForm.value.submitTime || '',
|
|
|
+ note: documentForm.value.note || ''
|
|
|
+ };
|
|
|
+
|
|
|
+ await addDocument(submitData);
|
|
|
+ proxy?.$modal.msgSuccess(t('document.document.message.addDocumentSuccess'));
|
|
|
+ documentDialog.visible = false;
|
|
|
+ await getList();
|
|
|
+ // 刷新文档列表
|
|
|
+ await getDocumentList();
|
|
|
+ } catch (error) {
|
|
|
+ console.error(t('document.document.message.addDocumentFailed'), error);
|
|
|
+ } finally {
|
|
|
+ documentButtonLoading.value = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// ========== 文档列表相关函数 ==========
|
|
|
+
|
|
|
+// 点击文件夹节点
|
|
|
+const handleFolderClick = (data: FolderListVO) => {
|
|
|
+ // 只有文件夹类型(type=0)才显示文档列表
|
|
|
+ if (data.type === 0) {
|
|
|
+ selectedFolder.value = data;
|
|
|
+ // 重置查询参数
|
|
|
+ documentQueryParams.name = '';
|
|
|
+ documentQueryParams.pageNum = 1;
|
|
|
+ // 加载文档列表
|
|
|
+ getDocumentList();
|
|
|
+ } else {
|
|
|
+ selectedFolder.value = null;
|
|
|
+ documentList.value = [];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 查询文档列表
|
|
|
+const getDocumentList = async () => {
|
|
|
+ if (!selectedFolder.value) return;
|
|
|
+
|
|
|
+ documentLoading.value = true;
|
|
|
+ documentQueryParams.folderId = selectedFolder.value.id;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res = await listDocument(documentQueryParams);
|
|
|
+ documentList.value = res.rows || [];
|
|
|
+ documentTotal.value = res.total || 0;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Failed to get document list:', error);
|
|
|
+ ElMessage.error(t('document.document.message.getDocumentListFailed'));
|
|
|
+ } finally {
|
|
|
+ documentLoading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 搜索按钮
|
|
|
+const handleDocumentQuery = () => {
|
|
|
+ documentQueryParams.pageNum = 1;
|
|
|
+ getDocumentList();
|
|
|
+};
|
|
|
+
|
|
|
+// 重置按钮
|
|
|
+const handleDocumentReset = () => {
|
|
|
+ documentQueryParams.name = '';
|
|
|
+ documentQueryParams.pageNum = 1;
|
|
|
+ getDocumentList();
|
|
|
+};
|
|
|
+
|
|
|
+// 审核文档
|
|
|
+const handleAudit = (row: DocumentVO) => {
|
|
|
+ console.log('审核文档:', row);
|
|
|
+ ElMessage.info('审核功能待实现');
|
|
|
+};
|
|
|
+
|
|
|
+// ========== 文件类型判断函数 ==========
|
|
|
+
|
|
|
+// 判断是否为Word文档
|
|
|
+const isWordFile = (fileName: string): boolean => {
|
|
|
+ if (!fileName) return false;
|
|
|
+ const lowerFileName = fileName.toLowerCase();
|
|
|
+ return lowerFileName.endsWith('.doc') || lowerFileName.endsWith('.docx');
|
|
|
+};
|
|
|
+
|
|
|
+// 判断是否为Excel文档
|
|
|
+const isExcelFile = (fileName: string): boolean => {
|
|
|
+ if (!fileName) return false;
|
|
|
+ const lowerFileName = fileName.toLowerCase();
|
|
|
+ return lowerFileName.endsWith('.xls') ||
|
|
|
+ lowerFileName.endsWith('.xlsx') ||
|
|
|
+ lowerFileName.endsWith('.csv');
|
|
|
+};
|
|
|
+
|
|
|
+// 判断是否为PPT文档
|
|
|
+const isPPTFile = (fileName: string): boolean => {
|
|
|
+ if (!fileName) return false;
|
|
|
+ const lowerFileName = fileName.toLowerCase();
|
|
|
+ return lowerFileName.endsWith('.ppt') || lowerFileName.endsWith('.pptx');
|
|
|
+};
|
|
|
+
|
|
|
+// 判断是否为PDF文档
|
|
|
+const isPDFFile = (fileName: string): boolean => {
|
|
|
+ if (!fileName) return false;
|
|
|
+ return fileName.toLowerCase().endsWith('.pdf');
|
|
|
+};
|
|
|
+
|
|
|
// 关闭所有菜单
|
|
|
const closeAllMenus = () => {
|
|
|
showSecondaryMenu.value = false;
|
|
|
@@ -570,6 +1047,11 @@ onUnmounted(() => {
|
|
|
document.removeEventListener('scroll', handleScroll, true);
|
|
|
// 清理菜单状态
|
|
|
closeAllMenus();
|
|
|
+ // 清理搜索定时器
|
|
|
+ if (submitterSearchTimer) {
|
|
|
+ clearTimeout(submitterSearchTimer);
|
|
|
+ submitterSearchTimer = null;
|
|
|
+ }
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
@@ -638,6 +1120,11 @@ onUnmounted(() => {
|
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
|
white-space: nowrap;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: var(--el-color-primary);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.node-actions {
|
|
|
@@ -672,6 +1159,42 @@ onUnmounted(() => {
|
|
|
overflow: auto;
|
|
|
}
|
|
|
|
|
|
+.document-list-container {
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ .search-form {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-name-cell {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 0 8px;
|
|
|
+
|
|
|
+ .file-icon {
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-name-text {
|
|
|
+ flex: 1;
|
|
|
+ text-align: left;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .download-btn {
|
|
|
+ flex-shrink: 0;
|
|
|
+ font-size: 16px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ transform: scale(1.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.detail-content {
|
|
|
max-width: 800px;
|
|
|
}
|