Browse Source

feat: 移除冗余

dev
jiannibang 3 years ago
parent
commit
6b7c42deba
  1. 2
      package.json
  2. 2
      public/static/offline/JSON/config.json
  3. 182
      public/static/offline/JSON/getBrandShopList.json
  4. 16351
      public/static/offline/JSON/getBrandShopListByFloor.json
  5. 16527
      public/static/offline/JSON/getBrandShopListByIndustryId.json
  6. 16
      public/static/offline/JSON/getDevCoordinateByIP.json
  7. 2
      public/static/offline/JSON/getMap.json
  8. 11537
      public/static/offline/JSON/getMapInfo.json
  9. 22
      public/static/offline/JSON/index.json
  10. 237
      public/static/offline/JSON/theme.json
  11. BIN
      public/static/offline/qianmugo/themes/fashion/activity.png
  12. BIN
      public/static/offline/qianmugo/themes/fashion/back.png
  13. BIN
      public/static/offline/qianmugo/themes/fashion/billboardHeaderBgH.png
  14. BIN
      public/static/offline/qianmugo/themes/fashion/billboardHeaderBgV.png
  15. BIN
      public/static/offline/qianmugo/themes/fashion/billboardLoc.png
  16. BIN
      public/static/offline/qianmugo/themes/fashion/mall.png
  17. BIN
      public/static/offline/qianmugo/themes/fashion/member.png
  18. BIN
      public/static/offline/qianmugo/themes/fashion/movie.png
  19. BIN
      public/static/offline/qianmugo/themes/fashion/parking.png
  20. BIN
      public/static/offline/qianmugo/themes/fashion/searchClear.png
  21. BIN
      public/static/offline/qianmugo/themes/fashion/searchClose.png
  22. BIN
      public/static/offline/qianmugo/themes/main/activity.png
  23. BIN
      public/static/offline/qianmugo/themes/main/back.png
  24. BIN
      public/static/offline/qianmugo/themes/main/billboardHeaderBgH.png
  25. BIN
      public/static/offline/qianmugo/themes/main/billboardHeaderBgV.png
  26. 0
      public/static/offline/qianmugo/themes/main/billboardLoc.png
  27. 0
      public/static/offline/qianmugo/themes/main/brandQrcodeClose.png
  28. 0
      public/static/offline/qianmugo/themes/main/delete.png
  29. 0
      public/static/offline/qianmugo/themes/main/grid.png
  30. 0
      public/static/offline/qianmugo/themes/main/gridActive.png
  31. 10
      public/static/offline/qianmugo/themes/main/index.js
  32. 0
      public/static/offline/qianmugo/themes/main/keyboard.png
  33. 0
      public/static/offline/qianmugo/themes/main/keyboard_active.png
  34. BIN
      public/static/offline/qianmugo/themes/main/loc.png
  35. BIN
      public/static/offline/qianmugo/themes/main/mall.png
  36. BIN
      public/static/offline/qianmugo/themes/main/member.png
  37. BIN
      public/static/offline/qianmugo/themes/main/movie.png
  38. BIN
      public/static/offline/qianmugo/themes/main/parking.png
  39. 0
      public/static/offline/qianmugo/themes/main/recBg.png
  40. 0
      public/static/offline/qianmugo/themes/main/row.png
  41. 0
      public/static/offline/qianmugo/themes/main/rowActive.png
  42. BIN
      public/static/offline/qianmugo/themes/main/searchClear.png
  43. BIN
      public/static/offline/qianmugo/themes/main/searchClose.png
  44. 0
      public/static/offline/qianmugo/themes/main/searchIcon.png
  45. 128
      public/static/offline/qianmugo/themes/main/style.json
  46. 0
      public/static/offline/qianmugo/themes/main/write.png
  47. 0
      public/static/offline/qianmugo/themes/main/write_active.png
  48. 276
      src/base/ShopItem/ShopItem.vue
  49. 239
      src/components/ActivitiesList/ActivitiesList.vue
  50. 200
      src/components/ActivityDetail/ActivityDetail.vue
  51. 455
      src/components/BrandDetail/BrandDetail.vue
  52. 266
      src/components/BrandDetail/children/Activity.vue
  53. 123
      src/components/BrandDetail/children/Comment.vue
  54. 155
      src/components/BrandDetail/children/Goods.vue
  55. 119
      src/components/BrandDetail/children/Info.vue
  56. 75
      src/components/BrandDetail/children/Recommend.vue
  57. 132
      src/components/BrandDetail/children/tabs.vue
  58. 104
      src/components/BrandRecommend/BrandRecommend.vue
  59. 38
      src/components/BrandScroll/BrandScroll.vue
  60. 204
      src/components/Busniness/Busniness.vue
  61. 154
      src/components/CarInfo/CarInfo.vue
  62. 131
      src/components/CarouselWithIntro/CarouselWithIntro.vue
  63. 118
      src/components/Customer/Customer.vue
  64. 36
      src/components/EffectFade/EffectFade.vue
  65. 194
      src/components/Industry/Industry.vue
  66. 86
      src/components/KeyboardByLetter/KeyboardByLetter.vue
  67. 107
      src/components/KeyboardByWritten/KeyboardByWritten.vue
  68. 49
      src/components/Language/Language.vue
  69. 314
      src/components/LoginByPhone/LoginByPhone.vue
  70. 83
      src/components/LoginError/LoginError.vue
  71. 104
      src/components/MallIntroduce/MallIntroduce.vue
  72. 5
      src/components/Map/Map.vue
  73. 105
      src/components/MockInput/MockInput.vue
  74. 388
      src/components/MovieDetail/MovieDetail.vue
  75. 231
      src/components/PlateInput/PlateInput.vue
  76. 91
      src/components/PlateKeyboard/PlateKeyboard.vue
  77. 73
      src/components/PlateKeyboard/keyboard.js
  78. 22
      src/components/PublicComponent/PublicComponent.vue
  79. 91
      src/components/PublicComponent/SearchBar.vue
  80. 142
      src/components/PublicComponent/Tabs.vue
  81. 92
      src/components/QRCode/QRCode.vue
  82. 125
      src/components/QuestionClassify/QuestionClassify.vue
  83. 193
      src/components/QuestionList/QuestionList.vue
  84. 396
      src/components/Search/Search.vue
  85. BIN
      src/components/Search/clear.png
  86. BIN
      src/components/Search/icon.png
  87. 40
      src/components/Search/tabs.js
  88. 84
      src/components/SearchResultItem/SearchResultItem.vue
  89. 140
      src/components/SearchResultList/SearchResultList.vue
  90. 86
      src/components/ServiceList/ServiceList.vue
  91. 104
      src/components/Sidebar/Sidebar.vue
  92. 72
      src/components/Sidebar/list.js
  93. 57
      src/components/Tabs/Tabs.vue
  94. 49
      src/components/Traffic/Traffic.vue
  95. 241
      src/components/Voice/Voice.vue
  96. 1
      src/components/Voice/voice.json
  97. 116
      src/components/WaterfallList/WaterfallList.vue
  98. 171
      src/components/Written/Written.vue
  99. 4
      src/composables/useHandleScreen.js
  100. 19
      src/layouts/View.vue

2
package.json

@ -61,7 +61,7 @@
"vue/multi-word-component-names": "off", "vue/multi-word-component-names": "off",
"no-debugger": "off", "no-debugger": "off",
"no-console": "off", "no-console": "off",
"prettier/prettier": "warn",
"prettier/prettier": "off",
"no-extra-semi": "off", "no-extra-semi": "off",
"no-unused-vars": "warn", "no-unused-vars": "warn",
"no-await-in-loop": "error", "no-await-in-loop": "error",

2
public/static/offline/JSON/config.json

@ -2,7 +2,7 @@
"code": "200", "code": "200",
"msg": "", "msg": "",
"data": { "data": {
"interfaceUrl": "https://project-iot.test.1000my.com/api",
"interfaceUrl": "https://iot.1000my.com",
"sourceUrl": "/static/offline", "sourceUrl": "/static/offline",
"backSocket": "ws://127.0.0.1:7181", "backSocket": "ws://127.0.0.1:7181",
"mobileNav": "https://1000my.obs.cn-east-2.myhuaweicloud.com/mobileqmgo/index.html#/", "mobileNav": "https://1000my.obs.cn-east-2.myhuaweicloud.com/mobileqmgo/index.html#/",

182
public/static/offline/JSON/getBrandShopList.json

File diff suppressed because one or more lines are too long

16351
public/static/offline/JSON/getBrandShopListByFloor.json

File diff suppressed because it is too large

16527
public/static/offline/JSON/getBrandShopListByIndustryId.json

File diff suppressed because it is too large

16
public/static/offline/JSON/getDevCoordinateByIP.json

@ -6,16 +6,16 @@
"machineTypeName": "集", "machineTypeName": "集",
"label": "windows", "label": "windows",
"screenAttribute": "命", "screenAttribute": "命",
"building": "A",
"building": "A",
"deviceCode": "11101009", "deviceCode": "11101009",
"buildingCode": "11101",
"projectCode": "project-200",
"floor": "L1层",
"floorOrder": 2,
"floorCode": "11101003",
"buildingCode": "5TUKOMgkahZYcBK2dd6Nx",
"projectCode": "project-1iijx369xueqhh5w8wyn0a",
"floor": "1F",
"floorOrder": 0,
"floorCode": "XqodUNp_NzIoQPdXeEgjQ",
"ip": "192.168.1.134", "ip": "192.168.1.134",
"mac": "Excepteur", "mac": "Excepteur",
"location": "43",
"angle": "nisi elit laboris"
"location": "0",
"angle": 0
} }
} }

2
public/static/offline/JSON/getMap.json

File diff suppressed because one or more lines are too long

11537
public/static/offline/JSON/getMapInfo.json

File diff suppressed because one or more lines are too long

22
public/static/offline/JSON/index.json

@ -1 +1,21 @@
{"code":200,"msg":"操作成功","data":{"hotSearch":[{"shopId":271,"shopName":"十日后"},{"shopId":506,"shopName":"弥小爷虾滑"},{"shopId":273,"shopName":"谭木匠"},{"shopId":425,"shopName":"星巴克"},{"shopId":485,"shopName":"荔滋烫捞"}],"temperature":25,"columnList":[{"moduleType":1,"name":"好吃的","nameEn":"DELICIOUS","introduce":"好吃的","introduceEn":"大吉大利今晚吃鸡","crossFileUrl":"/iotFile/2022/06/30/48001398f98b43ea9ba4b4a27e1bbe16.png","verticalFileUrl":"/iotFile/2022/06/21/ea1dfb78855e4ce9b9d0ab1b6e3a307d.png"},{"moduleType":2,"name":"好玩的","nameEn":"FUN","introduce":"好玩的","crossFileUrl":"/iotFile/2022/06/30/f94b26b8157f410b93f8346568a1766d.png","verticalFileUrl":"/iotFile/2022/06/21/50ac7a32182c469aa684dc34cfb11f21.png"},{"moduleType":3,"name":"值得买","nameEn":"WORTH TO BUY","introduce":"值得买","crossFileUrl":"/iotFile/2022/06/30/b85955b2f68a42938c886fac16278aa9.png","verticalFileUrl":"/iotFile/2022/06/20/f7515243eb234e00ae40fe662f79d484.png"}],"status":"多云转晴"}}
{
"code": 200,
"msg": "操作成功",
"data": {
"temperature": "11",
"columnList": [],
"hotSearch": [
{ "shopId": 43, "shopName": "心血管超声(心超)诊室2" },
{ "shopId": 63, "shopName": "临床心里科会议室" },
{ "shopId": 66, "shopName": "临床心里科诊室2" },
{ "shopId": 96, "shopName": "内镜手术室1" },
{ "shopId": 102, "shopName": "消化内镜中心VIP诊室2" },
{ "shopId": 155, "shopName": "妇科门诊护士台" },
{ "shopId": 286, "shopName": "呼吸内科/胸外科诊室1" },
{ "shopId": 342, "shopName": "儿科检验" },
{ "shopId": 358, "shopName": "特需门诊3" },
{ "shopId": 367, "shopName": "特需门诊19" }
],
"status": "晴"
}
}

237
public/static/offline/JSON/theme.json

@ -3,137 +3,156 @@
"msg": "操作成功", "msg": "操作成功",
"data": { "data": {
"image": { "image": {
"parking": "/qianmugo/themes/fashion/parking.png",
"recBg": "/qianmugo/themes/fashion/recBg.png",
"keyboard": "/qianmugo/themes/fashion/keyboard.png",
"loc": "/qianmugo/themes/fashion/loc.png",
"searchIcon": "/qianmugo/themes/fashion/searchIcon.png",
"activity": "/qianmugo/themes/fashion/activity.png",
"movie": "/qianmugo/themes/fashion/movie.png",
"write_active": "/qianmugo/themes/fashion/write_active.png",
"mall": "/qianmugo/themes/fashion/mall.png",
"gridActive": "/qianmugo/themes/fashion/gridActive.png",
"searchClear": "/qianmugo/themes/fashion/searchClear.png",
"back": "/qianmugo/themes/fashion/back.png",
"billboardLoc": "/qianmugo/themes/fashion/billboardLoc.png",
"billboardHeaderBgV": "/qianmugo/themes/fashion/billboardHeaderBgV.png",
"delete": "/qianmugo/themes/fashion/delete.png",
"keyboard_active": "/qianmugo/themes/fashion/keyboard_active.png",
"rowActive": "/qianmugo/themes/fashion/rowActive.png",
"grid": "/qianmugo/themes/fashion/grid.png",
"member": "/qianmugo/themes/fashion/member.png",
"row": "/qianmugo/themes/fashion/row.png",
"searchClose": "/qianmugo/themes/fashion/searchClose.png",
"write": "/qianmugo/themes/fashion/write.png",
"billboardHeaderBgH": "/qianmugo/themes/fashion/billboardHeaderBgH.png",
"brandQrcodeClose": "/qianmugo/themes/fashion/brandQrcodeClose.png"
"parking": "/qianmugo/themes/main/parking.png",
"recBg": "/qianmugo/themes/main/recBg.png",
"keyboard": "/qianmugo/themes/main/keyboard.png",
"loc": "/qianmugo/themes/main/loc.png",
"searchIcon": "/qianmugo/themes/main/searchIcon.png",
"activity": "/qianmugo/themes/main/activity.png",
"movie": "/qianmugo/themes/main/movie.png",
"write_active": "/qianmugo/themes/main/write_active.png",
"mall": "/qianmugo/themes/main/mall.png",
"gridActive": "/qianmugo/themes/main/gridActive.png",
"searchClear": "/qianmugo/themes/main/searchClear.png",
"back": "/qianmugo/themes/main/back.png",
"billboardLoc": "/qianmugo/themes/main/billboardLoc.png",
"billboardHeaderBgV": "/qianmugo/themes/main/billboardHeaderBgV.png",
"delete": "/qianmugo/themes/main/delete.png",
"keyboard_active": "/qianmugo/themes/main/keyboard_active.png",
"rowActive": "/qianmugo/themes/main/rowActive.png",
"grid": "/qianmugo/themes/main/grid.png",
"member": "/qianmugo/themes/main/member.png",
"row": "/qianmugo/themes/main/row.png",
"searchClose": "/qianmugo/themes/main/searchClose.png",
"write": "/qianmugo/themes/main/write.png",
"billboardHeaderBgH": "/qianmugo/themes/main/billboardHeaderBgH.png",
"brandQrcodeClose": "/qianmugo/themes/main/brandQrcodeClose.png"
},
"global": {
"background": "#dee6f6",
"appBackground": "#dee6f6",
"radius": ""
},
"weather": {
"iconColor": "#000",
"textColor": "#000"
},
"time": {
"timeColor": "#000",
"monthColor": "#000",
"weekColor": "#516dd8"
},
"searchBar": {
"backBorderRadius": "24px",
"background": "#fff",
"border": "none",
"borderRadius": "24px",
"placeholderColor": "rgba(0, 0, 0, 0.2)",
"stickBg": "linear-gradient(180deg, #6C7CA6 0%, #879ACA 100%)",
"color": "rgba(0, 0, 0, 0.8)"
},
"menu": {
"bg1": "rgba(255, 255, 255, 0.4)",
"bg2": "rgba(255, 255, 255, 0.4)",
"bg3": "rgba(255, 255, 255, 0.4)",
"bg4": "rgba(255, 255, 255, 0.4)",
"bg5": "rgba(255, 255, 255, 0.4)",
"activeBg1": "#ffffff",
"activeBg2": "#ffffff",
"activeBg3": "#ffffff",
"activeBg4": "#ffffff",
"activeBg5": "#ffffff",
"indexBg1": "#ffffff",
"indexBg2": "#ffffff",
"indexBg3": "#ffffff",
"indexBg4": "#ffffff",
"indexBg5": "#ffffff",
"color": "rgba(0, 0, 0, 0.4)",
"activeColor": "rgba(0, 0, 0, 0.8)",
"indexColor": "rgba(0, 0, 0, 0.8)",
"barBg": "linear-gradient(113.71deg, #435ACD 0%, #749CF3 100%)"
}, },
"index": { "index": {
"recBg": "rgba(255, 255, 255, 0.6)",
"guideBackground": "rgba(255, 255, 255, 0.6)",
"guideQrcodeBg": "#ffffff",
"guideBorder": "2px solid rgba(255, 255, 255, 0.6)",
"hotSearchTitleColor": "rgba(0, 0, 0, 0.8)",
"hotSearchBg": "rgba(255, 255, 255, 0.8)", "hotSearchBg": "rgba(255, 255, 255, 0.8)",
"hotSearchColor": "rgba(0, 0, 0, 0.6)", "hotSearchColor": "rgba(0, 0, 0, 0.6)",
"guideBackground": "linear-gradient(99.5deg, #f0b92b 0%, #f9d556 100%)",
"guideBorder": "none",
"guideTopBg": "linear-gradient(180deg, #435acd 0%, #749cf3 100%)",
"guideQrcodeBg": "rgba(255, 255, 255, 0.2)",
"guideColor": "#000000",
"guideMetaColor": "rgba(0, 0, 0, 0.6)", "guideMetaColor": "rgba(0, 0, 0, 0.6)",
"guideTopBg": "linear-gradient(180deg, #6257d7 0%, #f191c0 100%)",
"recBorder": "2px solid rgba(255, 255, 255, 0.6)",
"foodBg": "linear-gradient(180deg, #ffffff 0%, #f2f4f8 100%)",
"foodBorder": "4px solid #F68B51", "foodBorder": "4px solid #F68B51",
"hotSearchTitleColor": "#fff",
"guideColor": "#000000",
"foodBg": "rgba(255, 255, 255, 0.6)"
"recBg": "linear-gradient(180deg, #ffffff 0%, #f2f4f8 100%)",
"recBorder": "none"
}, },
"global": { "background": "linear-gradient(180deg, #6257d7 0%, #f191c0 100%)", "appBackground": "#dee6f6", "radius": "" },
"menu": {
"color": "#ffffff",
"activeBg5": "linear-gradient(180deg, #418fea 0%, #59b0f2 94.79%)",
"activeBg4": "linear-gradient(180deg, #c57df1 0%, #ec86f2 100%)",
"activeBg3": "linear-gradient(180deg, #53ba82 0%, #89d963 100%)",
"activeBg2": "linear-gradient(180deg, #f29348 0%, #f9ba61 100%)",
"indexBg3": "linear-gradient(180deg, #53ba82 0%, #89d963 100%)",
"activeBg1": "linear-gradient(180deg, #e97893 0%, #f48bca 100%)",
"indexBg4": "linear-gradient(180deg, #c57df1 0%, #ec86f2 100%)",
"indexBg5": "linear-gradient(180deg, #418fea 0%, #59b0f2 94.79%)",
"indexColor": "#FFFFFF",
"barBg": "linear-gradient(113.71deg, #435ACD 0%, #749CF3 100%)",
"bg2": "linear-gradient(180deg, #f29348 0%, #f9ba61 100%)",
"bg1": "linear-gradient(180deg, #e97893 0%, #f48bca 100%)",
"bg4": "linear-gradient(180deg, #c57df1 0%, #ec86f2 100%)",
"indexBg1": "linear-gradient(180deg, #e97893 0%, #f48bca 100%)",
"bg3": "linear-gradient(180deg, #53ba82 0%, #89d963 100%)",
"indexBg2": "linear-gradient(180deg, #f29348 0%, #f9ba61 100%)",
"bg5": "linear-gradient(180deg, #418fea 0%, #59b0f2 94.79%)",
"activeColor": "#ffffff"
"brand": {
"background": "rgba(255, 255, 255, 0.6)",
"color": "rgba(0, 0, 0, 0.8)",
"metaColor": "rgba(0, 0, 0, 0.6)",
"floorNameColor": "rgba(0, 0, 0, 0.8)",
"floorMetaColor": "rgba(0, 0, 0, 0.6)",
"qrcodeBg": "rgba(255, 255, 255, 0.8)",
"qrcodeTextColor": "rgba(0, 0, 0, 0.8)"
},
"food": {
"background": "linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.5) 100%)",
"metaColor": "rgba(0, 0, 0, 0.6)"
},
"carousel": {
"titleColor": "rgba(0, 0, 0, 0.8)",
"introColor": "rgba(0, 0, 0, 0.6)"
},
"map": {
"background": "none"
}, },
"carousel": { "titleColor": "#fff", "introColor": "#fff" },
"billboard": { "billboard": {
"titleColor": "#fff",
"background": "linear-gradient(180deg, #6257d7 0%, #f191c0 100%)",
"metaColor": "rgba(255, 255, 255, 0.8)",
"background": "#dee6f6",
"arrowRadius": "100px", "arrowRadius": "100px",
"legendColor": "rgba(255, 255, 255, 0.6)"
"legendColor": "rgba(0, 0, 0, 0.6)",
"titleColor": "#000000",
"metaColor": "rgba(0, 0, 0, 0.6)"
},
"guide": {
"floorBg": "linear-gradient(113.71deg, #435acd 0%, #749cf3 100%)",
"allFloorBg": "left / 150px 100% no-repeat linear-gradient(180deg, #6c7ca6 0%, #879aca 100%), #dee6f6",
"allFloorBgH": "right / 100px 100vh no-repeat var(--guide-floorBg), left / calc(100vw - 510px - 100px) 100vh no-repeat #dee6f6",
"arrowRadius": "100px",
"floorColor": "#FFFFFF",
"floorActiveColor": "rgba(0, 0, 0, 0.8)",
"floorActiveBg": "linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, #FFFFFF 100%)",
"currentBg": "linear-gradient(99.5deg, #F0B92B 0%, #F9D556 100%)",
"currentColor": "rgba(0, 0, 0, 0.8)"
}, },
"food": { "background": "linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.5) 100%)", "metaColor": "rgba(0, 0, 0, 0.6)" },
"search": { "search": {
"resultTitleColor": "rgba(0, 0, 0, 0.8)",
"background": "linear-gradient(180deg, #E0E3EE 0%, #D4D9E7 100%)",
"barBackground": "#fff",
"placeholderMetaColor": "rgba(81, 109, 216, 1)",
"keyboardActiveBg": "linear-gradient(180deg, #435ACD 0%, #749CF3 100%)",
"writeColor": "#516DD8", "writeColor": "#516DD8",
"topBg": "rgba(255, 255, 255, 0.6)", "topBg": "rgba(255, 255, 255, 0.6)",
"facTitleColor": "rgba(0, 0, 0, 0.8)",
"facNameColor": "rgba(0, 0, 0, 0.6)",
"tabsBg": "rgba(0, 0, 0, 0.05)",
"tabActiveBg": "#FFFFFF", "tabActiveBg": "#FFFFFF",
"tabColor": "rgba(0, 0, 0, 0.6)", "tabColor": "rgba(0, 0, 0, 0.6)",
"hotSearchBg": "rgba(255, 255, 255, 0.8)",
"placeholderMetaColor": "rgba(81, 109, 216, 1)",
"resultMetaColor": "rgba(0, 0, 0, 0.6)",
"hotSearchColor": "rgba(0, 0, 0, 0.6)",
"tabActiveColor": "rgba(0, 0, 0, 0.8)", "tabActiveColor": "rgba(0, 0, 0, 0.8)",
"facTitleColor": "rgba(0, 0, 0, 0.8)",
"tabsBg": "rgba(0, 0, 0, 0.05)",
"keyColor": "rgba(0, 0, 0, 0.8)",
"background": "linear-gradient(180deg, #E0E3EE 0%, #D4D9E7 100%)",
"facNameColor": "rgba(0, 0, 0, 0.6)",
"resultTitleColor": "rgba(0, 0, 0, 0.8)",
"resultMetaColor": "rgba(0, 0, 0, 0.6)",
"keyBg": "#FFFFFF", "keyBg": "#FFFFFF",
"barBackground": "#FFFFFF",
"keyboardActiveBg": "linear-gradient(180deg, #435ACD 0%, #749CF3 100%)",
"hotSearchTitleColor": "rgba(0, 0, 0, 0.8)"
"keyColor": "rgba(0, 0, 0, 0.8)",
"hotSearchTitleColor": "rgba(0, 0, 0, 0.8)",
"hotSearchBg": "rgba(255, 255, 255, 0.8)",
"hotSearchColor": "rgba(0, 0, 0, 0.6)"
}, },
"activities": { "activities": {
"btnBg": "linear-gradient(113.71deg, #435ACD 0%, #749CF3 100%)",
"btnColor": "#FFFFFF",
"detailBg": "#FFFFFF", "detailBg": "#FFFFFF",
"radius": "16px",
"titleColor": "rgba(0, 0, 0, 0.8)", "titleColor": "rgba(0, 0, 0, 0.8)",
"introColor": "rgba(0, 0, 0, 0.6)",
"btnColor": "#FFFFFF",
"btnBg": "linear-gradient(113.71deg, #435ACD 0%, #749CF3 100%)",
"radius": "16px"
},
"searchBar": {
"border": "2px solid #ffffff",
"stickBg": "#747ED3",
"borderRadius": "100px",
"placeholderColor": "rgba(0, 0, 0, 0.6)",
"color": "rgba(0, 0, 0, 0.8)",
"background": "rgba(255, 255, 255, 0.6)",
"backBorderRadius": "100px"
},
"weather": { "iconColor": "#fff", "textColor": "#fff" },
"time": { "monthColor": "#fff", "timeColor": "#fff", "weekColor": "#f0b92b" },
"brand": {
"floorMetaColor": "rgba(255, 255, 255, 0.8)",
"color": "rgba(0, 0, 0, 0.8)",
"qrcodeTextColor": "rgba(0, 0, 0, 0.8)",
"qrcodeBg": "rgba(255, 255, 255, 0.8)",
"background": "rgba(255, 255, 255, 0.6)",
"floorNameColor": "#fff",
"metaColor": "rgba(0, 0, 0, 0.6)"
},
"map": { "background": "#dee2f6" },
"guide": {
"floorActiveBg": "linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, #FFFFFF 100%)",
"allFloorBg": "left / 150px 100% no-repeat linear-gradient(180deg, #6257d7 0%, #f191c0 100%), #dee6f6",
"floorColor": "#FFFFFF",
"floorBg": "linear-gradient(90deg, #302159 0%, #7756b4 96.35%)",
"floorActiveColor": "rgba(0, 0, 0, 0.8)",
"allFloorBgH": "right / 100px 100vh no-repeat var(--guide-floorBg), left / calc(100vw - 510px - 100px) 100vh no-repeat #dee6f6",
"arrowRadius": "100px",
"currentColor": "rgba(0, 0, 0, 0.8)",
"currentBg": "linear-gradient(99.5deg, #F0B92B 0%, #F9D556 100%)"
"introColor": "rgba(0, 0, 0, 0.6)"
} }
} }
} }

