diff --git a/components/AppNavbar.vue b/components/AppNavbar.vue index 9e33222..415a4f4 100644 --- a/components/AppNavbar.vue +++ b/components/AppNavbar.vue @@ -27,21 +27,41 @@ export default { }, data() { return { - currentPages: [] + currentPages: [], + // 状态栏高度只在挂载时取一次:getSafeTop 在 render 中被调用, + // 每次都同步读系统信息会反复触发 wx.getSystemInfoSync 废弃告警。 + safeTop: 0 } }, methods: { getSafeTop() { - const systemInfo = uni.getSystemInfoSync() - return systemInfo.statusBarHeight + return this.safeTop }, back(){ if (this.currentPages.length === 1) uni.switchTab({url: '/pages/index/index'}) else uni.navigateBack() + }, + // 优先用新接口,取不到时回退旧接口,避免旧接口的废弃告警 + readSafeTop() { + try { + if (uni.getWindowInfo) { + const info = uni.getWindowInfo() + if (info && info.statusBarHeight !== undefined) return info.statusBarHeight + } + } catch (error) { + // ignore:新接口不可用时走下面的回退 + } + try { + const systemInfo = uni.getSystemInfoSync() + return (systemInfo && systemInfo.statusBarHeight) || 0 + } catch (error) { + return 0 + } } }, mounted() { this.currentPages = getCurrentPages() + this.safeTop = this.readSafeTop() } } diff --git a/packageUser/self-report/records.vue b/packageUser/self-report/records.vue index 678351e..02423c6 100644 --- a/packageUser/self-report/records.vue +++ b/packageUser/self-report/records.vue @@ -61,7 +61,7 @@ {{ statusText(item.status) }} - + 活动时间 {{ item.activityTime || '-' }} @@ -69,7 +69,7 @@ 巡店门店数 {{ item.storeCount || 0 }}家 - + 评分 {{ item.rating || '-' }}星 @@ -88,7 +88,7 @@ - + 暂无巡店记录 @@ -246,6 +246,8 @@ export default { ], activeStatus: 'all', records: [], + // 列表请求自增序号:切 tab 会连发请求且响应顺序不保证,只认最后一次,丢弃过期响应 + listRequestId: 0, loading: false, chainLoading: false, showChainPicker: false, @@ -336,19 +338,30 @@ export default { ]) }, async fetchList() { + const requestId = ++this.listRequestId + const type = this.activeType this.loading = true try { - const req = this.activeType === 'visit' ? visitReportUserListReq : selfReportUserListReq + const req = type === 'visit' ? visitReportUserListReq : selfReportUserListReq const {data} = await req({}) - this.records = this.unwrapList(data) + // 响应顺序不保证:切 tab 后返回的旧响应直接丢弃,避免另一类型的数据盖进当前 tab + if (requestId !== this.listRequestId) return + this.records = this.unwrapList(data).map(item => { + if (!item || typeof item !== 'object') return item + // 类型钉在记录上:后续归一化/筛选/重新提交都以它为准,不再依赖当前 tab + return Object.assign({}, item, {reportType: type}) + }) } catch (error) { + if (requestId !== this.listRequestId) return this.records = [] uni.showToast({ title: '记录加载失败', icon: 'none' }) } finally { - this.loading = false + if (requestId === this.listRequestId) { + this.loading = false + } } }, async fetchChainList() { @@ -368,8 +381,13 @@ export default { this.chainLoading = false } }, + // 记录所属填报类型:优先取记录自带的 reportType,历史/异常数据回退当前 tab + recordType(item) { + const type = item && item.reportType + return type === 'visit' || type === 'store' ? type : this.activeType + }, normalizeRecord(item = {}) { - if (this.activeType === 'visit') { + if (this.recordType(item) === 'visit') { return this.normalizeVisitRecord(item) } const createRaw = item.createTime || item.createdAt || item.date || '' @@ -381,6 +399,7 @@ export default { const summary = typeof summaryRaw === 'string' ? summaryRaw : JSON.stringify(summaryRaw || '') return { id: item.id, + reportType: 'store', merchantId: item.merchantId || item.merchant_id || '', merchantName: item.merchantName || item.merchant_name || '', chainName: item.chainName || item.chain_name || '-', @@ -419,6 +438,7 @@ export default { })) return { id: item.id, + reportType: 'visit', merchantId: '', merchantName: '', chainName: '-', @@ -509,13 +529,13 @@ export default { matchesItemChain(item) { const selectedId = this.filterForm.chainId if (selectedId === '' || selectedId === null || selectedId === undefined) return true - if (this.activeType === 'visit') { + if (this.recordType(item) === 'visit') { return (item.storeRows || []).some(store => this.matchesSelectedChain('', store.chainName, selectedId)) } return this.matchesSelectedChain(item.chainIdNormalized, item.chainName, selectedId) }, matchesItemKeyword(item, keyword) { - if (this.activeType === 'visit') { + if (this.recordType(item) === 'visit') { return (item.storeRows || []).some(store => String(store.merchantName || '').toLowerCase().includes(keyword)) } return String(item.merchantName || '').toLowerCase().includes(keyword) @@ -584,13 +604,14 @@ export default { }, handleResubmit(item) { if (!item || item.id === undefined || item.id === null) return - const url = this.activeType === 'visit' + // 按记录自带的类型跳转,不用当前 tab —— tab 与实际数据错位时也不会进错表单 + const url = this.recordType(item) === 'visit' ? `/packageUser/self-report/visit?id=${item.id}&mode=resubmit` : `/packageUser/self-report/create?id=${item.id}&mode=resubmit` uni.navigateTo({url}) }, cardTitle(item) { - if (this.activeType === 'visit') { + if (this.recordType(item) === 'visit') { const names = (item.storeRows || []) .map(store => store.merchantName) .filter(Boolean) @@ -602,7 +623,7 @@ export default { return item.merchantName || '-' }, cardSubTitle(item) { - if (this.activeType === 'visit') return '' + if (this.recordType(item) === 'visit') return '' return item.chainName && item.chainName !== '-' ? `连锁:${item.chainName}` : '' }, changeType(value) { diff --git a/packageUser/self-report/select.vue b/packageUser/self-report/select.vue index 694dcdd..3fef2da 100644 --- a/packageUser/self-report/select.vue +++ b/packageUser/self-report/select.vue @@ -82,10 +82,26 @@ export default { } }, onLoad() { - const systemInfo = uni.getSystemInfoSync() - this.statusBarHeight = systemInfo.statusBarHeight || 0 + this.statusBarHeight = this.readStatusBarHeight() }, methods: { + // 优先用新接口,取不到时回退旧接口,避免 getSystemInfoSync 的废弃告警 + readStatusBarHeight() { + try { + if (uni.getWindowInfo) { + const info = uni.getWindowInfo() + if (info && info.statusBarHeight !== undefined) return info.statusBarHeight || 0 + } + } catch (error) { + // ignore:新接口不可用时走下面的回退 + } + try { + const systemInfo = uni.getSystemInfoSync() + return (systemInfo && systemInfo.statusBarHeight) || 0 + } catch (error) { + return 0 + } + }, back() { if (getCurrentPages().length === 1) { uni.switchTab({url: '/pages/index/index'}) diff --git a/packageUser/self-report/visit.vue b/packageUser/self-report/visit.vue index 1e17d12..678cc3e 100644 --- a/packageUser/self-report/visit.vue +++ b/packageUser/self-report/visit.vue @@ -8,7 +8,7 @@ {{ storeList.length }} 家门店 暂无巡店记录,请点击下方「新增巡店」 - + 巡店{{ idx + 1 }} @@ -70,7 +70,7 @@ - + x @@ -117,7 +117,7 @@ @change="onChainPickerChange" > - + {{ item.text }} @@ -148,7 +148,7 @@ @change="onMerchantPickerChange" > - + {{ item.text }} @@ -233,6 +233,8 @@ export default { created() { // 门店明细挂实例上而非 data:data 里的一切都会进页面 data 树并被 setData 序列化。 this.merchantStore = [] + // 巡店项 key 的自增种子,同样放实例上,不必进 data 树 + this.storeKeySeed = 0 }, async onLoad(options = {}) { const id = options && options.id !== undefined && options.id !== null ? String(options.id).trim() : '' @@ -523,8 +525,23 @@ export default { }, // ---------- 巡店列表 ---------- + // wx:key 需要稳定且互不相同的标识;用自增序号,避免同一份数据里出现重复 key + buildStoreKey() { + this.storeKeySeed = (this.storeKeySeed || 0) + 1 + return `store_${this.storeKeySeed}` + }, + // 给数组里缺失 _rowKey 的项补齐(重提加载的旧数据没有该字段) + ensureStoreKeys(list) { + return (Array.isArray(list) ? list : []).map(item => { + if (item && item._rowKey) return item + return Object.assign({}, item, {_rowKey: this.buildStoreKey()}) + }) + }, createStoreItem() { return { + // 小程序 wx:key 只认属性名,模板字面量会被编译丢弃; + // 该字段同时让增删巡店时的节点复用稳定(删除靠后的项不会让前面的项重建) + _rowKey: this.buildStoreKey(), visitTime: this.formatDateTime(new Date()), merchantId: '', merchantName: '', @@ -729,7 +746,7 @@ export default { const nextForm = Object.assign(this.buildForm(), this.cloneValue(draft.form || {})) this.form = nextForm const storeList = Array.isArray(draft.storeList) ? draft.storeList : [] - this.storeList = storeList.map(item => this.normalizeStoreItem(item)) + this.storeList = this.ensureStoreKeys(storeList.map(item => this.normalizeStoreItem(item))) this.showChainPicker = false this.showMerchantPicker = false this.activePickerIndex = -1 @@ -762,7 +779,7 @@ export default { const summary = this.parseStoredJson(record.summary, record.summary || '') this.form.summary = typeof summary === 'string' ? summary : '' const storeList = this.parseStoredJson(record.storeList, []) - this.storeList = (Array.isArray(storeList) ? storeList : []).map(item => this.normalizeStoreItem(item)) + this.storeList = this.ensureStoreKeys((Array.isArray(storeList) ? storeList : []).map(item => this.normalizeStoreItem(item))) } catch (error) { await this.toast(error && error.message ? error.message : '填报记录加载失败') } finally {