| | |
| | | <template> |
| | | <view class="inspection-page"> |
| | | <!-- 顶部显示机台信息与年份选择 --> |
| | | <!-- 顶部标题和机台信息 --> |
| | | <!--设备点检--> |
| | | <view class="header"> |
| | | <view class="machine-info"> |
| | | <text>当前机台:</text> |
| | | <text class="machine-text">{{ machineNo || '未绑定' }}</text> |
| | | <view class="title-row"> |
| | | <text class="page-title">设备点检表</text> |
| | | </view> |
| | | <view class="year-picker"> |
| | | <text class="year-label">点检年份</text> |
| | | <picker mode="selector" |
| | | :range="yearOptions" |
| | | :value="yearPickerIndex" |
| | | @change="handleYearChange"> |
| | | <view class="picker-trigger">{{ currentYear }} 年</view> |
| | | </picker> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="section"> |
| | | <view class="section-title">日点检(31 天)</view> |
| | | <view class="grid days-grid"> |
| | | <view v-for="(day, index) in days" |
| | | :key="`day-${day}`" |
| | | class="grid-cell" |
| | | :class="{ checked: dailyChecks[index] }" |
| | | @click="toggleDay(index)"> |
| | | <text class="grid-text">{{ day }}日</text> |
| | | <view class="info-row"> |
| | | <view class="info-item"> |
| | | <text class="info-label">设备名称:</text> |
| | | <text class="info-value">{{ machineNo || '未绑定' }}号齿轮机</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="info-label">日期:</text> |
| | | <picker mode="date" fields="month" :value="currentDate" @change="handleDateChange"> |
| | | <view class="date-picker">{{ displayDate }}</view> |
| | | </picker> |
| | | </view> |
| | | </view> |
| | | <view class="summary">已点检:{{ checkedDaysCount }}/31</view> |
| | | </view> |
| | | |
| | | <view class="section"> |
| | | <view class="section-title">月点检(12 月)</view> |
| | | <view class="grid months-grid"> |
| | | <view v-for="(month, index) in months" |
| | | :key="`month-${month}`" |
| | | class="grid-cell" |
| | | :class="{ checked: monthlyChecks[index] }" |
| | | @click="toggleMonth(index)"> |
| | | <text class="grid-text">{{ month }}月</text> |
| | | </view> |
| | | </view> |
| | | <view class="summary">已点检:{{ checkedMonthsCount }}/12</view> |
| | | <!-- 点检表格 --> |
| | | <view class="table-container"> |
| | | <table class="inspection-table"> |
| | | <thead> |
| | | <tr> |
| | | <th class="col-category" rowspan="2">类别</th> |
| | | <th class="col-item" rowspan="2">点检、保养项目内容</th> |
| | | <th class="col-cycle" rowspan="2">周期</th> |
| | | <th class="col-days" colspan="31">日期</th> |
| | | </tr> |
| | | <tr> |
| | | <th v-for="day in 31" :key="`day-${day}`" class="day-header">{{ day }}</th> |
| | | </tr> |
| | | </thead> |
| | | <tbody> |
| | | <!-- 日常点检项目 --> |
| | | <tr v-for="(item, idx) in dailyItems" :key="`daily-${idx}`"> |
| | | <td v-if="idx === 0" :rowspan="dailyItems.length" class="category-cell">日常点检</td> |
| | | <td class="item-cell">{{ item.name }}</td> |
| | | <td class="cycle-cell">{{ item.cycle }}</td> |
| | | <td v-for="day in 31" |
| | | :key="`daily-${idx}-${day}`" |
| | | class="check-cell" |
| | | :class="{ checked: dailyChecks[idx][day - 1] }" |
| | | @click="toggleCheck('daily', idx, day - 1)"> |
| | | <text v-if="dailyChecks[idx][day - 1]">●</text> |
| | | </td> |
| | | </tr> |
| | | <!-- 月度点检项目 --> |
| | | <tr v-for="(item, idx) in monthlyItems" :key="`monthly-${idx}`"> |
| | | <td v-if="idx === 0" :rowspan="monthlyItems.length" class="category-cell">月度点检</td> |
| | | <td class="item-cell">{{ item.name }}</td> |
| | | <td class="cycle-cell">{{ item.cycle }}</td> |
| | | <td v-for="day in 31" |
| | | :key="`monthly-${idx}-${day}`" |
| | | class="check-cell" |
| | | :class="{ checked: monthlyChecks[idx][day - 1] }" |
| | | @click="toggleCheck('monthly', idx, day - 1)"> |
| | | <text v-if="monthlyChecks[idx][day - 1]">●</text> |
| | | </td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | </view> |
| | | |
| | | <!-- 底部按钮 --> |
| | | <view class="actions"> |
| | | <button class="btn-primary" |
| | | <button class="btn-save" |
| | | :loading="saving" |
| | | :disabled="saving || !machineNo" |
| | | @click="handleSave"> |
| | | 保存 |
| | | 提交 |
| | | </button> |
| | | <button class="btn-secondary" |
| | | <button class="btn-clear" |
| | | :disabled="saving" |
| | | @click="resetChecks"> |
| | | 清空 |
| | |
| | | } |
| | | }, |
| | | data() { |
| | | const currentYear = new Date().getFullYear() |
| | | const now = new Date() |
| | | const year = now.getFullYear() |
| | | const month = String(now.getMonth() + 1).padStart(2, '0') |
| | | return { |
| | | // 当前年份用于区分不同年度的点检表 |
| | | currentYear, |
| | | yearOptions: this.buildYearOptions(currentYear), |
| | | dailyChecks: Array(31).fill(false), |
| | | monthlyChecks: Array(12).fill(false), |
| | | currentDate: `${year}-${month}`, |
| | | dailyItems: [ |
| | | { name: '机芯是否清洁', cycle: '●' }, |
| | | { name: '设备开关', cycle: '●' }, |
| | | { name: '改善运行', cycle: '●' }, |
| | | { name: '清理清洁或调试是否有异常', cycle: '●' }, |
| | | { name: '工艺参数', cycle: '●' }, |
| | | { name: '机油运行是否有异常', cycle: '●' } |
| | | ], |
| | | monthlyItems: [ |
| | | { name: '电表油面是否正常是否有渗漏', cycle: '●' }, |
| | | { name: '万向接头复查并加油', cycle: '●' } |
| | | ], |
| | | dailyChecks: [], |
| | | monthlyChecks: [], |
| | | saving: false, |
| | | loading: false, |
| | | dirty: false |
| | | } |
| | | }, |
| | | computed: { |
| | | yearPickerIndex() { |
| | | const index = this.yearOptions.indexOf(this.currentYear) |
| | | return index >= 0 ? index : 0 |
| | | }, |
| | | days() { |
| | | return Array.from({ length: 31 }, (_, idx) => idx + 1) |
| | | }, |
| | | months() { |
| | | return Array.from({ length: 12 }, (_, idx) => idx + 1) |
| | | }, |
| | | checkedDaysCount() { |
| | | return this.dailyChecks.filter(Boolean).length |
| | | }, |
| | | checkedMonthsCount() { |
| | | return this.monthlyChecks.filter(Boolean).length |
| | | displayDate() { |
| | | // 格式化显示为 "2025年11月" |
| | | if (!this.currentDate) return '' |
| | | const [year, month] = this.currentDate.split('-') |
| | | return `${year}年${month}月` |
| | | } |
| | | }, |
| | | created() { |
| | | this.initChecks() |
| | | }, |
| | | watch: { |
| | | machineNo: { |
| | |
| | | } |
| | | }, |
| | | methods: { |
| | | buildYearOptions(baseYear) { |
| | | // 生成前后各两年的年份列表,方便切换历史记录 |
| | | const range = [] |
| | | for (let offset = -2; offset <= 2; offset += 1) { |
| | | range.push(baseYear + offset) |
| | | } |
| | | return range |
| | | initChecks() { |
| | | // 初始化点检数组:每个项目对应31天的勾选状态 |
| | | this.dailyChecks = this.dailyItems.map(() => Array(31).fill(false)) |
| | | this.monthlyChecks = this.monthlyItems.map(() => Array(31).fill(false)) |
| | | }, |
| | | handleYearChange(event) { |
| | | // 切换年份后读取对应的点检数据 |
| | | const index = Number(event.detail.value || 0) |
| | | const selected = this.yearOptions[index] |
| | | if (selected === this.currentYear) { |
| | | return |
| | | } |
| | | this.currentYear = selected |
| | | handleDateChange(event) { |
| | | this.currentDate = event.detail.value |
| | | this.loadInspectionData() |
| | | }, |
| | | async loadInspectionData() { |
| | | // 从后台或本地缓存加载点检记录 |
| | | if (!this.machineNo) { |
| | | return |
| | | } |
| | | if (!this.machineNo) return |
| | | |
| | | this.loading = true |
| | | try { |
| | | const record = await queryEquipmentInspection(this, { |
| | | machineNo: this.machineNo, |
| | | year: this.currentYear |
| | | date: this.currentDate |
| | | }, { mock: true, showLoading: true }) |
| | | this.dailyChecks = record.dailyChecks || Array(31).fill(false) |
| | | this.monthlyChecks = record.monthlyChecks || Array(12).fill(false) |
| | | |
| | | // 验证并设置日常点检数据 |
| | | if (record && Array.isArray(record.dailyChecks) && record.dailyChecks.length === 6) { |
| | | // 确保每个子数组都是有效的数组 |
| | | const isValid = record.dailyChecks.every(arr => Array.isArray(arr) && arr.length === 31) |
| | | if (isValid) { |
| | | // 使用 $set 确保 Vue 能够检测到嵌套数组的变化 |
| | | this.$set(this, 'dailyChecks', record.dailyChecks.map(arr => [...arr])) |
| | | } else { |
| | | console.warn('日常点检数据格式不正确,使用默认值') |
| | | this.$set(this, 'dailyChecks', this.dailyItems.map(() => Array(31).fill(false))) |
| | | } |
| | | } else { |
| | | this.$set(this, 'dailyChecks', this.dailyItems.map(() => Array(31).fill(false))) |
| | | } |
| | | |
| | | // 验证并设置月度点检数据 |
| | | if (record && Array.isArray(record.monthlyChecks) && record.monthlyChecks.length === 2) { |
| | | // 确保每个子数组都是有效的数组 |
| | | const isValid = record.monthlyChecks.every(arr => Array.isArray(arr) && arr.length === 31) |
| | | if (isValid) { |
| | | // 使用 $set 确保 Vue 能够检测到嵌套数组的变化 |
| | | this.$set(this, 'monthlyChecks', record.monthlyChecks.map(arr => [...arr])) |
| | | } else { |
| | | console.warn('月度点检数据格式不正确,使用默认值') |
| | | this.$set(this, 'monthlyChecks', this.monthlyItems.map(() => Array(31).fill(false))) |
| | | } |
| | | } else { |
| | | this.$set(this, 'monthlyChecks', this.monthlyItems.map(() => Array(31).fill(false))) |
| | | } |
| | | |
| | | this.dirty = false |
| | | } catch (error) { |
| | | console.error('加载设备点检信息失败', error) |
| | | this.$showMessage('点检记录加载失败') |
| | | this.initChecks() |
| | | } finally { |
| | | this.loading = false |
| | | } |
| | | }, |
| | | toggleDay(index) { |
| | | // 切换日点检的勾选状态 |
| | | toggleCheck(type, itemIdx, dayIdx) { |
| | | if (!this.machineNo) { |
| | | this.$showMessage('请先绑定机台') |
| | | return |
| | | } |
| | | this.$set(this.dailyChecks, index, !this.dailyChecks[index]) |
| | | this.dirty = true |
| | | }, |
| | | toggleMonth(index) { |
| | | // 切换月点检的勾选状态 |
| | | if (!this.machineNo) { |
| | | this.$showMessage('请先绑定机台') |
| | | return |
| | | |
| | | if (type === 'daily') { |
| | | // 安全检查:确保数组和索引有效 |
| | | if (!Array.isArray(this.dailyChecks) || !Array.isArray(this.dailyChecks[itemIdx])) { |
| | | console.error('日常点检数据结构异常,重新初始化') |
| | | this.initChecks() |
| | | return |
| | | } |
| | | this.$set(this.dailyChecks[itemIdx], dayIdx, !this.dailyChecks[itemIdx][dayIdx]) |
| | | } else { |
| | | // 安全检查:确保数组和索引有效 |
| | | if (!Array.isArray(this.monthlyChecks) || !Array.isArray(this.monthlyChecks[itemIdx])) { |
| | | console.error('月度点检数据结构异常,重新初始化') |
| | | this.initChecks() |
| | | return |
| | | } |
| | | this.$set(this.monthlyChecks[itemIdx], dayIdx, !this.monthlyChecks[itemIdx][dayIdx]) |
| | | } |
| | | this.$set(this.monthlyChecks, index, !this.monthlyChecks[index]) |
| | | this.dirty = true |
| | | }, |
| | | async handleSave() { |
| | | // 保存当前点检表,预留后台 POST 接口 |
| | | if (!this.machineNo) { |
| | | this.$showMessage('请先绑定机台') |
| | | return |
| | | } |
| | | if (this.saving) { |
| | | return |
| | | } |
| | | if (this.saving) return |
| | | |
| | | this.saving = true |
| | | try { |
| | | const response = await saveEquipmentInspection(this, { |
| | | machineNo: this.machineNo, |
| | | year: this.currentYear, |
| | | date: this.currentDate, |
| | | dailyChecks: this.dailyChecks, |
| | | monthlyChecks: this.monthlyChecks |
| | | }, { mock: true, showLoading: true }) |
| | |
| | | } |
| | | }, |
| | | resetChecks() { |
| | | // 一键清空勾选状态,便于重新录入 |
| | | this.dailyChecks = Array(31).fill(false) |
| | | this.monthlyChecks = Array(12).fill(false) |
| | | this.initChecks() |
| | | this.dirty = true |
| | | this.$showMessage('已清空所有点检记录') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .inspection-page { |
| | | display: flex; |
| | | flex-direction: column; |
| | | padding: 2vh 2vw; |
| | | font-size: 1.4vw; |
| | | color: #333333; |
| | | } |
| | | .inspection-page { |
| | | display: flex; |
| | | flex-direction: column; |
| | | padding: 10px; |
| | | font-size: 18px; /* 原14px,整体提升 */ |
| | | color: #333; |
| | | background: #f5f5f5; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .header { |
| | | display: flex; |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 2vh; |
| | | } |
| | | .header { |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | padding: 12px 16px; |
| | | margin-bottom: 10px; |
| | | box-shadow: 0 1px 4px rgba(0,0,0,0.1); |
| | | } |
| | | |
| | | .machine-info { |
| | | display: flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | font-size: 1.6vw; |
| | | } |
| | | .title-row { |
| | | text-align: center; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .machine-text { |
| | | font-weight: bold; |
| | | margin-left: 0.5vw; |
| | | } |
| | | .page-title { |
| | | font-size: 28px; /* 原20px */ |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .year-picker { |
| | | display: flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | .info-row { |
| | | display: flex; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | gap: 60px; |
| | | } |
| | | |
| | | .year-label { |
| | | margin-right: 1vw; |
| | | } |
| | | .info-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .picker-trigger { |
| | | border: 1px solid #d8d8d8; |
| | | border-radius: 0.8vw; |
| | | padding: 0.6vh 1.4vw; |
| | | background-color: #ffffff; |
| | | font-size: 1.4vw; |
| | | } |
| | | .info-label { |
| | | font-size: 18px; /* 原14px */ |
| | | color: #666; |
| | | } |
| | | |
| | | .section { |
| | | margin-bottom: 3vh; |
| | | background-color: #ffffff; |
| | | border-radius: 1vw; |
| | | padding: 2vh 1.5vw; |
| | | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); |
| | | } |
| | | .info-value { |
| | | font-size: 18px; /* 原14px */ |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 1.8vw; |
| | | font-weight: bold; |
| | | margin-bottom: 1.5vh; |
| | | } |
| | | .date-picker { |
| | | padding: 4px 12px; |
| | | background: #f0f0f0; |
| | | border-radius: 4px; |
| | | font-size: 18px; /* 原14px */ |
| | | cursor: pointer; |
| | | min-width: 100px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .grid { |
| | | display: grid; |
| | | grid-gap: 1vh; |
| | | } |
| | | .table-container { |
| | | flex: 1; |
| | | overflow: auto; |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | padding: 8px; |
| | | box-shadow: 0 1px 4px rgba(0,0,0,0.1); |
| | | } |
| | | |
| | | .days-grid { |
| | | grid-template-columns: repeat(7, 1fr); |
| | | } |
| | | .inspection-table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | font-size: 16px; /* 原12px */ |
| | | min-width: 1400px; |
| | | } |
| | | |
| | | .months-grid { |
| | | grid-template-columns: repeat(4, 1fr); |
| | | } |
| | | .inspection-table th, |
| | | .inspection-table td { |
| | | border: 1px solid #666; |
| | | padding: 6px 10px; /* 原4px 6px */ |
| | | text-align: center; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .grid-cell { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 6vh; |
| | | background-color: #f5f7fa; |
| | | border-radius: 0.8vw; |
| | | border: 1px solid #dcdfe6; |
| | | color: #606266; |
| | | transition: all 0.2s ease-in-out; |
| | | } |
| | | .inspection-table thead { |
| | | background: #e8e8e8; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | .grid-cell.checked { |
| | | background-color: #0faeff; |
| | | color: #ffffff; |
| | | border-color: transparent; |
| | | } |
| | | .inspection-table th { |
| | | font-weight: bold; |
| | | font-size: 18px; /* 原13px */ |
| | | background: #e8e8e8; |
| | | } |
| | | |
| | | .grid-cell:active { |
| | | transform: scale(0.98); |
| | | } |
| | | .col-category { |
| | | min-width: 80px; |
| | | width: 80px; |
| | | } |
| | | |
| | | .grid-text { |
| | | font-size: 1.4vw; |
| | | } |
| | | .col-item { |
| | | min-width: 180px; |
| | | width: 180px; |
| | | text-align: left; |
| | | } |
| | | |
| | | .summary { |
| | | margin-top: 1.5vh; |
| | | font-size: 1.3vw; |
| | | color: #909399; |
| | | } |
| | | .col-cycle { |
| | | min-width: 50px; |
| | | width: 50px; |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | flex-direction: row; |
| | | justify-content: flex-end; |
| | | gap: 1vw; |
| | | } |
| | | .col-days { |
| | | background: #d0d0d0; |
| | | } |
| | | |
| | | .btn-primary, |
| | | .btn-secondary { |
| | | min-width: 12vw; |
| | | padding: 1.2vh 1vw; |
| | | font-size: 1.4vw; |
| | | border-radius: 0.8vw; |
| | | border: none; |
| | | text-align: center; |
| | | } |
| | | .day-header { |
| | | width: 36px; /* 原30px */ |
| | | min-width: 36px; |
| | | font-size: 15px; /* 原11px */ |
| | | padding: 4px; |
| | | } |
| | | |
| | | .btn-primary { |
| | | background-color: #0faeff; |
| | | color: #ffffff; |
| | | } |
| | | .category-cell { |
| | | background: #f5f5f5; |
| | | font-weight: bold; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | .btn-secondary { |
| | | background-color: #ffffff; |
| | | color: #0faeff; |
| | | border: 1px solid #0faeff; |
| | | } |
| | | .item-cell { |
| | | text-align: left; |
| | | padding-left: 12px; /* 原8px */ |
| | | font-size: 16px; /* 原12px */ |
| | | } |
| | | |
| | | .btn-secondary:active, |
| | | .btn-primary:active { |
| | | opacity: 0.8; |
| | | } |
| | | .cycle-cell { |
| | | font-size: 20px; /* 原16px */ |
| | | color: #333; |
| | | } |
| | | |
| | | .check-cell { |
| | | width: 36px; /* 原30px */ |
| | | min-width: 36px; |
| | | height: 32px; /* 原28px */ |
| | | cursor: pointer; |
| | | background: #fff; |
| | | transition: background 0.2s; |
| | | font-size: 22px; /* 原18px */ |
| | | color: #0faeff; |
| | | } |
| | | |
| | | .check-cell:hover { |
| | | background: #f0f8ff; |
| | | } |
| | | |
| | | .check-cell.checked { |
| | | background: #e6f7ff; |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | justify-content: center; |
| | | gap: 24px; /* 原16px */ |
| | | margin-top: 10px; |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .btn-save, |
| | | .btn-clear { |
| | | min-width: 140px; /* 原120px */ |
| | | padding: 14px 32px; /* 原10px 24px */ |
| | | font-size: 20px; /* 原16px */ |
| | | border-radius: 6px; |
| | | border: none; |
| | | cursor: pointer; |
| | | transition: all 0.2s; |
| | | } |
| | | |
| | | .btn-save { |
| | | background: #00a2e9; |
| | | color: #fff; |
| | | } |
| | | |
| | | .btn-save:hover { |
| | | background: #0086c0; |
| | | } |
| | | |
| | | .btn-save:disabled { |
| | | background: #ccc; |
| | | cursor: not-allowed; |
| | | } |
| | | |
| | | .btn-clear { |
| | | background: #fff; |
| | | color: #333; |
| | | border: 1px solid #d0d0d0; |
| | | } |
| | | |
| | | .btn-clear:hover { |
| | | background: #f5f5f5; |
| | | } |
| | | |
| | | /* 针对1280*717屏幕优化 */ |
| | | @media screen and (max-width: 1280px) and (max-height: 800px) { |
| | | .inspection-page { |
| | | padding: 6px; |
| | | font-size: 15px; |
| | | } |
| | | |
| | | .header { |
| | | padding: 8px 12px; |
| | | margin-bottom: 6px; |
| | | } |
| | | |
| | | .page-title { |
| | | font-size: 22px; |
| | | } |
| | | |
| | | .info-label, |
| | | .info-value, |
| | | .date-picker { |
| | | font-size: 15px; |
| | | } |
| | | |
| | | .inspection-table { |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .inspection-table th { |
| | | font-size: 15px; |
| | | padding: 4px 6px; |
| | | } |
| | | |
| | | .inspection-table td { |
| | | padding: 4px 6px; |
| | | } |
| | | |
| | | .day-header { |
| | | width: 28px; |
| | | min-width: 28px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .check-cell { |
| | | width: 28px; |
| | | min-width: 28px; |
| | | height: 24px; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .item-cell { |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .btn-save, |
| | | .btn-clear { |
| | | min-width: 110px; |
| | | padding: 10px 20px; |
| | | font-size: 16px; |
| | | } |
| | | } |
| | | </style> |