BIN
public/static/offline/qianmugo/themes/fashion/activity.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/static/offline/qianmugo/themes/fashion/back.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

BIN
public/static/offline/qianmugo/themes/fashion/billboardHeaderBgH.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

BIN
public/static/offline/qianmugo/themes/fashion/billboardHeaderBgV.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 KiB

BIN
public/static/offline/qianmugo/themes/fashion/billboardLoc.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 888 B

BIN
public/static/offline/qianmugo/themes/fashion/mall.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/static/offline/qianmugo/themes/fashion/member.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

BIN
public/static/offline/qianmugo/themes/fashion/movie.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

BIN
public/static/offline/qianmugo/themes/fashion/parking.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/static/offline/qianmugo/themes/fashion/searchClear.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

BIN
public/static/offline/qianmugo/themes/fashion/searchClose.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

BIN
public/static/offline/qianmugo/themes/main/activity.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
public/static/offline/qianmugo/themes/main/back.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
public/static/offline/qianmugo/themes/main/billboardHeaderBgH.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
public/static/offline/qianmugo/themes/main/billboardHeaderBgV.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

0
public/static/offline/qianmugo/themes/fashion/loc.png → public/static/offline/qianmugo/themes/main/billboardLoc.png

Before

Width:  |  Height:  |  Size: 911 B

After

Width:  |  Height:  |  Size: 911 B

0
public/static/offline/qianmugo/themes/fashion/brandQrcodeClose.png → public/static/offline/qianmugo/themes/main/brandQrcodeClose.png

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

0
public/static/offline/qianmugo/themes/fashion/delete.png → public/static/offline/qianmugo/themes/main/delete.png

Before

Width:  |  Height:  |  Size: 668 B

After

Width:  |  Height:  |  Size: 668 B

0
public/static/offline/qianmugo/themes/fashion/grid.png → public/static/offline/qianmugo/themes/main/grid.png

Before

Width:  |  Height:  |  Size: 795 B

After

Width:  |  Height:  |  Size: 795 B

0
public/static/offline/qianmugo/themes/fashion/gridActive.png → public/static/offline/qianmugo/themes/main/gridActive.png

Before

Width:  |  Height:  |  Size: 766 B

After

Width:  |  Height:  |  Size: 766 B

10
public/static/offline/qianmugo/themes/main/index.js

@ -0,0 +1,10 @@
import style from './style.json'
function importAll(r) {
const obj = {}
r.keys().forEach(key => {
obj[key.replace('./', '').replace('.png', '')] = r(key)
})
return obj
}
const images = importAll(require.context('./', false, /\.(png|jpe?g|svg)$/))
export default { ...style, images }

0
public/static/offline/qianmugo/themes/fashion/keyboard.png → public/static/offline/qianmugo/themes/main/keyboard.png

Before

Width:  |  Height:  |  Size: 301 B

After

Width:  |  Height:  |  Size: 301 B

0
public/static/offline/qianmugo/themes/fashion/keyboard_active.png → public/static/offline/qianmugo/themes/main/keyboard_active.png

Before

Width:  |  Height:  |  Size: 304 B

After

Width:  |  Height:  |  Size: 304 B

BIN
public/static/offline/qianmugo/themes/main/loc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
public/static/offline/qianmugo/themes/main/mall.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
public/static/offline/qianmugo/themes/main/member.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
public/static/offline/qianmugo/themes/main/movie.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
public/static/offline/qianmugo/themes/main/parking.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

0
public/static/offline/qianmugo/themes/fashion/recBg.png → public/static/offline/qianmugo/themes/main/recBg.png

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 303 KiB

0
public/static/offline/qianmugo/themes/fashion/row.png → public/static/offline/qianmugo/themes/main/row.png

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 768 B

0
public/static/offline/qianmugo/themes/fashion/rowActive.png → public/static/offline/qianmugo/themes/main/rowActive.png

Before

Width:  |  Height:  |  Size: 738 B

After

Width:  |  Height:  |  Size: 738 B

BIN
public/static/offline/qianmugo/themes/main/searchClear.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
public/static/offline/qianmugo/themes/main/searchClose.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

0
public/static/offline/qianmugo/themes/fashion/searchIcon.png → public/static/offline/qianmugo/themes/main/searchIcon.png

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

128
public/static/offline/qianmugo/themes/main/style.json

@ -0,0 +1,128 @@
{
"global": {
"background": "#dee6f6",
"appBackground": "#dee6f6",
"radius": ""
},
"weather": {
"iconColor": "#000",
"textColor": "#000"
},
"time": {
"timeColor": "#000",
"monthColor": "#000",
"weekColor": "#516dd8"
},
"searchBar": {
"backBorderRadius": "24px",
"background": "#fff",
"border": "none",
"borderRadius": "24px",
"placeholderColor": "rgba(0, 0, 0, 0.2)",
"stickBg": "linear-gradient(180deg, #6C7CA6 0%, #879ACA 100%)",
"color": "rgba(0, 0, 0, 0.8)"
},
"menu": {
"bg1": "rgba(255, 255, 255, 0.4)",
"bg2": "rgba(255, 255, 255, 0.4)",
"bg3": "rgba(255, 255, 255, 0.4)",
"bg4": "rgba(255, 255, 255, 0.4)",
"bg5": "rgba(255, 255, 255, 0.4)",
"activeBg1": "#ffffff",
"activeBg2": "#ffffff",
"activeBg3": "#ffffff",
"activeBg4": "#ffffff",
"activeBg5": "#ffffff",
"indexBg1": "#ffffff",
"indexBg2": "#ffffff",
"indexBg3": "#ffffff",
"indexBg4": "#ffffff",
"indexBg5": "#ffffff",
"color": "rgba(0, 0, 0, 0.4)",
"activeColor": "rgba(0, 0, 0, 0.8)",
"indexColor": "rgba(0, 0, 0, 0.8)",
"barBg": "linear-gradient(113.71deg, #435ACD 0%, #749CF3 100%)"
},
"index": {
"hotSearchTitleColor": "rgba(0, 0, 0, 0.8)",
"hotSearchBg": "rgba(255, 255, 255, 0.8)",
"hotSearchColor": "rgba(0, 0, 0, 0.6)",
"guideBackground": "linear-gradient(99.5deg, #f0b92b 0%, #f9d556 100%)",
"guideBorder": "none",
"guideTopBg": "linear-gradient(180deg, #435acd 0%, #749cf3 100%)",
"guideQrcodeBg": "rgba(255, 255, 255, 0.2)",
"guideColor": "#000000",
"guideMetaColor": "rgba(0, 0, 0, 0.6)",
"foodBg": "linear-gradient(180deg, #ffffff 0%, #f2f4f8 100%)",
"foodBorder": "4px solid #F68B51",
"recBg": "linear-gradient(180deg, #ffffff 0%, #f2f4f8 100%)",
"recBorder": "none"
},
"brand": {
"background": "rgba(255, 255, 255, 0.6)",
"color": "rgba(0, 0, 0, 0.8)",
"metaColor": "rgba(0, 0, 0, 0.6)",
"floorNameColor": "rgba(0, 0, 0, 0.8)",
"floorMetaColor": "rgba(0, 0, 0, 0.6)",
"qrcodeBg": "rgba(255, 255, 255, 0.8)",
"qrcodeTextColor": "rgba(0, 0, 0, 0.8)"
},
"food": {
"background": "linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.5) 100%)",
"metaColor": "rgba(0, 0, 0, 0.6)"
},
"carousel": {
"titleColor": "rgba(0, 0, 0, 0.8)",
"introColor": "rgba(0, 0, 0, 0.6)"
},
"map": {
"background": "none"
},
"billboard": {
"background": "#dee6f6",
"arrowRadius": "100px",
"legendColor": "rgba(0, 0, 0, 0.6)",
"titleColor": "#000000",
"metaColor": "rgba(0, 0, 0, 0.6)"
},
"guide": {
"floorBg": "linear-gradient(113.71deg, #435acd 0%, #749cf3 100%)",
"allFloorBg": "left / 150px 100% no-repeat linear-gradient(180deg, #6c7ca6 0%, #879aca 100%), #dee6f6",
"allFloorBgH": "right / 100px 100vh no-repeat var(--guide-floorBg), left / calc(100vw - 510px - 100px) 100vh no-repeat #dee6f6",
"arrowRadius": "100px",
"floorColor": "#FFFFFF",
"floorActiveColor": "rgba(0, 0, 0, 0.8)",
"floorActiveBg": "linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, #FFFFFF 100%)",
"currentBg": "linear-gradient(99.5deg, #F0B92B 0%, #F9D556 100%)",
"currentColor": "rgba(0, 0, 0, 0.8)"
},
"search": {
"background": "linear-gradient(180deg, #E0E3EE 0%, #D4D9E7 100%)",
"barBackground": "#fff",
"placeholderMetaColor": "rgba(81, 109, 216, 1)",
"keyboardActiveBg": "linear-gradient(180deg, #435ACD 0%, #749CF3 100%)",
"writeColor": "#516DD8",
"topBg": "rgba(255, 255, 255, 0.6)",
"facTitleColor": "rgba(0, 0, 0, 0.8)",
"facNameColor": "rgba(0, 0, 0, 0.6)",
"tabsBg": "rgba(0, 0, 0, 0.05)",
"tabActiveBg": "#FFFFFF",
"tabColor": "rgba(0, 0, 0, 0.6)",
"tabActiveColor": "rgba(0, 0, 0, 0.8)",
"resultTitleColor": "rgba(0, 0, 0, 0.8)",
"resultMetaColor": "rgba(0, 0, 0, 0.6)",
"keyBg": "#FFFFFF",
"keyColor": "rgba(0, 0, 0, 0.8)",
"hotSearchTitleColor": "rgba(0, 0, 0, 0.8)",
"hotSearchBg": "rgba(255, 255, 255, 0.8)",
"hotSearchColor": "rgba(0, 0, 0, 0.6)"
},
"activities": {
"btnBg": "linear-gradient(113.71deg, #435ACD 0%, #749CF3 100%)",
"btnColor": "#FFFFFF",
"detailBg": "#FFFFFF",
"radius": "16px",
"titleColor": "rgba(0, 0, 0, 0.8)",
"introColor": "rgba(0, 0, 0, 0.6)"
}
}

0
public/static/offline/qianmugo/themes/fashion/write.png → public/static/offline/qianmugo/themes/main/write.png

Before

Width:  |  Height:  |  Size: 597 B

After

Width:  |  Height:  |  Size: 597 B

0
public/static/offline/qianmugo/themes/fashion/write_active.png → public/static/offline/qianmugo/themes/main/write_active.png

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

276
src/base/ShopItem/ShopItem.vue

