chore: initial commit

This commit is contained in:
2026-07-30 18:04:37 +08:00
commit 2ffacfd668
356 changed files with 43448 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
export const AUDIO_FEEDBACK_SOURCES = Object.freeze({
success: '/static/mp3/success.mp3',
error: '/static/mp3/fail.mp3'
})
let audioContext = null
function getAudioContext() {
if (audioContext) return audioContext
if (typeof uni === 'undefined' || typeof uni.createInnerAudioContext !== 'function') return null
audioContext = uni.createInnerAudioContext()
audioContext.autoplay = false
audioContext.loop = false
audioContext.volume = 1
return audioContext
}
export function playFeedbackSound(type) {
const source = AUDIO_FEEDBACK_SOURCES[type]
const player = getAudioContext()
if (!source || !player) return false
try {
player.stop()
player.src = source
player.play()
return true
} catch (_) {
return false
}
}
export function playSuccessSound() {
return playFeedbackSound('success')
}
export function playErrorSound() {
return playFeedbackSound('error')
}
export function stopFeedbackSound() {
if (!audioContext) return
try { audioContext.stop() } catch (_) {}
}
export function destroyFeedbackSound() {
if (!audioContext) return
try { audioContext.destroy() } catch (_) {}
audioContext = null
}
+23
View File
@@ -0,0 +1,23 @@
const HOME_URL = '/pages/home/home'
export function getCurrentRoute() {
if (typeof getCurrentPages !== 'function') return ''
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
return currentPage ? currentPage.route || '' : ''
}
export function navigateBackOr(fallbackUrl = HOME_URL) {
const pages = typeof getCurrentPages === 'function' ? getCurrentPages() : []
if (pages.length > 1) {
uni.navigateBack({ fail: () => uni.reLaunch({ url: fallbackUrl }) })
return
}
uni.reLaunch({ url: fallbackUrl })
}
export function navigateToUnique(url) {
const route = url.split('?')[0].replace(/^\//, '')
if (getCurrentRoute() === route) return
uni.navigateTo({ url })
}
+67
View File
@@ -0,0 +1,67 @@
export const ITEM_STATUS = Object.freeze({
PENDING: 'pending',
PICKED: 'picked',
PARTIAL: 'partial',
SKIPPED: 'skipped'
})
export function createItemStates(items) {
return items.reduce((states, item) => {
states[item.id] = ITEM_STATUS.PENDING
return states
}, {})
}
export function getPickSummary(items, states) {
const summary = {
total: items.length,
picked: 0,
partial: 0,
skipped: 0,
pending: 0
}
items.forEach((item) => {
const status = states[item.id] || ITEM_STATUS.PENDING
summary[status] += 1
})
summary.processed = summary.picked + summary.partial + summary.skipped
summary.progress = summary.total
? Math.round(summary.processed / summary.total * 100)
: 0
summary.canSubmit = summary.total > 0 && (
summary.pending === 0 || summary.picked > 0 || summary.partial > 0
)
summary.buttonText = '提交拣货完成'
return summary
}
export function submitStates(items, states) {
const nextStates = { ...states }
const autoSkippedIds = []
items.forEach((item) => {
const status = nextStates[item.id] || ITEM_STATUS.PENDING
if (status === ITEM_STATUS.PENDING) {
nextStates[item.id] = ITEM_STATUS.SKIPPED
autoSkippedIds.push(item.id)
}
})
return {
states: nextStates,
autoSkippedIds
}
}
export function classifyScan(items, states, scanCode) {
const item = items.find((entry) => entry.sku === scanCode) || null
if (!item) return { type: 'unknown', item: null }
const status = states[item.id] || ITEM_STATUS.PENDING
if (status === ITEM_STATUS.PENDING) return { type: 'pending', item }
return { type: 'processed', item, status }
}
+76
View File
@@ -0,0 +1,76 @@
export const SCAN_BROADCAST_ACTION = 'android.intent.action.SCANRESULT'
export const SCAN_BROADCAST_EXTRA = 'value'
const DUPLICATE_WINDOW_MS = 80
let receiver = null
let activity = null
let lastScan = { value: '', receivedAt: 0 }
export function normalizeScanValue(value) {
if (value === null || value === undefined) return ''
return String(value).trim()
}
export function isDuplicateScan(value, receivedAt = Date.now()) {
const duplicate = value === lastScan.value && receivedAt - lastScan.receivedAt < DUPLICATE_WINDOW_MS
if (!duplicate) lastScan = { value, receivedAt }
return duplicate
}
export function resetScanDeduplication() {
lastScan = { value: '', receivedAt: 0 }
}
export function startScanBroadcast({ onScan, onError } = {}) {
if (typeof onScan !== 'function') return () => {}
// #ifdef APP-PLUS
try {
if (receiver) stopScanBroadcast()
activity = plus.android.runtimeMainActivity()
const IntentFilter = plus.android.importClass('android.content.IntentFilter')
const filter = new IntentFilter()
filter.addAction(SCAN_BROADCAST_ACTION)
receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
onReceive(context, intent) {
try {
plus.android.importClass(intent)
const value = normalizeScanValue(intent.getStringExtra(SCAN_BROADCAST_EXTRA))
if (!value || isDuplicateScan(value)) return
onScan(value)
} catch (error) {
if (typeof onError === 'function') onError(error)
}
}
})
const Build = plus.android.importClass('android.os.Build')
if (Build.VERSION.SDK_INT >= 33) {
const Context = plus.android.importClass('android.content.Context')
activity.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED)
} else {
activity.registerReceiver(receiver, filter)
}
} catch (error) {
receiver = null
activity = null
if (typeof onError === 'function') onError(error)
}
// #endif
return stopScanBroadcast
}
export function stopScanBroadcast() {
// #ifdef APP-PLUS
if (activity && receiver) {
try {
activity.unregisterReceiver(receiver)
} catch (_) {}
}
// #endif
receiver = null
activity = null
resetScanDeduplication()
}