zhangwencui
8 天以前 41acc01d0056630ac11083cf31397f2462acaf4e
src/pages/equipmentManagement/inspection/index.vue
@@ -1,8 +1,8 @@
<template>
  <view class="inspection-page">
    <!-- 使用通用页面头部组件 -->
    <PageHeader title="设备巡检" @back="goBack" />
    <PageHeader title="巡检管理"
                @back="goBack" />
    <!-- 统计信息卡片 -->
    <view class="stats-cards">
      <view class="stat-card">
@@ -18,15 +18,20 @@
        <text class="stat-label">待巡检</text>
      </view>
    </view>
    <!-- 巡检清单 -->
    <view class="inspection-list" v-if="inspectionList.length > 0">
      <view v-for="(item, index) in inspectionList" :key="index">
        <view class="inspection-item" @click="startInspection(item)">
    <view class="inspection-list"
          v-if="inspectionList.length > 0">
      <view v-for="(item, index) in inspectionList"
            :key="index">
        <view class="inspection-item"
              @click="startInspection(item)">
          <view class="item-header">
            <view class="item-left">
              <view class="device-icon" :class="getStatusClass(item.status)">
                <up-icon :name="getStatusIcon(item.status)" size="16" color="#ffffff"></up-icon>
              <view class="device-icon"
                    :class="getStatusClass(item.status)">
                <up-icon :name="getStatusIcon(item.status)"
                         size="16"
                         color="#ffffff"></up-icon>
              </view>
              <view class="device-info">
                <text class="device-name">{{ item.deviceName }}</text>
@@ -34,14 +39,13 @@
              </view>
            </view>
            <view class="status-tag">
              <u-tag :type="getTagType(item.status)" size="mini">
              <u-tag :type="getTagType(item.status)"
                     size="mini">
                {{ getStatusText(item.status) }}
              </u-tag>
            </view>
          </view>
          <up-divider></up-divider>
          <view class="item-details">
            <view class="detail-row">
              <text class="detail-label">设备编号</text>
@@ -55,472 +59,485 @@
              <text class="detail-label">负责人</text>
              <text class="detail-value">{{ item.inspector || '-' }}</text>
            </view>
            <view class="detail-row" v-if="item.status === 2">
            <view class="detail-row"
                  v-if="item.status === 2">
              <text class="detail-label">完成时间</text>
              <text class="detail-value">{{ formatDateTime(item.completedTime) || '-' }}</text>
            </view>
          </view>
          <!-- 操作按钮 -->
          <view class="action-buttons">
            <u-button
              type="primary"
              size="small"
              class="action-btn"
              :disabled="item.status === 2"
              @click.stop="startInspection(item)"
            >
            <u-button type="primary"
                      size="small"
                      class="action-btn"
                      :disabled="item.status === 2"
                      @click.stop="startInspection(item)">
              {{ item.status === 0 ? '开始巡检' : item.status === 1 ? '继续巡检' : '查看详情' }}
            </u-button>
            <u-button
              type="success"
              size="small"
              class="action-btn"
              :disabled="item.status !== 1"
              @click.stop="scanQRCode(item)"
            >
            <u-button type="success"
                      size="small"
                      class="action-btn"
                      :disabled="item.status !== 1"
                      @click.stop="scanQRCode(item)">
              扫码打卡
            </u-button>
          </view>
        </view>
      </view>
    </view>
    <view v-else class="no-data">
      <up-empty mode="data" text="暂无巡检任务"></up-empty>
    <view v-else
          class="no-data">
      <up-empty mode="data"
                text="暂无巡检任务"></up-empty>
    </view>
  </view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
// import { getInspectionList } from '@/api/inspectionUpload/index'
import dayjs from 'dayjs'
  import { ref, computed, onMounted } from "vue";
  import { onShow } from "@dcloudio/uni-app";
  import PageHeader from "@/components/PageHeader.vue";
  // import { getInspectionList } from '@/api/inspectionUpload/index'
  import dayjs from "dayjs";
// 选中的日期
const selectedDate = ref(Date.now())
  // 选中的日期
  const selectedDate = ref(Date.now());
