<template>
|
<view class="upkeep-maintain">
|
<!-- 使用通用页面头部组件 -->
|
<PageHeader title="新增保养"
|
@back="goBack" />
|
<!-- 表单内容 -->
|
<u-form ref="formRef"
|
:model="form"
|
:rules="formRules"
|
label-width="110px"
|
:error-type="['message']">
|
<!-- 基本信息 -->
|
<u-form-item label="实际保养人"
|
prop="maintenanceActuallyName"
|
required
|
border-bottom>
|
<u-input v-model="form.maintenanceActuallyName"
|
placeholder="请输入实际保养人"
|
clearable />
|
</u-form-item>
|
<u-form-item label="实际保养日期"
|
prop="maintenanceActuallyTime"
|
required
|
border-bottom>
|
<u-input v-model="form.maintenanceActuallyTime"
|
placeholder="请选择实际保养日期"
|
readonly
|
@click="showDatePicker"
|
clearable />
|
<template #right>
|
<u-icon name="arrow-right"
|
@click.stop="showDatePicker" />
|
</template>
|
</u-form-item>
|
<u-form-item label="保养结果"
|
prop="maintenanceResult"
|
required
|
border-bottom>
|
<u-input v-model="form.maintenanceResult"
|
placeholder="请输入保养结果"
|
clearable />
|
</u-form-item>
|
<u-form-item label="保养状态"
|
prop="status"
|
required
|
border-bottom>
|
<u-input v-model="maintenancestatusText"
|
placeholder="请选择保养状态"
|
readonly
|
@click="showResultPicker"
|
clearable />
|
<template #right>
|
<u-icon name="arrow-right"
|
@click="showResultPicker" />
|
</template>
|
</u-form-item>
|
<u-form-item label="设备备件"
|
prop="sparePartsIds"
|
border-bottom>
|
<view class="spare-parts-container"
|
@click="showSparePartPicker">
|
<view v-if="selectedSpareParts.length > 0"
|
class="spare-parts-list">
|
<view v-for="(item, index) in selectedSpareParts"
|
:key="index"
|
class="spare-part-tag">
|
<text>{{ item.name }}</text>
|
<u-icon name="close"
|
size="12"
|
color="#fff"
|
@click="removeSparePart(index)" />
|
</view>
|
</view>
|
<text v-else
|
class="placeholder">请选择设备备件</text>
|
</view>
|
<template #right>
|
<u-icon name="arrow-right"
|
@click="showSparePartPicker" />
|
</template>
|
</u-form-item>
|
<!-- 上传附件 -->
|
<u-form-item v-if="form.status == '1'"
|
label="保养附件"
|
border-bottom>
|
<view class="simple-upload-area">
|
<view class="upload-buttons">
|
<u-button type="primary"
|
@click="chooseMedia('image')"
|
:loading="uploading"
|
:disabled="uploadFiles.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>
|
<u-button type="success"
|
@click="chooseMedia('video')"
|
:loading="uploading"
|
:disabled="uploadFiles.length >= uploadConfig.limit"
|
:customStyle="{ flex: 1 }">
|
<uni-icons type="videocam"
|
name="videocam"
|
size="18"
|
color="#fff"
|
style="margin-right: 5px;"></uni-icons>
|
{{ 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="uploadFiles.length > 0"
|
class="file-list">
|
<view v-for="(file, index) in uploadFiles"
|
:key="index"
|
class="file-item">
|
<view class="file-preview-container">
|
<image v-if="file.type === 'image' || isImageFile(file)"
|
:src="file.url || file.tempFilePath || file.path || file.downloadUrl"
|
class="file-preview"
|
mode="aspectFill" />
|
<view v-else-if="file.type === 'video'"
|
class="video-preview">
|
<uni-icons type="videocam"
|
name="videocam"
|
size="18"
|
color="#fff"
|
style="margin-right: 5px;"></uni-icons>
|
<text class="video-text">视频</text>
|
</view>
|
<!-- 删除按钮 -->
|
<view class="delete-btn"
|
@click="removeFile(index)">
|
<u-icon name="close"
|
size="12"
|
color="#fff"></u-icon>
|
</view>
|
</view>
|
<view class="file-info">
|
<text class="file-name">{{ file.bucketFilename || file.name || (file.type === 'image' ? '图片' : '视频')
|
}}</text>
|
<text class="file-size">{{ formatFileSize(file.size) }}</text>
|
</view>
|
</view>
|
</view>
|
<view v-if="uploadFiles.length === 0"
|
class="empty-state">
|
<text>请选择要上传的保养图片或视频</text>
|
</view>
|
</view>
|
</u-form-item>
|
<!-- 提交按钮 -->
|
<view class="footer-btns">
|
<u-button class="cancel-btn"
|
@click="goBack">取消</u-button>
|
<u-button class="save-btn"
|
@click="sendForm"
|
:loading="loading">保存</u-button>
|
</view>
|
</u-form>
|
<!-- 日期选择器 -->
|
<u-popup v-model="showDate"
|
mode="bottom"
|
:closeable="true">
|
<u-datetime-picker v-model="form.maintenanceActuallyTime"
|
mode="date"
|
title="选择日期"
|
@confirm="onDateConfirm"
|
@cancel="showDate = false" />
|
</u-popup>
|
<!-- 保养结果选择器 -->
|
<up-action-sheet :show="showResult"
|
:actions="resultColumns"
|
title="选择保养结果"
|
@select="onResultConfirm"
|
@close="showResult = false" />
|
<!-- 设备备件选择器 -->
|
<up-popup :show="showSparePart"
|
mode="bottom"
|
:closeable="true"
|
@close="showSparePart = false">
|
<view class="spare-part-popup">
|
<view class="popup-header">
|
<text class="popup-title">选择设备备件</text>
|
</view>
|
<view class="spare-part-options">
|
<view v-for="(item, index) in sparePartOptions"
|
:key="index"
|
class="spare-part-option"
|
:class="{ 'selected': isSparePartSelected(item.id) }"
|
@click="toggleSparePartSelection(item)">
|
<text>{{ item.name }}</text>
|
<u-icon v-if="isSparePartSelected(item.id)"
|
name="checkmark"
|
color="#2c7be5" />
|
</view>
|
</view>
|
<up-button type="primary"
|
size="small"
|
:customStyle="{ borderRadius: '6px', padding: '4px 12px' }"
|
@click="confirmSparePartSelection">确定</up-button>
|
</view>
|
</up-popup>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, onMounted } from "vue";
|
import { onShow } from "@dcloudio/uni-app";
|
import PageHeader from "@/components/PageHeader.vue";
|
import {
|
addMaintenance,
|
getSparePartsOptions,
|
} from "@/api/equipmentManagement/upkeep";
|
import useUserStore from "@/store/modules/user";
|
import dayjs from "dayjs";
|
import { formatDateToYMD } from "@/utils/ruoyi";
|
import config from "@/config";
|
|
// 显示提示信息
|
const showToast = message => {
|
uni.showToast({
|
title: message,
|
icon: "none",
|
});
|
};
|
|
defineOptions({
|
name: "设备保养表单",
|
});
|
|
const userStore = useUserStore();
|
|
// 表单引用
|
const formRef = ref(null);
|
const loading = ref(false);
|
const showDate = ref(false);
|
const showResult = ref(false);
|
const showSparePart = ref(false);
|
const currentDate = ref([
|
new Date().getFullYear(),
|
new Date().getMonth() + 1,
|
new Date().getDate(),
|
]);
|
const resultPickerValue = ref([]);
|
const maintenancestatusText = ref("");
|
const selectedSpareParts = ref([]);
|
const tempSelectedSpareParts = ref([]);
|
|
// 文件上传相关
|
const uploadFiles = ref([]);
|
const uploading = ref(false);
|
const uploadProgress = ref(0);
|
const number = ref(0);
|
|
// 上传配置
|
const uploadConfig = {
|
limit: 9,
|
fileType: ["jpg", "jpeg", "png", "gif", "webp", "mp4", "mov", "avi", "wmv"],
|
maxVideoDuration: 60,
|
};
|
|
// 上传文件URL
|
const uploadFileUrl = ref(`${config.baseUrl}/file/upload`);
|
|
// 保养结果选项
|
const resultColumns = [
|
{ name: "完结", value: 1 },
|
{ name: "待保养", value: 0 },
|
{ name: "失败", value: 2 },
|
];
|
|
// 表单验证规则
|
const formRules = {
|
maintenanceActuallyName: [
|
{ required: true, trigger: "blur", message: "请输入实际保养人" },
|
],
|
maintenanceActuallyTime: [
|
{ required: true, trigger: "change", message: "请选择实际保养日期" },
|
],
|
maintenanceResult: [
|
{ required: true, trigger: "change", message: "请选择保养结果" },
|
],
|
};
|
|
// 使用 ref 声明表单数据
|
const form = ref({
|
maintenanceActuallyName: userStore.nickName || "", // 默认使用当前用户昵称
|
maintenanceResult: undefined, // 保养结果
|
maintenanceActuallyTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // 实际保养日期(只显示日期)
|
sparePartsIds: undefined, // 设备备件ID
|
});
|
|
// 清除表单校验状态
|
const clearValidate = () => {
|
// uview-plus不需要手动清除验证状态,重置表单时会自动清除
|
};
|
|
// 重置表单数据和校验状态
|
const resetForm = () => {
|
form.value = {
|
maintenanceActuallyName: userStore.nickName || "",
|
maintenanceResult: undefined,
|
maintenanceActuallyTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
sparePartsIds: [],
|
};
|
maintenancestatusText.value = "";
|
selectedSpareParts.value = [];
|
tempSelectedSpareParts.value = [];
|
};
|
|
const resetFormAndValidate = () => {
|
resetForm();
|
// clearValidate(); // 删除这行,Vant4会自动处理
|
};
|
// 判断是否为图片文件
|
const isImageFile = file => {
|
// 检查contentType字段
|
if (file.contentType && file.contentType.startsWith("image/")) {
|
return true;
|
}
|
|
// 检查原有的type字段
|
if (file.type === "image") return true;
|
|
// 检查文件扩展名
|
const name = file.bucketFilename || file.originalFilename || file.name || "";
|
const ext = name.split(".").pop()?.toLowerCase();
|
return ["jpg", "jpeg", "png", "gif", "webp"].includes(ext);
|
};
|
|
// 提交表单
|
const sendForm = async () => {
|
console.log(form.value.sparePartsIds, "form.value.sparePartsIds");
|
try {
|
// 手动验证表单
|
let isValid = true;
|
let errorMessage = "";
|
if (!form.value.maintenanceActuallyName) {
|
isValid = false;
|
errorMessage = "请输入实际保养人";
|
} else if (!form.value.maintenanceActuallyTime) {
|
isValid = false;
|
errorMessage = "请选择实际保养日期";
|
} else if (form.value.maintenanceResult === undefined) {
|
isValid = false;
|
errorMessage = "请选择保养结果";
|
} else if (uploadFiles.value.length === 0 && form.value.status == "1") {
|
isValid = false;
|
errorMessage = "请上传保养照片";
|
}
|
|
if (!isValid) {
|
showToast(errorMessage);
|
return;
|
}
|
|
// 验证通过
|
submitFormData();
|
} catch (e) {
|
showToast("表单验证失败");
|
}
|
};
|
|
// 提交表单数据
|
const submitFormData = async () => {
|
try {
|
loading.value = true;
|
const id = getPageId();
|
|
if (!id) {
|
showToast("参数错误");
|
loading.value = false;
|
return;
|
}
|
// 准备提交数据,maintenanceActuallyTime 加上当前时分秒
|
const submitData = {
|
...form.value,
|
imagesFile: form.value.status == "1" ? uploadFiles.value : [],
|
sparePartsIds: form.value.sparePartsIds
|
? form.value.sparePartsIds.join(",")
|
: "",
|
};
|
const { code } = await addMaintenance({ id: id, ...submitData });
|
|
if (code == 200) {
|
showToast("新增保养成功");
|
resetFormAndValidate();
|
setTimeout(() => {
|
uni.navigateBack();
|
}, 1500);
|
} else {
|
loading.value = false;
|
}
|
} catch (e) {
|
loading.value = false;
|
showToast("操作失败");
|
}
|
};
|
|
// 返回上一页
|
const goBack = () => {
|
// 清除存储的id和数据
|
uni.removeStorageSync("repairId");
|
uni.removeStorageSync("upkeepItemData");
|
uni.navigateBack();
|
};
|
|
// 获取页面ID
|
const getPageId = () => {
|
// 从本地存储获取id
|
return uni.getStorageSync("repairId");
|
};
|
|
const dataform = ref({});
|
// 获取设备信息
|
const getUpkeepItemData = () => {
|
try {
|
const dataStr = uni.getStorageSync("upkeepItemData");
|
if (!dataStr) {
|
return null;
|
}
|
dataform.value = JSON.parse(dataStr);
|
fetchSparePartOptions(dataform.value.deviceLedgerId);
|
return JSON.parse(dataStr);
|
} catch (e) {
|
console.error("解析设备数据失败:", e);
|
return null;
|
}
|
};
|
|
// 显示日期选择器
|
const showDatePicker = () => {
|
showDate.value = true;
|
};
|
|
// 确认日期选择
|
const onDateConfirm = e => {
|
// 只保存年月日,不包含时分秒
|
form.value.maintenanceActuallyTime = dayjs(e.value).format(
|
"YYYY-MM-DD HH:mm:ss"
|
);
|
showDate.value = false;
|
};
|
|
// 显示保养结果选择器
|
const showResultPicker = () => {
|
showResult.value = true;
|
};
|
|
// 确认保养结果选择
|
const onResultConfirm = selected => {
|
form.value.status = selected.value;
|
maintenancestatusText.value = selected.name;
|
showResult.value = false;
|
};
|
|
// 显示设备备件选择器
|
const showSparePartPicker = () => {
|
tempSelectedSpareParts.value = [...selectedSpareParts.value];
|
showSparePart.value = true;
|
};
|
|
// 检查备件是否已选中
|
const isSparePartSelected = id => {
|
return tempSelectedSpareParts.value.some(
|
item => item.id === id || item.value === id
|
);
|
};
|
|
// 切换备件选中状态
|
const toggleSparePartSelection = item => {
|
const itemId = item.id || item.value;
|
const index = tempSelectedSpareParts.value.findIndex(
|
selected => selected.id === itemId || selected.value === itemId
|
);
|
if (index > -1) {
|
tempSelectedSpareParts.value.splice(index, 1);
|
} else {
|
tempSelectedSpareParts.value.push(item);
|
}
|
};
|
|
// 确认备件选择
|
const confirmSparePartSelection = () => {
|
selectedSpareParts.value = [...tempSelectedSpareParts.value];
|
form.value.sparePartsIds = selectedSpareParts.value.map(item => item.id);
|
showSparePart.value = false;
|
};
|
|
// 移除已选备件
|
const removeSparePart = index => {
|
selectedSpareParts.value.splice(index, 1);
|
form.value.sparePartsIds = selectedSpareParts.value.map(item => item.id);
|
};
|
const sparePartsIds = ref([]);
|
// 初始化表单数据
|
const initForm = () => {
|
// 获取设备信息
|
const itemData = getUpkeepItemData();
|
|
// 重置选择的备件
|
selectedSpareParts.value = [];
|
// 重置上传的文件
|
uploadFiles.value = [];
|
uploading.value = false;
|
uploadProgress.value = 0;
|
maintenancestatusText.value = "";
|
|
// 设置保养人为当前用户昵称
|
form.value.maintenanceActuallyName = userStore.nickName || "";
|
// 设置当前日期(只包含年月日)
|
form.value.maintenanceActuallyTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
currentDate.value = [
|
new Date().getFullYear(),
|
new Date().getMonth() + 1,
|
new Date().getDate(),
|
];
|
|
// 如果有设备信息,填充已有的保养数据
|
if (itemData) {
|
// 填充实际保养人
|
if (itemData.maintenanceActuallyName) {
|
form.value.maintenanceActuallyName = itemData.maintenanceActuallyName;
|
}
|
// 填充保养结果
|
if (
|
itemData.maintenanceResult !== undefined &&
|
itemData.maintenanceResult !== null
|
) {
|
form.value.maintenanceResult = itemData.maintenanceResult;
|
}
|
// 填充实际保养日期
|
if (itemData.maintenanceActuallyTime) {
|
form.value.maintenanceActuallyTime = itemData.maintenanceActuallyTime;
|
// 解析日期设置到日期选择器
|
const date = dayjs(itemData.maintenanceActuallyTime);
|
currentDate.value = [date.year(), date.month() + 1, date.date()];
|
}
|
// 填充保养状态
|
if (itemData.status) {
|
const statusMap = {
|
0: "待保养",
|
1: "完结",
|
2: "失败",
|
};
|
maintenancestatusText.value = statusMap[itemData.status] || "";
|
}
|
// 填充备件数据
|
|
// 处理字符串格式的备件IDs
|
sparePartsIds.value = itemData.sparePartsIds;
|
|
// 填充附件数据
|
if (itemData.files && itemData.files.length > 0) {
|
uploadFiles.value = itemData.files.map(file => ({
|
id: file.id,
|
name: file.name || file.bucketFilename || file.originalFilename,
|
url: file.url || file.downloadUrl,
|
type:
|
file.type ||
|
(file.contentType && file.contentType.startsWith("image/")
|
? "image"
|
: "video"),
|
size: file.size || file.byteSize,
|
}));
|
} else if (itemData.uploadFiles && itemData.uploadFiles.length > 0) {
|
uploadFiles.value = itemData.uploadFiles.map(file => ({
|
id: file.id,
|
name: file.name || file.bucketFilename || file.originalFilename,
|
url: file.url || file.downloadUrl || file.tempFilePath || file.path,
|
type: file.type,
|
size: file.size,
|
}));
|
}
|
}
|
};
|
|
onShow(async () => {
|
// 先获取备件选项,再初始化表单
|
const pageId = getPageId();
|
if (pageId) {
|
await fetchSparePartOptions(pageId);
|
}
|
// 页面显示时初始化表单
|
initForm();
|
});
|
const sparePartOptions = ref([]);
|
const fetchSparePartOptions = deviceLedgerId => {
|
return new Promise((resolve, reject) => {
|
getSparePartsOptions({ deviceLedgerId: deviceLedgerId })
|
.then(res => {
|
if (res.code == 200) {
|
sparePartOptions.value = res.data || [];
|
const idArray =
|
typeof sparePartsIds.value === "string"
|
? sparePartsIds.value.split(",")
|
: sparePartsIds.value;
|
|
if (idArray.length > 0) {
|
selectedSpareParts.value = sparePartOptions.value
|
.filter(
|
option =>
|
idArray.includes(option.id.toString()) ||
|
idArray.includes(option.value?.toString())
|
)
|
.map(option => ({
|
id: option.id || option.value,
|
name: option.name,
|
code: option.code,
|
value: option.id || option.value,
|
}));
|
// 设置备件IDs
|
form.value.sparePartsIds = idArray.join(",");
|
}
|
resolve(res.data);
|
} else {
|
resolve([]);
|
}
|
})
|
.catch(err => {
|
console.error("获取备件选项失败:", err);
|
resolve([]);
|
});
|
});
|
};
|
|
// 格式化文件URL
|
const formatFileUrl = url => {
|
if (!url) return "";
|
|
// 如果已经是完整的URL(http或https开头),直接返回
|
if (url.startsWith("http://") || url.startsWith("https://")) {
|
return url;
|
}
|
|
// 如果是本地路径(如 D:\\ruoyi\\prod\\uploads...),需要转换为网络URL
|
// 从路径中提取uploads后面的部分
|
const uploadsIndex = url.indexOf("uploads");
|
if (uploadsIndex !== -1) {
|
const relativePath = url.substring(uploadsIndex);
|
// 使用baseUrl + /profile/ + 相对路径
|
return `http://192.168.1.35:8888/profile/${relativePath}`;
|
}
|
|
// 其他情况,尝试直接拼接
|
return `http://192.168.1.35:8888/profile/${url}`;
|
};
|
|
// 格式化文件大小
|
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 chooseMedia = type => {
|
if (uploadFiles.value.length >= uploadConfig.limit) {
|
uni.showToast({
|
title: `最多只能选择${uploadConfig.limit}个文件`,
|
icon: "none",
|
});
|
return;
|
}
|
|
const remaining = uploadConfig.limit - uploadFiles.value.length;
|
|
// 优先:chooseMedia(支持 image/video)
|
if (typeof uni.chooseMedia === "function") {
|
uni.chooseMedia({
|
count: Math.min(remaining, 1),
|
mediaType: [type || "image"],
|
sizeType: ["compressed", "original"],
|
sourceType: ["camera"], // 支持相机和相册
|
success: res => {
|
try {
|
const files = res?.tempFiles || [];
|
if (!files.length) throw new Error("未获取到文件");
|
|
files.forEach((tf, idx) => {
|
const filePath = tf.tempFilePath || tf.path || "";
|
const fileType = tf.fileType || type || "image";
|
const ext = fileType === "video" ? "mp4" : "jpg";
|
const file = {
|
tempFilePath: filePath,
|
path: filePath,
|
type: fileType,
|
name: `${fileType}_${Date.now()}_${idx}.${ext}`,
|
size: tf.size || 0,
|
duration: tf.duration || 0,
|
createTime: Date.now(),
|
uid: Date.now() + Math.random() + idx,
|
};
|
|
console.log("chooseMedia 成功获取文件:", file);
|
handleBeforeUpload(file);
|
});
|
} catch (e) {
|
console.error("处理拍摄结果失败:", e);
|
uni.showToast({ title: "处理文件失败", icon: "error" });
|
}
|
},
|
fail: err => {
|
console.error("拍摄失败:", err);
|
uni.showToast({ title: "拍摄失败", icon: "error" });
|
},
|
});
|
return;
|
}
|
|
// 降级:chooseImage / chooseVideo
|
if (type === "video") {
|
uni.chooseVideo({
|
sourceType: ["camera", "album"],
|
maxDuration: uploadConfig.maxVideoDuration,
|
camera: "back",
|
success: res => {
|
try {
|
if (!res.tempFilePath) {
|
throw new Error("未获取到视频文件");
|
}
|
|
const file = {
|
tempFilePath: res.tempFilePath,
|
path: res.tempFilePath,
|
type: "video",
|
name: `video_${Date.now()}.mp4`,
|
size: res.size || 0,
|
duration: res.duration || 0,
|
createTime: new Date().getTime(),
|
uid: Date.now() + Math.random(),
|
};
|
|
handleBeforeUpload(file);
|
} catch (error) {
|
console.error("处理拍视频结果失败:", error);
|
uni.showToast({ title: "处理视频失败", icon: "error" });
|
}
|
},
|
fail: err => {
|
console.error("拍视频失败:", err);
|
uni.showToast({ title: "拍视频失败", icon: "error" });
|
},
|
});
|
} else {
|
uni.chooseImage({
|
count: 1,
|
sizeType: ["compressed", "original"],
|
sourceType: ["camera", "album"],
|
success: res => {
|
const tempFilePath = res?.tempFilePaths?.[0];
|
const tempFile = res?.tempFiles?.[0] || {};
|
if (!tempFilePath) return;
|
handleBeforeUpload({
|
tempFilePath,
|
path: tempFilePath,
|
type: "image",
|
name: `photo_${Date.now()}.jpg`,
|
size: tempFile.size || 0,
|
createTime: Date.now(),
|
uid: Date.now() + Math.random(),
|
});
|
},
|
});
|
}
|
};
|
|
// 删除文件
|
const removeFile = index => {
|
uni.showModal({
|
title: "确认删除",
|
content: "确定要删除这个文件吗?",
|
success: res => {
|
if (res.confirm) {
|
uploadFiles.value.splice(index, 1);
|
uni.showToast({
|
title: "删除成功",
|
icon: "success",
|
});
|
}
|
},
|
});
|
};
|
|
// 上传前校验
|
const handleBeforeUpload = async file => {
|
// 校验文件类型
|
if (
|
uploadConfig.fileType &&
|
Array.isArray(uploadConfig.fileType) &&
|
uploadConfig.fileType.length > 0
|
) {
|
const fileName = file.name || "";
|
const fileExtension = fileName
|
? fileName.split(".").pop().toLowerCase()
|
: "";
|
|
// 根据文件类型确定期望的扩展名
|
let expectedTypes = [];
|
if (file.type === "image") {
|
expectedTypes = ["jpg", "jpeg", "png", "gif", "webp"];
|
} else if (file.type === "video") {
|
expectedTypes = ["mp4", "mov", "avi", "wmv"];
|
}
|
|
// 检查文件扩展名是否在允许的类型中
|
if (fileExtension && expectedTypes.length > 0) {
|
const isAllowed = expectedTypes.some(
|
type => uploadConfig.fileType.includes(type) && type === fileExtension
|
);
|
|
if (!isAllowed) {
|
uni.showToast({
|
title: `文件格式不支持,请拍摄 ${expectedTypes.join("/")} 格式的文件`,
|
icon: "none",
|
});
|
return false;
|
}
|
}
|
}
|
|
// 校验通过,开始上传
|
uploadFile(file);
|
return true;
|
};
|
|
// 文件上传处理
|
const uploadFile = async file => {
|
uploading.value = true;
|
uploadProgress.value = 0;
|
number.value++;
|
|
// 确保token存在
|
const token = userStore.token;
|
if (!token) {
|
handleUploadError("用户未登录");
|
return;
|
}
|
|
uploadWithUniUploadFile(file, file.tempFilePath || file.path || "", token);
|
};
|
|
// 使用uni.uploadFile上传
|
const uploadWithUniUploadFile = (file, filePath, token) => {
|
if (!filePath) {
|
handleUploadError("文件路径不存在");
|
return;
|
}
|
|
const uploadTask = uni.uploadFile({
|
url: uploadFileUrl.value,
|
filePath: filePath,
|
name: "file",
|
formData: {
|
type: 10, // 保养附件类型
|
},
|
header: {
|
Authorization: `Bearer ${token}`,
|
},
|
success: res => {
|
try {
|
if (res.statusCode === 200) {
|
const response = JSON.parse(res.data);
|
if (response.code === 200) {
|
handleUploadSuccess(response, file);
|
uni.showToast({
|
title: "上传成功",
|
icon: "success",
|
});
|
} else {
|
handleUploadError(response.msg || "服务器返回错误");
|
}
|
} else {
|
handleUploadError(`服务器错误,状态码: ${res.statusCode}`);
|
}
|
} catch (e) {
|
console.error("解析响应失败:", e);
|
console.error("原始响应数据:", res.data);
|
handleUploadError("响应数据解析失败: " + e.message);
|
}
|
},
|
fail: err => {
|
console.error("上传失败:", err.errMsg || err);
|
number.value--;
|
|
let errorMessage = "上传失败";
|
if (err.errMsg) {
|
if (err.errMsg.includes("statusCode: null")) {
|
errorMessage = "网络连接失败,请检查网络设置";
|
} else if (err.errMsg.includes("timeout")) {
|
errorMessage = "上传超时,请重试";
|
} else if (err.errMsg.includes("fail")) {
|
errorMessage = "上传失败,请检查网络连接";
|
} else {
|
errorMessage = err.errMsg;
|
}
|
}
|
|
handleUploadError(errorMessage);
|
},
|
complete: () => {
|
uploading.value = false;
|
uploadProgress.value = 0;
|
},
|
});
|
|
// 监听上传进度
|
if (uploadTask && uploadTask.onProgressUpdate) {
|
uploadTask.onProgressUpdate(res => {
|
uploadProgress.value = res.progress;
|
});
|
}
|
};
|
|
// 上传成功处理
|
const handleUploadSuccess = (res, file) => {
|
console.log("上传成功响应:", res);
|
|
// 处理不同的数据结构:可能是数组,也可能是单个对象
|
let uploadedFile = null;
|
uploadedFile = res.data;
|
|
if (!uploadedFile) {
|
console.error("无法解析上传响应数据:", res);
|
number.value--;
|
handleUploadError("上传响应数据格式错误", false);
|
return;
|
}
|
console.log("上传成功文件数据:", uploadedFile, file);
|
|
// 确保上传的文件数据完整,包含id和type
|
const fileData = {
|
name: uploadedFile.originalName,
|
type: file.type,
|
url: uploadedFile.tempPath,
|
};
|
|
uploadFiles.value.push(fileData);
|
number.value = 0;
|
};
|
|
// 上传失败处理
|
const handleUploadError = (message = "上传文件失败", showRetry = false) => {
|
uploading.value = false;
|
uploadProgress.value = 0;
|
|
if (showRetry) {
|
uni.showModal({
|
title: "上传失败",
|
content: message + ",是否重试?",
|
success: res => {
|
if (res.confirm) {
|
// 用户选择重试,这里可以重新触发上传
|
}
|
},
|
});
|
} else {
|
uni.showToast({
|
title: message,
|
icon: "error",
|
});
|
}
|
};
|
|
onMounted(() => {
|
// 页面加载时初始化表单
|
initForm();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
@import "@/static/scss/form-common.scss";
|
.upkeep-maintain {
|
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;
|
}
|
|
.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;
|
}
|
|
// 响应式调整
|
@media (max-width: 768px) {
|
.submit-section {
|
padding: 12px;
|
}
|
}
|
|
.tip-text {
|
padding: 4px 16px 0 16px;
|
font-size: 12px;
|
color: #888;
|
}
|
|
/* 设备备件多选样式 */
|
.spare-parts-container {
|
flex: 1;
|
min-height: 40px;
|
display: flex;
|
align-items: center;
|
}
|
|
.spare-parts-list {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 8px;
|
}
|
|
.spare-part-tag {
|
display: flex;
|
align-items: center;
|
gap: 4px;
|
background: #2c7be5;
|
color: #fff;
|
padding: 4px 8px;
|
border-radius: 4px;
|
font-size: 12px;
|
}
|
|
.placeholder {
|
color: #c0c4cc;
|
font-size: 14px;
|
}
|
|
/* 备件选择弹窗样式 */
|
.spare-part-popup {
|
padding: 16px;
|
max-height: 60vh;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.popup-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 16px;
|
padding-bottom: 12px;
|
border-bottom: 1px solid #e8e8e8;
|
}
|
|
.popup-title {
|
font-size: 16px;
|
font-weight: 600;
|
color: #333;
|
}
|
|
.spare-part-options {
|
flex: 1;
|
overflow-y: auto;
|
}
|
|
.spare-part-option {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 12px 0;
|
border-bottom: 1px solid #f0f0f0;
|
font-size: 14px;
|
color: #333;
|
}
|
|
.spare-part-option.selected {
|
color: #2c7be5;
|
font-weight: 500;
|
}
|
|
/* 备件选择弹窗样式 */
|
.spare-part-popup {
|
width: 100%;
|
max-height: 80vh;
|
background: #fff;
|
border-radius: 16px 16px 0 0;
|
overflow: hidden;
|
}
|
|
.popup-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 16px 20px;
|
border-bottom: 1px solid #f0f0f0;
|
background: #f8f9fa;
|
}
|
|
.popup-title {
|
font-size: 16px;
|
font-weight: 600;
|
color: #333;
|
}
|
|
.spare-part-options {
|
padding: 10px 0;
|
max-height: 60vh;
|
overflow-y: auto;
|
}
|
|
.spare-part-option {
|
position: relative;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 14px 20px;
|
border-bottom: 1px solid #f0f0f0;
|
transition: all 0.2s ease;
|
}
|
|
.spare-part-option:last-child {
|
border-bottom: none;
|
}
|
|
.spare-part-option:hover {
|
background: #f8f9fa;
|
}
|
|
.spare-part-option.selected {
|
background: #e6f7ff;
|
color: #1890ff;
|
}
|
|
.spare-part-option.selected::before {
|
content: "";
|
position: absolute;
|
left: 0;
|
top: 0;
|
bottom: 0;
|
width: 4px;
|
background: #1890ff;
|
}
|
|
/* 文件上传样式 */
|
.simple-upload-area {
|
width: 100%;
|
}
|
|
.upload-buttons {
|
display: flex;
|
margin-bottom: 12px;
|
}
|
|
.upload-progress {
|
margin: 12px 0;
|
}
|
|
.file-list {
|
margin-top: 12px;
|
}
|
|
.file-item {
|
display: flex;
|
align-items: center;
|
margin-bottom: 12px;
|
padding: 10px;
|
background: #f8f9fa;
|
border-radius: 8px;
|
}
|
|
.file-preview-container {
|
position: relative;
|
margin-right: 12px;
|
}
|
|
.file-preview {
|
width: 80px;
|
height: 80px;
|
border-radius: 4px;
|
}
|
|
.video-preview {
|
width: 80px;
|
height: 80px;
|
background: #333;
|
border-radius: 4px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
color: #fff;
|
font-size: 12px;
|
}
|
|
.delete-btn {
|
position: absolute;
|
top: -8px;
|
right: -8px;
|
width: 20px;
|
height: 20px;
|
background: #f56c6c;
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.file-info {
|
flex: 1;
|
}
|
|
.file-name {
|
display: block;
|
font-size: 14px;
|
color: #333;
|
margin-bottom: 4px;
|
line-height: 1.4;
|
}
|
|
.file-size {
|
font-size: 12px;
|
color: #999;
|
}
|
|
.empty-state {
|
padding: 20px 0;
|
text-align: center;
|
color: #999;
|
font-size: 14px;
|
}
|
</style>
|