Merge branch 'dev_天津_阳光印刷' of http://114.132.189.42:9002/r/product-inventory-management into dev_天津_阳光印刷
| | |
| | | <template> |
| | | <div> |
| | | |
| | | <!-- 申请类型选择 --> |
| | | <el-card class="type-card"> |
| | | <div class="type-selector"> |
| | | <div |
| | | v-for="type in applicationTypes" |
| | | :key="type.value" |
| | | class="type-item" |
| | | :class="{ active: currentType === type.value }" |
| | | @click="changeType(type.value)" |
| | | > |
| | | <div v-for="type in applicationTypes" |
| | | :key="type.value" |
| | | class="type-item" |
| | | :class="{ active: currentType === type.value }" |
| | | @click="changeType(type.value)"> |
| | | <div class="type-icon"> |
| | | <el-icon :size="24"><component :is="type.icon"/></el-icon> |
| | | <el-icon :size="24"> |
| | | <component :is="type.icon" /> |
| | | </el-icon> |
| | | </div> |
| | | <div class="type-info"> |
| | | <div class="type-name">{{ type.name }}</div> |
| | |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 会议申请表单 --> |
| | | <el-card> |
| | | <div class="form-header"> |
| | | <h3>{{ getCurrentTypeName() }}申请</h3> |
| | | </div> |
| | | |
| | | <el-form |
| | | ref="meetingFormRef" |
| | | :model="meetingForm" |
| | | :rules="rules" |
| | | label-width="100px" |
| | | > |
| | | <el-form ref="meetingFormRef" |
| | | :model="meetingForm" |
| | | :rules="rules" |
| | | label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="会议主题" prop="title"> |
| | | <el-input v-model="meetingForm.title" placeholder="请输入会议主题"/> |
| | | <el-form-item label="会议主题" |
| | | prop="title"> |
| | | <el-input v-model="meetingForm.title" |
| | | placeholder="请输入会议主题" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="会议室" prop="roomId"> |
| | | <el-select v-model="meetingForm.roomId" placeholder="请选择会议室" style="width: 100%"> |
| | | <el-option |
| | | v-for="room in meetingRooms" |
| | | :key="room.id" |
| | | :label="`${room.name} (${room.location})`" |
| | | :value="room.id" |
| | | /> |
| | | <el-form-item label="会议室" |
| | | prop="roomId"> |
| | | <el-select v-model="meetingForm.roomId" |
| | | placeholder="请选择会议室" |
| | | style="width: 100%"> |
| | | <el-option v-for="room in meetingRooms" |
| | | :key="room.id" |
| | | :label="`${room.name} (${room.location})`" |
| | | :value="room.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="主持人" prop="host"> |
| | | <el-input v-model="meetingForm.host" placeholder="请输入主持人姓名"/> |
| | | <el-form-item label="主持人" |
| | | prop="host"> |
| | | <el-input v-model="meetingForm.host" |
| | | placeholder="请输入主持人姓名" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="会议日期" prop="meetingDate"> |
| | | <el-date-picker |
| | | v-model="meetingForm.meetingDate" |
| | | type="date" |
| | | placeholder="请选择会议日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | :disabled-date="disabledDate" |
| | | style="width: 100%" |
| | | /> |
| | | <el-form-item label="会议日期" |
| | | prop="meetingDate"> |
| | | <el-date-picker v-model="meetingForm.meetingDate" |
| | | type="date" |
| | | placeholder="请选择会议日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | :disabled-date="disabledDate" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <!-- 空列,保持布局 --> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="开始时间" prop="startTime"> |
| | | <el-select |
| | | v-model="meetingForm.startTime" |
| | | placeholder="请选择开始时间" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="time in startTimeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" |
| | | /> |
| | | <el-form-item label="开始时间" |
| | | prop="startTime"> |
| | | <el-select v-model="meetingForm.startTime" |
| | | placeholder="请选择开始时间" |
| | | style="width: 100%"> |
| | | <el-option v-for="time in startTimeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="结束时间" prop="endTime"> |
| | | <el-select |
| | | v-model="meetingForm.endTime" |
| | | placeholder="请选择结束时间" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="time in endTimeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" |
| | | /> |
| | | <el-form-item label="结束时间" |
| | | prop="endTime"> |
| | | <el-select v-model="meetingForm.endTime" |
| | | placeholder="请选择结束时间" |
| | | style="width: 100%"> |
| | | <el-option v-for="time in endTimeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-form-item label="参会人员" prop="participants"> |
| | | <el-select |
| | | v-model="meetingForm.participants" |
| | | multiple |
| | | filterable |
| | | placeholder="请选择参会人员" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="person in employees" |
| | | :key="person.id" |
| | | :label="`${person.staffName}${person.postName ? ` (${person.postName})` : ''}`" |
| | | :value="person.id" |
| | | /> |
| | | <el-form-item label="参会人员" |
| | | prop="participants"> |
| | | <el-select v-model="meetingForm.participants" |
| | | multiple |
| | | filterable |
| | | placeholder="请选择参会人员" |
| | | style="width: 100%"> |
| | | <el-option v-for="user in users" |
| | | :key="user.userId" |
| | | :label="user.deptNames ? `${user.nickName} (${user.deptNames})` : user.nickName" |
| | | :value="user.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="会议说明" prop="description"> |
| | | <el-input |
| | | v-model="meetingForm.description" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请输入会议说明" |
| | | /> |
| | | <el-form-item label="会议说明" |
| | | prop="description"> |
| | | <el-input v-model="meetingForm.description" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请输入会议说明" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div class="form-footer"> |
| | | <el-button @click="resetForm">重置</el-button> |
| | | <el-button type="primary" @click="submitForm">提交</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">提交</el-button> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, onMounted, computed, watch} from 'vue' |
| | | import {ElMessage} from 'element-plus' |
| | | import {Plus, Document, Promotion, Bell} from '@element-plus/icons-vue' |
| | | import {getRoomEnum, saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js' |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { ref, reactive, onMounted, computed, watch } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { Plus, Document, Promotion, Bell } from "@element-plus/icons-vue"; |
| | | import { |
| | | getRoomEnum, |
| | | saveMeetingApplication, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | // 当前申请类型 |
| | | const currentType = ref('department') // approval: 审批流程, department: 部门级, notification: 通知发布 |
| | | // 当前申请类型 |
| | | const currentType = ref("department"); // approval: 审批流程, department: 部门级, notification: 通知发布 |
| | | |
| | | // 申请类型选项 |
| | | const applicationTypes = ref([ |
| | | { |
| | | value: 'approval', |
| | | name: '审批流程会议', |
| | | desc: '需要经过多级审批的会议申请', |
| | | icon: Document |
| | | }, |
| | | { |
| | | value: 'department', |
| | | name: '部门级会议', |
| | | desc: '部门内部会议申请流程', |
| | | icon: Promotion |
| | | }, |
| | | { |
| | | value: 'notification', |
| | | name: '会议通知', |
| | | desc: '无需审批直接发布的会议通知', |
| | | icon: Bell |
| | | } |
| | | ]) |
| | | // 申请类型选项 |
| | | const applicationTypes = ref([ |
| | | { |
| | | value: "approval", |
| | | name: "审批流程会议", |
| | | desc: "需要经过多级审批的会议申请", |
| | | icon: Document, |
| | | }, |
| | | { |
| | | value: "department", |
| | | name: "部门级会议", |
| | | desc: "部门内部会议申请流程", |
| | | icon: Promotion, |
| | | }, |
| | | { |
| | | value: "notification", |
| | | name: "会议通知", |
| | | desc: "无需审批直接发布的会议通知", |
| | | icon: Bell, |
| | | }, |
| | | ]); |
| | | |
| | | // 表单数据 |
| | | const meetingForm = reactive({ |
| | | title: '', |
| | | type: '', |
| | | roomId: '', |
| | | host: '', |
| | | meetingDate: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | participants: [], |
| | | description: '' |
| | | }) |
| | | // 表单数据 |
| | | const meetingForm = reactive({ |
| | | title: "", |
| | | type: "", |
| | | roomId: "", |
| | | host: "", |
| | | meetingDate: "", |
| | | startTime: "", |
| | | endTime: "", |
| | | participants: [], |
| | | description: "", |
| | | }); |
| | | |
| | | // 表单引用 |
| | | const meetingFormRef = ref(null) |
| | | // 表单引用 |
| | | const meetingFormRef = ref(null); |
| | | |
| | | // 会议室列表 |
| | | const meetingRooms = ref([]) |
| | | // 会议室列表 |
| | | const meetingRooms = ref([]); |
| | | |
| | | // 员工列表 |
| | | const employees = ref([]) |
| | | // 用户列表(系统管理-用户管理) |
| | | const users = ref([]); |
| | | |
| | | // 时间选项(以半小时为间隔) |
| | | const timeOptions = ref([]) |
| | | // 时间选项(以半小时为间隔) |
| | | const timeOptions = ref([]); |
| | | |
| | | const getTimeInMinutes = (time) => { |
| | | if (!time) return -1 |
| | | const [hour, minute] = time.split(':').map(Number) |
| | | return hour * 60 + minute |
| | | } |
| | | const getTimeInMinutes = time => { |
| | | if (!time) return -1; |
| | | const [hour, minute] = time.split(":").map(Number); |
| | | return hour * 60 + minute; |
| | | }; |
| | | |
| | | const isToday = (dateText) => { |
| | | if (!dateText) return false |
| | | const [year, month, day] = dateText.split('-').map(Number) |
| | | const now = new Date() |
| | | return year === now.getFullYear() && month === now.getMonth() + 1 && day === now.getDate() |
| | | } |
| | | const isToday = dateText => { |
| | | if (!dateText) return false; |
| | | const [year, month, day] = dateText.split("-").map(Number); |
| | | const now = new Date(); |
| | | return ( |
| | | year === now.getFullYear() && |
| | | month === now.getMonth() + 1 && |
| | | day === now.getDate() |
| | | ); |
| | | }; |
| | | |
| | | const validateStartTime = (_rule, value, callback) => { |
| | | if (!value) { |
| | | callback() |
| | | return |
| | | } |
| | | |
| | | if (isToday(meetingForm.meetingDate)) { |
| | | const now = new Date() |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes() |
| | | if (getTimeInMinutes(value) > currentMinutes) { |
| | | callback(new Error('当天开始时间不能晚于当前时间')) |
| | | return |
| | | const validateStartTime = (_rule, value, callback) => { |
| | | if (!value) { |
| | | callback(); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | callback() |
| | | } |
| | | |
| | | const validateEndTime = (_rule, value, callback) => { |
| | | if (!value || !meetingForm.startTime) { |
| | | callback() |
| | | return |
| | | } |
| | | |
| | | if (getTimeInMinutes(value) <= getTimeInMinutes(meetingForm.startTime)) { |
| | | callback(new Error('结束时间必须大于开始时间')) |
| | | return |
| | | } |
| | | |
| | | callback() |
| | | } |
| | | |
| | | // 表单校验规则 |
| | | const rules = { |
| | | title: [{required: true, message: '请输入会议主题', trigger: 'blur'}], |
| | | roomId: [{required: true, message: '请选择会议室', trigger: 'change'}], |
| | | host: [{required: true, message: '请输入主持人', trigger: 'blur'}], |
| | | meetingDate: [{required: true, message: '请选择会议日期', trigger: 'change'}], |
| | | startTime: [ |
| | | {required: true, message: '请选择开始时间', trigger: 'change'}, |
| | | {validator: validateStartTime, trigger: 'change'} |
| | | ], |
| | | endTime: [ |
| | | {required: true, message: '请选择结束时间', trigger: 'change'}, |
| | | {validator: validateEndTime, trigger: 'change'} |
| | | ], |
| | | participants: [{required: true, message: '请选择参会人员', trigger: 'change'}] |
| | | } |
| | | |
| | | const startTimeOptions = computed(() => { |
| | | if (!isToday(meetingForm.meetingDate)) { |
| | | return timeOptions.value |
| | | } |
| | | const now = new Date() |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes() |
| | | return timeOptions.value.filter(item => getTimeInMinutes(item.value) <= currentMinutes) |
| | | }) |
| | | |
| | | const endTimeOptions = computed(() => { |
| | | if (!meetingForm.startTime) { |
| | | return timeOptions.value |
| | | } |
| | | const startMinutes = getTimeInMinutes(meetingForm.startTime) |
| | | return timeOptions.value.filter(item => getTimeInMinutes(item.value) > startMinutes) |
| | | }) |
| | | |
| | | // 初始化时间选项 |
| | | const initTimeOptions = () => { |
| | | const options = [] |
| | | const now = new Date() |
| | | const currentHour = now.getHours() |
| | | const currentMinute = now.getMinutes() |
| | | // meetingDate 是 "yyyy-MM-dd" |
| | | const meetingDate = new Date(meetingForm.meetingDate) |
| | | |
| | | const isSameDay = |
| | | now.getFullYear() === meetingDate.getFullYear() && |
| | | now.getMonth() === meetingDate.getMonth() && |
| | | now.getDate() === meetingDate.getDate() |
| | | |
| | | console.log('是否同一天:', isSameDay) |
| | | for (let hour = 8; hour <= 18; hour++) { |
| | | // 开始时间必须晚于当前时间 |
| | | if (hour < currentHour && isSameDay) { |
| | | continue |
| | | if (isToday(meetingForm.meetingDate)) { |
| | | const now = new Date(); |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes(); |
| | | if (getTimeInMinutes(value) > currentMinutes) { |
| | | callback(new Error("当天开始时间不能晚于当前时间")); |
| | | return; |
| | | } |
| | | } |
| | | if (hour === currentHour && currentMinute > 30 && isSameDay) { |
| | | continue |
| | | } |
| | | // 每个小时添加两个选项:整点和半点 |
| | | options.push({ |
| | | value: `${hour.toString().padStart(2, '0')}:00`, |
| | | label: `${hour.toString().padStart(2, '0')}:00` |
| | | }) |
| | | |
| | | if (hour < 18) { // 18:00之后没有半点选项 |
| | | callback(); |
| | | }; |
| | | |
| | | const validateEndTime = (_rule, value, callback) => { |
| | | if (!value || !meetingForm.startTime) { |
| | | callback(); |
| | | return; |
| | | } |
| | | |
| | | if (getTimeInMinutes(value) <= getTimeInMinutes(meetingForm.startTime)) { |
| | | callback(new Error("结束时间必须大于开始时间")); |
| | | return; |
| | | } |
| | | |
| | | callback(); |
| | | }; |
| | | |
| | | // 表单校验规则 |
| | | const rules = { |
| | | title: [{ required: true, message: "请输入会议主题", trigger: "blur" }], |
| | | roomId: [{ required: true, message: "请选择会议室", trigger: "change" }], |
| | | host: [{ required: true, message: "请输入主持人", trigger: "blur" }], |
| | | meetingDate: [ |
| | | { required: true, message: "请选择会议日期", trigger: "change" }, |
| | | ], |
| | | startTime: [ |
| | | { required: true, message: "请选择开始时间", trigger: "change" }, |
| | | { validator: validateStartTime, trigger: "change" }, |
| | | ], |
| | | endTime: [ |
| | | { required: true, message: "请选择结束时间", trigger: "change" }, |
| | | { validator: validateEndTime, trigger: "change" }, |
| | | ], |
| | | participants: [ |
| | | { required: true, message: "请选择参会人员", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | const startTimeOptions = computed(() => { |
| | | if (!isToday(meetingForm.meetingDate)) { |
| | | return timeOptions.value; |
| | | } |
| | | const now = new Date(); |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes(); |
| | | return timeOptions.value.filter( |
| | | item => getTimeInMinutes(item.value) <= currentMinutes |
| | | ); |
| | | }); |
| | | |
| | | const endTimeOptions = computed(() => { |
| | | if (!meetingForm.startTime) { |
| | | return timeOptions.value; |
| | | } |
| | | const startMinutes = getTimeInMinutes(meetingForm.startTime); |
| | | return timeOptions.value.filter( |
| | | item => getTimeInMinutes(item.value) > startMinutes |
| | | ); |
| | | }); |
| | | |
| | | // 初始化时间选项 |
| | | const initTimeOptions = () => { |
| | | const options = []; |
| | | const now = new Date(); |
| | | const currentHour = now.getHours(); |
| | | const currentMinute = now.getMinutes(); |
| | | // meetingDate 是 "yyyy-MM-dd" |
| | | const meetingDate = new Date(meetingForm.meetingDate); |
| | | |
| | | const isSameDay = |
| | | now.getFullYear() === meetingDate.getFullYear() && |
| | | now.getMonth() === meetingDate.getMonth() && |
| | | now.getDate() === meetingDate.getDate(); |
| | | |
| | | console.log("是否同一天:", isSameDay); |
| | | for (let hour = 8; hour <= 18; hour++) { |
| | | // 开始时间必须晚于当前时间 |
| | | if (hour < currentHour && isSameDay) { |
| | | continue; |
| | | } |
| | | if (hour === currentHour && currentMinute > 30 && isSameDay) { |
| | | continue; |
| | | } |
| | | // 每个小时添加两个选项:整点和半点 |
| | | options.push({ |
| | | value: `${hour.toString().padStart(2, '0')}:30`, |
| | | label: `${hour.toString().padStart(2, '0')}:30` |
| | | }) |
| | | value: `${hour.toString().padStart(2, "0")}:00`, |
| | | label: `${hour.toString().padStart(2, "0")}:00`, |
| | | }); |
| | | |
| | | if (hour < 18) { |
| | | // 18:00之后没有半点选项 |
| | | options.push({ |
| | | value: `${hour.toString().padStart(2, "0")}:30`, |
| | | label: `${hour.toString().padStart(2, "0")}:30`, |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | timeOptions.value = options |
| | | } |
| | | timeOptions.value = options; |
| | | }; |
| | | |
| | | watch(() => meetingForm.meetingDate, () => { |
| | | if (meetingForm.startTime && !startTimeOptions.value.some(item => item.value === meetingForm.startTime)) { |
| | | meetingForm.startTime = '' |
| | | } |
| | | if (meetingForm.endTime && !endTimeOptions.value.some(item => item.value === meetingForm.endTime)) { |
| | | meetingForm.endTime = '' |
| | | } |
| | | if (meetingForm.startTime) { |
| | | meetingFormRef.value?.validateField('startTime') |
| | | } |
| | | if (meetingForm.endTime) { |
| | | meetingFormRef.value?.validateField('endTime') |
| | | } |
| | | initTimeOptions() |
| | | }) |
| | | |
| | | watch(() => meetingForm.startTime, () => { |
| | | if (meetingForm.endTime && getTimeInMinutes(meetingForm.endTime) <= getTimeInMinutes(meetingForm.startTime)) { |
| | | meetingForm.endTime = '' |
| | | } |
| | | if (meetingForm.endTime) { |
| | | meetingFormRef.value?.validateField('endTime') |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 禁用日期(禁用今天之前的日期) |
| | | const disabledDate = (time) => { |
| | | // 禁用今天之前的日期 |
| | | return time.getTime() < Date.now() - 86400000 |
| | | } |
| | | |
| | | // 切换申请类型 |
| | | const changeType = (type) => { |
| | | currentType.value = type |
| | | } |
| | | |
| | | // 获取当前类型名称 |
| | | const getCurrentTypeName = () => { |
| | | const type = applicationTypes.value.find(t => t.value === currentType.value) |
| | | return type ? type.name : '' |
| | | } |
| | | |
| | | // 重置表单 |
| | | const resetForm = () => { |
| | | meetingFormRef.value?.resetFields() |
| | | } |
| | | |
| | | // 提交表单 |
| | | const submitForm = () => { |
| | | meetingFormRef.value?.validate((valid) => { |
| | | if (valid) { |
| | | |
| | | let formData = {...meetingForm} |
| | | formData.applicationType = currentType.value |
| | | formData.startTime = `${meetingForm.meetingDate} ${meetingForm.startTime}:00` |
| | | formData.endTime = `${meetingForm.meetingDate} ${meetingForm.endTime}:00` |
| | | formData.participants = JSON.stringify(formData.participants) |
| | | console.log(formData) |
| | | saveMeetingApplication(formData).then(() => { |
| | | |
| | | // 模拟提交操作 |
| | | ElMessage.success(`${getCurrentTypeName()}提交成功`) |
| | | |
| | | // 根据不同类型执行不同操作 |
| | | switch (currentType.value) { |
| | | case 'approval': |
| | | ElMessage.info('会议已提交审批流程') |
| | | break |
| | | case 'department': |
| | | ElMessage.info('部门级会议申请已提交') |
| | | break |
| | | case 'notification': |
| | | ElMessage.info('会议通知已发布') |
| | | break |
| | | } |
| | | resetForm() |
| | | }) |
| | | |
| | | watch( |
| | | () => meetingForm.meetingDate, |
| | | () => { |
| | | if ( |
| | | meetingForm.startTime && |
| | | !startTimeOptions.value.some(item => item.value === meetingForm.startTime) |
| | | ) { |
| | | meetingForm.startTime = ""; |
| | | } |
| | | if ( |
| | | meetingForm.endTime && |
| | | !endTimeOptions.value.some(item => item.value === meetingForm.endTime) |
| | | ) { |
| | | meetingForm.endTime = ""; |
| | | } |
| | | if (meetingForm.startTime) { |
| | | meetingFormRef.value?.validateField("startTime"); |
| | | } |
| | | if (meetingForm.endTime) { |
| | | meetingFormRef.value?.validateField("endTime"); |
| | | } |
| | | initTimeOptions(); |
| | | } |
| | | }) |
| | | } |
| | | ); |
| | | |
| | | // 页面加载时初始化 |
| | | onMounted(() => { |
| | | initTimeOptions() |
| | | getRoomEnum().then(res => { |
| | | meetingRooms.value = res.data |
| | | }) |
| | | staffOnJobListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | staffState: 1 |
| | | }).then(res => { |
| | | employees.value = res.data.records.sort((a, b) => (a.postName || '').localeCompare(b.postName || '')) |
| | | }) |
| | | }) |
| | | watch( |
| | | () => meetingForm.startTime, |
| | | () => { |
| | | if ( |
| | | meetingForm.endTime && |
| | | getTimeInMinutes(meetingForm.endTime) <= |
| | | getTimeInMinutes(meetingForm.startTime) |
| | | ) { |
| | | meetingForm.endTime = ""; |
| | | } |
| | | if (meetingForm.endTime) { |
| | | meetingFormRef.value?.validateField("endTime"); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | // 禁用日期(禁用今天之前的日期) |
| | | const disabledDate = time => { |
| | | // 禁用今天之前的日期 |
| | | return time.getTime() < Date.now() - 86400000; |
| | | }; |
| | | |
| | | // 切换申请类型 |
| | | const changeType = type => { |
| | | currentType.value = type; |
| | | }; |
| | | |
| | | // 获取当前类型名称 |
| | | const getCurrentTypeName = () => { |
| | | const type = applicationTypes.value.find(t => t.value === currentType.value); |
| | | return type ? type.name : ""; |
| | | }; |
| | | |
| | | // 重置表单 |
| | | const resetForm = () => { |
| | | meetingFormRef.value?.resetFields(); |
| | | }; |
| | | |
| | | // 提交表单 |
| | | const submitForm = () => { |
| | | meetingFormRef.value?.validate(valid => { |
| | | if (valid) { |
| | | let formData = { ...meetingForm }; |
| | | formData.applicationType = currentType.value; |
| | | formData.startTime = `${meetingForm.meetingDate} ${meetingForm.startTime}:00`; |
| | | formData.endTime = `${meetingForm.meetingDate} ${meetingForm.endTime}:00`; |
| | | formData.participants = JSON.stringify(formData.participants); |
| | | console.log(formData); |
| | | saveMeetingApplication(formData).then(() => { |
| | | // 模拟提交操作 |
| | | ElMessage.success(`${getCurrentTypeName()}提交成功`); |
| | | |
| | | // 根据不同类型执行不同操作 |
| | | switch (currentType.value) { |
| | | case "approval": |
| | | ElMessage.info("会议已提交审批流程"); |
| | | break; |
| | | case "department": |
| | | ElMessage.info("部门级会议申请已提交"); |
| | | break; |
| | | case "notification": |
| | | ElMessage.info("会议通知已发布"); |
| | | break; |
| | | } |
| | | resetForm(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 页面加载时初始化 |
| | | onMounted(() => { |
| | | initTimeOptions(); |
| | | getRoomEnum().then(res => { |
| | | meetingRooms.value = res.data; |
| | | }); |
| | | userListNoPageByTenantId().then(res => { |
| | | const list = Array.isArray(res?.data) ? res.data : []; |
| | | users.value = list |
| | | .map(item => ({ |
| | | userId: item?.userId, |
| | | nickName: |
| | | item?.nickName || item?.userName || String(item?.userId ?? ""), |
| | | deptNames: item?.deptNames || "", |
| | | })) |
| | | .filter( |
| | | item => |
| | | item.userId !== null && item.userId !== undefined && item.nickName |
| | | ) |
| | | .sort((a, b) => String(a.nickName).localeCompare(String(b.nickName))); |
| | | }); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .page-header h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | .page-header h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .type-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | .type-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .type-selector { |
| | | display: flex; |
| | | gap: 20px; |
| | | } |
| | | .type-selector { |
| | | display: flex; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .type-item { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 20px; |
| | | border: 1px solid #ebeef5; |
| | | border-radius: 8px; |
| | | cursor: pointer; |
| | | transition: all 0.3s; |
| | | } |
| | | .type-item { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 20px; |
| | | border: 1px solid #ebeef5; |
| | | border-radius: 8px; |
| | | cursor: pointer; |
| | | transition: all 0.3s; |
| | | } |
| | | |
| | | .type-item:hover { |
| | | border-color: #409eff; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | } |
| | | .type-item:hover { |
| | | border-color: #409eff; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .type-item.active { |
| | | border-color: #409eff; |
| | | background-color: #ecf5ff; |
| | | } |
| | | .type-item.active { |
| | | border-color: #409eff; |
| | | background-color: #ecf5ff; |
| | | } |
| | | |
| | | .type-icon { |
| | | margin-right: 15px; |
| | | color: #409eff; |
| | | } |
| | | .type-icon { |
| | | margin-right: 15px; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .type-name { |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | margin-bottom: 5px; |
| | | } |
| | | .type-name { |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .type-desc { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | .type-desc { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .form-header { |
| | | margin-bottom: 20px; |
| | | padding-bottom: 15px; |
| | | border-bottom: 1px solid #ebeef5; |
| | | } |
| | | .form-header { |
| | | margin-bottom: 20px; |
| | | padding-bottom: 15px; |
| | | border-bottom: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .form-header h3 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | .form-header h3 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .form-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | margin-top: 30px; |
| | | padding-top: 20px; |
| | | border-top: 1px solid #ebeef5; |
| | | } |
| | | .form-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | margin-top: 30px; |
| | | padding-top: 20px; |
| | | border-top: 1px solid #ebeef5; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div> |
| | | |
| | | <el-form :model="searchForm" inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable/> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable/> |
| | | </el-form-item> |
| | | <el-form-item label="审批状态"> |
| | | <el-select style="width: 100px" v-model="searchForm.status" placeholder="请选择审批状态" clearable> |
| | | <el-option label="待审批" value="0"/> |
| | | <el-option label="已通过" value="1"/> |
| | | <el-option label="未审批" value="2"/> |
| | | <el-option label="已取消" value="3"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <el-form :model="searchForm" |
| | | inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" |
| | | placeholder="请输入会议主题" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" |
| | | placeholder="请输入申请人" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="审批状态"> |
| | | <el-select style="width: 100px" |
| | | v-model="searchForm.status" |
| | | placeholder="请选择审批状态" |
| | | clearable> |
| | | <el-option label="待审批" |
| | | value="0" /> |
| | | <el-option label="已通过" |
| | | value="1" /> |
| | | <el-option label="未审批" |
| | | value="2" /> |
| | | <el-option label="已取消" |
| | | value="3" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <!-- 会议审批列表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="approvalList" border :height="tableHeight"> |
| | | <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip/> |
| | | <el-table-column prop="applicant" label="申请人" align="center" width="120"/> |
| | | <el-table-column prop="host" label="主理人" align="center" width="120"/> |
| | | <el-table-column prop="meetingTime" label="会议时间" align="center" width="180"> |
| | | <el-table v-loading="loading" |
| | | :data="approvalList" |
| | | border |
| | | :height="tableHeight"> |
| | | <el-table-column prop="title" |
| | | label="会议主题" |
| | | align="center" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" |
| | | label="申请人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="host" |
| | | label="主理人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="meetingTime" |
| | | label="会议时间" |
| | | align="center" |
| | | width="180"> |
| | | <template #default="scope"> |
| | | {{ formatDateTime(scope.row.meetingTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="会议地点" align="center" width="150"/> |
| | | <el-table-column prop="participants" label="参会人数" align="center" width="100"> |
| | | <el-table-column prop="location" |
| | | label="会议地点" |
| | | align="center" |
| | | width="150" /> |
| | | <el-table-column prop="participants" |
| | | label="参会人数" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="审批状态" align="center" width="120"> |
| | | <el-table-column prop="status" |
| | | label="审批状态" |
| | | align="center" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="200" fixed="right"> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | v-if="scope.row.status == '0'" |
| | | type="primary" |
| | | link |
| | | @click="handleApproval(scope.row)" |
| | | > |
| | | <el-button type="primary" |
| | | link |
| | | @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button v-if="scope.row.status == '0'" |
| | | type="primary" |
| | | link |
| | | @click="handleApproval(scope.row)"> |
| | | 审批 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 会议详情对话框 --> |
| | | <el-dialog |
| | | title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | <el-dialog title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions label-width="100px" class="meeting-desc" :column="2" border> |
| | | <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{ |
| | | <el-descriptions label-width="100px" |
| | | class="meeting-desc" |
| | | :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="主理人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.host |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2" |
| | | label-class-name="nowrap-label"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="会议地点" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="参会人数" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.participants.length |
| | | }}人</el-descriptions-item> |
| | | <el-descriptions-item label="审批状态" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="审批状态" |
| | | label-class-name="nowrap-label"> |
| | | <el-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请时间" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2" |
| | | <el-descriptions-item style="max-height: 400px" |
| | | label="会议说明" |
| | | :span="2" |
| | | label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 会议审批对话框 --> |
| | | <el-dialog |
| | | title="会议审批" |
| | | v-model="approvalDialogVisible" |
| | | > |
| | | <el-dialog title="会议审批" |
| | | v-model="approvalDialogVisible"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人">{{ currentMeeting.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-show="false" class="approval-opinion mt-20"> |
| | | <div v-show="false" |
| | | class="approval-opinion mt-20"> |
| | | <h4>审批意见</h4> |
| | | <el-input |
| | | v-model="approvalOpinion" |
| | | type="textarea" |
| | | placeholder="请输入审批意见" |
| | | :rows="4" |
| | | /> |
| | | <el-input v-model="approvalOpinion" |
| | | type="textarea" |
| | | placeholder="请输入审批意见" |
| | | :rows="4" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="approvalDialogVisible = false">取 消</el-button> |
| | | <el-button type="danger" @click="submitApproval('2')">不通过</el-button> |
| | | <el-button type="primary" @click="submitApproval('1')">通 过</el-button> |
| | | <el-button type="danger" |
| | | @click="submitApproval('2')">不通过</el-button> |
| | | <el-button type="primary" |
| | | @click="submitApproval('1')">通 过</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, onMounted} from 'vue' |
| | | import {ElMessage, ElMessageBox} from 'element-plus' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import {getRoomEnum, getExamineList,saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js' |
| | | import dayjs from "dayjs"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import { |
| | | getRoomEnum, |
| | | getExamineList, |
| | | saveMeetingApplication, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import dayjs from "dayjs"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | // 数据列表加载状态 |
| | | const loading = ref(false) |
| | | // 数据列表加载状态 |
| | | const loading = ref(false); |
| | | |
| | | // 总条数 |
| | | const total = ref(0) |
| | | // 总条数 |
| | | const total = ref(0); |
| | | |
| | | // 表格高度(根据窗口高度自适应) |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | // 审批列表数据 |
| | | const approvalList = ref([]) |
| | | // 表格高度(根据窗口高度自适应) |
| | | const tableHeight = ref(window.innerHeight - 380); |
| | | const roomEnum = ref([]); |
| | | const userList = ref([]); |
| | | // 审批列表数据 |
| | | const approvalList = ref([]); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | }); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false) |
| | | const approvalDialogVisible = ref(false) |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false); |
| | | const approvalDialogVisible = ref(false); |
| | | |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null) |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null); |
| | | |
| | | // 审批意见 |
| | | const approvalOpinion = ref('') |
| | | // 审批意见 |
| | | const approvalOpinion = ref(""); |
| | | |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getExamineList({...searchForm, ...queryParams}) |
| | | approvalList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.size |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}${staff.postName ? ` (${staff.postName})` : ''}` |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true; |
| | | let resp = await getExamineList({ ...searchForm, ...queryParams }); |
| | | const userMap = new Map(userList.value.map(u => [String(u.userId), u])); |
| | | approvalList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id); |
| | | it.location = `${room.name}(${room.location})`; |
| | | let participantIds = []; |
| | | try { |
| | | participantIds = Array.isArray(it.participants) |
| | | ? it.participants |
| | | : JSON.parse(it.participants); |
| | | } catch (_e) { |
| | | participantIds = []; |
| | | } |
| | | }) |
| | | if (!Array.isArray(participantIds)) participantIds = []; |
| | | it.staffCount = participantIds.length; |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format( |
| | | "HH:mm:ss" |
| | | )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`; |
| | | it.participants = participantIds.map(id => { |
| | | const user = userMap.get(String(id)); |
| | | const nickName = user?.nickName || user?.userName || String(id ?? ""); |
| | | const deptNames = user?.deptNames || ""; |
| | | return { |
| | | id, |
| | | name: deptNames ? `${nickName} (${deptNames})` : nickName, |
| | | }; |
| | | }); |
| | | |
| | | return it; |
| | | }); |
| | | total.value = resp.data.total; |
| | | loading.value = false; |
| | | }; |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1 |
| | | getList() |
| | | } |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | // 查看详情 |
| | | const viewDetail = row => { |
| | | currentMeeting.value = row; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | // 处理审批 |
| | | const handleApproval = row => { |
| | | currentMeeting.value = row; |
| | | approvalOpinion.value = ""; |
| | | approvalDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 处理审批 |
| | | const handleApproval = (row) => { |
| | | currentMeeting.value = row |
| | | approvalOpinion.value = '' |
| | | approvalDialogVisible.value = true |
| | | } |
| | | // 获取状态类型 |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | 0: "info", // 待审批 |
| | | 1: "success", // 已通过 |
| | | 2: "warning", // 未通过 |
| | | 3: "danger", // 取消 |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | '0': 'info', // 待审批 |
| | | '1': 'success', // 已通过 |
| | | '2': 'warning', // 未通过 |
| | | '3': 'danger' // 取消 |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | // 获取状态文本 |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | 0: "待审批", |
| | | 1: "已通过", |
| | | 2: "未通过", |
| | | 3: "已取消", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | '0': '待审批', |
| | | '1': '已通过', |
| | | '2': '未通过', |
| | | '3': '已取消' |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | // 格式化日期时间 |
| | | const formatDateTime = dateTime => { |
| | | if (!dateTime) return ""; |
| | | return dateTime.replace(" ", "\n"); |
| | | }; |
| | | |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | // 提交审批 |
| | | const submitApproval = status => { |
| | | // if (status === 'approved' && !approvalOpinion.value.trim()) { |
| | | // ElMessage.warning('请填写审批意见') |
| | | // return |
| | | // } |
| | | |
| | | // 提交审批 |
| | | const submitApproval = (status) => { |
| | | // if (status === 'approved' && !approvalOpinion.value.trim()) { |
| | | // ElMessage.warning('请填写审批意见') |
| | | // return |
| | | // } |
| | | |
| | | ElMessageBox.confirm( |
| | | `确认${status === '1' ? '通过' : '不通过'}该会议申请?`, |
| | | '审批确认', |
| | | ElMessageBox.confirm( |
| | | `确认${status === "1" ? "通过" : "不通过"}该会议申请?`, |
| | | "审批确认", |
| | | { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | } |
| | | ).then(() => { |
| | | saveMeetingApplication({ |
| | | id: currentMeeting.value.id, |
| | | status: status |
| | | }).then(resp=>{ |
| | | // 更新会议状态 |
| | | currentMeeting.value.status = status |
| | | ) |
| | | .then(() => { |
| | | saveMeetingApplication({ |
| | | id: currentMeeting.value.id, |
| | | status: status, |
| | | }).then(resp => { |
| | | // 更新会议状态 |
| | | currentMeeting.value.status = status; |
| | | |
| | | ElMessage.success('审批提交成功') |
| | | approvalDialogVisible.value = false |
| | | getList() |
| | | }) |
| | | ElMessage.success("审批提交成功"); |
| | | approvalDialogVisible.value = false; |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | }).catch(() => { |
| | | }) |
| | | } |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2] = await Promise.all([ |
| | | getRoomEnum(), |
| | | userListNoPageByTenantId(), |
| | | ]); |
| | | roomEnum.value = resp1.data; |
| | | const list = Array.isArray(resp2?.data) ? resp2.data : []; |
| | | userList.value = list |
| | | .map(item => ({ |
| | | userId: item?.userId, |
| | | nickName: item?.nickName, |
| | | userName: item?.userName, |
| | | deptNames: |
| | | item?.deptNames || (item?.dept?.deptName ? item.dept.deptName : ""), |
| | | })) |
| | | .filter(item => item.userId !== null && item.userId !== undefined); |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2]= await Promise.all([getRoomEnum(), staffOnJobListPage({current: -1, size: -1, staffState: 1})]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data.records |
| | | |
| | | await getList() |
| | | }) |
| | | await getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .page-header h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | .page-header h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .approval-opinion h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | .approval-opinion h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .nowrap-label { |
| | | white-space: nowrap !important; |
| | | } |
| | | .nowrap-label { |
| | | white-space: nowrap !important; |
| | | } |
| | | |
| | | .description-content { |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | line-height: 1.6; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | min-height: 60px; |
| | | } |
| | | .description-content { |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | line-height: 1.6; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | min-height: 60px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div> |
| | | |
| | | <el-form :model="searchForm" inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable/> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable/> |
| | | </el-form-item> |
| | | <el-form-item label="发布状态"> |
| | | <el-select style="width: 100px" v-model="searchForm.status" placeholder="请选择发布状态" clearable> |
| | | <el-option label="待发布" value="0"/> |
| | | <el-option label="已发布" value="1"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <el-form :model="searchForm" |
| | | inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" |
| | | placeholder="请输入会议主题" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" |
| | | placeholder="请输入申请人" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="发布状态"> |
| | | <el-select style="width: 100px" |
| | | v-model="searchForm.status" |
| | | placeholder="请选择发布状态" |
| | | clearable> |
| | | <el-option label="待发布" |
| | | value="0" /> |
| | | <el-option label="已发布" |
| | | value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <!-- 会议发布列表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="approvalList" border :height="tableHeight"> |
| | | <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip/> |
| | | <el-table-column prop="applicant" label="申请人" align="center" width="120"/> |
| | | <el-table-column prop="host" label="主理人" align="center" width="120"/> |
| | | <el-table-column prop="meetingTime" label="会议时间" align="center" width="180"> |
| | | <el-table v-loading="loading" |
| | | :data="approvalList" |
| | | border |
| | | :height="tableHeight"> |
| | | <el-table-column prop="title" |
| | | label="会议主题" |
| | | align="center" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" |
| | | label="申请人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="host" |
| | | label="主理人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="meetingTime" |
| | | label="会议时间" |
| | | align="center" |
| | | width="180"> |
| | | <template #default="scope"> |
| | | {{ formatDateTime(scope.row.meetingTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="会议地点" align="center" width="150"/> |
| | | <el-table-column prop="participants" label="参会人数" align="center" width="100"> |
| | | <el-table-column prop="location" |
| | | label="会议地点" |
| | | align="center" |
| | | width="150" /> |
| | | <el-table-column prop="participants" |
| | | label="参会人数" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="发布状态" align="center" width="120"> |
| | | <el-table-column prop="status" |
| | | label="发布状态" |
| | | align="center" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="200" fixed="right"> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | v-if="scope.row.status == '0'" |
| | | type="primary" |
| | | link |
| | | @click="handleApproval(scope.row)" |
| | | > |
| | | <el-button type="primary" |
| | | link |
| | | @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button v-if="scope.row.status == '0'" |
| | | type="primary" |
| | | link |
| | | @click="handleApproval(scope.row)"> |
| | | 发布 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 会议详情对话框 --> |
| | | <el-dialog |
| | | title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | <el-dialog title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions label-width="100px" class="meeting-desc" :column="2" border> |
| | | <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{ |
| | | <el-descriptions label-width="100px" |
| | | class="meeting-desc" |
| | | :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="主理人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.host |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2" |
| | | label-class-name="nowrap-label"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="会议地点" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="参会人数" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.participants.length |
| | | }}人</el-descriptions-item> |
| | | <el-descriptions-item label="发布状态" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="发布状态" |
| | | label-class-name="nowrap-label"> |
| | | <el-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请时间" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2" |
| | | <el-descriptions-item style="max-height: 400px" |
| | | label="会议说明" |
| | | :span="2" |
| | | label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 会议发布对话框 --> |
| | | <el-dialog |
| | | title="会议发布" |
| | | v-model="approvalDialogVisible" |
| | | > |
| | | <el-dialog title="会议发布" |
| | | v-model="approvalDialogVisible"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人">{{ currentMeeting.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="approval-opinion mt-20"> |
| | | <h4>发布意见</h4> |
| | | <el-input |
| | | v-model="publishComment" |
| | | type="textarea" |
| | | placeholder="请输入发布意见" |
| | | :rows="4" |
| | | /> |
| | | <el-input v-model="publishComment" |
| | | type="textarea" |
| | | placeholder="请输入发布意见" |
| | | :rows="4" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="approvalDialogVisible = false">取 消</el-button> |
| | | <!-- <el-button type="danger" @click="submitApproval('2')">不通过</el-button>--> |
| | | <el-button type="primary" @click="submitApproval('1')">发 布</el-button> |
| | | <!-- <el-button type="danger" @click="submitApproval('2')">不通过</el-button>--> |
| | | <el-button type="primary" |
| | | @click="submitApproval('1')">发 布</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, onMounted} from 'vue' |
| | | import {ElMessage, ElMessageBox} from 'element-plus' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import {getRoomEnum, getMeetingPublish,saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js' |
| | | import dayjs from "dayjs"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import { |
| | | getRoomEnum, |
| | | getMeetingPublish, |
| | | saveMeetingApplication, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import dayjs from "dayjs"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | // 数据列表加载状态 |
| | | const loading = ref(false) |
| | | // 数据列表加载状态 |
| | | const loading = ref(false); |
| | | |
| | | // 总条数 |
| | | const total = ref(0) |
| | | // 总条数 |
| | | const total = ref(0); |
| | | |
| | | // 表格高度(根据窗口高度自适应) |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | // 发布列表数据 |
| | | const approvalList = ref([]) |
| | | // 表格高度(根据窗口高度自适应) |
| | | const tableHeight = ref(window.innerHeight - 380); |
| | | const roomEnum = ref([]); |
| | | const userList = ref([]); |
| | | // 发布列表数据 |
| | | const approvalList = ref([]); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | }); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false) |
| | | const approvalDialogVisible = ref(false) |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false); |
| | | const approvalDialogVisible = ref(false); |
| | | |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null) |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null); |
| | | |
| | | // 发布意见 |
| | | const publishComment = ref('') |
| | | // 发布意见 |
| | | const publishComment = ref(""); |
| | | |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getMeetingPublish({...searchForm, ...queryParams}) |
| | | approvalList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.size |
| | | it.status = it.publishStatus |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}${staff.postName ? ` (${staff.postName})` : ''}` |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true; |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }); |
| | | const userMap = new Map(userList.value.map(u => [String(u.userId), u])); |
| | | approvalList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id); |
| | | it.location = `${room.name}(${room.location})`; |
| | | let participantIds = []; |
| | | try { |
| | | participantIds = Array.isArray(it.participants) |
| | | ? it.participants |
| | | : JSON.parse(it.participants); |
| | | } catch (_e) { |
| | | participantIds = []; |
| | | } |
| | | }) |
| | | if (!Array.isArray(participantIds)) participantIds = []; |
| | | it.staffCount = participantIds.length; |
| | | it.status = it.publishStatus; |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format( |
| | | "HH:mm:ss" |
| | | )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`; |
| | | it.participants = participantIds.map(id => { |
| | | const user = userMap.get(String(id)); |
| | | const nickName = user?.nickName || user?.userName || String(id ?? ""); |
| | | const deptNames = user?.deptNames || ""; |
| | | return { |
| | | id, |
| | | name: deptNames ? `${nickName} (${deptNames})` : nickName, |
| | | }; |
| | | }); |
| | | |
| | | return it; |
| | | }); |
| | | total.value = resp.data.total; |
| | | loading.value = false; |
| | | }; |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1 |
| | | getList() |
| | | } |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | // 查看详情 |
| | | const viewDetail = row => { |
| | | currentMeeting.value = row; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | // 处理发布 |
| | | const handleApproval = row => { |
| | | currentMeeting.value = row; |
| | | publishComment.value = ""; |
| | | approvalDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 处理发布 |
| | | const handleApproval = (row) => { |
| | | currentMeeting.value = row |
| | | publishComment.value = '' |
| | | approvalDialogVisible.value = true |
| | | } |
| | | // 获取状态类型 |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | 0: "info", // 待发布 |
| | | 1: "success", // 已通过 |
| | | 2: "danger", // 未通过 |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | '0': 'info', // 待发布 |
| | | '1': 'success', // 已通过 |
| | | '2': 'danger', // 未通过 |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | // 获取状态文本 |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | 0: "待发布", |
| | | 1: "已发布", |
| | | 2: "已取消", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | '0': '待发布', |
| | | '1': '已发布', |
| | | '2': '已取消', |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | // 格式化日期时间 |
| | | const formatDateTime = dateTime => { |
| | | if (!dateTime) return ""; |
| | | return dateTime.replace(" ", "\n"); |
| | | }; |
| | | |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | // 提交发布 |
| | | const submitApproval = status => { |
| | | // if (status === 'approved' && !publishComment.value.trim()) { |
| | | // ElMessage.warning('请填写发布意见') |
| | | // return |
| | | // } |
| | | |
| | | // 提交发布 |
| | | const submitApproval = (status) => { |
| | | // if (status === 'approved' && !publishComment.value.trim()) { |
| | | // ElMessage.warning('请填写发布意见') |
| | | // return |
| | | // } |
| | | |
| | | ElMessageBox.confirm( |
| | | `确认${status === '1' ? '发布' : '取消'}该会议?`, |
| | | '发布确认', |
| | | ElMessageBox.confirm( |
| | | `确认${status === "1" ? "发布" : "取消"}该会议?`, |
| | | "发布确认", |
| | | { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | } |
| | | ).then(() => { |
| | | saveMeetingApplication({ |
| | | id: currentMeeting.value.id, |
| | | publishStatus: status, |
| | | publishComment: publishComment.value |
| | | }).then(resp=>{ |
| | | // 更新会议状态 |
| | | currentMeeting.value.status = status |
| | | ) |
| | | .then(() => { |
| | | saveMeetingApplication({ |
| | | id: currentMeeting.value.id, |
| | | publishStatus: status, |
| | | publishComment: publishComment.value, |
| | | }).then(resp => { |
| | | // 更新会议状态 |
| | | currentMeeting.value.status = status; |
| | | |
| | | ElMessage.success('发布提交成功') |
| | | approvalDialogVisible.value = false |
| | | getList() |
| | | }) |
| | | ElMessage.success("发布提交成功"); |
| | | approvalDialogVisible.value = false; |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | }).catch(() => { |
| | | }) |
| | | } |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2] = await Promise.all([ |
| | | getRoomEnum(), |
| | | userListNoPageByTenantId(), |
| | | ]); |
| | | roomEnum.value = resp1.data; |
| | | const list = Array.isArray(resp2?.data) ? resp2.data : []; |
| | | userList.value = list |
| | | .map(item => ({ |
| | | userId: item?.userId, |
| | | nickName: item?.nickName, |
| | | userName: item?.userName, |
| | | deptNames: |
| | | item?.deptNames || (item?.dept?.deptName ? item.dept.deptName : ""), |
| | | })) |
| | | .filter(item => item.userId !== null && item.userId !== undefined); |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2]= await Promise.all([getRoomEnum(), staffOnJobListPage({current: -1, size: -1, staffState: 1})]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data.records |
| | | |
| | | await getList() |
| | | }) |
| | | await getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .page-header h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | .page-header h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .approval-opinion h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | .approval-opinion h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .nowrap-label { |
| | | white-space: nowrap !important; |
| | | } |
| | | .nowrap-label { |
| | | white-space: nowrap !important; |
| | | } |
| | | |
| | | .description-content { |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | line-height: 1.6; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | min-height: 60px; |
| | | } |
| | | .description-content { |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | line-height: 1.6; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | min-height: 60px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div> |
| | | |
| | | <el-form :model="searchForm" inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <el-form :model="searchForm" |
| | | inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" |
| | | placeholder="请输入会议主题" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" |
| | | placeholder="请输入申请人" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <!-- 会议列表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="meetingList" border :height="tableHeight"> |
| | | <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" label="申请人" align="center" width="120" /> |
| | | <el-table-column prop="host" label="主持人" align="center" width="120" /> |
| | | <el-table-column prop="meetingTime" label="会议时间" align="center" width="180"> |
| | | <el-table v-loading="loading" |
| | | :data="meetingList" |
| | | border |
| | | :height="tableHeight"> |
| | | <el-table-column prop="title" |
| | | label="会议主题" |
| | | align="center" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" |
| | | label="申请人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="host" |
| | | label="主持人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="meetingTime" |
| | | label="会议时间" |
| | | align="center" |
| | | width="180"> |
| | | <template #default="scope"> |
| | | {{ formatDateTime(scope.row.meetingTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="会议地点" align="center" width="150" /> |
| | | <el-table-column prop="participants" label="参会人数" align="center" width="100"> |
| | | <el-table-column prop="location" |
| | | label="会议地点" |
| | | align="center" |
| | | width="150" /> |
| | | <el-table-column prop="participants" |
| | | label="参会人数" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="200" fixed="right"> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | type="primary" |
| | | link |
| | | @click="addMinutes(scope.row)" |
| | | > |
| | | <el-button type="primary" |
| | | link |
| | | @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="addMinutes(scope.row)"> |
| | | 添加纪要 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 会议详情对话框 --> |
| | | <el-dialog |
| | | title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | <el-dialog title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions label-width="100px" class="meeting-desc" :column="2" border> |
| | | <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{ |
| | | <el-descriptions label-width="100px" |
| | | class="meeting-desc" |
| | | :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="主持人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="主持人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.host |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2" |
| | | label-class-name="nowrap-label"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="会议地点" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="参会人数" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.participants.length |
| | | }}人</el-descriptions-item> |
| | | <el-descriptions-item label="审批状态" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="审批状态" |
| | | label-class-name="nowrap-label"> |
| | | <el-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请时间" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2" |
| | | label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item> |
| | | <el-descriptions-item style="max-height: 400px" |
| | | label="会议说明" |
| | | :span="2" |
| | | label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 添加会议纪要对话框 --> |
| | | <el-dialog |
| | | title="添加会议纪要" |
| | | v-model="minutesDialogVisible" |
| | | width="80%" |
| | | @close="handleCloseMinutesDialog" |
| | | > |
| | | <el-dialog title="添加会议纪要" |
| | | v-model="minutesDialogVisible" |
| | | width="80%" |
| | | @close="handleCloseMinutesDialog"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item> |
| | | <el-descriptions-item label="主持人">{{ currentMeeting.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>会议纪要内容</h4> |
| | | <div class="editor-container"> |
| | | <Editor |
| | | v-model="minutesContent" |
| | | :min-height="400" |
| | | /> |
| | | <Editor v-model="minutesContent" |
| | | :min-height="400" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="minutesDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="submitMinutes">保 存</el-button> |
| | | <el-button type="primary" |
| | | @click="submitMinutes">保 存</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import Editor from '@/components/Editor/index.vue' |
| | | import { getRoomEnum, getMeetingPublish ,getMeetingMinutesByMeetingId,saveMeetingMinutes} from '@/api/collaborativeApproval/meeting.js' |
| | | import dayjs from "dayjs" |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import Editor from "@/components/Editor/index.vue"; |
| | | import { |
| | | getRoomEnum, |
| | | getMeetingPublish, |
| | | getMeetingMinutesByMeetingId, |
| | | saveMeetingMinutes, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import dayjs from "dayjs"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | // 数据列表加载状态 |
| | | const loading = ref(false) |
| | | // 数据列表加载状态 |
| | | const loading = ref(false); |
| | | |
| | | // 总条数 |
| | | const total = ref(0) |
| | | // 总条数 |
| | | const total = ref(0); |
| | | |
| | | // 表格高度(根据窗口高度自适应) |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | // 表格高度(根据窗口高度自适应) |
| | | const tableHeight = ref(window.innerHeight - 380); |
| | | const roomEnum = ref([]); |
| | | const userList = ref([]); |
| | | |
| | | // 会议列表数据 |
| | | const meetingList = ref([]) |
| | | // 会议列表数据 |
| | | const meetingList = ref([]); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | }); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | // status: '1' // 默认只显示已通过审批的会议 |
| | | }) |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: "", |
| | | applicant: "", |
| | | // status: '1' // 默认只显示已通过审批的会议 |
| | | }); |
| | | |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false) |
| | | const minutesDialogVisible = ref(false) |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false); |
| | | const minutesDialogVisible = ref(false); |
| | | |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null) |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null); |
| | | |
| | | // 会议纪要内容 |
| | | const minutesContent = ref('') |
| | | const minutesContentId = ref('') |
| | | // 会议纪要内容 |
| | | const minutesContent = ref(""); |
| | | const minutesContentId = ref(""); |
| | | |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }) |
| | | meetingList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.size |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id => id === staff.id)).map(staff => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}${staff.postName ? ` (${staff.postName})` : ''}` |
| | | } |
| | | }) |
| | | const parseParticipants = raw => { |
| | | if (!raw) return []; |
| | | if (Array.isArray(raw)) return raw; |
| | | try { |
| | | const parsed = JSON.parse(raw); |
| | | return Array.isArray(parsed) ? parsed : []; |
| | | } catch (_e) { |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true; |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }); |
| | | const userMap = new Map(userList.value.map(u => [String(u.userId), u])); |
| | | meetingList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id); |
| | | it.location = `${room.name}(${room.location})`; |
| | | const participantIds = parseParticipants(it.participants); |
| | | it.staffCount = participantIds.length; |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format( |
| | | "HH:mm:ss" |
| | | )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`; |
| | | it.participants = participantIds.map(id => { |
| | | const user = userMap.get(String(id)); |
| | | const nickName = user?.nickName || user?.userName || String(id ?? ""); |
| | | const deptNames = user?.deptNames || ""; |
| | | return { |
| | | id, |
| | | name: deptNames ? `${nickName} (${deptNames})` : nickName, |
| | | }; |
| | | }); |
| | | |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.current = 1 |
| | | getList() |
| | | } |
| | | return it; |
| | | }); |
| | | total.value = resp.data.total; |
| | | loading.value = false; |
| | | }; |
| | | |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | // status: '1' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: "", |
| | | applicant: "", |
| | | // status: '1' |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | // 添加会议纪要 |
| | | const addMinutes = async (row) => { |
| | | let resp = await getMeetingMinutesByMeetingId(row.id) |
| | | currentMeeting.value = row |
| | | if (resp.data){ |
| | | minutesContent.value = resp.data.content |
| | | minutesContentId.value = resp.data.id |
| | | }else { |
| | | minutesContent.value = `<h2>${row.title}会议纪要</h2> |
| | | <p><strong>会议时间:</strong>${row.meetingTime}</p> |
| | | <p><strong>会议地点:</strong>${row.location}</p> |
| | | <p><strong>主持人:</strong>${row.host}</p> |
| | | <p><strong>参会人员:</strong></p> |
| | | <ol> |
| | | ${row.participants.map(p => `<li>${p.name}</li>`).join('')} |
| | | </ol> |
| | | <p><strong>会议内容:</strong></p> |
| | | <ol> |
| | | <li>议题一: |
| | | <ul> |
| | | <li>讨论内容:</li> |
| | | <li>决议事项:</li> |
| | | </ul> |
| | | </li> |
| | | <li>议题二: |
| | | <ul> |
| | | <li>讨论内容:</li> |
| | | <li>决议事项:</li> |
| | | </ul> |
| | | </li> |
| | | </ol> |
| | | <p><strong>备注:</strong></p>` |
| | | } |
| | | // 查看详情 |
| | | const viewDetail = row => { |
| | | currentMeeting.value = row; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | minutesDialogVisible.value = true |
| | | } |
| | | // 添加会议纪要 |
| | | const addMinutes = async row => { |
| | | let resp = await getMeetingMinutesByMeetingId(row.id); |
| | | currentMeeting.value = row; |
| | | if (resp.data) { |
| | | minutesContent.value = resp.data.content; |
| | | minutesContentId.value = resp.data.id; |
| | | } else { |
| | | minutesContent.value = `<h2>${row.title}会议纪要</h2> |
| | | <p><strong>会议时间:</strong>${row.meetingTime}</p> |
| | | <p><strong>会议地点:</strong>${row.location}</p> |
| | | <p><strong>主持人:</strong>${row.host}</p> |
| | | <p><strong>参会人员:</strong></p> |
| | | <ol> |
| | | ${row.participants.map(p => `<li>${p.name}</li>`).join("")} |
| | | </ol> |
| | | <p><strong>会议内容:</strong></p> |
| | | <ol> |
| | | <li>议题一: |
| | | <ul> |
| | | <li>讨论内容:</li> |
| | | <li>决议事项:</li> |
| | | </ul> |
| | | </li> |
| | | <li>议题二: |
| | | <ul> |
| | | <li>讨论内容:</li> |
| | | <li>决议事项:</li> |
| | | </ul> |
| | | </li> |
| | | </ol> |
| | | <p><strong>备注:</strong></p>`; |
| | | } |
| | | |
| | | // 提交会议纪要 |
| | | const submitMinutes = () => { |
| | | if (!minutesContent.value) { |
| | | ElMessage.warning('请输入会议纪要内容') |
| | | return |
| | | } |
| | | saveMeetingMinutes({ |
| | | id: minutesContentId.value, |
| | | content: minutesContent.value, |
| | | meetingId: currentMeeting.value.id, |
| | | title: currentMeeting.value.title |
| | | }).then(resp=>{ |
| | | console.log('会议纪要内容:', minutesContent.value) |
| | | ElMessage.success('会议纪要保存成功') |
| | | minutesDialogVisible.value = false |
| | | }) |
| | | minutesDialogVisible.value = true; |
| | | }; |
| | | |
| | | } |
| | | // 提交会议纪要 |
| | | const submitMinutes = () => { |
| | | if (!minutesContent.value) { |
| | | ElMessage.warning("请输入会议纪要内容"); |
| | | return; |
| | | } |
| | | saveMeetingMinutes({ |
| | | id: minutesContentId.value, |
| | | content: minutesContent.value, |
| | | meetingId: currentMeeting.value.id, |
| | | title: currentMeeting.value.title, |
| | | }).then(resp => { |
| | | console.log("会议纪要内容:", minutesContent.value); |
| | | ElMessage.success("会议纪要保存成功"); |
| | | minutesDialogVisible.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 关闭会议纪要对话框 |
| | | const handleCloseMinutesDialog = () => { |
| | | minutesContent.value = '' |
| | | } |
| | | // 关闭会议纪要对话框 |
| | | const handleCloseMinutesDialog = () => { |
| | | minutesContent.value = ""; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | '0': 'info', // 待审批 |
| | | '1': 'success', // 已通过 |
| | | '2': 'warning', // 未通过 |
| | | '3': 'danger' // 取消 |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | // 获取状态类型 |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | 0: "info", // 待审批 |
| | | 1: "success", // 已通过 |
| | | 2: "warning", // 未通过 |
| | | 3: "danger", // 取消 |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | '0': '待审批', |
| | | '1': '已通过', |
| | | '2': '未通过', |
| | | '3': '已取消' |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | // 获取状态文本 |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | 0: "待审批", |
| | | 1: "已通过", |
| | | 2: "未通过", |
| | | 3: "已取消", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | // 格式化日期时间 |
| | | const formatDateTime = dateTime => { |
| | | if (!dateTime) return ""; |
| | | return dateTime.replace(" ", "\n"); |
| | | }; |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2] = await Promise.all([getRoomEnum(), staffOnJobListPage({current: -1, size: -1, staffState: 1})]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data.records |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2] = await Promise.all([ |
| | | getRoomEnum(), |
| | | userListNoPageByTenantId(), |
| | | ]); |
| | | roomEnum.value = resp1.data; |
| | | const list = Array.isArray(resp2?.data) ? resp2.data : []; |
| | | userList.value = list |
| | | .map(item => ({ |
| | | userId: item?.userId, |
| | | nickName: item?.nickName, |
| | | userName: item?.userName, |
| | | deptNames: |
| | | item?.deptNames || (item?.dept?.deptName ? item.dept.deptName : ""), |
| | | })) |
| | | .filter(item => item.userId !== null && item.userId !== undefined); |
| | | |
| | | await getList() |
| | | }) |
| | | await getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .page-header h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | .page-header h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .nowrap-label { |
| | | white-space: nowrap !important; |
| | | } |
| | | .nowrap-label { |
| | | white-space: nowrap !important; |
| | | } |
| | | |
| | | .editor-container { |
| | | border: 1px solid #dcdfe6; |
| | | border-radius: 4px; |
| | | } |
| | | .editor-container { |
| | | border: 1px solid #dcdfe6; |
| | | border-radius: 4px; |
| | | } |
| | | </style> |
| | |
| | | <el-form-item label="班组信息" |
| | | prop="teamList"> |
| | | <el-select v-model="reportForm.teamList" |
| | | ref="teamSelectRef" |
| | | multiple |
| | | filterable |
| | | allow-create |
| | |
| | | value-key="userName" |
| | | placeholder="请选择或输入班组成员" |
| | | @change="handleTeamListChange"> |
| | | <el-option v-for="user in reportForm.userIdsList" |
| | | :key="user.userId" |
| | | <el-option v-for="user in teamSelectOptions" |
| | | :key="user.userId || `custom-${user.nickName}`" |
| | | :label="user.nickName" |
| | | :value="{ userId: user.userId, userName: user.nickName }" /> |
| | | </el-select> |
| | |
| | | const currentUserId = ref(""); |
| | | const deviceOptions = ref([]); |
| | | const currentUserName = ref(""); |
| | | const teamSelectRef = ref(null); |
| | | |
| | | const ensureCurrentUser = async () => { |
| | | if (currentUserId.value) return; |
| | |
| | | teamList: [], |
| | | deviceId: null, |
| | | }); |
| | | const teamSelectOptions = computed(() => { |
| | | const base = |
| | | Array.isArray(reportForm.userIdsList) && reportForm.userIdsList.length > 0 |
| | | ? reportForm.userIdsList.map(u => ({ |
| | | userId: String(u.userId ?? ""), |
| | | nickName: String(u.nickName ?? "").trim(), |
| | | })) |
| | | : []; |
| | | const baseNameSet = new Set(base.map(u => u.nickName)); |
| | | const selected = Array.isArray(reportForm.teamList) |
| | | ? reportForm.teamList |
| | | : []; |
| | | const extraNames = selected |
| | | .map(item => { |
| | | if (typeof item === "string") return String(item).trim(); |
| | | const name = item?.userName ?? item?.nickName ?? ""; |
| | | return String(name).trim(); |
| | | }) |
| | | .filter(Boolean) |
| | | .filter(name => !baseNameSet.has(name)); |
| | | const extras = extraNames.map(name => ({ userId: "", nickName: name })); |
| | | return [...base, ...extras]; |
| | | }); |
| | | function removeLastFour(str) { |
| | | if (!str) return ""; // 空值保护 |
| | | return str.toString().slice(0, -4); // 核心:截取 0 到 倒数第4位 |
| | |
| | | |
| | | const handleTeamListChange = val => { |
| | | if (!Array.isArray(val)) return; |
| | | if (!val.some(item => typeof item === "string")) return; |
| | | reportForm.teamList = val.map(item => { |
| | | let hasString = false; |
| | | const newList = val.map(item => { |
| | | if (typeof item === "string") { |
| | | hasString = true; |
| | | return { userName: item }; |
| | | } |
| | | return item; |
| | | }); |
| | | |
| | | if (hasString) { |
| | | reportForm.teamList = newList; |
| | | } |
| | | |
| | | // 解决 allow-create 在 multiple 模式下输入框内容不自动清空的问题 |
| | | setTimeout(() => { |
| | | if (teamSelectRef.value) { |
| | | // 1. 清除内部 query 状态 |
| | | teamSelectRef.value.query = ""; |
| | | if (teamSelectRef.value.states) { |
| | | teamSelectRef.value.states.query = ""; |
| | | teamSelectRef.value.states.inputValue = ""; |
| | | } |
| | | // 2. 强制清除 DOM 输入框的值 |
| | | const input = teamSelectRef.value.$el?.querySelector("input"); |
| | | if (input) { |
| | | input.value = ""; |
| | | } |
| | | // 3. 针对某些版本,重置选中标签的内部偏移(防止输入框被挤占) |
| | | if (typeof teamSelectRef.value.resetInputState === "function") { |
| | | teamSelectRef.value.resetInputState(); |
| | | } |
| | | } |
| | | }, 50); |
| | | }; |
| | | // 审核人 |
| | | const handleReviewerIdChange = userId => { |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? '新增出厂检验' : '编辑出厂检验'" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? '新增出厂检验' : '编辑出厂检验'" |
| | | width="70%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产品名称:" prop="productId"> |
| | | <el-tree-select |
| | | v-model="form.productId" |
| | | placeholder="请选择" |
| | | clearable |
| | | check-strictly |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | :disabled="operationType === 'edit'" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="规格型号:" prop="productModelId"> |
| | | <el-select v-model="form.productModelId" placeholder="请选择" clearable :disabled="operationType === 'edit'" |
| | | filterable readonly @change="handleChangeModel"> |
| | | <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> |
| | | <el-form-item label="工序:" |
| | | prop="process"> |
| | | <el-select v-model="form.process" |
| | | placeholder="请选择工序" |
| | | clearable |
| | | :disabled="processQuantityDisabled" |
| | | @change="handleProcessChange" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in processList" |
| | | :key="item.name" |
| | | :label="item.name" |
| | | :value="item.name" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="指标选择:" prop="testStandardId"> |
| | | <el-select |
| | | v-model="form.testStandardId" |
| | | placeholder="请选择指标" |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" |
| | | /> |
| | | <el-form-item label="产品名称:" |
| | | prop="productId"> |
| | | <el-tree-select v-model="form.productId" |
| | | placeholder="请选择" |
| | | clearable |
| | | check-strictly |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | :disabled="operationType === 'edit'" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="规格型号:" |
| | | prop="productModelId"> |
| | | <el-select v-model="form.productModelId" |
| | | placeholder="请选择" |
| | | clearable |
| | | :disabled="operationType === 'edit'" |
| | | filterable |
| | | readonly |
| | | @change="handleChangeModel"> |
| | | <el-option v-for="item in modelOptions" |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="指标选择:" |
| | | prop="testStandardId"> |
| | | <el-select v-model="form.testStandardId" |
| | | placeholder="请选择指标" |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="单位:" prop="unit"> |
| | | <el-input v-model="form.unit" placeholder="请输入" disabled/> |
| | | <el-form-item label="单位:" |
| | | prop="unit"> |
| | | <el-input v-model="form.unit" |
| | | placeholder="请输入" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2" :disabled="quantityDisabled"/> |
| | | <el-form-item label="数量:" |
| | | prop="quantity"> |
| | | <el-input-number :step="0.01" |
| | | :min="0" |
| | | style="width: 100%" |
| | | v-model="form.quantity" |
| | | placeholder="请输入" |
| | | clearable |
| | | :precision="2" |
| | | :disabled="quantityDisabled" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测单位:" prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" placeholder="请输入" clearable/> |
| | | <el-form-item label="检测单位:" |
| | | prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" |
| | | placeholder="请输入" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测结果:" prop="checkResult"> |
| | | <el-form-item label="检测结果:" |
| | | prop="checkResult"> |
| | | <el-select v-model="form.checkResult"> |
| | | <el-option label="合格" value="合格" /> |
| | | <el-option label="不合格" value="不合格" /> |
| | | <el-option label="合格" |
| | | value="合格" /> |
| | | <el-option label="不合格" |
| | | value="不合格" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验员:" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="请选择" clearable filterable> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" |
| | | :value="item.nickName"/> |
| | | </el-select> |
| | | <el-form-item label="检验员:" |
| | | prop="checkName"> |
| | | <el-select v-model="form.checkName" |
| | | placeholder="请选择" |
| | | clearable |
| | | filterable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.nickName" |
| | | :label="item.nickName" |
| | | :value="item.nickName" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测日期:" prop="checkTime"> |
| | | <el-date-picker |
| | | v-model="form.checkTime" |
| | | type="date" |
| | | placeholder="请选择日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | <el-form-item label="检测日期:" |
| | | prop="checkTime"> |
| | | <el-date-picker v-model="form.checkTime" |
| | | type="date" |
| | | placeholder="请选择日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="400" |
| | | > |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" clearable/> |
| | | </template> |
| | | </PIMTable> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="400"> |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" |
| | | clearable /> |
| | | </template> |
| | | </PIMTable> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, toRefs, computed, getCurrentInstance, nextTick} from "vue"; |
| | | import {getOptions} from "@/api/procurementManagement/procurementLedger.js"; |
| | | import {modelList, productTreeList} from "@/api/basicData/product.js"; |
| | | import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import {userListNoPage} from "@/api/system/user.js"; |
| | | import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | import { |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | computed, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | } from "vue"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { |
| | | qualityInspectAdd, |
| | | qualityInspectUpdate, |
| | | } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { |
| | | qualityInspectDetailByProductId, |
| | | getQualityTestStandardParamByTestStandardId, |
| | | } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import { qualityInspectParamInfo } from "@/api/qualityManagement/qualityInspectParam.js"; |
| | | import { list } from "@/api/productionManagement/productionProcess"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: true, message: "请输入", trigger: "blur" },], |
| | | process: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | productModelId: [{ required: true, message: "请选择", trigger: "change" }], |
| | | testStandardId: [{required: false, message: "请选择指标", trigger: "change"}], |
| | | unit: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请输入", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | // 编辑时:productMainId 或 purchaseLedgerId 任一有值则数量置灰 |
| | | const quantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | const supplierList = ref([]); |
| | | const productOptions = ref([]); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "指标", |
| | | prop: "parameterItem", |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "标准值", |
| | | prop: "standardValue", |
| | | }, |
| | | { |
| | | label: "内控值", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "检验值", |
| | | prop: "testValue", |
| | | dataType: 'slot', |
| | | slot: 'slot', |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const userList = ref([]); |
| | | const currentProductId = ref(0); |
| | | const testStandardOptions = ref([]); // 指标选择下拉框数据 |
| | | const modelOptions = ref([]); |
| | | |
| | | // 打开弹框 |
| | | const openDialog = async (type, row, defaultCheckResult = "", defaultCheckName = "") => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | // 先清空表单验证状态,避免闪烁 |
| | | await nextTick(); |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | |
| | | // 并行加载基础数据 |
| | | const [userListsRes] = await Promise.all([ |
| | | userListNoPage(), |
| | | getProductOptions(), |
| | | getOptions().then((res) => { |
| | | supplierList.value = res.data; |
| | | }) |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | process: [{ required: true, message: "请选择工序", trigger: "change" }], |
| | | checkName: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | productModelId: [{ required: true, message: "请选择", trigger: "change" }], |
| | | testStandardId: [ |
| | | { required: false, message: "请选择指标", trigger: "change" }, |
| | | ], |
| | | unit: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请输入", trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请输入", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | // 编辑时:productMainId 或 purchaseLedgerId 任一有值则数量置灰 |
| | | const quantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | const processQuantityDisabled = computed(() => { |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | const processList = ref([]); // 工序下拉列表(工序名称 name) |
| | | const supplierList = ref([]); |
| | | const productOptions = ref([]); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "指标", |
| | | prop: "parameterItem", |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "标准值", |
| | | prop: "standardValue", |
| | | }, |
| | | { |
| | | label: "内控值", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "检验值", |
| | | prop: "testValue", |
| | | dataType: "slot", |
| | | slot: "slot", |
| | | }, |
| | | ]); |
| | | // 筛选 roleIds 包含 106 的用户 |
| | | userList.value = (userListsRes.data || []).filter(user => { |
| | | const roleIds = user.roleIds || []; |
| | | return roleIds.includes(106) || roleIds.includes('106'); |
| | | }); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const userList = ref([]); |
| | | const currentProductId = ref(0); |
| | | const testStandardOptions = ref([]); // 指标选择下拉框数据 |
| | | const modelOptions = ref([]); |
| | | |
| | | form.value = {} |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | // 打开弹框 |
| | | const openDialog = async ( |
| | | type, |
| | | row, |
| | | defaultCheckResult = "", |
| | | defaultCheckName = "" |
| | | ) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | await nextTick(); |
| | | proxy.$refs.formRef?.clearValidate?.(); |
| | | |
| | | if (operationType.value === 'edit') { |
| | | // 先保存 testStandardId,避免被清空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | // 先设置表单数据,但暂时清空 testStandardId,等选项加载完成后再设置 |
| | | form.value = {...row, testStandardId: ''} |
| | | // 如果传入了默认检测结果,覆盖row中的值 |
| | | if (defaultCheckResult) { |
| | | form.value.checkResult = defaultCheckResult; |
| | | } |
| | | // 如果传入了默认检验员,覆盖row中的值(优先使用传入的检验员) |
| | | console.log('formDia checkName debug:', { defaultCheckName, rowCheckName: row.checkName }); |
| | | form.value.checkName = defaultCheckName || row.checkName || ""; |
| | | currentProductId.value = row.productId || 0 |
| | | // 清空验证状态,避免数据加载过程中的校验闪烁 |
| | | nextTick(() => { |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | const [userListsRes] = await Promise.all([ |
| | | userListNoPage(), |
| | | getProductOptions(), |
| | | getOptions().then(res => { |
| | | supplierList.value = res.data; |
| | | }), |
| | | list() |
| | | .then(res => { |
| | | processList.value = res.data || []; |
| | | }) |
| | | .catch(() => { |
| | | processList.value = []; |
| | | }), |
| | | ]); |
| | | // 筛选 roleIds 包含 106 的用户 |
| | | userList.value = (userListsRes.data || []).filter(user => { |
| | | const roleIds = user.roleIds || []; |
| | | return roleIds.includes(106) || roleIds.includes("106"); |
| | | }); |
| | | |
| | | // 编辑模式下,并行加载规格型号和指标选项 |
| | | if (currentProductId.value) { |
| | | // 设置产品名称 |
| | | form.value.productName = findNodeById(productOptions.value, currentProductId.value); |
| | | |
| | | // 并行加载规格型号和指标选项 |
| | | const params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2 |
| | | }; |
| | | |
| | | Promise.all([ |
| | | modelList({ id: currentProductId.value }), |
| | | qualityInspectDetailByProductId(params) |
| | | ]).then(([modelRes, testStandardRes]) => { |
| | | // 设置规格型号选项 |
| | | modelOptions.value = modelRes || []; |
| | | // 如果表单中已有 productModelId,设置对应的 model 和 unit |
| | | if (form.value.productModelId && modelOptions.value.length > 0) { |
| | | const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId); |
| | | if (selectedModel) { |
| | | form.value.model = selectedModel.model || ''; |
| | | form.value.unit = selectedModel.unit || ''; |
| | | } |
| | | } |
| | | |
| | | // 设置指标选项 |
| | | testStandardOptions.value = testStandardRes.data || []; |
| | | |
| | | // 设置 testStandardId 并加载参数列表 |
| | | nextTick(() => { |
| | | if (savedTestStandardId) { |
| | | // 确保类型匹配(item.id 可能是数字或字符串) |
| | | const matchedOption = testStandardOptions.value.find(item => |
| | | item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // 确保使用匹配项的 id(保持类型一致) |
| | | form.value.testStandardId = matchedOption.id; |
| | | } else { |
| | | // 如果找不到匹配项,尝试直接使用原值 |
| | | console.warn('未找到匹配的指标选项,testStandardId:', savedTestStandardId, '可用选项:', testStandardOptions.value); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | } |
| | | } |
| | | // 编辑场景保留已有检验值,直接拉取原参数数据 |
| | | getQualityInspectParamList(row.id); |
| | | }); |
| | | }); |
| | | } else { |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } |
| | | } |
| | | const getProductOptions = () => { |
| | | return productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | const getModels = (value) => { |
| | | form.value.productModelId = undefined; |
| | | form.value.unit = undefined; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }) |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | const handleChangeModel = (value) => { |
| | | form.value.model = modelOptions.value.find(item => item.id == value)?.model || ''; |
| | | form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || ''; |
| | | } |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // 找到节点,返回该节点 |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // 在子节点中找到,返回该节点 |
| | | } |
| | | } |
| | | } |
| | | return null; // 没有找到节点,返回null |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map((item) => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // 将 id 改为 value |
| | | currentProductId.value = 0; |
| | | modelOptions.value = []; |
| | | form.value = { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: defaultCheckName || "", |
| | | productName: "", |
| | | productId: "", |
| | | productModelId: "", |
| | | model: "", |
| | | testStandardId: "", |
| | | unit: "", |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: defaultCheckResult || "", |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | // 提交产品表单 |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | form.value.inspectType = 2 |
| | | if (operationType.value === "add") { |
| | | tableData.value.forEach((item) => { |
| | | delete item.id |
| | | }) |
| | | } |
| | | const data = {...form.value, qualityInspectParams: tableData.value} |
| | | if (operationType.value === "add") { |
| | | qualityInspectAdd(data).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | }) |
| | | } else { |
| | | qualityInspectUpdate(data).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | }) |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | const getList = () => { |
| | | if (!currentProductId.value) { |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2 |
| | | } |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | // 保存下拉框选项数据 |
| | | testStandardOptions.value = res.data || []; |
| | | // 清空表格数据,等待用户选择指标 |
| | | tableData.value = []; |
| | | // 清空指标选择 |
| | | form.value.testStandardId = ''; |
| | | }) |
| | | } |
| | | |
| | | // 指标选择变化处理 |
| | | const handleTestStandardChange = (testStandardId) => { |
| | | if (!testStandardId) { |
| | | if (operationType.value === "edit") { |
| | | // 先保存 testStandardId,避免被清空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | // 先设置表单数据,但暂时清空 testStandardId,等选项加载完成后再设置 |
| | | form.value = { ...row, testStandardId: "" }; |
| | | // 如果传入了默认检测结果,覆盖row中的值 |
| | | if (defaultCheckResult) { |
| | | form.value.checkResult = defaultCheckResult; |
| | | } |
| | | // 如果传入了默认检验员,覆盖row中的值(优先使用传入的检验员) |
| | | console.log("formDia checkName debug:", { |
| | | defaultCheckName, |
| | | rowCheckName: row.checkName, |
| | | }); |
| | | form.value.checkName = defaultCheckName || row.checkName || ""; |
| | | currentProductId.value = row.productId || 0; |
| | | // 清空验证状态,避免数据加载过程中的校验闪烁 |
| | | nextTick(() => { |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | }); |
| | | |
| | | // 编辑模式下,并行加载规格型号和指标选项 |
| | | if (currentProductId.value) { |
| | | // 设置产品名称 |
| | | form.value.productName = findNodeById( |
| | | productOptions.value, |
| | | currentProductId.value |
| | | ); |
| | | |
| | | // 并行加载规格型号和指标选项 |
| | | const params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2, |
| | | process: form.value.process || "", |
| | | }; |
| | | |
| | | Promise.all([ |
| | | modelList({ id: currentProductId.value }), |
| | | qualityInspectDetailByProductId(params), |
| | | ]).then(([modelRes, testStandardRes]) => { |
| | | // 设置规格型号选项 |
| | | modelOptions.value = modelRes || []; |
| | | // 如果表单中已有 productModelId,设置对应的 model 和 unit |
| | | if (form.value.productModelId && modelOptions.value.length > 0) { |
| | | const selectedModel = modelOptions.value.find( |
| | | item => item.id == form.value.productModelId |
| | | ); |
| | | if (selectedModel) { |
| | | form.value.model = selectedModel.model || ""; |
| | | form.value.unit = selectedModel.unit || ""; |
| | | } |
| | | } |
| | | |
| | | // 设置指标选项 |
| | | testStandardOptions.value = testStandardRes.data || []; |
| | | |
| | | // 设置 testStandardId 并加载参数列表 |
| | | nextTick(() => { |
| | | if (savedTestStandardId) { |
| | | // 确保类型匹配(item.id 可能是数字或字符串) |
| | | const matchedOption = testStandardOptions.value.find( |
| | | item => |
| | | item.id == savedTestStandardId || |
| | | String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // 确保使用匹配项的 id(保持类型一致) |
| | | form.value.testStandardId = matchedOption.id; |
| | | } else { |
| | | // 如果找不到匹配项,尝试直接使用原值 |
| | | console.warn( |
| | | "未找到匹配的指标选项,testStandardId:", |
| | | savedTestStandardId, |
| | | "可用选项:", |
| | | testStandardOptions.value |
| | | ); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | } |
| | | } |
| | | // 编辑场景保留已有检验值,直接拉取原参数数据 |
| | | getQualityInspectParamList(row.id); |
| | | }); |
| | | }); |
| | | } else { |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } |
| | | }; |
| | | const getProductOptions = () => { |
| | | return productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | const getModels = value => { |
| | | form.value.productModelId = undefined; |
| | | form.value.unit = undefined; |
| | | modelOptions.value = []; |
| | | currentProductId.value = value; |
| | | form.value.productName = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res; |
| | | }); |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | const handleProcessChange = () => { |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | return; |
| | | form.value.testStandardId = ""; |
| | | if (currentProductId.value) { |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | const handleChangeModel = value => { |
| | | form.value.model = |
| | | modelOptions.value.find(item => item.id == value)?.model || ""; |
| | | form.value.unit = |
| | | modelOptions.value.find(item => item.id == value)?.unit || ""; |
| | | }; |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // 找到节点,返回该节点 |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // 在子节点中找到,返回该节点 |
| | | } |
| | | } |
| | | } |
| | | return null; // 没有找到节点,返回null |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // 将 id 改为 value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | tableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId).then(res => { |
| | | tableData.value = res.data || []; |
| | | }).catch(error => { |
| | | console.error('获取标准参数失败:', error); |
| | | // 提交产品表单 |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | form.value.inspectType = 2; |
| | | if (operationType.value === "add") { |
| | | tableData.value.forEach(item => { |
| | | delete item.id; |
| | | }); |
| | | } |
| | | const processName = form.value.process || ""; |
| | | const data = { |
| | | ...form.value, |
| | | process: processName, |
| | | qualityInspectParams: tableData.value, |
| | | }; |
| | | if (operationType.value === "add") { |
| | | qualityInspectAdd(data).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | }); |
| | | } else { |
| | | qualityInspectUpdate(data).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | const getList = () => { |
| | | if (!currentProductId.value) { |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2, |
| | | process: form.value.process || "", |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | // 保存下拉框选项数据 |
| | | testStandardOptions.value = res.data || []; |
| | | // 清空表格数据,等待用户选择指标 |
| | | tableData.value = []; |
| | | // 清空指标选择 |
| | | form.value.testStandardId = ""; |
| | | }); |
| | | }; |
| | | |
| | | // 指标选择变化处理 |
| | | const handleTestStandardChange = testStandardId => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId) |
| | | .then(res => { |
| | | tableData.value = res.data || []; |
| | | }) |
| | | .catch(error => { |
| | | console.error("获取标准参数失败:", error); |
| | | tableData.value = []; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | const getQualityInspectParamList = id => { |
| | | qualityInspectParamInfo(id).then(res => { |
| | | tableData.value = res.data; |
| | | }); |
| | | }; |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | tableData.value = []; |
| | | }).finally(() => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | const getQualityInspectParamList = (id) => { |
| | | qualityInspectParamInfo(id).then(res => { |
| | | tableData.value = res.data; |
| | | }) |
| | | } |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | tableData.value = []; |
| | | testStandardOptions.value = []; |
| | | form.value.testStandardId = ''; |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | testStandardOptions.value = []; |
| | | form.value.testStandardId = ""; |
| | | dialogFormVisible.value = false; |
| | | emit("close"); |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| | | </style> |
| | |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">产品名称:</span> |
| | | <el-input |
| | | v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请输入产品名称搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <span style="margin-left: 10px" class="search_title">检测日期:</span> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="请选择" clearable @change="changeDaterange" /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >搜索</el-button |
| | | > |
| | | <span class="search_title">工序:</span> |
| | | <el-input v-model="searchForm.process" |
| | | style="width: 240px" |
| | | placeholder="请输入工序搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" |
| | | class="search_title">产品名称:</span> |
| | | <el-input v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请输入产品名称搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" |
| | | class="search_title">检测日期:</span> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="changeDaterange" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">搜索</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">新增</el-button> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">新增</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">删除</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">删除</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia> |
| | | <FormDia ref="formDia" @close="handleQuery"></FormDia> |
| | | <files-dia ref="filesDia" @close="handleQuery"></files-dia> |
| | | <InspectionFormDia ref="inspectionFormDia" |
| | | @close="handleQuery"></InspectionFormDia> |
| | | <FormDia ref="formDia" |
| | | @close="handleQuery"></FormDia> |
| | | <files-dia ref="filesDia" |
| | | @close="handleQuery"></files-dia> |
| | | <!-- 检验结果选择对话框 --> |
| | | <el-dialog v-model="quickCheckVisible" title="检验" width="40%" @close="closeQuickCheck"> |
| | | <el-form :model="quickCheckForm" label-width="140px" label-position="top" ref="quickCheckRef"> |
| | | <el-dialog v-model="quickCheckVisible" |
| | | title="检验" |
| | | width="40%" |
| | | @close="closeQuickCheck"> |
| | | <el-form :model="quickCheckForm" |
| | | label-width="140px" |
| | | label-position="top" |
| | | ref="quickCheckRef"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测结果:" required> |
| | | <el-form-item label="检测结果:" |
| | | required> |
| | | <el-radio-group v-model="quickCheckForm.checkResult"> |
| | | <el-radio value="合格">合格</el-radio> |
| | | <el-radio value="不合格">不合格</el-radio> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检验员:" required> |
| | | <el-select v-model="quickCheckForm.checkName" placeholder="请选择" clearable style="width: 100%"> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/> |
| | | <el-form-item label="检验员:" |
| | | required> |
| | | <el-select v-model="quickCheckForm.checkName" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%"> |
| | | <el-option v-for="item in userList" |
| | | :key="item.nickName" |
| | | :label="item.nickName" |
| | | :value="item.nickName" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测日期:" required> |
| | | <el-date-picker |
| | | v-model="quickCheckForm.checkTime" |
| | | type="date" |
| | | placeholder="请选择日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | <el-form-item label="检测日期:" |
| | | required> |
| | | <el-date-picker v-model="quickCheckForm.checkTime" |
| | | type="date" |
| | | placeholder="请选择日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="指标选择:"> |
| | | <el-select |
| | | v-model="quickCheckForm.testStandardId" |
| | | placeholder="请选择指标" |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" |
| | | /> |
| | | <el-select v-model="quickCheckForm.testStandardId" |
| | | placeholder="请选择指标" |
| | | clearable |
| | | @change="handleTestStandardChange" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="检测单位:"> |
| | | <el-input v-model="quickCheckForm.checkCompany" placeholder="请输入" clearable style="width: 100%"/> |
| | | <el-input v-model="quickCheckForm.checkCompany" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- 检验值表格 --> |
| | | <el-form-item label="检验值:" v-if="quickCheckTableData.length > 0"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="quickCheckTableColumn" |
| | | :tableData="quickCheckTableData" |
| | | :tableLoading="quickCheckTableLoading" |
| | | height="200" |
| | | > |
| | | <el-form-item label="检验值:" |
| | | v-if="quickCheckTableData.length > 0"> |
| | | <PIMTable rowKey="id" |
| | | :column="quickCheckTableColumn" |
| | | :tableData="quickCheckTableData" |
| | | :tableLoading="quickCheckTableLoading" |
| | | height="200"> |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" clearable placeholder="请输入检验值"/> |
| | | <el-input v-model="row.testValue" |
| | | clearable |
| | | placeholder="请输入检验值" /> |
| | | </template> |
| | | </PIMTable> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="handleQuickCheckConfirm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="handleQuickCheckConfirm">确认</el-button> |
| | | <el-button @click="closeQuickCheck">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="30%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-form-item label="检验员:" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="请选择" clearable> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" |
| | | :value="item.nickName"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | title="编辑检验员" |
| | | width="30%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | <el-form-item label="检验员:" |
| | | prop="checkName"> |
| | | <el-select v-model="form.checkName" |
| | | placeholder="请选择" |
| | | clearable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.nickName" |
| | | :label="item.nickName" |
| | | :value="item.nickName" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue"; |
| | | import InspectionFormDia from "@/views/qualityManagement/finalInspection/components/inspectionFormDia.vue"; |
| | | import FormDia from "@/views/qualityManagement/finalInspection/components/formDia.vue"; |
| | | import {ElMessageBox} from "element-plus"; |
| | | import { |
| | | downloadQualityInspect, |
| | | qualityInspectDel, |
| | | qualityInspectDetail, |
| | | qualityInspectListPage, qualityInspectUpdate, |
| | | submitQualityInspect, |
| | | qualityInspectExportNew |
| | | } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import FilesDia from "@/views/qualityManagement/finalInspection/components/filesDia.vue"; |
| | | import dayjs from "dayjs"; |
| | | import {userListNoPage} from "@/api/system/user.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | onMounted, |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | } from "vue"; |
| | | import InspectionFormDia from "@/views/qualityManagement/finalInspection/components/inspectionFormDia.vue"; |
| | | import FormDia from "@/views/qualityManagement/finalInspection/components/formDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | downloadQualityInspect, |
| | | qualityInspectDel, |
| | | qualityInspectDetail, |
| | | qualityInspectListPage, |
| | | qualityInspectUpdate, |
| | | submitQualityInspect, |
| | | qualityInspectExportNew, |
| | | } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import { |
| | | qualityInspectDetailByProductId, |
| | | getQualityTestStandardParamByTestStandardId, |
| | | } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import FilesDia from "@/views/qualityManagement/finalInspection/components/filesDia.vue"; |
| | | import dayjs from "dayjs"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | productName: "", |
| | | entryDate: undefined, // 录入日期 |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }, |
| | | rules: { |
| | | checkName: [{required: true, message: "请选择", trigger: "change"}], |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "检测日期", |
| | | prop: "checkTime", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "生产工单号", |
| | | prop: "workOrderNo", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "检验员", |
| | | prop: "checkName", |
| | | }, |
| | | { |
| | | label: "产品名称", |
| | | prop: "productName", |
| | | }, |
| | | { |
| | | label: "规格型号", |
| | | prop: "model", |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "数量", |
| | | prop: "quantity", |
| | | width: 100 |
| | | }, |
| | | { |
| | | label: "检测单位", |
| | | prop: "checkCompany", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "检测结果", |
| | | prop: "checkResult", |
| | | dataType: "tag", |
| | | formatType: (params) => { |
| | | if (params == '不合格') { |
| | | return "danger"; |
| | | } else if (params == '合格') { |
| | | return "success"; |
| | | } else { |
| | | return null; |
| | | } |
| | | const data = reactive({ |
| | | searchForm: { |
| | | process: "", |
| | | productName: "", |
| | | entryDate: undefined, // 录入日期 |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }, |
| | | }, |
| | | { |
| | | label: "提交状态", |
| | | prop: "inspectState", |
| | | formatData: (params) => { |
| | | if (params) { |
| | | return "已提交"; |
| | | } else { |
| | | return "未提交"; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 280, |
| | | operation: [ |
| | | { |
| | | name: "检验", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openQuickCheck(row); |
| | | }, |
| | | disabled: (row) => { |
| | | // 已提交则禁用 |
| | | if (row.inspectState == 1) return true; |
| | | // 如果检验员有值,只有当前登录用户能编辑 |
| | | if (row.checkName) { |
| | | return row.checkName !== userStore.nickName; |
| | | } |
| | | return false; |
| | | } |
| | | rules: { |
| | | checkName: [{ required: true, message: "请选择", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "检测日期", |
| | | prop: "checkTime", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "生产工单号", |
| | | prop: "workOrderNo", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "工序", |
| | | prop: "process", |
| | | width: 230, |
| | | }, |
| | | { |
| | | label: "检验员", |
| | | prop: "checkName", |
| | | }, |
| | | { |
| | | label: "产品名称", |
| | | prop: "productName", |
| | | }, |
| | | { |
| | | label: "规格型号", |
| | | prop: "model", |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "数量", |
| | | prop: "quantity", |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "检测单位", |
| | | prop: "checkCompany", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "检测结果", |
| | | prop: "checkResult", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | if (params == "不合格") { |
| | | return "danger"; |
| | | } else if (params == "合格") { |
| | | return "success"; |
| | | } else { |
| | | return null; |
| | | } |
| | | }, |
| | | { |
| | | name: "附件", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openFilesFormDia(row); |
| | | }, |
| | | }, |
| | | { |
| | | label: "提交状态", |
| | | prop: "inspectState", |
| | | formatData: params => { |
| | | if (params) { |
| | | return "已提交"; |
| | | } else { |
| | | return "未提交"; |
| | | } |
| | | }, |
| | | { |
| | | name: "提交", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | submit(row.id); |
| | | }, |
| | | disabled: (row) => { |
| | | // 已提交则禁用 |
| | | if (row.inspectState == 1) return true; |
| | | // 如果检验员有值,只有当前登录用户能提交 |
| | | if (row.checkName) { |
| | | return row.checkName !== userStore.nickName; |
| | | } |
| | | return false; |
| | | } |
| | | }, |
| | | { |
| | | name: "分配检验员", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | if (!row.checkName) { |
| | | open(row) |
| | | } else { |
| | | proxy.$modal.msgError("检验员已存在"); |
| | | } |
| | | }, |
| | | disabled: (row) => { |
| | | return row.inspectState == 1 || row.checkName; |
| | | } |
| | | }, |
| | | { |
| | | name: "下载", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | downLoadFile(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const currentRow = ref(null) |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0 |
| | | }); |
| | | const formDia = ref() |
| | | const filesDia = ref() |
| | | const inspectionFormDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | const userStore = useUserStore() |
| | | const userList = ref([]); |
| | | const form = ref({ |
| | | checkName: "" |
| | | }); |
| | | const dialogFormVisible = ref(false); |
| | | const quickCheckVisible = ref(false); |
| | | const quickCheckForm = ref({ |
| | | checkResult: "合格", |
| | | checkName: "", |
| | | checkTime: "", |
| | | testStandardId: "", |
| | | checkCompany: "天津阳光彩印股份有限公司" |
| | | }); |
| | | const quickCheckRef = ref(null); |
| | | const testStandardOptions = ref([]); // 指标选择下拉框数据 |
| | | const quickCheckTableColumn = ref([ |
| | | { |
| | | label: "指标", |
| | | prop: "parameterItem", |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "标准值", |
| | | prop: "standardValue", |
| | | }, |
| | | { |
| | | label: "内控值", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "检验值", |
| | | prop: "testValue", |
| | | dataType: 'slot', |
| | | slot: 'slot', |
| | | }, |
| | | ]); |
| | | const quickCheckTableData = ref([]); |
| | | const quickCheckTableLoading = ref(false); |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 280, |
| | | operation: [ |
| | | { |
| | | name: "检验", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openQuickCheck(row); |
| | | }, |
| | | disabled: row => { |
| | | // 已提交则禁用 |
| | | if (row.inspectState == 1) return true; |
| | | // 如果检验员有值,只有当前登录用户能编辑 |
| | | if (row.checkName) { |
| | | return row.checkName !== userStore.nickName; |
| | | } |
| | | return false; |
| | | }, |
| | | }, |
| | | { |
| | | name: "附件", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openFilesFormDia(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "提交", |
| | | type: "text", |
| | | clickFun: row => { |
| | | submit(row.id); |
| | | }, |
| | | disabled: row => { |
| | | // 已提交则禁用 |
| | | if (row.inspectState == 1) return true; |
| | | // 如果检验员有值,只有当前登录用户能提交 |
| | | if (row.checkName) { |
| | | return row.checkName !== userStore.nickName; |
| | | } |
| | | return false; |
| | | }, |
| | | }, |
| | | { |
| | | name: "分配检验员", |
| | | type: "text", |
| | | clickFun: row => { |
| | | if (!row.checkName) { |
| | | open(row); |
| | | } else { |
| | | proxy.$modal.msgError("检验员已存在"); |
| | | } |
| | | }, |
| | | disabled: row => { |
| | | return row.inspectState == 1 || row.checkName; |
| | | }, |
| | | }, |
| | | { |
| | | name: "下载", |
| | | type: "text", |
| | | clickFun: row => { |
| | | downLoadFile(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const currentRow = ref(null); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref(); |
| | | const filesDia = ref(); |
| | | const inspectionFormDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | const userList = ref([]); |
| | | const form = ref({ |
| | | checkName: "", |
| | | }); |
| | | const dialogFormVisible = ref(false); |
| | | const quickCheckVisible = ref(false); |
| | | const quickCheckForm = ref({ |
| | | checkResult: "合格", |
| | | checkName: "", |
| | | checkTime: "", |
| | | testStandardId: "", |
| | | checkCompany: "", |
| | | }); |
| | | const quickCheckRef = ref(null); |
| | | const testStandardOptions = ref([]); // 指标选择下拉框数据 |
| | | const quickCheckTableColumn = ref([ |
| | | { |
| | | label: "指标", |
| | | prop: "parameterItem", |
| | | }, |
| | | { |
| | | label: "单位", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "标准值", |
| | | prop: "standardValue", |
| | | }, |
| | | { |
| | | label: "内控值", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "检验值", |
| | | prop: "testValue", |
| | | dataType: "slot", |
| | | slot: "slot", |
| | | }, |
| | | ]); |
| | | const quickCheckTableData = ref([]); |
| | | const quickCheckTableLoading = ref(false); |
| | | |
| | | const changeDaterange = (value) => { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | if (value) { |
| | | searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } |
| | | getList(); |
| | | }; |
| | | // 查询列表 |
| | | /** 搜索按钮操作 */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined |
| | | qualityInspectListPage({...params, inspectType: 2}).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | }; |
| | | // 表格选择数据 |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | const changeDaterange = value => { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | if (value) { |
| | | searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } |
| | | getList(); |
| | | }; |
| | | // 查询列表 |
| | | /** 搜索按钮操作 */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined; |
| | | qualityInspectListPage({ ...params, inspectType: 2 }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // 表格选择数据 |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // 打开弹框 |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | // 打开新增检验弹框 |
| | | const openInspectionForm = (type, row) => { |
| | | nextTick(() => { |
| | | inspectionFormDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | // 打开附件弹框 |
| | | const openFilesFormDia = (type, row) => { |
| | | nextTick(() => { |
| | | filesDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | // 打开弹框 |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row); |
| | | }); |
| | | }; |
| | | // 打开新增检验弹框 |
| | | const openInspectionForm = (type, row) => { |
| | | nextTick(() => { |
| | | inspectionFormDia.value?.openDialog(type, row); |
| | | }); |
| | | }; |
| | | // 打开附件弹框 |
| | | const openFilesFormDia = (type, row) => { |
| | | nextTick(() => { |
| | | filesDia.value?.openDialog(type, row); |
| | | }); |
| | | }; |
| | | |
| | | // 删除 |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("请选择数据"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | // 删除 |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("请选择数据"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | qualityInspectDel(ids).then((res) => { |
| | | qualityInspectDel(ids).then(res => { |
| | | proxy.$modal.msgSuccess("删除成功"); |
| | | getList(); |
| | | }); |
| | |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | | }); |
| | | }; |
| | | // 导出 |
| | | const handleOut = () => { |
| | | // 判断是否有选中数据 |
| | | const hasSelected = selectedRows.value.length > 0; |
| | | }; |
| | | // 导出 |
| | | const handleOut = () => { |
| | | // 判断是否有选中数据 |
| | | const hasSelected = selectedRows.value.length > 0; |
| | | |
| | | const confirmMsg = hasSelected |
| | | ? "选中的内容将被导出,是否确认导出?" |
| | | : "将导出全部数据,是否确认导出?"; |
| | | const confirmMsg = hasSelected |
| | | ? "选中的内容将被导出,是否确认导出?" |
| | | : "将导出全部数据,是否确认导出?"; |
| | | |
| | | ElMessageBox.confirm(confirmMsg, "导出", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | // 构建导出参数 |
| | | const exportData = { |
| | | inspectType: 2, |
| | | productName: searchForm.value.productName || "", |
| | | entryDateStart: searchForm.value.entryDateStart || "", |
| | | entryDateEnd: searchForm.value.entryDateEnd || "", |
| | | }; |
| | | ElMessageBox.confirm(confirmMsg, "导出", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | // 构建导出参数 |
| | | const exportData = { |
| | | inspectType: 2, |
| | | process: searchForm.value.process || "", |
| | | productName: searchForm.value.productName || "", |
| | | entryDateStart: searchForm.value.entryDateStart || "", |
| | | entryDateEnd: searchForm.value.entryDateEnd || "", |
| | | }; |
| | | |
| | | // 如果有选中数据,添加ids |
| | | if (hasSelected) { |
| | | exportData.ids = selectedRows.value.map(item => item.id); |
| | | } |
| | | // 如果有选中数据,添加ids |
| | | if (hasSelected) { |
| | | exportData.ids = selectedRows.value.map(item => item.id); |
| | | } |
| | | |
| | | // 调用新导出接口 |
| | | qualityInspectExportNew(exportData).then((blobData) => { |
| | | const blob = new Blob([blobData], { |
| | | type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', |
| | | }); |
| | | const downloadUrl = window.URL.createObjectURL(blob); |
| | | const link = document.createElement('a'); |
| | | link.href = downloadUrl; |
| | | link.download = '出厂检验.xlsx'; |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | window.URL.revokeObjectURL(downloadUrl); |
| | | }).catch((error) => { |
| | | proxy.$modal.msgError("导出失败"); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | | }); |
| | | }; |
| | | // 调用新导出接口 |
| | | qualityInspectExportNew(exportData) |
| | | .then(blobData => { |
| | | const blob = new Blob([blobData], { |
| | | type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | | }); |
| | | const downloadUrl = window.URL.createObjectURL(blob); |
| | | const link = document.createElement("a"); |
| | | link.href = downloadUrl; |
| | | link.download = "出厂检验.xlsx"; |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | window.URL.revokeObjectURL(downloadUrl); |
| | | }) |
| | | .catch(error => { |
| | | proxy.$modal.msgError("导出失败"); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | | }); |
| | | }; |
| | | |
| | | // 提价 |
| | | const submit = async (id) => { |
| | | const res = await submitQualityInspect({id: id}) |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | getList(); |
| | | } |
| | | } |
| | | // 提价 |
| | | const submit = async id => { |
| | | const res = await submitQualityInspect({ id: id }); |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | if (currentRow.value) { |
| | | const data = { |
| | | ...form.value, |
| | | id: currentRow.value.id |
| | | } |
| | | qualityInspectUpdate(data).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | getList(); |
| | | }) |
| | | } |
| | | }; |
| | | const submitForm = () => { |
| | | if (currentRow.value) { |
| | | const data = { |
| | | ...form.value, |
| | | id: currentRow.value.id, |
| | | }; |
| | | qualityInspectUpdate(data).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const open = async (row) => { |
| | | let userLists = await userListNoPage(); |
| | | // 筛选 roleIds 包含 106 的用户 |
| | | userList.value = (userLists.data || []).filter(user => { |
| | | const roleIds = user.roleIds || []; |
| | | return roleIds.includes(106) || roleIds.includes('106'); |
| | | }); |
| | | currentRow.value = row |
| | | dialogFormVisible.value = true |
| | | } |
| | | const open = async row => { |
| | | let userLists = await userListNoPage(); |
| | | // 筛选 roleIds 包含 106 的用户 |
| | | userList.value = (userLists.data || []).filter(user => { |
| | | const roleIds = user.roleIds || []; |
| | | return roleIds.includes(106) || roleIds.includes("106"); |
| | | }); |
| | | currentRow.value = row; |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | |
| | | const downLoadFile = (row) => { |
| | | downloadQualityInspect({ id: row.id }).then((blobData) => { |
| | | const blob = new Blob([blobData], { |
| | | type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
| | | }) |
| | | const downloadUrl = window.URL.createObjectURL(blob) |
| | | const downLoadFile = row => { |
| | | downloadQualityInspect({ id: row.id }).then(blobData => { |
| | | const blob = new Blob([blobData], { |
| | | type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", |
| | | }); |
| | | const downloadUrl = window.URL.createObjectURL(blob); |
| | | |
| | | const link = document.createElement('a') |
| | | link.href = downloadUrl |
| | | link.download = '出厂检验报告.docx' |
| | | document.body.appendChild(link) |
| | | link.click() |
| | | const link = document.createElement("a"); |
| | | link.href = downloadUrl; |
| | | link.download = "出厂检验报告.docx"; |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | |
| | | document.body.removeChild(link) |
| | | window.URL.revokeObjectURL(downloadUrl) |
| | | }) |
| | | }; |
| | | document.body.removeChild(link); |
| | | window.URL.revokeObjectURL(downloadUrl); |
| | | }); |
| | | }; |
| | | |
| | | // 打开检验结果选择对话框 |
| | | const openQuickCheck = async (row) => { |
| | | // 先调用详情接口获取完整数据(包含指标和检验值) |
| | | let detailData = row; |
| | | try { |
| | | const detailRes = await qualityInspectDetail(row.id); |
| | | if (detailRes.code === 200 && detailRes.data) { |
| | | detailData = detailRes.data; |
| | | } |
| | | } catch (e) { |
| | | console.error("获取检验详情失败", e); |
| | | } |
| | | currentRow.value = detailData; |
| | | // 打开检验结果选择对话框 |
| | | const openQuickCheck = async row => { |
| | | // 先调用详情接口获取完整数据(包含指标和检验值) |
| | | let detailData = row; |
| | | try { |
| | | const detailRes = await qualityInspectDetail(row.id); |
| | | if (detailRes.code === 200 && detailRes.data) { |
| | | detailData = detailRes.data; |
| | | } |
| | | } catch (e) { |
| | | console.error("获取检验详情失败", e); |
| | | } |
| | | currentRow.value = detailData; |
| | | |
| | | // 加载用户列表,筛选 roleIds 包含 106 的用户 |
| | | try { |
| | | const userLists = await userListNoPage(); |
| | | userList.value = (userLists.data || []).filter(user => { |
| | | const roleIds = user.roleIds || []; |
| | | return roleIds.includes(106) || roleIds.includes('106'); |
| | | }); |
| | | } catch (e) { |
| | | console.error("加载检验员列表失败", e); |
| | | userList.value = []; |
| | | } |
| | | // 加载指标选项(根据产品ID) |
| | | if (detailData.productId) { |
| | | try { |
| | | const params = { |
| | | productId: detailData.productId, |
| | | inspectType: 2 |
| | | }; |
| | | const res = await qualityInspectDetailByProductId(params); |
| | | testStandardOptions.value = res.data || []; |
| | | } catch (e) { |
| | | console.error("加载指标选项失败", e); |
| | | testStandardOptions.value = []; |
| | | } |
| | | } else { |
| | | testStandardOptions.value = []; |
| | | } |
| | | // 设置默认值(优先使用详情数据,检验员默认为当前登录用户) |
| | | quickCheckForm.value = { |
| | | checkResult: detailData.checkResult || "合格", |
| | | checkName: detailData.checkName || userStore.nickName || "", |
| | | checkTime: detailData.checkTime || dayjs().format("YYYY-MM-DD"), |
| | | testStandardId: detailData.testStandardId || "", |
| | | checkCompany: detailData.checkCompany || "天津阳光彩印股份有限公司" |
| | | }; |
| | | // 如果有检验参数数据,回显到表格 |
| | | if (detailData.qualityInspectParams && detailData.qualityInspectParams.length > 0) { |
| | | quickCheckTableData.value = detailData.qualityInspectParams; |
| | | } else { |
| | | quickCheckTableData.value = []; |
| | | } |
| | | quickCheckVisible.value = true; |
| | | }; |
| | | // 加载用户列表,筛选 roleIds 包含 106 的用户 |
| | | try { |
| | | const userLists = await userListNoPage(); |
| | | userList.value = (userLists.data || []).filter(user => { |
| | | const roleIds = user.roleIds || []; |
| | | return roleIds.includes(106) || roleIds.includes("106"); |
| | | }); |
| | | } catch (e) { |
| | | console.error("加载检验员列表失败", e); |
| | | userList.value = []; |
| | | } |
| | | // 加载指标选项(根据产品ID) |
| | | if (detailData.productId) { |
| | | try { |
| | | const params = { |
| | | productId: detailData.productId, |
| | | inspectType: 2, |
| | | process: detailData.process || "", |
| | | }; |
| | | const res = await qualityInspectDetailByProductId(params); |
| | | testStandardOptions.value = res.data || []; |
| | | } catch (e) { |
| | | console.error("加载指标选项失败", e); |
| | | testStandardOptions.value = []; |
| | | } |
| | | } else { |
| | | testStandardOptions.value = []; |
| | | } |
| | | // 设置默认值(优先使用详情数据,检验员默认为当前登录用户) |
| | | quickCheckForm.value = { |
| | | checkResult: detailData.checkResult || "合格", |
| | | checkName: detailData.checkName || userStore.nickName || "", |
| | | checkTime: detailData.checkTime || dayjs().format("YYYY-MM-DD"), |
| | | testStandardId: detailData.testStandardId || "", |
| | | checkCompany: detailData.checkCompany || "", |
| | | }; |
| | | // 如果有检验参数数据,回显到表格 |
| | | if ( |
| | | detailData.qualityInspectParams && |
| | | detailData.qualityInspectParams.length > 0 |
| | | ) { |
| | | quickCheckTableData.value = detailData.qualityInspectParams; |
| | | } else { |
| | | quickCheckTableData.value = []; |
| | | } |
| | | quickCheckVisible.value = true; |
| | | }; |
| | | |
| | | // 指标选择变化处理 |
| | | const handleTestStandardChange = (testStandardId) => { |
| | | if (!testStandardId) { |
| | | // 指标选择变化处理 |
| | | const handleTestStandardChange = testStandardId => { |
| | | if (!testStandardId) { |
| | | quickCheckTableData.value = []; |
| | | return; |
| | | } |
| | | quickCheckTableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId) |
| | | .then(res => { |
| | | quickCheckTableData.value = res.data || []; |
| | | }) |
| | | .catch(error => { |
| | | console.error("获取标准参数失败:", error); |
| | | quickCheckTableData.value = []; |
| | | }) |
| | | .finally(() => { |
| | | quickCheckTableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 关闭检验结果选择对话框 |
| | | const closeQuickCheck = () => { |
| | | quickCheckVisible.value = false; |
| | | quickCheckForm.value = { |
| | | checkResult: "合格", |
| | | checkName: "", |
| | | checkTime: "", |
| | | testStandardId: "", |
| | | checkCompany: "", |
| | | }; |
| | | quickCheckTableData.value = []; |
| | | return; |
| | | } |
| | | quickCheckTableLoading.value = true; |
| | | getQualityTestStandardParamByTestStandardId(testStandardId).then(res => { |
| | | quickCheckTableData.value = res.data || []; |
| | | }).catch(error => { |
| | | console.error('获取标准参数失败:', error); |
| | | quickCheckTableData.value = []; |
| | | }).finally(() => { |
| | | quickCheckTableLoading.value = false; |
| | | testStandardOptions.value = []; |
| | | }; |
| | | |
| | | // 确认检验结果 |
| | | const handleQuickCheckConfirm = () => { |
| | | if (!quickCheckForm.value.checkResult) { |
| | | proxy.$modal.msgWarning("请选择检测结果"); |
| | | return; |
| | | } |
| | | if (!quickCheckForm.value.checkName) { |
| | | proxy.$modal.msgWarning("请选择检验员"); |
| | | return; |
| | | } |
| | | if (!quickCheckForm.value.checkTime) { |
| | | proxy.$modal.msgWarning("请选择检测日期"); |
| | | return; |
| | | } |
| | | |
| | | const checkNameToPass = quickCheckForm.value.checkName; |
| | | const checkResultToPass = quickCheckForm.value.checkResult; |
| | | const checkTimeToPass = quickCheckForm.value.checkTime; |
| | | const testStandardIdToPass = quickCheckForm.value.testStandardId; |
| | | const checkCompanyToPass = quickCheckForm.value.checkCompany; |
| | | const qualityInspectParamsToPass = [...quickCheckTableData.value]; |
| | | |
| | | if (quickCheckForm.value.checkResult === "合格") { |
| | | // 合格:直接提交 |
| | | const data = { |
| | | id: currentRow.value.id, |
| | | checkResult: "合格", |
| | | checkName: checkNameToPass, |
| | | checkTime: checkTimeToPass, |
| | | testStandardId: testStandardIdToPass, |
| | | checkCompany: checkCompanyToPass, |
| | | inspectType: 2, |
| | | qualityInspectParams: qualityInspectParamsToPass, |
| | | }; |
| | | qualityInspectUpdate(data).then(res => { |
| | | proxy.$modal.msgSuccess("检验成功"); |
| | | closeQuickCheck(); |
| | | getList(); |
| | | }); |
| | | } else { |
| | | // 不合格:打开详细填写页面 |
| | | closeQuickCheck(); |
| | | nextTick(() => { |
| | | currentRow.value.checkTime = checkTimeToPass; |
| | | currentRow.value.checkCompany = checkCompanyToPass; |
| | | currentRow.value.testStandardId = testStandardIdToPass; |
| | | formDia.value?.openDialog( |
| | | "edit", |
| | | currentRow.value, |
| | | checkResultToPass, |
| | | checkNameToPass, |
| | | testStandardIdToPass, |
| | | checkCompanyToPass, |
| | | qualityInspectParamsToPass |
| | | ); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | }; |
| | | |
| | | // 关闭检验结果选择对话框 |
| | | const closeQuickCheck = () => { |
| | | quickCheckVisible.value = false; |
| | | quickCheckForm.value = { |
| | | checkResult: "合格", |
| | | checkName: "", |
| | | checkTime: "", |
| | | testStandardId: "", |
| | | checkCompany: "天津阳光彩印股份有限公司" |
| | | }; |
| | | quickCheckTableData.value = []; |
| | | testStandardOptions.value = []; |
| | | }; |
| | | |
| | | // 确认检验结果 |
| | | const handleQuickCheckConfirm = () => { |
| | | if (!quickCheckForm.value.checkResult) { |
| | | proxy.$modal.msgWarning("请选择检测结果"); |
| | | return; |
| | | } |
| | | if (!quickCheckForm.value.checkName) { |
| | | proxy.$modal.msgWarning("请选择检验员"); |
| | | return; |
| | | } |
| | | if (!quickCheckForm.value.checkTime) { |
| | | proxy.$modal.msgWarning("请选择检测日期"); |
| | | return; |
| | | } |
| | | |
| | | const checkNameToPass = quickCheckForm.value.checkName; |
| | | const checkResultToPass = quickCheckForm.value.checkResult; |
| | | const checkTimeToPass = quickCheckForm.value.checkTime; |
| | | const testStandardIdToPass = quickCheckForm.value.testStandardId; |
| | | const checkCompanyToPass = quickCheckForm.value.checkCompany; |
| | | const qualityInspectParamsToPass = [...quickCheckTableData.value]; |
| | | |
| | | if (quickCheckForm.value.checkResult === "合格") { |
| | | // 合格:直接提交 |
| | | const data = { |
| | | id: currentRow.value.id, |
| | | checkResult: "合格", |
| | | checkName: checkNameToPass, |
| | | checkTime: checkTimeToPass, |
| | | testStandardId: testStandardIdToPass, |
| | | checkCompany: checkCompanyToPass, |
| | | inspectType: 2, |
| | | qualityInspectParams: qualityInspectParamsToPass |
| | | }; |
| | | qualityInspectUpdate(data).then(res => { |
| | | proxy.$modal.msgSuccess("检验成功"); |
| | | closeQuickCheck(); |
| | | getList(); |
| | | }); |
| | | } else { |
| | | // 不合格:打开详细填写页面 |
| | | closeQuickCheck(); |
| | | nextTick(() => { |
| | | currentRow.value.checkTime = checkTimeToPass; |
| | | currentRow.value.checkCompany = checkCompanyToPass; |
| | | currentRow.value.testStandardId = testStandardIdToPass; |
| | | formDia.value?.openDialog("edit", currentRow.value, checkResultToPass, checkNameToPass, testStandardIdToPass, checkCompanyToPass, qualityInspectParamsToPass); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped></style> |
| | |
| | | <template> |
| | | <div class="app-container metric-binding"> |
| | | <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="metric-tabs"> |
| | | <el-tab-pane label="原材料检验" name="0" /> |
| | | <el-tab-pane label="过程检验" name="1" /> |
| | | <el-tab-pane label="出厂检验" name="2" /> |
| | | <el-tabs v-model="activeTab" |
| | | @tab-change="handleTabChange" |
| | | class="metric-tabs"> |
| | | <el-tab-pane label="原材料检验" |
| | | name="0" /> |
| | | <el-tab-pane label="过程检验" |
| | | name="1" /> |
| | | <el-tab-pane label="出厂检验" |
| | | name="2" /> |
| | | </el-tabs> |
| | | <el-row :gutter="16" class="metric-binding-row"> |
| | | <el-row :gutter="16" |
| | | class="metric-binding-row"> |
| | | <!-- 左侧:检测标准列表 --> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="14" :xl="14" class="left-col"> |
| | | <el-col :xs="24" |
| | | :sm="24" |
| | | :md="12" |
| | | :lg="14" |
| | | :xl="14" |
| | | class="left-col"> |
| | | <div class="panel left-panel"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="standardColumns" |
| | | :tableData="standardTableData" |
| | | :page="page" |
| | | :isSelection="false" |
| | | :rowClassName="rowClassNameCenter" |
| | | :tableLoading="tableLoading" |
| | | :rowClick="handleTableRowClick" |
| | | @pagination="handlePagination" |
| | | :total="page.total" |
| | | > |
| | | <template #standardNoCell="{ row }"> |
| | | <span class="clickable-link" @click="handleStandardRowClick(row)"> |
| | | {{ row.standardNo }} |
| | | </span> |
| | | </template> |
| | | |
| | | <!-- 表头搜索 --> |
| | | <template #standardNoHeader> |
| | | <el-input |
| | | v-model="searchForm.standardNo" |
| | | placeholder="标准编号" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | /> |
| | | </template> |
| | | <template #standardNameHeader> |
| | | <el-input |
| | | v-model="searchForm.standardName" |
| | | placeholder="标准名称" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | /> |
| | | </template> |
| | | <template #stateHeader> |
| | | <el-select |
| | | v-model="searchForm.state" |
| | | placeholder="状态" |
| | | clearable |
| | | size="small" |
| | | style="width: 110px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | > |
| | | <el-option label="草稿" value="0" /> |
| | | <el-option label="通过" value="1" /> |
| | | <el-option label="撤销" value="2" /> |
| | | </el-select> |
| | | </template> |
| | | </PIMTable> |
| | | <PIMTable rowKey="id" |
| | | :column="standardColumns" |
| | | :tableData="standardTableData" |
| | | :page="page" |
| | | :isSelection="false" |
| | | :rowClassName="rowClassNameCenter" |
| | | :tableLoading="tableLoading" |
| | | :rowClick="handleTableRowClick" |
| | | @pagination="handlePagination" |
| | | :total="page.total"> |
| | | <template #standardNoCell="{ row }"> |
| | | <span class="clickable-link" |
| | | @click="handleStandardRowClick(row)"> |
| | | {{ row.standardNo }} |
| | | </span> |
| | | </template> |
| | | <!-- 表头搜索 --> |
| | | <template #standardNoHeader> |
| | | <el-input v-model="searchForm.standardNo" |
| | | placeholder="标准编号" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" /> |
| | | </template> |
| | | <template #standardNameHeader> |
| | | <el-input v-model="searchForm.standardName" |
| | | placeholder="标准名称" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" /> |
| | | </template> |
| | | <template #stateHeader> |
| | | <el-select v-model="searchForm.state" |
| | | placeholder="状态" |
| | | clearable |
| | | size="small" |
| | | style="width: 110px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery"> |
| | | <el-option label="草稿" |
| | | value="0" /> |
| | | <el-option label="通过" |
| | | value="1" /> |
| | | <el-option label="撤销" |
| | | value="2" /> |
| | | </el-select> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-col> |
| | | |
| | | <!-- 右侧:绑定列表 --> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="10" :xl="10" class="right-col"> |
| | | <el-col :xs="24" |
| | | :sm="24" |
| | | :md="12" |
| | | :lg="10" |
| | | :xl="10" |
| | | class="right-col"> |
| | | <div class="panel right-panel"> |
| | | <div class="right-header"> |
| | | <div class="title">绑定关系</div> |
| | | <div class="desc" v-if="currentStandard"> |
| | | 当前检测标准编号:<span class="link-text">{{ currentStandard.standardNo }}</span> |
| | | </div> |
| | | <div class="desc" v-else>请选择左侧检测标准</div> |
| | | </div> |
| | | |
| | | <div class="right-toolbar"> |
| | | <el-button type="primary" :disabled="!currentStandard" @click="openBindingDialog">添加绑定</el-button> |
| | | <el-button type="danger" plain :disabled="!currentStandard" @click="handleBatchUnbind">删除</el-button> |
| | | </div> |
| | | |
| | | <el-table |
| | | v-loading="bindingLoading" |
| | | :data="bindingTableData" |
| | | border |
| | | :row-class-name="() => 'row-center'" |
| | | class="center-table" |
| | | style="width: 100%" |
| | | height="calc(100vh - 220px)" |
| | | @selection-change="handleBindingSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="48" align="center" /> |
| | | <el-table-column type="index" label="序号" width="60" align="center" /> |
| | | <el-table-column prop="productName" label="产品名称" min-width="140" /> |
| | | <el-table-column label="操作" width="120" fixed="right" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button link type="danger" size="small" @click="handleUnbind(row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div class="right-header"> |
| | | <div class="title">绑定关系</div> |
| | | <div class="desc" |
| | | v-if="currentStandard"> |
| | | 当前检测标准编号:<span class="link-text">{{ currentStandard.standardNo }}</span> |
| | | </div> |
| | | <div class="desc" |
| | | v-else>请选择左侧检测标准</div> |
| | | </div> |
| | | <div class="right-toolbar"> |
| | | <el-button type="primary" |
| | | :disabled="!currentStandard" |
| | | @click="openBindingDialog">添加绑定</el-button> |
| | | <el-button v-if="activeTab === '1' || activeTab === '2'" |
| | | type="primary" |
| | | :disabled="!leftProcessOptions.length" |
| | | @click="openProcessBindingDialog">按工序批量绑定</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | :disabled="!currentStandard" |
| | | @click="handleBatchUnbind">删除</el-button> |
| | | </div> |
| | | <el-table v-loading="bindingLoading" |
| | | :data="bindingTableData" |
| | | border |
| | | :row-class-name="() => 'row-center'" |
| | | class="center-table" |
| | | style="width: 100%" |
| | | height="calc(100vh - 220px)" |
| | | @selection-change="handleBindingSelectionChange"> |
| | | <el-table-column type="selection" |
| | | width="48" |
| | | align="center" /> |
| | | <el-table-column type="index" |
| | | label="序号" |
| | | width="60" |
| | | align="center" /> |
| | | <el-table-column prop="productName" |
| | | label="产品名称" |
| | | min-width="140" /> |
| | | <el-table-column label="操作" |
| | | width="120" |
| | | fixed="right" |
| | | align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button link |
| | | type="danger" |
| | | size="small" |
| | | @click="handleUnbind(row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 添加绑定弹框 --> |
| | | <el-dialog |
| | | v-model="bindingDialogVisible" |
| | | title="添加绑定" |
| | | width="520px" |
| | | @close="closeBindingDialog" |
| | | > |
| | | <el-dialog v-model="bindingDialogVisible" |
| | | title="添加绑定" |
| | | width="520px" |
| | | @close="closeBindingDialog"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产品"> |
| | | <el-tree-select |
| | | v-model="selectedProductIds" |
| | | multiple |
| | | collapse-tags |
| | | collapse-tags-tooltip |
| | | placeholder="请选择产品(可多选)" |
| | | clearable |
| | | check-strictly |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" |
| | | /> |
| | | <el-tree-select v-model="selectedProductIds" |
| | | multiple |
| | | collapse-tags |
| | | collapse-tags-tooltip |
| | | placeholder="请选择产品(可多选)" |
| | | clearable |
| | | check-strictly |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="closeBindingDialog">取消</el-button> |
| | | <el-button type="primary" @click="submitBinding">确定</el-button> |
| | | <el-button type="primary" |
| | | @click="submitBinding">确定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog v-model="processBindingDialogVisible" |
| | | title="按工序批量绑定" |
| | | width="520px" |
| | | @close="closeProcessBindingDialog"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产品"> |
| | | <el-tree-select v-model="processBindingProductIds" |
| | | multiple |
| | | collapse-tags |
| | | collapse-tags-tooltip |
| | | placeholder="请选择产品(可多选)" |
| | | clearable |
| | | check-strictly |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="工序"> |
| | | <el-select v-model="processBindingProcessIds" |
| | | multiple |
| | | filterable |
| | | clearable |
| | | collapse-tags |
| | | collapse-tags-tooltip |
| | | placeholder="请选择工序(必选,可多选)" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in leftProcessOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="closeProcessBindingDialog">取消</el-button> |
| | | <el-button type="primary" |
| | | @click="submitProcessBinding">确定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from '@element-plus/icons-vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue' |
| | | import { ElMessageBox } from 'element-plus' |
| | | import PIMTable from '@/components/PIMTable/PIMTable.vue' |
| | | import { productTreeList } from '@/api/basicData/product.js' |
| | | import { |
| | | qualityTestStandardListPage |
| | | } from '@/api/qualityManagement/metricMaintenance.js' |
| | | import { productProcessListPage } from '@/api/basicData/productProcess.js' |
| | | import { |
| | | qualityTestStandardBindingList, |
| | | qualityTestStandardBindingAdd, |
| | | qualityTestStandardBindingDel |
| | | } from '@/api/qualityManagement/qualityTestStandardBinding.js' |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | onMounted, |
| | | getCurrentInstance, |
| | | computed, |
| | | } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { productTreeList } from "@/api/basicData/product.js"; |
| | | import { qualityTestStandardListPage } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import { productProcessListPage } from "@/api/basicData/productProcess.js"; |
| | | import { |
| | | qualityTestStandardBindingList, |
| | | qualityTestStandardBindingAdd, |
| | | qualityTestStandardBindingDel, |
| | | } from "@/api/qualityManagement/qualityTestStandardBinding.js"; |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // 类别 Tab(0=原材料,1=过程,2=出厂) |
| | | const activeTab = ref('0') |
| | | // 类别 Tab(0=原材料,1=过程,2=出厂) |
| | | const activeTab = ref("0"); |
| | | |
| | | // 左侧标准列表:整行内容居中(配合样式) |
| | | const rowClassNameCenter = () => 'row-center' |
| | | // 左侧标准列表:整行内容居中(配合样式) |
| | | const rowClassNameCenter = () => "row-center"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | standardNo: '', |
| | | standardName: '', |
| | | state: '', |
| | | inspectType: '' |
| | | const data = reactive({ |
| | | searchForm: { |
| | | standardNo: "", |
| | | standardName: "", |
| | | state: "", |
| | | inspectType: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | // 左侧 |
| | | const standardTableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ current: 1, size: 10, total: 0 }); |
| | | |
| | | // 工序下拉(用于列表回显) |
| | | const processOptions = ref([]); |
| | | |
| | | const getProcessList = async () => { |
| | | try { |
| | | const res = await productProcessListPage({ current: 1, size: 1000 }); |
| | | if (res?.code === 200) { |
| | | const records = res?.data?.records || []; |
| | | processOptions.value = records.map(item => ({ |
| | | label: item.processName || item.name || item.label, |
| | | value: item.id || item.processId || item.value, |
| | | })); |
| | | } |
| | | } catch (error) { |
| | | console.error("获取工序列表失败:", error); |
| | | } |
| | | }; |
| | | |
| | | const standardColumns = ref([ |
| | | { |
| | | label: "标准编号", |
| | | prop: "standardNo", |
| | | dataType: "slot", |
| | | slot: "standardNoCell", |
| | | minWidth: 160, |
| | | align: "center", |
| | | headerSlot: "standardNoHeader", |
| | | }, |
| | | { |
| | | label: "标准名称", |
| | | prop: "standardName", |
| | | minWidth: 180, |
| | | align: "center", |
| | | headerSlot: "standardNameHeader", |
| | | }, |
| | | { |
| | | label: "类别", |
| | | prop: "inspectType", |
| | | align: "center", |
| | | dataType: "tag", |
| | | formatData: val => { |
| | | const map = { 0: "原材料检验", 1: "过程检验", 2: "出厂检验" }; |
| | | return map[val] || val; |
| | | }, |
| | | }, |
| | | { |
| | | label: "工序", |
| | | prop: "processId", |
| | | align: "center", |
| | | dataType: "tag", |
| | | formatData: val => { |
| | | const target = processOptions.value.find( |
| | | item => String(item.value) === String(val) |
| | | ); |
| | | return target?.label || val; |
| | | }, |
| | | }, |
| | | { |
| | | label: "备注", |
| | | prop: "remark", |
| | | minWidth: 160, |
| | | align: "center", |
| | | }, |
| | | // { |
| | | // label: '状态', |
| | | // prop: 'state', |
| | | // headerSlot: 'stateHeader', |
| | | // dataType: 'tag', |
| | | // formatData: (val) => { |
| | | // const map = { 0: '草稿', 1: '通过', 2: '撤销' } |
| | | // return map[val] || val |
| | | // }, |
| | | // formatType: (val) => { |
| | | // if (val == 1) return 'success' |
| | | // if (val == 2) return 'warning' |
| | | // return 'info' |
| | | // } |
| | | // } |
| | | ]); |
| | | |
| | | const handleTabChange = () => { |
| | | searchForm.value.standardNo = ""; |
| | | searchForm.value.standardName = ""; |
| | | searchForm.value.state = ""; |
| | | searchForm.value.inspectType = activeTab.value; |
| | | page.current = 1; |
| | | currentStandard.value = null; |
| | | bindingTableData.value = []; |
| | | bindingSelectedRows.value = []; |
| | | bindingDialogVisible.value = false; |
| | | processBindingDialogVisible.value = false; |
| | | getStandardList(); |
| | | }; |
| | | |
| | | const currentStandard = ref(null); |
| | | |
| | | // 右侧绑定 |
| | | const bindingTableData = ref([]); |
| | | const bindingLoading = ref(false); |
| | | const bindingSelectedRows = ref([]); |
| | | const bindingDialogVisible = ref(false); |
| | | const processBindingDialogVisible = ref(false); |
| | | |
| | | // 产品树(用于绑定选择) |
| | | const productOptions = ref([]); |
| | | const selectedProductIds = ref([]); |
| | | const processBindingProductIds = ref([]); |
| | | const processBindingProcessIds = ref([]); |
| | | const leftProcessOptions = computed(() => { |
| | | const ids = new Set( |
| | | (standardTableData.value || []) |
| | | .map(i => i?.processId) |
| | | .filter(v => v !== null && v !== undefined && String(v) !== "") |
| | | .map(v => String(v)) |
| | | ); |
| | | if (!ids.size) return []; |
| | | return (processOptions.value || []) |
| | | .filter(p => ids.has(String(p.value))) |
| | | .sort((a, b) => String(a.label).localeCompare(String(b.label), "zh-CN")); |
| | | }); |
| | | |
| | | const getProductOptions = async () => { |
| | | // 避免重复请求 |
| | | if (productOptions.value?.length) return; |
| | | const res = await productTreeList(); |
| | | productOptions.value = convertIdToValue(Array.isArray(res) ? res : []); |
| | | }; |
| | | |
| | | function convertIdToValue(data) { |
| | | return (data || []).map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | return newItem; |
| | | }); |
| | | } |
| | | }) |
| | | const { searchForm } = toRefs(data) |
| | | |
| | | // 左侧 |
| | | const standardTableData = ref([]) |
| | | const tableLoading = ref(false) |
| | | const page = reactive({ current: 1, size: 10, total: 0 }) |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getStandardList(); |
| | | }; |
| | | |
| | | // 工序下拉(用于列表回显) |
| | | const processOptions = ref([]) |
| | | const handlePagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getStandardList(); |
| | | }; |
| | | |
| | | const getProcessList = async () => { |
| | | try { |
| | | const res = await productProcessListPage({ current: 1, size: 1000 }) |
| | | if (res?.code === 200) { |
| | | const records = res?.data?.records || [] |
| | | processOptions.value = records.map((item) => ({ |
| | | label: item.processName || item.name || item.label, |
| | | value: item.id || item.processId || item.value |
| | | })) |
| | | } |
| | | } catch (error) { |
| | | console.error('获取工序列表失败:', error) |
| | | } |
| | | } |
| | | |
| | | const standardColumns = ref([ |
| | | { label: '标准编号', prop: 'standardNo', dataType: 'slot', slot: 'standardNoCell', minWidth: 160, align: 'center', headerSlot: 'standardNoHeader' }, |
| | | { label: '标准名称', prop: 'standardName', minWidth: 180, align: 'center', headerSlot: 'standardNameHeader' }, |
| | | { |
| | | label: '类别', |
| | | prop: 'inspectType', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | formatData: (val) => { |
| | | const map = { 0: '原材料检验', 1: '过程检验', 2: '出厂检验' } |
| | | return map[val] || val |
| | | } |
| | | }, |
| | | { |
| | | label: '工序', |
| | | prop: 'processId', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | formatData: (val) => { |
| | | const target = processOptions.value.find( |
| | | (item) => String(item.value) === String(val) |
| | | ) |
| | | return target?.label || val |
| | | } |
| | | }, |
| | | { |
| | | label: '备注', |
| | | prop: 'remark', |
| | | minWidth: 160, |
| | | align: 'center' |
| | | } |
| | | // { |
| | | // label: '状态', |
| | | // prop: 'state', |
| | | // headerSlot: 'stateHeader', |
| | | // dataType: 'tag', |
| | | // formatData: (val) => { |
| | | // const map = { 0: '草稿', 1: '通过', 2: '撤销' } |
| | | // return map[val] || val |
| | | // }, |
| | | // formatType: (val) => { |
| | | // if (val == 1) return 'success' |
| | | // if (val == 2) return 'warning' |
| | | // return 'info' |
| | | // } |
| | | // } |
| | | ]) |
| | | |
| | | const handleTabChange = () => { |
| | | searchForm.value.standardNo = '' |
| | | searchForm.value.standardName = '' |
| | | searchForm.value.state = '' |
| | | searchForm.value.inspectType = activeTab.value |
| | | page.current = 1 |
| | | currentStandard.value = null |
| | | bindingTableData.value = [] |
| | | bindingSelectedRows.value = [] |
| | | getStandardList() |
| | | } |
| | | |
| | | const currentStandard = ref(null) |
| | | |
| | | // 右侧绑定 |
| | | const bindingTableData = ref([]) |
| | | const bindingLoading = ref(false) |
| | | const bindingSelectedRows = ref([]) |
| | | const bindingDialogVisible = ref(false) |
| | | |
| | | // 产品树(用于绑定选择) |
| | | const productOptions = ref([]) |
| | | const selectedProductIds = ref([]) |
| | | |
| | | const getProductOptions = async () => { |
| | | // 避免重复请求 |
| | | if (productOptions.value?.length) return |
| | | const res = await productTreeList() |
| | | productOptions.value = convertIdToValue(Array.isArray(res) ? res : []) |
| | | } |
| | | |
| | | function convertIdToValue(data) { |
| | | return (data || []).map((item) => { |
| | | const { id, children, ...rest } = item |
| | | const newItem = { |
| | | ...rest, |
| | | value: id |
| | | } |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children) |
| | | } |
| | | return newItem |
| | | }) |
| | | } |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1 |
| | | getStandardList() |
| | | } |
| | | |
| | | const handlePagination = (obj) => { |
| | | page.current = obj.page |
| | | page.size = obj.limit |
| | | getStandardList() |
| | | } |
| | | |
| | | const getStandardList = () => { |
| | | tableLoading.value = true |
| | | qualityTestStandardListPage({ |
| | | ...searchForm.value, |
| | | current: page.current, |
| | | size: page.size, |
| | | state: 1 |
| | | }) |
| | | .then((res) => { |
| | | const records = res?.data?.records || [] |
| | | standardTableData.value = records |
| | | page.total = res?.data?.total || records.length |
| | | const getStandardList = () => { |
| | | tableLoading.value = true; |
| | | qualityTestStandardListPage({ |
| | | ...searchForm.value, |
| | | current: page.current, |
| | | size: page.size, |
| | | state: 1, |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false |
| | | }) |
| | | } |
| | | .then(res => { |
| | | const records = res?.data?.records || []; |
| | | standardTableData.value = records; |
| | | page.total = res?.data?.total || records.length; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 表格行点击,加载右侧绑定列表 |
| | | const handleTableRowClick = (row) => { |
| | | currentStandard.value = row |
| | | loadBindingList() |
| | | } |
| | | // 表格行点击,加载右侧绑定列表 |
| | | const handleTableRowClick = row => { |
| | | currentStandard.value = row; |
| | | loadBindingList(); |
| | | }; |
| | | |
| | | // 左侧行点击,加载右侧绑定列表(保留用于标准编号列的点击) |
| | | const handleStandardRowClick = (row) => { |
| | | currentStandard.value = row |
| | | loadBindingList() |
| | | } |
| | | // 左侧行点击,加载右侧绑定列表(保留用于标准编号列的点击) |
| | | const handleStandardRowClick = row => { |
| | | currentStandard.value = row; |
| | | loadBindingList(); |
| | | }; |
| | | |
| | | const loadBindingList = () => { |
| | | if (!currentStandard.value?.id) { |
| | | bindingTableData.value = [] |
| | | return |
| | | } |
| | | bindingLoading.value = true |
| | | qualityTestStandardBindingList({ testStandardId: currentStandard.value.id }) |
| | | .then((res) => { |
| | | const base = res?.data || [] |
| | | // 将当前标准的工序和备注带到绑定列表中展示 |
| | | bindingTableData.value = base.map((item) => ({ |
| | | ...item, |
| | | processId: currentStandard.value?.processId, |
| | | remark: currentStandard.value?.remark |
| | | })) |
| | | }) |
| | | .finally(() => { |
| | | bindingLoading.value = false |
| | | }) |
| | | } |
| | | const loadBindingList = () => { |
| | | if (!currentStandard.value?.id) { |
| | | bindingTableData.value = []; |
| | | return; |
| | | } |
| | | bindingLoading.value = true; |
| | | qualityTestStandardBindingList({ testStandardId: currentStandard.value.id }) |
| | | .then(res => { |
| | | const base = res?.data || []; |
| | | // 将当前标准的工序和备注带到绑定列表中展示 |
| | | bindingTableData.value = base.map(item => ({ |
| | | ...item, |
| | | processId: currentStandard.value?.processId, |
| | | remark: currentStandard.value?.remark, |
| | | })); |
| | | }) |
| | | .finally(() => { |
| | | bindingLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleBindingSelectionChange = (selection) => { |
| | | bindingSelectedRows.value = selection |
| | | } |
| | | const handleBindingSelectionChange = selection => { |
| | | bindingSelectedRows.value = selection; |
| | | }; |
| | | |
| | | const openBindingDialog = () => { |
| | | if (!currentStandard.value?.id) return |
| | | selectedProductIds.value = [] |
| | | getProductOptions() |
| | | bindingDialogVisible.value = true |
| | | } |
| | | const openBindingDialog = () => { |
| | | if (!currentStandard.value?.id) return; |
| | | selectedProductIds.value = []; |
| | | getProductOptions(); |
| | | bindingDialogVisible.value = true; |
| | | }; |
| | | |
| | | const closeBindingDialog = () => { |
| | | bindingDialogVisible.value = false |
| | | } |
| | | const closeBindingDialog = () => { |
| | | bindingDialogVisible.value = false; |
| | | }; |
| | | |
| | | const submitBinding = async () => { |
| | | const testStandardId = currentStandard.value?.id |
| | | if (!testStandardId) return |
| | | const ids = (selectedProductIds.value || []).filter(Boolean) |
| | | if (!ids.length) { |
| | | proxy.$message.warning('请选择产品') |
| | | return |
| | | } |
| | | const payload = ids.map((pid) => ({ |
| | | productId: pid, |
| | | testStandardId |
| | | })) |
| | | await qualityTestStandardBindingAdd(payload) |
| | | proxy.$message.success('添加成功') |
| | | bindingDialogVisible.value = false |
| | | loadBindingList() |
| | | } |
| | | const openProcessBindingDialog = () => { |
| | | if (activeTab.value !== "1" && activeTab.value !== "2") return; |
| | | processBindingProductIds.value = []; |
| | | processBindingProcessIds.value = []; |
| | | getProductOptions(); |
| | | processBindingDialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleUnbind = async (row) => { |
| | | const id = row?.id ?? row?.qualityTestStandardBindingId |
| | | if (id == null || id === '') return |
| | | try { |
| | | await ElMessageBox.confirm('确认删除该绑定?', '提示', { type: 'warning' }) |
| | | } catch { |
| | | return |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel([id]) |
| | | proxy.$message.success('删除成功') |
| | | loadBindingList() |
| | | } catch (err) { |
| | | console.error('删除绑定失败:', err) |
| | | proxy.$message?.error(err?.message || '删除失败') |
| | | } |
| | | } |
| | | const closeProcessBindingDialog = () => { |
| | | processBindingDialogVisible.value = false; |
| | | }; |
| | | |
| | | const handleBatchUnbind = async () => { |
| | | if (!bindingSelectedRows.value.length) { |
| | | proxy.$message.warning('请选择数据') |
| | | return |
| | | } |
| | | const ids = bindingSelectedRows.value |
| | | .map((i) => i?.id ?? i?.qualityTestStandardBindingId) |
| | | .filter((id) => id != null && id !== '') |
| | | if (!ids.length) { |
| | | proxy.$message.warning('选中数据缺少有效 id') |
| | | return |
| | | } |
| | | try { |
| | | await ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除提示', { type: 'warning' }) |
| | | } catch { |
| | | return |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel(ids) |
| | | proxy.$message.success('删除成功') |
| | | loadBindingList() |
| | | } catch (err) { |
| | | console.error('批量删除绑定失败:', err) |
| | | proxy.$message?.error(err?.message || '删除失败') |
| | | } |
| | | } |
| | | const submitBinding = async () => { |
| | | const ids = (selectedProductIds.value || []).filter(Boolean); |
| | | if (!ids.length) { |
| | | proxy.$message.warning("请选择产品"); |
| | | return; |
| | | } |
| | | const testStandardId = currentStandard.value?.id; |
| | | if (!testStandardId) return; |
| | | const payload = ids.map(pid => ({ |
| | | productId: pid, |
| | | testStandardId, |
| | | })); |
| | | await qualityTestStandardBindingAdd(payload); |
| | | proxy.$message.success("添加成功"); |
| | | bindingDialogVisible.value = false; |
| | | loadBindingList(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | searchForm.value.inspectType = activeTab.value |
| | | getStandardList() |
| | | getProcessList() |
| | | }) |
| | | const submitProcessBinding = async () => { |
| | | const ids = (processBindingProductIds.value || []).filter(Boolean); |
| | | if (!ids.length) { |
| | | proxy.$message.warning("请选择产品"); |
| | | return; |
| | | } |
| | | const processIds = (processBindingProcessIds.value || []).filter(Boolean); |
| | | if (!processIds.length) { |
| | | proxy.$message.warning("请选择工序"); |
| | | return; |
| | | } |
| | | const allow = new Set(leftProcessOptions.value.map(i => String(i.value))); |
| | | const processIdSet = new Set(processIds.map(i => String(i))); |
| | | for (const pid of processIdSet) { |
| | | if (!allow.has(pid)) { |
| | | proxy.$message.warning("所选工序必须在左侧列表中出现过"); |
| | | return; |
| | | } |
| | | } |
| | | const standards = (standardTableData.value || []).filter(s => |
| | | processIdSet.has(String(s?.processId)) |
| | | ); |
| | | if (!standards.length) { |
| | | proxy.$message.warning("左侧列表未找到匹配工序的检测标准"); |
| | | return; |
| | | } |
| | | |
| | | const uniq = new Set(); |
| | | const payload = []; |
| | | for (const std of standards) { |
| | | const testStandardId = std?.id; |
| | | if (!testStandardId) continue; |
| | | for (const pid of ids) { |
| | | const key = `${String(testStandardId)}__${String(pid)}`; |
| | | if (uniq.has(key)) continue; |
| | | uniq.add(key); |
| | | payload.push({ |
| | | productId: pid, |
| | | testStandardId, |
| | | }); |
| | | } |
| | | } |
| | | if (!payload.length) { |
| | | proxy.$message.warning("未生成有效绑定数据"); |
| | | return; |
| | | } |
| | | await qualityTestStandardBindingAdd(payload); |
| | | proxy.$message.success(`已添加 ${payload.length} 条绑定`); |
| | | processBindingDialogVisible.value = false; |
| | | if (currentStandard.value?.id) { |
| | | loadBindingList(); |
| | | } |
| | | }; |
| | | |
| | | const handleUnbind = async row => { |
| | | const id = row?.id ?? row?.qualityTestStandardBindingId; |
| | | if (id == null || id === "") return; |
| | | try { |
| | | await ElMessageBox.confirm("确认删除该绑定?", "提示", { type: "warning" }); |
| | | } catch { |
| | | return; |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel([id]); |
| | | proxy.$message.success("删除成功"); |
| | | loadBindingList(); |
| | | } catch (err) { |
| | | console.error("删除绑定失败:", err); |
| | | proxy.$message?.error(err?.message || "删除失败"); |
| | | } |
| | | }; |
| | | |
| | | const handleBatchUnbind = async () => { |
| | | if (!bindingSelectedRows.value.length) { |
| | | proxy.$message.warning("请选择数据"); |
| | | return; |
| | | } |
| | | const ids = bindingSelectedRows.value |
| | | .map(i => i?.id ?? i?.qualityTestStandardBindingId) |
| | | .filter(id => id != null && id !== ""); |
| | | if (!ids.length) { |
| | | proxy.$message.warning("选中数据缺少有效 id"); |
| | | return; |
| | | } |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | "选中的内容将被删除,是否确认删除?", |
| | | "删除提示", |
| | | { type: "warning" } |
| | | ); |
| | | } catch { |
| | | return; |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel(ids); |
| | | proxy.$message.success("删除成功"); |
| | | loadBindingList(); |
| | | } catch (err) { |
| | | console.error("批量删除绑定失败:", err); |
| | | proxy.$message?.error(err?.message || "删除失败"); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | searchForm.value.inspectType = activeTab.value; |
| | | getStandardList(); |
| | | getProcessList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .metric-binding { |
| | | padding: 0; |
| | | } |
| | | .metric-binding { |
| | | padding: 0; |
| | | } |
| | | |
| | | .metric-tabs { |
| | | margin: 10px; |
| | | } |
| | | .metric-tabs { |
| | | margin: 10px; |
| | | } |
| | | |
| | | .metric-binding-row { |
| | | width: 100%; |
| | | } |
| | | .metric-binding-row { |
| | | width: 100%; |
| | | } |
| | | |
| | | .metric-binding-row .left-col, |
| | | .metric-binding-row .right-col { |
| | | margin-bottom: 16px; |
| | | } |
| | | .metric-binding-row .left-col, |
| | | .metric-binding-row .right-col { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .metric-binding-row .panel { |
| | | background: #ffffff; |
| | | padding: 16px; |
| | | box-sizing: border-box; |
| | | height: 100%; |
| | | min-height: 400px; |
| | | } |
| | | .metric-binding-row .panel { |
| | | background: #ffffff; |
| | | padding: 16px; |
| | | box-sizing: border-box; |
| | | height: 100%; |
| | | min-height: 400px; |
| | | } |
| | | |
| | | .left-panel, |
| | | .right-panel { |
| | | height: 100%; |
| | | } |
| | | .left-panel, |
| | | .right-panel { |
| | | height: 100%; |
| | | } |
| | | |
| | | .toolbar { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | } |
| | | .toolbar { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .toolbar-right { |
| | | flex-shrink: 0; |
| | | } |
| | | .toolbar-right { |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .right-header { |
| | | display: flex; |
| | | align-items: baseline; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | .right-header { |
| | | display: flex; |
| | | align-items: baseline; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .right-header .title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | .right-header .title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .right-header .desc { |
| | | font-size: 13px; |
| | | color: #666; |
| | | } |
| | | .right-header .desc { |
| | | font-size: 13px; |
| | | color: #666; |
| | | } |
| | | |
| | | .right-toolbar { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | .right-toolbar { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .link-text { |
| | | color: #409eff; |
| | | cursor: default; |
| | | } |
| | | .link-text { |
| | | color: #409eff; |
| | | cursor: default; |
| | | } |
| | | |
| | | .clickable-link { |
| | | color: #409eff; |
| | | cursor: pointer; |
| | | } |
| | | .clickable-link { |
| | | color: #409eff; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .clickable-link:hover { |
| | | text-decoration: underline; |
| | | } |
| | | .clickable-link:hover { |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | :deep(.row-center td) { |
| | | text-align: center !important; |
| | | } |
| | | :deep(.row-center td) { |
| | | text-align: center !important; |
| | | } |
| | | |
| | | /* el-table 表头/内容统一居中(row-class-name 不作用于表头) */ |
| | | :deep(.center-table .el-table__header-wrapper th .cell) { |
| | | text-align: center !important; |
| | | } |
| | | :deep(.center-table .el-table__body-wrapper td .cell) { |
| | | text-align: center !important; |
| | | } |
| | | /* el-table 表头/内容统一居中(row-class-name 不作用于表头) */ |
| | | :deep(.center-table .el-table__header-wrapper th .cell) { |
| | | text-align: center !important; |
| | | } |
| | | :deep(.center-table .el-table__body-wrapper td .cell) { |
| | | text-align: center !important; |
| | | } |
| | | |
| | | /* PIMTable 表头居中 */ |
| | | :deep(.lims-table .pim-table-header-cell) { |
| | | text-align: center; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | /* PIMTable 表头居中 */ |
| | | :deep(.lims-table .pim-table-header-cell) { |
| | | text-align: center; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | :deep(.lims-table .pim-table-header-title) { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | :deep(.lims-table .pim-table-header-title) { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | :deep(.lims-table .pim-table-header-extra) { |
| | | width: 100%; |
| | | margin-top: 4px; |
| | | } |
| | | :deep(.lims-table .pim-table-header-extra) { |
| | | width: 100%; |
| | | margin-top: 4px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <FormDialog |
| | | v-model="dialogVisible" |
| | | :title="computedTitle" |
| | | :operation-type="operationType" |
| | | :width="width" |
| | | @close="emit('close')" |
| | | @cancel="handleCancel" |
| | | @confirm="handleConfirm" |
| | | > |
| | | <el-form |
| | | ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="100px" |
| | | > |
| | | <el-form-item label="标准编号" prop="standardNo"> |
| | | <el-input v-model="form.standardNo" placeholder="请输入标准编号" /> |
| | | <FormDialog v-model="dialogVisible" |
| | | :title="computedTitle" |
| | | :operation-type="operationType" |
| | | :width="width" |
| | | @close="emit('close')" |
| | | @cancel="handleCancel" |
| | | @confirm="handleConfirm"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="100px"> |
| | | <el-form-item label="标准编号" |
| | | prop="standardNo"> |
| | | <el-input v-model="form.standardNo" |
| | | placeholder="请输入标准编号" /> |
| | | </el-form-item> |
| | | <el-form-item label="标准名称" prop="standardName"> |
| | | <el-input v-model="form.standardName" placeholder="请输入标准名称" /> |
| | | <el-form-item label="标准名称" |
| | | prop="standardName"> |
| | | <el-input v-model="form.standardName" |
| | | placeholder="请输入标准名称" /> |
| | | </el-form-item> |
| | | <el-form-item v-if="needProcess" label="工序" prop="processId"> |
| | | <el-select v-model="form.processId" placeholder="请选择工序" style="width: 100%"> |
| | | <el-option |
| | | v-for="item in processOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | <el-form-item v-if="needProcess" |
| | | label="工序" |
| | | prop="processId"> |
| | | <el-select v-model="form.processId" |
| | | placeholder="请选择工序" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in processOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="状态" prop="state"> |
| | | <el-select v-model="form.state" placeholder="请选择状态" style="width: 100%"> |
| | | <el-option label="草稿" value="0" /> |
| | | <el-option label="通过" value="1" /> |
| | | <el-option label="撤销" value="2" /> |
| | | <el-form-item label="状态" |
| | | prop="state"> |
| | | <el-select v-model="form.state" |
| | | placeholder="请选择状态" |
| | | style="width: 100%"> |
| | | <el-option label="草稿" |
| | | value="0" /> |
| | | <el-option label="通过" |
| | | value="1" /> |
| | | <el-option label="撤销" |
| | | value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入备注" |
| | | /> |
| | | <el-form-item label="备注" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入备注" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | </FormDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, ref } from 'vue' |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue' |
| | | import { computed, ref } from "vue"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | operationType: { |
| | | type: String, |
| | | default: 'add' |
| | | }, |
| | | form: { |
| | | type: Object, |
| | | required: true |
| | | }, |
| | | rules: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | processOptions: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | width: { |
| | | type: String, |
| | | default: '500px' |
| | | }, |
| | | inspectType:{ |
| | | type: String, |
| | | default: null |
| | | } |
| | | }) |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | operationType: { |
| | | type: String, |
| | | default: "add", |
| | | }, |
| | | form: { |
| | | type: Object, |
| | | required: true, |
| | | }, |
| | | rules: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | processOptions: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | width: { |
| | | type: String, |
| | | default: "500px", |
| | | }, |
| | | inspectType: { |
| | | type: String, |
| | | default: null, |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(['update:modelValue', 'close', 'cancel', 'confirm']) |
| | | const emit = defineEmits(["update:modelValue", "close", "cancel", "confirm"]); |
| | | |
| | | const dialogVisible = computed({ |
| | | get: () => props.modelValue, |
| | | set: (val) => emit('update:modelValue', val) |
| | | }) |
| | | const dialogVisible = computed({ |
| | | get: () => props.modelValue, |
| | | set: val => emit("update:modelValue", val), |
| | | }); |
| | | |
| | | const formRef = ref(null) |
| | | const formRef = ref(null); |
| | | |
| | | const needProcess = computed(() => String(props.inspectType ?? '') === '1') |
| | | const needProcess = computed(() => |
| | | ["1", "2"].includes(String(props.inspectType ?? "")) |
| | | ); |
| | | |
| | | const computedTitle = computed(() => { |
| | | if (props.operationType === 'edit') return '编辑检测标准' |
| | | if (props.operationType === 'copy') return '复制检测标准' |
| | | return '新增检测标准' |
| | | }) |
| | | const computedTitle = computed(() => { |
| | | if (props.operationType === "edit") return "编辑检测标准"; |
| | | if (props.operationType === "copy") return "复制检测标准"; |
| | | return "新增检测标准"; |
| | | }); |
| | | |
| | | const handleConfirm = () => { |
| | | if (!formRef.value) { |
| | | emit('confirm') |
| | | return |
| | | } |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | emit('confirm') |
| | | const handleConfirm = () => { |
| | | if (!formRef.value) { |
| | | emit("confirm"); |
| | | return; |
| | | } |
| | | }) |
| | | } |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | emit("confirm"); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCancel = () => { |
| | | emit('cancel') |
| | | dialogVisible.value = false |
| | | } |
| | | const handleCancel = () => { |
| | | emit("cancel"); |
| | | dialogVisible.value = false; |
| | | }; |
| | | |
| | | const resetFields = () => { |
| | | formRef.value?.resetFields?.() |
| | | } |
| | | const resetFields = () => { |
| | | formRef.value?.resetFields?.(); |
| | | }; |
| | | |
| | | defineExpose({ |
| | | resetFields |
| | | }) |
| | | defineExpose({ |
| | | resetFields, |
| | | }); |
| | | </script> |
| | |
| | | <template> |
| | | <div class="app-container metric-maintenance"> |
| | | <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="metric-tabs"> |
| | | <el-tab-pane label="原材料检验" name="0" /> |
| | | <el-tab-pane label="过程检验" name="1" /> |
| | | <el-tab-pane label="出厂检验" name="2" /> |
| | | <el-tabs v-model="activeTab" |
| | | @tab-change="handleTabChange" |
| | | class="metric-tabs"> |
| | | <el-tab-pane label="原材料检验" |
| | | name="0" /> |
| | | <el-tab-pane label="过程检验" |
| | | name="1" /> |
| | | <el-tab-pane label="出厂检验" |
| | | name="2" /> |
| | | </el-tabs> |
| | | <el-row :gutter="16" class="metric-maintenance-row"> |
| | | <el-row :gutter="16" |
| | | class="metric-maintenance-row"> |
| | | <!-- 左侧:检测标准列表 --> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" class="left-col"> |
| | | <el-col :xs="24" |
| | | :sm="24" |
| | | :md="12" |
| | | :lg="12" |
| | | :xl="12" |
| | | class="left-col"> |
| | | <div class="left-panel"> |
| | | <div class="toolbar"> |
| | | <div class="toolbar-left"></div> |
| | | <div class="toolbar-right"> |
| | | <el-button type="primary" @click="openStandardDialog('add')">新增</el-button> |
| | | <el-button type="success" plain @click="handleBatchAudit(1)">批准</el-button> |
| | | <el-button type="warning" plain @click="handleBatchAudit(2)">撤销</el-button> |
| | | <el-button type="danger" plain @click="handleBatchDelete">删除</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="standardColumns" |
| | | :tableData="standardTableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | :tableLoading="tableLoading" |
| | | :rowClassName="rowClassNameCenter" |
| | | :rowClick="handleTableRowClick" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="handlePagination" |
| | | :total="page.total" |
| | | > |
| | | <template #standardNoCell="{ row }"> |
| | | <span class="clickable-link" @click="handleStandardRowClick(row)"> |
| | | {{ row.standardNo }} |
| | | </span> |
| | | </template> |
| | | |
| | | <!-- 表头搜索插槽 --> |
| | | <template #standardNoHeader> |
| | | <el-input |
| | | v-model="searchForm.standardNo" |
| | | placeholder="标准编号" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | /> |
| | | </template> |
| | | <template #standardNameHeader> |
| | | <el-input |
| | | v-model="searchForm.standardName" |
| | | placeholder="标准名称" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | /> |
| | | </template> |
| | | <template #stateHeader> |
| | | <el-select |
| | | v-model="searchForm.state" |
| | | placeholder="状态" |
| | | clearable |
| | | size="small" |
| | | style="width: 110px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | > |
| | | <el-option label="草稿" value="0" /> |
| | | <el-option label="通过" value="1" /> |
| | | <el-option label="撤销" value="2" /> |
| | | </el-select> |
| | | </template> |
| | | </PIMTable> |
| | | <div class="toolbar"> |
| | | <div class="toolbar-left"></div> |
| | | <div class="toolbar-right"> |
| | | <el-button type="primary" |
| | | @click="openStandardDialog('add')">新增</el-button> |
| | | <el-button type="success" |
| | | plain |
| | | @click="handleBatchAudit(1)">批准</el-button> |
| | | <el-button type="warning" |
| | | plain |
| | | @click="handleBatchAudit(2)">撤销</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleBatchDelete">删除</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable rowKey="id" |
| | | :column="standardColumns" |
| | | :tableData="standardTableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | :tableLoading="tableLoading" |
| | | :rowClassName="rowClassNameCenter" |
| | | :rowClick="handleTableRowClick" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="handlePagination" |
| | | :total="page.total"> |
| | | <template #standardNoCell="{ row }"> |
| | | <span class="clickable-link" |
| | | @click="handleStandardRowClick(row)"> |
| | | {{ row.standardNo }} |
| | | </span> |
| | | </template> |
| | | <!-- 表头搜索插槽 --> |
| | | <template #standardNoHeader> |
| | | <el-input v-model="searchForm.standardNo" |
| | | placeholder="标准编号" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" /> |
| | | </template> |
| | | <template #standardNameHeader> |
| | | <el-input v-model="searchForm.standardName" |
| | | placeholder="标准名称" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" /> |
| | | </template> |
| | | <template #stateHeader> |
| | | <el-select v-model="searchForm.state" |
| | | placeholder="状态" |
| | | clearable |
| | | size="small" |
| | | style="width: 110px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery"> |
| | | <el-option label="草稿" |
| | | value="0" /> |
| | | <el-option label="通过" |
| | | value="1" /> |
| | | <el-option label="撤销" |
| | | value="2" /> |
| | | </el-select> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-col> |
| | | |
| | | <!-- 右侧:标准参数列表 --> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" class="right-col"> |
| | | <el-col :xs="24" |
| | | :sm="24" |
| | | :md="12" |
| | | :lg="12" |
| | | :xl="12" |
| | | class="right-col"> |
| | | <div class="right-panel"> |
| | | <div class="right-header"> |
| | | <div class="title">标准参数</div> |
| | | <div class="desc" v-if="currentStandard"> |
| | | 您当前选择的检测标准编号是: |
| | | <span class="link-text">{{ currentStandard.standardNo }}</span> |
| | | </div> |
| | | <div class="desc" v-else>请先在左侧选择一个检测标准</div> |
| | | </div> |
| | | |
| | | <div class="right-toolbar"> |
| | | <el-button type="primary" :disabled="!currentStandard || isStandardReadonly" @click="openParamDialog('add')"> |
| | | 新增 |
| | | </el-button> |
| | | <el-button type="danger" plain :disabled="!currentStandard || isStandardReadonly" @click="handleParamBatchDelete"> |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | | |
| | | <el-table |
| | | v-loading="detailLoading" |
| | | :data="detailTableData" |
| | | border |
| | | :row-class-name="() => 'row-center'" |
| | | class="center-table" |
| | | style="width: 100%" |
| | | height="calc(100vh - 220px)" |
| | | @selection-change="handleParamSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="48" align="center" /> |
| | | <el-table-column type="index" label="序号" width="60" align="center" /> |
| | | <el-table-column prop="parameterItem" label="参数项" min-width="120" /> |
| | | <el-table-column prop="unit" label="单位" width="80" /> |
| | | <el-table-column prop="standardValue" label="标准值" min-width="120" /> |
| | | <el-table-column prop="controlValue" label="内控值" min-width="120" /> |
| | | <el-table-column prop="defaultValue" label="默认值" min-width="120" /> |
| | | <el-table-column label="操作" width="140" fixed="right" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button link type="primary" size="small" :disabled="isStandardReadonly" @click="openParamDialog('edit', row)"> |
| | | 编辑 |
| | | <div class="right-header"> |
| | | <div class="title">标准参数</div> |
| | | <div class="desc" |
| | | v-if="currentStandard"> |
| | | 您当前选择的检测标准编号是: |
| | | <span class="link-text">{{ currentStandard.standardNo }}</span> |
| | | </div> |
| | | <div class="desc" |
| | | v-else>请先在左侧选择一个检测标准</div> |
| | | </div> |
| | | <div class="right-toolbar"> |
| | | <el-button type="primary" |
| | | :disabled="!currentStandard || isStandardReadonly" |
| | | @click="openParamDialog('add')"> |
| | | 新增 |
| | | </el-button> |
| | | <el-button link type="danger" size="small" :disabled="isStandardReadonly" @click="handleParamDelete(row)"> |
| | | <el-button type="danger" |
| | | plain |
| | | :disabled="!currentStandard || isStandardReadonly" |
| | | @click="handleParamBatchDelete"> |
| | | 删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <el-table v-loading="detailLoading" |
| | | :data="detailTableData" |
| | | border |
| | | :row-class-name="() => 'row-center'" |
| | | class="center-table" |
| | | style="width: 100%" |
| | | height="calc(100vh - 220px)" |
| | | @selection-change="handleParamSelectionChange"> |
| | | <el-table-column type="selection" |
| | | width="48" |
| | | align="center" /> |
| | | <el-table-column type="index" |
| | | label="序号" |
| | | width="60" |
| | | align="center" /> |
| | | <el-table-column prop="parameterItem" |
| | | label="参数项" |
| | | min-width="120" /> |
| | | <el-table-column prop="unit" |
| | | label="单位" |
| | | width="80" /> |
| | | <el-table-column prop="standardValue" |
| | | label="标准值" |
| | | min-width="120" /> |
| | | <el-table-column prop="controlValue" |
| | | label="内控值" |
| | | min-width="120" /> |
| | | <el-table-column prop="defaultValue" |
| | | label="默认值" |
| | | min-width="120" /> |
| | | <el-table-column label="操作" |
| | | width="140" |
| | | fixed="right" |
| | | align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="isStandardReadonly" |
| | | @click="openParamDialog('edit', row)"> |
| | | 编辑 |
| | | </el-button> |
| | | <el-button link |
| | | type="danger" |
| | | size="small" |
| | | :disabled="isStandardReadonly" |
| | | @click="handleParamDelete(row)"> |
| | | 删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 新增 / 编辑检测标准 --> |
| | | <StandardFormDialog |
| | | ref="standardFormDialogRef" |
| | | v-model="standardDialogVisible" |
| | | :operation-type="standardOperationType" |
| | | :form="standardForm" |
| | | :rules="standardRules" |
| | | :inspect-type="activeTab" |
| | | :process-options="processOptions" |
| | | @confirm="submitStandardForm" |
| | | @close="closeStandardDialog" |
| | | @cancel="closeStandardDialog" |
| | | /> |
| | | |
| | | <ParamFormDialog |
| | | ref="paramFormDialogRef" |
| | | v-model="paramDialogVisible" |
| | | :operation-type="paramOperationType" |
| | | :inspectType="activeTab.value" |
| | | :form="paramForm" |
| | | @confirm="submitParamForm" |
| | | @close="closeParamDialog" |
| | | @cancel="closeParamDialog" |
| | | /> |
| | | <StandardFormDialog ref="standardFormDialogRef" |
| | | v-model="standardDialogVisible" |
| | | :operation-type="standardOperationType" |
| | | :form="standardForm" |
| | | :rules="standardRules" |
| | | :inspect-type="activeTab" |
| | | :process-options="processOptions" |
| | | @confirm="submitStandardForm" |
| | | @close="closeStandardDialog" |
| | | @cancel="closeStandardDialog" /> |
| | | <ParamFormDialog ref="paramFormDialogRef" |
| | | v-model="paramDialogVisible" |
| | | :operation-type="paramOperationType" |
| | | :inspectType="activeTab.value" |
| | | :form="paramForm" |
| | | @confirm="submitParamForm" |
| | | @close="closeParamDialog" |
| | | @cancel="closeParamDialog" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from '@element-plus/icons-vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance, computed } from 'vue' |
| | | import { ElMessageBox } from 'element-plus' |
| | | import { |
| | | qualityTestStandardListPage, |
| | | qualityTestStandardAdd, |
| | | qualityTestStandardUpdate, |
| | | qualityTestStandardDel, |
| | | qualityTestStandardCopyParam, |
| | | qualityTestStandardAudit, |
| | | qualityTestStandardParamList, |
| | | qualityTestStandardParamAdd, |
| | | qualityTestStandardParamUpdate, |
| | | qualityTestStandardParamDel |
| | | } from '@/api/qualityManagement/metricMaintenance.js' |
| | | import { productProcessListPage } from '@/api/basicData/productProcess.js' |
| | | import StandardFormDialog from './StandardFormDialog.vue' |
| | | import ParamFormDialog from './ParamFormDialog.vue' |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | onMounted, |
| | | getCurrentInstance, |
| | | computed, |
| | | } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | qualityTestStandardListPage, |
| | | qualityTestStandardAdd, |
| | | qualityTestStandardUpdate, |
| | | qualityTestStandardDel, |
| | | qualityTestStandardCopyParam, |
| | | qualityTestStandardAudit, |
| | | qualityTestStandardParamList, |
| | | qualityTestStandardParamAdd, |
| | | qualityTestStandardParamUpdate, |
| | | qualityTestStandardParamDel, |
| | | } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import { productProcessListPage } from "@/api/basicData/productProcess.js"; |
| | | import StandardFormDialog from "./StandardFormDialog.vue"; |
| | | import ParamFormDialog from "./ParamFormDialog.vue"; |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // 类别 Tab(0=原材料,1=过程,2=出厂) |
| | | const activeTab = ref('0') |
| | | // 类别 Tab(0=原材料,1=过程,2=出厂) |
| | | const activeTab = ref("0"); |
| | | |
| | | // 左侧标准列表:整行内容居中(配合样式) |
| | | const rowClassNameCenter = () => 'row-center' |
| | | // 左侧标准列表:整行内容居中(配合样式) |
| | | const rowClassNameCenter = () => "row-center"; |
| | | |
| | | // 标准状态为“通过(1)”时,右侧参数禁止增删改 |
| | | const isStandardReadonly = computed(() => { |
| | | const state = currentStandard.value?.state |
| | | return state === 1 || state === '1' |
| | | }) |
| | | // 标准状态为“通过(1)”时,右侧参数禁止增删改 |
| | | const isStandardReadonly = computed(() => { |
| | | const state = currentStandard.value?.state; |
| | | return state === 1 || state === "1"; |
| | | }); |
| | | |
| | | // 搜索条件 |
| | | const data = reactive({ |
| | | searchForm: { |
| | | standardNo: '', |
| | | standardName: '', |
| | | remark: '', |
| | | state: '', |
| | | inspectType: '', |
| | | processId: '' |
| | | }, |
| | | standardForm: { |
| | | id: undefined, |
| | | standardNo: '', |
| | | standardName: '', |
| | | remark: '', |
| | | state: '0', |
| | | inspectType: '', |
| | | processId: '' |
| | | }, |
| | | standardRules: { |
| | | standardNo: [{ required: true, message: '请输入标准编号', trigger: 'blur' }], |
| | | standardName: [{ required: true, message: '请输入标准名称', trigger: 'blur' }], |
| | | processId: [{ |
| | | validator: (_rule, value, callback) => { |
| | | const inspectType = String(standardForm.value.inspectType ?? activeTab.value ?? '') |
| | | if (inspectType === '1' && (value === '' || value === null || value === undefined)) { |
| | | callback(new Error('请选择工序')) |
| | | return |
| | | } |
| | | callback() |
| | | }, |
| | | trigger: 'change' |
| | | }] |
| | | } |
| | | }) |
| | | |
| | | const { searchForm, standardForm, standardRules } = toRefs(data) |
| | | |
| | | // 左侧表格 |
| | | const standardTableData = ref([]) |
| | | const selectedRows = ref([]) |
| | | const tableLoading = ref(false) |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0 |
| | | }) |
| | | |
| | | // 工序下拉 |
| | | const processOptions = ref([]) |
| | | |
| | | // 获取工序列表 |
| | | const getProcessList = async () => { |
| | | try { |
| | | const res = await productProcessListPage({ current: 1, size: 1000 }) |
| | | if (res?.code === 200) { |
| | | const records = res?.data?.records || [] |
| | | processOptions.value = records.map(item => ({ |
| | | label: item.processName || item.name || item.label, |
| | | value: item.id || item.processId || item.value |
| | | })) |
| | | } |
| | | } catch (error) { |
| | | console.error('获取工序列表失败:', error) |
| | | } |
| | | } |
| | | |
| | | // 当前选中的标准 & 右侧详情 |
| | | const currentStandard = ref(null) |
| | | const detailTableData = ref([]) |
| | | const detailLoading = ref(false) |
| | | const paramSelectedRows = ref([]) |
| | | const paramDialogVisible = ref(false) |
| | | const paramOperationType = ref('add') // add | edit |
| | | const paramFormDialogRef = ref(null) |
| | | const paramForm = reactive({ |
| | | id: undefined, |
| | | parameterItem: '', |
| | | unit: '', |
| | | standardValue: '', |
| | | controlValue: '', |
| | | defaultValue: '' |
| | | }) |
| | | |
| | | // 弹窗 |
| | | const standardDialogVisible = ref(false) |
| | | const standardOperationType = ref('add') // add | edit | copy |
| | | const standardFormDialogRef = ref(null) |
| | | |
| | | // 列定义 |
| | | const standardColumns = ref([ |
| | | { |
| | | label: '标准编号', |
| | | prop: 'standardNo', |
| | | dataType: 'slot', |
| | | slot: 'standardNoCell', |
| | | minWidth: 160, |
| | | align: 'center', |
| | | headerSlot: 'standardNoHeader' |
| | | }, |
| | | { |
| | | label: '标准名称', |
| | | prop: 'standardName', |
| | | minWidth: 180, |
| | | align: 'center', |
| | | headerSlot: 'standardNameHeader' |
| | | }, |
| | | { |
| | | label: '类别', |
| | | prop: 'inspectType', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | minWidth: 120, |
| | | formatData: (val) => { |
| | | const map = { |
| | | 0: '原材料检验', |
| | | 1: '过程检验', |
| | | 2: '出厂检验' |
| | | } |
| | | return map[val] || val |
| | | } |
| | | }, |
| | | { |
| | | label: '工序', |
| | | prop: 'processId', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | visible: visible => activeTab.value === '1', |
| | | formatData: (val) => { |
| | | const target = processOptions.value.find( |
| | | (item) => String(item.value) === String(val) |
| | | ) |
| | | return target?.label || val |
| | | } |
| | | }, |
| | | { |
| | | label: '状态', |
| | | prop: 'state', |
| | | headerSlot: 'stateHeader', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | formatData: (val) => { |
| | | const map = { |
| | | 0: '草稿', |
| | | 1: '通过', |
| | | 2: '撤销' |
| | | } |
| | | return map[val] || val |
| | | // 搜索条件 |
| | | const data = reactive({ |
| | | searchForm: { |
| | | standardNo: "", |
| | | standardName: "", |
| | | remark: "", |
| | | state: "", |
| | | inspectType: "", |
| | | processId: "", |
| | | }, |
| | | formatType: (val) => { |
| | | if (val === '1' || val === 1) return 'success' |
| | | if (val === '2' || val === 2) return 'warning' |
| | | return 'info' |
| | | } |
| | | }, |
| | | { |
| | | label: '备注', |
| | | prop: 'remark', |
| | | minWidth: 160, |
| | | align: 'center' |
| | | }, |
| | | { |
| | | dataType: 'action', |
| | | label: '操作', |
| | | align: 'center', |
| | | fixed: 'right', |
| | | width: 220, |
| | | operation: [ |
| | | { |
| | | name: '编辑', |
| | | type: 'text', |
| | | clickFun: (row) => { |
| | | openStandardDialog('edit', row) |
| | | } |
| | | }, |
| | | { |
| | | name: '复制', |
| | | type: 'text', |
| | | clickFun: async (row) => { |
| | | if (!row?.id) return |
| | | try { |
| | | await ElMessageBox.confirm('确认复制该标准参数?', '提示', { type: 'warning' }) |
| | | } catch { |
| | | return |
| | | } |
| | | await qualityTestStandardCopyParam(row.id) |
| | | proxy.$message.success('复制成功') |
| | | getStandardList() |
| | | if (currentStandard.value?.id === row.id) { |
| | | loadDetail(row.id) |
| | | } |
| | | } |
| | | }, |
| | | { |
| | | name: '删除', |
| | | type: 'text', |
| | | clickFun: (row) => { |
| | | handleDelete(row) |
| | | } |
| | | standardForm: { |
| | | id: undefined, |
| | | standardNo: "", |
| | | standardName: "", |
| | | remark: "", |
| | | state: "0", |
| | | inspectType: "", |
| | | processId: "", |
| | | }, |
| | | standardRules: { |
| | | standardNo: [ |
| | | { required: true, message: "请输入标准编号", trigger: "blur" }, |
| | | ], |
| | | standardName: [ |
| | | { required: true, message: "请输入标准名称", trigger: "blur" }, |
| | | ], |
| | | processId: [ |
| | | { |
| | | validator: (_rule, value, callback) => { |
| | | const inspectType = String( |
| | | standardForm.value.inspectType ?? activeTab.value ?? "" |
| | | ); |
| | | if ( |
| | | (inspectType === "1" || inspectType === "2") && |
| | | (value === "" || value === null || value === undefined) |
| | | ) { |
| | | callback(new Error("请选择工序")); |
| | | return; |
| | | } |
| | | callback(); |
| | | }, |
| | | trigger: "change", |
| | | }, |
| | | ], |
| | | }, |
| | | }); |
| | | |
| | | const { searchForm, standardForm, standardRules } = toRefs(data); |
| | | |
| | | // 左侧表格 |
| | | const standardTableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | // 工序下拉 |
| | | const processOptions = ref([]); |
| | | |
| | | // 获取工序列表 |
| | | const getProcessList = async () => { |
| | | try { |
| | | const res = await productProcessListPage({ current: 1, size: 1000 }); |
| | | if (res?.code === 200) { |
| | | const records = res?.data?.records || []; |
| | | processOptions.value = records.map(item => ({ |
| | | label: item.processName || item.name || item.label, |
| | | value: item.id || item.processId || item.value, |
| | | })); |
| | | } |
| | | ] |
| | | } |
| | | ]) |
| | | const handleTabChange = () => { |
| | | searchForm.value.standardNo = '' |
| | | searchForm.value.standardName = '' |
| | | searchForm.value.remark = '' |
| | | searchForm.value.state = '' |
| | | searchForm.value.processId = '' |
| | | searchForm.value.inspectType = activeTab.value |
| | | page.current = 1 |
| | | currentStandard.value = null |
| | | detailTableData.value = [] |
| | | paramSelectedRows.value = [] |
| | | getStandardList() |
| | | } |
| | | } catch (error) { |
| | | console.error("获取工序列表失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 查询列表 |
| | | const getStandardList = () => { |
| | | tableLoading.value = true |
| | | const params = { |
| | | ...searchForm.value, |
| | | current: page.current, |
| | | size: page.size |
| | | } |
| | | qualityTestStandardListPage(params) |
| | | .then((res) => { |
| | | const records = res?.data?.records || [] |
| | | standardTableData.value = records |
| | | page.total = res?.data?.total || records.length |
| | | // 当前选中的标准 & 右侧详情 |
| | | const currentStandard = ref(null); |
| | | const detailTableData = ref([]); |
| | | const detailLoading = ref(false); |
| | | const paramSelectedRows = ref([]); |
| | | const paramDialogVisible = ref(false); |
| | | const paramOperationType = ref("add"); // add | edit |
| | | const paramFormDialogRef = ref(null); |
| | | const paramForm = reactive({ |
| | | id: undefined, |
| | | parameterItem: "", |
| | | unit: "", |
| | | standardValue: "", |
| | | controlValue: "", |
| | | defaultValue: "", |
| | | }); |
| | | |
| | | // 弹窗 |
| | | const standardDialogVisible = ref(false); |
| | | const standardOperationType = ref("add"); // add | edit | copy |
| | | const standardFormDialogRef = ref(null); |
| | | |
| | | // 列定义 |
| | | const standardColumns = ref([ |
| | | { |
| | | label: "标准编号", |
| | | prop: "standardNo", |
| | | dataType: "slot", |
| | | slot: "standardNoCell", |
| | | minWidth: 160, |
| | | align: "center", |
| | | headerSlot: "standardNoHeader", |
| | | }, |
| | | { |
| | | label: "标准名称", |
| | | prop: "standardName", |
| | | minWidth: 180, |
| | | align: "center", |
| | | headerSlot: "standardNameHeader", |
| | | }, |
| | | { |
| | | label: "类别", |
| | | prop: "inspectType", |
| | | align: "center", |
| | | dataType: "tag", |
| | | minWidth: 120, |
| | | formatData: val => { |
| | | const map = { |
| | | 0: "原材料检验", |
| | | 1: "过程检验", |
| | | 2: "出厂检验", |
| | | }; |
| | | return map[val] || val; |
| | | }, |
| | | }, |
| | | { |
| | | label: "工序", |
| | | prop: "processId", |
| | | align: "center", |
| | | dataType: "tag", |
| | | visible: visible => ["1", "2"].includes(String(activeTab.value)), |
| | | formatData: val => { |
| | | const target = processOptions.value.find( |
| | | item => String(item.value) === String(val) |
| | | ); |
| | | return target?.label || val; |
| | | }, |
| | | }, |
| | | { |
| | | label: "状态", |
| | | prop: "state", |
| | | headerSlot: "stateHeader", |
| | | align: "center", |
| | | dataType: "tag", |
| | | formatData: val => { |
| | | const map = { |
| | | 0: "草稿", |
| | | 1: "通过", |
| | | 2: "撤销", |
| | | }; |
| | | return map[val] || val; |
| | | }, |
| | | formatType: val => { |
| | | if (val === "1" || val === 1) return "success"; |
| | | if (val === "2" || val === 2) return "warning"; |
| | | return "info"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "备注", |
| | | prop: "remark", |
| | | minWidth: 160, |
| | | align: "center", |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 220, |
| | | operation: [ |
| | | { |
| | | name: "编辑", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openStandardDialog("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "复制", |
| | | type: "text", |
| | | clickFun: async row => { |
| | | if (!row?.id) return; |
| | | try { |
| | | await ElMessageBox.confirm("确认复制该标准参数?", "提示", { |
| | | type: "warning", |
| | | }); |
| | | } catch { |
| | | return; |
| | | } |
| | | await qualityTestStandardCopyParam(row.id); |
| | | proxy.$message.success("复制成功"); |
| | | getStandardList(); |
| | | if (currentStandard.value?.id === row.id) { |
| | | loadDetail(row.id); |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | name: "删除", |
| | | type: "text", |
| | | clickFun: row => { |
| | | handleDelete(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const handleTabChange = () => { |
| | | searchForm.value.standardNo = ""; |
| | | searchForm.value.standardName = ""; |
| | | searchForm.value.remark = ""; |
| | | searchForm.value.state = ""; |
| | | searchForm.value.processId = ""; |
| | | searchForm.value.inspectType = activeTab.value; |
| | | page.current = 1; |
| | | currentStandard.value = null; |
| | | detailTableData.value = []; |
| | | paramSelectedRows.value = []; |
| | | getStandardList(); |
| | | }; |
| | | |
| | | // 查询列表 |
| | | const getStandardList = () => { |
| | | tableLoading.value = true; |
| | | const params = { |
| | | ...searchForm.value, |
| | | current: page.current, |
| | | size: page.size, |
| | | }; |
| | | qualityTestStandardListPage(params) |
| | | .then(res => { |
| | | const records = res?.data?.records || []; |
| | | standardTableData.value = records; |
| | | page.total = res?.data?.total || records.length; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getStandardList(); |
| | | }; |
| | | |
| | | const resetQuery = () => { |
| | | searchForm.value.standardNo = ""; |
| | | searchForm.value.standardName = ""; |
| | | searchForm.value.remark = ""; |
| | | searchForm.value.state = ""; |
| | | searchForm.value.inspectType = ""; |
| | | searchForm.value.processId = ""; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const handlePagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getStandardList(); |
| | | }; |
| | | |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // 批量审核:状态 1=批准,2=撤销 |
| | | const handleBatchAudit = async state => { |
| | | if (!selectedRows.value.length) { |
| | | proxy.$message.warning("请选择数据"); |
| | | return; |
| | | } |
| | | const text = state === 1 ? "批准" : "撤销"; |
| | | const payload = selectedRows.value |
| | | .filter(i => i?.id) |
| | | .map(item => ({ id: item.id, state })); |
| | | |
| | | if (!payload.length) { |
| | | proxy.$message.warning("请选择有效数据"); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | await ElMessageBox.confirm(`确认${text}选中的标准?`, "提示", { |
| | | type: "warning", |
| | | }); |
| | | } catch { |
| | | return; |
| | | } |
| | | await qualityTestStandardAudit(payload); |
| | | proxy.$message.success(`${text}成功`); |
| | | getStandardList(); |
| | | }; |
| | | |
| | | // 表格行点击,加载右侧参数 |
| | | const handleTableRowClick = row => { |
| | | currentStandard.value = row; |
| | | loadDetail(row.id); |
| | | }; |
| | | |
| | | // 左侧行点击,加载右侧参数(保留用于标准编号列的点击) |
| | | const handleStandardRowClick = row => { |
| | | currentStandard.value = row; |
| | | loadDetail(row.id); |
| | | }; |
| | | |
| | | const loadDetail = standardId => { |
| | | if (!standardId) { |
| | | detailTableData.value = []; |
| | | return; |
| | | } |
| | | detailLoading.value = true; |
| | | qualityTestStandardParamList({ testStandardId: standardId }) |
| | | .then(res => { |
| | | detailTableData.value = res?.data || []; |
| | | }) |
| | | .finally(() => { |
| | | detailLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleParamSelectionChange = selection => { |
| | | paramSelectedRows.value = selection; |
| | | }; |
| | | |
| | | const openParamDialog = (type, row) => { |
| | | if (!currentStandard.value?.id) return; |
| | | if (isStandardReadonly.value) { |
| | | proxy.$message.warning("该标准已通过,参数不可编辑"); |
| | | return; |
| | | } |
| | | paramOperationType.value = type; |
| | | if (type === "add") { |
| | | Object.assign(paramForm, { |
| | | id: undefined, |
| | | parameterItem: "", |
| | | unit: "", |
| | | standardValue: "", |
| | | controlValue: "", |
| | | defaultValue: "", |
| | | }); |
| | | } else if (type === "edit" && row) { |
| | | Object.assign(paramForm, row); |
| | | } |
| | | paramDialogVisible.value = true; |
| | | }; |
| | | |
| | | const closeParamDialog = () => { |
| | | paramDialogVisible.value = false; |
| | | paramFormDialogRef.value?.resetFields?.(); |
| | | }; |
| | | |
| | | const submitParamForm = async () => { |
| | | const testStandardId = currentStandard.value?.id; |
| | | if (!testStandardId) return; |
| | | if (isStandardReadonly.value) { |
| | | proxy.$message.warning("该标准已通过,参数不可编辑"); |
| | | return; |
| | | } |
| | | const payload = { ...paramForm, testStandardId }; |
| | | if (paramOperationType.value === "edit") { |
| | | await qualityTestStandardParamUpdate(payload); |
| | | proxy.$message.success("提交成功"); |
| | | } else { |
| | | await qualityTestStandardParamAdd(payload); |
| | | proxy.$message.success("提交成功"); |
| | | } |
| | | closeParamDialog(); |
| | | loadDetail(testStandardId); |
| | | }; |
| | | |
| | | const handleParamDelete = async row => { |
| | | if (!row?.id) return; |
| | | if (isStandardReadonly.value) { |
| | | proxy.$message.warning("该标准已通过,参数不可编辑"); |
| | | return; |
| | | } |
| | | try { |
| | | await ElMessageBox.confirm("确认删除该参数?", "提示", { type: "warning" }); |
| | | } catch { |
| | | return; |
| | | } |
| | | await qualityTestStandardParamDel([row.id]); |
| | | proxy.$message.success("删除成功"); |
| | | loadDetail(currentStandard.value?.id); |
| | | }; |
| | | |
| | | const handleParamBatchDelete = async () => { |
| | | if (isStandardReadonly.value) { |
| | | proxy.$message.warning("该标准已通过,参数不可编辑"); |
| | | return; |
| | | } |
| | | if (!paramSelectedRows.value.length) { |
| | | proxy.$message.warning("请选择数据"); |
| | | return; |
| | | } |
| | | const ids = paramSelectedRows.value.map(i => i.id); |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | "选中的内容将被删除,是否确认删除?", |
| | | "删除提示", |
| | | { type: "warning" } |
| | | ); |
| | | } catch { |
| | | return; |
| | | } |
| | | await qualityTestStandardParamDel(ids); |
| | | proxy.$message.success("删除成功"); |
| | | loadDetail(currentStandard.value?.id); |
| | | }; |
| | | |
| | | // 新增 / 编辑 / 复制 |
| | | const openStandardDialog = (type, row) => { |
| | | standardOperationType.value = type; |
| | | if (type === "add") { |
| | | Object.assign(standardForm.value, { |
| | | id: undefined, |
| | | standardNo: "", |
| | | standardName: "", |
| | | remark: "", |
| | | state: "0", |
| | | inspectType: activeTab.value, |
| | | processId: ["1", "2"].includes(String(activeTab.value)) ? "" : null, |
| | | }); |
| | | } else if (type === "edit" && row) { |
| | | Object.assign(standardForm.value, { |
| | | ...row, |
| | | // 确保 inspectType 和 state 转换为字符串,以匹配 el-select 的 value 类型 |
| | | inspectType: |
| | | row.inspectType !== null && row.inspectType !== undefined |
| | | ? String(row.inspectType) |
| | | : "", |
| | | state: |
| | | row.state !== null && row.state !== undefined ? String(row.state) : "0", |
| | | // 确保 processId 转换为字符串或数字(根据实际需要) |
| | | processId: |
| | | ["1", "2"].includes(String(row.inspectType)) && |
| | | row.processId !== null && |
| | | row.processId !== undefined |
| | | ? row.processId |
| | | : null, |
| | | }); |
| | | } else if (type === "copy" && row) { |
| | | const { id, ...rest } = row; |
| | | Object.assign(standardForm.value, { |
| | | ...rest, |
| | | id: undefined, |
| | | standardNo: "", |
| | | state: "0", |
| | | // 确保 inspectType 转换为字符串 |
| | | inspectType: activeTab.value, |
| | | processId: ["1", "2"].includes(String(activeTab.value)) |
| | | ? rest.processId ?? "" |
| | | : null, |
| | | }); |
| | | } |
| | | standardDialogVisible.value = true; |
| | | }; |
| | | |
| | | const closeStandardDialog = () => { |
| | | standardDialogVisible.value = false; |
| | | standardFormDialogRef.value?.resetFields?.(); |
| | | }; |
| | | |
| | | const submitStandardForm = () => { |
| | | const payload = { ...standardForm.value }; |
| | | payload.inspectType = activeTab.value; |
| | | if (!["1", "2"].includes(String(payload.inspectType))) { |
| | | payload.processId = null; |
| | | } |
| | | const isEdit = standardOperationType.value === "edit"; |
| | | if (isEdit) { |
| | | qualityTestStandardUpdate(payload).then(() => { |
| | | proxy.$message.success("提交成功"); |
| | | standardDialogVisible.value = false; |
| | | getStandardList(); |
| | | }); |
| | | } else { |
| | | qualityTestStandardAdd(payload).then(() => { |
| | | proxy.$message.success("提交成功"); |
| | | standardDialogVisible.value = false; |
| | | getStandardList(); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 删除(单条) |
| | | const handleDelete = row => { |
| | | const ids = [row.id]; |
| | | ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | qualityTestStandardDel(ids) |
| | | .then(() => { |
| | | proxy.$message.success("删除成功"); |
| | | getStandardList(); |
| | | if (currentStandard.value && currentStandard.value.id === row.id) { |
| | | currentStandard.value = null; |
| | | detailTableData.value = []; |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal?.msg("已取消"); |
| | | }); |
| | | }; |
| | | |
| | | // 批量删除 |
| | | const handleBatchDelete = () => { |
| | | if (!selectedRows.value.length) { |
| | | proxy.$message.warning("请选择数据"); |
| | | return; |
| | | } |
| | | const ids = selectedRows.value.map(item => item.id); |
| | | ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | } |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | qualityTestStandardDel(ids) |
| | | .then(() => { |
| | | proxy.$message.success("删除成功"); |
| | | getStandardList(); |
| | | if (currentStandard.value && ids.includes(currentStandard.value.id)) { |
| | | currentStandard.value = null; |
| | | detailTableData.value = []; |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal?.msg("已取消"); |
| | | }); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1 |
| | | getStandardList() |
| | | } |
| | | |
| | | const resetQuery = () => { |
| | | searchForm.value.standardNo = '' |
| | | searchForm.value.standardName = '' |
| | | searchForm.value.remark = '' |
| | | searchForm.value.state = '' |
| | | searchForm.value.inspectType = '' |
| | | searchForm.value.processId = '' |
| | | handleQuery() |
| | | } |
| | | |
| | | const handlePagination = (obj) => { |
| | | page.current = obj.page |
| | | page.size = obj.limit |
| | | getStandardList() |
| | | } |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection |
| | | } |
| | | |
| | | // 批量审核:状态 1=批准,2=撤销 |
| | | const handleBatchAudit = async (state) => { |
| | | if (!selectedRows.value.length) { |
| | | proxy.$message.warning('请选择数据') |
| | | return |
| | | } |
| | | const text = state === 1 ? '批准' : '撤销' |
| | | const payload = selectedRows.value |
| | | .filter(i => i?.id) |
| | | .map((item) => ({ id: item.id, state })) |
| | | |
| | | if (!payload.length) { |
| | | proxy.$message.warning('请选择有效数据') |
| | | return |
| | | } |
| | | |
| | | try { |
| | | await ElMessageBox.confirm(`确认${text}选中的标准?`, '提示', { type: 'warning' }) |
| | | } catch { |
| | | return |
| | | } |
| | | await qualityTestStandardAudit(payload) |
| | | proxy.$message.success(`${text}成功`) |
| | | getStandardList() |
| | | } |
| | | |
| | | // 表格行点击,加载右侧参数 |
| | | const handleTableRowClick = (row) => { |
| | | currentStandard.value = row |
| | | loadDetail(row.id) |
| | | } |
| | | |
| | | // 左侧行点击,加载右侧参数(保留用于标准编号列的点击) |
| | | const handleStandardRowClick = (row) => { |
| | | currentStandard.value = row |
| | | loadDetail(row.id) |
| | | } |
| | | |
| | | const loadDetail = (standardId) => { |
| | | if (!standardId) { |
| | | detailTableData.value = [] |
| | | return |
| | | } |
| | | detailLoading.value = true |
| | | qualityTestStandardParamList({ testStandardId: standardId }).then((res) => { |
| | | detailTableData.value = res?.data || [] |
| | | }) |
| | | .finally(() => { |
| | | detailLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | const handleParamSelectionChange = (selection) => { |
| | | paramSelectedRows.value = selection |
| | | } |
| | | |
| | | const openParamDialog = (type, row) => { |
| | | if (!currentStandard.value?.id) return |
| | | if (isStandardReadonly.value) { |
| | | proxy.$message.warning('该标准已通过,参数不可编辑') |
| | | return |
| | | } |
| | | paramOperationType.value = type |
| | | if (type === 'add') { |
| | | Object.assign(paramForm, { |
| | | id: undefined, |
| | | parameterItem: '', |
| | | unit: '', |
| | | standardValue: '', |
| | | controlValue: '', |
| | | defaultValue: '' |
| | | }) |
| | | } else if (type === 'edit' && row) { |
| | | Object.assign(paramForm, row) |
| | | } |
| | | paramDialogVisible.value = true |
| | | } |
| | | |
| | | const closeParamDialog = () => { |
| | | paramDialogVisible.value = false |
| | | paramFormDialogRef.value?.resetFields?.() |
| | | } |
| | | |
| | | const submitParamForm = async () => { |
| | | const testStandardId = currentStandard.value?.id |
| | | if (!testStandardId) return |
| | | if (isStandardReadonly.value) { |
| | | proxy.$message.warning('该标准已通过,参数不可编辑') |
| | | return |
| | | } |
| | | const payload = { ...paramForm, testStandardId } |
| | | if (paramOperationType.value === 'edit') { |
| | | await qualityTestStandardParamUpdate(payload) |
| | | proxy.$message.success('提交成功') |
| | | } else { |
| | | await qualityTestStandardParamAdd(payload) |
| | | proxy.$message.success('提交成功') |
| | | } |
| | | closeParamDialog() |
| | | loadDetail(testStandardId) |
| | | } |
| | | |
| | | const handleParamDelete = async (row) => { |
| | | if (!row?.id) return |
| | | if (isStandardReadonly.value) { |
| | | proxy.$message.warning('该标准已通过,参数不可编辑') |
| | | return |
| | | } |
| | | try { |
| | | await ElMessageBox.confirm('确认删除该参数?', '提示', { type: 'warning' }) |
| | | } catch { |
| | | return |
| | | } |
| | | await qualityTestStandardParamDel([row.id]) |
| | | proxy.$message.success('删除成功') |
| | | loadDetail(currentStandard.value?.id) |
| | | } |
| | | |
| | | const handleParamBatchDelete = async () => { |
| | | if (isStandardReadonly.value) { |
| | | proxy.$message.warning('该标准已通过,参数不可编辑') |
| | | return |
| | | } |
| | | if (!paramSelectedRows.value.length) { |
| | | proxy.$message.warning('请选择数据') |
| | | return |
| | | } |
| | | const ids = paramSelectedRows.value.map((i) => i.id) |
| | | try { |
| | | await ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除提示', { type: 'warning' }) |
| | | } catch { |
| | | return |
| | | } |
| | | await qualityTestStandardParamDel(ids) |
| | | proxy.$message.success('删除成功') |
| | | loadDetail(currentStandard.value?.id) |
| | | } |
| | | |
| | | // 新增 / 编辑 / 复制 |
| | | const openStandardDialog = (type, row) => { |
| | | standardOperationType.value = type |
| | | if (type === 'add') { |
| | | Object.assign(standardForm.value, { |
| | | id: undefined, |
| | | standardNo: '', |
| | | standardName: '', |
| | | remark: '', |
| | | state: '0', |
| | | inspectType: activeTab.value, |
| | | processId: activeTab.value === '1' ? '' : null |
| | | }) |
| | | } else if (type === 'edit' && row) { |
| | | Object.assign(standardForm.value, { |
| | | ...row, |
| | | // 确保 inspectType 和 state 转换为字符串,以匹配 el-select 的 value 类型 |
| | | inspectType: row.inspectType !== null && row.inspectType !== undefined ? String(row.inspectType) : '', |
| | | state: row.state !== null && row.state !== undefined ? String(row.state) : '0', |
| | | // 确保 processId 转换为字符串或数字(根据实际需要) |
| | | processId: String(row.inspectType) === '1' && row.processId !== null && row.processId !== undefined ? row.processId : null |
| | | }) |
| | | } else if (type === 'copy' && row) { |
| | | const { id, ...rest } = row |
| | | Object.assign(standardForm.value, { |
| | | ...rest, |
| | | id: undefined, |
| | | standardNo: '', |
| | | state: '0', |
| | | // 确保 inspectType 转换为字符串 |
| | | inspectType: activeTab.value, |
| | | processId: activeTab.value === '1' ? (rest.processId ?? '') : null |
| | | }) |
| | | } |
| | | standardDialogVisible.value = true |
| | | } |
| | | |
| | | const closeStandardDialog = () => { |
| | | standardDialogVisible.value = false |
| | | standardFormDialogRef.value?.resetFields?.() |
| | | } |
| | | |
| | | const submitStandardForm = () => { |
| | | const payload = { ...standardForm.value } |
| | | payload.inspectType = activeTab.value |
| | | if (String(payload.inspectType) !== '1') { |
| | | payload.processId = null |
| | | } |
| | | const isEdit = standardOperationType.value === 'edit' |
| | | if (isEdit) { |
| | | qualityTestStandardUpdate(payload).then(() => { |
| | | proxy.$message.success('提交成功') |
| | | standardDialogVisible.value = false |
| | | getStandardList() |
| | | }) |
| | | } else { |
| | | qualityTestStandardAdd(payload).then(() => { |
| | | proxy.$message.success('提交成功') |
| | | standardDialogVisible.value = false |
| | | getStandardList() |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // 删除(单条) |
| | | const handleDelete = (row) => { |
| | | const ids = [row.id] |
| | | ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除提示', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true |
| | | qualityTestStandardDel(ids) |
| | | .then(() => { |
| | | proxy.$message.success('删除成功') |
| | | getStandardList() |
| | | if (currentStandard.value && currentStandard.value.id === row.id) { |
| | | currentStandard.value = null |
| | | detailTableData.value = [] |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false |
| | | }) |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal?.msg('已取消') |
| | | }) |
| | | } |
| | | |
| | | // 批量删除 |
| | | const handleBatchDelete = () => { |
| | | if (!selectedRows.value.length) { |
| | | proxy.$message.warning('请选择数据') |
| | | return |
| | | } |
| | | const ids = selectedRows.value.map((item) => item.id) |
| | | ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除提示', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true |
| | | qualityTestStandardDel(ids) |
| | | .then(() => { |
| | | proxy.$message.success('删除成功') |
| | | getStandardList() |
| | | if (currentStandard.value && ids.includes(currentStandard.value.id)) { |
| | | currentStandard.value = null |
| | | detailTableData.value = [] |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false |
| | | }) |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal?.msg('已取消') |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | searchForm.value.inspectType = activeTab.value |
| | | getProcessList() |
| | | getStandardList() |
| | | }) |
| | | onMounted(() => { |
| | | searchForm.value.inspectType = activeTab.value; |
| | | getProcessList(); |
| | | getStandardList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .metric-maintenance { |
| | | padding: 0; |
| | | min-width: 0; |
| | | } |
| | | .metric-maintenance { |
| | | padding: 0; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .metric-tabs { |
| | | margin: 10px; |
| | | } |
| | | .metric-tabs { |
| | | margin: 10px; |
| | | } |
| | | |
| | | .metric-maintenance-row { |
| | | width: 100%; |
| | | } |
| | | .metric-maintenance-row { |
| | | width: 100%; |
| | | } |
| | | |
| | | .metric-maintenance-row .left-col, |
| | | .metric-maintenance-row .right-col { |
| | | margin-bottom: 16px; |
| | | } |
| | | .metric-maintenance-row .left-col, |
| | | .metric-maintenance-row .right-col { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .left-panel, |
| | | .right-panel { |
| | | min-width: 0; |
| | | background: #ffffff; |
| | | padding: 16px; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | height: 100%; |
| | | min-height: 400px; |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .left-panel, |
| | | .right-panel { |
| | | padding: 12px; |
| | | min-width: 0; |
| | | background: #ffffff; |
| | | padding: 16px; |
| | | box-sizing: border-box; |
| | | overflow: hidden; |
| | | height: 100%; |
| | | min-height: 400px; |
| | | } |
| | | } |
| | | |
| | | .toolbar { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | @media (max-width: 768px) { |
| | | .left-panel, |
| | | .right-panel { |
| | | padding: 12px; |
| | | } |
| | | } |
| | | |
| | | .toolbar-left { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | gap: 4px; |
| | | } |
| | | .toolbar { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .toolbar-right { |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | .toolbar-left { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .search-label { |
| | | margin: 0 4px 0 12px; |
| | | } |
| | | .toolbar-right { |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .search-label:first-of-type { |
| | | margin-left: 0; |
| | | } |
| | | .search-label { |
| | | margin: 0 4px 0 12px; |
| | | } |
| | | |
| | | .right-header { |
| | | display: flex; |
| | | align-items: baseline; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | .search-label:first-of-type { |
| | | margin-left: 0; |
| | | } |
| | | |
| | | .right-header .title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | .right-header { |
| | | display: flex; |
| | | align-items: baseline; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .right-header .desc { |
| | | font-size: 13px; |
| | | color: #666; |
| | | } |
| | | .right-header .title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .right-toolbar { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | .right-header .desc { |
| | | font-size: 13px; |
| | | color: #666; |
| | | } |
| | | |
| | | .link-text { |
| | | color: #409eff; |
| | | cursor: default; |
| | | } |
| | | .right-toolbar { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .clickable-link { |
| | | color: #409eff; |
| | | cursor: pointer; |
| | | } |
| | | .link-text { |
| | | color: #409eff; |
| | | cursor: default; |
| | | } |
| | | |
| | | .clickable-link:hover { |
| | | text-decoration: underline; |
| | | } |
| | | .clickable-link { |
| | | color: #409eff; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | :deep(.row-center td) { |
| | | text-align: center !important; |
| | | } |
| | | .clickable-link:hover { |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | /* el-table 表头/内容统一居中(row-class-name 不作用于表头) */ |
| | | :deep(.center-table .el-table__header-wrapper th .cell) { |
| | | text-align: center !important; |
| | | } |
| | | :deep(.center-table .el-table__body-wrapper td .cell) { |
| | | text-align: center !important; |
| | | } |
| | | :deep(.row-center td) { |
| | | text-align: center !important; |
| | | } |
| | | |
| | | /* PIMTable 表头居中 */ |
| | | :deep(.lims-table .pim-table-header-cell) { |
| | | text-align: center; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | /* el-table 表头/内容统一居中(row-class-name 不作用于表头) */ |
| | | :deep(.center-table .el-table__header-wrapper th .cell) { |
| | | text-align: center !important; |
| | | } |
| | | :deep(.center-table .el-table__body-wrapper td .cell) { |
| | | text-align: center !important; |
| | | } |
| | | |
| | | :deep(.lims-table .pim-table-header-title) { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | /* PIMTable 表头居中 */ |
| | | :deep(.lims-table .pim-table-header-cell) { |
| | | text-align: center; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | :deep(.lims-table .pim-table-header-extra) { |
| | | width: 100%; |
| | | margin-top: 4px; |
| | | } |
| | | :deep(.lims-table .pim-table-header-title) { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | :deep(.lims-table .pim-table-header-extra) { |
| | | width: 100%; |
| | | margin-top: 4px; |
| | | } |
| | | </style> |