LuTong 1 ماه پیش
کامیت
19209f9a81

BIN
HBuilderProjects/images/demouser.png


BIN
HBuilderProjects/images/dynamic.png


BIN
HBuilderProjects/images/empty.png


BIN
HBuilderProjects/images/hero.png


+ 3 - 0
HBuilderProjects/images/huo.svg

@@ -0,0 +1,3 @@
+<svg width="13" height="16" viewBox="0 0 13 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10.5313 5.20008L9.31456 6.4C9.31456 6.4 9.31456 1.6001 5.25847 0C5.25847 0 4.85288 4.40013 2.82495 6.00003C0.79701 7.59992 -3.25907 12.4 4.85267 16C4.85267 16 0.796586 11.6001 6.06965 8.39987C6.06965 8.39987 5.66385 9.99997 7.69199 11.6001C9.72014 13.2002 7.69199 16 7.69199 16C7.69199 16 17.4265 13.5999 10.53 5.20008H10.5313Z" fill="#FE3929"/>
+</svg>

BIN
HBuilderProjects/images/iconn.png


+ 10 - 0
HBuilderProjects/images/juli.svg

@@ -0,0 +1,10 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_22058_23919)">
+<path d="M8.20823 13.0566H8.1787C7.91292 13.0418 7.68897 12.8622 7.61761 12.6062L6.28132 7.74588L1.39143 6.38252C1.1355 6.31115 0.955848 6.08475 0.943544 5.82143C0.931239 5.55564 1.08874 5.31447 1.33483 5.2185L12.2196 0.985684C12.2909 0.958613 12.3648 0.943848 12.4435 0.943848C12.6084 0.943848 12.761 1.00783 12.8791 1.1235C13.0514 1.29576 13.1055 1.55416 13.0169 1.78057L8.78409 12.6653C8.68811 12.904 8.46417 13.0566 8.20823 13.0566ZM1.79257 5.77959L6.83995 7.18725L8.22054 12.2076L12.3106 1.68951L1.79257 5.77959Z" fill="#666666"/>
+</g>
+<defs>
+<clipPath id="clip0_22058_23919">
+<rect width="14" height="14" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 3 - 0
HBuilderProjects/images/liuyan.svg

@@ -0,0 +1,3 @@
+<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.4012 14L11.9778 13.7381L8.75576 11.2222C8.68314 11.1627 8.58973 11.1302 8.49325 11.131H2.18047C1.60179 11.1299 1.04719 10.9137 0.638392 10.5298C0.229595 10.1459 3.02427e-06 9.62571 4.11098e-06 9.08333V2.05159C-0.000552437 1.78269 0.0554057 1.51632 0.164683 1.26769C0.273961 1.01907 0.434419 0.793046 0.636894 0.602539C0.83937 0.412031 1.0799 0.260767 1.34475 0.157383C1.60959 0.0539995 1.89358 0.000520613 2.18047 0H2.99339V1.19048H2.18047C2.06022 1.19047 1.94115 1.2128 1.83015 1.25617C1.71916 1.29954 1.61843 1.3631 1.53379 1.44316C1.44915 1.52323 1.38227 1.61822 1.33703 1.72265C1.29178 1.82708 1.26906 1.93888 1.27018 2.05159V9.08333C1.27018 9.30998 1.36595 9.52739 1.53654 9.68803C1.70713 9.84866 1.93866 9.93943 2.18047 9.94048H8.50596C8.89875 9.9416 9.27892 10.0707 9.58137 10.3056L11.783 12.0238V10.627C11.7852 10.4449 11.8637 10.2709 12.0015 10.1425C12.1393 10.014 12.3254 9.9415 12.5197 9.94048H13.8195C14.0613 9.93943 14.2929 9.84866 14.4635 9.68803C14.6341 9.52739 14.7298 9.30998 14.7298 9.08333V2.05159C14.7298 1.82531 14.6339 1.6083 14.4632 1.4483C14.2925 1.2883 14.061 1.19841 13.8195 1.19841H6.52871V0.0079365H13.8195C14.3975 0.00898527 14.9514 0.224634 15.3601 0.607666C15.7688 0.990698 15.9989 1.5099 16 2.05159V9.08333C16 9.62571 15.7704 10.1459 15.3616 10.5298C14.9528 10.9137 14.3982 11.1299 13.8195 11.131H13.0532V13.2103L13.1252 13.5436L12.4012 14Z" fill="#151515"/>
+</svg>

BIN
HBuilderProjects/images/loc1.png


BIN
HBuilderProjects/images/loc2.png


BIN
HBuilderProjects/images/map.png


BIN
HBuilderProjects/images/storeNone.png


BIN
HBuilderProjects/images/uBtn.png


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
HBuilderProjects/images/xing.svg


BIN
HBuilderProjects/images/zwpl.png


+ 1406 - 0
HBuilderProjects/secondShareGoods.html

