| | |
| | | <template> |
| | | <view class="repair-add"> |
| | | <!-- 使用通用页面头部组件 --> |
| | | <PageHeader :title="operationType === 'edit' ? '编辑报修' : '新增报修'" @back="goBack" /> |
| | | |
| | | <!-- 表单内容 --> |
| | | <u-form @submit="sendForm" ref="formRef" :rules="formRules" :model="form" label-width="110"> |
| | | <!-- 基本信息 --> |
| | | <u-cell-group title="基本信息"> |
| | | <u-form-item label="设备名称" prop="deviceLedgerId" required border-bottom> |
| | | <u-input |
| | | v-model="deviceNameText" |
| | | placeholder="请选择设备名称" |
| | | @click="showDevicePicker" |
| | | clearable |
| | | /> |
| | | <template #right> |
| | | <u-icon name="scan" @click="startScan" class="scan-icon" /> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="规格型号" prop="deviceModel" border-bottom> |
| | | <u-input |
| | | v-model="form.deviceModel" |
| | | placeholder="请输入规格型号" |
| | | clearable |
| | | /> |
| | | </u-form-item> |
| | | <u-form-item label="报修日期" prop="repairTime" required border-bottom> |
| | | <u-input |
| | | v-model="form.repairTime" |
| | | placeholder="请选择报修日期" |
| | | readonly |
| | | @click="showDatePicker" |
| | | clearable |
| | | /> |
| | | <template #right> |
| | | <u-icon name="arrow-right" @click="showDatePicker"></u-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="报修人" prop="repairName" required border-bottom> |
| | | <u-input |
| | | v-model="form.repairName" |
| | | placeholder="请输入报修人" |
| | | clearable |
| | | /> |
| | | </u-form-item> |
| | | <u-form-item label="故障现象" prop="remark" required border-bottom> |
| | | <u-textarea |
| | | v-model="form.remark" |
| | | rows="3" |
| | | placeholder="请输入故障现象" |
| | | clearable |
| | | count |
| | | maxlength="200" |
| | | /> |
| | | </u-form-item> |
| | | </u-cell-group> |
| | | |
| | | <!-- 提交按钮 --> |
| | | <view class="footer-btns"> |
| | | <u-button class="cancel-btn" @click="goBack">取消</u-button> |
| | | <u-button class="save-btn" type="primary" @click="sendForm" :loading="loading">保存</u-button> |
| | | </view> |
| | | </u-form> |
| | | |
| | | <!-- 设备选择器 --> |
| | | <up-action-sheet |
| | | :show="showDevice" |
| | | :actions="deviceActionList" |
| | | title="选择设备名称" |
| | | @select="onDeviceSelect" |
| | | @close="showDevice = false" |
| | | /> |
| | | |
| | | <!-- 日期选择器 --> |
| | | <up-datetime-picker |
| | | :show="showDate" |
| | | v-model="pickerDateValue" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | mode="date" |
| | | /> |
| | | </view> |
| | | <view class="repair-add"> |
| | | <!-- 使用通用页面头部组件 --> |
| | | <PageHeader :title="operationType === 'edit' ? '编辑报修' : '新增报修'" |
| | | @back="goBack" /> |
| | | <!-- 表单内容 --> |
| | | <u-form @submit="sendForm" |
| | | ref="formRef" |
| | | :rules="formRules" |
| | | :model="form" |
| | | label-width="110"> |
| | | <!-- 基本信息 --> |
| | | <u-cell-group title="基本信息"> |
| | | <u-form-item label="设备名称" |
| | | prop="deviceLedgerId" |
| | | required |
| | | border-bottom |
| | | class="device-name-form-item"> |
| | | <view class="device-picker-wrap"> |
| | | <picker mode="selector" |
| | | class="device-picker-full" |
| | | :range="deviceOptions" |
| | | range-key="deviceName" |
| | | :value="deviceIndex" |
| | | @change="onDevicePickerChange"> |
| | | <view class="picker-input-row"> |
| | | <text class="picker-input-text" |
| | | :class="{ placeholder: !deviceNameText }">{{ deviceNameText || "请选择设备名称" }}</text> |
| | | <view class="picker-input-arrow"><u-icon name="arrow-right" /></view> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | </u-form-item> |
| | | <u-form-item label="规格型号" |
| | | prop="deviceModel" |
| | | border-bottom> |
| | | <u-input v-model="form.deviceModel" |
| | | placeholder="请输入规格型号" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item label="报修日期" |
| | | prop="repairTime" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.repairTime" |
| | | placeholder="请选择报修日期" |
| | | readonly |
| | | @click="showDatePicker" |
| | | clearable /> |
| | | <template #right> |
| | | <u-icon name="arrow-right" |
| | | @click="showDatePicker"></u-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="报修状态" |
| | | prop="status" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="repairStatusText" |
| | | placeholder="请选择报修状态" |
| | | readonly |
| | | @click="openRepairStatusPicker" |
| | | clearable /> |
| | | <template #right> |
| | | <u-icon name="arrow-right" |
| | | @click="openRepairStatusPicker"></u-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="报修人" |
| | | prop="repairName" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.repairName" |
| | | placeholder="请输入报修人" |
| | | clearable /> |
| | | </u-form-item> |
| | | <u-form-item label="故障现象" |
| | | prop="remark" |
| | | required |
| | | border-bottom> |
| | | <u-textarea v-model="form.remark" |
| | | rows="3" |
| | | placeholder="请输入故障现象" |
| | | clearable |
| | | count |
| | | maxlength="200" /> |
| | | </u-form-item> |
| | | <view class="simple-upload-area"> |
| | | <view class="upload-buttons"> |
| | | <u-button type="primary" |
| | | @click="chooseRepairMedia" |
| | | :loading="uploading" |
| | | :disabled="repairFileList.length >= uploadConfig.limit" |
| | | :customStyle="{ marginRight: '10px', flex: 1 }"> |
| | | <u-icon name="camera" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px;"></u-icon> |
| | | {{ uploading ? "上传中..." : "拍照" }} |
| | | </u-button> |
| | | </view> |
| | | <view v-if="uploading" |
| | | class="upload-progress"> |
| | | <u-line-progress :percentage="uploadProgress" |
| | | :showText="true" |
| | | activeColor="#409eff"></u-line-progress> |
| | | </view> |
| | | <view v-if="repairFileList.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in repairFileList" |
| | | :key="index" |
| | | class="file-item"> |
| | | <view class="file-preview-container"> |
| | | <view class="delete-btn" |
| | | @click="removeRepairFile(index)"> |
| | | <u-icon name="close" |
| | | size="12" |
| | | color="#fff"></u-icon> |
| | | </view> |
| | | <image :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | class="file-preview" |
| | | mode="aspectFill" /> |
| | | </view> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.bucketFilename || file.name || "图片" }}</text> |
| | | <text class="file-size">{{ formatFileSize(file.size) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="repairFileList.length === 0" |
| | | class="empty-state"> |
| | | <text>请选择要上传的现场照片</text> |
| | | </view> |
| | | </view> |
| | | </u-cell-group> |
| | | <!-- 提交按钮 --> |
| | | <view class="footer-btns"> |
| | | <u-button class="cancel-btn" |
| | | @click="goBack">取消</u-button> |
| | | <u-button class="save-btn" |
| | | type="primary" |
| | | @click="sendForm" |
| | | :loading="loading">保存</u-button> |
| | | </view> |
| | | </u-form> |
| | | <!-- 日期选择器 --> |
| | | <up-datetime-picker :show="showDate" |
| | | v-model="pickerDateValue" |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | mode="date" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, onUnmounted } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app'; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import { getDeviceLedger } from '@/api/equipmentManagement/ledger'; |
| | | import { addRepair, editRepair, getRepairById } from '@/api/equipmentManagement/repair'; |
| | | import dayjs from "dayjs"; |
| | | import { formatDateToYMD } from '@/utils/ruoyi'; |
| | | const showToast = (message) => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: 'none' |
| | | }) |
| | | } |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | import { |
| | | addRepair, |
| | | editRepair, |
| | | getRepairById, |
| | | } from "@/api/equipmentManagement/repair"; |
| | | import config from "@/config"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import dayjs from "dayjs"; |
| | | import { formatDateToYMD } from "@/utils/ruoyi"; |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | defineOptions({ |
| | | name: "设备报修表单", |
| | | }); |
| | | defineOptions({ |
| | | name: "设备报修表单", |
| | | }); |
| | | |
| | | // 表单引用 |
| | | const formRef = ref(null); |
| | | const operationType = ref('add'); |
| | | const loading = ref(false); |
| | | const showDevice = ref(false); |
| | | const showDate = ref(false); |
| | | const pickerDateValue = ref(Date.now()); |
| | | // 表单引用 |
| | | const formRef = ref(null); |
| | | const operationType = ref("add"); |
| | | const loading = ref(false); |
| | | const showDate = ref(false); |
| | | const pickerDateValue = ref(Date.now()); |
| | | |
| | | // 设备选项 |
| | | const deviceOptions = ref([]); |
| | | const deviceNameText = ref(''); |
| | | const deviceActionList = computed(() => { |
| | | return deviceOptions.value.map(item => ({ |
| | | name: item.deviceName, |
| | | value: item.id |
| | | })); |
| | | }); |
| | | // 上传配置(与巡检一致) |
| | | const uploadConfig = { |
| | | action: "/file/upload", |
| | | limit: 10, |
| | | }; |
| | | const uploadFileUrl = computed(() => config.baseUrl + uploadConfig.action); |
| | | const repairFileList = ref([]); |
| | | const uploading = ref(false); |
| | | const uploadProgress = ref(0); |
| | | |
| | | // 扫码相关状态 |
| | | const isScanning = ref(false); |
| | | const scanTimer = ref(null); |
| | | // 设备选项 |
| | | const deviceOptions = ref([]); |
| | | const deviceNameText = ref(""); |
| | | const deviceIndex = computed(() => { |
| | | const id = form.value.deviceLedgerId; |
| | | if (id == null || !deviceOptions.value.length) return 0; |
| | | const idx = deviceOptions.value.findIndex(item => item.id === id || item.id == id); |
| | | return idx >= 0 ? idx : 0; |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const formRules = { |
| | | deviceLedgerId: [{ required: true, trigger: "change", message: "请选择设备名称" }], |
| | | repairTime: [{ required: true, trigger: "change", message: "请选择报修日期" }], |
| | | repairName: [{ required: true, trigger: "blur", message: "请输入报修人" }], |
| | | remark: [{ required: true, trigger: "blur", message: "请输入故障现象" }], |
| | | }; |
| | | // 表单验证规则 |
| | | const formRules = { |
| | | deviceLedgerId: [ |
| | | { required: true, trigger: "change", message: "请选择设备名称" }, |
| | | ], |
| | | repairTime: [ |
| | | { required: true, trigger: "change", message: "请选择报修日期" }, |
| | | ], |
| | | status: [ |
| | | { required: true, trigger: "change", message: "请选择报修状态" }, |
| | | ], |
| | | repairName: [{ required: true, trigger: "blur", message: "请输入报修人" }], |
| | | remark: [{ required: true, trigger: "blur", message: "请输入故障现象" }], |
| | | }; |
| | | |
| | | // 使用 ref 声明表单数据 |
| | | const form = ref({ |
| | | deviceLedgerId: undefined, // 设备ID |
| | | deviceModel: undefined, // 规格型号 |
| | | repairTime: dayjs().format("YYYY-MM-DD"), // 报修日期 |
| | | repairName: undefined, // 报修人 |
| | | remark: undefined, // 故障现象 |
| | | }); |
| | | // 使用 ref 声明表单数据 |
| | | const form = ref({ |
| | | deviceLedgerId: undefined, // 设备ID |
| | | deviceModel: undefined, // 规格型号 |
| | | repairTime: dayjs().format("YYYY-MM-DD"), // 报修日期 |
| | | status: undefined, // 报修状态 |
| | | repairName: undefined, // 报修人 |
| | | remark: undefined, // 故障现象 |
| | | }); |
| | | |
| | | // 加载设备列表 |
| | | const loadDeviceName = async () => { |
| | | try { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data || []; |
| | | } catch (e) { |
| | | showToast('获取设备列表失败'); |
| | | } |
| | | }; |
| | | // 报修状态选项 |
| | | const repairStatusOptions = ref([ |
| | | { name: "待维修", value: "0" }, |
| | | { name: "完结", value: "1" }, |
| | | { name: "失败", value: "2" }, |
| | | ]); |
| | | const repairStatusText = ref(""); |
| | | |
| | | // 设置设备规格型号 |
| | | const setDeviceModel = (id) => { |
| | | const option = deviceOptions.value.find((item) => item.id === id); |
| | | if (option) { |
| | | form.value.deviceModel = option.deviceModel; |
| | | deviceNameText.value = option.deviceName; |
| | | } |
| | | }; |
| | | // 打开报修状态选择器 |
| | | const openRepairStatusPicker = () => { |
| | | uni.showActionSheet({ |
| | | itemList: repairStatusOptions.value.map(item => item.name), |
| | | success: res => { |
| | | form.value.status = repairStatusOptions.value[res.tapIndex].value; |
| | | repairStatusText.value = repairStatusOptions.value[res.tapIndex].name; |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // 加载表单数据(编辑模式) |
| | | const loadForm = async (id) => { |
| | | if (id) { |
| | | operationType.value = 'edit'; |
| | | try { |
| | | const { code, data } = await getRepairById(id); |
| | | if (code == 200) { |
| | | form.value.deviceLedgerId = data.deviceLedgerId; |
| | | form.value.deviceModel = data.deviceModel; |
| | | form.value.repairTime = data.repairTime; |
| | | form.value.repairName = data.repairName; |
| | | form.value.remark = data.remark; |
| | | // 设置设备名称显示 |
| | | const device = deviceOptions.value.find(item => item.id === data.deviceLedgerId); |
| | | if (device) { |
| | | deviceNameText.value = device.deviceName; |
| | | } |
| | | } |
| | | } catch (e) { |
| | | showToast('获取详情失败'); |
| | | } |
| | | } else { |
| | | // 新增模式 |
| | | operationType.value = 'add'; |
| | | } |
| | | }; |
| | | // 加载设备列表 |
| | | const loadDeviceName = async () => { |
| | | try { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data || []; |
| | | } catch (e) { |
| | | showToast("获取设备列表失败"); |
| | | } |
| | | }; |
| | | |
| | | // 清除表单校验状态 |
| | | const clearValidate = () => { |
| | | formRef.value?.clearValidate(); |
| | | }; |
| | | // 设置设备规格型号 |
| | | const setDeviceModel = id => { |
| | | const option = deviceOptions.value.find(item => item.id === id); |
| | | if (option) { |
| | | form.value.deviceModel = option.deviceModel; |
| | | deviceNameText.value = option.deviceName; |
| | | } |
| | | }; |
| | | |
| | | // 重置表单数据和校验状态 |
| | | const resetForm = () => { |
| | | form.value = { |
| | | deviceLedgerId: undefined, |
| | | deviceModel: undefined, |
| | | repairTime: dayjs().format("YYYY-MM-DD"), |
| | | repairName: undefined, |
| | | remark: undefined, |
| | | }; |
| | | deviceNameText.value = ''; |
| | | }; |
| | | // 加载表单数据(编辑模式) |
| | | const loadForm = async id => { |
| | | if (id) { |
| | | operationType.value = "edit"; |
| | | try { |
| | | const { code, data } = await getRepairById(id); |
| | | if (code == 200) { |
| | | form.value.deviceLedgerId = data.deviceLedgerId; |
| | | form.value.deviceModel = data.deviceModel; |
| | | form.value.repairTime = dayjs(data.repairTime).format("YYYY-MM-DD"); |
| | | form.value.status = data.status; |
| | | form.value.repairName = data.repairName; |
| | | form.value.remark = data.remark; |
| | | repairStatusText.value = |
| | | repairStatusOptions.value.find(item => item.value == data.status) |
| | | ?.name || ""; |
| | | // 设置设备名称显示 |
| | | const device = deviceOptions.value.find( |
| | | item => item.id === data.deviceLedgerId |
| | | ); |
| | | if (device) { |
| | | deviceNameText.value = device.deviceName; |
| | | } |
| | | // 回显附件列表(后端返回 fileList 时) |
| | | const list = data.fileList || data.commonFileList || []; |
| | | repairFileList.value = (Array.isArray(list) ? list : []).map(f => ({ |
| | | url: f.url || f.downloadUrl, |
| | | name: f.bucketFilename || f.originalFilename || f.name, |
| | | bucketFilename: f.bucketFilename || f.originalFilename || f.name, |
| | | size: f.size || f.byteSize, |
| | | uploadResponse: f, |
| | | })); |
| | | } |
| | | } catch (e) { |
| | | showToast("获取详情失败"); |
| | | } |
| | | } else { |
| | | // 新增模式 |
| | | operationType.value = "add"; |
| | | } |
| | | }; |
| | | |
| | | // 扫描二维码功能 |
| | | const startScan = () => { |
| | | if (isScanning.value) { |
| | | showToast('正在扫描中,请稍候...'); |
| | | return; |
| | | } |
| | | |
| | | // 调用uni-app的扫码API |
| | | uni.scanCode({ |
| | | scanType: ['qrCode', 'barCode'], |
| | | success: (res) => { |
| | | handleScanResult(res.result); |
| | | }, |
| | | fail: (err) => { |
| | | console.error('扫码失败:', err); |
| | | showToast('扫码失败,请重试'); |
| | | } |
| | | }); |
| | | }; |
| | | // 下拉框选择设备 |
| | | const onDevicePickerChange = e => { |
| | | const index = Number(e.detail?.value ?? 0); |
| | | const item = deviceOptions.value[index]; |
| | | if (item) { |
| | | form.value.deviceLedgerId = item.id; |
| | | setDeviceModel(item.id); |
| | | } |
| | | }; |
| | | |
| | | // 处理扫码结果 |
| | | const handleScanResult = (scanResult) => { |
| | | if (!scanResult) { |
| | | showToast('扫码结果为空'); |
| | | return; |
| | | } |
| | | |
| | | isScanning.value = true; |
| | | showToast('扫码成功,3秒后自动填充设备信息'); |
| | | |
| | | // 3秒后处理扫码结果 |
| | | scanTimer.value = setTimeout(() => { |
| | | processScanResult(scanResult); |
| | | isScanning.value = false; |
| | | }, 3000); |
| | | }; |
| | | // 格式化文件大小 |
| | | const formatFileSize = size => { |
| | | if (!size) return ""; |
| | | if (size < 1024) return size + "B"; |
| | | if (size < 1024 * 1024) return (size / 1024).toFixed(1) + "KB"; |
| | | return (size / (1024 * 1024)).toFixed(1) + "MB"; |
| | | }; |
| | | |
| | | // 处理扫码结果并匹配设备 |
| | | const processScanResult = (scanResult) => { |
| | | // 在设备列表中查找匹配的设备 |
| | | // 假设二维码内容是设备名称或设备编号 |
| | | const matchedDevice = deviceOptions.value.find(device => |
| | | device.deviceName === scanResult || |
| | | device.deviceCode === scanResult || |
| | | device.id.toString() === scanResult |
| | | ); |
| | | |
| | | if (matchedDevice) { |
| | | // 找到匹配的设备,自动填充 |
| | | form.value.deviceLedgerId = matchedDevice.id; |
| | | deviceNameText.value = matchedDevice.deviceName; |
| | | form.value.deviceModel = matchedDevice.deviceModel; |
| | | showToast('设备信息已自动填充'); |
| | | } else { |
| | | // 未找到匹配的设备 |
| | | showToast('未找到匹配的设备,请手动选择'); |
| | | } |
| | | }; |
| | | // 拍照选择(与巡检一致) |
| | | const chooseRepairMedia = () => { |
| | | if (repairFileList.value.length >= uploadConfig.limit) { |
| | | showToast(`最多只能选择${uploadConfig.limit}个文件`); |
| | | return; |
| | | } |
| | | const remaining = uploadConfig.limit - repairFileList.value.length; |
| | | if (typeof uni.chooseMedia === "function") { |
| | | uni.chooseMedia({ |
| | | count: Math.min(remaining, 9), |
| | | mediaType: ["image"], |
| | | sizeType: ["compressed", "original"], |
| | | sourceType: ["camera", "album"], |
| | | success: res => { |
| | | const files = res?.tempFiles || []; |
| | | files.forEach((tf, idx) => { |
| | | const filePath = tf.tempFilePath || tf.path || ""; |
| | | const file = { |
| | | tempFilePath: filePath, |
| | | path: filePath, |
| | | type: "image", |
| | | name: `repair_${Date.now()}_${idx}.jpg`, |
| | | size: tf.size || 0, |
| | | uid: Date.now() + Math.random() + idx, |
| | | }; |
| | | uploadRepairFile(file); |
| | | }); |
| | | }, |
| | | fail: () => showToast("选择图片失败"), |
| | | }); |
| | | } else { |
| | | uni.chooseImage({ |
| | | count: remaining, |
| | | sizeType: ["compressed", "original"], |
| | | sourceType: ["camera", "album"], |
| | | success: res => { |
| | | (res.tempFilePaths || []).forEach((path, idx) => { |
| | | uploadRepairFile({ |
| | | tempFilePath: path, |
| | | path: path, |
| | | type: "image", |
| | | name: `repair_${Date.now()}_${idx}.jpg`, |
| | | size: 0, |
| | | uid: Date.now() + Math.random(), |
| | | }); |
| | | }); |
| | | }, |
| | | fail: () => showToast("选择图片失败"), |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 显示设备选择器 |
| | | const showDevicePicker = () => { |
| | | showDevice.value = true; |
| | | }; |
| | | const uploadRepairFile = file => { |
| | | const filePath = file.tempFilePath || file.path; |
| | | if (!filePath) return; |
| | | uploading.value = true; |
| | | uploadProgress.value = 0; |
| | | const token = getToken(); |
| | | if (!token) { |
| | | showToast("请先登录"); |
| | | uploading.value = false; |
| | | return; |
| | | } |
| | | const uploadTask = uni.uploadFile({ |
| | | url: uploadFileUrl.value, |
| | | filePath, |
| | | name: "file", |
| | | header: { Authorization: "Bearer " + token }, |
| | | success: res => { |
| | | try { |
| | | if (res.statusCode === 200) { |
| | | const response = typeof res.data === "string" ? JSON.parse(res.data) : res.data; |
| | | const d = response?.data !== undefined ? response.data : response; |
| | | if (response && (response.code === 200 || response.code === 0) && d) { |
| | | const fileData = { |
| | | ...file, |
| | | id: d.id, |
| | | tempId: d.tempId ?? d.tempFileId ?? d.id, |
| | | url: d.url || d.downloadUrl || filePath, |
| | | bucketFilename: d.bucketFilename || d.originalFilename || file.name, |
| | | downloadUrl: d.downloadUrl || d.url, |
| | | size: d.size ?? d.byteSize ?? file.size, |
| | | uploadResponse: response, |
| | | }; |
| | | repairFileList.value.push(fileData); |
| | | } else { |
| | | showToast(response?.msg || "上传失败"); |
| | | } |
| | | } else { |
| | | showToast("上传失败"); |
| | | } |
| | | } catch (e) { |
| | | showToast("上传失败"); |
| | | } |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | }, |
| | | fail: () => { |
| | | showToast("上传失败"); |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | }, |
| | | }); |
| | | if (uploadTask && uploadTask.onProgressUpdate) { |
| | | uploadTask.onProgressUpdate(e => { |
| | | uploadProgress.value = e.progress; |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 确认设备选择 |
| | | const onDeviceSelect = (e) => { |
| | | form.value.deviceLedgerId = e.value; |
| | | setDeviceModel(e.value); |
| | | showDevice.value = false; |
| | | }; |
| | | const removeRepairFile = index => { |
| | | repairFileList.value.splice(index, 1); |
| | | }; |
| | | |
| | | // 显示日期选择器 |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | // 显示日期选择器 |
| | | const showDatePicker = () => { |
| | | showDate.value = true; |
| | | }; |
| | | |
| | | // 确认日期选择 |
| | | const onDateConfirm = (e) => { |
| | | form.value.repairTime = formatDateToYMD(e.value); |
| | | showDate.value = false; |
| | | }; |
| | | // 确认日期选择 |
| | | const onDateConfirm = e => { |
| | | form.value.repairTime = formatDateToYMD(e.value); |
| | | pickerDateValue.value = dayjs(e.value).format("YYYY-MM-DD"); |
| | | showDate.value = false; |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页面显示时获取参数 |
| | | getPageParams(); |
| | | }); |
| | | onShow(() => { |
| | | // 页面显示时获取参数 |
| | | getPageParams(); |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | // 页面加载时获取设备列表和参数 |
| | | loadDeviceName(); |
| | | getPageParams(); |
| | | }); |
| | | onMounted(() => { |
| | | // 页面加载时获取设备列表和参数 |
| | | loadDeviceName(); |
| | | getPageParams(); |
| | | }); |
| | | |
| | | // 组件卸载时清理定时器 |
| | | onUnmounted(() => { |
| | | if (scanTimer.value) { |
| | | clearTimeout(scanTimer.value); |
| | | } |
| | | }); |
| | | // 组件卸载时清理定时器 |
| | | |
| | | // 提交表单 |
| | | const sendForm = async () => { |
| | | try { |
| | | // 手动验证表单 |
| | | let isValid = true; |
| | | let errorMessage = ''; |
| | | if (!form.value.deviceLedgerId) { |
| | | isValid = false; |
| | | errorMessage = '请选择设备名称'; |
| | | } else if (!form.value.repairTime || form.value.repairTime.trim() === '') { |
| | | isValid = false; |
| | | errorMessage = '请选择报修日期'; |
| | | } else if (!form.value.repairName || form.value.repairName.trim() === '') { |
| | | isValid = false; |
| | | errorMessage = '请输入报修人'; |
| | | } else if (!form.value.remark || form.value.remark.trim() === '') { |
| | | isValid = false; |
| | | errorMessage = '请输入故障现象'; |
| | | } |
| | | // 提交表单 |
| | | const sendForm = async () => { |
| | | try { |
| | | // 手动验证表单 |
| | | let isValid = true; |
| | | let errorMessage = ""; |
| | | if (!form.value.deviceLedgerId) { |
| | | isValid = false; |
| | | errorMessage = "请选择设备名称"; |
| | | } else if (!form.value.repairTime || form.value.repairTime.trim() === "") { |
| | | isValid = false; |
| | | errorMessage = "请选择报修日期"; |
| | | } else if (form.value.status === undefined || form.value.status === null || form.value.status === "") { |
| | | isValid = false; |
| | | errorMessage = "请选择报修状态"; |
| | | } else if (!form.value.repairName || form.value.repairName.trim() === "") { |
| | | isValid = false; |
| | | errorMessage = "请输入报修人"; |
| | | } else if (!form.value.remark || form.value.remark.trim() === "") { |
| | | isValid = false; |
| | | errorMessage = "请输入故障现象"; |
| | | } |
| | | |
| | | if (!isValid) { |
| | | showToast(errorMessage); |
| | | return; |
| | | } |
| | | if (!isValid) { |
| | | showToast(errorMessage); |
| | | return; |
| | | } |
| | | |
| | | loading.value = true; |
| | | const id = getPageId(); |
| | | loading.value = true; |
| | | const id = getPageId(); |
| | | |
| | | // 准备提交数据 |
| | | const submitData = { ...form.value }; |
| | | // 附件数组:上传接口返回的附件信息(与巡检一致传 fileList) |
| | | const fileList = repairFileList.value.map(f => { |
| | | const d = f.uploadResponse?.data !== undefined ? f.uploadResponse.data : f.uploadResponse; |
| | | return d ? { ...d } : null; |
| | | }).filter(Boolean); |
| | | const submitData = { ...form.value }; |
| | | if (fileList.length) submitData.fileList = fileList; |
| | | |
| | | const { code } = id |
| | | ? await editRepair({ id: id, ...submitData }) |
| | | : await addRepair(submitData); |
| | | const { code } = id |
| | | ? await editRepair({ id: id, ...submitData }) |
| | | : await addRepair(submitData); |
| | | |
| | | if (code == 200) { |
| | | showToast(`${id ? "编辑" : "新增"}报修成功`); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | loading.value = false; |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | showToast('表单验证失败'); |
| | | } |
| | | }; |
| | | if (code == 200) { |
| | | showToast(`${id ? "编辑" : "新增"}报修成功`); |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | loading.value = false; |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | showToast("表单验证失败"); |
| | | } |
| | | }; |
| | | |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | | uni.removeStorageSync("repairId"); |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // 获取页面参数 |
| | | const getPageParams = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | |
| | | // 根据是否有id参数来判断是新增还是编辑 |
| | | if (options.id) { |
| | | // 编辑模式,获取详情 |
| | | loadForm(options.id); |
| | | } else { |
| | | // 新增模式 |
| | | loadForm(); |
| | | } |
| | | }; |
| | | // 获取页面参数 |
| | | const getPageParams = () => { |
| | | // 使用uni.getStorageSync获取id |
| | | const id = uni.getStorageSync("repairId"); |
| | | |
| | | // 获取页面ID |
| | | const getPageId = () => { |
| | | const pages = getCurrentPages(); |
| | | const currentPage = pages[pages.length - 1]; |
| | | const options = currentPage.options; |
| | | return options.id; |
| | | }; |
| | | // 根据是否有id参数来判断是新增还是编辑 |
| | | if (id) { |
| | | // 编辑模式,获取详情 |
| | | loadForm(id); |
| | | // 可选:获取后清除存储的id,避免影响后续操作 |
| | | uni.removeStorageSync("repairId"); |
| | | } else { |
| | | // 新增模式 |
| | | loadForm(); |
| | | } |
| | | }; |
| | | |
| | | // 获取页面ID |
| | | const getPageId = () => { |
| | | // 使用uni.getStorageSync获取id |
| | | const id = uni.getStorageSync("repairId"); |
| | | return id; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import '@/static/scss/form-common.scss'; |
| | | .repair-add { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 5rem; |
| | | } |
| | | @import "@/static/scss/form-common.scss"; |
| | | .repair-add { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 5rem; |
| | | } |
| | | |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05); |
| | | z-index: 1000; |
| | | } |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 0.75rem 0; |
| | | box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05); |
| | | z-index: 1000; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 6.375rem; |
| | | background: #C7C9CC; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | .cancel-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #ffffff; |
| | | width: 6.375rem; |
| | | background: #c7c9cc; |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #FFFFFF; |
| | | width: 14rem; |
| | | background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | .save-btn { |
| | | font-weight: 400; |
| | | font-size: 1rem; |
| | | color: #ffffff; |
| | | width: 14rem; |
| | | background: linear-gradient(140deg, #00baff 0%, #006cfb 100%); |
| | | box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2); |
| | | border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; |
| | | } |
| | | |
| | | // 响应式调整 |
| | | @media (max-width: 768px) { |
| | | .submit-section { |
| | | padding: 12px; |
| | | } |
| | | } |
| | | // 响应式调整 |
| | | @media (max-width: 768px) { |
| | | .submit-section { |
| | | padding: 12px; |
| | | } |
| | | } |
| | | |
| | | .tip-text { |
| | | padding: 4px 16px 0 16px; |
| | | font-size: 12px; |
| | | color: #888; |
| | | } |
| | | .tip-text { |
| | | padding: 4px 16px 0 16px; |
| | | font-size: 12px; |
| | | color: #888; |
| | | } |
| | | |
| | | .scan-icon { |
| | | color: #1989fa; |
| | | font-size: 18px; |
| | | margin-left: 8px; |
| | | cursor: pointer; |
| | | } |
| | | :deep(.device-name-form-item .u-form-item__content) { |
| | | justify-content: flex-start !important; |
| | | } |
| | | |
| | | .device-picker-wrap { |
| | | width: 100%; |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .device-picker-full { |
| | | display: block; |
| | | width: 100%; |
| | | } |
| | | |
| | | .picker-input-row { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | width: 100%; |
| | | min-height: 36px; |
| | | padding: 0; |
| | | } |
| | | |
| | | .picker-input-text { |
| | | flex: 1; |
| | | min-width: 0; |
| | | font-size: 15px; |
| | | color: #333; |
| | | line-height: 36px; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .picker-input-arrow { |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .picker-input-text.placeholder { |
| | | color: #c0c4cc; |
| | | } |
| | | |
| | | .simple-upload-area { |
| | | padding: 15px 20px; |
| | | } |
| | | |
| | | .upload-buttons { |
| | | display: flex; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .upload-progress { |
| | | margin: 15px 0; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .file-list { |
| | | margin-top: 15px; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 8px; |
| | | border: 1px solid #e9ecef; |
| | | width: calc(50% - 6px); |
| | | min-width: 120px; |
| | | } |
| | | |
| | | .file-preview-container { |
| | | position: relative; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .file-preview { |
| | | width: 80px; |
| | | height: 80px; |
| | | border-radius: 8px; |
| | | object-fit: cover; |
| | | border: 2px solid #f0f0f0; |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: -6px; |
| | | right: -6px; |
| | | width: 20px; |
| | | height: 20px; |
| | | background: #ff4757; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .file-info { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | .file-name { |
| | | font-size: 12px; |
| | | color: #333; |
| | | display: block; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | max-width: 100px; |
| | | } |
| | | |
| | | .file-size { |
| | | font-size: 10px; |
| | | color: #999; |
| | | margin-top: 2px; |
| | | display: block; |
| | | } |
| | | |
| | | .empty-state { |
| | | text-align: center; |
| | | padding: 24px 16px; |
| | | color: #999; |
| | | font-size: 14px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | border: 2px dashed #ddd; |
| | | } |
| | | </style> |