|
|
@@ -23,16 +23,62 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
+ <!-- 今日排班 -->
|
|
|
+ <view v-if="schedule" class="glass-card p-4">
|
|
|
+ <view class="section-head-inner mb-3">
|
|
|
+ <view class="section-bar mr-2"></view>
|
|
|
+ <text class="section-title flex-1">今日排班</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="info-label">班组</text>
|
|
|
+ <text class="info-value">{{ schedule.teamName || '-' }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="info-label">班次</text>
|
|
|
+ <text class="info-value">{{ shiftTypeLabel(schedule.shiftType) }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="info-label">分配车辆</text>
|
|
|
+ <text class="info-value">{{ vehiclePlateNos || '未分配车辆' }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view v-else-if="!scheduleLoading" class="glass-card p-4">
|
|
|
+ <view class="empty-wrap">
|
|
|
+ <text class="empty-title">今日无有效排班</text>
|
|
|
+ <text class="empty-text">当前账号所属班组今天没有排班记录,无法选择检查车辆。</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
<!-- 车辆检查 -->
|
|
|
<view class="glass-card p-4">
|
|
|
<view class="section-head-inner mb-3">
|
|
|
<view class="section-bar mr-2"></view>
|
|
|
<text class="section-title flex-1">车辆检查</text>
|
|
|
- <text v-if="vehicleCheckStatus" class="check-state check-state-done">已完成</text>
|
|
|
+ <text v-if="todayRecord" class="check-state check-state-done">已完成</text>
|
|
|
<text v-else class="check-state check-state-pending">待检查</text>
|
|
|
</view>
|
|
|
|
|
|
- <view v-if="!vehicleCheckStatus">
|
|
|
+ <view v-if="!todayRecord">
|
|
|
+ <view class="form-row mb-3">
|
|
|
+ <text class="form-label">检查车辆</text>
|
|
|
+ <picker
|
|
|
+ v-if="vehicles.length > 0"
|
|
|
+ mode="selector"
|
|
|
+ :range="vehicleOptions"
|
|
|
+ range-key="label"
|
|
|
+ :value="selectedVehicleIndex"
|
|
|
+ @change="onVehicleChange"
|
|
|
+ >
|
|
|
+ <view class="picker-value">
|
|
|
+ <text>{{ selectedVehicle ? selectedVehicle.label : '请选择检查车辆' }}</text>
|
|
|
+ <text class="uni-icons uniui-arrowright arrow-icon"></text>
|
|
|
+ </view>
|
|
|
+ </picker>
|
|
|
+ <view v-else class="picker-value disabled">
|
|
|
+ <text>{{ schedule ? '今日未分配车辆' : '无排班信息' }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
<view class="check-group">
|
|
|
<text class="check-group-title">常规检查</text>
|
|
|
<view
|
|
|
@@ -93,10 +139,10 @@
|
|
|
hover-class="submit-btn-hover"
|
|
|
:hover-start-time="0"
|
|
|
:hover-stay-time="120"
|
|
|
- :disabled="!vehicleCheckStatus"
|
|
|
+ :disabled="!canSubmit || submitting"
|
|
|
@click="submitCheck"
|
|
|
>
|
|
|
- 提交检查
|
|
|
+ {{ submitting ? '提交中...' : '提交检查' }}
|
|
|
</button>
|
|
|
</view>
|
|
|
|
|
|
@@ -105,6 +151,7 @@
|
|
|
<text class="uni-icons uniui-checkmarkempty done-tick"></text>
|
|
|
</view>
|
|
|
<text class="done-text">今日车辆检查已完成</text>
|
|
|
+ <text v-if="todayRecord.vehicleNo" class="done-meta">检查车辆:{{ todayRecord.vehicleNo }}</text>
|
|
|
<text v-if="remark" class="done-meta">备注:{{ remark }}</text>
|
|
|
<text class="done-meta">{{ today }}</text>
|
|
|
</view>
|
|
|
@@ -150,48 +197,81 @@
|
|
|
<script setup lang="ts">
|
|
|
import { ref, computed, onMounted } from 'vue'
|
|
|
import { useTaskStore } from '../../stores/task'
|
|
|
+import { useAuthStore } from '../../stores/auth'
|
|
|
+import {
|
|
|
+ getTodaySchedule,
|
|
|
+ getTodayCheckRecord,
|
|
|
+ submitDailyCheck,
|
|
|
+ type DailyCheckScheduleToday,
|
|
|
+ type DailyCheckRecord,
|
|
|
+ type DailyCheckItem,
|
|
|
+} from '../../api/dailyCheck'
|
|
|
import TopBar from '../../components/common/TopBar.vue'
|
|
|
import StatusTag from '../../components/common/StatusTag.vue'
|
|
|
import EmptyState from '../../components/common/EmptyState.vue'
|
|
|
|
|
|
const taskStore = useTaskStore()
|
|
|
+const authStore = useAuthStore()
|
|
|
|
|
|
const today = new Date().toISOString().split('T')[0]
|
|
|
-const storageKey = `vehicle_check_${today}`
|
|
|
const remarkKey = `vehicle_check_remark_${today}`
|
|
|
|
|
|
const checkItems = [
|
|
|
- { key: 'exterior', label: '车辆外观检查' },
|
|
|
- { key: 'tire', label: '轮胎气压检查' },
|
|
|
- { key: 'fluid', label: '油液检查' },
|
|
|
- { key: 'brake', label: '刹车系统检查' },
|
|
|
- { key: 'safety', label: '安全设备检查' },
|
|
|
+ { key: 'v1', label: '车辆外观无损坏' },
|
|
|
+ { key: 'v2', label: '轮胎气压正常' },
|
|
|
+ { key: 'v3', label: '机油液位正常' },
|
|
|
+ { key: 'v4', label: '冷却液液位正常' },
|
|
|
+ { key: 'v5', label: '灯光系统正常' },
|
|
|
+ { key: 'v6', label: '刹车系统正常' },
|
|
|
+ { key: 'v7', label: '车辆清洁' },
|
|
|
]
|
|
|
|
|
|
const equipmentItems = [
|
|
|
- { key: 'pump', label: '抽水泵检查' },
|
|
|
- { key: 'hose', label: '高压水管检查' },
|
|
|
- { key: 'tool', label: '施工工具检查' },
|
|
|
- { key: 'protective', label: '防护用品检查' },
|
|
|
+ { key: 'e1', label: '高压清洗设备' },
|
|
|
+ { key: 'e2', label: '吸污设备' },
|
|
|
+ { key: 'e3', label: '疏通设备' },
|
|
|
+ { key: 'e4', label: '安全防护设备' },
|
|
|
+ { key: 'e5', label: '应急工具包' },
|
|
|
+ { key: 'e6', label: '通讯设备' },
|
|
|
]
|
|
|
|
|
|
const systemItems = [
|
|
|
- { key: 'power', label: '动力系统' },
|
|
|
- { key: 'hydraulic', label: '液压系统' },
|
|
|
- { key: 'electrical', label: '电气系统' },
|
|
|
+ { key: 's1', label: 'GPS定位系统' },
|
|
|
+ { key: 's2', label: '视频监控系统' },
|
|
|
+ { key: 's3', label: '对讲系统' },
|
|
|
]
|
|
|
|
|
|
const allItems = [...checkItems, ...equipmentItems, ...systemItems]
|
|
|
|
|
|
-const initialChecks: Record<string, boolean> = {}
|
|
|
-allItems.forEach((item) => {
|
|
|
- initialChecks[item.key] = false
|
|
|
+const scheduleLoading = ref(false)
|
|
|
+const submitting = ref(false)
|
|
|
+const schedule = ref<DailyCheckScheduleToday['schedule']>(null)
|
|
|
+const vehicles = ref<DailyCheckScheduleToday['vehicles']>([])
|
|
|
+const selectedVehicleIndex = ref(0)
|
|
|
+const checks = ref<Record<string, boolean>>({})
|
|
|
+const remark = ref('')
|
|
|
+const todayRecord = ref<DailyCheckRecord | null>(null)
|
|
|
+
|
|
|
+const userId = computed(() => authStore.user?.id ? Number(authStore.user.id) : 0)
|
|
|
+const userName = computed(() => authStore.user?.name || '')
|
|
|
+
|
|
|
+const vehicleOptions = computed(() => {
|
|
|
+ return vehicles.value.map((v) => ({
|
|
|
+ value: v.vehicleId,
|
|
|
+ label: `${v.plateNo} ${v.vehicleType ? `(${v.vehicleType})` : ''}`,
|
|
|
+ }))
|
|
|
})
|
|
|
|
|
|
-const checks = ref<Record<string, boolean>>({ ...initialChecks })
|
|
|
-const remark = ref('')
|
|
|
+const selectedVehicle = computed(() => {
|
|
|
+ return vehicleOptions.value[selectedVehicleIndex.value] || null
|
|
|
+})
|
|
|
+
|
|
|
+const vehiclePlateNos = computed(() => {
|
|
|
+ return vehicles.value.map((v) => v.plateNo).filter(Boolean).join('、')
|
|
|
+})
|
|
|
|
|
|
-const vehicleCheckStatus = computed(() => {
|
|
|
+const canSubmit = computed(() => {
|
|
|
+ if (!selectedVehicle.value) return false
|
|
|
return allItems.every((item) => checks.value[item.key])
|
|
|
})
|
|
|
|
|
|
@@ -205,19 +285,90 @@ const todayTasks = computed<any[]>(() => {
|
|
|
)
|
|
|
})
|
|
|
|
|
|
+function shiftTypeLabel(shiftType?: string) {
|
|
|
+ const map: Record<string, string> = {
|
|
|
+ day: '白班',
|
|
|
+ night: '夜班',
|
|
|
+ emergency: '应急班',
|
|
|
+ month_plan: '月计划',
|
|
|
+ }
|
|
|
+ return map[shiftType || ''] || shiftType || '-'
|
|
|
+}
|
|
|
+
|
|
|
function toggleCheck(type: string) {
|
|
|
+ if (todayRecord.value) return
|
|
|
checks.value[type] = !checks.value[type]
|
|
|
- uni.setStorageSync(storageKey, JSON.stringify(checks.value))
|
|
|
}
|
|
|
|
|
|
-function submitCheck() {
|
|
|
- if (!vehicleCheckStatus.value) {
|
|
|
- uni.showToast({ title: '请完成所有检查项', icon: 'none' })
|
|
|
+async function loadSchedule() {
|
|
|
+ if (!userId.value) return
|
|
|
+ scheduleLoading.value = true
|
|
|
+ try {
|
|
|
+ const data = await getTodaySchedule(userId.value)
|
|
|
+ schedule.value = data.schedule
|
|
|
+ vehicles.value = data.vehicles || []
|
|
|
+ selectedVehicleIndex.value = 0
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载排班失败', error)
|
|
|
+ } finally {
|
|
|
+ scheduleLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function loadTodayRecord() {
|
|
|
+ if (!userId.value) return
|
|
|
+ try {
|
|
|
+ const data = await getTodayCheckRecord(userId.value)
|
|
|
+ todayRecord.value = data || null
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载今日检查记录失败', error)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function onVehicleChange(e: any) {
|
|
|
+ selectedVehicleIndex.value = e.detail.value || 0
|
|
|
+}
|
|
|
+
|
|
|
+async function submitCheck() {
|
|
|
+ if (!canSubmit.value) {
|
|
|
+ uni.showToast({ title: '请选择车辆并完成所有检查项', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
- uni.setStorageSync(storageKey, JSON.stringify(checks.value))
|
|
|
- uni.setStorageSync(remarkKey, remark.value)
|
|
|
- uni.showToast({ title: '提交成功', icon: 'success' })
|
|
|
+ if (!selectedVehicle.value) {
|
|
|
+ uni.showToast({ title: '请选择检查车辆', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const checkItemsData: DailyCheckItem[] = allItems.map((item) => ({
|
|
|
+ id: item.key,
|
|
|
+ category: item.key.startsWith('v') ? 'vehicle' : item.key.startsWith('e') ? 'equipment' : 'system',
|
|
|
+ name: item.label,
|
|
|
+ required: true,
|
|
|
+ checked: !!checks.value[item.key],
|
|
|
+ }))
|
|
|
+
|
|
|
+ const payload: DailyCheckRecord = {
|
|
|
+ checkDate: today,
|
|
|
+ vehicleId: selectedVehicle.value.value,
|
|
|
+ vehicleNo: selectedVehicle.value.label.split(' ')[0],
|
|
|
+ userId: userId.value,
|
|
|
+ userName: userName.value,
|
|
|
+ checkItems: checkItemsData,
|
|
|
+ remark: remark.value,
|
|
|
+ }
|
|
|
+
|
|
|
+ submitting.value = true
|
|
|
+ try {
|
|
|
+ await submitDailyCheck(payload)
|
|
|
+ uni.setStorageSync(remarkKey, remark.value)
|
|
|
+ uni.showToast({ title: '提交成功', icon: 'success' })
|
|
|
+ await loadTodayRecord()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('提交检查失败', error)
|
|
|
+ uni.showToast({ title: '提交失败', icon: 'none' })
|
|
|
+ } finally {
|
|
|
+ submitting.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function goToDetail(taskId: string | number) {
|
|
|
@@ -226,19 +377,17 @@ function goToDetail(taskId: string | number) {
|
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
|
- const saved = uni.getStorageSync(storageKey)
|
|
|
- if (saved) {
|
|
|
- try {
|
|
|
- checks.value = JSON.parse(saved)
|
|
|
- } catch {
|
|
|
- // 解析失败则使用默认值
|
|
|
- }
|
|
|
- }
|
|
|
+ allItems.forEach((item) => {
|
|
|
+ checks.value[item.key] = false
|
|
|
+ })
|
|
|
+
|
|
|
const savedRemark = uni.getStorageSync(remarkKey)
|
|
|
if (savedRemark) {
|
|
|
remark.value = savedRemark
|
|
|
}
|
|
|
|
|
|
+ loadSchedule()
|
|
|
+ loadTodayRecord()
|
|
|
taskStore.fetchMyTasks(today)
|
|
|
})
|
|
|
</script>
|
|
|
@@ -327,6 +476,45 @@ onMounted(() => {
|
|
|
color: #6b7280;
|
|
|
}
|
|
|
|
|
|
+/* 排班信息 */
|
|
|
+.info-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 6px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.info-label {
|
|
|
+ width: 72px;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #6b7280;
|
|
|
+}
|
|
|
+
|
|
|
+.info-value {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #1f2937;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-wrap {
|
|
|
+ padding: 12px 0;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-title {
|
|
|
+ display: block;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #b7791f;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-text {
|
|
|
+ display: block;
|
|
|
+ margin-top: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #9ca3af;
|
|
|
+}
|
|
|
+
|
|
|
/* 检查项 */
|
|
|
.check-state {
|
|
|
font-size: 12px;
|
|
|
@@ -341,6 +529,38 @@ onMounted(() => {
|
|
|
color: #b7791f;
|
|
|
}
|
|
|
|
|
|
+.form-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 8px 0 16px;
|
|
|
+ border-bottom: 1px solid rgba(164, 216, 152, 0.18);
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.form-label {
|
|
|
+ width: 72px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #4b5563;
|
|
|
+}
|
|
|
+
|
|
|
+.picker-value {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #1f2937;
|
|
|
+}
|
|
|
+
|
|
|
+.picker-value.disabled {
|
|
|
+ color: #9ca3af;
|
|
|
+}
|
|
|
+
|
|
|
+.arrow-icon {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #9ca3af;
|
|
|
+}
|
|
|
+
|
|
|
.check-group {
|
|
|
margin-bottom: 12px;
|
|
|
padding-top: 12px;
|
|
|
@@ -482,7 +702,7 @@ onMounted(() => {
|
|
|
}
|
|
|
|
|
|
.row-hover {
|
|
|
- background-color: rgba(164, 216, 152, 0.1);
|
|
|
+ background-color: rgba(164, 216, 152, 0.3);
|
|
|
}
|
|
|
|
|
|
.icon-chip {
|