perf(门店选择): 门店明细移出页面 data,修复首屏卡顿

门店接口 /shop/merchant/groupByChain 一次返回 8105 家门店(约 1.1MB)。
此前把门店明细原样放进页面 data,data 树膨胀到 1118KB,导致此后每次
setData 都要重新 diff/序列化整棵树——即使载荷只有 1 个字段,耗时也从
约 490ms 涨到约 1000ms,首屏因此多花约 3.5s。

改为把明细挂在实例属性 merchantStore 上(不进页面 data),
merchantChainList 只保留 {id, text, merchantCount} 骨架,
新增 getMerchantsByChainId() / getVisibleMerchants() 按需取数。

涉及页面:
- packageUser/self-report/create.vue
- packageUser/chronic/index.vue
- pages/task/detail.vue
- pages/share/create.vue
- pages/share/index.vue
- pages/audit/components/AuditShareItem.vue

实测(微信开发者工具自动化):
- 页面 data 门店明细 1118KB -> 1KB
- 单字段 setData 998ms -> 10~53ms
- self-report/create 首屏 9055ms -> 5341ms
- 门店 picker 选项数不变(大参林 2700 项),搜索与选中回填均正常
This commit is contained in:
2026-10-11 15:18:06 +08:00
parent 69e6ed8c59
commit 575b4a723a
6 changed files with 258 additions and 83 deletions
+47 -18
View File
@@ -370,26 +370,20 @@ export default {
editMerchantChainOptions() {
const list = Array.isArray(this.merchantChainList) ? this.merchantChainList : []
const keyword = (this.editMerchantSearchKeyword || '').trim()
if (!keyword) return list.length ? list : [{id: '', text: '暂无连锁', merchants: []}]
if (!keyword) return list.length ? list : [{id: '', text: '暂无连锁', merchantCount: 0}]
const filtered = list
.map(chain => {
const merchants = Array.isArray(chain.merchants) ? chain.merchants : []
const chainMatch = (chain.text || '').includes(keyword)
if (chainMatch) return {...chain}
const matchedMerchants = merchants.filter(merchant => (merchant.text || '').includes(keyword))
if (!matchedMerchants.length) return null
return {
...chain,
merchants: matchedMerchants
}
const visible = this.getVisibleMerchants(chain)
if (!visible.length) return null
return Object.assign({}, chain, {merchantCount: visible.length})
})
.filter(Boolean)
return filtered.length ? filtered : [{id: '', text: '无匹配连锁', merchants: []}]
return filtered.length ? filtered : [{id: '', text: '无匹配连锁', merchantCount: 0}]
},
editMerchantStoreOptions() {
const chainIndex = Number(this.editMerchantPickerValue[0] || 0)
const currentChain = this.editMerchantChainOptions[chainIndex] || this.editMerchantChainOptions[0] || {}
const merchants = Array.isArray(currentChain.merchants) ? currentChain.merchants : []
const merchants = this.getVisibleMerchants(currentChain)
return merchants.length ? merchants : [{id: '', text: '无门店'}]
},
editMerchantPickerIndexes() {
@@ -398,12 +392,12 @@ export default {
let chainIndex = chainList.findIndex(item => item.id === this.editForm.chainId)
if (chainIndex < 0 && this.editForm.merchantId) {
chainIndex = chainList.findIndex(item => {
const merchants = Array.isArray(item.merchants) ? item.merchants : []
const merchants = this.getMerchantsByChainId(item && item.id)
return merchants.some(merchant => merchant.id === this.editForm.merchantId)
})
}
if (chainIndex < 0) chainIndex = 0
const merchants = Array.isArray(chainList[chainIndex].merchants) ? chainList[chainIndex].merchants : []
const merchants = this.getMerchantsByChainId(chainList[chainIndex] && chainList[chainIndex].id)
let merchantIndex = merchants.findIndex(item => item.id === this.editForm.merchantId)
if (merchantIndex < 0) merchantIndex = 0
return [chainIndex, merchantIndex]
@@ -470,10 +464,28 @@ export default {
this.editMerchantPickerValue = [0, 0]
}
},
created() {
// 门店明细挂实例上而非 data:data 里的一切都会进页面 data 树并被 setData 序列化
this.merchantStore = []
},
beforeDestroy() {
this.clearGoodsCodeTimers()
},
methods: {
// 门店明细只存在实例的 merchantStore 上(不进页面 data),按连锁 id 取
getMerchantsByChainId(chainId) {
const store = Array.isArray(this.merchantStore) ? this.merchantStore : []
const chain = store.find(item => String(item.id) === String(chainId))
return chain && Array.isArray(chain.merchants) ? chain.merchants : []
},
// 搜索关键词命中的门店:连锁名命中则整列保留,否则按门店名过滤
getVisibleMerchants(chain) {
const merchants = this.getMerchantsByChainId(chain && chain.id)
const keyword = (this.editMerchantSearchKeyword || '').trim()
if (!keyword) return merchants
if ((chain.text || '').includes(keyword)) return merchants
return merchants.filter(merchant => (merchant.text || '').includes(keyword))
},
async toggleDetailExpanded() {
const nextExpanded = !this.detailExpanded
this.detailExpanded = nextExpanded
@@ -1048,11 +1060,12 @@ export default {
try {
const {data} = await merchantGroupByChainReq({})
const list = this.unwrapNestedList(data)
this.merchantChainList = list
const chains = list
.map((item, index) => this.normalizeMerchantChain(item, index))
.filter(item => Array.isArray(item.merchants) && item.merchants.length > 0)
this.setMerchantChains(chains)
} catch (error) {
this.merchantChainList = []
this.setMerchantChains([])
uni.showToast({
title: '门店列表获取失败',
icon: 'none'
@@ -1061,6 +1074,22 @@ export default {
this.merchantLoading = false
}
},
// 门店明细(全量可达 8000+ 条)只放在实例上,不进页面 data。
// 一旦放进 data,页面 data 树会膨胀到 1MB+,此后每次 setData 都要重新
// diff/序列化整棵树,连只含一个字段的 setData 也会从 ~490ms 涨到 ~1000ms。
setMerchantChains(chains) {
const list = Array.isArray(chains) ? chains : []
this.merchantStore = list.map(chain => ({
id: chain.id,
text: chain.text,
merchants: Array.isArray(chain.merchants) ? chain.merchants : []
}))
this.merchantChainList = this.merchantStore.map(chain => ({
id: chain.id,
text: chain.text,
merchantCount: chain.merchants.length
}))
},
async openEditMerchantPicker() {
if (!this.merchantChainList || this.merchantChainList.length === 0) {
await this.getMerchantList()
@@ -1075,7 +1104,7 @@ export default {
const chainIndex = Number(this.editMerchantPickerValue[0] || 0)
const merchantIndex = Number(this.editMerchantPickerValue[1] || 0)
const chain = this.editMerchantChainOptions[chainIndex] || null
const merchants = chain && Array.isArray(chain.merchants) ? chain.merchants : []
const merchants = this.getVisibleMerchants(chain)
const selected = merchants[merchantIndex]
if (!selected || selected.id === '' || selected.id === null || selected.id === undefined) {
return
@@ -1096,7 +1125,7 @@ export default {
if (!Number.isFinite(chainIndex) || chainIndex < 0) chainIndex = 0
if (!Number.isFinite(merchantIndex) || merchantIndex < 0) merchantIndex = 0
const chain = this.editMerchantChainOptions[chainIndex] || this.editMerchantChainOptions[0] || {}
const merchants = Array.isArray(chain.merchants) ? chain.merchants : []
const merchants = this.getVisibleMerchants(chain)
if (!merchants.length) {
merchantIndex = 0
} else if (chainIndex !== currentChainIndex || merchantIndex > merchants.length - 1) {