From 752b14d2caa47ccceac328f79389fbf5e2e62ce4 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 24 九月 2025 15:18:39 +0800
Subject: [PATCH] 分析追溯
---
src/pages/index.vue | 18
src/pages.json | 7
src/pages/inspectionUpload/index.vue | 793 ++++++++--------
src/static/images/icon/xunjianshangchuan@2x.png | 0
src/static/images/icon/zuoyezhidao@2x.png | 0
src/components/imageUpload/index.vue | 819 +++++++++++++++++
src/components/imageUpload/viewQrCodeFiles.vue | 297 ++++++
src/pages/inspectionUpload/components/qrCodeFormDia.vue | 236 +---
src/static/images/icon/zhinengpaidan@2x.png | 0
src/pages/equipmentManagement/faultAnalysis/index.vue | 353 +++++++
src/pages/equipmentManagement/repair/add.vue | 2
src/config.js | 1
src/static/images/icon/guzhangfenxi@2x.png | 0
src/static/images/icon/jieguoyanzheng@2x.png | 0
src/components/imageUpload/viewQrCodeFilesSimple.vue | 288 ++++++
15 files changed, 2,251 insertions(+), 563 deletions(-)
diff --git a/src/components/imageUpload/index.vue b/src/components/imageUpload/index.vue
new file mode 100644
index 0000000..51e4331
--- /dev/null
+++ b/src/components/imageUpload/index.vue
@@ -0,0 +1,819 @@
+<template>
+ <view class="camera-upload">
+ <!-- 鎷嶇収/鎷嶈棰戞寜閽� -->
+ <view v-if="!disabled" class="camera-buttons">
+ <view class="button-row">
+ <u-button
+ type="primary"
+ @click="takePhoto"
+ :loading="uploading"
+ :disabled="fileList.length >= limit"
+ :customStyle="{ marginRight: '10px', flex: 1 }"
+ >
+ <u-icon name="camera" size="18" color="#fff" style="margin-right: 5px;"></u-icon>
+ {{ uploading ? '涓婁紶涓�...' : '鎷嶇収' }}
+ </u-button>
+ <u-button
+ type="success"
+ @click="takeVideo"
+ :loading="uploading"
+ :disabled="fileList.length >= limit"
+ :customStyle="{ flex: 1 }"
+ >
+ <u-icon name="video" size="18" color="#fff" style="margin-right: 5px;"></u-icon>
+ {{ uploading ? '涓婁紶涓�...' : '鎷嶈棰�' }}
+ </u-button>
+ </view>
+ </view>
+
+ <!-- 鎻愮ず淇℃伅 -->
+ <view v-if="showTip && !disabled" class="upload-tip">
+ 璇蜂娇鐢ㄧ浉鏈�
+ <text v-if="fileSize" class="tip-text">
+ 鎷嶆憚澶у皬涓嶈秴杩� <text class="tip-highlight">{{ fileSize }}MB</text>
+ </text>
+ 鐨�
+ <text class="tip-highlight">鐓х墖鎴栬棰�</text>
+ </view>
+
+ <!-- 濯掍綋鏂囦欢鍒楄〃 -->
+ <view class="media-list">
+ <view
+ v-for="(file, index) in fileList"
+ :key="file.uid || index"
+ class="media-item"
+ >
+ <!-- 棰勮鍖哄煙 -->
+ <view class="media-preview" @click="previewMedia(file, index)">
+ <image
+ v-if="file.type === 'image'"
+ :src="file.url || file.tempFilePath"
+ class="preview-image"
+ mode="aspectFill"
+ ></image>
+ <video
+ v-else-if="file.type === 'video'"
+ :src="file.url || file.tempFilePath"
+ class="preview-video"
+ :controls="false"
+ ></video>
+ <view class="media-type-icon">
+ <u-icon
+ :name="file.type === 'image' ? 'photo' : 'video'"
+ size="12"
+ color="#fff"
+ ></u-icon>
+ </view>
+ </view>
+
+ <!-- 鎿嶄綔鎸夐挳 -->
+ <view class="media-actions" v-if="!disabled">
+ <u-button
+ type="error"
+ size="mini"
+ @click="handleDelete(index)"
+ :customStyle="{
+ minWidth: '40px',
+ height: '24px',
+ fontSize: '10px',
+ padding: '0 8px'
+ }"
+ >
+ 鍒犻櫎
+ </u-button>
+ </view>
+ </view>
+ </view>
+
+ <!-- 涓婁紶杩涘害 -->
+ <view v-if="uploading" class="upload-progress">
+ <u-line-progress
+ :percentage="uploadProgress"
+ :showText="true"
+ activeColor="#409eff"
+ ></u-line-progress>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue';
+import { getToken } from "@/utils/auth";
+
+// Props 瀹氫箟
+const props = defineProps({
+ modelValue: [String, Object, Array],
+ action: { type: String, default: "/common/minioUploads" },
+ data: { type: Object },
+ limit: { type: Number, default: 5 },
+ fileSize: { type: Number, default: 10 }, // 榛樿10MB锛岄�傚悎瑙嗛
+ fileType: {
+ type: Array,
+ default: () => ["jpg", "jpeg", "png", "mp4", "mov"]
+ },
+ isShowTip: { type: Boolean, default: true },
+ disabled: { type: Boolean, default: false },
+ drag: { type: Boolean, default: false }, // 鎷嶇収涓嶉渶瑕佹嫋鎷�
+ statusType: { type: Number, default: "" }, // 鐢ㄤ簬鍖哄垎涓嶅悓鐘舵�佺殑涓婁紶
+ maxVideoDuration: { type: Number, default: 30 }, // 鏈�澶ц棰戞椂闀匡紙绉掞級
+});
+
+// 浜嬩欢瀹氫箟
+const emit = defineEmits(['update:modelValue']);
+
+// 鍝嶅簲寮忔暟鎹�
+const number = ref(0);
+const uploadList = ref([]);
+const fileList = ref([]);
+const uploading = ref(false);
+const uploadProgress = ref(0);
+
+// 璁$畻灞炴��
+const uploadFileUrl = computed(() => {
+ // 鑾峰彇鍩虹API鍦板潃锛岄�傞厤uniapp鐜
+ let baseUrl = '';
+
+ // 灏濊瘯澶氱鏂瑰紡鑾峰彇baseUrl
+ if (process.env.VUE_APP_BASE_API) {
+ baseUrl = process.env.VUE_APP_BASE_API;
+ } else if (process.env.NODE_ENV === 'development') {
+ baseUrl = 'http://192.168.1.147:9036';
+ } else {
+ baseUrl = 'http://192.168.1.147:9036';
+ }
+
+ const fullUrl = baseUrl + props.action;
+ return fullUrl;
+});
+const headers = computed(() => {
+ const token = getToken();
+ return token ? { Authorization: "Bearer " + token } : {};
+});
+const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
+// 鍒濆鍖栧拰缂栬緫鍒濆鍖栨柟娉�
+const init = () => {
+ fileList.value = [];
+ uploadList.value = [];
+ number.value = 0;
+};
+
+const editInit = (val) => {
+ fileList.value = [];
+ val.storageBlobDTO.forEach((element) => {
+ // 纭繚鏂囦欢鏁版嵁鍖呭惈鎵�鏈夊繀瑕佸瓧娈碉紝鍖呮嫭id
+ const fileData = {
+ ...element,
+ id: element.id, // 淇濈暀鏈嶅姟鍣ㄨ繑鍥炵殑id
+ url: element.url || element.downloadUrl,
+ bucketFilename: element.bucketFilename || element.name,
+ downloadUrl: element.downloadUrl || element.url,
+ type: element.type || (element.url && element.url.includes('video') ? 'video' : 'image'),
+ name: element.name || element.bucketFilename || `鏂囦欢_${Date.now()}`,
+ size: element.size || 0,
+ createTime: element.createTime || new Date().getTime(),
+ uid: element.uid || new Date().getTime() + Math.random()
+ };
+ fileList.value.push(fileData);
+ uploadedSuccessfully();
+ });
+};
+
+// 娴嬭瘯鏈嶅姟鍣ㄨ繛鎺�
+const testServerConnection = () => {
+ return new Promise((resolve) => {
+ uni.request({
+ url: uploadFileUrl.value.replace('/common/minioUploads', '/common/test'),
+ method: 'GET',
+ timeout: 5000,
+ success: (res) => {
+ resolve(true);
+ },
+ fail: (err) => {
+ resolve(false);
+ }
+ });
+ });
+};
+
+// 缁勪欢閿�姣佹椂鐨勬竻鐞�
+onUnmounted(() => {
+ // 娓呯悊涓婁紶鐘舵��
+ if (uploading.value) {
+ uploading.value = false
+ }
+
+ // 闅愯棌鍙兘鏄剧ず鐨勫姞杞芥彁绀�
+ uni.hideLoading()
+ uni.hideToast()
+})
+
+// 鏆撮湶鏂规硶
+defineExpose({ init, editInit, testServerConnection });
+
+// 鐩戝惉 modelValue 鍙樺寲
+watch(
+ () => props.modelValue,
+ (val) => {
+ if (val) {
+ let temp = 1;
+ let list = [];
+
+ if (Array.isArray(val)) {
+ list = val;
+ } else if (typeof val === "string") {
+ list = val.split(",").map(url => ({ url: url.trim() }));
+ }
+
+ fileList.value = list.map((item) => {
+ if (typeof item === "string") {
+ item = { name: item, url: item };
+ }
+ // 纭繚姣忎釜鏂囦欢閮芥湁蹇呰鐨勫睘鎬э紝鍖呮嫭id
+ return {
+ ...item,
+ id: item.id, // 淇濈暀id瀛楁
+ uid: item.uid || new Date().getTime() + temp++,
+ type: item.type || (item.url && item.url.includes('video') ? 'video' : 'image'),
+ name: item.name || item.bucketFilename || `鏂囦欢_${Date.now()}`,
+ size: item.size || 0,
+ createTime: item.createTime || new Date().getTime()
+ };
+ });
+ } else {
+ fileList.value = [];
+ }
+ },
+ { deep: true, immediate: true }
+);
+
+// 鎷嶇収
+const takePhoto = () => {
+ if (fileList.value.length >= props.limit) {
+ uni.showToast({
+ title: `鏈�澶氬彧鑳芥媿鎽�${props.limit}涓枃浠禶,
+ icon: 'none'
+ });
+ return;
+ }
+
+ uni.chooseImage({
+ count: 1,
+ sizeType: ['compressed', 'original'],
+ sourceType: ['camera'],
+ success: (res) => {
+ try {
+ if (!res.tempFilePaths || res.tempFilePaths.length === 0) {
+ throw new Error('鏈幏鍙栧埌鍥剧墖鏂囦欢');
+ }
+
+ const tempFilePath = res.tempFilePaths[0];
+ const tempFile = res.tempFiles && res.tempFiles[0] ? res.tempFiles[0] : {};
+
+ const file = {
+ tempFilePath: tempFilePath,
+ type: 'image',
+ name: `photo_${Date.now()}.jpg`,
+ size: tempFile.size || 0,
+ createTime: new Date().getTime(),
+ uid: Date.now() + Math.random()
+ };
+
+ handleBeforeUpload(file);
+ } catch (error) {
+ console.error('澶勭悊鎷嶇収缁撴灉澶辫触:', error);
+ uni.showToast({
+ title: '澶勭悊鍥剧墖澶辫触',
+ icon: 'error'
+ });
+ }
+ },
+ fail: (err) => {
+ console.error('鎷嶇収澶辫触:', err);
+ uni.showToast({
+ title: '鎷嶇収澶辫触: ' + (err.errMsg || '鏈煡閿欒'),
+ icon: 'error'
+ });
+ }
+ });
+};
+
+// 鎷嶈棰�
+const takeVideo = () => {
+ if (fileList.value.length >= props.limit) {
+ uni.showToast({
+ title: `鏈�澶氬彧鑳芥媿鎽�${props.limit}涓枃浠禶,
+ icon: 'none'
+ });
+ return;
+ }
+
+ uni.chooseVideo({
+ sourceType: ['camera'],
+ maxDuration: props.maxVideoDuration,
+ camera: 'back',
+ success: (res) => {
+ try {
+ if (!res.tempFilePath) {
+ throw new Error('鏈幏鍙栧埌瑙嗛鏂囦欢');
+ }
+
+ const file = {
+ tempFilePath: res.tempFilePath,
+ type: 'video',
+ name: `video_${Date.now()}.mp4`,
+ size: res.size || 0,
+ duration: res.duration || 0,
+ createTime: new Date().getTime(),
+ uid: Date.now() + Math.random()
+ };
+
+ handleBeforeUpload(file);
+ } catch (error) {
+ console.error('澶勭悊鎷嶈棰戠粨鏋滃け璐�:', error);
+ uni.showToast({
+ title: '澶勭悊瑙嗛澶辫触',
+ icon: 'error'
+ });
+ }
+ },
+ fail: (err) => {
+ console.error('鎷嶈棰戝け璐�:', err);
+ uni.showToast({
+ title: '鎷嶈棰戝け璐�: ' + (err.errMsg || '鏈煡閿欒'),
+ icon: 'error'
+ });
+ }
+ });
+};
+
+// 鏂囦欢涓婁紶澶勭悊
+const uploadFile = (file) => {
+ uploading.value = true;
+ uploadProgress.value = 0;
+ number.value++; // 澧炲姞涓婁紶璁℃暟
+
+ // 纭繚鏂囦欢璺緞姝g‘
+ const filePath = file.tempFilePath || file.path;
+ if (!filePath) {
+ handleUploadError('鏂囦欢璺緞涓嶅瓨鍦�');
+ return;
+ }
+
+ // 纭繚token瀛樺湪
+ const token = getToken();
+ if (!token) {
+ handleUploadError('鐢ㄦ埛鏈櫥褰�');
+ return;
+ }
+
+ // 鍑嗗涓婁紶鍙傛暟
+ const uploadParams = {
+ url: uploadFileUrl.value,
+ filePath: filePath,
+ name: 'files',
+ formData: {
+ type: props.statusType || 0,
+ ...(props.data || {})
+ },
+ header: {
+ 'Authorization': `Bearer ${token}`
+ }
+ };
+
+ const uploadTask = uni.uploadFile({
+ ...uploadParams,
+ success: (res) => {
+ try {
+ if (res.statusCode === 200) {
+ const response = JSON.parse(res.data);
+ if (response.code === 200) {
+ handleUploadSuccess(response, file);
+ uni.showToast({
+ title: '涓婁紶鎴愬姛',
+ icon: 'success'
+ });
+ emit("update:modelValue", fileList.value);
+ } else {
+ handleUploadError(response.msg || '鏈嶅姟鍣ㄨ繑鍥為敊璇�');
+ }
+ } else {
+ handleUploadError(`鏈嶅姟鍣ㄩ敊璇紝鐘舵�佺爜: ${res.statusCode}`);
+ }
+ } catch (e) {
+ console.error('瑙f瀽鍝嶅簲澶辫触:', e);
+ console.error('鍘熷鍝嶅簲鏁版嵁:', res.data);
+ handleUploadError('鍝嶅簲鏁版嵁瑙f瀽澶辫触: ' + e.message);
+ }
+ },
+ fail: (err) => {
+ console.error('涓婁紶澶辫触:', err.errMsg || err);
+ number.value--; // 涓婁紶澶辫触鏃跺噺灏戣鏁�
+
+ let errorMessage = '涓婁紶澶辫触';
+ if (err.errMsg) {
+ if (err.errMsg.includes('statusCode: null')) {
+ errorMessage = '缃戠粶杩炴帴澶辫触锛岃妫�鏌ョ綉缁滆缃�';
+ } else if (err.errMsg.includes('timeout')) {
+ errorMessage = '涓婁紶瓒呮椂锛岃閲嶈瘯';
+ } else if (err.errMsg.includes('fail')) {
+ errorMessage = '涓婁紶澶辫触锛岃妫�鏌ョ綉缁滆繛鎺�';
+ } else {
+ errorMessage = err.errMsg;
+ }
+ }
+
+ handleUploadError(errorMessage);
+ },
+ complete: () => {
+ uploading.value = false;
+ uploadProgress.value = 0;
+ }
+ });
+
+ // 鐩戝惉涓婁紶杩涘害
+ if (uploadTask && uploadTask.onProgressUpdate) {
+ uploadTask.onProgressUpdate((res) => {
+ uploadProgress.value = res.progress;
+ });
+ }
+};
+// 鑾峰彇濯掍綋鏂囦欢鍚�
+const getMediaName = (file) => {
+ if (file.bucketFilename) {
+ return file.bucketFilename.length > 15
+ ? file.bucketFilename.substring(0, 15) + '...'
+ : file.bucketFilename;
+ }
+ if (file.name) {
+ return file.name.length > 15
+ ? file.name.substring(0, 15) + '...'
+ : file.name;
+ }
+ return file.type === 'image' ? '鐓х墖' : '瑙嗛';
+};
+
+// 鏍煎紡鍖栨枃浠跺ぇ灏�
+const formatFileSize = (size) => {
+ if (!size) return '';
+ if (size < 1024) return size + 'B';
+ if (size < 1024 * 1024) return (size / 1024).toFixed(1) + 'KB';
+ return (size / (1024 * 1024)).toFixed(1) + 'MB';
+};
+
+// 鏍煎紡鍖栨椂闂�
+const formatTime = (timestamp) => {
+ if (!timestamp) return '';
+ const date = new Date(timestamp);
+ const now = new Date();
+ const diff = now - date;
+
+ if (diff < 60000) return '鍒氬垰';
+ if (diff < 3600000) return Math.floor(diff / 60000) + '鍒嗛挓鍓�';
+ if (diff < 86400000) return Math.floor(diff / 3600000) + '灏忔椂鍓�';
+
+ return date.toLocaleDateString();
+};
+
+// 棰勮濯掍綋鏂囦欢
+const previewMedia = (file, index) => {
+ if (file.type === 'image') {
+ // 棰勮鍥剧墖
+ const urls = fileList.value
+ .filter(item => item.type === 'image')
+ .map(item => item.url || item.tempFilePath);
+
+ uni.previewImage({
+ urls: urls,
+ current: file.url || file.tempFilePath
+ });
+ } else if (file.type === 'video') {
+ // 棰勮瑙嗛
+ uni.previewVideo({
+ sources: [{
+ src: file.url || file.tempFilePath,
+ type: 'mp4'
+ }],
+ current: 0
+ });
+ }
+};
+
+// 涓嬭浇鏂囦欢
+const handleDownload = (index) => {
+ const file = fileList.value[index];
+ const url = file.url || file.downloadUrl;
+
+ if (!url) {
+ uni.showToast({
+ title: '鏂囦欢閾炬帴涓嶅瓨鍦紝鏃犳硶涓嬭浇',
+ icon: 'none'
+ });
+ return;
+ }
+
+ // 浣跨敤uniapp鐨勪笅杞紸PI
+ uni.downloadFile({
+ url: url,
+ success: (res) => {
+ if (res.statusCode === 200) {
+ // 淇濆瓨鍒扮浉鍐屾垨鏂囦欢绯荤粺
+ uni.saveFile({
+ tempFilePath: res.tempFilePath,
+ success: (saveRes) => {
+ uni.showToast({
+ title: '涓嬭浇鎴愬姛',
+ icon: 'success'
+ });
+ },
+ fail: (err) => {
+ console.error('淇濆瓨鏂囦欢澶辫触:', err);
+ uni.showToast({
+ title: '淇濆瓨鏂囦欢澶辫触',
+ icon: 'error'
+ });
+ }
+ });
+ }
+ },
+ fail: (err) => {
+ console.error('涓嬭浇澶辫触:', err);
+ uni.showToast({
+ title: '涓嬭浇澶辫触',
+ icon: 'error'
+ });
+ }
+ });
+};
+
+// 妫�鏌ョ綉缁滆繛鎺�
+const checkNetworkConnection = () => {
+ return new Promise((resolve) => {
+ uni.getNetworkType({
+ success: (res) => {
+ if (res.networkType === 'none') {
+ resolve(false);
+ } else {
+ resolve(true);
+ }
+ },
+ fail: () => {
+ resolve(false);
+ }
+ });
+ });
+};
+
+// 涓婁紶鍓嶆牎楠�
+const handleBeforeUpload = async (file) => {
+ // 妫�鏌ョ綉缁滆繛鎺�
+ const hasNetwork = await checkNetworkConnection();
+ if (!hasNetwork) {
+ uni.showToast({
+ title: '缃戠粶杩炴帴涓嶅彲鐢紝璇锋鏌ョ綉缁滆缃�',
+ icon: 'none'
+ });
+ return false;
+ }
+
+ // 鏍¢獙鏂囦欢澶у皬
+ if (props.fileSize && file.size) {
+ const isLt = file.size / 1024 / 1024 < props.fileSize;
+ if (!isLt) {
+ uni.showToast({
+ title: `鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`,
+ icon: 'none'
+ });
+ return false;
+ }
+ }
+
+ // 鏍¢獙瑙嗛鏃堕暱
+ if (file.type === 'video' && file.duration && file.duration > props.maxVideoDuration) {
+ uni.showToast({
+ title: `瑙嗛鏃堕暱涓嶈兘瓒呰繃 ${props.maxVideoDuration} 绉�!`,
+ icon: 'none'
+ });
+ return false;
+ }
+
+ // 鏍¢獙鏂囦欢绫诲瀷
+ if (props.fileType && Array.isArray(props.fileType) && props.fileType.length > 0) {
+ const fileName = file.name || '';
+ const fileExtension = fileName ? fileName.split('.').pop().toLowerCase() : '';
+
+ // 鏍规嵁鏂囦欢绫诲瀷纭畾鏈熸湜鐨勬墿灞曞悕
+ let expectedTypes = [];
+ if (file.type === 'image') {
+ expectedTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
+ } else if (file.type === 'video') {
+ expectedTypes = ['mp4', 'mov', 'avi', 'wmv'];
+ }
+
+ // 妫�鏌ユ枃浠舵墿灞曞悕鏄惁鍦ㄥ厑璁哥殑绫诲瀷涓�
+ if (fileExtension && expectedTypes.length > 0) {
+ const isAllowed = expectedTypes.some(type =>
+ props.fileType.includes(type) && type === fileExtension
+ );
+
+ if (!isAllowed) {
+ uni.showToast({
+ title: `鏂囦欢鏍煎紡涓嶆敮鎸侊紝璇锋媿鎽� ${expectedTypes.join('/')} 鏍煎紡鐨勬枃浠禶,
+ icon: 'none'
+ });
+ return false;
+ }
+ }
+ }
+
+ // 鏍¢獙閫氳繃锛屽紑濮嬩笂浼�
+ uploadFile(file);
+ return true;
+};
+
+// 涓婁紶澶辫触澶勭悊
+const handleUploadError = (message = '涓婁紶鏂囦欢澶辫触', showRetry = true) => {
+ if (showRetry) {
+ uni.showModal({
+ title: '涓婁紶澶辫触',
+ content: message + '锛屾槸鍚﹂噸璇曪紵',
+ success: (res) => {
+ if (res.confirm) {
+ // 鐢ㄦ埛閫夋嫨閲嶈瘯锛岃繖閲屽彲浠ラ噸鏂拌Е鍙戜笂浼�
+ }
+ }
+ });
+ } else {
+ uni.showToast({
+ title: message,
+ icon: 'error'
+ });
+ }
+};
+
+// 涓婁紶鎴愬姛鍥炶皟
+const handleUploadSuccess = (res, file) => {
+ if (res.code === 200 && res.data && Array.isArray(res.data) && res.data.length > 0) {
+ const uploadedFile = res.data[0];
+ // 纭繚涓婁紶鐨勬枃浠舵暟鎹畬鏁达紝鍖呭惈id
+ const fileData = {
+ ...file,
+ id: uploadedFile.id, // 娣诲姞鏈嶅姟鍣ㄨ繑鍥炵殑id
+ url: uploadedFile.url || uploadedFile.downloadUrl,
+ bucketFilename: uploadedFile.bucketFilename || file.name,
+ downloadUrl: uploadedFile.downloadUrl || uploadedFile.url,
+ size: uploadedFile.size || file.size,
+ createTime: uploadedFile.createTime || new Date().getTime()
+ };
+
+ uploadList.value.push(fileData);
+ uploadedSuccessfully();
+ } else {
+ number.value--; // 涓婁紶澶辫触鏃跺噺灏戣鏁�
+ handleUploadError(res.msg || '涓婁紶澶辫触');
+ }
+};
+
+// 鍒犻櫎鏂囦欢
+const handleDelete = (index) => {
+ uni.showModal({
+ title: '纭鍒犻櫎',
+ content: '纭畾瑕佸垹闄よ繖涓枃浠跺悧锛�',
+ success: (res) => {
+ if (res.confirm) {
+ fileList.value.splice(index, 1);
+ emit("update:modelValue", listToString(fileList.value));
+ uni.showToast({
+ title: '鍒犻櫎鎴愬姛',
+ icon: 'success'
+ });
+ }
+ }
+ });
+};
+
+// 涓婁紶缁撴潫澶勭悊
+const uploadedSuccessfully = () => {
+ if (number.value > 0 && uploadList.value.length === number.value) {
+ // 鍚堝苟宸插瓨鍦ㄧ殑鏂囦欢鍜屽垰涓婁紶鐨勬枃浠�
+ const existingFiles = fileList.value.filter((f) => f.url !== undefined);
+ fileList.value = [...existingFiles, ...uploadList.value];
+
+ // 閲嶇疆鐘舵��
+ uploadList.value = [];
+ number.value = 0;
+
+ // 瑙﹀彂鏇存柊浜嬩欢锛屼紶閫掑畬鏁寸殑鏂囦欢鍒楄〃
+ emit("update:modelValue", fileList.value);
+ }
+};
+
+const listToString = (list, separator = ",") => {
+ const strs = list
+ .filter(item => item.url)
+ .map(item => item.url)
+ .join(separator);
+ return strs;
+};
+</script>
+<style scoped lang="scss">
+.camera-upload {
+ width: 100%;
+}
+
+.camera-buttons {
+ margin-bottom: 15px;
+
+ .button-row {
+ display: flex;
+ gap: 10px;
+ }
+}
+
+.upload-tip {
+ font-size: 12px;
+ color: #909399;
+ margin-bottom: 15px;
+ text-align: center;
+ line-height: 1.5;
+
+ .tip-text {
+ margin: 0 2px;
+ }
+
+ .tip-highlight {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+}
+
+.media-list {
+ margin-top: 10px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.media-item {
+ position: relative;
+ width: 80px;
+ height: 80px;
+ border-radius: 8px;
+ overflow: hidden;
+ background-color: #f5f5f5;
+ border: 2px solid #e9ecef;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #409eff;
+ transform: scale(1.02);
+ }
+}
+
+.media-preview {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+
+ .preview-image, .preview-video {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+
+ .media-type-icon {
+ position: absolute;
+ top: 4px;
+ right: 4px;
+ width: 20px;
+ height: 20px;
+ background-color: rgba(0, 0, 0, 0.6);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+}
+
+.media-actions {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
+ padding: 4px;
+ display: flex;
+ justify-content: center;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+
+ .media-item:hover & {
+ opacity: 1;
+ }
+}
+
+.upload-progress {
+ margin-top: 15px;
+ padding: 0 10px;
+}
+</style>
diff --git a/src/components/imageUpload/viewQrCodeFiles.vue b/src/components/imageUpload/viewQrCodeFiles.vue
new file mode 100644
index 0000000..32f5684
--- /dev/null
+++ b/src/components/imageUpload/viewQrCodeFiles.vue
@@ -0,0 +1,297 @@
+<template>
+ <view>
+ <!-- 寮圭獥 -->
+ <u-popup
+ v-model="dialogVisitable"
+ mode="center"
+ width="90%"
+ height="80%"
+ border-radius="20"
+ @close="cancel"
+ >
+ <view class="popup-content">
+ <view class="popup-header">
+ <text class="popup-title">鏌ョ湅闄勪欢</text>
+ <u-icon
+ name="close"
+ size="24"
+ color="#999"
+ @click="cancel"
+ ></u-icon>
+ </view>
+
+ <view class="upload-container">
+ <view class="form-container">
+ <view class="title">宸℃闄勪欢</view>
+
+ <!-- 鍥剧墖鍒楄〃 -->
+ <view v-if="beforeProductionImgs.length > 0" class="media-section">
+ <view class="section-title">鍥剧墖</view>
+ <view class="image-grid">
+ <view
+ v-for="(item, index) in beforeProductionImgs"
+ :key="index"
+ class="image-item"
+ @click="previewImage(item, index)"
+ >
+ <image
+ :src="item"
+ mode="aspectFill"
+ class="image-preview"
+ />
+ </view>
+ </view>
+ </view>
+
+ <!-- 瑙嗛鍒楄〃 -->
+ <view v-if="beforeProductionVideos.length > 0" class="media-section">
+ <view class="section-title">瑙嗛</view>
+ <view class="video-grid">
+ <view
+ v-for="(videoUrl, index) in beforeProductionVideos"
+ :key="index"
+ class="video-item"
+ @click="previewVideo(videoUrl)"
+ >
+ <view class="video-preview">
+ <u-icon name="play-circle-fill" size="40" color="#fff"></u-icon>
+ </view>
+ <view class="video-tip">鐐瑰嚮鎾斁</view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 绌虹姸鎬� -->
+ <view v-if="beforeProductionImgs.length === 0 && beforeProductionVideos.length === 0" class="empty-state">
+ <u-empty
+ mode="data"
+ text="鏆傛棤闄勪欢"
+ :iconSize="60"
+ ></u-empty>
+ </view>
+ </view>
+ </view>
+ </view>
+ </u-popup>
+ </view>
+</template>
+
+<script setup>
+import { ref, onUnmounted } from 'vue'
+
+// 鎺у埗寮圭獥鏄剧ず
+const dialogVisitable = ref(false)
+// 鍥剧墖鏁扮粍
+const beforeProductionImgs = ref([])
+// 瑙嗛鏁扮粍
+const beforeProductionVideos = ref([])
+
+// 鎵撳紑寮圭獥骞跺姞杞芥暟鎹�
+const openDialog = async (row) => {
+ console.log('鎵撳紑闄勪欢鏌ョ湅寮圭獥锛屾暟鎹�:', row)
+
+ // 澶勭悊鏁版嵁锛屽垎绂诲浘鐗囧拰瑙嗛
+ const { images: beforeImgs, videos: beforeVids } = processItems(row.storageBlobDTO || [])
+
+ beforeProductionImgs.value = beforeImgs
+ beforeProductionVideos.value = beforeVids
+ dialogVisitable.value = true
+}
+
+// 棰勮鍥剧墖
+const previewImage = (url, index) => {
+ uni.previewImage({
+ urls: beforeProductionImgs.value,
+ current: index,
+ fail: (err) => {
+ console.error('鍥剧墖棰勮澶辫触:', err)
+ uni.showToast({
+ title: '鍥剧墖棰勮澶辫触',
+ icon: 'error'
+ })
+ }
+ })
+}
+
+// 棰勮瑙嗛
+const previewVideo = (url) => {
+ uni.previewVideo({
+ sources: [{
+ src: url
+ }],
+ fail: (err) => {
+ console.error('瑙嗛棰勮澶辫触:', err)
+ uni.showToast({
+ title: '瑙嗛棰勮澶辫触',
+ icon: 'error'
+ })
+ }
+ })
+}
+
+// 琛ㄥ崟鍏抽棴鏂规硶
+const cancel = () => {
+ dialogVisitable.value = false
+ // 閲嶇疆鏁版嵁
+ beforeProductionImgs.value = []
+ beforeProductionVideos.value = []
+}
+
+// 澶勭悊姣忎竴绫绘暟鎹細鍒嗙鍥剧墖鍜岃棰�
+function processItems(items) {
+ if (!items || !Array.isArray(items)) {
+ return { images: [], videos: [] }
+ }
+
+ const images = []
+ const videos = []
+
+ items.forEach(item => {
+ if (item.contentType?.startsWith('image/')) {
+ images.push(item.url)
+ } else if (item.contentType?.startsWith('video/')) {
+ videos.push(item.url)
+ }
+ })
+
+ return { images, videos }
+}
+
+// 缁勪欢閿�姣佹椂鐨勬竻鐞�
+onUnmounted(() => {
+ // 鍏抽棴寮圭獥
+ dialogVisitable.value = false
+ // 娓呯悊鏁版嵁
+ beforeProductionImgs.value = []
+ beforeProductionVideos.value = []
+})
+
+// 鏆撮湶鏂规硶缁欑埗缁勪欢
+defineExpose({ openDialog })
+</script>
+
+<style scoped lang="scss">
+.popup-content {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.popup-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+}
+
+.upload-container {
+ flex: 1;
+ padding: 20px;
+ overflow-y: auto;
+}
+
+.form-container {
+ width: 100%;
+}
+
+.title {
+ font-size: 16px;
+ color: #1890ff;
+ line-height: 24px;
+ font-weight: 600;
+ padding-left: 12px;
+ position: relative;
+ margin: 0 0 20px 0;
+
+ &::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 4px;
+ width: 4px;
+ height: 16px;
+ background-color: #1890ff;
+ border-radius: 2px;
+ }
+}
+
+.media-section {
+ margin-bottom: 30px;
+}
+
+.section-title {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 15px;
+ font-weight: 500;
+}
+
+.image-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+.image-item {
+ width: 100px;
+ height: 100px;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.image-preview {
+ width: 100%;
+ height: 100%;
+}
+
+.video-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.video-item {
+ width: 160px;
+ height: 90px;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.video-preview {
+ width: 100%;
+ height: 100%;
+ background-color: #333;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.video-tip {
+ position: absolute;
+ bottom: 5px;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 12px;
+ color: #fff;
+ background-color: rgba(0, 0, 0, 0.6);
+ padding: 2px 8px;
+ border-radius: 4px;
+}
+
+.empty-state {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 200px;
+}
+</style>
diff --git a/src/components/imageUpload/viewQrCodeFilesSimple.vue b/src/components/imageUpload/viewQrCodeFilesSimple.vue
new file mode 100644
index 0000000..574a713
--- /dev/null
+++ b/src/components/imageUpload/viewQrCodeFilesSimple.vue
@@ -0,0 +1,288 @@
+<template>
+ <view>
+ <!-- 寮圭獥 -->
+ <u-popup
+ v-model="dialogVisitable"
+ mode="center"
+ width="90%"
+ height="80%"
+ border-radius="20"
+ @close="cancel"
+ >
+ <view class="popup-content">
+ <view class="popup-header">
+ <text class="popup-title">鏌ョ湅闄勪欢</text>
+ <u-icon
+ name="close"
+ size="24"
+ color="#999"
+ @click="cancel"
+ ></u-icon>
+ </view>
+
+ <view class="upload-container">
+ <view class="form-container">
+ <view class="title">宸℃闄勪欢</view>
+
+ <!-- 鍥剧墖鍒楄〃 -->
+ <view v-if="beforeProductionImgs.length > 0" class="media-section">
+ <view class="section-title">鍥剧墖</view>
+ <view class="image-grid">
+ <view
+ v-for="(item, index) in beforeProductionImgs"
+ :key="index"
+ class="image-item"
+ @click="previewImage(item, index)"
+ >
+ <image
+ :src="item"
+ mode="aspectFill"
+ class="image-preview"
+ />
+ </view>
+ </view>
+ </view>
+
+ <!-- 瑙嗛鍒楄〃 -->
+ <view v-if="beforeProductionVideos.length > 0" class="media-section">
+ <view class="section-title">瑙嗛</view>
+ <view class="video-grid">
+ <view
+ v-for="(videoUrl, index) in beforeProductionVideos"
+ :key="index"
+ class="video-item"
+ @click="previewVideo(videoUrl)"
+ >
+ <view class="video-preview">
+ <u-icon name="play-circle-fill" size="40" color="#fff"></u-icon>
+ </view>
+ <view class="video-tip">鐐瑰嚮鎾斁</view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 绌虹姸鎬� -->
+ <view v-if="beforeProductionImgs.length === 0 && beforeProductionVideos.length === 0" class="empty-state">
+ <u-empty
+ mode="data"
+ text="鏆傛棤闄勪欢"
+ :iconSize="60"
+ ></u-empty>
+ </view>
+ </view>
+ </view>
+ </view>
+ </u-popup>
+ </view>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+// 鎺у埗寮圭獥鏄剧ず
+const dialogVisitable = ref(false)
+// 鍥剧墖鏁扮粍
+const beforeProductionImgs = ref([])
+// 瑙嗛鏁扮粍
+const beforeProductionVideos = ref([])
+
+// 鎵撳紑寮圭獥骞跺姞杞芥暟鎹�
+const openDialog = async (row) => {
+ console.log('鎵撳紑闄勪欢鏌ョ湅寮圭獥锛屾暟鎹�:', row)
+
+ // 澶勭悊鏁版嵁锛屽垎绂诲浘鐗囧拰瑙嗛
+ const { images: beforeImgs, videos: beforeVids } = processItems(row.storageBlobDTO || [])
+
+ beforeProductionImgs.value = beforeImgs
+ beforeProductionVideos.value = beforeVids
+ dialogVisitable.value = true
+}
+
+// 棰勮鍥剧墖
+const previewImage = (url, index) => {
+ uni.previewImage({
+ urls: beforeProductionImgs.value,
+ current: index,
+ fail: (err) => {
+ console.error('鍥剧墖棰勮澶辫触:', err)
+ uni.showToast({
+ title: '鍥剧墖棰勮澶辫触',
+ icon: 'error'
+ })
+ }
+ })
+}
+
+// 棰勮瑙嗛
+const previewVideo = (url) => {
+ uni.previewVideo({
+ sources: [{
+ src: url
+ }],
+ fail: (err) => {
+ console.error('瑙嗛棰勮澶辫触:', err)
+ uni.showToast({
+ title: '瑙嗛棰勮澶辫触',
+ icon: 'error'
+ })
+ }
+ })
+}
+
+// 琛ㄥ崟鍏抽棴鏂规硶
+const cancel = () => {
+ dialogVisitable.value = false
+ // 閲嶇疆鏁版嵁
+ beforeProductionImgs.value = []
+ beforeProductionVideos.value = []
+}
+
+// 澶勭悊姣忎竴绫绘暟鎹細鍒嗙鍥剧墖鍜岃棰�
+function processItems(items) {
+ if (!items || !Array.isArray(items)) {
+ return { images: [], videos: [] }
+ }
+
+ const images = []
+ const videos = []
+
+ items.forEach(item => {
+ if (item.contentType?.startsWith('image/')) {
+ images.push(item.url)
+ } else if (item.contentType?.startsWith('video/')) {
+ videos.push(item.url)
+ }
+ })
+
+ return { images, videos }
+}
+
+// 鏆撮湶鏂规硶缁欑埗缁勪欢
+defineExpose({ openDialog })
+</script>
+
+<style scoped lang="scss">
+.popup-content {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.popup-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+}
+
+.upload-container {
+ flex: 1;
+ padding: 20px;
+ overflow-y: auto;
+}
+
+.form-container {
+ width: 100%;
+}
+
+.title {
+ font-size: 16px;
+ color: #1890ff;
+ line-height: 24px;
+ font-weight: 600;
+ padding-left: 12px;
+ position: relative;
+ margin: 0 0 20px 0;
+
+ &::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 4px;
+ width: 4px;
+ height: 16px;
+ background-color: #1890ff;
+ border-radius: 2px;
+ }
+}
+
+.media-section {
+ margin-bottom: 30px;
+}
+
+.section-title {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 15px;
+ font-weight: 500;
+}
+
+.image-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+.image-item {
+ width: 100px;
+ height: 100px;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.image-preview {
+ width: 100%;
+ height: 100%;
+}
+
+.video-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.video-item {
+ width: 160px;
+ height: 90px;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.video-preview {
+ width: 100%;
+ height: 100%;
+ background-color: #333;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.video-tip {
+ position: absolute;
+ bottom: 5px;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 12px;
+ color: #fff;
+ background-color: rgba(0, 0, 0, 0.6);
+ padding: 2px 8px;
+ border-radius: 4px;
+}
+
+.empty-state {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 200px;
+}
+</style>
diff --git a/src/config.js b/src/config.js
index d41d2bb..b7c35cd 100644
--- a/src/config.js
+++ b/src/config.js
@@ -3,6 +3,7 @@
// baseUrl: 'https://vue.ruoyi.vip/prod-api',
// baseUrl: 'http://localhost/prod-api',
baseUrl: 'http://114.132.189.42:9036',
+ // baseUrl: 'http://192.168.1.147:9036',
//cloud鍚庡彴缃戝叧鍦板潃
// baseUrl: 'http://192.168.10.3:8080',
// 搴旂敤淇℃伅
diff --git a/src/pages.json b/src/pages.json
index 3937f03..a2b3ca6 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -392,6 +392,13 @@
"navigationBarTitleText": "宸℃涓婁紶",
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "pages/equipmentManagement/faultAnalysis/index",
+ "style": {
+ "navigationBarTitleText": "鏁呴殰鍒嗘瀽杩芥函",
+ "navigationStyle": "custom"
+ }
}
],
"subPackages": [
diff --git a/src/pages/equipmentManagement/faultAnalysis/index.vue b/src/pages/equipmentManagement/faultAnalysis/index.vue
new file mode 100644
index 0000000..5bf8c6e
--- /dev/null
+++ b/src/pages/equipmentManagement/faultAnalysis/index.vue
@@ -0,0 +1,353 @@
+<template>
+ <view class="fault-analysis-page">
+ <!-- 椤甸潰澶撮儴 -->
+ <PageHeader title="鏁呴殰鍒嗘瀽杩芥函" @back="goBack" />
+
+ <!-- 缁熻姒傝 -->
+ <view class="overview-section">
+ <view class="section-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">缁熻姒傝</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+
+ <view class="overview-content">
+ <view class="overview-item">
+ <view class="overview-number">{{ overviewData.totalFaults }}</view>
+ <view class="overview-label">鏁呴殰鎬绘暟</view>
+ </view>
+ <view class="overview-item">
+ <view class="overview-number">{{ overviewData.totalDowntime }}</view>
+ <view class="overview-label">鎬诲仠鏈烘椂闀�(h)</view>
+ </view>
+ <view class="overview-item">
+ <view class="overview-number">{{ overviewData.avgRepairTime }}</view>
+ <view class="overview-label">骞冲潎淇鏃堕棿(h)</view>
+ </view>
+ <view class="overview-item">
+ <view class="overview-number">{{ overviewData.faultRate }}%</view>
+ <view class="overview-label">鏁呴殰鐜�</view>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 鏁呴殰绫诲瀷缁熻 -->
+ <view class="stat-section">
+ <view class="section-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">鏁呴殰绫诲瀷缁熻</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+
+ <view class="item-details">
+ <view class="detail-row" v-for="(item, index) in faultTypeStats" :key="index">
+ <text class="detail-label">{{ item.name }}</text>
+ <text class="detail-value">{{ item.value }}娆�</text>
+ <text class="detail-value highlight">{{ item.percent }}%</text>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 鏍瑰洜鍒嗘瀽缁熻 -->
+ <view class="stat-section">
+ <view class="section-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="search" size="16" color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">鏍瑰洜鍒嗘瀽缁熻</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+
+ <view class="item-details">
+ <view class="detail-row" v-for="(item, index) in rootCauseStats" :key="index">
+ <text class="detail-label">{{ item.name }}</text>
+ <text class="detail-value">{{ item.value }}娆�</text>
+ <text class="detail-value highlight">{{ item.percent }}%</text>
+ </view>
+ </view>
+ </view>
+ </view>
+
+ <!-- 璇︾粏鏁版嵁鍒楄〃 -->
+ <view class="table-section">
+ <view class="section-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="list" size="16" color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">璇︾粏鏁版嵁</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+
+ <view class="fault-list">
+ <view v-for="(item, index) in tableData" :key="index" class="fault-item">
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">璁惧鍚嶇О</text>
+ <text class="detail-value">{{ item.equipmentName }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏁呴殰绫诲瀷</text>
+ <view class="detail-value">
+ <u-tag :type="getFaultTypeTagType(item.faultType)" size="small">
+ {{ item.faultType }}
+ </u-tag>
+ </view>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏍瑰洜</text>
+ <text class="detail-value">{{ item.rootCause }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍋滄満鏃堕暱</text>
+ <text class="detail-value highlight">{{ item.downtime }}h</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鐝粍</text>
+ <text class="detail-value">{{ item.team }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍙戠敓鏃堕棿</text>
+ <text class="detail-value">{{ item.occurTime }}</text>
+ </view>
+ </view>
+ <up-divider v-if="index < tableData.length - 1"></up-divider>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import PageHeader from '@/components/PageHeader.vue'
+
+// 缁熻姒傝鏁版嵁
+const overviewData = ref({
+ totalFaults: 156,
+ totalDowntime: 1248.5,
+ avgRepairTime: 8.0,
+ faultRate: 3.2
+})
+
+// 鏁呴殰绫诲瀷缁熻
+const faultTypeStats = ref([
+ { name: '鏈烘鏁呴殰', value: 45, percent: 28.8 },
+ { name: '鐢垫皵鏁呴殰', value: 32, percent: 20.5 },
+ { name: '娑插帇鏁呴殰', value: 28, percent: 17.9 },
+ { name: '姘斿姩鏁呴殰', value: 25, percent: 16.0 },
+ { name: '鍏朵粬鏁呴殰', value: 26, percent: 16.7 }
+])
+
+// 鏍瑰洜鍒嗘瀽缁熻
+const rootCauseStats = ref([
+ { name: '鎿嶄綔涓嶅綋', value: 35, percent: 22.4 },
+ { name: '璁惧鑰佸寲', value: 28, percent: 17.9 },
+ { name: '缁存姢涓嶈冻', value: 22, percent: 14.1 },
+ { name: '鐜鍥犵礌', value: 18, percent: 11.5 },
+ { name: '璁捐缂洪櫡', value: 15, percent: 9.6 },
+ { name: '鍏朵粬鍘熷洜', value: 38, percent: 24.4 }
+])
+
+// 琛ㄦ牸鏁版嵁
+const tableData = ref([
+ {
+ equipmentName: '鐢熶骇绾緼-01',
+ faultType: '鏈烘鏁呴殰',
+ rootCause: '杞存壙纾ㄦ崯',
+ downtime: 12.5,
+ team: '鐢熶骇涓�鐝�',
+ occurTime: '2024-01-15 14:30'
+ },
+ {
+ equipmentName: '妫�娴嬭澶嘊-02',
+ faultType: '鐢垫皵鏁呴殰',
+ rootCause: '鐢佃矾鐭矾',
+ downtime: 8.0,
+ team: '鐢熶骇浜岀彮',
+ occurTime: '2024-01-16 09:15'
+ },
+ {
+ equipmentName: '杈呭姪璁惧C-03',
+ faultType: '娑插帇鏁呴殰',
+ rootCause: '娌圭娉勬紡',
+ downtime: 6.5,
+ team: '缁翠慨鐝�',
+ occurTime: '2024-01-17 16:45'
+ },
+ {
+ equipmentName: '鐢熶骇绾緼-02',
+ faultType: '姘斿姩鏁呴殰',
+ rootCause: '姘斿帇涓嶈冻',
+ downtime: 4.0,
+ team: '鐢熶骇涓�鐝�',
+ occurTime: '2024-01-18 11:20'
+ },
+ {
+ equipmentName: '妫�娴嬭澶嘊-01',
+ faultType: '鏈烘鏁呴殰',
+ rootCause: '浼犲姩甯︽柇瑁�',
+ downtime: 15.0,
+ team: '鐢熶骇浜岀彮',
+ occurTime: '2024-01-19 08:30'
+ }
+])
+
+// 鏂规硶
+const goBack = () => {
+ uni.navigateBack()
+}
+
+const getFaultTypeTagType = (faultType) => {
+ const typeMap = {
+ '鏈烘鏁呴殰': 'error',
+ '鐢垫皵鏁呴殰': 'warning',
+ '娑插帇鏁呴殰': 'info',
+ '姘斿姩鏁呴殰': 'success',
+ '鍏朵粬鏁呴殰': 'primary'
+ }
+ return typeMap[faultType] || 'primary'
+}
+
+// 鐢熷懡鍛ㄦ湡
+onMounted(() => {
+ console.log('鏁呴殰鍒嗘瀽椤甸潰宸插姞杞�')
+})
+</script>
+
+<style scoped>
+.fault-analysis-page {
+ background-color: #f5f5f5;
+ min-height: 100vh;
+}
+
+.overview-section,
+.stat-section,
+.table-section {
+ margin: 15px;
+}
+
+.section-item {
+ background: #ffffff;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+}
+
+.item-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 15px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+
+.item-left {
+ display: flex;
+ align-items: center;
+}
+
+.document-icon {
+ width: 28px;
+ height: 28px;
+ background: rgba(255, 255, 255, 0.2);
+ border-radius: 6px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 10px;
+}
+
+.item-id {
+ color: #ffffff;
+ font-size: 16px;
+ font-weight: 600;
+}
+
+.overview-content {
+ display: flex;
+ justify-content: space-around;
+ padding: 20px 15px;
+}
+
+.overview-item {
+ text-align: center;
+ flex: 1;
+}
+
+.overview-number {
+ font-size: 24px;
+ font-weight: bold;
+ color: #007aff;
+ margin-bottom: 5px;
+}
+
+.overview-label {
+ font-size: 12px;
+ color: #666;
+}
+
+.item-details {
+ padding: 15px;
+}
+
+.detail-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px 0;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.detail-row:last-child {
+ border-bottom: none;
+}
+
+.detail-label {
+ font-size: 14px;
+ color: #666;
+ min-width: 80px;
+}
+
+.detail-value {
+ font-size: 14px;
+ color: #333;
+ font-weight: 500;
+ text-align: right;
+}
+
+.detail-value.highlight {
+ color: #007aff;
+ font-weight: bold;
+}
+
+.fault-list {
+ padding: 15px;
+}
+
+.fault-item {
+ margin-bottom: 15px;
+}
+
+.fault-item:last-child {
+ margin-bottom: 0;
+}
+</style>
diff --git a/src/pages/equipmentManagement/repair/add.vue b/src/pages/equipmentManagement/repair/add.vue
index 8717814..0940052 100644
--- a/src/pages/equipmentManagement/repair/add.vue
+++ b/src/pages/equipmentManagement/repair/add.vue
@@ -356,7 +356,7 @@
// 缂栬緫妯″紡锛岃幏鍙栬鎯�
loadForm(id);
// 鍙�夛細鑾峰彇鍚庢竻闄ゅ瓨鍌ㄧ殑id锛岄伩鍏嶅奖鍝嶅悗缁搷浣�
- // uni.removeStorageSync('repairId');
+ uni.removeStorageSync('repairId');
} else {
// 鏂板妯″紡
loadForm();
diff --git a/src/pages/index.vue b/src/pages/index.vue
index 2062b64..1749b98 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -268,21 +268,26 @@
label: '璁惧淇濆吇',
},
{
- icon: '/static/images/icon/shebeixunjian@2x.png',
+ icon: '/static/images/icon/xunjianshangchuan@2x.png',
label: '宸℃涓婁紶',
},
{
- icon: 'flash',
+ icon: '/static/images/icon/guzhangfenxi@2x.png',
+ label: '鍒嗘瀽杩芥函',
+ bgColor: '#ff9800'
+ },
+ {
+ icon: '/static/images/icon/zhinengpaidan@2x.png',
label: '鏅鸿兘娲惧崟',
bgColor: '#ff6b35'
},
{
- icon: 'file-text',
+ icon: '/static/images/icon/zuoyezhidao@2x.png',
label: '浣滀笟鎸囧',
bgColor: '#4caf50'
},
{
- icon: 'checkmark-circle',
+ icon: '/static/images/icon/jieguoyanzheng@2x.png',
label: '缁撴灉楠岃瘉',
bgColor: '#9c27b0'
}
@@ -382,6 +387,11 @@
url: '/pages/inspectionUpload/index'
});
break;
+ case '鍒嗘瀽杩芥函':
+ uni.navigateTo({
+ url: '/pages/equipmentManagement/faultAnalysis/index'
+ });
+ break;
case '鏅鸿兘娲惧崟':
uni.navigateTo({
url: '/pages/equipmentManagement/smartDispatch/index'
diff --git a/src/pages/inspectionUpload/components/qrCodeFormDia.vue b/src/pages/inspectionUpload/components/qrCodeFormDia.vue
index 3dc83ef..bcf30ef 100644
--- a/src/pages/inspectionUpload/components/qrCodeFormDia.vue
+++ b/src/pages/inspectionUpload/components/qrCodeFormDia.vue
@@ -42,36 +42,15 @@
<u-form-item label="闄勪欢" prop="storageBlobDTO" labelWidth="80">
<view class="upload-container">
- <u-upload
- :fileList="form.storageBlobDTO"
- @afterRead="afterRead"
- @delete="deleteFile"
- name="files"
- multiple
- :maxCount="10"
- :maxSize="50 * 1024 * 1024"
- accept="image/*,video/*"
- :previewFullImage="true"
- :camera="true"
- :gallery="true"
- ></u-upload>
- <view class="upload-actions">
- <u-button
- type="primary"
- size="small"
- @click="chooseImage"
- :customStyle="{ marginRight: '10px' }"
- >
- 鎷嶇収
- </u-button>
- <u-button
- type="success"
- size="small"
- @click="chooseVideo"
- >
- 褰曞儚
- </u-button>
- </view>
+ <ImageUpload
+ v-model="form.storageBlobDTO"
+ :limit="10"
+ :fileSize="50"
+ :fileType="['jpg', 'jpeg', 'png', 'mp4', 'mov']"
+ :maxVideoDuration="60"
+ :statusType="0"
+ @update:modelValue="handleStorageBlobUpdate"
+ />
</view>
</u-form-item>
@@ -102,9 +81,10 @@
</template>
<script setup>
-import { reactive, ref, onMounted, nextTick } from 'vue'
+import { reactive, ref, onMounted, onUnmounted, nextTick } from 'vue'
import { addOrEditQrCodeRecord } from '@/api/inspectionUpload/index.js'
import useUserStore from '@/store/modules/user.ts'
+import ImageUpload from '@/components/imageUpload/index.vue'
const emit = defineEmits(['closeDia'])
@@ -113,6 +93,9 @@
const userStore = useUserStore()
const userInfo = ref({})
const locationLoading = ref(false)
+
+// 璇锋眰鍙栨秷鏍囧織
+let isRequestCancelled = false
// 鑾峰彇褰撳墠鏃堕棿
function getCurrentDateTime() {
@@ -149,125 +132,50 @@
onMounted(async () => {
try {
const res = await userStore.getInfo()
- userInfo.value = res.user
- form.scannerName = userInfo.value.nickName
- form.scannerId = userInfo.value.userId
- form.scanTime = getCurrentDateTime()
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (!isRequestCancelled && userInfo.value !== undefined) {
+ userInfo.value = res.user
+ form.scannerName = userInfo.value.nickName
+ form.scannerId = userInfo.value.userId
+ form.scanTime = getCurrentDateTime()
+ }
} catch (error) {
console.error('鑾峰彇鐢ㄦ埛淇℃伅澶辫触:', error)
}
})
-// 鏂囦欢涓婁紶澶勭悊
-const afterRead = (event) => {
- const { file } = event
- console.log('鏂囦欢閫夋嫨:', file)
-
- // 鐩存帴娣诲姞鍒版枃浠跺垪琛紝涓嶄笂浼犲埌鏈嶅姟鍣�
- const fileItem = {
- url: file.url,
- name: file.name || `鏂囦欢_${Date.now()}`,
- status: 'success',
- size: file.size || 0,
- type: file.type || 'image/jpeg'
- }
-
- form.storageBlobDTO.push(fileItem)
-
- uni.showToast({
- title: '鏂囦欢娣诲姞鎴愬姛',
- icon: 'success'
- })
-}
-
-// 鎷嶇収
-const chooseImage = () => {
- uni.chooseImage({
- count: 1,
- sizeType: ['original', 'compressed'],
- sourceType: ['camera'],
- success: (res) => {
- console.log('鎷嶇収鎴愬姛:', res)
- const tempFilePath = res.tempFilePaths[0]
-
- const fileItem = {
- url: tempFilePath,
- name: `鐓х墖_${Date.now()}.jpg`,
- status: 'success',
- type: 'image/jpeg'
- }
-
- form.storageBlobDTO.push(fileItem)
-
- uni.showToast({
- title: '鎷嶇収鎴愬姛',
- icon: 'success'
- })
- },
- fail: (err) => {
- console.error('鎷嶇収澶辫触:', err)
- uni.showToast({
- title: '鎷嶇収澶辫触',
- icon: 'error'
- })
- }
- })
-}
-
-// 褰曞儚
-const chooseVideo = () => {
- uni.chooseVideo({
- sourceType: ['camera'],
- maxDuration: 60, // 鏈�澶�60绉�
- camera: 'back',
- success: (res) => {
- console.log('褰曞儚鎴愬姛:', res)
- const tempFilePath = res.tempFilePath
-
- const fileItem = {
- url: tempFilePath,
- name: `瑙嗛_${Date.now()}.mp4`,
- status: 'success',
- type: 'video/mp4',
- duration: res.duration,
- size: res.size
- }
-
- form.storageBlobDTO.push(fileItem)
-
- uni.showToast({
- title: '褰曞儚鎴愬姛',
- icon: 'success'
- })
- },
- fail: (err) => {
- console.error('褰曞儚澶辫触:', err)
- uni.showToast({
- title: '褰曞儚澶辫触',
- icon: 'error'
- })
- }
- })
-}
-
-// 鍒犻櫎鏂囦欢
-const deleteFile = (event) => {
- const { index } = event
- form.storageBlobDTO.splice(index, 1)
+// 澶勭悊storageBlobDTO鏁版嵁鏇存柊
+const handleStorageBlobUpdate = (value) => {
+ form.storageBlobDTO = value || []
}
// 鑾峰彇褰撳墠浣嶇疆
const getCurrentLocation = () => {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (isRequestCancelled) return
+
locationLoading.value = true
uni.showLoading({ title: '鑾峰彇浣嶇疆涓�...' })
uni.getLocation({
type: 'gcj02',
success: (res) => {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (isRequestCancelled) {
+ uni.hideLoading()
+ return
+ }
+
// 浣跨敤閫嗗湴鐞嗙紪鐮佽幏鍙栧湴鍧�淇℃伅
uni.request({
url: `https://restapi.amap.com/v3/geocode/regeo?key=c120a5dc69a9f61839f7763e6057005f&location=${res.longitude},${res.latitude}&radius=1000&extensions=all`,
success: (geoRes) => {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (isRequestCancelled) {
+ uni.hideLoading()
+ return
+ }
+
uni.hideLoading()
locationLoading.value = false
@@ -303,6 +211,12 @@
}
},
fail: (err) => {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (isRequestCancelled) {
+ uni.hideLoading()
+ return
+ }
+
uni.hideLoading()
locationLoading.value = false
console.error('閫嗗湴鐞嗙紪鐮佸け璐�:', err)
@@ -318,6 +232,12 @@
})
},
fail: (err) => {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (isRequestCancelled) {
+ uni.hideLoading()
+ return
+ }
+
uni.hideLoading()
locationLoading.value = false
uni.showToast({
@@ -335,15 +255,11 @@
// 鎵撳紑寮规
const openDialog = async (row) => {
console.log('寮规鎺ユ敹鍒扮殑鏁版嵁:', row)
- console.log('寮规鎵撳紑鍓嶇姸鎬�:', dialogVisitable.value)
-
dialogVisitable.value = true
form.deviceName = row.deviceName || ''
form.location = row.location || ''
- form.qrCodeId = row.qrCodeId || row.id || ''
+ form.qrCodeId = row.qrCodeId
form.storageBlobDTO = []
-
- console.log('寮规鎵撳紑鍚庣姸鎬�:', dialogVisitable.value)
console.log('寮规琛ㄥ崟鏁版嵁:', form)
// 寮哄埗鏇存柊瑙嗗浘
@@ -354,6 +270,9 @@
// 鎻愪氦琛ㄥ崟
const submitForm = async () => {
try {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (isRequestCancelled) return
+
console.log('寮�濮嬫彁浜よ〃鍗曪紝褰撳墠琛ㄥ崟鏁版嵁:', form)
// 琛ㄥ崟楠岃瘉
@@ -389,15 +308,27 @@
scannerName: form.scannerName,
scannerId: form.scannerId,
scanTime: form.scanTime,
- storageBlobDTO: form.storageBlobDTO,
+ storageBlobDTO: form.storageBlobDTO.map(file => ({
+ id: file.id, // 娣诲姞id瀛楁
+ url: file.url,
+ bucketFilename: file.bucketFilename || file.name,
+ downloadUrl: file.downloadUrl || file.url,
+ type: 0,
+ size: file.size,
+ createTime: file.createTime || new Date().getTime()
+ })),
qrCode: {
- id: form.qrCodeId || form.qrCode.id
+ id: form.qrCodeId
}
}
console.log('鍑嗗鎻愪氦鐨勬暟鎹�:', submitData)
const response = await addOrEditQrCodeRecord(submitData)
+
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (isRequestCancelled) return
+
console.log('鎻愪氦鍝嶅簲:', response)
uni.showToast({
@@ -407,6 +338,9 @@
cancel()
} catch (error) {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (isRequestCancelled) return
+
console.error('鎻愪氦澶辫触:', error)
// 鏄剧ず鏇磋缁嗙殑閿欒淇℃伅
@@ -430,6 +364,25 @@
dialogVisitable.value = false
emit('closeDia')
}
+
+// 缁勪欢閿�姣佹椂鐨勬竻鐞�
+onUnmounted(() => {
+ // 璁剧疆鍙栨秷鏍囧織锛岄樆姝㈠悗缁殑寮傛鎿嶄綔
+ isRequestCancelled = true
+
+ // 娓呯悊鐘舵��
+ if (locationLoading.value) {
+ locationLoading.value = false
+ }
+
+ // 鍏抽棴寮圭獥
+ if (dialogVisitable.value) {
+ dialogVisitable.value = false
+ }
+
+ // 闅愯棌鍙兘鏄剧ず鐨勫姞杞芥彁绀�
+ uni.hideLoading()
+})
defineExpose({ openDialog })
</script>
@@ -491,12 +444,5 @@
.upload-container {
width: 100%;
-}
-
-.upload-actions {
- display: flex;
- justify-content: flex-start;
- margin-top: 10px;
- gap: 10px;
}
</style>
diff --git a/src/pages/inspectionUpload/index.vue b/src/pages/inspectionUpload/index.vue
index aed4e6e..3b9e0d7 100644
--- a/src/pages/inspectionUpload/index.vue
+++ b/src/pages/inspectionUpload/index.vue
@@ -1,74 +1,14 @@
<template>
<view class="inspection-upload-page">
<!-- 椤甸潰澶撮儴 -->
- <PageHeader title="宸℃涓婁紶" />
-
- <!-- 鏍囩椤� -->
- <view class="tabs-container">
- <view class="custom-tabs">
- <view
- v-for="(tab, index) in tabs"
- :key="index"
- class="tab-item"
- :class="{ 'tab-active': currentTabIndex === index }"
- @click="handleTabChange(index)"
- >
- {{ tab.name }}
- </view>
- <view class="tab-line" :style="{ left: currentTabIndex * 50 + '%' }"></view>
- </view>
- </view>
-
- <!-- 鎵爜妯″潡 -->
- <view v-if="activeTab === 'qrCode'" class="scan-section">
- <view class="scan-controls">
- <u-button
- :type="isScanning ? 'error' : 'primary'"
- :loading="scanLoading"
- @click="toggleScan"
- >
- {{ scanButtonText }}
- </u-button>
- </view>
-
- <!-- 鎵爜鍖哄煙 -->
- <view v-show="isScanning" class="qr-scan-container">
- <camera
- class="qr-camera"
- device-position="back"
- flash="off"
- @scancode="handleScanCode"
- @error="handleCameraError"
- ></camera>
- <view class="scan-overlay">
- <view class="scan-frame"></view>
- <view class="scan-tip">璇峰皢浜岀淮鐮佹斁鍏ユ鍐�</view>
- </view>
- </view>
-
- <!-- 鐘舵�佹彁绀� -->
- <view class="status-info">
- <u-alert
- v-if="cameraError"
- :title="cameraError"
- type="error"
- :showIcon="true"
- :closable="true"
- @close="cameraError = ''"
- ></u-alert>
- <view v-if="isScanning" class="scanning-text">
- <u-loading-icon mode="circle" color="#1890ff" size="20"></u-loading-icon>
- <text class="scanning-label">姝e湪鎵弿浜岀淮鐮�...</text>
- </view>
- </view>
- </view>
+ <PageHeader title="宸℃涓婁紶" @back="goBack"/>
<!-- 鏁版嵁鍒楄〃 -->
<view class="table-section">
<!-- 鐢熶骇宸℃鍒楄〃 -->
- <view v-if="activeTab === 'task'" class="task-list">
+ <view class="task-list">
<view
- v-for="(item, index) in tableData"
+ v-for="(item, index) in taskTableData"
:key="index"
class="task-item"
@click="handleAdd(item)"
@@ -91,147 +31,127 @@
>
涓婁紶
</u-button>
- </view>
- </view>
- <view class="task-details">
- <view class="detail-item">
- <text class="detail-label">澶囨敞锛�</text>
- <text class="detail-value">{{ item.remarks || '鏃�' }}</text>
- </view>
- <view class="detail-item">
- <text class="detail-label">鎵ц浜猴細</text>
- <text class="detail-value">{{ item.inspector }}</text>
- </view>
- </view>
- </view>
- </view>
-
- <!-- 鐜板満宸℃鍒楄〃 -->
- <view v-if="activeTab === 'qrCode'" class="qr-list">
- <view
- v-for="(item, index) in tableData"
- :key="index"
- class="qr-item"
- @click="viewFile(item)"
- >
- <view class="qr-header">
- <view class="qr-info">
- <text class="device-name">{{ item.qrCode?.deviceName }}</text>
- <text class="device-location">{{ item.qrCode?.location }}</text>
- </view>
- <view class="qr-actions">
<u-button
- type="primary"
+ type="info"
size="small"
- @click.stop="viewFile(item)"
+ @click.stop="startScanForTask(item)"
:customStyle="{
borderRadius: '15px',
height: '30px',
fontSize: '12px'
}"
>
- 鏌ョ湅闄勪欢
+ 鎵爜
</u-button>
</view>
</view>
- <view class="qr-details">
+ <view class="task-details">
<view class="detail-item">
- <text class="detail-label">宸℃浜猴細</text>
- <text class="detail-value">{{ item.scanner }}</text>
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remarks || '鏃�' }}</text>
</view>
<view class="detail-item">
- <text class="detail-label">宸℃鏃堕棿锛�</text>
- <text class="detail-value">{{ item.scanTime }}</text>
+ <text class="detail-label">鎵ц浜�</text>
+ <text class="detail-value">{{ item.inspector }}</text>
</view>
</view>
</view>
</view>
<!-- 绌虹姸鎬� -->
- <view v-if="tableData.length === 0 && !tableLoading" class="empty-state">
- <u-empty
- mode="data"
- text="鏆傛棤鏁版嵁"
- :iconSize="80"
- ></u-empty>
- </view>
-
- <!-- 鍔犺浇鐘舵�� -->
- <view v-if="tableLoading" class="loading-state">
- <u-loading-icon mode="circle" color="#1890ff" size="40"></u-loading-icon>
- <text class="loading-text">鍔犺浇涓�...</text>
+ <view v-if="taskTableData.length === 0" class="no-data">
+ <text>鏆傛棤鏁版嵁</text>
</view>
</view>
- <!-- 鍒嗛〉 -->
- <view v-if="total > 0" class="pagination-container">
- <u-pagination
- :total="total"
- :current="pageNum"
- :pageSize="pageSize"
- @change="handlePageChange"
- :showTotal="true"
- :showSizer="false"
- :showJumper="false"
- ></u-pagination>
+ <!-- 鎵爜鍖哄煙 - 鍏ㄥ眬寮圭獥 -->
+ <view v-if="isScanning" class="qr-scan-overlay">
+ <view class="qr-scan-container">
+ <view class="scan-header">
+ <text class="scan-title">鎵弿浜岀淮鐮�</text>
+ <u-button
+ type="error"
+ size="small"
+ @click.stop="stopScan"
+ :customStyle="{
+ borderRadius: '15px',
+ height: '30px',
+ fontSize: '12px'
+ }"
+ >
+ 鍏抽棴
+ </u-button>
+ </view>
+ <camera
+ class="qr-camera"
+ device-position="back"
+ flash="off"
+ @scancode="handleScanCode"
+ @error="handleCameraError"
+ ></camera>
+ <view class="scan-frame-wrapper">
+ <view class="scan-frame"></view>
+ <view class="scan-tip">璇峰皢浜岀淮鐮佹斁鍏ユ鍐�</view>
+ </view>
+ <u-alert
+ v-if="cameraError"
+ :title="cameraError"
+ type="error"
+ :showIcon="true"
+ :closable="true"
+ @close="cameraError = ''"
+ :customStyle="{
+ margin: '10px 0'
+ }"
+ ></u-alert>
+ </view>
</view>
<!-- 寮圭獥缁勪欢 -->
<form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
- <qr-code-form-dia ref="qrCodeFormDia" @closeDia="handleQuery"></qr-code-form-dia>
</view>
</template>
<script setup>
-import { onMounted, ref, reactive, computed, nextTick } from 'vue'
+import { onMounted, onUnmounted, ref, nextTick } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import PageHeader from '@/components/PageHeader.vue'
import FormDia from './components/formDia.vue'
-import QrCodeFormDia from './components/qrCodeFormDia.vue'
-import { qrCodeScanRecordList } from '@/api/inspectionUpload/index.js'
-import { getInspectionTaskList } from '@/api/equipmentManagement/inspection.js'
+import { getLedgerById } from '@/api/equipmentManagement/ledger.js'
import {inspectionTaskList} from "@/api/inspectionManagement";
-// import ViewQrCodeFiles from '@/pages/inspectionManagement/components/viewQrCodeFiles.vue'
// 缁勪欢寮曠敤
const formDia = ref()
-const qrCodeFormDia = ref()
-// 褰撳墠鏍囩
-const activeTab = ref('task')
-
-const tabName = ref('task')
-const currentTabIndex = ref(0)
-
-// 鏍囩椤垫暟鎹�
-const tabs = reactive([
- { name: '鐢熶骇宸℃' },
- { name: '鐜板満宸℃' }
-])
+// 鍔犺浇鎻愮ず鏂规硶
+const showLoadingToast = (message) => {
+ uni.showLoading({
+ title: message,
+ mask: true
+ })
+}
+const closeToast = () => {
+ uni.hideLoading()
+}
// 琛ㄦ牸鏁版嵁
-const tableData = ref([])
-const tableLoading = ref(false)
-const total = ref(0)
-const pageNum = ref(1)
-const pageSize = ref(10)
+const taskTableData = ref([]) // 鐢熶骇宸℃鏁版嵁
+
+// 褰撳墠鎵弿鐨勪换鍔�
+const currentScanningTask = ref(null)
+
+// 璇锋眰鍙栨秷鏍囧織锛岀敤浜庡彇娑堟鍦ㄨ繘琛岀殑璇锋眰
+let isRequestCancelled = false
// 鎵爜鐩稿叧鐘舵��
const isScanning = ref(false)
-const scanLoading = ref(false)
const cameraError = ref('')
-
-// 璁$畻灞炴��
-const scanButtonText = computed(() => {
- if (scanLoading.value) return '姝e湪鍒濆鍖�...'
- return isScanning.value ? '鍋滄鎵爜' : '寮�濮嬫壂鐮�'
-})
// 鐢熷懡鍛ㄦ湡
onMounted(() => {
// 寤惰繜鍒濆鍖栵紝纭繚DOM宸叉覆鏌�
nextTick(() => {
- handleTabClick({ props: { name: 'task' } })
+ getList()
})
})
@@ -240,84 +160,112 @@
getList()
})
-// 鏍囩椤靛垏鎹�
-const handleTabChange = (index) => {
- currentTabIndex.value = index
- const tabNames = ['task', 'qrCode']
- activeTab.value = tabNames[index]
- tabName.value = tabNames[index]
- tableData.value = []
- pageNum.value = 1
- getList()
-}
+// 缁勪欢閿�姣佹椂鐨勬竻鐞�
+onUnmounted(() => {
+ // 璁剧疆鍙栨秷鏍囧織锛岄樆姝㈠悗缁殑寮傛鎿嶄綔
+ isRequestCancelled = true
+
+ // 鍋滄鎵爜
+ if (isScanning.value) {
+ isScanning.value = false
+ }
+})
-// 鏍囩椤电偣鍑伙紙鍏煎鏃ф柟娉曪級
-const handleTabClick = (tab) => {
- tabName.value = tab.props.name
- activeTab.value = tab.props.name
- tableData.value = []
- getList()
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+ uni.navigateBack()
}
// 鏌ヨ鏁版嵁
const handleQuery = () => {
- pageNum.value = 1
- pageSize.value = 10
getList()
}
// 鑾峰彇鍒楄〃鏁版嵁
const getList = () => {
- tableLoading.value = true
- if (tabName.value === "task") {
- inspectionTaskList({size: pageSize.value, current: pageNum.value}).then(res => {
- tableLoading.value = false;
- tableData.value = res.data.records;
- total.value = res.data.total;
- })
- } else {
- qrCodeScanRecordList({size: pageSize.value, current: pageNum.value}).then(res => {
- tableLoading.value = false;
- tableData.value = res.data.records;
- total.value = res.data.total;
- })
- }
+ // 鏄剧ず鍔犺浇鎻愮ず
+ showLoadingToast('鍔犺浇涓�...')
+
+ // 璁剧疆鍙栨秷鏍囧織
+ isRequestCancelled = false
+
+ inspectionTaskList({}).then(res => {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪涓旇姹傛湭琚彇娑�
+ if (!isRequestCancelled) {
+ console.log('鐢熶骇宸℃API杩斿洖鏁版嵁:', res);
+
+ // 澶勭悊涓嶅悓鐨勬暟鎹粨鏋�
+ let records = [];
+ if (res && res.data) {
+ // 灏濊瘯澶氱鍙兘鐨勬暟鎹粨鏋�
+ if (Array.isArray(res.data.records)) {
+ records = res.data.records;
+ } else if (Array.isArray(res.data.rows)) {
+ records = res.data.rows;
+ } else if (Array.isArray(res.data)) {
+ records = res.data;
+ } else if (Array.isArray(res.data.list)) {
+ records = res.data.list;
+ }
+ }
+
+ if (records.length > 0) {
+ taskTableData.value = records;
+ console.log('鐢熶骇宸℃鏁版嵁璁剧疆鎴愬姛锛岃褰曟暟:', records.length);
+ } else {
+ console.warn('鐢熶骇宸℃鏁版嵁涓虹┖鎴栨牸寮忎笉姝g‘:', res);
+ taskTableData.value = [];
+ uni.showToast({
+ title: '鏆傛棤宸℃浠诲姟鏁版嵁',
+ icon: 'none'
+ });
+ }
+ }
+ // 鍏抽棴鍔犺浇鎻愮ず
+ closeToast()
+ }).catch(err => {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪涓旇姹傛湭琚彇娑�
+ if (!isRequestCancelled) {
+ console.error('鑾峰彇鐢熶骇宸℃鏁版嵁澶辫触:', err);
+ taskTableData.value = [];
+ // 娣诲姞閿欒鎻愮ず
+ uni.showToast({
+ title: '鑾峰彇鏁版嵁澶辫触',
+ icon: 'error'
+ })
+ }
+ // 鍏抽棴鍔犺浇鎻愮ず
+ closeToast()
+ })
}
-// 鍒嗛〉鍙樺寲
-const handlePageChange = (page) => {
- pageNum.value = page
- getList()
-}
// 涓婁紶
const handleAdd = (row) => {
nextTick(() => {
- formDia.value?.openDialog(row)
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪
+ if (formDia.value && formDia.value.openDialog) {
+ formDia.value.openDialog(row)
+ } else {
+ console.error('涓婁紶缁勪欢寮曠敤涓嶅瓨鍦�')
+ uni.showToast({
+ title: '缁勪欢鏈噯澶囧ソ',
+ icon: 'error'
+ })
+ }
})
}
-// 鏌ョ湅闄勪欢
-const viewFile = (row) => {
- console.log('鏌ョ湅闄勪欢:', row)
- uni.showToast({
- title: '鏌ョ湅闄勪欢鍔熻兘寮�鍙戜腑',
- icon: 'none'
- })
-}
-
-// 鎵爜鐩稿叧鏂规硶
-const toggleScan = async () => {
- if (isScanning.value) {
- await stopScan()
- } else {
- await startScan()
- }
-}
-
-const startScan = async () => {
+// 涓烘寚瀹氫换鍔″紑濮嬫壂鐮�
+const startScanForTask = async (task) => {
try {
- scanLoading.value = true
+ // 璁板綍褰撳墠鎵弿鐨勪换鍔�
+ currentScanningTask.value = task
+ console.log('涓轰换鍔″紑濮嬫壂鐮�:', task.taskName)
+
+ // 鏄剧ず鎵弿鐣岄潰
+ isScanning.value = true
+
// 浣跨敤uniapp鐨勬壂鐮丄PI
uni.scanCode({
success: (res) => {
@@ -330,29 +278,37 @@
title: '鎵爜澶辫触',
icon: 'error'
})
+ // 鍏抽棴鎵弿鐣岄潰
+ isScanning.value = false
},
complete: () => {
- scanLoading.value = false
+ // 鎵爜瀹屾垚鍚庡叧闂壂鎻忕晫闈�
+ setTimeout(() => {
+ isScanning.value = false
+ }, 1000)
}
})
} catch (e) {
console.error('鍚姩鎵爜澶辫触:', e)
- scanLoading.value = false
uni.showToast({
title: '鍚姩鎵爜澶辫触',
icon: 'error'
})
+ isScanning.value = false
}
}
-const stopScan = async () => {
+// 鍋滄鎵爜
+const stopScan = () => {
isScanning.value = false
+ currentScanningTask.value = null
}
// 鎵爜鎴愬姛澶勭悊
const handleScanSuccess = async (result) => {
try {
console.log('澶勭悊鎵爜缁撴灉:', result)
+ console.log('褰撳墠鍏宠仈浠诲姟:', currentScanningTask.value?.taskName)
uni.showToast({
title: '璇嗗埆鎴愬姛',
@@ -361,17 +317,51 @@
// 瑙f瀽浜岀淮鐮佹暟鎹�
let qrData
+ let deviceId = ''
+
try {
qrData = JSON.parse(result.result)
console.log('瑙f瀽鐨勪簩缁寸爜鏁版嵁:', qrData)
+ deviceId = qrData.deviceId || qrData.qrCodeId
} catch (e) {
- // 濡傛灉涓嶆槸JSON鏍煎紡锛岀洿鎺ヤ娇鐢ㄦ壂鐮佺粨鏋滀綔涓鸿澶囧悕绉�
- qrData = {
- deviceName: result.result,
- location: '',
- qrCodeId: result.result // 娣诲姞浜岀淮鐮両D
+ // 濡傛灉涓嶆槸JSON鏍煎紡锛屽皾璇曚粠URL涓彁鍙杁eviceId
+ const url = result.result
+
+ if (url.includes('deviceId=')) {
+ // 浠嶶RL涓彁鍙杁eviceId
+ const match = url.match(/deviceId=(\d+)/)
+ if (match && match[1]) {
+ deviceId = match[1]
+ }
}
- console.log('浣跨敤榛樿鏁版嵁鏍煎紡:', qrData)
+
+ qrData = {
+ deviceName: deviceId ? `璁惧${deviceId}` : result.result,
+ location: '',
+ qrCodeId: deviceId // 浣跨敤鎻愬彇鐨刣eviceId鎴栧師濮嬬粨鏋�
+ }
+ }
+
+ // 濡傛灉鏈夎澶嘔D锛屽皾璇曚粠API鑾峰彇鐪熷疄鐨勮澶囧悕绉�
+ if (deviceId) {
+ try {
+ console.log('姝e湪鏌ヨ璁惧淇℃伅锛岃澶嘔D:', deviceId)
+ const response = await getLedgerById(deviceId)
+ console.log('璁惧淇℃伅鏌ヨ缁撴灉:', response)
+
+ if (response.code === 200 && response.data) {
+ qrData.deviceName = response.data.deviceName || `璁惧${deviceId}`
+ qrData.location = response.data.storageLocation || ''
+ console.log('鑾峰彇鍒拌澶囧悕绉�:', qrData.deviceName)
+ } else {
+ console.warn('璁惧淇℃伅鏌ヨ澶辫触锛屼娇鐢ㄩ粯璁ゅ悕绉�')
+ qrData.deviceName = qrData.deviceName || `璁惧${deviceId}`
+ }
+ } catch (apiError) {
+ console.error('鏌ヨ璁惧淇℃伅澶辫触:', apiError)
+ // API璋冪敤澶辫触鏃朵娇鐢ㄩ粯璁ゅ悕绉�
+ qrData.deviceName = qrData.deviceName || `璁惧${deviceId}`
+ }
}
// 纭繚鏁版嵁瀹屾暣鎬�
@@ -379,51 +369,40 @@
qrData.deviceName = result.result
}
if (!qrData.qrCodeId) {
- qrData.qrCodeId = result.result
+ qrData.qrCodeId = deviceId || result.result
}
- callBackendAPI(qrData)
+ // 灏嗘壂鐮佹暟鎹笌浠诲姟鍏宠仈
+ if (currentScanningTask.value) {
+ // 鍏宠仈浠诲姟淇℃伅
+ const taskData = {
+ ...currentScanningTask.value,
+ qrCodeData: qrData
+ }
+
+ // 鎵撳紑涓婁紶寮圭獥锛屼紶閫掑叧鑱斿悗鐨勪换鍔℃暟鎹�
+ nextTick(() => {
+ if (formDia.value && formDia.value.openDialog) {
+ formDia.value.openDialog(taskData)
+ } else {
+ console.error('涓婁紶缁勪欢寮曠敤涓嶅瓨鍦�')
+ uni.showToast({
+ title: '缁勪欢鏈噯澶囧ソ',
+ icon: 'error'
+ })
+ }
+ })
+ }
} catch (error) {
console.error('澶勭悊鎵爜缁撴灉澶辫触:', error)
uni.showToast({
title: error.message || '鏁版嵁瑙f瀽澶辫触',
icon: 'error'
})
+ } finally {
+ // 鍏抽棴鎵弿鐣岄潰
+ isScanning.value = false
}
-}
-
-const callBackendAPI = (result) => {
- console.log('鍑嗗鎵撳紑寮规锛屾暟鎹�:', result)
- console.log('寮规缁勪欢寮曠敤:', qrCodeFormDia.value)
-
- // 纭繚缁勪欢寮曠敤瀛樺湪
- if (qrCodeFormDia.value) {
- console.log('鐩存帴璋冪敤寮规openDialog鏂规硶')
- qrCodeFormDia.value.openDialog(result)
- } else {
- // 濡傛灉缁勪欢寮曠敤涓嶅瓨鍦紝绛夊緟涓嬩竴涓猼ick
- console.log('缁勪欢寮曠敤涓嶅瓨鍦紝绛夊緟nextTick')
- nextTick(() => {
- console.log('nextTick鍚庡脊妗嗙粍浠跺紩鐢�:', qrCodeFormDia.value)
- if (qrCodeFormDia.value) {
- console.log('nextTick鍚庤皟鐢ㄥ脊妗唎penDialog鏂规硶')
- qrCodeFormDia.value.openDialog(result)
- } else {
- console.error('寮规缁勪欢寮曠敤涓嶅瓨鍦�')
- uni.showToast({
- title: '寮规缁勪欢鏈噯澶囧ソ',
- icon: 'error'
- })
- }
- })
- }
-}
-
-
-// 鎵爜澶勭悊
-const handleScanCode = (result) => {
- console.log('鎵爜缁撴灉:', result)
- handleScanSuccess(result)
}
// 鎽勫儚澶撮敊璇鐞�
@@ -434,104 +413,192 @@
</script>
<style scoped lang="scss">
+// 瀵煎叆閿�鍞ā鍧楀叕鍏辨牱寮�
+@import '@/styles/sales-common.scss';
+
+// 椤甸潰瀹瑰櫒鏍峰紡
.inspection-upload-page {
min-height: 100vh;
- background-color: #f5f5f5;
-}
-
-.tabs-container {
- background-color: #fff;
- margin: 0;
- border-bottom: 1px solid #e8e8e8;
-}
-
-.custom-tabs {
- display: flex;
+ background: #f8f9fa;
position: relative;
- background-color: #fff;
- width: 100%;
}
-.tab-item {
+// 鍒楄〃瀹瑰櫒鏍峰紡
+.table-section {
+ padding: 20px;
+}
+
+// 浠诲姟鍒楄〃鏍峰紡 - 浣跨敤閿�鍞彴璐︾殑鏍峰紡瑙勮寖
+.task-list {
+ .task-item {
+ background: #ffffff;
+ border-radius: 12px;
+ margin-bottom: 16px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ padding: 0 16px;
+
+ &:active {
+ transform: scale(0.98);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
+ }
+ }
+}
+
+// 椤圭洰澶撮儴鏍峰紡
+.task-header {
+ padding: 16px 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 0;
+}
+
+.task-info {
flex: 1;
- text-align: center;
- padding: 20px 0;
- font-size: 16px;
+}
+
+.task-name {
+ font-size: 14px;
+ color: #333;
font-weight: 500;
- color: #606266;
- transition: all 0.3s ease;
- cursor: pointer;
- position: relative;
- z-index: 2;
+ margin-bottom: 0;
+ line-height: 1.4;
}
-.tab-item.tab-active {
- color: #1890ff;
- font-weight: 600;
+.task-location {
+ font-size: 12px;
+ color: #666;
+ margin-top: 4px;
}
-.tab-line {
- position: absolute;
- bottom: 0;
- width: 50%;
- height: 3px;
- background-color: #1890ff;
- transition: left 0.3s ease;
-}
-
-.scan-section {
- background-color: #fff;
- padding: 10px;
-}
-
-.scan-controls {
+// 浠诲姟鎿嶄綔鎸夐挳鏍峰紡
+.task-actions {
display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-left: 0;
+}
+
+// 浠诲姟璇︽儏鏍峰紡 - 浣跨敤閿�鍞彴璐︾殑璇︽儏琛屾牱寮�
+.task-details {
+ padding: 16px 0;
+
+ .detail-item {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ margin-bottom: 8px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .detail-label {
+ font-size: 12px;
+ color: #777777;
+ min-width: 60px;
+ flex-shrink: 0;
+ }
+
+ .detail-value {
+ font-size: 12px;
+ color: #000000;
+ text-align: right;
+ flex: 1;
+ margin-left: 16px;
+ line-height: 1.4;
+ }
+ }
+}
+
+// 鏃犳暟鎹彁绀烘牱寮� - 浣跨敤閿�鍞彴璐︾殑鏍峰紡
+.no-data {
+ padding: 40px 0;
+ text-align: center;
+ color: #999;
+ background: none;
+ margin: 0;
+}
+
+.no-data text {
+ font-size: 14px;
+ color: #999;
+}
+
+/* 鎵爜寮圭獥鏍峰紡 */
+.qr-scan-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.8);
+ z-index: 9999;
+ display: flex;
+ flex-direction: column;
justify-content: center;
+ align-items: center;
+ padding: 20px;
}
.qr-scan-container {
- position: relative;
width: 100%;
- max-width: 500px;
- margin: 0 auto;
- background: #000;
- border-radius: 8px;
+ max-width: 400px;
+ background-color: #000;
+ border-radius: 12px;
overflow: hidden;
+}
+
+.scan-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px;
+ background-color: rgba(0, 0, 0, 0.7);
+}
+
+.scan-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #fff;
}
.qr-camera {
width: 100%;
+ height: 400px;
+}
+
+.scan-frame-wrapper {
+ position: relative;
+ width: 100%;
height: 300px;
}
-.scan-overlay {
+.scan-frame {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
- width: 70%;
- height: 70%;
+ width: 80%;
+ height: 80%;
border: 3px solid #1890ff;
border-radius: 8px;
box-shadow: 0 0 20px rgba(24, 144, 255, 0.3);
animation: pulse 2s infinite;
}
-.scan-frame {
- width: 100%;
- height: 100%;
- border: 2px solid #fff;
- border-radius: 4px;
-}
-
.scan-tip {
position: absolute;
- bottom: -30px;
+ bottom: 10px;
left: 50%;
transform: translateX(-50%);
color: #fff;
font-size: 14px;
text-align: center;
+ background-color: rgba(0, 0, 0, 0.6);
+ padding: 5px 15px;
+ border-radius: 20px;
}
@keyframes pulse {
@@ -540,104 +607,4 @@
100% { opacity: 0.8; }
}
-.status-info {
- margin-top: 16px;
- text-align: center;
-}
-
-.scanning-text {
- display: flex;
- align-items: center;
- justify-content: center;
- color: #1890ff;
- margin-top: 8px;
-}
-
-.scanning-label {
- margin-left: 8px;
- font-size: 14px;
-}
-
-.table-section {
- padding: 0 15px;
-}
-
-.task-list, .qr-list {
- .task-item, .qr-item {
- background-color: #fff;
- border-radius: 8px;
- margin-bottom: 10px;
- padding: 15px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
- }
-}
-
-.task-header, .qr-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- margin-bottom: 10px;
-}
-
-.task-info, .qr-info {
- flex: 1;
-}
-
-.task-name, .device-name {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- margin-bottom: 4px;
-}
-
-.task-location, .device-location {
- font-size: 14px;
- color: #666;
-}
-
-.task-actions, .qr-actions {
- margin-left: 10px;
-}
-
-.task-details, .qr-details {
- .detail-item {
- display: flex;
- margin-bottom: 6px;
-
- .detail-label {
- font-size: 14px;
- color: #666;
- min-width: 60px;
- }
-
- .detail-value {
- font-size: 14px;
- color: #333;
- flex: 1;
- }
- }
-}
-
-.empty-state, .loading-state {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 40px 20px;
- background-color: #fff;
- border-radius: 8px;
- margin: 10px 15px;
-}
-
-.loading-text {
- margin-top: 10px;
- font-size: 14px;
- color: #666;
-}
-
-.pagination-container {
- padding: 20px 15px;
- background-color: #fff;
- margin-top: 10px;
-}
</style>
\ No newline at end of file
diff --git a/src/static/images/icon/guzhangfenxi@2x.png b/src/static/images/icon/guzhangfenxi@2x.png
new file mode 100644
index 0000000..7a0efd5
--- /dev/null
+++ b/src/static/images/icon/guzhangfenxi@2x.png
Binary files differ
diff --git a/src/static/images/icon/jieguoyanzheng@2x.png b/src/static/images/icon/jieguoyanzheng@2x.png
new file mode 100644
index 0000000..e8e72fc
--- /dev/null
+++ b/src/static/images/icon/jieguoyanzheng@2x.png
Binary files differ
diff --git a/src/static/images/icon/xunjianshangchuan@2x.png b/src/static/images/icon/xunjianshangchuan@2x.png
new file mode 100644
index 0000000..84be030
--- /dev/null
+++ b/src/static/images/icon/xunjianshangchuan@2x.png
Binary files differ
diff --git a/src/static/images/icon/zhinengpaidan@2x.png b/src/static/images/icon/zhinengpaidan@2x.png
new file mode 100644
index 0000000..8317c98
--- /dev/null
+++ b/src/static/images/icon/zhinengpaidan@2x.png
Binary files differ
diff --git a/src/static/images/icon/zuoyezhidao@2x.png b/src/static/images/icon/zuoyezhidao@2x.png
new file mode 100644
index 0000000..8180fa3
--- /dev/null
+++ b/src/static/images/icon/zuoyezhidao@2x.png
Binary files differ
--
Gitblit v1.9.3