|
|
@@ -0,0 +1,370 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import { FormInstance, FormRules } from 'element-plus'
|
|
|
+import { Close } from '@element-plus/icons-vue'
|
|
|
+import * as UserApi from '@/api/system/user'
|
|
|
+import { useUserStore } from '@/store/modules/user'
|
|
|
+import { getStrDictOptions } from '@/utils/dict'
|
|
|
+import * as DeptApi from '@/api/system/dept'
|
|
|
+import { IotRdDailyReportApi } from '@/api/pms/iotrddailyreport'
|
|
|
+
|
|
|
+const deptId = useUserStore().getUser.deptId
|
|
|
+
|
|
|
+interface Props {
|
|
|
+ visible: boolean
|
|
|
+ loadList: () => void
|
|
|
+}
|
|
|
+
|
|
|
+const props = withDefaults(defineProps<Props>(), {
|
|
|
+ visible: false
|
|
|
+})
|
|
|
+
|
|
|
+const emits = defineEmits(['update:visible'])
|
|
|
+
|
|
|
+const NON_PROD_FIELDS = [
|
|
|
+ { key: 'repairTime', label: '设备故障' },
|
|
|
+ { key: 'selfStopTime', label: '设备保养' },
|
|
|
+ { key: 'accidentTime', label: '工程质量' },
|
|
|
+ { key: 'complexityTime', label: '技术受限' },
|
|
|
+ { key: 'rectificationTime', label: '生产组织' },
|
|
|
+ { key: 'waitingStopTime', label: '不可抗力' },
|
|
|
+ { key: 'partyaDesign', label: '甲方设计' },
|
|
|
+ { key: 'partyaPrepare', label: '甲方准备' },
|
|
|
+ { key: 'partyaResource', label: '甲方资源' },
|
|
|
+ { key: 'relocationTime', label: '生产配合' },
|
|
|
+ { key: 'winterBreakTime', label: '待命' },
|
|
|
+ { key: 'otherNptTime', label: '其他非生产时间' }
|
|
|
+]
|
|
|
+
|
|
|
+interface Form {
|
|
|
+ deptId: number
|
|
|
+ location: string
|
|
|
+ responsiblePerson: number
|
|
|
+ timeRange: string[]
|
|
|
+ rdStatus: string
|
|
|
+ dailyFuel: number
|
|
|
+ productionStatus: string
|
|
|
+ nextPlan: string
|
|
|
+ externalRental: string
|
|
|
+ malfunction: string
|
|
|
+ otherNptReason: string
|
|
|
+ [key: (typeof NON_PROD_FIELDS)[number]['key']]: any
|
|
|
+}
|
|
|
+
|
|
|
+const original = {
|
|
|
+ deptId,
|
|
|
+ timeRange: ['08:00', '08:00'],
|
|
|
+ ...NON_PROD_FIELDS.reduce((acc, field) => ({ ...acc, [field.key]: 0 }), {}),
|
|
|
+ winterBreakTime: 24
|
|
|
+}
|
|
|
+
|
|
|
+const formRef = ref<FormInstance>()
|
|
|
+
|
|
|
+const form = ref<Partial<Form>>({ ...original })
|
|
|
+
|
|
|
+function noProductionTimeRule() {
|
|
|
+ return {
|
|
|
+ validator: (_rule: any, _value: any, callback: any) => {
|
|
|
+ let totalTime = 0
|
|
|
+ NON_PROD_FIELDS.forEach((field) => {
|
|
|
+ const val = parseFloat(form.value[field.key])
|
|
|
+ if (!isNaN(val)) {
|
|
|
+ totalTime += val
|
|
|
+ }
|
|
|
+ })
|
|
|
+ const fixedTotal = Number(totalTime.toFixed(2))
|
|
|
+ if (fixedTotal > 24) {
|
|
|
+ callback(new Error(`总时间(${fixedTotal}h)不能超过 24 小时`))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: 'blur'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleRowValidate(key: string) {
|
|
|
+ if (!formRef.value) return
|
|
|
+
|
|
|
+ const propsToValidate = NON_PROD_FIELDS.map((field) => field.key)
|
|
|
+
|
|
|
+ if (key === 'otherNptTime') propsToValidate.push('otherNptReason')
|
|
|
+
|
|
|
+ formRef.value.validateField(propsToValidate)
|
|
|
+}
|
|
|
+
|
|
|
+const rules: FormRules = {
|
|
|
+ timeRange: [
|
|
|
+ { required: true, message: '请选择时间节点', trigger: ['blur', 'change'], type: 'array' }
|
|
|
+ ],
|
|
|
+ location: [{ required: true, message: '请输入施工地点', trigger: ['blur', 'change'] }],
|
|
|
+ responsiblePerson: [
|
|
|
+ { required: true, message: '请选择带班干部', trigger: ['blur', 'change'], type: 'array' }
|
|
|
+ ],
|
|
|
+ rdStatus: [{ required: true, message: '请选择施工状态', trigger: ['blur', 'change'] }],
|
|
|
+ dailyFuel: [{ required: true, message: '请输入当日油耗', trigger: ['blur', 'change'] }],
|
|
|
+ productionStatus: [{ required: true, message: '请输入生产状态', trigger: ['blur', 'change'] }],
|
|
|
+ nextPlan: [{ required: true, message: '请输入下计划', trigger: ['blur', 'change'] }]
|
|
|
+}
|
|
|
+
|
|
|
+interface UserOptions {
|
|
|
+ label: string
|
|
|
+ value: number
|
|
|
+ raw: any
|
|
|
+}
|
|
|
+
|
|
|
+const userOptions = ref<UserOptions[]>([])
|
|
|
+
|
|
|
+const deptName = ref('')
|
|
|
+
|
|
|
+async function loadDept() {
|
|
|
+ const res = await DeptApi.getDept(deptId)
|
|
|
+ deptName.value = res.name
|
|
|
+}
|
|
|
+
|
|
|
+async function loadUserOptions() {
|
|
|
+ const res = await UserApi.selectedDeptsEmployee({ deptIds: [deptId] })
|
|
|
+ userOptions.value = res.map((item: any) => ({
|
|
|
+ label: item.nickname,
|
|
|
+ value: item.id,
|
|
|
+ raw: item
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
+const loading = ref(false)
|
|
|
+
|
|
|
+async function load() {
|
|
|
+ loading.value = true
|
|
|
+ await loadDept()
|
|
|
+ await loadUserOptions()
|
|
|
+ loading.value = false
|
|
|
+}
|
|
|
+
|
|
|
+const rdStatusOptions = getStrDictOptions(DICT_TYPE.PMS_PROJECT_RD_STATUS)
|
|
|
+
|
|
|
+function handleOpenForm() {
|
|
|
+ form.value = { ...original }
|
|
|
+ load()
|
|
|
+ emits('update:visible', true)
|
|
|
+}
|
|
|
+
|
|
|
+function handleCloseForm() {
|
|
|
+ emits('update:visible', false)
|
|
|
+}
|
|
|
+
|
|
|
+defineExpose({ handleOpenForm })
|
|
|
+
|
|
|
+const formLoading = ref(false)
|
|
|
+
|
|
|
+const message = useMessage()
|
|
|
+
|
|
|
+async function submitForm() {
|
|
|
+ if (!formRef.value) return
|
|
|
+
|
|
|
+ try {
|
|
|
+ formLoading.value = true
|
|
|
+ await formRef.value.validate()
|
|
|
+ const { timeRange, ...rest } = form.value
|
|
|
+ const data = { ...rest, startTime: timeRange?.[0], endTime: timeRange?.[1] }
|
|
|
+
|
|
|
+ await IotRdDailyReportApi.createIotRdDailyReport(data)
|
|
|
+
|
|
|
+ message.success('新增成功')
|
|
|
+ handleCloseForm()
|
|
|
+ props.loadList()
|
|
|
+ } catch (error) {
|
|
|
+ console.log('error :>> ', error)
|
|
|
+ } finally {
|
|
|
+ formLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <el-drawer
|
|
|
+ :model-value="visible"
|
|
|
+ @update:model-value="emits('update:visible', $event)"
|
|
|
+ header-class="mb-0!"
|
|
|
+ :with-header="false"
|
|
|
+ size="50%"
|
|
|
+ :close-on-press-escape="false"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ >
|
|
|
+ <div class="size-full flex flex-col gap-4">
|
|
|
+ <div class="flex justify-between items-center">
|
|
|
+ <div class="text-xl font-bold text-gray-900 leading-tight"> 新建日报 </div>
|
|
|
+ <el-button link @click="handleCloseForm">
|
|
|
+ <el-icon size="24"><Close /></el-icon>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <el-form
|
|
|
+ ref="formRef"
|
|
|
+ size="default"
|
|
|
+ label-position="top"
|
|
|
+ :model="form"
|
|
|
+ require-asterisk-position="right"
|
|
|
+ class="flex flex-col"
|
|
|
+ :rules="rules"
|
|
|
+ :loading="loading"
|
|
|
+ >
|
|
|
+ <div class="grid grid-cols-2 gap-x-8">
|
|
|
+ <el-form-item label="施工队伍">{{ deptName }}</el-form-item>
|
|
|
+ <el-form-item label="施工地点" prop="location">
|
|
|
+ <el-input v-model="form.location" clearable placeholder="请输入施工地点" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="带班干部" prop="responsiblePerson">
|
|
|
+ <el-select
|
|
|
+ v-model="form.responsiblePerson"
|
|
|
+ multiple
|
|
|
+ :options="userOptions"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ collapse-tags
|
|
|
+ collapse-tags-tooltip
|
|
|
+ :max-collapse-tags="5"
|
|
|
+ tag-type="primary"
|
|
|
+ placeholder="请选择带班干部"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="时间节点" prop="timeRange">
|
|
|
+ <el-time-picker
|
|
|
+ v-model="form.timeRange"
|
|
|
+ is-range
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始时间"
|
|
|
+ end-placeholder="结束时间"
|
|
|
+ placeholder="选择时间范围"
|
|
|
+ clearable
|
|
|
+ format="HH:mm"
|
|
|
+ value-format="HH:mm"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="施工状态" prop="rdStatus">
|
|
|
+ <el-select
|
|
|
+ v-model="form.rdStatus"
|
|
|
+ :options="rdStatusOptions"
|
|
|
+ placeholder="请选择"
|
|
|
+ class="w-full"
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="当日油耗(L)" prop="dailyFuel">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.dailyFuel"
|
|
|
+ :min="0"
|
|
|
+ :controls="false"
|
|
|
+ align="left"
|
|
|
+ class="w-full!"
|
|
|
+ placeholder="请输入当日油耗"
|
|
|
+ >
|
|
|
+ <template #suffix>升(L)</template>
|
|
|
+ </el-input-number>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="当日生产动态" prop="productionStatus">
|
|
|
+ <el-input
|
|
|
+ v-model="form.productionStatus"
|
|
|
+ type="textarea"
|
|
|
+ :autosize="{ minRows: 2 }"
|
|
|
+ resize="none"
|
|
|
+ show-word-limit
|
|
|
+ :maxlength="1000"
|
|
|
+ placeholder="请输入外组设备"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="下步工作计划" prop="nextPlan">
|
|
|
+ <el-input
|
|
|
+ v-model="form.nextPlan"
|
|
|
+ type="textarea"
|
|
|
+ :autosize="{ minRows: 2 }"
|
|
|
+ resize="none"
|
|
|
+ show-word-limit
|
|
|
+ :maxlength="1000"
|
|
|
+ placeholder="请输入下步工作计划"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="外租设备" prop="externalRental">
|
|
|
+ <el-input
|
|
|
+ v-model="form.externalRental"
|
|
|
+ type="textarea"
|
|
|
+ :autosize="{ minRows: 2 }"
|
|
|
+ resize="none"
|
|
|
+ show-word-limit
|
|
|
+ :maxlength="1000"
|
|
|
+ placeholder="请输入外组设备"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="故障情况" prop="malfunction">
|
|
|
+ <el-input
|
|
|
+ v-model="form.malfunction"
|
|
|
+ type="textarea"
|
|
|
+ :autosize="{ minRows: 2 }"
|
|
|
+ show-word-limit
|
|
|
+ resize="none"
|
|
|
+ :maxlength="1000"
|
|
|
+ placeholder="请输入故障情况"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <el-divider class="m-0! mb-3! border-2! border-[var(--el-color-primary)]!" />
|
|
|
+ <h3 class="text-lg font-bold text-gray-800 mb-6 flex items-center gap-2">
|
|
|
+ <div class="w-1 h-4 bg-blue-600 rounded-full"></div>
|
|
|
+ 非生产时间
|
|
|
+ </h3>
|
|
|
+ <div class="grid grid-cols-4 gap-x-8">
|
|
|
+ <el-form-item
|
|
|
+ v-for="field in NON_PROD_FIELDS"
|
|
|
+ :key="field.key"
|
|
|
+ :label="field.label"
|
|
|
+ :prop="field.key"
|
|
|
+ :rules="noProductionTimeRule()"
|
|
|
+ >
|
|
|
+ <el-input-number
|
|
|
+ v-model="form[field.key]"
|
|
|
+ :min="0"
|
|
|
+ :max="24"
|
|
|
+ :controls="false"
|
|
|
+ class="w-full!"
|
|
|
+ align="left"
|
|
|
+ @blur="handleRowValidate(field.key)"
|
|
|
+ >
|
|
|
+ <template #suffix>小时(H)</template>
|
|
|
+ </el-input-number>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item
|
|
|
+ class="col-span-4"
|
|
|
+ label="其他非生产原因"
|
|
|
+ prop="otherNptReason"
|
|
|
+ :rules="{
|
|
|
+ required: form.otherNptTime > 0,
|
|
|
+ message: '请填写原因',
|
|
|
+ trigger: ['blur', 'change']
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="form.otherNptReason"
|
|
|
+ type="textarea"
|
|
|
+ :autosize="{ minRows: 2 }"
|
|
|
+ resize="none"
|
|
|
+ show-word-limit
|
|
|
+ :maxlength="1000"
|
|
|
+ placeholder="当'其他非生产时间'大于0时必填"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <el-button size="default" type="primary" @click="submitForm" :loading="formLoading">
|
|
|
+ 确 定
|
|
|
+ </el-button>
|
|
|
+ <el-button size="default" @click="emits('update:visible', false)">取 消</el-button>
|
|
|
+ </template>
|
|
|
+ </el-drawer>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+:deep(.el-form-item__label) {
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+</style>
|