|
|
@@ -62,22 +62,146 @@
|
|
|
<span v-else>-</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
+ <!-- 操作列 -->
|
|
|
+ <el-table-column label="操作" width="180" align="center" fixed="right" class-name="small-padding fixed-width">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button
|
|
|
+ v-if="scope.row.status === 2"
|
|
|
+ v-hasPermi="['taskCenter:qc:submit']"
|
|
|
+ type="primary"
|
|
|
+ icon="Upload"
|
|
|
+ style="padding: 0 5px; font-size: 10px; height: 24px"
|
|
|
+ @click="handleSubmit(scope.row)"
|
|
|
+ >
|
|
|
+ 递交
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ v-if="scope.row.status === 0 || scope.row.status === 3"
|
|
|
+ v-hasPermi="['taskCenter:qc:audit']"
|
|
|
+ type="success"
|
|
|
+ icon="Check"
|
|
|
+ style="padding: 0 5px; font-size: 10px; height: 24px"
|
|
|
+ @click="handleAudit(scope.row)"
|
|
|
+ >
|
|
|
+ 审核
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
</el-table>
|
|
|
|
|
|
<!-- 分页 -->
|
|
|
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
|
|
|
:total="total" @pagination="getTaskList" />
|
|
|
</el-card>
|
|
|
+
|
|
|
+ <!-- 递交文档对话框 -->
|
|
|
+ <el-dialog v-model="submitDialog.visible" :title="submitDialog.title" width="500px" append-to-body>
|
|
|
+ <el-alert title="仅支持上传 PDF 格式文件" type="info" :closable="false" style="margin-bottom: 20px" />
|
|
|
+ <el-form ref="submitFormRef" :model="submitForm" :rules="submitRules" label-width="100px">
|
|
|
+ <el-form-item label="文件" prop="document">
|
|
|
+ <fileUpload v-model="submitForm.document" :limit="1" :file-type="['pdf']" :is-show-tip="false" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="cancelSubmit">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="submitButtonLoading" @click="submitSubmitForm">确认</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 审核弹窗 -->
|
|
|
+ <el-dialog v-model="auditDialog.visible" title="审核" width="500px" append-to-body>
|
|
|
+ <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="1">通过</el-radio>
|
|
|
+ <el-radio :label="2">驳回</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <template v-if="auditForm.result === 2">
|
|
|
+ <el-form-item label="问题类型" prop="rejectionType">
|
|
|
+ <el-select v-model="auditForm.rejectionType" placeholder="请选择问题类型" style="width: 100%">
|
|
|
+ <el-option
|
|
|
+ v-for="dict in qc_question_type"
|
|
|
+ :key="dict.value"
|
|
|
+ :label="parseI18nName(dict.label)"
|
|
|
+ :value="dict.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="意见" prop="opinion">
|
|
|
+ <el-input
|
|
|
+ v-model="auditForm.opinion"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入意见"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="指定处理人" prop="designatedDealer">
|
|
|
+ <el-select
|
|
|
+ v-model="auditForm.designatedDealer"
|
|
|
+ filterable
|
|
|
+ remote
|
|
|
+ reserve-keyword
|
|
|
+ placeholder="请输入成员昵称搜索"
|
|
|
+ :remote-method="searchDealers"
|
|
|
+ :loading="dealerSearchLoading"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="dealer in dealerOptions"
|
|
|
+ :key="dealer.id"
|
|
|
+ :label="`${dealer.name} / ${dealer.dept} --- ${dealer.phoneNumber}`"
|
|
|
+ :value="dealer.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="截止日期" prop="deadline">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="auditForm.deadline"
|
|
|
+ type="date"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ placeholder="请选择截止日期"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="cancelAudit">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="auditLoading" @click="submitAudit">确认</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, reactive, onMounted } from 'vue';
|
|
|
+import { ref, reactive, onMounted, nextTick, computed, toRefs, getCurrentInstance } from 'vue';
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
-import { listQcTasks } from '@/api/home/taskCenter/qc';
|
|
|
-import { QcTaskVO, QcTaskQuery } from '@/api/home/taskCenter/qc/types';
|
|
|
+import type { FormInstance } from 'element-plus';
|
|
|
+import { listQcTasks, submitQcTask, auditQcTask } from '@/api/home/taskCenter/qc';
|
|
|
+import { QcTaskVO, QcTaskQuery, QcTaskSubmitForm, QcTaskAuditForm } from '@/api/home/taskCenter/qc/types';
|
|
|
+import { queryMemberNotInCenter } from '@/api/project/management';
|
|
|
+import { MemberNotInCenterVO, MemberNotInCenterQuery } from '@/api/project/management/types';
|
|
|
+import { parseI18nName } from '@/utils/i18n';
|
|
|
+import fileUpload from '@/components/FileUpload/index.vue';
|
|
|
+
|
|
|
+const { proxy } = getCurrentInstance() as any;
|
|
|
+const { qc_question_type } = toRefs<any>(proxy?.useDict('qc_question_type'));
|
|
|
|
|
|
const loading = ref(false);
|
|
|
+const submitButtonLoading = ref(false);
|
|
|
+const auditLoading = ref(false);
|
|
|
+
|
|
|
+// 当前任务
|
|
|
+const currentTask = ref<QcTaskVO | null>(null);
|
|
|
|
|
|
// 查询参数
|
|
|
const queryParams = reactive<QcTaskQuery>({
|
|
|
@@ -93,6 +217,58 @@ const queryParams = reactive<QcTaskQuery>({
|
|
|
const taskList = ref<QcTaskVO[]>([]);
|
|
|
const total = ref(0);
|
|
|
|
|
|
+// 递交对话框
|
|
|
+const submitDialog = reactive({
|
|
|
+ visible: false,
|
|
|
+ title: ''
|
|
|
+});
|
|
|
+
|
|
|
+// 递交表单ref
|
|
|
+const submitFormRef = ref<FormInstance>();
|
|
|
+
|
|
|
+// 递交表单数据
|
|
|
+const submitForm = ref({
|
|
|
+ document: ''
|
|
|
+});
|
|
|
+
|
|
|
+// 递交表单验证规则
|
|
|
+const submitRules = {
|
|
|
+ document: [{ required: true, message: '请上传文件', trigger: 'change' }]
|
|
|
+};
|
|
|
+
|
|
|
+// 审核相关
|
|
|
+const auditFormRef = ref<FormInstance>();
|
|
|
+const auditDialog = reactive({
|
|
|
+ visible: false
|
|
|
+});
|
|
|
+
|
|
|
+// 审核表单初始数据
|
|
|
+const initAuditForm: QcTaskAuditForm = {
|
|
|
+ taskId: 0,
|
|
|
+ detailId: 0,
|
|
|
+ result: 1,
|
|
|
+ rejectionType: undefined,
|
|
|
+ opinion: undefined,
|
|
|
+ designatedDealer: undefined,
|
|
|
+ deadline: undefined
|
|
|
+};
|
|
|
+
|
|
|
+const auditForm = ref<QcTaskAuditForm>({ ...initAuditForm });
|
|
|
+
|
|
|
+// 审核表单验证规则
|
|
|
+const auditRules = computed(() => ({
|
|
|
+ result: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
|
|
|
+ rejectionType: auditForm.value.result === 2 ? [{ required: true, message: '请选择问题类型', trigger: 'change' }] : [],
|
|
|
+ opinion: auditForm.value.result === 2 ? [{ required: true, message: '请输入意见', trigger: 'blur' }] : [],
|
|
|
+ designatedDealer: auditForm.value.result === 2 ? [{ required: true, message: '请选择指定处理人', trigger: 'change' }] : [],
|
|
|
+ deadline: auditForm.value.result === 2 ? [{ required: true, message: '请选择截止日期', trigger: 'change' }] : []
|
|
|
+}));
|
|
|
+
|
|
|
+// 处理人搜索相关
|
|
|
+const dealerSearchLoading = ref(false);
|
|
|
+const dealerOptions = ref<MemberNotInCenterVO[]>([]);
|
|
|
+let dealerSearchTimer: NodeJS.Timeout | null = null;
|
|
|
+
|
|
|
/**
|
|
|
* 获取任务列表
|
|
|
*/
|
|
|
@@ -133,6 +309,142 @@ const resetQuery = () => {
|
|
|
handleQuery();
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * 处理递交
|
|
|
+ */
|
|
|
+const handleSubmit = (row: QcTaskVO) => {
|
|
|
+ currentTask.value = row;
|
|
|
+ submitForm.value = {
|
|
|
+ document: ''
|
|
|
+ };
|
|
|
+ submitDialog.visible = true;
|
|
|
+ submitDialog.title = '递交文档';
|
|
|
+ nextTick(() => {
|
|
|
+ submitFormRef.value?.clearValidate();
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 取消递交
|
|
|
+ */
|
|
|
+const cancelSubmit = () => {
|
|
|
+ submitDialog.visible = false;
|
|
|
+ submitForm.value = {
|
|
|
+ document: ''
|
|
|
+ };
|
|
|
+ currentTask.value = null;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 提交递交表单
|
|
|
+ */
|
|
|
+const submitSubmitForm = () => {
|
|
|
+ submitFormRef.value?.validate(async (valid: boolean) => {
|
|
|
+ if (valid && currentTask.value) {
|
|
|
+ submitButtonLoading.value = true;
|
|
|
+ try {
|
|
|
+ const submitData: QcTaskSubmitForm = {
|
|
|
+ detailId: currentTask.value.id,
|
|
|
+ document: Number(submitForm.value.document)
|
|
|
+ };
|
|
|
+ await submitQcTask(submitData);
|
|
|
+ ElMessage.success('递交成功');
|
|
|
+ submitDialog.visible = false;
|
|
|
+ await getTaskList();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('递交失败:', error);
|
|
|
+ ElMessage.error('递交失败');
|
|
|
+ } finally {
|
|
|
+ submitButtonLoading.value = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/** 审核任务 */
|
|
|
+const handleAudit = (row: QcTaskVO) => {
|
|
|
+ currentTask.value = row;
|
|
|
+ auditForm.value = {
|
|
|
+ ...initAuditForm,
|
|
|
+ taskId: row.taskId || 0,
|
|
|
+ detailId: row.id
|
|
|
+ };
|
|
|
+ dealerOptions.value = [];
|
|
|
+ auditDialog.visible = true;
|
|
|
+};
|
|
|
+
|
|
|
+/** 搜索处理人 */
|
|
|
+const searchDealers = async (query: string) => {
|
|
|
+ if (!query || query.trim() === '') {
|
|
|
+ dealerOptions.value = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dealerSearchTimer) {
|
|
|
+ clearTimeout(dealerSearchTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ dealerSearchTimer = setTimeout(async () => {
|
|
|
+ dealerSearchLoading.value = true;
|
|
|
+ try {
|
|
|
+ const queryParams: MemberNotInCenterQuery = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ projectId: currentTask.value?.projectId || 0,
|
|
|
+ folderId: 0,
|
|
|
+ name: query
|
|
|
+ };
|
|
|
+ const res = await queryMemberNotInCenter(queryParams);
|
|
|
+ dealerOptions.value = res.rows || [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('搜索处理人失败:', error);
|
|
|
+ ElMessage.error('搜索处理人失败');
|
|
|
+ } finally {
|
|
|
+ dealerSearchLoading.value = false;
|
|
|
+ }
|
|
|
+ }, 300);
|
|
|
+};
|
|
|
+
|
|
|
+/** 取消审核 */
|
|
|
+const cancelAudit = () => {
|
|
|
+ auditDialog.visible = false;
|
|
|
+ auditFormRef.value?.resetFields();
|
|
|
+ currentTask.value = null;
|
|
|
+};
|
|
|
+
|
|
|
+/** 提交审核 */
|
|
|
+const submitAudit = () => {
|
|
|
+ auditFormRef.value?.validate(async (valid: boolean) => {
|
|
|
+ if (valid) {
|
|
|
+ auditLoading.value = true;
|
|
|
+ try {
|
|
|
+ const submitData: QcTaskAuditForm = {
|
|
|
+ taskId: auditForm.value.taskId,
|
|
|
+ detailId: auditForm.value.detailId,
|
|
|
+ result: auditForm.value.result
|
|
|
+ };
|
|
|
+
|
|
|
+ // 驳回时添加额外字段
|
|
|
+ if (auditForm.value.result === 2) {
|
|
|
+ submitData.rejectionType = auditForm.value.rejectionType;
|
|
|
+ submitData.opinion = auditForm.value.opinion;
|
|
|
+ submitData.designatedDealer = auditForm.value.designatedDealer;
|
|
|
+ submitData.deadline = auditForm.value.deadline;
|
|
|
+ }
|
|
|
+
|
|
|
+ await auditQcTask(submitData);
|
|
|
+ ElMessage.success('审核成功');
|
|
|
+ auditDialog.visible = false;
|
|
|
+ await getTaskList();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('审核失败:', error);
|
|
|
+ } finally {
|
|
|
+ auditLoading.value = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
getTaskList();
|
|
|
});
|
|
|
@@ -142,6 +454,26 @@ onMounted(() => {
|
|
|
.qc-task-container {
|
|
|
padding: 20px;
|
|
|
|
|
|
+ .flex {
|
|
|
+ display: flex;
|
|
|
+ }
|
|
|
+
|
|
|
+ .justify-between {
|
|
|
+ justify-content: space-between;
|
|
|
+ }
|
|
|
+
|
|
|
+ .items-center {
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .text-lg {
|
|
|
+ font-size: 1.125rem;
|
|
|
+ }
|
|
|
+
|
|
|
+ .font-bold {
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+
|
|
|
.search-form {
|
|
|
margin-bottom: 15px;
|
|
|
}
|