| | |
| | | <text class="info-label">经纬度</text> |
| | | <text class="info-value">{{ form.latitude }}, {{ form.longitude }}</text> |
| | | </view> |
| | | <view class="info-item photo-item"> |
| | | <text class="info-label">拜访照片</text> |
| | | <view class="photo-wrap"> |
| | | <view v-if="visitImageList.length" |
| | | class="photo-list"> |
| | | <image v-for="(img, idx) in visitImageList" |
| | | :key="img.id || img.url || idx" |
| | | class="photo-img" |
| | | :src="img.url" |
| | | mode="aspectFill" |
| | | @click="previewVisitImage(idx)" /> |
| | | </view> |
| | | <text v-else |
| | | class="empty-text">-</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 备注信息 --> |
| | | <view class="section"> |
| | |
| | | |
| | | import { ref, onMounted } from "vue"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { normalizeFileUrl } from "@/utils/filePreview"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | |
| | | const userStore = useUserStore(); |
| | |
| | | locationAddress: "", |
| | | remark: "", |
| | | }); |
| | | const visitImageList = ref([]); |
| | | |
| | | const isLikelyPathValue = value => { |
| | | const v = String(value || "").trim(); |
| | | if (!v) return false; |
| | | if (/^(https?:)?\/\//i.test(v)) return true; |
| | | if (/^(blob:|data:|wxfile:|file:|content:)/i.test(v)) return true; |
| | | if (v.includes("/") || v.includes("\\")) return true; |
| | | if (/\.[a-zA-Z0-9]{2,8}($|\?)/.test(v)) return true; |
| | | return false; |
| | | }; |
| | | |
| | | const buildVisitFilePreviewUrl = file => { |
| | | const candidates = [ |
| | | file?.link, |
| | | file?.url, |
| | | file?.downloadUrl, |
| | | file?.path, |
| | | file?.filePath, |
| | | file?.tempPath, |
| | | file?.urlFull, |
| | | ].filter(Boolean); |
| | | for (const raw of candidates) { |
| | | if (!isLikelyPathValue(raw)) continue; |
| | | const normalized = normalizeFileUrl(raw); |
| | | if (normalized) return normalized; |
| | | } |
| | | return ""; |
| | | }; |
| | | |
| | | const buildVisitImageList = row => { |
| | | const rawList = []; |
| | | if (Array.isArray(row?.commonFileList)) rawList.push(...row.commonFileList); |
| | | if (Array.isArray(row?.storageBlobDTO)) rawList.push(...row.storageBlobDTO); |
| | | const uniq = []; |
| | | const seen = new Set(); |
| | | rawList.forEach(item => { |
| | | const url = buildVisitFilePreviewUrl(item); |
| | | if (!url || seen.has(url)) return; |
| | | seen.add(url); |
| | | uniq.push({ ...item, url }); |
| | | }); |
| | | visitImageList.value = uniq; |
| | | }; |
| | | |
| | | const previewVisitImage = idx => { |
| | | const urls = visitImageList.value.map(item => item?.url).filter(Boolean); |
| | | if (!urls.length) return; |
| | | uni.previewImage({ urls, current: urls[idx] || urls[0] }); |
| | | }; |
| | | |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | |
| | | const row = uni.getStorageSync("clientVisit"); |
| | | if (row) { |
| | | form.value = { ...row }; |
| | | buildVisitImageList(row); |
| | | } else { |
| | | showToast("暂无拜访记录数据"); |
| | | } |
| | |
| | | text-align: right; |
| | | } |
| | | |
| | | .photo-item { |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .photo-wrap { |
| | | flex: 1; |
| | | display: flex; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .photo-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | justify-content: flex-start; |
| | | gap: 8px; |
| | | width: 100%; |
| | | } |
| | | |
| | | .photo-img { |
| | | width: 72px; |
| | | height: 72px; |
| | | border-radius: 8px; |
| | | background: #f5f5f5; |
| | | } |
| | | |
| | | .empty-text { |
| | | color: #999; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .multi-line { |
| | | text-align: left; |
| | | word-break: break-all; |