const api = require('../../utils/api') const locationGuard = require('../../utils/locationGuard') Page({ data: { user: null, moldName: '', processName: '', attendancePointName: '', stampingMethod: '', isCleaning: false, isMisc: false, moldDisplayName: '', sceneToken: '', state: null, roleChoices: [], selectionToken: '', toastVisible: false, toastMessage: '', locationChecking: true, locationAllowed: false, locationMessage: '正在校验当前位置', currentLocation: null, occupiedDetail: null, temporaryWorkerModalVisible: false, temporaryWorkerPrompt: '', temporaryWorkerName: '', }, toastTimer: null, onLoad(options) { this.resolveOpenMold(options) }, async resolveOpenMold(options) { const scene = this.decodeRepeated(options.scene || '') let moldName = this.extractMoldName(options, scene) let processName = this.extractProcessName(options, scene) let attendancePointName = this.decodeRepeated(options.attendancePointName || options.attendance_point_name || '') let stampingMethod = this.decodeRepeated(options.stampingMethod || options.stamping_method || '') let isCleaning = false let isMisc = false let moldDisplayName = api.moldDisplayName(moldName, processName, stampingMethod) const sceneToken = this.extractSceneToken(scene || options.moldName || '') if (!moldName && sceneToken && api.getCurrentUser()) { try { const resolved = await api.resolveMoldScene(sceneToken) moldName = resolved.moldName processName = resolved.processName attendancePointName = resolved.attendancePointName || '' stampingMethod = resolved.stampingMethod || '' isCleaning = !!resolved.isCleaning isMisc = !!resolved.isMisc moldDisplayName = resolved.displayName } catch (error) { this.showTip(error.message || '未找到模具二维码') } } this.setData({ moldName, processName, attendancePointName, stampingMethod, isCleaning, isMisc, moldDisplayName, sceneToken }) this.verifyOpenLocation() }, async resolveSceneAfterLogin() { if (this.data.moldName || !this.data.sceneToken) { return true } try { const resolved = await api.resolveMoldScene(this.data.sceneToken) this.setData({ moldName: resolved.moldName, processName: resolved.processName, attendancePointName: resolved.attendancePointName || '', stampingMethod: resolved.stampingMethod || '', isCleaning: !!resolved.isCleaning, isMisc: !!resolved.isMisc, moldDisplayName: resolved.displayName, }) await this.verifyOpenLocation() return false } catch (error) { this.showTip(error.message || '未找到模具二维码') return false } }, extractMoldName(options, scene) { const explicit = options.moldName || options.deviceNo if (explicit) { const value = this.decodeRepeated(explicit) return value.startsWith('mold=') ? '' : value } const raw = this.decodeRepeated(scene) const moldMatched = raw.match(/(?:^|[?&])?moldName=([^&]+)/) if (moldMatched && moldMatched[1]) { return this.decodeRepeated(moldMatched[1]) } const oldMatched = raw.match(/(?:^|[?&])?deviceNo=([^&]+)/) if (oldMatched && oldMatched[1]) { return this.decodeRepeated(oldMatched[1]) } if (raw.startsWith('mold=')) { return '' } return raw }, extractProcessName(options, scene) { const explicit = options.processName || options.process_name if (explicit) { return this.decodeRepeated(explicit) } const raw = this.decodeRepeated(scene) const matched = raw.match(/(?:^|[?&])?(?:processName|process_name)=([^&]+)/) return matched && matched[1] ? this.decodeRepeated(matched[1]) : '' }, extractSceneToken(scene) { const raw = this.decodeRepeated(scene) const matched = raw.match(/(?:^|[?&])?mold=([^&]+)/) return matched && matched[1] ? this.decodeRepeated(matched[1]) : '' }, decodeRepeated(value) { let text = String(value || '').trim() for (let index = 0; index < 3; index += 1) { try { const decoded = decodeURIComponent(text) if (decoded === text) { break } text = decoded } catch (error) { break } } return text }, isCleaningProcess() { return !!this.data.isCleaning || String(this.data.stampingMethod || '').trim() === '清洗' }, openCleaningReport() { const moldName = encodeURIComponent(this.data.moldName || '') const processName = encodeURIComponent(this.data.processName || '') const stampingMethod = encodeURIComponent(this.data.stampingMethod || '') const displayName = encodeURIComponent(this.data.moldDisplayName || '') const attendancePointName = encodeURIComponent(this.data.attendancePointName || '') wx.redirectTo({ url: `/pages/cleaningReport/cleaningReport?moldName=${moldName}&processName=${processName}&attendancePointName=${attendancePointName}&stampingMethod=${stampingMethod}&isCleaning=1&displayName=${displayName}`, }) }, onShow() { if (this.data.locationAllowed) { this.load() } }, onUnload() { if (this.toastTimer) { clearTimeout(this.toastTimer) this.toastTimer = null } }, showTip(message, duration = 5000) { if (this.toastTimer) { clearTimeout(this.toastTimer) } this.setData({ toastVisible: true, toastMessage: String(message || ''), }) this.toastTimer = setTimeout(() => { this.setData({ toastVisible: false, toastMessage: '' }) this.toastTimer = null }, duration) }, async verifyOpenLocation() { this.setData({ locationChecking: true, locationAllowed: false, locationMessage: '正在校验当前位置', }) try { const locationScope = api.getCurrentUser() ? 'accessible' : 'public' const locationResult = await locationGuard.ensureFactoryLocation(this.data.attendancePointName, locationScope) this.setData({ locationChecking: false, locationAllowed: true, locationMessage: '', currentLocation: locationResult.current, }) await this.load() } catch (error) { this.setData({ locationChecking: false, locationAllowed: false, locationMessage: error.message || '当前位置不在允许范围内,不能扫码报工', currentLocation: null, user: null, state: null, }) } }, async load() { let user = api.getCurrentUser() let state = null this.setData({ occupiedDetail: null }) if (user && !this.data.moldName && this.data.sceneToken) { const shouldContinue = await this.resolveSceneAfterLogin() if (!shouldContinue) { return } } if (user && this.data.moldName) { try { user = await api.refreshCurrentUser() || user if (this.isCleaningProcess()) { this.setData({ user, state: null }, () => { this.openCleaningReport() }) return } state = await api.getClockState( this.data.moldName, this.data.processName, this.data.attendancePointName, this.data.currentLocation, ) if (state && state.isCleaning) { this.setData({ user, state: null, stampingMethod: state.stampingMethod || '', isCleaning: true, isMisc: false, }, () => { this.openCleaningReport() }) return } if (state && state.isMisc) { this.setData({ stampingMethod: state.stampingMethod || '', isMisc: true, moldDisplayName: state.displayName || this.data.moldDisplayName, }) } } catch (error) { if (await this.handleMoldOccupiedError(error)) { user = api.getCurrentUser() this.setData({ user, state: null }) return } if (await this.handleUnfinishedReportError(error)) { user = api.getCurrentUser() this.setData({ user, state: null }) return } this.showTip(error.message) user = api.getCurrentUser() } } this.setData({ user, state }) }, async wechatLogin(e) { const phoneCode = e.detail && e.detail.code if (!phoneCode) { this.showTip('未获得手机号授权', 3000) return } wx.showLoading({ title: '登录中' }) try { const result = await api.loginWithPhoneCode(phoneCode) if (result.needsTemporaryWorker) { wx.hideLoading() await this.confirmTemporaryWorker(result) return } if (result.needsSelection) { this.setData({ roleChoices: result.matchedPeople, selectionToken: result.selectionToken, }) return } await this.load() } catch (error) { this.showTip(error.message) } finally { wx.hideLoading() } }, confirmTemporaryWorker(result) { return new Promise(resolve => { this.temporaryWorkerResult = result this.temporaryWorkerResolve = resolve this.setData({ temporaryWorkerModalVisible: true, temporaryWorkerPrompt: `该手机号${result.temporaryPhone || ''}不在人员清单中。临时工账号有效期只有24小时,请填写姓名后创建。`, temporaryWorkerName: '', }) }) }, onTemporaryWorkerNameInput(e) { this.setData({ temporaryWorkerName: e.detail.value }) }, closeTemporaryWorkerModal() { this.setData({ temporaryWorkerModalVisible: false, temporaryWorkerPrompt: '', temporaryWorkerName: '', }) this.temporaryWorkerResult = null if (this.temporaryWorkerResolve) { this.temporaryWorkerResolve() this.temporaryWorkerResolve = null } }, async submitTemporaryWorker() { const result = this.temporaryWorkerResult const name = String(this.data.temporaryWorkerName || '').trim() if (!name) { this.showTip('请输入姓名', 3000) return } if (!result || !result.temporaryToken) { this.showTip('临时工注册信息已失效,请重新登录') this.closeTemporaryWorkerModal() return } this.setData({ temporaryWorkerModalVisible: false }) wx.showLoading({ title: '定位中' }) try { const attendancePoint = await locationGuard.ensureTemporaryWorkerLocation() wx.showLoading({ title: '创建中' }) await api.createTemporaryWorker(result.temporaryToken, name, attendancePoint) await this.load() wx.showToast({ title: '临时工已生效', icon: 'success' }) } catch (error) { this.showTip(error.message) } finally { wx.hideLoading() this.closeTemporaryWorkerModal() } }, async chooseRole(e) { const role = e.currentTarget.dataset.role wx.showLoading({ title: '登录中' }) try { await api.selectRole(this.data.selectionToken, role) this.setData({ roleChoices: [], selectionToken: '' }) await this.load() } catch (error) { this.showTip(error.message) } finally { wx.hideLoading() } }, canOperate() { const { user, moldName, state } = this.data if (!user) { this.showTip('请先登录', 3000) return false } if (user.role !== 'worker') { this.showTip('只有冲压工人可以报工', 3000) return false } if (!moldName) { this.showTip('缺少模具名称', 3000) return false } if (!state) { this.showTip('当前模具码不可用', 3000) return false } return true }, formatAutoSubmitHours(value) { const hours = Number(value || 15) if (!Number.isFinite(hours)) { return '15' } return Number.isInteger(hours) ? String(hours) : String(Math.round(hours * 100) / 100) }, confirmStartWork() { const hoursText = this.formatAutoSubmitHours(this.data.state && this.data.state.autoSubmitHours) return new Promise(resolve => { wx.showModal({ title: '开始上班', content: `该班次时长最多为${hoursText}个小时。${hoursText}小时后未下班报工,系统将自动报工`, confirmText: '开始上班', cancelText: '取消', success: res => resolve(!!res.confirm), fail: () => resolve(false), }) }) }, async switchMold() { if (!this.canOperate()) { return } try { await api.switchDevice( this.data.moldName, this.data.processName, this.data.attendancePointName, this.data.currentLocation, ) wx.showToast({ title: '已记录换模具', icon: 'success' }) await this.load() } catch (error) { if (await this.handleMoldOccupiedError(error)) { return } if (await this.handleUnfinishedReportError(error)) { return } this.showTip(error.message) } }, async finishWork() { if (!this.canOperate()) { return } try { const session = await api.finishWork( this.data.moldName, this.data.processName, this.data.attendancePointName, this.data.currentLocation, ) wx.navigateTo({ url: `/pages/reportForm/reportForm?sessionId=${session.id}`, }) } catch (error) { if (await this.handleMoldOccupiedError(error)) { return } if (await this.handleUnfinishedReportError(error)) { return } this.showTip(error.message) } }, async handleAction() { if (!this.canOperate()) { return } const { moldName, state } = this.data try { if (state.action === 'start') { const confirmed = await this.confirmStartWork() if (!confirmed) { return } await api.startWork(moldName, this.data.processName, this.data.attendancePointName, this.data.currentLocation) wx.showToast({ title: '已开始上班', icon: 'success' }) await this.load() return } if (state.action === 'switch') { await this.switchMold() return } if (state.action === 'report' || (state.action === 'finish' && state.endAtText && state.session)) { wx.navigateTo({ url: `/pages/reportForm/reportForm?sessionId=${state.session.id}`, }) return } if (state.action === 'refinish') { await this.finishWork() return } await this.finishWork() } catch (error) { if (await this.handleMoldOccupiedError(error)) { return } if (await this.handleUnfinishedReportError(error)) { return } this.showTip(error.message) } }, resolveUnfinishedReportDetail(error) { if (!error || error.statusCode !== 400) { return null } let detail = error.detail if (typeof detail === 'string') { try { detail = JSON.parse(detail) } catch (parseError) { detail = null } } return detail && detail.code === 'unfinished_report_mold_mismatch' ? detail : null }, async openUnfinishedReport(detail) { let sessionId = detail.session_id if (detail.reporting_expired) { wx.showLoading({ title: '处理中' }) try { const session = await api.finishWork( this.data.moldName, this.data.processName, this.data.attendancePointName, this.data.currentLocation, ) sessionId = session.id } finally { wx.hideLoading() } } wx.navigateTo({ url: `/pages/reportForm/reportForm?sessionId=${sessionId}`, }) }, handleUnfinishedReportError(error) { const detail = this.resolveUnfinishedReportDetail(error) if (!detail || !detail.session_id) { return Promise.resolve(false) } const message = detail.message || error.message || '当前有未完成的报工' return new Promise(resolve => { wx.showModal({ title: '未完成报工', content: `${message}\n是否立刻前往未完成的报工?`, cancelText: '否', confirmText: '是', confirmColor: '#1463ff', success: async res => { if (!res.confirm) { resolve(true) return } try { await this.openUnfinishedReport(detail) } catch (openError) { this.showTip(openError.message || '打开未完成报工失败') } finally { resolve(true) } }, fail: () => resolve(true), }) }) }, resolveMoldOccupiedDetail(error) { if (!error || error.statusCode !== 423) { return null } let detail = error.detail if (typeof detail === 'string') { try { detail = JSON.parse(detail) } catch (parseError) { detail = null } } return detail && detail.code === 'mold_occupied' ? detail : null }, handleMoldOccupiedError(error) { const detail = this.resolveMoldOccupiedDetail(error) if (!detail) { return Promise.resolve(false) } this.setData({ occupiedDetail: detail }) return new Promise(resolve => { wx.showModal({ title: '模具已被占用', content: detail.message || `已经被${detail.occupied_name || detail.occupied_phone || '其他员工'}占用,请联系管理员解除占用`, confirmText: '反馈给管理员', cancelText: '确定', confirmColor: '#1463ff', success: async res => { if (!res.confirm) { resolve(true) return } wx.showLoading({ title: '反馈中' }) try { await api.createMoldLockFeedback(detail.lock_id) wx.showToast({ title: '反馈成功', icon: 'success' }) } catch (feedbackError) { this.showTip(feedbackError.message || '反馈失败') } finally { wx.hideLoading() resolve(true) } }, fail: () => resolve(true), }) }) }, async feedbackMoldOccupied() { const detail = this.data.occupiedDetail if (!detail || !detail.lock_id) { this.showTip('占用信息缺失,无法反馈') return } wx.showLoading({ title: '反馈中' }) try { await api.createMoldLockFeedback(detail.lock_id) wx.showToast({ title: '反馈成功', icon: 'success' }) } catch (error) { this.showTip(error.message || '反馈失败') } finally { wx.hideLoading() } }, noop() {}, })