Huanyi 3 месяцев назад
Родитель
Сommit
8425476791

+ 12 - 0
src/api/Qc/task/index.ts

@@ -61,3 +61,15 @@ export const delTask = (id: string | number | Array<string | number>) => {
     method: 'delete'
   });
 };
+
+/**
+ * 生成质控任务详情
+ * @param projectId
+ */
+export const generateTaskDetail = (projectId: string | number): AxiosPromise<any> => {
+  return request({
+    url: '/qcTask/detail/generate',
+    method: 'get',
+    params: { projectId }
+  });
+};

+ 170 - 0
src/api/Qc/task/types.ts

@@ -14,11 +14,21 @@ export interface TaskVO {
    */
   initiator: number;
 
+  /**
+   * 发起人昵称
+   */
+  initiatorName?: string;
+
   /**
    * 质控项目
    */
   projectId: string | number;
 
+  /**
+   * 质控项目名称
+   */
+  projectName?: string;
+
   /**
    * 开始时间
    */
@@ -59,6 +69,11 @@ export interface TaskVO {
    */
   updateTime: string;
 
+  /**
+   * 质控任务详情列表
+   */
+  details?: TaskDetailVO[];
+
 }
 
 export interface TaskForm extends BaseEntity {
@@ -102,6 +117,11 @@ export interface TaskForm extends BaseEntity {
    */
   note?: string;
 
+  /**
+   * 质控任务详情列表
+   */
+  details?: TaskDetailForm[];
+
 }
 
 export interface TaskQuery extends PageQuery {
@@ -161,3 +181,153 @@ export interface TaskQuery extends PageQuery {
    */
   params?: any;
 }
+
+/**
+ * 质控任务详情
+ */
+export interface TaskDetailVO {
+  /**
+   * 序号
+   */
+  id?: number;
+
+  /**
+   * 任务ID
+   */
+  taskId?: number;
+
+  /**
+   * 文档序号
+   */
+  documentId: number;
+
+  /**
+   * 文档名称
+   */
+  documentName?: string;
+
+  /**
+   * 质控人名称
+   */
+  executorName: string;
+
+  /**
+   * 质控人ID
+   */
+  executor: number;
+
+  /**
+   * 质控人状态
+   */
+  executorStatus?: string;
+
+  /**
+   * 状态
+   */
+  status?: number;
+
+  /**
+   * 备注
+   */
+  note?: string;
+
+  /**
+   * 创建部门
+   */
+  createDept?: number;
+
+  /**
+   * 创建者
+   */
+  createBy?: string;
+
+  /**
+   * 创建时间
+   */
+  createTime?: string;
+
+  /**
+   * 更新者
+   */
+  updateBy?: string;
+
+  /**
+   * 更新时间
+   */
+  updateTime?: string;
+
+  /**
+   * 参数
+   */
+  params?: any;
+}
+
+/**
+ * 质控任务详情表单
+ */
+export interface TaskDetailForm {
+  /**
+   * 序号
+   */
+  id?: number;
+
+  /**
+   * 任务ID
+   */
+  taskId?: number;
+
+  /**
+   * 文档序号
+   */
+  documentId: number;
+
+  /**
+   * 质控人ID
+   */
+  executor: number;
+
+  /**
+   * 项目ID
+   */
+  projectId?: number;
+
+  /**
+   * 状态
+   */
+  status?: number;
+
+  /**
+   * 备注
+   */
+  note?: string;
+
+  /**
+   * 创建部门
+   */
+  createDept?: number;
+
+  /**
+   * 创建者
+   */
+  createBy?: number;
+
+  /**
+   * 创建时间
+   */
+  createTime?: string;
+
+  /**
+   * 更新者
+   */
+  updateBy?: number;
+
+  /**
+   * 更新时间
+   */
+  updateTime?: string;
+
+  /**
+   * 参数
+   */
+  params?: any;
+}

+ 2 - 3
src/api/document/document/index.ts

@@ -95,13 +95,12 @@ export const listDocumentAuditLog = (query: any): AxiosPromise<any> => {
 /**
  * 归档文档
  * @param documentId 文档ID
- * @param folderId 文件夹ID
  */
-export const filingDocument = (documentId: number, folderId?: number | string) => {
+export const filingDocument = (documentId: number) => {
   return request({
     url: '/document/document/filing',
     method: 'put',
-    data: { documentId, folderId }
+    data: { documentId }
   });
 };
 

+ 5 - 0
src/api/document/document/types.ts

@@ -69,6 +69,11 @@ export interface DocumentVO {
    */
   updateTime?: string;
 
+  /**
+   * 创建者ID
+   */
+  createBy?: string | number;
+
   /**
    * 计划递交人
    */

+ 0 - 2
src/api/home/taskCenter/filing/types.ts

@@ -28,6 +28,4 @@ export interface FilingTaskQuery extends PageQuery {
 
 export interface FilingTaskFilingForm {
     documentId: number;
-    folderId: number | string;
-    projectId: number | string;
 }

+ 2 - 0
src/lang/en_US.ts

@@ -10,6 +10,8 @@ import home from './modules/home/index_en';
 import search from './modules/search/en_US';
 
 export default {
+  // 页面标题
+  title: 'DaoXiuYuan Intelligent eTMF',
   // 路由国际化
   route: {
     dashboard: 'Home Page',

+ 2 - 0
src/lang/zh_CN.ts

@@ -10,6 +10,8 @@ import home from './modules/home/index';
 import search from './modules/search/zh_CN';
 
 export default {
+  // 页面标题
+  title: '道修远智能eTMF',
   // 路由国际化
   route: {
     dashboard: '首页',

+ 13 - 0
src/main.ts

@@ -55,3 +55,16 @@ app.use(plugins);
 directive(app);
 
 app.mount('#app');
+
+// 设置页面标题(国际化)
+const updateTitle = () => {
+  document.title = i18n.global.t('title') || import.meta.env.VITE_APP_TITLE || '道修远智能eTMF';
+};
+
+// 初始化标题
+updateTitle();
+
+// 监听语言变化,动态更新标题
+watch(() => i18n.global.locale.value, () => {
+  updateTitle();
+});

+ 258 - 100
src/views/Qc/task/index.vue

@@ -4,8 +4,8 @@
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true">
-            <el-form-item label="质控名称" prop="name">
-              <el-input v-model="queryParams.name" placeholder="请输入质控名称" clearable @keyup.enter="handleQuery" />
+            <el-form-item label="任务名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入任务名称" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="发起人" prop="initiator">
               <el-input v-model="queryParams.initiator" placeholder="请输入发起人" clearable @keyup.enter="handleQuery" />
@@ -91,9 +91,17 @@
       <el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
         <el-table-column label="序号" align="center" prop="id" v-if="true" />
-        <el-table-column label="质控名称" align="center" prop="name" />
-        <el-table-column label="发起人" align="center" prop="initiator" />
-        <el-table-column label="质控项目" align="center" prop="projectId" />
+        <el-table-column label="任务名称" align="center" prop="name" width="200" />
+        <el-table-column label="发起人" align="center" prop="initiator" width="150">
+          <template #default="scope">
+            <span>{{ scope.row.initiatorName || scope.row.initiator }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="质控项目" align="center" prop="projectId" width="200">
+          <template #default="scope">
+            <span>{{ scope.row.projectName || scope.row.projectId }}</span>
+          </template>
+        </el-table-column>
         <el-table-column label="开始时间" align="center" prop="startDate" width="180">
           <template #default="scope">
             <span>{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}</span>
@@ -104,28 +112,38 @@
             <span>{{ parseTime(scope.row.deadline, '{y}-{m}-{d}') }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="状态" align="center" prop="status" />
-        <el-table-column label="备注" align="center" prop="note" />
-        <el-table-column label="创建者" align="center" prop="createBy" />
-        <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <el-table-column label="状态" align="center" prop="status" width="100">
           <template #default="scope">
-            <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+            <el-tag :type="scope.row.status === 1 ? 'success' : 'warning'">
+              {{ scope.row.status === 1 ? '已完成' : '未完成' }}
+            </el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="更新者" align="center" prop="updateBy" />
-        <el-table-column label="更新时间" align="center" prop="updateTime" width="180">
-          <template #default="scope">
-            <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" align="center" fixed="right"  class-name="small-padding fixed-width">
+        <el-table-column label="备注" align="center" prop="note" />
+        <el-table-column label="创建者" align="center" prop="createBy" width="150" />
+        <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
+        <el-table-column label="更新者" align="center" prop="updateBy" width="150" />
+        <el-table-column label="更新时间" align="center" prop="updateTime" width="180" />
+        <el-table-column label="操作" align="center" fixed="right" width="200" class-name="small-padding fixed-width">
           <template #default="scope">
-            <el-tooltip content="修改" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['Qc:task:edit']"></el-button>
-            </el-tooltip>
-            <el-tooltip content="删除" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['Qc:task:remove']"></el-button>
-            </el-tooltip>
+            <el-button
+              v-hasPermi="['qc:task:edit']"
+              type="primary"
+              icon="Edit"
+              style="padding: 0 5px; font-size: 10px; height: 24px"
+              @click="handleUpdate(scope.row)"
+            >
+              修改
+            </el-button>
+            <el-button
+              v-hasPermi="['qc:task:remove']"
+              type="danger"
+              icon="Delete"
+              style="padding: 0 5px; font-size: 10px; height: 24px"
+              @click="handleDelete(scope.row)"
+            >
+              删除
+            </el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -133,13 +151,17 @@
       <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
     <!-- 添加或修改文档质控任务对话框 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body>
       <el-form ref="taskFormRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="质控名称" prop="name">
-          <el-input v-model="form.name" placeholder="请输入质控名称" />
+        <el-form-item label="任务名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入任务名称" />
         </el-form-item>
         <el-form-item label="发起人" prop="initiator">
-          <el-input v-model="form.initiator" placeholder="请输入发起人" />
+          <el-input v-model="initiatorName" placeholder="请选择发起人" readonly @click="handleSelectInitiator" style="cursor: pointer">
+            <template #suffix>
+              <el-icon><User /></el-icon>
+            </template>
+          </el-input>
         </el-form-item>
         <el-form-item label="质控项目" prop="projectId">
           <el-select
@@ -151,35 +173,58 @@
             :remote-method="remoteSearchProject"
             :loading="projectLoading"
             style="width: 100%"
+            @change="handleProjectChange"
           >
-            <el-option
-              v-for="item in projectList"
-              :key="item.id"
-              :label="item.name"
-              :value="item.id"
-            />
+            <el-option v-for="item in projectList" :key="item.id" :label="item.name" :value="item.id" />
           </el-select>
         </el-form-item>
+
+        <!-- 质控任务列表 -->
+        <el-form-item v-if="taskDetailList.length > 0" label="质控任务">
+          <el-table :data="taskDetailList" border style="width: 100%" max-height="400">
+            <el-table-column prop="documentId" label="文档序号" width="100" align="center" />
+            <el-table-column prop="documentName" label="文档名称" min-width="150" />
+            <el-table-column label="质控人" width="150" align="center">
+              <template #default="scope">
+                <span :style="{ color: scope.row.executorStatus && scope.row.executorStatus !== '0' ? 'red' : '' }">
+                  {{ scope.row.executorName }}
+                </span>
+              </template>
+            </el-table-column>
+            <el-table-column label="状态" width="100" align="center">
+              <template #default="scope">
+                <el-tag v-if="scope.row.executorStatus" :type="scope.row.executorStatus === '0' ? 'success' : 'danger'">
+                  {{ getUserStatusText(scope.row.executorStatus) }}
+                </el-tag>
+                <span v-else>-</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" width="120" align="center">
+              <template #default="scope">
+                <el-button
+                  v-if="scope.row.executorStatus && scope.row.executorStatus !== '0'"
+                  type="warning"
+                  icon="RefreshRight"
+                  style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
+                  @click="handleChangeExecutor(scope.row)"
+                >
+                  更换质控人
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-form-item>
+
         <el-form-item label="开始时间" prop="startDate">
-          <el-date-picker clearable
-            v-model="form.startDate"
-            type="datetime"
-            value-format="YYYY-MM-DD HH:mm:ss"
-            placeholder="请选择开始时间"
-            style="width: 100%">
+          <el-date-picker clearable v-model="form.startDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择开始时间" style="width: 100%">
           </el-date-picker>
         </el-form-item>
         <el-form-item label="截止时间" prop="deadline">
-          <el-date-picker clearable
-            v-model="form.deadline"
-            type="datetime"
-            value-format="YYYY-MM-DD HH:mm:ss"
-            placeholder="请选择截止时间"
-            style="width: 100%">
+          <el-date-picker clearable v-model="form.deadline" type="date" value-format="YYYY-MM-DD" placeholder="请选择截止时间" style="width: 100%">
           </el-date-picker>
         </el-form-item>
         <el-form-item label="备注" prop="note">
-            <el-input v-model="form.note" type="textarea" placeholder="请输入内容" />
+          <el-input v-model="form.note" type="textarea" placeholder="请输入内容" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -189,14 +234,22 @@
         </div>
       </template>
     </el-dialog>
+
+    <!-- 更换质控人对话框 -->
+    <UserSelect ref="userSelectRef" :multiple="false" @confirm-call-back="handleUserSelectCallback" />
+
+    <!-- 选择发起人对话框 -->
+    <UserSelect ref="initiatorSelectRef" :multiple="false" @confirm-call-back="handleInitiatorSelectCallback" />
   </div>
 </template>
 
 <script setup name="Task" lang="ts">
-import { listTask, getTask, delTask, addTask, updateTask } from '@/api/qc/task';
-import { TaskVO, TaskQuery, TaskForm } from '@/api/qc/task/types';
+import { listTask, getTask, delTask, addTask, updateTask, generateTaskDetail } from '@/api/qc/task';
+import { TaskVO, TaskQuery, TaskForm, TaskDetailVO } from '@/api/qc/task/types';
 import { getProjectByName } from '@/api/project/management';
 import { useUserStore } from '@/store/modules/user';
+import UserSelect from '@/components/UserSelect/index.vue';
+import { UserVO } from '@/api/system/user/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const userStore = useUserStore();
@@ -218,6 +271,18 @@ const dateRangeUpdateTime = ref<[DateModelType, DateModelType]>(['', '']);
 const projectList = ref<Array<{ id: number; name: string }>>([]);
 const projectLoading = ref(false);
 
+// 质控任务详情列表
+const taskDetailList = ref<TaskDetailVO[]>([]);
+// 当前选中要更换质控人的任务详情
+const currentTaskDetail = ref<TaskDetailVO | null>(null);
+
+// 用户选择组件引用
+const userSelectRef = ref<InstanceType<typeof UserSelect>>();
+const initiatorSelectRef = ref<InstanceType<typeof UserSelect>>();
+
+// 发起人昵称(用于显示)
+const initiatorName = ref<string>('');
+
 const queryFormRef = ref<ElFormInstance>();
 const taskFormRef = ref<ElFormInstance>();
 
@@ -226,6 +291,18 @@ const dialog = reactive<DialogOption>({
   title: ''
 });
 
+// 用户状态枚举映射
+const userStatusMap = {
+  '0': '正常',
+  '1': '停用',
+  '2': '删除'
+};
+
+/** 获取用户状态文本 */
+const getUserStatusText = (status: string) => {
+  return userStatusMap[status] || '未知';
+};
+
 const initFormData: TaskForm = {
   id: undefined,
   name: undefined,
@@ -234,10 +311,10 @@ const initFormData: TaskForm = {
   startDate: undefined,
   deadline: undefined,
   status: undefined,
-  note: undefined,
-}
+  note: undefined
+};
 const data = reactive<PageData<TaskForm, TaskQuery>>({
-  form: {...initFormData},
+  form: { ...initFormData },
   queryParams: {
     pageNum: 1,
     pageSize: 10,
@@ -251,31 +328,17 @@ const data = reactive<PageData<TaskForm, TaskQuery>>({
       startDate: undefined,
       deadline: undefined,
       createTime: undefined,
-      updateTime: undefined,
+      updateTime: undefined
     }
   },
   rules: {
-    id: [
-      { required: true, message: "序号不能为空", trigger: "blur" }
-    ],
-    name: [
-      { required: true, message: "质控名称不能为空", trigger: "blur" }
-    ],
-    initiator: [
-      { required: true, message: "发起人不能为空", trigger: "blur" }
-    ],
-    projectId: [
-      { required: true, message: "质控项目不能为空", trigger: "change" }
-    ],
-    startDate: [
-      { required: true, message: "开始时间不能为空", trigger: "blur" }
-    ],
-    deadline: [
-      { required: true, message: "截止时间不能为空", trigger: "blur" }
-    ],
-    status: [
-      { required: true, message: "状态不能为空", trigger: "change" }
-    ],
+    id: [{ required: true, message: '序号不能为空', trigger: 'blur' }],
+    name: [{ required: true, message: '任务名称不能为空', trigger: 'blur' }],
+    initiator: [{ required: true, message: '发起人不能为空', trigger: 'blur' }],
+    projectId: [{ required: true, message: '质控项目不能为空', trigger: 'change' }],
+    startDate: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
+    deadline: [{ required: true, message: '截止时间不能为空', trigger: 'blur' }],
+    status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
   }
 });
 
@@ -299,6 +362,59 @@ const remoteSearchProject = async (query: string) => {
   }
 };
 
+/** 项目选择变化时加载质控任务详情 */
+const handleProjectChange = async (projectId: number) => {
+  if (!projectId) {
+    taskDetailList.value = [];
+    return;
+  }
+
+  try {
+    const res = await generateTaskDetail(projectId);
+    if (res.code === 200 && res.data) {
+      taskDetailList.value = res.data;
+    } else {
+      taskDetailList.value = [];
+      proxy?.$modal.msgWarning('获取质控任务详情失败');
+    }
+  } catch (error) {
+    console.error('获取质控任务详情失败:', error);
+    taskDetailList.value = [];
+    proxy?.$modal.msgError('获取质控任务详情失败');
+  }
+};
+
+/** 更换质控人 */
+const handleChangeExecutor = (row: TaskDetailVO) => {
+  currentTaskDetail.value = row;
+  userSelectRef.value?.open();
+};
+
+/** 用户选择回调 */
+const handleUserSelectCallback = (users: UserVO[]) => {
+  if (users && users.length > 0 && currentTaskDetail.value) {
+    const selectedUser = users[0];
+    currentTaskDetail.value.executor = selectedUser.userId;
+    currentTaskDetail.value.executorName = selectedUser.nickName || selectedUser.userName;
+    currentTaskDetail.value.executorStatus = '0'; // 新选择的用户状态为正常
+    proxy?.$modal.msgSuccess('更换质控人成功');
+  }
+};
+
+/** 选择发起人 */
+const handleSelectInitiator = () => {
+  initiatorSelectRef.value?.open();
+};
+
+/** 发起人选择回调 */
+const handleInitiatorSelectCallback = (users: UserVO[]) => {
+  if (users && users.length > 0) {
+    const selectedUser = users[0];
+    form.value.initiator = selectedUser.userId;
+    initiatorName.value = selectedUser.nickName || selectedUser.userName;
+  }
+};
+
 /** 查询文档质控任务列表 */
 const getList = async () => {
   loading.value = true;
@@ -311,26 +427,29 @@ const getList = async () => {
   taskList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 
 /** 取消按钮 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 
 /** 表单重置 */
 const reset = () => {
-  form.value = {...initFormData};
+  form.value = { ...initFormData };
   projectList.value = [];
+  taskDetailList.value = [];
+  currentTaskDetail.value = null;
+  initiatorName.value = '';
   taskFormRef.value?.resetFields();
-}
+};
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 
 /** 重置按钮操作 */
 const resetQuery = () => {
@@ -340,31 +459,40 @@ const resetQuery = () => {
   dateRangeUpdateTime.value = ['', ''];
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 
 /** 多选框选中数据 */
 const handleSelectionChange = (selection: TaskVO[]) => {
-  ids.value = selection.map(item => item.id);
+  ids.value = selection.map((item) => item.id);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 新增按钮操作 */
 const handleAdd = () => {
   reset();
   // 设置默认发起人为当前用户
-  form.value.initiator = userStore.nickname || userStore.name;
+  form.value.initiator = userStore.userId;
+  initiatorName.value = userStore.nickname || userStore.name;
   dialog.visible = true;
-  dialog.title = "添加文档质控任务";
-}
+  dialog.title = '添加文档质控任务';
+};
 
 /** 修改按钮操作 */
 const handleUpdate = async (row?: TaskVO) => {
   reset();
-  const _id = row?.id || ids.value[0]
+  const _id = row?.id || ids.value[0];
   const res = await getTask(_id);
   Object.assign(form.value, res.data);
-  
+
+  // 设置发起人昵称
+  if (res.data.initiatorName) {
+    initiatorName.value = res.data.initiatorName;
+  } else if (res.data.initiator) {
+    // 如果后端没有返回昵称,使用ID作为显示(或者可以调用接口获取)
+    initiatorName.value = String(res.data.initiator);
+  }
+
   // 如果有项目ID,加载项目信息到下拉列表
   if (form.value.projectId) {
     try {
@@ -374,43 +502,73 @@ const handleUpdate = async (row?: TaskVO) => {
       console.error('加载项目信息失败:', error);
     }
   }
-  
+
+  // 直接使用返回的 details 数据
+  if (res.data.details && res.data.details.length > 0) {
+    taskDetailList.value = res.data.details;
+  }
+
   dialog.visible = true;
-  dialog.title = "修改文档质控任务";
-}
+  dialog.title = '修改文档质控任务';
+};
 
 /** 提交按钮 */
 const submitForm = () => {
   taskFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       buttonLoading.value = true;
+
+      // 构建提交数据,将 taskDetailList 转换为 details 格式
+      const submitData = {
+        ...form.value,
+        details: taskDetailList.value.map((item) => ({
+          id: item.id,
+          taskId: item.taskId || form.value.id,
+          documentId: item.documentId,
+          executor: item.executor,
+          projectId: form.value.projectId, // 添加 projectId
+          status: item.status,
+          note: item.note,
+          createDept: item.createDept || form.value.createDept,
+          createBy: item.createBy || form.value.createBy,
+          createTime: item.createTime || form.value.createTime,
+          updateBy: item.updateBy || form.value.updateBy,
+          updateTime: item.updateTime || form.value.updateTime,
+          params: item.params || {}
+        }))
+      };
+
       if (form.value.id) {
-        await updateTask(form.value).finally(() =>  buttonLoading.value = false);
+        await updateTask(submitData).finally(() => (buttonLoading.value = false));
       } else {
-        await addTask(form.value).finally(() =>  buttonLoading.value = false);
+        await addTask(submitData).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("操作成功");
+      proxy?.$modal.msgSuccess('操作成功');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 
 /** 删除按钮操作 */
 const handleDelete = async (row?: TaskVO) => {
   const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除文档质控任务编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await proxy?.$modal.confirm('是否确认删除文档质控任务编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
   await delTask(_ids);
-  proxy?.$modal.msgSuccess("删除成功");
+  proxy?.$modal.msgSuccess('删除成功');
   await getList();
-}
+};
 
 /** 导出按钮操作 */
 const handleExport = () => {
-  proxy?.download('Qc/task/export', {
-    ...queryParams.value
-  }, `task_${new Date().getTime()}.xlsx`)
-}
+  proxy?.download(
+    'Qc/task/export',
+    {
+      ...queryParams.value
+    },
+    `task_${new Date().getTime()}.xlsx`
+  );
+};
 
 onMounted(() => {
   getList();

+ 109 - 54
src/views/document/folder/document/DocumentList.vue

@@ -116,7 +116,7 @@
             {{ t('document.document.button.submit') }}
           </el-button>
           <el-button
-            v-if="(scope.row.status === 0 || scope.row.status === 2) && scope.row.submitterId === userStore.userId"
+            v-if="(scope.row.status === 0 || scope.row.status === 2) && scope.row.createBy === userStore.userId"
             v-hasPermi="['document:document:confirmSubmit']"
             type="danger"
             icon="Delete"
@@ -220,23 +220,36 @@
       i18n-prefix="document.document"
     />
 
-    <!-- 归档确认对话框 -->
-    <ArchiveConfirmDialog 
-      ref="archiveConfirmDialogRef"
-      v-model="archiveDialog.visible" 
-      :document="archiveDialog.currentDocument" 
-      :tree-data="treeData"
-      :project-id="projectId"
-      @confirm="handleArchiveConfirm" 
-    />
-
     <!-- 审核文档对话框 -->
-    <DocumentAuditDialog
-      v-model="auditDialog.visible"
-      :document="auditDialog.document"
-      :title="t('document.document.dialog.auditDocument')"
-      @submit="handleAuditSubmit"
-    />
+    <el-dialog 
+      v-model="auditDialog.visible" 
+      :title="t('document.document.dialog.auditDocument')" 
+      width="500px"
+      @close="handleAuditDialogClose"
+    >
+      <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="100px">
+        <el-form-item label="审核结果" prop="result">
+          <el-radio-group v-model="auditForm.result">
+            <el-radio :label="3">通过</el-radio>
+            <el-radio :label="2">驳回</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item v-if="auditForm.result === 2" label="驳回理由" prop="rejectReason">
+          <el-input 
+            v-model="auditForm.rejectReason" 
+            type="textarea" 
+            :rows="4"
+            placeholder="请输入驳回理由"
+            maxlength="500"
+            show-word-limit
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="auditDialog.visible = false">取消</el-button>
+        <el-button type="primary" :loading="auditDialog.loading" @click="handleAuditConfirm">确定</el-button>
+      </template>
+    </el-dialog>
   </div>
   <el-empty v-else :description="t('document.document.empty.description')"> </el-empty>
 </template>
@@ -264,8 +277,6 @@ import { parseI18nName } from '@/utils/i18n';
 import fileUpload from '@/components/FileUpload/index.vue';
 import AuditLogDialog from '@/components/AuditLogDialog/index.vue';
 import { listDocumentAuditLog, auditDocument } from '@/api/document/document';
-import ArchiveConfirmDialog from '@/components/ArchiveConfirmDialog/index.vue';
-import DocumentAuditDialog from '@/components/DocumentAuditDialog/index.vue';
 import DocumentStatusTag from '@/components/DocumentStatusTag/index.vue';
 
 interface Props {
@@ -356,17 +367,39 @@ const auditLogDialog = reactive({
   documentId: ''
 });
 
-// 归档对话框
-const archiveDialog = reactive({
-  visible: false,
-  currentDocument: null as DocumentVO | null
-});
-const archiveConfirmDialogRef = ref<InstanceType<typeof ArchiveConfirmDialog>>();
-
 // 审核对话框状态
 const auditDialog = reactive({
   visible: false,
-  document: null as DocumentVO | null
+  document: null as DocumentVO | null,
+  loading: false
+});
+
+// 审核表单数据
+const auditForm = ref({
+  documentId: 0,
+  result: 3, // 默认通过
+  rejectReason: ''
+});
+
+// 审核表单引用
+const auditFormRef = ref<ElFormInstance>();
+
+// 审核表单验证规则
+const auditRules = reactive({
+  result: [
+    {
+      required: true,
+      message: '请选择审核结果',
+      trigger: 'change'
+    }
+  ],
+  rejectReason: [
+    {
+      required: true,
+      message: '请输入驳回理由',
+      trigger: 'blur'
+    }
+  ]
 });
 
 /**
@@ -424,24 +457,55 @@ const handleViewAuditLog = (row: DocumentVO) => {
 // 审核按钮点击事件
 const handleAuditClick = (row: DocumentVO) => {
   auditDialog.document = row;
+  auditForm.value = {
+    documentId: row.id as number,
+    result: 3, // 默认通过
+    rejectReason: ''
+  };
   auditDialog.visible = true;
+  nextTick(() => {
+    auditFormRef.value?.clearValidate();
+  });
 };
 
-// 处理审核提交
-const handleAuditSubmit = async (auditData: any) => {
+// 审核对话框关闭事件
+const handleAuditDialogClose = () => {
+  auditFormRef.value?.resetFields();
+  auditForm.value = {
+    documentId: 0,
+    result: 3,
+    rejectReason: ''
+  };
+};
+
+// 处理审核确认
+const handleAuditConfirm = async () => {
+  // 如果选择驳回,需要验证驳回理由
+  if (auditForm.value.result === 2) {
+    try {
+      await auditFormRef.value?.validate();
+    } catch (error) {
+      return;
+    }
+  }
+
+  auditDialog.loading = true;
   try {
-    console.log('[页面] 收到审核数据:', auditData);
-    
-    // 调用审核接口
-    await auditDocument(auditData);
+    const submitData = {
+      documentId: auditForm.value.documentId,
+      result: auditForm.value.result,
+      rejectReason: auditForm.value.result === 2 ? auditForm.value.rejectReason : undefined
+    };
     
+    await auditDocument(submitData);
     ElMessage.success(t('document.document.message.auditSuccess'));
-    
-    // 刷新列表
-    getDocumentList();
+    auditDialog.visible = false;
+    await getDocumentList();
   } catch (error) {
-    console.error('[页面] 审核失败:', error);
+    console.error('审核失败:', error);
     ElMessage.error(t('document.document.message.auditFailed'));
+  } finally {
+    auditDialog.loading = false;
   }
 };
 
@@ -521,28 +585,19 @@ const handleConfirmSubmit = async (row: DocumentVO) => {
 };
 
 // 归档文档
-const handleArchive = (row: DocumentVO) => {
-  archiveDialog.currentDocument = row;
-  archiveDialog.visible = true;
-};
-
-// 处理归档确认
-const handleArchiveConfirm = async (data: { document: DocumentVO; folderId: number | string; projectId: number | string }) => {
+const handleArchive = async (row: DocumentVO) => {
   try {
-    // 调用归档接口,传入新的 folderId
-    await filingDocument(data.document.id, data.folderId);
+    await proxy?.$modal.confirm('确认要归档该文档吗?');
+    await filingDocument(row.id as number);
     ElMessage.success(t('document.document.message.archiveSuccess'));
-    // 关闭所有对话框
-    archiveConfirmDialogRef.value?.closeDialog();
-    archiveDialog.visible = false;
     await getDocumentList();
   } catch (error) {
-    console.error(t('document.document.message.archiveFailed'), error);
-    ElMessage.error(t('document.document.message.archiveFailed'));
-    // 关闭loading
-    archiveConfirmDialogRef.value?.closeLoading();
+    if (error !== 'cancel') {
+      console.error(t('document.document.message.archiveFailed'), error);
+      ElMessage.error(t('document.document.message.archiveFailed'));
+    }
   }
-};;
+};
 
 // 标识文档
 const handleMark = (row: DocumentVO) => {

+ 101 - 26
src/views/home/taskCenter/audit/index.vue

@@ -104,12 +104,35 @@
     </el-card>
 
     <!-- 审核文档对话框 -->
-    <DocumentAuditDialog
-      v-model="auditDialog.visible"
-      :document="auditDialog.document"
-      :title="auditDialog.title"
-      @submit="handleAuditSubmit"
-    />
+    <el-dialog 
+      v-model="auditDialog.visible" 
+      :title="auditDialog.title" 
+      width="500px"
+      @close="handleAuditDialogClose"
+    >
+      <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="100px">
+        <el-form-item label="审核结果" prop="result">
+          <el-radio-group v-model="auditForm.result">
+            <el-radio :label="3">通过</el-radio>
+            <el-radio :label="2">驳回</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item v-if="auditForm.result === 2" label="驳回理由" prop="rejectReason">
+          <el-input 
+            v-model="auditForm.rejectReason" 
+            type="textarea" 
+            :rows="4"
+            placeholder="请输入驳回理由"
+            maxlength="500"
+            show-word-limit
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="auditDialog.visible = false">取消</el-button>
+        <el-button type="primary" :loading="auditDialog.loading" @click="handleAuditConfirm">确定</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -123,7 +146,6 @@ import { downloadDocumentFile } from '@/api/document/document';
 import { Edit, Download, Check } from '@element-plus/icons-vue';
 import DictTag from '@/components/DictTag/index.vue';
 import DocumentStatusTag from '@/components/DocumentStatusTag/index.vue';
-import DocumentAuditDialog from '@/components/DocumentAuditDialog/index.vue';
 
 const { proxy } = getCurrentInstance() as any;
 const { plan_document_type } = proxy.useDict('plan_document_type');
@@ -147,7 +169,36 @@ const total = ref(0);
 const auditDialog = reactive({
   visible: false,
   title: '审核文档',
-  document: null as AuditTaskVO | null
+  document: null as AuditTaskVO | null,
+  loading: false
+});
+
+// 审核表单数据
+const auditForm = ref({
+  documentId: 0,
+  result: 3, // 默认通过
+  rejectReason: ''
+});
+
+// 审核表单引用
+const auditFormRef = ref<FormInstance>();
+
+// 审核表单验证规则
+const auditRules = reactive({
+  result: [
+    {
+      required: true,
+      message: '请选择审核结果',
+      trigger: 'change'
+    }
+  ],
+  rejectReason: [
+    {
+      required: true,
+      message: '请输入驳回理由',
+      trigger: 'blur'
+    }
+  ]
 });
 
 /**
@@ -221,36 +272,60 @@ const resetQuery = () => {
  * 处理审核
  */
 const handleAudit = (row: AuditTaskVO) => {
-  // 将 AuditTaskVO 转换为 Document 接口格式
-  auditDialog.document = {
-    id: row.id,
-    name: row.name,
-    fileName: row.fileName || row.name, // 优先使用 fileName(带后缀),否则使用 name
-    ossId: row.ossId,
-    url: row.ossUrl || '' // 使用 ossUrl 作为 url
+  auditDialog.document = row;
+  auditForm.value = {
+    documentId: row.id,
+    result: 3, // 默认通过
+    rejectReason: ''
   };
   auditDialog.visible = true;
-  
-  console.log('[任务中心] 打开审核对话框,文档信息:', auditDialog.document);
+  nextTick(() => {
+    auditFormRef.value?.clearValidate();
+  });
 };
 
 /**
- * 处理审核提交
+ * 审核对话框关闭事件
  */
-const handleAuditSubmit = async (auditData: any) => {
+const handleAuditDialogClose = () => {
+  auditFormRef.value?.resetFields();
+  auditForm.value = {
+    documentId: 0,
+    result: 3,
+    rejectReason: ''
+  };
+};
+
+/**
+ * 处理审核确认
+ */
+const handleAuditConfirm = async () => {
+  // 如果选择驳回,需要验证驳回理由
+  if (auditForm.value.result === 2) {
+    try {
+      await auditFormRef.value?.validate();
+    } catch (error) {
+      return;
+    }
+  }
+
+  auditDialog.loading = true;
   try {
-    console.log('[任务中心] 收到审核数据:', auditData);
-    
-    // 调用审核接口
-    await auditDocument(auditData);
+    const submitData: AuditTaskAuditForm = {
+      documentId: auditForm.value.documentId,
+      result: auditForm.value.result,
+      rejectReason: auditForm.value.result === 2 ? auditForm.value.rejectReason : ''
+    };
     
+    await auditDocument(submitData);
     ElMessage.success('审批成功');
-    
-    // 刷新任务列表
+    auditDialog.visible = false;
     await getTaskList();
   } catch (error) {
-    console.error('[任务中心] 审批失败:', error);
+    console.error('审批失败:', error);
     ElMessage.error('审批失败');
+  } finally {
+    auditDialog.loading = false;
   }
 };
 

+ 6 - 79
src/views/home/taskCenter/filing/index.vue

@@ -106,16 +106,6 @@
       <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
         :total="total" @pagination="getTaskList" />
     </el-card>
-
-    <!-- 归档确认对话框 -->
-    <ArchiveConfirmDialog 
-      ref="archiveConfirmDialogRef"
-      v-model="archiveDialog.visible" 
-      :document="archiveDialog.currentDocument" 
-      :tree-data="treeData"
-      :project-id="archiveDialog.projectId"
-      @confirm="handleArchiveConfirm" 
-    />
   </div>
 </template>
 
@@ -124,13 +114,10 @@ import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
 import { ElMessage } from 'element-plus';
 import { listFilingTasks, filingDocument } from '@/api/home/taskCenter/filing';
 import { FilingTaskVO, FilingTaskFilingForm } from '@/api/home/taskCenter/filing/types';
-import { listFolderOnProject } from '@/api/document/folder';
-import { FolderListVO } from '@/api/document/folder/types';
 import { downloadDocumentFile } from '@/api/document/document';
 import { FolderAdd, Download } from '@element-plus/icons-vue';
 import DictTag from '@/components/DictTag/index.vue';
 import DocumentStatusTag from '@/components/DocumentStatusTag/index.vue';
-import ArchiveConfirmDialog from '@/components/ArchiveConfirmDialog/index.vue';
 
 const { proxy } = getCurrentInstance() as any;
 const { plan_document_type } = proxy.useDict('plan_document_type');
@@ -151,17 +138,6 @@ const queryParams = reactive({
 const taskList = ref<FilingTaskVO[]>([]);
 const total = ref(0);
 
-// 归档对话框
-const archiveDialog = reactive({
-  visible: false,
-  currentDocument: null as any,
-  projectId: undefined as number | string | undefined
-});
-const archiveConfirmDialogRef = ref<InstanceType<typeof ArchiveConfirmDialog>>();
-
-// 文件夹树数据
-const treeData = ref<FolderListVO[]>([]);
-
 /**
  * 解析时间格式
  * @param time 时间字符串
@@ -230,72 +206,23 @@ const resetQuery = () => {
   handleQuery();
 };
 
-/**
- * 获取文件夹树数据
- */
-const getFolderTree = async (projectId: number | string) => {
-  try {
-    const res = await listFolderOnProject(projectId);
-    // 检查响应格式,可能是 res.data 或 res.rows
-    if (res.code === 200) {
-      treeData.value = res.data || res.rows || [];
-      console.log('获取到的树数据:', treeData.value);
-    } else {
-      ElMessage.error(res.msg || '获取文件夹列表失败');
-      treeData.value = [];
-    }
-  } catch (error) {
-    console.error('获取文件夹列表失败:', error);
-    ElMessage.error('获取文件夹列表失败');
-    treeData.value = [];
-  }
-};
-
 /**
  * 处理归档
  */
 const handleArchive = async (row: FilingTaskVO) => {
-  // 从任务数据中获取项目ID
-  if (!row.projectId) {
-    ElMessage.warning('无法获取项目信息,请刷新后重试');
-    return;
-  }
-  
-  // 获取文件夹树数据
-  await getFolderTree(row.projectId);
-  
-  // 设置归档对话框数据
-  archiveDialog.currentDocument = {
-    id: row.id,
-    name: row.name,
-    folderId: row.folderId
-  };
-  archiveDialog.projectId = row.projectId;
-  archiveDialog.visible = true;
-};
-
-/**
- * 处理归档确认
- */
-const handleArchiveConfirm = async (data: { document: any; folderId: number | string; projectId: number | string }) => {
   try {
+    await proxy?.$modal.confirm('确认要归档该文档吗?');
     const filingData: FilingTaskFilingForm = {
-      documentId: data.document.id,
-      folderId: data.folderId,
-      projectId: data.projectId
+      documentId: row.id
     };
     await filingDocument(filingData);
     ElMessage.success('归档成功');
-    // 关闭所有对话框
-    archiveConfirmDialogRef.value?.closeDialog();
-    archiveDialog.visible = false;
-    // 刷新任务列表
     await getTaskList();
   } catch (error) {
-    console.error('归档失败:', error);
-    ElMessage.error('归档失败');
-    // 关闭loading
-    archiveConfirmDialogRef.value?.closeLoading();
+    if (error !== 'cancel') {
+      console.error('归档失败:', error);
+      ElMessage.error('归档失败');
+    }
   }
 };