You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

860 lines
19 KiB

  1. <template>
  2. <view class="deepMate-page">
  3. <!-- 顶部导航栏 - 固定定位 -->
  4. <view class="header" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
  5. <view class="header-left">
  6. <image
  7. src="https://d31zlh4on95l9h.cloudfront.net/images/f91e09b5987802185e7679055dafd272.svg"
  8. class="icon"
  9. ></image>
  10. </view>
  11. <view class="header-center">
  12. <text class="title">DeepMate</text>
  13. </view>
  14. <view class="header-right">
  15. <image
  16. src="https://d31zlh4on95l9h.cloudfront.net/images/d7c4e74201213a25dd9574e908233928.svg"
  17. class="icon"
  18. ></image>
  19. <image
  20. src="https://d31zlh4on95l9h.cloudfront.net/images/099903c4aabf5713488b5cb60815e3f7.svg"
  21. class="icon"
  22. ></image>
  23. <!-- 新增新会话按钮
  24. <button class="new-chat-button" @click="newChat">
  25. <text class="new-chat-text">新会话</text>
  26. </button> -->
  27. </view>
  28. </view>
  29. <!-- 主要内容区域 -->
  30. <view class="main-content">
  31. <!-- 顶部固定区域占位符 -->
  32. <view class="banner-placeholder"></view>
  33. <view class="banner-panel"
  34. :class="messages.length === 0 ? '' : 'panelShow'"
  35. >
  36. <image
  37. src="https://d31zlh4on95l9h.cloudfront.net/images/42e18bd7fe97d4f4f37aa70439a0990b.svg"
  38. class="pray-banner"
  39. :class="messages.length === 0 ? '' : 'show'"
  40. mode="aspectFill"
  41. ></image>
  42. <view class="contain">
  43. <!-- 机器人头像和欢迎语 -->
  44. <view class="robot-container">
  45. <image
  46. src="https://d31zlh4on95l9h.cloudfront.net/images/61fa384381c88ad80be28f41827fe0e5.svg"
  47. class="robot-avatar"
  48. ></image>
  49. <view class="welcome-message">
  50. <text class="greeting">Hi, 我是您的股市随身顾问~</text>
  51. <text class="description"
  52. >个股诊断市场情绪解读都可以找我</text
  53. >
  54. </view>
  55. </view>
  56. <!-- 功能标签栏 -->
  57. <view
  58. class="function-tabs"
  59. v-if="messages.length === 0"
  60. scroll-x="true"
  61. show-scrollbar="false"
  62. >
  63. <view class="tab-item">个股诊断</view>
  64. <view class="tab-item">市场情绪温度计</view>
  65. <view class="tab-item">买卖时机提示</view>
  66. <view class="tab-item">个股</view>
  67. </view>
  68. <!-- 特斯拉推荐卡片 -->
  69. <view class="recommend-card" v-if="messages.length === 0">
  70. <view class="arrow" v-if="messages.length === 0"></view>
  71. <view class="card-content">
  72. <image
  73. src="../../static/images/tesla-logo.png"
  74. class="logo"
  75. ></image>
  76. <view class="card-text">
  77. <text class="main-question">当前特斯拉该如何布局</text>
  78. <text class="stock-code">TSLA</text>
  79. </view>
  80. <image
  81. src="https://d31zlh4on95l9h.cloudfront.net/images/40d94054644f6e3f1c366751f07f0010.svg"
  82. class="arrow-icon"
  83. @click="goBlank"
  84. ></image>
  85. </view>
  86. </view>
  87. </view>
  88. </view>
  89. <!-- 可能感兴趣的话题 -->
  90. <view v-if="messages.length === 0" class="interest-section">
  91. <text class="section-title">- 您可能感兴趣 -</text>
  92. <view class="topics-list">
  93. <view class="topic-item" v-for="topic in hotTopics" :key="topic.id">
  94. <image :src="topic.icon" class="tag-icon"></image>
  95. <text class="topic-text" @click="sendMessageList(topic.text)">{{
  96. topic.text
  97. }}</text>
  98. </view>
  99. </view>
  100. </view>
  101. <!-- 聊天区域 -->
  102. <view class="chat-container" v-if="messages.length > 0">
  103. <!-- 给聊天容器添加滚动引用 -->
  104. <scroll-view class="chat-scroll-view" scroll-y="true" :scroll-top="scrollTop">
  105. <view class="message-list" id="messageList">
  106. <view
  107. v-for="(message, index) in messages"
  108. :key="index"
  109. :class="
  110. message.isUser ? 'message user-message' : 'message bot-message'
  111. "
  112. >
  113. <!-- 会话图标 -->
  114. <text
  115. :class="
  116. message.isUser
  117. ? 'fa-solid fa-user message-icon'
  118. : 'fa-solid fa-robot message-icon'
  119. "
  120. ></text>
  121. <!-- 会话内容 -->
  122. <view class="message-content">
  123. <text class="message-text">{{ message.content }}</text>
  124. <!-- loading -->
  125. <view
  126. class="loading-dots"
  127. v-if="message.isThinking "
  128. >
  129. <text class="dot"></text>
  130. <text class="dot"></text>
  131. <text class="dot"></text>
  132. </view>
  133. </view>
  134. </view>
  135. </view>
  136. </scroll-view>
  137. </view>
  138. </view>
  139. <!-- 输入框区域 -->
  140. <view class="input-area">
  141. <view class="input-wrapper">
  142. <input
  143. type="text"
  144. placeholder="请输入股票代码/名称,获取AI洞察"
  145. placeholder-style="color:#fff;opacity:1"
  146. class="input-field"
  147. v-model="inputMessage"
  148. @confirm="sendMessage"
  149. />
  150. <image class="send-button" @click="sendMessage" :disabled="isSending">
  151. <!-- <image
  152. src="https://d31zlh4on95l9h.cloudfront.net/images/95f1ea2262e9157db13c93c0dc1c5d96.svg"
  153. class="send-icon"
  154. ></image> -->
  155. </image>
  156. </view>
  157. <text class="disclaimer"
  158. >以上数据由AI生成不作为最终投资建议决策需独立</text
  159. >
  160. </view>
  161. <image
  162. class="back-to-top"
  163. src="https://d31zlh4on95l9h.cloudfront.net/images/ba357635d2bb480241952bb1cabacd73.svg"
  164. @click="scrollToTop"
  165. ></image>
  166. <footerBar class="static-footer" :type="type"></footerBar>
  167. </view>
  168. </template>
  169. <script setup>
  170. const { safeAreaInsets } = uni.getSystemInfoSync();
  171. import { ref, onMounted, nextTick, watch } from "vue";
  172. import footerBar from "../../components/footerBar-cn.vue";
  173. const type = ref('member')
  174. const inputMessage = ref("");
  175. const isSending = ref(false);
  176. const uuid = ref("");
  177. const messages = ref([]);
  178. const scrollTop = ref(0); // 用于控制scroll-view的滚动位置
  179. const hotTopics = ref([
  180. {
  181. id: 1,
  182. text: "英伟达(NVDA)股票情绪温度?",
  183. icon: "https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg",
  184. },
  185. {
  186. id: 2,
  187. text: "博通(AVGO)明天还能涨吗?",
  188. icon: "https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg",
  189. },
  190. {
  191. id: 3,
  192. text: "为什么Fluence Energy(FLNC)会暴涨?",
  193. icon: "https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg",
  194. },
  195. {
  196. id: 4,
  197. text: "为什么Fluence Energy(FLNC)会暴涨?",
  198. icon: "https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg",
  199. },
  200. ]);
  201. // 初始化
  202. onMounted(() => {
  203. initUUID();
  204. // 如果有历史消息,滚动到底部
  205. if (messages.value.length > 0) {
  206. setTimeout(() => {
  207. scrollToBottom();
  208. }, 200);
  209. }
  210. });
  211. // 监听消息变化,当有新消息时自动滚动到最新消息
  212. watch(messages, (newMessages) => {
  213. // 延迟执行滚动,确保DOM更新完成
  214. setTimeout(() => {
  215. scrollToBottom();
  216. }, 100);
  217. }, { deep: true });
  218. // 初始化 UUID
  219. const initUUID = () => {
  220. let storedUUID = uni.getStorageSync("user_uuid");
  221. if (!storedUUID) {
  222. storedUUID = generateUUID();
  223. uni.setStorageSync("user_uuid", storedUUID);
  224. }
  225. uuid.value = storedUUID;
  226. };
  227. // 生成简单UUID
  228. const generateUUID = () => {
  229. return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
  230. var r = (Math.random() * 16) | 0,
  231. v = c == "x" ? r : (r & 0x3) | 0x8;
  232. return v.toString(16);
  233. });
  234. };
  235. // 新会话
  236. const newChat = () => {
  237. messages.value = [];
  238. uni.removeStorageSync("user_uuid");
  239. initUUID();
  240. };
  241. // 跳转到空白页
  242. const goBlank = () => {
  243. uni.navigateTo({
  244. url: "/pages/blank/blank",
  245. });
  246. };
  247. // 发送消息
  248. const sendMessage = () => {
  249. if (inputMessage.value.trim() === "" || isSending.value) return;
  250. const userMessage = {
  251. content: inputMessage.value,
  252. isUser: true,
  253. isThinking: false,
  254. isTyping: false,
  255. };
  256. messages.value.push(userMessage);
  257. inputMessage.value = "";
  258. // 滚动到底部
  259. setTimeout(() => {
  260. scrollToBottom();
  261. }, 100);
  262. // 模拟机器人回复
  263. simulateBotResponse(userMessage.content);
  264. };
  265. // 发送消息
  266. const sendMessageList = (listMessage) => {
  267. console.log(listMessage);
  268. const userMessage = {
  269. content: listMessage,
  270. isUser: true,
  271. isThinking: false,
  272. isTyping: false,
  273. };
  274. messages.value.push(userMessage);
  275. inputMessage.value = "";
  276. // 滚动到底部
  277. setTimeout(() => {
  278. scrollToBottom();
  279. }, 100);
  280. // 模拟机器人回复
  281. simulateBotResponse(userMessage.content);
  282. };
  283. // 模拟机器人回复
  284. const simulateBotResponse = (userMessage) => {
  285. isSending.value = true;
  286. // 添加机器人加载消息
  287. const botMsg = {
  288. content: "",
  289. isUser: false,
  290. isTyping: true,
  291. isThinking: false,
  292. };
  293. messages.value.push(botMsg);
  294. // 滚动到底部
  295. setTimeout(() => {
  296. scrollToBottom();
  297. }, 100);
  298. // 模拟流式响应
  299. let responseText = `我已经收到您的消息: "${userMessage}"。作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?`;
  300. let index = 0;
  301. const typeWriter = () => {
  302. if (index < responseText.length) {
  303. // 使用 Vue 的响应式更新机制
  304. messages.value[messages.value.length - 1].content = responseText.substring(0, index + 1);
  305. index++;
  306. // 滚动到底部,更频繁地触发滚动以适应文本增长
  307. setTimeout(() => {
  308. scrollToBottom();
  309. }, 20);
  310. setTimeout(typeWriter, 30);
  311. } else {
  312. messages.value[messages.value.length - 1].isTyping = false;
  313. isSending.value = false;
  314. // 最后确保滚动到底部
  315. setTimeout(() => {
  316. scrollToBottom();
  317. }, 100);
  318. }
  319. };
  320. setTimeout(typeWriter, 500);
  321. };
  322. // 滚动到底部
  323. const scrollToBottom = () => {
  324. // 使用scroll-view的scrollTop属性来控制滚动
  325. const query = uni.createSelectorQuery();
  326. query.select("#messageList").boundingClientRect();
  327. query.exec((res) => {
  328. if (res[0]) {
  329. // 设置scrollTop为消息列表的高度,实现滚动到底部
  330. scrollTop.value = res[0].height;
  331. }
  332. });
  333. };
  334. const scrollToTop = () => {
  335. // 滚动到顶部
  336. scrollTop.value = 0;
  337. };
  338. </script>
  339. <style scoped>
  340. .deepMate-page {
  341. display: flex;
  342. flex-direction: column;
  343. height: 100vh;
  344. max-height: 100vh; /* 限制最大高度 */
  345. background-color: #ffffff;
  346. padding: 0; /* 移除padding,避免影响布局 */
  347. position: relative;
  348. overflow: hidden; /* 禁止页面整体滚动 */
  349. -webkit-overflow-scrolling: none; /* 禁用iOS弹性滚动 */
  350. }
  351. /* 顶部导航栏 - 固定定位 */
  352. .header {
  353. display: flex;
  354. justify-content: space-between;
  355. align-items: center;
  356. padding: 20rpx 30rpx;
  357. background-color: #ffffff;
  358. position: fixed;
  359. top: 0;
  360. left: 0;
  361. right: 0;
  362. z-index: 999;
  363. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
  364. }
  365. /* 顶部固定区域占位符 */
  366. .header-placeholder {
  367. height: 120rpx; /* 与header高度一致 */
  368. }
  369. /* 顶部固定区域占位符 */
  370. .banner-placeholder {
  371. height: 80rpx; /* 与header高度一致,防止内容被固定头部遮挡 */
  372. }
  373. .header-left,
  374. .header-right {
  375. display: flex;
  376. align-items: center;
  377. }
  378. .header-left .icon,
  379. .header-right .icon {
  380. width: 40rpx;
  381. height: 40rpx;
  382. margin-right: 20rpx;
  383. }
  384. .header-center .title {
  385. font-size: 36rpx;
  386. font-weight: bold;
  387. color: #333333;
  388. }
  389. .new-chat-button {
  390. background-color: #ff6600;
  391. border: none;
  392. border-radius: 8rpx;
  393. padding: 10rpx 20rpx;
  394. }
  395. .new-chat-text {
  396. color: white;
  397. font-size: 24rpx;
  398. }
  399. .main-content {
  400. flex: 1;
  401. padding: 20rpx;
  402. overflow-y: hidden; /* 禁止主内容区域滚动 */
  403. margin-top: 20rpx;
  404. margin-bottom: 250rpx; /* 为底部输入区域留出空间 */
  405. max-height: calc(100vh - 200rpx); /* 确保内容区域不会超出视口 */
  406. -webkit-overflow-scrolling: none; /* 禁用iOS弹性滚动 */
  407. }
  408. .robot-container {
  409. display: flex;
  410. align-items: center;
  411. margin-bottom: 30rpx;
  412. }
  413. .robot-avatar {
  414. width: 130rpx;
  415. height: 130rpx;
  416. border-radius: 50%;
  417. margin-right: 10rpx;
  418. }
  419. .welcome-message {
  420. flex: 1;
  421. }
  422. .greeting {
  423. font-size: 32rpx;
  424. margin-left: 50rpx;
  425. top: 40rpx;
  426. font-weight: bold;
  427. color: #333333;
  428. line-height: 48rpx;
  429. }
  430. .description {
  431. display: block;
  432. font-size: 24rpx;
  433. color: #666666;
  434. line-height: 36rpx;
  435. margin-top: 10rpx;
  436. margin-left: 45rpx;
  437. }
  438. .function-tabs {
  439. display: flex;
  440. margin-bottom: 30rpx;
  441. }
  442. .tab-item {
  443. padding: 5rpx 20rpx;
  444. border-radius: 20rpx;
  445. font-size: 20rpx;
  446. font-weight: 700;
  447. color: #666666;
  448. background-color: #fffefe;
  449. margin-right: 20rpx;
  450. transition: all 0.3s;
  451. }
  452. .tab-item.active {
  453. color: #ff6600;
  454. background-color: #fff;
  455. border: 1rpx solid #ff6600;
  456. }
  457. .recommend-card {
  458. background: url("https://d31zlh4on95l9h.cloudfront.net/images/4da1d629a55c307c3605ca15bf15189a.svg");
  459. background-repeat: no-repeat;
  460. /* border-radius: 20rpx; */
  461. padding: 40rpx;
  462. margin-bottom: 30rpx;
  463. /* box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); */
  464. }
  465. .card-content {
  466. display: flex;
  467. align-items: center;
  468. justify-content: space-between;
  469. }
  470. .logo {
  471. width: 80rpx;
  472. height: 80rpx;
  473. background-color: #ff0000;
  474. border-radius: 10rpx;
  475. display: flex;
  476. align-items: center;
  477. justify-content: center;
  478. margin-right: 20rpx;
  479. }
  480. .card-text {
  481. flex: 1;
  482. margin-left: 20rpx;
  483. }
  484. .main-question {
  485. font-size: 32rpx;
  486. color: #333333;
  487. line-height: 48rpx;
  488. }
  489. .stock-code {
  490. display: block;
  491. font-size: 24rpx;
  492. color: #ff3b30;
  493. background-color: #ffffff;
  494. padding: 2rpx 15rpx;
  495. border-radius: 12rpx;
  496. margin-top: 8rpx;
  497. width: fit-content;
  498. border: 1rpx solid #ff3b30;
  499. }
  500. .arrow-icon {
  501. background: url("https://d31zlh4on95l9h.cloudfront.net/images/40d94054644f6e3f1c366751f07f0010.svg");
  502. background-repeat: no-repeat;
  503. left: 0.5rem;
  504. top: 1.8rem;
  505. background-size: 100% 100%;
  506. width: 60rpx;
  507. height: 60rpx;
  508. }
  509. .interest-section {
  510. margin-bottom: 30rpx;
  511. }
  512. .section-title {
  513. display: block;
  514. text-align: center;
  515. font-size: 26rpx;
  516. color: #666666;
  517. margin-bottom: 20rpx;
  518. }
  519. .topics-list {
  520. display: flex;
  521. flex-direction: column;
  522. gap: 15rpx;
  523. }
  524. .topic-item {
  525. display: flex;
  526. align-items: center;
  527. padding: 15rpx 20rpx;
  528. background-color: #f0f0f0;
  529. border-radius: 15rpx;
  530. width: fit-content;
  531. }
  532. .tag-icon {
  533. width: 24rpx;
  534. height: 24rpx;
  535. margin-right: 10rpx;
  536. }
  537. .topic-text {
  538. font-size: 28rpx;
  539. color: #333333;
  540. flex: 1;
  541. }
  542. /* 聊天区域样式 */
  543. .chat-container {
  544. margin-top: 30rpx;
  545. border-radius: 10rpx;
  546. height: fit-content;
  547. /* overflow-y: auto; */
  548. }
  549. .chat-scroll-view {
  550. height: calc(80vh - 250rpx); /* 根据需要调整高度 */
  551. margin-top: 120rpx;
  552. }
  553. .message-list {
  554. /* padding: 20rpx; */
  555. /* margin-top: 200rpx; */
  556. }
  557. .message {
  558. display: flex;
  559. align-items: flex-start;
  560. margin-bottom: 30rpx;
  561. }
  562. .user-message {
  563. flex-direction: row-reverse;
  564. }
  565. .message-icon {
  566. font-size: 24rpx;
  567. margin: 0 10rpx;
  568. padding: 10rpx;
  569. border-radius: 50%;
  570. background-color: #ddd;
  571. width: 40rpx;
  572. height: 40rpx;
  573. display: flex;
  574. align-items: center;
  575. justify-content: center;
  576. }
  577. .user-message .message-icon {
  578. background-color: #007aff;
  579. color: white;
  580. }
  581. .bot-message .message-icon {
  582. background-color: #34c759;
  583. color: white;
  584. }
  585. .message-content {
  586. max-width: 70%;
  587. position: relative;
  588. }
  589. .user-message .message-content {
  590. background-color: #007aff;
  591. border-radius: 10rpx;
  592. padding: 15rpx;
  593. }
  594. .bot-message .message-content {
  595. background-color: #f0f0f0;
  596. border-radius: 10rpx;
  597. padding: 15rpx;
  598. }
  599. .message-text {
  600. font-size: 28rpx;
  601. line-height: 40rpx;
  602. }
  603. .user-message .message-text {
  604. color: white;
  605. }
  606. .bot-message .message-text {
  607. color: #333;
  608. }
  609. .loading-dots {
  610. display: flex;
  611. align-items: center;
  612. padding-top: 10rpx;
  613. }
  614. .dot {
  615. width: 10rpx;
  616. height: 10rpx;
  617. background-color: #666;
  618. border-radius: 50%;
  619. margin: 0 4rpx;
  620. animation: loading 1.4s infinite ease-in-out both;
  621. }
  622. .user-message .dot {
  623. background-color: white;
  624. }
  625. .dot:nth-child(1) {
  626. animation-delay: -0.32s;
  627. }
  628. .dot:nth-child(2) {
  629. animation-delay: -0.16s;
  630. }
  631. @keyframes loading {
  632. 0%,
  633. 80%,
  634. 100% {
  635. transform: scale(0);
  636. }
  637. 40% {
  638. transform: scale(1);
  639. }
  640. }
  641. .input-area {
  642. position: fixed;
  643. bottom: 70rpx;
  644. left: 0;
  645. right: 0;
  646. padding: 0 40rpx 80rpx 40rpx;
  647. background-color: #ffffff;
  648. z-index: 999;
  649. -webkit-overflow-scrolling: none; /* 禁用iOS弹性滚动 */
  650. }
  651. .input-wrapper {
  652. position: relative;
  653. display: flex;
  654. align-items: center;
  655. padding: 15rpx 20rpx;
  656. background-color: rgb(220, 31, 29);
  657. border-radius: 100rpx;
  658. display: flex;
  659. align-items: center;
  660. justify-content: center;
  661. height: 50rpx;
  662. }
  663. .mic-icon {
  664. width: 36rpx;
  665. height: 36rpx;
  666. margin-right: 20rpx;
  667. }
  668. .input-field {
  669. flex: 1;
  670. font-size: 28rpx;
  671. color: #fff;
  672. display: flex;
  673. align-items: center;
  674. justify-content: center;
  675. margin-left: 60rpx;
  676. background: none;
  677. border: none;
  678. outline: none;
  679. }
  680. .input-field::placeholder {
  681. color: #ffffff !important;
  682. opacity: 1;
  683. }
  684. .send-button {
  685. background: url("https://d31zlh4on95l9h.cloudfront.net/images/95f1ea2262e9157db13c93c0dc1c5d96.svg");
  686. background-repeat: no-repeat;
  687. background-size: 100% 100%;
  688. height: 50rpx;
  689. width: 50rpx;
  690. padding: 0;
  691. border: 1rpx solid transparent;
  692. margin-left: 20rpx;
  693. }
  694. .send-icon {
  695. width: 36rpx;
  696. height: 36rpx;
  697. }
  698. .disclaimer {
  699. font-size: 15rpx;
  700. color: #4d4c4c;
  701. display: flex;
  702. align-items: center;
  703. justify-content: center;
  704. margin-top: 15rpx;
  705. }
  706. .banner-panel {
  707. position: relative;
  708. height: 480rpx; /* 拉长容器,灰色背景跟随变高 */
  709. overflow: hidden; /* 让圆角和内部层剪裁一致 */
  710. border-radius: 15rpx;
  711. }
  712. .panelShow{
  713. height: 12%;
  714. position: fixed;
  715. top: 70rpx;
  716. z-index: 999;
  717. width: 95%;
  718. }
  719. .pray-banner {
  720. position: absolute;
  721. /* background-size: 100% 100%; */
  722. inset: 0; /* 顶部、底部、左、右都贴合容器 */
  723. width: 100%;
  724. height: 88%;
  725. border-radius: 15rpx;
  726. z-index: 1; /* 在灰底之上、内容之下 */
  727. }
  728. .contain {
  729. margin: 0 20rpx;
  730. gap: 5rpx;
  731. }
  732. .banner-panel .robot-container,
  733. .banner-panel .function-tabs,
  734. .banner-panel .recommend-card {
  735. position: relative;
  736. z-index: 2;
  737. }
  738. .back-to-top {
  739. position: fixed;
  740. right: 30rpx;
  741. bottom: 35%;
  742. width: 100rpx;
  743. height: 100rpx;
  744. z-index: 1000;
  745. }
  746. .back-to-top:active {
  747. transform: scale(0.96);
  748. }
  749. .static-footer {
  750. position: fixed;
  751. bottom: 0;
  752. z-index: 999;
  753. }
  754. /* 顶部固定区域占位符 */
  755. .banner-placeholder {
  756. height: 120rpx; /* 与header高度一致,防止内容被固定头部遮挡 */
  757. }
  758. </style>