| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # uni-app PDA æ«ç ï¼Android 广æï¼æ¥å
¥æå |
| | | |
| | | éç¨åºæ¯ï¼å·¥ä¸ PDA / æ«ææªè®¾å¤âæä¸ç©çæ«ç é®åï¼ç±ç³»ç»ä»¥å¹¿æï¼Broadcastï¼æ¹å¼è¾åºæ«ç å
容âçæ¨¡å¼ã |
| | | ç®æ ææï¼é¡µé¢åªéè¦å¼å
¥ä¸ä¸ªç»ä»¶å¹¶çå¬ä¸ä¸ªäºä»¶ï¼å°±è½åæ¬é¡¹ç®ä¸æ ·æ¥æ¶ PDA æ«ç ç»æï¼åæ¶å¨æ²¡æ PDA è½åç项ç®ä¸å¯éçº§ä¸ºç¸æºæ«ç ï¼`uni.scanCode`ï¼ã |
| | | |
| | | --- |
| | | |
| | | ## 1. åçæ¦è§ï¼æ¬é¡¹ç®çç¨æ³ï¼ |
| | | |
| | | æ¬é¡¹ç®ç PDA æ«ç é¾è·¯æ¯ï¼ |
| | | |
| | | - ç»ä»¶çå¬ Android 广æï¼æ¿å° `intent.getStringExtra(extraKey)` çå符串 |
| | | - å°æ«ç å符串éè¿ `uni.$emit('scan', { code })` æä¸ºå
¨å±äºä»¶ |
| | | - 页é¢å¨ `onShow` æ¶ `uni.$on('scan', cb)` æ¥æ¶ï¼ä¸å¡éè§£æ `data.code` å¹¶è°ç¨æ¥å£ |
| | | |
| | | 对åºå®ç°æä»¶ï¼ |
| | | |
| | | - PDA 广æçå¬ç»ä»¶ï¼[scan.vue](file:///d:/newcode/mes-ocea-unapp/components/scan/scan.vue#L18-L91) |
| | | - 页颿¥æ¶ç¤ºä¾ï¼[productBack/index.vue](file:///d:/newcode/mes-ocea-unapp/pages/wareHouse/productBack/index.vue#L96-L127) |
| | | |
| | | --- |
| | | |
| | | ## 2. æ¥å
¥åç½®æ¡ä»¶ |
| | | |
| | | **å¹³å°éå¶** |
| | | |
| | | - ä»
æ¯æ `APP-PLUS`ï¼æå
æ Appï¼ä¸ `Android` è®¾å¤ |
| | | - H5 / å°ç¨åº / iOS 䏿¯æ `plus.android`ï¼éè¦èµ°ç¸æºæ«ç éçº§æ¹æ¡ |
| | | |
| | | **ä½ å¿
é¡»æ¿å°è®¾å¤ç两项é
ç½®** |
| | | |
| | | - `action`ï¼æ«ç 广æç Actionï¼ä¾å¦æ¬é¡¹ç®æäº PDA 为 `com.service.scanner.data`ï¼ |
| | | - `extraKey`ï¼å¹¿æ Intent ä¸åæ¾æ«ç å符串ç keyï¼ä¾å¦æ¬é¡¹ç®ä¸º `ScanCode`ï¼ |
| | | |
| | | è·åæ¹å¼ï¼æ¨è顺åºï¼ï¼ |
| | | |
| | | - æ¥ç PDA ååæ«ææå¡/æ«æè®¾ç½®éçâ广æè¾åºâé
ç½®ï¼é常è½ç´æ¥çå° Action ä¸ extraKeyï¼ |
| | | - å设å¤åå/éæåç´¢è¦å¹¿æåè®®ï¼æå¯é ï¼ |
| | | - 临æ¶å¨é¡¹ç®éæ `action/extraKey` ææ¥å¿ï¼æ¬é¡¹ç®å·²æå°ï¼ï¼ç»åååææ¡£ç¡®è®¤ |
| | | |
| | | --- |
| | | |
| | | ## 3. å¯è¿ç§»çéç¨ç»ä»¶ï¼Vue2/Vue3 éç¨ï¼ |
| | | |
| | | uni-app ç Vue3 项ç®ä¾ç¶æ¯æ Options APIï¼å æ¤æ¨èç¨ Options API åä¸ä¸ªç»ä»¶ï¼è¾¾å°ä¸ä»½ç»ä»¶åæ¶å
¼å®¹ Vue2 ä¸ Vue3ã |
| | | |
| | | ### 3.1 æ°å¢ç»ä»¶æä»¶ |
| | | |
| | | å¨ä½ çç®æ 项ç®ä¸æ°å»ºï¼ |
| | | |
| | | - `components/pda-scan/pda-scan.vue` |
| | | |
| | | å
容å¦ä¸ï¼å¯ç´æ¥å¤å¶ï¼ï¼ |
| | | |
| | | ```vue |
| | | <template> |
| | | <view /> |
| | | </template> |
| | | |
| | | <script> |
| | | let mainActivity; |
| | | let receiver; |
| | | let intentFilter; |
| | | let isRegistered = false; |
| | | let lock = false; |
| | | |
| | | export default { |
| | | name: "pda-scan", |
| | | props: { |
| | | eventName: { |
| | | type: String, |
| | | default: "scan" |
| | | }, |
| | | action: { |
| | | type: String, |
| | | default: "" |
| | | }, |
| | | extraKey: { |
| | | type: String, |
| | | default: "" |
| | | }, |
| | | debounceMs: { |
| | | type: Number, |
| | | default: 150 |
| | | }, |
| | | autoStart: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | created() { |
| | | this.init(); |
| | | }, |
| | | onHide() { |
| | | this.stop(); |
| | | }, |
| | | beforeDestroy() { |
| | | this.stop(); |
| | | }, |
| | | beforeUnmount() { |
| | | this.stop(); |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // #ifdef APP-PLUS |
| | | if (!this.autoStart) return; |
| | | if (!plus || plus.os.name !== "Android") return; |
| | | |
| | | uni.getSystemInfo({ |
| | | success: (sys) => { |
| | | const cfg = this.resolveBroadcastConfig(sys); |
| | | if (!cfg.action || !cfg.extraKey) return; |
| | | this.initAndroidReceiver(cfg.action, cfg.extraKey); |
| | | this.start(); |
| | | } |
| | | }); |
| | | // #endif |
| | | }, |
| | | resolveBroadcastConfig(sys) { |
| | | const brand = String(sys.brand || "").toUpperCase(); |
| | | const model = String(sys.model || "").toUpperCase(); |
| | | |
| | | if (this.action && this.extraKey) { |
| | | return { action: this.action, extraKey: this.extraKey }; |
| | | } |
| | | |
| | | if (brand.indexOf("DROI") !== -1 || model.indexOf("MV-IDP5204") !== -1) { |
| | | return { action: "com.service.scanner.data", extraKey: "ScanCode" }; |
| | | } |
| | | |
| | | return { action: "", extraKey: "" }; |
| | | }, |
| | | initAndroidReceiver(action, extraKey) { |
| | | const _this = this; |
| | | mainActivity = plus.android.runtimeMainActivity(); |
| | | const IntentFilter = plus.android.importClass("android.content.IntentFilter"); |
| | | intentFilter = new IntentFilter(); |
| | | intentFilter.addAction(action); |
| | | |
| | | receiver = plus.android.implements("io.dcloud.feature.internal.reflect.BroadcastReceiver", { |
| | | onReceive(context, intent) { |
| | | plus.android.importClass(intent); |
| | | const code = intent.getStringExtra(extraKey); |
| | | _this.emitCode(code); |
| | | } |
| | | }); |
| | | }, |
| | | start() { |
| | | // #ifdef APP-PLUS |
| | | if (!plus || plus.os.name !== "Android") return; |
| | | if (!mainActivity || !receiver || !intentFilter) return; |
| | | if (isRegistered) return; |
| | | try { |
| | | mainActivity.registerReceiver(receiver, intentFilter); |
| | | isRegistered = true; |
| | | } catch (e) {} |
| | | // #endif |
| | | }, |
| | | stop() { |
| | | // #ifdef APP-PLUS |
| | | if (!plus || plus.os.name !== "Android") return; |
| | | if (!mainActivity || !receiver) return; |
| | | if (!isRegistered) return; |
| | | try { |
| | | mainActivity.unregisterReceiver(receiver); |
| | | } catch (e) {} |
| | | isRegistered = false; |
| | | // #endif |
| | | }, |
| | | emitCode(code) { |
| | | const value = code == null ? "" : String(code); |
| | | if (!value) return; |
| | | |
| | | if (lock) return; |
| | | lock = true; |
| | | setTimeout(() => { |
| | | lock = false; |
| | | }, this.debounceMs); |
| | | |
| | | uni.$emit(this.eventName, { code: value }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | ``` |
| | | |
| | | ### 3.2 ä½ éè¦æ¹åªé |
| | | |
| | | 1) 设å¤éé
ï¼æå
³é®ï¼ |
| | | ä¿®æ¹ `resolveBroadcastConfig()` é对 `action/extraKey` çè¿åé»è¾ï¼å å
¥ä½ 设å¤ç广æé
ç½®ã |
| | | |
| | | 2) ä¹å¯ä»¥ä¸æ¹ä»£ç ï¼ç´æ¥ä¼ åè¦çï¼æ¨èç¨äºå¤è®¾å¤/å¤ç¯å¢ï¼ |
| | | |
| | | ```vue |
| | | <pda-scan action="ä½ çaction" extraKey="ä½ çextraKey" /> |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## 4. 页é¢ä¾§ç¨æ³ï¼PDA å¹¿ææ«ç ï¼ |
| | | |
| | | ### 4.1 Vue2 页é¢ï¼Options APIï¼ç¤ºä¾ |
| | | |
| | | ```vue |
| | | <template> |
| | | <view> |
| | | <pda-scan /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import PdaScan from "@/components/pda-scan/pda-scan.vue"; |
| | | |
| | | export default { |
| | | components: { PdaScan }, |
| | | data() { |
| | | return { |
| | | lastCode: "" |
| | | }; |
| | | }, |
| | | onShow() { |
| | | uni.$off("scan"); |
| | | uni.$on("scan", (data) => { |
| | | this.handleScan(data.code); |
| | | }); |
| | | }, |
| | | onHide() { |
| | | uni.$off("scan"); |
| | | }, |
| | | methods: { |
| | | handleScan(code) { |
| | | this.lastCode = code; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | ``` |
| | | |
| | | ### 4.2 Vue3 页é¢ï¼`<script setup>`ï¼ç¤ºä¾ |
| | | |
| | | ```vue |
| | | <template> |
| | | <view> |
| | | <pda-scan /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import PdaScan from "@/components/pda-scan/pda-scan.vue"; |
| | | import { onShow, onHide } from "@dcloudio/uni-app"; |
| | | |
| | | function handleScan(code) {} |
| | | |
| | | onShow(() => { |
| | | uni.$off("scan"); |
| | | uni.$on("scan", (data) => { |
| | | handleScan(data.code); |
| | | }); |
| | | }); |
| | | |
| | | onHide(() => { |
| | | uni.$off("scan"); |
| | | }); |
| | | </script> |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## 5. ç¸æºæ«ç éçº§ï¼æ²¡æ PDA ä¾èµä¹è½ç¨ï¼ |
| | | |
| | | å½é¡¹ç®è·å¨é `APP-PLUS`ãæ iOSãææ²¡æé
ç½® `action/extraKey` æ¶ï¼PDA 广æä¸ä¼çæã建议æä¾ä¸ä¸ªæé®èµ°ç¸æºæ«ç ï¼ |
| | | |
| | | ```js |
| | | function goCameraScan() { |
| | | uni.scanCode({ |
| | | success: (res) => { |
| | | handleScan(res.result); |
| | | } |
| | | }); |
| | | } |
| | | ``` |
| | | |
| | | è¿æ ·ä½ çä¸å¡å¤ç彿°åªè¦å䏿¬¡ï¼`handleScan(code)`ã |
| | | |
| | | --- |
| | | |
| | | ## 6. ä¸å¡ä¾§è§£æå»ºè®®ï¼å
¼å®¹â纯å符串æ¡ç / JSON äºç»´ç âï¼ |
| | | |
| | | æ¬é¡¹ç®å¤ä¸ªé¡µé¢çæ¯ç¨æ¨¡å¼æ¯ï¼ |
| | | |
| | | - 妿 `code` æ¯ JSONï¼ä¾å¦å
å« `sn_no` / `lot_batch_no`ï¼ï¼å
`JSON.parse` |
| | | - 䏿¯ JSON å°±å½åæ®éæ¡ç å符串 |
| | | |
| | | å¯ç´æ¥å¤ç¨ä¸é¢çè§£æå½æ°ï¼ |
| | | |
| | | ```js |
| | | function normalizeScanText(raw) { |
| | | if (raw == null) return ""; |
| | | return String(raw).replace(/\n/g, "").replace(/\s*/g, "").trim(); |
| | | } |
| | | |
| | | function parseScan(raw) { |
| | | const text = normalizeScanText(raw); |
| | | if (!text) return { text: "", data: null }; |
| | | try { |
| | | const data = JSON.parse(text); |
| | | return { text, data }; |
| | | } catch (e) { |
| | | return { text, data: null }; |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## 7. 常è§é®é¢ææ¥æ¸
å |
| | | |
| | | - App è¿è¡å¨ H5/å°ç¨åºç¯å¢ï¼ä¸ä¼è§¦å广æçå¬ï¼åªè½ç¸æºæ«ç ï¼ |
| | | - iOSï¼ä¸æ¯æ `plus.android` |
| | | - Android è®¾å¤æ«ç 没ååºï¼ |
| | | - è®¾å¤æ¯å¦å¼å¯äºâ广æè¾åºâ |
| | | - `action/extraKey` æ¯å¦å¹é
ï¼ä¸ä¸ªå符ä¸å¯¹é½æ¶ä¸å°/åä¸å°ç ï¼ |
| | | - æ¯å¦åå¨å¤ä¸ªé¡µé¢åæ¶ `uni.$on('scan')` 导è´é»è¾ä¸²æ°ï¼å»ºè®®é¡µé¢ `onShow` å
`uni.$off`ï¼ |
| | | - æ¥é `unregisterReceiver`ï¼ |
| | | - å¤åçå¨âæª register å°± unregisterâï¼ç»ä»¶éå·²å `isRegistered` ä¸ try/catch ä¿æ¤ï¼å¦æä½ æ¹ä»£ç åååºç°ï¼ä¼å
æ£æ¥ `start/stop` æ¯å¦è¢«éå¤è°ç¨ |
| | | |
| | | --- |
| | | |
| | | ## 8. 䏿¬é¡¹ç®ä¿æä¸è´çäºä»¶åè®® |
| | | |
| | | 为äºè®©ä½ å¨å
¶ä»é¡¹ç®éå¤å»âæ¿å°æ«ç å页é¢ç´æ¥å¤çâçä½éªï¼å»ºè®®ä¿æï¼ |
| | | |
| | | - äºä»¶åï¼`scan` |
| | | - äºä»¶åæ°ï¼`{ code: string }` |
| | | |
| | | å¦æä½ è¦æ¹äºä»¶åï¼ä¾å¦å¤ä¸ªæ«ç æºåºåï¼ï¼ç»ä»¶æä¾äº `eventName`ï¼ |
| | | |
| | | ```vue |
| | | <pda-scan eventName="scan_pda" /> |
| | | ``` |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="pda-media-upload"> |
| | | <view class="upload-buttons"> |
| | | <u-button type="primary" |
| | | @click="pickMedia('image')" |
| | | :loading="uploading" |
| | | :disabled="disabled || modelValue.length >= limit" |
| | | :customStyle="{ marginRight: '10px', flex: 1 }"> |
| | | <u-icon name="camera" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px"></u-icon> |
| | | {{ uploading ? "ä¸ä¼ ä¸..." : "æç
§" }} |
| | | </u-button> |
| | | <u-button type="success" |
| | | @click="pickMedia('video')" |
| | | :loading="uploading" |
| | | :disabled="disabled || modelValue.length >= limit" |
| | | :customStyle="{ flex: 1 }"> |
| | | <uni-icons type="videocam" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px"></uni-icons> |
| | | {{ uploading ? "ä¸ä¼ ä¸..." : "æè§é¢" }} |
| | | </u-button> |
| | | </view> |
| | | <view v-if="uploading" |
| | | class="upload-progress"> |
| | | <u-line-progress :percentage="uploadProgress" |
| | | :showText="true" |
| | | activeColor="#409eff"></u-line-progress> |
| | | </view> |
| | | <view v-if="modelValue.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in modelValue" |
| | | :key="index" |
| | | class="file-item"> |
| | | <view class="file-preview-container"> |
| | | <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)" |
| | | :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | class="file-preview" |
| | | mode="aspectFill" /> |
| | | <view v-else-if="file.type === 'video'" |
| | | class="video-preview"> |
| | | <uni-icons type="videocam" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px"></uni-icons> |
| | | <text class="video-text">è§é¢</text> |
| | | </view> |
| | | <view class="delete-btn" |
| | | @click="removeFile(index)"> |
| | | <u-icon name="close" |
| | | size="12" |
| | | color="#fff"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.bucketFilename || file.name || (file.type === 'image' ? 'å¾ç' : 'è§é¢') }}</text> |
| | | <text class="file-size">{{ formatFileSize(file.size) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else |
| | | class="empty-state"> |
| | | <text>è¯·éæ©è¦ä¸ä¼ çå¾çæè§é¢</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from "vue"; |
| | | import { getToken } from "@/utils/auth"; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { type: Array, default: () => [] }, |
| | | action: { type: String, default: "" }, |
| | | uploadUrl: { type: String, default: "" }, |
| | | limit: { type: Number, default: 10 }, |
| | | uploadType: { type: Number, default: 10 }, |
| | | formData: { type: Object, default: () => ({}) }, |
| | | disabled: { type: Boolean, default: false }, |
| | | }); |
| | | |
| | | const emit = defineEmits([ |
| | | "update:modelValue", |
| | | "uploaded", |
| | | "uploading", |
| | | "progress", |
| | | "choose-media", |
| | | ]); |
| | | |
| | | const uploading = ref(false); |
| | | const uploadProgress = ref(0); |
| | | |
| | | // çå¬ä¸ä¼ ç¶æï¼ååºäºä»¶ |
| | | const watchUploading = val => { |
| | | emit("uploading", val); |
| | | }; |
| | | // çå¬è¿åº¦ï¼ååºäºä»¶ |
| | | const watchProgress = val => { |
| | | emit("progress", val); |
| | | }; |
| | | |
| | | const showToast = message => { |
| | | uni.showToast({ title: message, icon: "none" }); |
| | | }; |
| | | |
| | | const updateList = list => { |
| | | emit("update:modelValue", list); |
| | | }; |
| | | |
| | | const formatFileSize = size => { |
| | | if (!size) return "0 B"; |
| | | const units = ["B", "KB", "MB", "GB"]; |
| | | let index = 0; |
| | | let fileSize = size; |
| | | while (fileSize >= 1024 && index < units.length - 1) { |
| | | fileSize /= 1024; |
| | | index++; |
| | | } |
| | | return `${fileSize.toFixed(2)} ${units[index]}`; |
| | | }; |
| | | |
| | | const removeFile = index => { |
| | | const next = props.modelValue.slice(); |
| | | next.splice(index, 1); |
| | | updateList(next); |
| | | }; |
| | | |
| | | const uploadFile = file => { |
| | | const url = props.action || props.uploadUrl; |
| | | if (!url) { |
| | | showToast("æªé
ç½®ä¸ä¼ å°å"); |
| | | return; |
| | | } |
| | | |
| | | const token = getToken(); |
| | | if (!token) { |
| | | showToast("ç¨æ·æªç»å½"); |
| | | return; |
| | | } |
| | | |
| | | uploading.value = true; |
| | | watchUploading(true); |
| | | uploadProgress.value = 0; |
| | | watchProgress(0); |
| | | |
| | | const uploadTask = uni.uploadFile({ |
| | | url: url, |
| | | filePath: file.tempFilePath, |
| | | name: "files", |
| | | header: { |
| | | Authorization: `Bearer ${token}`, |
| | | }, |
| | | formData: { |
| | | type: props.uploadType, |
| | | ...props.formData, |
| | | }, |
| | | success: res => { |
| | | try { |
| | | const data = JSON.parse(res.data); |
| | | if (data.code === 200) { |
| | | const resultData = Array.isArray(data.data) |
| | | ? data.data[0] |
| | | : data.data; |
| | | const finalUrl = resultData.url || resultData.previewURL; |
| | | const finalName = resultData.name || resultData.originalFilename; |
| | | const finalId = |
| | | resultData.tempId || resultData.id || resultData.tempFileId; |
| | | |
| | | const uploadedFile = { |
| | | ...file, |
| | | ...resultData, |
| | | url: finalUrl, |
| | | name: finalName, |
| | | tempId: finalId, |
| | | status: "success", |
| | | }; |
| | | |
| | | updateList(props.modelValue.concat([uploadedFile])); |
| | | emit("uploaded", uploadedFile); |
| | | uni.showToast({ title: "ä¸ä¼ æå", icon: "success" }); |
| | | } else { |
| | | showToast(data.msg || "ä¸ä¼ 失败"); |
| | | } |
| | | } catch (e) { |
| | | showToast("è§£æååºå¤±è´¥"); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | console.error("ä¸ä¼ 失败:", err); |
| | | showToast("ä¸ä¼ 失败"); |
| | | }, |
| | | complete: () => { |
| | | uploading.value = false; |
| | | watchUploading(false); |
| | | }, |
| | | }); |
| | | |
| | | uploadTask.onProgressUpdate(res => { |
| | | uploadProgress.value = res.progress; |
| | | watchProgress(res.progress); |
| | | }); |
| | | }; |
| | | |
| | | const pickMedia = type => { |
| | | emit("choose-media", type); |
| | | |
| | | if (props.modelValue.length >= props.limit) { |
| | | showToast(`æå¤åªè½éæ©${props.limit}个æä»¶`); |
| | | return; |
| | | } |
| | | |
| | | const remaining = props.limit - props.modelValue.length; |
| | | |
| | | if (typeof uni.chooseMedia === "function") { |
| | | uni.chooseMedia({ |
| | | count: Math.min(remaining, 1), |
| | | mediaType: [type || "image"], |
| | | sizeType: ["compressed", "original"], |
| | | sourceType: ["camera"], |
| | | success: res => { |
| | | try { |
| | | const files = res?.tempFiles || []; |
| | | if (!files.length) throw new Error("æªè·åå°æä»¶"); |
| | | |
| | | files.forEach((tf, idx) => { |
| | | const filePath = tf.tempFilePath || tf.path || ""; |
| | | const fileType = tf.fileType || type || "image"; |
| | | const ext = fileType === "video" ? "mp4" : "jpg"; |
| | | const file = { |
| | | tempFilePath: filePath, |
| | | path: filePath, |
| | | type: fileType, |
| | | name: `${fileType}_${Date.now()}_${idx}.${ext}`, |
| | | size: tf.size || 0, |
| | | duration: tf.duration || 0, |
| | | createTime: Date.now(), |
| | | }; |
| | | uploadFile(file); |
| | | }); |
| | | } catch (err) { |
| | | showToast(err.message || "å¤çæä»¶å¤±è´¥"); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | console.error("éæ©åªä½å¤±è´¥:", err); |
| | | showToast("éæ©å¤±è´¥"); |
| | | }, |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | if (type === "video") { |
| | | uni.chooseVideo({ |
| | | sourceType: ["camera"], |
| | | success: res => { |
| | | const file = { |
| | | tempFilePath: res.tempFilePath, |
| | | path: res.tempFilePath, |
| | | type: "video", |
| | | name: `video_${Date.now()}.mp4`, |
| | | size: res.size || 0, |
| | | duration: res.duration || 0, |
| | | createTime: Date.now(), |
| | | }; |
| | | uploadFile(file); |
| | | }, |
| | | fail: () => { |
| | | showToast("éæ©è§é¢å¤±è´¥"); |
| | | }, |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | uni.chooseImage({ |
| | | count: Math.min(remaining, 9), |
| | | sizeType: ["compressed"], |
| | | sourceType: ["camera"], |
| | | success: res => { |
| | | const list = res.tempFilePaths || res.tempFiles || []; |
| | | list.forEach((src, idx) => { |
| | | const path = typeof src === "string" ? src : src.path; |
| | | const file = { |
| | | tempFilePath: path, |
| | | path: path, |
| | | type: "image", |
| | | name: `image_${Date.now()}_${idx}.jpg`, |
| | | size: 0, |
| | | createTime: Date.now(), |
| | | }; |
| | | uploadFile(file); |
| | | }); |
| | | }, |
| | | fail: () => { |
| | | showToast("éæ©å¾ç失败"); |
| | | }, |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .upload-buttons { |
| | | display: flex; |
| | | } |
| | | |
| | | .upload-progress { |
| | | margin-top: 12px; |
| | | } |
| | | |
| | | .file-list { |
| | | margin-top: 12px; |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10px; |
| | | background: #f7f8fa; |
| | | border-radius: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .file-preview-container { |
| | | position: relative; |
| | | width: 60px; |
| | | height: 60px; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | margin-right: 12px; |
| | | background: #e9edf2; |
| | | } |
| | | |
| | | .file-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .video-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | } |
| | | |
| | | .video-text { |
| | | font-size: 12px; |
| | | color: #fff; |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: 4px; |
| | | right: 4px; |
| | | width: 18px; |
| | | height: 18px; |
| | | border-radius: 9px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | } |
| | | |
| | | .file-info { |
| | | flex: 1; |
| | | min-width: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .file-name { |
| | | font-size: 13px; |
| | | color: #333; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .file-size { |
| | | font-size: 12px; |
| | | color: #999; |
| | | } |
| | | |
| | | .empty-state { |
| | | margin-top: 12px; |
| | | padding: 14px 0; |
| | | text-align: center; |
| | | color: #999; |
| | | font-size: 13px; |
| | | } |
| | | </style> |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view /> |
| | | </template> |
| | | |
| | | <script> |
| | | let mainActivity; |
| | | let receiver; |
| | | let intentFilter; |
| | | let isRegistered = false; |
| | | let lock = false; |
| | | |
| | | export default { |
| | | name: "PdaScan", |
| | | props: { |
| | | active: { type: Boolean, default: true }, |
| | | eventName: { type: String, default: "scan" }, |
| | | action: { type: String, default: "" }, |
| | | extraKey: { type: String, default: "" }, |
| | | debounceMs: { type: Number, default: 150 }, |
| | | }, |
| | | mounted() { |
| | | this.init(); |
| | | if (this.active) this.start(); |
| | | }, |
| | | beforeUnmount() { |
| | | this.stop(); |
| | | }, |
| | | watch: { |
| | | active(val) { |
| | | if (val) { |
| | | this.init(); |
| | | this.start(); |
| | | } else { |
| | | this.stop(); |
| | | } |
| | | }, |
| | | }, |
| | | methods: { |
| | | canUseAndroidBroadcast() { |
| | | try { |
| | | if (typeof plus === "undefined") return false; |
| | | if (!plus.os || plus.os.name !== "Android") return false; |
| | | if (!plus.android) return false; |
| | | return true; |
| | | } catch (e) { |
| | | return false; |
| | | } |
| | | }, |
| | | resolveBroadcastConfig(sys) { |
| | | if (this.action && this.extraKey) { |
| | | return { action: this.action, extraKey: this.extraKey }; |
| | | } |
| | | |
| | | const brand = String(sys?.brand || "").toUpperCase(); |
| | | const model = String(sys?.model || "").toUpperCase(); |
| | | |
| | | if ( |
| | | brand.includes("DROI") || |
| | | model.includes("MV-IDP5204") || |
| | | model.includes("IDP") |
| | | ) { |
| | | return { action: "com.service.scanner.data", extraKey: "ScanCode" }; |
| | | } |
| | | |
| | | return { action: "", extraKey: "" }; |
| | | }, |
| | | init() { |
| | | if (!this.canUseAndroidBroadcast()) return; |
| | | if (mainActivity && receiver && intentFilter) return; |
| | | |
| | | let sys = {}; |
| | | try { |
| | | sys = uni.getSystemInfoSync() || {}; |
| | | } catch (e) { |
| | | sys = {}; |
| | | } |
| | | |
| | | const cfg = this.resolveBroadcastConfig(sys); |
| | | if (!cfg.action || !cfg.extraKey) return; |
| | | this.initAndroidReceiver(cfg.action, cfg.extraKey); |
| | | }, |
| | | initAndroidReceiver(action, extraKey) { |
| | | const _this = this; |
| | | mainActivity = plus.android.runtimeMainActivity(); |
| | | const IntentFilter = plus.android.importClass( |
| | | "android.content.IntentFilter" |
| | | ); |
| | | intentFilter = new IntentFilter(); |
| | | intentFilter.addAction(action); |
| | | |
| | | receiver = plus.android.implements( |
| | | "io.dcloud.feature.internal.reflect.BroadcastReceiver", |
| | | { |
| | | onReceive(context, intent) { |
| | | plus.android.importClass(intent); |
| | | const code = intent.getStringExtra(extraKey); |
| | | _this.emitCode(code); |
| | | }, |
| | | } |
| | | ); |
| | | }, |
| | | start() { |
| | | if (!this.canUseAndroidBroadcast()) return; |
| | | if (!mainActivity || !receiver || !intentFilter) return; |
| | | if (isRegistered) return; |
| | | try { |
| | | mainActivity.registerReceiver(receiver, intentFilter); |
| | | isRegistered = true; |
| | | } catch (e) {} |
| | | }, |
| | | stop() { |
| | | if (!this.canUseAndroidBroadcast()) return; |
| | | if (!mainActivity || !receiver) return; |
| | | if (!isRegistered) return; |
| | | try { |
| | | mainActivity.unregisterReceiver(receiver); |
| | | } catch (e) {} |
| | | isRegistered = false; |
| | | }, |
| | | emitCode(code) { |
| | | const value = code == null ? "" : String(code); |
| | | const text = value.replace(/\n/g, "").trim(); |
| | | if (!text) return; |
| | | |
| | | if (lock) return; |
| | | lock = true; |
| | | setTimeout(() => { |
| | | lock = false; |
| | | }, this.debounceMs); |
| | | |
| | | const payload = { code: text }; |
| | | uni.$emit(this.eventName, payload); |
| | | this.$emit("scan", payload); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | mode="date" /> |
| | | <PdaScan :active="pdaScanActive" |
| | | eventName="scan_repair_device" /> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | import { onShow, onLoad } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import CommonUpload from "@/components/CommonUpload.vue"; |
| | | import PdaScan from "@/components/pda-scan/pda-scan.vue"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | import { |
| | | addRepair, |
| | |
| | | // æ«ç ç¸å
³ç¶æ |
| | | const isScanning = ref(false); |
| | | const scanTimer = ref(null); |
| | | const pdaScanActive = ref(false); |
| | | const pdaScanTimer = ref(null); |
| | | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | |
| | | return; |
| | | } |
| | | |
| | | // è°ç¨uni-appçæ«ç API |
| | | const canUsePdaBroadcast = () => { |
| | | try { |
| | | if (typeof plus === "undefined") return false; |
| | | if (plus?.os?.name !== "Android") return false; |
| | | if (!plus?.android) return false; |
| | | |
| | | const sys = uni.getSystemInfoSync?.() || {}; |
| | | const brand = String(sys?.brand || "").toUpperCase(); |
| | | const model = String(sys?.model || "").toUpperCase(); |
| | | |
| | | return ( |
| | | brand.includes("DROI") || |
| | | model.includes("MV-IDP5204") || |
| | | model.includes("IDP") |
| | | ); |
| | | } catch (e) { |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | if (canUsePdaBroadcast()) { |
| | | if (pdaScanTimer.value) { |
| | | clearTimeout(pdaScanTimer.value); |
| | | pdaScanTimer.value = null; |
| | | } |
| | | uni.$off("scan_repair_device"); |
| | | uni.$on("scan_repair_device", data => { |
| | | pdaScanActive.value = false; |
| | | uni.$off("scan_repair_device"); |
| | | if (pdaScanTimer.value) { |
| | | clearTimeout(pdaScanTimer.value); |
| | | pdaScanTimer.value = null; |
| | | } |
| | | handleScanResult(data?.code); |
| | | }); |
| | | pdaScanActive.value = true; |
| | | uni.showToast({ title: "请使ç¨PDAæ«ç ", icon: "none" }); |
| | | pdaScanTimer.value = setTimeout(() => { |
| | | pdaScanActive.value = false; |
| | | uni.$off("scan_repair_device"); |
| | | pdaScanTimer.value = null; |
| | | }, 20000); |
| | | return; |
| | | } |
| | | |
| | | uni.scanCode({ |
| | | scanType: ["qrCode", "barCode"], |
| | | success: res => { |
| | |
| | | if (scanTimer.value) { |
| | | clearTimeout(scanTimer.value); |
| | | } |
| | | if (pdaScanTimer.value) { |
| | | clearTimeout(pdaScanTimer.value); |
| | | } |
| | | uni.$off("scan_repair_device"); |
| | | }); |
| | | |
| | | // æäº¤è¡¨å |
| | |
| | | margin-left: 8px; |
| | | cursor: pointer; |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | @confirm="onDateConfirm" |
| | | @cancel="showDate = false" |
| | | mode="date" /> |
| | | <PdaScan :active="pdaScanActive" |
| | | eventName="scan_upkeep_device" /> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import CommonUpload from "@/components/CommonUpload.vue"; |
| | | import PdaScan from "@/components/pda-scan/pda-scan.vue"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | import { |
| | | addUpkeep, |
| | |
| | | // æ«ç ç¸å
³ç¶æ |
| | | const isScanning = ref(false); |
| | | const scanTimer = ref(null); |
| | | const pdaScanActive = ref(false); |
| | | const pdaScanTimer = ref(null); |
| | | |
| | | // 表åéªè¯è§å |
| | | const formRules = { |
| | |
| | | return; |
| | | } |
| | | |
| | | // è°ç¨uni-appçæ«ç API |
| | | const canUsePdaBroadcast = () => { |
| | | try { |
| | | if (typeof plus === "undefined") return false; |
| | | if (plus?.os?.name !== "Android") return false; |
| | | if (!plus?.android) return false; |
| | | |
| | | const sys = uni.getSystemInfoSync?.() || {}; |
| | | const brand = String(sys?.brand || "").toUpperCase(); |
| | | const model = String(sys?.model || "").toUpperCase(); |
| | | |
| | | return ( |
| | | brand.includes("DROI") || |
| | | model.includes("MV-IDP5204") || |
| | | model.includes("IDP") |
| | | ); |
| | | } catch (e) { |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | if (canUsePdaBroadcast()) { |
| | | if (pdaScanTimer.value) { |
| | | clearTimeout(pdaScanTimer.value); |
| | | pdaScanTimer.value = null; |
| | | } |
| | | uni.$off("scan_upkeep_device"); |
| | | uni.$on("scan_upkeep_device", data => { |
| | | pdaScanActive.value = false; |
| | | uni.$off("scan_upkeep_device"); |
| | | if (pdaScanTimer.value) { |
| | | clearTimeout(pdaScanTimer.value); |
| | | pdaScanTimer.value = null; |
| | | } |
| | | handleScanResult(data?.code); |
| | | }); |
| | | pdaScanActive.value = true; |
| | | uni.showToast({ title: "请使ç¨PDAæ«ç ", icon: "none" }); |
| | | pdaScanTimer.value = setTimeout(() => { |
| | | pdaScanActive.value = false; |
| | | uni.$off("scan_upkeep_device"); |
| | | pdaScanTimer.value = null; |
| | | }, 20000); |
| | | return; |
| | | } |
| | | |
| | | uni.scanCode({ |
| | | scanType: ["qrCode", "barCode"], |
| | | success: res => { |
| | |
| | | if (scanTimer.value) { |
| | | clearTimeout(scanTimer.value); |
| | | } |
| | | if (pdaScanTimer.value) { |
| | | clearTimeout(pdaScanTimer.value); |
| | | } |
| | | uni.$off("scan_upkeep_device"); |
| | | }); |
| | | |
| | | // æäº¤è¡¨å |
| | |
| | | margin-left: 8px; |
| | | cursor: pointer; |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | <view class="section-card" |
| | | v-if="hasException === true"> |
| | | <view class="section-title">å·¡æ£ç
§ç/è§é¢</view> |
| | | <!-- å½ååç±»çä¸ä¼ åºå --> |
| | | <view class="upload-area"> |
| | | <view class="upload-buttons"> |
| | | <u-button type="primary" |
| | | @click="chooseMedia('image')" |
| | | :loading="uploading" |
| | | :disabled="beforeModelValue.length >= uploadConfig.limit" |
| | | :customStyle="{ marginRight: '10px', flex: 1 }"> |
| | | <u-icon name="camera" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px"></u-icon> |
| | | {{ uploading ? 'ä¸ä¼ ä¸...' : 'æç
§' }} |
| | | </u-button> |
| | | <u-button type="success" |
| | | @click="chooseMedia('video')" |
| | | :loading="uploading" |
| | | :disabled="beforeModelValue.length >= uploadConfig.limit" |
| | | :customStyle="{ flex: 1 }"> |
| | | <uni-icons type="videocam" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px"></uni-icons> |
| | | {{ uploading ? 'ä¸ä¼ ä¸...' : 'æè§é¢' }} |
| | | </u-button> |
| | | </view> |
| | | <!-- ä¸ä¼ è¿åº¦ --> |
| | | <view v-if="uploading" |
| | | class="upload-progress"> |
| | | <u-line-progress :percentage="uploadProgress" |
| | | :showText="true" |
| | | activeColor="#409eff"></u-line-progress> |
| | | </view> |
| | | <!-- å½ååç±»çæä»¶å表 --> |
| | | <view v-if="beforeModelValue.length > 0" |
| | | class="file-list"> |
| | | <view v-for="(file, index) in beforeModelValue" |
| | | :key="index" |
| | | class="file-item"> |
| | | <view class="file-preview-container"> |
| | | <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)" |
| | | :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | class="file-preview" |
| | | mode="aspectFill" /> |
| | | <view v-else-if="file.type === 'video'" |
| | | class="video-preview"> |
| | | <uni-icons type="videocam" |
| | | size="18" |
| | | color="#fff" |
| | | style="margin-right: 5px"></uni-icons> |
| | | <text class="video-text">è§é¢</text> |
| | | </view> |
| | | <!-- å é¤æé® --> |
| | | <view class="delete-btn" |
| | | @click="removeFile(index)"> |
| | | <u-icon name="close" |
| | | size="12" |
| | | color="#fff"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="file-info"> |
| | | <text class="file-name">{{ file.bucketFilename || file.name || (file.type === 'image' ? 'å¾ç' : 'è§é¢') }}</text> |
| | | <text class="file-size">{{ formatFileSize(file.size) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="beforeModelValue.length === 0" |
| | | class="empty-state"> |
| | | <text>è¯·éæ©è¦ä¸ä¼ çå¾çæè§é¢</text> |
| | | </view> |
| | | </view> |
| | | <!-- ä½¿ç¨ PDA åªä½ä¸ä¼ ç»ä»¶ --> |
| | | <PdaMediaUpload v-model="beforeModelValue" |
| | | :action="uploadFileUrl" |
| | | :limit="uploadConfig.limit" |
| | | :fileSize="uploadConfig.fileSize" |
| | | :formData="{ type: 10 }" |
| | | :allowCamera="true" |
| | | :allowVideo="true" |
| | | @update:modelValue="handleFilesUpdate" |
| | | @choose-media="handleChooseMedia" |
| | | @uploading="uploading = $event" |
| | | @progress="uploadProgress = $event" /> |
| | | <!-- ç»è®¡ä¿¡æ¯ --> |
| | | <view class="upload-summary"> |
| | | <text class="summary-text"> |
| | |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import { onLoad, onShow, onHide } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import PdaMediaUpload from "@/components/pda-media-upload/pda-media-upload.vue"; |
| | | import { uploadInspectionTask } from "@/api/inspectionManagement"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import config from "@/config"; |
| | |
| | | }); |
| | | }; |
| | | |
| | | // å 餿件 |
| | | const removeFile = index => { |
| | | beforeModelValue.value.splice(index, 1); |
| | | // ç嬿件å表ååï¼ç»ä»¶å
é¨å¤çå é¤ï¼ç¶ç»ä»¶åªåæ¥ï¼ |
| | | const handleFilesUpdate = files => { |
| | | beforeModelValue.value = files; |
| | | }; |
| | | |
| | | // çå¬ç»ä»¶ç choose-media äºä»¶ï¼ç¨äºå
¼å®¹æ§é»è¾ï¼æè
å¨è¿éæ·»å èªå®ä¹æä½ï¼ |
| | | const handleChooseMedia = type => { |
| | | // å¯ä»¥å¨è¿éæ·»å èªå®ä¹çåç½®æä½ |
| | | console.log("ç¨æ·éæ©ä¸ä¼ ç±»å:", type); |
| | | }; |
| | | |
| | | // PDA æ«ç çå¬ï¼å¯éï¼å¦æéè¦éè¿æ«ç æ·»å æä¸ªæ ç¾æä¿¡æ¯ï¼å¨è¿éå¤çï¼ |
| | | const handlePdaScan = data => { |
| | | console.log("PDA æ«ç ç»æ:", data); |
| | | uni.showToast({ |
| | | title: `æ«ç ç»æ: ${data.code}`, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | onShow(() => { |
| | | // 页颿¾ç¤ºæ¶ï¼çå¬ PDA æ«ç ï¼å¦ææéè¦çè¯ï¼ |
| | | uni.$on("scan_inspection", handlePdaScan); |
| | | }); |
| | | |
| | | onHide(() => { |
| | | uni.$off("scan_inspection"); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | </up-grid> |
| | | </view> |
| | | </view> |
| | | <PdaScan :active="pdaScanActive" |
| | | eventName="scan_work_report" /> |
| | | <DownloadProgressMask /> |
| | | </view> |
| | | </template> |
| | |
| | | import { userLoginFacotryList } from "@/api/login"; |
| | | import { getProductWorkOrderById } from "@/api/productionManagement/productionReporting"; |
| | | import DownloadProgressMask from "@/components/DownloadProgressMask.vue"; |
| | | import PdaScan from "@/components/pda-scan/pda-scan.vue"; |
| | | import modal from "@/plugins/modal"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | |
| | |
| | | const show = ref(false); |
| | | const factoryList = ref([]); |
| | | const factoryListTem = ref([]); |
| | | const pdaScanActive = ref(false); |
| | | let scanWorkReportTimer = null; |
| | | const iconStyle = { |
| | | fontSize: "1.125rem", |
| | | color: "#2979ff", |
| | |
| | | }); |
| | | } |
| | | const getcode = async () => { |
| | | uni.scanCode({ |
| | | success: async res => { |
| | | // è§£æäºç»´ç å
容 |
| | | const scanResult = res.result; |
| | | let orderRow = ""; |
| | | const canUsePdaBroadcast = () => { |
| | | try { |
| | | if (typeof plus === "undefined") return false; |
| | | if (plus?.os?.name !== "Android") return false; |
| | | if (!plus?.android) return false; |
| | | |
| | | // å¤ææ«æç»ææ¯å¦ä¸ºçº¯æ°åï¼idï¼ |
| | | const isNumericId = /^\d+$/.test(scanResult.trim()); |
| | | const sys = uni.getSystemInfoSync?.() || {}; |
| | | const brand = String(sys?.brand || "").toUpperCase(); |
| | | const model = String(sys?.model || "").toUpperCase(); |
| | | |
| | | if (isNumericId) { |
| | | // 妿æ¯çº¯æ°åï¼æ ¹æ® id è·åå·¥åæ°æ® |
| | | const workOrderId = scanResult.trim(); |
| | | modal.loading("æ£å¨è·åå·¥åä¿¡æ¯..."); |
| | | try { |
| | | const workRes = await getProductWorkOrderById({ id: workOrderId }); |
| | | modal.closeLoading(); |
| | | return ( |
| | | brand.includes("DROI") || |
| | | model.includes("MV-IDP5204") || |
| | | model.includes("IDP") |
| | | ); |
| | | } catch (e) { |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | console.log("å·¥åæ¥è¯¢ç»æ:", workRes); |
| | | const handleWorkReportScan = async scanResultRaw => { |
| | | const scanResult = scanResultRaw == null ? "" : String(scanResultRaw); |
| | | let orderRow = ""; |
| | | |
| | | if (workRes.code === 200 && workRes.data) { |
| | | // æ°æ¥å£è¿åçæ¯å个对象ï¼ä¸æ¯æ°ç» |
| | | const workData = workRes.data; |
| | | console.log("工忰æ®:", workData); |
| | | const isNumericId = /^\d+$/.test(scanResult.trim()); |
| | | |
| | | if (workData.endOrder === true) { |
| | | modal.msgError("该订åå·²ç»æï¼æ æ³æ¥å·¥"); |
| | | return; |
| | | } |
| | | if (isNumericId) { |
| | | const workOrderId = scanResult.trim(); |
| | | modal.loading("æ£å¨è·åå·¥åä¿¡æ¯..."); |
| | | try { |
| | | const workRes = await getProductWorkOrderById({ id: workOrderId }); |
| | | modal.closeLoading(); |
| | | |
| | | orderRow = JSON.stringify(workData); |
| | | console.log("å·¥åæ¥è¯¢ç»æ:", workRes); |
| | | |
| | | console.log("æé çorderRow:", orderRow); |
| | | } else { |
| | | modal.msgError("æªæ¾å°å¯¹åºçå·¥åä¿¡æ¯"); |
| | | if (workRes.code === 200 && workRes.data) { |
| | | const workData = workRes.data; |
| | | console.log("工忰æ®:", workData); |
| | | |
| | | if (workData.endOrder === true) { |
| | | modal.msgError("该订åå·²ç»æï¼æ æ³æ¥å·¥"); |
| | | return; |
| | | } |
| | | } catch (error) { |
| | | modal.closeLoading(); |
| | | console.error("è·åå·¥åä¿¡æ¯å¤±è´¥:", error); |
| | | modal.msgError("è·åå·¥åä¿¡æ¯å¤±è´¥: " + (error.message || "æªç¥é误")); |
| | | return; |
| | | } |
| | | } else { |
| | | // 妿䏿¯çº¯æ°åï¼å°è¯ä»æ«ç ç»æä¸æåorderRowåæ° |
| | | try { |
| | | // å¤çæ··åæ ¼å¼: http://...?orderRow={...} |
| | | const orderRowStart = scanResult.indexOf("orderRow={"); |
| | | if (orderRowStart !== -1) { |
| | | // æåä»orderRow={å¼å§çJSONå
容 |
| | | const jsonPart = scanResult.substring(orderRowStart + 9); // 9æ¯"orderRow=".length |
| | | orderRow = jsonPart; |
| | | } else { |
| | | // å¦æç´æ¥æ¯JSONå符串ï¼å°è¯è§£æ |
| | | orderRow = scanResult; |
| | | } |
| | | } catch (e) { |
| | | console.error(e, "è§£æå¤±è´¥====????====="); |
| | | orderRow = ""; |
| | | } |
| | | |
| | | // éªè¯æ¯å¦ä¸ºææçJSON |
| | | try { |
| | | JSON.parse(orderRow); |
| | | } catch (error) { |
| | | modal.msgError("订åè§£æå¤±è´¥ï¼è¯·æ£æ¥äºç»´ç æ ¼å¼"); |
| | | orderRow = JSON.stringify(workData); |
| | | |
| | | console.log("æé çorderRow:", orderRow); |
| | | } else { |
| | | modal.msgError("æªæ¾å°å¯¹åºçå·¥åä¿¡æ¯"); |
| | | return; |
| | | } |
| | | } catch (error) { |
| | | modal.closeLoading(); |
| | | console.error("è·åå·¥åä¿¡æ¯å¤±è´¥:", error); |
| | | modal.msgError("è·åå·¥åä¿¡æ¯å¤±è´¥: " + (error.message || "æªç¥é误")); |
| | | return; |
| | | } |
| | | // æ«ç æåå跳转å°ç产æ¥å·¥é¡µé¢ï¼å¹¶ä¼ éorderRowåæ° |
| | | uni.navigateTo({ |
| | | url: `/pages/productionManagement/productionReport/index?orderRow=${orderRow}`, |
| | | }); |
| | | } else { |
| | | try { |
| | | const orderRowStart = scanResult.indexOf("orderRow={"); |
| | | if (orderRowStart !== -1) { |
| | | const jsonPart = scanResult.substring(orderRowStart + 9); |
| | | orderRow = jsonPart; |
| | | } else { |
| | | orderRow = scanResult; |
| | | } |
| | | } catch (e) { |
| | | console.error(e, "è§£æå¤±è´¥====????====="); |
| | | orderRow = ""; |
| | | } |
| | | |
| | | try { |
| | | JSON.parse(orderRow); |
| | | } catch (error) { |
| | | modal.msgError("订åè§£æå¤±è´¥ï¼è¯·æ£æ¥äºç»´ç æ ¼å¼"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | uni.navigateTo({ |
| | | url: `/pages/productionManagement/productionReport/index?orderRow=${orderRow}`, |
| | | }); |
| | | }; |
| | | |
| | | if (canUsePdaBroadcast()) { |
| | | if (scanWorkReportTimer) { |
| | | clearTimeout(scanWorkReportTimer); |
| | | scanWorkReportTimer = null; |
| | | } |
| | | uni.$off("scan_work_report"); |
| | | uni.$on("scan_work_report", async data => { |
| | | pdaScanActive.value = false; |
| | | uni.$off("scan_work_report"); |
| | | if (scanWorkReportTimer) { |
| | | clearTimeout(scanWorkReportTimer); |
| | | scanWorkReportTimer = null; |
| | | } |
| | | await handleWorkReportScan(data?.code); |
| | | }); |
| | | pdaScanActive.value = true; |
| | | uni.showToast({ |
| | | title: "请使ç¨PDAæ«ç ", |
| | | icon: "none", |
| | | }); |
| | | scanWorkReportTimer = setTimeout(() => { |
| | | pdaScanActive.value = false; |
| | | uni.$off("scan_work_report"); |
| | | scanWorkReportTimer = null; |
| | | }, 20000); |
| | | return; |
| | | } |
| | | |
| | | uni.scanCode({ |
| | | success: async res => { |
| | | await handleWorkReportScan(res.result); |
| | | }, |
| | | fail: err => { |
| | | uni.showToast({ |