From 793391c23ba45b3dab55657ecd2448d87e17f854 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 16 九月 2025 13:49:53 +0800
Subject: [PATCH] 设备巡检、智能派单
---
src/pages/equipmentManagement/inspection/index.vue | 526 ++++++++++++
src/pages/index.vue | 19
src/pages/equipmentManagement/smartDispatch/index.vue | 801 +++++++++++++++++++
src/pages.json | 21
src/static/app-logo.png | 0
src/api/equipmentManagement/inspection.js | 220 +++++
src/config.js | 2
src/pages/equipmentManagement/inspection/detail.vue | 805 +++++++++++++++++++
src/static/images/icon/shebeixunjian@2x.png | 39
9 files changed, 2,432 insertions(+), 1 deletions(-)
diff --git a/src/api/equipmentManagement/inspection.js b/src/api/equipmentManagement/inspection.js
new file mode 100644
index 0000000..5545b4e
--- /dev/null
+++ b/src/api/equipmentManagement/inspection.js
@@ -0,0 +1,220 @@
+import request from "@/utils/request";
+
+/**
+ * @desc 鑾峰彇宸℃娓呭崟鍒楄〃
+ * @param {Object} params - 鏌ヨ鍙傛暟
+ * @param {string} params.date - 宸℃鏃ユ湡
+ * @param {string} params.inspector - 宸℃鍛�
+ * @param {number} params.status - 宸℃鐘舵�� 0:寰呭贰妫� 1:宸℃涓� 2:宸插畬鎴�
+ * @returns {Promise}
+ */
+export const getInspectionList = (params) => {
+ return request({
+ url: "/device/inspection/list",
+ method: "get",
+ params,
+ });
+};
+
+/**
+ * @desc 鑾峰彇宸℃璇︽儏
+ * @param {string|number} id - 宸℃ID
+ * @returns {Promise}
+ */
+export const getInspectionDetail = (id) => {
+ return request({
+ url: `/device/inspection/${id}`,
+ method: "get",
+ });
+};
+
+/**
+ * @desc 寮�濮嬪贰妫�
+ * @param {Object} data - 宸℃鏁版嵁
+ * @param {string|number} data.inspectionId - 宸℃ID
+ * @param {string} data.startTime - 寮�濮嬫椂闂�
+ * @returns {Promise}
+ */
+export const startInspection = (data) => {
+ return request({
+ url: "/device/inspection/start",
+ method: "post",
+ data,
+ });
+};
+
+/**
+ * @desc 鎻愪氦宸℃璁板綍
+ * @param {Object} data - 宸℃璁板綍鏁版嵁
+ * @param {string|number} data.deviceId - 璁惧ID
+ * @param {string} data.deviceCode - 璁惧缂栫爜
+ * @param {string} data.inspectionDate - 宸℃鏃ユ湡
+ * @param {string} data.inspector - 宸℃鍛�
+ * @param {string} data.scanTime - 鎵爜鏃堕棿
+ * @param {Array} data.items - 宸℃椤圭洰鍒楄〃
+ * @param {string} data.completedAt - 瀹屾垚鏃堕棿
+ * @returns {Promise}
+ */
+export const submitInspectionRecord = (data) => {
+ return request({
+ url: "/device/inspection/submit",
+ method: "post",
+ data,
+ });
+};
+
+/**
+ * @desc 鏇存柊宸℃椤圭洰
+ * @param {Object} data - 宸℃椤圭洰鏁版嵁
+ * @param {string|number} data.inspectionId - 宸℃ID
+ * @param {string|number} data.itemId - 椤圭洰ID
+ * @param {string} data.result - 宸℃缁撴灉 normal:姝e父 abnormal:寮傚父
+ * @param {string} data.abnormalDesc - 寮傚父鎻忚堪
+ * @param {Array} data.images - 鍥剧墖鍒楄〃
+ * @param {Array} data.videos - 瑙嗛鍒楄〃
+ * @param {string} data.remark - 澶囨敞
+ * @returns {Promise}
+ */
+export const updateInspectionItem = (data) => {
+ return request({
+ url: "/device/inspection/item/update",
+ method: "put",
+ data,
+ });
+};
+
+/**
+ * @desc 鎵爜鎵撳崱
+ * @param {Object} data - 鎵撳崱鏁版嵁
+ * @param {string|number} data.inspectionId - 宸℃ID
+ * @param {string} data.deviceCode - 璁惧缂栫爜
+ * @param {string} data.qrCode - 浜岀淮鐮佸唴瀹�
+ * @param {string} data.checkInTime - 鎵撳崱鏃堕棿
+ * @param {string} data.location - 鎵撳崱浣嶇疆
+ * @returns {Promise}
+ */
+export const checkInByQRCode = (data) => {
+ return request({
+ url: "/device/inspection/checkin",
+ method: "post",
+ data,
+ });
+};
+
+/**
+ * @desc 涓婁紶宸℃鏂囦欢锛堝浘鐗�/瑙嗛锛�
+ * @param {FormData} formData - 鏂囦欢鏁版嵁
+ * @returns {Promise}
+ */
+export const uploadInspectionFile = (formData) => {
+ return request({
+ url: "/device/inspection/upload",
+ method: "post",
+ data: formData,
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+};
+
+/**
+ * @desc 鑾峰彇宸℃缁熻鏁版嵁
+ * @param {Object} params - 鏌ヨ鍙傛暟
+ * @param {string} params.startDate - 寮�濮嬫棩鏈�
+ * @param {string} params.endDate - 缁撴潫鏃ユ湡
+ * @param {string} params.inspector - 宸℃鍛�
+ * @returns {Promise}
+ */
+export const getInspectionStats = (params) => {
+ return request({
+ url: "/device/inspection/stats",
+ method: "get",
+ params,
+ });
+};
+
+/**
+ * @desc 鑾峰彇宸℃鍘嗗彶璁板綍
+ * @param {Object} params - 鏌ヨ鍙傛暟
+ * @param {string|number} params.deviceId - 璁惧ID
+ * @param {number} params.current - 褰撳墠椤�
+ * @param {number} params.size - 椤甸潰澶у皬
+ * @returns {Promise}
+ */
+export const getInspectionHistory = (params) => {
+ return request({
+ url: "/device/inspection/history",
+ method: "get",
+ params,
+ });
+};
+
+/**
+ * @desc 瀵煎嚭宸℃璁板綍
+ * @param {Object} params - 瀵煎嚭鍙傛暟
+ * @param {string} params.startDate - 寮�濮嬫棩鏈�
+ * @param {string} params.endDate - 缁撴潫鏃ユ湡
+ * @param {string} params.inspector - 宸℃鍛�
+ * @param {Array} params.deviceIds - 璁惧ID鍒楄〃
+ * @returns {Promise}
+ */
+export const exportInspectionRecords = (params) => {
+ return request({
+ url: "/device/inspection/export",
+ method: "get",
+ params,
+ responseType: 'blob'
+ });
+};
+
+/**
+ * @desc 鍒犻櫎宸℃璁板綍
+ * @param {string|number} id - 宸℃璁板綍ID
+ * @returns {Promise}
+ */
+export const deleteInspectionRecord = (id) => {
+ return request({
+ url: `/device/inspection/${id}`,
+ method: "delete",
+ });
+};
+
+/**
+ * @desc 鎵归噺鍒犻櫎宸℃璁板綍
+ * @param {Array} ids - 宸℃璁板綍ID鍒楄〃
+ * @returns {Promise}
+ */
+export const batchDeleteInspectionRecords = (ids) => {
+ return request({
+ url: "/device/inspection/batch/delete",
+ method: "delete",
+ data: { ids },
+ });
+};
+
+/**
+ * @desc 鑾峰彇璁惧浜岀淮鐮�
+ * @param {string|number} deviceId - 璁惧ID
+ * @returns {Promise}
+ */
+export const getDeviceQRCode = (deviceId) => {
+ return request({
+ url: `/device/qrcode/${deviceId}`,
+ method: "get",
+ });
+};
+
+/**
+ * @desc 楠岃瘉璁惧浜岀淮鐮�
+ * @param {Object} data - 楠岃瘉鏁版嵁
+ * @param {string} data.qrCode - 浜岀淮鐮佸唴瀹�
+ * @param {string|number} data.deviceId - 璁惧ID
+ * @returns {Promise}
+ */
+export const verifyDeviceQRCode = (data) => {
+ return request({
+ url: "/device/qrcode/verify",
+ method: "post",
+ data,
+ });
+};
\ No newline at end of file
diff --git a/src/config.js b/src/config.js
index 05dff3c..d41d2bb 100644
--- a/src/config.js
+++ b/src/config.js
@@ -12,7 +12,7 @@
// 搴旂敤鐗堟湰
version: "1.1.0",
// 搴旂敤logo
- logo: "/static/logo.png",
+ logo: "/static/app-logo.png",
// 瀹樻柟缃戠珯
site_url: "http://ruoyi.vip",
// 鏀跨瓥鍗忚
diff --git a/src/pages.json b/src/pages.json
index fb03449..8eda358 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -350,6 +350,27 @@
"navigationBarTitleText": "缁翠慨淇濆吇",
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "pages/equipmentManagement/inspection/index",
+ "style": {
+ "navigationBarTitleText": "璁惧宸℃",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/equipmentManagement/inspection/detail",
+ "style": {
+ "navigationBarTitleText": "宸℃璇︽儏",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/equipmentManagement/smartDispatch/index",
+ "style": {
+ "navigationBarTitleText": "鏅鸿兘娲惧崟",
+ "navigationStyle": "custom"
+ }
}
],
"subPackages": [
diff --git a/src/pages/equipmentManagement/inspection/detail.vue b/src/pages/equipmentManagement/inspection/detail.vue
new file mode 100644
index 0000000..4b62205
--- /dev/null
+++ b/src/pages/equipmentManagement/inspection/detail.vue
@@ -0,0 +1,805 @@
+<template>
+ <view class="inspection-detail">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="璁惧宸℃璇︽儏" @back="goBack" />
+
+ <!-- 璁惧淇℃伅鍗$墖 -->
+ <view class="device-card">
+ <view class="device-header">
+ <view class="device-icon">
+ <up-icon name="settings" size="24" color="#1890ff"></up-icon>
+ </view>
+ <view class="device-info">
+ <text class="device-name">{{ deviceInfo.deviceName }}</text>
+ <text class="device-code">{{ deviceInfo.deviceCode }}</text>
+ </view>
+ <view class="qr-scan" @click="scanDeviceQR">
+ <up-icon name="scan" size="20" color="#1890ff"></up-icon>
+ <text class="scan-text">鎵爜</text>
+ </view>
+ </view>
+ <view class="device-details">
+ <view class="detail-item">
+ <text class="label">浣嶇疆锛�</text>
+ <text class="value">{{ deviceInfo.location }}</text>
+ </view>
+ <view class="detail-item">
+ <text class="label">宸℃鏃堕棿锛�</text>
+ <text class="value">{{ deviceInfo.inspectionTime }}</text>
+ </view>
+ <view class="detail-item">
+ <text class="label">璐熻矗浜猴細</text>
+ <text class="value">{{ deviceInfo.inspector }}</text>
+ </view>
+ </view>
+ </view>
+
+ <!-- 宸℃椤圭洰娓呭崟 -->
+ <view class="inspection-items">
+ <view class="section-title">
+ <up-icon name="list" size="18" color="#333"></up-icon>
+ <text class="title-text">宸℃椤圭洰娓呭崟</text>
+ <text class="progress-text">({{ completedItems }}/{{ totalItems }})</text>
+ </view>
+
+ <view class="items-list">
+ <view
+ v-for="(item, index) in inspectionItems"
+ :key="index"
+ class="inspection-item"
+ :class="{ 'completed': item.completed, 'abnormal': item.isAbnormal }"
+ >
+ <view class="item-header" @click="toggleItem(index)">
+ <view class="item-left">
+ <view class="checkbox" :class="{ 'checked': item.completed }">
+ <up-icon v-if="item.completed" name="checkmark" size="14" color="#ffffff"></up-icon>
+ </view>
+ <text class="item-name">{{ item.name }}</text>
+ </view>
+ <view class="item-status">
+ <u-tag v-if="item.isAbnormal" type="error" size="mini">寮傚父</u-tag>
+ <u-tag v-else-if="item.completed" type="success" size="mini">姝e父</u-tag>
+ <u-tag v-else type="info" size="mini">寰呮</u-tag>
+ </view>
+ </view>
+
+ <!-- 灞曞紑鐨勮鎯呭唴瀹� -->
+ <view v-if="item.expanded" class="item-content">
+ <view class="item-description">
+ <text class="desc-text">{{ item.description }}</text>
+ </view>
+
+ <!-- 宸℃缁撴灉閫夋嫨 -->
+ <view class="result-section">
+ <text class="section-label">宸℃缁撴灉锛�</text>
+ <view class="result-options">
+ <u-radio-group v-model="item.result" @change="onResultChange(index, $event)">
+ <u-radio
+ v-for="option in resultOptions"
+ :key="option.value"
+ :label="option.value"
+ :name="option.label"
+ size="small"
+ >
+ {{ option.label }}
+ </u-radio>
+ </u-radio-group>
+ </view>
+ </view>
+
+ <!-- 寮傚父鎯呭喌鎻忚堪 -->
+ <view v-if="item.result === 'abnormal'" class="abnormal-section">
+ <text class="section-label">寮傚父鎻忚堪锛�</text>
+ <up-textarea
+ v-model="item.abnormalDesc"
+ placeholder="璇疯缁嗘弿杩板紓甯告儏鍐�"
+ :maxlength="200"
+ count
+ height="80"
+ ></up-textarea>
+ </view>
+
+ <!-- 鍥剧墖涓婁紶 -->
+ <view class="upload-section">
+ <text class="section-label">鐜板満鐓х墖锛�</text>
+ <up-upload
+ :fileList="item.images"
+ @afterRead="(event) => afterRead(event, index, 'images')"
+ @delete="(event) => deleteFile(event, index, 'images')"
+ name="images"
+ multiple
+ :maxCount="5"
+ :previewImage="true"
+ >
+ <view class="upload-btn">
+ <up-icon name="camera" size="20" color="#999"></up-icon>
+ <text class="upload-text">娣诲姞鐓х墖</text>
+ </view>
+ </up-upload>
+ </view>
+
+ <!-- 瑙嗛涓婁紶 -->
+ <view class="upload-section">
+ <text class="section-label">鐜板満瑙嗛锛�</text>
+ <up-upload
+ :fileList="item.videos"
+ @afterRead="(event) => afterRead(event, index, 'videos')"
+ @delete="(event) => deleteFile(event, index, 'videos')"
+ name="videos"
+ :maxCount="2"
+ accept="video"
+ >
+ <view class="upload-btn">
+ <up-icon name="play-circle" size="20" color="#999"></up-icon>
+ <text class="upload-text">娣诲姞瑙嗛</text>
+ </view>
+ </up-upload>
+ </view>
+
+ <!-- 澶囨敞 -->
+ <view class="remark-section">
+ <text class="section-label">澶囨敞锛�</text>
+ <up-textarea
+ v-model="item.remark"
+ placeholder="璇疯緭鍏ュ娉ㄤ俊鎭紙鍙�夛級"
+ :maxlength="100"
+ count
+ height="60"
+ ></up-textarea>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 搴曢儴鎿嶄綔鎸夐挳 -->
+ <view class="bottom-actions">
+ <u-button
+ type="primary"
+ size="large"
+ :disabled="!canSubmit"
+ @click="submitInspection"
+ :loading="submitting"
+ >
+ {{ allCompleted ? '鎻愪氦宸℃璁板綍' : `缁х画宸℃ (${completedItems}/${totalItems})` }}
+ </u-button>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import { onShow } from '@dcloudio/uni-app'
+import PageHeader from '@/components/PageHeader.vue'
+import { submitInspectionRecord } from '@/api/equipmentManagement/inspection'
+import dayjs from 'dayjs'
+
+// 璁惧淇℃伅
+const deviceInfo = ref({})
+
+// 宸℃椤圭洰鍒楄〃
+const inspectionItems = ref([])
+
+// 鎻愪氦鐘舵��
+const submitting = ref(false)
+
+// 宸℃缁撴灉閫夐」
+const resultOptions = [
+ { label: '姝e父', value: 'normal' },
+ { label: '寮傚父', value: 'abnormal' }
+]
+
+// 鏄剧ず鎻愮ず淇℃伅
+const showToast = (message) => {
+ uni.showToast({
+ title: message,
+ icon: 'none'
+ })
+}
+
+// 璁$畻灞炴��
+const totalItems = computed(() => inspectionItems.value.length)
+const completedItems = computed(() => inspectionItems.value.filter(item => item.completed).length)
+const allCompleted = computed(() => completedItems.value === totalItems.value && totalItems.value > 0)
+const canSubmit = computed(() => completedItems.value > 0)
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+ if (completedItems.value > 0) {
+ uni.showModal({
+ title: '鎻愮ず',
+ content: '褰撳墠鏈夋湭淇濆瓨鐨勫贰妫�璁板綍锛岀‘瀹氳绂诲紑鍚楋紵',
+ success: (res) => {
+ if (res.confirm) {
+ uni.navigateBack()
+ }
+ }
+ })
+ } else {
+ uni.navigateBack()
+ }
+}
+
+// 鎵弿璁惧浜岀淮鐮�
+const scanDeviceQR = () => {
+ uni.scanCode({
+ success: (res) => {
+ console.log('鎵爜缁撴灉锛�', res)
+ if (res.result.includes(deviceInfo.value.deviceCode)) {
+ showToast('璁惧纭鎴愬姛')
+ // 璁板綍鎵爜鏃堕棿
+ deviceInfo.value.scanTime = new Date().toISOString()
+ } else {
+ showToast('璁惧浜岀淮鐮佷笉鍖归厤')
+ }
+ },
+ fail: (err) => {
+ console.log('鎵爜澶辫触锛�', err)
+ showToast('鎵爜澶辫触')
+ }
+ })
+}
+
+// 鍒囨崲宸℃椤圭洰
+const toggleItem = (index) => {
+ inspectionItems.value[index].expanded = !inspectionItems.value[index].expanded
+}
+
+// 宸℃缁撴灉鏀瑰彉
+const onResultChange = (index, value) => {
+ const item = inspectionItems.value[index]
+ item.result = value
+ item.completed = true
+ item.isAbnormal = value === 'abnormal'
+
+ // 濡傛灉閫夋嫨姝e父锛屾竻绌哄紓甯告弿杩�
+ if (value === 'normal') {
+ item.abnormalDesc = ''
+ }
+}
+
+// 鏂囦欢涓婁紶鍚庡鐞�
+const afterRead = async (event, index, type) => {
+ const { file } = event
+ const item = inspectionItems.value[index]
+
+ // 妯℃嫙涓婁紶杩囩▼
+ uni.showLoading({ title: '涓婁紶涓�...' })
+
+ try {
+ // 杩欓噷搴旇璋冪敤瀹為檯鐨勪笂浼燗PI
+ await new Promise(resolve => setTimeout(resolve, 1000))
+
+ // 娣诲姞鍒板搴旂殑鏂囦欢鍒楄〃
+ if (type === 'images') {
+ item.images = item.images || []
+ item.images.push({
+ url: file.url,
+ name: file.name,
+ size: file.size
+ })
+ } else if (type === 'videos') {
+ item.videos = item.videos || []
+ item.videos.push({
+ url: file.url,
+ name: file.name,
+ size: file.size
+ })
+ }
+
+ uni.hideLoading()
+ showToast('涓婁紶鎴愬姛')
+ } catch (error) {
+ uni.hideLoading()
+ showToast('涓婁紶澶辫触')
+ }
+}
+
+// 鍒犻櫎鏂囦欢
+const deleteFile = (event, index, type) => {
+ const item = inspectionItems.value[index]
+ if (type === 'images') {
+ item.images.splice(event.index, 1)
+ } else if (type === 'videos') {
+ item.videos.splice(event.index, 1)
+ }
+}
+
+// 鎻愪氦宸℃璁板綍
+const submitInspection = async () => {
+ if (!canSubmit.value) {
+ showToast('璇疯嚦灏戝畬鎴愪竴椤瑰贰妫�')
+ return
+ }
+
+ // 妫�鏌ュ紓甯搁」鐩槸鍚﹀~鍐欎簡鎻忚堪
+ const abnormalItems = inspectionItems.value.filter(item => item.isAbnormal)
+ for (const item of abnormalItems) {
+ if (!item.abnormalDesc || item.abnormalDesc.trim() === '') {
+ showToast(`璇峰~鍐�"${item.name}"鐨勫紓甯告弿杩癭)
+ return
+ }
+ }
+
+ submitting.value = true
+
+ try {
+ const recordData = {
+ deviceId: deviceInfo.value.id,
+ deviceCode: deviceInfo.value.deviceCode,
+ inspectionDate: dayjs().format('YYYY-MM-DD'),
+ inspector: deviceInfo.value.inspector,
+ scanTime: deviceInfo.value.scanTime,
+ items: inspectionItems.value.map(item => ({
+ name: item.name,
+ result: item.result,
+ completed: item.completed,
+ isAbnormal: item.isAbnormal,
+ abnormalDesc: item.abnormalDesc,
+ images: item.images || [],
+ videos: item.videos || [],
+ remark: item.remark
+ })),
+ completedAt: new Date().toISOString()
+ }
+
+ // 妯℃嫙API璋冪敤
+ await new Promise(resolve => setTimeout(resolve, 2000))
+
+ // 瀹為檯API璋冪敤
+ // await submitInspectionRecord(recordData)
+
+ showToast('宸℃璁板綍鎻愪氦鎴愬姛')
+
+ // 杩斿洖鍒楄〃椤甸潰
+ setTimeout(() => {
+ uni.navigateBack()
+ }, 1500)
+
+ } catch (error) {
+ showToast('鎻愪氦澶辫触锛岃閲嶈瘯')
+ } finally {
+ submitting.value = false
+ }
+}
+
+// 鍒濆鍖栨暟鎹�
+const initData = () => {
+ // 浠庡瓨鍌ㄤ腑鑾峰彇褰撳墠宸℃淇℃伅
+ const currentInspection = uni.getStorageSync('currentInspection')
+ if (currentInspection) {
+ deviceInfo.value = currentInspection
+ }
+
+ // 妯℃嫙宸℃椤圭洰鏁版嵁
+ inspectionItems.value = [
+ {
+ name: '璁惧澶栬妫�鏌�',
+ description: '妫�鏌ヨ澶囧瑙傛槸鍚︽湁鎹熷潖銆侀攬铓�銆佸彉褰㈢瓑寮傚父鎯呭喌',
+ completed: false,
+ expanded: false,
+ result: '',
+ isAbnormal: false,
+ abnormalDesc: '',
+ images: [],
+ videos: [],
+ remark: ''
+ },
+ {
+ name: '杩愯鐘舵�佹鏌�',
+ description: '妫�鏌ヨ澶囪繍琛屾槸鍚︽甯革紝鏈夋棤寮傚父澹伴煶銆佹尟鍔ㄧ瓑',
+ completed: false,
+ expanded: false,
+ result: '',
+ isAbnormal: false,
+ abnormalDesc: '',
+ images: [],
+ videos: [],
+ remark: ''
+ },
+ {
+ name: '瀹夊叏瑁呯疆妫�鏌�',
+ description: '妫�鏌ュ悇绫诲畨鍏ㄨ缃槸鍚﹀畬濂斤紝瀹夊叏鏍囪瘑鏄惁娓呮櫚',
+ completed: false,
+ expanded: false,
+ result: '',
+ isAbnormal: false,
+ abnormalDesc: '',
+ images: [],
+ videos: [],
+ remark: ''
+ },
+ {
+ name: '鐜鏉′欢妫�鏌�',
+ description: '妫�鏌ヨ澶囧懆鍥寸幆澧冩槸鍚︾鍚堣姹傦紝閫氶銆佺収鏄庣瓑鏄惁姝e父',
+ completed: false,
+ expanded: false,
+ result: '',
+ isAbnormal: false,
+ abnormalDesc: '',
+ images: [],
+ videos: [],
+ remark: ''
+ },
+ {
+ name: '浠〃璇绘暟璁板綍',
+ description: '璁板綍鐩稿叧浠〃鐨勮鏁帮紝妫�鏌ユ槸鍚﹀湪姝e父鑼冨洿鍐�',
+ completed: false,
+ expanded: false,
+ result: '',
+ isAbnormal: false,
+ abnormalDesc: '',
+ images: [],
+ videos: [],
+ remark: ''
+ }
+ ]
+}
+
+onMounted(() => {
+ initData()
+})
+
+onShow(() => {
+ // 椤甸潰鏄剧ず鏃跺埛鏂版暟鎹�
+})
+</script>
+
+<style scoped lang="scss">
+.inspection-detail {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ min-height: 100vh;
+ padding-bottom: 80px;
+ position: relative;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 200px;
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.8) 0%, rgba(118, 75, 162, 0.8) 100%);
+ z-index: 0;
+ }
+}
+
+.device-card {
+ background: rgba(255, 255, 255, 0.95);
+ backdrop-filter: blur(15px);
+ margin: 10px 20px;
+ border-radius: 20px;
+ padding: 24px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ position: relative;
+ z-index: 1;
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
+ }
+}
+
+.device-header {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ margin-bottom: 20px;
+}
+
+.device-icon {
+ width: 56px;
+ height: 56px;
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ border-radius: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3);
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: scale(1.05);
+ }
+}
+
+.device-info {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+.device-name {
+ font-size: 20px;
+ font-weight: 600;
+ color: #1a1a1a;
+ line-height: 1.3;
+}
+
+.device-code {
+ font-size: 13px;
+ color: #8c8c8c;
+ font-weight: 500;
+ padding: 4px 12px;
+ background: rgba(140, 140, 140, 0.1);
+ border-radius: 12px;
+ display: inline-block;
+ width: fit-content;
+}
+
+.qr-scan {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 6px;
+ padding: 12px 16px;
+ background: linear-gradient(135deg, #52c41a, #389e0d);
+ border-radius: 12px;
+ box-shadow: 0 4px 15px rgba(82, 196, 26, 0.3);
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: scale(1.05);
+ box-shadow: 0 6px 20px rgba(82, 196, 26, 0.4);
+ }
+
+ &:active {
+ transform: scale(0.98);
+ }
+}
+
+.scan-text {
+ font-size: 13px;
+ color: #ffffff;
+ font-weight: 600;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.device-details {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ background: rgba(248, 250, 252, 0.8);
+ border-radius: 16px;
+ padding: 16px;
+ backdrop-filter: blur(10px);
+}
+
+.detail-item {
+ display: flex;
+ align-items: center;
+ font-size: 14px;
+ padding: 8px 0;
+ transition: all 0.2s ease;
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.5);
+ margin: 0 -8px;
+ padding-left: 8px;
+ padding-right: 8px;
+ border-radius: 8px;
+ }
+}
+
+.label {
+ color: #595959;
+ min-width: 80px;
+ font-weight: 500;
+}
+
+.value {
+ color: #262626;
+ flex: 1;
+ font-weight: 500;
+}
+
+.inspection-items {
+ margin: 10px 20px;
+ position: relative;
+ z-index: 1;
+}
+
+.section-title {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 20px 0;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+ background: rgba(255, 255, 255, 0.95);
+ backdrop-filter: blur(15px);
+ border-radius: 16px;
+ padding: 20px;
+ margin-bottom: 16px;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+}
+
+.title-text {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1a1a1a;
+ flex: 1;
+}
+
+.progress-text {
+ font-size: 15px;
+ font-weight: 600;
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.items-list {
+ background: rgba(255, 255, 255, 0.95);
+ backdrop-filter: blur(15px);
+ border-radius: 20px;
+ overflow: hidden;
+ margin-top: 0;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.inspection-item {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+ transition: all 0.3s ease;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ &.completed {
+ background: rgba(82, 196, 26, 0.05);
+ border-left: 4px solid #52c41a;
+ }
+
+ &.abnormal {
+ background: rgba(255, 77, 79, 0.05);
+ border-left: 4px solid #ff4d4f;
+ }
+
+ &:hover {
+ background: rgba(102, 126, 234, 0.05);
+ }
+}
+
+.item-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 15px;
+ cursor: pointer;
+}
+
+.item-left {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ flex: 1;
+}
+
+.checkbox {
+ width: 20px;
+ height: 20px;
+ border: 2px solid #d9d9d9;
+ border-radius: 4px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s;
+
+ &.checked {
+ background: #52c41a;
+ border-color: #52c41a;
+ }
+}
+
+.item-name {
+ font-size: 15px;
+ color: #333;
+ font-weight: 500;
+}
+
+.item-status {
+ flex-shrink: 0;
+}
+
+.item-content {
+ padding: 0 15px 20px;
+ border-top: 1px solid #f5f5f5;
+}
+
+.item-description {
+ padding: 15px 0;
+}
+
+.desc-text {
+ font-size: 14px;
+ color: #666;
+ line-height: 1.5;
+}
+
+.result-section,
+.abnormal-section,
+.upload-section,
+.remark-section {
+ margin-top: 15px;
+}
+
+.section-label {
+ display: block;
+ font-size: 14px;
+ color: #333;
+ margin-bottom: 8px;
+ font-weight: 500;
+}
+
+.result-options {
+ display: flex;
+ gap: 20px;
+}
+
+.upload-btn {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 88px;
+ height: 88px;
+ border: 2px dashed rgba(102, 126, 234, 0.3);
+ border-radius: 16px;
+ background: rgba(102, 126, 234, 0.05);
+ gap: 8px;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: rgba(102, 126, 234, 0.5);
+ background: rgba(102, 126, 234, 0.1);
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+}
+
+.upload-text {
+ font-size: 13px;
+ color: #667eea;
+ font-weight: 500;
+}
+
+.bottom-actions {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background: rgba(255, 255, 255, 0.95);
+ backdrop-filter: blur(20px);
+ padding: 20px;
+ border-top: 1px solid rgba(255, 255, 255, 0.2);
+ box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.1);
+ z-index: 10;
+
+ button {
+ height: 48px;
+ border-radius: 16px;
+ font-weight: 600;
+ font-size: 16px;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/pages/equipmentManagement/inspection/index.vue b/src/pages/equipmentManagement/inspection/index.vue
new file mode 100644
index 0000000..d647dea
--- /dev/null
+++ b/src/pages/equipmentManagement/inspection/index.vue
@@ -0,0 +1,526 @@
+<template>
+ <view class="inspection-page">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="璁惧宸℃" @back="goBack" />
+
+ <!-- 缁熻淇℃伅鍗$墖 -->
+ <view class="stats-cards">
+ <view class="stat-card">
+ <text class="stat-number">{{ totalCount }}</text>
+ <text class="stat-label">鎬讳换鍔�</text>
+ </view>
+ <view class="stat-card">
+ <text class="stat-number">{{ completedCount }}</text>
+ <text class="stat-label">宸插畬鎴�</text>
+ </view>
+ <view class="stat-card">
+ <text class="stat-number">{{ pendingCount }}</text>
+ <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="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>
+ <view class="device-info">
+ <text class="device-name">{{ item.deviceName }}</text>
+ <text class="device-location">{{ item.location }}</text>
+ </view>
+ </view>
+ <view class="status-tag">
+ <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>
+ <text class="detail-value">{{ item.deviceCode || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">宸℃鏃堕棿</text>
+ <text class="detail-value">{{ item.inspectionTime || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">璐熻矗浜�</text>
+ <text class="detail-value">{{ item.inspector || '-' }}</text>
+ </view>
+ <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)"
+ >
+ {{ 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>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <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/equipmentManagement/inspection'
+import dayjs from 'dayjs'
+
+// 閫変腑鐨勬棩鏈�
+const selectedDate = ref(Date.now())
+
+// 宸℃娓呭崟鏁版嵁
+const inspectionList = ref([])
+
+// 鏄剧ず鎻愮ず淇℃伅
+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 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骞碝M鏈圖D鏃�')
+}
+
+// 鏍煎紡鍖栨棩鏈熸椂闂�
+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('浜岀淮鐮佷笉鍖归厤锛岃鎵弿姝g‘鐨勮澶囦簩缁寸爜')
+ }
+ },
+ fail: (err) => {
+ console.log('鎵爜澶辫触锛�', err)
+ showToast('鎵爜澶辫触')
+ }
+ })
+}
+
+// 鏇存柊鎵撳崱鐘舵��
+const updateCheckInStatus = (id) => {
+ // 杩欓噷搴旇璋冪敤API鏇存柊鎵撳崱鐘舵��
+ // 鏆傛椂妯℃嫙鏇存柊鏈湴鏁版嵁
+ const item = inspectionList.value.find(item => item.id === id)
+ if (item) {
+ item.checkInTime = new Date().toISOString()
+ }
+}
+
+// 鏌ヨ宸℃娓呭崟
+const getList = () => {
+ uni.showLoading({
+ title: '鍔犺浇涓�...',
+ mask: true
+ })
+
+ const params = {
+ date: dayjs(selectedDate.value).format('YYYY-MM-DD')
+ }
+
+ // 妯℃嫙鏁版嵁锛屽疄闄呭簲璇ヨ皟鐢ˋPI
+ setTimeout(() => {
+ inspectionList.value = [
+ {
+ id: 1,
+ deviceName: '绌哄帇鏈篈01',
+ deviceCode: 'KYJ-A01',
+ location: '鐢熶骇杞﹂棿A鍖�',
+ inspectionTime: '08:00-09:00',
+ inspector: '寮犱笁',
+ status: 0, // 0:寰呭贰妫� 1:宸℃涓� 2:宸插畬鎴�
+ completedTime: null
+ },
+ {
+ id: 2,
+ deviceName: '鍐峰嵈濉擝02',
+ deviceCode: 'LQT-B02',
+ location: '鐢熶骇杞﹂棿B鍖�',
+ inspectionTime: '09:00-10:00',
+ inspector: '鏉庡洓',
+ status: 1,
+ completedTime: null
+ },
+ {
+ id: 3,
+ deviceName: '鍙樺帇鍣–03',
+ 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';
+
+.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);
+ }
+
+ .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: #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>
\ No newline at end of file
diff --git a/src/pages/equipmentManagement/smartDispatch/index.vue b/src/pages/equipmentManagement/smartDispatch/index.vue
new file mode 100644
index 0000000..d673103
--- /dev/null
+++ b/src/pages/equipmentManagement/smartDispatch/index.vue
@@ -0,0 +1,801 @@
+<template>
+ <view class="sales-account">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="鏅鸿兘娲惧崟" @back="goBack" />
+
+ <!-- 绛涢�夊尯鍩� -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input
+ class="search-text"
+ placeholder="璇疯緭鍏ヨ澶囧悕绉版悳绱�"
+ v-model="searchKeyword"
+ @change="filterTasks"
+ clearable
+ />
+ </view>
+ <view class="filter-button" @click="filterTasks">
+ <up-icon name="search" size="24" color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+ <!-- 缁熻淇℃伅鍖哄煙 -->
+ <view class="summary-info">
+ <view class="summary-item">
+ <text class="summary-label">寰呮淳鍗曚换鍔�</text>
+ <text class="summary-value highlight">{{ pendingTasks.length }}</text>
+ </view>
+ <view class="summary-item">
+ <text class="summary-label">缁翠慨缁勬暟閲�</text>
+ <text class="summary-value">{{ repairTeams.length }}</text>
+ </view>
+ <view class="summary-item">
+ <text class="summary-label">浠婃棩宸叉淳鍗�</text>
+ <text class="summary-value">{{ todayDispatchedCount }}</text>
+ </view>
+ </view>
+
+ <!-- 寰呮淳鍗曚换鍔″垪琛� -->
+ <view class="ledger-list" v-if="filteredTasks.length > 0">
+ <view v-for="(task, index) in filteredTasks" :key="task.id">
+ <view class="ledger-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">{{ task.deviceName }}</text>
+ </view>
+ <view class="item-right">
+ <view class="item-tag" :class="getDeviceTypeClass(task.deviceType)">
+ <text class="tag-text">{{ task.deviceType }}</text>
+ </view>
+ <view class="priority-tag" :class="getPriorityClass(task.priority)">
+ <text class="tag-text">{{ getPriorityText(task.priority) }}</text>
+ </view>
+ </view>
+ </view>
+ <up-divider></up-divider>
+
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">璁惧缂栧彿</text>
+ <text class="detail-value">{{ task.deviceCode }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏁呴殰鎻忚堪</text>
+ <text class="detail-value">{{ task.faultDescription }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎶ヤ慨浜�</text>
+ <text class="detail-value">{{ task.reporterName }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎶ヤ慨鏃堕棿</text>
+ <text class="detail-value">{{ formatDate(task.reportTime) }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">棰勪及宸ユ椂</text>
+ <text class="detail-value highlight">{{ task.estimatedHours }}灏忔椂</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎵�闇�鎶�鑳�</text>
+ <text class="detail-value">{{ task.requiredSkills.join('銆�') }}</text>
+ </view>
+
+ <!-- 鎺ㄨ崘缁翠慨缁� -->
+ <view class="recommended-team" v-if="task.recommendedTeam">
+ <view class="team-header">
+ <text class="team-title">鎺ㄨ崘缁翠慨缁�</text>
+ <view class="match-score">
+ <text class="score-text">鍖归厤搴�: {{ task.recommendedTeam.matchScore }}%</text>
+ </view>
+ </view>
+ <view class="team-info">
+ <view class="detail-row">
+ <text class="detail-label">缁勫悕</text>
+ <text class="detail-value">{{ task.recommendedTeam.teamName }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">璐熻矗浜�</text>
+ <text class="detail-value">{{ task.recommendedTeam.leaderName }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">褰撳墠璐熻浇</text>
+ <text class="detail-value" :class="getWorkloadClass(task.recommendedTeam.currentWorkload)">
+ {{ task.recommendedTeam.currentWorkload }}%
+ </text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎶�鑳藉尮閰�</text>
+ <text class="detail-value">{{ task.recommendedTeam.matchedSkills.join('銆�') }}</text>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 鎿嶄綔鎸夐挳 -->
+ <view class="action-buttons">
+ <u-button
+ type="success"
+ size="small"
+ class="action-btn"
+ @click="confirmDispatch(task)"
+ :disabled="!task.recommendedTeam"
+ >
+ 纭娲惧崟
+ </u-button>
+ <u-button
+ type="primary"
+ size="small"
+ plain
+ class="action-btn"
+ @click="manualDispatch(task)"
+ >
+ 鎵嬪姩娲惧崟
+ </u-button>
+ <u-button
+ type="warning"
+ size="small"
+ plain
+ class="action-btn"
+ @click="reAnalyze(task)"
+ >
+ 閲嶆柊鍒嗘瀽
+ </u-button>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <view v-else class="no-data">
+ <text>鏆傛棤寰呮淳鍗曚换鍔�</text>
+ </view>
+
+
+
+ <!-- 鎵嬪姩娲惧崟寮圭獥 -->
+ <up-popup v-model:show="showManualDispatchPopup" mode="center" border-radius="20">
+ <view class="manual-dispatch-popup">
+ <view class="popup-header">
+ <text class="popup-title">鎵嬪姩娲惧崟</text>
+ </view>
+
+ <view class="team-list">
+ <view
+ v-for="team in availableTeams"
+ :key="team.id"
+ class="team-option"
+ :class="{ active: selectedTeamId === team.id }"
+ @click="selectedTeamId = team.id"
+ >
+ <view class="team-info">
+ <text class="team-name">{{ team.teamName }}</text>
+ <text class="team-leader">璐熻矗浜�: {{ team.leaderName }}</text>
+ <text class="team-workload" :class="getWorkloadClass(team.currentWorkload)">
+ 璐熻浇: {{ team.currentWorkload }}%
+ </text>
+ </view>
+ <view class="team-skills">
+ <text class="skills-label">鎶�鑳�:</text>
+ <text class="skills-text">{{ team.skills.join('銆�') }}</text>
+ </view>
+ </view>
+ </view>
+
+ <view class="popup-actions">
+ <u-button type="info" plain @click="showManualDispatchPopup = false">鍙栨秷</u-button>
+ <u-button type="primary" @click="confirmManualDispatch" :disabled="!selectedTeamId">纭娲惧崟</u-button>
+ </view>
+ </view>
+ </up-popup>
+
+ <!-- 娴姩鎿嶄綔鎸夐挳 -->
+ <view class="fab-button" @click="autoDispatchAll">
+ <up-icon name="plus" size="24" color="#ffffff"></up-icon>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import { onShow } from '@dcloudio/uni-app'
+import PageHeader from '@/components/PageHeader.vue'
+
+const showToast = (message) => {
+ uni.showToast({
+ title: message,
+ icon: 'none'
+ })
+}
+
+// 鎼滅储鍏抽敭璇�
+const searchKeyword = ref('')
+
+
+
+// 鎵嬪姩娲惧崟鐩稿叧
+const showManualDispatchPopup = ref(false)
+const selectedTask = ref(null)
+const selectedTeamId = ref(null)
+
+
+
+
+
+// 浠婃棩宸叉淳鍗曟暟閲�
+const todayDispatchedCount = ref(12)
+
+// 寰呮淳鍗曚换鍔℃暟鎹�
+const pendingTasks = ref([
+ {
+ id: 1,
+ deviceName: '鏁版帶杞﹀簥CK6140',
+ deviceCode: 'CNC-001',
+ deviceType: '鏈烘璁惧',
+ faultDescription: '涓昏酱寮傚搷锛屽垏鍓婄簿搴︿笅闄�',
+ reporterName: '寮犲笀鍌�',
+ reportTime: '2024-01-15 09:30:00',
+ priority: 1,
+ estimatedHours: 4,
+ requiredSkills: ['鏈烘缁翠慨', '鏁版帶鎶�鏈�'],
+ recommendedTeam: {
+ id: 1,
+ teamName: '鏈烘缁翠慨涓�缁�',
+ leaderName: '鏉庡伐绋嬪笀',
+ currentWorkload: 65,
+ matchScore: 95,
+ matchedSkills: ['鏈烘缁翠慨', '鏁版帶鎶�鏈�']
+ }
+ },
+ {
+ id: 2,
+ deviceName: '鍙橀鍣ˋBB-ACS800',
+ deviceCode: 'INV-002',
+ deviceType: '鐢垫皵璁惧',
+ faultDescription: '棰戠箒鎶ヨ锛岃緭鍑虹數鍘嬩笉绋冲畾',
+ reporterName: '鐜嬫妧鏈憳',
+ reportTime: '2024-01-15 10:15:00',
+ priority: 2,
+ estimatedHours: 3,
+ requiredSkills: ['鐢垫皵缁翠慨', '鍙橀鍣ㄦ妧鏈�'],
+ recommendedTeam: {
+ id: 2,
+ teamName: '鐢垫皵缁翠慨缁�',
+ leaderName: '璧靛伐绋嬪笀',
+ currentWorkload: 45,
+ matchScore: 88,
+ matchedSkills: ['鐢垫皵缁翠慨', '鍙橀鍣ㄦ妧鏈�']
+ }
+ },
+ {
+ id: 3,
+ deviceName: '鍘嬪姏浼犳劅鍣≒T100',
+ deviceCode: 'SEN-003',
+ deviceType: '浠〃璁惧',
+ faultDescription: '璇绘暟寮傚父锛岄浂鐐规紓绉讳弗閲�',
+ reporterName: '闄堟搷浣滃憳',
+ reportTime: '2024-01-15 11:00:00',
+ priority: 3,
+ estimatedHours: 2,
+ requiredSkills: ['浠〃缁翠慨', '浼犳劅鍣ㄦ妧鏈�'],
+ recommendedTeam: {
+ id: 3,
+ teamName: '浠〃缁翠慨缁�',
+ leaderName: '瀛欏伐绋嬪笀',
+ currentWorkload: 30,
+ matchScore: 92,
+ matchedSkills: ['浠〃缁翠慨', '浼犳劅鍣ㄦ妧鏈�']
+ }
+ },
+ {
+ id: 4,
+ deviceName: '宸ユ帶鏈篒PC-610',
+ deviceCode: 'PC-004',
+ deviceType: '璁$畻鏈鸿澶�',
+ faultDescription: '绯荤粺棰戠箒姝绘満锛岀‖鐩樻晠闅�',
+ reporterName: '鍒樼▼搴忓憳',
+ reportTime: '2024-01-15 13:45:00',
+ priority: 1,
+ estimatedHours: 5,
+ requiredSkills: ['璁$畻鏈虹淮淇�', '绯荤粺缁存姢'],
+ recommendedTeam: {
+ id: 4,
+ teamName: 'IT缁翠慨缁�',
+ leaderName: '鍛ㄥ伐绋嬪笀',
+ currentWorkload: 80,
+ matchScore: 90,
+ matchedSkills: ['璁$畻鏈虹淮淇�', '绯荤粺缁存姢']
+ }
+ },
+ {
+ id: 5,
+ deviceName: '鍙夎溅TCM-FD30',
+ deviceCode: 'VEH-005',
+ deviceType: '杞﹁締璁惧',
+ faultDescription: '娑插帇绯荤粺婕忔补锛屽崌闄嶆棤鍔�',
+ reporterName: '椹徃鏈�',
+ reportTime: '2024-01-15 14:20:00',
+ priority: 2,
+ estimatedHours: 6,
+ requiredSkills: ['杞﹁締缁翠慨', '娑插帇鎶�鏈�'],
+ recommendedTeam: {
+ id: 5,
+ teamName: '杞﹁締缁翠慨缁�',
+ leaderName: '鍚村笀鍌�',
+ currentWorkload: 55,
+ matchScore: 85,
+ matchedSkills: ['杞﹁締缁翠慨', '娑插帇鎶�鏈�']
+ }
+ }
+])
+
+// 缁翠慨缁勬暟鎹�
+const repairTeams = ref([
+ {
+ id: 1,
+ teamName: '鏈烘缁翠慨涓�缁�',
+ leaderName: '鏉庡伐绋嬪笀',
+ currentWorkload: 65,
+ skills: ['鏈烘缁翠慨', '鏁版帶鎶�鏈�', '鐒婃帴鎶�鏈�']
+ },
+ {
+ id: 2,
+ teamName: '鐢垫皵缁翠慨缁�',
+ leaderName: '璧靛伐绋嬪笀',
+ currentWorkload: 45,
+ skills: ['鐢垫皵缁翠慨', '鍙橀鍣ㄦ妧鏈�', 'PLC缂栫▼']
+ },
+ {
+ id: 3,
+ teamName: '浠〃缁翠慨缁�',
+ leaderName: '瀛欏伐绋嬪笀',
+ currentWorkload: 30,
+ skills: ['浠〃缁翠慨', '浼犳劅鍣ㄦ妧鏈�', '鑷姩鍖栨帶鍒�']
+ },
+ {
+ id: 4,
+ teamName: 'IT缁翠慨缁�',
+ leaderName: '鍛ㄥ伐绋嬪笀',
+ currentWorkload: 80,
+ skills: ['璁$畻鏈虹淮淇�', '绯荤粺缁存姢', '缃戠粶鎶�鏈�']
+ },
+ {
+ id: 5,
+ teamName: '杞﹁締缁翠慨缁�',
+ leaderName: '鍚村笀鍌�',
+ currentWorkload: 55,
+ skills: ['杞﹁締缁翠慨', '娑插帇鎶�鏈�', '鍙戝姩鏈虹淮淇�']
+ },
+ {
+ id: 6,
+ teamName: '鏈烘缁翠慨浜岀粍',
+ leaderName: '閽卞伐绋嬪笀',
+ currentWorkload: 40,
+ skills: ['鏈烘缁翠慨', '绮惧瘑鍔犲伐', '璁惧璋冭瘯']
+ }
+])
+
+// 璁$畻鍙敤缁翠慨缁勶紙鎺掗櫎褰撳墠鎺ㄨ崘鐨勭粍锛�
+const availableTeams = computed(() => {
+ if (!selectedTask.value) return repairTeams.value
+ return repairTeams.value.filter(team =>
+ !selectedTask.value.recommendedTeam || team.id !== selectedTask.value.recommendedTeam.id
+ )
+})
+
+// 绛涢�夊悗鐨勪换鍔″垪琛�
+const filteredTasks = computed(() => {
+ let tasks = pendingTasks.value
+
+ // 鎼滅储鍏抽敭璇嶇瓫閫�
+ if (searchKeyword.value) {
+ tasks = tasks.filter(task =>
+ task.deviceName.includes(searchKeyword.value) ||
+ task.deviceCode.includes(searchKeyword.value) ||
+ task.faultDescription.includes(searchKeyword.value)
+ )
+ }
+
+
+
+
+
+ return tasks
+})
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+ uni.navigateBack()
+}
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (dateStr) => {
+ if (!dateStr) return ''
+ const date = new Date(dateStr)
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ const hours = String(date.getHours()).padStart(2, '0')
+ const minutes = String(date.getMinutes()).padStart(2, '0')
+ return `${month}-${day} ${hours}:${minutes}`
+}
+
+// 鑾峰彇璁惧绫诲瀷鏍峰紡绫�
+const getDeviceTypeClass = (deviceType) => {
+ const typeMap = {
+ '鏈烘璁惧': 'tag-mechanical',
+ '鐢垫皵璁惧': 'tag-electric',
+ '浠〃璁惧': 'tag-instrument',
+ '璁$畻鏈鸿澶�': 'tag-computer',
+ '杞﹁締璁惧': 'tag-vehicle',
+ '鍏朵粬璁惧': 'tag-other'
+ }
+ return typeMap[deviceType] || 'tag-unknown'
+}
+
+// 鑾峰彇浼樺厛绾ф牱寮忕被
+const getPriorityClass = (priority) => {
+ const priorityMap = {
+ 1: 'priority-urgent',
+ 2: 'priority-high',
+ 3: 'priority-medium',
+ 4: 'priority-low'
+ }
+ return priorityMap[priority] || 'priority-medium'
+}
+
+// 鑾峰彇浼樺厛绾ф枃鏈�
+const getPriorityText = (priority) => {
+ const priorityMap = {
+ 1: '绱ф��',
+ 2: '楂�',
+ 3: '涓�',
+ 4: '浣�'
+ }
+ return priorityMap[priority] || '涓�'
+}
+
+// 鑾峰彇宸ヤ綔璐熻浇鏍峰紡绫�
+const getWorkloadClass = (workload) => {
+ if (workload >= 80) return 'danger'
+ if (workload >= 60) return 'highlight'
+ return ''
+}
+
+// 绛涢�変换鍔�
+const filterTasks = () => {
+ // 鎼滅储閫昏緫宸插湪璁$畻灞炴�ilteredTasks涓鐞�
+ // 褰搒earchKeyword鍙樺寲鏃讹紝璁$畻灞炴�т細鑷姩閲嶆柊璁$畻
+ // 杩欓噷鍙互娣诲姞棰濆鐨勭瓫閫夐�昏緫鎴栨彁绀�
+ if (searchKeyword.value && filteredTasks.value.length === 0) {
+ // 濡傛灉鏈夋悳绱㈠叧閿瘝浣嗘病鏈夌粨鏋滐紝鍙互缁欑敤鎴锋彁绀�
+ console.log('鏈壘鍒板尮閰嶇殑璁惧浠诲姟')
+ }
+}
+
+
+
+
+
+// 纭娲惧崟
+const confirmDispatch = (task) => {
+ uni.showModal({
+ title: '纭娲惧崟',
+ content: `纭灏嗕换鍔�"${task.deviceName}"娲剧粰"${task.recommendedTeam.teamName}"锛焋,
+ confirmText: '纭',
+ cancelText: '鍙栨秷',
+ success: (res) => {
+ if (res.confirm) {
+ // 妯℃嫙娲惧崟鎴愬姛
+ const index = pendingTasks.value.findIndex(t => t.id === task.id)
+ if (index > -1) {
+ pendingTasks.value.splice(index, 1)
+ todayDispatchedCount.value++
+ showToast('娲惧崟鎴愬姛')
+ }
+ }
+ }
+ })
+}
+
+// 鎵嬪姩娲惧崟
+const manualDispatch = (task) => {
+ selectedTask.value = task
+ selectedTeamId.value = null
+ showManualDispatchPopup.value = true
+}
+
+// 纭鎵嬪姩娲惧崟
+const confirmManualDispatch = () => {
+ if (!selectedTeamId.value) {
+ showToast('璇烽�夋嫨缁翠慨缁�')
+ return
+ }
+
+ const selectedTeam = repairTeams.value.find(team => team.id === selectedTeamId.value)
+ if (!selectedTeam) {
+ showToast('缁翠慨缁勪笉瀛樺湪')
+ return
+ }
+
+ // 鍏堝叧闂墜鍔ㄦ淳鍗曞脊妗�
+ showManualDispatchPopup.value = false
+
+ uni.showModal({
+ title: '纭娲惧崟',
+ content: `纭灏嗕换鍔�"${selectedTask.value.deviceName}"娲剧粰"${selectedTeam.teamName}"锛焋,
+ confirmText: '纭',
+ cancelText: '鍙栨秷',
+ success: (res) => {
+ if (res.confirm) {
+ // 妯℃嫙娲惧崟鎴愬姛
+ const index = pendingTasks.value.findIndex(t => t.id === selectedTask.value.id)
+ if (index > -1) {
+ pendingTasks.value.splice(index, 1)
+ todayDispatchedCount.value++
+ showToast('娲惧崟鎴愬姛')
+ }
+ }
+ }
+ })
+}
+
+// 閲嶆柊鍒嗘瀽
+const reAnalyze = (task) => {
+ showToast('姝e湪閲嶆柊鍒嗘瀽...')
+
+ // 妯℃嫙閲嶆柊鍒嗘瀽杩囩▼
+ setTimeout(() => {
+ // 闅忔満璋冩暣鍖归厤搴﹀拰鎺ㄨ崘缁�
+ const teams = repairTeams.value.filter(team =>
+ team.skills.some(skill => task.requiredSkills.includes(skill))
+ )
+
+ if (teams.length > 0) {
+ const randomTeam = teams[Math.floor(Math.random() * teams.length)]
+ const matchedSkills = randomTeam.skills.filter(skill => task.requiredSkills.includes(skill))
+ const matchScore = Math.floor(Math.random() * 20) + 80 // 80-99鐨勯殢鏈哄尮閰嶅害
+
+ task.recommendedTeam = {
+ id: randomTeam.id,
+ teamName: randomTeam.teamName,
+ leaderName: randomTeam.leaderName,
+ currentWorkload: randomTeam.currentWorkload,
+ matchScore: matchScore,
+ matchedSkills: matchedSkills
+ }
+
+ showToast('閲嶆柊鍒嗘瀽瀹屾垚')
+ } else {
+ showToast('鏈壘鍒板悎閫傜殑缁翠慨缁�')
+ }
+ }, 1500)
+}
+
+// 涓�閿嚜鍔ㄦ淳鍗�
+const autoDispatchAll = () => {
+ const tasksWithRecommendation = filteredTasks.value.filter(task => task.recommendedTeam)
+
+ if (tasksWithRecommendation.length === 0) {
+ showToast('娌℃湁鍙嚜鍔ㄦ淳鍗曠殑浠诲姟')
+ return
+ }
+
+ uni.showModal({
+ title: '涓�閿淳鍗�',
+ content: `纭鑷姩娲惧彂${tasksWithRecommendation.length}涓换鍔★紵`,
+ confirmText: '纭',
+ cancelText: '鍙栨秷',
+ success: (res) => {
+ if (res.confirm) {
+ // 妯℃嫙鎵归噺娲惧崟
+ tasksWithRecommendation.forEach(task => {
+ const index = pendingTasks.value.findIndex(t => t.id === task.id)
+ if (index > -1) {
+ pendingTasks.value.splice(index, 1)
+ todayDispatchedCount.value++
+ }
+ })
+ showToast(`鎴愬姛娲惧彂${tasksWithRecommendation.length}涓换鍔)
+ }
+ }
+ })
+}
+
+onMounted(() => {
+ // 椤甸潰鍔犺浇鏃剁殑鍒濆鍖栭�昏緫
+})
+
+onShow(() => {
+ // 椤甸潰鏄剧ず鏃剁殑閫昏緫
+})
+</script>
+
+<style scoped lang="scss">
+@import '@/styles/sales-common.scss';
+
+// 鏅鸿兘娲惧崟鐗规湁鏍峰紡
+.sales-account {
+ padding-bottom: 80px;
+}
+
+
+
+// 璁惧绫诲瀷鏍囩鏍峰紡
+.tag-mechanical {
+ background: #4caf50;
+}
+
+.tag-electric {
+ background: #2196f3;
+}
+
+.tag-instrument {
+ background: #ff9800;
+}
+
+.tag-computer {
+ background: #9c27b0;
+}
+
+.tag-vehicle {
+ background: #795548;
+}
+
+.tag-other {
+ background: #607d8b;
+}
+
+// 浼樺厛绾ф爣绛炬牱寮�
+.priority-urgent {
+ background: #f44336;
+}
+
+.priority-high {
+ background: #ff9800;
+}
+
+.priority-medium {
+ background: #2196f3;
+}
+
+.priority-low {
+ background: #4caf50;
+}
+
+// 鎺ㄨ崘缁翠慨缁勬牱寮�
+.recommended-team {
+ margin-top: 16px;
+ padding: 12px;
+ background: #f8f9fa;
+ border-radius: 8px;
+ border-left: 4px solid #2979ff;
+}
+
+.team-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+}
+
+.team-title {
+ font-size: 14px;
+ font-weight: 500;
+ color: #333;
+}
+
+.match-score {
+ background: #2979ff;
+ padding: 2px 8px;
+ border-radius: 12px;
+}
+
+.score-text {
+ font-size: 12px;
+ color: #ffffff;
+ font-weight: 500;
+}
+
+.team-info {
+ .detail-row {
+ margin-bottom: 4px;
+ }
+}
+
+// 寮圭獥鏍峰紡
+.manual-dispatch-popup {
+ padding: 20px;
+ background: #ffffff;
+ border-radius: 20px;
+ max-height: 80vh;
+ width: 90vw;
+ max-width: 400px;
+ overflow-y: auto;
+}
+
+.popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.popup-title {
+ font-size: 16px;
+ font-weight: 500;
+ color: #333;
+}
+
+.popup-actions {
+ display: flex;
+ gap: 12px;
+ margin-top: 20px;
+ padding-top: 16px;
+ border-top: 1px solid #f0f0f0;
+}
+
+.popup-actions .u-button {
+ flex: 1;
+}
+
+// 缁翠慨缁勯�夐」鏍峰紡
+.team-list {
+ max-height: 300px;
+ overflow-y: auto;
+}
+
+.team-option {
+ padding: 12px;
+ margin-bottom: 8px;
+ border: 1px solid #e0e0e0;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &.active {
+ border-color: #2979ff;
+ background: #f3f7ff;
+ }
+
+ &:hover {
+ border-color: #2979ff;
+ }
+}
+
+.team-name {
+ font-size: 14px;
+ font-weight: 500;
+ color: #333;
+ display: block;
+ margin-bottom: 4px;
+}
+
+.team-leader,
+.team-workload {
+ font-size: 12px;
+ color: #666;
+ display: block;
+ margin-bottom: 2px;
+}
+
+.team-skills {
+ margin-top: 8px;
+ padding-top: 8px;
+ border-top: 1px solid #f0f0f0;
+}
+
+.skills-label {
+ font-size: 12px;
+ color: #666;
+ margin-right: 4px;
+}
+
+.skills-text {
+ font-size: 12px;
+ color: #333;
+}
+</style>
\ No newline at end of file
diff --git a/src/pages/index.vue b/src/pages/index.vue
index bece424..05398bd 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -266,6 +266,15 @@
{
icon: '/static/images/icon/shbeibaoyang@2x.png',
label: '璁惧淇濆吇',
+ },
+ {
+ icon: '/static/images/icon/shebeixunjian@2x.png',
+ label: '璁惧宸℃',
+ },
+ {
+ icon: 'flash',
+ label: '鏅鸿兘娲惧崟',
+ bgColor: '#ff6b35'
}
]);
@@ -358,6 +367,16 @@
url: '/pages/equipmentManagement/upkeep/index'
});
break;
+ case '璁惧宸℃':
+ uni.navigateTo({
+ url: '/pages/equipmentManagement/inspection/index'
+ });
+ break;
+ case '鏅鸿兘娲惧崟':
+ uni.navigateTo({
+ url: '/pages/equipmentManagement/smartDispatch/index'
+ });
+ break;
default:
uni.showToast({
title: `鐐瑰嚮浜�${item.label}`,
diff --git a/src/static/app-logo.png b/src/static/app-logo.png
new file mode 100644
index 0000000..b22092f
--- /dev/null
+++ b/src/static/app-logo.png
Binary files differ
diff --git a/src/static/images/icon/shebeixunjian@2x.png b/src/static/images/icon/shebeixunjian@2x.png
new file mode 100644
index 0000000..0d6a719
--- /dev/null
+++ b/src/static/images/icon/shebeixunjian@2x.png
@@ -0,0 +1,39 @@
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <!-- 鑳屾櫙鍦嗗舰 -->
+ <circle cx="24" cy="24" r="20" fill="#1890ff" opacity="0.1"/>
+
+ <!-- 璁惧涓讳綋 -->
+ <rect x="14" y="12" width="20" height="16" rx="2" fill="#1890ff" stroke="#1890ff" stroke-width="1.5"/>
+
+ <!-- 璁惧灞忓箷 -->
+ <rect x="16" y="14" width="16" height="8" rx="1" fill="#ffffff"/>
+
+ <!-- 灞忓箷鍐呭绾挎潯 -->
+ <line x1="18" y1="16" x2="30" y2="16" stroke="#1890ff" stroke-width="1"/>
+ <line x1="18" y1="18" x2="26" y2="18" stroke="#1890ff" stroke-width="1"/>
+ <line x1="18" y1="20" x2="28" y2="20" stroke="#1890ff" stroke-width="1"/>
+
+ <!-- 璁惧鎸夐挳 -->
+ <circle cx="18" cy="25" r="1.5" fill="#ffffff"/>
+ <circle cx="22" cy="25" r="1.5" fill="#ffffff"/>
+ <circle cx="26" cy="25" r="1.5" fill="#ffffff"/>
+ <circle cx="30" cy="25" r="1.5" fill="#ffffff"/>
+
+ <!-- 宸℃璺緞 -->
+ <path d="M10 32 Q16 28 24 32 Q32 36 38 32" stroke="#52c41a" stroke-width="2" fill="none" stroke-dasharray="2,2"/>
+
+ <!-- 宸℃鐐� -->
+ <circle cx="10" cy="32" r="2" fill="#52c41a"/>
+ <circle cx="24" cy="32" r="2" fill="#52c41a"/>
+ <circle cx="38" cy="32" r="2" fill="#52c41a"/>
+
+ <!-- 妫�鏌ユ爣璁� -->
+ <path d="M20 36 L22 38 L26 34" stroke="#52c41a" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
+
+ <!-- 鎵弿绾� -->
+ <line x1="8" y1="8" x2="12" y2="8" stroke="#faad14" stroke-width="2" stroke-linecap="round"/>
+ <line x1="8" y1="8" x2="8" y2="12" stroke="#faad14" stroke-width="2" stroke-linecap="round"/>
+
+ <line x1="36" y1="8" x2="40" y2="8" stroke="#faad14" stroke-width="2" stroke-linecap="round"/>
+ <line x1="40" y1="8" x2="40" y2="12" stroke="#faad14" stroke-width="2" stroke-linecap="round"/>
+</svg>
\ No newline at end of file
--
Gitblit v1.9.3