fix(小程序): 修正 v-for :key 表达式告警,恢复列表 wx:key
非 h5 平台的 :key 只接受简单属性名,模板字面量与运算表达式会被编译器
整条丢弃(uni-template-compiler 的 getForKey:Identifier 走 *this 或原名,
MemberExpression 取属性名,其余一律 warning + 返回空串)。
所以这批告警不只是文案问题:产物里对应的 <block wx:for> 后面确实没有
wx:key,列表项因此失去复用标识,增删时会整段重建节点。本次按编译器
接受的写法逐一改正,共 13 个文件。
改法:
- 纯 URL 字符串数组(打卡/门头/小票/凭证/巡店照片)直接用元素自身
:key="img",编译为 wx:key="*this"
- 对象数组用其已有唯一字段:连锁/门店 item.id、抄送用户 user.id、
审计销量行 detail.key、编辑销量项 detail._key
- 本身无唯一字段的巡店列表补 _key:task/detail 沿用 visit.vue 已验证的
_rowKey(自增 seed,增删稳定),records 与 AuditVisitReportItem 用序号
sales.vue 另有一处隐蔽问题:row 是 {item, index} 包装对象,
:key="row.item._key || row.index" 即便写成简单形式也会生成 wx:key="_key",
在 row 上取不到值。改为在 visibleSalesItems 里把 key 提到包装对象顶层,
_key 缺失时用序号兜底。create.vue 的销量摘要同样补 __key 兜底,避免重复 key。
注:task/detail 的 storeList 提交走 sanitizeStoreList() 显式白名单,
_rowKey 不会外泄到后端。
验证:HBuilderX cli 重新编译 —— 编译成功,非 h5 平台 :key 告警 0 条
(修改前满屏 40+ 条),产物中 wx:for 与 wx:key 数量逐文件相等。
This commit is contained in:
@@ -291,7 +291,7 @@
|
||||
<picker-view-column>
|
||||
<view
|
||||
v-for="(item, idx) in merchantFilteredOptions"
|
||||
:key="`merchant-${idx}`"
|
||||
:key="item.id"
|
||||
class="merchant-picker-item"
|
||||
>
|
||||
{{ item.text }}
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<view class="section-extra">最多{{ boothLimit }}张</view>
|
||||
</view>
|
||||
<view class="photo-grid">
|
||||
<view v-for="(item, idx) in boothPhotos" :key="`booth-${idx}`" class="photo-item">
|
||||
<view v-for="(item, idx) in boothPhotos" :key="item" class="photo-item">
|
||||
<image :src="item" mode="aspectFill" class="photo-image" @tap="previewImages(boothPhotos, item)"/>
|
||||
<view class="photo-remove" @tap="removeBooth(idx)">x</view>
|
||||
</view>
|
||||
@@ -95,7 +95,7 @@
|
||||
</view>
|
||||
<view v-if="salesItems.length === 0" class="audit-empty-tip">请点击上方进入「销量填写」添加商品</view>
|
||||
<view v-else class="sales-summary-list">
|
||||
<view class="sales-summary-item" v-for="(item, idx) in salesSummaryRows" :key="item._key || idx">
|
||||
<view class="sales-summary-item" v-for="(item, idx) in salesSummaryRows" :key="item._key">
|
||||
<text class="sales-summary-name">{{ item.name }}</text>
|
||||
<text class="sales-summary-qty">{{ formatSalesQty(item.qty) }}{{ item.unit || '盒' }}</text>
|
||||
</view>
|
||||
@@ -119,7 +119,7 @@
|
||||
<view class="section-extra">最多{{ receiptLimit }}张</view>
|
||||
</view>
|
||||
<view class="photo-grid">
|
||||
<view v-for="(item, idx) in receiptPhotos" :key="`receipt-${idx}`" class="photo-item">
|
||||
<view v-for="(item, idx) in receiptPhotos" :key="item" class="photo-item">
|
||||
<image :src="item" mode="aspectFill" class="photo-image" @tap="previewImages(receiptPhotos, item)"/>
|
||||
<view class="photo-remove" @tap="removeReceipt(idx)">x</view>
|
||||
</view>
|
||||
@@ -201,7 +201,7 @@
|
||||
@change="onChainPickerChange"
|
||||
>
|
||||
<picker-view-column>
|
||||
<view v-for="(item, idx) in merchantChainOptions" :key="`chain-${idx}`" class="merchant-picker-item">
|
||||
<view v-for="(item, idx) in merchantChainOptions" :key="item.id" class="merchant-picker-item">
|
||||
{{ item.text }}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
@@ -232,7 +232,7 @@
|
||||
@change="onMerchantPickerChange"
|
||||
>
|
||||
<picker-view-column>
|
||||
<view v-for="(item, idx) in merchantStoreOptions" :key="`merchant-${idx}`" class="merchant-picker-item">
|
||||
<view v-for="(item, idx) in merchantStoreOptions" :key="item.id" class="merchant-picker-item">
|
||||
{{ item.text }}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
@@ -360,7 +360,11 @@ export default {
|
||||
},
|
||||
salesSummaryRows() {
|
||||
const items = Array.isArray(this.salesItems) ? this.salesItems : []
|
||||
return items.slice(0, this.salesSummaryLimit)
|
||||
// wx:key 需要稳定唯一标识;销量项正常都带 _key(销量页生成/重提回填),
|
||||
// 旧草稿缺失时按序号兜底,避免出现重复 key
|
||||
return items.slice(0, this.salesSummaryLimit).map((item, index) => Object.assign({}, item, {
|
||||
_key: (item && item._key) ? item._key : `sales_summary_${index}`
|
||||
}))
|
||||
},
|
||||
totalRewardAmount() {
|
||||
return (Array.isArray(this.salesItems) ? this.salesItems : []).reduce((sum, item) => {
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
<!-- 跑店填报:巡店列表(驻店填报没有门店列表,走下面的固定区块) -->
|
||||
<block v-if="item.reportType === 'visit'">
|
||||
<view v-if="!item.storeRows.length" class="detail-empty">暂无巡店记录</view>
|
||||
<view v-for="(store, sIdx) in item.storeRows" :key="`store-${sIdx}`" class="store-block">
|
||||
<view v-for="(store, sIdx) in item.storeRows" :key="store._key" class="store-block">
|
||||
<view class="store-block-head">
|
||||
<text class="store-block-title">巡店{{ sIdx + 1 }}</text>
|
||||
<text class="store-block-time">{{ store.visitTime || '-' }}</text>
|
||||
@@ -104,7 +104,7 @@
|
||||
<view v-if="store.photos.length" class="media-images">
|
||||
<image
|
||||
v-for="(img, imgIdx) in store.photos"
|
||||
:key="`store-${sIdx}-photo-${imgIdx}`"
|
||||
:key="img"
|
||||
class="media-image"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@@ -121,7 +121,7 @@
|
||||
<view v-if="item.clockImages.length" class="media-images">
|
||||
<image
|
||||
v-for="(img, idx) in item.clockImages"
|
||||
:key="`clock-${idx}`"
|
||||
:key="img"
|
||||
class="media-image"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@@ -136,7 +136,7 @@
|
||||
<view v-if="item.boothImages.length" class="media-images">
|
||||
<image
|
||||
v-for="(img, idx) in item.boothImages"
|
||||
:key="`booth-${idx}`"
|
||||
:key="img"
|
||||
class="media-image"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@@ -151,7 +151,7 @@
|
||||
<view v-if="item.receiptImages.length" class="media-images">
|
||||
<image
|
||||
v-for="(img, idx) in item.receiptImages"
|
||||
:key="`receipt-${idx}`"
|
||||
:key="img"
|
||||
class="media-image"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@@ -164,7 +164,7 @@
|
||||
<view class="record-section">
|
||||
<view class="record-section-title">销量填写</view>
|
||||
<view v-if="item.salesRows.length" class="sales-list">
|
||||
<view class="sales-item" v-for="(detail, idx) in item.salesRows" :key="`sales-${idx}`">
|
||||
<view class="sales-item" v-for="(detail, idx) in item.salesRows" :key="detail.key">
|
||||
<view class="sales-head">
|
||||
<view class="sales-name">{{ detail.name }}</view>
|
||||
<view class="sales-qty">{{ detail.qty }}{{ detail.unit }}</view>
|
||||
@@ -427,7 +427,8 @@ export default {
|
||||
const summaryRaw = this.parseMaybeJson(item.summary, item.summary || '')
|
||||
const summary = typeof summaryRaw === 'string' ? summaryRaw : JSON.stringify(summaryRaw || '')
|
||||
const storeList = this.parseMaybeJson(item.storeList, [])
|
||||
const storeRows = (Array.isArray(storeList) ? storeList : []).map(store => ({
|
||||
const storeRows = (Array.isArray(storeList) ? storeList : []).map((store, sIdx) => ({
|
||||
_key: `store_${sIdx}`,
|
||||
merchantName: store && (store.merchantName || store.storeName) ? (store.merchantName || store.storeName) : '',
|
||||
chainName: store && store.chainName ? store.chainName : '',
|
||||
visitTime: store && (store.visitTime || store.time) ? (store.visitTime || store.time) : '',
|
||||
@@ -467,8 +468,7 @@ export default {
|
||||
const items = Array.isArray(group && group.items) ? group.items : []
|
||||
items.forEach((detail, iIdx) => {
|
||||
rows.push({
|
||||
key: `${gIdx}-${iIdx}`,
|
||||
groupTitle: group && group.title ? group.title : `分组${gIdx + 1}`,
|
||||
key: `${gIdx}-${iIdx}`, groupTitle: group && group.title ? group.title : `分组${gIdx + 1}`,
|
||||
name: detail && detail.name ? detail.name : '-',
|
||||
makerAbb: detail && detail.makerAbb ? detail.makerAbb : '',
|
||||
spec: detail && detail.spec ? detail.spec : '',
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
</view>
|
||||
|
||||
<view v-else class="sp-list">
|
||||
<view class="sp-card" v-for="row in visibleSalesItems" :key="row.item._key || row.index">
|
||||
<view class="sp-card" v-for="row in visibleSalesItems" :key="row.key">
|
||||
<view class="sp-badge">{{ formatBadge(row.index) }}</view>
|
||||
<image v-if="row.item.cover" :src="row.item.cover" mode="aspectFill" class="sp-cover"/>
|
||||
<view v-else class="sp-cover sp-cover-empty">
|
||||
@@ -261,7 +261,9 @@ export default {
|
||||
const keyword = (this.keyword || '').trim().toLowerCase()
|
||||
const cate = this.filterCate
|
||||
return (Array.isArray(this.salesItems) ? this.salesItems : [])
|
||||
.map((item, index) => ({item, index}))
|
||||
// wx:key 只认 item 上的属性名,表达式会被编译丢弃;
|
||||
// 这里把 key 提到包装对象顶层,_key 缺失时用序号兜底保证唯一
|
||||
.map((item, index) => ({item, index, key: (item && item._key) || `sales_row_${index}`}))
|
||||
.filter(row => {
|
||||
if (cate && String(row.item.groupTitle || '') !== cate) return false
|
||||
if (!keyword) return true
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<view class="chip">必填</view>
|
||||
</view>
|
||||
<view v-if="images.length" class="proof-list">
|
||||
<view class="proof-item" v-for="(img, idx) in images" :key="`${img}-${idx}`">
|
||||
<view class="proof-item" v-for="(img, idx) in images" :key="img">
|
||||
<image class="proof-image" :src="img" mode="aspectFill"/>
|
||||
<view class="proof-remove" @tap.stop="removeProof(idx)">x</view>
|
||||
</view>
|
||||
@@ -97,7 +97,7 @@
|
||||
<view class="history-meta">备注:{{ item.remark || '-' }}</view>
|
||||
<view class="history-meta">凭证:</view>
|
||||
<view v-if="item.proofs && item.proofs.length" class="history-proof-list">
|
||||
<view class="history-proof-item" v-for="(proof, proofIdx) in item.proofs" :key="`${item.id}-proof-${proofIdx}`">
|
||||
<view class="history-proof-item" v-for="(proof, proofIdx) in item.proofs" :key="proof">
|
||||
<image
|
||||
class="history-proof-image"
|
||||
:src="proof"
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<scroll-view v-else class="copy-user-list" scroll-y>
|
||||
<view
|
||||
v-for="user in copyUsers"
|
||||
:key="`copy-user-${user.id}`"
|
||||
:key="user.id"
|
||||
:class="['copy-user-item', selectedCopyUserId === user.id ? 'copy-user-item-active' : '']"
|
||||
@tap="selectedCopyUserId = user.id"
|
||||
>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<view v-if="clockImageList.length" class="media-images">
|
||||
<image
|
||||
v-for="(img, idx) in clockImageList"
|
||||
:key="`clock-${idx}`"
|
||||
:key="img"
|
||||
class="media-image"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@@ -41,7 +41,7 @@
|
||||
<view v-if="boothImageList.length" class="media-images">
|
||||
<image
|
||||
v-for="(img, idx) in boothImageList"
|
||||
:key="`booth-${idx}`"
|
||||
:key="img"
|
||||
class="media-image"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@@ -56,7 +56,7 @@
|
||||
<view v-if="receiptImageList.length" class="media-images">
|
||||
<image
|
||||
v-for="(img, idx) in receiptImageList"
|
||||
:key="`receipt-${idx}`"
|
||||
:key="img"
|
||||
class="media-image"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@@ -72,7 +72,7 @@
|
||||
<view v-if="item.status === 0" class="section-action" @tap="openEditPopup">修改销量</view>
|
||||
</view>
|
||||
<view v-if="salesRows.length" class="detail-list">
|
||||
<view class="detail-item" v-for="(detail, idx) in salesRows" :key="`sales-${idx}`">
|
||||
<view class="detail-item" v-for="(detail, idx) in salesRows" :key="detail.key">
|
||||
<view class="detail-head">
|
||||
<view class="detail-name">{{ detail.name }}</view>
|
||||
<view class="detail-value text-red">销量:{{ detail.qty }}{{ detail.unit }}</view>
|
||||
@@ -125,7 +125,7 @@
|
||||
</view>
|
||||
<view v-if="editSalesItems.length === 0" class="detail-empty">暂无销量商品</view>
|
||||
<view v-else class="edit-sales-list">
|
||||
<view class="edit-sales-item" v-for="(detail, idx) in editSalesItems" :key="detail._key || idx">
|
||||
<view class="edit-sales-item" v-for="(detail, idx) in editSalesItems" :key="detail._key">
|
||||
<view class="edit-sales-head">
|
||||
<view>
|
||||
<view class="edit-sales-name">{{ detail.name || '商品' }}</view>
|
||||
@@ -199,12 +199,12 @@
|
||||
</view>
|
||||
<picker-view class="merchant-picker-view" :value="merchantEditPickerValue" @change="onMerchantEditPickerChange">
|
||||
<picker-view-column>
|
||||
<view v-for="(chain, index) in merchantEditChains" :key="`edit-chain-${index}`" class="merchant-picker-item">
|
||||
<view v-for="(chain, index) in merchantEditChains" :key="chain.id" class="merchant-picker-item">
|
||||
{{ chain.text }}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view v-for="(merchant, index) in merchantEditStores" :key="`edit-merchant-${index}`" class="merchant-picker-item">
|
||||
<view v-for="(merchant, index) in merchantEditStores" :key="merchant.id" class="merchant-picker-item">
|
||||
{{ merchant.text }}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
<scroll-view v-else class="copy-user-list" scroll-y>
|
||||
<view
|
||||
v-for="user in copyUsers"
|
||||
:key="`copy-share-user-${user.id}`"
|
||||
:key="user.id"
|
||||
:class="['copy-user-item', selectedCopyUserId === user.id ? 'copy-user-item-active' : '']"
|
||||
@tap="selectedCopyUserId = user.id"
|
||||
>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<scroll-view v-else class="copy-user-list" scroll-y>
|
||||
<view
|
||||
v-for="user in copyUsers"
|
||||
:key="`copy-user-${user.id}`"
|
||||
:key="user.id"
|
||||
:class="['copy-user-item', selectedCopyUserId === user.id ? 'copy-user-item-active' : '']"
|
||||
@tap="selectedCopyUserId = user.id"
|
||||
>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<view v-if="detailExpanded" class="record-detail">
|
||||
<view v-if="loadingDetail" class="detail-empty">加载中...</view>
|
||||
<view v-else-if="!storeRows.length" class="detail-empty">暂无巡店记录</view>
|
||||
<view v-for="(store, idx) in storeRows" :key="`store-${idx}`" class="visit-store-block">
|
||||
<view v-for="(store, idx) in storeRows" :key="store._key" class="visit-store-block">
|
||||
<view class="visit-store-head">
|
||||
<text class="visit-store-title">巡店{{ idx + 1 }}</text>
|
||||
<text class="visit-store-time">{{ store.visitTime || '-' }}</text>
|
||||
@@ -27,7 +27,7 @@
|
||||
<view v-if="store.photos.length" class="media-images">
|
||||
<image
|
||||
v-for="(img, imgIdx) in store.photos"
|
||||
:key="`store-${idx}-photo-${imgIdx}`"
|
||||
:key="img"
|
||||
class="media-image"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@@ -78,7 +78,9 @@ export default {
|
||||
storeRows() {
|
||||
const source = this.currentRecord
|
||||
const list = this.parseMaybeJson(source.storeList, [])
|
||||
return (Array.isArray(list) ? list : []).map(store => ({
|
||||
return (Array.isArray(list) ? list : []).map((store, sIdx) => ({
|
||||
// wx:key 只认属性名,表达式会被编译丢弃;巡店记录本身没有 id,按序号补一个
|
||||
_key: `store_${sIdx}`,
|
||||
merchantName: store && (store.merchantName || store.storeName) ? (store.merchantName || store.storeName) : '',
|
||||
chainName: store && store.chainName ? store.chainName : '',
|
||||
visitTime: store && (store.visitTime || store.time) ? (store.visitTime || store.time) : '',
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<view v-if="receiptImages.length" class="media-images">
|
||||
<image
|
||||
v-for="(receipt, receiptIdx) in receiptImages"
|
||||
:key="`receipt-${receiptIdx}`"
|
||||
:key="receipt"
|
||||
class="media-image"
|
||||
:src="receipt"
|
||||
mode="aspectFill"
|
||||
|
||||
+14
-5
@@ -126,7 +126,7 @@
|
||||
<view class="section-extra" v-if="totalTaskNum">目标{{ totalTaskNum }}家</view>
|
||||
</view>
|
||||
<view v-if="!storeList.length" class="audit-empty-tip">暂无巡店记录</view>
|
||||
<view v-for="(item, idx) in storeList" :key="`store-${idx}`" class="visit-store-card">
|
||||
<view v-for="(item, idx) in storeList" :key="item._rowKey" class="visit-store-card">
|
||||
<view class="visit-store-head">
|
||||
<view>
|
||||
<view class="visit-store-title">巡店{{ idx + 1 }}</view>
|
||||
@@ -183,7 +183,7 @@
|
||||
<view v-else class="audit-empty-tip visit-store-empty">暂无巡店照片</view>
|
||||
</view>
|
||||
<view v-else class="photo-grid">
|
||||
<view v-for="(photo, photoIdx) in item.photos" :key="`store-${idx}-photo-${photoIdx}`" class="photo-item">
|
||||
<view v-for="(photo, photoIdx) in item.photos" :key="photo" class="photo-item">
|
||||
<image :src="photo" mode="aspectFill" class="photo-image" @tap="previewStorePhotos(idx, photoIdx)"/>
|
||||
<view v-if="!isAuditEntry" class="photo-remove" @tap="removeStorePhoto(idx, photoIdx)">x</view>
|
||||
</view>
|
||||
@@ -451,12 +451,12 @@
|
||||
@change="onMerchantPickerChange"
|
||||
>
|
||||
<picker-view-column>
|
||||
<view v-for="(item, idx) in merchantChainOptions" :key="`chain-${idx}`" class="merchant-picker-item">
|
||||
<view v-for="(item, idx) in merchantChainOptions" :key="item.id" class="merchant-picker-item">
|
||||
{{ item.text }}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view v-for="(item, idx) in merchantStoreOptions" :key="`merchant-${idx}`" class="merchant-picker-item">
|
||||
<view v-for="(item, idx) in merchantStoreOptions" :key="item.id" class="merchant-picker-item">
|
||||
{{ item.text }}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
@@ -498,7 +498,7 @@
|
||||
<scroll-view v-else class="audit-copy-user-list" scroll-y>
|
||||
<view
|
||||
v-for="user in auditCopyUsers"
|
||||
:key="`audit-copy-user-${user.id}`"
|
||||
:key="user.id"
|
||||
:class="['audit-copy-user-item', auditSelectedCopyUserId === user.id ? 'audit-copy-user-item-active' : '']"
|
||||
@tap="auditSelectedCopyUserId = user.id"
|
||||
>
|
||||
@@ -1046,6 +1046,9 @@ export default {
|
||||
},
|
||||
createStoreItem(merchant = null, autoSelected = true) {
|
||||
return Object.assign({
|
||||
// 小程序 wx:key 只认属性名,模板字面量会被编译丢弃;
|
||||
// 该字段同时让增删巡店时的节点复用稳定(删除靠后的项不会让前面的项重建)
|
||||
_rowKey: this.buildStoreKey(),
|
||||
visitTime: this.formatDateTime(new Date()),
|
||||
lat: '',
|
||||
lng: '',
|
||||
@@ -1053,11 +1056,17 @@ export default {
|
||||
photos: []
|
||||
}, this.buildStoreMerchantFields(merchant, autoSelected))
|
||||
},
|
||||
// wx:key 需要稳定且互不相同的标识;用自增序号,避免同一份数据里出现重复 key
|
||||
buildStoreKey() {
|
||||
this.storeKeySeed = (this.storeKeySeed || 0) + 1
|
||||
return `store_${this.storeKeySeed}`
|
||||
},
|
||||
normalizeStoreItem(item) {
|
||||
const photos = Array.isArray(item && item.photos)
|
||||
? item.photos.filter(Boolean)
|
||||
: this.normalizeReceiptPhotos(item && (item.photos || item.images || item.image))
|
||||
return {
|
||||
_rowKey: item && item._rowKey ? item._rowKey : this.buildStoreKey(),
|
||||
merchantId: item && (item.merchantId ?? item.storeId ?? item.id) !== undefined
|
||||
? (item.merchantId ?? item.storeId ?? item.id)
|
||||
: '',
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="photos.length" class="photo-list">
|
||||
<view class="photo-item" v-for="(photo, index) in photos" :key="`${photo}-${index}`">
|
||||
<view class="photo-item" v-for="(photo, index) in photos" :key="photo">
|
||||
<image class="photo-image" :src="photo" mode="aspectFill" @tap="previewPhoto(index)"/>
|
||||
<view class="photo-remove" @tap.stop="removePhoto(index)">x</view>
|
||||
</view>
|
||||
@@ -217,7 +217,7 @@
|
||||
<view class="history-line">活动小结:{{ item.summary || '-' }}</view>
|
||||
<view class="history-line">照片:</view>
|
||||
<view v-if="item.photos.length" class="history-photo-list">
|
||||
<view class="history-photo-item" v-for="(photo, photoIndex) in item.photos" :key="`${item.id}-${photoIndex}`">
|
||||
<view class="history-photo-item" v-for="(photo, photoIndex) in item.photos" :key="photo">
|
||||
<image
|
||||
class="history-photo-image"
|
||||
:src="photo"
|
||||
|
||||
Reference in New Issue
Block a user