<template>
|
<u-popup :show="dialogVisitable"
|
mode="center"
|
:round="12"
|
:zIndex="900"
|
@close="cancel">
|
<view class="popup-content">
|
<view class="popup-title">{{ operationType === "add" ? "新增保养任务" : "编辑保养任务" }}</view>
|
<view class="form-body">
|
<view class="form-item">
|
<text class="label">任务名称</text>
|
<up-input v-model="form.taskName"
|
placeholder="请输入任务名称"
|
border="none"
|
customStyle="background: #f7f8fa; padding: 0 20rpx; min-height: 72rpx; border-radius: 12rpx;" />
|
</view>
|
<view class="form-item">
|
<text class="label">设备</text>
|
<view class="picker-value inspector-picker"
|
@click="openDevicePopup">
|
<text>{{ deviceNamesText || "请选择设备" }}</text>
|
<u-icon name="arrow-right"
|
size="14"
|
color="#999" />
|
</view>
|
<view class="inspector-tags"
|
v-if="form.deviceIds?.length">
|
<view v-for="id in form.deviceIds"
|
:key="id"
|
class="inspector-tag"
|
@click="toggleDevice(id)">
|
<text>{{ getDeviceName(id) }}</text>
|
<u-icon name="close"
|
size="11"
|
color="#1677ff" />
|
</view>
|
</view>
|
</view>
|
<view class="form-item">
|
<text class="label">录入人</text>
|
<picker mode="selector"
|
:range="userList"
|
range-key="nickName"
|
:value="inspectorIndex"
|
@change="onInspectorChange">
|
<view class="picker-value">{{ form.registrant || "请选择录入人" }}</view>
|
</picker>
|
</view>
|
<view class="form-item">
|
<text class="label">登记时间</text>
|
<picker mode="date"
|
:value="form.registrationDate || todayStr"
|
@change="onRegistrationDateChange">
|
<view class="picker-value">{{ form.registrationDate || "请选择登记时间" }}</view>
|
</picker>
|
</view>
|
<view class="form-item">
|
<text class="label">任务频率</text>
|
<picker mode="selector"
|
:range="frequencyOptions"
|
range-key="label"
|
:value="frequencyIndex"
|
@change="onFrequencyChange">
|
<view class="picker-value">{{ currentFrequencyLabel || "请选择任务频率" }}</view>
|
</picker>
|
</view>
|
<view class="form-item"
|
v-if="form.frequencyType === 'DAILY'">
|
<text class="label">时间</text>
|
<picker mode="time"
|
:value="form.frequencyDetail || '08:00'"
|
@change="onDailyTimeChange">
|
<view class="picker-value">{{ form.frequencyDetail || "请选择时间" }}</view>
|
</picker>
|
</view>
|
<view class="form-item"
|
v-if="form.frequencyType === 'WEEKLY'">
|
<text class="label">每周日期</text>
|
<picker mode="selector"
|
:range="weekOptions"
|
range-key="label"
|
:value="weekIndex"
|
@change="onWeekChange">
|
<view class="picker-value">{{ currentWeekLabel || "请选择星期" }}</view>
|
</picker>
|
</view>
|
<view class="form-item"
|
v-if="form.frequencyType === 'WEEKLY'">
|
<text class="label">每周时间</text>
|
<picker mode="time"
|
:value="form.time || '08:00'"
|
@change="onWeekTimeChange">
|
<view class="picker-value">{{ form.time || "请选择时间" }}</view>
|
</picker>
|
</view>
|
<view class="form-item"
|
v-if="form.frequencyType === 'MONTHLY'">
|
<text class="label">每月日期与时间</text>
|
<picker mode="date"
|
fields="day"
|
:value="monthlyDate"
|
@change="onMonthlyDateChange">
|
<view class="picker-value">{{ monthlyDate || "请选择日期" }}</view>
|
</picker>
|
<picker mode="time"
|
:value="monthlyTime"
|
@change="onMonthlyTimeChange">
|
<view class="picker-value">{{ monthlyTime || "请选择时间" }}</view>
|
</picker>
|
</view>
|
<view class="form-item"
|
v-if="form.frequencyType === 'QUARTERLY'">
|
<text class="label">季度日期与时间</text>
|
<picker mode="date"
|
:value="quarterlyDate"
|
@change="onQuarterlyDateChange">
|
<view class="picker-value">{{ quarterlyDate || "请选择日期" }}</view>
|
</picker>
|
<picker mode="time"
|
:value="quarterlyTime"
|
@change="onQuarterlyTimeChange">
|
<view class="picker-value">{{ quarterlyTime || "请选择时间" }}</view>
|
</picker>
|
</view>
|
<view class="form-item">
|
<text class="label">备注</text>
|
<u-textarea v-model="form.remarks"
|
placeholder="请输入备注"
|
:height="80"
|
count />
|
</view>
|
</view>
|
<view class="popup-footer">
|
<u-button @click="cancel">取消</u-button>
|
<u-button type="primary"
|
@click="submitForm">保存</u-button>
|
</view>
|
</view>
|
</u-popup>
|
<u-popup :show="showDevicePopup"
|
mode="bottom"
|
:round="16"
|
:zIndex="1100"
|
@close="closeDevicePopup">
|
<view class="inspector-popup">
|
<view class="inspector-header">
|
<text class="inspector-title">选择设备</text>
|
</view>
|
<scroll-view scroll-y
|
class="inspector-list">
|
<view v-for="item in deviceOptions"
|
:key="item.id"
|
class="inspector-row"
|
@click="toggleDevice(item.id)">
|
<text class="inspector-name">{{ item.deviceName }}</text>
|
<u-icon v-if="(form.deviceIds || []).includes(item.id)"
|
name="checkmark-circle-fill"
|
color="#1677ff"
|
size="20" />
|
<u-icon v-else
|
name=""
|
color="#d5d8de"
|
size="20" />
|
</view>
|
</scroll-view>
|
<view class="inspector-footer">
|
<u-button @click="closeDevicePopup">确定</u-button>
|
</view>
|
</view>
|
</u-popup>
|
</template>
|
|
<script setup>
|
import { computed, reactive, ref } from "vue";
|
import useUserStore from "@/store/modules/user";
|
import {
|
deviceMaintenanceTaskAdd,
|
deviceMaintenanceTaskEdit,
|
} from "@/api/equipmentManagement/upkeep.js";
|
import { userListNoPageByTenantId } from "@/api/system/user.js";
|
import { getDeviceLedger } from "@/api/equipmentManagement/ledger.js";
|
|
const emit = defineEmits(["closeDia"]);
|
const userStore = useUserStore();
|
const dialogVisitable = ref(false);
|
const operationType = ref("add");
|
const deviceOptions = ref([]);
|
const userList = ref([]);
|
const showDevicePopup = ref(false);
|
|
const todayStr = ref("");
|
try {
|
const d = new Date();
|
todayStr.value = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
} catch (e) {}
|
|
const defaultForm = () => ({
|
id: undefined,
|
taskName: "",
|
deviceIds: [],
|
registrantId: undefined,
|
registrant: "",
|
registrationDate: "",
|
remarks: "",
|
frequencyType: "",
|
frequencyDetail: "",
|
week: "",
|
time: "",
|
});
|
|
const form = reactive(defaultForm());
|
|
const frequencyOptions = [
|
{ label: "每日", value: "DAILY" },
|
{ label: "每周", value: "WEEKLY" },
|
{ label: "每月", value: "MONTHLY" },
|
{ label: "季度", value: "QUARTERLY" },
|
];
|
|
const weekOptions = [
|
{ label: "周一", value: "MON" },
|
{ label: "周二", value: "TUE" },
|
{ label: "周三", value: "WED" },
|
{ label: "周四", value: "THU" },
|
{ label: "周五", value: "FRI" },
|
{ label: "周六", value: "SAT" },
|
{ label: "周日", value: "SUN" },
|
];
|
|
const monthlyDate = ref("");
|
const monthlyTime = ref("");
|
const quarterlyDate = ref("");
|
const quarterlyTime = ref("");
|
|
const frequencyIndex = computed(() => {
|
const index = frequencyOptions.findIndex(item => item.value === form.frequencyType);
|
return index >= 0 ? index : 0;
|
});
|
|
const weekIndex = computed(() => {
|
const index = weekOptions.findIndex(item => item.value === form.week);
|
return index >= 0 ? index : 0;
|
});
|
|
const currentFrequencyLabel = computed(() => {
|
return frequencyOptions.find(item => item.value === form.frequencyType)?.label || "";
|
});
|
|
const currentWeekLabel = computed(() => {
|
return weekOptions.find(item => item.value === form.week)?.label || "";
|
});
|
|
const inspectorIndex = computed(() => {
|
const index = userList.value.findIndex(item => String(item.userId) === String(form.registrantId));
|
return index >= 0 ? index : 0;
|
});
|
|
const deviceNamesText = computed(() => {
|
if (!form.deviceIds?.length) return "";
|
return form.deviceIds
|
.map(id => getDeviceName(id))
|
.filter(Boolean)
|
.join("、");
|
});
|
|
function getDeviceName(id) {
|
const d = deviceOptions.value.find(item => String(item.id) === String(id));
|
return d?.deviceName || "";
|
}
|
|
const resetForm = () => {
|
Object.assign(form, defaultForm());
|
monthlyDate.value = "";
|
monthlyTime.value = "";
|
quarterlyDate.value = "";
|
quarterlyTime.value = "";
|
showDevicePopup.value = false;
|
};
|
|
const normalizeIdList = val => {
|
if (!val) return [];
|
if (Array.isArray(val)) return val.map(v => (Number.isNaN(Number(v)) ? v : Number(v)));
|
if (typeof val === "string") {
|
const s = val.trim();
|
if (s.startsWith("[") && s.endsWith("]")) {
|
try {
|
const arr = JSON.parse(s);
|
return Array.isArray(arr) ? arr : [];
|
} catch {
|
return [];
|
}
|
}
|
return s.split(",").map(v => v.trim()).filter(Boolean).map(v => (Number.isNaN(Number(v)) ? v : Number(v)));
|
}
|
return [];
|
};
|
|
const parseWeeklyDetail = detail => {
|
if (!detail || typeof detail !== "string" || !detail.includes(",")) return;
|
const [week, time] = detail.split(",");
|
form.week = week || "";
|
form.time = time || "";
|
};
|
|
const parseMonthlyDetail = detail => {
|
if (!detail || typeof detail !== "string" || !detail.includes(",")) return;
|
const [day, time] = detail.split(",");
|
if (day) {
|
const y = new Date().getFullYear();
|
const m = new Date().getMonth() + 1;
|
monthlyDate.value = `${y}-${String(m).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
}
|
if (time) monthlyTime.value = time;
|
};
|
|
const parseQuarterlyDetail = detail => {
|
if (!detail || typeof detail !== "string") return;
|
const parts = detail.split(",");
|
if (parts.length >= 3) {
|
const [mm, dd, time] = parts;
|
const y = new Date().getFullYear();
|
quarterlyDate.value = `${y}-${String(mm).padStart(2, "0")}-${String(dd).padStart(2, "0")}`;
|
quarterlyTime.value = time || "";
|
}
|
};
|
|
const openDialog = async (type, row) => {
|
operationType.value = type;
|
dialogVisitable.value = true;
|
resetForm();
|
try {
|
const [userRes, deviceRes] = await Promise.all([
|
userListNoPageByTenantId(),
|
getDeviceLedger(),
|
]);
|
userList.value = userRes?.data || [];
|
deviceOptions.value = deviceRes?.data || [];
|
if (type === "edit" && row) {
|
Object.assign(form, {
|
...defaultForm(),
|
...row,
|
deviceIds: normalizeIdList(row.deviceIds ?? row.taskIds ?? row.deviceIds),
|
});
|
if (!form.registrationDate && row.registrationDate) form.registrationDate = row.registrationDate;
|
if (row.registrantId) form.registrantId = row.registrantId;
|
if (row.registrant) form.registrant = row.registrant;
|
if (form.frequencyType === "WEEKLY") parseWeeklyDetail(form.frequencyDetail);
|
if (form.frequencyType === "MONTHLY") parseMonthlyDetail(form.frequencyDetail);
|
if (form.frequencyType === "QUARTERLY") parseQuarterlyDetail(form.frequencyDetail);
|
} else {
|
form.registrationDate = todayStr.value;
|
const userInfo = await userStore.getInfo();
|
if (userInfo?.user?.userId) {
|
form.registrantId = userInfo.user.userId;
|
form.registrant = userInfo.user.nickName || userInfo.user.userName || "";
|
}
|
}
|
} catch (error) {
|
uni.showToast({
|
title: "初始化失败",
|
icon: "none",
|
});
|
}
|
};
|
|
const openDevicePopup = () => {
|
showDevicePopup.value = true;
|
};
|
|
const closeDevicePopup = () => {
|
showDevicePopup.value = false;
|
};
|
|
const toggleDevice = id => {
|
const ids = form.deviceIds || [];
|
if (ids.includes(id)) {
|
form.deviceIds = ids.filter(i => i !== id);
|
} else {
|
form.deviceIds = [...ids, id];
|
}
|
};
|
|
const onInspectorChange = e => {
|
const index = Number(e?.detail?.value ?? 0);
|
const user = userList.value[index];
|
if (user) {
|
form.registrantId = user.userId;
|
form.registrant = user.nickName || "";
|
}
|
};
|
|
const onRegistrationDateChange = e => {
|
form.registrationDate = e?.detail?.value || "";
|
};
|
|
const onFrequencyChange = e => {
|
const index = Number(e?.detail?.value ?? 0);
|
const selected = frequencyOptions[index];
|
form.frequencyType = selected?.value || "";
|
form.frequencyDetail = "";
|
form.week = "";
|
form.time = "";
|
monthlyDate.value = "";
|
monthlyTime.value = "";
|
quarterlyDate.value = "";
|
quarterlyTime.value = "";
|
};
|
|
const onDailyTimeChange = e => {
|
form.frequencyDetail = e?.detail?.value || "";
|
};
|
|
const onWeekChange = e => {
|
const index = Number(e?.detail?.value ?? 0);
|
const selected = weekOptions[index];
|
form.week = selected?.value || "";
|
};
|
|
const onWeekTimeChange = e => {
|
form.time = e?.detail?.value || "";
|
};
|
|
const onMonthlyDateChange = e => {
|
monthlyDate.value = e?.detail?.value || "";
|
};
|
|
const onMonthlyTimeChange = e => {
|
monthlyTime.value = e?.detail?.value || "";
|
};
|
|
const onQuarterlyDateChange = e => {
|
quarterlyDate.value = e?.detail?.value || "";
|
};
|
|
const onQuarterlyTimeChange = e => {
|
quarterlyTime.value = e?.detail?.value || "";
|
};
|
|
const validateForm = () => {
|
if (!form.taskName?.trim()) {
|
uni.showToast({ title: "请输入任务名称", icon: "none" });
|
return false;
|
}
|
if (!form.deviceIds?.length) {
|
uni.showToast({ title: "请选择设备", icon: "none" });
|
return false;
|
}
|
if (!form.registrantId && !form.registrant) {
|
uni.showToast({ title: "请选择录入人", icon: "none" });
|
return false;
|
}
|
if (!form.registrationDate) {
|
uni.showToast({ title: "请选择登记时间", icon: "none" });
|
return false;
|
}
|
if (!form.frequencyType) {
|
uni.showToast({ title: "请选择任务频率", icon: "none" });
|
return false;
|
}
|
if (form.frequencyType === "DAILY" && !form.frequencyDetail) {
|
uni.showToast({ title: "请选择时间", icon: "none" });
|
return false;
|
}
|
if (form.frequencyType === "WEEKLY" && (!form.week || !form.time)) {
|
uni.showToast({ title: "请选择每周日期和时间", icon: "none" });
|
return false;
|
}
|
if (form.frequencyType === "MONTHLY" && (!monthlyDate.value || !monthlyTime.value)) {
|
uni.showToast({ title: "请选择每月日期和时间", icon: "none" });
|
return false;
|
}
|
if (form.frequencyType === "QUARTERLY" && (!quarterlyDate.value || !quarterlyTime.value)) {
|
uni.showToast({ title: "请选择季度日期和时间", icon: "none" });
|
return false;
|
}
|
return true;
|
};
|
|
const buildFrequencyDetail = () => {
|
if (form.frequencyType === "WEEKLY") return `${form.week},${form.time}`;
|
if (form.frequencyType === "MONTHLY") {
|
const day = monthlyDate.value.split("-")[2] || "";
|
return `${day},${monthlyTime.value}`;
|
}
|
if (form.frequencyType === "QUARTERLY") {
|
const [y, m, d] = (quarterlyDate.value || "").split("-");
|
return `${m},${d},${quarterlyTime.value}`;
|
}
|
return form.frequencyDetail || "";
|
};
|
|
const submitForm = async () => {
|
if (!validateForm()) return;
|
try {
|
const payload = {
|
id: form.id,
|
taskName: form.taskName.trim(),
|
deviceIds: JSON.stringify(form.deviceIds || []),
|
registrantId: form.registrantId,
|
registrant: form.registrant,
|
registrationDate: form.registrationDate,
|
frequencyType: form.frequencyType,
|
frequencyDetail: buildFrequencyDetail(),
|
remarks: form.remarks || "",
|
status: "0",
|
active: true,
|
deleted: 0,
|
};
|
if (operationType.value === "edit" && form.id) {
|
await deviceMaintenanceTaskEdit(payload);
|
} else {
|
delete payload.id;
|
await deviceMaintenanceTaskAdd(payload);
|
}
|
uni.showToast({
|
title: "提交成功",
|
icon: "success",
|
});
|
cancel();
|
} catch (error) {
|
uni.showToast({
|
title: "提交失败,请重试",
|
icon: "none",
|
});
|
}
|
};
|
|
const cancel = () => {
|
dialogVisitable.value = false;
|
resetForm();
|
emit("closeDia");
|
};
|
|
defineExpose({ openDialog });
|
</script>
|
|
<style scoped lang="scss">
|
:deep(.uni-picker-container) {
|
z-index: 1200 !important;
|
}
|
|
.popup-content {
|
width: 88vw;
|
max-height: 80vh;
|
background: #fff;
|
border-radius: 20rpx;
|
overflow: hidden;
|
}
|
|
.popup-title {
|
text-align: center;
|
font-size: 32rpx;
|
color: #1f1f1f;
|
font-weight: 600;
|
padding: 24rpx 20rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
}
|
|
.form-body {
|
padding: 24rpx;
|
max-height: 56vh;
|
overflow-y: auto;
|
}
|
|
.form-item {
|
margin-bottom: 20rpx;
|
}
|
|
.label {
|
display: block;
|
margin-bottom: 10rpx;
|
font-size: 24rpx;
|
color: #666;
|
}
|
|
.picker-value {
|
min-height: 72rpx;
|
background: #f7f8fa;
|
border-radius: 12rpx;
|
padding: 0 20rpx;
|
display: flex;
|
align-items: center;
|
color: #333;
|
font-size: 26rpx;
|
}
|
|
.inspector-picker {
|
justify-content: space-between;
|
}
|
|
.inspector-tags {
|
display: flex;
|
flex-wrap: wrap;
|
margin-top: 10rpx;
|
gap: 10rpx;
|
}
|
|
.inspector-tag {
|
display: flex;
|
align-items: center;
|
gap: 8rpx;
|
background: #edf3ff;
|
color: #1677ff;
|
font-size: 22rpx;
|
border-radius: 999rpx;
|
padding: 6rpx 14rpx;
|
}
|
|
.popup-footer {
|
display: flex;
|
gap: 16rpx;
|
padding: 20rpx 24rpx 24rpx;
|
border-top: 1rpx solid #f0f0f0;
|
}
|
|
.inspector-popup {
|
background: #fff;
|
border-radius: 24rpx 24rpx 0 0;
|
overflow: hidden;
|
max-height: 70vh;
|
padding-bottom: env(safe-area-inset-bottom);
|
}
|
|
.inspector-header {
|
padding: 24rpx;
|
text-align: center;
|
border-bottom: 1rpx solid #f0f0f0;
|
}
|
|
.inspector-title {
|
color: #1f1f1f;
|
font-size: 30rpx;
|
font-weight: 600;
|
}
|
|
.inspector-list {
|
max-height: 46vh;
|
padding: 0 24rpx;
|
}
|
|
.inspector-row {
|
min-height: 82rpx;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
border-bottom: 1rpx solid #f5f5f5;
|
}
|
|
.inspector-name {
|
font-size: 26rpx;
|
color: #333;
|
}
|
|
.inspector-footer {
|
display: flex;
|
gap: 16rpx;
|
padding: 20rpx 24rpx 24rpx;
|
}
|
</style>
|