@ -1,44 +1,9 @@
<template> <template>
<div :id="shop.houseNumber" class="group-item" :class="{ isRow, isFood, isGuide, isActive }" @click="handleShop">
<div class="logo-wrapper" :class="{ hasFood: isFood && shop.foodMaterialList.length }">
<img loading="lazy" :src="config.sourceUrl + (isFood && shop.foodMaterialList.length ? shop.foodMaterialList[0] : shop.logoUrl)" alt="" class="shop-logo" />
</div>
<div :id="shop.houseNumber" class="group-item isRow" @click="handleShop">
<p class="name"> <p class="name">
<span class="shop-name">{{ switchLanguage(shop, 'shopName') }}</span> <span class="shop-name">{{ switchLanguage(shop, 'shopName') }}</span>
<span class="name-right" v-if="$route.path === '/guide'"> {{ shop.distance ? shop.distance + '' : '' }}<img v-if="shop.dir" class="dir" :src="shop.dir" /> </span>
<span class="name-right" v-else>{{ shop.floor }}</span>
<span class="name-right">导览</span>
</p> </p>
<Transition appear enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
<div v-if="isActive && !isRow" class="qrcode-wrapper">
<QRCodeFromText
class="qrcode"
:size="120"
:text="`${config.mobileNav}?code=${currentFloor.projectCode}&s=${currentFloor.floorOrder}_${currentFloor.location}_屏幕的位置&e=${shop.houseNumber}`"
></QRCodeFromText>
<div class="qrcode-meta">扫码导航</div>
</div>
</Transition>
<Teleport to="body">
<Transition appear enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
<div v-if="isActive && isRow" class="qrcode-modal" @click="deactivate">
<div class="content" @click.stop="void 0">
<div
class="close"
:style="{
backgroundImage: `url(${theme.images.brandQrcodeClose}) `
}"
@click="deactivate"
></div>
<QRCodeFromText
class="qrcode"
:size="135"
:text="`${config.mobileNav}?code=${currentFloor.projectCode}&s=${currentFloor.floorOrder}_${currentFloor.location}_屏幕的位置&e=${shop.houseNumber}`"
></QRCodeFromText>
<div class="qrcode-meta">扫码导航</div>
</div>
</div>
</Transition></Teleport
>
</div> </div>
</template> </template>
@ -53,7 +18,6 @@ const { currentFloor, config, theme } = storeToRefs(store)
const props = defineProps({ const props = defineProps({
shop: Object, shop: Object,
config: Object, config: Object,
isRow: Boolean,
isFood: Boolean, isFood: Boolean,
isGuide: Boolean, isGuide: Boolean,
isActive: Boolean isActive: Boolean
@ -68,17 +32,15 @@ const deactivate = () => store.SET_SHOP(null)
<style lang="scss" scoped> <style lang="scss" scoped>
.group-item { .group-item {
width: 230px;
height: 200px;
background: var(--brand-background);
background: #ffffff;
border-radius: var(--global-radius, 8px); border-radius: var(--global-radius, 8px);
overflow: hidden; overflow: hidden;
.logo-wrapper {
width: 100%;
height: 160px;
padding: 20px 55px;
background-color: #fff;
}
display: flex;
width: 460px;
height: 76px;
padding-left: 12px;
align-items: center;
.shop-logo { .shop-logo {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -88,230 +50,28 @@ const deactivate = () => store.SET_SHOP(null)
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
height: 40px;
padding: 0 12px;
flex: 1;
padding-left: 24px;
} }
.shop-name { .shop-name {
font-weight: 700; font-weight: 700;
font-size: 14px;
line-height: 16px;
color: var(--brand-color, rgba(0, 0, 0, 0.8));
font-size: 20px;
line-height: 76px;
color: rgba(0, 0, 0, 0.8);
flex: 1; flex: 1;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.name-right { .name-right {
width: 120px;
display: flex; display: flex;
line-height: 76px;
align-items: center; align-items: center;
font-weight: 700;
font-size: 12px;
line-height: 14px;
font-family: 'font_bold';
color: var(--brand-metaColor, rgba(0, 0, 0, 0.4));
}
.dir {
width: 20px;
height: 20px;
margin-left: 7px;
background: linear-gradient(113.71deg, #435acd 0%, #749cf3 100%);
border-radius: var(--guide-arrowRadius);
}
&.isRow {
display: flex;
width: 460px;
height: 76px;
padding-left: 12px;
align-items: center;
.logo-wrapper {
width: 64px;
height: 64px;
flex: 0 0 64px;
padding: 7px;
}
.name {
flex: 1;
padding: 0 24px;
}
.shop-name {
font-weight: 700;
font-size: 18px;
line-height: 21px;
}
.name-right {
font-weight: 500;
font-size: 18px;
line-height: 21px;
}
.dir {
width: 32px;
height: 32px;
background: linear-gradient(113.71deg, #435acd 0%, #749cf3 100%);
border-radius: var(--guide-arrowRadius);
}
}
&.isFood {
display: flex;
flex-direction: column;
width: 230px;
height: 242px;
background: var(--food-background);
border-radius: var(--global-radius, 40px);
align-items: center;
.logo-wrapper {
width: 180px;
height: 180px;
background: rgba(255, 255, 255, 0.8);
border-radius: 200px;
padding: 30px;
&.hasFood {
padding: 0;
}
.shop-logo {
border-radius: 200px;
}
}
.name {
display: block;
width: 100%;
}
.shop-name {
display: block;
margin-top: 12px;
text-align: center;
}
.name-right {
display: block;
text-align: center;
font-family: 'Montserrat';
font-style: normal;
font-weight: 700;
font-size: 14px;
line-height: 17px;
color: var(--food-metaColor, --brand-mataColor, rgba(0, 0, 0, 0.6));
margin-top: 6px;
}
}
&.isActive {
position: relative;
.qrcode-wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(99.5deg, #f0b92b 0%, #f9d556 100%);
}
.qrcode {
height: 160px;
background: #ffffff;
border-radius: var(--global-radius, 8px);
display: flex;
justify-content: center;
align-items: center;
}
.qrcode-meta {
font-weight: 700;
font-size: 14px;
color: rgba(0, 0, 0, 0.8);
text-align: center;
line-height: 40px;
}
&.isFood {
.qrcode-wrapper {
background: #ffffff;
display: flex;
flex-direction: column;
align-items: center;
}
.qrcode {
height: 180px;
}
.qrcode-meta {
width: 194px;
height: 45px;
background: linear-gradient(99.5deg, #f0b92b 0%, #f9d556 100%);
border-radius: var(--global-radius, 100px);
line-height: 45px;
margin-top: 9px;
}
}
&.isRow {
background: linear-gradient(99.5deg, #f0b92b 0%, #f9d556 100%);
}
}
}
.qrcode-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
.content {
position: absolute;
width: 300px;
height: 300px;
left: 391px;
bottom: 427px;
background: var(--brand-qrcodeBg, rgba(255, 255, 255, 0.8));
box-shadow: 0px 20px 60px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(7px);
border-radius: var(--global-radius, 12px);
display: flex;
flex-direction: column;
align-items: center;
.close {
position: absolute;
width: 56px;
height: 56px;
right: 8px;
top: 8px;
background: center / cover no-repeat;
}
.qrcode {
width: 150px;
height: 150px;
display: flex;
justify-content: center;
align-items: center;
margin-top: 60px;
}
.qrcode-meta {
font-weight: 700; font-weight: 700;
font-size: 20px; font-size: 20px;
line-height: 23px;
text-align: center;
margin-top: 27px;
color: var(--brand-qrcodeTextColor, rgba(0, 0, 0, 0.8));
}
}
}
@media (min-aspect-ratio: 1/1) {
.group-item {
&.isGuide {
width: 190px;
&.isActive {
position: relative;
}
}
&.isRow {
width: 388px;
left: auto;
right: 105px;
&.isActive {
left: auto;
position: static;
}
}
}
.qrcode-modal {
.content {
left: auto;
right: 112px;
bottom: 280px;
}
color: #516dd8;
justify-content: center;
} }
} }
</style> </style>

239
src/components/ActivitiesList/ActivitiesList.vue

@ -1,239 +0,0 @@
<template>
<ScrollView class="scroll" :list="list" :scrollX="isH" :scrollbar="!isH">
<TransitionGroup name="zoom" tag="div" class="scroll-content">
<div :class="['content-item', item === activity ? 'd' : '']" v-for="item of list" :key="item.activityId">
<div class="front">
<img :src="config.sourceUrl + item.fileUrl" alt="" />
<div class="tr">{{ item.startDate }} - {{ item.endDate }}</div>
<div class="bottom">
<div class="left">{{ switchLanguage(item, 'activityName') }}</div>
<div class="right" @click="handleActivity(item)">查看详情</div>
</div>
</div>
<div class="back">
<ScrollView class="backscroll" scrollbar stopPropagation>
<div>
<div class="title">{{ switchLanguage(item, 'activityName') }}</div>
<div class="desc">
{{ switchLanguage(item, 'activityContent') }}
</div>
</div>
</ScrollView>
</div>
</div>
</TransitionGroup>
</ScrollView>
</template>
<script setup>
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import 'swiper/css'
import 'swiper/css/pagination'
import 'swiper/css/effect-coverflow'
import { useMediaQuery } from '@vueuse/core'
const isH = useMediaQuery('(min-aspect-ratio: 1/1)')
defineProps({
list: {
type: Array,
default: () => []
}
})
const showDetail = ref(false)
const activity = ref(null)
function handleActivity(item) {
activity.value = item
showDetail.value = true
}
const store = useStore()
const { config } = storeToRefs(store)
</script>
<style lang="scss" scoped>
.scroll {
position: relative;
flex: 1;
overflow: hidden;
:deep(.bscroll-vertical-scrollbar) {
width: 48px !important;
top: 48px !important;
right: 7px !important;
background: center / 6px 250px no-repeat url(@/assets/images/scrollBar.png);
border-radius: 6px;
opacity: 1 !important;
height: 250px !important;
&::after {
position: absolute;
content: '';
left: 0;
top: 120px;
margin: auto;
width: 48px;
height: 61px;
background: center / cover no-repeat url(@/assets/images/scrollHand.png);
}
.bscroll-indicator {
height: 95px !important;
width: 6px !important;
left: 0;
right: 0;
margin: auto;
background: #ffffff !important;
border-radius: 6px !important;
border: none !important;
}
}
.scroll-content {
width: 100%;
display: inline-flex;
flex-direction: column;
align-items: center;
padding: 48px 0;
}
.content-item {
position: relative;
width: 944px;
height: 532px;
background: var(--activities-detailBg);
border-radius: var(--activities-radius);
overflow: hidden;
margin-bottom: 24px;
&.d {
.front {
z-index: 0;
}
.back {
z-index: 1;
}
}
.front {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.tr {
position: absolute;
top: 0;
right: 0;
font-family: 'Montserrat';
background: rgba(0, 0, 0, 0.4);
box-shadow: 0px 15px 24px rgba(173, 196, 236, 0.25);
font-weight: 700;
font-size: 16px;
line-height: 40px;
color: #ffffff;
padding: 0 27px;
height: 40px;
border-radius: 0px 0px 0px 12px;
}
.bottom {
position: absolute;
display: flex;
bottom: 0;
width: 100%;
height: 64px;
.left {
flex: 1;
height: 100%;
padding: 0 40px;
background: rgba(0, 0, 0, 0.4);
font-weight: 700;
font-size: 18px;
line-height: 64px;
color: #ffffff;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.right {
display: flex;
align-items: center;
justify-content: center;
width: 234px;
height: 100%;
background: var(--activities-btnBg);
box-shadow: 0px 15px 24px rgba(173, 196, 236, 0.25);
font-weight: 700;
font-size: 18px;
line-height: 21px;
text-align: center;
color: var(--activities-btnColor);
}
}
}
.back {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
z-index: 0;
background: var(--activities-detailBg);
.backscroll {
position: relative;
flex: 1;
:deep(.bscroll-vertical-scrollbar) {
width: 6px !important;
top: 137px !important;
right: 31px !important;
background: rgba(0, 0, 0, 0.05) !important;
border-radius: 6px;
opacity: 1 !important;
height: 174px !important;
&::after {
display: none;
}
.bscroll-indicator {
height: 95px !important;
width: 6px !important;
left: 0;
right: 0;
margin: auto;
background: rgba(0, 0, 0, 0.1) !important;
border-radius: 6px !important;
border: none !important;
}
}
.title {
font-weight: 700;
font-size: 28px;
line-height: 150%;
padding: 48px 68px 40px 68px;
color: var(--activities-titleColor);
}
.desc {
padding: 0 68px;
padding-bottom: 23px;
font-weight: 400;
font-size: 16px;
line-height: 200%;
color: var(--activities-introColor);
}
}
}
}
}
@media (min-aspect-ratio: 1/1) {
.scroll {
.scroll-content {
width: auto;
flex-direction: row;
padding: 100px 68px;
}
.content-item + .content-item {
margin-left: 32px;
}
}
}
</style>

200
src/components/ActivityDetail/ActivityDetail.vue

@ -1,200 +0,0 @@
<template>
<Dialog @close="back">
<div class="content">
<div class="carousel">
<EffectFade :list="[activity.fileUrl, activity.fileUrl]">
<template v-slot="{ item }">
<div class="banner-wrapper">
<img :src="config.sourceUrl + item" alt="" class="banner" />
</div>
</template>
</EffectFade>
</div>
<div class="intro-content">
<h1 class="name pos">{{ switchLanguage(activity, 'activityName') }}</h1>
<ScrollView class="content-scroll" scrollbar>
<p class="intro pos">{{ switchLanguage(activity, 'activityContent') }}</p>
</ScrollView>
<div class="time-wrapper pos">
<p class="common first">{{ $t('ActivityTime') }}{{ activity.startDate }} {{ $t('zhi') + activity.endDate }}</p>
<p class="common">{{ $t('activityAddress') }}{{ switchLanguage(activity, 'activityAddress') }}</p>
</div>
<Go class="detail-go" v-if="activity.shopId || activity.point" @click="handleActivity" />
</div>
<Button class="btn" @click="back" />
<ThumbQRCode class="thumbs" :title="$t('yuyue')" />
</div>
</Dialog>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { useStore } from '@/store/root'
import Dialog from '@/layouts/Dialog.vue'
import Button from '@/base/Button/Button.vue'
import EffectFade from '@/components/EffectFade/EffectFade.vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import Go from '@/base/Go/Go.vue'
import ThumbQRCode from '@/base/ThumbQRCode/ThumbQRCode.vue'
import Shop from '@/utils/Class/Shop'
const MALL_TYPE = 1 //
const BRAND_TYPE = 2 //
const MEMBER_TYPE = 3 //
const props = defineProps({
activity: Object,
config: Object
})
const router = useRouter()
const store = useStore()
function handleActivity() {
let shop
if (props.activity.activityType === BRAND_TYPE) {
shop = store.shopList.find(item => item.shopId === props.activity.shopId)
}
if (props.activity.activityType === MALL_TYPE || props.activity.activityType === MEMBER_TYPE) {
const { floorOrder, floor, activityName, fileUrl, point } = props.activity
shop = new Shop(activityName, floorOrder, floor, fileUrl, point)
}
if (!shop) {
return
}
store.SET_SHOP(shop)
router.push('/nav')
}
const emits = defineEmits(['close'])
function back() {
emits('close')
}
</script>
<style lang="scss" scoped>
.pos {
position: relative;
z-index: 5;
}
.content {
position: relative;
width: 700px;
margin: 0 auto;
margin-top: 350px;
z-index: 2;
.thumbs {
position: absolute;
top: 120px;
right: -156px;
}
&::before {
content: '';
position: absolute;
top: 373px;
left: 0;
z-index: 2;
width: 700px;
height: 420px;
background: #ffffff;
border-radius: 12px;
}
.intro-content {
position: relative;
height: 420px;
.name {
padding: 28px 56px 32px 56px;
font-weight: 700;
font-size: 24px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.8);
}
}
.common {
font-weight: 700;
font-size: 14px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
margin-left: 56px;
&.first {
padding-bottom: 8px;
}
}
.content-scroll {
position: relative;
height: 140px;
overflow: hidden;
margin: 0 34px 64px 56px;
padding-right: 16px;
:deep(.bscroll-vertical-scrollbar) {
width: 6px !important;
background: rgba(0, 0, 0, 0.02) !important;
border-radius: 6px !important;
opacity: 1 !important;
.bscroll-indicator {
width: 6px !important;
background: rgba(0, 0, 0, 0.1) !important;
border-radius: 6px !important;
border: none !important;
}
}
.intro {
font-weight: 400;
font-size: 14px;
line-height: 200%;
text-align: justify;
color: rgba(0, 0, 0, 0.6);
margin-bottom: 64px;
}
}
.detail-go {
bottom: -70px;
}
.carousel {
width: 700px;
height: 393px;
:deep(.swiper) {
overflow: visible !important;
z-index: auto;
}
:deep(.swiper-pagination) {
right: 32px;
left: auto;
bottom: 5px;
width: auto;
}
:deep(.swiper-pagination-bullet) {
width: 16px !important;
height: 3px !important;
background: rgba(0, 0, 0, 0.1);
border-radius: 3px !important;
opacity: inherit !important;
&.swiper-pagination-bullet-active {
background: #f1b33e;
border-radius: 3px !important;
}
}
}
.banner-wrapper {
width: 700px;
height: 393px;
border-radius: 12px;
.banner {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 12px;
}
}
.btn {
position: absolute;
top: 0;
right: -120px;
}
}
</style>

455
src/components/BrandDetail/BrandDetail.vue

@ -1,455 +0,0 @@
<template>
<Dialog @close="back">
<div class="content" :class="{ active: shop.activityList.length || shop.thirdKouCode }">
<div class="left">
<div class="carousel">
<EffectFade :list="shop.doorMaterialList">
<template v-slot="{ item }">
<div class="banner-wrapper">
<img :src="config.sourceUrl + item" alt="" class="banner" />
</div>
</template>
</EffectFade>
<div class="like-wrapper" @click="setLike">
<img :src="showLikeHeart ? require('@/assets/images/detail/like_active.svg') : require('@/assets/images/detail/like.svg')" class="like-icon" alt="" />
<span class="like-num">{{ likeNumber }}</span>
</div>
</div>
<div class="info-content">
<div class="shop-info">
<div class="logo-wrapper">
<img :src="config.sourceUrl + shop.logoUrl" alt="" class="logo" />
</div>
<div class="group" v-if="shop.industryUrl?.length">
<div class="icon-box">
<img :src="config.sourceUrl + shop.industryUrl" alt="" />
</div>
<div class="group-right">
<span class="tip-name">业态</span>
<span class="name">{{ switchLanguage(shop, 'industryFatherName') }}</span>
</div>
</div>
<div class="group" v-if="shop.houseNumber?.length">
<div class="icon-box">
<img src="../../assets/images/nav/detail-add.svg" alt="" />
</div>
<div class="group-right">
<span class="tip-name">地址</span>
<span class="name">{{ shop.houseNumber }}</span>
</div>
</div>
<div class="group" v-if="shop.contact?.length">
<div class="icon-box">
<img src="../../assets/images/nav/phone.svg" alt="" />
</div>
<div class="group-right">
<span class="tip-name">电话</span>
<span class="name">{{ shop.contact }}</span>
</div>
</div>
<div class="group" v-if="shop.businessHours?.length">
<div class="icon-box">
<img src="../../assets/images/nav/time.svg" alt="" />
</div>
<div class="group-right">
<span class="tip-name">营业时间</span>
<span class="name">{{ shop.businessHours }}</span>
</div>
</div>
</div>
<div class="shop-intro">
<p class="shop-name">{{ switchLanguage(shop, 'shopName') }}</p>
<ScrollView class="intro-scroll" scrollbar>
<p class="intro">{{ switchLanguage(shop, 'intro') }}</p>
</ScrollView>
</div>
<Go class="detail-go" @click="go" />
</div>
<div class="lineup-wrapper">
<div class="lineup-left">
<h1 class="title-tip">{{ $t('qiandengdai') }}</h1>
<div class="flex align-center">
<ScrollView class="lineup-scroll" scroll-x>
<div class="lineup-content">
<div class="flex">
<div class="lineup-item" v-for="item of 10" :key="item">
<span class="number">12</span>
<span class="line-up-text">小桌</span>
</div>
</div>
</div>
</ScrollView>
<div class="line"></div>
<p class="abs-money">147<i>/</i></p>
</div>
</div>
<div class="lineup-btn" @click="handleLineupBtn">{{ $t('join') }}</div>
</div>
</div>
<div class="right" v-if="shop.activityList.length || shop.thirdKouCode">
<Tabs />
</div>
<Button class="btn" @click="back" />
<ThumbQRCode class="thumbs" :title="$t('yuyue')" />
<Transition enter-active-class="animate__animated animate__zoomIn" leave-active-class="animate__animated animate__zoomOut">
<QRCode v-if="showLineupQr" @close="showLineupQr = false" />
</Transition>
</div>
</Dialog>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router'
import { useStore } from '@/store/root'
import { getBrandLikesNumber, setBrandLikesNumber } from '@/http/api'
import Dialog from '@/layouts/Dialog.vue'
import Button from '@/base/Button/Button.vue'
import EffectFade from '@/components/EffectFade/EffectFade.vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import Go from '@/base/Go/Go.vue'
import ThumbQRCode from '@/base/ThumbQRCode/ThumbQRCode.vue'
import Tabs from './children/tabs.vue'
import { useStatistics } from '@/composables/useStatistics'
const QRCode = defineAsyncComponent(() => import('@/components/QRCode/QRCode.vue'))
const router = useRouter()
const store = useStore()
const { shop, config, showDetail, showSearch, showVoice } = storeToRefs(store)
useStatistics('shop')
const showLikeHeart = ref(false)
const likeNumber = ref(0)
getBrandLikesNumber(shop.value.shopId).then(({ data }) => {
likeNumber.value = data ?? 0
})
function setLike() {
setBrandLikesNumber(shop.value.shopId).then(() => {
showLikeHeart.value = true
likeNumber.value++
})
}
const showLineupQr = ref(false)
function handleLineupBtn() {
showLineupQr.value = true
}
async function go() {
await router.push('/nav')
showDetail.value && store.SET_SHOW_DETAIL(false)
showSearch.value && store.SET_SHOW_SEARCH(false)
showVoice.value && store.SET_SHOW_VOICE(false)
}
function back() {
store.SET_SHOW_DETAIL(false)
}
</script>
<style lang="scss" scoped>
.content {
position: absolute;
top: 350px;
left: 50%;
transform: translate3d(-50%, 0, 0);
z-index: 2;
&.active {
top: 100px;
}
&::before {
content: '';
position: absolute;
top: 373px;
left: 0;
z-index: 2;
width: 700px;
height: 420px;
background: linear-gradient(180deg, #ffbd35 0%, #ffd260 100%);
border-radius: 12px;
}
.thumbs {
position: absolute;
top: 120px;
right: -156px;
}
.detail-go {
bottom: -90px;
}
.left {
width: 700px;
.carousel {
position: relative;
width: 700px;
height: 393px;
background-color: #fff;
border-radius: 12px;
overflow: hidden;
.like-wrapper {
position: absolute;
left: 571px;
top: 32px;
height: 52px;
display: flex;
align-items: center;
padding: 0 14px;
background: rgba(0, 0, 0, 0.2);
border-radius: 54px;
z-index: 1;
.like-num {
padding-left: 8px;
font-weight: 700;
font-size: 20px;
font-family: 'font_bold';
color: #ffffff;
}
}
:deep(.swiper) {
overflow: visible !important;
z-index: auto;
}
:deep(.swiper-pagination) {
right: 32px;
left: auto;
bottom: 5px;
width: auto;
}
:deep(.swiper-pagination-bullet) {
width: 16px !important;
height: 3px !important;
background: rgba(0, 0, 0, 0.1);
border-radius: 3px !important;
opacity: inherit !important;
&.swiper-pagination-bullet-active {
background: rgba(255, 255, 255, 1);
border-radius: 3px !important;
}
}
}
.banner-wrapper {
width: 700px;
height: 393px;
border-radius: 12px;
.banner {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 12px;
}
}
.info-content {
position: relative;
display: flex;
align-items: stretch;
width: 700px;
height: 420px;
margin-top: -20px;
}
.shop-info {
position: relative;
z-index: 3;
width: 225px;
background: rgba(0, 0, 0, 0.02);
border-radius: 0px 0px 0px 12px;
padding-left: 32px;
.logo-wrapper {
width: 160px;
height: 160px;
background: #ffffff;
box-shadow: 0px 20px 30px rgba(221, 152, 55, 0.5);
border-radius: 16px;
padding: 20px;
margin-bottom: 32px;
margin-top: -51px;
.logo {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.group {
display: flex;
align-items: center;
margin-bottom: 18px;
.icon-box {
width: 36px;
height: 36px;
background: rgba(0, 0, 0, 0.05);
border-radius: 6px;
margin-right: 12px;
padding: 9px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.group-right {
display: flex;
flex-direction: column;
justify-content: center;
.tip-name {
font-size: 12px;
transform: scale(0.83);
color: rgba(0, 0, 0, 0.4);
padding-bottom: 6px;
}
.name {
font-weight: 700;
font-family: 'font_bold';
font-size: 12px;
line-height: 14px;
color: rgba(0, 0, 0, 0.6);
}
}
}
}
.shop-intro {
position: relative;
z-index: 3;
flex: 1;
padding-top: 58px;
padding-left: 40px;
.shop-name {
font-weight: 700;
font-size: 32px;
line-height: 38px;
text-align: justify;
color: rgba(0, 0, 0, 0.8);
font-family: 'font_bold';
padding-bottom: 40px;
}
.intro-scroll {
position: relative;
overflow: hidden;
height: 207px;
margin-right: 16px;
padding-right: 18px;
.intro {
font-size: 14px;
line-height: 200%;
text-align: justify;
color: rgba(0, 0, 0, 0.6);
}
flex: 1;
overflow: hidden;
:deep(.bscroll-vertical-scrollbar) {
width: 6px !important;
background: rgba(0, 0, 0, 0.02) !important;
border-radius: 6px !important;
opacity: 1 !important;
height: 200px !important;
.bscroll-indicator {
height: 95px !important;
width: 6px !important;
background: rgba(0, 0, 0, 0.1) !important;
border-radius: 6px !important;
border: none !important;
}
}
}
}
.flex {
display: flex;
}
.align-center {
align-items: flex-end;
}
.lineup-wrapper {
display: flex;
align-items: flex-end;
margin-top: -20px;
padding: 79px 16px 16px 32px;
background: #ffffff;
border-radius: 12px;
.lineup-left {
flex: 1;
.title-tip {
font-weight: 700;
font-size: 14px;
font-family: 'font_bold';
text-align: justify;
color: rgba(0, 0, 0, 0.4);
padding-bottom: 15px;
}
}
.lineup-scroll {
display: flex;
width: 310px;
overflow: hidden;
margin-right: 40px;
.lineup-content {
display: inline-block;
white-space: nowrap;
.lineup-item {
display: flex;
align-items: flex-end;
margin-right: 24px;
}
.number {
font-weight: 700;
font-size: 24px;
line-height: 22px;
color: rgba(0, 0, 0, 0.6);
font-family: 'font_bold';
padding-right: 4px;
}
.line-up-text {
font-weight: 700;
font-size: 14px;
line-height: 16px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.2);
}
}
}
.line {
width: 1px;
height: 20px;
background: rgba(0, 0, 0, 0.1);
margin-right: 40px;
}
.abs-money {
font-weight: 700;
font-family: 'font_bold';
font-size: 24px;
text-align: right;
color: #f1b33e;
i {
font-size: 14px;
color: rgba(0, 0, 0, 0.2);
}
}
.lineup-btn {
background: linear-gradient(90deg, #f6a62c 0%, #ffbc3f 100%);
box-shadow: 0px 8px 20px rgba(221, 152, 55, 0.2);
border-radius: 24px;
width: 128px;
height: 37px;
font-weight: 700;
font-family: 'font_bold';
font-size: 14px;
line-height: 37px;
text-align: center;
color: #fff;
}
}
}
.right {
width: 700px;
flex: 1;
margin-top: 40px;
}
.btn {
position: absolute;
top: 0;
right: -120px;
}
}
</style>

266
src/components/BrandDetail/children/Activity.vue

@ -1,266 +0,0 @@
<template>
<div>
<ScrollView class="activity-scroll" :class="{ active: tab.length === 1 }" scrollbar>
<div class="scroll-content">
<h1 class="title">{{ $t('brandActivity') }}</h1>
<div class="activity-item" v-for="item of list" :key="item.activityId">
<div class="img-wrapper">
<img :src="config.sourceUrl + item.fileUrl" alt="" class="img" />
</div>
<div class="flex-right">
<div class="activity-title">
<p class="name">{{ switchLanguage(item, 'activityName') }}</p>
<p class="look" @click="handleActivity(item)">{{ $t('lookActivity') }}</p>
</div>
<p class="activity-intro">{{ switchLanguage(item, 'activityContent') }}</p>
</div>
</div>
<h1 class="title">{{ $t('coupon') }}</h1>
<div class="coupon-item">
<div class="coupon-left">
<p class="money">¥<i>30</i></p>
<div class="coupon-info">
<p class="coupon-name">30元代金券满129可用</p>
<p class="coupon-time">使用时间2021-01-01至2021-12-30</p>
</div>
</div>
<div class="coupon-btn">{{ $t('get') }}</div>
</div>
</div>
</ScrollView>
<div class="activity-detail" v-if="showActivityDetail">
<h1 class="activity-title">{{ switchLanguage(activity, 'activityName') }}</h1>
<ScrollView class="scroll" scrollbar>
<p class="scroll-content">{{ switchLanguage(activity, 'activityContent') }}</p>
</ScrollView>
<div class="common first">{{ $t('ActivityTime') }}{{ activity.startDate }} {{ $t('zhi') + activity.endDate }}</div>
<div class="common">{{ $t('activityAddress') }}{{ switchLanguage(activity, 'activityAddress') }}</div>
<div class="close-activity" @click="showActivityDetail = false">
<img src="../../../assets/images/detail/yellow_close.png" alt="" />
</div>
</div>
</div>
</template>
<script setup>
import { computed, ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
defineProps({
tab: Array
})
const store = useStore()
const { shop, config } = storeToRefs(store)
const list = computed(() => shop.value.activityList)
const showActivityDetail = ref(false)
const activity = ref({})
function handleActivity(item) {
activity.value = item
showActivityDetail.value = true
}
</script>
<style lang="scss" scoped>
.activity-scroll {
position: relative;
overflow: hidden;
height: 690px;
&.active {
height: 860px;
}
.title {
font-weight: 700;
font-family: 'font_bold';
font-size: 24px;
margin-bottom: 24px;
color: rgba(0, 0, 0, 0.8);
}
.activity-item {
display: flex;
align-items: center;
width: 700px;
height: 106px;
background: rgba(255, 255, 255, 0.8);
border-radius: 12px;
margin-bottom: 16px;
padding-right: 16px;
padding-left: 6px;
&:last-of-type {
margin-bottom: 40px;
}
.img-wrapper {
width: 160px;
height: 90px;
border-radius: 8px;
overflow: hidden;
margin-right: 32px;
.img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.flex-right {
flex: 1;
.activity-title {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 8px;
.name {
font-weight: 700;
font-size: 20px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
}
.look {
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.6);
background: url('../../../assets/images/detail/arrow.svg') no-repeat right center;
background-size: 18px 18px;
padding-right: 26px;
}
}
.activity-intro {
font-weight: 400;
font-size: 14px;
line-height: 150%;
color: rgba(0, 0, 0, 0.4);
@include more-wrap;
}
}
}
.coupon-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px 0 32px;
width: 700px;
height: 106px;
margin-bottom: 16px;
background: rgba(255, 255, 255, 0.8);
border-radius: 12px;
.coupon-left {
display: flex;
align-items: center;
}
.coupon-name {
font-weight: 700;
font-size: 18px;
line-height: 21px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
padding-bottom: 6px;
}
.coupon-time {
font-size: 14px;
color: rgba(0, 0, 0, 0.4);
}
.money {
font-weight: 700;
font-family: 'font_bold';
font-size: 24px;
color: #f1b33e;
margin-right: 27px;
i {
font-size: 48px;
padding-left: 4px;
}
}
.coupon-btn {
width: 140px;
height: 48px;
text-align: center;
line-height: 48px;
background: linear-gradient(90deg, #f6a62c 0%, #ffbc3f 100%);
box-shadow: 0px 20px 30px rgba(221, 152, 55, 0.2);
border-radius: 160px;
color: #fff;
font-weight: 700;
font-family: 'font_bold';
}
}
}
.activity-detail {
position: absolute;
width: 832px;
min-height: 396px;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
background: #ffffff;
box-shadow: 0px 40px 60px rgba(0, 0, 0, 0.08);
border-radius: 12px;
z-index: 10;
padding-top: 48px;
padding-left: 56px;
.activity-title {
font-weight: 700;
font-size: 24px;
line-height: 28px;
text-align: justify;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.8);
margin-bottom: 32px;
}
.scroll {
position: relative;
height: 155px;
overflow: hidden;
margin-right: 34px;
padding-right: 16px;
:deep(.bscroll-vertical-scrollbar) {
width: 6px !important;
background: rgba(0, 0, 0, 0.02) !important;
border-radius: 6px !important;
opacity: 1 !important;
.bscroll-indicator {
width: 6px !important;
background: rgba(0, 0, 0, 0.1) !important;
border-radius: 6px !important;
border: none !important;
}
}
.scroll-content {
font-size: 14px;
line-height: 200%;
text-align: justify;
color: rgba(0, 0, 0, 0.6);
}
}
.common {
font-weight: 700;
font-size: 14px;
line-height: 16px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
&.first {
margin-bottom: 12px;
padding-top: 49px;
}
}
.close-activity {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 12px;
right: 12px;
width: 56px;
height: 56px;
background: rgba(0, 0, 0, 0.02);
border-radius: 12px;
img {
width: 56px;
height: 56px;
}
}
}
</style>

123
src/components/BrandDetail/children/Comment.vue

@ -1,123 +0,0 @@
<template>
<div class="wrapper">
<h1 class="title">{{ $t('comment') }}</h1>
<div class="score-wrapper">
<div class="score-num">{{ score }}</div>
<div class="star">
<div class="star-num">
<img
:src="item === 0 ? require('@/assets/images/detail/star_active.svg') : require('@/assets/images/detail/star.svg')"
v-for="item of starList()"
:key="item"
alt=""
class="star-icon"
/>
</div>
<div class="total-comment">{{ totalComment }} {{ $t('totalComment') }}</div>
</div>
</div>
<ScrollView class="scroll" :list="commentList" scrollbar>
<ul class="comments">
<li class="comments-item" v-for="item of commentList" :key="item.tag_count">
<span class="txt">
{{ item.tag_content }}
<span>{{ item.tag_count }}</span>
</span>
</li>
</ul>
</ScrollView>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
const comment = ref({
comment_info: {
avg_score: '2.912871287128713',
comment_tag_list: {
comment_tag: [
{
emotion: 1,
tag_content: '服务周到',
tag_count: 6
}
]
},
image_count: 34,
total_comments: 505
},
has_more: false,
page_size: 20
})
const commentList = computed(() => comment.value?.comment_info?.comment_tag_list?.comment_tag ?? [])
const score = computed(() => Math.floor(comment.value?.comment_info?.avg_score) ?? 0)
const totalComment = computed(() => comment.value?.comment_info?.total_comments ?? 0)
function starList() {
return new Array(5).fill(0, 0, score.value).fill(1, score.value)
}
</script>
<style lang="scss" scoped>
.title {
font-weight: 700;
font-family: 'font_bold';
font-size: 24px;
color: rgba(0, 0, 0, 0.8);
margin-bottom: 24px;
}
.scroll {
position: relative;
height: 618px;
overflow: hidden;
.comments {
display: flex;
flex-wrap: wrap;
.comments-item {
height: 43px;
background: #ffffff;
border-radius: 8px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
height: 40px;
padding: 0 24px;
margin: 0 16px 16px 0;
span {
font-size: 16px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
font-weight: 700;
}
}
}
}
.star-num {
margin-bottom: 4px;
}
.star-icon {
width: 20px;
height: 20px;
margin-right: 8px;
}
.score-wrapper {
display: flex;
align-items: center;
margin-bottom: 25px;
.score-num {
font-weight: 700;
font-family: 'font_bold';
font-size: 48px;
color: #f1b33e;
margin-right: 19px;
}
.total-comment {
font-size: 14px;
color: rgba(0, 0, 0, 0.4);
}
}
</style>

155
src/components/BrandDetail/children/Goods.vue

@ -1,155 +0,0 @@
<template>
<div class="wrapper">
<h1 class="title">{{ $t('goods') }}</h1>
<ScrollView class="scroll" :list="goodsList">
<ul class="goods">
<li class="goods-item" v-for="item of goodsList" :key="item.item_name">
<div class="cover">
<img :src="item.item_cover" alt="" class="goods-img" />
</div>
<div class="goods-right">
<h1 class="goods-title">{{ item.item_name }}</h1>
<h1 class="goods-sub">{{ item.sub_title }}</h1>
<h1 class="price">
<span class="sell">
¥<i>{{ item.sell_price }}</i>
</span>
<span class="original">
¥<i>{{ item.original_price }}</i>
</span>
</h1>
</div>
<div class="button" @click="handleGoods(item)">{{ $t('buy') }}</div>
</li>
</ul>
</ScrollView>
<QRCode v-if="showCodeRef" :url="goodsRef.item_detail_url" :title="$t('scanBuy')" @click="show(false)" />
</div>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
const QRCode = defineAsyncComponent(() => import('@/base/QRCode/QRCode.vue'))
const goodsList = ref([
{
item_store: { store_name: '大紧特紧测试门店' },
item_detail_url:
'alipays://platformapi/startapp?appId=77700272&amp;startMultApp=YES&amp;query=itemId%3D613552052869%26channel%3DALL%26cityId%3D330100%26storeId%3D230021002%26sourceFrom%3DITEM_DETAIL_BASE_INFO&amp;url=%2Findex.html%23pages%2Findex%2Findex&amp;chInfo=ch_tribeopen__chsub_yinli',
item_cover: 'https://wx1.sinaimg.cn/orj360/004j2Ftyly1gt4lt4kltaj60u01c6n8r02.jpg',
item_name: '这是商品的名称',
sub_title: '正文介绍正文介绍正文介绍正文介绍正文介绍正文介绍正文介绍正文介绍正文介绍正文介绍正文介绍正文介绍',
sell_price: '333',
original_price: '11111'
}
])
const showCodeRef = ref(false)
const goodsRef = ref({})
function handleGoods(item) {
goodsRef.value = item
showCodeRef.value = true
}
function show(flag) {
showCodeRef.value = flag
}
</script>
<style lang="scss" scoped>
.title {
font-weight: 700;
font-family: 'font_bold';
font-size: 24px;
color: rgba(0, 0, 0, 0.8);
margin-bottom: 24px;
}
.scroll {
position: relative;
overflow: hidden;
height: 690px;
}
.goods {
padding-bottom: 8px;
}
.goods-item {
width: 700px;
height: 156px;
background: rgba(255, 255, 255, 0.8);
border-radius: 12px;
position: relative;
display: flex;
align-items: center;
margin-bottom: 16px;
padding: 0 24px 0 8px;
.cover {
flex-shrink: 0;
width: 136px;
height: 136px;
margin-right: 24px;
margin-right: 32px;
border-radius: 8px;
overflow: hidden;
.goods-img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.goods-title {
font-family: 'font_bold';
font-weight: 700;
font-size: 20px;
color: rgba(0, 0, 0, 0.6);
padding-bottom: 8px;
}
.goods-sub {
font-size: 14px;
line-height: 150%;
color: rgba(0, 0, 0, 0.4);
margin-bottom: 10px;
@include more-wrap;
}
.price {
display: flex;
align-items: baseline;
.sell {
display: flex;
align-items: baseline;
font-weight: 700;
font-family: 'font_bold';
font-size: 16px;
color: #f1b33e;
padding-right: 10px;
i {
font-size: 32px;
}
}
.original {
font-weight: 400;
font-size: 18px;
text-decoration: line-through;
color: rgba(0, 0, 0, 0.2);
}
}
.button {
position: absolute;
bottom: 16px;
right: 24px;
width: 140px;
height: 48px;
text-align: center;
line-height: 48px;
background: linear-gradient(90deg, #f6a62c 0%, #ffbc3f 100%);
box-shadow: 0px 20px 30px rgba(221, 152, 55, 0.2);
border-radius: 150px;
font-weight: 700;
font-size: 14px;
font-family: 'font_bold';
color: #ffffff;
}
}
</style>

119
src/components/BrandDetail/children/Info.vue

@ -1,119 +0,0 @@
<template>
<div class="wrapper">
<h1 class="title">{{ $t('serviceInfo') }}</h1>
<h2 class="time">{{ businessTime }}</h2>
<h1 class="title">营业信息</h1>
<div class="info-group">
<div class="info-list" v-for="(item, index) of serviceList" :key="index">
<p class="info">{{ item }}</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const info = ref({
service_info: {
service_desc: '有停车位 有WIFI 有包厢 无烟区',
service_tag_list: ['有停车位', 'sadf', '有停车位', 'sadf', '有停车位', 'sadf', 'sadf', 'sadddddf', 'sadf阿斯蒂芬', 'sa顶顶顶df', 'sadf']
},
store_album: {
picture_list: {
picture: [
{
picture_name: '门店内景',
picture_url: 'https://img.alicdn.com/i4/2200788639881/O1CN01T3Urj72MrYBKTXquz_!!2200788639881-0-koubei.jpg',
sequence: -1
},
{
picture_name: '门店内景',
picture_url: 'https://img.alicdn.com/i1/2200788639881/O1CN01xyp6q72MrYBGxRpS6_!!2200788639881-0-koubei.jpg',
sequence: -1
},
{
picture_name: '门店内景',
picture_url: 'https://img.alicdn.com/i4/2200788639881/O1CN01T3Urj72MrYBKTXquz_!!2200788639881-0-koubei.jpg',
sequence: -1
},
{
picture_name: '门店内景',
picture_url: 'https://img.alicdn.com/i2/2200788639881/O1CN01WKCpfo2MrYBLru6At_!!2200788639881-0-koubei.jpg',
sequence: -1
}
]
}
},
store_dto: {
billboard: '江宁区米粉米线热销榜第2名',
brand_name: '阿香米线',
business_time: '周一-周日 10:00-21:30',
category_name: '美食,快餐小吃,米粉/米线',
comment_score: '3.4444444444444446',
comment_total_count: '9',
contact_info: '18117095769',
district_info: {
address: '浦珠北路1号万象汇商业广场B1层Y103号商铺',
city_code: '320100',
city_name: '南京市',
district_code: '320111',
district_name: '浦口区',
latitude: '32.125232',
longitude: '118.730226',
province_code: '320000',
province_name: '江苏省'
},
mall_id: '20210207880000000000000009900410',
score: '2.9',
store_detail_url:
'alipays://platformapi/startapp?appId=2021002144672445&supportTourist=true&startMultApp=YES&query=chInfo%3Dch_tribeopen__chsub_1000mu%26sourceFrom%3DTRIBE_OPEN_SHOP',
store_id: '573127418',
store_logo: 'https://img.alicdn.com/i4/2200788639881/O1CN01T3Urj72MrYBKTXquz_!!2200788639881-0-koubei.jpg',
store_name: '阿香米线(南京桥北万象汇餐厅)'
}
})
const businessTime = computed(() => info.value?.store_dto?.business_time ?? '')
const serviceList = computed(() => info.value?.service_info?.service_tag_list ?? [])
</script>
<style lang="scss" scoped>
.title {
font-weight: 700;
font-family: 'font_bold';
font-size: 24px;
color: rgba(0, 0, 0, 0.8);
margin-bottom: 24px;
}
.time {
font-size: 14px;
line-height: 150%;
color: rgba(0, 0, 0, 0.4);
margin-bottom: 26px;
}
.info-group {
display: flex;
flex-wrap: wrap;
.info-list {
padding: 0 24px;
text-align: center;
line-height: 43px;
height: 43px;
background: #ffffff;
border-radius: 8px;
margin-right: 16px;
margin-bottom: 16px;
.info {
font-weight: 700;
font-size: 16px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
}
}
}
</style>

75
src/components/BrandDetail/children/Recommend.vue

@ -1,75 +0,0 @@
<template>
<div class="wrapper">
<h1 class="title">{{ $t('recommend') }}</h1>
<ScrollView class="scroll" :list="recommendList">
<ul class="recommends">
<li class="recommend-item" v-for="item of recommendList" :key="item.good_detail_name">
<div class="pic-wrapper">
<img class="pic" :src="item?.picture?.picture_url" alt="" />
</div>
<span class="txt">{{ item.good_detail_name }}</span>
</li>
</ul>
</ScrollView>
</div>
</template>
<script setup>
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import { ref } from 'vue'
const recommendList = ref([
{
good_detail_name: '测试推荐菜',
picture: {
picture_url: 'https://img0.baidu.com/it/u=2284782435,2170248613&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1657299600&t=624c0227aa15519b1309ace7f41184ec'
}
}
])
</script>
<style lang="scss" scoped>
.title {
font-weight: 700;
font-family: 'font_bold';
font-size: 24px;
color: rgba(0, 0, 0, 0.8);
margin-bottom: 24px;
}
.scroll {
position: relative;
overflow: hidden;
height: 690px;
.recommends {
display: grid;
grid-template-columns: repeat(3, 220px);
flex-wrap: wrap;
gap: 24px 19px;
padding-bottom: 24px;
.recommend-item {
height: 162px;
background: rgba(255, 255, 255, 0.4);
border-radius: 8px;
overflow: hidden;
}
.pic-wrapper {
width: 220px;
height: 124px;
margin-bottom: 12px;
.pic {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.txt {
font-weight: 700;
font-size: 12px;
color: rgba(0, 0, 0, 0.6);
padding-left: 12px;
font-family: 'font_bold';
}
}
}
</style>

132
src/components/BrandDetail/children/tabs.vue

@ -1,132 +0,0 @@
<template>
<div class="tabs-wrapper" v-if="menuList.tabList.length > 1">
<div
class="tab"
:ref="el => tabsEl.push(el)"
:class="{ active: index === tabIdx }"
@click="handleTab(index)"
v-for="(item, index) of menuList.tabList"
:key="item.name"
>
{{ switchLanguage(item, 'name') }}
</div>
</div>
<component :tab="menuList.tabList" :is="currentComp"></component>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { ref, defineAsyncComponent, computed } from 'vue'
const Activity = defineAsyncComponent(() => import('./Activity.vue'))
const Goods = defineAsyncComponent(() => import('./Goods.vue'))
const Comment = defineAsyncComponent(() => import('./Comment.vue'))
const Info = defineAsyncComponent(() => import('./Info.vue'))
const Recommend = defineAsyncComponent(() => import('./Recommend.vue'))
const tabList = [
{
name: '品牌活动',
nameEn: 'Activity'
},
{
name: '商品',
nameEn: 'goods'
},
{
name: '评价',
nameEn: 'comments'
},
{
name: '信息',
nameEn: 'infomation'
},
{
name: '推荐菜',
nameEn: 'recommend'
}
]
const tabIdx = ref(0)
function handleTab(index) {
tabIdx.value = index
moveTo(index)
}
const PADDING_LEFT = 6
const tabsEl = ref([])
const distance = ref(0)
function moveTo(index) {
distance.value = tabsEl.value[index].offsetLeft - PADDING_LEFT + 'px'
}
const store = useStore()
const { shop } = storeToRefs(store)
const _dynamicCompList = [Activity, Goods, Comment, Info, Recommend]
const menuList = computed(() => {
if (shop.value.activityList.length && shop.value.thirdKouCode) {
return { tabList, dynamicCompList: _dynamicCompList }
}
if (shop.value.activityList.length && !shop.value.thirdKouCode) {
return { tabList: tabList.slice(0, 1), dynamicCompList: _dynamicCompList.slice(0, 1) }
}
if (!shop.value.activityList.length && shop.value.thirdKouCode) {
return { tabList: tabList.slice(1), dynamicCompList: _dynamicCompList.slice(1) }
}
//thirdKouCode
return { tabList: [], dynamicCompList: [] }
})
const currentComp = computed(() => menuList.value.dynamicCompList[tabIdx.value])
</script>
<style lang="scss" scoped>
.tabs-wrapper {
position: relative;
display: flex;
height: 80px;
padding: 6px;
background: rgba(0, 0, 0, 0.05);
border-radius: 12px;
margin-bottom: 40px;
&::before {
position: absolute;
content: '';
top: 6px;
left: 6px;
background: #ffffff;
box-shadow: inset 0px -4px 0px rgba(177, 189, 220, 0.1);
border-radius: 8px;
width: 128px;
height: 68px;
z-index: 0;
transition: all 0.5s;
transform: translate3d(v-bind(distance), 0, 0);
}
.tab {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 128px;
height: 68px;
font-weight: 700;
font-family: 'font_bold';
font-size: 18px;
color: rgba(0, 0, 0, 0.4);
margin-right: 12px;
z-index: 1;
transition: color 0.5s;
&:last-child {
margin-right: 0;
}
&.active {
color: rgba(0, 0, 0, 0.8);
}
}
}
</style>

104
src/components/BrandRecommend/BrandRecommend.vue

@ -1,104 +0,0 @@
<template>
<div class="brand-recommend">
<p class="title">热门推荐</p>
<ScrollView :list="list" scroll-x class="content">
<div class="recommend-content">
<div class="group-item" @click="handleShop(item)" v-for="item of list" :key="item.shopId">
<div class="logo-wrapper">
<img loading="lazy" :src="config.sourceUrl + item.materialList?.[0]" alt="" class="shop-logo" />
</div>
<p class="name">
<span class="shop-name">{{ switchLanguage(item, 'shopName') }}</span>
<span class="name-right"><img :src="config.sourceUrl + item.industryUrl" class="format-icon" alt="" />{{ item.floor }}</span>
</p>
</div>
</div>
</ScrollView>
</div>
</template>
<script setup>
import ScrollView from '@/base/ScrollView/ScrollView.vue'
defineProps({
list: Array,
config: Object
})
const emits = defineEmits(['click'])
function handleShop(item) {
emits('click', item)
}
</script>
<style lang="scss" scoped>
.brand-recommend {
margin-left: 170px;
.title {
font-weight: 700;
font-size: 24px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.8);
padding-bottom: 12px;
}
.content {
width: 910px;
overflow: hidden;
margin-bottom: 40px;
}
.recommend-content {
display: inline-block;
white-space: nowrap;
padding-top: 32px;
height: 255px;
}
.group-item {
display: inline-block;
width: 320px;
height: 220px;
background: rgba(255, 255, 255, 0.4);
border-radius: 8px;
overflow: hidden;
margin-right: 16px;
}
.logo-wrapper {
width: 320px;
height: 180px;
background-color: #fff;
}
.shop-logo {
width: 100%;
height: 100%;
border-radius: 8px;
object-fit: cover;
}
.name {
display: flex;
align-items: center;
justify-content: space-between;
padding: 13px 12px;
}
.shop-name {
font-weight: 700;
font-size: 12px;
line-height: 14px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
}
.name-right {
font-weight: 700;
display: flex;
align-items: center;
font-size: 12px;
line-height: 14px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.4);
}
.format-icon {
width: 14px;
height: 14px;
margin-right: 8px;
}
}
</style>

38
src/components/BrandScroll/BrandScroll.vue

@ -5,11 +5,10 @@
<h1 class="info"> <h1 class="info">
{{ item.name }}<span class="meta">/ {{ item.shopList.length }}</span> <span v-if="item.name === currentFloor.floor" class="current">您在本层</span> {{ item.name }}<span class="meta">/ {{ item.shopList.length }}</span> <span v-if="item.name === currentFloor.floor" class="current">您在本层</span>
</h1> </h1>
<TransitionGroup name="zoom" mode="out-in" tag="div" :class="{ group: true, isRow }">
<TransitionGroup name="zoom" mode="out-in" tag="div" :class="{ group: true }">
<ShopItem <ShopItem
:config="config" :config="config"
:isGuide="$route.path === '/guide' && !isRow" :isGuide="$route.path === '/guide' && !isRow"
:isRow="isRow"
:isFood="isFood" :isFood="isFood"
:shop="el" :shop="el"
@click="handleShop(el)" @click="handleShop(el)"
@ -36,7 +35,6 @@ const scroll = ref(null)
const props = defineProps({ const props = defineProps({
list: Array, list: Array,
config: Object, config: Object,
isRow: Boolean,
isFood: Boolean, isFood: Boolean,
shop: Object, shop: Object,
needFocus: Boolean needFocus: Boolean
@ -46,13 +44,7 @@ const emits = defineEmits(['click'])
function handleShop(item) { function handleShop(item) {
emits('click', item) emits('click', item)
} }
watch(
() => props.isRow,
() =>
nextTick(() => {
scroll.value.refresh()
})
)
watch( watch(
() => props.list, () => props.list,
() => () =>
@ -82,15 +74,9 @@ watch([scroll, () => props.shop], () => {
<style lang="scss" scoped> <style lang="scss" scoped>
.brand-scroll { .brand-scroll {
overflow: hidden; overflow: hidden;
height: 1255px;
height: 848px;
margin-left: 68px; margin-left: 68px;
&.guide {
height: 770px;
}
&.brand {
height: 1512px;
}
:deep(.bscroll-vertical-scrollbar) { :deep(.bscroll-vertical-scrollbar) {
top: 102px !important; top: 102px !important;
width: 48px !important; width: 48px !important;
@ -126,13 +112,16 @@ watch([scroll, () => props.shop], () => {
padding-bottom: 180px; padding-bottom: 180px;
} }
.info { .info {
margin-right: 68px;
font-weight: 900; font-weight: 900;
font-size: 32px; font-size: 32px;
line-height: 38px;
line-height: 70px;
height: 70px;
color: var(--brand-floorNameColor); color: var(--brand-floorNameColor);
padding-bottom: 24px;
display: flex; display: flex;
align-items: baseline; align-items: baseline;
border-bottom: 1px solid #ffffff;
margin-bottom: 16px;
.meta { .meta {
margin-left: 12px; margin-left: 12px;
font-weight: 500; font-weight: 500;
@ -155,17 +144,12 @@ watch([scroll, () => props.shop], () => {
} }
.group { .group {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 24px 8px;
margin-bottom: 40px;
padding-right: 68px;
}
.isRow {
&.group {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 8px 24px; gap: 8px 24px;
margin-bottom: 32px;
padding-right: 68px;
} }
}
@media (min-aspect-ratio: 1/1) { @media (min-aspect-ratio: 1/1) {
.brand-scroll { .brand-scroll {
height: calc(100vh - 280px); height: calc(100vh - 280px);

204
src/components/Busniness/Busniness.vue

@ -1,204 +0,0 @@
<template>
<ScrollView class="business-scroll">
<div class="business-content">
<div class="three-wrapper" :class="{ active: cardIdx === index }" v-for="(item, index) of 10" :key="item">
<div class="front">
<div class="title">
<p class="title-left">巴黎贝甜</p>
<p class="title-right">¥<i>30</i></p>
</div>
<p class="sub-title">30元代金券满129可用</p>
<ScrollView class="intro-scroll" scrollbar>
<p class="intro">
欧时力(香港)集团全权代理意大利品牌欧时力(OCHIRLY)并组建欧时力(中国)有限公司全权负责OCHIRLY在大中华区的品牌经营欧时力自1999年进入中国市场以来欧时力以前所未有的速度在中国市场发展壮大在短短的两三年间迅速于中国60多个一二类主要消费城市的160余家加盟店以及专柜年销售额达到2.5亿销售业绩评效均名列前茅整体业绩不断上扬在女装市场享有一定的知名度和美誉度欧时力(香港)集团全权代理意大利品牌欧时力(OCHIRLY)并组建欧时力(中国)有限公司全权负责OCHIRLY在大中华区的品牌经营欧时力自1999年进入中国市场以来欧时力以前所未有的速度在中国市场发展壮大在短短的两三年间迅速于中国60多个一二类主要消费城市的160余家加盟店以及专柜年销售额达到2.5亿在短短的两三年间迅速于中国60多个一二类主要消费城市的160余家加盟店以及专柜年销售额达到2.5亿在短短的两三年间迅速于中
</p>
</ScrollView>
<p class="time first">领取时间2021-01-01</p>
<p class="time">使用时间2021-01-01至2021-12-30</p>
<div class="btn" @click="handleCard(item, index)">立即领取</div>
</div>
<div class="back">
<div class="code">
<img src="" alt="" class="code-img" />
<div class="text">微信扫一扫</div>
</div>
<div class="btn" @click="cardIdx = -1">返回</div>
</div>
</div>
</div>
</ScrollView>
</template>
<script setup>
import { ref } from 'vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
const cardIdx = ref(-1)
function handleCard(item, index) {
cardIdx.value = index
}
</script>
<style lang="scss" scoped>
.business-scroll {
position: relative;
margin-left: 170px;
overflow: hidden;
margin-top: 58px;
height: 1560px;
.business-content {
display: grid;
grid-template-columns: repeat(2, 420px);
gap: 56px 30px;
}
.three-wrapper {
display: inline-block;
width: 420px;
height: 528px;
position: relative;
transform-style: preserve-3d;
transform: perspective(800px);
transition: all 0.5s;
transform-origin: center;
margin-right: 25px;
&.active {
transform: rotateY(-180deg);
}
}
.front,
.back {
width: 420px;
height: 528px;
backface-visibility: hidden;
background: url('../../assets/images/member/line.png') no-repeat center 445px rgba(255, 255, 255, 0.8);
background-size: 497px 1px;
overflow: hidden;
border-radius: 12px;
.btn {
height: 83px;
line-height: 83px;
font-weight: 700;
font-size: 20px;
font-family: 'font_bold';
text-align: center;
color: rgba(0, 0, 0, 0.6);
}
&::before,
&::after {
content: '';
position: absolute;
width: 18px;
height: 18px;
background: #f7f7f9;
bottom: 75px;
border-radius: 50%;
}
&::before {
left: -9px;
}
&::after {
right: -9px;
}
}
.front {
position: relative;
.title {
display: flex;
justify-content: space-between;
color: #f1b33e;
align-items: baseline;
padding: 32px 56px 25px 56px;
.title-left {
font-weight: 700;
font-size: 20px;
font-family: 'font_bold';
}
.title-right {
font-size: 24px;
font-weight: 700;
font-family: 'font_bold';
i {
font-size: 48px;
font-weight: 700;
padding-left: 6px;
}
}
}
.sub-title {
font-weight: 700;
font-size: 24px;
color: rgba(0, 0, 0, 0.8);
margin-bottom: 40px;
font-family: 'font_bold';
margin-left: 56px;
}
.intro-scroll {
height: 152px;
overflow: hidden;
position: relative;
margin-bottom: 45px;
margin-right: 34px;
padding-right: 16px;
margin-left: 56px;
.intro {
font-size: 14px;
line-height: 200%;
text-align: justify;
color: rgba(0, 0, 0, 0.6);
white-space: normal;
}
:deep(.bscroll-vertical-scrollbar) {
width: 6px !important;
background: rgba(0, 0, 0, 0.02) !important;
border-radius: 6px !important;
opacity: 1 !important;
.bscroll-indicator {
width: 6px !important;
background: rgba(0, 0, 0, 0.1) !important;
border-radius: 6px !important;
border: none !important;
}
}
}
.time {
font-size: 14px;
color: rgba(0, 0, 0, 0.4);
margin-left: 56px;
margin-bottom: 41px;
&.first {
margin-bottom: 9px;
}
}
}
.back {
position: absolute;
left: 0;
top: 0;
transform: rotateY(-180deg);
.code {
height: 445px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.code-img {
width: 150px;
height: 150px;
}
.text {
font-weight: 700;
font-size: 20px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.8);
padding-top: 47px;
}
}
}
}
</style>

154
src/components/CarInfo/CarInfo.vue

@ -1,154 +0,0 @@
<template>
<Dialog @close="close">
<div class="content">
<div class="info">
<div class="group">
<p class="text">车牌号</p>
<p class="text-bold">沪AB66688</p>
</div>
<div class="group">
<p class="text">车位号</p>
<p class="text-bold">E-123</p>
</div>
<div class="group">
<p class="text">停车时长</p>
<p class="text-bold">2<i>小时</i>30<i>分钟</i></p>
</div>
<div class="last">
<p class="text">费用</p>
<p class="text-bold">¥<i>2</i></p>
</div>
</div>
<div class="right">
<div class="car-wrapper">
<img src="" alt="" class="car-img" />
</div>
<div class="codes-wrapper">
<img src="" alt="" class="code" />
<p class="tip">扫码缴费</p>
</div>
</div>
<Button class="car-btn" @click="close" />
<Go class="go" />
</div>
</Dialog>
</template>
<script setup>
import Button from '@/base/Button/Button.vue'
import Go from '@/base/Go/Go.vue'
import Dialog from '@/layouts/Dialog.vue'
const emits = defineEmits(['close'])
function close() {
emits('close')
}
</script>
<style lang="scss" scoped>
.content {
position: relative;
display: flex;
margin: 0 auto;
margin-top: 160px;
width: max-content;
animation-duration: 0.2s;
}
.info {
width: 480px;
height: 668px;
background: #ffffff;
border-radius: 12px;
padding: 64px 0 0 0;
}
.group {
margin-bottom: 64px;
padding-left: 64px;
.text-bold {
font-weight: 700;
font-size: 32px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.8);
i {
font-size: 20px;
padding: 0 5px;
}
}
}
.text {
font-weight: 400;
font-size: 20px;
line-height: 23px;
margin-bottom: 18px;
color: rgba(0, 0, 0, 0.4);
}
.last {
font-weight: 700;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.8);
padding-left: 64px;
padding-top: 38px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
.text-bold {
font-size: 24px;
i {
font-size: 48px;
padding-left: 5px;
}
}
}
.right {
margin-left: 32px;
.car-wrapper {
width: 480px;
height: 270px;
margin-bottom: 32px;
background: #ffffff;
border-radius: 12px;
overflow: hidden;
.car-img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.codes-wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 480px;
height: 366px;
background: #ffffff;
border-radius: 12px;
.code {
width: 150px;
height: 150px;
}
.tip {
font-weight: 700;
font-size: 20px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.8);
padding-top: 41px;
}
}
}
.car-btn {
position: absolute;
top: 0;
right: -112px;
}
.go {
position: absolute;
bottom: -160px;
left: 50%;
transform: translateX(-50%);
}
</style>

131
src/components/CarouselWithIntro/CarouselWithIntro.vue

@ -1,131 +0,0 @@
<template>
<div class="benefits-wrapper">
<div class="carousel">
<EffectFade :list="data?.fileList ?? []">
<template v-slot="{ item }">
<div class="banner-wrapper">
<img class="banner" :src="config.sourceUrl + item" alt="" />
</div>
</template>
</EffectFade>
</div>
<div class="right">
<h1 class="title">{{ switchLanguage(data, 'title') }}</h1>
<ScrollView class="intro-scroll" scrollbar>
<p class="intro">
{{ switchLanguage(data, 'content') }}
</p>
</ScrollView>
<div class="qrcodeWrapper">
<ThumbQRCode class="item" v-for="item of qr" :url="config.sourceUrl + item.fileUrl" :title="switchLanguage(item, 'name')" :key="item.name" />
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import EffectFade from '@/components/EffectFade/EffectFade.vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import ThumbQRCode from '@/base/ThumbQRCode/ThumbQRCode.vue'
const props = defineProps({
data: Object
})
const store = useStore()
const { config } = storeToRefs(store)
const qr = computed(() => props.data.qrFileList ?? [])
</script>
<style lang="scss" scoped>
.benefits-wrapper {
display: flex;
flex-direction: column;
width: 100%;
flex: 1;
align-items: center;
padding-top: 48px;
.carousel {
flex-shrink: 0;
width: 944px;
height: 548px;
overflow: hidden;
:deep(.swiper) {
overflow: visible;
.swiper-pagination {
bottom: -16px;
.swiper-pagination-bullet {
width: 34px !important;
height: 4px !important;
background: rgba(0, 0, 0, 0.6);
border-radius: 6px !important;
opacity: inherit !important;
margin: 0;
&.swiper-pagination-bullet-active {
background: #ffffff;
border-radius: 6px !important;
}
}
}
}
.banner {
width: 944px;
height: 532px;
border-radius: var(--global-radius, 8px);
object-fit: cover;
}
}
}
.qrcodeWrapper {
display: flex;
margin-left: 68px;
.item {
margin-right: 10px;
}
}
.title {
padding-top: 32px;
padding-left: 68px;
font-weight: 700;
font-size: 28px;
line-height: 150%;
color: var(--carousel-titleColor);
}
.intro-scroll {
position: relative;
height: 250px;
overflow: hidden;
margin-left: 68px;
margin-right: 31px;
padding-right: 31px;
margin-bottom: 33px;
margin-top: 32px;
.intro {
font-weight: 400;
font-size: 16px;
line-height: 200%;
text-align: justify;
color: var(--carousel-introColor);
}
}
@media (min-aspect-ratio: 1/1) {
.benefits-wrapper {
flex-direction: row;
align-items: flex-start;
padding-top: 100px;
padding-left: 63px;
.carousel {
position: relative;
}
}
.title {
padding-top: 0;
}
}
</style>

118
src/components/Customer/Customer.vue

@ -1,118 +0,0 @@
<template>
<div class="customer-wrapper">
<div class="masker" @click="close"></div>
<div class="customer-content">
<div class="customer-avatar">
<div class="avatar-wrapper">
<img src="../../assets/images/search/customer.png" class="avatar" alt="" />
</div>
<p class="customer-name">客服001</p>
</div>
<div class="time">00:34</div>
<div class="status">{{ $t('waiting') }}...</div>
<img src="../../assets/images/search/out.svg" class="off" @click="close" alt="" />
<div class="close" @click="close">
<img src="../../assets/images/detail/yellow_close.png" alt="" />
</div>
</div>
</div>
</template>
<script setup>
const emits = defineEmits(['close'])
function close() {
emits('close')
}
</script>
<style lang="scss" scoped>
.customer-wrapper {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
animation-duration: 0.2s;
.masker {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.customer-content {
position: relative;
width: 600px;
height: 400px;
margin: 0 auto;
margin-top: 760px;
background: #ffffff;
box-shadow: 0px 40px 60px rgba(0, 0, 0, 0.08);
border-radius: 12px;
.customer-avatar {
display: flex;
align-items: center;
padding: 24px 0 0 32px;
margin-bottom: 50px;
.avatar-wrapper {
width: 56px;
height: 56px;
box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.04);
border-radius: 50%;
padding: 14px;
background-color: var(--color-fff);
.avatar {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.customer-name {
font-weight: 700;
font-size: 16px;
padding-left: 16px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
}
}
.time {
font-weight: 700;
font-size: 48px;
color: rgba(0, 0, 0, 0.8);
font-family: 'font_bold';
padding-bottom: 8px;
text-align: center;
}
.status {
color: rgba(0, 0, 0, 0.4);
text-align: center;
font-size: 18px;
margin-bottom: 49px;
}
.off {
display: block;
width: 80px;
height: 80px;
margin: 0 auto;
}
}
.close {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 12px;
right: 12px;
width: 56px;
height: 56px;
background: rgba(0, 0, 0, 0.02);
border-radius: 12px;
img {
width: 56px;
height: 56px;
}
}
}
</style>

36
src/components/EffectFade/EffectFade.vue

@ -1,36 +0,0 @@
<template>
<swiper v-if="list.length" :watchOverflow="true" :modules="[Autoplay, EffectFade, Pagination]" :pagination="true" effect="fade" :autoplay="true">
<swiper-slide v-for="item of list" :key="item">
<slot :item="item"></slot>
</swiper-slide>
</swiper>
<img v-else src="../../assets/images/nodata.svg" class="stay-tuned" alt="" />
</template>
<script setup>
import SwiperCore, { Autoplay, EffectFade, Pagination } from 'swiper'
import { Swiper, SwiperSlide } from 'swiper/vue'
import 'swiper/css'
import 'swiper/css/effect-fade'
import 'swiper/css/pagination'
SwiperCore.use([Autoplay, EffectFade, Pagination])
defineProps({
list: {
type: Array,
default: () => []
}
})
</script>
<style lang="scss" scoped>
.stay-tuned {
position: absolute;
left: 50%;
top: 50%;
transform: translate3d(-50%, -50%, 0);
object-fit: contain;
}
</style>

194
src/components/Industry/Industry.vue

@ -1,194 +0,0 @@
<template>
<ScrollView class="format-wrapper" ref="scroll" :refresh-delay="800" :list="list">
<div class="format-scroll">
<div class="format-item" @click="handleFormat({ industryName: '全部品牌', industryNameEn: 'all' }, -1)">
<div class="parent flex" :class="{ active: parentIdxRef === -1 }">
<div class="format-left flex">
<img src="../../assets/images/detail/all.svg" class="icon" alt="" />
<span class="text">{{ $t('all') }}</span>
</div>
<div class="format-right flex">
<span class="total">{{ total }}</span>
<img src="../../assets/images/detail/all_dot.svg" class="right-icon" alt="" />
</div>
</div>
</div>
<div class="format-item" v-for="(item, index) of list" :key="item.industryId">
<div
class="parent flex"
@click="handleFormat(item, index)"
:class="[index === parentIdxRef ? 'active' : '', index === parentIdxRef && childIdxRef !== -1 ? 'child-active' : '']"
>
<div class="format-left flex">
<img :src="config.sourceUrl + item.fileUrl" class="icon" alt="" />
<span class="text">{{ switchLanguage(item, 'industryName') }}</span>
</div>
<div class="format-right flex">
<span class="total">{{ item.shopNum }}</span>
<Arrow :size="18" fill="rgba(0, 0, 0, 0.2)" v-if="item.industryList?.length" class="right-icon svg-icon" />
<img src="../../assets/images/detail/all_dot.svg" class="icon" v-else alt="" />
</div>
</div>
<template v-if="item.industryList?.length">
<div class="format-child-wrapper" v-if="index === parentIdxRef">
<div class="format-item" v-for="(child, index) of item.industryList" :key="child.industryId">
<div class="parent flex" :class="{ active: childIdxRef === index }" @click="handleChildFormat(child, index)">
<div class="format-left flex">
<span class="text">{{ switchLanguage(child, 'industryName') }}</span>
</div>
<div class="format-right flex">
<span class="total">{{ child.shopNum ?? 0 }}</span>
</div>
</div>
</div>
</div>
</template>
</div>
</div>
</ScrollView>
</template>
<script setup>
import { computed, nextTick, ref } from 'vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import { Arrow } from '@/base/Svg'
const props = defineProps({
list: {
type: Array,
default: () => []
},
config: {
type: Object,
default: () => ({})
}
})
const total = computed(() => props.list.reduce((_total, item) => _total + item.shopNum, 0))
const emits = defineEmits(['click'])
emits('click', { industryName: '全部品牌', industryNameEn: 'all' }, -1)
const scroll = ref(null)
const parentIdxRef = ref(-1)
const childIdxRef = ref(-1)
function handleFormat(item, index) {
emits('click', item, index)
childIdxRef.value = -1
parentIdxRef.value = index
nextTick(() => {
scroll.value.refresh()
})
}
function handleChildFormat(child, childIdx) {
childIdxRef.value = childIdx
emits('click', child, childIdx)
nextTick(() => {
scroll.value.refresh()
})
}
</script>
<style lang="scss" scoped>
.format-wrapper {
overflow: hidden;
height: 810px;
border-radius: 8px;
width: 200px;
margin-top: 18px;
.format-scroll {
overflow: hidden;
border-radius: 8px;
}
.format-item {
width: 200px;
background-color: #fff;
.parent {
height: 56px;
padding: 0 12px 0 24px;
justify-content: space-between;
align-items: center;
&.active {
background: linear-gradient(90deg, #ffbd35 0%, #ffd260 100%);
.icon,
.right-icon {
filter: brightness(50) invert(70%);
&.svg-icon {
filter: none;
}
}
:deep(path) {
fill: rgba(0, 0, 0, 0.6);
}
.svg-icon {
transform: rotate(-180deg);
}
.text,
.total {
color: rgba(0, 0, 0, 0.8);
}
}
&.child-active {
background: transparent;
.icon,
.right-icon {
filter: none;
}
:deep(path) {
fill: rgba(0, 0, 0, 0.6);
}
.right-icon {
transform: rotate(-180deg);
}
.text,
.total {
color: var(--color-9b0);
}
}
}
.format-right {
.icon {
margin-left: 8px;
margin-right: 0;
}
}
.icon {
width: 16px;
height: 16px;
margin-right: 8px;
}
.text,
.total {
font-weight: 700;
font-size: 14px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
}
.total {
font-weight: 400;
font-family: 'font_regular';
color: rgba(0, 0, 0, 0.4);
}
.right-icon {
margin-left: 8px;
transition: transform 0.3s;
}
}
.format-child-wrapper {
.parent {
padding: 0 48px;
}
.text {
color: rgba(0, 0, 0, 0.4);
}
.total {
color: rgba(0, 0, 0, 0.2);
}
}
}
.flex {
display: flex;
align-items: center;
}
</style>

86
src/components/KeyboardByLetter/KeyboardByLetter.vue

@ -1,86 +0,0 @@
<template>
<div class="keyboard">
<div
class="letter-item"
@click="handleLetter(item, index)"
:class="[item === '空格' ? 'space' : '', item === 'del' ? 'del' : '', letterIdx === index ? 'active' : '']"
v-for="(item, index) of letter"
:key="item"
v-html="generateInnerHTML(item)"
></div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { letter } from '../Search/tabs'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
const store = useStore()
const { theme } = storeToRefs(store)
function generateInnerHTML(item) {
return item === 'del' ? `<img style="width:24px;height:24px" src="${theme.value.images.delete}" alt="">` : item
}
const emits = defineEmits(['del', 'handle-letter'])
const timer = ref(null)
const letterIdx = ref(-1)
function handleLetter(item, index) {
letterIdx.value = index
clearTimeout(timer.value)
timer.value = setTimeout(() => {
letterIdx.value = -1
clearTimeout(timer.value)
}, 300)
if (item !== '空格' && item !== 'del') {
emits('handle-letter', item)
return
}
item === 'del' && emits('del')
}
</script>
<style lang="scss" scoped>
.keyboard {
display: flex;
flex-wrap: wrap;
padding-top: 40px;
.letter-item {
display: flex;
justify-content: center;
align-items: center;
width: 48px;
height: 48px;
text-align: center;
line-height: 48px;
background: var(--search-keyBg, #ffffff);
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.03), inset 0px -1px 0px rgba(177, 189, 220, 0.1);
border-radius: var(--global-radius, 8px);
font-weight: 700;
font-size: 18px;
font-family: 'font_bold';
color: var(--search-keyColor, rgba(0, 0, 0, 0.6));
margin-right: 8px;
margin-bottom: 8px;
&:nth-child(21) {
margin-left: 28px;
}
&.space {
font-size: 14px;
}
&.del {
width: 104px;
img {
width: 24px;
height: 24px;
}
}
&.active {
background: var(--search-keyboardActiveBg);
color: #fff;
}
}
}
</style>

107
src/components/KeyboardByWritten/KeyboardByWritten.vue

@ -1,107 +0,0 @@
<template>
<div class="written-wrapper">
<ScrollView class="word-scroll" scroll-x :list="wordsList">
<div class="word-content">
<div class="word" @click="handleWord(item, index)" :class="{ active: wordIdx === index }" v-for="(item, index) of wordsList" :key="item">{{ item }}</div>
</div>
</ScrollView>
<Written
@result="getWordList"
:width="552"
:height="196"
background-color="transparent"
fill-font-size="48px"
fill-style="rgba(0, 0, 0, 0.02)"
:stroke-style="theme.search.writeColor"
/>
<div class="del" @click="del">
<img :src="theme.images.delete" class="del-icon" alt="" />
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Written from '../Written/Written.vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
const wordsList = ref([])
const store = useStore()
const { theme } = storeToRefs(store)
function getWordList(list) {
wordsList.value = list
}
const emits = defineEmits(['handle-word', 'del'])
const wordIdx = ref(-1)
const timer = ref(null)
function handleWord(item, index) {
clearTimeout(timer.value)
wordIdx.value = index
emits('handle-word', item)
timer.value = setTimeout(() => {
wordIdx.value = -1
clearTimeout(timer.value)
}, 300)
}
function del() {
emits('del')
}
</script>
<style lang="scss" scoped>
.written-wrapper {
position: relative;
width: 552px;
height: 252px;
background: var(--search-keyBg, #ffffff);
border-radius: var(--global-radius, 12px);
margin-top: 16px;
overflow: hidden;
.word-scroll {
position: relative;
height: 56px;
background: var(--search-keyBg, #ffffff);
box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.04);
.word-content {
display: inline-block;
white-space: nowrap;
}
.word {
display: inline-block;
width: 66px;
height: 56px;
text-align: center;
line-height: 56px;
font-weight: 700;
font-size: 18px;
font-family: 'font_bold';
color: var(--search-keyColor, rgba(0, 0, 0, 0.6));
&.active {
color: #fff;
background: var(--search-keyboardActiveBg);
}
}
}
.del {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 104px;
height: 48px;
right: 8px;
bottom: 8px;
background: rgba(0, 0, 0, 0.05);
box-shadow: inset 0px -1px 0px rgba(177, 189, 220, 0.1);
border-radius: var(--global-radius, 8px);
img {
width: 24px;
height: 24px;
}
}
}
</style>

49
src/components/Language/Language.vue

@ -1,49 +0,0 @@
<template>
<div class="language">
<div class="language-item" @click="setLanguage('zh')" :class="{ active: language === 'zh' }"></div>
<div class="language-item" @click="setLanguage('en')" :class="{ active: language === 'en' }">EN</div>
</div>
</template>
<script setup>
import { useStore } from '@/store/root'
import { storeToRefs } from 'pinia'
const store = useStore()
const { language } = storeToRefs(store)
function setLanguage(lang) {
store.language !== lang && store.SET_LANGUAGE(lang)
}
</script>
<style lang="scss" scoped>
.language {
position: absolute;
left: 24px;
bottom: 56px;
display: flex;
align-items: center;
justify-content: center;
width: 93px;
height: 32px;
border-radius: 12px;
background-color: var(--color-black-opacity-02);
padding: 0 3px;
}
.language-item {
width: 43px;
height: 26px;
border-radius: 10px;
text-align: center;
line-height: 26px;
font-size: 14px;
color: var(--color-black-opacity-4);
font-family: 'font_bold';
font-weight: bolder;
&.active {
background: var(--color-linear-lightgoldenyellow);
color: var(--color-black-opacity-6);
}
}
</style>

314
src/components/LoginByPhone/LoginByPhone.vue

@ -1,314 +0,0 @@
<template>
<div class="login-by-phone">
<div class="masker" @click="handleClose"></div>
<div class="detail-content">
<h4 class="login-text">{{ $t('login') }}</h4>
<div class="phone-group" @click="changeInputFocus('phone')">
<img src="../../assets/images/member/hpone_tip.png" alt="" class="group-icon" />
<div class="line"></div>
<input v-model="phoneRef" readonly type="text" :placeholder="$t('phoneNum')" class="input" />
</div>
<div class="pwd-box">
<div class="phone-group pwd">
<img src="../../assets/images/member/pwd.png" alt="" class="group-icon" />
<div class="line"></div>
<input v-model="msgRef" @click="changeInputFocus('msg')" readonly type="text" :placeholder="$t('enterCode')" class="input" />
</div>
<div class="button" @click="sendMsg" :class="{ active: isSended }">{{ isSended ? countDownRef + ' ' + $t('again') : $t('sendCode') }}</div>
</div>
<div class="comfirm" @click="comfirm" :class="{ active: phoneRef.length && msgRef.length }">{{ $t('denglu') }}</div>
<div class="phone-keyboard">
<div
class="num"
@click="handleNum(item === 10 ? '' : item === 11 ? 0 : item, index)"
:class="{ active: numIdx === index }"
v-for="(item, index) of 12"
:key="item"
v-html="generate(item)"
></div>
</div>
<div class="close" @click="handleClose">
<img src="../../assets/images/detail/yellow_close.png" alt="" />
</div>
</div>
</div>
</template>
<script setup>
import { ref, onBeforeUnmount } from 'vue'
import { checkPhoneNumber } from '@/utils/utils'
import Message from '@/base/Message/Message'
const emits = defineEmits(['success', 'error', 'close'])
function generate(item) {
return item === 10 ? '' : item === 11 ? 0 : item === 12 ? `<img src=${require('../../assets/images/member/del.svg')} class="del-icon" alt="" />` : item
}
const phoneRef = ref('')
const msgRef = ref('')
const inputFocusRef = ref('phone')
const numIdx = ref(-1)
const numTimer = ref(null)
function handleNum(item, index) {
clearTimeout(numTimer.value)
numIdx.value = index
numTimer.value = setTimeout(() => {
numIdx.value = -1
clearTimeout(numTimer.value)
}, 300)
if (item === 12) {
del()
return
}
if (inputFocusRef.value === 'phone') {
if (phoneRef.value.length < 11) {
phoneRef.value += item
}
}
if (inputFocusRef.value === 'msg') {
msgRef.value += item
}
}
function changeInputFocus(name) {
inputFocusRef.value = name
}
function del() {
if (inputFocusRef.value === 'phone') {
phoneRef.value = phoneRef.value.substring(0, phoneRef.value.length - 1)
}
if (inputFocusRef.value === 'msg') {
msgRef.value = msgRef.value.substring(0, msgRef.value.length - 1)
}
}
const countDownRef = ref(60)
const countDownTimer = ref(null)
const isSended = ref(false)
//
function sendMsg() {
if (isSended.value) return
if (!checkPhoneNumber(phoneRef.value)) {
Message({ type: 'error', text: '请输入正确的手机号' })
return
}
if (!phoneRef.value.length) {
Message({ type: 'error', text: '手机号不能为空' })
return
}
isSended.value = true
inputFocusRef.value = 'msg'
countDownTimer.value = setInterval(countDown, 1000)
}
//
function countDown() {
countDownRef.value--
if (countDownRef.value <= 0) {
isSended.value = false
countDownRef.value = 60
clearInterval(countDownTimer.value)
}
}
//
function comfirm() {
if (!checkPhoneNumber(phoneRef.value)) {
Message({ type: 'error', text: '请输入正确的手机号' })
return
}
emits('success')
}
onBeforeUnmount(() => {
clearInterval(countDownTimer.value)
})
function handleClose() {
emits('close')
}
</script>
<style lang="scss" scoped>
.login-by-phone {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 51;
animation-duration: 0.3s;
}
.masker {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 20;
}
.detail-content {
position: relative;
width: 484px;
height: 732px;
margin: 0 auto;
margin-top: 594px;
background: #ffffff;
box-shadow: 0px 40px 60px rgba(0, 0, 0, 0.08);
border-radius: 12px;
z-index: 21;
.close {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 12px;
right: 12px;
width: 56px;
height: 56px;
background: rgba(0, 0, 0, 0.02);
border-radius: 12px;
img {
width: 56px;
height: 56px;
}
}
.login-text {
font-weight: 700;
font-size: 24px;
line-height: 28px;
margin-bottom: 40px;
padding-top: 48px;
padding-left: 72px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.8);
}
::-webkit-input-placeholder {
font-size: 12px;
color: var(--color-0cb);
}
.pwd-box {
display: flex;
justify-content: space-between;
width: 340px;
margin-top: 12px;
margin-left: 72px;
margin-bottom: 24px;
.phone-group {
margin-left: 0;
}
.button {
width: 116px;
height: 64px;
left: 296px;
top: 192px;
text-align: center;
line-height: 64px;
background: linear-gradient(90deg, #ffbd35 0%, #ffd260 100%);
border-radius: 12px;
font-weight: 700;
font-size: 14px;
color: rgba(0, 0, 0, 0.6);
font-family: 'font_bold';
}
}
.phone-group {
display: flex;
align-items: center;
width: 340px;
height: 64px;
margin-left: 72px;
background: rgba(0, 0, 0, 0.05);
border-radius: 12px;
padding-left: 20px;
&.pwd {
width: 212px;
}
.group-icon {
width: 20px;
}
.line {
width: 1px;
height: 14px;
background: rgba(0, 0, 0, 0.05);
border-radius: 1px;
margin: 0 14px;
}
}
.input {
width: 100%;
display: block;
border: none;
outline: none;
color: rgba(0, 0, 0, 0.4);
font-size: 16px;
background-color: transparent;
}
.comfirm {
width: 340px;
height: 64px;
line-height: 64px;
left: 72px;
top: 280px;
background: linear-gradient(90deg, #ffbd35 0%, #ffd260 100%);
border-radius: 12px;
font-weight: 700;
font-size: 18px;
text-align: center;
color: rgba(0, 0, 0, 0.6);
font-family: 'font_bold';
margin: 0 auto;
margin-bottom: 40px;
}
.phone-keyboard {
display: grid;
grid-template-columns: repeat(3, 108px);
grid-template-rows: repeat(4, 56px);
gap: 12px 8px;
background: linear-gradient(180deg, rgba(0, 0, 0, 0.05) 0%, rgba(0, 0, 0, 0.025) 100%);
padding: 40px 70px 48px 70px;
.num {
background: #ffffff;
box-shadow: inset 0px -4px 0px rgba(177, 189, 220, 0.1);
border-radius: 8px;
font-weight: 700;
font-size: 20px;
font-family: 'font_bold';
display: flex;
align-items: center;
justify-content: center;
color: rgba(0, 0, 0, 0.6);
&.active {
background: linear-gradient(90deg, #ffbd35 0%, #ffd260 100%);
box-shadow: inset 0px -4px 0px rgba(177, 189, 220, 0.1);
}
}
.del {
height: 56px;
width: 112px;
display: inline-flex;
align-items: center;
justify-content: center;
.del-icon {
width: 24px;
height: 24px;
}
}
}
}
.close {
position: absolute;
top: 24px;
width: 56px;
height: 56px;
right: 24px;
}
</style>

83
src/components/LoginError/LoginError.vue

@ -1,83 +0,0 @@
<template>
<div class="full-code">
<div class="code-wrapper">
<img :src="icon" class="img" alt="" />
<p class="youhui">{{ title }}</p>
<slot />
<div class="close" @click="handleClose">
<img src="../../assets/images/detail/yellow_close.png" alt="" />
</div>
</div>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
default: '注册成功'
},
icon: {
type: String,
default: require('@/assets/images/member/login_success.png')
}
})
const emits = defineEmits(['close'])
function handleClose() {
emits('close')
}
</script>
<style lang="scss" scoped>
.full-code {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.code-wrapper {
position: absolute;
top: 50%;
left: 50%;
width: 592px;
min-height: 512px;
transform: translate3d(-50%, -50%, 0);
background: #ffffff;
box-shadow: 0px 0px 80px rgba(0, 0, 0, 0.163735);
padding-top: 96px;
z-index: 10;
.img {
display: block;
margin: 0 auto;
margin-bottom: 96px;
width: 200px;
height: 200px;
}
.youhui {
font-weight: 700;
font-size: 20px;
line-height: 23px;
text-align: center;
color: rgba(0, 0, 0, 0.8);
}
.close {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 12px;
right: 12px;
width: 56px;
height: 56px;
background: rgba(0, 0, 0, 0.02);
border-radius: 12px;
img {
width: 56px;
height: 56px;
}
}
}
</style>

104
src/components/MallIntroduce/MallIntroduce.vue

@ -1,104 +0,0 @@
<template>
<div class="mall-wrapper">
<div class="carousel">
<EffectFade :list="banner">
<template v-slot="{ item }">
<div class="banner-wrapper">
<img :src="config.sourceUrl + item" alt="" class="banner" />
</div>
</template>
</EffectFade>
</div>
<h1 class="title">{{ $t('mallIntroduce') }}</h1>
<ScrollView :list="introduce.content" scrollbar class="mall-intro-scroll">
<p class="mall-intro">
{{ switchLanguage(introduce, 'content') }}
</p>
</ScrollView>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { getMallInfoList } from '@/http/api'
import EffectFade from '@/components/EffectFade/EffectFade.vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
const store = useStore()
const { config } = storeToRefs(store)
const introduce = ref({})
const banner = computed(() => introduce.value?.fileList ?? [])
getMallInfoList().then(({ data }) => {
introduce.value = data ?? {}
})
</script>
<style lang="scss" scoped>
.mall-wrapper {
width: 870px;
margin-left: 170px;
margin-right: 40px;
margin-top: 58px;
background: rgba(255, 255, 255, 0.4);
box-shadow: inset 1px 0px 0px #ffffff;
border-radius: 12px;
padding-bottom: 52px;
.carousel {
width: 870px;
height: 490px;
background: #ffffff;
margin-right: 56px;
border-radius: 8px;
overflow: hidden;
.banner-wrapper {
width: 870px;
height: 490px;
}
.banner {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.title {
padding-top: 48px;
padding-bottom: 32px;
margin-left: 56px;
font-weight: 700;
font-size: 24px;
color: rgba(0, 0, 0, 0.8);
font-family: 'font_bold';
}
.mall-intro-scroll {
position: relative;
overflow: hidden;
height: 758px;
margin-left: 56px;
margin-right: 34px;
padding-right: 16px;
.mall-intro {
font-weight: 400;
font-size: 14px;
line-height: 200%;
text-align: justify;
color: rgba(0, 0, 0, 0.6);
}
:deep(.bscroll-vertical-scrollbar) {
width: 6px !important;
background: rgba(0, 0, 0, 0.02) !important;
border-radius: 6px !important;
opacity: 1 !important;
.bscroll-indicator {
width: 6px !important;
background: rgba(0, 0, 0, 0.1) !important;
border-radius: 6px !important;
border: none !important;
}
}
}
}
</style>

5
src/components/Map/Map.vue

@ -29,7 +29,7 @@ const { shop } = storeToRefs(store)
#mapContainer { #mapContainer {
position: absolute; position: absolute;
top: 118px;
top: 330px;
left: 0; left: 0;
height: 642px; height: 642px;
width: 1080px; width: 1080px;
@ -39,9 +39,6 @@ const { shop } = storeToRefs(store)
&.billboard { &.billboard {
pointer-events: none; pointer-events: none;
} }
&.guide {
top: 408px;
}
} }
#fac { #fac {
position: absolute; position: absolute;

105
src/components/MockInput/MockInput.vue

@ -1,105 +0,0 @@
<template>
<div class="big-input">
<div class="input-left" @click="handleSearch">
<div class="search-content">
<img src="../../assets/images/index/index_search.png" class="index-search-icon" alt="" />
<div class="mock-input">
<p class="search">搜索</p>
<p class="search-en">search</p>
</div>
</div>
</div>
<div class="input-right" @click="handleVoice">
<img src="../../assets/images/index/index_voice.png" class="index-voice-icon" alt="" />
</div>
</div>
</template>
<script setup>
import { useStore } from '@/store/root'
const store = useStore()
function handleSearch() {
store.SET_SHOW_SEARCH(true)
}
function handleVoice() {
store.SET_SHOW_SEARCH(true)
store.SET_SHOW_VOICE(true)
}
</script>
<style lang="scss" scoped>
.big-input {
display: flex;
height: 108px;
margin-right: 40px;
margin-bottom: 46px;
z-index: 55;
.input-left {
position: relative;
flex: 1;
background: linear-gradient(90deg, #f6a62c 0%, #ffbc3f 100%);
box-shadow: 0px 12px 20px rgba(253, 177, 48, 0.1);
border-radius: 12px;
margin-right: 20px;
&::after {
position: absolute;
border-radius: 12px;
content: '';
left: 0;
right: 0;
top: 0;
bottom: 4px;
background: #fff;
}
.search-content {
position: relative;
z-index: 2;
}
.search-content {
position: relative;
display: flex;
align-items: center;
width: 629px;
height: 104px;
background-color: var(--color-white-opacity);
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.3), inset 0px 4px 30px rgba(0, 0, 0, 0.02);
border-radius: 12px;
&::before {
position: absolute;
content: '';
width: 2px;
height: 30px;
left: 112px;
top: 37px;
background: var(--color-black-opacity-2);
}
.index-search-icon {
width: 48px;
height: 48px;
margin-right: 54px;
margin-left: 44px;
}
.search {
font-weight: 700;
font-size: 20px;
line-height: 28px;
font-family: 'font_bold';
color: var(--color-black-opacity-6);
}
.search-en {
font-weight: 700;
font-family: 'font_bold';
font-size: 14px;
text-transform: uppercase;
color: var(--color-black-opacity-4);
}
}
}
.index-voice-icon {
margin-top: -9px;
width: 246px;
}
}
</style>

388
src/components/MovieDetail/MovieDetail.vue

@ -1,388 +0,0 @@
<template>
<Dialog @close="close">
<div class="content">
<div class="content-left">
<div class="poster-wrapper">
<img class="poster" :src="movie.posterPath" alt="" />
</div>
<p class="name">{{ movie.showName }}</p>
<div class="group">
<div class="text">导演</div>
<div class="info">{{ movie.director }}</div>
</div>
<div class="group">
<div class="text">类型</div>
<div class="info">{{ movie.type }}</div>
</div>
<div class="group">
<div class="text">语言</div>
<div class="info">{{ movie.language }}</div>
</div>
<div class="group">
<div class="text">片长</div>
<div class="info">{{ movie.duration }} 分钟</div>
</div>
<div class="group last">
<div class="text">主演</div>
<div class="info">{{ movie.leadingRole }}</div>
</div>
<div class="remark">
<span>{{ $t('rate') }}</span>
{{ movie.remark }}
</div>
<div class="price">
<div class="price-num">
¥
<i>{{ movie.price / 100 }}</i>
</div>
</div>
</div>
<div class="content-right">
<ScrollView class="date-scroll" scroll-x :list="dateList">
<div class="date-content">
<div
class="date-item"
:ref="el => tabsEl.push(el)"
:class="{ active: index === tabIdx }"
@click="handleTab(index)"
v-for="(item, index) of dateList"
:key="item.week"
>
<span class="date">{{ item.customDate }}</span>
<span class="week">{{ item.week }}</span>
</div>
</div>
</ScrollView>
<ScrollView class="list-scroll">
<div class="list-content">
<table>
<tr v-for="item of 2" :key="item">
<td class="w-190">
<div class="start">12:00</div>
<div class="end">12:00</div>
</td>
<td class="w-190">
<div class="type">IMAX</div>
<div class="ting">激光 3 </div>
</td>
<td class="w-190 line">
<div class="ticket">06<i></i></div>
<div class="rest">余票</div>
</td>
<td class="w-222 center">
<div class="price-num">¥<i>36</i></div>
</td>
<td>
<div class="button" @click="showQRCodeDialog">{{ $t('ticket') }}</div>
</td>
</tr>
</table>
</div>
</ScrollView>
</div>
<Button @click="close" class="btn" />
</div>
<QRCode v-if="showCode" @close="showCode = false" title="微信扫码购买" />
</Dialog>
</template>
<script setup>
import { computed, ref, defineAsyncComponent } from 'vue'
import { futureDate } from '@/utils/utils'
import Dialog from '@/layouts/Dialog.vue'
import Button from '@/base/Button/Button.vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
const QRCode = defineAsyncComponent(() => import('@/base/QRCode/QRCode.vue'))
defineProps({
movie: {
type: Object,
default: () => ({})
}
})
const dateList = computed(() => futureDate())
const tabIdx = ref(0)
function handleTab(index) {
tabIdx.value = index
moveTo(index)
}
const PADDING_LEFT = 6
const tabsEl = ref([])
const distance = ref(0)
function moveTo(index) {
distance.value = tabsEl.value[index].offsetLeft - PADDING_LEFT + 'px'
}
const showCode = ref(false)
function showQRCodeDialog() {
showCode.value = true
}
const emits = defineEmits(['close'])
function close() {
emits('close')
}
</script>
<style lang="scss" scoped>
.content {
position: relative;
display: flex;
margin: 0 auto;
margin-top: 360px;
width: max-content;
z-index: 10;
.price-num {
padding-left: 24px;
padding-right: 24px;
color: #f1b33e;
font-weight: 700;
font-family: 'font_bold';
font-size: 16px;
i {
font-size: 32px;
padding: 0 4px;
}
}
.content-left {
width: 260px;
height: 727px;
background: #ffffff;
border-radius: 12px;
padding: 4px;
margin-right: 24px;
.poster-wrapper {
width: 252px;
height: 380px;
border-radius: 8px;
overflow: hidden;
margin-bottom: 24px;
.poster {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.name {
font-weight: 700;
font-size: 20px;
color: rgba(0, 0, 0, 0.8);
font-family: 'font_bold';
margin-bottom: 20px;
@include no-wrap;
padding-left: 24px;
}
.group {
display: flex;
padding: 0 24px;
margin-bottom: 8px;
&.last {
margin-bottom: 14px;
.info {
height: 28px;
@include more-wrap;
}
}
.text {
font-weight: 700;
font-size: 12px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.4);
padding-right: 8px;
flex-shrink: 0;
}
.info {
font-weight: 400;
font-size: 12px;
line-height: 14px;
color: rgba(0, 0, 0, 0.4);
}
}
.remark {
font-weight: 700;
color: rgba(0, 0, 0, 0.6);
margin-left: 24px;
font-family: 'font_bold';
font-size: 14px;
margin-right: 24px;
padding-bottom: 47px;
margin-bottom: 16px;
border-bottom: 1px solid rgba(0, 0, 0, 0.02);
span {
font-size: 16px;
}
}
}
.content-right {
display: flex;
flex-direction: column;
}
.date-scroll {
position: relative;
overflow: hidden;
width: 680px;
height: 128px;
background: rgba(0, 0, 0, 0.2);
border-radius: 12px;
padding: 6px 0;
margin-bottom: 24px;
.date-content {
display: inline-block;
white-space: nowrap;
padding: 0 6px;
&::before {
position: absolute;
content: '';
left: 6px;
background: linear-gradient(90deg, #f6a62c 0%, #ffbc3f 100%);
box-shadow: 0 20px 30px rgba(221, 152, 55, 0.2);
border-radius: 8px;
border: 1px solid rgba(255, 189, 53, 1);
width: 134px;
height: 116px;
z-index: 0;
transition: all 0.5s;
transform: translate3d(v-bind(distance), 0, 0);
}
.date-item {
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-right: 12px;
width: 134px;
height: 116px;
}
.date {
position: relative;
z-index: 5;
font-weight: 700;
font-family: 'font_bold';
font-size: 20px;
color: #ffffff;
padding-bottom: 5px;
}
.week {
font-weight: 400;
position: relative;
z-index: 5;
font-size: 14px;
text-align: center;
color: #ffffff;
}
}
}
.list-scroll {
flex: 1;
overflow: hidden;
border-radius: 8px;
table {
width: 680px;
tr {
display: block;
background-color: #fff;
border-radius: 8px;
margin-bottom: 8px;
padding: 26px 48px;
}
td {
position: relative;
vertical-align: middle;
text-align: left;
&.center {
text-align: center;
}
.start {
font-weight: 700;
font-size: 24px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
padding-bottom: 4px;
}
.end {
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.4);
}
.type {
font-weight: 700;
font-family: 'font_bold';
font-size: 24px;
color: rgba(0, 0, 0, 0.6);
padding-bottom: 4px;
}
.ting {
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.4);
}
.ticket {
font-weight: 700;
font-size: 24px;
color: rgba(0, 0, 0, 0.6);
font-family: 'font_bold';
padding-bottom: 4px;
}
.rest {
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.4);
}
.button {
width: 136px;
height: 56px;
line-height: 56px;
text-align: center;
background: linear-gradient(90deg, #f6a62c 0%, #ffbc3f 100%);
box-shadow: 0px 20px 30px rgba(221, 152, 55, 0.2);
border-radius: 53px;
font-weight: 700;
font-size: 18px;
font-family: 'font_bold';
color: #ffffff;
}
}
.w-190 {
width: 190px;
}
.w-150 {
width: 150px;
}
.w-222 {
width: 222px;
}
}
.line {
&:after {
content: '';
position: absolute;
right: 0;
height: 32px;
width: 1px;
border-right: 1px solid rgba(38, 36, 36, 0.1);
top: 50%;
transform: translateY(-50%);
}
}
}
.btn {
position: absolute;
right: 0;
top: -104px;
}
}
</style>

231
src/components/PlateInput/PlateInput.vue

@ -1,231 +0,0 @@
<template>
<TransitionGroup name="width" mode="out-in" tag="div" class="plateinput-wrapper" :class="[searchMethods === '车位' ? 'active' : '']">
<div
class="input"
v-for="(item, index) of renderInputLength"
:key="item"
:class="[index === license.length - 1 ? 'active' : '', searchMethods === '车位' ? 'space' : '']"
>
<span class="text">{{ license[index] }}</span>
</div>
<div
key="energy"
v-if="needsEnergy"
class="last input"
@click="handleEnergy"
:class="[_isEnergy ? 'enrgy' : '', _isEnergy && license.length === ENERGY_PLATE ? 'active' : '']"
>
<span class="text" v-if="!_isEnergy">新能源</span>
<span class="text" v-else>{{ _isEnergy ? (license.length === ENERGY_PLATE && license[license.length - 1]) || '' : '' }}</span>
</div>
<div key="btn" class="btn" @click="confirm">
<img src="../../assets/images/parking/search-parking.png" class="icon" alt="" />
</div>
</TransitionGroup>
</template>
<script setup>
import { ref, computed } from 'vue'
const NOT_ENERGY_PLATE = 7
const ENERGY_PLATE = 8
const props = defineProps({
spaceMaxLength: {
type: Number,
default: 5
},
license: {
type: Array,
default: () => []
},
needsEnergy: {
type: Boolean,
default: true
},
searchMethods: {
type: String,
validator: status => ['车牌', '车位'].includes(status)
}
})
const renderInputLength = computed(() => (props.searchMethods === '车牌' ? NOT_ENERGY_PLATE : props.spaceMaxLength))
const emits = defineEmits(['handle-energy', 'confirm'])
const _isEnergy = ref(false)
emits('handle-energy', _isEnergy.value)
function handleEnergy() {
_isEnergy.value = !_isEnergy.value
emits('handle-energy', _isEnergy.value)
}
function confirm() {
emits('confirm')
}
</script>
<style lang="scss" scoped>
.plateinput-wrapper {
display: flex;
position: relative;
margin-bottom: 32px;
margin-left: 170px;
&.active {
&::before {
left: 141px;
}
}
&::before {
content: '';
position: absolute;
width: 2px;
height: 63px;
background: rgba(0, 0, 0, 0.1);
border-radius: 2px;
left: 176px;
top: 50%;
transform: translateY(-50%);
transition: left 0.5s;
}
}
.input {
position: relative;
border-radius: 8px;
width: 80px;
height: 100px;
margin-right: 8px;
font-weight: 700;
font-size: 32px;
line-height: 100px;
text-align: center;
color: rgba(0, 0, 0, 0.8);
font-family: 'font_bold';
background: #ffffff;
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.03);
border-radius: 8px;
&.space {
margin-right: 8px !important;
width: 132px;
&:first-child {
margin-right: 22px !important;
}
}
.text {
position: relative;
z-index: 3;
}
&::after {
width: 80px;
height: 100px;
left: 0;
top: 0;
content: '';
position: absolute;
background: #ffffff;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.03);
border-radius: 8px;
z-index: 2;
}
&.space {
&:nth-child(2) {
margin-right: 12px;
}
&::after {
width: 132px;
}
}
&.active {
&::before {
position: absolute;
content: '';
top: -2px;
right: -2px;
bottom: -2px;
left: -2px;
background: var(--color-linear-lightgoldenyellow);
border-radius: 8px;
z-index: 1;
}
}
&:nth-child(2) {
margin-right: 18px;
}
}
.last {
line-height: initial;
&.enrgy {
.text {
padding-top: 0;
font-weight: 700;
font-size: 32px;
line-height: 90px;
text-align: center;
color: rgba(0, 0, 0, 0.8);
}
&::after {
background: #ffffff;
content: '';
}
}
&::after {
content: '+';
font-weight: 700;
font-size: 32px;
color: rgba(0, 0, 0, 0.4);
line-height: 60px;
background: transparent;
}
.text {
width: 80px;
height: 100px;
display: inline-block;
font-weight: 700;
font-size: 18px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.4);
border: 2px solid #fff;
text-align: center;
border-radius: 8px;
padding-top: 51px;
}
}
.btn {
position: absolute;
right: 40px;
display: flex;
align-items: center;
justify-content: center;
margin-left: 12px;
width: 156px;
height: 100px;
background: linear-gradient(90deg, #ffbd35 0%, #ffd260 100%);
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.03), inset 0px -4px 0px rgba(243, 176, 36, 0.5);
border-radius: 8px;
transition: all 0.5s;
.icon {
width: 32px;
}
}
.width-enter-active,
.width-leave-active {
width: 80px;
transition: all 0.5s;
transition-timing-function: cubic-bezier(0.75, 0, 0.24, 1);
}
.width-enter-from,
.width-leave-to {
width: 0;
opacity: 0;
}
.width-move {
transition: transform 0.5s !important;
transition-timing-function: cubic-bezier(0.75, 0, 0.24, 1);
}
</style>

91
src/components/PlateKeyboard/PlateKeyboard.vue

@ -1,91 +0,0 @@
<template>
<div class="keyboard-wrapper">
<div
class="keyboard-item"
@click="handleKeyboard(item, index)"
:class="[isUppercaseWord(item) ? 'uppercase' : '', item === 'del' ? 'del' : '', keyboardIdx === index ? 'active' : '', searchMethods === '车位' ? 'space' : '']"
v-for="(item, index) of list"
:key="item"
v-html="generateInnerHTML(item)"
></div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { keyboard } from './keyboard'
import { isUppercaseWord, isZhWord } from '@/utils/utils'
const props = defineProps({
searchMethods: {
type: String,
validator: status => ['车牌', '车位'].includes(status)
}
})
const list = computed(() => (props.searchMethods === '车位' ? keyboard.filter(item => !isZhWord(item)) : keyboard))
function generateInnerHTML(item) {
return item === 'del' ? `<img src="${require('@/assets/images/parking/del.svg')}" alt="">` : item
}
const emits = defineEmits(['handle-keyboard', 'del'])
const keyboardIdx = ref(-1)
const keyboardTimer = ref(null)
function handleKeyboard(item, index) {
item === 'del' ? emits('del') : emits('handle-keyboard', item)
clearTimeout(keyboardTimer.value)
keyboardIdx.value = index
keyboardTimer.value = setTimeout(() => {
keyboardIdx.value = -1
clearTimeout(keyboardTimer.value)
}, 300)
}
</script>
<style lang="scss" scoped>
.keyboard-wrapper {
display: flex;
flex-wrap: wrap;
margin-left: 170px;
margin-right: 32px;
margin-bottom: 56px;
.keyboard-item {
width: 41.76px;
height: 48px;
background: rgba(255, 255, 255, 0.6);
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.03), inset 0px -1px 0px rgba(177, 189, 220, 0.1);
border-radius: 6px;
text-align: center;
margin-right: 7px;
margin-bottom: 8px;
font-size: 18px;
font-family: 'font_medium';
line-height: 48px;
color: rgba(0, 0, 0, 0.8);
&.space {
width: 90.48px;
&.del {
width: 187.92px;
}
}
&.uppercase {
background: #ffffff;
}
&.del {
display: flex;
align-items: center;
justify-content: center;
width: 90px;
line-height: initial;
background: rgba(0, 0, 0, 0.05);
box-shadow: inset 0px -1px 0px rgba(0, 0, 0, 0.1);
}
&.active {
background: #f1b33e;
color: #fff;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.03), inset 0px -1px 0px rgba(177, 189, 220, 0.1);
}
}
}
</style>

73
src/components/PlateKeyboard/keyboard.js

@ -1,73 +0,0 @@
export const keyboard = [
'粤',
'京',
'津',
'渝',
'冀',
'豫',
'云',
'辽',
'黑',
'A',
'B',
'C',
'D',
'E',
'F',
1,
2,
3,
'湘',
'皖',
'鲁',
'苏',
'浙',
'赣',
'鄂',
'桂',
'甘',
'G',
'H',
'J',
'K',
'L',
'M',
4,
5,
6,
'晋',
'蒙',
'陕',
'吉',
'闽',
'贵',
'沪',
'青',
'藏',
'N',
'P',
'Q',
'R',
'S',
'T',
7,
8,
9,
'川',
'宁',
'琼',
'港',
'澳',
'新',
'使',
'领',
'警',
'U',
'V',
'W',
'X',
'Y',
'Z',
'0',
'del'
]

22
src/components/PublicComponent/PublicComponent.vue

@ -8,19 +8,10 @@
<!-- 倒计时返回首页提示 --> <!-- 倒计时返回首页提示 -->
<AutoBackNotification v-if="countDownGif" :title="title" :delay="isWall ? countDownToWall : countDownNum" /> <AutoBackNotification v-if="countDownGif" :title="title" :delay="isWall ? countDownToWall : countDownNum" />
<Teleport to="body">
<Transition enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
<BrandDetail v-if="showDetail" />
</Transition>
</Teleport>
<Transition enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
<Search v-if="showSearch" />
</Transition>
<Temperature /> <Temperature />
<Time /> <Time />
<SearchBar v-if="$route.path !== '/billboard'" /> <SearchBar v-if="$route.path !== '/billboard'" />
<Tabs />
</template> </template>
<script setup> <script setup>
@ -34,20 +25,16 @@ import { useInitMap } from '@/composables/useInitMap'
import Map from '@/components/Map/Map.vue' import Map from '@/components/Map/Map.vue'
const Logout = defineAsyncComponent(() => import('@/base/Logout/Logout.vue')) const Logout = defineAsyncComponent(() => import('@/base/Logout/Logout.vue'))
const AutoBackNotification = defineAsyncComponent(() => import('@/base/AutoBackNotification/AutoBackNotification.vue')) const AutoBackNotification = defineAsyncComponent(() => import('@/base/AutoBackNotification/AutoBackNotification.vue'))
const BrandDetail = defineAsyncComponent(() => import('@/components/BrandDetail/BrandDetail.vue'))
const Search = defineAsyncComponent(() => import('@/components/Search/Search.vue'))
const Temperature = defineAsyncComponent(() => import('@/base/Temperature/Temperature.vue')) const Temperature = defineAsyncComponent(() => import('@/base/Temperature/Temperature.vue'))
const Time = defineAsyncComponent(() => import('@/base/Time/Time.vue')) const Time = defineAsyncComponent(() => import('@/base/Time/Time.vue'))
const SearchBar = defineAsyncComponent(() => import('./SearchBar.vue'))
const Tabs = defineAsyncComponent(() => import('./Tabs.vue'))
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const store = useStore() const store = useStore()
const { shop, showDetail, showSearch, sidebarList, language, showVoice } = storeToRefs(store)
const { shop, sidebarList, language } = storeToRefs(store)
const { logoutRef, resetClickNumber, setLogoutRef, addTotalClick } = useLogout() const { logoutRef, resetClickNumber, setLogoutRef, addTotalClick } = useLogout()
const { title, countDownGif, isWall, countDownToWall, countDownNum, send, checkHandleScreen } = useHandleScreen(handleScreen)
const { title, countDownGif, countDownNum, send, checkHandleScreen } = useHandleScreen(handleScreen)
function handleGO() { function handleGO() {
router.push('/nav') router.push('/nav')
@ -62,9 +49,6 @@ async function handleScreen() {
setLogoutRef(false) setLogoutRef(false)
resetClickNumber() resetClickNumber()
store.SET_SELECTED_MODULE(sidebarList.value[0].title) store.SET_SELECTED_MODULE(sidebarList.value[0].title)
showDetail.value && store.SET_SHOW_DETAIL(false)
showSearch.value && store.SET_SHOW_SEARCH(false)
showVoice.value && store.SET_SHOW_VOICE(false)
language.value !== 'zh' && store.SET_LANGUAGE('zh') language.value !== 'zh' && store.SET_LANGUAGE('zh')
await router.push('/billboard') await router.push('/billboard')
} }
@ -84,7 +68,7 @@ onBeforeUnmount(() => {
}) })
watch(route, to => { watch(route, to => {
if (to.fullPath === '/guide' || to.fullPath === '/nav' || to.fullPath === '/billboard') {
if (to.fullPath === '/guide' || to.fullPath === '/nav') {
window?.Map_QM?.startRender() window?.Map_QM?.startRender()
} else { } else {
window?.Map_QM?.cancelRender() window?.Map_QM?.cancelRender()

91
src/components/PublicComponent/SearchBar.vue

@ -1,91 +0,0 @@
<template>
<div class="row">
<div class="back" :style="{ backgroundImage: `url(${theme.images.back})` }" v-if="$route.path !== '/index'" @click="() => $router.push('/index')"></div>
<div class="bar" @click="handleSearch">
<div class="icon" :style="{ backgroundImage: `url(${theme.images.searchIcon})` }"></div>
<div class="stick"></div>
<div class="placeholder">精确查询品牌/公共设施</div>
</div>
</div>
</template>
<script setup>
import { useStore } from '@/store/root'
import { storeToRefs } from 'pinia'
const store = useStore()
const handleSearch = () => store.SET_SHOW_SEARCH(true)
const { theme } = storeToRefs(store)
</script>
<style lang="scss" scoped>
@media (max-aspect-ratio: 1/1) {
.row {
position: fixed;
top: 260px;
left: 68px;
right: 68px;
height: 100px;
display: flex;
z-index: 100;
}
}
.row {
.back {
width: 130px;
height: 100px;
margin-right: 40px;
background: center / cover no-repeat;
box-shadow: 0px 15px 24px rgba(0, 0, 0, 0.05);
border-radius: var(--searchBar-backBorderRadius);
}
.bar {
flex: 1;
height: 100px;
background: var(--searchBar-background);
border: var(--searchBar-border);
box-shadow: 0px 15px 24px rgba(0, 0, 0, 0.05);
border-radius: var(--searchBar-borderRadius);
display: flex;
padding-left: 40px;
align-items: center;
.icon {
width: 56px;
height: 56px;
background: center / cover no-repeat;
}
.stick {
width: 4px;
height: 38px;
background: var(--searchBar-stickBg);
margin: 0 32px;
}
.placeholder {
font-family: 'HarmonyOS Sans SC';
font-style: normal;
font-weight: 700;
font-size: 20px;
line-height: 23px;
color: var(--searchBar-placeholderColor);
}
}
}
@media (min-aspect-ratio: 1/1) {
.row {
.back {
position: fixed;
left: 68px;
top: 161px;
z-index: 100;
}
.bar {
position: fixed;
width: 698px;
top: 38px;
left: 0;
right: 0;
margin: auto;
z-index: 100;
}
}
}
</style>

142
src/components/PublicComponent/Tabs.vue

@ -1,142 +0,0 @@
<template>
<div class="tabs" v-if="sidebarList.find(({ path }) => path === $route.path)">
<div :class="['item', tab.path === $route.path ? 'active' : '']" v-for="tab of sidebarList" :key="tab.title" @click="goPage(tab)">
<div class="icon"><img :src="theme.images[tab.moduleName]" /></div>
<div class="title">{{ switchLanguage(tab, 'title') }}</div>
</div>
</div>
</template>
<script setup>
import { useStore } from '@/store/root'
import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router'
const router = useRouter()
const store = useStore()
const { sidebarList, theme } = storeToRefs(store)
const goPage = item => {
store.SET_SELECTED_MODULE(item.title)
router.push(item.path)
}
</script>
<style lang="scss" scoped>
.tabs {
position: fixed;
top: 400px;
left: 68px;
right: 68px;
display: flex;
.item:nth-child(5n + 1) {
&.active {
.icon {
background: var(--menu-activeBg1);
}
}
.icon {
background: var(--menu-bg1);
}
}
.item:nth-child(5n + 2) {
&.active {
.icon {
background: var(--menu-activeBg2);
}
}
.icon {
background: var(--menu-bg2);
}
}
.item:nth-child(5n + 3) {
&.active {
.icon {
background: var(--menu-activeBg3);
}
}
.icon {
background: var(--menu-bg3);
}
}
.item:nth-child(5n + 4) {
&.active {
.icon {
background: var(--menu-activeBg4);
}
}
.icon {
background: var(--menu-bg4);
}
}
.item:nth-child(5n + 5) {
&.active {
.icon {
background: var(--menu-activeBg5);
}
}
.icon {
background: var(--menu-bg5);
}
}
.item {
position: relative;
display: flex;
flex-direction: column;
flex: 1;
height: 170px;
.icon {
display: flex;
justify-content: center;
align-items: center;
height: 97px;
background: rgba(255, 255, 255, 0.4);
border-radius: var(--global-radius, 24px);
img {
width: 80px;
height: 80px;
}
}
.title {
font-weight: 700;
font-size: 18px;
line-height: 21px;
text-align: center;
color: var(--menu-color);
margin-top: 12px;
}
&.active {
.title {
color: var(--menu-activeColor);
}
&::after {
content: '';
display: block;
position: absolute;
width: 124px;
height: 6px;
left: 0;
right: 0;
bottom: 0;
margin: auto;
background: var(--menu-barBg);
border-radius: 2px 2px 0px 0px;
}
}
}
.item + .item {
margin-left: 24px;
}
}
@media (min-aspect-ratio: 1/1) {
.tabs {
top: 156px;
left: 456px;
right: 456px;
.item {
height: 124px;
.icon {
height: 80px;
}
}
}
}
</style>

92
src/components/QRCode/QRCode.vue

@ -1,92 +0,0 @@
<template>
<div class="full-code">
<div class="code-wrapper">
<img :src="icon" class="img" alt="" v-if="icon.length" />
<img src="../../assets/images/nodata.svg" class="img nodata" alt="" v-else />
<p class="youhui" v-if="icon.length">{{ title }}</p>
<slot />
<div class="close" @click="handleClose">
<img src="../../assets/images/detail/yellow_close.png" alt="" />
</div>
</div>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
default: '扫码排队'
},
icon: {
type: String,
default: ''
}
})
const emits = defineEmits(['close'])
function handleClose() {
emits('close')
}
</script>
<style lang="scss" scoped>
.full-code {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 10;
animation-duration: 0.2s;
}
.code-wrapper {
position: absolute;
top: 50%;
left: 50%;
width: 480px;
min-height: 366px;
transform: translate3d(-50%, -50%, 0);
background: #ffffff;
box-shadow: 0 40px 60px rgba(0, 0, 0, 0.08);
border-radius: 12px;
padding-top: 80px;
z-index: 10;
.img {
display: block;
margin: 0 auto 49px;
width: 150px;
height: 150px;
&.nodata {
width: 300px;
height: 300px;
}
}
.youhui {
font-weight: 700;
font-family: 'font_bold';
font-size: 20px;
line-height: 23px;
text-align: center;
color: rgba(0, 0, 0, 0.8);
}
.close {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 12px;
right: 12px;
width: 56px;
height: 56px;
background: rgba(0, 0, 0, 0.02);
border-radius: 12px;
img {
width: 56px;
height: 56px;
}
}
}
</style>

125
src/components/QuestionClassify/QuestionClassify.vue

@ -1,125 +0,0 @@
<template>
<div class="question-content">
<div class="masker" @click="closeQuestionClassify"></div>
<div class="question-detail">
<h1 class="title">商场相关常见问题</h1>
<h1 class="sub">全部分类</h1>
<ScrollView class="question-scroll" scrollbar>
<div class="scroll">
<QuestionList />
</div>
</ScrollView>
<div class="close" @click="closeQuestionClassify">
<img src="../../assets/images/detail/yellow_close.png" alt="" />
</div>
</div>
</div>
</template>
<script setup>
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import QuestionList from '@/components/QuestionList/QuestionList.vue'
const emits = defineEmits(['close'])
function closeQuestionClassify() {
emits('close')
}
</script>
<style lang="scss" scoped>
.question-content {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
animation-duration: 0.2s;
}
.masker {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.question-detail {
position: absolute;
width: 854px;
height: 964px;
background: #ffffff;
box-shadow: 0px 40px 60px rgba(0, 0, 0, 0.08);
border-radius: 16px;
top: 462px;
left: 50%;
margin-left: -427px;
.title {
font-weight: 700;
font-size: 32px;
font-family: 'font_bold';
text-align: center;
color: rgba(0, 0, 0, 0.8);
padding-bottom: 40px;
padding-top: 56px;
}
.sub {
text-align: center;
font-weight: 700;
font-size: 24px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
margin-bottom: 24px;
}
.question-scroll {
position: relative;
padding-right: 16px;
overflow: hidden;
height: 788px;
background: rgba(0, 0, 0, 0.02);
.scroll {
display: flex;
flex-wrap: wrap;
padding: 80px 80px 0 117px;
:deep(.question_item) {
margin-right: 16px;
margin-bottom: 40px;
}
}
:deep(.bscroll-vertical-scrollbar) {
width: 6px !important;
right: 95px !important;
top: 80px !important;
background: rgba(0, 0, 0, 0.02) !important;
border-radius: 6px !important;
opacity: 1 !important;
height: 152px !important;
.bscroll-indicator {
height: 95px !important;
width: 6px !important;
background: rgba(0, 0, 0, 0.1) !important;
border-radius: 6px !important;
border: none !important;
}
}
}
}
.close {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 12px;
right: 12px;
width: 56px;
height: 56px;
background: rgba(0, 0, 0, 0.02);
border-radius: 12px;
img {
width: 56px;
height: 56px;
}
}
</style>

193
src/components/QuestionList/QuestionList.vue

@ -1,193 +0,0 @@
<template>
<div class="question_item" @click="handleQuestion">
<h1 class="prefix_title">停车相关</h1>
<h1 class="prefix_sub">停车如何收费</h1>
<p class="prefix_detail">MORE</p>
</div>
<Teleport to="body">
<Transition enter-active-class="animate__animated animate__fadeInDown" leave-active-class="animate__animated animate__fadeOutUp">
<div class="question-content" v-if="showQuestion">
<div class="masker" @click="closeQuestionDetail"></div>
<div class="question-detail">
<h1 class="title">商场相关常见问题</h1>
<h1 class="sub">停车相关</h1>
<ScrollView class="question-scroll" scrollbar>
<div class="questions">
<div class="question" v-for="item of 10" :key="item">
<div class="ask">停车如何收费</div>
<div class="answer">
迪奥小姐仿佛一曲爱之赞歌承袭Christian
Dior迪奥先生心中的玫瑰挚恋及DIOR迪奥的高订格调曼妙香韵尽现摩登时尚自由洒脱优雅从容的风格迪奥小姐玫舞轻旋淡香水活力花香调
</div>
</div>
</div>
</ScrollView>
<div class="close" @click="closeQuestionDetail">
<img src="../../assets/images/detail/yellow_close.png" alt="" />
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<script setup>
import { ref } from 'vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
defineProps({
list: {
type: Array,
default: () => []
}
})
const showQuestion = ref(false)
function handleQuestion() {
showQuestion.value = true
}
function closeQuestionDetail() {
showQuestion.value = false
}
</script>
<style lang="scss" scoped>
.question_item {
width: 175px;
height: 225px;
background: #ffffff;
box-shadow: 0px 20px 40px rgba(31, 46, 84, 0.04);
border-radius: 12px;
.prefix_title {
font-weight: 700;
font-size: 20px;
color: rgba(0, 0, 0, 0.8);
font-family: 'font_bold';
padding: 32px 20px 15px 32px;
@include no-wrap;
}
.prefix_sub {
font-size: 14px;
padding: 0 20px 75px 32px;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
color: rgba(0, 0, 0, 0.6);
@include no-wrap;
}
.prefix_detail {
padding-top: 23px;
font-family: 'font_bold';
padding-left: 32px;
font-weight: 700;
font-size: 16px;
color: rgba(0, 0, 0, 0.6);
}
}
.question-content {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
animation-duration: 0.2s;
}
.masker {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.question-detail {
position: absolute;
width: 854px;
height: 964px;
background: #ffffff;
box-shadow: 0px 40px 60px rgba(0, 0, 0, 0.08);
border-radius: 16px;
top: 462px;
left: 50%;
margin-left: -427px;
.title {
font-weight: 700;
font-size: 32px;
font-family: 'font_bold';
text-align: center;
color: rgba(0, 0, 0, 0.8);
padding-bottom: 40px;
padding-top: 56px;
}
.sub {
text-align: center;
font-weight: 700;
font-size: 24px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
margin-bottom: 24px;
}
.question-scroll {
position: relative;
margin-left: 56px;
margin-right: 34px;
padding-right: 16px;
overflow: hidden;
height: 788px;
:deep(.bscroll-vertical-scrollbar) {
width: 6px !important;
background: rgba(0, 0, 0, 0.02) !important;
border-radius: 6px !important;
opacity: 1 !important;
height: 152px !important;
.bscroll-indicator {
height: 95px !important;
width: 6px !important;
background: rgba(0, 0, 0, 0.1) !important;
border-radius: 6px !important;
border: none !important;
}
}
.questions {
padding-bottom: 24px;
}
.question {
margin-bottom: 24px;
padding: 40px;
background: rgba(0, 0, 0, 0.02);
border-radius: 12px;
}
.ask {
font-weight: 700;
font-size: 18px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
margin-bottom: 16px;
}
.answer {
font-size: 16px;
line-height: 200%;
text-align: justify;
color: rgba(0, 0, 0, 0.6);
}
}
}
.close {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 12px;
right: 12px;
width: 56px;
height: 56px;
background: rgba(0, 0, 0, 0.02);
border-radius: 12px;
img {
width: 56px;
height: 56px;
}
}
</style>

396
src/components/Search/Search.vue

@ -1,396 +0,0 @@
<template>
<Dialog @close="close">
<div class="bar">
<div class="icon" :style="{ backgroundImage: `url(${theme.images.searchIcon})` }"></div>
<div class="stick"></div>
<div v-if="!keywords" class="placeholder">请输入中英文<span class="meta">首字母</span>查询例如 XBK (星巴克)</div>
<input v-else @click="setSearch" v-model="keywords" type="text" readonly class="input" />
<img :src="theme.images.searchClear" class="clear" v-if="keywords" @click="clear" />
</div>
<div class="search-content">
<div class="top" v-if="!showVoice">
<div class="facs">
<h1 class="title">公共设施</h1>
<div class="facility-list">
<FacilityItem @click="_handleFacility(item)" class="margin" :facility="item" v-for="item of facilityList" :key="item.title" />
</div>
</div>
<div class="keyboard-wrapper">
<div class="tabs tabs-wrapper">
<div class="tab" :class="{ active: tabIdx === index }" @click="handleTab(index)" v-for="(item, index) of list" :key="item.name">
<img :src="tabIdx === index ? item.iconActive : item.icon" alt="" />
{{ item.name }}
</div>
</div>
<KeyboardByLetter @del="del" @handle-letter="handleLetter" v-if="tabIdx === 0" />
<KeyboardByWritten @del="del" @handle-word="handleLetter" v-else />
</div>
</div>
<div class="recs" v-show="!keywords.length && !showVoice">
<h1 class="title">大家都在找</h1>
<div class="hot-scroll">
<div class="item" v-for="(item, i) of hotRecommend" :key="item.shopId" @click="handleShop(item)">
<div class="text">{{ item.shopName }}</div>
<img class="medal" v-if="i === 0" src="@/views/Index/fir.svg" />
<img class="medal" v-if="i === 1" src="@/views/Index/sec.svg" />
<img class="medal" v-if="i === 2" src="@/views/Index/thi.svg" />
</div>
</div>
</div>
<SearchResultList @handle-shop="handleShop" :list="searchShopListRef" :config="config" v-if="keywords.length && !showVoice" />
<Voice @handle-question="showClassify = true" v-if="showVoice" />
</div>
<img :src="theme.images.searchClose" class="search-btn" @click="close" />
</Dialog>
</template>
<script setup>
import { computed, defineAsyncComponent, ref, onBeforeUnmount } from 'vue'
import { useRouter } from 'vue-router'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { useSearchShop } from '@/composables/useSearchShop'
import { useFacilityNav } from '@/composables/useFacilityNav'
import { useStatistics } from '@/composables/useStatistics'
import Dialog from '@/layouts/Dialog.vue'
import FacilityItem from '@/base/FacilityItem/FacilityItem.vue'
import SearchResultList from '@/components/SearchResultList/SearchResultList.vue'
const KeyboardByLetter = defineAsyncComponent(() => import('@/components/KeyboardByLetter/KeyboardByLetter.vue'))
const KeyboardByWritten = defineAsyncComponent(() => import('@/components/KeyboardByWritten/KeyboardByWritten.vue'))
const Voice = defineAsyncComponent(() => import('@/components/Voice/Voice.vue'))
const MAX_LENGTH = 7
const { _handleFacility } = useFacilityNav()
const router = useRouter()
const store = useStore()
const { indexList, shopList, facilityList, config, showVoice, theme } = storeToRefs(store)
const list = computed(() =>
theme.value
? [
{
name: '键盘输入',
nameEn: 'keyboard input',
icon: theme.value.images.keyboard,
iconActive: theme.value.images.keyboard_active
},
{
name: '手写输入',
nameEn: 'handwriting input',
icon: theme.value.images.write,
iconActive: theme.value.images.write_active
}
]
: []
)
const hotRecommend = computed(() => indexList.value.hotSearch ?? [])
const showClassify = ref(false)
async function handleShop(item) {
useStatistics('brandSearch')
const shop = shopList.value.find(_shop => _shop.shopId === item.shopId)
store.SET_SHOP(shop)
if (router.currentRoute.value.fullPath !== '/guide') {
store.SET_SELECTED_MODULE('Guide')
await router.push('/guide')
}
store.SET_SHOW_SEARCH(false)
}
const tabIdx = ref(0)
function handleTab(index) {
tabIdx.value = index
}
const keywords = ref('')
const searchType = ref(0) //0: 1:
const { searchShopListRef } = useSearchShop(keywords, searchType)
function handleLetter(item) {
if (keywords.value.length >= MAX_LENGTH) return
keywords.value += item
}
function del() {
keywords.value = keywords.value.substring(0, keywords.value.length - 1)
}
const clear = () => {
keywords.value = ''
}
function setSearch() {
showVoice.value && store.SET_SHOW_VOICE(false)
}
function close() {
store.SET_SHOW_SEARCH(false)
}
onBeforeUnmount(() => {
showVoice.value && store.SET_SHOW_VOICE(false)
})
</script>
<style lang="scss" scoped>
:deep(.header-left) {
padding-left: 80px;
}
:deep(.view-container) {
position: relative;
z-index: 50;
}
.bar {
position: fixed;
top: 260px;
left: 68px;
right: 68px;
background: var(--search-barBackground);
border: var(--searchBar-border);
box-shadow: 0px 15px 24px rgba(0, 0, 0, 0.05);
border-radius: var(--searchBar-borderRadius);
display: flex;
align-items: center;
padding-right: 10px;
padding-left: 40px;
height: 100px;
z-index: 52;
.icon {
width: 56px;
height: 56px;
background: center / cover no-repeat;
}
.stick {
width: 4px;
height: 38px;
background: var(--searchBar-stickBg);
margin: 0 32px;
}
.placeholder {
font-weight: 700;
font-size: 20px;
line-height: 23px;
color: var(--searchBar-placeholderColor);
.meta {
color: var(--search-placeholderMetaColor);
}
}
.input {
flex: 1;
border: none;
outline: none;
width: 262px;
background: transparent;
font-weight: 700;
font-size: 48px;
line-height: 59px;
color: var(--searchBar-color);
}
.clear {
display: flex;
width: 80px;
height: 80px;
}
.voice-icon {
width: 190px;
}
}
.search-content {
position: absolute;
top: 315px;
right: 40px;
bottom: 302px;
left: 40px;
background: var(--search-background);
backdrop-filter: blur(10px);
border-radius: var(--searchBar-borderRadius);
z-index: 2;
overflow: hidden;
.top {
height: 460px;
background: var(--search-topBg, rgba(255, 255, 255, 0.6));
border-radius: var(--searchBar-borderRadius);
margin: 10px 10px 48px 10px;
display: flex;
justify-content: space-between;
padding-left: 46px;
overflow: hidden;
.facs {
.title {
font-weight: 900;
font-size: 24px;
line-height: 28px;
color: var(--search-facTitleColor);
}
width: 300px;
padding-top: 111px;
.facility-list {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
width: 264px;
margin-top: 40px;
gap: 12px 24px;
}
}
.tabs-wrapper {
display: flex;
width: 100%;
height: 64px;
background: var(--search-tabsBg, rgba(0, 0, 0, 0.05));
border-radius: var(--global-radius, 12px);
overflow: hidden;
.tab {
height: 100%;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: var(--search-tabColor, rgba(0, 0, 0, 0.6));
border-radius: var(--global-radius, 12px);
&.active {
background: var(--search-tabActiveBg, #ffffff);
color: var(--search-tabActiveColor, rgba(0, 0, 0, 0.8));
}
}
img {
width: 24px;
height: 24px;
margin-right: 12px;
}
}
.keyboard-wrapper {
flex: 1;
padding-top: 75px;
padding-left: 36px;
background: rgba(0, 0, 0, 0.05);
padding-right: 38px;
.tabs {
padding-right: 8px;
}
}
}
.recs {
padding-left: 56px;
.title {
color: var(--search-hotSearchTitleColor);
}
.hot-scroll {
padding-top: 16px;
white-space: nowrap;
width: 944px;
.item {
position: relative;
display: inline-block;
max-width: 170px;
height: 52px;
background: var(--search-hotSearchBg);
border-radius: var(--global-radius, 100px);
.text {
text-align: center;
font-weight: 900;
font-size: 16px;
line-height: 52px;
color: var(--search-hotSearchColor);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0 32px;
}
.medal {
position: absolute;
width: 32px;
height: 32px;
left: 4px;
top: -10px;
}
}
.item + .item {
margin-left: 24px;
}
}
}
}
.search-right {
position: relative;
background: rgba(255, 255, 255, 0.6);
padding-top: 180px;
padding-left: 64px;
width: 696px;
.title,
.title-en {
margin-right: 76px;
}
}
.search-btn {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
left: 0;
right: 0;
margin: auto;
bottom: 242px;
width: 120px;
height: 120px;
z-index: 52;
}
@media (min-aspect-ratio: 1/1) {
.search-content {
top: 87px;
right: 125px;
bottom: 87px;
left: 125px;
padding: 100px 670px 0 0;
.top {
position: absolute;
top: 10px;
right: 10px;
bottom: 10px;
width: 660px;
height: auto;
margin: 0;
padding-left: 0;
flex-direction: column;
.facs {
width: auto;
flex: 1;
padding-top: 90px;
padding: 90px 54px;
.facility-list {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}
}
.keyboard-wrapper {
flex: 0 0 456px;
}
}
}
.bar {
top: 32px;
width: 698px;
left: 0;
right: 0;
margin: auto;
}
.search-btn {
left: auto;
bottom: auto;
right: 145px;
top: 107px;
width: 87px;
height: 87px;
}
}
</style>

BIN
src/components/Search/clear.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 760 B

BIN
src/components/Search/icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

40
src/components/Search/tabs.js

@ -1,40 +0,0 @@
export const letter = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
0,
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'空格',
'del'
]

84
src/components/SearchResultItem/SearchResultItem.vue

@ -1,84 +0,0 @@
<template>
<div class="item" @click="handleShop">
<div class="icon-wrapper">
<img :src="config.sourceUrl + shop.logoUrl" class="icon" alt="" />
</div>
<div class="item-bottom">
<div class="left">{{ switchLanguage(shop, 'shopName') }}</div>
<div class="right">{{ shop.floor }}</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
shop: {
type: Object,
default: () => ({})
},
config: {
type: Object,
default: () => ({})
}
})
const emits = defineEmits(['click'])
function handleShop() {
emits('click', props.shop)
}
</script>
<style lang="scss" scoped>
.item {
display: flex;
flex-direction: column;
align-items: center;
width: 210px;
height: 200px;
background: var(--brand-background);
border-radius: var(--global-radius, 8px);
margin-right: 8px;
margin-top: 24px;
.icon-wrapper {
display: flex;
width: 210px;
height: 160px;
background: #ffffff;
border-radius: var(--global-radius, 8px);
justify-content: center;
align-items: center;
.icon {
width: 120px;
height: 120px;
object-fit: contain;
}
}
.item-bottom {
display: flex;
width: 100%;
flex: 1;
padding: 0 12px;
align-items: center;
.left,
.right {
font-weight: 700;
font-size: 14px;
line-height: 16px;
}
.left {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--brand-color);
}
.right {
width: 40px;
text-align: right;
color: var(--brand-metaColor);
}
}
}
</style>

140
src/components/SearchResultList/SearchResultList.vue

@ -1,140 +0,0 @@
<template>
<ScrollView :list="list" class="search-result-scroll" :scrollbar="true">
<div class="scroll-result">
<h1 class="title">
搜索结果 <span class="meta">/ {{ list.length }}</span>
</h1>
<TransitionGroup name="zoom" tag="div" class="list">
<SearchResultItem :config="config" @click="handleShop" :shop="item" v-for="item of list" :key="item.shopId" />
</TransitionGroup>
</div>
</ScrollView>
</template>
<script setup>
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import SearchResultItem from '@/components/SearchResultItem/SearchResultItem.vue'
defineProps({
list: {
type: Array,
default: () => []
},
config: {
type: Object,
default: () => ({})
}
})
const emits = defineEmits(['handle-shop'])
function handleShop(item) {
emits('handle-shop', item)
}
</script>
<style lang="scss" scoped>
.search-result-scroll {
position: relative;
height: 842px;
overflow: hidden;
margin-left: 68px;
margin-right: 7px;
.scroll-result {
padding-bottom: 200px;
}
.list {
display: flex;
flex-wrap: wrap;
margin-bottom: 44px;
}
.activity {
display: flex;
flex-wrap: wrap;
.flex {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.pl-24 {
padding-left: 24px;
}
.activity-item {
display: flex;
align-items: center;
padding: 0 8px;
width: 457px;
height: 84px;
background: rgba(255, 255, 255, 0.8);
border-radius: 12px;
margin-right: 6px;
margin-bottom: 8px;
.activity-name {
font-weight: 700;
font-size: 18px;
color: rgba(0, 0, 0, 0.6);
font-family: 'font_bold';
padding-bottom: 16px;
}
.intro {
color: rgba(0, 0, 0, 0.6);
font-size: 14px;
}
.img-wrapper {
width: 120px;
height: 68px;
margin-right: 24px;
border-radius: 8px;
.img {
width: 100%;
height: 100%;
border-radius: 8px;
object-fit: cover;
}
}
}
}
.title {
font-style: normal;
font-weight: 900;
font-size: 24px;
line-height: 28px;
color: var(--search-resultTitleColor, rgba(0, 0, 0, 0.8));
.meta {
font-weight: 500;
font-size: 20px;
line-height: 23px;
color: var(--search-resultMetaColor, rgba(0, 0, 0, 0.6));
}
}
:deep(.bscroll-vertical-scrollbar) {
width: 48px !important;
background: center / 6px 250px no-repeat url(@/assets/images/scrollBar.png);
border-radius: 6px;
opacity: 1 !important;
height: 250px !important;
&::after {
position: absolute;
content: '';
left: 0;
top: 120px;
margin: auto;
width: 48px;
height: 61px;
background: center / cover no-repeat url(@/assets/images/scrollHand.png);
}
.bscroll-indicator {
height: 95px !important;
width: 6px !important;
left: 0;
right: 0;
margin: auto;
background: #ffffff !important;
border-radius: 6px !important;
border: none !important;
}
}
}
</style>

86
src/components/ServiceList/ServiceList.vue

@ -1,86 +0,0 @@
<template>
<ScrollView class="serve-scroll" :list="serveList">
<TransitionGroup name="zoom" tag="div" class="serve-content">
<div class="serve-item" v-for="item of serveList" :key="item.name">
<div class="white-bg">
<div class="icon-box">
<img class="icon" :src="config.sourceUrl + item.fileUrl" alt="" />
</div>
</div>
<p class="name">{{ item.name }}</p>
<p class="en">{{ item.nameEn }}</p>
</div>
</TransitionGroup>
</ScrollView>
</template>
<script setup>
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { getServeList } from '@/http/api'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
const store = useStore()
const { config } = storeToRefs(store)
const serveList = ref([])
getServeList().then(({ data }) => {
serveList.value = data?.serveList ?? []
})
</script>
<style lang="scss" scoped>
.serve-scroll {
position: relative;
overflow: hidden;
margin-left: 170px;
margin-right: 24px;
margin-top: 58px;
height: 1560px;
.serve-content {
display: flex;
flex-wrap: wrap;
.serve-item {
width: 279px;
height: 225px;
// TODO
background: #fcf8ec;
box-shadow: 0px 20px 40px rgba(31, 46, 84, 0.04);
border-radius: 12px;
margin-right: 16px;
margin-bottom: 29px;
.white-bg {
height: 147px;
background: #fff;
border-radius: 12px;
padding: 32px;
margin-bottom: 20px;
}
.icon-box {
width: 56px;
height: 56px;
.icon {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.name {
font-weight: 700;
font-family: 'font_bold';
font-size: 16px;
padding-left: 32px;
color: rgba(0, 0, 0, 0.6);
padding-bottom: 6px;
}
.en {
font-weight: 400;
font-size: 12px;
padding-left: 32px;
color: rgba(0, 0, 0, 0.6);
}
}
}
}
</style>

104
src/components/Sidebar/Sidebar.vue

@ -1,104 +0,0 @@
<template>
<div class="sidebar-wrapper">
<div class="menu-list" @click="goPage(item)" :class="{ active: selectedModule === item.title }" v-for="item of sidebarList" :key="item.title">
<img :src="item.icon" alt="" />
<span class="text">{{ item.title }}</span>
<span class="text-en">{{ item.titleEn }}</span>
</div>
</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { useRouter } from 'vue-router'
const router = useRouter()
const store = useStore()
const { sidebarList, selectedModule } = storeToRefs(store)
function goPage(item) {
store.SET_SELECTED_MODULE(item.title)
router.push(item.path)
}
</script>
<style lang="scss" scoped>
.sidebar-wrapper {
position: fixed;
display: flex;
flex-direction: column;
align-items: center;
width: 130px;
height: 1920px;
left: 0px;
top: 0;
background: var(--color-white-opacity-8);
box-shadow: var(--shadow-seven);
backdrop-filter: blur(20px);
z-index: 50;
.menu-list {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 110px;
height: 140px;
margin-bottom: 10px;
&::before,
&::after {
position: absolute;
content: '';
width: 110px;
height: 140px;
left: 0;
bottom: 0;
border-radius: 8px;
transition: bottom 0.5s;
}
&.active {
&::before {
background-color: #fff;
z-index: 3;
bottom: 4px;
}
&::after {
background: var(--color-linear-lightgoldenyellow);
z-index: 2;
bottom: 0;
height: 136px;
}
}
img {
position: relative;
width: 80px;
height: 80px;
z-index: 10;
}
.text {
position: relative;
z-index: 10;
color: var(--color-black-opacity-6);
font-family: 'font_bold';
font-weight: 700;
font-size: 14px;
padding: 4px 0;
}
.text-en {
position: relative;
z-index: 10;
color: var(--color-black-opacity-4);
font-size: 12px;
transform: scale(0.83);
}
}
}
</style>

72
src/components/Sidebar/list.js

@ -1,72 +0,0 @@
export const sidebarList = [
{
icon: require('@/assets/images/sidebar/activity.png'),
title: '商场活动',
moduleName: 'activity',
titleEn: 'SELECTION',
path: '/activity'
},
{
icon: require('@/assets/images/sidebar/member.png'),
title: '尊享会员',
moduleName: 'member',
titleEn: 'MEMBER',
path: '/member'
},
{
icon: require('@/assets/images/sidebar/parking.png'),
title: '泊车缴费',
moduleName: 'parking',
titleEn: 'PARKING',
path: '/parking'
},
{
icon: require('@/assets/images/sidebar/movie.png'),
title: '影视天地',
moduleName: 'movie',
titleEn: 'MOVIE',
path: '/movie'
},
{
icon: require('@/assets/images/sidebar/mall.png'),
title: '商场介绍',
moduleName: 'mall',
titleEn: 'MALL',
path: '/mall'
}
// {
// icon: require('@/assets/images/sidebar/index.png'),
// title: '首页',
// moduleName: 'home',
// titleEn: 'HOME',
// path: '/'
// },
// {
// icon: require('@/assets/images/sidebar/guide.png'),
// title: '地图导览',
// moduleName: 'home',
// titleEn: 'MAP',
// path: '/guide'
// },
// {
// icon: require('@/assets/images/sidebar/brand.png'),
// title: '品牌列表',
// moduleName: 'brand',
// titleEn: 'BRAND',
// path: '/brand'
// }
// {
// icon: require('@/assets/images/sidebar/foods.png'),
// title: '特色美食',
// moduleName: 'food',
// titleEn: 'FOOD',
// path: '/foods'
// },
// {
// icon: require('@/assets/images/sidebar/service.png'),
// title: '商场服务',
// titleEn: 'SERVICE',
// moduleName: 'service',
// path: '/service'
// }
]

57
src/components/Tabs/Tabs.vue

@ -1,57 +0,0 @@
<template>
<div class="tabs-wrapper">
<div class="tab" :class="{ active: tabIdx === index }" @click="handleTab(index)" v-for="(item, index) of list" :key="item.name">
<img :src="tabIdx === index ? item.iconActive : item.icon" alt="" />
{{ item.name }}
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
defineProps({
list: {
type: Array,
default: () => []
}
})
const emits = defineEmits(['click'])
const tabIdx = ref(0)
function handleTab(index) {
tabIdx.value = index
emits('click', index)
}
</script>
<style lang="scss" scoped>
.tabs-wrapper {
display: flex;
width: 100%;
height: 64px;
background: var(--search-tabsBg, rgba(0, 0, 0, 0.05));
border-radius: var(--global-radius, 12px);
overflow: hidden;
.tab {
height: 100%;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: var(--search-tabColor, rgba(0, 0, 0, 0.6));
border-radius: var(--global-radius, 12px);
&.active {
background: var(--search-tabActiveBg, #ffffff);
color: var(--search-tabActiveColor, rgba(0, 0, 0, 0.8));
}
}
img {
width: 24px;
height: 24px;
margin-right: 12px;
}
}
</style>

49
src/components/Traffic/Traffic.vue

@ -1,49 +0,0 @@
<template>
<div class="carousel">
<EffectFade :list="list">
<template v-slot="{ item }">
<div class="banner-wrapper">
<img :src="config.sourceUrl + item" alt="" class="banner" />
</div>
</template>
</EffectFade>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { getTrafficList } from '@/http/api'
import EffectFade from '../EffectFade/EffectFade.vue'
const store = useStore()
const { config } = storeToRefs(store)
const list = ref([])
getTrafficList().then(({ data }) => {
list.value = data?.fileList ?? []
})
</script>
<style lang="scss" scoped>
.carousel {
position: relative;
width: 870px;
height: 1300px;
margin-top: 58px;
margin-left: 170px;
margin-right: 40px;
}
.banner-wrapper {
width: 870px;
height: 1300px;
overflow: hidden;
}
.banner {
width: 100%;
height: 100%;
border-radius: 10px;
object-fit: cover;
}
</style>

241
src/components/Voice/Voice.vue

@ -1,241 +0,0 @@
<template>
<div class="speech-wrapper" :class="{ active: !startSpeech }">
<div class="lottie-box">
<Lottie :isLocale="true" ref="lottieInstance" :path="lottie" v-show="startSpeech" />
</div>
<div class="start-btn" @click="handleSpeech">
<img src="../../assets/images/search/speech.png" alt="" />
<span class="text">点我开始说话</span>
</div>
</div>
<ScrollView class="voice-scroll">
<div class="voice-content">
<h1 class="title">试着问我一下这些问题吧</h1>
<div class="commons">
<div class="common-question">
<img class="img" src="/static/img/xsj.png" alt="" />
<div class="common-right">
<h1 class="sub">卫生间</h1>
<h1 class="desc">附近卫生间怎么走</h1>
</div>
</div>
</div>
<h1 class="title">您还可查看更多商场相关问题</h1>
<div class="groups">
<div class="box-group" @click="showCustomerDialog = true">
<img src="../../assets/images/search/customer.png" class="icon" alt="" />
<p class="text">转人工服务</p>
</div>
<div class="box-group" @click="handleMoreQuestion">
<img src="../../assets/images/search/question.png" class="icon" alt="" />
<p class="text">常见问题</p>
</div>
</div>
<div class="dialog flex">
<div class="customer">我想开小票</div>
</div>
<div class="dialog">
<p class="machine">您可以尝试换一种提问方式导航到服务台或是拨打400-888-8888或转人工服务</p>
</div>
</div>
</ScrollView>
<Teleport to="body">
<Transition enter-active-class="animate__aniamted animate__zoomIn" leave-active-class="animate__aniamted animate__zoomOut">
<Customer @close="showCustomerDialog = false" v-if="showCustomerDialog" />
</Transition>
</Teleport>
</template>
<script setup>
import { ref, nextTick, defineAsyncComponent } from 'vue'
import lottie from './voice.json'
import Lottie from '@/components/Lottie/Lottie.vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
const Customer = defineAsyncComponent(() => import('@/components/Customer/Customer.vue'))
const startSpeech = ref(false)
const lottieInstance = ref(null)
function handleSpeech() {
startSpeech.value = !startSpeech.value
nextTick(() => {
if (!startSpeech.value) {
lottieInstance.value.lottieInstance.stop()
} else {
lottieInstance.value.lottieInstance.play()
}
})
}
const emits = defineEmits(['handle-question'])
function handleMoreQuestion() {
emits('handle-question')
}
const showCustomerDialog = ref(false)
</script>
<style lang="scss" scoped>
.speech-wrapper {
position: absolute;
left: 16px;
right: 16px;
top: 416px;
height: 360px;
:deep(svg) {
height: 85px !important;
}
.lottie-box {
position: relative;
height: 115px;
margin-top: -42px;
&::before,
&::after {
content: '';
position: absolute;
width: 328px;
top: 42px;
height: 2px;
background: linear-gradient(90deg, #ffbd35 0%, #ffd260 100%);
}
&::before {
left: 0;
}
&::after {
right: 0;
}
}
:deep(svg) {
transform: scale(3.5) !important;
}
.start-btn {
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
left: 50%;
top: 98px;
transform: translateX(-50%);
img {
width: 160px;
}
.text {
font-weight: 700;
font-size: 18px;
color: rgba(0, 0, 0, 0.6);
font-family: 'font_bold';
}
}
&.active {
&:before {
position: absolute;
content: '';
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, #ffbd35 0%, #ffd260 100%);
}
}
}
.voice-scroll {
position: fixed;
top: 802px;
left: 80px;
right: 58px;
bottom: 0;
.title {
font-weight: 700;
font-size: 24px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
padding-bottom: 24px;
}
.commons {
display: flex;
flex-wrap: wrap;
margin-bottom: 56px;
}
.common-question {
display: flex;
align-items: center;
width: 352px;
height: 72px;
background: rgba(255, 255, 255, 0.8);
border-radius: 12px;
padding: 14px 24px;
margin-right: 8px;
margin-bottom: 8px;
.img {
width: 44px;
height: 44px;
margin-right: 24px;
}
.sub {
font-weight: 700;
font-size: 14px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
padding-bottom: 6px;
}
.desc {
color: rgba(0, 0, 0, 0.4);
font-size: 12px;
}
}
.groups {
display: flex;
}
.box-group {
width: 196px;
height: 177px;
background: #ffffff;
box-shadow: 0px 20px 40px rgba(31, 46, 84, 0.04);
border-radius: 12px;
margin-right: 16px;
margin-bottom: 24px;
.icon {
display: inline-block;
margin: 32px 0 32px 32px;
width: 48px;
}
.text {
font-weight: 700;
font-size: 16px;
line-height: 19px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
padding-left: 32px;
padding-top: 23px;
border-top: 1px solid rgba(0, 0, 0, 0.05);
}
}
}
.machine {
display: inline-block;
padding: 24px 40px;
background: rgba(0, 0, 0, 0.05);
border-radius: 0px 32px 32px 32px;
font-size: 18px;
line-height: 21px;
color: #605e74;
margin-bottom: 16px;
}
.flex {
display: flex;
flex-direction: row-reverse;
}
.customer {
padding: 24px 40px;
background: #ffffff;
font-weight: 700;
font-size: 24px;
line-height: 28px;
font-family: 'font_bold';
color: rgba(0, 0, 0, 0.6);
border-radius: 32px 0px 32px 32px;
text-align: right;
margin-bottom: 24px;
}
</style>

1
src/components/Voice/voice.json

File diff suppressed because one or more lines are too long

116
src/components/WaterfallList/WaterfallList.vue

@ -1,116 +0,0 @@
<template>
<ScrollView class="waterfall-scroll" :list="list" observe-image>
<TransitionGroup name="zoom" tag="div" class="waterfall-content-scroll">
<div class="waterfall-item" v-for="item of list" @click="handleWaterfall(item)" :key="item.name">
<img :src="config.sourceUrl + item.fileUrl" alt="" class="waterfall-img" />
<div class="title-wrapper">
<h6 class="waterfall-title">{{ switchLanguage(item, 'name') }}</h6>
<h5 class="sub" v-if="item?.shopId">
<h6 class="waterfall-name">{{ switchLanguage(item, 'shopName') }}</h6>
<h6 class="house-num pb-32">{{ item.houseNumber }}</h6>
</h5>
</div>
</div>
</TransitionGroup>
</ScrollView>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router'
import { useStore } from '@/store/root'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import Shop from '@/utils/Class/Shop'
defineProps({
list: {
type: Array,
default: () => []
},
config: {
type: Object,
default: () => ({})
}
})
const router = useRouter()
const store = useStore()
const { shopList } = storeToRefs(store)
function handleWaterfall(item) {
let shop
if (item?.shopId) {
shop = shopList.value.find(_shop => _shop.shopId === item.shopId)
if (shop) {
store.SET_SHOP(shop)
store.SET_SHOW_DETAIL(true)
}
} else {
const { name, floorOrder, floor, logoUrl, point } = item
shop = new Shop(name, floorOrder, floor, logoUrl, point)
if (shop) {
store.SET_SHOP(shop)
router.push('/nav')
}
}
}
</script>
<style lang="scss" scoped>
.waterfall-scroll {
position: absolute;
left: 88px;
right: 70px;
top: 272px;
bottom: 0;
overflow: hidden;
z-index: 10;
border-radius: 12px;
.waterfall-content-scroll {
column-count: 2;
padding-bottom: 16px;
}
.waterfall-item {
break-inside: avoid;
height: auto;
width: 440px;
background-color: var(--color-white-opacity);
margin-right: 24px;
margin-bottom: 16px;
overflow: hidden;
border-radius: 12px;
.waterfall-img {
width: 440px;
margin-bottom: 32px;
}
.title-wrapper {
padding-left: 32px;
padding-right: 32px;
}
.waterfall-title {
font-weight: 700;
font-size: 20px;
font-family: 'font_bold';
color: var(--color-black-opacity-8);
padding-bottom: 13px;
@include no-wrap;
}
.sub {
display: flex;
justify-content: space-between;
}
.waterfall-name,
.house-num {
font-weight: 700;
font-family: 'font_bold';
font-size: 16px;
line-height: 19px;
color: var(--color-black-opacity-4);
}
.pb-32 {
padding-bottom: 32px;
}
}
}
</style>

171
src/components/Written/Written.vue

@ -1,171 +0,0 @@
<template>
<canvas :style="{ backgroundColor: backgroundColor, borderRadius: borderRadius }" ref="inkCanvasRef" :width="width" :height="height" id="ink-canvas"></canvas>
</template>
<script setup>
import { reactive, computed, onMounted, onBeforeUnmount, watch, ref } from 'vue'
import { getHandWriting } from '@/http/api'
const props = defineProps({
width: {
type: Number,
default: 800
},
height: {
type: Number,
default: 800
},
lang: {
type: String,
default: 'CN',
validator(value) {
return ['CN', 'EN'].includes(value)
}
},
backgroundColor: {
type: String,
default: '#f2f2f2'
},
borderRadius: {
type: String,
default: '10px'
},
fillText: {
type: String,
default: '手写区域'
},
fillFontSize: {
type: String,
default: '100px'
},
strokeStyle: {
type: String,
default: '#000'
},
fillStyle: {
type: String,
default: 'rgba(85, 73, 54, 0.1)'
}
})
const emits = defineEmits(['result'])
const state = reactive({
handWrite: '',
isClick: false,
//X
clickX: [],
//Y
clickY: [],
//1
clickC: [],
X: 0,
Y: 0,
oldX: 0,
oldY: 0,
timer: 0,
list: []
})
const inkCanvasRef = ref(null)
const ctxRef = computed(() => inkCanvasRef.value.getContext('2d'))
function updateBound() {
const bound = inkCanvasRef.value.getBoundingClientRect()
state.X = bound.x
state.Y = bound.y
}
function down(ev) {
const cx = Math.floor(ev.clientX || ev.targetTouches[0].clientX) - state.X
const cy = Math.floor(ev.clientY || ev.targetTouches[0].clientY) - state.Y
clearTimeout(state.timer)
state.oldX = cx
state.oldY = cy
ctxRef.value.beginPath()
state.isClick = true
}
function move(ev) {
ev.preventDefault()
if (state.isClick) {
const cx = Math.floor(ev.clientX || ev.targetTouches[0].clientX) - state.X
const cy = Math.floor(ev.clientY || ev.targetTouches[0].clientY) - state.Y
state.clickX.push(cx)
state.clickY.push(cy)
state.clickC.push(0)
//
ctxRef.value.lineWidth = 8
ctxRef.value.lineCap = 'round'
ctxRef.value.moveTo(state.oldX, state.oldY)
ctxRef.value.lineTo(cx, cy)
ctxRef.value.stroke()
state.oldX = cx
state.oldY = cy
}
}
function mouseUp() {
if (state.isClick) {
state.isClick = false
state.timer = setTimeout(() => {
reload()
}, 1500)
//
state.clickC.pop()
state.clickC.push(1)
_getHandWriting()
}
}
function reload() {
if (!inkCanvasRef.value) {
return
}
state.clickX = []
state.clickY = []
state.clickC = []
ctxRef.value.strokeStyle = props.strokeStyle
ctxRef.value.fillStyle = props.fillStyle
ctxRef.value.clearRect(0, 0, props.width, props.height)
ctxRef.value.font = `bold ${props.fillFontSize} Arial`
ctxRef.value.textAlign = 'center'
ctxRef.value.textBaseline = 'middle'
ctxRef.value.fillText(props.fillText, props.width / 2, props.height / 2, 1000)
}
function _getHandWriting() {
const params = {
lib: props.lang,
lpXis: state.clickX,
lpYis: state.clickY,
lpCis: state.clickC
}
getHandWriting(params)
.then(res => {
state.list = res
})
.catch(err => {
console.log(err)
})
}
watch(
() => state.list,
newVal => {
emits('result', newVal)
}
)
onMounted(() => {
updateBound()
reload()
inkCanvasRef.value.addEventListener('touchstart', down)
inkCanvasRef.value.addEventListener('touchmove', move)
inkCanvasRef.value.addEventListener('touchend', mouseUp)
})
onBeforeUnmount(() => {
inkCanvasRef.value.removeEventListener('touchstart', down)
inkCanvasRef.value.removeEventListener('touchmove', move)
inkCanvasRef.value.removeEventListener('touchend', mouseUp)
})
</script>

4
src/composables/useHandleScreen.js

@ -134,11 +134,11 @@ export const useHandleScreen = callback => {
state.isWall = false state.isWall = false
state.autoTimer = setTimeout(async () => { state.autoTimer = setTimeout(async () => {
if (state.times[0] !== 0) { if (state.times[0] !== 0) {
if (router.currentRoute.value.fullPath !== '/index') {
if (router.currentRoute.value.fullPath !== '/guide') {
await indexPromise() await indexPromise()
} }
if ((state.times[1] === 0 && router.currentRoute.value.fullPath === '/index') || (state.times[0] === 0 && state.times[1] === 0)) {
if ((state.times[1] === 0 && router.currentRoute.value.fullPath === '/guide') || (state.times[0] === 0 && state.times[1] === 0)) {
await rootPromise() await rootPromise()
callback && callback() callback && callback()
} }

19
src/layouts/View.vue

@ -34,24 +34,7 @@ defineProps({
.bgTop { .bgTop {
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.5) 100%); background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.5) 100%);
border-bottom: 1px solid rgba(255, 255, 255, 0.8); border-bottom: 1px solid rgba(255, 255, 255, 0.8);
height: 408px;
&.long {
height: 570px;
}
&.mid {
height: 515px;
}
}
@media (min-aspect-ratio: 1/1) {
.bgTop {
height: 280px;
&.long {
height: 280px;
}
&.mid {
height: 280px;
}
}
height: 330px;
} }
} }
</style> </style>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save