Merge branch 'refs/heads/yyb'
# Conflicts:
# src/pages/production/twist/receive/monofil.vue
| | |
| | | <script setup lang="ts"> |
| | | import { onLaunch, onShow, onHide } from "@dcloudio/uni-app"; |
| | | import { useThemeStore } from "@/store"; |
| | | import { ref } from "vue"; |
| | | |
| | | // 主é¢åå§å |
| | | const themeStore = useThemeStore(); |
| | | |
| | | // å
¨å±æ«ç å¹¿ææ¥æ¶å¨ |
| | | let main: any = null; |
| | | let receiver: any = null; |
| | | let filter: any = null; |
| | | |
| | | // åå§åæ«ç å¹¿ææ¥æ¶ |
| | | const initGlobalScan = () => { |
| | | // #ifdef APP-PLUS |
| | | try { |
| | | main = plus.android.runtimeMainActivity(); |
| | | const IntentFilter = plus.android.importClass("android.content.IntentFilter"); |
| | | filter = new IntentFilter(); |
| | | filter.addAction("com.dwexample.ACTION"); |
| | | |
| | | receiver = plus.android.implements("io.dcloud.feature.internal.reflect.BroadcastReceiver", { |
| | | onReceive: (context: any, intent: any) => { |
| | | console.log("ð [å
¨å±Scan] onReceive 触å:", context, intent); |
| | | plus.android.importClass(intent); |
| | | const scanResult = intent.getStringExtra("com.motorolasolutions.emdk.datawedge.data_string"); |
| | | console.log("ð [å
¨å±Scan] æ«æç»æ:", scanResult); |
| | | |
| | | // åéå°ææå¯è½çäºä»¶ |
| | | const eventNames = ["scan", "scanIndex", "scanJX", "scanLS"]; |
| | | eventNames.forEach((eventName) => { |
| | | uni.$emit(eventName, { code: scanResult }); |
| | | console.log(`ð [å
¨å±Scan] å·²åé ${eventName} äºä»¶`); |
| | | }); |
| | | }, |
| | | }); |
| | | |
| | | // 注åå¹¿ææ¥æ¶å¨ |
| | | main.registerReceiver(receiver, filter); |
| | | console.log("ð [å
¨å±Scan] å
¨å±æ«ç å¹¿ææ¥æ¶å¨å·²å¯å¨"); |
| | | } catch (error) { |
| | | console.error("ð [å
¨å±Scan] åå§å失败:", error); |
| | | } |
| | | // #endif |
| | | }; |
| | | |
| | | // 忢æ«ç å¹¿ææ¥æ¶ |
| | | const stopGlobalScan = () => { |
| | | // #ifdef APP-PLUS |
| | | try { |
| | | if (main && receiver) { |
| | | main.unregisterReceiver(receiver); |
| | | console.log("ð [å
¨å±Scan] å
¨å±æ«ç å¹¿ææ¥æ¶å¨å·²åæ¢"); |
| | | } |
| | | } catch (error) { |
| | | console.error("ð [å
¨å±Scan] åæ¢å¤±è´¥:", error); |
| | | } |
| | | // #endif |
| | | }; |
| | | |
| | | onLaunch(() => { |
| | | console.log("App Launch"); |
| | | // åå§åä¸»é¢ |
| | | themeStore.initTheme(); |
| | | // åå§åå
¨å±æ«ç å¹¿ææ¥æ¶å¨ |
| | | initGlobalScan(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | console.log("App Show"); |
| | | // åºç¨æ¾ç¤ºæ¶éæ°å¯å¨å¹¿ææ¥æ¶å¨ |
| | | initGlobalScan(); |
| | | }); |
| | | |
| | | onHide(() => { |
| | | console.log("App Hide"); |
| | | // åºç¨éèæ¶ä¸åæ¢å¹¿æï¼ä¿æåå°æ¥æ¶ï¼ |
| | | }); |
| | | </script> |
| | | |
| | |
| | | data: data, |
| | | }); |
| | | }, |
| | | |
| | | // å é¤åä¸é¢ç¨ |
| | | deleteStrandedWireDish(id: number) { |
| | | return request<BaseResult<any>>({ |
| | | url: `/strandedWire/deleteStrandedWireDish/${id}`, |
| | | method: "DELETE", |
| | | }); |
| | | }, |
| | | |
| | | // å é¤ç»çº¿æ¥å·¥ |
| | | deleteWireOutput(params: { id: number }) { |
| | | // å°åæ°æ¼æ¥å° URL ä½ä¸º query åæ° |
| | | const queryString = `?id=${params.id}`; |
| | | return request<BaseResult<any>>({ |
| | | url: `/strandedWire/deleteWireOutput${queryString}`, |
| | | method: "DELETE", |
| | | }); |
| | | }, |
| | | }; |
| | | |
| | | export default TwistApi; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | import { BaseResult } from "@/models/base"; |
| | | |
| | | const RoutingInspectionApi = { |
| | | // æ¥è¯¢å·¡æ£è®°å½ |
| | | getDeviceInspectListByPatrol(params: any) { |
| | | return request<BaseResult<any>>({ |
| | | url: "/wireInspection/getDeviceInspectListByPatrol", |
| | | method: "GET", |
| | | data: params, |
| | | }); |
| | | }, |
| | | // è·åå·¡æ£æ°æ® |
| | | getInspectListByPatrol(data: any) { |
| | | return request<BaseResult<any>>({ |
| | | url: "/wireInspection/getInspectListByPatrol", |
| | | method: "POST", |
| | | data: data, |
| | | }); |
| | | }, |
| | | // è·åæä¸åä¸ªç»æè®°å½ 0 |
| | | getDrawInspectInfoById(params: any) { |
| | | return request<BaseResult<any>>({ |
| | | url: "/wireInspection/getDrawInspectInfoById/" + params.id, |
| | | method: "GET", |
| | | // data: params, |
| | | }); |
| | | }, |
| | | // è·åç»çº¿åä¸ªç»æè®°å½ 1 |
| | | getStrandedInspectionStructureInfoById(params: any) { |
| | | return request<BaseResult<any>>({ |
| | | url: "/wireInspection/getStrandedInspectionStructureInfoById/" + params.id, |
| | | method: "GET", |
| | | // data: params, |
| | | }); |
| | | }, |
| | | // æä¸å·¡æ£ä¿å |
| | | drawPatrolCheckInspection(data: any) { |
| | | return request<BaseResult<any>>({ |
| | | url: "/wireInspection/drawPatrolCheckInspection?deviceUid=" + data.deviceUid, |
| | | method: "POST", |
| | | data: data, |
| | | }); |
| | | }, |
| | | // ç»çº¿å·¡æ£ä¿å |
| | | strandedPatrolCheckInspection(data: any) { |
| | | return request<BaseResult<any>>({ |
| | | url: "/wireInspection/strandedPatrolCheckInspection?deviceUid=" + data.deviceUid, |
| | | method: "POST", |
| | | data: data, |
| | | }); |
| | | }, |
| | | |
| | | // éªè¯äºç»´ç |
| | | assertScanQR(params: { deviceUid: string }) { |
| | | return request<BaseResult<any>>({ |
| | | url: "/wireInspection/assertScanQR?deviceUid=" + params.deviceUid, |
| | | method: "GET", |
| | | }); |
| | | }, |
| | | }; |
| | | |
| | | export default RoutingInspectionApi; |
| | |
| | | </wd-tag> |
| | | </view> |
| | | </template> |
| | | <wd-row class="my-2" v-if="data[map.systemNo]"> |
| | | <wd-col :span="24"> |
| | | <view class="flex"> |
| | | <view class="icon_box"> |
| | | <wd-icon name="folder" color="#0D867F"></wd-icon> |
| | | </view> |
| | | <text class="text-[#646874] mx-2"> |
| | | è´¨é追溯å·: |
| | | <text class="text-[#252525]">{{ data[map.systemNo] }}</text> |
| | | </text> |
| | | </view> |
| | | </wd-col> |
| | | </wd-row> |
| | | <wd-row class="my-2"> |
| | | <wd-col :span="12"> |
| | | <view class="flex"> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { ref, computed, onUnmounted } from "vue"; |
| | | import { onShow, onHide } from "@dcloudio/uni-app"; |
| | | import RoutingInspectionApi from "@/api/routingInspection/routingInspection"; |
| | | |
| | | /** |
| | | * æ«ç æ°æ®æ¥å£ |
| | | */ |
| | | interface ScanCodeData { |
| | | uid?: string; |
| | | deviceModel?: string; |
| | | [key: string]: any; |
| | | } |
| | | |
| | | // å
¨å±çå¬å¨ç¶ææ å°ï¼ç¡®ä¿æ¯ä¸ªäºä»¶ååªæä¸ä¸ªçå¬å¨ï¼ |
| | | const globalListeners = new Map<string, boolean>(); |
| | | |
| | | // æ·»å ä¸ä¸ªå
¨å±çæè·æææ«ç äºä»¶ççå¬å¨ï¼ç¨äºè°è¯ï¼ |
| | | let debugListenerInitialized = false; |
| | | |
| | | const initDebugListener = () => { |
| | | if (debugListenerInitialized) return; |
| | | |
| | | // ç嬿æå¯è½çæ«ç äºä»¶ |
| | | const eventNames = ["scan", "scanIndex", "scanJX", "scanLS"]; |
| | | eventNames.forEach((eventName) => { |
| | | uni.$on(eventName, (data: any) => { |
| | | console.log(`ð [å
¨å±è°è¯] æè·å° ${eventName} äºä»¶:`, data); |
| | | }); |
| | | }); |
| | | |
| | | debugListenerInitialized = true; |
| | | console.log("ð [å
¨å±è°è¯] è°è¯çå¬å¨å·²åå§å"); |
| | | }; |
| | | |
| | | /** |
| | | * æ«ç 管ç Composable |
| | | * ç»ä¸ç®¡çæ«ç äºä»¶çå¬ãç¼ååå¨åæ°æ®è¯»å |
| | | * å
¨å±çå¬å¨ï¼ä¸é页é¢åæ¢èå
³é |
| | | * @param eventName çå¬çäºä»¶åç§°ï¼é»è®¤ä¸º "scan" |
| | | */ |
| | | export function useScanCode(eventName: string = "scan") { |
| | | // å½åæ«ç çè®¾å¤ UID |
| | | const deviceUid = ref<string>(""); |
| | | // å½åæ«ç çæºå°åå· |
| | | const deviceModel = ref<string>(""); |
| | | // 宿´çæ«ç æ°æ® |
| | | const scanData = ref<ScanCodeData>({}); |
| | | // ä¿åäºä»¶åç§° |
| | | const currentEventName = eventName; |
| | | |
| | | /** |
| | | * 仿¬å°ç¼åå è½½æ«ç æ°æ® |
| | | */ |
| | | const loadFromCache = () => { |
| | | try { |
| | | const cachedData = uni.getStorageSync("scanCodeData"); |
| | | if (cachedData) { |
| | | scanData.value = cachedData; |
| | | deviceUid.value = cachedData.uid || ""; |
| | | deviceModel.value = cachedData.deviceModel || ""; |
| | | console.log("[useScanCode] ä»ç¼åå è½½æ«ç æ°æ®:", cachedData); |
| | | return cachedData; |
| | | } |
| | | } catch (error) { |
| | | console.error("[useScanCode] 读åç¼å失败:", error); |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | /** |
| | | * ä¿åæ«ç æ°æ®å°ç¼å |
| | | */ |
| | | const saveToCache = (data: ScanCodeData) => { |
| | | try { |
| | | uni.setStorageSync("scanCodeData", data); |
| | | console.log("[useScanCode] å·²ä¿åå°ç¼å:", data); |
| | | return true; |
| | | } catch (error) { |
| | | console.error("[useScanCode] ä¿åç¼å失败:", error); |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * æ¸
空æ«ç æ°æ®åç¼å |
| | | */ |
| | | const clearScanData = () => { |
| | | try { |
| | | uni.removeStorageSync("scanCodeData"); |
| | | deviceUid.value = ""; |
| | | deviceModel.value = ""; |
| | | scanData.value = {}; |
| | | console.log("[useScanCode] å·²æ¸
空æ«ç æ°æ®"); |
| | | } catch (error) { |
| | | console.error("[useScanCode] æ¸
空ç¼å失败:", error); |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * éªè¯äºç»´ç ï¼ä»
è°ç¨æ¥å£ï¼ä¸å¤çè¿åç»æï¼ |
| | | */ |
| | | const validateQRCode = async (uid: string): Promise<void> => { |
| | | try { |
| | | console.log("[useScanCode] è°ç¨éªè¯äºç»´ç æ¥å£, deviceUid:", uid); |
| | | await RoutingInspectionApi.assertScanQR({ deviceUid: uid }); |
| | | console.log("[useScanCode] éªè¯æ¥å£è°ç¨å®æ"); |
| | | } catch (error: any) { |
| | | console.error("[useScanCode] éªè¯æ¥å£è°ç¨å¼å¸¸:", error); |
| | | // å³ä½¿å¼å¸¸ä¹ä¸å½±ååç»æµç¨ |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * å¤çæ«ç äºä»¶ |
| | | */ |
| | | const handleScanEvent = async (params: any) => { |
| | | console.log(`========== [useScanCode][${currentEventName}] æ¶å°æ«ç äºä»¶ ==========`); |
| | | console.log(`[useScanCode][${currentEventName}] æ¥æ¶åæ°:`, params); |
| | | console.log(`[useScanCode][${currentEventName}] è§¦åæ¶é´:`, new Date().toLocaleTimeString()); |
| | | |
| | | try { |
| | | if (!params?.code) { |
| | | console.warn("[useScanCode] æ«ç å
容为空"); |
| | | return; |
| | | } |
| | | |
| | | let codeObj: ScanCodeData = {}; |
| | | try { |
| | | codeObj = JSON.parse(params.code); |
| | | console.log("[useScanCode] è§£æåç对象:", codeObj); |
| | | } catch (err) { |
| | | console.error("[useScanCode] JSON è§£æå¤±è´¥:", err); |
| | | console.log("[useScanCode] åå§å符串:", params.code); |
| | | // 妿䏿¯ JSONï¼å°è¯ä½ä¸ºæ®éå符串å¤ç |
| | | codeObj = { code: params.code }; |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦æå¿
è¦çåæ®µï¼uid æ deviceModelï¼ |
| | | if (!codeObj.uid && !codeObj.deviceModel) { |
| | | console.warn("[useScanCode] æ«ç æ°æ®ç¼ºå° uid å deviceModelï¼ä¸ä¿åç¼å"); |
| | | uni.showToast({ |
| | | title: "æ«ç æ°æ®æ æ", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // æ´æ°æ¬å°ç¶æ |
| | | scanData.value = codeObj; |
| | | deviceUid.value = codeObj.uid || ""; |
| | | deviceModel.value = codeObj.deviceModel || ""; |
| | | |
| | | // ä¿åå°ç¼å |
| | | saveToCache(codeObj); |
| | | |
| | | // 妿æ uidï¼è°ç¨éªè¯æ¥å£ï¼ä¸çå¾
ç»æï¼ |
| | | if (codeObj.uid) { |
| | | validateQRCode(codeObj.uid); |
| | | } |
| | | |
| | | // æ¾ç¤ºæåæç¤º |
| | | uni.showToast({ |
| | | title: "æ«ç æå", |
| | | icon: "success", |
| | | }); |
| | | |
| | | console.log("[useScanCode] æ«ç æ°æ®å·²æ´æ°:", { |
| | | uid: deviceUid.value, |
| | | deviceModel: deviceModel.value, |
| | | }); |
| | | } catch (error) { |
| | | console.error("[useScanCode] å¤çæ«ç æ°æ®å¼å¸¸:", error); |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * å¯ç¨æ«ç çå¬ï¼å
¨å±ï¼æ¯æ¬¡é½éæ°æ³¨åä»¥ç¡®ä¿ææï¼ |
| | | */ |
| | | const enableListener = () => { |
| | | // å
ç§»é¤å¯è½åå¨çæ§çå¬å¨ |
| | | uni.$off(currentEventName, handleScanEvent); |
| | | // æ·»å æ°ççå¬å¨ |
| | | uni.$on(currentEventName, handleScanEvent); |
| | | // æ 记为å
¨å±å·²å¯ç¨ |
| | | globalListeners.set(currentEventName, true); |
| | | console.log( |
| | | `[useScanCode][${currentEventName}] â
å
¨å±çå¬å¨å·²å¯ç¨/å·æ°ï¼ä¸ä¼é页é¢åæ¢å
³éï¼` |
| | | ); |
| | | }; |
| | | |
| | | /** |
| | | * ç¦ç¨æ«ç çå¬ï¼ä»
ç¨äºåºç¨éåºæ¶æ¸
çï¼æ£å¸¸é¡µé¢åæ¢ä¸è°ç¨ï¼ |
| | | */ |
| | | const disableListener = () => { |
| | | if (!globalListeners.get(currentEventName)) { |
| | | console.log(`[useScanCode][${currentEventName}] çå¬å¨æªå¯ç¨ï¼è·³è¿`); |
| | | return; |
| | | } |
| | | |
| | | uni.$off(currentEventName, handleScanEvent); |
| | | globalListeners.delete(currentEventName); |
| | | console.log(`[useScanCode][${currentEventName}] â å
¨å±çå¬å¨å·²ç¦ç¨`); |
| | | }; |
| | | |
| | | /** |
| | | * 设置页é¢çå½å¨æé©åï¼å
¨å±çå¬å¨æ¨¡å¼ï¼ |
| | | * çå¬å¨å
¨å±å¯ç¨ä¸æ¬¡ï¼ä¸é页é¢åæ¢å
³é |
| | | * @param options é
ç½®é项 |
| | | */ |
| | | const setupLifecycle = ( |
| | | options: { |
| | | loadCacheOnShow?: boolean; // æ¯å¦å¨ onShow æ¶å è½½ç¼å |
| | | } = {} |
| | | ) => { |
| | | const { loadCacheOnShow = true } = options; |
| | | |
| | | // åªå¨ onShow æ¶å è½½ç¼åï¼ä¸ç¦ç¨çå¬å¨ |
| | | if (loadCacheOnShow) { |
| | | onShow(() => { |
| | | console.log(`[useScanCode][${currentEventName}] onShow 触å`); |
| | | loadFromCache(); |
| | | }); |
| | | } |
| | | |
| | | // 注æï¼ä¸åå¨ onHide å onUnmounted ä¸ç¦ç¨çå¬å¨ |
| | | // çå¬å¨ä¿æå
¨å±æ¿æ´»ç¶æ |
| | | }; |
| | | |
| | | // 页é¢å è½½æ¶èªå¨ä»ç¼å读å |
| | | loadFromCache(); |
| | | |
| | | return { |
| | | // ç¶æ |
| | | deviceUid, |
| | | deviceModel, |
| | | scanData, |
| | | // 计ç®å±æ§ |
| | | hasScanned: computed(() => !!deviceUid.value), |
| | | displayText: computed(() => deviceModel.value || "æªæ«ç "), |
| | | // æ¹æ³ |
| | | loadFromCache, |
| | | saveToCache, |
| | | clearScanData, |
| | | validateQRCode, |
| | | enableListener, |
| | | disableListener, |
| | | setupLifecycle, |
| | | }; |
| | | } |
| | |
| | | "name" : "线ç¼ä¸æ¥", |
| | | "appid" : "__UNI__F64E0A4", |
| | | "description" : "", |
| | | "versionName" : "1.0.12", |
| | | "versionName" : "1.0.15", |
| | | "versionCode" : "100", |
| | | "transformPx" : false, |
| | | /* 5+Appç¹æç¸å
³ */ |
| | |
| | | "^cu-(.*)": "@/components/cu-$1/index.vue" |
| | | } |
| | | }, |
| | | |
| | | "pages": [ |
| | | { |
| | | "path": "pages/index/index", |
| | |
| | | "navigationBarTitleText": "ä¸ªäººèµæ" |
| | | } |
| | | }, |
| | | |
| | | { |
| | | "path": "pages/work/user/index", |
| | | "style": { |
| | |
| | | "style": { |
| | | "navigationBarTitleText": "æ¶ææ¥å·¥" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/routingInspection/index", |
| | | "style": { |
| | | "navigationBarTitleText": "å·¡æ£" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/routingInspection/detail/indexJX", |
| | | "style": { |
| | | "navigationBarTitleText": "ç»çº¿å·¡æ£è¯¦æ
" |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/routingInspection/detail/indexLS", |
| | | "style": { |
| | | "navigationBarTitleText": "æä¸å·¡æ£è¯¦æ
" |
| | | } |
| | | } |
| | | ], |
| | | "globalStyle": { |
| | |
| | | } |
| | | ] |
| | | } |
| | | } |
| | | } |
| | |
| | | </wd-notice-bar> |
| | | <!-- å¿«æ·å¯¼èª --> |
| | | <wd-grid clickable :column="1" class="mt-2"> |
| | | <view v-for="(item, index) in navList"> |
| | | <wd-grid-item v-if="item.show" :key="index" use-slot link-type="navigateTo" :url="item.url"> |
| | | <view v-for="(item, index) in navList" :key="index"> |
| | | <wd-grid-item v-if="item.show" use-slot link-type="navigateTo" :url="item.url"> |
| | | <view class="p-2"> |
| | | <image class="w-72rpx h-72rpx rounded-8rpx" :src="item.icon" /> |
| | | </view> |
| | |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { reactive } from "vue"; |
| | | import { reactive, computed } from "vue"; |
| | | import { dayjs, useMessage, useToast } from "wot-design-uni"; |
| | | import LogAPI, { VisitStatsVO } from "@/api/system/log"; |
| | | import WorkerCallingCard from "@/components/worker-calling-card/index.vue"; |
| | | import HomeApi from "@/api/home"; |
| | | import { useUserStore } from "@/store/modules/user"; |
| | | |
| | | const visitStatsData = ref<VisitStatsVO>({ |
| | | todayUvCount: 0, |
| | |
| | | |
| | | const message = useMessage(); |
| | | const toast = useToast(); |
| | | |
| | | // è·åå½åç»å½ç¨æ·ä¿¡æ¯ |
| | | const userStore = useUserStore(); |
| | | const userInfo: any = computed(() => userStore.userInfo); |
| | | |
| | | // 夿æ¯å¦ä¸ºå·¡æ£åè§è² |
| | | const isInspector = computed(() => { |
| | | if (!userInfo.value || !userInfo.value.roles || !Array.isArray(userInfo.value.roles)) { |
| | | return false; |
| | | } |
| | | console.log( |
| | | "userInfo.value.roles", |
| | | userInfo.value.roles.some((role: any) => role.roleKey === "qualitative-inspector") |
| | | ); |
| | | return userInfo.value.roles.some((role: any) => role.roleKey === "qualitative-inspector"); |
| | | }); |
| | | |
| | | const fileProgress = reactive({ |
| | | show: false, |
| | |
| | | url: "/pages/timely/index", |
| | | show: false, |
| | | }, |
| | | { |
| | | icon: "/static/icons/routingInspection.png", |
| | | title: "å·¡æ£", |
| | | url: "/pages/routingInspection/index", |
| | | show: false, |
| | | }, |
| | | ]); |
| | | |
| | | // å 载访é®ç»è®¡æ°æ® |
| | |
| | | const init = async () => { |
| | | checkVersion(); |
| | | const { data } = await HomeApi.getIndex(); |
| | | |
| | | // 夿æ¯å¦ä¸ºå·¡æ£åè§è² |
| | | if (data.deviceGroupName == "æ¶æç»") { |
| | | navList[1].show = true; |
| | | } else { |
| | | navList[0].show = true; |
| | | } |
| | | if (isInspector.value) { |
| | | // 妿æ¯å·¡æ£åï¼æ¾ç¤ºå·¡æ£èå |
| | | navList[2].show = true; |
| | | } |
| | | }; |
| | | |
| | | /** |
| | |
| | | <template> |
| | | <wd-row> |
| | | <wd-col v-for="(item, index) in data" :key="index" :span="item.span ?? 12" class="my-1"> |
| | | <wd-col v-for="(item, index) in data" :key="index" :span="24" class="my-1"> |
| | | <view class="flex w-full h-[20px]"> |
| | | <view class="text-[#646874] pl-1 mr-3">{{ item.label }}</view> |
| | | <view class="font-medium pr-1" :style="{ color: item.color ?? color }"> |
| | |
| | | <template> |
| | | <view class="statistics_box"> |
| | | <wd-row> |
| | | <!-- <wd-row> |
| | | <wd-col :span="12"> |
| | | <view class="h_48 px-4 flex items-center"> |
| | | <view class="icon_box"> |
| | |
| | | <text class="text-lg text-[#339599] ml-2 font-semibold">87%</text> |
| | | </view> |
| | | </wd-col> |
| | | </wd-row> |
| | | </wd-row> --> |
| | | </view> |
| | | </template> |
| | | <style scoped lang="scss"> |
| | |
| | | :map="{ |
| | | deviceModel: 'deviceModel', |
| | | model: 'model', |
| | | systemNo: 'systemNo', |
| | | totalAmount: 'totalAmount', |
| | | amount: 'amount', |
| | | unAmount: 'unAmount', |
| | |
| | | const cardData = reactive({ |
| | | deviceModel: undefined, |
| | | model: undefined, |
| | | systemNo: undefined, |
| | | totalAmount: undefined, |
| | | amount: undefined, |
| | | unAmount: undefined, |
| | |
| | | }); |
| | | cardData.deviceModel = data.deviceModel; |
| | | cardData.model = data.model; |
| | | cardData.systemNo = data.systemNo; |
| | | cardData.totalAmount = data.totalLength; |
| | | cardData.amount = data.length; |
| | | cardData.unAmount = data.unLength; |
| | |
| | | :map="{ |
| | | deviceModel: 'deviceModel', |
| | | model: 'model', |
| | | systemNo: 'systemNo', |
| | | totalAmount: 'totalAmount', |
| | | amount: 'amount', |
| | | unAmount: 'unAmount', |
| | |
| | | const cardData = reactive({ |
| | | deviceModel: undefined, |
| | | model: undefined, |
| | | systemNo: undefined, |
| | | totalAmount: undefined, |
| | | amount: undefined, |
| | | unAmount: undefined, |
| | |
| | | }); |
| | | cardData.deviceModel = data.deviceModel; |
| | | cardData.model = data.model; |
| | | cardData.systemNo = data.systemNo; |
| | | cardData.totalAmount = data.totalAmount; |
| | | cardData.amount = data.amount; |
| | | cardData.unAmount = data.unAmount; |
| | |
| | | <view> |
| | | <wd-row> |
| | | <wd-col :span="21"> |
| | | <wd-search placeholder-left hide-cancel></wd-search> |
| | | <wd-search |
| | | v-model="searchKeyword" |
| | | placeholder="请è¾å
¥è§æ ¼åå·" |
| | | placeholder-left |
| | | hide-cancel |
| | | @search="handleSearch" |
| | | @clear="handleClear" |
| | | ></wd-search> |
| | | </wd-col> |
| | | <wd-col :span="3"> |
| | | <view class="scan_box" @click="openScan"> |
| | |
| | | <wd-tab :title="`å¾
ç产(${total.wait})`" class="tab_bg"> |
| | | <ProductList |
| | | ref="waitRef" |
| | | :key="`wait-${searchKey}`" |
| | | :api="ManageApi.getProductList" |
| | | state="å¾
宿" |
| | | :model="searchKeyword" |
| | | @ok="changeWait" |
| | | /> |
| | | </wd-tab> |
| | | <wd-tab :title="`é¨å宿(${total.partial})`" class="tab_bg"> |
| | | <ProductList |
| | | ref="partialRef" |
| | | :key="`partial-${searchKey}`" |
| | | :api="ManageApi.getProductList" |
| | | state="é¨å宿" |
| | | :model="searchKeyword" |
| | | @ok="changePartial" |
| | | /> |
| | | </wd-tab> |
| | | <wd-tab :title="`å·²ç产(${total.already})`" class="tab_bg"> |
| | | <ProductList |
| | | ref="alreadyRef" |
| | | :key="`already-${searchKey}`" |
| | | :api="ManageApi.getProductList" |
| | | state="已宿" |
| | | :model="searchKeyword" |
| | | @ok="changeAlready" |
| | | /> |
| | | </wd-tab> |
| | |
| | | const toast = useToast(); |
| | | |
| | | const waitRef = ref(); |
| | | const partialRef = ref(); |
| | | const alreadyRef = ref(); |
| | | const tab = ref<number>(0); |
| | | const searchKeyword = ref(""); |
| | | const searchKey = ref(0); |
| | | const total = reactive({ |
| | | wait: 0, |
| | | partial: 0, |
| | | already: 0, |
| | | }); |
| | | |
| | |
| | | total.wait = num; |
| | | }; |
| | | |
| | | const changePartial = (num: number) => { |
| | | total.partial = num; |
| | | }; |
| | | |
| | | const changeAlready = (num: number) => { |
| | | total.already = num; |
| | | }; |
| | | |
| | | const handleSearch = () => { |
| | | searchKey.value++; |
| | | }; |
| | | |
| | | const handleClear = () => { |
| | | searchKeyword.value = ""; |
| | | handleSearch(); |
| | | }; |
| | | |
| | | const openScan = () => { |
| | | scanRef.value.triggerScan(); |
| | | }; |
| | |
| | | const map = reactive({ |
| | | deviceModel: "deviceModel", |
| | | model: "model", |
| | | systemNo: "systemNo", |
| | | totalAmount: "totalAmount", |
| | | amount: "amount", |
| | | unAmount: "unAmount", |
| | |
| | | type: String, |
| | | default: "", |
| | | }, |
| | | model: { |
| | | type: String, |
| | | default: "", |
| | | }, |
| | | }); |
| | | |
| | | const emits = defineEmits(["ok"]); |
| | | const list = ref<any[]>([]); |
| | | |
| | | const toDetail = (id: number, type: string) => { |
| | | toast.show("ç¹å»å¡ç"); |
| | | // toast.show("ç¹å»å¡ç"); |
| | | if (type == "æä¸") { |
| | | uni.navigateTo({ |
| | | url: `/pages/production/detail/wireDetail?id=${id}`, |
| | |
| | | }; |
| | | |
| | | const getList = async (pageNo: number, pageSize: number) => { |
| | | const { code, data } = await props.api({ |
| | | const params: any = { |
| | | userName: userInfo.value.userName, |
| | | state: props.state, |
| | | current: pageNo, |
| | | size: pageSize, |
| | | }); |
| | | }; |
| | | if (props.model) { |
| | | params.model = props.model; |
| | | } |
| | | const { code, data } = await props.api(params); |
| | | if (code == 200) { |
| | | if (data.type == "ç»çº¿") { |
| | | map.deviceModel = "deviceModel"; |
| | | map.model = "model"; |
| | | map.systemNo = "systemNo"; |
| | | map.totalAmount = "totalLength"; |
| | | map.amount = "length"; |
| | | map.unAmount = "unLength"; |
| | | } else if (data.type == "æä¸") { |
| | | map.deviceModel = "deviceModel"; |
| | | map.model = "model"; |
| | | map.systemNo = "systemNo"; |
| | | map.totalAmount = "totalAmount"; |
| | | map.amount = "amount"; |
| | | map.unAmount = "unAmount"; |
| | |
| | | <view class="attachment-list"> |
| | | <wd-status-tip v-if="attachmentList.length === 0" image="content" tip="ææ éä»¶" /> |
| | | |
| | | <wd-card |
| | | v-for="item in attachmentList" |
| | | :key="item.id" |
| | | type="rectangle" |
| | | custom-class="attachment-card" |
| | | :border="false" |
| | | > |
| | | <view class="attachment-item" @click="previewAttachment(item)"> |
| | | <view class="attachment-info"> |
| | | <view class="attachment-name">{{ item.bucketFileName || item.name }}</view> |
| | | <view class="attachment-meta"> |
| | | <text class="file-type">{{ getFileType(item.bucketFileName) }}</text> |
| | | <text class="upload-time">{{ formatTime(item.createTime) }}</text> |
| | | <view v-for="item in attachmentList" :key="item.id" class="attachment-card"> |
| | | <view class="media-wrapper" @click="previewAttachment(item)"> |
| | | <!-- å¾çé¢è§ --> |
| | | <template v-if="isImageType(item.url)"> |
| | | <image |
| | | v-if="!item.loadError" |
| | | :src="getFullUrl(item.url)" |
| | | mode="aspectFill" |
| | | class="media-preview" |
| | | @error="onImageError(item)" |
| | | @load="onImageLoad(item)" |
| | | /> |
| | | <!-- å¾çå 载失败æ¾ç¤ºé»è®¤å¾æ --> |
| | | <view v-else class="file-icon-wrapper"> |
| | | <wd-icon name="picture" size="48px" color="#ccc" /> |
| | | <text class="file-name error-text">å 载失败</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <!-- è§é¢é¢è§ --> |
| | | <template v-else-if="isVideoType(item.url)"> |
| | | <video |
| | | v-if="!item.loadError" |
| | | :src="getFullUrl(item.url)" |
| | | class="media-preview" |
| | | :controls="false" |
| | | :show-center-play-btn="false" |
| | | @error="onVideoError(item)" |
| | | /> |
| | | <!-- è§é¢å 载失败æ¾ç¤ºé»è®¤å¾æ --> |
| | | <view v-else class="file-icon-wrapper"> |
| | | <wd-icon name="video" size="48px" color="#ccc" /> |
| | | <text class="file-name error-text">å 载失败</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <!-- å
¶ä»æä»¶ç±»åæ¾ç¤ºå¾æ --> |
| | | <view v-else class="file-icon-wrapper"> |
| | | <wd-icon name="file-outline" size="48px" color="#999" /> |
| | | <text class="file-name">æä»¶</text> |
| | | </view> |
| | | <view class="attachment-actions" @click.stop> |
| | | <wd-icon name="delete" color="#ff4757" @click="deleteAttachment(item.id)" /> |
| | | |
| | | <!-- å é¤æé® --> |
| | | <view class="delete-btn" @click.stop="deleteAttachment(item.id)"> |
| | | <wd-icon name="delete" color="#fff" size="20px" /> |
| | | </view> |
| | | </view> |
| | | </wd-card> |
| | | </view> |
| | | </view> |
| | | |
| | | <wd-toast /> |
| | |
| | | import { useToast } from "wot-design-uni"; |
| | | import AttachmentAPI from "@/api/product/attachment"; |
| | | |
| | | // H5 ä½¿ç¨ VITE_APP_BASE_API ä½ä¸ºä»£çè·¯å¾ï¼å
¶ä»å¹³å°ä½¿ç¨ VITE_APP_API_URL ä½ä¸ºè¯·æ±è·¯å¾ |
| | | let baseUrl = import.meta.env.VITE_APP_API_URL; |
| | | // #ifdef H5 |
| | | baseUrl = import.meta.env.VITE_APP_BASE_API; |
| | | // #endif |
| | | |
| | | const toast = useToast(); |
| | | |
| | | // 页é¢åæ° |
| | |
| | | const attachmentList = ref<any[]>([]); |
| | | |
| | | const detailData = ref<any>({}); |
| | | |
| | | // è·å宿´çå¾ç/è§é¢ URL |
| | | const getFullUrl = (url: string) => { |
| | | if (!url) return ""; |
| | | // å¦æå·²ç»æ¯å®æ´ç URLï¼http æ https å¼å¤´ï¼ï¼ç´æ¥è¿å |
| | | if (url.startsWith("http://") || url.startsWith("https://")) { |
| | | return url; |
| | | } |
| | | // 妿æ¯ç¸å¯¹è·¯å¾ï¼æ¼æ¥åºç¡ URL |
| | | return `${baseUrl}${url.startsWith("/") ? "" : "/"}${url}`; |
| | | }; |
| | | |
| | | // ä» URL ææä»¶å䏿忩å±å |
| | | const getExtension = (urlOrFileName: string) => { |
| | | if (!urlOrFileName) return ""; |
| | | // ç§»é¤æ¥è¯¢åæ°ååå¸ |
| | | const cleanUrl = urlOrFileName.split("?")[0].split("#")[0]; |
| | | // è·åæåä¸ä¸ªç¹åé¢çå
容 |
| | | const extension = cleanUrl.split(".").pop()?.toLowerCase(); |
| | | return extension || ""; |
| | | }; |
| | | |
| | | // 夿æ¯å¦ä¸ºå¾çç±»å |
| | | const isImageType = (urlOrFileName: string) => { |
| | | const extension = getExtension(urlOrFileName); |
| | | return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(extension); |
| | | }; |
| | | |
| | | // 夿æ¯å¦ä¸ºè§é¢ç±»å |
| | | const isVideoType = (urlOrFileName: string) => { |
| | | const extension = getExtension(urlOrFileName); |
| | | return ["mp4", "mov", "avi", "wmv", "flv", "mkv", "webm"].includes(extension); |
| | | }; |
| | | |
| | | // å¾çå è½½æå |
| | | const onImageLoad = (item: any) => { |
| | | item.loadError = false; |
| | | }; |
| | | |
| | | // å¾çå 载失败 |
| | | const onImageError = (item: any) => { |
| | | console.error("å¾çå 载失败:", item.url); |
| | | item.loadError = true; |
| | | }; |
| | | |
| | | // è§é¢å 载失败 |
| | | const onVideoError = (item: any) => { |
| | | console.error("è§é¢å 载失败:", item.url); |
| | | item.loadError = true; |
| | | }; |
| | | |
| | | // è·åéä»¶å表 |
| | | const getAttachmentList = async (data: any) => { |
| | | try { |
| | |
| | | // é¢è§éä»¶ |
| | | const previewAttachment = (item: any) => { |
| | | // æ ¹æ®æä»¶ç±»åè¿è¡é¢è§ |
| | | const fileName = item.bucketFileName || item.name; |
| | | const fileType = getFileType(fileName); |
| | | const fileType = getFileType(item.url); |
| | | const fullUrl = getFullUrl(item.url); |
| | | |
| | | if (fileType.startsWith("image")) { |
| | | // å¾çé¢è§ |
| | | uni.previewImage({ |
| | | urls: [item.url], |
| | | current: item.url, |
| | | urls: [fullUrl], |
| | | current: fullUrl, |
| | | }); |
| | | } else { |
| | | // å
¶ä»æä»¶ç±»åï¼å¯ä»¥ä¸è½½ææå¼ |
| | | uni.downloadFile({ |
| | | url: item.url, |
| | | url: fullUrl, |
| | | success: (res) => { |
| | | uni.openDocument({ |
| | | filePath: res.tempFilePath, |
| | | success: () => { |
| | | console.log("æå¼ææ¡£æå"); |
| | | // æå¼ææ¡£æå |
| | | }, |
| | | fail: (error) => { |
| | | console.error("æå¼ææ¡£å¤±è´¥:", error); |
| | |
| | | }; |
| | | |
| | | // è·åæä»¶ç±»å |
| | | const getFileType = (fileName: string) => { |
| | | if (!fileName) return "unknown"; |
| | | const extension = fileName.split(".").pop()?.toLowerCase(); |
| | | const getFileType = (urlOrFileName: string) => { |
| | | if (!urlOrFileName) return "unknown"; |
| | | const extension = getExtension(urlOrFileName); |
| | | switch (extension) { |
| | | case "jpg": |
| | | case "jpeg": |
| | |
| | | case "bmp": |
| | | case "webp": |
| | | return "image"; |
| | | case "mp4": |
| | | case "mov": |
| | | case "avi": |
| | | case "wmv": |
| | | case "flv": |
| | | case "mkv": |
| | | case "webm": |
| | | return "video"; |
| | | case "pdf": |
| | | return "pdf"; |
| | | case "doc": |
| | |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: grid; |
| | | grid-template-columns: repeat(3, 1fr); |
| | | gap: 8px; |
| | | |
| | | .attachment-card { |
| | | margin-bottom: 12px; |
| | | border-radius: 4px; |
| | | width: 100%; |
| | | aspect-ratio: 1; |
| | | } |
| | | } |
| | | |
| | | .attachment-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 12px; |
| | | .media-wrapper { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 100%; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | background: #f5f5f5; |
| | | |
| | | .attachment-info { |
| | | flex: 1; |
| | | .media-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-bottom: 4px; |
| | | word-break: break-all; |
| | | } |
| | | .file-icon-wrapper { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 8px; |
| | | text-align: center; |
| | | |
| | | .attachment-meta { |
| | | display: flex; |
| | | gap: 12px; |
| | | .file-name { |
| | | margin-top: 8px; |
| | | font-size: 12px; |
| | | color: #999; |
| | | color: #666; |
| | | word-break: break-all; |
| | | display: -webkit-box; |
| | | -webkit-line-clamp: 2; |
| | | line-clamp: 2; |
| | | -webkit-box-orient: vertical; |
| | | overflow: hidden; |
| | | |
| | | &.error-text { |
| | | color: #ff4757; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .attachment-actions { |
| | | margin-left: 12px; |
| | | |
| | | :deep(.wd-icon) { |
| | | font-size: 20px; |
| | | cursor: pointer; |
| | | } |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: 4px; |
| | | right: 4px; |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 50%; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 10; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <wd-card> |
| | | <wd-cell-group :border="true"> |
| | | <wd-cell title="åä¸ç¼å·" :value="data.monofilamentNumber" /> |
| | | <wd-cell title="ç论é¿åº¦" :value="data.amount + ' (m)'" /> |
| | | <wd-cell title="ç产é¿åº¦" :value="data.actuallyLength + ' (m)'" /> |
| | | <wd-cell title="éé" :value="data.actuallyWeight + ' (kg)'" /> |
| | | </wd-cell-group> |
| | | </wd-card> |
| | | <view class="swipe-container"> |
| | | <view |
| | | class="swipe-content" |
| | | :style="{ transform: `translateX(${translateX}px)` }" |
| | | @touchstart="handleTouchStart" |
| | | @touchmove="handleTouchMove" |
| | | @touchend="handleTouchEnd" |
| | | > |
| | | <wd-card> |
| | | <wd-cell-group :border="true"> |
| | | <wd-cell title="åä¸ç¼å·" :value="data.monofilamentNumber" /> |
| | | <wd-cell title="ç论é¿åº¦" :value="data.amount + ' (m)'" /> |
| | | <wd-cell title="ç产é¿åº¦" :value="data.actuallyLength + ' (m)'" /> |
| | | <wd-cell title="éé" :value="data.actuallyWeight + ' (kg)'" /> |
| | | </wd-cell-group> |
| | | </wd-card> |
| | | </view> |
| | | <view class="swipe-delete" @click="handleDelete"> |
| | | <text class="delete-text">å é¤</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | defineProps({ |
| | | import { ref } from "vue"; |
| | | |
| | | const props = defineProps({ |
| | | data: { |
| | | type: Object, |
| | | default: () => {}, |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["delete", "swipe-open"]); |
| | | |
| | | const translateX = ref(0); |
| | | const startX = ref(0); |
| | | const startY = ref(0); |
| | | const currentX = ref(0); |
| | | const isSwipeOpen = ref(false); |
| | | const deleteWidth = 80; // å 餿é®å®½åº¦ |
| | | const isHorizontalSwipe = ref(false); |
| | | |
| | | const handleTouchStart = (e: any) => { |
| | | startX.value = e.touches[0].clientX; |
| | | startY.value = e.touches[0].clientY; |
| | | currentX.value = translateX.value; |
| | | isHorizontalSwipe.value = false; |
| | | }; |
| | | |
| | | const handleTouchMove = (e: any) => { |
| | | const moveX = e.touches[0].clientX - startX.value; |
| | | const moveY = e.touches[0].clientY - startY.value; |
| | | |
| | | // 夿æ¯å¦ä¸ºæ°´å¹³æ»å¨ï¼æ°´å¹³ç§»å¨è·ç¦»å¤§äºåç´ç§»å¨è·ç¦»ï¼ |
| | | if (!isHorizontalSwipe.value && Math.abs(moveX) > Math.abs(moveY) && Math.abs(moveX) > 10) { |
| | | isHorizontalSwipe.value = true; |
| | | } |
| | | |
| | | // åªææ°´å¹³æ»å¨æ¶æå¤çå 餿»å¨ |
| | | if (isHorizontalSwipe.value) { |
| | | e.stopPropagation(); |
| | | const newTranslateX = currentX.value + moveX; |
| | | |
| | | // éå¶æ»å¨èå´ï¼åªè½åå·¦æ»å¨ï¼æå¤§æ»å¨è·ç¦»ä¸ºå 餿é®å®½åº¦ |
| | | if (newTranslateX <= 0 && newTranslateX >= -deleteWidth) { |
| | | translateX.value = newTranslateX; |
| | | } else if (newTranslateX < -deleteWidth) { |
| | | translateX.value = -deleteWidth; |
| | | } else if (newTranslateX > 0) { |
| | | translateX.value = 0; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleTouchEnd = (e: any) => { |
| | | // åªææ°´å¹³æ»å¨æ¶æå¤çç»æé»è¾ |
| | | if (isHorizontalSwipe.value) { |
| | | e.stopPropagation(); |
| | | // 夿æ¯å¦åºè¯¥æå¼æå
³éå é¤æé® |
| | | if (translateX.value < -deleteWidth / 2) { |
| | | // æ»å¨è¶
è¿ä¸åï¼æå¼å é¤æé® |
| | | translateX.value = -deleteWidth; |
| | | isSwipeOpen.value = true; |
| | | emit("swipe-open", props.data); |
| | | } else { |
| | | // æ»å¨ä¸è¶³ä¸åï¼å
³éå é¤æé® |
| | | translateX.value = 0; |
| | | isSwipeOpen.value = false; |
| | | } |
| | | } |
| | | isHorizontalSwipe.value = false; |
| | | }; |
| | | |
| | | const handleDelete = () => { |
| | | // å
å
³éæ»å¨ |
| | | translateX.value = 0; |
| | | isSwipeOpen.value = false; |
| | | emit("delete", props.data); |
| | | }; |
| | | |
| | | // å
³éæ»å¨çæ¹æ³ï¼ä¾å¤é¨è°ç¨ |
| | | const closeSwipe = () => { |
| | | if (isSwipeOpen.value) { |
| | | translateX.value = 0; |
| | | isSwipeOpen.value = false; |
| | | } |
| | | }; |
| | | |
| | | // æ´é²æ¹æ³ä¾ç¶ç»ä»¶è°ç¨ |
| | | defineExpose({ |
| | | closeSwipe, |
| | | isSwipeOpen, |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped></style> |
| | | <style lang="scss" scoped> |
| | | .swipe-container { |
| | | position: relative; |
| | | overflow: hidden; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .swipe-content { |
| | | position: relative; |
| | | transition: transform 0.3s ease; |
| | | z-index: 2; |
| | | background: #fff; |
| | | touch-action: pan-y; |
| | | } |
| | | |
| | | .swipe-delete { |
| | | position: absolute; |
| | | right: 0; |
| | | top: 12px; |
| | | bottom: 12px; |
| | | width: 80px; |
| | | background: #ff4444; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 1; |
| | | border-radius: 4px; |
| | | box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05); |
| | | |
| | | .delete-text { |
| | | color: #fff; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <wd-row> |
| | | <wd-col v-for="(item, index) in data" :key="index" :span="item.span || 24" class="my-1"> |
| | | <view class="flex w-full h-[20px]"> |
| | | <view class="text-[#646874] pl-1 mr-3">{{ item.label }}</view> |
| | | <view class="font-medium pr-1" :style="{ color: item.color ?? color }"> |
| | | {{ value[item.prop] }} {{ value[item.unitProp] }} {{ item.unit }} |
| | | </view> |
| | | </view> |
| | | </wd-col> |
| | | </wd-row> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | defineProps({ |
| | | data: { |
| | | type: Array as any, |
| | | default: () => { |
| | | return []; |
| | | }, |
| | | }, |
| | | value: { |
| | | type: Object, |
| | | default: () => { |
| | | return {}; |
| | | }, |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: "#333333", |
| | | }, |
| | | unit: { |
| | | type: String, |
| | | default: "", |
| | | }, |
| | | }); |
| | | </script> |
| | |
| | | <template> |
| | | <wd-tabs v-model="tab" auto-line-width> |
| | | <wd-tab title="åä¸é¢ç¨" name="åä¸é¢ç¨"> |
| | | <Monofil /> |
| | | <Monofil v-if="tab === 'åä¸é¢ç¨'" /> |
| | | </wd-tab> |
| | | <wd-tab title="çå
·é¢ç¨" name="çå
·é¢ç¨"> |
| | | <view class="content"> |
| | | <Plate /> |
| | | <Plate v-if="tab === 'çå
·é¢ç¨'" /> |
| | | </view> |
| | | </wd-tab> |
| | | <wd-tab title="é¢è¯é¢ç¨" name="é¢è¯é¢ç¨"> |
| | | <wd-tab title="è¯çº¿é¢ç¨" name="è¯çº¿é¢ç¨"> |
| | | <view class="content"> |
| | | <SteelCore /> |
| | | <SteelCore v-if="tab === 'è¯çº¿é¢ç¨'" /> |
| | | </view> |
| | | </wd-tab> |
| | | </wd-tabs> |
| | |
| | | <block v-for="item in nodeList" :key="item"> |
| | | <wd-tab :title="item.twistedLayer" :name="item.twistedLayer"> |
| | | <scroll-view class="content" scroll-y> |
| | | <MonofilCard v-for="(m, i) in item.strandedWireDish" :key="i" :data="m" /> |
| | | <MonofilCard |
| | | v-for="(m, i) in item.strandedWireDish" |
| | | :key="i" |
| | | :data="m" |
| | | @delete="handleDeleteCard(item, m)" |
| | | /> |
| | | </scroll-view> |
| | | </wd-tab> |
| | | </block> |
| | |
| | | import MonofilCard from "../components/MonofilCard.vue"; |
| | | import StatisticsModal from "../components/StatisticsModal.vue"; |
| | | import { useToast } from "wot-design-uni"; |
| | | import { onLoad, onUnload } from "@dcloudio/uni-app"; |
| | | import { onLoad, onUnload, onShow, onHide } from "@dcloudio/uni-app"; |
| | | import Scan from "@/components/scan/index.vue"; |
| | | import ManageApi from "@/api/product/manage"; |
| | | import TwistApi from "@/api/product/twist"; |
| | |
| | | const showStatisticsModal = ref(false); |
| | | const showManualInput = ref(false); |
| | | const manualOutPutId = ref(""); |
| | | const isPageVisible = ref(false); // æ è®°é¡µé¢æ¯å¦å¯è§ |
| | | |
| | | // ç嬿 ç¾åæ¢ |
| | | watch(tab, () => { |
| | | if (tab.value) { |
| | | console.log("tab.value:===========1", tab.value); |
| | | getList(); |
| | | } |
| | | }); |
| | | |
| | | const getScanCode = async (code: any) => { |
| | | console.log("èªå®ä¹æ«æçç»æåè°å½æ°:", code); |
| | | // let parseData = code.trim(); |
| | | console.log("code:===========", JSON.parse(code.code)); |
| | | console.log("id:=============", JSON.parse(code.code).id); |
| | | // æ£æ¥é¡µé¢æ¯å¦å¯è§ï¼å¦æä¸å¯è§åä¸å¤çæ«ç æ°æ® |
| | | if (!isPageVisible.value) { |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | // æ£æ¥æ¯å¦å·²éæ©æ ç¾ |
| | | if (!tab.value) { |
| | |
| | | } |
| | | |
| | | // æ¾å°å½åéä¸çå± |
| | | console.log("tab.value:===========2", tab.value); |
| | | const currentLayer = nodeList.value.find((node) => node.twistedLayer === tab.value); |
| | | if (!currentLayer) { |
| | | toast.error("æªæ¾å°å½åéä¸çå±"); |
| | | return; |
| | | } |
| | | console.log("tab.value:===========3", currentLayer); |
| | | // å¨å起请æ±åï¼å
æ ¡éªè¯¥å䏿¯å¦å·²å¨å½åæå
¶ä»å±çº§è¢«é¢ç¨ |
| | | const scannedOutputId = JSON.parse(code.code).id; |
| | | const alreadyUsed = nodeList.value.some((node) => |
| | | (node.strandedWireDish || []).some((item: any) => item.outputId === scannedOutputId) |
| | | ); |
| | | if (alreadyUsed) { |
| | | toast.error("该åä¸å·²é¢ç¨ï¼è¯·å¿é夿«ç "); |
| | | return; |
| | | |
| | | // è§£ææ«ç æ°æ® |
| | | const scanData = JSON.parse(code.code); |
| | | |
| | | // 夿å±çº§æ¯å¦å¹é
|
| | | if (scanData.layer && scanData.layer !== currentLayer.twistedLayer) { |
| | | toast.error( |
| | | `é¢ç¨å±çº§ä¸å¯¹ï¼å½å屿¯ï¼${currentLayer.twistedLayer}ï¼é¢ç¨åä¸å±æ¯ï¼${scanData.layer}` |
| | | ); |
| | | // return; |
| | | } |
| | | |
| | | const { data } = await TwistApi.getScarn({ |
| | | outPutId: scannedOutputId, |
| | | outPutId: scanData.id, |
| | | twistId: currentLayer.twistId, |
| | | }); |
| | | |
| | |
| | | // 设置é»è®¤ç¬¬ä¸å± |
| | | if (nodeList.value && nodeList.value.length > 0 && !tab.value) { |
| | | tab.value = nodeList.value[0].twistedLayer; |
| | | console.log("设置é»è®¤ç¬¬ä¸å±:", tab.value); |
| | | // 设置é»è®¤æ ç¾åï¼å 载第ä¸å±çæ°æ® |
| | | getList(); |
| | | } |
| | |
| | | } |
| | | }; |
| | | |
| | | // å é¤å¡ç |
| | | const handleDeleteCard = async (layer: any, cardData: any) => { |
| | | // æ¾ç¤ºç¡®è®¤æç¤º |
| | | uni.showModal({ |
| | | title: "æç¤º", |
| | | content: "ç¡®å®è¦å é¤è¯¥åä¸åï¼", |
| | | success: async (res) => { |
| | | if (res.confirm) { |
| | | try { |
| | | // 妿æidï¼è°ç¨æ¥å£å é¤ |
| | | if (cardData.id !== undefined && cardData.id !== null) { |
| | | const { code, msg } = await TwistApi.deleteStrandedWireDish(cardData.id); |
| | | if (code !== 200) { |
| | | toast.error(msg || "å é¤å¤±è´¥"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // åç«¯ç´æ¥å é¤ï¼æ 论æ¯å¦æidï¼é½ä»å端å é¤ï¼ |
| | | if (layer.strandedWireDish && Array.isArray(layer.strandedWireDish)) { |
| | | const index = layer.strandedWireDish.findIndex( |
| | | (item: any) => item.monofilamentNumber === cardData.monofilamentNumber |
| | | ); |
| | | if (index !== -1) { |
| | | layer.strandedWireDish.splice(index, 1); |
| | | toast.success("å 餿å"); |
| | | // å·æ°å½åå±çæ°æ®æ¾ç¤º |
| | | getList(); |
| | | } |
| | | } |
| | | } catch (error: any) { |
| | | toast.error(error.msg || "å é¤å¤±è´¥"); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | onLoad(async (options: any) => { |
| | | // å¼å¯å¹¿æçå¬äºä»¶ |
| | | uni.$on("scanMono", getScanCode); |
| | |
| | | getRootNumber(options.id); |
| | | // getRootNumber(118); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶æ 记为å¯è§ |
| | | isPageVisible.value = true; |
| | | }); |
| | | |
| | | onHide(() => { |
| | | // 页é¢éèæ¶æ 记为ä¸å¯è§ |
| | | isPageVisible.value = false; |
| | | }); |
| | | |
| | | onUnload(() => { |
| | | // å¼å¯å¹¿æçå¬äºä»¶ |
| | | // åæ¶å¹¿æçå¬äºä»¶ |
| | | uni.$off("scanMono", getScanCode); |
| | | isPageVisible.value = false; |
| | | }); |
| | | </script> |
| | | |
| | |
| | | <template> |
| | | <view> |
| | | <CardTitle title="ç»çº¿é¢è¯é¢ç¨" :hideAction="false" /> |
| | | <CardTitle title="ç»çº¿è¯çº¿é¢ç¨" :hideAction="false" /> |
| | | <SteelCoreForm ref="formRef" class="mx-4" /> |
| | | <view class="footer"> |
| | | <wd-button |
| | |
| | | |
| | | // æ¥æ¶åè¡¨é¡µä¼ éçæ°æ® |
| | | const receiveEditData = (data: any) => { |
| | | console.log("receiveEditData æ¥æ¶å°çæ°æ®:", data); |
| | | if (data && formRef.value) { |
| | | // ç¡®ä¿ list å editId é½åå¨ |
| | | if (data.list && data.editId) { |
| | | formRef.value.setFormData(data.list, data.editId); |
| | | } else { |
| | | console.error("æ°æ®æ ¼å¼é误:", data); |
| | | } |
| | | } |
| | | }; |
| | |
| | | <template> |
| | | <wd-form ref="form" :model="model" class="relative form_box"> |
| | | <wd-cell-group :border="true"> |
| | | <wd-picker |
| | | v-model="diskMaterialValue" |
| | | :columns="diskMaterialOptions" |
| | | label="è¯çº¿ç±»å" |
| | | label-width="100px" |
| | | prop="diskMaterial" |
| | | placeholder="è¯·éæ©è¯çº¿ç±»å" |
| | | clearable |
| | | @confirm="handleDiskMaterialChange" |
| | | /> |
| | | <wd-input |
| | | v-model="model.model" |
| | | label="è§æ ¼åå·" |
| | |
| | | <script lang="ts" setup> |
| | | import useFormData from "@/hooks/useFormData"; |
| | | import TwistApi from "@/api/product/twist"; |
| | | import ManageApi from "@/api/product/manage"; |
| | | import { useToast } from "wot-design-uni"; |
| | | |
| | | const props = defineProps({ |
| | |
| | | type: Object, |
| | | default: null, |
| | | }, |
| | | wireId: { |
| | | type: [String, Number], |
| | | default: undefined, |
| | | }, |
| | | }); |
| | | |
| | | const emits = defineEmits(["refresh"]); |
| | |
| | | const allListData = ref<any[]>([]); // åå¨å®æ´åè¡¨æ°æ® |
| | | const toast = useToast(); |
| | | const { form: model } = useFormData({ |
| | | diskMaterial: undefined, // è¯çº¿ç±»å |
| | | model: undefined, // è§æ ¼åå· |
| | | monofilamentNumber: undefined, // æ ·åç¼å· |
| | | amount: undefined, // æ°é |
| | |
| | | type: "é¢è¯", |
| | | }); |
| | | |
| | | // è¯çº¿ç±»ååå
¸æ°æ® |
| | | const diskMaterialOptions = ref<Array<{ label: string; value: string }>>([]); |
| | | const diskMaterialValue = ref(""); |
| | | |
| | | // å è½½è¯çº¿ç±»ååå
¸æ°æ® |
| | | const loadDiskMaterialDict = async () => { |
| | | try { |
| | | const res = await ManageApi.dictAPI("core_wire_type"); |
| | | if (res.data && Array.isArray(res.data)) { |
| | | diskMaterialOptions.value = res.data.map((item: any) => ({ |
| | | label: item.dictLabel || "", |
| | | value: item.dictValue || "", |
| | | })); |
| | | } |
| | | } catch (error) { |
| | | // å è½½åå
¸å¤±è´¥ï¼éé»å¤ç |
| | | } |
| | | }; |
| | | |
| | | // å¤çè¯çº¿ç±»åéæ© |
| | | const handleDiskMaterialChange = (val: any) => { |
| | | model.diskMaterial = val.value; |
| | | }; |
| | | |
| | | // çå¬ model.diskMaterial ååï¼åæ¥éæ©å¨æ¾ç¤º |
| | | watch( |
| | | () => model.diskMaterial, |
| | | (newValue) => { |
| | | diskMaterialValue.value = newValue || ""; |
| | | }, |
| | | { immediate: true } |
| | | ); |
| | | |
| | | // æ°å¢æäº¤ |
| | | const submit = async () => { |
| | | const currentWireId = props.wireId || paramsId.value; |
| | | const { code } = await TwistApi.addStrandedWireDish([ |
| | | { |
| | | wireId: paramsId.value, |
| | | wireId: currentWireId, |
| | | ...model, |
| | | }, |
| | | ]); |
| | |
| | | // ä¿çåææ°æ®ï¼ç¶åæ´æ°ä¿®æ¹çåæ®µ |
| | | const updatedItem = { |
| | | ...item, // å
ä¿çåæçæææ°æ® |
| | | diskMaterial: model.diskMaterial, |
| | | model: model.model, |
| | | monofilamentNumber: model.monofilamentNumber, |
| | | amount: model.amount, |
| | |
| | | const setFormData = (list: any[], currentEditId: number) => { |
| | | // å®å
¨æ£æ¥ï¼ç¡®ä¿listæ¯æ°ç» |
| | | if (!Array.isArray(list)) { |
| | | console.error("setFormData: list 忰䏿¯æ°ç»", list); |
| | | return; |
| | | } |
| | | |
| | |
| | | // æ¾å°å½åç¼è¾é¡¹å¹¶åæ¾å°è¡¨å |
| | | const currentItem = list.find((item) => item.id === currentEditId); |
| | | if (currentItem) { |
| | | model.diskMaterial = currentItem.diskMaterial; |
| | | model.model = currentItem.model; |
| | | model.monofilamentNumber = currentItem.monofilamentNumber; |
| | | model.amount = currentItem.amount; |
| | | model.weight = currentItem.weight; |
| | | model.supplier = currentItem.supplier; |
| | | model.type = currentItem.type || "é¢è¯"; |
| | | // 设置è¯çº¿ç±»åçåæ¾å¼ |
| | | diskMaterialValue.value = currentItem.diskMaterial || ""; |
| | | } |
| | | }; |
| | | |
| | |
| | | () => props.editData, |
| | | (newData) => { |
| | | if (newData && props.mode === "edit") { |
| | | model.diskMaterial = newData.diskMaterial || ""; |
| | | model.model = newData.model || ""; |
| | | model.monofilamentNumber = newData.monofilamentNumber || ""; |
| | | model.amount = newData.amount || ""; |
| | | model.weight = newData.weight || ""; |
| | | model.supplier = newData.supplier || ""; |
| | | model.type = newData.type || "é¢è¯"; |
| | | diskMaterialValue.value = newData.diskMaterial || ""; |
| | | } |
| | | }, |
| | | { immediate: true, deep: true } |
| | |
| | | |
| | | // éç½®è¡¨åæ°æ® |
| | | const resetFormData = () => { |
| | | model.diskMaterial = undefined; |
| | | model.model = undefined; |
| | | model.monofilamentNumber = undefined; |
| | | model.amount = undefined; |
| | | model.weight = undefined; |
| | | model.supplier = undefined; |
| | | model.type = "é¢è¯"; |
| | | diskMaterialValue.value = ""; |
| | | }; |
| | | |
| | | // å¡«å
è¡¨åæ°æ®ï¼ç¨äºæ«ç ååæ¾ï¼ |
| | | const fillFormData = (data: any) => { |
| | | if (data) { |
| | | model.diskMaterial = data.diskMaterial || ""; |
| | | model.model = data.model || ""; |
| | | model.monofilamentNumber = data.monofilamentNumber || ""; |
| | | model.amount = data.oneLength || data.amount || ""; |
| | | model.weight = data.weight || ""; |
| | | model.supplier = data.supplier || ""; |
| | | model.type = data.type || "é¢è¯"; |
| | | diskMaterialValue.value = data.diskMaterial || ""; |
| | | } |
| | | }; |
| | | |
| | | onLoad((options: any) => { |
| | | paramsId.value = options.id; |
| | | }); |
| | | |
| | | onMounted(async () => { |
| | | await loadDiskMaterialDict(); |
| | | }); |
| | | |
| | | defineExpose({ |
| | |
| | | submitEdit, |
| | | setFormData, |
| | | resetFormData, |
| | | fillFormData, |
| | | }); |
| | | </script> |
| | | |
| | |
| | | @query="getList" |
| | | > |
| | | <template #top> |
| | | <CardTitle title="é¢è¯é¢ç¨" :hideAction="true" :full="false" @action="addReport" /> |
| | | <CardTitle title="è¯çº¿é¢ç¨" :hideAction="false" :full="false"> |
| | | <template #action> |
| | | <wd-button type="icon" icon="scan" color="#0D867F" @click="openScan"></wd-button> |
| | | <wd-button type="icon" icon="add-circle" color="#0D867F" @click="addReport"></wd-button> |
| | | </template> |
| | | </CardTitle> |
| | | </template> |
| | | <wd-card v-for="(item, index) in cardList" :key="index" type="rectangle" custom-class="round"> |
| | | <template #title> |
| | |
| | | <wd-button type="text" @click="cancelAdd">åæ¶</wd-button> |
| | | <wd-button type="text" @click="submitAdd">ç¡®å®</wd-button> |
| | | </view> |
| | | <SteelCore ref="addFormRef" mode="add" @refresh="reloadList" /> |
| | | <SteelCore ref="addFormRef" mode="add" :wireId="paramsId" @refresh="reloadList" /> |
| | | </wd-popup> |
| | | <wd-popup v-model="editDialog.visible" position="bottom" custom-class="yl-popup"> |
| | | <view class="action px-3"> |
| | |
| | | <SteelCore |
| | | ref="editFormRef" |
| | | mode="edit" |
| | | :wireId="paramsId" |
| | | :editData="editDialog.currentItem" |
| | | @refresh="reloadList" |
| | | /> |
| | | </wd-popup> |
| | | <Scan ref="scanRef" emitName="scanSteelCore" /> |
| | | <wd-toast /> |
| | | </view> |
| | | </template> |
| | |
| | | import ProductionCard from "../../../components/ProductionCard.vue"; |
| | | import { useToast } from "wot-design-uni"; |
| | | import SteelCore from "./form.vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import { onLoad, onUnload, onShow, onHide } from "@dcloudio/uni-app"; |
| | | import ManageApi from "@/api/product/manage"; |
| | | import TwistApi from "@/api/product/twist"; |
| | | import zPaging from "@/components/z-paging/z-paging.vue"; |
| | | import Scan from "@/components/scan/index.vue"; |
| | | |
| | | const paramsId = ref(); |
| | | const pagingRef = ref(); |
| | | const addFormRef = ref(); |
| | | const editFormRef = ref(); |
| | | const scanRef = ref(); |
| | | const toast = useToast(); |
| | | const isPageVisible = ref(false); // æ è®°é¡µé¢æ¯å¦å¯è§ |
| | | const addDialog = reactive({ |
| | | visible: false, |
| | | }); |
| | |
| | | pagingRef.value.refresh(); |
| | | }; |
| | | |
| | | // æ«ç ç¸å
³æ¹æ³ |
| | | const openScan = () => { |
| | | scanRef.value.triggerScan(); |
| | | }; |
| | | |
| | | const getScanCode = async (code: any) => { |
| | | // æ£æ¥é¡µé¢æ¯å¦å¯è§ï¼å¦æä¸å¯è§åä¸å¤çæ«ç æ°æ® |
| | | if (!isPageVisible.value) { |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | const parseData = JSON.parse(code.code); |
| | | |
| | | // æ£æ¥å¿
éåæ®µï¼modelãsupplierãdiskMaterial |
| | | const requiredFields = ["model", "supplier", "diskMaterial"]; |
| | | const missingFields = requiredFields.filter((field) => !parseData[field]); |
| | | |
| | | if (missingFields.length > 0) { |
| | | toast.error(`äºç»´ç é误ï¼è¯·æ´æ¢äºç»´ç ï¼`); |
| | | return; |
| | | } |
| | | |
| | | // æå¼æ°å¢å¼¹æ¡å¹¶å¡«å
æ«ç è·åçä¿¡æ¯ |
| | | addDialog.visible = true; |
| | | |
| | | // çå¾
å¼¹æ¡æå¼åå¡«å
è¡¨åæ°æ® |
| | | // 使ç¨åéçå¾
ï¼nextTick + setTimeout ç¡®ä¿ç»ä»¶å·²å®å
¨æè½½ |
| | | nextTick(() => { |
| | | setTimeout(() => { |
| | | if (addFormRef.value) { |
| | | addFormRef.value.fillFormData(parseData); |
| | | toast.success("æ«ç æåï¼è¯·ç¡®è®¤ä¿¡æ¯"); |
| | | } else { |
| | | toast.error("表åå 载失败ï¼è¯·éè¯"); |
| | | } |
| | | }, 200); // å»¶è¿200msç¡®ä¿å¼¹æ¡åç»ä»¶å·²å®å
¨æ¸²æ |
| | | }); |
| | | } catch (error) { |
| | | toast.error("äºç»´ç å¼å¸¸ï¼è¯·æ´æ¢äºç»´ç ï¼"); |
| | | } |
| | | }; |
| | | |
| | | onLoad((options: any) => { |
| | | // å¼å¯å¹¿æçå¬äºä»¶ |
| | | uni.$on("scanSteelCore", getScanCode); |
| | | paramsId.value = options.id; |
| | | }); |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶æ 记为å¯è§ |
| | | isPageVisible.value = true; |
| | | }); |
| | | |
| | | onHide(() => { |
| | | // 页é¢éèæ¶æ 记为ä¸å¯è§ |
| | | isPageVisible.value = false; |
| | | }); |
| | | |
| | | onUnload(() => { |
| | | // åæ¶å¹¿æçå¬äºä»¶ |
| | | uni.$off("scanSteelCore", getScanCode); |
| | | isPageVisible.value = false; |
| | | }); |
| | | </script> |
| | | |
| | |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | :deep(.wd-button__content) { |
| | | color: #0d867f; |
| | | } |
| | | </style> |
| | |
| | | </template> |
| | | </view> |
| | | </wd-tab> |
| | | <wd-tab title="é¢è¯é¢ç¨èªæ£" name="steel"> |
| | | <wd-tab title="è¯çº¿é¢ç¨èªæ£" name="steel"> |
| | | <view class="form-section"> |
| | | <wd-form :model="localSteelData"> |
| | | <wd-form-item label="è§æ ¼åå·" prop="model" required> |
| | |
| | | <wd-form ref="form" :model="model" class="relative form_box"> |
| | | <wd-cell-group :border="true"> |
| | | <wd-input |
| | | v-model="model.contractNo" |
| | | label="é¢ç¨æå·" |
| | | label-width="100px" |
| | | prop="contractNo" |
| | | clearable |
| | | placeholder="请è¾å
¥é¢ç¨æå·" |
| | | /> |
| | | <wd-input |
| | | v-model="model.status" |
| | | label="æé(kg)" |
| | | label-width="100px" |
| | | prop="status" |
| | | clearable |
| | | placeholder="请è¾å
¥æé" |
| | | /> |
| | | <wd-input |
| | | v-model="model.clientName" |
| | | label="åä¸çå·" |
| | | label-width="100px" |
| | | prop="clientName" |
| | | clearable |
| | | placeholder="请è¾å
¥åä¸çå·" |
| | | /> |
| | | <wd-input |
| | | v-model="model.workbench" |
| | | label="å®é
éé(kg)" |
| | | label-width="100px" |
| | | prop="workbench" |
| | | clearable |
| | | placeholder="请è¾å
¥å®é
éé" |
| | | /> |
| | | <wd-input |
| | | v-model="model.quality" |
| | | label="çé¿(m)" |
| | | label-width="100px" |
| | | prop="quality" |
| | | clearable |
| | | placeholder="请è¾å
¥çé¿" |
| | | /> |
| | | <wd-input |
| | | v-model="model.specification" |
| | | label="ç论éé(kg)" |
| | | label-width="100px" |
| | | prop="specification" |
| | | clearable |
| | | placeholder="请è¾å
¥ç论éé" |
| | | /> |
| | | <wd-input |
| | | v-model="model.disc" |
| | | label="è§æ ¼åå·" |
| | | label-width="100px" |
| | | prop="disc" |
| | | clearable |
| | | placeholder="请è¾å
¥è§æ ¼åå·" |
| | | /> |
| | | <wd-input |
| | | v-model="model.actuallyLength" |
| | | label="å®é
çé¿(m)" |
| | | label="ç产é¿åº¦(m)" |
| | | label-width="100px" |
| | | prop="actuallyLength" |
| | | clearable |
| | | placeholder="请è¾å
¥å®é
çé¿" |
| | | /> |
| | | placeholder="请è¾å
¥ç产é¿åº¦" |
| | | type="digit" |
| | | > |
| | | <template #label> |
| | | <span style="color: #f56c6c">ç产é¿åº¦(m)</span> |
| | | </template> |
| | | </wd-input> |
| | | <wd-input |
| | | v-model="model.tare" |
| | | label="çå
·ç®é(kg)" |
| | | label-width="100px" |
| | | prop="tare" |
| | | :disabled="!isFirstReport" |
| | | :clearable="isFirstReport" |
| | | :placeholder="isFirstReport ? '请è¾å
¥çå
·ç®é' : 'çå
·ç®éèªå¨å¸¦åº'" |
| | | type="digit" |
| | | > |
| | | <template #label> |
| | | <span style="color: #f56c6c">çå
·ç®é(kg)</span> |
| | | </template> |
| | | </wd-input> |
| | | </wd-cell-group> |
| | | </wd-form> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { computed, watch } from "vue"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | | import { useToast } from "wot-design-uni"; |
| | | import { useToast, dayjs } from "wot-design-uni"; |
| | | import TwistApi from "@/api/product/twist"; |
| | | |
| | | // å®ä¹ props |
| | | const props = defineProps<{ |
| | | firstTareValue?: number; |
| | | teamId?: string | number | null; |
| | | isFirstReport?: boolean; // æ¯å¦æ¯ç¬¬ä¸æ¡æ¥å·¥ |
| | | }>(); |
| | | |
| | | // è®¡ç®æ¯å¦æ¯ç¬¬ä¸æ¡æ¥å·¥ |
| | | const isFirstReport = computed(() => props.isFirstReport ?? true); |
| | | |
| | | const paramsId = ref(); |
| | | const toast = useToast(); |
| | | const { form: model } = useFormData({ |
| | | poleNumber: undefined, // é¢ç¨æå· |
| | | poleWeight: undefined, // æé(kg) |
| | | monofilamentNumber: undefined, // åä¸çå· |
| | | actuallyWeight: undefined, // å®é
éé(kg) |
| | | oneLength: undefined, // çé¿(m) |
| | | theoryWeight: undefined, // ç论éé(kg) |
| | | model: undefined, // è§æ ¼åå· |
| | | actuallyLength: undefined, // å®é
çé¿(m) |
| | | const { form: model, resetForm } = useFormData({ |
| | | actuallyLength: undefined, // ç产é¿åº¦(m) |
| | | tare: undefined, // çå
·ç®é(kg) |
| | | }); |
| | | |
| | | // ä¸»è¡¨æ°æ®ï¼ä»ç¶ç»ä»¶ä¼ å
¥ï¼ |
| | | const mainTableData = ref<any>({}); |
| | | |
| | | // è®¾ç½®ä¸»è¡¨æ°æ® |
| | | const setMainTableData = (data: any) => { |
| | | mainTableData.value = data; |
| | | }; |
| | | |
| | | // çå¬ firstTareValue ååï¼å¦æä¸æ¯ç¬¬ä¸æ¡ï¼èªå¨å¡«å
|
| | | watch( |
| | | () => props.firstTareValue, |
| | | (newVal) => { |
| | | if (!isFirstReport.value && newVal !== undefined) { |
| | | model.tare = newVal; |
| | | } |
| | | }, |
| | | { immediate: true } |
| | | ); |
| | | |
| | | const submit = async () => { |
| | | const { code } = await TwistApi.addTwistOutput({ |
| | | // è·åç¬¬ä¸æ¡æ°æ®çç®éå¼ï¼ç¨äºåç»æ°å¢çæ¥å·¥ |
| | | const firstTareValue = props.firstTareValue; |
| | | |
| | | // å¦æä¸»è¡¨æ°æ®æªè·åï¼å°è¯éæ°è·å |
| | | if (!mainTableData.value.model) { |
| | | try { |
| | | const { data } = await TwistApi.getTwistDetailById({ |
| | | id: paramsId.value, |
| | | }); |
| | | mainTableData.value = { |
| | | model: data.model, |
| | | totalLength: data.totalLength, |
| | | systemNo: data.systemNo, |
| | | }; |
| | | } catch (error) { |
| | | console.error("è·åä¸»è¡¨æ°æ®å¤±è´¥:", error); |
| | | toast.error("è·åè§æ ¼åå·æ°æ®å¤±è´¥ï¼è¯·éè¯"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 忬¡æ£æ¥ä¸»è¡¨æ°æ® |
| | | if (!mainTableData.value.model) { |
| | | toast.error("è§æ ¼åå·æ°æ®æªè·åï¼è¯·éè¯"); |
| | | return false; |
| | | } |
| | | |
| | | const submitData = { |
| | | teamId: props.teamId || null, |
| | | wireId: paramsId.value, |
| | | ...model, |
| | | }); |
| | | type: "ç»çº¿", |
| | | actuallyLength: model.actuallyLength, |
| | | tare: model.tare || firstTareValue, |
| | | // ä»ä¸»è¡¨è·åçåæ®µ |
| | | model: mainTableData.value.model, // è§æ ¼åå· |
| | | oneLength: mainTableData.value.totalLength, |
| | | systemNo: mainTableData.value.systemNo, |
| | | monofilamentNumber: undefined, // æ¹æ¬¡å·ï¼å端èªå¨çæï¼ |
| | | // çäº§æ¥æèªå¨è®¾ç½®ä¸ºå½åæ¶é´ |
| | | productTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }; |
| | | |
| | | // è°è¯æ¥å¿ |
| | | console.log("æäº¤æ°æ®:", submitData); |
| | | console.log("ä¸»è¡¨æ°æ®:", mainTableData.value); |
| | | |
| | | const { code } = await TwistApi.addTwistOutput(submitData); |
| | | if (code == 200) { |
| | | toast.success("æäº¤æå"); |
| | | resetForm(); |
| | | return true; |
| | | } else { |
| | | toast.error("æäº¤å¤±è´¥"); |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | // è·åè¡¨åæ°æ® |
| | | const getFormData = () => { |
| | | return { ...model }; |
| | | }; |
| | | |
| | | onLoad((options: any) => { |
| | |
| | | |
| | | defineExpose({ |
| | | submit, |
| | | getFormData, |
| | | setMainTableData, |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .form_box { |
| | | } |
| | | .submit_btn { |
| | | position: absolute; |
| | | bottom: 0; |
| | |
| | | <text class="text-[#0D867F] ml-2 font-medium">ç产人</text> |
| | | <text class="text-[#333333] ml-2">{{ item.productUser }}</text> |
| | | </view> |
| | | <view class="text-[#A8A8A8]" @click="toEdit">ç¼è¾</view> |
| | | <!-- <view class="text-[#A8A8A8]" @click="toEdit">ç¼è¾</view> --> |
| | | </view> |
| | | </template> |
| | | <ProductionCard :data="cardAttr" :value="item" /> |
| | | <TwistReportCard :data="cardAttr" :value="item" /> |
| | | <template #footer> |
| | | <wd-button size="small" plain style="margin-right: 10px" @click="toAttachment(item)"> |
| | | éä»¶ |
| | | </wd-button> |
| | | <wd-button size="small" plain @click="handleSelfCheck(item.id)">èªæ£</wd-button> |
| | | <wd-button |
| | | size="small" |
| | | plain |
| | | style="margin-right: 10px" |
| | | @click="handleSelfCheck(item.id)" |
| | | > |
| | | èªæ£ |
| | | </wd-button> |
| | | <wd-button size="small" plain type="error" @click="handleDelete(item)">å é¤</wd-button> |
| | | </template> |
| | | </wd-card> |
| | | </z-paging> |
| | |
| | | <wd-button type="text" @click="cancel">åæ¶</wd-button> |
| | | <wd-button type="text" @click="submit">ç¡®å®</wd-button> |
| | | </view> |
| | | <TwistForm ref="twistFormRef" /> |
| | | <TwistForm |
| | | ref="twistFormRef" |
| | | :first-tare-value="twistReportList.length > 0 ? twistReportList[0].tare : undefined" |
| | | :team-id="teamId" |
| | | :is-first-report="twistReportList.length === 0" |
| | | /> |
| | | </wd-popup> |
| | | <wd-popup v-model="drawFormRef.visible" position="bottom" custom-class="yl-popup"> |
| | | <Draw |
| | |
| | | <script setup lang="ts"> |
| | | import CardTitle from "@/components/card-title/index.vue"; |
| | | import TwistForm from "./form.vue"; |
| | | import { useToast } from "wot-design-uni"; |
| | | import ProductionCard from "../../components/ProductionCard.vue"; |
| | | import { useToast, dayjs } from "wot-design-uni"; |
| | | import TwistReportCard from "../components/TwistReportCard.vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import { ref, reactive } from "vue"; |
| | | import { ref, reactive, nextTick } from "vue"; |
| | | import ManageApi from "@/api/product/manage"; |
| | | import TwistApi from "@/api/product/twist"; |
| | | import Draw from "./draw.vue"; |
| | | import HomeApi from "@/api/home"; |
| | | import { setTeamId, getTeamId } from "@/utils/cache"; |
| | | |
| | | const drawFormRef = reactive({ |
| | | visible: false, |
| | |
| | | |
| | | const cardAttr = ref<any[]>([ |
| | | { |
| | | label: "é¢ç¨æå·", |
| | | prop: "poleNumber", |
| | | }, |
| | | { |
| | | label: "æé(kg)", |
| | | prop: "poleWeight", |
| | | }, |
| | | { |
| | | label: "åä¸çå·", |
| | | label: "æ¹æ¬¡å·", |
| | | prop: "monofilamentNumber", |
| | | span: 24, |
| | | }, |
| | | { |
| | | label: "å®é
éé(kg)", |
| | | prop: "actuallyWeight", |
| | | label: "è´¨é追溯å·", |
| | | prop: "systemNo", |
| | | span: 24, |
| | | }, |
| | | { |
| | | label: "çé¿(m)", |
| | | prop: "oneLength", |
| | | }, |
| | | { |
| | | label: "ç论éé(kg)", |
| | | prop: "theoryWeight", |
| | | }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | | prop: "model", |
| | | }, |
| | | { |
| | | label: "ç产é¿åº¦(m)", |
| | | prop: "actuallyLength", |
| | | }, |
| | | { |
| | | label: "çå
·ç®é(kg)", |
| | | prop: "tare", |
| | | }, |
| | | { |
| | | label: "çäº§æ¥æ", |
| | | prop: "productTime", |
| | | span: 24, |
| | | }, |
| | | { |
| | | label: "å å·¥æ¶é´(h)", |
| | | prop: "processHour", |
| | | }, |
| | | ]); |
| | | |
| | | const twistReportList = ref<any[]>([]); |
| | | const teamId = ref<string | number | null>(null); |
| | | |
| | | // è·åå¹¶ç¼åçç»ID |
| | | const initTeamId = async () => { |
| | | // å
å°è¯ä»ç¼åè·å |
| | | const cachedTeamId = getTeamId(); |
| | | if (cachedTeamId) { |
| | | teamId.value = cachedTeamId; |
| | | return; |
| | | } |
| | | |
| | | // 妿ç¼å䏿²¡æï¼åè°ç¨æ¥å£è·å |
| | | try { |
| | | const { data } = await HomeApi.getIndex(); |
| | | if (data && data.team) { |
| | | teamId.value = data.team; |
| | | setTeamId(data.team); |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åçç»ID失败:", error); |
| | | } |
| | | }; |
| | | |
| | | const toEdit = () => { |
| | | uni.navigateTo({ |
| | |
| | | }); |
| | | }; |
| | | |
| | | // ä¸»è¡¨æ°æ® |
| | | const mainTableData = ref<any>({}); |
| | | |
| | | // è·åä¸»è¡¨æ°æ® |
| | | const getMainTableData = async () => { |
| | | try { |
| | | const { data } = await TwistApi.getTwistDetailById({ |
| | | id: paramsId.value, |
| | | }); |
| | | mainTableData.value = { |
| | | model: data.model, |
| | | totalLength: data.totalLength, |
| | | systemNo: data.systemNo, |
| | | }; |
| | | // è®¾ç½®ä¸»è¡¨æ°æ®å°è¡¨åç»ä»¶ |
| | | if (twistFormRef.value) { |
| | | twistFormRef.value.setMainTableData(mainTableData.value); |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åä¸»è¡¨æ°æ®å¤±è´¥:", error); |
| | | } |
| | | }; |
| | | |
| | | const addReport = async () => { |
| | | dialog.visible = true; |
| | | // æå¼æ°å¢å¼¹çªæ¶èªå¨æ§è¡ |
| | | // await showDrawPopup(); |
| | | // æ£æ¥æ¯å¦æææ°æ®é½å·²ä¿åï¼é½æidï¼ |
| | | if (twistReportList.value.length > 0 && twistReportList.value.every((item) => item.id)) { |
| | | // ç¡®ä¿ä¸»è¡¨æ°æ®å·²è·å |
| | | if (!mainTableData.value.model) { |
| | | await getMainTableData(); |
| | | } |
| | | dialog.visible = true; |
| | | // çå¾
å¼¹çªæå¼åè®¾ç½®æ°æ® |
| | | await nextTick(); |
| | | if (twistFormRef.value) { |
| | | twistFormRef.value.setMainTableData(mainTableData.value); |
| | | } |
| | | } else if (twistReportList.value.length === 0) { |
| | | // ç¡®ä¿ä¸»è¡¨æ°æ®å·²è·å |
| | | if (!mainTableData.value.model) { |
| | | await getMainTableData(); |
| | | } |
| | | dialog.visible = true; |
| | | // çå¾
å¼¹çªæå¼åè®¾ç½®æ°æ® |
| | | await nextTick(); |
| | | if (twistFormRef.value) { |
| | | twistFormRef.value.setMainTableData(mainTableData.value); |
| | | } |
| | | } else { |
| | | toast.warning("请å
ä¿åæ¬æ¡æ°æ®ï¼åæ°å¢"); |
| | | } |
| | | }; |
| | | |
| | | const submit = async () => { |
| | | // éªè¯å¿
å¡«åæ®µ - æ ¹æ®åè代ç ï¼éè¦æ£æ¥ç产é¿åº¦åçå
·ç®é |
| | | const formData = twistFormRef.value?.getFormData?.() || {}; |
| | | const firstTareValue = |
| | | twistReportList.value.length > 0 ? twistReportList.value[0].tare : undefined; |
| | | |
| | | if (!formData.actuallyLength) { |
| | | toast.warning("请è¾å
¥ç产é¿åº¦ååæäº¤"); |
| | | return; |
| | | } |
| | | |
| | | if (!formData.tare && !firstTareValue) { |
| | | toast.warning("请è¾å
¥çå
·ç®éååæäº¤"); |
| | | return; |
| | | } |
| | | |
| | | const isSuccess = await twistFormRef.value.submit(); |
| | | dialog.visible = !isSuccess; // 妿æäº¤æåï¼å
³éå¼¹çª |
| | | if (isSuccess) { |
| | | // æäº¤æååæ§è¡ |
| | | // showDrawPopup(); |
| | | dialog.visible = false; |
| | | // æäº¤æååå·æ°å表 |
| | | pagingRef.value?.reload(); |
| | | } |
| | | }; |
| | | |
| | | const cancel = () => { |
| | | toast.show("åæ¶"); |
| | | dialog.visible = false; |
| | | }; |
| | | |
| | |
| | | }); |
| | | }; |
| | | |
| | | // å 餿¥å·¥è®°å½ |
| | | const handleDelete = (item: any) => { |
| | | uni.showModal({ |
| | | title: "æç¤º", |
| | | content: "ç¡®å®å é¤åï¼", |
| | | success: async (res) => { |
| | | if (res.confirm) { |
| | | try { |
| | | if (item.id) { |
| | | const { code } = await TwistApi.deleteWireOutput({ id: item.id }); |
| | | if (code == 200) { |
| | | toast.success("å 餿å"); |
| | | // å·æ°å表 |
| | | pagingRef.value?.reload(); |
| | | } else { |
| | | toast.error("å é¤å¤±è´¥"); |
| | | } |
| | | } else { |
| | | toast.warning("该记å½å°æªä¿åï¼æ æ³å é¤"); |
| | | } |
| | | } catch (error) { |
| | | console.error("å é¤å¤±è´¥:", error); |
| | | toast.error("å é¤å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // ä¿çåæçconfirm彿°ï¼ç¨äºå
¶ä»å°æ¹è°ç¨ |
| | | // const confirm = async () => { |
| | | // await showDrawPopup(); |
| | |
| | | wireId: paramsId.value, |
| | | type: "ç»çº¿", |
| | | }); |
| | | // æ ¼å¼åçäº§æ¥æ |
| | | if (Array.isArray(data)) { |
| | | data.forEach((item: any) => { |
| | | if (item.productTime) { |
| | | item.productTime = dayjs(item.productTime).format("YYYY-MM-DD HH:mm:ss"); |
| | | } |
| | | }); |
| | | } |
| | | pagingRef.value.complete(data); |
| | | }; |
| | | |
| | | onLoad((options: any) => { |
| | | onLoad(async (options: any) => { |
| | | paramsId.value = options.id; |
| | | // è·åå¹¶ç¼åçç»ID |
| | | await initTeamId(); |
| | | // è·åä¸»è¡¨æ°æ® |
| | | await getMainTableData(); |
| | | showDrawPopup(); |
| | | }); |
| | | </script> |
| | |
| | | <view class="attachment-list"> |
| | | <wd-status-tip v-if="attachmentList.length === 0" image="content" tip="ææ éä»¶" /> |
| | | |
| | | <wd-card |
| | | v-for="item in attachmentList" |
| | | :key="item.id" |
| | | type="rectangle" |
| | | custom-class="attachment-card" |
| | | :border="false" |
| | | > |
| | | <view class="attachment-item" @click="previewAttachment(item)"> |
| | | <view class="attachment-info"> |
| | | <view class="attachment-name">{{ item.bucketFileName || item.name }}</view> |
| | | <view class="attachment-meta"> |
| | | <text class="file-type">{{ getFileType(item.bucketFileName) }}</text> |
| | | <text class="upload-time">{{ formatTime(item.createTime) }}</text> |
| | | <view v-for="item in attachmentList" :key="item.id" class="attachment-card"> |
| | | <view class="media-wrapper" @click="previewAttachment(item)"> |
| | | <!-- å¾çé¢è§ --> |
| | | <template v-if="isImageType(item.url)"> |
| | | <image |
| | | v-if="!item.loadError" |
| | | :src="getFullUrl(item.url)" |
| | | mode="aspectFill" |
| | | class="media-preview" |
| | | @error="onImageError(item)" |
| | | @load="onImageLoad(item)" |
| | | /> |
| | | <!-- å¾çå 载失败æ¾ç¤ºé»è®¤å¾æ --> |
| | | <view v-else class="file-icon-wrapper"> |
| | | <wd-icon name="picture" size="48px" color="#ccc" /> |
| | | <text class="file-name error-text">å 载失败</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <!-- è§é¢é¢è§ --> |
| | | <template v-else-if="isVideoType(item.url)"> |
| | | <video |
| | | v-if="!item.loadError" |
| | | :src="getFullUrl(item.url)" |
| | | class="media-preview" |
| | | :controls="false" |
| | | :show-center-play-btn="false" |
| | | @error="onVideoError(item)" |
| | | /> |
| | | <!-- è§é¢å 载失败æ¾ç¤ºé»è®¤å¾æ --> |
| | | <view v-else class="file-icon-wrapper"> |
| | | <wd-icon name="video" size="48px" color="#ccc" /> |
| | | <text class="file-name error-text">å 载失败</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <!-- å
¶ä»æä»¶ç±»åæ¾ç¤ºå¾æ --> |
| | | <view v-else class="file-icon-wrapper"> |
| | | <wd-icon name="file-outline" size="48px" color="#999" /> |
| | | <text class="file-name">æä»¶</text> |
| | | </view> |
| | | <view class="attachment-actions" @click.stop> |
| | | <wd-icon name="delete" color="#ff4757" @click="deleteAttachment(item.id)" /> |
| | | |
| | | <!-- å é¤æé® --> |
| | | <view class="delete-btn" @click.stop="deleteAttachment(item.id)"> |
| | | <wd-icon name="delete" color="#fff" size="20px" /> |
| | | </view> |
| | | </view> |
| | | </wd-card> |
| | | </view> |
| | | </view> |
| | | |
| | | <wd-toast /> |
| | |
| | | import { useToast } from "wot-design-uni"; |
| | | import AttachmentAPI from "@/api/product/attachment"; |
| | | |
| | | // H5 ä½¿ç¨ VITE_APP_BASE_API ä½ä¸ºä»£çè·¯å¾ï¼å
¶ä»å¹³å°ä½¿ç¨ VITE_APP_API_URL ä½ä¸ºè¯·æ±è·¯å¾ |
| | | let baseUrl = import.meta.env.VITE_APP_API_URL; |
| | | // #ifdef H5 |
| | | baseUrl = import.meta.env.VITE_APP_BASE_API; |
| | | // #endif |
| | | |
| | | const toast = useToast(); |
| | | |
| | | // 页é¢åæ° |
| | |
| | | const attachmentList = ref<any[]>([]); |
| | | |
| | | const detailData = ref<any>({}); |
| | | |
| | | // è·å宿´çå¾ç/è§é¢ URL |
| | | const getFullUrl = (url: string) => { |
| | | if (!url) return ""; |
| | | // å¦æå·²ç»æ¯å®æ´ç URLï¼http æ https å¼å¤´ï¼ï¼ç´æ¥è¿å |
| | | if (url.startsWith("http://") || url.startsWith("https://")) { |
| | | return url; |
| | | } |
| | | // 妿æ¯ç¸å¯¹è·¯å¾ï¼æ¼æ¥åºç¡ URL |
| | | return `${baseUrl}${url.startsWith("/") ? "" : "/"}${url}`; |
| | | }; |
| | | |
| | | // ä» URL ææä»¶å䏿忩å±å |
| | | const getExtension = (urlOrFileName: string) => { |
| | | if (!urlOrFileName) return ""; |
| | | // ç§»é¤æ¥è¯¢åæ°ååå¸ |
| | | const cleanUrl = urlOrFileName.split("?")[0].split("#")[0]; |
| | | // è·åæåä¸ä¸ªç¹åé¢çå
容 |
| | | const extension = cleanUrl.split(".").pop()?.toLowerCase(); |
| | | return extension || ""; |
| | | }; |
| | | |
| | | // 夿æ¯å¦ä¸ºå¾çç±»å |
| | | const isImageType = (urlOrFileName: string) => { |
| | | const extension = getExtension(urlOrFileName); |
| | | return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(extension); |
| | | }; |
| | | |
| | | // 夿æ¯å¦ä¸ºè§é¢ç±»å |
| | | const isVideoType = (urlOrFileName: string) => { |
| | | const extension = getExtension(urlOrFileName); |
| | | return ["mp4", "mov", "avi", "wmv", "flv", "mkv", "webm"].includes(extension); |
| | | }; |
| | | |
| | | // å¾çå è½½æå |
| | | const onImageLoad = (item: any) => { |
| | | item.loadError = false; |
| | | }; |
| | | |
| | | // å¾çå 载失败 |
| | | const onImageError = (item: any) => { |
| | | console.error("å¾çå 载失败:", item.url); |
| | | item.loadError = true; |
| | | }; |
| | | |
| | | // è§é¢å 载失败 |
| | | const onVideoError = (item: any) => { |
| | | console.error("è§é¢å 载失败:", item.url); |
| | | item.loadError = true; |
| | | }; |
| | | |
| | | // è·åéä»¶å表 |
| | | const getAttachmentList = async (data: any) => { |
| | | try { |
| | |
| | | // é¢è§éä»¶ |
| | | const previewAttachment = (item: any) => { |
| | | // æ ¹æ®æä»¶ç±»åè¿è¡é¢è§ |
| | | const fileName = item.bucketFileName || item.name; |
| | | const fileType = getFileType(fileName); |
| | | const fileType = getFileType(item.url); |
| | | const fullUrl = getFullUrl(item.url); |
| | | |
| | | if (fileType.startsWith("image")) { |
| | | // å¾çé¢è§ |
| | | uni.previewImage({ |
| | | urls: [item.url], |
| | | current: item.url, |
| | | urls: [fullUrl], |
| | | current: fullUrl, |
| | | }); |
| | | } else { |
| | | // å
¶ä»æä»¶ç±»åï¼å¯ä»¥ä¸è½½ææå¼ |
| | | uni.downloadFile({ |
| | | url: item.url, |
| | | url: fullUrl, |
| | | success: (res) => { |
| | | uni.openDocument({ |
| | | filePath: res.tempFilePath, |
| | | success: () => { |
| | | console.log("æå¼ææ¡£æå"); |
| | | // æå¼ææ¡£æå |
| | | }, |
| | | fail: (error) => { |
| | | console.error("æå¼ææ¡£å¤±è´¥:", error); |
| | |
| | | }; |
| | | |
| | | // è·åæä»¶ç±»å |
| | | const getFileType = (fileName: string) => { |
| | | if (!fileName) return "unknown"; |
| | | const extension = fileName.split(".").pop()?.toLowerCase(); |
| | | const getFileType = (urlOrFileName: string) => { |
| | | if (!urlOrFileName) return "unknown"; |
| | | const extension = getExtension(urlOrFileName); |
| | | switch (extension) { |
| | | case "jpg": |
| | | case "jpeg": |
| | |
| | | case "bmp": |
| | | case "webp": |
| | | return "image"; |
| | | case "mp4": |
| | | case "mov": |
| | | case "avi": |
| | | case "wmv": |
| | | case "flv": |
| | | case "mkv": |
| | | case "webm": |
| | | return "video"; |
| | | case "pdf": |
| | | return "pdf"; |
| | | case "doc": |
| | |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: grid; |
| | | grid-template-columns: repeat(3, 1fr); |
| | | gap: 8px; |
| | | |
| | | .attachment-card { |
| | | margin-bottom: 12px; |
| | | border-radius: 4px; |
| | | width: 100%; |
| | | aspect-ratio: 1; |
| | | } |
| | | } |
| | | |
| | | .attachment-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 12px; |
| | | .media-wrapper { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 100%; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | background: #f5f5f5; |
| | | |
| | | .attachment-info { |
| | | flex: 1; |
| | | .media-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-bottom: 4px; |
| | | word-break: break-all; |
| | | } |
| | | .file-icon-wrapper { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 8px; |
| | | text-align: center; |
| | | |
| | | .attachment-meta { |
| | | display: flex; |
| | | gap: 12px; |
| | | .file-name { |
| | | margin-top: 8px; |
| | | font-size: 12px; |
| | | color: #999; |
| | | color: #666; |
| | | word-break: break-all; |
| | | display: -webkit-box; |
| | | -webkit-line-clamp: 2; |
| | | line-clamp: 2; |
| | | -webkit-box-orient: vertical; |
| | | overflow: hidden; |
| | | |
| | | &.error-text { |
| | | color: #ff4757; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .attachment-actions { |
| | | margin-left: 12px; |
| | | |
| | | :deep(.wd-icon) { |
| | | font-size: 20px; |
| | | cursor: pointer; |
| | | } |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: 4px; |
| | | right: 4px; |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 50%; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 10; |
| | | } |
| | | } |
| | | </style> |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="fixed-header"> |
| | | <view class="header-container"> |
| | | <wd-button |
| | | icon="file-add" |
| | | :round="false" |
| | | size="small" |
| | | custom-class="add_btn" |
| | | @click="editList" |
| | | v-if="!isEdit" |
| | | > |
| | | ç¼è¾ |
| | | </wd-button> |
| | | <wd-button |
| | | icon="close" |
| | | type="info" |
| | | :round="false" |
| | | size="small" |
| | | custom-class="add_btn" |
| | | @click="close" |
| | | v-if="isEdit" |
| | | > |
| | | åæ¶ |
| | | </wd-button> |
| | | <wd-button |
| | | icon="check" |
| | | type="success" |
| | | :round="false" |
| | | size="small" |
| | | custom-class="add_btn" |
| | | @click="saveList" |
| | | v-if="isEdit" |
| | | > |
| | | ä¿å |
| | | </wd-button> |
| | | <view class="placeholder"></view> |
| | | <view class="scan-info"> |
| | | <text class="scan-device-text">å½åæ«ç æºå°: {{ scannedDeviceModel || "æªæ«ç " }}</text> |
| | | </view> |
| | | <view class="scan-wrapper" @click="openScan"> |
| | | <wd-icon name="scan" size="24px" color="#0D867F"></wd-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="list"> |
| | | <!-- åºæ¬ä¿¡æ¯æ¨¡å --> |
| | | <wd-row> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "åºæ¬ä¿¡æ¯" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <wd-form-item label="æ¥æ" prop="recordDate"> |
| | | {{ formatDate(recordData.fixedInfo?.recordDate) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="çæ¬¡" prop="workShift"> |
| | | {{ formatValue(recordData.fixedInfo?.workShift) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="åå·è§æ ¼" prop="model"> |
| | | {{ formatValue(recordData.fixedInfo?.model) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="æå线çå·" prop="systemNo"> |
| | | {{ formatValue(recordData.fixedInfo?.systemNo) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="è®°å½äºº" prop="createUserName"> |
| | | {{ formatValue(recordData.fixedInfo?.createUserName) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="æºå°" prop="deviceModel"> |
| | | {{ formatValue(recordData.fixedInfo?.deviceModel) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="产åç±»å«" prop="productType"> |
| | | {{ formatValue(recordData.fixedInfo?.productType) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="ç产é¿åº¦" prop="actuallyLength"> |
| | | {{ formatValue(recordData.fixedInfo?.actuallyLength, "m") }} |
| | | </wd-form-item> |
| | | <wd-form-item label="å¼ å设置" prop="tensionSetting"> |
| | | {{ formatValue(recordData.fixedInfo?.tensionSetting, "N/m") }} |
| | | </wd-form-item> |
| | | <!-- ç»å¶å¤å¾ï¼å¯ç¼è¾ï¼ --> |
| | | <wd-form-item label="ç»åå¤å¾" prop="twistedOuterDiameter" required> |
| | | <template v-if="isEdit"> |
| | | <wd-input |
| | | v-model="formData.twistedOuterDiameter" |
| | | placeholder="请è¾å
¥ç»åå¤å¾ï¼mmï¼" |
| | | type="number" |
| | | /> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(formData.twistedOuterDiameter, "mm") }} |
| | | </template> |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- å·¥èºè®°å½è¯¦æ
模å --> |
| | | <wd-row> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "å·¥èºè®°å½è¯¦æ
" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <wd-form-item label="è®°å½ä½ç½®" prop="recordPosition"> |
| | | {{ recordData.structureInfo?.recordPosition || "-" }} |
| | | </wd-form-item> |
| | | <wd-form-item label="è®°å½äºº" prop="createUserName"> |
| | | {{ recordData.structureInfo?.createUserName || "-" }} |
| | | </wd-form-item> |
| | | <wd-form-item label="ç¶æ" prop="status"> |
| | | <wd-tag custom-class="space" :type="getStatusType(recordData.structureInfo?.status)"> |
| | | {{ getStatusText(recordData.structureInfo?.status) }} |
| | | </wd-tag> |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- ç»ææ£æ¥æ¨¡å --> |
| | | <wd-row> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "ç»ææ£æ¥" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <wd-form-item label="æåç»æ" prop="structureFormula" required> |
| | | <template v-if="isEdit"> |
| | | <wd-input v-model="formData.structureFormula" placeholder="请è¾å
¥æåç»æ" /> |
| | | </template> |
| | | <template v-else> |
| | | {{ formData.structureFormula || "-" }} |
| | | </template> |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- ç»ææ åå¼å宿µï¼å¯ç¼è¾ï¼ --> |
| | | <wd-row v-if="formData.structureItems.length"> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "ç»ææ åå¼å宿µ" }}</text> |
| | | </view> |
| | | <wd-col |
| | | :span="24" |
| | | v-for="(item, index) in formData.structureItems" |
| | | :key="index" |
| | | style="padding-bottom: 10px" |
| | | > |
| | | <wd-form-item |
| | | prop="structureItemsGroup" |
| | | :label="formatValue(item.structureName)" |
| | | label-width="400rpx" |
| | | style="color: red" |
| | | required |
| | | ></wd-form-item> |
| | | <wd-form-item label="æ åå¼" prop="structureValue" required> |
| | | {{ formatValue(item.structureValue) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="宿µæ ¹æ°" prop="actualValue1" required> |
| | | <template v-if="isEdit"> |
| | | <wd-input v-model="item.actualValue1" placeholder="请è¾å
¥å®æµæ ¹æ°" type="number" /> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(item.actualValue1, "æ ¹") }} |
| | | </template> |
| | | </wd-form-item> |
| | | <wd-form-item label="宿µç´å¾" prop="actualValue2" required> |
| | | <template v-if="isEdit"> |
| | | <wd-input |
| | | v-model="item.actualValue2" |
| | | placeholder="请è¾å
¥å®æµç´å¾ï¼mmï¼" |
| | | type="number" |
| | | /> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(item.actualValue2, "mm") }} |
| | | </template> |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- ç»çº¿å·¥èºè´¨éæ§å¶ï¼å¯ç¼è¾ï¼ --> |
| | | <wd-row v-if="formData.inspectTwist.length"> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "ç»çº¿å·¥èºè´¨éæ§å¶" }}</text> |
| | | </view> |
| | | <wd-col |
| | | :span="24" |
| | | v-for="(item, index) in formData.inspectTwist" |
| | | :key="index" |
| | | style="padding-bottom: 10px" |
| | | > |
| | | <wd-form-item |
| | | :label="formatValue(item.twistName)" |
| | | label-width="400rpx" |
| | | style="color: red" |
| | | prop="inspectTwistGroup" |
| | | required |
| | | ></wd-form-item> |
| | | <wd-form-item label="ç»å" prop="direction" required> |
| | | <template v-if="isEdit"> |
| | | <wd-select-picker |
| | | label="" |
| | | v-model="item.direction" |
| | | :columns="twistDirectionOptions" |
| | | type="radio" |
| | | placeholder="è¯·éæ©ç»å" |
| | | :clearable="false" |
| | | ></wd-select-picker> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(item.direction) }} |
| | | </template> |
| | | </wd-form-item> |
| | | <wd-form-item label="èè·" prop="pitch" required> |
| | | <template v-if="isEdit"> |
| | | <wd-input |
| | | v-model="item.pitch" |
| | | placeholder="请è¾å
¥èè·ï¼mmï¼" |
| | | type="number" |
| | | @input="updatePitchRatio(item)" |
| | | /> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(item.pitch, "mm") }} |
| | | </template> |
| | | </wd-form-item> |
| | | <wd-form-item label="è徿¯" prop="pitchRatio" required> |
| | | {{ formatValue(item.pitchRatio) }} |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- å¤è§åç»è®ºï¼å¯ç¼è¾ï¼ --> |
| | | <wd-row> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "å¤è§åç»è®º" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <wd-form-item label="产åå¤è§" prop="productAppearance" required> |
| | | <template v-if="isEdit"> |
| | | <view style="display: flex; flex-wrap: wrap; gap: 10px"> |
| | | <wd-checkbox |
| | | v-for="(opt, idx) in appearanceOptions" |
| | | :key="idx" |
| | | :value="opt.value" |
| | | :modelValue="formData.productAppearance.includes(opt.value)" |
| | | @click="handleAppearanceClick(opt.value)" |
| | | style="width: 100px" |
| | | > |
| | | {{ opt.label }} |
| | | </wd-checkbox> |
| | | </view> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatProductAppearance(formData.productAppearance) }} |
| | | </template> |
| | | </wd-form-item> |
| | | <wd-form-item label="ç»è®º" prop="conclusion" required> |
| | | <template v-if="isEdit"> |
| | | <wd-radio-group v-model="formData.conclusion" inline class="conclusion-radio-group"> |
| | | <wd-radio |
| | | v-for="(opt, idx) in conclusionOptions" |
| | | :key="idx" |
| | | :value="opt.value" |
| | | shape="dot" |
| | | > |
| | | {{ opt.label }} |
| | | </wd-radio> |
| | | </wd-radio-group> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(formData.conclusion) }} |
| | | </template> |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- å·¡æ£ç»ææ¨¡å --> |
| | | <wd-row> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "å·¡æ£ç»æ" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <wd-form-item label="æ ·åæ¯å¦é½å
¨" prop="isFully" required> |
| | | <template v-if="isEdit"> |
| | | <wd-radio-group v-model="formData.isFully" inline class="conclusion-radio-group"> |
| | | <wd-radio |
| | | v-for="(opt, idx) in sampleCompleteOptions" |
| | | :key="idx" |
| | | :value="opt.value" |
| | | shape="dot" |
| | | > |
| | | {{ opt.label }} |
| | | </wd-radio> |
| | | </wd-radio-group> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(formData.isFully) }} |
| | | </template> |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- é件模åï¼å«ä¸ä¼ åè½ï¼ --> |
| | | <wd-row class="attachment-section"> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "éä»¶" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <AttachmentUpload |
| | | :detailData="detailData" |
| | | :isEdit="isEdit" |
| | | :deviceType="paramsType" |
| | | ref="attachmentRef" |
| | | v-if="detailDataLoaded" |
| | | /> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <wd-popup v-model="show" custom-style="border-radius:32rpx;" @close="handleClose"> |
| | | <div class="image-preview"> |
| | | <img :src="previewImageUrl" alt="é¢è§å¾ç" style="width: 100%; height: auto" /> |
| | | </div> |
| | | </wd-popup> |
| | | <wd-toast /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, reactive, computed, onUnmounted } from "vue"; |
| | | import { onLoad, onShow, onHide } from "@dcloudio/uni-app"; |
| | | import RoutingInspectionApi from "@/api/routingInspection/routingInspection"; |
| | | import { useToast } from "wot-design-uni"; |
| | | import AttachmentUpload from "../upload.vue"; |
| | | import { useUserStore } from "@/store/modules/user"; |
| | | import { useScanCode } from "@/composables/useScanCode"; |
| | | |
| | | const paramsType = ref(""); |
| | | const paramsId = ref(""); |
| | | const recordData = ref<any>({ structureInfo: { files: [], structureRecordResult: {} } }); |
| | | const show = ref(false); |
| | | const previewImageUrl = ref(""); |
| | | const isEdit = ref(false); |
| | | const tempFiles = ref<any[]>([]); // 临æ¶å卿°ä¸ä¼ çéä»¶ |
| | | const toast = useToast(); |
| | | const attachmentRef = ref<any>(null); |
| | | const detailData = reactive<any>({}); |
| | | const detailDataLoaded = ref(false); |
| | | |
| | | // è·åå½åç»å½ç¨æ·ä¿¡æ¯ |
| | | const userStore = useUserStore(); |
| | | const userInfo: any = computed(() => userStore.userInfo); |
| | | |
| | | // ä½¿ç¨æ«ç 管ç composableï¼å
¨å±çå¬å¨ï¼ä¸é页é¢åæ¢å
³éï¼ |
| | | const { |
| | | deviceUid, |
| | | deviceModel: scannedDeviceModel, |
| | | loadFromCache, |
| | | enableListener, |
| | | } = useScanCode("scanJX"); |
| | | |
| | | const formData = reactive({ |
| | | twistedOuterDiameter: "", // ç»å¶å¤å¾ |
| | | structureFormula: "", // æåç»æ |
| | | structureItems: [], // ç»ææ åå¼å宿µ |
| | | inspectTwist: [], // ç»çº¿å·¥èºè´¨éæ§å¶ |
| | | productAppearance: [] as string[], // 产åå¤è§ï¼æ¹ä¸ºæ°ç»åå¨éä¸å¼ï¼ |
| | | conclusion: "", // ç»è®ºï¼æ¹ä¸ºæ°ç»åå¨éä¸å¼ï¼ |
| | | isFully: "", // æ ·åæ¯å¦é½å
¨ |
| | | }); |
| | | |
| | | const twistDirectionOptions = [ |
| | | { label: "å·¦å", value: "å·¦å" }, |
| | | { label: "å³å", value: "å³å" }, |
| | | ]; |
| | | |
| | | const appearanceOptions = [ |
| | | { label: "æ å¤è§é®é¢", value: "æ å¤è§é®é¢" }, |
| | | { label: "表é¢å伤", value: "表é¢å伤" }, |
| | | { label: "ç´å¾ä¸å", value: "ç´å¾ä¸å" }, |
| | | { label: "å
¶ä»ç¼ºé·", value: "å
¶ä»ç¼ºé·" }, |
| | | ]; |
| | | |
| | | const conclusionOptions = [ |
| | | { label: "åæ ¼", value: "åæ ¼" }, |
| | | { label: "ä¸åæ ¼", value: "ä¸åæ ¼" }, |
| | | ]; |
| | | const sampleCompleteOptions = [ |
| | | { label: "æ¯", value: "æ¯" }, |
| | | { label: "å¦", value: "å¦" }, |
| | | ]; |
| | | |
| | | const initFormData = () => { |
| | | const structureResult = recordData.value.structureInfo?.structureRecordResult || {}; |
| | | const inspectionResult = recordData.value.inspectionResult || {}; |
| | | |
| | | formData.twistedOuterDiameter = |
| | | recordData.value.structureInfo.structureRecordResult.twistedOuterDiameter || ""; |
| | | formData.structureFormula = structureResult.inspectStructure?.structureFormula || ""; |
| | | formData.isFully = inspectionResult.isFully || ""; |
| | | formData.conclusion = structureResult.conclusion || ""; |
| | | |
| | | // åå§å产åå¤è§ |
| | | const appearance = Array.isArray(structureResult.productAppearance) |
| | | ? structureResult.productAppearance |
| | | : structureResult.productAppearance |
| | | ? [structureResult.productAppearance] |
| | | : []; |
| | | formData.productAppearance = appearance; |
| | | |
| | | formData.structureItems = JSON.parse( |
| | | JSON.stringify(structureResult.inspectStructure?.structureItems || []) |
| | | ); |
| | | formData.inspectTwist = JSON.parse(JSON.stringify(structureResult.inspectTwist || [])); |
| | | |
| | | formData.inspectTwist.forEach((item: any) => { |
| | | if (!item.direction) item.direction = ""; |
| | | }); |
| | | }; |
| | | |
| | | const getDetailData = async (id: string, deviceType: string) => { |
| | | try { |
| | | const response = await RoutingInspectionApi.getStrandedInspectionStructureInfoById({ id }); |
| | | recordData.value = response.data; |
| | | detailData.value = response.data.structureInfo; |
| | | |
| | | // å¦æè®°å½äººä¸ºç©ºï¼é»è®¤è®¾ç½®ä¸ºå½åç»å½ç¨æ· |
| | | if (recordData.value.structureInfo && !recordData.value.structureInfo.createUserName) { |
| | | recordData.value.structureInfo.createUserName = |
| | | userInfo.value?.nickName || userInfo.value?.userName || ""; |
| | | } |
| | | |
| | | console.log("detailData.value", detailData.value); |
| | | tempFiles.value = []; // æ¸
ç©ºä¸´æ¶æä»¶ |
| | | initFormData(); // æ°æ®è¿åååå§å表å |
| | | detailDataLoaded.value = true; // æ°æ®å è½½å®æåï¼æ¸²æåç»ä»¶ |
| | | console.log("ç¶ç»ä»¶-æ°æ®å°±ç»ªåæå°"); |
| | | } catch (error) { |
| | | console.error("è·å详æ
失败:", error); |
| | | uni.showToast({ title: "å 载失败", icon: "error" }); |
| | | } |
| | | }; |
| | | |
| | | // 页é¢å è½½ |
| | | onLoad((options: any) => { |
| | | try { |
| | | paramsId.value = options.id; |
| | | paramsType.value = options.deviceType; |
| | | getDetailData(options.id, options.deviceType); |
| | | } catch (error) { |
| | | console.error("è·å详æ
失败:", error); |
| | | uni.showToast({ title: "å 载失败", icon: "error" }); |
| | | } |
| | | }); |
| | | |
| | | // ç¼è¾æ¨¡å¼åæ¢ |
| | | const editList = () => { |
| | | isEdit.value = true; |
| | | }; |
| | | |
| | | // åæ¶ç¼è¾ï¼é置表åï¼ |
| | | const close = () => { |
| | | isEdit.value = false; |
| | | tempFiles.value = []; |
| | | initFormData(); |
| | | }; |
| | | |
| | | // ä¿åç¼è¾ï¼å«å¿
å¡«é¡¹æ ¡éªï¼ |
| | | const saveList = async () => { |
| | | // 1. åºç¡åæ®µæ ¡éª |
| | | if (!formData.structureFormula) return uni.showToast({ title: "æåç»æä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.twistedOuterDiameter) |
| | | return uni.showToast({ title: "ç»å¶å¤å¾ä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.productAppearance.length) |
| | | return uni.showToast({ title: "产åå¤è§ä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.conclusion) return uni.showToast({ title: "ç»è®ºä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.isFully) return uni.showToast({ title: "æ ·åæ¯å¦é½å
¨ä¸ºå¿
填项", icon: "none" }); |
| | | // 2. ç»æé¡¹å¾ªç¯æ ¡éª |
| | | for (const item of formData.structureItems) { |
| | | if (!item.structureValue) |
| | | return uni.showToast({ title: `${item.structureName}æ åå¼ä¸ºå¿
填项`, icon: "none" }); |
| | | if (!item.actualValue1) |
| | | return uni.showToast({ title: `${item.structureName}宿µæ ¹æ°ä¸ºå¿
填项`, icon: "none" }); |
| | | if (!item.actualValue2) |
| | | return uni.showToast({ title: `${item.structureName}宿µç´å¾ä¸ºå¿
填项`, icon: "none" }); |
| | | } |
| | | |
| | | // 3. ç»çº¿å·¥èºé¡¹å¾ªç¯æ ¡éª |
| | | for (const item of formData.inspectTwist) { |
| | | if (!item.direction) |
| | | return uni.showToast({ title: `${item.twistName}ç»å为å¿
填项`, icon: "none" }); |
| | | if (!item.pitch) return uni.showToast({ title: `${item.twistName}èè·ä¸ºå¿
填项`, icon: "none" }); |
| | | if (!item.pitchRatio) |
| | | return uni.showToast({ title: `${item.twistName}è徿¯ä¸ºå¿
填项`, icon: "none" }); |
| | | } |
| | | // éªè¯æ«ç æ°æ®ï¼ä»ç¼åææ°æ«ç è·åï¼ |
| | | console.log("ä¿ååæ£æ¥ deviceUid:", deviceUid.value); |
| | | if (!deviceUid.value) { |
| | | return uni.showToast({ |
| | | title: "请å
æ«æè®¾å¤äºç»´ç ", |
| | | icon: "none", |
| | | duration: 2000, |
| | | }); |
| | | } |
| | | const { newFiles } = attachmentRef.value.getSubmitFiles(); |
| | | console.log("newFiles", newFiles); |
| | | const allFileIds = [...newFiles]; |
| | | try { |
| | | const res = await RoutingInspectionApi.strandedPatrolCheckInspection({ |
| | | deviceUid: deviceUid.value, |
| | | id: paramsId.value, |
| | | inspectionResult: { |
| | | twistedOuterDiameter: formData.twistedOuterDiameter, |
| | | structureFormula: formData.structureFormula, |
| | | structureItems: formData.structureItems, |
| | | inspectTwist: formData.inspectTwist, |
| | | productAppearance: formData.productAppearance, |
| | | conclusion: formData.conclusion, |
| | | isFully: formData.isFully, |
| | | }, |
| | | result: { |
| | | isFully: formData.isFully, |
| | | }, |
| | | processInspectionAttachmentList: allFileIds, |
| | | }); |
| | | |
| | | if (res.code === 200) { |
| | | // è®¾ç½®å·æ°æ è®°ï¼åè¯å表页éè¦å·æ° |
| | | uni.setStorageSync("needRefreshInspectionList", true); |
| | | |
| | | uni.showToast({ |
| | | title: "ä¿åæå", |
| | | icon: "success", |
| | | duration: 1500, |
| | | }); |
| | | // å»¶è¿è¿åå表页ï¼è®©ç¨æ·çå°æåæç¤º |
| | | setTimeout(() => { |
| | | uni.navigateBack({ |
| | | delta: 1, |
| | | }); |
| | | }, 1500); |
| | | } else { |
| | | uni.showModal({ title: res.msg || "ä¿å失败", icon: "error" }); |
| | | } |
| | | } catch (e) { |
| | | console.error("ä¿å失败:", e); |
| | | uni.showModal({ title: e.message || "ä¿å失败", icon: "error" }); |
| | | } |
| | | }; |
| | | |
| | | const handleClose = () => { |
| | | show.value = false; |
| | | }; |
| | | |
| | | // ç¶æç±»åæ å° |
| | | const getStatusType = (status: number) => { |
| | | switch (status) { |
| | | case 0: |
| | | return "warning"; // å¾
å·¡æ£ |
| | | case 1: |
| | | return "danger"; // 已驳å |
| | | case 2: |
| | | return "primary"; // å¾
å®¡æ ¸ |
| | | case 3: |
| | | return "success"; // éè¿ |
| | | default: |
| | | return "default"; |
| | | } |
| | | }; |
| | | |
| | | // ç¶æææ¬æ å° |
| | | const getStatusText = (status: number) => { |
| | | switch (status) { |
| | | case 0: |
| | | return "å¾
å·¡æ£"; |
| | | case 1: |
| | | return "已驳å"; |
| | | case 2: |
| | | return "å¾
å®¡æ ¸"; |
| | | case 3: |
| | | return "éè¿"; |
| | | default: |
| | | return "æªç¥"; |
| | | } |
| | | }; |
| | | |
| | | // æ ¼å¼å产åå¤è§æ¾ç¤º |
| | | const formatProductAppearance = (productAppearance: string[]) => { |
| | | if (!productAppearance || productAppearance.length === 0) return "-"; |
| | | return productAppearance.join("ã"); |
| | | }; |
| | | |
| | | // æ ¼å¼åæ°å¼æ¾ç¤º |
| | | const formatValue = (value: any, unit?: string) => { |
| | | if (value === null || value === undefined || value === "") return "-"; |
| | | return unit ? `${value}${unit}` : value; |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¾ç¤º |
| | | const formatDate = (date: string) => { |
| | | if (!date) return "-"; |
| | | return new Date(date).toLocaleDateString("zh-CN", { |
| | | year: "numeric", |
| | | month: "2-digit", |
| | | day: "2-digit", |
| | | }); |
| | | }; |
| | | |
| | | // 计ç®è徿¯ |
| | | const calculatePitchRatio = (pitch: string, dia: string) => { |
| | | // 妿pitchædia为空ï¼åè¿å"-" |
| | | if (!pitch || !dia) return "-"; |
| | | |
| | | // å°pitchådia转æ¢ä¸ºæµ®ç¹æ° |
| | | const pitchNum = parseFloat(pitch); |
| | | const diaNum = parseFloat(dia); |
| | | |
| | | // 妿pitchNumædiaNumæ¯NaNï¼æè
diaNum为0ï¼åè¿å"-" |
| | | if (isNaN(pitchNum) || isNaN(diaNum) || diaNum === 0) return "-"; |
| | | |
| | | // 计ç®pitchNumådiaNumçæ¯å¼ï¼å¹¶ä¿ç两ä½å°æ° |
| | | return (pitchNum / diaNum).toFixed(2); |
| | | }; |
| | | |
| | | // æ´æ°è徿¯ï¼å½èè·ååæ¶èªå¨è®¡ç®ï¼ |
| | | const updatePitchRatio = (item: any) => { |
| | | // 使ç¨ç»åå¤å¾ä½ä¸ºç´å¾æ¥è®¡ç®è徿¯ |
| | | const dia = item.dia; |
| | | item.pitchRatio = calculatePitchRatio(item.pitch, dia); |
| | | }; |
| | | |
| | | // å¤ç产åå¤è§éæ©çäºæ¥é»è¾ |
| | | const handleAppearanceClick = (value: string) => { |
| | | const currentValues = [...formData.productAppearance]; |
| | | const isCurrentlyChecked = currentValues.includes(value); |
| | | |
| | | let newSelection: string[] = []; |
| | | |
| | | if (value === "æ å¤è§é®é¢") { |
| | | if (isCurrentlyChecked) { |
| | | // åæ¶éä¸"æ å¤è§é®é¢" |
| | | newSelection = []; |
| | | } else { |
| | | // éä¸"æ å¤è§é®é¢"ï¼æ¸
空å
¶ä»é项 |
| | | newSelection = ["æ å¤è§é®é¢"]; |
| | | } |
| | | } else { |
| | | // ç¹å»å
¶ä»é项 |
| | | if (isCurrentlyChecked) { |
| | | // åæ¶éä¸è¯¥é项 |
| | | newSelection = currentValues.filter((v) => v !== value); |
| | | } else { |
| | | // éä¸è¯¥é项ï¼ç§»é¤"æ å¤è§é®é¢" |
| | | const filteredValues = currentValues.filter((v) => v !== "æ å¤è§é®é¢"); |
| | | newSelection = [...filteredValues, value]; |
| | | } |
| | | } |
| | | |
| | | formData.productAppearance = newSelection; |
| | | }; |
| | | |
| | | const openScan = () => { |
| | | console.log("indexJX - ç¹å»æ«ç æé®ï¼å
¨å±æ«ç 模å¼ï¼æ éæå¨è§¦åï¼"); |
| | | // å
¨å±æ«ç 模å¼ä¸ï¼ç¡¬ä»¶æ«ç ä¼èªå¨è§¦åï¼æ éæå¨è°ç¨ |
| | | uni.showToast({ |
| | | title: "è¯·ä½¿ç¨æ«ç æªæ«æ", |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 页颿¾ç¤ºæ¶çå¤ç |
| | | onShow(() => { |
| | | console.log("========== indexJX - onShow 触å =========="); |
| | | // éæ°å¯ç¨çå¬å¨ï¼ç¡®ä¿çå¬å¨ææï¼ |
| | | enableListener(); |
| | | // å è½½ç¼åï¼æ´æ°UIæ¾ç¤ºï¼ |
| | | const cachedData = loadFromCache(); |
| | | |
| | | // å¦ææ²¡æç¼åæ°æ®ï¼æç¤ºç¨æ·éè¦æ«ç |
| | | if (!cachedData || !cachedData.uid) { |
| | | console.log("â ï¸ æªæ£æµå°æ«ç ç¼åï¼ç¨æ·éè¦æ«æè®¾å¤äºç»´ç "); |
| | | // å¨ç¼è¾æ¨¡å¼ä¸ææç¤º |
| | | if (isEdit.value) { |
| | | setTimeout(() => { |
| | | uni.showToast({ |
| | | title: "è¯·æ«æè®¾å¤äºç»´ç ååä¿å", |
| | | icon: "none", |
| | | duration: 2000, |
| | | }); |
| | | }, 500); |
| | | } |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .fixed-header { |
| | | position: fixed; |
| | | top: 44; |
| | | left: 0; |
| | | right: 0; |
| | | background: #f3f9f8; |
| | | z-index: 999; |
| | | padding: 12px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | min-height: 60px; |
| | | box-sizing: border-box; |
| | | overflow: visible; |
| | | } |
| | | |
| | | .header-container { |
| | | display: flex; |
| | | align-items: center; |
| | | width: 100%; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .placeholder { |
| | | flex: 1; |
| | | } |
| | | |
| | | .scan-info { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-right: 10px; |
| | | |
| | | .scan-device-text { |
| | | font-size: 14px; |
| | | color: #0d867f; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | |
| | | .scan-wrapper { |
| | | width: 38px; |
| | | height: 38px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 6px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .list { |
| | | padding: 12px; |
| | | padding-top: 84px; |
| | | background: #f3f9f8; |
| | | min-height: 100vh; |
| | | box-sizing: border-box; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .title { |
| | | position: relative; |
| | | margin-left: 10px; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #0d867f; |
| | | } |
| | | |
| | | .title::after { |
| | | position: absolute; |
| | | content: ""; |
| | | top: 4px; |
| | | left: -10px; |
| | | width: 4px; |
| | | height: 16px; |
| | | background: #0d867f; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | // 产åå¤è§åç»è®ºéæ©å¨æ ·å¼ï¼ä¸è¡ä¸¤ä¸ªï¼ |
| | | .checkbox-group { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 16rpx; |
| | | padding: 8rpx 0; |
| | | } |
| | | |
| | | .checkbox-item { |
| | | width: calc(50% - 8rpx); |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | // éä»¶ç¸å
³æ ·å¼ |
| | | .attachment-section { |
| | | width: 100%; |
| | | } |
| | | |
| | | .attachment-grid { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .attachment-item { |
| | | width: calc(25% - 10px); |
| | | box-sizing: border-box; |
| | | position: relative; |
| | | } |
| | | |
| | | .upload-btn { |
| | | width: 80px; |
| | | height: 80px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 1px dashed #ccc; |
| | | border-radius: 4px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .upload-icon { |
| | | font-size: 32px; |
| | | color: #0d867f; |
| | | } |
| | | |
| | | // éä»¶å é¤å¾æ |
| | | .delete-icon { |
| | | position: absolute; |
| | | top: -8px; |
| | | right: -8px; |
| | | width: 24px; |
| | | height: 24px; |
| | | background-color: rgba(255, 0, 0, 0.8); |
| | | color: white; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 10; |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .attachment-item { |
| | | width: calc(25% - 10px); |
| | | } |
| | | } |
| | | |
| | | // ç¼è¾æ¨¡å¼ä¸è¡¨åç»ä»¶æ ·å¼ä¼å |
| | | :deep(.wd-form-item) { |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | :deep(.wd-input, .wd-select, .wd-radio-group, .wd-checkbox-group) { |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | :deep(.wd-form-item__label) { |
| | | &::after { |
| | | content: "*"; |
| | | color: red; |
| | | margin-left: 4rpx; |
| | | } |
| | | } |
| | | |
| | | // ä¿®å¤éæ©å¨æ ·å¼ |
| | | :deep(.wd-select) { |
| | | width: 100%; |
| | | } |
| | | |
| | | :deep(.wd-checkbox) { |
| | | margin-right: 0; |
| | | } |
| | | .conclusion-radio-group { |
| | | display: flex; |
| | | align-items: flex-start; // åç´æ¹åé¡¶é¨å¯¹é½ï¼ä¸ç§»å
³é®ï¼ |
| | | gap: 20rpx; // é项ä¹é´çé´è· |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="fixed-header"> |
| | | <view class="header-container"> |
| | | <wd-button |
| | | icon="file-add" |
| | | :round="false" |
| | | size="small" |
| | | custom-class="add_btn" |
| | | @click="editList" |
| | | v-if="!isEdit" |
| | | > |
| | | ç¼è¾ |
| | | </wd-button> |
| | | <wd-button |
| | | icon="close" |
| | | type="info" |
| | | :round="false" |
| | | size="small" |
| | | custom-class="add_btn" |
| | | @click="close" |
| | | v-if="isEdit" |
| | | > |
| | | åæ¶ |
| | | </wd-button> |
| | | <wd-button |
| | | icon="check" |
| | | type="success" |
| | | :round="false" |
| | | size="small" |
| | | custom-class="add_btn" |
| | | @click="saveList" |
| | | v-if="isEdit" |
| | | > |
| | | ä¿å |
| | | </wd-button> |
| | | <view class="placeholder"></view> |
| | | <view class="scan-info"> |
| | | <text class="scan-device-text">å½åæ«ç æºå°: {{ scannedDeviceModel || "æªæ«ç " }}</text> |
| | | </view> |
| | | <view class="scan-wrapper" @click="openScan"> |
| | | <wd-icon name="scan" size="24px" color="#0D867F"></wd-icon> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="list"> |
| | | <!-- åºæ¬ä¿¡æ¯æ¨¡å --> |
| | | <wd-row> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "åºæ¬ä¿¡æ¯" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <wd-form-item label="æ¥æ" prop="recordDate"> |
| | | {{ formatDate(detailData.fixedInfo?.recordDate) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="æºå°" prop="deviceModel"> |
| | | {{ formatValue(detailData.fixedInfo?.deviceModel) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="çæ¬¡" prop="workShift"> |
| | | {{ formatValue(detailData.fixedInfo?.workShift) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="çç»" prop="teamName"> |
| | | {{ formatValue(detailData.fixedInfo?.teamName) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="åä¸è§æ ¼" prop="model"> |
| | | {{ formatValue(detailData.fixedInfo?.model) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="ç产轴æ°" prop="outputNumber"> |
| | | {{ formatValue(detailData.fixedInfo?.outputNumber, "è½´") }} |
| | | </wd-form-item> |
| | | <wd-form-item label="åå·" prop="poleModel"> |
| | | {{ formatValue(detailData.fixedInfo?.poleModel) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="æ¹æ¬¡" prop="poleNumber"> |
| | | {{ formatValue(detailData.fixedInfo?.poleNumber) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="è®°å½äºº" prop="createUserName"> |
| | | {{ formatValue(detailData.fixedInfo?.createUserName) }} |
| | | </wd-form-item> |
| | | <wd-form-item label="馿£çå·" prop="firstNo"> |
| | | {{ formatValue(detailData.fixedInfo?.firstNo) }} |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- å·¥èºè®°å½è¯¦æ
模å --> |
| | | <wd-row> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "å·¥èºè®°å½è¯¦æ
" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <wd-form-item label="å·¡æ£å" prop="processInspectionUserName"> |
| | | {{ detailData.processInspectionUserName || "-" }} |
| | | </wd-form-item> |
| | | <wd-form-item label="ç¶æ" prop="status"> |
| | | <wd-tag custom-class="space" :type="getStatusType(detailData.status)"> |
| | | {{ getStatusText(detailData.status) }} |
| | | </wd-tag> |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- æ£éªç»æ --> |
| | | <wd-row> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "æ£éªç»æ" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <wd-form-item label="åä¸ç´å¾" prop="dia"> |
| | | {{ formatValue(detailData.inspectionResult?.dia, "mm") || "-" }} |
| | | </wd-form-item> |
| | | |
| | | <wd-form-item label="æå¤§ç´å¾" prop="maxDia" required> |
| | | <template v-if="isEdit"> |
| | | <wd-input v-model="formData.maxDia" placeholder="请è¾å
¥æå¤§ç´å¾(mm)" type="number" /> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(detailData.inspectionResult?.maxDia, "mm") || "-" }} |
| | | </template> |
| | | </wd-form-item> |
| | | |
| | | <wd-form-item label="æå°ç´å¾" prop="minDia" required> |
| | | <template v-if="isEdit"> |
| | | <wd-input v-model="formData.minDia" placeholder="请è¾å
¥æå°ç´å¾(mm)" type="number" /> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(detailData.inspectionResult?.minDia, "mm") || "-" }} |
| | | </template> |
| | | </wd-form-item> |
| | | |
| | | <wd-form-item label="å¤è§" prop="appearance" required> |
| | | <template v-if="isEdit"> |
| | | <view style="display: flex; flex-wrap: wrap; gap: 10px"> |
| | | <wd-checkbox |
| | | v-for="(opt, idx) in appearanceOptions" |
| | | :key="idx" |
| | | :value="opt.value" |
| | | :modelValue="formData.appearance?.includes(opt.value) || false" |
| | | @click="handleAppearanceClick(opt.value)" |
| | | style="width: 100px" |
| | | > |
| | | {{ opt.label }} |
| | | </wd-checkbox> |
| | | </view> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatProductAppearance(formData.appearance) }} |
| | | </template> |
| | | </wd-form-item> |
| | | |
| | | <wd-form-item label="å·ç»ç´§å¯" prop="windingTightness" required> |
| | | <template v-if="isEdit"> |
| | | <wd-radio-group |
| | | v-model="formData.windingTightness" |
| | | inline |
| | | class="conclusion-radio-group" |
| | | > |
| | | <wd-radio |
| | | v-for="(opt, idx) in sampleCompleteOptions" |
| | | :key="idx" |
| | | :value="opt.value" |
| | | shape="dot" |
| | | > |
| | | {{ opt.label }} |
| | | </wd-radio> |
| | | </wd-radio-group> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(detailData.inspectionResult?.windingTightness) }} |
| | | </template> |
| | | </wd-form-item> |
| | | |
| | | <wd-form-item label="æåæ´é½" prop="arrangementNeatness" required> |
| | | <template v-if="isEdit"> |
| | | <wd-radio-group |
| | | v-model="formData.arrangementNeatness" |
| | | inline |
| | | class="conclusion-radio-group" |
| | | > |
| | | <wd-radio |
| | | v-for="(opt, idx) in sampleCompleteOptions" |
| | | :key="idx" |
| | | :value="opt.value" |
| | | shape="dot" |
| | | > |
| | | {{ opt.label }} |
| | | </wd-radio> |
| | | </wd-radio-group> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(detailData.inspectionResult?.arrangementNeatness) }} |
| | | </template> |
| | | </wd-form-item> |
| | | |
| | | <wd-form-item |
| | | label="å¤å±é线离侧æ¿è¾¹ç¼è·ç¦»" |
| | | prop="aluminumWireDistance" |
| | | label-width="360rpx" |
| | | required |
| | | > |
| | | <template v-if="isEdit"> |
| | | <wd-input |
| | | v-model="formData.aluminumWireDistance" |
| | | placeholder="请è¾å
¥è·ç¦»(mm)" |
| | | type="number" |
| | | /> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(detailData.inspectionResult?.aluminumWireDistance, "mm") || "-" }} |
| | | </template> |
| | | </wd-form-item> |
| | | |
| | | <wd-form-item label="æåæ¨¡åæ¥å¤´æ
åµ" prop="jointCondition" label-width="280rpx" required> |
| | | <template v-if="isEdit"> |
| | | <wd-radio-group v-model="formData.jointCondition" inline class="conclusion-radio-group"> |
| | | <wd-radio |
| | | v-for="(opt, idx) in jointConditionOptions" |
| | | :key="idx" |
| | | :value="opt.value" |
| | | shape="dot" |
| | | > |
| | | {{ opt.label }} |
| | | </wd-radio> |
| | | </wd-radio-group> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(detailData.inspectionResult?.jointCondition) || "-" }} |
| | | </template> |
| | | </wd-form-item> |
| | | |
| | | <wd-form-item label="ç»è®º" prop="conclusion" required> |
| | | <template v-if="isEdit"> |
| | | <wd-radio-group v-model="formData.conclusion" inline class="conclusion-radio-group"> |
| | | <wd-radio |
| | | v-for="(opt, idx) in conclusionOptions" |
| | | :key="idx" |
| | | :value="opt.value" |
| | | shape="dot" |
| | | > |
| | | {{ opt.label }} |
| | | </wd-radio> |
| | | </wd-radio-group> |
| | | </template> |
| | | <template v-else> |
| | | {{ formatValue(detailData.inspectionResult?.conclusion) || "-" }} |
| | | </template> |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- å·¡æ£ç»æ --> |
| | | <wd-row> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "å·¡æ£ç»æ" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <wd-form-item |
| | | label="éæåãä¸ãå°¾æ ·åæ¯å¦é½å
¨" |
| | | prop="isFully" |
| | | required |
| | | label-width="420rpx" |
| | | > |
| | | <template v-if="isEdit"> |
| | | <wd-radio-group v-model="formData.isFully" inline class="conclusion-radio-group"> |
| | | <wd-radio |
| | | v-for="(opt, idx) in sampleCompleteOptions" |
| | | :key="idx" |
| | | :value="opt.value" |
| | | shape="dot" |
| | | > |
| | | {{ opt.label }} |
| | | </wd-radio> |
| | | </wd-radio-group> |
| | | </template> |
| | | <template v-else> |
| | | <wd-tag |
| | | custom-class="space" |
| | | :type="detailData.processInspectionResult?.isFully ? 'success' : 'danger'" |
| | | > |
| | | {{ detailData.processInspectionResult?.isFully ? "æ¯" : "å¦" }} |
| | | </wd-tag> |
| | | </template> |
| | | </wd-form-item> |
| | | </wd-col> |
| | | </wd-row> |
| | | |
| | | <!-- é件模å --> |
| | | <wd-row class="attachment-section"> |
| | | <view style="margin: 10rpx"> |
| | | <text class="title">{{ "éä»¶" }}</text> |
| | | </view> |
| | | <wd-col :span="24"> |
| | | <AttachmentUpload |
| | | :detailData="detailData" |
| | | :isEdit="isEdit" |
| | | :deviceType="paramsType" |
| | | ref="attachmentRef" |
| | | /> |
| | | </wd-col> |
| | | </wd-row> |
| | | <wd-popup v-model="show" custom-style="border-radius:32rpx;" @close="handleClose"> |
| | | <div class="image-preview"> |
| | | <img :src="previewImageUrl" alt="é¢è§å¾ç" style="width: 100%; height: auto" /> |
| | | </div> |
| | | </wd-popup> |
| | | <wd-toast /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, reactive, computed, onUnmounted } from "vue"; |
| | | import { onLoad, onShow, onHide } from "@dcloudio/uni-app"; |
| | | import RoutingInspectionApi from "@/api/routingInspection/routingInspection"; |
| | | import { useToast } from "wot-design-uni"; |
| | | import AttachmentUpload from "../upload.vue"; |
| | | import { useUserStore } from "@/store/modules/user"; |
| | | import { useScanCode } from "@/composables/useScanCode"; |
| | | |
| | | // æ ¸å¿ç¶æ |
| | | const paramsId = ref(""); |
| | | const paramsType = ref(""); |
| | | const detailData = ref<any>({}); |
| | | const show = ref(false); |
| | | const previewImageUrl = ref(""); |
| | | const isEdit = ref(false); |
| | | const tempFiles = ref<any[]>([]); |
| | | const toast = useToast(); |
| | | const attachmentRef = ref<any>(null); |
| | | |
| | | // è·åå½åç»å½ç¨æ·ä¿¡æ¯ |
| | | const userStore = useUserStore(); |
| | | const userInfo: any = computed(() => userStore.userInfo); |
| | | |
| | | // ä½¿ç¨æ«ç 管ç composableï¼å
¨å±çå¬å¨ï¼ä¸é页é¢åæ¢å
³éï¼ |
| | | const { |
| | | deviceUid, |
| | | deviceModel: scannedDeviceModel, |
| | | loadFromCache, |
| | | enableListener, |
| | | } = useScanCode("scanLS"); |
| | | |
| | | // è¡¨åæ°æ® |
| | | const formData = reactive({ |
| | | dia: "", |
| | | maxDia: "", |
| | | minDia: "", |
| | | appearance: [] as string[], |
| | | windingTightness: "", |
| | | arrangementNeatness: "", |
| | | aluminumWireDistance: "", |
| | | jointCondition: "", |
| | | conclusion: "", |
| | | isFully: "", |
| | | }); |
| | | |
| | | // å¤è§é项 |
| | | const appearanceOptions = [ |
| | | { label: "æ å¤è§é®é¢", value: "æ å¤è§é®é¢" }, |
| | | { label: "表é¢å伤", value: "表é¢å伤" }, |
| | | { label: "ç´å¾ä¸å", value: "ç´å¾ä¸å" }, |
| | | { label: "å
¶ä»ç¼ºé·", value: "å
¶ä»ç¼ºé·" }, |
| | | ]; |
| | | const sampleCompleteOptions = [ |
| | | { label: "æ¯", value: "æ¯" }, |
| | | { label: "å¦", value: "å¦" }, |
| | | ]; |
| | | const jointConditionOptions = [ |
| | | { label: "æ", value: "æ" }, |
| | | { label: "æ ", value: "æ " }, |
| | | ]; |
| | | const conclusionOptions = [ |
| | | { label: "åæ ¼", value: "åæ ¼" }, |
| | | { label: "ä¸åæ ¼", value: "ä¸åæ ¼" }, |
| | | ]; |
| | | // ç¶ææ å° |
| | | const getStatusType = (status: number) => { |
| | | switch (status) { |
| | | case 0: |
| | | return "warning"; |
| | | case 1: |
| | | return "danger"; |
| | | case 2: |
| | | return "primary"; |
| | | case 3: |
| | | return "success"; |
| | | default: |
| | | return "default"; |
| | | } |
| | | }; |
| | | |
| | | const getStatusText = (status: number) => { |
| | | switch (status) { |
| | | case 0: |
| | | return "å¾
å·¡æ£"; |
| | | case 1: |
| | | return "已驳å"; |
| | | case 2: |
| | | return "å¾
å®¡æ ¸"; |
| | | case 3: |
| | | return "éè¿"; |
| | | default: |
| | | return "æªç¥"; |
| | | } |
| | | }; |
| | | |
| | | // æ ¼å¼åå·¥å
· |
| | | const formatProductAppearance = (productAppearance: string[]) => { |
| | | if (!productAppearance || !Array.isArray(productAppearance) || !productAppearance.length) { |
| | | return "-"; |
| | | } |
| | | return productAppearance.join("ã"); |
| | | }; |
| | | |
| | | // å¤çå¤è§éæ©çäºæ¥é»è¾ |
| | | const handleAppearanceClick = (value: string) => { |
| | | // ç¡®ä¿ appearance æ¯æ°ç» |
| | | if (!Array.isArray(formData.appearance)) { |
| | | formData.appearance = []; |
| | | } |
| | | |
| | | const currentValues = [...formData.appearance]; |
| | | const isCurrentlyChecked = currentValues.includes(value); |
| | | |
| | | let newSelection: string[] = []; |
| | | |
| | | if (value === "æ å¤è§é®é¢") { |
| | | if (isCurrentlyChecked) { |
| | | // åæ¶éä¸"æ å¤è§é®é¢" |
| | | newSelection = []; |
| | | } else { |
| | | // éä¸"æ å¤è§é®é¢"ï¼æ¸
空å
¶ä»é项 |
| | | newSelection = ["æ å¤è§é®é¢"]; |
| | | } |
| | | } else { |
| | | // ç¹å»å
¶ä»é项 |
| | | if (isCurrentlyChecked) { |
| | | // åæ¶éä¸è¯¥é项 |
| | | newSelection = currentValues.filter((v) => v !== value); |
| | | } else { |
| | | // éä¸è¯¥é项ï¼ç§»é¤"æ å¤è§é®é¢" |
| | | const filteredValues = currentValues.filter((v) => v !== "æ å¤è§é®é¢"); |
| | | newSelection = [...filteredValues, value]; |
| | | } |
| | | } |
| | | |
| | | formData.appearance = newSelection; |
| | | }; |
| | | |
| | | const formatValue = (value: any, unit?: string) => { |
| | | if (value === null || value === undefined || value === "") return "-"; |
| | | return unit ? `${value}${unit}` : value; |
| | | }; |
| | | |
| | | const formatDate = (date: string) => { |
| | | if (!date) return "-"; |
| | | return new Date(date).toLocaleDateString("zh-CN", { |
| | | year: "numeric", |
| | | month: "2-digit", |
| | | day: "2-digit", |
| | | }); |
| | | }; |
| | | |
| | | // åå§å表å |
| | | const initFormData = () => { |
| | | const inspectionResult = detailData.value.inspectionResult || {}; |
| | | const processInspectionResult = detailData.value.processInspectionResult || {}; |
| | | formData.dia = inspectionResult.dia || ""; |
| | | formData.maxDia = inspectionResult.maxDia || ""; |
| | | formData.minDia = inspectionResult.minDia || ""; |
| | | // ç¡®ä¿ appearance æ¯æ°ç» |
| | | formData.appearance = Array.isArray(inspectionResult.appearance) |
| | | ? inspectionResult.appearance |
| | | : inspectionResult.appearance |
| | | ? [inspectionResult.appearance] |
| | | : []; |
| | | formData.windingTightness = inspectionResult.windingTightness || ""; |
| | | formData.arrangementNeatness = inspectionResult.arrangementNeatness || ""; |
| | | formData.aluminumWireDistance = inspectionResult.aluminumWireDistance || ""; |
| | | formData.jointCondition = inspectionResult.jointCondition || ""; |
| | | formData.conclusion = inspectionResult.conclusion || ""; |
| | | formData.isFully = processInspectionResult.isFully ? "æ¯" : "å¦"; |
| | | }; |
| | | |
| | | // è·å详æ
|
| | | const getDetailData = async (id: string, deviceType: string) => { |
| | | try { |
| | | const response = await RoutingInspectionApi.getDrawInspectInfoById({ id }); |
| | | detailData.value = response.data; |
| | | |
| | | // 妿巡æ£å为空ï¼é»è®¤è®¾ç½®ä¸ºå½åç»å½ç¨æ· |
| | | if (!detailData.value.processInspectionUserName) { |
| | | detailData.value.processInspectionUserName = |
| | | userInfo.value?.nickName || userInfo.value?.userName || ""; |
| | | } |
| | | |
| | | tempFiles.value = []; |
| | | initFormData(); |
| | | } catch (error) { |
| | | console.error("è·å详æ
失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // 页é¢å è½½ |
| | | onLoad((options: any) => { |
| | | paramsId.value = options.id; |
| | | paramsType.value = options.deviceType; |
| | | getDetailData(options.id, options.deviceType); |
| | | }); |
| | | |
| | | // ç¼è¾åæ¢ |
| | | const editList = () => { |
| | | isEdit.value = true; |
| | | }; |
| | | |
| | | // åæ¶ç¼è¾ |
| | | const close = () => { |
| | | isEdit.value = false; |
| | | tempFiles.value = []; |
| | | initFormData(); |
| | | }; |
| | | |
| | | // ä¿åç¼è¾ |
| | | const saveList = async () => { |
| | | // æ ¡éª |
| | | if (!formData.maxDia) return uni.showToast({ title: "æå¤§ç´å¾ä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.minDia) return uni.showToast({ title: "æå°ç´å¾ä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.appearance.length) return uni.showToast({ title: "å¤è§ä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.windingTightness) return uni.showToast({ title: "å·ç»ç´§å¯ä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.arrangementNeatness) |
| | | return uni.showToast({ title: "æåæ´é½ä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.aluminumWireDistance) |
| | | return uni.showToast({ title: "å¤å±é线离侧æ¿è¾¹ç¼è·ç¦»ä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.jointCondition) |
| | | return uni.showToast({ title: "æåæ¨¡åæ¥å¤´æ
åµä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.conclusion) return uni.showToast({ title: "ç»è®ºä¸ºå¿
填项", icon: "none" }); |
| | | if (!formData.isFully) return uni.showToast({ title: "éææ ·åæ¯å¦é½å
¨ä¸ºå¿
填项", icon: "none" }); |
| | | |
| | | // éªè¯æ«ç æ°æ®ï¼ä»ç¼åææ°æ«ç è·åï¼ |
| | | console.log("ä¿ååæ£æ¥ deviceUid:", deviceUid.value); |
| | | if (!deviceUid.value) { |
| | | return uni.showToast({ |
| | | title: "请å
æ«æè®¾å¤äºç»´ç ", |
| | | icon: "none", |
| | | duration: 2000, |
| | | }); |
| | | } |
| | | const { newFiles } = attachmentRef.value.getSubmitFiles(); |
| | | console.log("newFiles", newFiles); |
| | | const allFileIds = [...newFiles]; |
| | | // æäº¤ |
| | | try { |
| | | const res = await RoutingInspectionApi.drawPatrolCheckInspection({ |
| | | deviceUid: deviceUid.value, |
| | | id: paramsId.value, |
| | | inspectionResult: { |
| | | dia: formData.dia, |
| | | maxDia: formData.maxDia, |
| | | minDia: formData.minDia, |
| | | appearance: formData.appearance, |
| | | windingTightness: formData.windingTightness, |
| | | arrangementNeatness: formData.arrangementNeatness, |
| | | aluminumWireDistance: formData.aluminumWireDistance, |
| | | jointCondition: formData.jointCondition, |
| | | conclusion: formData.conclusion, |
| | | }, |
| | | result: { isFully: formData.isFully }, |
| | | processInspectionAttachmentList: allFileIds, |
| | | }); |
| | | if (res.code === 200) { |
| | | // è®¾ç½®å·æ°æ è®°ï¼åè¯å表页éè¦å·æ° |
| | | uni.setStorageSync("needRefreshInspectionList", true); |
| | | |
| | | uni.showToast({ |
| | | title: "ä¿åæå", |
| | | icon: "success", |
| | | duration: 1500, |
| | | }); |
| | | // å»¶è¿è¿åå表页ï¼è®©ç¨æ·çå°æåæç¤º |
| | | setTimeout(() => { |
| | | uni.navigateBack({ |
| | | delta: 1, |
| | | }); |
| | | }, 1500); |
| | | } else { |
| | | uni.showModal({ title: res.msg || "ä¿å失败", icon: "error" }); |
| | | } |
| | | } catch (e) { |
| | | console.error("ä¿å失败:", e); |
| | | uni.showModal({ title: e.message || "ä¿å失败", icon: "error" }); |
| | | } |
| | | }; |
| | | |
| | | const handleClose = () => { |
| | | show.value = false; |
| | | }; |
| | | |
| | | const openScan = () => { |
| | | console.log("indexLS - ç¹å»æ«ç æé®ï¼å
¨å±æ«ç 模å¼ï¼æ éæå¨è§¦åï¼"); |
| | | // å
¨å±æ«ç 模å¼ä¸ï¼ç¡¬ä»¶æ«ç ä¼èªå¨è§¦åï¼æ éæå¨è°ç¨ |
| | | uni.showToast({ |
| | | title: "è¯·ä½¿ç¨æ«ç æªæ«æ", |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 页颿¾ç¤ºæ¶çå¤ç |
| | | onShow(() => { |
| | | console.log("========== indexLS - onShow 触å =========="); |
| | | // éæ°å¯ç¨çå¬å¨ï¼ç¡®ä¿çå¬å¨ææï¼ |
| | | enableListener(); |
| | | // å è½½ç¼åï¼æ´æ°UIæ¾ç¤ºï¼ |
| | | const cachedData = loadFromCache(); |
| | | |
| | | // å¦ææ²¡æç¼åæ°æ®ï¼æç¤ºç¨æ·éè¦æ«ç |
| | | if (!cachedData || !cachedData.uid) { |
| | | console.log("â ï¸ æªæ£æµå°æ«ç ç¼åï¼ç¨æ·éè¦æ«æè®¾å¤äºç»´ç "); |
| | | // å¨ç¼è¾æ¨¡å¼ä¸ææç¤º |
| | | if (isEdit.value) { |
| | | setTimeout(() => { |
| | | uni.showToast({ |
| | | title: "è¯·æ«æè®¾å¤äºç»´ç ååä¿å", |
| | | icon: "none", |
| | | duration: 2000, |
| | | }); |
| | | }, 500); |
| | | } |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .fixed-header { |
| | | position: fixed; |
| | | top: 44; |
| | | left: 0; |
| | | right: 0; |
| | | background: #f3f9f8; |
| | | z-index: 999; |
| | | padding: 12px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | min-height: 60px; |
| | | box-sizing: border-box; |
| | | overflow: visible; |
| | | } |
| | | |
| | | .header-container { |
| | | display: flex; |
| | | align-items: center; |
| | | width: 100%; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .placeholder { |
| | | flex: 1; |
| | | } |
| | | |
| | | .scan-info { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-right: 10px; |
| | | |
| | | .scan-device-text { |
| | | font-size: 14px; |
| | | color: #0d867f; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | |
| | | .scan-wrapper { |
| | | width: 38px; |
| | | height: 38px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 6px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .list { |
| | | padding: 12px; |
| | | padding-top: 84px; |
| | | background: #f3f9f8; |
| | | min-height: 100vh; |
| | | box-sizing: border-box; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .title { |
| | | position: relative; |
| | | margin-left: 10px; |
| | | font-size: 16px; |
| | | font-weight: 500; |
| | | color: #0d867f; |
| | | } |
| | | |
| | | .title::after { |
| | | position: absolute; |
| | | content: ""; |
| | | top: 4px; |
| | | left: -10px; |
| | | width: 4px; |
| | | height: 16px; |
| | | background: #0d867f; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .attachment-section { |
| | | width: 100%; |
| | | } |
| | | |
| | | .attachment-grid { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .attachment-item { |
| | | width: calc(25% - 10px); |
| | | box-sizing: border-box; |
| | | position: relative; |
| | | } |
| | | |
| | | .upload-btn { |
| | | width: 80px; |
| | | height: 80px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 1px dashed #ccc; |
| | | border-radius: 4px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .upload-icon { |
| | | font-size: 32px; |
| | | color: #0d867f; |
| | | } |
| | | |
| | | .delete-icon { |
| | | position: absolute; |
| | | top: -8px; |
| | | right: -8px; |
| | | width: 24px; |
| | | height: 24px; |
| | | background-color: rgba(255, 0, 0, 0.8); |
| | | color: white; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 10; |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .attachment-item { |
| | | width: calc(25% - 10px); |
| | | margin: 10; |
| | | } |
| | | } |
| | | |
| | | :deep(.wd-form-item) { |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | :deep(.wd-input, .wd-select, .wd-radio-group, .wd-checkbox-group) { |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | :deep(.wd-form-item__label)::after { |
| | | content: "*"; |
| | | color: red; |
| | | margin-left: 4rpx; |
| | | } |
| | | |
| | | :deep(.wd-select) { |
| | | width: 100%; |
| | | } |
| | | |
| | | :deep(.wd-checkbox) { |
| | | margin-right: 0; |
| | | } |
| | | .conclusion-radio-group { |
| | | display: flex; |
| | | align-items: flex-start; // åç´æ¹åé¡¶é¨å¯¹é½ï¼ä¸ç§»å
³é®ï¼ |
| | | gap: 20rpx; // é项ä¹é´çé´è· |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view> |
| | | <wd-row> |
| | | <wd-col :span="21"> |
| | | <wd-search |
| | | v-model="searchKeyword" |
| | | placeholder="请è¾å
¥çç»åç§°" |
| | | placeholder-left |
| | | hide-cancel |
| | | @search="handleSearch" |
| | | @clear="handleClear" |
| | | ></wd-search> |
| | | </wd-col> |
| | | <wd-col :span="3"> |
| | | <view class="scan_box" @click="openScan"> |
| | | <wd-icon name="scan" size="24px" color="#0D867F"></wd-icon> |
| | | </view> |
| | | </wd-col> |
| | | </wd-row> |
| | | <wd-tabs v-model="tab" auto-line-width slidable="always" :map-num="patrolList.length"> |
| | | <wd-tab |
| | | v-for="(item, index) in patrolList" |
| | | :key="index" |
| | | :title="`${item.deviceModel}ï¼å¾
æ£æ¥${item.pendingNum}æ¡ï¼`" |
| | | class="tab_bg" |
| | | > |
| | | <ProductList |
| | | :key="searchKey" |
| | | :api="RoutingInspectionApi.getInspectListByPatrol" |
| | | :ProList="{ ...item, teamName: searchKeyword }" |
| | | /> |
| | | </wd-tab> |
| | | </wd-tabs> |
| | | <wd-toast /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { ref, reactive, computed, onMounted, onUnmounted } from "vue"; |
| | | import { onShow, onHide } from "@dcloudio/uni-app"; |
| | | import ProductList from "./list/index.vue"; |
| | | import { useUserStore } from "@/store/modules/user"; |
| | | import reportApi from "@/api/work/report"; |
| | | import { useToast } from "wot-design-uni"; |
| | | import RoutingInspectionApi from "@/api/routingInspection/routingInspection"; |
| | | import { useScanCode } from "@/composables/useScanCode"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const userInfo: any = computed(() => userStore.userInfo); |
| | | const toast = useToast(); |
| | | const tab = ref<number>(0); |
| | | const patrolList = ref<any[]>([]); // å·¡æ£è®¾å¤åè¡¨æ°æ® |
| | | const searchKeyword = ref<string>(""); // æç´¢å
³é®è¯ï¼çç»åç§°ï¼ |
| | | const searchKey = ref<number>(0); // ç¨äºå¼ºå¶å·æ°å表 |
| | | |
| | | // ä½¿ç¨æ«ç 管ç composableï¼å
¨å±çå¬å¨ï¼ä¸é页é¢åæ¢å
³éï¼ |
| | | const { deviceUid, deviceModel, hasScanned, displayText, loadFromCache, enableListener } = |
| | | useScanCode("scanIndex"); |
| | | |
| | | const handlePatrolData = (index: number, count: number) => { |
| | | // å¯ä»¥å¨è¿éæ´æ°ç¹å®å·¡æ£è®¾å¤çå¾
æ£æ¥æ°é |
| | | // ä¾å¦ï¼patrolList.value[index].pendingNum = count; |
| | | }; |
| | | |
| | | // å¤çæç´¢ |
| | | const handleSearch = (value: string) => { |
| | | console.log("æç´¢çç»:", value); |
| | | searchKey.value++; // æ´æ° key 强å¶å·æ°å表 |
| | | }; |
| | | |
| | | // å¤çæ¸
空æç´¢ |
| | | const handleClear = () => { |
| | | console.log("æ¸
空æç´¢"); |
| | | searchKeyword.value = ""; |
| | | searchKey.value++; // æ´æ° key 强å¶å·æ°å表 |
| | | }; |
| | | |
| | | const openScan = () => { |
| | | console.log("index.vue - ç¹å»æ«ç æé®ï¼å
¨å±æ«ç 模å¼ï¼æ éæå¨è§¦åï¼"); |
| | | // å
¨å±æ«ç 模å¼ä¸ï¼ç¡¬ä»¶æ«ç ä¼èªå¨è§¦åï¼æ éæå¨è°ç¨ |
| | | uni.showToast({ |
| | | title: "è¯·ä½¿ç¨æ«ç æªæ«æ", |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // è·åå·¡æ£è®¾å¤å表 |
| | | const loadPatrolList = async () => { |
| | | try { |
| | | const { data } = await RoutingInspectionApi.getDeviceInspectListByPatrol({}); |
| | | if (data) { |
| | | patrolList.value = data; |
| | | } |
| | | } catch (error) { |
| | | toast.error("è·åå·¡æ£è®¾å¤å表失败"); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½æ¶è·åå·¡æ£è®¾å¤å表 |
| | | loadPatrolList(); |
| | | // å¯ç¨å
¨å±çå¬å¨ |
| | | enableListener(); |
| | | console.log("index.vue - onMounted"); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | console.log("========== index.vue - onShow 触å =========="); |
| | | // 页颿¾ç¤ºæ¶éæ°å¯ç¨çå¬å¨ï¼ç¡®ä¿çå¬å¨ææï¼ |
| | | enableListener(); |
| | | // å è½½ç¼åï¼æ´æ°UIæ¾ç¤ºï¼ |
| | | loadFromCache(); |
| | | |
| | | // æ£æ¥æ¯å¦éè¦å·æ°å表ï¼åªææäº¤æååæå·æ°ï¼ |
| | | const needRefresh = uni.getStorageSync("needRefreshInspectionList"); |
| | | if (needRefresh) { |
| | | console.log("æ£æµå°éè¦å·æ°å表ï¼å¼å§å·æ°..."); |
| | | // éæ°å 载巡æ£è®¾å¤å表ï¼å·æ°å¾
æ£æ¥æ°éï¼ |
| | | loadPatrolList(); |
| | | // 强å¶å·æ° ProductList ç»ä»¶ |
| | | searchKey.value++; |
| | | // æ¸
é¤å·æ°æ è®° |
| | | uni.removeStorageSync("needRefreshInspectionList"); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | ::v-deep .wd-search__block { |
| | | border-radius: unset; |
| | | } |
| | | .scan_box { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 38px; |
| | | height: 38px; |
| | | padding: 6px; |
| | | background: #fff; |
| | | } |
| | | ::v-deep .wd-tabs__line { |
| | | background: #0d867f; |
| | | } |
| | | ::v-deep .wd-tabs__nav { |
| | | border-bottom: 1px #dddddd solid; |
| | | } |
| | | .tab_bg { |
| | | background: #f3f9f8; |
| | | } |
| | | |
| | | .icon_box { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 20px; |
| | | height: 20px; |
| | | background: #e7f4ec99; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .statistics_box { |
| | | margin: 15px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="card_box"> |
| | | <z-paging |
| | | ref="pagingRef" |
| | | v-model="list" |
| | | :fixed="false" |
| | | :auto-show-back-to-top="true" |
| | | @query="getList" |
| | | > |
| | | <ProductCard |
| | | v-for="(item, index) in list" |
| | | :key="index" |
| | | :data="item" |
| | | :map="map" |
| | | @click="toDetail(item.id, item.deviceType)" |
| | | /> |
| | | </z-paging> |
| | | <wd-toast /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import ProductCard from "../product_card/index.vue"; |
| | | import { useUserStore } from "@/store/modules/user"; |
| | | import zPaging from "@/components/z-paging/z-paging.vue"; |
| | | import { useToast } from "wot-design-uni"; |
| | | |
| | | const toast = useToast(); |
| | | const userStore = useUserStore(); |
| | | const userInfo: any = computed(() => userStore.userInfo); |
| | | const pagingRef = ref(); |
| | | const map = reactive({ |
| | | deviceModel: "deviceModel", |
| | | model: "model", |
| | | firstNo: "firstNo", |
| | | recordDate: "recordDate", |
| | | workShift: "workShift", |
| | | teamName: "teamName", |
| | | poleModel: "poleModel", |
| | | poleNumber: "poleNumber", |
| | | outputNumber: "outputNumber", |
| | | inspectPerson: "inspectPerson", |
| | | status: "status", |
| | | productType: "productType", |
| | | recordPosition: "recordPosition", |
| | | rejectList: [ |
| | | { |
| | | rejectPerson: "rejectPerson", |
| | | rejectTime: "rejectTime", |
| | | rejectReason: { |
| | | reason: "reason", |
| | | }, |
| | | }, |
| | | ], // æ¹ä¸ºå¯¹è±¡ï¼å
嫿éçåµå¥å±æ§ |
| | | }); |
| | | const props = defineProps({ |
| | | api: { |
| | | type: Function, |
| | | default: () => {}, |
| | | }, |
| | | ProList: { |
| | | type: Object, |
| | | default: () => {}, |
| | | }, |
| | | }); |
| | | |
| | | const list = ref<any[]>([]); |
| | | |
| | | const toDetail = (id: number, deviceType: number) => { |
| | | console.log("ç¹å»å¡ç", id, deviceType); |
| | | if (deviceType == 1) { |
| | | // ç»çº¿ |
| | | uni.navigateTo({ |
| | | url: `/pages/routingInspection/detail/indexJX?id=${id}&deviceType=${deviceType}`, |
| | | }); |
| | | } else if (deviceType == 0) { |
| | | // æä¸ |
| | | uni.navigateTo({ |
| | | url: `/pages/routingInspection/detail/indexLS?id=${id}&deviceType=${deviceType}`, |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const getList = async (pageNo = 1, pageSize = 10) => { |
| | | const { code, data } = await props.api({ |
| | | deviceModel: props.ProList.deviceModel, |
| | | status: "0", |
| | | deviceType: props.ProList.deviceType, |
| | | teamName: props.ProList.teamName || "", // çç»åç§°æç´¢ |
| | | current: pageNo, |
| | | size: pageSize, |
| | | }); |
| | | if (code == 200) { |
| | | map.deviceModel = "deviceModel"; |
| | | map.model = "model"; |
| | | map.firstNo = "firstNo"; |
| | | map.recordDate = "recordDate"; |
| | | map.workShift = "workShift"; |
| | | map.teamName = "teamName"; |
| | | map.poleModel = "poleModel"; |
| | | map.poleNumber = "poleNumber"; |
| | | map.outputNumber = "outputNumber"; |
| | | map.inspectPerson = "inspectPerson"; |
| | | map.productType = "productType"; |
| | | map.recordPosition = "recordPosition"; |
| | | map.rejectList = [ |
| | | { |
| | | rejectPerson: "rejectPerson", |
| | | rejectTime: "rejectTime", |
| | | rejectReason: { |
| | | reason: "reason", |
| | | }, |
| | | }, |
| | | ]; |
| | | map.status = "status"; |
| | | if (data.total == 0) { |
| | | pagingRef.value.complete(true); |
| | | } else { |
| | | console.log("data.records", data.records); |
| | | pagingRef.value.complete(data.records); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .card_box { |
| | | height: calc(100vh - 120px); |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <wd-card class="card_bg" @click="handleCardClick"> |
| | | <template #title> |
| | | <view class="flex justify-between w-full"> |
| | | <text class="font-medium text-[#252525]">è®°å½ä½ç½®: {{ data[map.recordPosition] }}</text> |
| | | <wd-tag color="#0D867F" bg-color="#E7F4EC"> |
| | | <text class="text-xs">{{ data[map.model] }}</text> |
| | | </wd-tag> |
| | | </view> |
| | | </template> |
| | | <wd-row class="my-2"> |
| | | <wd-col :span="24"> |
| | | <view class="flex"> |
| | | <view class="icon_box"> |
| | | <wd-icon name="folder" color="#0D867F"></wd-icon> |
| | | </view> |
| | | <text class="text-[#646874] mx-2"> |
| | | çæ¬¡: |
| | | <text class="text-[#252525]">{{ data[map.workShift] }}</text> |
| | | </text> |
| | | </view> |
| | | </wd-col> |
| | | </wd-row> |
| | | <wd-row class="my-2"> |
| | | <wd-col :span="24"> |
| | | <view class="flex"> |
| | | <view class="icon_box"> |
| | | <wd-icon name="folder" color="#0D867F"></wd-icon> |
| | | </view> |
| | | <text class="text-[#646874] mx-2"> |
| | | çç»: |
| | | <text class="text-[#252525]"> |
| | | {{ data[map.teamName]?.slice(-2) || data[map.teamName] }} |
| | | </text> |
| | | </text> |
| | | </view> |
| | | </wd-col> |
| | | </wd-row> |
| | | <wd-row class="my-2" v-if="data[map.productType]"> |
| | | <wd-col :span="24"> |
| | | <view class="flex"> |
| | | <view class="icon_box"> |
| | | <wd-icon name="folder" color="#0D867F"></wd-icon> |
| | | </view> |
| | | <text class="text-[#646874] mx-2"> |
| | | 产åç±»å«: |
| | | <text class="text-[#252525]">{{ data[map.productType] }}</text> |
| | | </text> |
| | | </view> |
| | | </wd-col> |
| | | </wd-row> |
| | | <wd-row class="my-2"> |
| | | <wd-col :span="12"> |
| | | <view class="flex"> |
| | | <view class="icon_box"> |
| | | <wd-icon name="folder" color="#0D867F"></wd-icon> |
| | | </view> |
| | | <text class="text-[#646874] mx-2"> |
| | | èªæ£äºº: |
| | | <text class="text-[#252525]">{{ data[map.inspectPerson] }}</text> |
| | | </text> |
| | | </view> |
| | | </wd-col> |
| | | <wd-col :span="12"> |
| | | <view class="flex"> |
| | | <view class="icon_box"> |
| | | <wd-icon name="folder" color="#0D867F"></wd-icon> |
| | | </view> |
| | | <text class="text-[#646874] mx-2"> |
| | | ç¶æ: |
| | | <text class="text-[#252525]">{{ data[map.status] == 1 ? "被驳å" : "å·¡æ£" }}</text> |
| | | </text> |
| | | </view> |
| | | </wd-col> |
| | | </wd-row> |
| | | <wd-row class="my-2"> |
| | | <wd-col :span="16"> |
| | | <view class="flex"> |
| | | <view class="icon_box"> |
| | | <wd-icon name="folder" color="#0D867F"></wd-icon> |
| | | </view> |
| | | <text class="text-[#646874] mx-2"> |
| | | è®°å½æ¶é´: |
| | | <text class="text-[#252525]">{{ data[map.recordDate] }}</text> |
| | | </text> |
| | | </view> |
| | | </wd-col> |
| | | <wd-col :span="8"> |
| | | <view class="flex" @click.stop> |
| | | <wd-button |
| | | v-if="data[map.status] == 1" |
| | | size="small" |
| | | type="primary" |
| | | @click="showRejectPopup = true" |
| | | style="margin-left: auto" |
| | | > |
| | | æ¥ç驳åä¿¡æ¯ |
| | | </wd-button> |
| | | </view> |
| | | </wd-col> |
| | | </wd-row> |
| | | </wd-card> |
| | | <wd-popup |
| | | v-model="showRejectPopup" |
| | | title="驳åä¿¡æ¯" |
| | | custom-style="border-radius:32rpx;height: 800rpx;width: 600rpx;" |
| | | > |
| | | <wd-card |
| | | v-for="(item, index) in data.rejectList" |
| | | :key="index" |
| | | :class="index % 2 === 0 ? 'reject-card-bg-1' : 'reject-card-bg-2'" |
| | | style="margin-bottom: 8px; padding: 10px; border-radius: 8px" |
| | | > |
| | | <view class="content"> |
| | | <view> |
| | | <view |
| | | style=" |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | color: rgba(0, 0, 0, 0.85); |
| | | font-size: 14px; |
| | | margin-bottom: 8px; |
| | | " |
| | | > |
| | | <view>{{ item.rejectPerson }}</view> |
| | | <view>{{ item.rejectTime }}</view> |
| | | </view> |
| | | <view |
| | | style=" |
| | | color: rgba(0, 0, 0, 0.85); |
| | | font-size: 14px; |
| | | word-break: break-word; |
| | | overflow-wrap: break-word; |
| | | max-width: 100%; |
| | | padding: 5px 0; |
| | | " |
| | | > |
| | | {{ item.rejectReason.reason }} |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </wd-card> |
| | | </wd-popup> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref } from "vue"; |
| | | const emit = defineEmits(["click"]); |
| | | defineProps({ |
| | | data: { |
| | | type: Object, |
| | | default: () => {}, |
| | | }, |
| | | map: { |
| | | type: Object, |
| | | default: () => {}, |
| | | }, |
| | | }); |
| | | const showRejectPopup = ref<boolean>(false); |
| | | const handleCardClick = () => { |
| | | emit("click"); |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .card_bg { |
| | | box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05); |
| | | padding-bottom: 10px; |
| | | } |
| | | |
| | | // æ·»å ï¼ä¸¤ç§ä¸åçèæ¯è²æ ·å¼ |
| | | .reject-card-bg-1 { |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | .reject-card-bg-2 { |
| | | background-color: #eef2f7; |
| | | } |
| | | |
| | | .page-class { |
| | | :deep() { |
| | | .custom-shadow { |
| | | box-shadow: |
| | | 0 3px 1px -2px rgb(0 0 0 / 20%), |
| | | 0 2px 2px 0 rgb(0 0 0 / 14%), |
| | | 0 1px 5px 0 rgb(0 0 0 / 12%); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | // ä¿®æ¹ï¼è°æ´å
容åºåçæ ·å¼ |
| | | .content { |
| | | padding: 5px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="attachment-container"> |
| | | <!-- 头鍿ä½åº --> |
| | | <view class="header-actions"> |
| | | <wd-button |
| | | icon="file-add" |
| | | :round="false" |
| | | size="small" |
| | | custom-class="add_btn" |
| | | @click="addAttachment" |
| | | v-if="isEdit" |
| | | > |
| | | æ°å¢ |
| | | </wd-button> |
| | | </view> |
| | | |
| | | <!-- éä»¶å表 --> |
| | | <view class="attachment-list"> |
| | | <wd-status-tip |
| | | v-if="attachmentList.length === 0" |
| | | image="content" |
| | | tip="ææ éä»¶" |
| | | custom-class="status-tip-full" |
| | | /> |
| | | |
| | | <view v-for="(item, index) in attachmentList" :key="item.id || index" class="attachment-card"> |
| | | <view class="media-wrapper" @click="previewAttachment(item)"> |
| | | <!-- å¾çé¢è§ --> |
| | | <template v-if="isImageType(item.url)"> |
| | | <image |
| | | :src="getFullUrl(item.url)" |
| | | mode="aspectFill" |
| | | class="media-preview" |
| | | style="width: 100%; height: 100%" |
| | | @error="onImageError(item, index)" |
| | | @load="onImageLoad(item, index)" |
| | | :show-menu-by-longpress="true" |
| | | /> |
| | | <!-- å è½½ä¸é®ç½© --> |
| | | <view v-if="item.loading" class="loading-mask"> |
| | | <text class="loading-text">å è½½ä¸...</text> |
| | | </view> |
| | | <!-- å¾çå 载失败æ¾ç¤ºé»è®¤å¾æ --> |
| | | <view v-if="item.loadError" class="file-icon-wrapper error-overlay"> |
| | | <wd-icon name="picture" size="48px" color="#ccc" /> |
| | | <text class="file-name error-text">å 载失败</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <!-- è§é¢é¢è§ --> |
| | | <template v-else-if="isVideoType(item.url)"> |
| | | <video |
| | | :src="getFullUrl(item.url)" |
| | | class="media-preview" |
| | | :controls="false" |
| | | :show-center-play-btn="true" |
| | | @error="onVideoError(item, index)" |
| | | object-fit="cover" |
| | | /> |
| | | <!-- è§é¢å 载失败æ¾ç¤ºé»è®¤å¾æ --> |
| | | <view v-if="item.loadError" class="file-icon-wrapper error-overlay"> |
| | | <wd-icon name="video" size="48px" color="#ccc" /> |
| | | <text class="file-name error-text">å 载失败</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <!-- å
¶ä»æä»¶ç±»åæ¾ç¤ºå¾æ --> |
| | | <view v-else class="file-icon-wrapper"> |
| | | <wd-icon name="file-outline" size="48px" color="#999" /> |
| | | <text class="file-name">æä»¶</text> |
| | | </view> |
| | | |
| | | <!-- å é¤æé® --> |
| | | <view class="delete-btn" @click.stop="deleteAttachment(item.id)" v-if="isEdit"> |
| | | <wd-icon name="delete" color="#fff" size="20px" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <wd-toast /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, computed, watch } from "vue"; |
| | | import { useToast } from "wot-design-uni"; |
| | | import AttachmentAPI from "@/api/product/attachment"; |
| | | |
| | | // H5 ä½¿ç¨ VITE_APP_BASE_API ä½ä¸ºä»£çè·¯å¾ï¼å
¶ä»å¹³å°ä½¿ç¨ VITE_APP_API_URL ä½ä¸ºè¯·æ±è·¯å¾ |
| | | let baseUrlValue = import.meta.env.VITE_APP_API_URL || ""; |
| | | // #ifdef H5 |
| | | baseUrlValue = import.meta.env.VITE_APP_BASE_API || ""; |
| | | // #endif |
| | | |
| | | const baseUrl = ref(baseUrlValue); // 使ç¨ref使å
¶å¨æ¨¡æ¿ä¸å¯è®¿é® |
| | | |
| | | // å¤é¨åæ° |
| | | const props = defineProps({ |
| | | detailData: { type: Object, default: () => ({}) }, |
| | | isEdit: { type: Boolean, default: false }, |
| | | deviceType: { type: String, default: "" }, |
| | | }); |
| | | |
| | | const toast = useToast(); |
| | | |
| | | // è·ååå§æ°æ® |
| | | const getInitialData = () => { |
| | | // å¤çä¸åçæ°æ®ç»æ |
| | | let data = props.detailData; |
| | | |
| | | // å¦ææ¯ ref 对象ï¼è·åå
¶ value |
| | | if (data && typeof data === "object" && "value" in data) { |
| | | data = data.value; |
| | | } |
| | | |
| | | // å¦ææ¯æ°ç»ï¼ç´æ¥è¿å |
| | | if (Array.isArray(data)) { |
| | | return data.map((item) => ({ |
| | | ...item, |
| | | loading: false, |
| | | loadError: false, |
| | | })); |
| | | } |
| | | |
| | | // 妿æ files 屿§ |
| | | if (data && data.files) { |
| | | const files = Array.isArray(data.files) ? data.files : []; |
| | | return files.map((item) => ({ |
| | | ...item, |
| | | loading: false, |
| | | loadError: false, |
| | | })); |
| | | } |
| | | |
| | | return []; |
| | | }; |
| | | |
| | | const attachmentList = ref<any[]>(getInitialData()); |
| | | const attachmentIds = ref<string[]>(attachmentList.value.map((item: any) => item.id) || []); |
| | | |
| | | // çå¬ props.detailData åå |
| | | watch( |
| | | () => props.detailData, |
| | | (newVal) => { |
| | | const newData = getInitialData(); |
| | | if (newData.length > 0) { |
| | | attachmentList.value = newData.map((item) => ({ |
| | | ...item, |
| | | loading: false, |
| | | loadError: false, |
| | | })); |
| | | attachmentIds.value = newData.map((item: any) => item.id); |
| | | } |
| | | }, |
| | | { deep: true, immediate: false } |
| | | ); |
| | | |
| | | // è·å宿´çå¾ç/è§é¢ URL |
| | | const getFullUrl = (url: string) => { |
| | | if (!url) return ""; |
| | | |
| | | // å¦æå·²ç»æ¯å®æ´ç URLï¼http æ https å¼å¤´ï¼ï¼ç´æ¥è¿å |
| | | if (url.startsWith("http://") || url.startsWith("https://")) { |
| | | return url; |
| | | } |
| | | |
| | | // æ£æ¥ baseUrl æ¯å¦ææ |
| | | if (!baseUrl.value) { |
| | | console.error("â baseUrlæªé
ç½®ï¼url:", url); |
| | | return url; |
| | | } |
| | | |
| | | // 妿æ¯ç¸å¯¹è·¯å¾ï¼æ¼æ¥åºç¡ URL |
| | | const separator = url.startsWith("/") || baseUrl.value.endsWith("/") ? "" : "/"; |
| | | return `${baseUrl.value}${separator}${url}`; |
| | | }; |
| | | |
| | | // å¾çå è½½æå |
| | | const onImageLoad = (item: any, index: number) => { |
| | | item.loading = false; |
| | | item.loadError = false; |
| | | attachmentList.value = [...attachmentList.value]; |
| | | }; |
| | | |
| | | // å¾çå 载失败 |
| | | const onImageError = (item: any, index: number) => { |
| | | console.error(`å¾çå 载失败 [${index}]:`, item.url); |
| | | item.loading = false; |
| | | item.loadError = true; |
| | | attachmentList.value = [...attachmentList.value]; |
| | | }; |
| | | |
| | | // è§é¢å 载失败 |
| | | const onVideoError = (item: any, index: number) => { |
| | | console.error(`è§é¢å 载失败 [${index}]:`, item.url); |
| | | item.loading = false; |
| | | item.loadError = true; |
| | | attachmentList.value = [...attachmentList.value]; |
| | | }; |
| | | |
| | | // æ°å¢éä»¶ |
| | | const addAttachment = () => { |
| | | // æ¾ç¤ºéæ©æä»¶ç±»åçå¼¹çª |
| | | uni.showActionSheet({ |
| | | itemList: ["éæ©å¾ç", /* "éæ©è§é¢", */ "æç
§" /* , "å½å" */], |
| | | success: (res) => { |
| | | switch (res.tapIndex) { |
| | | case 0: // éæ©å¾ç |
| | | chooseImages(); |
| | | break; |
| | | // case 1: // éæ©è§é¢ |
| | | // chooseVideos(); |
| | | // break; |
| | | case 1: // æç
§ |
| | | takePhoto(); |
| | | break; |
| | | // case 3: // å½å |
| | | // recordVideo(); |
| | | // break; |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | console.error("éæ©æä»¶ç±»å失败:", error); |
| | | toast.show("éæ©æä»¶ç±»å失败"); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // éæ©å¾ç |
| | | const chooseImages = () => { |
| | | uni.chooseImage({ |
| | | count: 9, |
| | | sizeType: ["original", "compressed"], |
| | | sourceType: ["album"], |
| | | success: async (res) => { |
| | | const filePaths = Array.isArray(res.tempFilePaths) ? res.tempFilePaths : [res.tempFilePaths]; |
| | | await handleFileUpload(filePaths); |
| | | }, |
| | | fail: (error) => { |
| | | console.error("éæ©å¾ç失败:", error); |
| | | toast.show("éæ©å¾ç失败"); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // éæ©è§é¢ |
| | | const chooseVideos = () => { |
| | | uni.chooseVideo({ |
| | | sourceType: ["album"], |
| | | maxDuration: 60, |
| | | camera: "back", |
| | | success: async (res) => { |
| | | await handleFileUpload([res.tempFilePath]); |
| | | }, |
| | | fail: (error) => { |
| | | console.error("éæ©è§é¢å¤±è´¥:", error); |
| | | toast.show("éæ©è§é¢å¤±è´¥"); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // æç
§ |
| | | const takePhoto = () => { |
| | | uni.chooseImage({ |
| | | count: 1, |
| | | sizeType: ["original", "compressed"], |
| | | sourceType: ["camera"], |
| | | success: async (res) => { |
| | | const filePaths = Array.isArray(res.tempFilePaths) ? res.tempFilePaths : [res.tempFilePaths]; |
| | | await handleFileUpload(filePaths); |
| | | }, |
| | | fail: (error) => { |
| | | console.error("æç
§å¤±è´¥:", error); |
| | | toast.show("æç
§å¤±è´¥"); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // å½å |
| | | const recordVideo = () => { |
| | | uni.chooseVideo({ |
| | | sourceType: ["camera"], |
| | | maxDuration: 60, |
| | | camera: "back", |
| | | success: async (res) => { |
| | | await handleFileUpload([res.tempFilePath]); |
| | | }, |
| | | fail: (error) => { |
| | | console.error("å½å失败:", error); |
| | | toast.show("å½å失败"); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // å¤çæä»¶ä¸ä¼ |
| | | const handleFileUpload = async (filePaths: string[]) => { |
| | | try { |
| | | toast.show("æ£å¨ä¸ä¼ ..."); |
| | | |
| | | // ä¸ä¼ æä»¶ |
| | | const uploadResults: any = await AttachmentAPI.uploadAttachmentFiles(filePaths); |
| | | const result = uploadResults.map((it: any) => { |
| | | return it.data; |
| | | }); |
| | | |
| | | // æ´æ°éä»¶å表 |
| | | const flattenedResult = result.flat(); |
| | | attachmentList.value.push(...flattenedResult); |
| | | |
| | | // æåéä»¶ID |
| | | attachmentIds.value = attachmentList.value.map((item: any) => item.id); |
| | | toast.show("ä¸ä¼ æå"); |
| | | } catch (error) { |
| | | console.error("ä¸ä¼ 失败:", error); |
| | | toast.show("ä¸ä¼ 失败"); |
| | | } |
| | | }; |
| | | |
| | | // å é¤éä»¶ |
| | | const deleteAttachment = async (aid: number) => { |
| | | try { |
| | | uni.showModal({ |
| | | title: "确认å é¤", |
| | | content: "ç¡®å®è¦å é¤è¿ä¸ªéä»¶åï¼", |
| | | success: async (res) => { |
| | | if (res.confirm) { |
| | | // å端æå¨å é¤ï¼ç´æ¥ä»å表ä¸ç§»é¤è¿æ¡æ°æ® |
| | | attachmentList.value = attachmentList.value.filter((item) => item.id !== aid); |
| | | |
| | | // è·åå©ä½çéä»¶ID |
| | | attachmentIds.value = attachmentList.value.map((item) => item.id); |
| | | toast.show("å 餿å"); |
| | | } |
| | | }, |
| | | }); |
| | | } catch (error) { |
| | | console.error("å é¤å¤±è´¥:", error); |
| | | toast.show("å é¤å¤±è´¥"); |
| | | } |
| | | }; |
| | | |
| | | // é¢è§éä»¶ |
| | | const previewAttachment = (item: any) => { |
| | | // æ ¹æ®æä»¶ç±»åè¿è¡é¢è§ |
| | | const fileType = getFileType(item.url); |
| | | const fullUrl = getFullUrl(item.url); |
| | | |
| | | if (fileType.startsWith("image")) { |
| | | // å¾çé¢è§ |
| | | uni.previewImage({ |
| | | urls: [fullUrl], |
| | | current: fullUrl, |
| | | }); |
| | | } else { |
| | | // å
¶ä»æä»¶ç±»åï¼å¯ä»¥ä¸è½½ææå¼ |
| | | uni.downloadFile({ |
| | | url: fullUrl, |
| | | success: (res) => { |
| | | uni.openDocument({ |
| | | filePath: res.tempFilePath, |
| | | success: () => { |
| | | // æå¼ææ¡£æå |
| | | }, |
| | | fail: (error) => { |
| | | console.error("æå¼ææ¡£å¤±è´¥:", error); |
| | | toast.show("æ æ³é¢è§æ¤æä»¶ç±»å"); |
| | | }, |
| | | }); |
| | | }, |
| | | fail: (error) => { |
| | | console.error("ä¸è½½æä»¶å¤±è´¥:", error); |
| | | toast.show("ä¸è½½æä»¶å¤±è´¥"); |
| | | }, |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // ä» URL ææä»¶å䏿忩å±å |
| | | const getExtension = (urlOrFileName: string) => { |
| | | if (!urlOrFileName) return ""; |
| | | // ç§»é¤æ¥è¯¢åæ°ååå¸ |
| | | const cleanUrl = urlOrFileName.split("?")[0].split("#")[0]; |
| | | // è·åæåä¸ä¸ªç¹åé¢çå
容 |
| | | const extension = cleanUrl.split(".").pop()?.toLowerCase(); |
| | | return extension || ""; |
| | | }; |
| | | |
| | | // 夿æ¯å¦ä¸ºå¾çç±»å |
| | | const isImageType = (urlOrFileName: string) => { |
| | | const extension = getExtension(urlOrFileName); |
| | | return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(extension); |
| | | }; |
| | | |
| | | // 夿æ¯å¦ä¸ºè§é¢ç±»å |
| | | const isVideoType = (urlOrFileName: string) => { |
| | | const extension = getExtension(urlOrFileName); |
| | | return ["mp4", "mov", "avi", "wmv", "flv", "mkv", "webm"].includes(extension); |
| | | }; |
| | | |
| | | // è·åæä»¶ç±»å |
| | | const getFileType = (urlOrFileName: string) => { |
| | | if (!urlOrFileName) return "unknown"; |
| | | const extension = getExtension(urlOrFileName); |
| | | switch (extension) { |
| | | case "jpg": |
| | | case "jpeg": |
| | | case "png": |
| | | case "gif": |
| | | case "bmp": |
| | | case "webp": |
| | | return "image"; |
| | | case "mp4": |
| | | case "mov": |
| | | case "avi": |
| | | case "wmv": |
| | | case "flv": |
| | | case "mkv": |
| | | case "webm": |
| | | return "video"; |
| | | case "pdf": |
| | | return "pdf"; |
| | | case "doc": |
| | | case "docx": |
| | | return "word"; |
| | | case "xls": |
| | | case "xlsx": |
| | | return "excel"; |
| | | case "ppt": |
| | | case "pptx": |
| | | return "powerpoint"; |
| | | case "txt": |
| | | return "text"; |
| | | case "zip": |
| | | case "rar": |
| | | return "archive"; |
| | | default: |
| | | return "file"; |
| | | } |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = (size: number) => { |
| | | 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 = (time: string) => { |
| | | const date = new Date(time); |
| | | return date.toLocaleString(); |
| | | }; |
| | | // 坹夿´é²æ¹æ³ï¼è·åææéæäº¤çæä»¶ |
| | | const getSubmitFiles = () => ({ |
| | | newFiles: attachmentIds.value || [], |
| | | }); |
| | | defineExpose({ getSubmitFiles }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .attachment-container { |
| | | padding: 12px; |
| | | background: #f3f9f8; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .header-actions { |
| | | margin-bottom: 12px; |
| | | |
| | | :deep(.add_btn) { |
| | | background: #0d867f; |
| | | color: white; |
| | | border: none; |
| | | } |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: grid; |
| | | grid-template-columns: repeat(3, 1fr); |
| | | gap: 8px; |
| | | |
| | | :deep(.status-tip-full) { |
| | | grid-column: 1 / -1; |
| | | width: 100%; |
| | | } |
| | | |
| | | .attachment-card { |
| | | width: 100%; |
| | | position: relative; |
| | | |
| | | // ä½¿ç¨ padding-top å®ç°æ£æ¹å½¢ï¼å
¼å®¹æ§æ´å¥½ï¼ |
| | | &::before { |
| | | content: ""; |
| | | display: block; |
| | | padding-top: 100%; // é«åº¦çäºå®½åº¦ |
| | | } |
| | | } |
| | | } |
| | | |
| | | .media-wrapper { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | background: #f5f5f5; |
| | | |
| | | .media-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | display: block; |
| | | } |
| | | |
| | | .loading-mask { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: rgba(0, 0, 0, 0.3); |
| | | z-index: 5; |
| | | |
| | | .loading-text { |
| | | font-size: 12px; |
| | | color: #fff; |
| | | } |
| | | } |
| | | |
| | | .file-icon-wrapper { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 8px; |
| | | text-align: center; |
| | | |
| | | .file-name { |
| | | margin-top: 8px; |
| | | font-size: 12px; |
| | | color: #666; |
| | | word-break: break-all; |
| | | display: -webkit-box; |
| | | -webkit-line-clamp: 2; |
| | | line-clamp: 2; |
| | | -webkit-box-orient: vertical; |
| | | overflow: hidden; |
| | | |
| | | &.error-text { |
| | | color: #ff4757; |
| | | } |
| | | } |
| | | |
| | | &.error-overlay { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(255, 255, 255, 0.9); |
| | | z-index: 5; |
| | | } |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: 4px; |
| | | right: 4px; |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 50%; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 10; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | const TOKEN_KEY = "app-token"; |
| | | const USER_INFO_KEY = "user-info"; |
| | | const DICT_KEY = "dict"; |
| | | const TEAM_ID_KEY = "team-id"; |
| | | import { type DictData } from "@/api/system/dict"; |
| | | |
| | | // 设置 token |
| | |
| | | uni.removeStorageSync(DICT_KEY); |
| | | } |
| | | |
| | | // 设置çç»ID |
| | | export function setTeamId(teamId: string | number) { |
| | | uni.setStorageSync(TEAM_ID_KEY, teamId); |
| | | } |
| | | |
| | | // è·åçç»ID |
| | | export function getTeamId(): string | number | null { |
| | | return uni.getStorageSync(TEAM_ID_KEY) || null; |
| | | } |
| | | |
| | | // æ¸
é¤çç»ID |
| | | export function clearTeamId() { |
| | | uni.removeStorageSync(TEAM_ID_KEY); |
| | | } |
| | | |
| | | // æ¸
餿æç¼åä¿¡æ¯ |
| | | export function clearAll() { |
| | | clearToken(); |
| | | clearUserInfo(); |
| | | clearDictCache(); |
| | | clearTeamId(); |
| | | } |
| | |
| | | uni.showToast({ |
| | | title: resData.msg || "ä¸å¡å¤ç失败", |
| | | icon: "none", |
| | | duration: 2000, |
| | | }); |
| | | reject({ |
| | | message: resData.msg || "ä¸å¡å¤ç失败", |
| | | code: resData.code, |
| | | duration: 2000, |
| | | }); |
| | | } |
| | | }, |
| | |
| | | reject({ |
| | | message: "ç½ç»è¯·æ±å¤±è´¥", |
| | | error, |
| | | duration: 2000, |
| | | }); |
| | | }, |
| | | }); |