@@ -0,0 +1,1406 @@
+<!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>商品分享</title>
+	<style>
+		:root {
+			--orange: #F58220;
+			--price-red: #E62E2E;
+			--tag-bg: #fff5e9;
+			--tag-text: #b05b28;
+			--text: #1a1a1a;
+			--text-secondary: #999999;
+			--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: #fff;
+			color: var(--text);
+			min-height: 100vh;
+			padding-bottom: calc(100px + var(--safe-bottom));
+		}
+
+		.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: #F47D1F;
+			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;
+		}
+
+		/* 顶部大图 + 叠字 */
+		.goods-hero {
+			position: relative;
+			width: 100%;
+			min-height: 58vh;
+			max-height: 520px;
+			background: #e8e8e8 center / cover no-repeat;
+			overflow: hidden;
+		}
+
+		.goods-hero__img {
+			position: absolute;
+			inset: 0;
+			width: 100%;
+			height: 100%;
+			object-fit: cover;
+			display: block;
+		}
+
+		.goods-hero__shade {
+			position: absolute;
+			inset: 0;
+			background: linear-gradient(
+				180deg,
+				rgba(0, 0, 0, 0.35) 0%,
+				rgba(0, 0, 0, 0.1) 38%,
+				rgba(0, 0, 0, 0.45) 100%
+			);
+			pointer-events: none;
+		}
+
+		.goods-hero__inner {
+			position: relative;
+			z-index: 1;
+			min-height: 58vh;
+			max-height: 520px;
+			padding: calc(12px + env(safe-area-inset-top, 0px)) 16px 28px;
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+			align-items: flex-start;
+		}
+
+		.goods-hero__user {
+			display: flex;
+			align-items: center;
+			gap: 10px;
+		}
+
+		.goods-hero__avatar {
+			width: 40px;
+			height: 40px;
+			border-radius: 50%;
+			object-fit: cover;
+			flex-shrink: 0;
+			background: rgba(255, 255, 255, 0.3);
+		}
+
+		.goods-hero__user-meta {
+			display: flex;
+			flex-direction: column;
+			gap: 2px;
+		}
+
+		.goods-hero__name {
+			font-size: 15px;
+			font-weight: 600;
+			color: #fff;
+			text-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
+		}
+
+		.goods-hero__time {
+			font-size: 12px;
+			color: rgba(255, 255, 255, 0.82);
+			text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
+		}
+
+		.goods-hero__slogan {
+			margin-top: auto;
+			padding-bottom: 4px;
+		}
+
+		.goods-hero__slogan-line {
+			font-size: 26px;
+			font-weight: 800;
+			line-height: 1.25;
+			color: #fff;
+			letter-spacing: 0.02em;
+			text-shadow: 0 2px 12px rgba(0, 0, 0, 0.45);
+		}
+
+		/* 白色内容区 */
+		.goods-panel {
+			background: #fff;
+			border-radius: 16px 16px 0 0;
+			margin-top: -16px;
+			position: relative;
+			z-index: 2;
+			padding: 20px 16px 24px;
+			box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.06);
+		}
+
+		.goods-panel__row-price {
+			display: flex;
+			align-items: flex-end;
+			justify-content: space-between;
+			gap: 12px;
+			margin-bottom: 14px;
+		}
+
+		.goods-panel__price {
+			display: flex;
+			align-items: baseline;
+			gap: 2px;
+			color: var(--price-red);
+			font-weight: 800;
+			line-height: 1;
+		}
+
+		.goods-panel__price-yen {
+			font-size: 18px;
+		}
+
+		.goods-panel__price-num {
+			font-size: 28px;
+			letter-spacing: -0.02em;
+		}
+
+		.goods-panel__dist {
+			display: inline-flex;
+			align-items: center;
+			gap: 4px;
+			font-size: 12px;
+			color: var(--text-secondary);
+			flex-shrink: 0;
+			padding-bottom: 2px;
+		}
+
+		.goods-panel__dist svg,
+		.goods-panel__dist img {
+			width: 14px;
+			height: 14px;
+			opacity: 0.85;
+			display: block;
+			flex-shrink: 0;
+			object-fit: contain;
+		}
+
+		.goods-panel__title {
+			font-size: 17px;
+			font-weight: 700;
+			line-height: 1.45;
+			color: var(--text);
+			margin-bottom: 16px;
+		}
+
+		.goods-panel__row-stats {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			gap: 12px;
+			margin-bottom: 16px;
+		}
+
+		.goods-panel__stats {
+			display: flex;
+			align-items: center;
+			gap: 18px;
+		}
+
+		.goods-panel__stat {
+			display: inline-flex;
+			align-items: center;
+			gap: 5px;
+			font-size: 14px;
+			color: var(--text-secondary);
+		}
+
+		.goods-panel__stat svg {
+			width: 16px;
+			height: 16px;
+			flex-shrink: 0;
+		}
+
+		.goods-panel__stat img {
+			width: 16px;
+			height: 16px;
+			flex-shrink: 0;
+			display: block;
+			object-fit: contain;
+		}
+
+		.goods-panel__share {
+			display: inline-flex;
+			align-items: center;
+			gap: 4px;
+			padding: 0;
+			border: none;
+			background: none;
+			font-size: 14px;
+			color: var(--text-secondary);
+			cursor: pointer;
+			-webkit-tap-highlight-color: transparent;
+		}
+
+		.goods-panel__share svg {
+			width: 18px;
+			height: 18px;
+		}
+
+		.goods-panel__tags {
+			display: flex;
+			flex-wrap: wrap;
+			align-items: center;
+			gap: 8px;
+		}
+
+		.goods-tag {
+			display: inline-block;
+			padding: 2px 8px;
+			border-radius: 4px;
+			font-size: 12px;
+			font-weight: 400;
+			line-height: 1.35;
+			background: #fff5e9;
+		}
+
+		.goods-tag--topic {
+			color: #b05b28;
+		}
+
+		.goods-tag--label {
+			color: #333333;
+		}
+
+		/* 留言 */
+		.goods-comments {
+			margin-top: 20px;
+			padding-top: 20px;
+			border-top: 1px solid #eee;
+		}
+
+		.goods-comments__head {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			margin-bottom: 18px;
+		}
+
+		.goods-comments__title {
+			font-size: 17px;
+			font-weight: 700;
+			color: var(--text);
+		}
+
+		.goods-comments__total {
+			display: inline-flex;
+			align-items: center;
+			gap: 4px;
+			font-size: 14px;
+			color: #151515;
+		}
+
+		.goods-comments__total svg {
+			width: 18px;
+			height: 18px;
+			opacity: 0.85;
+		}
+
+		.goods-comments__list {
+			list-style: none;
+		}
+
+		.goods-comments__empty {
+			list-style: none;
+			padding: 24px 12px;
+			text-align: center;
+			font-size: 14px;
+			color: var(--text-secondary);
+		}
+
+		.goods-cmt {
+			padding-bottom: 18px;
+			margin-bottom: 18px;
+			border-bottom: 1px solid #eee;
+		}
+
+		.goods-cmt:last-child {
+			margin-bottom: 0;
+			padding-bottom: 0;
+			border-bottom: none;
+		}
+
+		.goods-cmt__row {
+			display: flex;
+			align-items: flex-start;
+			gap: 12px;
+		}
+
+		.goods-cmt__avatar {
+			width: 40px;
+			height: 40px;
+			border-radius: 50%;
+			object-fit: cover;
+			background: #e8e8e8;
+			flex-shrink: 0;
+		}
+
+		.goods-cmt--nested .goods-cmt__avatar {
+			width: 32px;
+			height: 32px;
+		}
+
+		.goods-cmt__body {
+			flex: 1;
+			min-width: 0;
+		}
+
+		.goods-cmt__name {
+			font-size: 15px;
+			font-weight: 700;
+			color: var(--text);
+			line-height: 1.3;
+		}
+
+		.goods-cmt__time {
+			display: block;
+			margin-top: 2px;
+			font-size: 12px;
+			color: var(--text-secondary);
+			line-height: 1.35;
+		}
+
+		.goods-cmt__text {
+			margin-top: 10px;
+			font-size: 15px;
+			line-height: 1.5;
+			color: var(--text);
+			word-break: break-word;
+		}
+
+		.goods-cmt__actions {
+			display: flex;
+			align-items: center;
+			gap: 20px;
+			margin-top: 12px;
+		}
+
+		.goods-cmt__action {
+			display: inline-flex;
+			align-items: center;
+			gap: 4px;
+			padding: 0;
+			border: none;
+			background: none;
+			font-size: 13px;
+			color: var(--text-secondary);
+			cursor: pointer;
+			-webkit-tap-highlight-color: transparent;
+		}
+
+		.goods-cmt__action svg {
+			width: 16px;
+			height: 16px;
+			flex-shrink: 0;
+			opacity: 0.88;
+		}
+
+		.goods-cmt__thread {
+			margin-top: 14px;
+			padding-left: 52px;
+		}
+
+		.goods-cmt__replies-inner.is-collapsed {
+			display: none;
+		}
+
+		.goods-cmt__thread-bar {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			width: 100%;
+			margin-top: 10px;
+			padding: 10px 12px;
+			border: none;
+			border-radius: 8px;
+			background: #f5f5f5;
+			font-size: 13px;
+			color: #888;
+			cursor: pointer;
+			-webkit-tap-highlight-color: transparent;
+		}
+
+		.goods-cmt__thread-bar-right {
+			display: inline-flex;
+			align-items: center;
+			gap: 4px;
+			color: var(--text-secondary);
+		}
+
+		.goods-cmt__thread-chevron {
+			width: 14px;
+			height: 14px;
+			transition: transform 0.2s ease;
+			flex-shrink: 0;
+		}
+
+		.goods-cmt__thread-bar.is-collapsed .goods-cmt__thread-chevron {
+			transform: rotate(180deg);
+		}
+
+		.goods-cmt--nested {
+			padding-bottom: 0;
+			margin-bottom: 0;
+			border-bottom: none;
+		}
+
+		.goods-cmt--nested .goods-cmt__text {
+			margin-top: 8px;
+		}
+	</style>
+</head>
+<body>
+	<section class="goods-hero" id="goodsHero" aria-label="商品配图">
+		<img class="goods-hero__img" id="goodsHeroImg" src="" alt="">
+		<div class="goods-hero__shade" aria-hidden="true"></div>
+		<div class="goods-hero__inner">
+			<div class="goods-hero__user">
+				<img class="goods-hero__avatar" id="goodsUserAvatar" src="images/demouser.png" alt="">
+				<div class="goods-hero__user-meta">
+					<span class="goods-hero__name" id="goodsUserName">维尼</span>
+					<span class="goods-hero__time" id="goodsTimeAgo">11小时前</span>
+				</div>
+			</div>
+			<div class="goods-hero__slogan" id="goodsHeroSloganWrap" style="display:none;">
+				<div class="goods-hero__slogan-line" id="goodsSlogan1"></div>
+				<div class="goods-hero__slogan-line" id="goodsSlogan2"></div>
+			</div>
+		</div>
+	</section>
+
+	<div class="goods-panel">
+		<div class="goods-panel__row-price">
+			<div class="goods-panel__price">
+				<span class="goods-panel__price-yen">¥</span>
+				<span class="goods-panel__price-num" id="goodsPrice">690.00</span>
+			</div>
+			<div class="goods-panel__dist">
+				<img src="images/juli.svg" alt="" width="14" height="14" decoding="async" aria-hidden="true">
+				<span id="goodsDistance">距离 2.5km</span>
+			</div>
+		</div>
+		<h1 class="goods-panel__title" id="goodsTitle">HCK哈士奇BC-330RDE烤漆复古冰箱家用大容量单门冰柜高颜值网红</h1>
+		<div class="goods-panel__row-stats">
+			<div class="goods-panel__stats">
+				<span class="goods-panel__stat goods-panel__stat--hot" id="goodsHotStat">
+					<img src="images/huo.svg" alt="" width="16" height="16" decoding="async" aria-hidden="true">
+					<span id="goodsHotCount" style="color:#151515;">510</span>
+				</span>
+				<span class="goods-panel__stat">
+					<img src="images/xing.svg" alt="" width="16" height="16" decoding="async" aria-hidden="true">
+					<span id="goodsFavCount" style="color:#151515;">260</span>
+				</span>
+			</div>
+		</div>
+		<div class="goods-panel__tags" id="goodsTags"></div>
+
+		<section class="goods-comments" id="goodsComments" aria-label="留言">
+			<div class="goods-comments__head">
+				<h2 class="goods-comments__title">留言</h2>
+				<div class="goods-comments__total">
+					<img src="images/liuyan.svg" alt="" width="16" height="16" decoding="async" aria-hidden="true">
+					<span id="goodsCommentTotal">24</span>
+				</div>
+			</div>
+			<ul class="goods-comments__list" id="goodsCommentList"></ul>
+		</section>
+	</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 https://test.ailien.shop/alienSecond/recommend/querySecondGoodsDetail
+		 *   body: application/x-www-form-urlencoded(「表单数据」);键 goodsId、longitude、latitude,值均为字符串;经纬度未传时用默认 121.662567 / 38.925757
+		 * 留言:GET {API_BASE}/commonComment/getListBySourceType — 成功时 data 常为数组;项含 parentId、childCommonComments、headName、headImg、createdTime、content、likeCount
+		 * URL 常用:goodsId、userId、longitude/lon/jingdu、latitude/lat/weidu、pageNum、pageSize、sourceType(含 hash 内 ? 参数)
+		 */
+		/**
+		 * 唤起 App 落地:二手商品详情(与 pages.json 路径一致)
+		 */
+		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) {}
+		}
+
+		function tryOpenHBuilderApp() {
+			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 q(name) {
+			var v = mergeSearchAndHashParams().get(name);
+			return v == null ? '' : String(v);
+		}
+		function tryDecode(s) {
+			s = String(s || '');
+			if (!s) return '';
+			try {
+				return decodeURIComponent(s.replace(/\+/g, ' '));
+			} catch (e) {
+				return s;
+			}
+		}
+
+		var DEFAULT_HERO = '';
+		var API_BASE = 'https://test.ailien.shop/alienStore';
+		/** 二手商品等业务接口前缀(与 alienStore 分离) */
+		var API_BASE_SECOND = 'https://test.ailien.shop/alienSecond';
+		/** 与接口调试「表单数据」一致;地址栏未带经纬度时使用 */
+		var DEFAULT_REQUEST_LONGITUDE = '121.662567';
+		var DEFAULT_REQUEST_LATITUDE = '38.925757';
+		var COMMENT_SOURCE_TYPE = 4;
+
+		function apiGet(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 apiPostJson(path, body) {
+			return fetch(API_BASE + path, {
+				method: 'POST',
+				mode: 'cors',
+				credentials: 'omit',
+				headers: {
+					Accept: 'application/json',
+					'Content-Type': 'application/json'
+				},
+				body: JSON.stringify(body || {})
+			}).then(function (res) {
+				if (!res.ok) throw new Error('HTTP ' + res.status);
+				return res.json();
+			});
+		}
+
+		function pick(d, keys) {
+			if (!d || typeof d !== 'object') return null;
+			for (var i = 0; i < keys.length; i++) {
+				var k = keys[i];
+				if (d[k] != null && d[k] !== '') return d[k];
+			}
+			return null;
+		}
+
+		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 fmtPriceNum(v) {
+			var n = v != null ? Number(v) : NaN;
+			return !isNaN(n) ? n.toFixed(2) : String(v != null ? v : '');
+		}
+
+		/** kind: topic(#话题 棕橙)| label(分类名 深灰,不带 #) */
+		function appendGoodsTag(mount, raw, kind) {
+			if (!mount || raw == null) return;
+			var s = String(raw).trim();
+			if (!s) return;
+			var span = document.createElement('span');
+			span.className =
+				'goods-tag ' + (kind === 'label' ? 'goods-tag--label' : 'goods-tag--topic');
+			if (kind === 'label') {
+				span.textContent = s.replace(/^#+/, '');
+			} else {
+				span.textContent = s.indexOf('#') === 0 ? s : '#' + s;
+			}
+			mount.appendChild(span);
+		}
+
+		function renderGoodsDetail(d) {
+			if (!d || typeof d !== 'object') return;
+
+			var title = pick(d, ['goodsName', 'title', 'secondGoodsTitle', 'name']);
+			if (title) {
+				var el = document.getElementById('goodsTitle');
+				if (el) el.textContent = String(title);
+			}
+
+			var price = pick(d, ['price', 'goodsPrice', 'salePrice', 'currentPrice', 'amount']);
+			if (price != null) {
+				var pe = document.getElementById('goodsPrice');
+				if (pe) pe.textContent = fmtPriceNum(price);
+			}
+
+			var dist = pick(d, ['dist', 'distance', 'distanceText', 'distanceDesc', 'distanceDescription', 'distanceKm']);
+			if (dist != null) {
+				var dt = String(dist).trim();
+				if (dt) {
+					if (!/^距离/.test(dt)) dt = '距离 ' + dt;
+					if (!/km$/i.test(dt) && !/千米/.test(dt) && !/米/.test(dt) && !/\s*m\s*$/i.test(dt)) {
+						dt = dt.replace(/\s+$/, '') + 'km';
+					}
+					var de = document.getElementById('goodsDistance');
+					if (de) de.textContent = dt;
+				}
+			}
+
+			var img =
+				pick(d, ['coverUrl', 'mainImage', 'goodsImage', 'firstImage', 'bannerUrl']) ||
+				(Array.isArray(d.goodsImageList) && d.goodsImageList[0]) ||
+				(Array.isArray(d.imageList) && d.imageList[0]);
+			if (img) {
+				var hi = document.getElementById('goodsHeroImg');
+				if (hi) {
+					hi.src = String(img);
+					hi.alt = '商品图';
+				}
+			}
+
+			var nick = pick(d, ['publishUserNick', 'userNickName', 'nickName', 'userName', 'publisherName']);
+			if (nick) {
+				var ne = document.getElementById('goodsUserName');
+				if (ne) ne.textContent = String(nick);
+			}
+
+			var av = pick(d, ['publishUserAvatar', 'userAvatar', 'userHead', 'avatar', 'headImg']);
+			if (av) {
+				var ae = document.getElementById('goodsUserAvatar');
+				if (ae) ae.src = String(av);
+			}
+
+			var tm = pick(d, ['timeAgo', 'releaseTime', 'publishTime', 'createTime', 'publishTimeStr']);
+			if (tm) {
+				var te = document.getElementById('goodsTimeAgo');
+				if (te) te.textContent = String(tm);
+			}
+
+			var s1 = pick(d, ['promotionLineOne', 'promotionTitle', 'subtitle', 'slogan1', 'line1']);
+			var s2 = pick(d, ['promotionLineTwo', 'subTitle', 'slogan2', 'line2']);
+			var wrap = document.getElementById('goodsHeroSloganWrap');
+			var e1 = document.getElementById('goodsSlogan1');
+			var e2 = document.getElementById('goodsSlogan2');
+			if (wrap && e1 && e2) {
+				if (s1 || s2) {
+					if (s1) e1.textContent = String(s1);
+					if (s2) e2.textContent = String(s2);
+					wrap.style.display = '';
+				} else {
+					wrap.style.display = 'none';
+				}
+			}
+
+			var hotStat = document.getElementById('goodsHotStat');
+			var cs = pick(d, ['collectStatus']);
+			if (hotStat) {
+				var hideHot =
+					cs != null &&
+					(Number(cs) === 0 || String(cs).trim() === '0');
+				hotStat.style.display = hideHot ? 'none' : '';
+			}
+			var hot = pick(d, ['browseCount', 'viewCount', 'hotCount', 'readCount']);
+			if (hot != null) {
+				var he = document.getElementById('goodsHotCount');
+				if (he) he.textContent = String(hot);
+			}
+
+			var fav = pick(d, ['collectCount', 'wantCount', 'favCount', 'favoriteCount']);
+			if (fav != null) {
+				var fe = document.getElementById('goodsFavCount');
+				if (fe) fe.textContent = String(fav);
+			}
+
+			var topicStr = d.topic != null ? String(d.topic).trim() : '';
+			var labelStr = d.label != null ? String(d.label).trim() : '';
+			var hasApiTags = topicStr !== '' || labelStr !== '';
+			var mount = document.getElementById('goodsTags');
+			if (mount) {
+				if (hasApiTags) {
+					mount.innerHTML = '';
+					if (topicStr) appendGoodsTag(mount, topicStr, 'topic');
+					if (labelStr) appendGoodsTag(mount, labelStr, 'label');
+				} else {
+					var tags = d.tagList || d.tags || d.labelList || d.goodsTags;
+					if (tags) {
+						mount.innerHTML = '';
+						var arr = Array.isArray(tags) ? tags : String(tags).split(/[,,]/);
+						arr.forEach(function (t) {
+							if (t == null || t === '') return;
+							var label =
+								typeof t === 'object'
+									? (t.tagName || t.name || t.label || '')
+									: String(t);
+							label = String(label).trim();
+							if (!label) return;
+							var isTopic = label.indexOf('#') === 0;
+							appendGoodsTag(mount, label, isTopic ? 'topic' : 'label');
+						});
+					}
+				}
+			}
+		}
+
+		var SVG_LIKE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg>';
+		var SVG_REPLY = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>';
+		var SVG_CHEV = '<svg class="goods-cmt__thread-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>';
+
+		function cmtAvatarUrl(c) {
+			return String(
+				pick(c, ['headImg', 'userAvatar', 'userHead', 'avatar', 'avatarUrl']) || ''
+			).trim();
+		}
+
+		function cmtUserName(c) {
+			var s = String(pick(c, ['headName', 'userName', 'nickName', 'userNickName', 'commentUserName', 'headPhone']) || '—').trim();
+			return s || '—';
+		}
+
+		function cmtTime(c) {
+			return String(pick(c, ['createTime', 'createdTime', 'commentTime']) || '');
+		}
+
+		function cmtContent(c) {
+			return String(pick(c, ['content', 'commentContent', 'text']) || '');
+		}
+
+		function cmtLikeCount(c) {
+			var v = pick(c, ['likeCount', 'likeNum', 'praiseCount']);
+			return v != null ? String(v) : '0';
+		}
+
+		function cmtReplies(c) {
+			if (!c || typeof c !== 'object') return [];
+			var sub =
+				c.childCommonComments ||
+				c.replyList ||
+				c.replyVOList ||
+				c.children ||
+				c.subComments ||
+				c.replies;
+			if (sub == null) return [];
+			return Array.isArray(sub) ? sub.filter(Boolean) : [];
+		}
+
+		function appendCmtRow(parentBody, c, showReply) {
+			var row = document.createElement('div');
+			row.className = 'goods-cmt__row';
+			var img = document.createElement('img');
+			img.className = 'goods-cmt__avatar';
+			img.alt = '';
+			var au = cmtAvatarUrl(c);
+			img.src = au || 'images/demouser.png';
+			var body = document.createElement('div');
+			body.className = 'goods-cmt__body';
+			var name = document.createElement('span');
+			name.className = 'goods-cmt__name';
+			name.textContent = cmtUserName(c);
+			var time = document.createElement('span');
+			time.className = 'goods-cmt__time';
+			time.textContent = cmtTime(c);
+			var text = document.createElement('p');
+			text.className = 'goods-cmt__text';
+			text.textContent = cmtContent(c);
+			var actions = document.createElement('div');
+			actions.className = 'goods-cmt__actions';
+			var likeBtn = document.createElement('button');
+			likeBtn.type = 'button';
+			likeBtn.className = 'goods-cmt__action';
+			likeBtn.setAttribute('aria-label', '点赞');
+			likeBtn.innerHTML = SVG_LIKE + '<span>' + cmtLikeCount(c) + '</span>';
+			actions.appendChild(likeBtn);
+			if (showReply) {
+				var repBtn = document.createElement('button');
+				repBtn.type = 'button';
+				repBtn.className = 'goods-cmt__action';
+				repBtn.setAttribute('aria-label', '回复');
+				repBtn.innerHTML = SVG_REPLY + '<span>回复</span>';
+				actions.appendChild(repBtn);
+			}
+			body.appendChild(name);
+			body.appendChild(time);
+			body.appendChild(text);
+			body.appendChild(actions);
+			row.appendChild(img);
+			row.appendChild(body);
+			parentBody.appendChild(row);
+			return body;
+		}
+
+		function buildNestedCmt(c) {
+			var wrap = document.createElement('div');
+			wrap.className = 'goods-cmt goods-cmt--nested';
+			appendCmtRow(wrap, c, false);
+			return wrap;
+		}
+
+		function filterTopLevelComments(list) {
+			if (!Array.isArray(list) || !list.length) return [];
+			var top = list.filter(function (c) {
+				if (!c) return false;
+				var pid = c.parentId != null ? c.parentId : c.pid;
+				if (typeof pid === 'string') pid = pid.trim();
+				return pid == null || pid === '' || Number(pid) === 0;
+			});
+			return top.length ? top : list.slice();
+		}
+
+		function countCommentsInApiList(records) {
+			if (!Array.isArray(records)) return 0;
+			var tops = filterTopLevelComments(records);
+			var n = 0;
+			for (var i = 0; i < tops.length; i++) {
+				var c = tops[i];
+				if (!c) continue;
+				n += 1 + cmtReplies(c).length;
+			}
+			return n;
+		}
+
+		function normalizeCommentListPayload(d) {
+			if (typeof d === 'string') {
+				try {
+					d = JSON.parse(d);
+				} catch (e) {
+					return { records: [], total: 0 };
+				}
+			}
+			if (Array.isArray(d)) {
+				return { records: d, total: countCommentsInApiList(d) };
+			}
+			if (d && typeof d === 'object') {
+				var arr = d.records || d.list || d.rows;
+				if (!Array.isArray(arr) && Array.isArray(d.data)) arr = d.data;
+				if (!Array.isArray(arr)) arr = [];
+				var total =
+					d.total != null && !isNaN(Number(d.total))
+						? Number(d.total)
+						: countCommentsInApiList(arr);
+				return { records: arr, total: total };
+			}
+			return { records: [], total: 0 };
+		}
+
+		function renderCommentsList(rows, total) {
+			var mount = document.getElementById('goodsCommentList');
+			var totEl = document.getElementById('goodsCommentTotal');
+			if (!mount) return;
+			mount.innerHTML = '';
+			var list = filterTopLevelComments(Array.isArray(rows) ? rows : []);
+			var n = total != null && !isNaN(Number(total)) ? Number(total) : countCommentsInApiList(Array.isArray(rows) ? rows : []);
+			if (totEl) totEl.textContent = String(n);
+
+			if (!list.length) {
+				var empty = document.createElement('li');
+				empty.className = 'goods-comments__empty';
+				empty.setAttribute('role', 'status');
+				empty.textContent = '暂无留言';
+				mount.appendChild(empty);
+				return;
+			}
+
+			list.forEach(function (c) {
+				if (!c) return;
+				var li = document.createElement('li');
+				li.className = 'goods-cmt';
+				appendCmtRow(li, c, true);
+				var replies = cmtReplies(c);
+				if (replies.length) {
+					var thread = document.createElement('div');
+					thread.className = 'goods-cmt__thread';
+					var inner = document.createElement('div');
+					inner.className = 'goods-cmt__replies-inner';
+					replies.forEach(function (r) {
+						if (r) inner.appendChild(buildNestedCmt(r));
+					});
+					var bar = document.createElement('button');
+					bar.type = 'button';
+					bar.className = 'goods-cmt__thread-bar';
+					bar.setAttribute('aria-expanded', 'true');
+					bar.innerHTML =
+						'<span>共<span class="js-reply-count">' + replies.length + '</span>条回复</span>' +
+						'<span class="goods-cmt__thread-bar-right">' +
+						'<span class="js-thread-label">收起</span>' + SVG_CHEV + '</span>';
+					thread.appendChild(inner);
+					thread.appendChild(bar);
+					li.appendChild(thread);
+				}
+				mount.appendChild(li);
+			});
+		}
+
+		function bindCommentThreadDelegation() {
+			var list = document.getElementById('goodsCommentList');
+			if (!list || list.dataset.threadDeleg) return;
+			list.dataset.threadDeleg = '1';
+			list.addEventListener('click', function (e) {
+				var bar = e.target.closest('.goods-cmt__thread-bar');
+				if (!bar) return;
+				var inner = bar.previousElementSibling;
+				if (!inner || !inner.classList.contains('goods-cmt__replies-inner')) return;
+				var collapsed = inner.classList.toggle('is-collapsed');
+				bar.classList.toggle('is-collapsed', collapsed);
+				bar.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
+				var lab = bar.querySelector('.js-thread-label');
+				if (lab) lab.textContent = collapsed ? '展开' : '收起';
+			});
+		}
+
+		function pickGoodsStatusFromStatusApi(res) {
+			if (!res || typeof res !== 'object') return null;
+			var d = isApiOk(res) ? (res.data != null ? res.data : res.result) : null;
+			if (d != null && typeof d === 'object' && d.goodsStatus != null) {
+				return d.goodsStatus;
+			}
+			if (res.goodsStatus != null) return res.goodsStatus;
+			return null;
+		}
+
+		/** 打开页时先拉取商品状态;query 参数名为 id */
+		function fetchGoodsStatusById(id) {
+			var sid = String(id != null ? id : '').trim();
+			if (!sid) return Promise.resolve(null);
+			var path =
+				'/secondGoods/getGoodsStatusById?id=' + encodeURIComponent(sid);
+			return fetch(API_BASE_SECOND + 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 fetchSecondGoodsDetail(goodsId, lon, lat, phoneId) {
+			var lonStr =
+				lon != null && String(lon).trim() !== ''
+					? String(lon).trim()
+					: String(DEFAULT_REQUEST_LONGITUDE);
+			var latStr =
+				lat != null && String(lat).trim() !== ''
+					? String(lat).trim()
+					: String(DEFAULT_REQUEST_LATITUDE);
+			var pid =
+				phoneId != null && String(phoneId).trim() !== ''
+					? String(phoneId).trim()
+					: '';
+			/** 表单字段:键为 string,值一律 string(x-www-form-urlencoded) */
+			var formBody = {
+				goodsId: String(goodsId != null ? goodsId : '').trim(),
+				longitude: String(lonStr),
+				latitude: String(latStr),
+				phoneId: pid
+			};
+			var form = new URLSearchParams();
+			form.set('goodsId', formBody.goodsId);
+			form.set('longitude', formBody.longitude);
+			form.set('latitude', formBody.latitude);
+			form.set('phoneId', formBody.phoneId);
+            
+			return fetch(API_BASE_SECOND + '/recommend/querySecondGoodsDetailWithOutJWT', {
+				method: 'POST',
+				mode: 'cors',
+				credentials: 'omit',
+				headers: {
+					Accept: 'application/json',
+					'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
+				},
+				body: form.toString()
+			}).then(function (res) {
+				if (!res.ok) throw new Error('HTTP ' + res.status);
+				return res.json();
+			}).then(function (res) {
+				var d = isApiOk(res) ? res.data || res.result : null;
+				if (d && typeof d === 'object') {
+					renderGoodsDetail(d);
+				} else if (res && res.msg) {
+					console.warn('[querySecondGoodsDetail]', res.msg);
+				}
+			});
+		}
+
+		function fetchCommentList(goodsId, userId, pageNum, pageSize) {
+			var st = (q('sourceType') || '').trim();
+			var sourceType = st && !isNaN(Number(st)) ? Number(st) : COMMENT_SOURCE_TYPE;
+			var path = '/commonComment/getListBySourceType?' +
+				'sourceType=' + encodeURIComponent(String(sourceType)) +
+				'&sourceId=' + encodeURIComponent(String(goodsId)) +
+				'&pageNum=' + encodeURIComponent(String(pageNum || 1)) +
+				'&pageSize=' + encodeURIComponent(String(pageSize || 10)) +
+				'&userId=' + encodeURIComponent(userId != null ? String(userId) : '');
+			return apiGet(path).then(function (res) {
+				if (!isApiOk(res)) {
+					if (res && res.msg) console.warn('[getListBySourceType]', res.msg);
+					renderCommentsList([], 0);
+					return;
+				}
+				var raw = res.data != null ? res.data : res.result;
+				var pack = normalizeCommentListPayload(raw);
+				renderCommentsList(pack.records, pack.total);
+			});
+		}
+
+		function run() {
+			var goodsId = (q('goodsId') || q('id') || '').trim();
+			var userId = (q('userId') || '').trim();
+			var phoneId = (q('phoneId') || q('phone_id') || '').trim();
+			var lon = (q('longitude') || q('lon') || q('jingdu') || '').trim();
+			var lat = (q('latitude') || q('lat') || q('weidu') || '').trim();
+			var pageNum = (q('pageNum') || '1').trim();
+			var pageSize = (q('pageSize') || '10').trim();
+
+			bindCommentThreadDelegation();
+
+			if (!goodsId) {
+				applyFromUrl();
+				return;
+			}
+
+			fetchGoodsStatusById(goodsId)
+				.catch(function (e) {
+					console.warn('[getGoodsStatusById]', e);
+					return null;
+				})
+				.then(function (statusRes) {
+					var gs = pickGoodsStatusFromStatusApi(statusRes);
+					var gsNum = gs != null ? Number(gs) : NaN;
+					var gsStr = gs != null ? String(gs).trim() : '';
+					var gsGotoUndefined =
+						gs != null &&
+						((!isNaN(gsNum) && (gsNum === 2 || gsNum === 4 || gsNum === 5)) ||
+							gsStr === '2' || gsStr === '4' || gsStr === '5');
+					if (gsGotoUndefined) {
+						var undefinedUrl = 'shareUndefined.html';
+						if (userId) {
+							undefinedUrl += '?userId=' + encodeURIComponent(userId);
+						}
+						window.location.replace(undefinedUrl);
+						return { skipRest: true };
+					}
+					return Promise.all([
+						fetchSecondGoodsDetail(goodsId, lon, lat, phoneId).catch(function (e) {
+							console.warn('[querySecondGoodsDetail]', e);
+						}),
+						fetchCommentList(goodsId, userId, pageNum, pageSize).catch(function (e) {
+							console.warn('[getListBySourceType]', e);
+							renderCommentsList([], 0);
+						})
+					]).then(function () {
+						return { skipRest: false };
+					});
+				})
+				.then(function (flag) {
+					if (flag && flag.skipRest) return;
+					applyFromUrl();
+				});
+		}
+
+		function applyFromUrl() {
+			var heroImg = document.getElementById('goodsHeroImg');
+			var imgUrl = q('image') || q('goodsImage') || q('coverUrl') || q('img');
+			imgUrl = tryDecode(imgUrl).trim();
+			if (imgUrl) {
+				heroImg.src = imgUrl;
+				heroImg.alt = '商品图';
+			} else {
+				heroImg.src = DEFAULT_HERO;
+				heroImg.alt = '';
+			}
+
+			var av = tryDecode(q('userImage') || q('avatar')).trim();
+			document.getElementById('goodsUserAvatar').src = av || 'images/demouser.png';
+
+			var uname = tryDecode(q('userName') || q('nickname')).trim();
+			if (uname) document.getElementById('goodsUserName').textContent = uname;
+
+			var ago = tryDecode(q('timeAgo') || q('publishTime')).trim();
+			if (ago) document.getElementById('goodsTimeAgo').textContent = ago;
+
+			var s1 = tryDecode(q('slogan1') || q('line1')).trim();
+			var s2 = tryDecode(q('slogan2') || q('line2')).trim();
+			var elS1 = document.getElementById('goodsSlogan1');
+			var elS2 = document.getElementById('goodsSlogan2');
+			var elWrap = document.getElementById('goodsHeroSloganWrap');
+			if (s1 && elS1) elS1.textContent = s1;
+			if (s2 && elS2) elS2.textContent = s2;
+			if (elWrap && (s1 || s2)) elWrap.style.display = '';
+
+			var price = tryDecode(q('price')).trim();
+			if (price) {
+				var pn = parseFloat(price.replace(/[^\d.]/g, ''));
+				document.getElementById('goodsPrice').textContent =
+					!isNaN(pn) ? pn.toFixed(2) : price;
+			}
+
+			var dist = tryDecode(q('distance') || q('distanceKm') || q('dist')).trim();
+			if (dist) {
+				if (!/^距离/.test(dist)) dist = '距离 ' + dist;
+				if (!/km$/i.test(dist) && !/千米/.test(dist) && !/米/.test(dist) && !/\s*m\s*$/i.test(dist)) {
+					dist = dist.replace(/\s+$/, '') + 'km';
+				}
+				document.getElementById('goodsDistance').textContent = dist;
+			}
+
+			var title = tryDecode(q('title') || q('goodsTitle')).trim();
+			if (title) document.getElementById('goodsTitle').textContent = title;
+
+			var hot = tryDecode(q('hotCount') || q('hot')).trim();
+			if (hot) document.getElementById('goodsHotCount').textContent = hot;
+
+			var fav = tryDecode(q('favCount') || q('fav') || q('collectCount')).trim();
+			if (fav) document.getElementById('goodsFavCount').textContent = fav;
+
+			var cc = tryDecode(q('commentCount') || q('comments')).trim();
+			if (cc) {
+				var elTot = document.getElementById('goodsCommentTotal');
+				if (elTot) elTot.textContent = cc;
+			}
+
+			var tagsRaw = tryDecode(q('tags') || q('tag')).trim();
+			var mount = document.getElementById('goodsTags');
+			var hasGoodsId = (q('goodsId') || q('id') || '').trim();
+			if (mount) {
+				if (tagsRaw) {
+					mount.innerHTML = '';
+					tagsRaw.split(/[,,]/).map(function (t) { return t.trim(); }).filter(Boolean).forEach(function (t) {
+						appendGoodsTag(mount, t, t.indexOf('#') === 0 ? 'topic' : 'label');
+					});
+				} else if (!hasGoodsId) {
+					mount.innerHTML = '';
+					['复古冰箱', '网红冰箱', '复古烤漆', '冰箱'].forEach(function (t) {
+						appendGoodsTag(mount, t, 'label');
+					});
+				}
+			}
+		}
+
+		function sharePage() {
+			var title = document.getElementById('goodsTitle').textContent || '商品分享';
+			var url = location.href;
+			if (navigator.share) {
+				navigator.share({ title: title, text: title, url: url }).catch(function () {});
+				return;
+			}
+			if (navigator.clipboard && navigator.clipboard.writeText) {
+				navigator.clipboard.writeText(url).then(function () {
+					window.alert('链接已复制');
+				}).catch(function () {
+					window.prompt('复制链接', url);
+				});
+			} else {
+				window.prompt('复制链接', url);
+			}
+		}
+
+		var shareBtn = document.getElementById('goodsShareBtn');
+		if (shareBtn) shareBtn.addEventListener('click', sharePage);
+
+		var openAppBtn = document.getElementById('openApp');
+		if (openAppBtn) {
+			openAppBtn.addEventListener('click', function () {
+				tryOpenHBuilderApp();
+			});
+		}
+
+		run();
+	})();
+	</script>
+</body>
+</html>

+ 995 - 0
HBuilderProjects/shareCheckIn.html

@@ -0,0 +1,995 @@
+<!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>打卡分享</title>
+	<style>
+		:root {
+			--orange: #F58220;
+			--text: #333333;
+			--text-secondary: #999999;
+			--text-muted: #666666;
+			--page-bg: #F5F6F7;
+			--card-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+			--card-border: 1px solid #EEEEEE;
+			--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.page-checkin {
+			font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
+			background: var(--page-bg);
+			color: var(--text);
+			padding-bottom: calc(100px + var(--safe-bottom));
+			min-height: 100vh;
+			overflow-x: hidden;
+		}
+
+		/* 用户 + 打卡信息(顶部) */
+		.checkin-head {
+			padding: 16px 15px 14px;
+		}
+
+		.checkin-head__row {
+			display: flex;
+			align-items: flex-start;
+			gap: 12px;
+		}
+
+		.checkin-head__avatar {
+			width: 48px;
+			height: 48px;
+			border-radius: 50%;
+			object-fit: cover;
+			background: #ddd;
+			flex-shrink: 0;
+		}
+
+		.checkin-head__main {
+			flex: 1;
+			min-width: 0;
+		}
+
+		.checkin-head__name {
+			font-size: 17px;
+			font-weight: 700;
+			line-height: 1.3;
+			margin-bottom: 8px;
+		}
+
+		.checkin-head__status {
+			font-size: 15px;
+			line-height: 1.55;
+			color: var(--text);
+			margin-bottom: 8px;
+		}
+
+		.checkin-head__accent {
+			color: var(--orange);
+			font-weight: 700;
+		}
+
+		.checkin-head__loc {
+			display: flex;
+			align-items: flex-start;
+			gap: 6px;
+			font-size: 14px;
+			color: var(--text-muted);
+			line-height: 1.45;
+		}
+
+		.checkin-head__loc svg {
+			flex-shrink: 0;
+			margin-top: 2px;
+			color: var(--text);
+		}
+
+		/* 主图轮播 */
+		.checkin-hero {
+			position: relative;
+			margin: 0 15px 14px;
+			border-radius: 12px;
+			overflow: hidden;
+			background: #e8e8e8;
+		}
+
+		.checkin-hero__track {
+			display: flex;
+			transition: transform 0.35s ease;
+		}
+
+		.checkin-hero__slide {
+			flex: 0 0 100%;
+			background: #e0e0e0;
+		}
+
+		.checkin-hero__slide img {
+			width: 100%;
+			height: 100%;
+			object-fit: cover;
+			display: block;
+		}
+
+		.checkin-hero__dots {
+			position: absolute;
+			bottom: 12px;
+			left: 0;
+			right: 0;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			gap: 6px;
+		}
+
+		.checkin-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;
+		}
+
+		.checkin-hero__dot.is-active {
+			width: 16px;
+			background: #fff;
+		}
+
+		/* 店铺卡片 */
+		.checkin-card {
+			margin: 0 15px 12px;
+			padding: 14px;
+			background: #fff;
+			border-radius: 12px;
+			border: var(--card-border);
+			box-shadow: var(--card-shadow);
+		}
+
+		.store-row {
+			display: flex;
+			gap: 12px;
+			align-items: flex-start;
+		}
+
+		.store-row__thumb {
+			width: 80px;
+			height: 80px;
+			border-radius: 8px;
+			object-fit: cover;
+			background: #eee;
+			flex-shrink: 0;
+		}
+
+		.store-row__main {
+			flex: 1;
+			min-width: 0;
+		}
+
+		.store-row__title {
+			font-size: 16px;
+			font-weight: 700;
+			color: var(--text);
+			margin-bottom: 6px;
+		}
+
+		.store-row__rating {
+			display: flex;
+			align-items: center;
+			flex-wrap: wrap;
+			gap: 4px 8px;
+			margin-bottom: 6px;
+		}
+
+		.store-row__rating .stars {
+			display: inline-flex;
+			align-items: center;
+			gap: 2px;
+		}
+
+		.store-row__rating .star-slot {
+			position: relative;
+			display: inline-block;
+			width: 12px;
+			height: 12px;
+			flex-shrink: 0;
+		}
+
+		.store-row__rating .star {
+			width: 12px;
+			height: 12px;
+			display: block;
+		}
+
+		.store-row__rating .star--base {
+			color: #e5e5e5;
+		}
+
+		.store-row__rating .star-fill-wrap {
+			position: absolute;
+			left: 0;
+			top: 0;
+			height: 100%;
+			overflow: hidden;
+			pointer-events: none;
+		}
+
+		.store-row__rating .star--fill {
+			color: var(--orange);
+		}
+
+		.store-row__rating .rating-num {
+			font-size: 13px;
+			font-weight: 600;
+			color: var(--orange);
+		}
+
+		.store-row__meta {
+			font-size: 12px;
+			color: var(--text-secondary);
+		}
+
+		.store-row__cat {
+			font-size: 12px;
+			color: var(--text-secondary);
+			margin-bottom: 4px;
+		}
+
+		.store-row__tagline {
+			font-size: 13px;
+			color: var(--text);
+			line-height: 1.45;
+		}
+
+		.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;
+			background: #F47D1F;
+		}
+
+		.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 class="page-checkin">
+
+	<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>
+				<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"/>
+						<circle cx="12" cy="10" r="3"/>
+					</svg>
+					<span id="checkInPoiLine">— · —星</span>
+				</div>
+			</div>
+		</div>
+	</header>
+
+	<!-- 配图轮播:仅用 URL 与 options.item 的 checkInImages;无图则用 images/map.png(不用 coverUrl / imagePath) -->
+	<div class="checkin-hero" id="dynHero">
+		<div class="checkin-hero__track" id="dynHeroTrack"></div>
+		<div class="checkin-hero__dots" id="dynHeroDots" aria-hidden="true"></div>
+	</div>
+
+	<div class="checkin-card">
+		<div class="store-row">
+			<img class="store-row__thumb" id="storeThumb" src="images/hero.png" alt="">
+			<div class="store-row__main">
+				<div class="store-row__title" id="storeTitle">Sober</div>
+				<div class="store-row__rating">
+					<span class="stars" id="storeStarsRow" aria-hidden="true"></span>
+					<span class="rating-num" id="storeScore">4.9</span>
+					<!-- <span class="store-row__meta"><span id="storeReviews">200</span>条</span> -->
+				</div>
+				<!-- <div class="store-row__cat" id="storeCat">live house</div>
+				<div class="store-row__tagline" id="storeTagline">24小时水果饮料无限畅享受!</div> -->
+			</div>
+		</div>
+	</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';
+
+		/**
+		 * 与 shareIndex / group_user manifest 一致:shopro、包名以云打包为准
+		 * 打卡落地:默认 pages/checkIn/index;URL 带 isShareCheckInSquare 且为真时 → pages/checkIn/details(合并 search + hash query)
+		 */
+		var APP_ANDROID_PACKAGE = 'com.alien.Udianzaizhe';
+		var APP_IOS_URL_SCHEME = 'shopro://';
+		var APP_UNI_STORE_PATH = 'pages/checkIn/index';
+
+		/** 与 shareUndefined.html 一致 */
+		var API_BASE = 'http://120.26.186.130:8000/alienStore';
+
+		function showDownloadTip() {
+			var msg = '请到应用商店下载';
+			if (typeof uni !== 'undefined' && typeof uni.showToast === 'function') {
+				uni.showToast({ title: msg, icon: 'none', duration: 2500 });
+			} else {
+				window.alert(msg);
+			}
+		}
+
+		/**
+		 * 唤起 App 时携带当前 H5 全部查询参数:先 location.search,再 hash 中 ? 后一段;
+		 * 逐项 append,避免丢参、并保留重名键顺序。
+		 */
+		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 sid = params.get('storeId') || params.get('id') || '';
+			if (sid && !params.has('storeId')) {
+				params.set('storeId', sid);
+			}
+			var qsOut = params.toString();
+			return qsOut ? ('?' + qsOut) : '';
+		}
+
+		/**
+		 * isShareCheckInSquare 为真(如 1、true)时打开详情页;缺省、空、0、false → 打卡首页
+		 */
+		function getCheckInAppUniPath() {
+			var p = mergeSearchAndHashParams();
+			if (!p.has('isShareCheckInSquare')) {
+				return 'pages/checkIn/index';
+			}
+			var v = String(p.get('isShareCheckInSquare') || '').trim().toLowerCase();
+			if (v === '' || v === '0' || v === 'false' || v === 'no') {
+				return 'pages/checkIn/index';
+			}
+			return 'pages/checkIn/details';
+		}
+
+		function buildAppDeepLink() {
+			var path = getCheckInAppUniPath().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) {}
+		}
+
+		function tryOpenHBuilderApp() {
+			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 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 getMergedQueryString() {
+			var chunks = [];
+			if (location.search && location.search.length > 1) {
+				chunks.push(location.search.slice(1));
+			}
+			var hash = location.hash || '';
+			var qi = hash.indexOf('?');
+			if (qi >= 0) {
+				chunks.push(hash.slice(qi + 1));
+			}
+			return chunks.join('&');
+		}
+
+		function forEachQueryParam(queryStr, fn) {
+			if (!queryStr) return;
+			var p = new URLSearchParams(queryStr);
+			p.forEach(function (val, key) {
+				fn(val, key);
+			});
+		}
+
+		function getAllParamValuesCI(queryStr, nameLower) {
+			var list = [];
+			forEachQueryParam(queryStr, function (val, key) {
+				if (key && String(key).toLowerCase() === nameLower) {
+					list.push(val);
+				}
+			});
+			return list;
+		}
+
+		/** checkInImages 无任何有效地址时,头图用该默认图 */
+		var CHECKIN_HERO_DEFAULT_MAP_SRC = 'images/map.png';
+		/** URL 参数 userImage 及 options.item.userImage 均无有效图时使用 */
+		var CHECKIN_USER_AVATAR_DEFAULT = 'images/demouser.png';
+
+		function getMergedParam(name) {
+			var m = getMergedQueryString();
+			if (!m) return '';
+			var v = new URLSearchParams(m).get(name);
+			return v == null ? '' : String(v);
+		}
+
+		/**
+		 * getDeleteFlagById:data 为数字 1(旧格式),或 data.businessStatus 为 99 时进「已删除」落地页。
+		 */
+		function shouldRedirectToShareCheckInUndefined(res) {
+			if (!res || typeof res !== 'object') return false;
+			var d = res.data;
+			if (d === 1 || d === '1' || Number(d) === 1) return true;
+			if (d && typeof d === 'object') {
+				var bs = d.businessStatus;
+				if (bs === 99 || bs === '99' || Number(bs) === 99) return true;
+			}
+			return false;
+		}
+
+		/** 当前页合并 query + 接口返回的 storeId、businessStatus(若有)供 shareCheckInUndefined 使用 */
+		function buildShareCheckInUndefinedHref(res) {
+			var params = new URLSearchParams(getMergedQueryString());
+			if (res && res.data && typeof res.data === 'object') {
+				if (res.data.storeId != null) {
+					var sid = String(res.data.storeId).trim();
+					if (sid) params.set('storeId', sid);
+				}
+				if (res.data.businessStatus != null && String(res.data.businessStatus).trim() !== '') {
+					params.set('businessStatus', String(res.data.businessStatus).trim());
+				}
+			}
+			var m = params.toString();
+			return 'shareCheckInUndefined.html' + (m ? ('?' + m) : '');
+		}
+
+		/** 进入页先拉删除标记;query 拼在 ? 后,如 getDeleteFlagById?id=545 */
+		function fetchGetDeleteFlagByIdIfId() {
+			var id =
+				getMergedParam('id').trim() ||
+				getMergedParam('storeId').trim() ||
+				q('id').trim() ||
+				q('storeId').trim();
+			if (!id) return Promise.resolve(null);
+			var qs = new URLSearchParams();
+			qs.set('id', id);
+			var path = '/storeClockIn/getDeleteFlagById?' + qs.toString();
+			return apiFetch(path)
+				.then(function (res) {
+					return res;
+				})
+				.catch(function (e) {
+					console.warn('[getDeleteFlagById]', e);
+					return null;
+				});
+		}
+
+		function tryDecode(s) {
+			if (!s) return '';
+			try {
+				return decodeURIComponent(s);
+			} catch (e) {
+				return s;
+			}
+		}
+
+		function pushUniqueUrl(list, u) {
+			u = String(u == null ? '' : u).trim();
+			if (!u) return;
+			if (list.indexOf(u) >= 0) return;
+			list.push(u);
+		}
+
+		/** 解析单个 query 值:JSON 数组或逗号分隔 URL,写入 list */
+		function pushUrlsFromRawParam(list, val) {
+			val = String(val == null ? '' : val).trim();
+			if (!val) return;
+			if (val.charAt(0) === '[') {
+				try {
+					var arr = JSON.parse(val);
+					if (Array.isArray(arr)) {
+						arr.forEach(function (u) {
+							pushUniqueUrl(list, u);
+						});
+						return;
+					}
+				} catch (e1) {
+					try {
+						var arr2 = JSON.parse(decodeURIComponent(val));
+						if (Array.isArray(arr2)) {
+							arr2.forEach(function (u) {
+								pushUniqueUrl(list, u);
+							});
+							return;
+						}
+					} catch (e2) {}
+				}
+			}
+			val.split(',').forEach(function (seg) {
+				seg = String(seg).trim();
+				if (!seg) return;
+				if (/%[0-9A-Fa-f]{2}/.test(seg)) {
+					try {
+						seg = decodeURIComponent(seg.replace(/\+/g, ' '));
+					} catch (e) {}
+				}
+				pushUniqueUrl(list, seg);
+			});
+		}
+
+		function parseOptionsItem() {
+			var raw = getMergedParam('options');
+			if (!raw) return null;
+			var opts;
+			try {
+				opts = JSON.parse(raw);
+			} catch (e) {
+				return null;
+			}
+			if (!opts || typeof opts !== 'object') return null;
+			var item = opts.item;
+			if (typeof item === 'string' && item) {
+				try {
+					item = JSON.parse(item);
+				} catch (e2) {
+					return null;
+				}
+			}
+			if (!item || typeof item !== 'object') return null;
+			return item;
+		}
+
+		function normalizeMediaUrl(raw) {
+			raw = String(raw == null ? '' : raw).trim();
+			if (!raw || raw === '0') return '';
+			if (/%[0-9A-Fa-f]{2}/.test(raw)) {
+				try {
+					raw = decodeURIComponent(raw.replace(/\+/g, ' '));
+				} catch (e) {}
+			}
+			return raw;
+		}
+
+		function resolveUserAvatarUrl() {
+			var top = normalizeMediaUrl(getMergedParam('userImage'));
+			if (top) return top;
+			var item = parseOptionsItem();
+			if (!item) return '';
+			var u = item.userImage;
+			if (u == null || u === '') return '';
+			if (typeof u === 'number') return '';
+			return normalizeMediaUrl(String(u));
+		}
+
+		/** 轮播仅使用 checkInImages:① URL 参数 ② options.item.checkInImages;无则用默认图 */
+		function collectImageUrlsFromUrl() {
+			var merged = getMergedQueryString();
+			var urls = [];
+
+			getAllParamValuesCI(merged, 'checkinimages').forEach(function (val) {
+				pushUrlsFromRawParam(urls, val);
+			});
+
+			var item = parseOptionsItem();
+			if (item && item.checkInImages != null && item.checkInImages !== '') {
+				if (Array.isArray(item.checkInImages)) {
+					item.checkInImages.forEach(function (u) {
+						pushUniqueUrl(urls, u);
+					});
+				} else {
+					pushUrlsFromRawParam(urls, String(item.checkInImages));
+				}
+			}
+
+			if (!urls.length) {
+				pushUniqueUrl(urls, CHECKIN_HERO_DEFAULT_MAP_SRC);
+			}
+			return urls;
+		}
+
+		var heroI = 0;
+		var heroTimer = null;
+		var dynHeroSwipeInited = false;
+
+		function getHeroSlideCount() {
+			return document.getElementById('dynHeroDots').querySelectorAll('.checkin-hero__dot').length;
+		}
+
+		function initDynCarousel(slides) {
+			var track = document.getElementById('dynHeroTrack');
+			var dotsWrap = document.getElementById('dynHeroDots');
+
+			function go(n) {
+				var count = getHeroSlideCount();
+				if (count < 1) return;
+				heroI = ((n % count) + count) % count;
+				track.style.transform = 'translateX(' + (-heroI * 100) + '%)';
+				dotsWrap.querySelectorAll('.checkin-hero__dot').forEach(function (el, idx) {
+					el.classList.toggle('is-active', idx === heroI);
+				});
+			}
+
+			if (heroTimer) {
+				clearInterval(heroTimer);
+				heroTimer = null;
+			}
+			if (slides < 2) {
+				heroI = 0;
+				track.style.transform = 'translateX(0)';
+				return;
+			}
+			heroTimer = setInterval(function () { go(heroI + 1); }, 4000);
+			if (!dynHeroSwipeInited) {
+				dynHeroSwipeInited = true;
+				var hero = document.getElementById('dynHero');
+				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 buildDynSlides(urls) {
+			heroI = 0;
+			var track = document.getElementById('dynHeroTrack');
+			var dotsWrap = document.getElementById('dynHeroDots');
+			track.innerHTML = '';
+			dotsWrap.innerHTML = '';
+			track.style.transform = 'translateX(0)';
+			var list = (urls || []).filter(Boolean);
+			if (!list.length) {
+				var slide = document.createElement('div');
+				slide.className = 'checkin-hero__slide';
+				var img = document.createElement('img');
+				img.src = CHECKIN_HERO_DEFAULT_MAP_SRC;
+				img.alt = '';
+				slide.appendChild(img);
+				track.appendChild(slide);
+				var dot = document.createElement('span');
+				dot.className = 'checkin-hero__dot is-active';
+				dotsWrap.appendChild(dot);
+				return initDynCarousel(1);
+			}
+			list.forEach(function (url, d) {
+				var s = document.createElement('div');
+				s.className = 'checkin-hero__slide';
+				var im = document.createElement('img');
+				im.src = url;
+				im.alt = '';
+				s.appendChild(im);
+				track.appendChild(s);
+				var dot = document.createElement('span');
+				dot.className = 'checkin-hero__dot' + (d === 0 ? ' is-active' : '');
+				dotsWrap.appendChild(dot);
+			});
+			initDynCarousel(list.length);
+		}
+
+		function formatStar(score) {
+			var n = Number(score);
+			if (isNaN(n)) return '—';
+			return (Math.round(n * 10) / 10).toString();
+		}
+
+		var STAR_PATH_D = 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z';
+
+		function createStarSvg(className) {
+			var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+			svg.setAttribute('class', 'star ' + (className || ''));
+			svg.setAttribute('viewBox', '0 0 24 24');
+			svg.setAttribute('fill', 'currentColor');
+			var p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+			p.setAttribute('d', STAR_PATH_D);
+			svg.appendChild(p);
+			return svg;
+		}
+
+		/** 按 0~5 分(与 App 店铺评分一致)绘制五星:灰底 + 橙色按小数裁剪 */
+		function applyStoreStarsRow(score) {
+			var wrap = document.getElementById('storeStarsRow');
+			if (!wrap) return;
+			var n = Number(score);
+			if (isNaN(n) || n < 0) n = 0;
+			if (n > 5) n = 5;
+			wrap.innerHTML = '';
+			for (var i = 0; i < 5; i++) {
+				var frac = Math.min(1, Math.max(0, n - i));
+				var slot = document.createElement('span');
+				slot.className = 'star-slot';
+				slot.appendChild(createStarSvg('star--base'));
+				if (frac > 0) {
+					var fw = document.createElement('span');
+					fw.className = 'star-fill-wrap';
+					fw.style.width = (frac * 100) + '%';
+					fw.appendChild(createStarSvg('star--fill'));
+					slot.appendChild(fw);
+				}
+				wrap.appendChild(slot);
+			}
+		}
+
+		function applyPage() {
+			var item = parseOptionsItem();
+
+			var uname = getMergedParam('userName') || getMergedParam('nickname') || q('userName');
+			if (uname) document.getElementById('userName').textContent = tryDecode(uname);
+			else if (item && item.userName) document.getElementById('userName').textContent = String(item.userName);
+
+			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);
+			}
+			if (idx === '') idx = '1';
+			document.getElementById('checkInPlaceIndex').textContent = tryDecode(idx);
+
+			var poiName = getMergedParam('poiName') || getMergedParam('locationName') || q('poiName') || q('locationName');
+			if (!poiName && item) {
+				poiName = item.poiName || item.addressName || item.storeName || '';
+				poiName = poiName ? String(poiName) : '';
+			}
+			if (!poiName) poiName = getMergedParam('storeName') || q('storeName');
+			poiName = poiName ? tryDecode(String(poiName)) : '';
+
+			var poiScore = getMergedParam('scoreAvg') || q('scoreAvg');
+			if (poiScore === '') poiScore = getMergedParam('poiScore') || getMergedParam('storeRating') || q('poiScore') || q('storeRating');
+			if (poiScore === '' && item && item.scoreAvg != null) poiScore = String(item.scoreAvg);
+			var starText = formatStar(poiScore);
+			document.getElementById('checkInPoiLine').textContent =
+				(poiName || '—') + ' · ' + starText + '星';
+
+			buildDynSlides(collectImageUrlsFromUrl());
+
+			var cardName = getMergedParam('cardStoreName') || q('cardStoreName');
+			if (!cardName && item && item.cardStoreName) cardName = String(item.cardStoreName);
+			if (!cardName && item && item.storeName) cardName = String(item.storeName);
+			if (!cardName) cardName = getMergedParam('storeName') || q('storeName') || 'Sober';
+			document.getElementById('storeTitle').textContent = tryDecode(cardName);
+
+			var sAvg = getMergedParam('scoreAvg') || q('scoreAvg');
+			if (sAvg === '') sAvg = getMergedParam('cardScore') || q('cardScore');
+			if (sAvg === '' && item && item.scoreAvg != null) sAvg = String(item.scoreAvg);
+			if (sAvg === '') sAvg = '4.9';
+			var sn = Number(sAvg);
+			document.getElementById('storeScore').textContent = !isNaN(sn) ? sn.toFixed(1) : sAvg;
+			applyStoreStarsRow(isNaN(sn) ? 0 : sn);
+
+			var rc = getMergedParam('ratingCount') || q('ratingCount');
+			if (rc === '' && item && item.ratingCount != null) rc = String(item.ratingCount);
+			if (rc === '' && item && item.commitCount != null) rc = String(item.commitCount);
+			if (rc === '') rc = '200';
+			var elReviews = document.getElementById('storeReviews');
+			if (elReviews) elReviews.textContent = rc;
+
+			var cat = getMergedParam('businessTypeName') || getMergedParam('category') || q('category') || q('storeCat');
+			if (!cat && item && item.businessTypeName) cat = String(item.businessTypeName);
+			if (!cat) cat = 'live house';
+			var elCat = document.getElementById('storeCat');
+			if (elCat) elCat.textContent = tryDecode(cat);
+
+			var tag = getMergedParam('tagline') || q('tagline');
+			if (!tag && item && item.promotionText) tag = String(item.promotionText);
+			if (!tag) tag = '24小时水果饮料无限畅享受!';
+			var elTag = document.getElementById('storeTagline');
+			if (elTag) elTag.textContent = tryDecode(tag);
+
+			var thumb = getMergedParam('coverUrl') || q('coverUrl');
+			if (!thumb && item && item.coverUrl != null && String(item.coverUrl).trim() !== '') {
+				thumb = String(item.coverUrl);
+			}
+			if (!thumb) thumb = getMergedParam('cardCover') || q('cardCover');
+			if (!thumb && item) {
+				if (item.entranceImage) thumb = String(item.entranceImage);
+				else if (Array.isArray(item.storeAlbumUrlList) && item.storeAlbumUrlList[0]) thumb = item.storeAlbumUrlList[0];
+				else if (Array.isArray(item.imageList) && item.imageList[0]) thumb = item.imageList[0];
+			}
+			if (thumb) document.getElementById('storeThumb').src = normalizeMediaUrl(thumb) || thumb;
+		}
+
+		function boot() {
+			fetchGetDeleteFlagByIdIfId().then(function (res) {
+				if (shouldRedirectToShareCheckInUndefined(res)) {
+					window.location.replace(buildShareCheckInUndefinedHref(res));
+					return;
+				}
+				applyPage();
+			});
+			var openBtn = document.getElementById('openApp');
+			if (openBtn) {
+				openBtn.addEventListener('click', function () {
+					tryOpenHBuilderApp();
+				});
+			}
+		}
+
+		if (document.readyState === 'complete') {
+			boot();
+		} else {
+			window.onload = boot;
+		}
+	})();
+	</script>
+</body>
+</html>