// 巡检清单数据
const inspectionList = ref([])
  // 巡检清单数据
  const inspectionList = ref([]);
// 显示提示信息
const showToast = (message) => {
  uni.showToast({
    title: message,
    icon: 'none'
  })
}
  // 显示提示信息
  const showToast = message => {
    uni.showToast({
      title: message,
      icon: "none",
    });
  };
// 计算统计数据
const totalCount = computed(() => inspectionList.value.length)
const completedCount = computed(() => inspectionList.value.filter(item => item.status === 2).length)
const pendingCount = computed(() => inspectionList.value.filter(item => item.status === 0).length)
  // 计算统计数据
  const totalCount = computed(() => inspectionList.value.length);
  const completedCount = computed(
    () => inspectionList.value.filter(item => item.status === 2).length
  );
  const pendingCount = computed(
    () => inspectionList.value.filter(item => item.status === 0).length
  );
// 返回上一页
const goBack = () => {
  uni.navigateBack()
}
  // 返回上一页
  const goBack = () => {
    uni.navigateBack();
  };
// 日期格式化器
const dateFormatter = (type, value) => {
  if (type === 'year') {
    return `${value}年`
  }
  if (type === 'month') {
    return `${value}月`
  }
  if (type === 'day') {
    return `${value}日`
  }
  return value
}
// 格式化日期
const formatDate = (timestamp) => {
  return dayjs(timestamp).format('YYYY年MM月DD日')
}
// 格式化日期时间
const formatDateTime = (dateStr) => {
  if (!dateStr) return ''
  return dayjs(dateStr).format('MM-DD HH:mm')
}
// 日期改变事件
const onDateChange = (value) => {
  selectedDate.value = value.value
  getList()
}
// 获取状态样式类
const getStatusClass = (status) => {
  switch (status) {
    case 0: return 'status-pending'
    case 1: return 'status-progress'
    case 2: return 'status-completed'
    default: return 'status-pending'
  }
}
// 获取状态图标
const getStatusIcon = (status) => {
  switch (status) {
    case 0: return 'clock'
    case 1: return 'play-circle'
    case 2: return 'checkmark-circle'
    default: return 'clock'
  }
}
// 获取标签类型
const getTagType = (status) => {
  switch (status) {
    case 0: return 'warning'
    case 1: return 'primary'
    case 2: return 'success'
    default: return 'info'
  }
}
// 获取状态文本
const getStatusText = (status) => {
  switch (status) {
    case 0: return '待巡检'
    case 1: return '巡检中'
    case 2: return '已完成'
    default: return '未知'
  }
}
// 开始巡检
const startInspection = (item) => {
  // 存储当前巡检项目信息
  uni.setStorageSync('currentInspection', item)
  uni.navigateTo({
    url: '/pages/equipmentManagement/inspection/detail'
  })
}
// 扫码打卡
const scanQRCode = (item) => {
  uni.scanCode({
    success: (res) => {
      console.log('扫码结果:', res)
      // 验证二维码内容
      if (res.result.includes(item.deviceCode)) {
        showToast('打卡成功')
        // 更新打卡状态
        updateCheckInStatus(item.id)
      } else {
        showToast('二维码不匹配,请扫描正确的设备二维码')
      }
    },
    fail: (err) => {
      console.log('扫码失败:', err)
      showToast('扫码失败')
  // 日期格式化器
  const dateFormatter = (type, value) => {
    if (type === "year") {
      return `${value}年`;
    }
  })
}
    if (type === "month") {
      return `${value}月`;
    }
    if (type === "day") {
      return `${value}日`;
    }
    return value;
  };
// 更新打卡状态
const updateCheckInStatus = (id) => {
  // 这里应该调用API更新打卡状态
  // 暂时模拟更新本地数据
  const item = inspectionList.value.find(item => item.id === id)
  if (item) {
    item.checkInTime = new Date().toISOString()
  }
}
  // 格式化日期
  const formatDate = timestamp => {
    return dayjs(timestamp).format("YYYY年MM月DD日");
  };
// 查询巡检清单
const getList = () => {
  uni.showLoading({
    title: '加载中...',
    mask: true
  })
  const params = {
    date: dayjs(selectedDate.value).format('YYYY-MM-DD')
  }
  // 模拟数据,实际应该调用API
  setTimeout(() => {
    inspectionList.value = [
      {
        id: 1,
        deviceName: '空压机A01',
        deviceCode: 'KYJ-A01',
        location: '生产车间A区',
        inspectionTime: '08:00-09:00',
        inspector: '张三',
        status: 0, // 0:待巡检 1:巡检中 2:已完成
        completedTime: null
  // 格式化日期时间
  const formatDateTime = dateStr => {
    if (!dateStr) return "";
    return dayjs(dateStr).format("MM-DD HH:mm");
  };
  // 日期改变事件
  const onDateChange = value => {
    selectedDate.value = value.value;
    getList();
  };
  // 获取状态样式类
  const getStatusClass = status => {
    switch (status) {
      case 0:
        return "status-pending";
      case 1:
        return "status-progress";
      case 2:
        return "status-completed";
      default:
        return "status-pending";
    }
  };
  // 获取状态图标
  const getStatusIcon = status => {
    switch (status) {
      case 0:
        return "clock";
      case 1:
        return "play-circle";
      case 2:
        return "checkmark-circle";
      default:
        return "clock";
    }
  };
  // 获取标签类型
  const getTagType = status => {
    switch (status) {
      case 0:
        return "warning";
      case 1:
        return "primary";
      case 2:
        return "success";
      default:
        return "info";
    }
  };
  // 获取状态文本
  const getStatusText = status => {
    switch (status) {
      case 0:
        return "待巡检";
      case 1:
        return "巡检中";
      case 2:
        return "已完成";
      default:
        return "未知";
    }
  };
  // 开始巡检
  const startInspection = item => {
    // 存储当前巡检项目信息
    uni.setStorageSync("currentInspection", item);
    uni.navigateTo({
      url: "/pages/equipmentManagement/inspection/detail",
    });
  };
  // 扫码打卡
  const scanQRCode = item => {
    uni.scanCode({
      success: res => {
        console.log("扫码结果:", res);
        // 验证二维码内容
        if (res.result.includes(item.deviceCode)) {
          showToast("打卡成功");
          // 更新打卡状态
          updateCheckInStatus(item.id);
        } else {
          showToast("二维码不匹配,请扫描正确的设备二维码");
        }
      },
      {
        id: 2,
        deviceName: '冷却塔B02',
        deviceCode: 'LQT-B02',
        location: '生产车间B区',
        inspectionTime: '09:00-10:00',
        inspector: '李四',
        status: 1,
        completedTime: null
      fail: err => {
        console.log("扫码失败:", err);
        showToast("扫码失败");
      },
      {
        id: 3,
        deviceName: '变压器C03',
        deviceCode: 'BYQ-C03',
        location: '配电房',
        inspectionTime: '10:00-11:00',
        inspector: '王五',
        status: 2,
        completedTime: '2024-01-15T10:30:00'
      }
    ]
    uni.hideLoading()
  }, 1000)
  // 实际API调用
  // getInspectionList(params)
  //   .then((res) => {
  //     inspectionList.value = res.records || res.data?.records || []
  //     uni.hideLoading()
  //   })
  //   .catch(() => {
  //     uni.hideLoading()
  //     showToast('获取数据失败')
  //   })
}
    });
  };
onMounted(() => {
  getList()
})
  // 更新打卡状态
  const updateCheckInStatus = id => {
    // 这里应该调用API更新打卡状态
    // 暂时模拟更新本地数据
    const item = inspectionList.value.find(item => item.id === id);
    if (item) {
      item.checkInTime = new Date().toISOString();
    }
  };
onShow(() => {
  getList()
})
  // 查询巡检清单
  const getList = () => {
    uni.showLoading({
      title: "加载中...",
      mask: true,
    });
    const params = {
      date: dayjs(selectedDate.value).format("YYYY-MM-DD"),
    };
    // 模拟数据,实际应该调用API
    setTimeout(() => {
      inspectionList.value = [
        {
          id: 1,
          deviceName: "空压机A01",
          deviceCode: "KYJ-A01",
          location: "生产车间A区",
          inspectionTime: "08:00-09:00",
          inspector: "张三",
          status: 0, // 0:待巡检 1:巡检中 2:已完成
          completedTime: null,
        },
        {
          id: 2,
          deviceName: "冷却塔B02",
          deviceCode: "LQT-B02",
          location: "生产车间B区",
          inspectionTime: "09:00-10:00",
          inspector: "李四",
          status: 1,
          completedTime: null,
        },
        {
          id: 3,
          deviceName: "变压器C03",
          deviceCode: "BYQ-C03",
          location: "配电房",
          inspectionTime: "10:00-11:00",
          inspector: "王五",
          status: 2,
          completedTime: "2024-01-15T10:30:00",
        },
      ];
      uni.hideLoading();
    }, 1000);
    // 实际API调用
    // getInspectionList(params)
    //   .then((res) => {
    //     inspectionList.value = res.records || res.data?.records || []
    //     uni.hideLoading()
    //   })
    //   .catch(() => {
    //     uni.hideLoading()
    //     showToast('获取数据失败')
    //   })
  };
  onMounted(() => {
    getList();
  });
  onShow(() => {
    getList();
  });
</script>
<style scoped lang="scss">
@import '@/styles/sales-common.scss';
  @import "@/styles/sales-common.scss";
.inspection-page {
  background: #ffffff;
  min-height: 100vh;
  padding-bottom: 20px;
}
.stats-cards {
  display: flex;
  gap: 10px;
  padding: 0 15px;
  margin: 15px 0;
}
.stat-card {
  flex: 1;
  background: #ffffff;
  border-radius: 12px;
  padding: 15px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  border: 1px solid #f0f0f0;
  transition: all 0.3s ease;
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  .inspection-page {
    background: #ffffff;
    min-height: 100vh;
    padding-bottom: 20px;
  }
  .stat-number {
    font-size: 24px;
    font-weight: 700;
    color: #1890ff;
  .stats-cards {
    display: flex;
    gap: 10px;
    padding: 0 15px;
    margin: 15px 0;
  }
  .stat-label {
  .stat-card {
    flex: 1;
    background: #ffffff;
    border-radius: 12px;
    padding: 15px 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    border: 1px solid #f0f0f0;
    transition: all 0.3s ease;
    &:hover {
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }
    .stat-number {
      font-size: 24px;
      font-weight: 700;
      color: #1890ff;
    }
    .stat-label {
      font-size: 13px;
      color: #666;
      font-weight: 500;
    }
  }
  .inspection-list {
    padding: 0 15px;
  }
  .inspection-item {
    background: #ffffff;
    border-radius: 12px;
    padding: 15px;
    margin-bottom: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    border: 1px solid #f0f0f0;
    transition: all 0.3s ease;
    &:hover {
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }
  }
  .item-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 12px;
  }
  .item-left {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    flex: 1;
  }
  .device-icon {
    width: 48px;
    height: 48px;
    border-radius: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
    transition: all 0.3s ease;
    &.status-pending {
      background: linear-gradient(135deg, #faad14, #fa8c16);
      box-shadow: 0 4px 16px rgba(250, 173, 20, 0.3);
    }
    &.status-progress {
      background: linear-gradient(135deg, #1890ff, #096dd9);
      box-shadow: 0 4px 16px rgba(24, 144, 255, 0.3);
    }
    &.status-completed {
      background: linear-gradient(135deg, #52c41a, #389e0d);
      box-shadow: 0 4px 16px rgba(82, 196, 26, 0.3);
    }
    &:hover {
      transform: scale(1.05);
    }
  }
  .device-info {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .device-name {
    font-size: 18px;
    font-weight: 600;
    color: #1a1a1a;
    line-height: 1.3;
  }
  .device-location {
    font-size: 13px;
    color: #666;
    color: #8c8c8c;
    font-weight: 500;
    padding: 2px 8px;
    background: rgba(140, 140, 140, 0.1);
    border-radius: 12px;
    display: inline-block;
    width: fit-content;
  }
  .status-tag {
    flex-shrink: 0;
    transform: scale(1.1);
  }
  .item-details {
    margin: 15px 0;
    background: rgba(248, 250, 252, 0.8);
    border-radius: 12px;
    padding: 12px;
    backdrop-filter: blur(10px);
  }
  .detail-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 0;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    transition: all 0.2s ease;
    &:last-child {
      border-bottom: none;
      padding-bottom: 0;
    }
    &:hover {
      background: rgba(255, 255, 255, 0.5);
      margin: 0 -8px;
      padding-left: 8px;
      padding-right: 8px;
      border-radius: 8px;
    }
  }
  .detail-label {
    font-size: 14px;
    color: #595959;
    min-width: 80px;
    font-weight: 500;
  }
}
.inspection-list {
  padding: 0 15px;
}
.inspection-item {
  background: #ffffff;
  border-radius: 12px;
  padding: 15px;
  margin-bottom: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  border: 1px solid #f0f0f0;
  transition: all 0.3s ease;
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  .detail-value {
    font-size: 14px;
    color: #262626;
    text-align: right;
    flex: 1;
    font-weight: 500;
  }
}
.item-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 12px;
}
.item-left {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  flex: 1;
}
.device-icon {
  width: 48px;
  height: 48px;
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
  &.status-pending {
    background: linear-gradient(135deg, #faad14, #fa8c16);
    box-shadow: 0 4px 16px rgba(250, 173, 20, 0.3);
  .action-buttons {
    display: flex;
    gap: 10px;
    margin-top: 15px;
    padding-top: 15px;
    border-top: 1px solid rgba(0, 0, 0, 0.06);
  }
  &.status-progress {
    background: linear-gradient(135deg, #1890ff, #096dd9);
    box-shadow: 0 4px 16px rgba(24, 144, 255, 0.3);
  .action-btn {
    flex: 1;
    height: 44px;
    border-radius: 12px;
    font-weight: 600;
    font-size: 14px;
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    &:hover {
      transform: translateY(-2px);
      box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
    }
    &:active {
      transform: translateY(0);
    }
  }
  &.status-completed {
    background: linear-gradient(135deg, #52c41a, #389e0d);
    box-shadow: 0 4px 16px rgba(82, 196, 26, 0.3);
  .no-data {
    padding: 80px 20px;
    text-align: center;
  }
  &:hover {
    transform: scale(1.05);
  }
}
.device-info {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.device-name {
  font-size: 18px;
  font-weight: 600;
  color: #1a1a1a;
  line-height: 1.3;
}
.device-location {
  font-size: 13px;
  color: #8c8c8c;
  font-weight: 500;
  padding: 2px 8px;
  background: rgba(140, 140, 140, 0.1);
  border-radius: 12px;
  display: inline-block;
  width: fit-content;
}
.status-tag {
  flex-shrink: 0;
  transform: scale(1.1);
}
.item-details {
  margin: 15px 0;
  background: rgba(248, 250, 252, 0.8);
  border-radius: 12px;
  padding: 12px;
  backdrop-filter: blur(10px);
}
.detail-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
  transition: all 0.2s ease;
  &:last-child {
    border-bottom: none;
    padding-bottom: 0;
  }
  &:hover {
    background: rgba(255, 255, 255, 0.5);
    margin: 0 -8px;
    padding-left: 8px;
    padding-right: 8px;
    border-radius: 8px;
  }
}
.detail-label {
  font-size: 14px;
  color: #595959;
  min-width: 80px;
  font-weight: 500;
}
.detail-value {
  font-size: 14px;
  color: #262626;
  text-align: right;
  flex: 1;
  font-weight: 500;
}
.action-buttons {
  display: flex;
  gap: 10px;
  margin-top: 15px;
  padding-top: 15px;
  border-top: 1px solid rgba(0, 0, 0, 0.06);
}
.action-btn {
  flex: 1;
  height: 44px;
  border-radius: 12px;
  font-weight: 600;
  font-size: 14px;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
  }
  &:active {
    transform: translateY(0);
  }
}
.no-data {
  padding: 80px 20px;
  text-align: center;
}
</style>