LuTong 1 месяц назад
Родитель
Сommit
75ed4f6faa
1 измененных файлов с 1153 добавлено и 0 удалено
  1. 1153 0
      HBuilderProjects/shareUndefined1.html

+ 1153 - 0
HBuilderProjects/shareUndefined1.html

@@ -0,0 +1,1153 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+	<meta charset="utf-8">
+	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
+	<meta name="format-detection" content="telephone=no">
+	<title>U店在哪</title>
+	<style>
+		:root {
+			--orange: #F58220;
+			--orange-light: #FFF4E8;
+			--red: #FF4D4F;
+			--text: #333333;
+			--text-secondary: #999999;
+			--border: #EEEEEE;
+			--bg: #FFFFFF;
+			--safe-bottom: env(safe-area-inset-bottom, 0px);
+		}
+
+		* {
+			margin: 0;
+			padding: 0;
+			box-sizing: border-box;
+		}
+
+		html {
+			font-size: 16px;
+			-webkit-tap-highlight-color: transparent;
+			overflow-x: hidden;
+		}
+
+		body {
+			font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
+			background: var(--bg);
+			color: var(--text);
+			padding-bottom: calc(88px + var(--safe-bottom));
+			min-height: 100vh;
+			overflow-x: hidden;
+		}
+
+		/* Header */
+		.header {
+			position: sticky;
+			top: 0;
+			z-index: 100;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 44px;
+			padding: 0 15px;
+			background: var(--bg);
+			border-bottom: 1px solid var(--border);
+		}
+
+		.header__back {
+			position: absolute;
+			left: 15px;
+			width: 24px;
+			height: 24px;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			color: var(--text);
+			text-decoration: none;
+		}
+
+		.header__title {
+			font-size: 17px;
+			font-weight: 600;
+			color: var(--text);
+		}
+
+		/* Hero carousel */
+		.hero {
+			position: relative;
+			margin: 0 15px;
+			margin-top: 10px;
+			border-radius: 0 0 12px 12px;
+			overflow: hidden;
+		}
+
+		/* 内容不存在 / 已删除:居中插图 + 说明文案 */
+		.hero.hero--empty {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			padding: 48px 24px 56px;
+			min-height: min(52vh, 440px);
+			background: var(--bg);
+			overflow: visible;
+		}
+
+		.hero--empty__illustration {
+			display: block;
+			width: 100%;
+			max-width: 280px;
+			height: auto;
+			object-fit: contain;
+		}
+
+		.hero--empty__tip {
+			padding: 0 16px;
+			font-size: 15px;
+			line-height: 1.5;
+			color: var(--text-secondary);
+			text-align: center;
+			font-weight: 400;
+		}
+
+		.hero__track {
+			display: flex;
+			transition: transform 0.35s ease;
+		}
+
+		.hero__slide {
+			flex: 0 0 100%;
+			aspect-ratio: 16 / 10;
+			background: #f0f0f0;
+		}
+
+		.hero__slide img {
+			width: 100%;
+			height: 100%;
+			object-fit: cover;
+			display: block;
+			vertical-align: middle;
+		}
+
+		.hero__dots {
+			position: absolute;
+			bottom: 12px;
+			left: 0;
+			right: 0;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			gap: 6px;
+		}
+
+		.hero__dot {
+			width: 6px;
+			height: 6px;
+			border-radius: 3px;
+			background: rgba(255, 255, 255, 0.45);
+			transition: width 0.25s ease, background 0.25s ease;
+		}
+
+		.hero__dot.is-active {
+			width: 16px;
+			background: #fff;
+		}
+
+		/* Store block */
+		.section {
+			padding: 16px 15px 0;
+		}
+
+		.store-name {
+			font-size: 20px;
+			font-weight: 700;
+			line-height: 1.35;
+			margin-bottom: 12px;
+		}
+
+		.row {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			gap: 10px;
+		}
+
+		.rating-row {
+			margin-bottom: 12px;
+		}
+
+		.stars {
+			display: inline-flex;
+			align-items: center;
+			gap: 2px;
+		}
+
+		.star {
+			width: 14px;
+			height: 14px;
+			color: var(--orange);
+		}
+
+		.rating-num {
+			margin-left: 6px;
+			font-size: 14px;
+			font-weight: 600;
+			color: var(--orange);
+		}
+
+		.link-muted {
+			display: inline-flex;
+			align-items: center;
+			gap: 2px;
+			font-size: 13px;
+			color: var(--text-secondary);
+			text-decoration: none;
+		}
+
+		.chevron {
+			width: 14px;
+			height: 14px;
+			opacity: 0.6;
+		}
+
+		.hours-row {
+			margin-bottom: 16px;
+			align-items: flex-start;
+		}
+
+		.hours-left {
+			flex: 1;
+			min-width: 0;
+		}
+
+		.status {
+			font-size: 14px;
+			color: var(--red);
+			margin-right: 8px;
+		}
+
+		.status.is-open {
+			color: var(--orange);
+		}
+
+		.hours-text {
+			font-size: 14px;
+			color: var(--text-secondary);
+		}
+
+		.divider {
+			height: 8px;
+			background: #F7F7F7;
+			margin: 0;
+		}
+
+		/* Location */
+		.loc-wrap {
+			display: flex;
+			padding: 16px 15px;
+			gap: 12px;
+		}
+
+		.loc-main {
+			flex: 1;
+			min-width: 0;
+			padding-right: 12px;
+		}
+
+		.addr {
+			font-size: 15px;
+			font-weight: 600;
+			line-height: 1.45;
+			margin-bottom: 10px;
+		}
+
+		.meta-line {
+			display: flex;
+			align-items: flex-start;
+			gap: 6px;
+			font-size: 13px;
+			color: var(--text-secondary);
+			line-height: 1.45;
+			margin-top: 6px;
+		}
+
+		.meta-line svg {
+			flex-shrink: 0;
+			margin-top: 2px;
+			opacity: 0.75;
+		}
+
+		.loc-actions {
+			display: flex;
+			flex-direction: row;
+			align-items: center;
+			justify-content: center;
+			gap: 18px;
+			flex-shrink: 0;
+		}
+
+		.act-btn {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			gap: 4px;
+			background: none;
+			border: none;
+			padding: 0;
+			cursor: pointer;
+			font-size: 12px;
+			color: var(--text);
+		}
+
+		.act-btn__icon {
+			width: 44px;
+			height: 44px;
+			border-radius: 50%;
+			background: #F5F5F5;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			color: var(--orange);
+		}
+
+		/* More */
+		.more-title {
+			padding: 8px 15px 12px;
+			font-size: 16px;
+			font-weight: 700;
+		}
+
+		.more-scroll {
+			display: grid;
+			grid-template-columns: repeat(2, 1fr);
+			gap: 10px;
+			padding: 0 15px 20px;
+		}
+
+		#recEmpty {
+			grid-column: 1 / -1;
+		}
+
+		.rec-card {
+			min-width: 0;
+			background: #fff;
+			border-radius: 8px;
+			overflow: hidden;
+			box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+		}
+
+		.rec-card__img {
+			aspect-ratio: 4 / 3;
+			background: #eee;
+		}
+
+		.rec-card__img img {
+			width: 100%;
+			height: 100%;
+			object-fit: cover;
+		}
+
+		.rec-card__body {
+			padding: 10px;
+		}
+
+		.rec-card__top {
+			display: flex;
+			justify-content: space-between;
+			align-items: baseline;
+			gap: 8px;
+			margin-bottom: 6px;
+		}
+
+		.rec-card__name {
+			font-size: 15px;
+			font-weight: 700;
+			flex: 1;
+			min-width: 0;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		.rec-card__dist {
+			font-size: 12px;
+			color: var(--text-secondary);
+			flex-shrink: 0;
+		}
+
+		.rec-card__rating {
+			display: flex;
+			align-items: center;
+			flex-wrap: wrap;
+			gap: 4px 8px;
+		}
+
+		.rec-card__rating .stars .star {
+			width: 11px;
+			height: 11px;
+		}
+
+		.rec-card__rating .rating-num {
+			font-size: 12px;
+		}
+
+		.rec-meta {
+			font-size: 12px;
+			color: var(--text-secondary);
+		}
+
+		.rec-card__seller {
+			display: flex;
+			align-items: center;
+			gap: 8px;
+			margin-top: 8px;
+			min-width: 0;
+		}
+
+		.rec-card__avatar {
+			width: 22px;
+			height: 22px;
+			border-radius: 50%;
+			object-fit: cover;
+			flex-shrink: 0;
+			background: #eee;
+		}
+
+		.rec-card__seller-name {
+			font-size: 12px;
+			color: var(--text-secondary);
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		/* FAB */
+		.fab-wrap {
+			position: fixed;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			z-index: 200;
+			padding: 12px 24px calc(12px + var(--safe-bottom));
+			background: linear-gradient(to top, rgba(255, 255, 255, 0.98) 70%, transparent);
+			pointer-events: none;
+		}
+
+		.fab-wrap .fab {
+			pointer-events: auto;
+		}
+
+		.fab {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			gap: 10px;
+			width: 100%;
+			max-width: 320px;
+			margin: 0 auto;
+			height: 48px;
+			border: none;
+			border-radius: 24px;
+			background: var(--orange);
+			color: #fff;
+			font-size: 16px;
+			font-weight: 600;
+			box-shadow: 0 4px 16px rgba(245, 130, 32, 0.45);
+			cursor: pointer;
+		}
+
+		.fab__logo {
+			width: 28px;
+			height: 28px;
+			flex-shrink: 0;
+		}
+
+		.home-indicator {
+			height: 5px;
+			background: #000;
+			border-radius: 3px;
+			width: 134px;
+			margin: 8px auto 4px;
+			opacity: 0.2;
+		}
+	</style>
+</head>
+<body>
+
+	<div class="hero hero--empty" id="hero" role="status" aria-live="polite">
+		<img class="hero--empty__illustration" src="images/empty.png" alt="" decoding="async">
+		<p class="hero--empty__tip">内容已删除/下架/卖出</p>
+	</div>
+
+	
+
+	<div class="divider"></div>
+
+	<h3 class="more-title">更多推荐</h3>
+	<div class="more-scroll" id="recList">
+		<p id="recEmpty" style="padding:12px;color:#999;font-size:14px;display:none;">暂无推荐</p>
+	</div>
+
+	<div class="fab-wrap">
+		<button type="button" class="fab" id="openApp">
+			<img src="images/ubtn.png" alt="APP内打开" decoding="async">
+		</button>
+		<div class="home-indicator" aria-hidden="true"></div>
+	</div>
+
+	<script>
+	(function () {
+		'use strict';
+		/**
+		 * 更多推荐:POST http://124.93.18.180:9100/ai/life-manager/api/v1/second_hand/global-recommend
+		 * 常用 query:userId、userLat/userLng 或 lat/weidu、lon/jingdu、radiusKm、page、pageSize、categoryOneId、categoryTwoId
+		 *
+		 * 注意:用 file:// 打开本页时,浏览器可能因 CORS 拦截跨域请求。
+		 */
+		var API_BASE = 'http://120.26.186.130:8000/alienStore';
+		var API_LIFE_AI_BASE = 'http://124.93.18.180:9100';
+		var SECOND_GLOBAL_RECOMMEND_PATH = '/ai/life-manager/api/v1/second_hand/global-recommend';
+		var DEFAULT_REC_USER_LAT = 38.925756;
+		var DEFAULT_REC_USER_LNG = 121.662543;
+		var DEFAULT_REC_RADIUS_KM = 195.69;
+
+		/**
+		 * 与 secondShareGoods.html 一致:唤起 App 打开二手商品详情页(合并 search + hash 内 query)
+		 */
+		var APP_ANDROID_PACKAGE = 'com.alien.Udianzaizhe';
+		var APP_IOS_URL_SCHEME = 'shopro://';
+		var APP_UNI_STORE_PATH = 'pages/secondHandTransactions/pages/detail/index';
+
+		function showDownloadTip() {
+			var msg = '请到应用商店下载';
+			if (typeof uni !== 'undefined' && typeof uni.showToast === 'function') {
+				uni.showToast({ title: msg, icon: 'none', duration: 2500 });
+			} else {
+				window.alert(msg);
+			}
+		}
+
+		function mergeSearchAndHashParams() {
+			var params = new URLSearchParams();
+			function ingest(querySlice) {
+				if (!querySlice) return;
+				var p = new URLSearchParams(querySlice);
+				p.forEach(function (val, key) {
+					params.append(key, val);
+				});
+			}
+			if (location.search && location.search.length > 1) {
+				ingest(location.search.slice(1));
+			}
+			var hash = location.hash || '';
+			var qi = hash.indexOf('?');
+			if (qi >= 0) {
+				ingest(hash.slice(qi + 1));
+			}
+			return params;
+		}
+
+		function buildAppOpenQueryStringMerged() {
+			var params = mergeSearchAndHashParams();
+			var gid = params.get('goodsId') || params.get('id') || '';
+			if (gid && !params.has('goodsId')) {
+				params.set('goodsId', gid);
+			}
+			var sid = params.get('storeId') || params.get('id') || '';
+			if (sid && !params.has('storeId')) {
+				params.set('storeId', sid);
+			}
+			var qsOut = params.toString();
+			return qsOut ? ('?' + qsOut) : '';
+		}
+
+		function buildAppDeepLink() {
+			var path = String(APP_UNI_STORE_PATH || 'pages/secondHandTransactions/pages/detail/index').replace(/^\//, '');
+			var s = buildAppOpenQueryStringMerged();
+			var root = APP_IOS_URL_SCHEME.replace(/\/$/, '');
+			if (!s) {
+				return root + '/' + path;
+			}
+			return root + '/' + path + s;
+		}
+
+		function isWeChatInAppBrowser() {
+			return /MicroMessenger/i.test(navigator.userAgent || '');
+		}
+
+		function launchAppDeepLink(deepLink) {
+			try {
+				var a = document.createElement('a');
+				a.href = deepLink;
+				a.setAttribute('target', '_self');
+				document.body.appendChild(a);
+				a.click();
+				document.body.removeChild(a);
+			} catch (e1) {}
+			try {
+				window.location.href = deepLink;
+			} catch (e2) {}
+		}
+
+		/**
+		 * App-Plus:检测是否已安装;H5:scheme 唤起 + 超时提示(与 secondShareGoods 一致)。
+		 */
+		function tryOpenUShopApp() {
+			var deepLink = buildAppDeepLink();
+
+			if (typeof plus !== 'undefined' && plus.runtime) {
+				var installed = null;
+				try {
+					if (typeof plus.runtime.isApplicationExist === 'function') {
+						installed = plus.runtime.isApplicationExist({
+							pname: APP_ANDROID_PACKAGE,
+							action: APP_IOS_URL_SCHEME
+						});
+					}
+				} catch (e) {
+					console.warn(e);
+				}
+				if (installed === false) {
+					showDownloadTip();
+					return;
+				}
+				try {
+					plus.runtime.openURL(deepLink);
+				} catch (e2) {
+					console.warn(e2);
+					showDownloadTip();
+				}
+				return;
+			}
+
+			var t0 = Date.now();
+			var done = false;
+			function finish() {
+				if (done) return;
+				done = true;
+				document.removeEventListener('visibilitychange', onVis);
+				window.removeEventListener('pagehide', onHide);
+			}
+			function onVis() {
+				if (document.visibilityState === 'hidden') finish();
+			}
+			function onHide() {
+				finish();
+			}
+			document.addEventListener('visibilitychange', onVis);
+			window.addEventListener('pagehide', onHide);
+
+			if (isWeChatInAppBrowser()) {
+				window.alert('若点击后无法打开 App:请先点右上角「···」,选择「在浏览器中打开」,再点「APP内打开」。');
+			}
+
+			try {
+				launchAppDeepLink(deepLink);
+			} catch (e3) {
+				finish();
+				showDownloadTip();
+				return;
+			}
+
+			window.setTimeout(function () {
+				if (done) return;
+				if (document.visibilityState === 'visible' && Date.now() - t0 < 3500) {
+					showDownloadTip();
+				}
+				finish();
+			}, 2600);
+		}
+
+		function qs() {
+			return new URLSearchParams(location.search || '');
+		}
+		function q(name) {
+			var v = qs().get(name);
+			return v == null ? '' : String(v);
+		}
+
+		function showErr(msg) {
+			var el = document.getElementById('pageError');
+			el.textContent = msg;
+			el.style.display = 'block';
+		}
+
+		function getStoreId() {
+			return q('id') || q('storeId');
+		}
+
+		function apiFetch(path) {
+			return fetch(API_BASE + path, {
+				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();
+			});
+		}
+
+		function isApiOk(res) {
+			if (!res || typeof res !== 'object') return false;
+			if (res.success === false) return false;
+			var c = res.code;
+			return c === 200 || c === '200' || Number(c) === 200;
+		}
+
+		function fetchSecondGlobalRecommend() {
+			var userIdRaw = q('userId').trim();
+			var userId =
+				userIdRaw !== '' && !isNaN(Number(userIdRaw)) ? Number(userIdRaw) : null;
+
+			var latRaw = (q('userLat') || q('latitude') || q('lat') || q('weidu')).trim();
+			var lngRaw = (q('userLng') || q('longitude') || q('lon') || q('jingdu')).trim();
+			var userLat =
+				latRaw !== '' && !isNaN(Number(latRaw)) ? Number(latRaw) : DEFAULT_REC_USER_LAT;
+			var userLng =
+				lngRaw !== '' && !isNaN(Number(lngRaw)) ? Number(lngRaw) : DEFAULT_REC_USER_LNG;
+
+			var rkRaw = q('radiusKm').trim();
+			var radiusKm =
+				rkRaw !== '' && !isNaN(Number(rkRaw)) ? Number(rkRaw) : DEFAULT_REC_RADIUS_KM;
+
+			var page = parseInt(q('page') || '1', 10);
+			var pageSize = parseInt(q('pageSize') || '20', 10);
+			if (isNaN(page) || page < 1) page = 1;
+			if (isNaN(pageSize) || pageSize < 1) pageSize = 20;
+
+			var c1Raw = q('categoryOneId').trim();
+			var c2Raw = q('categoryTwoId').trim();
+			var categoryOneId =
+				c1Raw === '' || c1Raw.toLowerCase() === 'null'
+					? null
+					: isNaN(Number(c1Raw))
+						? null
+						: Number(c1Raw);
+			var categoryTwoId =
+				c2Raw === '' || c2Raw.toLowerCase() === 'null'
+					? null
+					: isNaN(Number(c2Raw))
+						? null
+						: Number(c2Raw);
+
+			var body = {
+				categoryOneId: categoryOneId,
+				categoryTwoId: categoryTwoId,
+				page: page,
+				pageSize: pageSize,
+				radiusKm: radiusKm,
+				userLat: userLat,
+				userLng: userLng,
+				userId: userId
+			};
+
+			return fetch(API_LIFE_AI_BASE + SECOND_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();
+			});
+		}
+
+		function normalizeGlobalRecommendList(res) {
+			if (!res || typeof res !== 'object') return [];
+			var raw = null;
+			if (isApiOk(res)) {
+				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;
+			}
+			return [];
+		}
+
+		function buildHeroSlides(urls) {
+			var track = document.getElementById('heroTrack');
+			var dotsWrap = document.getElementById('heroDots');
+			track.innerHTML = '';
+			dotsWrap.innerHTML = '';
+			var list = (urls || []).filter(Boolean);
+			if (!list.length) {
+				var slide = document.createElement('div');
+				slide.className = 'hero__slide';
+				var img = document.createElement('img');
+				img.src = 'images/hero.png';
+				img.alt = '店铺';
+				slide.appendChild(img);
+				track.appendChild(slide);
+				var dot = document.createElement('span');
+				dot.className = 'hero__dot is-active';
+				dotsWrap.appendChild(dot);
+				return initHeroCarousel(1);
+			}
+			list.forEach(function (url, d) {
+				var slide = document.createElement('div');
+				slide.className = 'hero__slide';
+				var img = document.createElement('img');
+				img.src = url;
+				img.alt = '店铺图';
+				slide.appendChild(img);
+				track.appendChild(slide);
+				var dot = document.createElement('span');
+				dot.className = 'hero__dot' + (d === 0 ? ' is-active' : '');
+				dotsWrap.appendChild(dot);
+			});
+			initHeroCarousel(list.length);
+		}
+
+		var heroI = 0;
+		var heroTimer = null;
+		function initHeroCarousel(slides) {
+			var track = document.getElementById('heroTrack');
+			var dotsWrap = document.getElementById('heroDots');
+			var dots = dotsWrap.querySelectorAll('.hero__dot');
+			if (slides < 2) return;
+
+			function go(n) {
+				heroI = (n + slides) % slides;
+				track.style.transform = 'translateX(' + (-heroI * 100) + '%)';
+				dots.forEach(function (el, idx) {
+					el.classList.toggle('is-active', idx === heroI);
+				});
+			}
+			if (heroTimer) clearInterval(heroTimer);
+			heroTimer = setInterval(function () { go(heroI + 1); }, 4000);
+
+			var hero = document.getElementById('hero');
+			var startX = 0;
+			hero.addEventListener('touchstart', function (e) {
+				startX = e.changedTouches[0].clientX;
+			}, { passive: true });
+			hero.addEventListener('touchend', function (e) {
+				var dx = e.changedTouches[0].clientX - startX;
+				if (Math.abs(dx) > 40) go(dx < 0 ? heroI + 1 : heroI - 1);
+			}, { passive: true });
+		}
+
+		function timeToMinutes(t) {
+			var parts = String(t || '').trim().split(':');
+			var h = parseInt(parts[0], 10) || 0;
+			var m = parseInt(parts[1], 10) || 0;
+			return h * 60 + m;
+		}
+
+		function isTimeInRange(startTime, endTime) {
+			var s = String(startTime || '').trim();
+			var e = String(endTime || '').trim();
+			if (s === '00:00' && e === '00:00') return true;
+			if (s === '00:00' && e === '23:59') return true;
+			var cur = new Date().getHours() * 60 + new Date().getMinutes();
+			var sm = timeToMinutes(s);
+			var em = timeToMinutes(e);
+			if (em < sm) {
+				return cur >= sm || cur <= em;
+			}
+			return cur >= sm && cur <= em;
+		}
+
+		function parseHolidayDate(str) {
+			if (!str) return null;
+			var d = new Date(String(str).trim().replace(/-/g, '/'));
+			return isNaN(d.getTime()) ? null : d;
+		}
+
+		function isTodayInHolidayRange(holidayInfo) {
+			if (!holidayInfo) return false;
+			var hi = holidayInfo;
+			if (typeof hi === 'string') {
+				try {
+					hi = JSON.parse(hi);
+				} catch (err) {
+					return false;
+				}
+			}
+			var start = hi.startDate || hi.holidayStartDate || hi.beginDate || hi.start || hi.holidayStart;
+			var end = hi.endDate || hi.holidayEndDate || hi.finishDate || hi.end || hi.holidayEnd;
+			if (!start || !end) return false;
+			var startD = parseHolidayDate(start);
+			var endD = parseHolidayDate(end);
+			if (!startD || !endD) return false;
+			var today = new Date();
+			today.setHours(0, 0, 0, 0);
+			startD.setHours(0, 0, 0, 0);
+			endD.setHours(0, 0, 0, 0);
+			return today >= startD && today <= endD;
+		}
+
+		function findNormalBusinessInfo(list) {
+			var wdJs = new Date().getDay();
+			var i;
+			var x;
+			var w;
+			for (i = 0; i < list.length; i++) {
+				x = list[i];
+				if (!x || x.holidayInfo) continue;
+				w = x.weekDay != null ? x.weekDay : x.week;
+				if (w === undefined || w === null) continue;
+				if (Number(w) === wdJs) return x;
+			}
+			var wdMap = wdJs === 0 ? 7 : wdJs;
+			for (i = 0; i < list.length; i++) {
+				x = list[i];
+				if (!x || x.holidayInfo) continue;
+				w = x.weekDay != null ? x.weekDay : x.week;
+				if (w === undefined || w === null) continue;
+				if (Number(w) === wdMap) return x;
+			}
+			for (i = 0; i < list.length; i++) {
+				x = list[i];
+				if (x && !x.holidayInfo && (x.startTime || x.endTime)) return x;
+			}
+			return null;
+		}
+
+		/** 营业状态:暂停营业 | 商家暂未配置营业时间 | 休息中 | 营业中 xx:xx至xx:xx | 营业中 全天营业 */
+		function computeBusinessStatusDisplay(d) {
+			if (!d) return { text: '—', isOpen: false };
+			if (Number(d.businessStatus) === 1) {
+				return { text: '暂停营业', isOpen: false };
+			}
+
+			var list = d.storeBusinessInfoVos || d.storeBusinessInfos || [];
+			if (!list.length) {
+				return { text: '商家暂未配置营业时间', isOpen: false };
+			}
+
+			var holidayList = Array.isArray(d.holidayBusinessInfoList) && d.holidayBusinessInfoList.length
+				? d.holidayBusinessInfoList
+				: list.filter(function (item) { return item && item.holidayInfo; });
+
+			var todayHolidayItem = holidayList.find(function (item) {
+				return isTodayInHolidayRange(item.holidayInfo);
+			});
+
+			var normal = d.normalBusinessInfo || d.normalBusinessInfoVo || findNormalBusinessInfo(list);
+
+			var isOpen = false;
+			var currentItem = null;
+
+			if (todayHolidayItem) {
+				isOpen = isTimeInRange(todayHolidayItem.startTime, todayHolidayItem.endTime);
+				currentItem = todayHolidayItem;
+			} else if (normal) {
+				isOpen = isTimeInRange(normal.startTime, normal.endTime);
+				currentItem = normal;
+			}
+
+			if (!currentItem) return { text: '休息中', isOpen: false };
+			if (!isOpen) return { text: '休息中', isOpen: false };
+
+			var st = String(currentItem.startTime || '').trim();
+			var et = String(currentItem.endTime || '').trim();
+			if (st === '00:00' && et === '00:00') return { text: '营业中 全天营业', isOpen: true };
+			if (st === '00:00' && et === '23:59') return { text: '营业中 全天营业', isOpen: true };
+			var needNextDay = timeToMinutes(et) < timeToMinutes(st);
+			var suffix = (needNextDay ? '次日' : '') + et;
+			return { text: ('营业中 ' + st + ' 至 ' + suffix).trim(), isOpen: true };
+		}
+
+		function renderDetail(d) {
+			if (!d) return;
+			document.getElementById('storeName').textContent = d.storeName || '—';
+			var score = d.scoreAvg != null ? Number(d.scoreAvg) : NaN;
+			document.getElementById('scoreAvg').textContent = !isNaN(score) ? score.toFixed(1) : '—';
+			document.getElementById('commitCount').textContent = d.commitCount != null ? String(d.commitCount) : '0';
+			var bizDisp = computeBusinessStatusDisplay(d);
+			var bizEl = document.getElementById('bizStatus');
+			bizEl.textContent = bizDisp.text;
+			bizEl.className = 'status' + (bizDisp.isOpen ? ' is-open' : '');
+			document.getElementById('hoursText').textContent = '';
+			document.getElementById('storeAddr').textContent = d.queryAddress || d.storeAddress || '—';
+
+			if (d.distance != null) {
+				document.getElementById('metaDistance').style.display = 'flex';
+				document.getElementById('distanceText').textContent =
+					'距您' + (Number(d.distance) > 1 ? d.distance + '千米' : Math.round(Number(d.distance) * 1000) + '米');
+			}
+			if (d.subwayName && d.distance2 != null) {
+				document.getElementById('metaSubway').style.display = 'flex';
+				document.getElementById('subwayText').textContent =
+					'距' + d.subwayName + '步行' + (Number(d.distance2) >= 1000
+						? (Number(d.distance2) / 1000).toFixed(1) + 'km'
+						: Math.round(Number(d.distance2)) + 'm');
+			}
+
+			var imgs = [];
+			if (Array.isArray(d.storeAlbumUrlList) && d.storeAlbumUrlList.length) imgs = d.storeAlbumUrlList.slice();
+			else if (d.entranceImage) imgs = [d.entranceImage];
+			buildHeroSlides(imgs);
+		}
+
+		function formatRecDist(m) {
+			if (m == null || m === '') return '';
+			var n = Number(m);
+			if (isNaN(n)) return String(m);
+			return n >= 1000 ? (n / 1000).toFixed(1) + 'km' : Math.round(n) + 'm';
+		}
+
+		function escHtml(s) {
+			return String(s == null ? '' : s)
+				.replace(/&/g, '&amp;')
+				.replace(/</g, '&lt;')
+				.replace(/>/g, '&gt;')
+				.replace(/"/g, '&quot;');
+		}
+
+		/** global-recommend records:homeImage、title、position、dist、price、amount、likeCount、collectCount、userName、userImage */
+		function renderRecommended(list) {
+			var wrap = document.getElementById('recList');
+			var empty = document.getElementById('recEmpty');
+			wrap.querySelectorAll('.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 card = document.createElement('article');
+				card.className = 'rec-card';
+				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 =
+					home ||
+					(item.coverUrl != null ? String(item.coverUrl).trim() : '') ||
+					(item.mainImage != null ? String(item.mainImage).trim() : '') ||
+					(item.goodsImage != null ? String(item.goodsImage).trim() : '') ||
+					(Array.isArray(item.goodsImageList) && item.goodsImageList[0]) ||
+					(Array.isArray(item.imageList) && item.imageList[0]) ||
+					'images/hero.png';
+				var name = item.title != null && String(item.title).trim() !== ''
+					? String(item.title).replace(/\r?\n/g, ' ').replace(/\s+/g, ' ').trim()
+					: (item.goodsName ||
+							item.secondGoodsTitle ||
+							item.name ||
+							'商品');
+				var dist = '';
+				if (item.position != null && String(item.position).trim() !== '') {
+					dist = String(item.position).trim();
+				} else if (item.dist != null && item.dist !== '') {
+					var dn = Number(item.dist);
+					if (!isNaN(dn)) {
+						dist = dn.toFixed(dn % 1 === 0 ? 0 : 2) + 'km';
+					}
+				} else if (item.distance != null && item.distance !== '') {
+					var d2 = Number(item.distance);
+					dist = !isNaN(d2) ? d2.toFixed(d2 % 1 === 0 ? 0 : 2) + 'km' : String(item.distance);
+				}
+				var priceStr = '';
+				if (item.price != null && String(item.price).trim() !== '') {
+					var ps = String(item.price).trim();
+					var pNum = parseFloat(ps.replace(/[^\d.]/g, ''));
+					priceStr = !isNaN(pNum) ? pNum.toFixed(2) : ps;
+				} else if (item.amount != null && item.amount !== '') {
+					var an = Number(item.amount);
+					priceStr = !isNaN(an) ? an.toFixed(2) : String(item.amount);
+				} else {
+					priceStr = '—';
+				}
+				var seller =
+					item.userName != null && String(item.userName).trim() !== ''
+						? String(item.userName).trim()
+						: '';
+				var userImg =
+					item.userImage != null && String(item.userImage).trim() !== ''
+						? String(item.userImage).trim()
+						: '';
+				var likeN = item.likeCount != null ? Number(item.likeCount) : NaN;
+				var colN = item.collectCount != null ? Number(item.collectCount) : NaN;
+				var metaBits = [];
+				// if (!isNaN(likeN)) metaBits.push(likeN + '赞');
+				// if (!isNaN(colN)) metaBits.push(colN + '想要');
+				var metaRight = metaBits.join(' · ');
+				var showSellerRow = !!(seller || userImg);
+
+				card.innerHTML =
+					'<div class="rec-card__img"><img class="rec-card__cover" src="" alt=""></div>' +
+					'<div class="rec-card__body">' +
+					'<div class="rec-card__top">' +
+					'<span class="rec-card__name">' + escHtml(name) + '</span>' +
+					(dist ? '<span class="rec-card__dist">' + escHtml(dist) + '</span>' : '') +
+					'</div>' +
+					'<div class="rec-card__rating">' +
+					'<span class="rec-meta" style="color:#E62E2E;font-weight:600;">¥' + escHtml(priceStr) + '</span>' +
+					(metaRight
+						? '<span class="rec-meta">' + escHtml(metaRight) + '</span>'
+						: '') +
+					'</div>' +
+					(showSellerRow
+						? '<div class="rec-card__seller">' +
+							'<img class="rec-card__avatar" src="" alt="" width="22" height="22" decoding="async">' +
+							(seller ? '<span class="rec-card__seller-name">' + escHtml(seller) + '</span>' : '') +
+							'</div>'
+						: '') +
+					'</div>';
+				var coverIm = card.querySelector('img.rec-card__cover');
+				if (coverIm) {
+					coverIm.src = imgUrl;
+					coverIm.onerror = function () {
+						this.onerror = null;
+						this.src = 'images/hero.png';
+					};
+				}
+				var avIm = card.querySelector('img.rec-card__avatar');
+				if (avIm) {
+					avIm.src = userImg || 'images/demouser.png';
+					avIm.onerror = function () {
+						this.onerror = null;
+						this.src = 'images/demouser.png';
+					};
+				}
+				wrap.appendChild(card);
+			});
+		}
+
+		function run() {
+			fetchSecondGlobalRecommend()
+				.then(function (res) {
+					var list = normalizeGlobalRecommendList(res);
+					if (!list.length && res && res.msg) {
+						console.warn('[global-recommend]', res.msg);
+					}
+					renderRecommended(list);
+				})
+				.catch(function (e) {
+					console.error(e);
+					renderRecommended([]);
+				});
+		}
+
+		function boot() {
+			run();
+			var openBtn = document.getElementById('openApp');
+			if (openBtn) {
+				openBtn.addEventListener('click', function () {
+					tryOpenUShopApp();
+				});
+			}
+		}
+
+		if (document.readyState === 'complete') {
+			boot();
+		} else {
+			window.onload = boot;
+		}
+	})();
+	</script>
+</body>
+</html>