+ 672 - 0
HBuilderProjects/shareCheckInUndefined.html

@@ -0,0 +1,672 @@
+<!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>打卡分享</title>
+	<style>
+		:root {
+			--orange: #F58220;
+			--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;
+		}
+
+		.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);
+		}
+
+		.hero--empty__illustration {
+			display: block;
+			width: 100%;
+			max-width: 280px;
+			height: auto;
+			object-fit: contain;
+		}
+
+		.hero--empty__tip {
+			padding: 0 16px;
+			margin-top: 12px;
+			font-size: 15px;
+			line-height: 1.5;
+			color: var(--text-secondary);
+			text-align: center;
+			font-weight: 400;
+		}
+
+		.divider {
+			height: 8px;
+			background: #F7F7F7;
+			margin: 0;
+		}
+
+		.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: 10px;
+			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;
+			display: block;
+			border-radius: 0;
+		}
+
+		.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 6px;
+		}
+
+		.rec-card__rating .stars {
+			display: inline-flex;
+			align-items: center;
+			gap: 2px;
+		}
+
+		.rec-card__rating .rec-star {
+			display: block;
+			flex-shrink: 0;
+		}
+
+		.rec-card__rating .rating-num {
+			font-size: 12px;
+			font-weight: 600;
+			color: var(--orange);
+		}
+
+		.rec-meta {
+			font-size: 12px;
+			color: var(--text-secondary);
+		}
+
+		.rec-card__footer {
+			margin-top: 8px;
+			font-size: 12px;
+			color: var(--text-secondary);
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		.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 img {
+			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" role="status" aria-live="polite">
+		<img class="hero--empty__illustration" id="heroEmptyIllustration" src="images/empty.png" alt="" decoding="async">
+		<p class="hero--empty__tip" id="heroEmptyTip">内容已删除</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 …/dev-life-manager-ai/ai/multimodal-services/api/v1/search/global/store-recommend
+		 * 请求体:page、pageSize、storeId(字符串)、userCity、userLat、userLng;均可由 URL query 覆盖。
+		 */
+		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 DEFAULT_USER_LAT = 38.925747;
+		var DEFAULT_USER_LNG = 121.662531;
+		var DEFAULT_USER_CITY = '大连市';
+		var DEFAULT_STORE_ID = '378';
+
+		var APP_ANDROID_PACKAGE = 'com.alien.Udianzaizhe';
+		var APP_IOS_URL_SCHEME = 'shopro://';
+		var APP_UNI_STORE_PATH = 'pages/checkIn/index';
+
+		function qs() {
+			return new URLSearchParams(location.search || '');
+		}
+		function q(name) {
+			var v = qs().get(name);
+			return v == null ? '' : String(v);
+		}
+
+		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;
+		}
+
+		/** businessStatus=99(商户关闭):插图与文案;否则为「内容已删除」 */
+		function applyEmptyHeroState() {
+			var merged = mergeSearchAndHashParams();
+			var bsRaw = merged.get('businessStatus');
+			if (bsRaw == null || bsRaw === '') bsRaw = q('businessStatus');
+			var bs = bsRaw == null ? '' : String(bsRaw).trim();
+			var isClosed = Number(bs) === 99 || bs === '99';
+			var img = document.getElementById('heroEmptyIllustration');
+			var tip = document.getElementById('heroEmptyTip');
+			if (!img || !tip) return;
+			if (isClosed) {
+				img.src = 'images/storeNone.png';
+				img.alt = '';
+				tip.textContent = '抱歉商户已关闭,看看别的吧';
+			} else {
+				img.src = 'images/empty.png';
+				img.alt = '';
+				tip.textContent = '内容已删除';
+			}
+			img.onerror = function () {
+				this.onerror = null;
+				this.src = 'images/empty.png';
+			};
+		}
+
+		function buildAppOpenQueryStringMerged() {
+			var params = mergeSearchAndHashParams();
+			var sid = params.get('storeId') || params.get('id') || '';
+			if (sid && !params.has('storeId')) {
+				params.set('storeId', sid);
+			}
+			return params.toString() ? ('?' + params.toString()) : '';
+		}
+
+		function buildAppDeepLink() {
+			var path = String(APP_UNI_STORE_PATH || 'pages/checkIn/index').replace(/^\//, '');
+			var s = buildAppOpenQueryStringMerged();
+			var root = APP_IOS_URL_SCHEME.replace(/\/$/, '');
+			if (!s) {
+				return root + '/' + path;
+			}
+			return root + '/' + path + s;
+		}
+
+		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) {}
+		}
+
+		function tryOpenHBuilderApp() {
+			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;
+				}
+				if (installed === true) {
+					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);
+
+			launchAppDeepLink(deepLink);
+
+			window.setTimeout(function () {
+				if (done) return;
+				if (document.visibilityState === 'visible' && Date.now() - t0 < 3500) {
+					showDownloadTip();
+				}
+				finish();
+			}, 2600);
+		}
+
+		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 fetchStoreGlobalRecommend() {
+			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_USER_LAT;
+			var userLng =
+				lngRaw !== '' && !isNaN(Number(lngRaw)) ? Number(lngRaw) : DEFAULT_USER_LNG;
+
+			var page = parseInt(q('page') || '1', 10);
+			var pageSize = parseInt(q('pageSize') || '10', 10);
+			if (isNaN(page) || page < 1) page = 1;
+			if (isNaN(pageSize) || pageSize < 1) pageSize = 10;
+
+			var storeIdRaw = (q('storeId') || q('id') || '').trim();
+			var storeId = storeIdRaw !== '' ? storeIdRaw : DEFAULT_STORE_ID;
+
+			var userCityRaw = (q('userCity') || q('city') || '').trim();
+			var userCity = userCityRaw !== '' ? userCityRaw : DEFAULT_USER_CITY;
+
+			var body = {
+				page: page,
+				pageSize: pageSize,
+				storeId: String(storeId),
+				userCity: userCity,
+				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();
+			});
+		}
+
+		function normalizeStoreRecommendList(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 formatRecDistance(item) {
+			/** 后端 distance 为千米,展示为米 */
+			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 escHtml(s) {
+			return String(s == null ? '' : s)
+				.replace(/&/g, '&amp;')
+				.replace(/</g, '&lt;')
+				.replace(/>/g, '&gt;')
+				.replace(/"/g, '&quot;');
+		}
+
+		var 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 starsHtml(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 = [];
+			var i;
+			for (i = 1; i <= 5; i++) {
+				var fill = i <= rounded ? '#F58220' : '#E5E5E5';
+				parts.push(
+					'<svg class="rec-star" width="11" height="11" viewBox="0 0 24 24" aria-hidden="true">' +
+					'<path fill="' + fill + '" d="' + STAR_PATH + '"/></svg>'
+				);
+			}
+			return '<span class="stars">' + parts.join('') + '</span>';
+		}
+
+		function pickScore(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 pickReviewCount(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 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 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 = formatRecDistance(item);
+
+				var scoreVal = pickScore(item);
+				var displayScore = scoreVal != null ? scoreVal.toFixed(1) : '—';
+				var starScore = scoreVal != null ? scoreVal : 0;
+
+				var rc = pickReviewCount(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 = 'rec-card';
+				card.innerHTML =
+					'<div class="rec-card__img"><img class="rec-card__cover" src="" alt="" decoding="async"></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">' +
+					starsHtml(starScore) +
+					'<span class="rating-num">' + escHtml(displayScore) + '</span>' +
+					(reviewLabel ? '<span class="rec-meta">' + escHtml(reviewLabel) + '</span>' : '') +
+					'</div>' +
+					(seller ? '<div class="rec-card__footer">' + escHtml(seller) + '</div>' : '') +
+					'</div>';
+
+				var coverIm = card.querySelector('img.rec-card__cover');
+				if (coverIm) {
+					coverIm.src = imgUrl;
+					coverIm.onerror = function () {
+						this.onerror = null;
+						this.src = '';
+					};
+				}
+				wrap.appendChild(card);
+			});
+		}
+
+		function run() {
+			fetchStoreGlobalRecommend()
+				.then(function (res) {
+					var list = normalizeStoreRecommendList(res);
+					if (!list.length && res && res.msg) {
+						console.warn('[store-recommend]', res.msg);
+					}
+					renderRecommended(list);
+				})
+				.catch(function (e) {
+					console.error(e);
+					renderRecommended([]);
+				});
+		}
+
+		function boot() {
+			applyEmptyHeroState();
+			run();
+			var openBtn = document.getElementById('openApp');
+			if (openBtn) {
+				openBtn.addEventListener('click', function () {
+					tryOpenHBuilderApp();
+				});
+			}
+		}
+
+		if (document.readyState === 'complete') {
+			boot();
+		} else {
+			window.onload = boot;
+		}
+	})();
+	</script>
+</body>
+</html>

+ 1638 - 0
HBuilderProjects/shareDynamic.html

@@ -0,0 +1,1638 @@
+<!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>动态详情</title>
+	<style>
+		:root {
+			--orange: #F58220;
+			--text: #333333;
+			--text-secondary: #999999;
+			--text-muted: #666666;
+			--border: #EEEEEE;
+			--page-bg: #F5F6F7;
+			--card-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+			--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.page-dynamic {
+			font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
+			background: #fff;
+			color: var(--text);
+			padding-bottom: calc(100px + var(--safe-bottom));
+			min-height: 100vh;
+			overflow-x: hidden;
+		}
+
+		/* 顶部轮播 */
+		.dyn-hero {
+			position: relative;
+			margin: 12px 15px 0;
+			border-radius: 12px 12px 0 0;
+			overflow: hidden;
+			background: #e8e8e8;
+		}
+
+		.dyn-hero__track {
+			display: flex;
+			transition: transform 0.35s ease;
+		}
+
+		.dyn-hero__slide {
+			flex: 0 0 100%;
+			aspect-ratio: 16 / 10;
+			background: #e0e0e0;
+		}
+
+		.dyn-hero__slide img,
+		.dyn-hero__slide video {
+			width: 100%;
+			height: 100%;
+			object-fit: cover;
+			display: block;
+		}
+
+		/* 仅展示首帧:禁止点击触发播放;叠层 poster 时视频背景透明,避免 iOS 黑底盖住图 */
+		.dyn-hero__slide .dyn-hero__media-wrap {
+			position: relative;
+			width: 100%;
+			height: 100%;
+			overflow: hidden;
+			background: #e0e0e0;
+		}
+
+		/* 封面叠在 video 之上:微信/iOS 里 video 常呈灰底,否则会盖住已加载的 poster/img */
+		.dyn-hero__slide .dyn-hero__poster-swap {
+			position: absolute;
+			left: 0;
+			top: 0;
+			width: 100%;
+			height: 100%;
+			object-fit: cover;
+			z-index: 2;
+			transition: opacity 0.2s ease;
+		}
+
+		.dyn-hero__slide video.dyn-hero__video--poster-only {
+			position: relative;
+			z-index: 1;
+			pointer-events: none;
+			background: transparent;
+		}
+
+		.dyn-hero--no-dots .dyn-hero__dots {
+			display: none;
+		}
+
+		.dyn-hero__dots {
+			position: absolute;
+			bottom: 12px;
+			left: 0;
+			right: 0;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			gap: 6px;display: none;
+		}
+
+		.dyn-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;
+		}
+
+		.dyn-hero__dot.is-active {
+			width: 16px;
+			background: #fff;
+		}
+
+		/* 用户信息 */
+		.dyn-user {
+			padding: 16px 15px 12px;
+			background:#fff;
+		}
+
+		.dyn-user__row {
+			display: flex;
+			align-items: flex-start;
+			gap: 12px;
+		}
+
+		.dyn-user__avatar {
+			width: 48px;
+			height: 48px;
+			border-radius: 50%;
+			object-fit: cover;
+			background: #ddd;
+			flex-shrink: 0;
+		}
+
+		.dyn-user__name {
+			font-size: 17px;
+			font-weight: 700;
+			color: var(--text);
+			line-height: 1.3;
+			padding-top: 2px;
+		}
+
+		.dyn-user__desc {
+			margin-top: 10px;
+			font-size: 15px;
+			line-height: 1.6;
+			color: var(--text-muted);
+		}
+
+		/* 卡片通用 */
+		.dyn-card {
+			margin: 0 15px 12px;
+			padding: 14px;
+			background: #fff;
+			border-radius: 12px;
+			box-shadow: var(--card-shadow);
+		}
+
+		/* 店铺卡片 */
+		.store-row {
+			display: flex;
+			gap: 12px;
+			align-items: flex-start;
+		}
+
+		.store-row__thumb {
+			width: 80px;
+			height: 80px;
+			border-radius: 8px;
+			object-fit: cover;
+			background: #eee;
+			flex-shrink: 0;
+		}
+
+		.store-row__main {
+			flex: 1;
+			min-width: 0;
+		}
+
+		.store-row__title {
+			font-size: 16px;
+			font-weight: 700;
+			color: var(--text);
+			margin-bottom: 6px;
+		}
+
+		.store-row__rating {
+			display: flex;
+			align-items: center;
+			flex-wrap: wrap;
+			gap: 4px 8px;
+			margin-bottom: 6px;
+		}
+
+		.store-row__rating .stars {
+			display: inline-flex;
+			align-items: center;
+			gap: 2px;
+			color: var(--orange);
+		}
+
+		.store-row__rating .star {
+			width: 12px;
+			height: 12px;
+			color: var(--orange);
+		}
+
+		.store-row__rating .rating-num {
+			font-size: 13px;
+			font-weight: 600;
+			color: var(--orange);
+		}
+
+		.store-row__meta {
+			font-size: 12px;
+			color: var(--text-secondary);
+		}
+
+		.store-row__cat {
+			font-size: 12px;
+			color: var(--text-secondary);
+			margin-bottom: 4px;
+		}
+
+		.store-row__tagline {
+			font-size: 13px;
+			color: var(--text);
+			line-height: 1.45;
+		}
+
+		/* 评价 */
+		.comment-card__title {
+			font-size: 15px;
+			font-weight: 700;
+			color: var(--text);
+			margin-bottom: 20px;
+		}
+
+		.comment-empty {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			padding: 28px 16px 20px;
+			color: #aaa;
+			font-size: 14px;
+		}
+
+		.comment-empty__icon {
+			width: 56px;
+			height: 56px;
+			margin-bottom: 12px;
+			object-fit: contain;
+			display: block;
+		}
+
+		.comment-list {
+			display: none;
+		}
+
+		.comment-list.is-visible {
+			display: block;
+		}
+
+		.comment-item {
+			display: flex;
+			align-items: flex-start;
+			gap: 10px;
+			padding: 12px 0;
+			border-top: 1px solid var(--border);
+		}
+
+		.comment-item:first-child {
+			border-top: none;
+			padding-top: 0;
+		}
+
+		.comment-item__avatar {
+			width: 40px;
+			height: 40px;
+			border-radius: 50%;
+			object-fit: cover;
+			flex-shrink: 0;
+			background: #eee;
+		}
+
+		.comment-item__col {
+			flex: 1;
+			min-width: 0;
+		}
+
+		.comment-item__meta {
+			font-size: 13px;
+			color: var(--text-secondary);
+			margin-bottom: 6px;
+		}
+
+		.comment-item__name {
+			font-weight: 600;
+			color: var(--text);
+		}
+
+		.comment-item__text {
+			font-size: 14px;
+			line-height: 1.55;
+			color: var(--text);
+			word-break: break-word;
+			margin-bottom: 4px;
+		}
+
+		.comment-item__time {
+			display: block;
+			font-size: 12px;
+			font-weight: 400;
+			color: var(--text-secondary);
+			margin-top: 2px;
+		}
+
+		/* 底部按钮 */
+		.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;
+			background: #F47D1F;
+		}
+
+		.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 class="page-dynamic">
+	<!-- 顶部配图:含 .mp4 时只展示首帧静图(不播放视频)、不轮播;纯图片可多图轮播。 -->
+
+	<div class="dyn-hero" id="dynHero">
+		<div class="dyn-hero__track" id="dynHeroTrack"></div>
+		<div class="dyn-hero__dots" id="dynHeroDots" aria-hidden="true"></div>
+	</div>
+
+	<section class="dyn-user">
+		<div class="dyn-user__row">
+			<img class="dyn-user__avatar" id="userAvatar" src="images/hero.png" alt="">
+			<div>
+				<div class="dyn-user__name" id="userName">用户名称</div>
+                <p class="dyn-user__desc" id="userDesc">文案描述文案描述文案描述文案描述文案描述文案描述文案描述文案描述文案描述文案描述文案描述</p>
+			</div>
+		</div>
+		
+	</section>
+
+	<div class="dyn-card store-card">
+		<div class="store-row">
+			<img class="store-row__thumb" id="storeThumb" src="images/dynamic.png" alt="">
+			<div class="store-row__main">
+				<div class="store-row__title" id="storeTitle">Sober</div>
+				<div class="store-row__rating">
+					<span class="stars" aria-hidden="true">
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+					</span>
+					<span class="rating-num" id="storeScore">4.9</span>
+					<span class="store-row__meta"><span id="storeReviews">200</span>条</span>
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<div class="dyn-card comment-card">
+		<div class="comment-card__title">评价 <span id="commentCount" style="color:#aaa">(0)</span></div>
+		<div id="commentList" class="comment-list" aria-live="polite"></div>
+		<div class="comment-empty" id="commentEmpty">
+			<img class="comment-empty__icon" src="images/zwpl.png" alt="" width="56" height="56" decoding="async">
+			<span>暂无评论</span>
+		</div>
+	</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';
+
+		var API_BASE = 'https://test.ailien.shop/alienStore';
+		var COMMENT_PAGE_NUM = 1;
+		var COMMENT_PAGE_SIZE = 20;
+		var COMMENT_AVATAR_FALLBACK = 'images/demouser.png';
+
+		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();
+			});
+		}
+
+		/**
+		 * 与 shareIndex / group_user manifest 一致
+		 * 动态详情默认落地:pages/newdetails/index(onLoad 使用 item 等,与当前页合并 query 一致)
+		 * 打开其它 App 页:H5 URL 上带 appPath 或 appPage,如 ?appPath=pages%2FcheckIn%2Findex(不要前导 /)
+		 */
+		var APP_ANDROID_PACKAGE = 'com.alien.Udianzaizhe';
+		var APP_IOS_URL_SCHEME = 'shopro://';
+		var APP_UNI_STORE_PATH = 'pages/newdetails/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);
+			}
+		}
+
+		/**
+		 * 唤起 App 时携带当前 H5 全部查询参数:先 location.search,再 hash 中 ? 后一段;
+		 * 逐项 append,避免丢参、并保留重名键顺序(与 getMergedQueryString 合并规则一致)。
+		 */
+		function mergeSearchAndHashParams() {
+			var params = new URLSearchParams();
+			function ingestAppend(querySlice) {
+				if (!querySlice) return;
+				var p = new URLSearchParams(querySlice);
+				p.forEach(function (val, key) {
+					params.append(key, val);
+				});
+			}
+			function ingestSet(querySlice) {
+				if (!querySlice) return;
+				var p = new URLSearchParams(querySlice);
+				p.forEach(function (val, key) {
+					params.set(key, val);
+				});
+			}
+			if (location.search && location.search.length > 1) {
+				ingestAppend(location.search.slice(1));
+			}
+			var hash = location.hash || '';
+			var qi = hash.indexOf('?');
+			if (qi >= 0) {
+				/** hash 内 query 覆盖 search(如 localhost:5173/#/pages/...?item= 场景) */
+				ingestSet(hash.slice(qi + 1));
+			}
+			return params;
+		}
+
+		/**
+		 * hash 形如 #/pages/newdetails/index?item=...&needShowMore=... 时取出 Uni 页面路径(与 App 内 route 一致)
+		 */
+		function extractUniPagePathFromHash() {
+			var h = location.hash || '';
+			if (h.length < 3 || h.charAt(0) !== '#' || h.charAt(1) !== '/') return '';
+			var rest = h.slice(2).trim();
+			if (!rest) return '';
+			var qpos = rest.indexOf('?');
+			var pathPart = (qpos >= 0 ? rest.slice(0, qpos) : rest).trim();
+			if (!pathPart || pathPart.indexOf('pages/') !== 0) return '';
+			return pathPart.replace(/^\/+/, '');
+		}
+
+		function buildAppOpenQueryStringMerged() {
+			var params = mergeSearchAndHashParams();
+
+			/** newdetails onLoad:仅当地址栏未带 item 时补全;同时按需补顶层 imagePath */
+			if (!params.has('item')) {
+				var itemObj = parseOptionsItem();
+				var imagePath = getMergedParam('imagePath');
+				if (!imagePath && itemObj && itemObj.imagePath != null && String(itemObj.imagePath).trim() !== '') {
+					imagePath = String(itemObj.imagePath);
+				}
+				if (!imagePath) {
+					var carouselUrls = collectImageUrlsFromUrl();
+					if (carouselUrls && carouselUrls.length) {
+						imagePath = carouselUrls.join(',');
+					}
+				} else {
+					imagePath = normalizeMediaUrl(imagePath) || imagePath;
+				}
+				var dynId =
+					getMergedParam('sourceId') ||
+					getMergedParam('dynamicId') ||
+					getMergedParam('id') ||
+					q('id');
+				if (!dynId && itemObj && itemObj.id != null && String(itemObj.id).trim() !== '') {
+					dynId = String(itemObj.id);
+				}
+				var base = {};
+				if (itemObj) {
+					try {
+						base = JSON.parse(JSON.stringify(itemObj));
+					} catch (eCopy) {
+						base = {};
+					}
+				}
+				if (imagePath) {
+					base.imagePath = imagePath;
+				}
+				if (dynId != null && String(dynId).trim() !== '' && base.id == null) {
+					base.id = dynId;
+				}
+				if (Object.keys(base).length) {
+					try {
+						params.set('item', JSON.stringify(base));
+					} catch (eItem) {}
+				}
+			}
+
+			if (!params.has('imagePath')) {
+				var ipTop = getMergedParam('imagePath');
+				if (!ipTop) {
+					var it2 = parseOptionsItem();
+					if (it2 && it2.imagePath != null && String(it2.imagePath).trim() !== '') {
+						ipTop = String(it2.imagePath);
+					}
+				}
+				if (!ipTop) {
+					var cu = collectImageUrlsFromUrl();
+					if (cu && cu.length) {
+						ipTop = cu.join(',');
+					}
+				}
+				if (ipTop) {
+					params.set('imagePath', normalizeMediaUrl(ipTop) || ipTop);
+				}
+			}
+
+			var sid = params.get('storeId') || params.get('id') || '';
+			if (sid && !params.has('storeId')) {
+				params.set('storeId', sid);
+			}
+			/** 顶层 imagePath 若未 encode,URLSearchParams 会截断在 OSS 的 &fm= 等处;用原始串解析结果覆盖 */
+			var ipMerged = getMergedParam('imagePath');
+			if (ipMerged) {
+				params.set('imagePath', normalizeMediaUrl(ipMerged) || ipMerged);
+			}
+			var qsOut = params.toString();
+			return qsOut ? ('?' + qsOut) : '';
+		}
+
+		/**
+		 * 真机系统浏览器对超长 shopro:// 常截断;去掉超大 item,用 id/type 让 App 内再拉详情。
+		 */
+		function compactShoproDeepLinkIfTooLong(fullUrl, maxLen) {
+			maxLen = maxLen || 7200;
+			if (!fullUrl || fullUrl.length <= maxLen) return fullUrl;
+			var scheme = 'shopro://';
+			if (fullUrl.indexOf(scheme) !== 0) return fullUrl;
+			var after = fullUrl.slice(scheme.length).replace(/^\/+/, '');
+			var qI = after.indexOf('?');
+			var pathPart = qI >= 0 ? after.slice(0, qI) : after;
+			var queryPart = qI >= 0 ? after.slice(qI + 1) : '';
+			if (!queryPart) return fullUrl;
+			var sp = new URLSearchParams(queryPart);
+			var item = sp.get('item');
+			if (!item || item.length < 800) return fullUrl;
+			var idm = /"id"\s*:\s*(\d+)/.exec(item);
+			if (idm) {
+				if (!sp.get('dynamicId')) sp.set('dynamicId', idm[1]);
+				if (!sp.get('sourceId')) sp.set('sourceId', idm[1]);
+			}
+			var tpm = /"type"\s*:\s*"(\d+)"/.exec(item) || /"type"\s*:\s*(\d+)/.exec(item);
+			if (tpm && !sp.get('sourceType')) sp.set('sourceType', tpm[1]);
+			sp.delete('item');
+			var qs = sp.toString();
+			var out = scheme + pathPart + (qs ? ('?' + qs) : '');
+			return out.length <= maxLen ? out : fullUrl;
+		}
+
+		function buildAppDeepLink() {
+			var explicit = (getMergedParam('appPath') || getMergedParam('appPage') || '').trim().replace(/^\//, '');
+			var fromHash = extractUniPagePathFromHash();
+			var defaultPath = String(APP_UNI_STORE_PATH || '/pages/newdetails/index').replace(/^\//, '');
+			/**
+			 * 不要用「任意 pages/ 的 hash」当 App 路径:分享页常在 #/pages/shareDynamic?…,
+			 * 会误唤起成该页而不是 newdetails。仅当 hash 明确含 newdetails 时才采用 hash 路径。
+			 */
+			var path = explicit;
+			if (!path && fromHash && /newdetails/i.test(fromHash)) {
+				path = fromHash.replace(/^\//, '');
+			}
+			if (!path) {
+				path = defaultPath;
+			}
+			path = String(path || defaultPath).replace(/^\//, '');
+			var s = buildAppOpenQueryStringMerged();
+			var root = APP_IOS_URL_SCHEME.replace(/\/$/, '');
+			var raw = !s ? root + '/' + path : root + '/' + path + s;
+			return compactShoproDeepLinkIfTooLong(raw, 7200);
+		}
+
+		function isWeChatInAppBrowser() {
+			return /MicroMessenger/i.test(navigator.userAgent || '');
+		}
+
+		/** 微信内置浏览器、iOS:OSS 截图/封面易因 Referer 被拒;video 首帧不可靠 */
+		function preferStaticStillOverVideo() {
+			return isIOSVideoEnv() || isWeChatInAppBrowser();
+		}
+
+		/** 减轻微信/X5 对阿里云 OSS 图片、video poster 的 Referer 拦截 */
+		function applyMediaNoReferrer(el) {
+			if (!el) return;
+			try {
+				el.setAttribute('referrerpolicy', 'no-referrer');
+				if ('referrerPolicy' in el) {
+					el.referrerPolicy = 'no-referrer';
+				}
+			} catch (e) {}
+		}
+
+		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) {}
+		}
+
+		function tryOpenHBuilderApp() {
+			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);
+				}
+				/**
+				 * 不在「未安装」时直接弹下载:部分 ROM 对 isApplicationExist 误判为 false,
+				 * 仍应尝试 openURL,避免一点按钮就「请到应用商店下载」。
+				 */
+				try {
+					plus.runtime.openURL(deepLink);
+				} catch (e2) {
+					console.warn(e2);
+					if (installed === false) {
+						showDownloadTip();
+					}
+				}
+				return;
+			}
+
+			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;
+			}
+
+			/**
+			 * 不在超时后弹「去应用商店」:App 已打开时页面常仍 visible,易误报;
+			 * 若未安装,用户无反应可自行去商店,避免打断操作。
+			 */
+			window.setTimeout(function () {
+				finish();
+			}, 3200);
+		}
+
+		function qs() {
+			return new URLSearchParams(location.search || '');
+		}
+		function q(name) {
+			var v = qs().get(name);
+			return v == null ? '' : String(v);
+		}
+
+		/** 与 mergeSearchAndHashParams 一致:hash 内 query 覆盖 search,供 getMergedParam 与 App 唤起共用 */
+		function getMergedQueryString() {
+			var m = mergeSearchAndHashParams().toString();
+			return m || '';
+		}
+
+		function forEachQueryParam(queryStr, fn) {
+			if (!queryStr) return;
+			var p = new URLSearchParams(queryStr);
+			p.forEach(function (val, key) {
+				fn(val, key);
+			});
+		}
+
+		/**
+		 * App 分享链接里 URL 类参数常未 encode,值里的 &(如 OSS 的 &fm=)会被拆成多个 query;
+		 * 从合并后的原始 query 串中按「下一个已知分享参数名」截取整段值。
+		 */
+		var SHARE_QUERY_URL_PARAM_KEYS = [
+			'options',
+			'item',
+			'sourceType',
+			'businessType',
+			'sourceId',
+			'dynamicId',
+			'id',
+			'userId',
+			'userImage',
+			'coverUrl',
+			'imagePath',
+			'imagePaths',
+			'images',
+			'image_path',
+			'storeName',
+			'scoreAvg',
+			'commentCount',
+			'shareUserName',
+			'storeId',
+			'content',
+			'desc',
+			'text',
+			'tagline',
+			'category',
+			'storeCat',
+			'userName',
+			'phoneId',
+			'longitude',
+			'latitude',
+			'goodsId',
+			'pageNum',
+			'pageSize',
+			'needShowMore',
+			'fromHomeFeed',
+			'appPath',
+			'appPage'
+		];
+
+		function findShareQueryValueStart(q, paramName) {
+			var nlow = String(paramName || '').toLowerCase() + '=';
+			var low = String(q || '').toLowerCase();
+			var i = low.indexOf('&' + nlow);
+			if (i >= 0) return i + 1 + nlow.length;
+			i = low.indexOf('?' + nlow);
+			if (i >= 0) return i + 1 + nlow.length;
+			if (low.indexOf(nlow) === 0) return nlow.length;
+			return -1;
+		}
+
+		function extractUrlLikeShareQueryParam(fullQuery, paramName) {
+			var nm = String(paramName || '').trim();
+			if (!nm || !fullQuery) return '';
+			var start = findShareQueryValueStart(fullQuery, nm);
+			if (start < 0) return '';
+			var rest = fullQuery.slice(start);
+			var nml = nm.toLowerCase();
+			var keys = SHARE_QUERY_URL_PARAM_KEYS.filter(function (k) {
+				return String(k).toLowerCase() !== nml;
+			});
+			keys.sort(function (a, b) {
+				return b.length - a.length;
+			});
+			if (!keys.length) {
+				try {
+					return decodeURIComponent(rest.replace(/\+/g, ' '));
+				} catch (e) {
+					return rest;
+				}
+			}
+			var esc = keys.map(function (k) {
+				return String(k).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+			});
+			var re = new RegExp('&(' + esc.join('|') + ')=', 'i');
+			var m = re.exec(rest);
+			var end = m ? m.index : rest.length;
+			var rawVal = rest.slice(0, end).replace(/\+/g, ' ');
+			try {
+				return decodeURIComponent(rawVal);
+			} catch (e2) {
+				return rawVal;
+			}
+		}
+
+		function getAllParamValuesCI(queryStr, nameLower) {
+			if (!queryStr) return [];
+			if (nameLower === 'imagepath' || nameLower === 'imagepaths') {
+				var one =
+					extractUrlLikeShareQueryParam(queryStr, 'imagePath') ||
+					(nameLower === 'imagepaths'
+						? extractUrlLikeShareQueryParam(queryStr, 'imagePaths')
+						: '');
+				if (one) return [one];
+			}
+			var list = [];
+			forEachQueryParam(queryStr, function (val, key) {
+				if (key && String(key).toLowerCase() === nameLower) {
+					list.push(val);
+				}
+			});
+			return list;
+		}
+
+		function getMergedParam(name) {
+			var m = getMergedQueryString();
+			if (!m) return '';
+			var n = String(name);
+			var nl = n.toLowerCase();
+			if (nl === 'imagepath' || nl === 'coverurl' || nl === 'userimage') {
+				var smart = extractUrlLikeShareQueryParam(m, n);
+				if (smart) return smart;
+			}
+			var v = new URLSearchParams(m).get(name);
+			if (v == null) {
+				new URLSearchParams(m).forEach(function (val, key) {
+					if (v == null && key && String(key).toLowerCase() === nl) {
+						v = val;
+					}
+				});
+			}
+			return v == null ? '' : String(v);
+		}
+
+		function pushUniqueUrl(list, u) {
+			u = String(u == null ? '' : u).trim();
+			if (!u) return;
+			if (list.indexOf(u) >= 0) return;
+			list.push(u);
+		}
+
+		function parseOptionsItem() {
+			var raw = getMergedParam('options');
+			if (!raw) return null;
+			var opts;
+			try {
+				opts = JSON.parse(raw);
+			} catch (e) {
+				return null;
+			}
+			if (!opts || typeof opts !== 'object') return null;
+			var item = opts.item;
+			if (typeof item === 'string' && item) {
+				try {
+					item = JSON.parse(item);
+				} catch (e2) {
+					return null;
+				}
+			}
+			if (!item || typeof item !== 'object') return null;
+			return item;
+		}
+
+		function normalizeMediaUrl(raw) {
+			raw = String(raw == null ? '' : raw).trim();
+			if (!raw || raw === '0') return '';
+			if (/%[0-9A-Fa-f]{2}/.test(raw)) {
+				try {
+					raw = decodeURIComponent(raw.replace(/\+/g, ' '));
+				} catch (e) {}
+			}
+			return raw;
+		}
+
+		/** 媒体 URL 是否以 .mp4 结尾(忽略 query/hash,大小写不敏感) */
+		function isMp4VideoUrl(url) {
+			if (!url || typeof url !== 'string') return false;
+			var path = url.split(/[?#]/)[0].toLowerCase();
+			return path.length >= 4 && path.slice(-4) === '.mp4';
+		}
+
+		/**
+		 * 播放用地址:只保留到 .mp4 为止,去掉 ?x-oss-process=video/snapshot... 等(否则会拿到截图流而非视频)
+		 */
+		function getMp4PlaybackUrl(url) {
+			var u = normalizeMediaUrl(String(url || ''));
+			if (!u) return '';
+			var q = u.indexOf('?');
+			var h = u.indexOf('#');
+			var cut = u.length;
+			if (q >= 0) cut = Math.min(cut, q);
+			if (h >= 0) cut = Math.min(cut, h);
+			var base = u.slice(0, cut);
+			if (/\.mp4$/i.test(base)) return base;
+			return u;
+		}
+
+		function mp4UrlToJpgUrl(u) {
+			var q = u.indexOf('?');
+			var h = u.indexOf('#');
+			var cut = u.length;
+			if (q >= 0) cut = Math.min(cut, q);
+			if (h >= 0) cut = Math.min(cut, h);
+			var pathPart = u.slice(0, cut);
+			var rest = u.slice(cut);
+			if (!/\.mp4$/i.test(pathPart)) return '';
+			return pathPart.slice(0, -4) + '.jpg' + rest;
+		}
+
+		function mp4UrlToOssSnapshotUrl(mp4Base) {
+			if (!mp4Base) return '';
+			return (
+				mp4Base +
+				(mp4Base.indexOf('?') >= 0 ? '&' : '?') +
+				'x-oss-process=video/snapshot,t_0,f_jpg,m_fast'
+			);
+		}
+
+		/** 微信内部分 CDN 对 t_0 抽帧异常时,换非零时刻再试 */
+		function mp4UrlToOssSnapshotUrlAtMs(mp4Base, ms) {
+			if (!mp4Base) return '';
+			var t = Math.max(0, parseInt(ms, 10) || 0);
+			return (
+				mp4Base +
+				(mp4Base.indexOf('?') >= 0 ? '&' : '?') +
+				'x-oss-process=video/snapshot,t_' +
+				t +
+				',f_jpg,m_fast'
+			);
+		}
+
+		/** 完整 URL 是否更像「图片/截图」而非纯视频文件 */
+		function urlLooksLikeStillImage(u) {
+			if (!u) return false;
+			var path = u.split(/[?#]/)[0].toLowerCase();
+			if (/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(path)) return true;
+			if (/x-oss-process=/i.test(u) && /snapshot|image|f_jpg/i.test(u)) return true;
+			return false;
+		}
+
+		function isIOSVideoEnv() {
+			return (
+				/iP(hone|od|ad)/i.test(navigator.userAgent || '') ||
+				(navigator.platform === 'MacIntel' && (navigator.maxTouchPoints || 0) > 1)
+			);
+		}
+
+		function pushUniqueStillUrl(arr, u) {
+			u = normalizeMediaUrl(String(u || '').trim());
+			if (!u || arr.indexOf(u) >= 0) return;
+			arr.push(u);
+		}
+
+		/**
+		 * iOS / 微信内置浏览器:video 首帧与 OSS 截图常灰屏或被 Referer 拦截。
+		 * 优先静态图链(封面、jpg、多档 OSS snapshot);叠层在 video 之上且不隐藏;失败再回退 video。
+		 */
+		function appendMp4FirstFrameToSlide(slide, src) {
+			var full = normalizeMediaUrl(String(src || ''));
+			var playUrl = getMp4PlaybackUrl(full);
+			if (!playUrl) return;
+
+			var jpg = mp4UrlToJpgUrl(playUrl);
+			var snap = mp4UrlToOssSnapshotUrl(playUrl);
+			var snapAr =
+				playUrl +
+				(playUrl.indexOf('?') >= 0 ? '&' : '?') +
+				'x-oss-process=video/snapshot,t_0,f_jpg,ar_auto,m_fast';
+			var poster = '';
+			var item = parseOptionsItem();
+			var optFrame =
+				item && item.videoFirstFrame != null
+					? normalizeMediaUrl(String(item.videoFirstFrame).trim())
+					: '';
+			if (optFrame) {
+				poster = optFrame;
+			} else if (full.length > playUrl.length && urlLooksLikeStillImage(full)) {
+				poster = full;
+			} else if (jpg) {
+				poster = jpg;
+			} else if (snap) {
+				poster = snap;
+			}
+
+			var stillList = [];
+			if (optFrame) pushUniqueStillUrl(stillList, optFrame);
+			if (item && item.coverImage != null && String(item.coverImage).trim() !== '') {
+				pushUniqueStillUrl(stillList, item.coverImage);
+			}
+			if (item && item.cover != null && String(item.cover).trim() !== '') {
+				pushUniqueStillUrl(stillList, item.cover);
+			}
+			if (full.length > playUrl.length && urlLooksLikeStillImage(full)) {
+				pushUniqueStillUrl(stillList, full);
+			}
+			if (jpg) pushUniqueStillUrl(stillList, jpg);
+			if (snap) pushUniqueStillUrl(stillList, snap);
+			if (snapAr && snapAr !== snap) pushUniqueStillUrl(stillList, snapAr);
+			if (isWeChatInAppBrowser()) {
+				var snap400 = mp4UrlToOssSnapshotUrlAtMs(playUrl, 400);
+				var snap1200 = mp4UrlToOssSnapshotUrlAtMs(playUrl, 1200);
+				if (snap400 && snap400 !== snap) pushUniqueStillUrl(stillList, snap400);
+				if (snap1200 && snap1200 !== snap && snap1200 !== snap400) {
+					pushUniqueStillUrl(stillList, snap1200);
+				}
+			}
+
+			var fragile = preferStaticStillOverVideo();
+			if (fragile && stillList.length) {
+				var wrapImg = document.createElement('div');
+				wrapImg.className = 'dyn-hero__media-wrap';
+				var heroIm = document.createElement('img');
+				heroIm.className = 'dyn-hero__poster-swap';
+				heroIm.alt = '';
+				heroIm.style.width = '100%';
+				heroIm.style.height = '100%';
+				heroIm.style.objectFit = 'cover';
+				applyMediaNoReferrer(heroIm);
+				var idx = 0;
+				heroIm.onerror = function () {
+					idx += 1;
+					if (idx < stillList.length) {
+						heroIm.src = stillList[idx];
+					} else {
+						if (heroIm.parentNode) heroIm.parentNode.removeChild(heroIm);
+						appendMp4VideoFirstFrameBranch(wrapImg, playUrl, poster, snap, true);
+					}
+				};
+				heroIm.src = stillList[0];
+				wrapImg.appendChild(heroIm);
+				slide.appendChild(wrapImg);
+				return;
+			}
+
+			var wrap = document.createElement('div');
+			wrap.className = 'dyn-hero__media-wrap';
+			appendMp4VideoFirstFrameBranch(wrap, playUrl, poster, snap, fragile);
+			slide.appendChild(wrap);
+		}
+
+		function appendMp4VideoFirstFrameBranch(wrap, playUrl, poster, snap, fragilePosterEnv) {
+			var posterIm = null;
+			if (poster) {
+				posterIm = document.createElement('img');
+				posterIm.className = 'dyn-hero__poster-swap';
+				posterIm.alt = '';
+				applyMediaNoReferrer(posterIm);
+				posterIm.src = poster;
+				posterIm.decoding = 'async';
+				posterIm.onerror = function () {
+					this.style.display = 'none';
+				};
+				wrap.appendChild(posterIm);
+			}
+
+			var vid = document.createElement('video');
+			vid.className = 'dyn-hero__video--poster-only';
+			applyMediaNoReferrer(vid);
+			var playSrc = playUrl;
+			if (fragilePosterEnv && playUrl.indexOf('#') < 0) {
+				try {
+					playSrc = playUrl + '#t=0.12';
+				} catch (e0) {
+					playSrc = playUrl;
+				}
+			}
+			vid.src = playSrc;
+			if (poster) vid.setAttribute('poster', poster);
+			vid.setAttribute('playsinline', '');
+			vid.setAttribute('webkit-playsinline', '');
+			vid.setAttribute('disableremoteplayback', '');
+			vid.setAttribute('preload', 'auto');
+			vid.muted = true;
+			vid.setAttribute('muted', '');
+			vid.playsInline = true;
+			vid.loop = false;
+			vid.defaultMuted = true;
+
+			function hidePosterSwap() {
+				if (fragilePosterEnv) return;
+				if (!posterIm) return;
+				posterIm.style.opacity = '0';
+				window.setTimeout(function () {
+					if (posterIm && posterIm.parentNode) {
+						posterIm.style.visibility = 'hidden';
+					}
+				}, 220);
+			}
+
+			function iosKickDecodePaint() {
+				if (!fragilePosterEnv) return;
+				var pr = vid.play();
+				if (pr && typeof pr.then === 'function') {
+					pr.then(function () {
+						vid.pause();
+					}).catch(function () {});
+				}
+			}
+
+			function seekToFirstFrame() {
+				try {
+					if (vid.readyState < 2) return;
+					var dur = vid.duration;
+					var floorT = fragilePosterEnv ? 0.12 : 0.04;
+					if (typeof dur === 'number' && isFinite(dur) && dur > 0) {
+						var t = Math.max(floorT, Math.min(dur * 0.06, dur - 0.05));
+						vid.currentTime = t;
+					} else {
+						vid.currentTime = floorT;
+					}
+				} catch (e) {}
+			}
+
+			vid.addEventListener('loadedmetadata', function () {
+				seekToFirstFrame();
+			});
+			vid.addEventListener('loadeddata', function () {
+				seekToFirstFrame();
+			});
+			vid.addEventListener('seeked', function () {
+				vid.pause();
+				iosKickDecodePaint();
+				if (!fragilePosterEnv && vid.videoWidth > 0) {
+					hidePosterSwap();
+				}
+			});
+			vid.addEventListener('canplay', function once() {
+				vid.removeEventListener('canplay', once);
+				seekToFirstFrame();
+			});
+
+			vid.addEventListener('timeupdate', function onTU() {
+				if (!fragilePosterEnv && vid.currentTime >= 0.08 && vid.videoWidth > 0) {
+					vid.removeEventListener('timeupdate', onTU);
+					hidePosterSwap();
+				}
+			});
+
+			vid.addEventListener('error', function () {
+				if (vid.getAttribute('data-fallback') === '1') return;
+				vid.setAttribute('data-fallback', '1');
+				if (snap && String(vid.src || '').indexOf('x-oss-process') < 0) {
+					vid.src = snap;
+					try {
+						vid.load();
+					} catch (e2) {}
+				}
+			});
+
+			wrap.appendChild(vid);
+		}
+
+		/** 顶层 userImage;若无则 options.item.userImage(须为字符串地址) */
+		function resolveUserAvatarUrl() {
+			var top = normalizeMediaUrl(getMergedParam('userImage'));
+			if (top) return top;
+			var item = parseOptionsItem();
+			if (!item) return '';
+			var u = item.userImage;
+			if (u == null || u === '') return '';
+			if (typeof u === 'number') return '';
+			return normalizeMediaUrl(String(u));
+		}
+
+		/**
+		 * App 分享链接里 imagePath 常在 options JSON 的 item 字符串内:
+		 * options={ "item": "{\"imagePath\":\"https://...\",\"imageList\":[...]}" }
+		 */
+		function collectImagesFromOptionsParam() {
+			var item = parseOptionsItem();
+			if (!item) return [];
+			var out = [];
+
+			if (Array.isArray(item.imageList)) {
+				item.imageList.forEach(function (u) {
+					pushUniqueUrl(out, u);
+				});
+			}
+			if (item.imagePath) {
+				String(item.imagePath).split(',').forEach(function (seg) {
+					pushUniqueUrl(out, seg);
+				});
+			}
+			pushUniqueUrl(out, item.cover);
+			pushUniqueUrl(out, item.src);
+			return out;
+		}
+
+		/**
+		 * 从地址栏收集轮播图:① 顶层 imagePath;② options.item 内 imagePath / imageList;
+		 * ③ 顶层 coverUrl。
+		 */
+		function collectImageUrlsFromUrl() {
+			var merged = getMergedQueryString();
+			var vals = getAllParamValuesCI(merged, 'imagepath');
+			if (!vals.length) {
+				vals = getAllParamValuesCI(merged, 'imagepaths');
+			}
+			if (!vals.length) {
+				vals = getAllParamValuesCI(merged, 'images');
+			}
+			if (!vals.length) {
+				vals = getAllParamValuesCI(merged, 'image_path');
+			}
+
+			var urls = [];
+			vals.forEach(function (val) {
+				val = String(val == null ? '' : val).trim();
+				if (!val) return;
+
+				if (val.charAt(0) === '[') {
+					try {
+						var arr = JSON.parse(val);
+						if (Array.isArray(arr)) {
+							arr.forEach(function (u) {
+								pushUniqueUrl(urls, u);
+							});
+							return;
+						}
+					} catch (e1) {
+						try {
+							var arr2 = JSON.parse(decodeURIComponent(val));
+							if (Array.isArray(arr2)) {
+								arr2.forEach(function (u) {
+									pushUniqueUrl(urls, u);
+								});
+								return;
+							}
+						} catch (e2) {}
+					}
+				}
+
+				val.split(',').forEach(function (seg) {
+					seg = String(seg).trim();
+					if (!seg) return;
+					if (/%[0-9A-Fa-f]{2}/.test(seg)) {
+						try {
+							seg = decodeURIComponent(seg.replace(/\+/g, ' '));
+						} catch (e) {}
+					}
+					pushUniqueUrl(urls, seg);
+				});
+			});
+
+			if (!urls.length) {
+				collectImagesFromOptionsParam().forEach(function (u) {
+					pushUniqueUrl(urls, u);
+				});
+			}
+
+			if (!urls.length) {
+				var cover = getMergedParam('coverUrl');
+				if (cover) {
+					try {
+						cover = decodeURIComponent(cover);
+					} catch (e) {}
+					pushUniqueUrl(urls, cover);
+				}
+			}
+
+			return urls;
+		}
+
+		var heroI = 0;
+		var heroTimer = null;
+		var dynHeroSwipeInited = false;
+
+		function getHeroSlideCount() {
+			return document.getElementById('dynHeroDots').querySelectorAll('.dyn-hero__dot').length;
+		}
+
+		function initDynCarousel(slides) {
+			var track = document.getElementById('dynHeroTrack');
+			var dotsWrap = document.getElementById('dynHeroDots');
+
+			function go(n) {
+				var count = getHeroSlideCount();
+				if (count < 1) return;
+				heroI = ((n % count) + count) % count;
+				track.style.transform = 'translateX(' + (-heroI * 100) + '%)';
+				dotsWrap.querySelectorAll('.dyn-hero__dot').forEach(function (el, idx) {
+					el.classList.toggle('is-active', idx === heroI);
+				});
+			}
+
+			if (heroTimer) {
+				clearInterval(heroTimer);
+				heroTimer = null;
+			}
+
+			if (slides < 2) {
+				heroI = 0;
+				track.style.transform = 'translateX(0)';
+				return;
+			}
+
+			heroTimer = setInterval(function () { go(heroI + 1); }, 4000);
+
+			if (!dynHeroSwipeInited) {
+				dynHeroSwipeInited = true;
+				var hero = document.getElementById('dynHero');
+				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 buildDynSlides(urls) {
+			heroI = 0;
+			var track = document.getElementById('dynHeroTrack');
+			var dotsWrap = document.getElementById('dynHeroDots');
+			var heroEl = document.getElementById('dynHero');
+			track.innerHTML = '';
+			dotsWrap.innerHTML = '';
+			track.style.transform = 'translateX(0)';
+			if (heroEl) heroEl.classList.remove('dyn-hero--no-dots');
+
+			var list = (urls || []).map(function (u) {
+				return normalizeMediaUrl(u);
+			}).filter(Boolean);
+			if (!list.length) {
+				var slide = document.createElement('div');
+				slide.className = 'dyn-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 = 'dyn-hero__dot is-active';
+				dotsWrap.appendChild(dot);
+				return initDynCarousel(1);
+			}
+
+			/** 任意一条为 .mp4:只展示第一个的首帧静图,不轮播、无指示点(与纯图多条轮播区分) */
+			var firstMp4 = '';
+			for (var mi = 0; mi < list.length; mi++) {
+				if (isMp4VideoUrl(list[mi])) {
+					firstMp4 = list[mi];
+					break;
+				}
+			}
+			if (firstMp4) {
+				if (heroEl) heroEl.classList.add('dyn-hero--no-dots');
+				var vSlide = document.createElement('div');
+				vSlide.className = 'dyn-hero__slide';
+				appendMp4FirstFrameToSlide(vSlide, firstMp4);
+				track.appendChild(vSlide);
+				return initDynCarousel(1);
+			}
+
+			list.forEach(function (url, d) {
+				var s = document.createElement('div');
+				s.className = 'dyn-hero__slide';
+				if (isMp4VideoUrl(url)) {
+					appendMp4FirstFrameToSlide(s, url);
+				} else {
+					var im = document.createElement('img');
+					if (/^https?:/i.test(url)) {
+						applyMediaNoReferrer(im);
+					}
+					im.src = url;
+					im.alt = '';
+					s.appendChild(im);
+				}
+				track.appendChild(s);
+				var dot = document.createElement('span');
+				dot.className = 'dyn-hero__dot' + (d === 0 ? ' is-active' : '');
+				dotsWrap.appendChild(dot);
+			});
+			initDynCarousel(list.length);
+		}
+
+		function resolveCommentRequestParams() {
+			var item = parseOptionsItem();
+			var sourceType = getMergedParam('sourceType') || getMergedParam('businessType') || q('sourceType') || q('businessType');
+			if (sourceType === '' && item && item.type != null) sourceType = String(item.type);
+
+			var sourceId = getMergedParam('sourceId') || getMergedParam('dynamicId') || q('dynamicId') || q('id');
+			if (!sourceId && item && item.id != null) sourceId = String(item.id);
+
+			var userId = getMergedParam('userId') || q('userId');
+
+			if (!sourceType || !sourceId || !userId) return null;
+			return {
+				sourceType: String(sourceType),
+				sourceId: String(sourceId),
+				userId: String(userId),
+				pageNum: COMMENT_PAGE_NUM,
+				pageSize: COMMENT_PAGE_SIZE
+			};
+		}
+
+		function normalizeCommentPayload(data) {
+			if (!data) return { list: [], total: 0 };
+			if (Array.isArray(data)) return { list: data, total: data.length };
+			var list = data.list || data.records || data.rows;
+			if (!Array.isArray(list) && Array.isArray(data.data)) list = data.data;
+			if (!Array.isArray(list)) list = [];
+			var total = data.total != null ? Number(data.total) : (data.count != null ? Number(data.count) : list.length);
+			if (isNaN(total)) total = list.length;
+			return { list: list, total: total };
+		}
+
+		function formatCommentCountParens(n) {
+			return '(' + String(n) + ')';
+		}
+
+		function renderComments(resData) {
+			var norm = normalizeCommentPayload(resData);
+			var list = norm.list;
+			var total = norm.total;
+			document.getElementById('commentCount').textContent = formatCommentCountParens(total);
+			document.getElementById('storeReviews').textContent = String(total);
+			var wrap = document.getElementById('commentList');
+			var empty = document.getElementById('commentEmpty');
+			wrap.innerHTML = '';
+			wrap.classList.remove('is-visible');
+			if (!list.length) {
+				empty.style.display = 'flex';
+				return;
+			}
+			empty.style.display = 'none';
+			wrap.classList.add('is-visible');
+			list.forEach(function (row) {
+				if (!row || typeof row !== 'object') return;
+				var text = row.content != null ? String(row.content) : (row.commentContent != null ? String(row.commentContent) : (row.context != null ? String(row.context) : (row.text != null ? String(row.text) : '')));
+				var uname = row.userName != null ? String(row.userName) : (row.nickName != null ? String(row.nickName) : (row.nickname != null ? String(row.nickname) : (row.headName != null ? String(row.headName) : '用户')));
+				var time = row.createdTime != null ? String(row.createdTime) : (row.createTime != null ? String(row.createTime) : (row.time != null ? String(row.time) : ''));
+				var headImg = row.headImg != null ? String(row.headImg).trim() : '';
+				if (!headImg || headImg === '0') headImg = COMMENT_AVATAR_FALLBACK;
+
+				var rowEl = document.createElement('div');
+				rowEl.className = 'comment-item';
+				var avatar = document.createElement('img');
+				avatar.className = 'comment-item__avatar';
+				avatar.alt = '';
+				avatar.src = headImg;
+				avatar.onerror = function () {
+					this.onerror = null;
+					this.src = COMMENT_AVATAR_FALLBACK;
+				};
+				var col = document.createElement('div');
+				col.className = 'comment-item__col';
+				var meta = document.createElement('div');
+				meta.className = 'comment-item__meta';
+				var nameEl = document.createElement('span');
+				nameEl.className = 'comment-item__name';
+				nameEl.textContent = uname;
+				meta.appendChild(nameEl);
+				var body = document.createElement('div');
+				body.className = 'comment-item__text';
+				body.textContent = text;
+				col.appendChild(meta);
+				col.appendChild(body);
+				if (time) {
+					var timeEl = document.createElement('div');
+					timeEl.className = 'comment-item__time';
+					timeEl.textContent = time;
+					col.appendChild(timeEl);
+				}
+				rowEl.appendChild(avatar);
+				rowEl.appendChild(col);
+				wrap.appendChild(rowEl);
+			});
+		}
+
+		function loadComments() {
+			var p = resolveCommentRequestParams();
+			if (!p) return;
+			var path = '/commonComment/getListBySourceType?' +
+				'sourceType=' + encodeURIComponent(p.sourceType) +
+				'&sourceId=' + encodeURIComponent(p.sourceId) +
+				'&userId=' + encodeURIComponent(p.userId) +
+				'&pageNum=' + encodeURIComponent(String(p.pageNum)) +
+				'&pageSize=' + encodeURIComponent(String(p.pageSize));
+			apiFetch(path)
+				.then(function (res) {
+					if (res.code !== 200) {
+						renderComments(null);
+						return;
+					}
+					renderComments(res.data);
+				})
+				.catch(function (e) {
+					console.error(e);
+					renderComments(null);
+				});
+		}
+
+		function applyQueryContent() {
+			/** 优先展示分享者昵称(App 传 shareUserName);否则动态作者 userName、店铺名 */
+			var name =
+				getMergedParam('shareUserName') ||
+				q('shareUserName') ||
+				q('userName') ||
+				q('storeName');
+			if (name) document.getElementById('userName').textContent = decodeURIComponent(name);
+
+			var avatarUrl = resolveUserAvatarUrl();
+			if (avatarUrl) {
+				document.getElementById('userAvatar').src = avatarUrl;
+			}
+
+			var desc = q('content') || q('desc') || q('text');
+			if (desc) {
+				document.getElementById('userDesc').textContent = decodeURIComponent(desc);
+			}
+
+			var storeNameParam = getMergedParam('storeName') || q('storeName');
+			if (storeNameParam) {
+				try {
+					document.getElementById('storeTitle').textContent = decodeURIComponent(storeNameParam);
+				} catch (e) {
+					document.getElementById('storeTitle').textContent = storeNameParam;
+				}
+			}
+
+			var scoreAvgParam = getMergedParam('scoreAvg') || q('scoreAvg');
+			if (scoreAvgParam !== '') {
+				var scNum = parseFloat(scoreAvgParam, 10);
+				document.getElementById('storeScore').textContent =
+					!isNaN(scNum) ? scNum.toFixed(1) : String(scoreAvgParam);
+			}
+
+			buildDynSlides(collectImageUrlsFromUrl());
+
+			if (!resolveCommentRequestParams()) {
+				var cc = getMergedParam('commentCount') || q('commentCount');
+				if (cc !== '') {
+					var n = parseInt(cc, 10);
+					if (!isNaN(n)) {
+						document.getElementById('commentCount').textContent = formatCommentCountParens(n);
+						document.getElementById('storeReviews').textContent = String(n);
+						document.getElementById('commentEmpty').style.display = n > 0 ? 'none' : 'flex';
+					}
+				}
+			}
+
+			var tag = q('tagline');
+			if (tag) document.getElementById('storeTagline').textContent = decodeURIComponent(tag);
+
+			var cat = q('category') || q('storeCat');
+			if (cat) document.getElementById('storeCat').textContent = decodeURIComponent(cat);
+		}
+
+		function boot() {
+			applyQueryContent();
+			loadComments();
+			var openBtn = document.getElementById('openApp');
+			if (openBtn) {
+				openBtn.addEventListener('click', function () {
+					tryOpenHBuilderApp();
+				});
+			}
+		}
+
+		if (document.readyState === 'complete') {
+			boot();
+		} else {
+			window.onload = boot;
+		}
+	})();
+	</script>
+</body>
+</html>

+ 2315 - 0
HBuilderProjects/shareIndex.html

@@ -0,0 +1,2315 @@
+<!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__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 {
+			font-size: 14px;
+			font-weight: 600;
+			color: var(--orange);
+		}
+
+		.store-evaluate-row {
+			display: none;
+			flex-direction: row;
+			flex-wrap: wrap;
+			align-items: center;
+			gap: 6px 6px;
+			margin: 0 0 12px;
+			padding: 0;
+			font-size: 13px;
+			line-height: 1.4;
+			color: var(--text-secondary);
+		}
+
+		.store-evaluate-row.is-visible {
+			display: flex;
+		}
+
+		.store-evaluate__item {
+			white-space: nowrap;
+		}
+
+		.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;
+			border-bottom:1px solid #eee;
+			padding-bottom: 20px;
+		}
+
+		.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,
+		.meta-line img {
+			flex-shrink: 0;
+			margin-top: 2px;
+			opacity: 0.75;
+			display: block;
+		}
+
+		.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: 10px;
+			background: #F5F5F5;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			color: var(--orange);
+		}
+
+		.act-btn__icon img {
+			width: 22px;
+			height: 22px;
+			object-fit: contain;
+			display: block;
+		}
+
+		/* businessStatus===99 时仅展示关店提示并隐藏 .share-content;底部 FAB 仍在 #shareBelowContentEl */
+		.closed-rec-wrap {
+			display: none;
+		}
+
+		.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;
+		}
+
+		#shareClosedRecEmpty {
+			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-card__rating .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;
+		}
+
+		.share-none {
+			display: none;
+			text-align: center;
+			padding: 28px 20px 16px;
+			color: var(--text-muted);
+			font-size: 15px;
+			line-height: 1.5;
+		}
+
+		.share-none img {
+			width: 240px;
+			height: 240px;
+			margin-bottom: 12px;
+			display: inline-block;
+		}
+
+		.share-none p {
+			margin: 0;
+			padding:0 0 60px 0;
+			color:#999;
+		}
+
+		/* 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;
+			background:#F47D1F;
+		}
+
+		.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;
+		}
+
+		/* 营销活动(getStoreCouponList.marketingList) */
+		.marketing-section {
+			padding: 4px 15px 20px;
+		}
+
+		.marketing-section__title {
+			font-size: 16px;
+			font-weight: 700;
+			color: var(--text);
+			margin: 0 0 12px;
+		}
+
+		.marketing-section__list {
+			display: flex;
+			flex-direction: column;
+			gap: 12px;
+		}
+
+		.marketing-card {
+			border-radius: 12px;
+			overflow: hidden;
+			background: #f0f0f0;
+			box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+		}
+
+		.marketing-card__media img,
+		.marketing-card__media video {
+			width: 100%;
+			display: block;
+			aspect-ratio: 16 / 9;
+			object-fit: cover;
+			vertical-align: middle;
+		}
+
+		.marketing-card__media video {
+			background: #000;
+		}
+
+		.marketing-card--more {
+			display: none;
+		}
+
+		.marketing-section.is-expanded .marketing-card--more {
+			display: block;
+		}
+
+		.marketing-section__more {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			gap: 4px;
+			width: 100%;
+			margin-top: 14px;
+			padding: 8px 0;
+			border: none;
+			background: none;
+			font-size: 14px;
+			color: var(--text-secondary);
+			cursor: pointer;
+			-webkit-tap-highlight-color: transparent;
+		}
+
+		.marketing-section__more-chevron {
+			width: 16px;
+			height: 16px;
+			flex-shrink: 0;
+			transition: transform 0.2s ease;
+		}
+
+		.marketing-section.is-expanded .marketing-section__more-chevron {
+			transform: rotate(180deg);
+		}
+
+		.marketing-section__empty {
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+			padding: 10px 0 8px;
+		}
+
+		.marketing-section__empty img {
+			width: 200px;
+			height: 200px;
+			display: block;
+			object-fit: contain;
+		}
+
+		.marketing-section__empty-label {
+			margin: 12px 0 0;
+			font-size: 14px;
+			color: var(--text-secondary);
+			text-align: center;
+		}
+
+		/* 演出安排 performance/list/query */
+		.performance-section {
+			padding: 8px 0 20px;
+			border-top: 1px solid var(--border);
+			margin-top: 4px;
+		}
+
+		.performance-section__title {
+			font-size: 16px;
+			font-weight: 700;
+			color: var(--text);
+			margin: 0 15px 12px;
+		}
+
+		.performance-strip {
+			display: flex;
+			flex-direction: row;
+			flex-wrap: nowrap;
+			gap: 12px;
+			overflow-x: auto;
+			overflow-y: hidden;
+			padding: 0 15px 4px;
+			-webkit-overflow-scrolling: touch;
+			scrollbar-width: none;
+		}
+
+		.performance-strip::-webkit-scrollbar {
+			display: none;
+		}
+
+		.performance-card {
+			flex: 0 0 auto;
+			display: flex;
+			flex-direction: row;
+			align-items: stretch;
+			gap: 10px;
+			width: 272px;
+			min-width: 272px;
+			padding: 0;
+			background: #fff;
+			border-radius: 12px;
+			overflow: hidden;
+		}
+
+		.performance-card__poster {
+			flex: 0 0 76px;
+			width: 76px;
+			min-height: 102px;
+			background: #eee;
+			align-self: stretch;
+		}
+
+		.performance-card__poster img {
+			width: 100%;
+			height: 100%;
+			min-height: 102px;
+			object-fit: cover;
+			display: block;
+			vertical-align: middle;
+		}
+
+		.performance-card__body {
+			flex: 1;
+			min-width: 0;
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			gap: 4px;
+			padding: 10px 12px 10px 0;
+		}
+
+		.performance-card__name {
+			font-size: 15px;
+			font-weight: 700;
+			color: var(--text);
+			line-height: 1.3;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			display: -webkit-box;
+			-webkit-line-clamp: 2;
+			-webkit-box-orient: vertical;
+		}
+
+		.performance-card__meta {
+			font-size: 12px;
+			color: var(--text-secondary);
+			line-height: 1.35;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		/* 人员阵容 queryStaffListByTitle */
+		.staff-section {
+			padding: 8px 0 24px;
+			border-top: 1px solid var(--border);
+			margin-top: 4px;
+		}
+
+		.staff-section__head {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			gap: 12px;
+			padding: 0 15px;
+			margin-bottom: 12px;
+		}
+
+		.staff-section__page-title {
+			font-size: 16px;
+			font-weight: 700;
+			color: var(--text);
+			margin: 0;
+			flex: 1;
+			min-width: 0;
+		}
+
+		.staff-section__all {
+			flex-shrink: 0;
+			display: inline-flex;
+			align-items: center;
+			gap: 2px;
+			font-size: 13px;
+			color: #151515;
+			text-decoration: none;
+			background: none;
+			border: none;
+			padding: 0;
+			cursor: pointer;
+			-webkit-tap-highlight-color: transparent;
+		}
+
+		.staff-section__all-chevron {
+			width: 14px;
+			height: 14px;
+			opacity: 0.7;
+		}
+
+		.staff-group {
+			margin-bottom: 20px;
+		}
+
+		.staff-group:last-child {
+			margin-bottom: 0;
+		}
+
+		.staff-strip {
+			display: flex;
+			flex-direction: row;
+			flex-wrap: nowrap;
+			gap: 10px;
+			overflow-x: auto;
+			overflow-y: hidden;
+			padding: 0 15px 4px;
+			-webkit-overflow-scrolling: touch;
+			scrollbar-width: none;
+		}
+
+		.staff-strip::-webkit-scrollbar {
+			display: none;
+		}
+
+		.staff-card {
+			flex: 0 0 112px;
+			width: 112px;
+			min-width: 112px;
+		}
+
+		.staff-card__img-wrap {
+			position: relative;
+			border-radius: 10px;
+			overflow: hidden;
+			background: #eee;
+			aspect-ratio: 3 / 4;
+		}
+
+		.staff-card__img-wrap img {
+			width: 100%;
+			height: 100%;
+			object-fit: cover;
+			display: block;
+			vertical-align: middle;
+		}
+
+		.staff-card__likes {
+			position: absolute;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			padding: 6px 8px;
+			font-size: 11px;
+			color: #fff;
+			line-height: 1.2;
+			background: linear-gradient(to top, rgba(0, 0, 0, 0.65), transparent);
+		}
+
+		.staff-card__name-row {
+			display: flex;
+			align-items: center;
+			gap: 4px;
+			margin-top: 8px;
+			min-width: 0;
+		}
+
+		.staff-card__name {
+			font-size: 14px;
+			font-weight: 700;
+			color: var(--text);
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+			flex: 1;
+			min-width: 0;
+		}
+
+		.staff-card__badge {
+			flex-shrink: 0;
+			font-size: 10px;
+			font-weight: 600;
+			color: #fff;
+			background: #ff9d4d;
+			padding: 2px 5px;
+			border-radius: 3px;
+			line-height: 1.2;
+			white-space: nowrap;
+		}
+
+		.staff-card__meta {
+			display: flex;
+			align-items: center;
+			flex-wrap: wrap;
+			gap: 0 4px;
+			margin-top: 4px;
+			font-size: 12px;
+			color: var(--text-secondary);
+			line-height: 1.35;
+		}
+
+		.staff-card__position {
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+			max-width: 100%;
+		}
+
+		.staff-card__reviews {
+			display: inline-flex;
+			align-items: center;
+			gap: 4px;
+		}
+
+		.staff-card__sep {
+			color: #ddd;
+			font-weight: 300;
+		}
+	</style>
+</head>
+<body>
+	<div class="share-none" id="shareClosedBanner" aria-hidden="true">
+		<img src="images/storeNone.png" alt="">
+		<p>抱歉,商户已关闭,看看别的吧</p>
+	</div>
+	<div class="closed-rec-wrap" id="shareClosedRecommendWrap" aria-hidden="true">
+		<div class="closed-rec-divider"></div>
+		<h3 class="closed-rec-title">更多推荐</h3>
+		<div class="closed-rec-grid" id="shareClosedRecList">
+			<p id="shareClosedRecEmpty" class="closed-rec-empty" style="display:none;">暂无推荐</p>
+		</div>
+	</div>
+	<div class="share-content" id="shareContentEl">
+		<div class="hero" id="hero">
+			<div class="hero__track" id="heroTrack">
+				<div class="hero__slide"><img id="heroFallbackImg" src="images/hero.png" alt="店铺图"></div>
+			</div>
+			<div class="hero__dots" id="heroDots" aria-hidden="true"></div>
+		</div>
+
+		<!-- 数据由 getClientStoreDetail(id,userId,jingdu,weidu)填充,见 renderDetail -->
+		<section class="section" id="mainSection">
+			<p id="pageError" class="page-error" style="display:none;color:#ff4d4f;padding:12px 0;font-size:14px;"></p>
+			<h2 class="store-name" id="storeName">加载中…</h2>
+			<div class="row rating-row">
+				<div class="row" style="flex:1; justify-content:flex-start;">
+					<span class="stars" aria-label="5.0 分">
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+						<svg class="star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
+					</span>
+					<span class="rating-num" id="scoreAvg">—</span>
+					<span id="commitCount" style="font-size: 14px;">0</span>
+				</div>
+				<!-- <a href="#" class="link-muted"><span id="commitCount">0</span>条评价
+					<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
+				</a> -->
+			</div>
+
+			<div class="store-evaluate-row" id="storeEvaluateRow" aria-hidden="true">
+				<span class="store-evaluate__item">酒水:<span id="storeEvaluateScoreOne">—</span></span>
+				<span class="store-evaluate__item">氛围:<span id="storeEvaluateScoreTwo">—</span></span>
+				<span class="store-evaluate__item">服务:<span id="storeEvaluateScoreThree">—</span></span>
+			</div>
+			<div class="row hours-row">
+				<div class="hours-left">
+					<span class="status" id="bizStatus">—</span>
+					<span class="hours-text" id="hoursText">—</span>
+				</div>
+				<a href="#" class="link-muted">详情
+					<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
+				</a>
+			</div>
+		</section>
+
+
+		<div class="loc-wrap">
+			<div class="loc-main">
+				<p class="addr" id="storeAddr">—</p>
+				<div class="meta-line" id="metaDistance" style="display:none;">
+					<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
+					<span id="distanceText">—</span>
+				</div>
+				<div class="meta-line" id="metaSubway" style="display:none;">
+					<img src="images/iconn.png" alt="" width="12" height="12" decoding="async">
+					<span id="subwayText">—</span>
+				</div>
+			</div>
+			<div class="loc-actions">
+				<button type="button" class="act-btn" aria-label="导航">
+					<span class="act-btn__icon">
+						<img src="images/loc1.png" alt="" width="22" height="22" decoding="async">
+					</span>
+					导航
+				</button>
+				<button type="button" class="act-btn" aria-label="电话">
+					<span class="act-btn__icon">
+						<img src="images/loc2.png" alt="" width="22" height="22" decoding="async">
+					</span>
+					电话
+				</button>
+			</div>
+		</div>
+
+		<section class="marketing-section" id="shareMarketingSection" style="display:none;" aria-hidden="true">
+			<h3 class="marketing-section__title">营销活动</h3>
+			<div class="marketing-section__list" id="marketingListEl"></div>
+			<button type="button" class="marketing-section__more" id="marketingMoreBtn" style="display:none;">
+				<span id="marketingMoreText"></span>
+				<svg class="marketing-section__more-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
+			</button>
+		</section>
+
+		<section class="performance-section" id="sharePerformanceSection" style="display:none;" aria-hidden="true">
+			<h3 class="performance-section__title" id="performanceSectionTitle">演出安排(0)</h3>
+			<div class="performance-strip" id="performanceStripEl"></div>
+		</section>
+
+		<section class="staff-section" id="shareStaffSection" style="display:none;" aria-hidden="true">
+			<div class="staff-section__head">
+				<h3 class="staff-section__page-title">人员阵容(<span id="staffSectionCountEl">0</span>)</h3>
+				<button type="button" class="staff-section__all" id="staffSectionAllBtn" aria-label="查看全部">
+					查看全部
+					<svg class="staff-section__all-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="9 6 15 12 9 18"/></svg>
+				</button>
+			</div>
+			<div id="staffGroupsMount"></div>
+		</section>
+	</div>
+	<div id="shareBelowContentEl">
+		<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>
+	</div>
+
+	<script>
+	(function () {
+		'use strict';
+		/**
+		 * 与 group_user sharePopup 复制链接时携带的 query 一致,例如:
+		 * ?id=436&storeId=436&userId=628&businessSection=3&lat=...&lon=...&jingdu=...&weidu=...&storeName=...&coverUrl=...&desc=...
+		 *
+		 * 进入页先 GET .../store/info/getOne?id=(与 shareDynamic.html 一致);data.businessStatus===99 时先展示关店区(#shareClosedBanner、#shareClosedRecommendWrap)。
+		 * 店铺头图/名称/评分/地址等(#mainSection、.loc-wrap):GET .../store/info/getClientStoreDetail?id=&userId=&jingdu=&weidu=
+		 * 经纬度:URL 可用 jingdu/weidu;若无则用 lon/lng、lat(与分享链一致)
+		 * 营销活动: GET .../store/info/getStoreCouponList?storeId= → data.marketingList
+		 * 演出安排: GET .../performance/list/query?storeId=&page=&size= → data.records, data.total(仅 URL businessSection=2 时展示与请求)
+		 * 人员阵容: GET .../storeStaffConfig/queryStaffListByTitle?storeId= → data[]
+		 *
+		 * 注意:用 file:// 打开本页时,浏览器可能因 CORS 拦截跨域请求。
+		 * 请用本地 HTTP 打开(如 VS Code Live Server、npx serve)或让后端为 H5 域名配置 Access-Control-Allow-Origin。
+		 */
+		var API_BASE = 'https://test.ailien.shop/alienStore';
+
+		/**
+		 * 关店(businessStatus=99)更多推荐:POST …/ai/multimodal-services/api/v1/search/global/store-recommend
+		 * 与 shareCheckInUndefined.html 一致;请求体 page、pageSize、storeId、userCity、userLat、userLng 可由 URL 覆盖。
+		 */
+		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 DEFAULT_REC_USER_LAT = 38.925747;
+		var DEFAULT_REC_USER_LNG = 121.662531;
+		var DEFAULT_REC_USER_CITY = '大连市';
+
+		/**
+		 * 与 group_user 一致:
+		 * - manifest:app-plus.distribute.android.schemes、ios.urltypes → shopro(即 shopro://)
+		 * - Android 包名:manifest 未写 packagename 时与 App.vue 升级配置 androidPackageName 一致(云打包以 HBuilderX 为准)
+		 */
+		var APP_ANDROID_PACKAGE = 'com.alien.Udianzaizhe';
+		var APP_IOS_URL_SCHEME = 'shopro://';
+		/** businessSection 未传或其它值时的默认落地页(与 1 相同) */
+		var APP_UNI_STORE_PATH = 'pages/ieisureEntertainment/eightTypeList/delicacyDetails';
+
+		function showDownloadTip() {
+			var msg = '请到应用商店下载';
+			if (typeof uni !== 'undefined' && typeof uni.showToast === 'function') {
+				uni.showToast({ title: msg, icon: 'none', duration: 2500 });
+			} else {
+				window.alert(msg);
+			}
+		}
+
+		/** 详情页 onLoad 使用 storeId;分享链常为 id,补全 storeId 避免 App 内参数为空 */
+		function buildAppOpenQueryString() {
+			var raw = (location.search && location.search.charAt(0) === '?') ? location.search.slice(1) : (location.search || '');
+			var params = new URLSearchParams(raw);
+			var sid = params.get('storeId') || params.get('id') || '';
+			if (sid && !params.get('storeId')) params.set('storeId', sid);
+			var qs = params.toString();
+			return qs ? ('?' + qs) : '';
+		}
+
+		/**
+		 * 按 URL 参数 businessSection 打开对应 App 页:
+		 * 1 → delicacyDetails,2 → barDetails,3 → lifeDetails
+		 */
+		function getAppUniPathForBusinessSection() {
+			var bs = String(q('businessSection') || '').trim();
+			if (bs === '1') {
+				return 'pages/ieisureEntertainment/eightTypeList/delicacyDetails';
+			}
+			if (bs === '2') {
+				return 'pages/ieisureEntertainment/eightTypeList/barDetails';
+			}
+			if (bs === '3') {
+				return 'pages/ieisureEntertainment/eightTypeList/lifeDetails';
+			}
+			return String(APP_UNI_STORE_PATH || 'pages/ieisureEntertainment/eightTypeList/delicacyDetails').replace(/^\//, '');
+		}
+
+		function buildAppDeepLink() {
+			var path = getAppUniPathForBusinessSection().replace(/^\//, '');
+			var s = buildAppOpenQueryString();
+			var root = APP_IOS_URL_SCHEME.replace(/\/$/, '');
+			if (!s) return root + '/' + path;
+			return root + '/' + path + s;
+		}
+
+		function isWeChatInAppBrowser() {
+			return /MicroMessenger/i.test(navigator.userAgent || '');
+		}
+
+		/** 部分 WebView 对 location.href 拦截,用 a 标签点击略稳 */
+		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) {}
+		}
+
+		/**
+		 * 有 plus:检测 App 是否安装后 openURL;深链形如 shopro://{APP_UNI_STORE_PATH}?…
+		 * 系统浏览器:唤起 scheme;微信内常拦截 scheme,需「在浏览器打开」。
+		 */
+		function tryOpenHBuilderApp() {
+			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 shouldShowPerformanceSection() {
+			return String(q('businessSection') || '').trim() === '2';
+		}
+
+		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();
+			});
+		}
+
+		/** 进入页先拉店铺概要;与 shareDynamic.html fetchGetDeleteFlagByIdIfId 相同路径与 id 参数 */
+		function fetchStoreInfoGetOne(storeId) {
+			if (!storeId) return Promise.resolve(null);
+			var qs = new URLSearchParams();
+			qs.set('id', String(storeId).trim());
+			return apiFetch('/store/info/getOne?' + qs.toString()).catch(function (e) {
+				console.warn('[store/info/getOne]', e);
+				return null;
+			});
+		}
+
+		function isGetOneBusinessStatus99(res) {
+			if (!res || typeof res !== 'object') return false;
+			var d = res.data;
+			if (d && typeof d === 'object') {
+				var bs = d.businessStatus;
+				if (bs === 99 || bs === '99' || Number(bs) === 99) return true;
+			}
+			return false;
+		}
+
+		var MARKETING_PREVIEW_COUNT = 3;
+		var closedMerchantFlag = false;
+		var marketingListCache = [];
+		/** 避免详情先返回时用空 cache 误展示「暂无活动」图 */
+		var marketingCouponLoaded = false;
+		var staffGroupsCache = [];
+		var performanceListCache = { records: [], total: 0 };
+
+		function isMp4VideoUrl(url) {
+			if (!url || typeof url !== 'string') return false;
+			var path = url.split(/[?#]/)[0].toLowerCase();
+			return path.length >= 4 && path.slice(-4) === '.mp4';
+		}
+
+		function getMp4PlaybackUrl(url) {
+			var u = String(url || '').trim();
+			if (!u) return '';
+			var q = u.indexOf('?');
+			var h = u.indexOf('#');
+			var cut = u.length;
+			if (q >= 0) cut = Math.min(cut, q);
+			if (h >= 0) cut = Math.min(cut, h);
+			var base = u.slice(0, cut);
+			if (/\.mp4$/i.test(base)) return base;
+			return u;
+		}
+
+		function mp4UrlToJpgUrl(u) {
+			var q = u.indexOf('?');
+			var h = u.indexOf('#');
+			var cut = u.length;
+			if (q >= 0) cut = Math.min(cut, q);
+			if (h >= 0) cut = Math.min(cut, h);
+			var pathPart = u.slice(0, cut);
+			var rest = u.slice(cut);
+			if (!/\.mp4$/i.test(pathPart)) return '';
+			return pathPart.slice(0, -4) + '.jpg' + rest;
+		}
+
+		function createMarketingMedia(url) {
+			var wrap = document.createElement('div');
+			wrap.className = 'marketing-card__media';
+			var full = String(url == null ? '' : url).trim();
+			if (!full) return wrap;
+
+			if (isMp4VideoUrl(full)) {
+				var playUrl = getMp4PlaybackUrl(full);
+				var vid = document.createElement('video');
+				vid.src = playUrl;
+				vid.setAttribute('controls', '');
+				vid.setAttribute('playsinline', '');
+				vid.setAttribute('webkit-playsinline', '');
+				vid.muted = true;
+				vid.setAttribute('muted', '');
+				vid.setAttribute('autoplay', '');
+				vid.preload = 'auto';
+				var poster = '';
+				if (full.length > playUrl.length) poster = full;
+				else poster = mp4UrlToJpgUrl(playUrl);
+				if (poster) vid.setAttribute('poster', poster);
+				vid.addEventListener('canplay', function onCap() {
+					vid.removeEventListener('canplay', onCap);
+					var p = vid.play();
+					if (p && typeof p.catch === 'function') p.catch(function () {});
+				});
+				wrap.appendChild(vid);
+			} else {
+				var img = document.createElement('img');
+				img.src = full;
+				img.alt = '';
+				img.decoding = 'async';
+				wrap.appendChild(img);
+			}
+			return wrap;
+		}
+
+		function renderMarketingList(rawList) {
+			marketingListCache = Array.isArray(rawList) ? rawList.slice() : [];
+			var section = document.getElementById('shareMarketingSection');
+			var listEl = document.getElementById('marketingListEl');
+			var moreBtn = document.getElementById('marketingMoreBtn');
+			var moreText = document.getElementById('marketingMoreText');
+			if (!section || !listEl) return;
+
+			listEl.innerHTML = '';
+			section.classList.remove('is-expanded');
+			if (moreBtn) {
+				moreBtn.style.display = 'none';
+				moreBtn.setAttribute('aria-expanded', 'false');
+			}
+
+			if (closedMerchantFlag) {
+				section.style.display = 'none';
+				section.setAttribute('aria-hidden', 'true');
+				return;
+			}
+
+			var items = marketingListCache.filter(function (x) {
+				return x && x.imgUrl && String(x.imgUrl).trim();
+			}).filter(function (x) {
+				var t = x.activityType;
+				if (t == null || t === '') return true;
+				return String(t).toUpperCase() === 'MARKETING';
+			});
+			items.sort(function (a, b) {
+				var sa = Number(a.imgSort);
+				var sb = Number(b.imgSort);
+				if (isNaN(sa)) sa = 0;
+				if (isNaN(sb)) sb = 0;
+				return sa - sb;
+			});
+
+			if (!items.length) {
+				if (!marketingCouponLoaded) {
+					section.style.display = 'none';
+					section.setAttribute('aria-hidden', 'true');
+					return;
+				}
+				section.style.display = 'block';
+				section.setAttribute('aria-hidden', 'false');
+				var emptyWrap = document.createElement('div');
+				emptyWrap.className = 'marketing-section__empty';
+				emptyWrap.setAttribute('role', 'status');
+				var emptyImg = document.createElement('img');
+				emptyImg.src = 'images/empty.png';
+				emptyImg.alt = '';
+				emptyImg.decoding = 'async';
+				var emptyLabel = document.createElement('p');
+				emptyLabel.className = 'marketing-section__empty-label';
+				emptyLabel.textContent = '暂无活动';
+				emptyWrap.appendChild(emptyImg);
+				emptyWrap.appendChild(emptyLabel);
+				listEl.appendChild(emptyWrap);
+				return;
+			}
+
+			section.style.display = 'block';
+			section.setAttribute('aria-hidden', 'false');
+
+			items.forEach(function (item, idx) {
+				var card = document.createElement('article');
+				card.className = 'marketing-card';
+				if (idx >= MARKETING_PREVIEW_COUNT) card.classList.add('marketing-card--more');
+				card.appendChild(createMarketingMedia(item.imgUrl));
+				listEl.appendChild(card);
+			});
+
+			var extra = items.length - MARKETING_PREVIEW_COUNT;
+			if (extra > 0 && moreBtn && moreText) {
+				moreText.textContent = '查看剩余' + extra + '个活动';
+				moreBtn.style.display = 'flex';
+			}
+		}
+
+		function bindMarketingMore() {
+			var section = document.getElementById('shareMarketingSection');
+			var moreBtn = document.getElementById('marketingMoreBtn');
+			if (!section || !moreBtn || moreBtn.dataset.bound) return;
+			moreBtn.dataset.bound = '1';
+			moreBtn.addEventListener('click', function () {
+				section.classList.add('is-expanded');
+				moreBtn.style.display = 'none';
+				moreBtn.setAttribute('aria-expanded', 'true');
+			});
+		}
+
+		function createStaffCardElement(staff) {
+			var card = document.createElement('article');
+			card.className = 'staff-card';
+
+			var imgWrap = document.createElement('div');
+			imgWrap.className = 'staff-card__img-wrap';
+
+			var imgUrl = String(staff.staffImage || '').trim();
+			if (imgUrl) {
+				var img = document.createElement('img');
+				img.src = imgUrl;
+				img.alt = String(staff.name || '');
+				img.decoding = 'async';
+				img.loading = 'lazy';
+				imgWrap.appendChild(img);
+			}
+
+			var likes = document.createElement('div');
+			likes.className = 'staff-card__likes';
+			var lc = staff.likeCount != null ? Number(staff.likeCount) : 0;
+			if (isNaN(lc)) lc = 0;
+			likes.textContent = lc + '人喜欢';
+			imgWrap.appendChild(likes);
+
+			var nameRow = document.createElement('div');
+			nameRow.className = 'staff-card__name-row';
+			var nameEl = document.createElement('span');
+			nameEl.className = 'staff-card__name';
+			nameEl.textContent = String(staff.name != null ? staff.name : '').trim() || '—';
+			nameRow.appendChild(nameEl);
+			var perf = staff.hasPerformanceToday;
+			if (perf === true || perf === 1 || String(perf).toLowerCase() === 'true') {
+				var badge = document.createElement('span');
+				badge.className = 'staff-card__badge';
+				badge.textContent = '今日演出';
+				nameRow.appendChild(badge);
+			}
+
+			card.appendChild(imgWrap);
+			card.appendChild(nameRow);
+
+			var meta = document.createElement('div');
+			meta.className = 'staff-card__meta';
+			var pos = document.createElement('span');
+			pos.className = 'staff-card__position';
+			pos.textContent = String(staff.staffPosition || '').trim() || '\u00a0';
+			meta.appendChild(pos);
+			var pc = staff.positiveCommentsCount != null ? Number(staff.positiveCommentsCount) : 0;
+			if (!isNaN(pc) && pc > 0) {
+				var sep = document.createElement('span');
+				sep.className = 'staff-card__sep';
+				sep.textContent = '|';
+				var rev = document.createElement('span');
+				rev.className = 'staff-card__reviews';
+				rev.textContent = '好评' + pc + '次';
+				meta.appendChild(sep);
+				meta.appendChild(rev);
+			}
+			card.appendChild(meta);
+
+			return card;
+		}
+
+		function createPerformanceCardElement(row) {
+			var card = document.createElement('article');
+			card.className = 'performance-card';
+
+			var posterWrap = document.createElement('div');
+			posterWrap.className = 'performance-card__poster';
+			var posterUrl = String(row.performancePoster || '').trim();
+			if (posterUrl) {
+				var img = document.createElement('img');
+				img.src = posterUrl;
+				img.alt = String(row.performanceName || '');
+				img.decoding = 'async';
+				img.loading = 'lazy';
+				posterWrap.appendChild(img);
+			}
+
+			var body = document.createElement('div');
+			body.className = 'performance-card__body';
+
+			var nameEl = document.createElement('div');
+			nameEl.className = 'performance-card__name';
+			nameEl.textContent = String(row.performanceName != null ? row.performanceName : '').trim() || '—';
+			body.appendChild(nameEl);
+
+			var dr = String(row.dateRange || '').trim();
+			if (dr) {
+				var line1 = document.createElement('div');
+				line1.className = 'performance-card__meta';
+				line1.textContent = dr;
+				body.appendChild(line1);
+			}
+
+			var si = String(row.scheduleInfo || '').trim();
+			if (si) {
+				var line2 = document.createElement('div');
+				line2.className = 'performance-card__meta';
+				line2.textContent = si;
+				line2.title = si;
+				body.appendChild(line2);
+			}
+
+			card.appendChild(posterWrap);
+			card.appendChild(body);
+			return card;
+		}
+
+		function renderPerformanceList() {
+			var section = document.getElementById('sharePerformanceSection');
+			var titleEl = document.getElementById('performanceSectionTitle');
+			var strip = document.getElementById('performanceStripEl');
+			if (!section || !titleEl || !strip) return;
+
+			strip.innerHTML = '';
+
+			if (!shouldShowPerformanceSection()) {
+				section.style.display = 'none';
+				section.setAttribute('aria-hidden', 'true');
+				return;
+			}
+
+			if (closedMerchantFlag) {
+				section.style.display = 'none';
+				section.setAttribute('aria-hidden', 'true');
+				return;
+			}
+
+			var records = performanceListCache.records;
+			var total = performanceListCache.total;
+			if (isNaN(total)) total = records.length;
+
+			titleEl.textContent = '演出安排(' + total + ')';
+
+			if (!records.length) {
+				section.style.display = 'none';
+				section.setAttribute('aria-hidden', 'true');
+				return;
+			}
+
+			section.style.display = 'block';
+			section.setAttribute('aria-hidden', 'false');
+
+			records.forEach(function (row) {
+				if (!row) return;
+				strip.appendChild(createPerformanceCardElement(row));
+			});
+		}
+
+		function renderStaffGroups(raw) {
+			staffGroupsCache = Array.isArray(raw) ? raw.slice() : [];
+			var section = document.getElementById('shareStaffSection');
+			var mount = document.getElementById('staffGroupsMount');
+			if (!section || !mount) return;
+
+			var totalStaff = 0;
+			staffGroupsCache.forEach(function (g) {
+				if (g && Array.isArray(g.staffList)) totalStaff += g.staffList.length;
+			});
+			var countEl = document.getElementById('staffSectionCountEl');
+			if (countEl) countEl.textContent = String(totalStaff);
+
+			mount.innerHTML = '';
+
+			if (closedMerchantFlag) {
+				section.style.display = 'none';
+				section.setAttribute('aria-hidden', 'true');
+				return;
+			}
+
+			var groups = staffGroupsCache.filter(function (g) {
+				return g && Array.isArray(g.staffList) && g.staffList.length;
+			});
+
+			if (!groups.length) {
+				section.style.display = 'none';
+				section.setAttribute('aria-hidden', 'true');
+				return;
+			}
+
+			section.style.display = 'block';
+			section.setAttribute('aria-hidden', 'false');
+
+			groups.forEach(function (group) {
+				var wrap = document.createElement('div');
+				wrap.className = 'staff-group';
+				if (group.titleId != null) wrap.dataset.titleId = String(group.titleId);
+
+				var strip = document.createElement('div');
+				strip.className = 'staff-strip';
+				group.staffList.forEach(function (staff) {
+					if (!staff) return;
+					strip.appendChild(createStaffCardElement(staff));
+				});
+
+				wrap.appendChild(strip);
+				mount.appendChild(wrap);
+			});
+		}
+
+		function bindStaffSection() {
+			var btn = document.getElementById('staffSectionAllBtn');
+			if (!btn || btn.dataset.bound) return;
+			btn.dataset.bound = '1';
+			btn.addEventListener('click', function (e) {
+				e.preventDefault();
+				tryOpenHBuilderApp();
+			});
+		}
+
+		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) === 99) {
+				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 };
+		}
+
+		/**
+		 * 分项评分:可能在 storeEvaluate 对象内,或为 JSON 字符串;部分接口把字段放在详情根上。
+		 */
+		function parseStoreEvaluateRaw(raw) {
+			if (raw == null || raw === '') return null;
+			if (typeof raw === 'string') {
+				var t = raw.trim();
+				if (!t) return null;
+				try {
+					raw = JSON.parse(t);
+				} catch (e) {
+					return null;
+				}
+			}
+			if (Array.isArray(raw) && raw.length && raw[0] && typeof raw[0] === 'object') {
+				raw = raw[0];
+			}
+			if (raw && typeof raw === 'object' && !Array.isArray(raw)) return raw;
+			return null;
+		}
+
+		function getStoreEvaluateScores(d) {
+			var ev = parseStoreEvaluateRaw(d.storeEvaluate)
+				|| parseStoreEvaluateRaw(d.evaluate)
+				|| parseStoreEvaluateRaw(d.evaluateInfo);
+			function lane(key, snake) {
+				var v;
+				if (ev && ev[key] != null && ev[key] !== '') v = ev[key];
+				else if (d[key] != null && d[key] !== '') v = d[key];
+				else if (snake && d[snake] != null && d[snake] !== '') v = d[snake];
+				else return null;
+				var n = Number(v);
+				return !isNaN(n) ? n : null;
+			}
+			return {
+				scoreOne: lane('scoreOne', 'score_one'),
+				scoreTwo: lane('scoreTwo', 'score_two'),
+				scoreThree: lane('scoreThree', 'score_three')
+			};
+		}
+
+		function fmtEvalScore(n) {
+			return n != null && !isNaN(n) ? Number(n).toFixed(1) : null;
+		}
+
+		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, '&amp;')
+				.replace(/</g, '&lt;')
+				.replace(/>/g, '&gt;')
+				.replace(/"/g, '&quot;');
+		}
+
+		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 = [];
+			var i;
+			for (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 fetchShareClosedStoreRecommend(storeIdStr) {
+			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 page = parseInt(q('page') || '1', 10);
+			var pageSize = parseInt(q('pageSize') || '10', 10);
+			if (isNaN(page) || page < 1) page = 1;
+			if (isNaN(pageSize) || pageSize < 1) pageSize = 10;
+
+			var userCityRaw = (q('userCity') || q('city') || '').trim();
+			var userCity = userCityRaw !== '' ? userCityRaw : DEFAULT_REC_USER_CITY;
+
+			var body = {
+				page: page,
+				pageSize: pageSize,
+				storeId: String(storeIdStr || ''),
+				userCity: userCity,
+				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();
+			});
+		}
+
+		function renderShareClosedRecommended(list) {
+			var wrap = document.getElementById('shareClosedRecList');
+			var empty = document.getElementById('shareClosedRecEmpty');
+			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 loadShareClosedRecommendations() {
+			var sid = getStoreId();
+			if (!sid) {
+				renderShareClosedRecommended([]);
+				return;
+			}
+			fetchShareClosedStoreRecommend(sid)
+				.then(function (res) {
+					var list = normalizeClosedStoreRecommendList(res);
+					if (!list.length && res && res.msg) {
+						console.warn('[store-recommend]', res.msg);
+					}
+					renderShareClosedRecommended(list);
+				})
+				.catch(function (e) {
+					console.error(e);
+					renderShareClosedRecommended([]);
+				});
+		}
+
+		function applyShareIndexClosedMerchantUi(closed) {
+			var wasClosed = closedMerchantFlag;
+			closedMerchantFlag = closed;
+			var banner = document.getElementById('shareClosedBanner');
+			var contentEl = document.getElementById('shareContentEl');
+			var belowEl = document.getElementById('shareBelowContentEl');
+			var recWrap = document.getElementById('shareClosedRecommendWrap');
+			if (banner) {
+				banner.style.display = closed ? 'block' : 'none';
+				banner.setAttribute('aria-hidden', closed ? 'false' : 'true');
+			}
+			if (contentEl) {
+				contentEl.style.display = closed ? 'none' : '';
+				contentEl.setAttribute('aria-hidden', closed ? 'true' : 'false');
+			}
+			if (belowEl) {
+				belowEl.style.display = '';
+				belowEl.setAttribute('aria-hidden', 'false');
+			}
+			if (recWrap) {
+				if (closed) {
+					recWrap.style.display = 'block';
+					recWrap.setAttribute('aria-hidden', 'false');
+					if (!wasClosed) {
+						loadShareClosedRecommendations();
+					}
+				} else {
+					recWrap.style.display = 'none';
+					recWrap.setAttribute('aria-hidden', 'true');
+					renderShareClosedRecommended([]);
+				}
+			}
+		}
+
+		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 evRow = document.getElementById('storeEvaluateRow');
+			var evOne = document.getElementById('storeEvaluateScoreOne');
+			var evTwo = document.getElementById('storeEvaluateScoreTwo');
+			var evThree = document.getElementById('storeEvaluateScoreThree');
+			if (evRow && evOne && evTwo && evThree) {
+				evRow.classList.remove('is-visible');
+				evRow.setAttribute('aria-hidden', 'true');
+				var es = getStoreEvaluateScores(d);
+				var a = fmtEvalScore(es.scoreOne);
+				var b = fmtEvalScore(es.scoreTwo);
+				var c = fmtEvalScore(es.scoreThree);
+				if (a !== null || b !== null || c !== null) {
+					evOne.textContent = a !== null ? a : '—';
+					evTwo.textContent = b !== null ? b : '—';
+					evThree.textContent = c !== null ? c : '—';
+					evRow.classList.add('is-visible');
+					evRow.setAttribute('aria-hidden', 'false');
+				}
+			}
+
+			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);
+
+			var closedMerchant = Number(d.businessStatus) === 99;
+			applyShareIndexClosedMerchantUi(closedMerchant);
+			renderMarketingList(marketingListCache);
+			renderPerformanceList();
+			renderStaffGroups(staffGroupsCache);
+		}
+
+		function getLngForApi() {
+			var v = q('jingdu');
+			if (v) return v;
+			return q('lon') || q('lng') || q('longitude');
+		}
+		function getLatForApi() {
+			var v = q('weidu');
+			if (v) return v;
+			return q('lat') || q('latitude');
+		}
+
+		function mergeSearchAndHashParamsForRedirect() {
+			var params = new URLSearchParams();
+			function ingest(slice) {
+				if (!slice) return;
+				var p = new URLSearchParams(slice);
+				p.forEach(function (val, key) {
+					params.set(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;
+		}
+
+		/** getClientStoreDetail 无承载数据时进 shareCheckInUndefined(businessStatus=99 与落地页约定一致) */
+		function buildShareCheckInUndefinedRedirectHref() {
+			var p = mergeSearchAndHashParamsForRedirect();
+			p.set('businessStatus', '99');
+			var sid = getStoreId();
+			if (sid && !p.has('storeId')) {
+				p.set('storeId', sid);
+			}
+			var j = getLngForApi();
+			var w = getLatForApi();
+			if (j && !p.has('userLng')) {
+				p.set('userLng', j);
+			}
+			if (w && !p.has('userLat')) {
+				p.set('userLat', w);
+			}
+			var qs = p.toString();
+			return 'shareCheckInUndefined.html' + (qs ? ('?' + qs) : '?businessStatus=99');
+		}
+
+		function run() {
+			var sid = getStoreId();
+			var userId = q('userId');
+			var jingdu = getLngForApi();
+			var weidu = getLatForApi();
+
+			if (!sid) {
+				showErr('缺少店铺 id(请从 App 分享链接打开,URL 需带 id 或 storeId)');
+				document.getElementById('storeName').textContent = '参数错误';
+				return;
+			}
+
+			var detailPath = '/store/info/getClientStoreDetail?' +
+				'id=' + encodeURIComponent(sid) +
+				'&userId=' + encodeURIComponent(userId) +
+				'&jingdu=' + encodeURIComponent(jingdu) +
+				'&weidu=' + encodeURIComponent(weidu);
+
+			fetchStoreInfoGetOne(sid).then(function (getOneRes) {
+				if (isGetOneBusinessStatus99(getOneRes)) {
+					applyShareIndexClosedMerchantUi(true);
+				}
+
+				apiFetch(detailPath)
+					.then(function (res) {
+						var tip = res && res.msg != null ? String(res.msg).trim() : '';
+						if (tip === '暂无承载数据') {
+							if (closedMerchantFlag) {
+								return;
+							}
+							window.location.replace(buildShareCheckInUndefinedRedirectHref());
+							return;
+						}
+						if (res.code !== 200 || !res.data) {
+							showErr(res.msg || '店铺详情加载失败');
+							return;
+						}
+						renderDetail(res.data);
+					})
+					.catch(function (e) {
+						console.error(e);
+						showErr('店铺详情请求失败(若为 file:// 打开,请检查 CORS 或改用 http 访问本页)');
+					});
+
+				var couponPath = '/store/info/getStoreCouponList?storeId=' + encodeURIComponent(sid);
+				marketingCouponLoaded = false;
+				apiFetch(couponPath)
+					.then(function (res) {
+						marketingCouponLoaded = true;
+						var list = (res && res.code === 200 && res.data && res.data.marketingList) || [];
+						if (!Array.isArray(list)) list = [];
+						renderMarketingList(list);
+					})
+					.catch(function (e) {
+						marketingCouponLoaded = true;
+						console.warn('[getStoreCouponList]', e);
+						renderMarketingList([]);
+					});
+
+				if (shouldShowPerformanceSection()) {
+					var perfPath = '/performance/list/query?storeId=' + encodeURIComponent(sid) + '&page=1&size=100';
+					apiFetch(perfPath)
+						.then(function (res) {
+							var d = (res && res.code === 200 && res.data) || null;
+							if (!d || typeof d !== 'object') {
+								performanceListCache.records = [];
+								performanceListCache.total = 0;
+							} else {
+								performanceListCache.records = Array.isArray(d.records) ? d.records.slice() : [];
+								var pt = d.total;
+								performanceListCache.total = pt != null && !isNaN(Number(pt))
+									? Number(pt)
+									: performanceListCache.records.length;
+							}
+							renderPerformanceList();
+						})
+						.catch(function (e) {
+							console.warn('[performance/list/query]', e);
+							performanceListCache.records = [];
+							performanceListCache.total = 0;
+							renderPerformanceList();
+						});
+				} else {
+					performanceListCache.records = [];
+					performanceListCache.total = 0;
+					renderPerformanceList();
+				}
+
+				var staffPath = '/storeStaffConfig/queryStaffListByTitle?storeId=' + encodeURIComponent(sid);
+				apiFetch(staffPath)
+					.then(function (res) {
+						var arr = (res && res.code === 200 && res.data) || [];
+						if (!Array.isArray(arr)) arr = [];
+						renderStaffGroups(arr);
+					})
+					.catch(function (e) {
+						console.warn('[queryStaffListByTitle]', e);
+						renderStaffGroups([]);
+					});
+			});
+		}
+
+		function boot() {
+			run();
+			bindMarketingMore();
+			bindStaffSection();
+			var openBtn = document.getElementById('openApp');
+			if (openBtn) {
+				openBtn.addEventListener('click', function () {
+					tryOpenHBuilderApp();
+				});
+			}
+		}
+
+		if (document.readyState === 'complete') {
+			boot();
+		} else {
+			window.onload = boot;
+		}
+	})();
+	</script>
+</body>
+</html>

+ 1153 - 0
HBuilderProjects/shareUndefined.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>

+ 107 - 0
index.html

@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>门店接口测试页面</title>
+    <style>
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+        }
+        body {
+            padding: 20px;
+            font-family: Arial, sans-serif;
+            max-width: 800px;
+            margin: 0 auto;
+        }
+        .title {
+            text-align: center;
+            margin: 20px 0;
+            color: #333;
+        }
+        .btn-box {
+            display: flex;
+            gap: 10px;
+            margin-bottom: 20px;
+            flex-wrap: wrap;
+        }
+        button {
+            padding: 12px 20px;
+            background: #409eff;
+            color: white;
+            border: none;
+            border-radius: 6px;
+            cursor: pointer;
+            font-size: 16px;
+            flex: 1;
+            min-width: 150px;
+        }
+        button:active {
+            background: #337ecc;
+        }
+        .result {
+            padding: 15px;
+            background: #f5f7fa;
+            border-radius: 8px;
+            min-height: 100px;
+            white-space: pre-wrap;
+            word-break: break-all;
+            font-size: 14px;
+            line-height: 1.6;
+        }
+        .tip {
+            color: #666;
+            margin: 10px 0;
+            font-size: 12px;
+        }
+    </style>
+</head>
+<body>
+    <h2 class="title">门店接口测试(无Token,纯前端调用)</h2>
+    <p class="tip">✅ 页面不携带任何Token,彻底解决401问题</p>
+
+    <div class="btn-box">
+        <button onclick="getMoreStores()">获取推荐门店列表</button>
+        <button onclick="getStoreDetail()">获取门店详情</button>
+    </div>
+
+    <div class="result" id="result">请求结果将展示在这里...</div>
+
+<script>
+// 接口基础地址
+const baseUrl = 'http://120.26.186.130:8000';
+
+// 1. 获取更多推荐门店
+async function getMoreStores() {
+    const resultDom = document.getElementById('result');
+    resultDom.textContent = '加载中...';
+    
+    try {
+        // 核心:无Token、无请求头,纯GET请求
+        const res = await fetch(`${baseUrl}/alienStore/store/info/getMoreRecommendedStores?lat=38.925771&lon=121.662472&businessSection=3`);
+        const data = await res.json();
+        resultDom.textContent = JSON.stringify(data, null, 2);
+    } catch (err) {
+        resultDom.textContent = '请求失败:' + err.message;
+    }
+}
+
+// 2. 获取门店详情
+async function getStoreDetail() {
+    const resultDom = document.getElementById('result');
+    resultDom.textContent = '加载中...';
+    
+    try {
+        // 核心:无Token、无请求头,纯GET请求
+        const res = await fetch(`${baseUrl}/alienStore/store/info/getClientStoreDetail?id=436&userId=628&jingdu=121.6574543718373&weidu=38.92492394348767`);
+        const data = await res.json();
+        resultDom.textContent = JSON.stringify(data, null, 2);
+    } catch (err) {
+        resultDom.textContent = '请求失败:' + err.message;
+    }
+}
+</script>
+</body>
+</html>

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است