const api = require('../../utils/api') const util = require('../../utils/util') Page({ data: { user: null, mode: 'mine', title: '我的报工记录', startDate: '', endDate: '', status: '', showStatusFilter: true, rows: [], page: 1, pageInput: '1', totalPages: 1, refreshing: false, scrollTop: 0, expandedRecordId: '', detailExpandedAll: false, filterVoided: true, statusOptions: [ { label: '全部', value: '' }, { label: '待审核', value: 'pending' }, { label: '已通过', value: 'approved' }, ], }, onLoad(options) { const mode = options.mode || 'mine' const defaultDate = util.today() const startDate = mode === 'pending' ? (options.startDate || '') : (options.startDate || defaultDate) const endDate = mode === 'pending' ? (options.endDate || '') : (options.endDate || defaultDate) const titleMap = { audit: '审核记录', pending: '待审核清单', mine: '我的报工记录', } this.setData({ mode, title: titleMap[mode] || '我的报工记录', startDate, endDate, status: mode === 'pending' ? 'pending' : (options.status || ''), showStatusFilter: mode !== 'pending', }) }, onShow() { this.load() }, toggleDetails() { this.setData({ detailExpandedAll: !this.data.detailExpandedAll, expandedRecordId: '', }) }, onScroll(e) { this.currentScrollTop = e.detail.scrollTop || 0 }, queryRecordTop(id) { return new Promise(resolve => { this.createSelectorQuery() .select(`#record-${id}`) .boundingClientRect(rect => { resolve(rect ? rect.top : null) }) .exec() }) }, restoreRecordPosition(id, beforeTop) { if (beforeTop === null || beforeTop === undefined) { return } wx.nextTick(() => { this.queryRecordTop(id).then(afterTop => { if (afterTop === null || afterTop === undefined) { return } const nextScrollTop = Math.max( 0, (this.currentScrollTop || 0) + afterTop - beforeTop, ) this.currentScrollTop = nextScrollTop this.setData({ scrollTop: nextScrollTop }) }) }) }, toggleRecordDetail(e) { const id = String(e.currentTarget.dataset.id || '') const switchingRecord = this.data.detailExpandedAll || (!!this.data.expandedRecordId && this.data.expandedRecordId !== id) this.queryRecordTop(id).then(beforeTop => { this.setData({ detailExpandedAll: false, expandedRecordId: this.data.expandedRecordId === id ? '' : id, }, () => { if (switchingRecord) { this.restoreRecordPosition(id, beforeTop) } }) }) }, async load(page = this.data.page) { const user = api.getCurrentUser() if (!user) { this.setData({ rows: [], user }) return } const filters = { startDate: this.data.startDate, endDate: this.data.endDate, status: this.data.status, page, pageSize: 8, includeVoided: !this.data.filterVoided, } if (this.data.mode === 'audit') { filters.reviewerPhone = user.phone } else if (this.data.mode === 'pending') { filters.status = 'pending' } else { filters.employeePhone = user.phone } try { const result = await api.listReports(filters) this.setData({ user, rows: result.rows, page: result.page, pageInput: String(result.page || 1), totalPages: result.totalPages, expandedRecordId: '', detailExpandedAll: false, }) } catch (error) { wx.showToast({ title: error.message, icon: 'none' }) } }, async onPullDownRefresh() { this.setData({ refreshing: true }) try { await this.load(1) } finally { this.setData({ refreshing: false }) if (wx.stopPullDownRefresh) { wx.stopPullDownRefresh() } } }, onStartChange(e) { this.setData({ startDate: e.detail.value, page: 1 }) this.load(1) }, onEndChange(e) { this.setData({ endDate: e.detail.value, page: 1 }) this.load(1) }, shiftRangeDate(field, days) { const otherField = field === 'startDate' ? 'endDate' : 'startDate' const current = this.data[field] const other = this.data[otherField] const base = current || other || util.today() const updates = { [field]: util.addDays(base, days), page: 1, } if (!current && !other) { updates[otherField] = base } this.setData(updates) this.load(1) }, decreaseStartDate() { this.shiftRangeDate('startDate', -1) }, increaseEndDate() { this.shiftRangeDate('endDate', 1) }, chooseStatus(e) { this.setData({ status: e.currentTarget.dataset.value, page: 1, }) this.load(1) }, prevPage() { if (this.data.page > 1) { this.load(this.data.page - 1) } }, nextPage() { if (this.data.page < this.data.totalPages) { this.load(this.data.page + 1) } }, onPageInput(e) { this.setData({ pageInput: e.detail.value }) }, jumpToPage(e) { wx.hideKeyboard() const inputValue = e && e.detail && e.detail.value !== undefined ? e.detail.value : this.data.pageInput const target = Math.max(1, Math.min(Number(inputValue) || this.data.page, this.data.totalPages)) if (target === this.data.page) { this.setData({ pageInput: String(this.data.page || 1) }) return } this.load(target) }, openPendingReview(e) { const id = e.currentTarget.dataset.id wx.navigateTo({ url: `/pages/review/review?reportId=${id}`, }) }, toggleFilterVoided() { this.setData({ filterVoided: !this.data.filterVoided, page: 1 }) this.load(1) }, onFilterVoidedChange(e) { this.setData({ filterVoided: !!e.detail.value, page: 1 }) this.load(1) }, async voidReport(e) { const id = e.currentTarget.dataset.id wx.showModal({ title: '确认作废', content: '作废的记录将在30天后被系统彻底删除,彻底删除前可随时撤销作废,是否确定删除', cancelText: '否', confirmText: '是', confirmColor: '#c5221f', success: async res => { if (!res.confirm) { return } wx.showLoading({ title: '处理中' }) try { await api.voidReport(id) wx.showToast({ title: '已作废', icon: 'success' }) await this.load(this.data.page) } catch (error) { wx.showToast({ title: error.message || '作废失败', icon: 'none' }) } finally { wx.hideLoading() } }, }) }, async unvoidReport(e) { const id = e.currentTarget.dataset.id wx.showLoading({ title: '处理中' }) try { await api.unvoidReport(id) wx.showToast({ title: '已撤销作废', icon: 'success' }) await this.load(this.data.page) } catch (error) { wx.showToast({ title: error.message || '撤销失败', icon: 'none' }) } finally { wx.hideLoading() } }, openEditReview(e) { if (this.data.mode === 'mine') { return } const id = e.currentTarget.dataset.id wx.navigateTo({ url: `/pages/review/review?reportId=${id}`, }) }, })