gaoluyang
8 天以前 c58665039ce8b7c895ed4f1000ff4cf525a92085
1.设备保养开发联调
已修改2个文件
已添加3个文件
1175 ■■■■■ 文件已修改
src/pages.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/equipmentManagement/upkeep/add.vue 413 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/equipmentManagement/upkeep/index.vue 438 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/equipmentManagement/upkeep/maintain.vue 298 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages.json
@@ -328,6 +328,27 @@
          "navigationStyle": "custom"
        }
    }
    {
      "path": "pages/equipmentManagement/upkeep/index",
        "style": {
          "navigationBarTitleText": "设备保养",
          "navigationStyle": "custom"
        }
    },
    {
      "path": "pages/equipmentManagement/upkeep/add",
        "style": {
          "navigationBarTitleText": "新增保养计划",
          "navigationStyle": "custom"
        }
    },
    {
      "path": "pages/equipmentManagement/upkeep/maintain",
        "style": {
          "navigationBarTitleText": "维修保养",
          "navigationStyle": "custom"
        }
    }
  ],
  "subPackages": [
    {
src/pages/equipmentManagement/upkeep/add.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,413 @@
<template>
    <view class="upkeep-add">
        <!-- ä½¿ç”¨é€šç”¨é¡µé¢å¤´éƒ¨ç»„ä»¶ -->
        <PageHeader :title="operationType === 'edit' ? '编辑保养计划' : '新增保养计划'" @back="goBack" />
        <!-- è¡¨å•内容 -->
        <van-form @submit="sendForm" ref="formRef" label-width="110px" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center">
            <!-- åŸºæœ¬ä¿¡æ¯ -->
            <van-cell-group title="基本信息" inset>
                <van-field
                    v-model="deviceNameText"
                    label="设备名称"
                    placeholder="请选择设备名称"
                    :rules="formRules.deviceLedgerId"
                    required
                    readonly
                    @click="showDevicePicker"
                    clearable
                >
                    <template #right-icon>
                        <van-icon name="scan" @click.stop="startScan" class="scan-icon" />
                    </template>
                </van-field>
                <van-field
                    v-model="form.deviceModel"
                    label="规格型号"
                    placeholder="请输入规格型号"
                    readonly
                    clearable
                />
                <van-field
                    v-model="form.maintenancePlanTime"
                    label="计划保养日期"
                    placeholder="请选择计划保养日期"
                    :rules="formRules.maintenancePlanTime"
                    required
                    readonly
                    @click="showDatePicker"
                    clearable
                />
            </van-cell-group>
            <!-- æäº¤æŒ‰é’® -->
            <view class="footer-btns">
                <van-button class="cancel-btn" @click="goBack">取消</van-button>
                <van-button class="save-btn" native-type="submit" form-type="submit" :loading="loading">保存</van-button>
            </view>
        </van-form>
        <!-- è®¾å¤‡é€‰æ‹©å™¨ -->
        <van-popup v-model:show="showDevice" position="bottom">
            <van-picker
                :model-value="devicePickerValue"
                :columns="deviceColumns"
                @confirm="onDeviceConfirm"
                @cancel="showDevice = false"
            />
        </van-popup>
        <!-- æ—¥æœŸé€‰æ‹©å™¨ -->
        <van-popup v-model:show="showDate" position="bottom">
            <van-date-picker
                v-model="currentDate"
                title="选择日期"
                @confirm="onDateConfirm"
                @cancel="showDate = false"
            />
        </van-popup>
    </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 { addUpkeep, editUpkeep, getUpkeepById } from '@/api/equipmentManagement/upkeep';
import dayjs from "dayjs";
import { showToast } from 'vant';
defineOptions({
    name: "设备保养计划表单",
});
// è¡¨å•引用
const formRef = ref(null);
const operationType = ref('add');
const loading = ref(false);
const showDevice = ref(false);
const devicePickerValue = ref([]);
const showDate = ref(false);
const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]);
// è®¾å¤‡é€‰é¡¹
const deviceOptions = ref([]);
const deviceNameText = ref('');
// æ‰«ç ç›¸å…³çŠ¶æ€
const isScanning = ref(false);
const scanTimer = ref(null);
// è¡¨å•验证规则
const formRules = {
    deviceLedgerId: [{ required: true, trigger: "change", message: "请选择设备名称" }],
    maintenancePlanTime: [{ required: true, trigger: "change", message: "请选择计划保养日期" }],
};
// ä½¿ç”¨ ref å£°æ˜Žè¡¨å•数据
const form = ref({
    deviceLedgerId: undefined, // è®¾å¤‡ID
    deviceModel: undefined, // è§„格型号
    maintenancePlanTime: dayjs().format("YYYY-MM-DD"), // è®¡åˆ’保养日期
});
// è®¾å¤‡é€‰æ‹©å™¨åˆ—
const deviceColumns = computed(() => {
    return deviceOptions.value.map(item => ({
        text: item.deviceName,
        value: item.id
    }));
});
// åŠ è½½è®¾å¤‡åˆ—è¡¨
const loadDeviceName = async () => {
    try {
        const { data } = await getDeviceLedger();
        deviceOptions.value = data || [];
    } catch (e) {
        showToast('获取设备列表失败');
    }
};
// è®¾ç½®è®¾å¤‡è§„格型号
const setDeviceModel = (id) => {
    const option = deviceOptions.value.find((item) => item.id === id);
    if (option) {
        form.value.deviceModel = option.deviceModel;
        deviceNameText.value = option.deviceName;
    }
};
// åŠ è½½è¡¨å•æ•°æ®ï¼ˆç¼–è¾‘æ¨¡å¼ï¼‰
const loadForm = async (id) => {
    if (id) {
        operationType.value = 'edit';
        try {
            const { code, data } = await getUpkeepById(id);
            if (code == 200) {
                form.value.deviceLedgerId = data.deviceLedgerId;
                form.value.deviceModel = data.deviceModel;
                form.value.maintenancePlanTime = dayjs(data.maintenancePlanTime).format("YYYY-MM-DD");
                // è®¾ç½®è®¾å¤‡åç§°æ˜¾ç¤º
                const device = deviceOptions.value.find(item => item.id === data.deviceLedgerId);
                if (device) {
                    deviceNameText.value = device.deviceName;
                }
            }
        } catch (e) {
            showToast('获取详情失败');
        }
    } else {
        // æ–°å¢žæ¨¡å¼
        operationType.value = 'add';
    }
};
// æ¸…除表单校验状态
const clearValidate = () => {
    formRef.value?.clearValidate();
};
// é‡ç½®è¡¨å•数据和校验状态
const resetForm = () => {
    form.value = {
        deviceLedgerId: undefined,
        deviceModel: undefined,
        maintenancePlanTime: dayjs().format("YYYY-MM-DD"),
    };
    deviceNameText.value = '';
};
const resetFormAndValidate = () => {
    resetForm();
    clearValidate();
};
// æ‰«æäºŒç»´ç åŠŸèƒ½
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 handleScanResult = (scanResult) => {
    if (!scanResult) {
        showToast('扫码结果为空');
        return;
    }
    isScanning.value = true;
    showToast('扫码成功,3秒后自动填充设备信息');
    // 3秒后处理扫码结果
    scanTimer.value = setTimeout(() => {
        processScanResult(scanResult);
        isScanning.value = false;
    }, 3000);
};
// å¤„理扫码结果并匹配设备
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 showDevicePicker = () => {
    showDevice.value = true;
};
// ç¡®è®¤è®¾å¤‡é€‰æ‹©
const onDeviceConfirm = ({ selectedValues, selectedOptions }) => {
    form.value.deviceLedgerId = selectedOptions[0].value;
    devicePickerValue.value = selectedValues;
    showDevice.value = false;
    setDeviceModel(selectedOptions[0].value);
};
// æ˜¾ç¤ºæ—¥æœŸé€‰æ‹©å™¨
const showDatePicker = () => {
    showDate.value = true;
};
// ç¡®è®¤æ—¥æœŸé€‰æ‹©
const onDateConfirm = ({ selectedValues }) => {
    form.value.maintenancePlanTime = selectedValues.join('-');
    currentDate.value = selectedValues;
    showDate.value = false;
};
onShow(() => {
    // é¡µé¢æ˜¾ç¤ºæ—¶èŽ·å–å‚æ•°
    getPageParams();
});
onMounted(() => {
    // é¡µé¢åŠ è½½æ—¶èŽ·å–è®¾å¤‡åˆ—è¡¨å’Œå‚æ•°
    loadDeviceName();
    getPageParams();
});
// ç»„件卸载时清理定时器
onUnmounted(() => {
    if (scanTimer.value) {
        clearTimeout(scanTimer.value);
    }
});
// æäº¤è¡¨å•
const sendForm = async () => {
    try {
        // æ‰‹åŠ¨éªŒè¯è¡¨å•
        await formRef.value?.validate();
        loading.value = true;
        const id = getPageId();
        // å‡†å¤‡æäº¤æ•°æ®
        const submitData = { ...form.value };
        // ç¡®ä¿æ—¥æœŸæ ¼å¼æ­£ç¡®
        if (submitData.maintenancePlanTime && !submitData.maintenancePlanTime.includes(':')) {
            submitData.maintenancePlanTime = submitData.maintenancePlanTime + ' 00:00:00';
        }
        const { code } = id
            ? await editUpkeep({ id: id, ...submitData })
            : await addUpkeep(submitData);
        if (code == 200) {
            showToast(`${id ? "编辑" : "新增"}计划成功`);
            setTimeout(() => {
                uni.navigateBack();
            }, 1500);
        } else {
            loading.value = false;
        }
    } catch (e) {
        loading.value = false;
        showToast('表单验证失败');
    }
};
// è¿”回上一页
const goBack = () => {
    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();
    }
};
// èŽ·å–é¡µé¢ID
const getPageId = () => {
    const pages = getCurrentPages();
    const currentPage = pages[pages.length - 1];
    const options = currentPage.options;
    return options.id;
};
</script>
<style scoped lang="scss">
.upkeep-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;
}
.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;
}
.scan-icon {
    color: #1989fa;
    font-size: 18px;
    margin-left: 8px;
    cursor: pointer;
}
</style>
src/pages/equipmentManagement/upkeep/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,438 @@
<template>
  <view class="device-upkeep">
    <!-- ä½¿ç”¨é€šç”¨é¡µé¢å¤´éƒ¨ç»„ä»¶ -->
    <PageHeader title="设备保养" @back="goBack" />
    <!-- æœç´¢åŒºåŸŸ -->
    <view class="search-filter-section">
      <view class="search-bar">
        <view class="search-input">
          <input
            class="search-text"
            placeholder="请输入设备名称"
            v-model="searchKeyword"
            confirm-type="search"
            @confirm="getList"
          />
        </view>
        <view class="filter-button" @click="getList">
          <up-icon name="search" size="24" color="#999"></up-icon>
        </view>
      </view>
    </view>
    <!-- è®¾å¤‡ä¿å…»åˆ—表 -->
    <view class="upkeep-list" v-if="upkeepList.length > 0">
      <view v-for="(item, index) in upkeepList" :key="index">
        <view class="upkeep-item" @click="toggleSelection(item)">
          <view class="item-header">
            <view class="item-left">
              <view class="document-icon">
                <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
              </view>
              <text class="item-id">设备名称:{{ item.deviceName }}</text>
            </view>
            <view class="status-tag">
              <van-tag v-if="item.status === 1" type="success">完结</van-tag>
              <van-tag v-if="item.status === 0" type="danger">待保养</van-tag>
            </view>
          </view>
          <up-divider></up-divider>
          <view class="item-details">
            <view class="detail-row">
              <text class="detail-label">规格型号</text>
              <text class="detail-value">{{ item.deviceModel || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">计划保养日期</text>
              <text class="detail-value">{{ formatDate(item.maintenancePlanTime) || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">录入人</text>
              <text class="detail-value">{{ item.createUserName || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">录入日期</text>
              <text class="detail-value">{{ formatDateTime(item.createTime) || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">实际保养人</text>
              <text class="detail-value">{{ item.maintenanceActuallyName || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">实际保养日期</text>
              <text class="detail-value">{{ formatDateTime(item.maintenanceActuallyTime) || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">保养结果</text>
              <view class="detail-value">
                <van-tag v-if="item.maintenanceResult === 1" type="success">
                  å®Œå¥½
                </van-tag>
                <van-tag v-if="item.maintenanceResult === 0" type="danger">
                  ç»´ä¿®
                </van-tag>
                <text v-if="item.maintenanceResult === undefined || item.maintenanceResult === null">-</text>
              </view>
            </view>
          </view>
          <!-- æŒ‰é’®åŒºåŸŸ -->
          <view class="action-buttons">
            <van-button
              type="primary"
              size="small"
              class="action-btn"
              @click.stop="edit(item.id)"
            >
              ç¼–辑
            </van-button>
            <van-button
              type="warning"
              size="small"
              class="action-btn"
              :disabled="item.status === 1"
              @click.stop="addMaintain(item.id)"
            >
              ä¿å…»
            </van-button>
            <van-button
              type="danger"
              size="small"
              plain
              class="action-btn"
              @click.stop="delUpkeepByIds(item.id)"
            >
              åˆ é™¤
            </van-button>
          </view>
        </view>
      </view>
    </view>
    <view v-else class="no-data">
      <text>暂无设备保养数据</text>
    </view>
    <!-- æµ®åŠ¨æ°”æ³¡æŒ‰é’® -->
    <van-floating-bubble
      axis="xy"
      icon="plus"
      @click="addPlan"
    />
  </view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
import { getUpkeepPage, delUpkeep } from '@/api/equipmentManagement/upkeep'
import useUserStore from "@/store/modules/user"
import { showToast } from 'vant';
import dayjs from "dayjs"
const userStore = useUserStore()
// æœç´¢å…³é”®è¯
const searchKeyword = ref('')
// è®¾å¤‡ä¿å…»æ•°æ®
const upkeepList = ref([])
// å¤šé€‰åˆ—表
const multipleList = ref([])
// è¿”回上一页
const goBack = () => {
  uni.navigateBack()
}
// æ ¼å¼åŒ–日期
const formatDate = (dateStr) => {
  if (!dateStr) return ''
  return dayjs(dateStr).format("YYYY-MM-DD")
}
// æ ¼å¼åŒ–日期时间
const formatDateTime = (dateStr) => {
  if (!dateStr) return ''
  return dayjs(dateStr).format("YYYY-MM-DD HH:mm:ss")
}
// æŸ¥è¯¢åˆ—表
const getList = () => {
  const params = {
    current: -1,
    size: -1,
    deviceName: searchKeyword.value || undefined
  }
  getUpkeepPage(params)
    .then((res) => {
      // å¦‚æžœres.data不是数组,设置为空数组
      upkeepList.value = res.records || res.data?.records || []
    })
    .catch(() => {
      showToast('获取数据失败')
    })
}
// åˆ‡æ¢é€‰æ‹©çŠ¶æ€
const toggleSelection = (item) => {
  const index = multipleList.value.findIndex(selected => selected.id === item.id)
  if (index > -1) {
    multipleList.value.splice(index, 1)
  } else {
    multipleList.value.push(item)
  }
}
// æ£€æŸ¥æ˜¯å¦å·²é€‰æ‹©
const isSelected = (item) => {
  return multipleList.value.some(selected => selected.id === item.id)
}
// æ–°å¢žä¿å…» - è·³è½¬åˆ°ä¿å…»é¡µé¢
const addMaintain = (id) => {
  if (!id && multipleList.value.length !== 1) {
    showToast('请选择一条记录')
    return
  }
  const targetId = id || multipleList.value[0].id
  uni.navigateTo({
    url: `/pages/equipmentManagement/upkeep/maintain?id=${targetId}`
  })
}
// æ–°å¢žè®¡åˆ’ - è·³è½¬åˆ°æ–°å¢žé¡µé¢
const addPlan = () => {
  uni.navigateTo({
    url: '/pages/equipmentManagement/upkeep/add'
  })
}
// ç¼–辑 - è·³è½¬åˆ°add页面,通过id区分新增还是编辑
const edit = (id) => {
  if (!id) return
  uni.navigateTo({
    url: `/pages/equipmentManagement/upkeep/add?id=${id}`
  })
}
// åˆ é™¤ä¿å…»æ•°æ®
const delUpkeepByIds = async (ids) => {
  const deleteIds = Array.isArray(ids) ? ids : [ids]
  if (deleteIds.length === 0) {
    showToast('请选择要删除的记录')
    return
  }
  uni.showModal({
    title: '警告',
    content: '确认删除保养数据, æ­¤æ“ä½œä¸å¯é€†?',
    confirmText: '确定',
    cancelText: '取消',
    success: async (res) => {
      if (!res.confirm) return
      try {
        // é€ä¸ªåˆ é™¤
        for (const id of deleteIds) {
          const response = await delUpkeep(id)
          if (response.code !== 200) {
            showToast('删除失败')
            return
          }
        }
        showToast('删除成功')
        multipleList.value = []
        getList()
      } catch (e) {
        showToast('删除失败')
      }
    }
  })
}
onMounted(() => {
  getList()
})
onShow(() => {
  getList()
})
</script>
<style scoped lang="scss">
.u-divider {
  margin: 0 !important;
}
.device-upkeep {
  min-height: 100vh;
  background: #f8f9fa;
  position: relative;
  padding-bottom: 80px;
}
.search-filter-section {
  padding: 10px 20px;
  background: #ffffff;
}
.search-bar {
  display: flex;
  align-items: center;
  gap: 12px;
}
.search-input {
  flex: 1;
  background: #f5f5f5;
  border-radius: 24px;
  padding: 10px 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.search-text {
  flex: 1;
  font-size: 14px;
  color: #333;
  background: transparent;
  border: none;
  outline: none;
}
.search-text::placeholder {
  color: #999;
}
.filter-button {
  width: 40px;
  height: 40px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.action-section {
  padding: 10px 20px;
  background: #ffffff;
  border-bottom: 1px solid #f0f0f0;
}
.action-buttons {
  display: flex;
  gap: 8px;
  justify-content: flex-start;
}
.upkeep-list {
  padding: 20px;
}
.upkeep-item {
  background: #ffffff;
  border-radius: 12px;
  margin-bottom: 16px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  padding: 0 16px;
}
.item-header {
  padding: 16px 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.item-left {
  display: flex;
  align-items: center;
  gap: 8px;
}
.checkbox-wrapper {
  display: flex;
  align-items: center;
}
.document-icon {
  width: 24px;
  height: 24px;
  background: #2979ff;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.item-id {
  font-size: 14px;
  color: #333;
  font-weight: 500;
}
.status-tag {
  display: flex;
  align-items: center;
}
.item-details {
  padding: 16px 0;
}
.detail-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  margin-bottom: 8px;
  &:last-child {
    margin-bottom: 0;
  }
}
.detail-label {
  font-size: 12px;
  color: #777777;
  min-width: 80px;
}
.detail-value {
  font-size: 12px;
  color: #000000;
  text-align: right;
  flex: 1;
  margin-left: 16px;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
.detail-value.highlight {
  color: #2979ff;
  font-weight: 500;
}
.no-data {
  padding: 40px 0;
  text-align: center;
  color: #999;
}
.upkeep-item .action-buttons {
  display: flex;
  gap: 8px;
  padding: 0 0 16px 0;
  justify-content: space-between;
}
.action-btn {
  flex: 1;
}
</style>
src/pages/equipmentManagement/upkeep/maintain.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,298 @@
<template>
    <view class="upkeep-maintain">
        <!-- ä½¿ç”¨é€šç”¨é¡µé¢å¤´éƒ¨ç»„ä»¶ -->
        <PageHeader title="新增保养" @back="goBack" />
        <!-- è¡¨å•内容 -->
        <van-form @submit="sendForm" ref="formRef" label-width="110px" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center">
            <!-- åŸºæœ¬ä¿¡æ¯ -->
            <van-cell-group title="保养信息" inset>
                <van-field
                    v-model="form.maintenanceActuallyName"
                    label="实际保养人"
                    placeholder="请输入实际保养人"
                    :rules="formRules.maintenanceActuallyName"
                    required
                    clearable
                />
                <van-field
                    v-model="form.maintenanceActuallyTime"
                    label="实际保养日期"
                    placeholder="请选择实际保养日期"
                    :rules="formRules.maintenanceActuallyTime"
                    required
                    readonly
                    @click="showDatePicker"
                    clearable
                />
                <van-field
                    v-model="maintenanceResultText"
                    label="保养结果"
                    placeholder="请选择保养结果"
                    :rules="formRules.maintenanceResult"
                    required
                    readonly
                    @click="showResultPicker"
                    clearable
                />
            </van-cell-group>
            <!-- æäº¤æŒ‰é’® -->
            <view class="footer-btns">
                <van-button class="cancel-btn" @click="goBack">取消</van-button>
                <van-button class="save-btn" native-type="submit" form-type="submit" :loading="loading">保存</van-button>
            </view>
        </van-form>
        <!-- æ—¥æœŸé€‰æ‹©å™¨ -->
        <van-popup v-model:show="showDate" position="bottom">
            <van-date-picker
                v-model="currentDate"
                title="选择日期"
                @confirm="onDateConfirm"
                @cancel="showDate = false"
            />
        </van-popup>
        <!-- ä¿å…»ç»“果选择器 -->
        <van-popup v-model:show="showResult" position="bottom">
            <van-picker
                :model-value="resultPickerValue"
                :columns="resultColumns"
                @confirm="onResultConfirm"
                @cancel="showResult = false"
            />
        </van-popup>
    </view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import PageHeader from '@/components/PageHeader.vue';
import { addMaintenance } from '@/api/equipmentManagement/upkeep';
import useUserStore from "@/store/modules/user";
import dayjs from "dayjs";
import { showToast } from 'vant';
defineOptions({
    name: "设备保养表单",
});
const userStore = useUserStore();
// è¡¨å•引用
const formRef = ref(null);
const loading = ref(false);
const showDate = ref(false);
const showResult = ref(false);
const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()]);
const resultPickerValue = ref([]);
const maintenanceResultText = ref('');
// ä¿å…»ç»“果选项
const resultColumns = [
    { text: '完好', value: 1 },
    { text: 'ç»´ä¿®', value: 0 }
];
// è¡¨å•验证规则
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"), // å®žé™…保养日期(只显示日期)
});
// æ¸…除表单校验状态
const clearValidate = () => {
    // Vant4中不需要手动清除验证状态,重置表单时会自动清除
    // formRef.value?.clearValidate(); // åˆ é™¤è¿™è¡Œ
};
// é‡ç½®è¡¨å•数据和校验状态
const resetForm = () => {
    form.value = {
        maintenanceActuallyName: userStore.nickName || '',
        maintenanceResult: undefined,
        maintenanceActuallyTime: dayjs().format("YYYY-MM-DD"),
    };
    maintenanceResultText.value = '';
};
const resetFormAndValidate = () => {
    resetForm();
    // clearValidate(); // åˆ é™¤è¿™è¡Œï¼ŒVant4会自动处理
};
// æäº¤è¡¨å•
const sendForm = async () => {
    try {
        // ä½¿ç”¨Vant4的正确验证方式
        formRef.value?.validate().then(() => {
            // éªŒè¯é€šè¿‡
            submitFormData();
        }).catch((errors) => {
            // éªŒè¯å¤±è´¥
            showToast('请填写完整信息');
        });
    } 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 };
        if (submitData.maintenanceActuallyTime && !submitData.maintenanceActuallyTime.includes(':')) {
            // å¦‚æžœ maintenanceActuallyTime åªåŒ…含日期,添加当前时分秒
            submitData.maintenanceActuallyTime = submitData.maintenanceActuallyTime + ' ' + dayjs().format('HH:mm:ss');
        }
        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 = () => {
    uni.navigateBack();
};
// èŽ·å–é¡µé¢ID
const getPageId = () => {
    const pages = getCurrentPages();
    const currentPage = pages[pages.length - 1];
    const options = currentPage.options;
    return options.id;
};
// æ˜¾ç¤ºæ—¥æœŸé€‰æ‹©å™¨
const showDatePicker = () => {
    showDate.value = true;
};
// ç¡®è®¤æ—¥æœŸé€‰æ‹©
const onDateConfirm = ({ selectedValues }) => {
    // åªä¿å­˜å¹´æœˆæ—¥ï¼Œä¸åŒ…含时分秒
    form.value.maintenanceActuallyTime = selectedValues.join('-');
    currentDate.value = selectedValues;
    showDate.value = false;
};
// æ˜¾ç¤ºä¿å…»ç»“果选择器
const showResultPicker = () => {
    showResult.value = true;
};
// ç¡®è®¤ä¿å…»ç»“果选择
const onResultConfirm = ({ selectedValues, selectedOptions }) => {
    form.value.maintenanceResult = selectedOptions[0].value;
    maintenanceResultText.value = selectedOptions[0].text;
    resultPickerValue.value = selectedValues;
    showResult.value = false;
};
// åˆå§‹åŒ–表单数据
const initForm = () => {
    // è®¾ç½®ä¿å…»äººä¸ºå½“前用户昵称
    form.value.maintenanceActuallyName = userStore.nickName || '';
    // è®¾ç½®å½“前日期(只包含年月日)
    form.value.maintenanceActuallyTime = dayjs().format('YYYY-MM-DD');
    currentDate.value = [new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()];
};
onShow(() => {
    // é¡µé¢æ˜¾ç¤ºæ—¶åˆå§‹åŒ–表单
    initForm();
});
onMounted(() => {
    // é¡µé¢åŠ è½½æ—¶åˆå§‹åŒ–è¡¨å•
    initForm();
});
</script>
<style scoped lang="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;
}
</style>
src/pages/index.vue
@@ -370,6 +370,11 @@
                url: '/pages/equipmentManagement/repair/index'
            });
            break;
        case '设备保养':
            uni.navigateTo({
                url: '/pages/equipmentManagement/upkeep/index'
            });
            break;
        default:
            uni.showToast({
                title: `点击了${item.label}`,