indexNew.vue 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454
  1. <template>
  2. <view class="container">
  3. <!-- 固定顶部导航栏 -->
  4. <view class="fixed-header" :class="{'show-fixed': showFixed}">
  5. <view class="status_bar" :style="{height: statusBarHeight + 'px'}">
  6. <view class="top_view"></view>
  7. </view>
  8. <view class="nav-content">
  9. <view class="left-section">
  10. <view class="logo-container">
  11. <image class="logo-image"
  12. src="https://ndtk.tos-cn-guangzhou.volces.com/uploads/156adea827104e38ae0e25ec4701ecfe.jpg"
  13. mode="aspectFit"></image>
  14. <text class="logo-text">宏匠</text>
  15. </view>
  16. </view>
  17. <view class="right-section">
  18. <view class="fixed-search-bar" @click="goSearch">
  19. <view class="search-icon-wrapper">
  20. <image class="search-icon" src="/static/images/search.png"></image>
  21. </view>
  22. <view class="search-input">搜索</view>
  23. </view>
  24. </view>
  25. </view>
  26. </view>
  27. <!-- 原始顶部导航栏 -->
  28. <view class="header">
  29. <view class="status_bar" :style="{height: statusBarHeight + 'px'}">
  30. <view class="top_view"></view>
  31. </view>
  32. <view class="header-content">
  33. <view class="logo-section">
  34. <view class="logo-container">
  35. <image class="logo-image"
  36. src="https://ndtk.tos-cn-guangzhou.volces.com/uploads/156adea827104e38ae0e25ec4701ecfe.jpg"
  37. mode="aspectFit"></image>
  38. <text class="logo-text">宏匠</text>
  39. </view>
  40. <view class="sub-title">传承千年唐卡艺术</view>
  41. </view>
  42. <view class="search-bar" @click="goSearch">
  43. <view class="search-icon-wrapper">
  44. <image class="search-icon" src="/static/images/search.png"></image>
  45. </view>
  46. <view class="search-input">寻找心仪的唐卡</view>
  47. </view>
  48. </view>
  49. </view>
  50. <!-- 轮播图 -->
  51. <view class="swiper-section">
  52. <view class="swiper-container">
  53. <swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="4000" :duration="800"
  54. indicator-color="rgba(139,69,19,0.3)" indicator-active-color="#8B4513">
  55. <swiper-item v-for="(item, index) in bannerList" :key="index" @click="handleBannerClick(item)">
  56. <image class="swiper-image" :src="item.image" mode="aspectFill"></image>
  57. </swiper-item>
  58. </swiper>
  59. </view>
  60. </view>
  61. <!-- 爆款产品 -->
  62. <view class="hot-section">
  63. <view class="hot-header">
  64. <text class="hot-title">🔥 爆款唐卡</text>
  65. <view class="hot-decoration"></view>
  66. </view>
  67. <view class="hot-grid">
  68. <view class="hot-item" v-for="(item, index) in menuList" :key="index" @click="goCategory(item)">
  69. <view class="hot-icon-wrapper">
  70. <image class="hot-icon" :src="item.image" mode="aspectFit"></image>
  71. <view class="hot-badge">热</view>
  72. <view class="hot-glow"></view>
  73. </view>
  74. <text class="hot-name">{{item.title}}</text>
  75. </view>
  76. </view>
  77. </view>
  78. <!-- 成为代理商品 -->
  79. <view class="product-section agent-section">
  80. <view class="section-header">
  81. <view class="section-title">
  82. <text class="title-text">代理专享</text>
  83. <view class="title-decoration">
  84. <view class="decoration-line"></view>
  85. <image class="decoration-symbol" src="/static/images/zhuwang.png" mode="aspectFit"></image>
  86. <view class="decoration-line"></view>
  87. </view>
  88. </view>
  89. <view class="section-subtitle">传承千年工艺,开启艺术创业之路</view>
  90. </view>
  91. <view class="product-list">
  92. <view class="product-item agent-item" v-for="(item, index) in agentProducts" :key="index"
  93. @click="goDetail(item.id)">
  94. <view class="product-image-container">
  95. <image class="product-image" :src="item.images" mode="aspectFill"></image>
  96. <view class="agent-badge">代理专享</view>
  97. </view>
  98. <view class="product-content">
  99. <view class="product-name">{{item.name}}</view>
  100. <view class="product-meta">
  101. <view class="update-time">{{item.spec}}</view>
  102. <view class="sold-count">已售{{item.soldCount}}件</view>
  103. </view>
  104. <view class="product-footer">
  105. <view class="price-section">
  106. <text class="price-symbol">¥</text>
  107. <text class="price-value">{{item.price}}</text>
  108. </view>
  109. <view class="buy-btn agent-btn" @click.stop="goDetail(item.id)">
  110. <text class="buy-text">申请代理</text>
  111. </view>
  112. </view>
  113. </view>
  114. </view>
  115. </view>
  116. </view>
  117. <!-- 精选商品 -->
  118. <view class="product-section selected-section">
  119. <view class="section-header">
  120. <view class="section-title">
  121. <text class="title-text">精选唐卡</text>
  122. <view class="title-decoration">
  123. <view class="decoration-line"></view>
  124. <image class="decoration-symbol" src="/static/images/zhuwang.png" mode="aspectFit"></image>
  125. <view class="decoration-line"></view>
  126. </view>
  127. </view>
  128. <view class="section-subtitle">匠心之作,艺术臻品</view>
  129. </view>
  130. <view class="selected-product-grid">
  131. <view class="selected-product-card" v-for="(item, index) in selectedProducts" :key="index"
  132. @click="goDetail(item.id)">
  133. <view class="selected-product-image-wrapper">
  134. <image class="selected-product-image" :src="item.images" mode="aspectFill"></image>
  135. <view class="selected-product-badge" v-if="item.soldCount > 100">热销</view>
  136. </view>
  137. <view class="selected-product-info">
  138. <view class="selected-product-name">{{item.name}}</view>
  139. <view class="selected-product-desc">{{item.spec}}</view>
  140. <view class="selected-product-meta">
  141. <view class="rating">
  142. <text class="rating-star">★</text>
  143. <text class="rating-value">{{ item.productAvg.toFixed(1) }}</text>
  144. </view>
  145. <view class="selected-sold-count">已售{{item.soldCount}}件</view>
  146. </view>
  147. <view class="selected-product-footer">
  148. <view class="selected-price-section">
  149. <text class="selected-price-symbol">¥</text>
  150. <text class="selected-price-value">{{item.price}}</text>
  151. </view>
  152. <view class="selected-buy-btn" @click.stop="goDetail(item.id)">
  153. <text class="selected-buy-text">立即购买</text>
  154. </view>
  155. </view>
  156. </view>
  157. </view>
  158. </view>
  159. <!-- 加载更多按钮 -->
  160. <view class="load-more-section" v-if="selectedHasMore && selectedProducts.length > 0">
  161. <view class="load-more-btn" @click="loadMoreSelectedProducts" :class="{'loading': isLoading}">
  162. <text v-if="!isLoading">查看更多</text>
  163. <text v-else>加载中...</text>
  164. </view>
  165. </view>
  166. <!-- 没有更多数据提示 -->
  167. <view class="no-more-tip" v-if="!selectedHasMore && selectedProducts.length > 0">
  168. <text>~ 已经到底啦 ~</text>
  169. </view>
  170. </view>
  171. <!-- 底部装饰 -->
  172. <view class="bottom-decoration">
  173. <view class="decoration-pattern"></view>
  174. </view>
  175. <!-- 邀请弹框 -->
  176. <view class="invitation-modal" v-if="showInvitationModal" @click="closeInvitationModal">
  177. <view class="modal-content" @click.stop="">
  178. <view class="modal-header">
  179. <text class="modal-title">🎉 邀请通知</text>
  180. <view class="close-btn" @click="closeInvitationModal">✕</view>
  181. </view>
  182. <view class="modal-body">
  183. <view class="inviter-info">
  184. <image class="inviter-avatar" :src="inviterInfo.avatar || '/static/images/avatar.png'"
  185. mode="aspectFill"></image>
  186. <view class="inviter-details">
  187. <text class="inviter-name">{{ inviterInfo.account || '好友' }}</text>
  188. <text class="inviter-desc">邀请您成为下级用户</text>
  189. </view>
  190. </view>
  191. <view class="invitation-desc">
  192. <text class="desc-text" v-if="isLogin">接受邀请后,您将成为该用户的下级。</text>
  193. <text class="desc-text" v-else>接受邀请后,您将成为该用户的下级。请先登录以完成邀请接受。</text>
  194. </view>
  195. </view>
  196. <view class="modal-footer">
  197. <button class="reject-btn" @click="rejectInvitation">拒绝</button>
  198. <button class="accept-btn" @click="acceptInvitation">
  199. <text v-if="isLogin">接受邀请</text>
  200. <text v-else>登录并接受</text>
  201. </button>
  202. </view>
  203. </view>
  204. </view>
  205. </view>
  206. </template>
  207. <script>
  208. import {
  209. carouselQueryAll,
  210. wxLogin,
  211. getIndexMenu,
  212. getNewProducts,
  213. getAgentProducts,
  214. getSelectedProducts,
  215. userLogin,
  216. getMemberDetail,
  217. getIndexMemberDetail,
  218. acceptInvitation,
  219. rejectInvitation,
  220. getPendingInvitations
  221. } from '@/config/api.js';
  222. import {
  223. mapGetters,
  224. mapActions
  225. } from 'vuex';
  226. import {
  227. shareImg
  228. } from '@/common/config.js'
  229. export default {
  230. data() {
  231. return {
  232. bannerList: [],
  233. menuList: [],
  234. agentProducts: [], // 代理商品列表
  235. selectedProducts: [], // 精选商品列表
  236. selectedParams: {
  237. current: 1,
  238. size: 6,
  239. name: ''
  240. },
  241. selectedTotal: 0, // 精选商品总数
  242. selectedHasMore: true, // 是否还有更多精选商品
  243. params: {
  244. current: 1,
  245. size: 10,
  246. name: '',
  247. sort: '',
  248. arrow: ''
  249. },
  250. isLoading: false, // 是否正在加载
  251. noMoreData: false, // 是否没有更多数据
  252. showFixed: false, // 是否显示固定顶部
  253. scrollTop: 0, // 当前滚动位置
  254. statusBarHeight: 0,
  255. // 邀请相关
  256. showInvitationModal: false, // 是否显示邀请弹框
  257. inviterInfo: {}, // 邀请人信息
  258. inviterId: null, // 邀请人ID
  259. hasProcessedUrlInvitation: false, // 是否已处理URL中的邀请
  260. }
  261. },
  262. computed: {
  263. ...mapGetters(['isLogin'])
  264. },
  265. onLoad(options) {
  266. // 获取状态栏高度
  267. const systemInfo = uni.getSystemInfoSync();
  268. this.statusBarHeight = systemInfo.statusBarHeight;
  269. // 计算导航栏总高度 (状态栏 + 导航栏实际高度)
  270. this.navTotalHeight = this.statusBarHeight + 90; // 状态栏 + 导航栏高度
  271. // 处理邀请参数
  272. console.log('页面加载参数:', options);
  273. if (options.inviter) {
  274. console.log('发现邀请人ID:', options.inviter);
  275. this.inviterId = options.inviter;
  276. }
  277. this.refresh();
  278. },
  279. // 微信小程序分享配置
  280. onShareAppMessage() {
  281. return {
  282. title: `邀请您加入宏匠唐卡`,
  283. path: `/pages/index/indexNew`,
  284. imageUrl: shareImg // 分享图片,需要添加
  285. }
  286. },
  287. onShareTimeline(res) {
  288. let that = this;
  289. let shareInfo = store.state.vuex_shareInfo;
  290. let query = shareInfo.query;
  291. //携带当前页面资源ID参数
  292. let currentPage = getCurrentPages()[getCurrentPages().length - 1];
  293. let options = currentPage.options;
  294. if (JSON.stringify(options) != '{}' && options.id) {
  295. query += `&id=${options.id}`;
  296. }
  297. return {
  298. title: shareInfo.title,
  299. query: query,
  300. imageUrl: shareImg,
  301. success(res) {
  302. uni.showToast({
  303. title: '分享成功'
  304. })
  305. },
  306. fail(res) {
  307. uni.showToast({
  308. title: '分享失败',
  309. icon: 'none'
  310. })
  311. },
  312. complete() {}
  313. }
  314. },
  315. onShow() {
  316. console.log('onShow 触发,当前 inviterId =', this.inviterId);
  317. // #ifdef H5
  318. this.login()
  319. // #endif
  320. // 检查是否有邀请
  321. this.checkInvitation();
  322. },
  323. onReady() {
  324. console.log('onReady 触发,当前 inviterId =', this.inviterId);
  325. // 页面渲染完成后也检查一次邀请,确保弹框能正常显示
  326. setTimeout(() => {
  327. console.log('onReady 延时触发 checkInvitation,当前 inviterId =', this.inviterId);
  328. this.checkInvitation();
  329. }, 500);
  330. },
  331. onPullDownRefresh() {
  332. this.refresh()
  333. uni.stopPullDownRefresh();
  334. },
  335. onPageScroll(e) {
  336. // 获取当前滚动位置
  337. this.scrollTop = e.scrollTop;
  338. // 当滚动超过200时显示固定顶部
  339. this.showFixed = this.scrollTop > 200;
  340. },
  341. methods: {
  342. ...mapActions(['setToken', 'setUserInfo']),
  343. refresh() {
  344. this.getShufflingList();
  345. this.getMenuList();
  346. this.getProductList();
  347. },
  348. // 获取菜单列表
  349. getMenuList() {
  350. getIndexMenu().then(res => {
  351. if (res.code === 200) {
  352. // 按sort字段排序
  353. console.log(res.data)
  354. this.menuList = res.data.sort((a, b) => a.sort - b.sort);
  355. }
  356. })
  357. },
  358. getShufflingList() {
  359. carouselQueryAll().then(res => {
  360. if (res.code === 200) {
  361. this.bannerList = res.data;
  362. }
  363. });
  364. },
  365. goSearch() {
  366. uni.navigateTo({
  367. url: '/packageShop/pages/search/index?type=1'
  368. });
  369. },
  370. login() {
  371. let params = {
  372. username: "USER082927",
  373. grant_type: "web",
  374. memberId: "1957060037088083973"
  375. }
  376. userLogin(params).then((loginData) => {
  377. uni.setStorageSync('access_token', loginData.access_token);
  378. uni.setStorageSync('refresh_token', loginData.refresh_token);
  379. uni.setStorageSync('user', loginData);
  380. this.$store.commit('isLogin', true);
  381. this.$store.commit('refresh_token', loginData.refresh_token);
  382. })
  383. },
  384. goCategory(item) {
  385. if (!item) return;
  386. switch (item.dataType) {
  387. case 0: // 普通页面路径
  388. if (item.path) {
  389. uni.navigateTo({
  390. url: item.path
  391. });
  392. }
  393. break;
  394. case 1: // 文章内容
  395. if (item.id) {
  396. uni.navigateTo({
  397. url: '/packageShop/pages/article/detail?id=' + item.id
  398. });
  399. }
  400. break;
  401. case 2: // 底部导航栏路径
  402. if (item.path) {
  403. uni.switchTab({
  404. url: item.path
  405. });
  406. }
  407. break;
  408. default:
  409. console.log('未知的跳转类型');
  410. }
  411. },
  412. handleBannerClick(item) {
  413. if (item.path) {
  414. uni.navigateTo({
  415. url: item.path
  416. });
  417. }
  418. },
  419. handleLogin() {
  420. uni.navigateTo({
  421. url: '/packageUser/pages/login/index'
  422. });
  423. },
  424. /**
  425. * 加载代理商品列表
  426. */
  427. loadAgentProducts() {
  428. getAgentProducts({}).then(res => {
  429. if (res.code === 200) {
  430. // 处理代理商品数据
  431. this.agentProducts = res.data.filter(item => item.shelfLife === 1).map(item => ({
  432. id: item.id,
  433. images: item.images,
  434. name: item.name,
  435. spec: `库存:${item.stock}`,
  436. updateTime: item.updateTime.split(' ')[0].replace(/-/g, '月').substring(5) +
  437. '日',
  438. price: item.price,
  439. soldCount: item.salesTotal,
  440. productType: item.productType,
  441. productAvg: parseFloat(item.productAvg) || 0
  442. }));
  443. } else {
  444. uni.showToast({
  445. title: res.msg || '获取代理商品失败',
  446. icon: 'none'
  447. });
  448. }
  449. }).catch(err => {
  450. console.error('获取代理商品失败:', err);
  451. uni.showToast({
  452. title: '获取代理商品失败',
  453. icon: 'none'
  454. });
  455. });
  456. },
  457. /**
  458. * 加载精选唐卡商品列表
  459. */
  460. loadSelectedProducts(loadMore = false) {
  461. if (!loadMore) {
  462. uni.showLoading({
  463. title: '加载中...'
  464. });
  465. }
  466. getSelectedProducts(this.selectedParams).then(res => {
  467. if (res.code === 200) {
  468. const productList = res.data.records.filter(item => item.shelfLife === 1).map(item => ({
  469. id: item.id,
  470. images: item.images,
  471. name: item.name,
  472. spec: `库存:${item.stock}`,
  473. updateTime: item.updateTime.split(' ')[0].replace(/-/g, '月').substring(5) +
  474. '日',
  475. price: item.price,
  476. soldCount: item.salesTotal,
  477. productType: item.productType,
  478. productAvg: parseFloat(item.productAvg) || 0
  479. }));
  480. if (loadMore) {
  481. // 加载更多,追加数据
  482. this.selectedProducts = [...this.selectedProducts, ...productList];
  483. } else {
  484. // 初始加载或刷新
  485. this.selectedProducts = productList;
  486. }
  487. // 更新分页信息
  488. this.selectedTotal = res.data.total;
  489. this.selectedHasMore = this.selectedParams.current < res.data.pages;
  490. } else {
  491. uni.showToast({
  492. title: res.msg || '获取精选商品失败',
  493. icon: 'none'
  494. });
  495. }
  496. }).catch(err => {
  497. console.error('获取精选商品失败:', err);
  498. uni.showToast({
  499. title: '获取精选商品失败',
  500. icon: 'none'
  501. });
  502. }).finally(() => {
  503. if (!loadMore) {
  504. uni.hideLoading();
  505. }
  506. });
  507. },
  508. /**
  509. * 加载更多精选商品
  510. */
  511. loadMoreSelectedProducts() {
  512. if (!this.selectedHasMore || this.isLoading) {
  513. return;
  514. }
  515. this.isLoading = true;
  516. this.selectedParams.current++;
  517. this.loadSelectedProducts(true);
  518. setTimeout(() => {
  519. this.isLoading = false;
  520. }, 1000);
  521. },
  522. /**
  523. * 获取商品列表(入口方法)
  524. */
  525. getProductList() {
  526. // 并行加载代理商品和精选商品
  527. this.loadAgentProducts();
  528. this.loadSelectedProducts();
  529. },
  530. // 跳转到商品详情页
  531. goDetail(id) {
  532. uni.navigateTo({
  533. url: '/packageShop/pages/detail/index?id=' + id
  534. });
  535. },
  536. // 检查邀请
  537. async checkInvitation() {
  538. console.log('开始检查邀请, inviterId:', this.inviterId);
  539. // 1. 优先检查URL参数中的邀请人ID(分享链接方式)
  540. if (this.inviterId && !this.hasProcessedUrlInvitation) {
  541. console.log('处理URL中的邀请人ID:', this.inviterId);
  542. const urlInviterId = this.inviterId;
  543. this.hasProcessedUrlInvitation = true; // 标记已处理,避免重复处理
  544. await this.showInvitationDialog(urlInviterId, 'share');
  545. return; // 处理了分享链接邀请就不再检查其他邀请
  546. }
  547. // 2. 检查本地存储中是否有待处理的邀请
  548. const pendingInvitation = uni.getStorageSync('pendingInvitation');
  549. console.log('本地存储的待处理邀请:', pendingInvitation);
  550. if (pendingInvitation && pendingInvitation.inviterId) {
  551. await this.showInvitationDialog(pendingInvitation.inviterId, 'local');
  552. uni.removeStorageSync('pendingInvitation');
  553. return;
  554. }
  555. // 3. 查询后端是否有待处理的邀请记录(账号邀请方式)
  556. await this.checkPendingInvitationsFromServer();
  557. },
  558. // 查询后端待处理邀请记录
  559. async checkPendingInvitationsFromServer() {
  560. try {
  561. // 获取当前用户信息
  562. const currentUser = uni.getStorageSync('user');
  563. console.log('当前用户信息:', currentUser ? currentUser.account : '未登录'); // 调试日志
  564. if (!currentUser || !currentUser.account) {
  565. console.log('用户未登录,跳过查询后端邀请');
  566. return;
  567. }
  568. // 检查是否已有推荐人
  569. if (currentUser.parentId) {
  570. console.log('用户已有推荐人,跳过查询邀请', currentUser);
  571. return;
  572. }
  573. console.log('查询后端待处理邀请记录...');
  574. const res = await getPendingInvitations();
  575. if (res.code === 200 && res.data && res.data.length > 0) {
  576. console.log('发现待处理邀请:', res.data);
  577. // 取第一个待处理的邀请(按时间倒序,最新的在前面)
  578. const latestInvitation = res.data[0];
  579. await this.showInvitationDialog(latestInvitation.inviterId, 'server', latestInvitation);
  580. } else {
  581. console.log('没有待处理的邀请记录');
  582. }
  583. } catch (error) {
  584. console.error('查询待处理邀请失败:', error);
  585. }
  586. },
  587. // 显示邀请弹框
  588. async showInvitationDialog(inviterId, source = 'unknown', invitationData = null) {
  589. try {
  590. console.log(`显示邀请弹框 - 来源: ${source}, 邀请人ID: ${inviterId}`);
  591. // 获取当前用户信息
  592. const currentUser = uni.getStorageSync('user');
  593. console.log('当前用户信息:', currentUser); // 调试日志
  594. // 检查用户是否已有推荐人(仅在用户已登录时检查)
  595. if (currentUser && currentUser.account && currentUser.parentId) {
  596. console.log('用户已有推荐人,不显示邀请弹框');
  597. return; // 已有推荐人,不显示邀请弹框
  598. }
  599. // 如果用户未登录,保存邀请信息到本地存储,但仍然显示邀请弹框
  600. if (!currentUser || !currentUser.account) {
  601. console.log('用户未登录,保存邀请信息到本地存储并显示邀请弹框');
  602. uni.setStorageSync('pendingInvitation', {
  603. inviterId
  604. });
  605. }
  606. // 先设置邀请者ID,确保后续操作能够使用
  607. this.inviterId = inviterId;
  608. console.log('设置 this.inviterId =', this.inviterId);
  609. // 如果是从服务器查询的邀请,直接使用已有的邀请人信息
  610. if (source === 'server' && invitationData) {
  611. this.inviterInfo = {
  612. id: invitationData.inviterId,
  613. account: invitationData.inviterAccount,
  614. avatar: invitationData.inviterAvatar
  615. };
  616. this.showInvitationModal = true;
  617. console.log('显示邀请弹框成功 (server)');
  618. return;
  619. }
  620. // 其他情况需要查询邀请人信息
  621. const res = await getMemberDetail(inviterId);
  622. if (res.code === 200) {
  623. this.inviterInfo = res.data;
  624. this.showInvitationModal = true;
  625. console.log('显示邀请弹框成功 (api success)');
  626. } else {
  627. console.log('获取邀请人信息失败:', res.msg);
  628. // 即使获取邀请人信息失败,也显示一个默认的邀请弹框
  629. this.inviterInfo = {
  630. id: inviterId,
  631. account: '好友',
  632. avatar: '/static/images/avatar.png'
  633. };
  634. this.showInvitationModal = true;
  635. console.log('使用默认信息显示邀请弹框 (api failed)');
  636. }
  637. } catch (error) {
  638. console.error('获取邀请人信息失败:', error);
  639. // 出现错误时也显示一个默认的邀请弹框
  640. this.inviterInfo = {
  641. id: inviterId,
  642. account: '好友',
  643. avatar: '/static/images/avatar.png'
  644. };
  645. this.inviterId = inviterId;
  646. this.showInvitationModal = true;
  647. console.log('使用默认信息显示邀请弹框 (error)');
  648. }
  649. },
  650. // 接受邀请
  651. async acceptInvitation() {
  652. try {
  653. console.log('开始接受邀请,当前 this.inviterId =', this.inviterId);
  654. // 检查用户是否已登录
  655. const currentUser = uni.getStorageSync('user');
  656. console.log(currentUser, "当前用户信息")
  657. if (!currentUser || !currentUser.account) {
  658. // 用户未登录,跳转到登录页面
  659. console.log('用户未登录,跳转到登录页面,保存 inviterId =', this.inviterId);
  660. uni.showToast({
  661. title: '请先登录',
  662. icon: 'none'
  663. });
  664. // 保存邀请信息,登录后继续处理
  665. uni.setStorageSync('pendingInvitation', {
  666. inviterId: this.inviterId
  667. });
  668. // 关闭弹框
  669. this.closeInvitationModal();
  670. // 跳转到登录页面
  671. setTimeout(() => {
  672. uni.navigateTo({
  673. url: '/packageUser/pages/login/index'
  674. });
  675. }, 500);
  676. return;
  677. }
  678. // 用户已登录,直接处理邀请
  679. console.log('用户已登录,准备调用 acceptInvitation API,inviterId =', this.inviterId);
  680. if (!this.inviterId) {
  681. console.error('邀请者ID为空,无法接受邀请');
  682. uni.showToast({
  683. title: '邀请信息错误',
  684. icon: 'none'
  685. });
  686. return;
  687. }
  688. uni.showLoading({
  689. title: '处理中...'
  690. });
  691. console.log('调用 acceptInvitation API,参数 inviterId =', this.inviterId);
  692. const res = await acceptInvitation(this.inviterId);
  693. if (res.code === 200) {
  694. // 先隐藏加载遮罩
  695. uni.hideLoading();
  696. // 更新本地用户信息
  697. const userInfo = uni.getStorageSync('user') || {};
  698. userInfo.parentId = this.inviterId;
  699. uni.setStorageSync('user', userInfo);
  700. // 成功接受邀请后清空邀请ID
  701. this.closeInvitationModal(true);
  702. // 延迟显示成功提示,确保弹框关闭后再显示
  703. setTimeout(() => {
  704. this.showSuccessMessage();
  705. }, 200);
  706. } else {
  707. uni.hideLoading();
  708. setTimeout(() => {
  709. uni.showToast({
  710. title: res.msg || '操作失败',
  711. icon: 'none',
  712. duration: 2000,
  713. mask: true
  714. });
  715. }, 100);
  716. }
  717. } catch (error) {
  718. console.error('接受邀请失败:', error);
  719. uni.hideLoading();
  720. setTimeout(() => {
  721. uni.showToast({
  722. title: '操作失败',
  723. icon: 'none',
  724. duration: 2000,
  725. mask: true
  726. });
  727. }, 100);
  728. }
  729. },
  730. // 拒绝邀请
  731. async rejectInvitation() {
  732. try {
  733. // 先隐藏邀请弹框,避免z-index冲突
  734. const tempInviterId = this.inviterId;
  735. const tempInviterInfo = this.inviterInfo;
  736. this.showInvitationModal = false;
  737. const res = await uni.showModal({
  738. title: '确认拒绝',
  739. content: '确定要拒绝这个邀请吗?'
  740. });
  741. if (res.confirm) {
  742. uni.showLoading({
  743. title: '处理中...'
  744. });
  745. const result = await rejectInvitation(tempInviterId);
  746. if (result.code === 200) {
  747. uni.showToast({
  748. title: '已拒绝邀请',
  749. icon: 'success'
  750. });
  751. } else {
  752. uni.showToast({
  753. title: result.msg || '操作失败',
  754. icon: 'none'
  755. });
  756. }
  757. // 拒绝成功后清除数据
  758. this.closeInvitationModal(true);
  759. } else {
  760. // 用户取消拒绝,恢复邀请弹框
  761. this.inviterId = tempInviterId;
  762. this.inviterInfo = tempInviterInfo;
  763. this.showInvitationModal = true;
  764. }
  765. } catch (error) {
  766. console.error('拒绝邀请失败:', error);
  767. uni.showToast({
  768. title: '操作失败',
  769. icon: 'none'
  770. });
  771. } finally {
  772. uni.hideLoading();
  773. }
  774. },
  775. // 显示成功消息(针对手机端优化)
  776. showSuccessMessage() {
  777. console.log('显示邀请接受成功消息');
  778. // 获取系统信息判断环境
  779. const systemInfo = uni.getSystemInfoSync();
  780. console.log('当前环境:', systemInfo.platform);
  781. // 优先尝试使用 showToast
  782. try {
  783. uni.showToast({
  784. title: '邀请接受成功',
  785. icon: 'success',
  786. duration: 2500,
  787. mask: true,
  788. success: () => {
  789. console.log('showToast 成功显示');
  790. },
  791. fail: (err) => {
  792. console.error('showToast 失败:', err);
  793. // 如果 showToast 失败,则使用 showModal 作为备选
  794. this.showSuccessModal();
  795. }
  796. });
  797. } catch (error) {
  798. console.error('showToast 异常:', error);
  799. // 异常情况下使用 showModal
  800. this.showSuccessModal();
  801. }
  802. },
  803. // 备选成功提示方法
  804. showSuccessModal() {
  805. console.log('使用 showModal 显示成功消息');
  806. uni.showModal({
  807. title: '成功',
  808. content: '邀请接受成功!',
  809. showCancel: false,
  810. confirmText: '知道了',
  811. success: (res) => {
  812. console.log('showModal 成功显示');
  813. try {
  814. uni.vibrateShort({});
  815. } catch (e) {
  816. console.log('振动反馈不支持或失败');
  817. }
  818. }
  819. });
  820. },
  821. // 关闭邀请弹框
  822. closeInvitationModal(clearInviterId = false) {
  823. this.showInvitationModal = false;
  824. this.inviterInfo = {};
  825. if (clearInviterId) {
  826. this.inviterId = null;
  827. }
  828. }
  829. }
  830. }
  831. </script>
  832. <style lang="scss">
  833. .status_bar {
  834. height: var(--status-bar-height);
  835. width: 100%;
  836. }
  837. .top_view {
  838. height: var(--status-bar-height);
  839. width: 100%;
  840. position: fixed;
  841. top: 0;
  842. z-index: 999;
  843. }
  844. .container {
  845. min-height: 100vh;
  846. background: linear-gradient(135deg, #F5E6D3 0%, #E2D1C3 100%);
  847. position: relative;
  848. }
  849. .container::before {
  850. content: '';
  851. position: fixed;
  852. top: 0;
  853. left: 0;
  854. right: 0;
  855. bottom: 0;
  856. background:
  857. radial-gradient(circle at 25% 25%, rgba(255, 215, 0, 0.15) 0%, transparent 25%),
  858. radial-gradient(circle at 75% 75%, rgba(139, 69, 19, 0.1) 0%, transparent 25%),
  859. radial-gradient(circle at 50% 50%, rgba(255, 215, 0, 0.08) 0%, transparent 30%);
  860. pointer-events: none;
  861. z-index: 0;
  862. animation: mysticOrbs 12s ease-in-out infinite;
  863. }
  864. .container::after {
  865. content: '';
  866. position: fixed;
  867. top: 0;
  868. left: 0;
  869. right: 0;
  870. bottom: 0;
  871. background-image:
  872. url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="mystic" width="40" height="40" patternUnits="userSpaceOnUse"><circle cx="20" cy="20" r="1" fill="%23A67C52" opacity="0.1"/><circle cx="10" cy="10" r="0.5" fill="%23FFD700" opacity="0.08"/><circle cx="30" cy="30" r="0.5" fill="%23FFD700" opacity="0.08"/></pattern></defs><rect width="100" height="100" fill="url(%23mystic)"/></svg>');
  873. opacity: 0.2;
  874. pointer-events: none;
  875. z-index: 0;
  876. animation: mysticPattern 18s linear infinite;
  877. }
  878. /* 顶部导航栏 */
  879. .header {
  880. background: linear-gradient(135deg, #9C6644 0%, #7E4F2D 100%);
  881. padding: 0;
  882. position: relative;
  883. overflow: hidden;
  884. border-bottom: 1rpx solid rgba(255, 215, 0, 0.1);
  885. }
  886. .fixed-header {
  887. background: linear-gradient(135deg, #9C6644 0%, #7E4F2D 100%);
  888. position: fixed;
  889. top: 0;
  890. left: 0;
  891. right: 0;
  892. z-index: 100;
  893. transform: translateY(-100%);
  894. transition: transform 0.3s ease-in-out;
  895. overflow: hidden;
  896. box-shadow: 0 4rpx 20rpx rgba(156, 102, 68, 0.3);
  897. border-bottom: 1rpx solid rgba(255, 215, 0, 0.1);
  898. }
  899. .fixed-header.show-fixed {
  900. transform: translateY(0);
  901. }
  902. .fixed-header::before {
  903. content: '';
  904. position: absolute;
  905. top: -50%;
  906. left: -50%;
  907. right: -50%;
  908. bottom: -50%;
  909. background:
  910. radial-gradient(circle at 30% 50%, rgba(255, 215, 0, 0.25) 0%, transparent 60%),
  911. radial-gradient(circle at 70% 50%, rgba(255, 215, 0, 0.2) 0%, transparent 60%),
  912. radial-gradient(circle at 50% 30%, rgba(255, 215, 0, 0.15) 0%, transparent 50%);
  913. opacity: 0.9;
  914. animation: headerAura 15s ease-in-out infinite;
  915. transform-origin: center;
  916. }
  917. .fixed-header::after {
  918. content: '';
  919. position: absolute;
  920. top: 0;
  921. left: 0;
  922. right: 0;
  923. bottom: 0;
  924. background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="sacred" width="40" height="40" patternUnits="userSpaceOnUse"><path d="M20 5 L35 20 L20 35 L5 20 Z" fill="none" stroke="rgba(255,215,0,0.15)" stroke-width="1"/></pattern></defs><rect width="100" height="100" fill="url(%23sacred)"/></svg>');
  925. opacity: 0.4;
  926. animation: sacredPattern 15s linear infinite;
  927. pointer-events: none;
  928. }
  929. .nav-content {
  930. height: 90rpx;
  931. display: flex;
  932. align-items: center;
  933. justify-content: space-between;
  934. padding: 0 24rpx;
  935. position: relative;
  936. z-index: 1;
  937. }
  938. .left-section {
  939. flex: 1;
  940. display: flex;
  941. align-items: center;
  942. }
  943. .logo-container {
  944. display: flex;
  945. align-items: center;
  946. margin-left: 24rpx;
  947. flex-shrink: 0;
  948. white-space: nowrap;
  949. }
  950. .logo-image {
  951. height: 60rpx;
  952. width: 60rpx;
  953. position: relative;
  954. z-index: 1;
  955. border-radius: 50%;
  956. display: block;
  957. border: 2rpx solid rgba(255, 215, 0, 0.3);
  958. }
  959. .logo-text {
  960. font-size: 36rpx;
  961. color: #FFD700;
  962. font-weight: 600;
  963. font-family: 'KaiTi', '楷体', serif;
  964. margin-left: 12rpx;
  965. letter-spacing: 2rpx;
  966. text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
  967. white-space: nowrap;
  968. }
  969. .right-section {
  970. flex: 2;
  971. display: flex;
  972. align-items: center;
  973. justify-content: flex-end;
  974. padding-right: 200rpx;
  975. /* 增加右侧padding,为胶囊按钮预留更多空间 */
  976. }
  977. .fixed-search-bar {
  978. width: 300rpx;
  979. /* 限制搜索框宽度 */
  980. height: 64rpx;
  981. display: flex;
  982. align-items: center;
  983. background: rgba(255, 248, 231, 0.95);
  984. border: 2rpx solid rgba(139, 69, 19, 0.2);
  985. border-radius: 32rpx;
  986. padding: 0 20rpx;
  987. box-shadow: 0 4rpx 16rpx rgba(139, 69, 19, 0.15);
  988. backdrop-filter: blur(10rpx);
  989. transition: all 0.3s ease;
  990. position: relative;
  991. margin-right: 20rpx;
  992. /* 添加右侧间距 */
  993. }
  994. .fixed-search-bar::before {
  995. content: '';
  996. position: absolute;
  997. top: 2rpx;
  998. left: 2rpx;
  999. right: 2rpx;
  1000. bottom: 2rpx;
  1001. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1002. border-radius: 30rpx;
  1003. pointer-events: none;
  1004. }
  1005. .fixed-search-bar:active {
  1006. transform: scale(0.98);
  1007. box-shadow: 0 2rpx 8rpx rgba(139, 69, 19, 0.2);
  1008. }
  1009. .search-icon-wrapper {
  1010. width: 32rpx;
  1011. height: 32rpx;
  1012. display: flex;
  1013. align-items: center;
  1014. justify-content: center;
  1015. margin-right: 12rpx;
  1016. }
  1017. .search-icon {
  1018. width: 28rpx;
  1019. height: 28rpx;
  1020. opacity: 0.6;
  1021. }
  1022. .search-input {
  1023. font-size: 26rpx;
  1024. color: #A67C52;
  1025. flex: 1;
  1026. }
  1027. .header::before {
  1028. content: '';
  1029. position: absolute;
  1030. top: -50%;
  1031. left: -50%;
  1032. right: -50%;
  1033. bottom: -50%;
  1034. background:
  1035. radial-gradient(circle at 30% 50%, rgba(255, 215, 0, 0.2) 0%, transparent 60%),
  1036. radial-gradient(circle at 70% 50%, rgba(255, 215, 0, 0.15) 0%, transparent 60%),
  1037. radial-gradient(circle at 50% 30%, rgba(255, 215, 0, 0.1) 0%, transparent 50%);
  1038. opacity: 0.8;
  1039. animation: headerAura 15s ease-in-out infinite;
  1040. transform-origin: center;
  1041. }
  1042. .header::after {
  1043. content: '';
  1044. position: absolute;
  1045. top: 0;
  1046. left: 0;
  1047. right: 0;
  1048. bottom: 0;
  1049. background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="sacred" width="30" height="30" patternUnits="userSpaceOnUse"><path d="M15 5 L25 15 L15 25 L5 15 Z" fill="none" stroke="rgba(255,215,0,0.1)" stroke-width="1"/></pattern></defs><rect width="100" height="100" fill="url(%23sacred)"/></svg>');
  1050. opacity: 0.3;
  1051. animation: sacredPattern 15s linear infinite;
  1052. pointer-events: none;
  1053. }
  1054. .header-content {
  1055. position: relative;
  1056. z-index: 1;
  1057. padding: 0rpx 30rpx 30rpx;
  1058. }
  1059. .logo-section {
  1060. margin-bottom: 20rpx;
  1061. width: 100%;
  1062. display: flex;
  1063. flex-direction: column;
  1064. align-items: flex-start;
  1065. }
  1066. .logo-section .logo-container {
  1067. display: flex;
  1068. align-items: center;
  1069. justify-content: flex-start;
  1070. margin-bottom: 12rpx;
  1071. margin-left: 24rpx;
  1072. }
  1073. .logo-section .logo-image {
  1074. height: 80rpx;
  1075. width: 80rpx;
  1076. border-radius: 50%;
  1077. display: block;
  1078. object-fit: cover;
  1079. border: 2rpx solid rgba(255, 215, 0, 0.3);
  1080. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
  1081. }
  1082. .logo-section .logo-text {
  1083. font-size: 48rpx;
  1084. font-family: 'KaiTi', '楷体', serif;
  1085. white-space: nowrap;
  1086. color: #FFD700;
  1087. font-weight: 700;
  1088. margin-left: 16rpx;
  1089. letter-spacing: 3rpx;
  1090. text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
  1091. }
  1092. .sub-title {
  1093. font-size: 28rpx;
  1094. color: rgba(255, 215, 0, 0.8);
  1095. margin-top: 12rpx;
  1096. font-weight: 300;
  1097. letter-spacing: 1rpx;
  1098. margin-left: 24rpx;
  1099. }
  1100. .search-bar {
  1101. display: flex;
  1102. align-items: center;
  1103. background: rgba(255, 248, 231, 0.95);
  1104. border: 2rpx solid rgba(139, 69, 19, 0.2);
  1105. border-radius: 50rpx;
  1106. padding: 20rpx 30rpx;
  1107. box-shadow: 0 8rpx 32rpx rgba(139, 69, 19, 0.15);
  1108. backdrop-filter: blur(10rpx);
  1109. transition: all 0.3s ease;
  1110. position: relative;
  1111. }
  1112. .search-bar::before {
  1113. content: '';
  1114. position: absolute;
  1115. top: 4rpx;
  1116. left: 4rpx;
  1117. right: 4rpx;
  1118. bottom: 4rpx;
  1119. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1120. border-radius: 46rpx;
  1121. pointer-events: none;
  1122. }
  1123. .search-bar:active {
  1124. transform: scale(0.98);
  1125. box-shadow: 0 4rpx 16rpx rgba(139, 69, 19, 0.2);
  1126. }
  1127. .search-icon-wrapper {
  1128. width: 48rpx;
  1129. height: 48rpx;
  1130. background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
  1131. border-radius: 50%;
  1132. display: flex;
  1133. align-items: center;
  1134. justify-content: center;
  1135. margin-right: 16rpx;
  1136. box-shadow: 0 4rpx 12rpx rgba(139, 69, 19, 0.3);
  1137. }
  1138. .search-icon {
  1139. width: 28rpx;
  1140. height: 28rpx;
  1141. filter: brightness(0) invert(1);
  1142. }
  1143. .search-input {
  1144. flex: 1;
  1145. font-size: 30rpx;
  1146. color: #8B4513;
  1147. font-weight: 400;
  1148. }
  1149. /* 轮播图 */
  1150. .swiper-section {
  1151. margin: 20rpx 20rpx 0;
  1152. position: relative;
  1153. }
  1154. .swiper-container {
  1155. border-radius: 20rpx;
  1156. overflow: hidden;
  1157. box-shadow: 0 8rpx 24rpx rgba(139, 69, 19, 0.12);
  1158. position: relative;
  1159. }
  1160. .swiper-container::before {
  1161. content: '';
  1162. position: absolute;
  1163. top: 4rpx;
  1164. left: 4rpx;
  1165. right: 4rpx;
  1166. bottom: 4rpx;
  1167. border: 1rpx solid rgba(255, 215, 0, 0.2);
  1168. border-radius: 16rpx;
  1169. pointer-events: none;
  1170. z-index: 2;
  1171. }
  1172. .swiper {
  1173. width: 100%;
  1174. height: 280rpx;
  1175. }
  1176. .swiper-image {
  1177. width: 100%;
  1178. height: 100%;
  1179. }
  1180. /* 分类导航 */
  1181. .category-section {
  1182. margin: 20rpx 20rpx;
  1183. background: rgba(255, 248, 231, 0.9);
  1184. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1185. border-radius: 20rpx;
  1186. padding: 24rpx 20rpx;
  1187. box-shadow: 0 8rpx 32rpx rgba(139, 69, 19, 0.08);
  1188. backdrop-filter: blur(10rpx);
  1189. position: relative;
  1190. }
  1191. .category-section::before {
  1192. content: '';
  1193. position: absolute;
  1194. top: 10rpx;
  1195. left: 10rpx;
  1196. right: 10rpx;
  1197. bottom: 10rpx;
  1198. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1199. border-radius: 20rpx;
  1200. pointer-events: none;
  1201. }
  1202. .category-header {
  1203. text-align: center;
  1204. margin-bottom: 30rpx;
  1205. position: relative;
  1206. }
  1207. .category-title {
  1208. font-size: 32rpx;
  1209. font-weight: 700;
  1210. color: #8B4513;
  1211. position: relative;
  1212. display: inline-block;
  1213. padding: 0 40rpx;
  1214. }
  1215. .category-title::before,
  1216. .category-title::after {
  1217. content: '❈';
  1218. color: #8B4513;
  1219. opacity: 0.6;
  1220. font-size: 24rpx;
  1221. position: absolute;
  1222. top: 50%;
  1223. transform: translateY(-50%);
  1224. }
  1225. .category-title::before {
  1226. left: 0;
  1227. }
  1228. .category-title::after {
  1229. right: 0;
  1230. }
  1231. .category-decoration {
  1232. width: 100rpx;
  1233. height: 2rpx;
  1234. background: linear-gradient(90deg, transparent, #8B4513, transparent);
  1235. margin: 16rpx auto 0;
  1236. }
  1237. .category-grid {
  1238. display: grid;
  1239. grid-template-columns: repeat(4, 1fr);
  1240. gap: 20rpx;
  1241. position: relative;
  1242. z-index: 1;
  1243. }
  1244. .category-item {
  1245. display: flex;
  1246. flex-direction: column;
  1247. align-items: center;
  1248. transition: all 0.3s ease;
  1249. }
  1250. .category-item:active {
  1251. transform: scale(0.95);
  1252. }
  1253. .category-icon-wrapper {
  1254. width: 100rpx;
  1255. height: 100rpx;
  1256. background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
  1257. border: 2rpx solid #FFD700;
  1258. border-radius: 50%;
  1259. display: flex;
  1260. align-items: center;
  1261. justify-content: center;
  1262. margin-bottom: 12rpx;
  1263. box-shadow: 0 8rpx 24rpx rgba(139, 69, 19, 0.3);
  1264. transition: all 0.3s ease;
  1265. }
  1266. .category-item:active .category-icon-wrapper {
  1267. transform: scale(0.9);
  1268. box-shadow: 0 4rpx 12rpx rgba(139, 69, 19, 0.4);
  1269. }
  1270. .category-icon {
  1271. width: 100rpx;
  1272. height: 100rpx;
  1273. border-radius: 50%;
  1274. }
  1275. .category-name {
  1276. font-size: 24rpx;
  1277. color: #8B4513;
  1278. font-weight: 500;
  1279. text-align: center;
  1280. }
  1281. /* 爆款产品 */
  1282. .hot-section {
  1283. margin: 20rpx 20rpx;
  1284. background: linear-gradient(135deg, rgba(255, 248, 231, 0.95) 0%, rgba(255, 235, 205, 0.95) 100%);
  1285. border: 2rpx solid rgba(139, 69, 19, 0.15);
  1286. border-radius: 24rpx;
  1287. padding: 30rpx 20rpx;
  1288. box-shadow: 0 12rpx 40rpx rgba(139, 69, 19, 0.12);
  1289. backdrop-filter: blur(10rpx);
  1290. position: relative;
  1291. overflow: hidden;
  1292. }
  1293. .hot-section::before {
  1294. content: '';
  1295. position: absolute;
  1296. top: 0;
  1297. left: 0;
  1298. right: 0;
  1299. bottom: 0;
  1300. background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="hot-pattern" width="20" height="20" patternUnits="userSpaceOnUse"><circle cx="10" cy="10" r="1" fill="%23A67C52" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23hot-pattern)"/></svg>');
  1301. opacity: 0.3;
  1302. pointer-events: none;
  1303. }
  1304. .hot-header {
  1305. text-align: center;
  1306. margin-bottom: 30rpx;
  1307. position: relative;
  1308. z-index: 1;
  1309. }
  1310. .hot-title {
  1311. font-size: 36rpx;
  1312. font-weight: 700;
  1313. color: #8B4513;
  1314. text-shadow: 0 2rpx 4rpx rgba(139, 69, 19, 0.2);
  1315. position: relative;
  1316. display: inline-block;
  1317. padding: 0 40rpx;
  1318. animation: hotTitleGlow 3s ease-in-out infinite;
  1319. }
  1320. .hot-title::before,
  1321. .hot-title::after {
  1322. content: '❈';
  1323. color: #8B4513;
  1324. opacity: 0.6;
  1325. font-size: 24rpx;
  1326. position: absolute;
  1327. top: 50%;
  1328. transform: translateY(-50%);
  1329. }
  1330. .hot-title::before {
  1331. left: 0;
  1332. }
  1333. .hot-title::after {
  1334. right: 0;
  1335. }
  1336. .hot-decoration {
  1337. width: 100rpx;
  1338. height: 2rpx;
  1339. background: linear-gradient(90deg, transparent, #8B4513, transparent);
  1340. margin: 16rpx auto 0;
  1341. }
  1342. .hot-grid {
  1343. display: grid;
  1344. grid-template-columns: repeat(4, 1fr);
  1345. gap: 20rpx;
  1346. position: relative;
  1347. z-index: 1;
  1348. }
  1349. .hot-item {
  1350. display: flex;
  1351. flex-direction: column;
  1352. align-items: center;
  1353. transition: all 0.3s ease;
  1354. }
  1355. .hot-item:active {
  1356. transform: scale(0.95);
  1357. }
  1358. .hot-icon-wrapper {
  1359. width: 120rpx;
  1360. height: 120rpx;
  1361. background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
  1362. border: 3rpx solid #FFD700;
  1363. border-radius: 50%;
  1364. display: flex;
  1365. align-items: center;
  1366. justify-content: center;
  1367. margin-bottom: 12rpx;
  1368. box-shadow: 0 8rpx 24rpx rgba(139, 69, 19, 0.3);
  1369. transition: all 0.3s ease;
  1370. position: relative;
  1371. overflow: hidden;
  1372. animation: mysticPulse 3s ease-in-out infinite;
  1373. }
  1374. .hot-icon-wrapper::before {
  1375. content: '';
  1376. position: absolute;
  1377. top: -50%;
  1378. left: -50%;
  1379. width: 200%;
  1380. height: 200%;
  1381. background: conic-gradient(from 0deg,
  1382. transparent 0deg,
  1383. rgba(255, 215, 0, 0.1) 45deg,
  1384. rgba(255, 215, 0, 0.3) 90deg,
  1385. rgba(255, 215, 0, 0.1) 135deg,
  1386. transparent 180deg,
  1387. rgba(255, 215, 0, 0.05) 225deg,
  1388. rgba(255, 215, 0, 0.2) 270deg,
  1389. rgba(255, 215, 0, 0.05) 315deg,
  1390. transparent 360deg);
  1391. transform: rotate(0deg);
  1392. transition: all 0.3s ease;
  1393. animation: mysticRotate 6s ease-in-out infinite;
  1394. }
  1395. .hot-item:active .hot-icon-wrapper {
  1396. transform: scale(0.9);
  1397. box-shadow: 0 4rpx 12rpx rgba(139, 69, 19, 0.4);
  1398. animation: hotClick 0.3s ease-out;
  1399. }
  1400. .hot-item:active .hot-icon-wrapper::before {
  1401. transform: rotate(45deg) translateX(100%);
  1402. }
  1403. .hot-glow {
  1404. position: absolute;
  1405. top: 50%;
  1406. left: 50%;
  1407. width: 140rpx;
  1408. height: 140rpx;
  1409. background: radial-gradient(circle,
  1410. rgba(255, 215, 0, 0.4) 0%,
  1411. rgba(255, 215, 0, 0.2) 30%,
  1412. rgba(255, 215, 0, 0.1) 60%,
  1413. transparent 80%);
  1414. border-radius: 50%;
  1415. transform: translate(-50%, -50%);
  1416. animation: hotGlow 3s ease-in-out infinite alternate;
  1417. pointer-events: none;
  1418. z-index: 0;
  1419. filter: blur(2rpx);
  1420. }
  1421. @keyframes mysticPulse {
  1422. 0% {
  1423. box-shadow: 0 8rpx 24rpx rgba(139, 69, 19, 0.3);
  1424. transform: scale(1);
  1425. }
  1426. 25% {
  1427. box-shadow: 0 8rpx 24rpx rgba(139, 69, 19, 0.3), 0 0 20rpx rgba(255, 215, 0, 0.3);
  1428. transform: scale(1.02);
  1429. }
  1430. 50% {
  1431. box-shadow: 0 8rpx 24rpx rgba(139, 69, 19, 0.3), 0 0 30rpx rgba(255, 215, 0, 0.5);
  1432. transform: scale(1.05);
  1433. }
  1434. 75% {
  1435. box-shadow: 0 8rpx 24rpx rgba(139, 69, 19, 0.3), 0 0 25rpx rgba(255, 215, 0, 0.4);
  1436. transform: scale(1.03);
  1437. }
  1438. 100% {
  1439. box-shadow: 0 8rpx 24rpx rgba(139, 69, 19, 0.3);
  1440. transform: scale(1);
  1441. }
  1442. }
  1443. @keyframes mysticRotate {
  1444. 0% {
  1445. transform: rotate(0deg);
  1446. }
  1447. 25% {
  1448. transform: rotate(90deg);
  1449. }
  1450. 50% {
  1451. transform: rotate(180deg);
  1452. }
  1453. 75% {
  1454. transform: rotate(270deg);
  1455. }
  1456. 100% {
  1457. transform: rotate(360deg);
  1458. }
  1459. }
  1460. @keyframes hotGlow {
  1461. 0% {
  1462. opacity: 0.4;
  1463. transform: translate(-50%, -50%) scale(0.9);
  1464. }
  1465. 50% {
  1466. opacity: 0.7;
  1467. transform: translate(-50%, -50%) scale(1.1);
  1468. }
  1469. 100% {
  1470. opacity: 0.5;
  1471. transform: translate(-50%, -50%) scale(1.0);
  1472. }
  1473. }
  1474. @keyframes hotClick {
  1475. 0% {
  1476. transform: scale(1);
  1477. }
  1478. 50% {
  1479. transform: scale(0.85);
  1480. }
  1481. 100% {
  1482. transform: scale(0.9);
  1483. }
  1484. }
  1485. @keyframes hotTitleGlow {
  1486. 0%,
  1487. 100% {
  1488. text-shadow: 0 2rpx 4rpx rgba(139, 69, 19, 0.2);
  1489. }
  1490. 50% {
  1491. text-shadow: 0 2rpx 4rpx rgba(139, 69, 19, 0.2), 0 0 10rpx rgba(255, 215, 0, 0.6);
  1492. }
  1493. }
  1494. @keyframes mysticOrbs {
  1495. 0%,
  1496. 100% {
  1497. transform: scale(1) rotate(0deg);
  1498. }
  1499. 25% {
  1500. transform: scale(1.1) rotate(90deg);
  1501. }
  1502. 50% {
  1503. transform: scale(0.9) rotate(180deg);
  1504. }
  1505. 75% {
  1506. transform: scale(1.05) rotate(270deg);
  1507. }
  1508. }
  1509. @keyframes mysticPattern {
  1510. 0% {
  1511. transform: translateX(0px) translateY(0px);
  1512. }
  1513. 100% {
  1514. transform: translateX(-40px) translateY(-40px);
  1515. }
  1516. }
  1517. @keyframes headerAura {
  1518. 0% {
  1519. transform: scale(1) rotate(0deg);
  1520. opacity: 0.8;
  1521. }
  1522. 25% {
  1523. transform: scale(1.1) rotate(5deg);
  1524. opacity: 0.9;
  1525. }
  1526. 50% {
  1527. transform: scale(1.15) rotate(0deg);
  1528. opacity: 1;
  1529. }
  1530. 75% {
  1531. transform: scale(1.1) rotate(-5deg);
  1532. opacity: 0.9;
  1533. }
  1534. 100% {
  1535. transform: scale(1) rotate(0deg);
  1536. opacity: 0.8;
  1537. }
  1538. }
  1539. @keyframes sacredPattern {
  1540. 0% {
  1541. transform: translateX(0px) translateY(0px) rotate(0deg);
  1542. }
  1543. 100% {
  1544. transform: translateX(-30px) translateY(-30px) rotate(360deg);
  1545. }
  1546. }
  1547. .hot-icon {
  1548. width: 80rpx;
  1549. height: 80rpx;
  1550. border-radius: 50%;
  1551. z-index: 1;
  1552. position: relative;
  1553. }
  1554. .hot-badge {
  1555. position: absolute;
  1556. top: 5rpx;
  1557. right: 8rpx;
  1558. background: linear-gradient(135deg, #FF6B35 0%, #FF9A56 100%);
  1559. color: #fff;
  1560. font-size: 18rpx;
  1561. padding: 4rpx 8rpx;
  1562. border-radius: 10rpx;
  1563. font-weight: 600;
  1564. z-index: 2;
  1565. box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.4);
  1566. border: 1rpx solid rgba(255, 255, 255, 0.3);
  1567. }
  1568. .hot-name {
  1569. font-size: 24rpx;
  1570. color: #8B4513;
  1571. font-weight: 500;
  1572. text-align: center;
  1573. line-height: 1.3;
  1574. }
  1575. /* 商品区域通用样式 */
  1576. .product-section {
  1577. margin: 0 20rpx 40rpx;
  1578. }
  1579. .section-header {
  1580. text-align: center;
  1581. margin-bottom: 30rpx;
  1582. position: relative;
  1583. padding: 30rpx 0;
  1584. }
  1585. .section-header::before,
  1586. .section-header::after {
  1587. content: '';
  1588. position: absolute;
  1589. height: 2rpx;
  1590. width: 60rpx;
  1591. background: linear-gradient(90deg, transparent, #8B4513, transparent);
  1592. top: 50%;
  1593. transform: translateY(-50%);
  1594. }
  1595. .section-header::before {
  1596. left: 20%;
  1597. }
  1598. .section-header::after {
  1599. right: 20%;
  1600. }
  1601. .section-title {
  1602. display: flex;
  1603. align-items: center;
  1604. justify-content: center;
  1605. margin-bottom: 12rpx;
  1606. }
  1607. .title-text {
  1608. font-size: 36rpx;
  1609. font-weight: 700;
  1610. color: #8B4513;
  1611. margin: 0 20rpx;
  1612. position: relative;
  1613. display: inline-block;
  1614. }
  1615. .title-decoration {
  1616. display: flex;
  1617. align-items: center;
  1618. gap: 10rpx;
  1619. }
  1620. .decoration-line {
  1621. width: 40rpx;
  1622. height: 2rpx;
  1623. background: linear-gradient(90deg, #8B4513, #654321);
  1624. }
  1625. .decoration-symbol {
  1626. width: 30rpx;
  1627. height: 30rpx;
  1628. opacity: 0.8;
  1629. }
  1630. .section-subtitle {
  1631. font-size: 26rpx;
  1632. color: #A67C52;
  1633. font-weight: 300;
  1634. }
  1635. /* 代理商品列表 */
  1636. .agent-section {
  1637. background: rgba(255, 248, 231, 0.5);
  1638. border-radius: 24rpx;
  1639. padding: 20rpx;
  1640. margin: 0 20rpx 40rpx;
  1641. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1642. }
  1643. .product-list {
  1644. display: grid;
  1645. grid-template-columns: repeat(2, 1fr);
  1646. gap: 20rpx;
  1647. }
  1648. .product-item {
  1649. background: rgba(255, 248, 231, 0.95);
  1650. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1651. border-radius: 20rpx;
  1652. overflow: hidden;
  1653. box-shadow: 0 8rpx 32rpx rgba(139, 69, 19, 0.08);
  1654. backdrop-filter: blur(10rpx);
  1655. transition: all 0.3s ease;
  1656. position: relative;
  1657. }
  1658. .product-item::before {
  1659. content: '';
  1660. position: absolute;
  1661. top: 10rpx;
  1662. left: 10rpx;
  1663. right: 10rpx;
  1664. bottom: 10rpx;
  1665. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1666. border-radius: 16rpx;
  1667. pointer-events: none;
  1668. z-index: 1;
  1669. }
  1670. .product-item:active {
  1671. transform: translateY(-4rpx);
  1672. box-shadow: 0 12rpx 40rpx rgba(139, 69, 19, 0.12);
  1673. }
  1674. .product-image-container {
  1675. position: relative;
  1676. width: 100%;
  1677. height: 280rpx;
  1678. overflow: hidden;
  1679. }
  1680. .product-image {
  1681. width: 100%;
  1682. height: 100%;
  1683. transition: transform 0.3s ease;
  1684. }
  1685. .product-item:active .product-image {
  1686. transform: scale(1.05);
  1687. }
  1688. .agent-badge {
  1689. position: absolute;
  1690. top: 16rpx;
  1691. right: 16rpx;
  1692. background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
  1693. color: #8B4513;
  1694. font-size: 22rpx;
  1695. padding: 6rpx 12rpx;
  1696. border-radius: 12rpx;
  1697. font-weight: 600;
  1698. border: 1rpx solid rgba(255, 255, 255, 0.3);
  1699. z-index: 2;
  1700. }
  1701. .product-content {
  1702. padding: 20rpx;
  1703. position: relative;
  1704. z-index: 2;
  1705. }
  1706. .product-name {
  1707. font-size: 28rpx;
  1708. color: #8B4513;
  1709. font-weight: 600;
  1710. line-height: 1.4;
  1711. margin-bottom: 8rpx;
  1712. display: -webkit-box;
  1713. -webkit-line-clamp: 2;
  1714. -webkit-box-orient: vertical;
  1715. overflow: hidden;
  1716. }
  1717. .product-desc {
  1718. font-size: 24rpx;
  1719. color: #A67C52;
  1720. margin-bottom: 12rpx;
  1721. }
  1722. .product-meta {
  1723. display: flex;
  1724. justify-content: space-between;
  1725. align-items: center;
  1726. margin-bottom: 16rpx;
  1727. }
  1728. .update-time {
  1729. font-size: 22rpx;
  1730. color: #999;
  1731. }
  1732. .sold-count {
  1733. font-size: 22rpx;
  1734. color: #999;
  1735. }
  1736. .product-footer {
  1737. display: flex;
  1738. justify-content: space-between;
  1739. align-items: center;
  1740. }
  1741. .price-section {
  1742. display: flex;
  1743. align-items: baseline;
  1744. }
  1745. .price-symbol {
  1746. font-size: 24rpx;
  1747. color: #8B4513;
  1748. font-weight: 600;
  1749. }
  1750. .price-value {
  1751. font-size: 32rpx;
  1752. color: #8B4513;
  1753. font-weight: 700;
  1754. }
  1755. .buy-btn {
  1756. background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
  1757. padding: 12rpx 20rpx;
  1758. border-radius: 20rpx;
  1759. border: 1rpx solid #FFD700;
  1760. box-shadow: 0 4rpx 12rpx rgba(139, 69, 19, 0.3);
  1761. transition: all 0.3s ease;
  1762. }
  1763. .agent-btn {
  1764. background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
  1765. border: 1rpx solid #FFD700;
  1766. box-shadow: 0 4rpx 12rpx rgba(139, 69, 19, 0.3);
  1767. }
  1768. .buy-btn:active {
  1769. transform: scale(0.95);
  1770. box-shadow: 0 2rpx 8rpx rgba(139, 69, 19, 0.4);
  1771. }
  1772. .agent-btn:active {
  1773. box-shadow: 0 2rpx 8rpx rgba(139, 69, 19, 0.4);
  1774. }
  1775. .buy-text {
  1776. font-size: 24rpx;
  1777. color: #FFD700;
  1778. font-weight: 500;
  1779. }
  1780. /* 精选商品网格 */
  1781. .selected-section {
  1782. background: rgba(255, 248, 231, 0.3);
  1783. border-radius: 24rpx;
  1784. padding: 20rpx;
  1785. margin: 0 20rpx 40rpx;
  1786. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1787. }
  1788. .selected-product-grid {
  1789. display: grid;
  1790. grid-template-columns: repeat(2, 1fr);
  1791. gap: 20rpx;
  1792. }
  1793. .selected-product-card {
  1794. background: rgba(255, 248, 231, 0.95);
  1795. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1796. border-radius: 20rpx;
  1797. overflow: hidden;
  1798. box-shadow: 0 8rpx 32rpx rgba(139, 69, 19, 0.08);
  1799. backdrop-filter: blur(10rpx);
  1800. transition: all 0.3s ease;
  1801. position: relative;
  1802. }
  1803. .selected-product-card::before {
  1804. content: '';
  1805. position: absolute;
  1806. top: 10rpx;
  1807. left: 10rpx;
  1808. right: 10rpx;
  1809. bottom: 10rpx;
  1810. border: 1rpx solid rgba(139, 69, 19, 0.1);
  1811. border-radius: 16rpx;
  1812. pointer-events: none;
  1813. z-index: 1;
  1814. }
  1815. .selected-product-card:active {
  1816. transform: translateY(-4rpx);
  1817. box-shadow: 0 12rpx 40rpx rgba(139, 69, 19, 0.12);
  1818. }
  1819. .selected-product-image-wrapper {
  1820. position: relative;
  1821. width: 100%;
  1822. height: 280rpx;
  1823. overflow: hidden;
  1824. }
  1825. .selected-product-image {
  1826. width: 100%;
  1827. height: 100%;
  1828. transition: transform 0.3s ease;
  1829. }
  1830. .selected-product-card:active .selected-product-image {
  1831. transform: scale(1.05);
  1832. }
  1833. .selected-product-badge {
  1834. position: absolute;
  1835. top: 16rpx;
  1836. right: 16rpx;
  1837. background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
  1838. border: 1rpx solid #FFD700;
  1839. color: #FFD700;
  1840. font-size: 22rpx;
  1841. padding: 6rpx 12rpx;
  1842. border-radius: 12rpx;
  1843. font-weight: 500;
  1844. z-index: 2;
  1845. }
  1846. .selected-product-info {
  1847. padding: 20rpx;
  1848. position: relative;
  1849. z-index: 2;
  1850. }
  1851. .selected-product-name {
  1852. font-size: 28rpx;
  1853. color: #8B4513;
  1854. font-weight: 600;
  1855. line-height: 1.4;
  1856. margin-bottom: 8rpx;
  1857. display: -webkit-box;
  1858. -webkit-line-clamp: 2;
  1859. -webkit-box-orient: vertical;
  1860. overflow: hidden;
  1861. }
  1862. .selected-product-desc {
  1863. font-size: 24rpx;
  1864. color: #A67C52;
  1865. margin-bottom: 12rpx;
  1866. }
  1867. .selected-product-meta {
  1868. display: flex;
  1869. justify-content: space-between;
  1870. align-items: center;
  1871. margin-bottom: 16rpx;
  1872. }
  1873. .rating {
  1874. display: flex;
  1875. align-items: center;
  1876. gap: 4rpx;
  1877. }
  1878. .rating-star {
  1879. font-size: 24rpx;
  1880. color: #FFD700;
  1881. }
  1882. .rating-value {
  1883. font-size: 22rpx;
  1884. color: #666;
  1885. font-weight: 500;
  1886. }
  1887. .selected-sold-count {
  1888. font-size: 22rpx;
  1889. color: #999;
  1890. }
  1891. .selected-product-footer {
  1892. display: flex;
  1893. justify-content: space-between;
  1894. align-items: center;
  1895. }
  1896. .selected-price-section {
  1897. display: flex;
  1898. align-items: baseline;
  1899. }
  1900. .selected-price-symbol {
  1901. font-size: 24rpx;
  1902. color: #8B4513;
  1903. font-weight: 600;
  1904. }
  1905. .selected-price-value {
  1906. font-size: 32rpx;
  1907. color: #8B4513;
  1908. font-weight: 700;
  1909. }
  1910. .selected-buy-btn {
  1911. background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
  1912. padding: 12rpx 20rpx;
  1913. border-radius: 20rpx;
  1914. border: 1rpx solid #FFD700;
  1915. box-shadow: 0 4rpx 12rpx rgba(139, 69, 19, 0.3);
  1916. transition: all 0.3s ease;
  1917. }
  1918. .selected-buy-btn:active {
  1919. transform: scale(0.95);
  1920. box-shadow: 0 2rpx 8rpx rgba(139, 69, 19, 0.4);
  1921. }
  1922. .selected-buy-text {
  1923. font-size: 24rpx;
  1924. color: #FFD700;
  1925. font-weight: 500;
  1926. }
  1927. /* 底部装饰 */
  1928. .bottom-decoration {
  1929. height: 60rpx;
  1930. background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
  1931. margin: 0 20rpx;
  1932. border-radius: 20rpx 20rpx 0 0;
  1933. opacity: 0.1;
  1934. position: relative;
  1935. overflow: hidden;
  1936. }
  1937. .bottom-decoration::before {
  1938. content: '';
  1939. position: absolute;
  1940. top: 0;
  1941. left: 0;
  1942. right: 0;
  1943. bottom: 0;
  1944. background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="endless-knot-bottom" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M10 0L20 10L10 20L0 10z" fill="none" stroke="rgba(255,215,0,0.1)" stroke-width="1"/></pattern></defs><rect width="100" height="100" fill="url(%23endless-knot-bottom)"/></svg>');
  1945. opacity: 0.2;
  1946. }
  1947. /* 邀请弹框样式 */
  1948. .invitation-modal {
  1949. position: fixed;
  1950. top: 0;
  1951. left: 0;
  1952. right: 0;
  1953. bottom: 0;
  1954. background: rgba(0, 0, 0, 0.6);
  1955. display: flex;
  1956. align-items: center;
  1957. justify-content: center;
  1958. z-index: 99999;
  1959. backdrop-filter: blur(5rpx);
  1960. }
  1961. .modal-content {
  1962. width: 600rpx;
  1963. max-width: 90vw;
  1964. background: linear-gradient(135deg, rgba(255, 248, 231, 0.98) 0%, rgba(255, 235, 205, 0.98) 100%);
  1965. border-radius: 24rpx;
  1966. overflow: hidden;
  1967. box-shadow: 0 20rpx 60rpx rgba(139, 69, 19, 0.3);
  1968. border: 2rpx solid rgba(255, 215, 0, 0.3);
  1969. position: relative;
  1970. animation: modalSlideIn 0.3s ease-out;
  1971. }
  1972. .modal-content::before {
  1973. content: '';
  1974. position: absolute;
  1975. top: 0;
  1976. left: 0;
  1977. right: 0;
  1978. bottom: 0;
  1979. background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="modal-pattern" width="30" height="30" patternUnits="userSpaceOnUse"><circle cx="15" cy="15" r="1" fill="%23A67C52" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23modal-pattern)"/></svg>');
  1980. opacity: 0.3;
  1981. pointer-events: none;
  1982. }
  1983. .modal-header {
  1984. background: linear-gradient(135deg, #9C6644 0%, #7E4F2D 100%);
  1985. padding: 30rpx;
  1986. display: flex;
  1987. align-items: center;
  1988. justify-content: space-between;
  1989. position: relative;
  1990. z-index: 1;
  1991. }
  1992. .modal-title {
  1993. font-size: 32rpx;
  1994. font-weight: 700;
  1995. color: #FFD700;
  1996. text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
  1997. }
  1998. .close-btn {
  1999. width: 48rpx;
  2000. height: 48rpx;
  2001. background: rgba(255, 255, 255, 0.2);
  2002. border-radius: 50%;
  2003. display: flex;
  2004. align-items: center;
  2005. justify-content: center;
  2006. font-size: 24rpx;
  2007. color: #FFD700;
  2008. font-weight: 600;
  2009. transition: all 0.3s ease;
  2010. }
  2011. .close-btn:active {
  2012. background: rgba(255, 255, 255, 0.3);
  2013. transform: scale(0.9);
  2014. }
  2015. .modal-body {
  2016. padding: 40rpx 30rpx;
  2017. position: relative;
  2018. z-index: 1;
  2019. }
  2020. .inviter-info {
  2021. display: flex;
  2022. align-items: center;
  2023. margin-bottom: 30rpx;
  2024. background: rgba(255, 255, 255, 0.8);
  2025. border-radius: 20rpx;
  2026. padding: 24rpx;
  2027. box-shadow: 0 4rpx 16rpx rgba(139, 69, 19, 0.1);
  2028. }
  2029. .inviter-avatar {
  2030. width: 100rpx;
  2031. height: 100rpx;
  2032. border-radius: 50%;
  2033. margin-right: 20rpx;
  2034. border: 3rpx solid #FFD700;
  2035. box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3);
  2036. }
  2037. .inviter-details {
  2038. flex: 1;
  2039. display: flex;
  2040. flex-direction: column;
  2041. }
  2042. .inviter-name {
  2043. font-size: 32rpx;
  2044. font-weight: 700;
  2045. color: #8B4513;
  2046. margin-bottom: 8rpx;
  2047. }
  2048. .inviter-desc {
  2049. font-size: 26rpx;
  2050. color: #A67C52;
  2051. }
  2052. .invitation-desc {
  2053. background: rgba(255, 255, 255, 0.6);
  2054. border-radius: 16rpx;
  2055. padding: 24rpx;
  2056. border-left: 4rpx solid #8B4513;
  2057. }
  2058. .desc-text {
  2059. font-size: 26rpx;
  2060. color: #666;
  2061. line-height: 1.6;
  2062. }
  2063. .modal-footer {
  2064. padding: 0 30rpx 30rpx;
  2065. display: flex;
  2066. gap: 20rpx;
  2067. position: relative;
  2068. z-index: 1;
  2069. }
  2070. .reject-btn,
  2071. .accept-btn {
  2072. flex: 1;
  2073. height: 80rpx;
  2074. border-radius: 40rpx;
  2075. font-size: 28rpx;
  2076. font-weight: 600;
  2077. border: none;
  2078. transition: all 0.3s ease;
  2079. box-shadow: 0 4rpx 16rpx rgba(139, 69, 19, 0.2);
  2080. }
  2081. .reject-btn {
  2082. background: rgba(255, 255, 255, 0.9);
  2083. color: #999;
  2084. border: 2rpx solid rgba(153, 153, 153, 0.3);
  2085. }
  2086. .reject-btn:active {
  2087. background: rgba(255, 255, 255, 0.7);
  2088. transform: scale(0.98);
  2089. }
  2090. .accept-btn {
  2091. background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
  2092. color: #FFD700;
  2093. border: 2rpx solid #FFD700;
  2094. box-shadow: 0 4rpx 16rpx rgba(139, 69, 19, 0.3), 0 0 20rpx rgba(255, 215, 0, 0.2);
  2095. }
  2096. .accept-btn:active {
  2097. transform: scale(0.98);
  2098. box-shadow: 0 2rpx 8rpx rgba(139, 69, 19, 0.4);
  2099. }
  2100. @keyframes modalSlideIn {
  2101. 0% {
  2102. opacity: 0;
  2103. transform: scale(0.8) translateY(-50rpx);
  2104. }
  2105. 100% {
  2106. opacity: 1;
  2107. transform: scale(1) translateY(0);
  2108. }
  2109. }
  2110. /* 响应式设计 */
  2111. @media (max-width: 750rpx) {
  2112. .selected-product-grid {
  2113. grid-template-columns: 1fr;
  2114. }
  2115. .category-grid {
  2116. grid-template-columns: repeat(3, 1fr);
  2117. }
  2118. .category-item {
  2119. width: 120rpx;
  2120. }
  2121. .modal-content {
  2122. width: 90vw;
  2123. }
  2124. .modal-footer {
  2125. flex-direction: column;
  2126. }
  2127. .reject-btn,
  2128. .accept-btn {
  2129. width: 100%;
  2130. }
  2131. }
  2132. /* 加载更多按钮样式 */
  2133. .load-more-section {
  2134. display: flex;
  2135. justify-content: center;
  2136. padding: 40rpx 20rpx;
  2137. }
  2138. .load-more-btn {
  2139. background: linear-gradient(135deg, #8B4513, #A0522D);
  2140. color: white;
  2141. padding: 24rpx 60rpx;
  2142. border-radius: 50rpx;
  2143. font-size: 28rpx;
  2144. font-weight: 600;
  2145. box-shadow: 0 8rpx 24rpx rgba(139, 69, 19, 0.2);
  2146. transition: all 0.3s ease;
  2147. text-align: center;
  2148. min-width: 200rpx;
  2149. }
  2150. .load-more-btn.loading {
  2151. background: linear-gradient(135deg, #999, #bbb);
  2152. color: #666;
  2153. }
  2154. .load-more-btn:active {
  2155. transform: translateY(2rpx);
  2156. box-shadow: 0 4rpx 12rpx rgba(139, 69, 19, 0.3);
  2157. }
  2158. /* 没有更多数据提示样式 */
  2159. .no-more-tip {
  2160. display: flex;
  2161. justify-content: center;
  2162. padding: 30rpx 20rpx;
  2163. color: #999;
  2164. font-size: 26rpx;
  2165. }
  2166. </style>