|
|
@@ -75,6 +75,17 @@
|
|
|
line-height: 1.55;
|
|
|
color: var(--text);
|
|
|
margin-bottom: 8px;
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-break: break-word;
|
|
|
+ }
|
|
|
+
|
|
|
+ .checkin-head__status .checkin-head__status-num {
|
|
|
+ font-size: 1.28em;
|
|
|
+ font-weight: 700;
|
|
|
+ color: var(--orange);
|
|
|
+ line-height: 1.25;
|
|
|
+ vertical-align: baseline;
|
|
|
+ padding:0 5px;
|
|
|
}
|
|
|
|
|
|
.checkin-head__accent {
|
|
|
@@ -301,18 +312,173 @@
|
|
|
margin: 8px auto 4px;
|
|
|
opacity: 0.2;
|
|
|
}
|
|
|
+
|
|
|
+ /* deleteFlag=1:与 shareIndex 关店区一致 — 顶部已删除 + empty.png + 更多推荐 */
|
|
|
+ .share-checkin-deleted-root {
|
|
|
+ min-height: 40vh;
|
|
|
+ }
|
|
|
+
|
|
|
+ .checkin-deleted-top {
|
|
|
+ text-align: center;
|
|
|
+ padding: 28px 20px 20px;
|
|
|
+ color: var(--text-muted);
|
|
|
+ font-size: 15px;
|
|
|
+ line-height: 1.5;
|
|
|
+ background: var(--page-bg);
|
|
|
+ }
|
|
|
+
|
|
|
+ .checkin-deleted-top img {
|
|
|
+ width: 240px;
|
|
|
+ max-width: 85vw;
|
|
|
+ height: auto;
|
|
|
+ object-fit: contain;
|
|
|
+ display: inline-block;
|
|
|
+ }
|
|
|
+
|
|
|
+ .checkin-deleted-top__text {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #989898;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-divider {
|
|
|
+ height: 8px;
|
|
|
+ background: #F7F7F7;
|
|
|
+ margin: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-title {
|
|
|
+ padding: 8px 15px 12px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ gap: 10px;
|
|
|
+ padding: 0 15px 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ #checkInDeletedRecEmpty {
|
|
|
+ grid-column: 1 / -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-empty {
|
|
|
+ padding: 12px;
|
|
|
+ color: var(--text-secondary);
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card {
|
|
|
+ min-width: 0;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 10px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__img {
|
|
|
+ aspect-ratio: 4 / 3;
|
|
|
+ background: #eee;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__img img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ display: block;
|
|
|
+ border-radius: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__body {
|
|
|
+ padding: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__top {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__name {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 700;
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__dist {
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--text-secondary);
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__rating {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 4px 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__rating .closed-rec-stars {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__rating .closed-rec-star {
|
|
|
+ display: block;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-rating-num {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--orange);
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-meta {
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--text-secondary);
|
|
|
+ }
|
|
|
+
|
|
|
+ .closed-rec-card__footer {
|
|
|
+ margin-top: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--text-secondary);
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
</style>
|
|
|
</head>
|
|
|
<body class="page-checkin">
|
|
|
|
|
|
+ <div id="shareCheckInDeletedRoot" class="share-checkin-deleted-root" style="display:none;" aria-hidden="true">
|
|
|
+ <section class="checkin-deleted-top">
|
|
|
+ <img src="images/empty.png" alt="" decoding="async">
|
|
|
+ <p class="checkin-deleted-top__text">内容已删除</p>
|
|
|
+ </section>
|
|
|
+ <div class="closed-rec-divider"></div>
|
|
|
+ <h3 class="closed-rec-title">更多推荐</h3>
|
|
|
+ <div class="closed-rec-grid" id="checkInDeletedRecList">
|
|
|
+ <p id="checkInDeletedRecEmpty" class="closed-rec-empty" style="display:none;">暂无推荐</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div id="shareCheckInNormalRoot">
|
|
|
<header class="checkin-head">
|
|
|
<div class="checkin-head__row">
|
|
|
<img class="checkin-head__avatar" id="userAvatar" src="images/demouser.png" alt="">
|
|
|
<div class="checkin-head__main">
|
|
|
<div class="checkin-head__name" id="userName">用户名称</div>
|
|
|
- <p class="checkin-head__status">
|
|
|
- 我在 <span class="checkin-head__accent" id="checkInYear">2025</span> 年打卡的第 <span class="checkin-head__accent" id="checkInPlaceIndex">1</span> 个地点
|
|
|
- </p>
|
|
|
+ <p class="checkin-head__status" id="checkInHeadStatus"></p>
|
|
|
<div class="checkin-head__loc">
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
|
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
|
|
|
@@ -345,6 +511,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
|
|
|
<div class="fab-wrap">
|
|
|
<button type="button" class="fab" id="openApp">
|
|
|
@@ -366,7 +533,7 @@
|
|
|
var APP_UNI_STORE_PATH = 'pages/checkIn/index';
|
|
|
|
|
|
/** 与 shareUndefined.html 一致 */
|
|
|
- var API_BASE = 'http://120.26.186.130:8000/alienStore';
|
|
|
+ var API_BASE = 'https://test.ailien.shop/alienStore';
|
|
|
|
|
|
function showDownloadTip() {
|
|
|
var msg = '请到应用商店下载';
|
|
|
@@ -583,11 +750,29 @@
|
|
|
return v == null ? '' : String(v);
|
|
|
}
|
|
|
|
|
|
+ function mergedQ(name) {
|
|
|
+ var v = getMergedParam(name);
|
|
|
+ if (v != null && String(v).trim() !== '') return String(v).trim();
|
|
|
+ var v2 = q(name);
|
|
|
+ return v2 == null ? '' : String(v2).trim();
|
|
|
+ }
|
|
|
+
|
|
|
+ /** getDeleteFlagById:data.deleteFlag 为 1 时本页展示「内容已删除 + 更多推荐」(与 shareIndex 关店态一致) */
|
|
|
+ function isShareCheckInDeletedByDeleteFlag(res) {
|
|
|
+ if (!res || typeof res !== 'object') return false;
|
|
|
+ var d = res.data;
|
|
|
+ if (!d || typeof d !== 'object') return false;
|
|
|
+ var df = d.deleteFlag;
|
|
|
+ return df === 1 || df === '1' || Number(df) === 1;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
- * getDeleteFlagById:data 为数字 1(旧格式),或 data.businessStatus 为 99 时进「已删除」落地页。
|
|
|
+ * getDeleteFlagById:data 为数字 1(旧格式),或 data.businessStatus 为 99 → shareCheckInUndefined.html;
|
|
|
+ * deleteFlag=1 不在此分支(本页内嵌已删除态)。
|
|
|
*/
|
|
|
function shouldRedirectToShareCheckInUndefined(res) {
|
|
|
if (!res || typeof res !== 'object') return false;
|
|
|
+ if (isShareCheckInDeletedByDeleteFlag(res)) return false;
|
|
|
var d = res.data;
|
|
|
if (d === 1 || d === '1' || Number(d) === 1) return true;
|
|
|
if (d && typeof d === 'object') {
|
|
|
@@ -613,12 +798,14 @@
|
|
|
return 'shareCheckInUndefined.html' + (m ? ('?' + m) : '');
|
|
|
}
|
|
|
|
|
|
- /** 进入页先拉删除标记;query 拼在 ? 后,如 getDeleteFlagById?id=545 */
|
|
|
+ /** 进入页先拉删除标记;id 优先打卡记录 id(id / checkInId),与 getStoreClockInList 分享链一致 */
|
|
|
function fetchGetDeleteFlagByIdIfId() {
|
|
|
var id =
|
|
|
getMergedParam('id').trim() ||
|
|
|
+ getMergedParam('checkInId').trim() ||
|
|
|
getMergedParam('storeId').trim() ||
|
|
|
q('id').trim() ||
|
|
|
+ q('checkInId').trim() ||
|
|
|
q('storeId').trim();
|
|
|
if (!id) return Promise.resolve(null);
|
|
|
var qs = new URLSearchParams();
|
|
|
@@ -634,6 +821,399 @@
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ /** 与 shareIndex.html 关店「更多推荐」:POST 全局店铺推荐 */
|
|
|
+ var API_LIFE_AI_BASE = 'http://124.93.18.180:9100';
|
|
|
+ var STORE_GLOBAL_RECOMMEND_PATH =
|
|
|
+ '/ai/multimodal-services/api/v1/search/global/store-recommend';
|
|
|
+ var FALLBACK_REC_USER_LAT = 38.925749;
|
|
|
+ var FALLBACK_REC_USER_LNG = 121.662531;
|
|
|
+ var FALLBACK_REC_USER_CITY = '大连市';
|
|
|
+ var cachedRecUserLoc = null;
|
|
|
+ var ensureRecommendLocPromise = null;
|
|
|
+
|
|
|
+ function getCityFromReverseGeocodeClient(lat, lng) {
|
|
|
+ var u =
|
|
|
+ 'https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=' +
|
|
|
+ encodeURIComponent(lat) +
|
|
|
+ '&longitude=' +
|
|
|
+ encodeURIComponent(lng) +
|
|
|
+ '&localityLanguage=zh';
|
|
|
+ return fetch(u, {
|
|
|
+ method: 'GET',
|
|
|
+ mode: 'cors',
|
|
|
+ credentials: 'omit',
|
|
|
+ headers: { Accept: 'application/json' }
|
|
|
+ })
|
|
|
+ .then(function (res) {
|
|
|
+ if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
|
+ return res.json();
|
|
|
+ })
|
|
|
+ .then(function (j) {
|
|
|
+ if (!j || typeof j !== 'object') return '';
|
|
|
+ return (
|
|
|
+ (j.city != null && String(j.city).trim()) ||
|
|
|
+ (j.locality != null && String(j.locality).trim()) ||
|
|
|
+ (j.principalSubdivision != null && String(j.principalSubdivision).trim()) ||
|
|
|
+ ''
|
|
|
+ );
|
|
|
+ })
|
|
|
+ .catch(function () {
|
|
|
+ return '';
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function tryIpApproxLocationThen(finish) {
|
|
|
+ fetch('https://get.geojs.io/v1/ip/geo.json', {
|
|
|
+ method: 'GET',
|
|
|
+ mode: 'cors',
|
|
|
+ credentials: 'omit',
|
|
|
+ headers: { Accept: 'application/json' }
|
|
|
+ })
|
|
|
+ .then(function (res) {
|
|
|
+ if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
|
+ return res.json();
|
|
|
+ })
|
|
|
+ .then(function (data) {
|
|
|
+ var lat = parseFloat(data.latitude);
|
|
|
+ var lng = parseFloat(data.longitude);
|
|
|
+ var city = data.city != null ? String(data.city).trim() : '';
|
|
|
+ if (!isNaN(lat) && !isNaN(lng)) {
|
|
|
+ finish({ lat: lat, lng: lng, city: city || FALLBACK_REC_USER_CITY });
|
|
|
+ } else {
|
|
|
+ finish({
|
|
|
+ lat: FALLBACK_REC_USER_LAT,
|
|
|
+ lng: FALLBACK_REC_USER_LNG,
|
|
|
+ city: FALLBACK_REC_USER_CITY
|
|
|
+ });
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(function () {
|
|
|
+ finish({
|
|
|
+ lat: FALLBACK_REC_USER_LAT,
|
|
|
+ lng: FALLBACK_REC_USER_LNG,
|
|
|
+ city: FALLBACK_REC_USER_CITY
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function tryGpsThenIpForRecommendLoc(finish) {
|
|
|
+ if (!navigator.geolocation || typeof navigator.geolocation.getCurrentPosition !== 'function') {
|
|
|
+ tryIpApproxLocationThen(finish);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ navigator.geolocation.getCurrentPosition(
|
|
|
+ function (pos) {
|
|
|
+ var lat = pos.coords.latitude;
|
|
|
+ var lng = pos.coords.longitude;
|
|
|
+ getCityFromReverseGeocodeClient(lat, lng).then(function (city) {
|
|
|
+ finish({ lat: lat, lng: lng, city: city || FALLBACK_REC_USER_CITY });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ function () {
|
|
|
+ tryIpApproxLocationThen(finish);
|
|
|
+ },
|
|
|
+ { enableHighAccuracy: false, timeout: 12000, maximumAge: 600000 }
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ function ensureRecommendUserLocation() {
|
|
|
+ if (cachedRecUserLoc) return Promise.resolve(cachedRecUserLoc);
|
|
|
+ if (ensureRecommendLocPromise) return ensureRecommendLocPromise;
|
|
|
+ ensureRecommendLocPromise = new Promise(function (resolve) {
|
|
|
+ tryGpsThenIpForRecommendLoc(function (loc) {
|
|
|
+ cachedRecUserLoc = loc;
|
|
|
+ resolve(loc);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ return ensureRecommendLocPromise;
|
|
|
+ }
|
|
|
+
|
|
|
+ var CLOSED_REC_STAR_PATH =
|
|
|
+ 'M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z';
|
|
|
+
|
|
|
+ function closedRecEscHtml(s) {
|
|
|
+ return String(s == null ? '': s)
|
|
|
+ .replace(/&/g, '&')
|
|
|
+ .replace(/</g, '<')
|
|
|
+ .replace(/>/g, '>')
|
|
|
+ .replace(/"/g, '"');
|
|
|
+ }
|
|
|
+
|
|
|
+ function closedRecStarsHtml(score) {
|
|
|
+ var s = Number(score);
|
|
|
+ if (isNaN(s)) s = 0;
|
|
|
+ s = Math.max(0, Math.min(5, s));
|
|
|
+ var rounded = Math.round(s);
|
|
|
+ var parts = [];
|
|
|
+ for (var i = 1; i <= 5; i++) {
|
|
|
+ var fill = i <= rounded ? '#F58220' : '#E5E5E5';
|
|
|
+ parts.push(
|
|
|
+ '<svg class="closed-rec-star" width="11" height="11" viewBox="0 0 24 24" aria-hidden="true">' +
|
|
|
+ '<path fill="' +
|
|
|
+ fill +
|
|
|
+ '" d="' +
|
|
|
+ CLOSED_REC_STAR_PATH +
|
|
|
+ '"/></svg>'
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return '<span class="closed-rec-stars">' + parts.join('') + '</span>';
|
|
|
+ }
|
|
|
+
|
|
|
+ function closedRecFormatDistance(item) {
|
|
|
+ if (item.distance != null && item.distance !== '') {
|
|
|
+ var km = Number(item.distance);
|
|
|
+ if (!isNaN(km) && km >= 0) return Math.round(km * 1000) + '米';
|
|
|
+ return String(item.distance).trim();
|
|
|
+ }
|
|
|
+ if (item.position != null && String(item.position).trim() !== '') {
|
|
|
+ return String(item.position).trim();
|
|
|
+ }
|
|
|
+ if (item.dist != null && item.dist !== '') {
|
|
|
+ var dn = Number(item.dist);
|
|
|
+ if (!isNaN(dn)) {
|
|
|
+ return dn >= 1 ? dn.toFixed(dn % 1 === 0 ? 0 : 1) + 'km' : Math.round(dn * 1000) + '米';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ function closedRecPickScore(item) {
|
|
|
+ var x =
|
|
|
+ item.scoreAvg != null
|
|
|
+ ? Number(item.scoreAvg)
|
|
|
+ : item.score != null
|
|
|
+ ? Number(item.score)
|
|
|
+ : item.rating != null
|
|
|
+ ? Number(item.rating)
|
|
|
+ : item.starScore != null
|
|
|
+ ? Number(item.starScore)
|
|
|
+ : NaN;
|
|
|
+ return isNaN(x) ? null : x;
|
|
|
+ }
|
|
|
+
|
|
|
+ function closedRecPickReviewCount(item) {
|
|
|
+ var n =
|
|
|
+ item.commitCount != null
|
|
|
+ ? Number(item.commitCount)
|
|
|
+ : item.commentCount != null
|
|
|
+ ? Number(item.commentCount)
|
|
|
+ : item.reviewCount != null
|
|
|
+ ? Number(item.reviewCount)
|
|
|
+ : item.evaluateCount != null
|
|
|
+ ? Number(item.evaluateCount)
|
|
|
+ : NaN;
|
|
|
+ return isNaN(n) ? 0 : Math.max(0, Math.floor(n));
|
|
|
+ }
|
|
|
+
|
|
|
+ function normalizeClosedStoreRecommendList(res) {
|
|
|
+ if (!res || typeof res !== 'object') return [];
|
|
|
+ var raw = res.data != null ? res.data : res.result;
|
|
|
+ if (Array.isArray(raw)) return raw;
|
|
|
+ if (raw && typeof raw === 'object') {
|
|
|
+ if (Array.isArray(raw.list)) return raw.list;
|
|
|
+ if (Array.isArray(raw.records)) return raw.records;
|
|
|
+ if (Array.isArray(raw.rows)) return raw.rows;
|
|
|
+ if (Array.isArray(raw.content)) return raw.content;
|
|
|
+ if (Array.isArray(raw.stores)) return raw.stores;
|
|
|
+ if (Array.isArray(raw.storeList)) return raw.storeList;
|
|
|
+ if (Array.isArray(raw.storeVos)) return raw.storeVos;
|
|
|
+ if (Array.isArray(raw.items)) return raw.items;
|
|
|
+ }
|
|
|
+ if (Array.isArray(res.list)) return res.list;
|
|
|
+ if (Array.isArray(res.records)) return res.records;
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 逆地理等偶发日文/繁体「連」→ 简体「连」(如 大連市 → 大连市) */
|
|
|
+ function normalizeRecommendUserCityToZhCn(city) {
|
|
|
+ var s = String(city == null ? '' : city).trim();
|
|
|
+ if (!s || s === 'undefined') return '';
|
|
|
+ return s.replace(/連/g, '连');
|
|
|
+ }
|
|
|
+
|
|
|
+ function fetchCheckInDeletedStoreRecommend(storeIdStr) {
|
|
|
+ var latRaw = (
|
|
|
+ mergedQ('userLat') ||
|
|
|
+ mergedQ('latitude') ||
|
|
|
+ mergedQ('lat') ||
|
|
|
+ mergedQ('weidu')
|
|
|
+ ).trim();
|
|
|
+ var lngRaw = (
|
|
|
+ mergedQ('userLng') ||
|
|
|
+ mergedQ('longitude') ||
|
|
|
+ mergedQ('lon') ||
|
|
|
+ mergedQ('jingdu')
|
|
|
+ ).trim();
|
|
|
+ var userCityRaw = (mergedQ('userCity') || mergedQ('city')).trim();
|
|
|
+ var page = parseInt(mergedQ('page') || q('page') || '1', 10);
|
|
|
+ var pageSize = parseInt(mergedQ('pageSize') || q('pageSize') || '10', 10);
|
|
|
+ if (isNaN(page) || page < 1) page = 1;
|
|
|
+ if (isNaN(pageSize) || pageSize < 1) pageSize = 10;
|
|
|
+
|
|
|
+ function postRecommend(userLat, userLng, userCity) {
|
|
|
+ var cityZh = normalizeRecommendUserCityToZhCn(userCity);
|
|
|
+ if (!cityZh) cityZh = FALLBACK_REC_USER_CITY;
|
|
|
+ var body = {
|
|
|
+ page: page,
|
|
|
+ pageSize: pageSize,
|
|
|
+ storeId: String(storeIdStr || ''),
|
|
|
+ userCity: cityZh,
|
|
|
+ userLat: userLat,
|
|
|
+ userLng: userLng
|
|
|
+ };
|
|
|
+ return fetch(API_LIFE_AI_BASE + STORE_GLOBAL_RECOMMEND_PATH, {
|
|
|
+ method: 'POST',
|
|
|
+ mode: 'cors',
|
|
|
+ credentials: 'omit',
|
|
|
+ headers: {
|
|
|
+ Accept: 'application/json',
|
|
|
+ 'Content-Type': 'application/json;charset=UTF-8'
|
|
|
+ },
|
|
|
+ body: JSON.stringify(body)
|
|
|
+ }).then(function (res) {
|
|
|
+ if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
|
+ return res.json();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ var hasUrlCoords =
|
|
|
+ latRaw !== '' && !isNaN(Number(latRaw)) && lngRaw !== '' && !isNaN(Number(lngRaw));
|
|
|
+ if (hasUrlCoords) {
|
|
|
+ var ul = Number(latRaw);
|
|
|
+ var ug = Number(lngRaw);
|
|
|
+ if (userCityRaw !== '') return postRecommend(ul, ug, userCityRaw);
|
|
|
+ return getCityFromReverseGeocodeClient(ul, ug).then(function (city) {
|
|
|
+ return postRecommend(ul, ug, city || FALLBACK_REC_USER_CITY);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return ensureRecommendUserLocation().then(function (loc) {
|
|
|
+ var city = userCityRaw !== '' ? userCityRaw : loc.city;
|
|
|
+ return postRecommend(loc.lat, loc.lng, city);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function renderCheckInDeletedRecommended(list) {
|
|
|
+ var wrap = document.getElementById('checkInDeletedRecList');
|
|
|
+ var empty = document.getElementById('checkInDeletedRecEmpty');
|
|
|
+ if (!wrap || !empty) return;
|
|
|
+ wrap.querySelectorAll('.closed-rec-card').forEach(function (n) {
|
|
|
+ n.remove();
|
|
|
+ });
|
|
|
+ if (!list || !list.length) {
|
|
|
+ empty.style.display = 'block';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ empty.style.display = 'none';
|
|
|
+ list.forEach(function (item) {
|
|
|
+ if (!item || typeof item !== 'object') return;
|
|
|
+ var imgUrlField = item.imgUrl != null ? String(item.imgUrl).trim() : '';
|
|
|
+ var home = item.homeImage != null ? String(item.homeImage).trim() : '';
|
|
|
+ if (home && /\.mp4(\?|#|$)/i.test(home)) {
|
|
|
+ var vf = item.videoFirstFrame != null ? String(item.videoFirstFrame).trim() : '';
|
|
|
+ if (vf) home = vf;
|
|
|
+ }
|
|
|
+ var imgUrl =
|
|
|
+ imgUrlField ||
|
|
|
+ home ||
|
|
|
+ (item.coverUrl != null ? String(item.coverUrl).trim() : '') ||
|
|
|
+ (item.mainImage != null ? String(item.mainImage).trim() : '') ||
|
|
|
+ (item.goodsImage != null ? String(item.goodsImage).trim() : '') ||
|
|
|
+ (item.entranceImage != null ? String(item.entranceImage).trim() : '') ||
|
|
|
+ (Array.isArray(item.goodsImageList) && item.goodsImageList[0]) ||
|
|
|
+ (Array.isArray(item.imageList) && item.imageList[0]) ||
|
|
|
+ (Array.isArray(item.storeAlbumUrlList) && item.storeAlbumUrlList[0]) ||
|
|
|
+ '';
|
|
|
+ var name =
|
|
|
+ item.title != null && String(item.title).trim() !== ''
|
|
|
+ ? String(item.title).replace(/\r?\n/g, ' ').replace(/\s+/g, ' ').trim()
|
|
|
+ : item.storeName ||
|
|
|
+ item.goodsName ||
|
|
|
+ item.secondGoodsTitle ||
|
|
|
+ item.name ||
|
|
|
+ '推荐';
|
|
|
+ var dist = closedRecFormatDistance(item);
|
|
|
+ var scoreVal = closedRecPickScore(item);
|
|
|
+ var displayScore = scoreVal != null ? scoreVal.toFixed(1) : '—';
|
|
|
+ var starScore = scoreVal != null ? scoreVal : 0;
|
|
|
+ var rc = closedRecPickReviewCount(item);
|
|
|
+ var reviewLabel = rc > 0 ? rc + '条评价' : '';
|
|
|
+ var seller =
|
|
|
+ item.userName != null && String(item.userName).trim() !== ''
|
|
|
+ ? String(item.userName).trim()
|
|
|
+ : item.nickName != null && String(item.nickName).trim() !== ''
|
|
|
+ ? String(item.nickName).trim()
|
|
|
+ : '';
|
|
|
+ var card = document.createElement('article');
|
|
|
+ card.className = 'closed-rec-card';
|
|
|
+ card.innerHTML =
|
|
|
+ '<div class="closed-rec-card__img"><img class="closed-rec-card__cover" src="" alt="" decoding="async"></div>' +
|
|
|
+ '<div class="closed-rec-card__body">' +
|
|
|
+ '<div class="closed-rec-card__top">' +
|
|
|
+ '<span class="closed-rec-card__name">' +
|
|
|
+ closedRecEscHtml(name) +
|
|
|
+ '</span>' +
|
|
|
+ (dist ? '<span class="closed-rec-card__dist">' + closedRecEscHtml(dist) + '</span>' : '') +
|
|
|
+ '</div>' +
|
|
|
+ '<div class="closed-rec-card__rating">' +
|
|
|
+ closedRecStarsHtml(starScore) +
|
|
|
+ '<span class="closed-rec-rating-num">' +
|
|
|
+ closedRecEscHtml(displayScore) +
|
|
|
+ '</span>' +
|
|
|
+ (reviewLabel
|
|
|
+ ? '<span class="closed-rec-meta">' + closedRecEscHtml(reviewLabel) + '</span>'
|
|
|
+ : '') +
|
|
|
+ '</div>' +
|
|
|
+ (seller ? '<div class="closed-rec-card__footer">' + closedRecEscHtml(seller) + '</div>' : '') +
|
|
|
+ '</div>';
|
|
|
+ var coverIm = card.querySelector('img.closed-rec-card__cover');
|
|
|
+ if (coverIm) {
|
|
|
+ coverIm.src = imgUrl;
|
|
|
+ coverIm.onerror = function () {
|
|
|
+ this.onerror = null;
|
|
|
+ this.src = '';
|
|
|
+ };
|
|
|
+ }
|
|
|
+ wrap.appendChild(card);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function loadCheckInDeletedRecommendations() {
|
|
|
+ var sid =
|
|
|
+ mergedQ('storeId') ||
|
|
|
+ mergedQ('id') ||
|
|
|
+ '';
|
|
|
+ if (!sid) {
|
|
|
+ renderCheckInDeletedRecommended([]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ fetchCheckInDeletedStoreRecommend(sid)
|
|
|
+ .then(function (res) {
|
|
|
+ var list = normalizeClosedStoreRecommendList(res);
|
|
|
+ if (!list.length && res && res.msg) {
|
|
|
+ console.warn('[store-recommend]', res.msg);
|
|
|
+ }
|
|
|
+ renderCheckInDeletedRecommended(list);
|
|
|
+ })
|
|
|
+ .catch(function (e) {
|
|
|
+ console.warn(e);
|
|
|
+ renderCheckInDeletedRecommended([]);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function applyCheckInDeletedByDeleteFlagUi() {
|
|
|
+ var delRoot = document.getElementById('shareCheckInDeletedRoot');
|
|
|
+ var normRoot = document.getElementById('shareCheckInNormalRoot');
|
|
|
+ if (delRoot) {
|
|
|
+ delRoot.style.display = 'block';
|
|
|
+ delRoot.setAttribute('aria-hidden', 'false');
|
|
|
+ }
|
|
|
+ if (normRoot) {
|
|
|
+ normRoot.style.display = 'none';
|
|
|
+ normRoot.setAttribute('aria-hidden', 'true');
|
|
|
+ }
|
|
|
+ loadCheckInDeletedRecommendations();
|
|
|
+ }
|
|
|
+
|
|
|
function tryDecode(s) {
|
|
|
if (!s) return '';
|
|
|
try {
|
|
|
@@ -882,6 +1462,18 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ function escapeHtmlForStatus(s) {
|
|
|
+ return String(s)
|
|
|
+ .replace(/&/g, '&')
|
|
|
+ .replace(/</g, '<')
|
|
|
+ .replace(/>/g, '>')
|
|
|
+ .replace(/"/g, '"');
|
|
|
+ }
|
|
|
+
|
|
|
+ function wrapStatusLineDigits(htmlEscaped) {
|
|
|
+ return htmlEscaped.replace(/(\d+)/g, '<span class="checkin-head__status-num">$1</span>');
|
|
|
+ }
|
|
|
+
|
|
|
function applyPage() {
|
|
|
var item = parseOptionsItem();
|
|
|
|
|
|
@@ -892,18 +1484,30 @@
|
|
|
var av = resolveUserAvatarUrl();
|
|
|
document.getElementById('userAvatar').src = av || CHECKIN_USER_AVATAR_DEFAULT;
|
|
|
|
|
|
- var year = getMergedParam('checkInYear') || getMergedParam('year') || q('checkInYear') || q('year');
|
|
|
- if (!year && item && item.checkInYear != null) year = String(item.checkInYear);
|
|
|
- if (!year) year = String(new Date().getFullYear());
|
|
|
- document.getElementById('checkInYear').textContent = tryDecode(year);
|
|
|
-
|
|
|
- var idx = getMergedParam('checkInIndex') || getMergedParam('placeIndex') || q('checkInIndex') || q('placeIndex');
|
|
|
- if (idx === '' && item) {
|
|
|
- if (item.checkInIndex != null) idx = String(item.checkInIndex);
|
|
|
- else if (item.placeIndex != null) idx = String(item.placeIndex);
|
|
|
+ /** getStoreClockInList 等与 App 分享的打卡正文:URL content / textContent,或 options.item(含 context) */
|
|
|
+ var elStatus = document.getElementById('checkInHeadStatus');
|
|
|
+ if (elStatus) {
|
|
|
+ var statusText =
|
|
|
+ getMergedParam('content') ||
|
|
|
+ getMergedParam('textContent') ||
|
|
|
+ q('content') ||
|
|
|
+ q('textContent');
|
|
|
+ if (!statusText && item) {
|
|
|
+ if (item.content != null && String(item.content).trim() !== '') {
|
|
|
+ statusText = String(item.content);
|
|
|
+ } else if (item.context != null && String(item.context).trim() !== '') {
|
|
|
+ statusText = String(item.context);
|
|
|
+ } else if (item.textContent != null && String(item.textContent).trim() !== '') {
|
|
|
+ statusText = String(item.textContent);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ statusText = statusText ? tryDecode(String(statusText)).trim() : '';
|
|
|
+ if (!statusText) {
|
|
|
+ elStatus.textContent = '';
|
|
|
+ } else {
|
|
|
+ elStatus.innerHTML = wrapStatusLineDigits(escapeHtmlForStatus(statusText));
|
|
|
+ }
|
|
|
}
|
|
|
- if (idx === '') idx = '1';
|
|
|
- document.getElementById('checkInPlaceIndex').textContent = tryDecode(idx);
|
|
|
|
|
|
var poiName = getMergedParam('poiName') || getMergedParam('locationName') || q('poiName') || q('locationName');
|
|
|
if (!poiName && item) {
|
|
|
@@ -970,6 +1574,10 @@
|
|
|
|
|
|
function boot() {
|
|
|
fetchGetDeleteFlagByIdIfId().then(function (res) {
|
|
|
+ if (isShareCheckInDeletedByDeleteFlag(res)) {
|
|
|
+ applyCheckInDeletedByDeleteFlagUi();
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (shouldRedirectToShareCheckInUndefined(res)) {
|
|
|
window.location.replace(buildShareCheckInUndefinedHref(res));
|
|
|
return;
|