Bläddra i källkod

feat: 初始化提交

laiqi 1 år sedan
förälder
incheckning
c02a755504
100 ändrade filer med 10814 tillägg och 44 borttagningar
  1. 0 36
      README.en.md
  2. 0 8
      README.md
  3. 1 0
      admin
  4. 6 0
      hn-shop-uniapp/.gitignore
  5. 30 0
      hn-shop-uniapp/.hbuilderx/launch.json
  6. 13 0
      hn-shop-uniapp/.vite/deps/_metadata.json
  7. 1 0
      hn-shop-uniapp/.vite/deps/package.json
  8. 766 0
      hn-shop-uniapp/.vite/deps/weixin-js-sdk.js
  9. 3 0
      hn-shop-uniapp/.vite/deps/weixin-js-sdk.js.map
  10. 174 0
      hn-shop-uniapp/App.vue
  11. 191 0
      hn-shop-uniapp/LICENSE
  12. 70 0
      hn-shop-uniapp/README.md
  13. 14 0
      hn-shop-uniapp/androidPrivacy.json
  14. 47 0
      hn-shop-uniapp/api/address.js
  15. 203 0
      hn-shop-uniapp/api/app.js
  16. 11 0
      hn-shop-uniapp/api/article/category.js
  17. 17 0
      hn-shop-uniapp/api/article/index.js
  18. 11 0
      hn-shop-uniapp/api/balance.js
  19. 11 0
      hn-shop-uniapp/api/balance/log.js
  20. 17 0
      hn-shop-uniapp/api/captcha.js
  21. 35 0
      hn-shop-uniapp/api/cart.js
  22. 35 0
      hn-shop-uniapp/api/cashier/index.js
  23. 11 0
      hn-shop-uniapp/api/category/index.js
  24. 19 0
      hn-shop-uniapp/api/checkout.js
  25. 23 0
      hn-shop-uniapp/api/comment.js
  26. 16 0
      hn-shop-uniapp/api/coupon.js
  27. 11 0
      hn-shop-uniapp/api/express.js
  28. 37 0
      hn-shop-uniapp/api/goods/index.js
  29. 11 0
      hn-shop-uniapp/api/goods/service.js
  30. 11 0
      hn-shop-uniapp/api/help.js
  31. 12 0
      hn-shop-uniapp/api/invoice/index.js
  32. 29 0
      hn-shop-uniapp/api/login/index.js
  33. 22 0
      hn-shop-uniapp/api/myCoupon.js
  34. 42 0
      hn-shop-uniapp/api/order.js
  35. 17 0
      hn-shop-uniapp/api/order/comment.js
  36. 13 0
      hn-shop-uniapp/api/page.js
  37. 11 0
      hn-shop-uniapp/api/points/log.js
  38. 27 0
      hn-shop-uniapp/api/recharge.js
  39. 11 0
      hn-shop-uniapp/api/recharge/order.js
  40. 35 0
      hn-shop-uniapp/api/refund.js
  41. 17 0
      hn-shop-uniapp/api/region.js
  42. 11 0
      hn-shop-uniapp/api/setting.js
  43. 78 0
      hn-shop-uniapp/api/store.js
  44. 18 0
      hn-shop-uniapp/api/upload.js
  45. 34 0
      hn-shop-uniapp/api/user.js
  46. 11 0
      hn-shop-uniapp/api/user/coupon.js
  47. 43 0
      hn-shop-uniapp/app.scss
  48. 184 0
      hn-shop-uniapp/colorui/animation.css
  49. 65 0
      hn-shop-uniapp/colorui/components/cu-custom.vue
  50. 36 0
      hn-shop-uniapp/colorui/icon.css
  51. 3929 0
      hn-shop-uniapp/colorui/main.css
  52. 237 0
      hn-shop-uniapp/colorui/reset.css
  53. 7 0
      hn-shop-uniapp/common/constant/index.js
  54. 0 0
      hn-shop-uniapp/common/data/region.js
  55. 11 0
      hn-shop-uniapp/common/enum/Client.js
  56. 10 0
      hn-shop-uniapp/common/enum/coupon/ApplyRange.js
  57. 10 0
      hn-shop-uniapp/common/enum/coupon/CouponType.js
  58. 10 0
      hn-shop-uniapp/common/enum/coupon/ExpireType.js
  59. 5 0
      hn-shop-uniapp/common/enum/coupon/index.js
  60. 85 0
      hn-shop-uniapp/common/enum/enum.js
  61. 10 0
      hn-shop-uniapp/common/enum/goods/GoodsType.js
  62. 10 0
      hn-shop-uniapp/common/enum/goods/SpecType.js
  63. 4 0
      hn-shop-uniapp/common/enum/goods/index.js
  64. 10 0
      hn-shop-uniapp/common/enum/order/DeliveryStatus.js
  65. 10 0
      hn-shop-uniapp/common/enum/order/DeliveryType.js
  66. 9 0
      hn-shop-uniapp/common/enum/order/OrderSource.js
  67. 12 0
      hn-shop-uniapp/common/enum/order/OrderStatus.js
  68. 10 0
      hn-shop-uniapp/common/enum/order/OrderType.js
  69. 10 0
      hn-shop-uniapp/common/enum/order/PayStatus.js
  70. 10 0
      hn-shop-uniapp/common/enum/order/ReceiptStatus.js
  71. 11 0
      hn-shop-uniapp/common/enum/order/delivery/DeliveryMethod.js
  72. 3 0
      hn-shop-uniapp/common/enum/order/delivery/index.js
  73. 17 0
      hn-shop-uniapp/common/enum/order/index.js
  74. 11 0
      hn-shop-uniapp/common/enum/order/refund/AuditStatus.js
  75. 12 0
      hn-shop-uniapp/common/enum/order/refund/RefundStatus.js
  76. 10 0
      hn-shop-uniapp/common/enum/order/refund/RefundType.js
  77. 9 0
      hn-shop-uniapp/common/enum/order/refund/index.js
  78. 11 0
      hn-shop-uniapp/common/enum/payment/Method.js
  79. 3 0
      hn-shop-uniapp/common/enum/payment/index.js
  80. 37 0
      hn-shop-uniapp/common/enum/setting/Key.js
  81. 12 0
      hn-shop-uniapp/common/enum/store/page/category/Style.js
  82. 3 0
      hn-shop-uniapp/common/enum/store/page/category/index.js
  83. 77 0
      hn-shop-uniapp/common/model/Region.js
  84. 88 0
      hn-shop-uniapp/common/model/Setting.js
  85. 280 0
      hn-shop-uniapp/common/model/Store.js
  86. 36 0
      hn-shop-uniapp/components/add-cart-btn/index.vue
  87. 178 0
      hn-shop-uniapp/components/add-cart-popup/index.vue
  88. 57 0
      hn-shop-uniapp/components/avatar-image/index.vue
  89. 183 0
      hn-shop-uniapp/components/countdown/index.vue
  90. 63 0
      hn-shop-uniapp/components/customer-btn/index.vue
  91. 67 0
      hn-shop-uniapp/components/empty/index.vue
  92. 1377 0
      hn-shop-uniapp/components/goods-sku-popup/index.vue
  93. 473 0
      hn-shop-uniapp/components/goods-sku-popup/number-box/index.vue
  94. 43 0
      hn-shop-uniapp/components/page/diyComponents/ICPLicense/index.vue
  95. 123 0
      hn-shop-uniapp/components/page/diyComponents/article/index.vue
  96. 154 0
      hn-shop-uniapp/components/page/diyComponents/banner/index.vue
  97. 31 0
      hn-shop-uniapp/components/page/diyComponents/blank/index.vue
  98. 206 0
      hn-shop-uniapp/components/page/diyComponents/coupon/index.vue
  99. 39 0
      hn-shop-uniapp/components/page/diyComponents/empty.vue
  100. 290 0
      hn-shop-uniapp/components/page/diyComponents/goods/index.vue

+ 0 - 36
README.en.md

@@ -1,36 +0,0 @@
-# fcwl-platform-admin
-
-#### Description
-最新ruoyi vue3+Tailwind+Panin+element-plus+国际化
-
-#### Software Architecture
-Software architecture description
-
-#### Installation
-
-1.  xxxx
-2.  xxxx
-3.  xxxx
-
-#### Instructions
-
-1.  xxxx
-2.  xxxx
-3.  xxxx
-
-#### Contribution
-
-1.  Fork the repository
-2.  Create Feat_xxx branch
-3.  Commit your code
-4.  Create Pull Request
-
-
-#### Gitee Feature
-
-1.  You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
-2.  Gitee blog [blog.gitee.com](https://blog.gitee.com)
-3.  Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
-4.  The most valuable open source project [GVP](https://gitee.com/gvp)
-5.  The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
-6.  The most popular members  [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 0 - 8
README.md

@@ -1,8 +0,0 @@
-<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">信飞通AI后台管理系统</h1>
- 
-## 说明
-信飞通
-
-组织管理
-用户管理 -> 账户管理
-系统管理

+ 1 - 0
admin

@@ -0,0 +1 @@
+Subproject commit e42ca46cd981d71a3399756c352d391f188dcb70

+ 6 - 0
hn-shop-uniapp/.gitignore

@@ -0,0 +1,6 @@
+/unpackage
+/更新节点.txt
+/待处理.txt
+
+
+uni_modules

+ 30 - 0
hn-shop-uniapp/.hbuilderx/launch.json

@@ -0,0 +1,30 @@
+{
+    // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+    // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version" : "0.0",
+    "configurations" : [
+        {
+            "app-plus" : {
+                "launchtype" : "remote"
+            },
+            "default" : {
+                "launchtype" : "remote"
+            },
+            "h5" : {
+                "launchtype" : "remote"
+            },
+            "mp-weixin" : {
+                "launchtype" : "remote"
+            },
+            "type" : "uniCloud"
+        },
+        {
+            "playground" : "custom",
+            "type" : "uni-app:app-android"
+        },
+        {
+            "openVueDevtools" : true,
+            "type" : "uni-app:h5"
+        }
+    ]
+}

+ 13 - 0
hn-shop-uniapp/.vite/deps/_metadata.json

@@ -0,0 +1,13 @@
+{
+  "hash": "4ae7f80a",
+  "browserHash": "741dc287",
+  "optimized": {
+    "weixin-js-sdk": {
+      "src": "../../node_modules/weixin-js-sdk/index.js",
+      "file": "weixin-js-sdk.js",
+      "fileHash": "8d88497e",
+      "needsInterop": true
+    }
+  },
+  "chunks": {}
+}

+ 1 - 0
hn-shop-uniapp/.vite/deps/package.json

@@ -0,0 +1 @@
+{"type":"module"}

+ 766 - 0
hn-shop-uniapp/.vite/deps/weixin-js-sdk.js

@@ -0,0 +1,766 @@
+var __getOwnPropNames = Object.getOwnPropertyNames;
+var __commonJS = (cb, mod) => function __require() {
+  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
+};
+
+// ../../../../www/@hbuilder/yoshop2-pro/node_modules/weixin-js-sdk/index.js
+var require_weixin_js_sdk = __commonJS({
+  "../../../../www/@hbuilder/yoshop2-pro/node_modules/weixin-js-sdk/index.js"(exports, module) {
+    !function(e, n) {
+      module.exports = n(e);
+    }(window, function(o, e) {
+      if (!o.jWeixin) {
+        var n, c = {
+          config: "preVerifyJSAPI",
+          onMenuShareTimeline: "menu:share:timeline",
+          onMenuShareAppMessage: "menu:share:appmessage",
+          onMenuShareQQ: "menu:share:qq",
+          onMenuShareWeibo: "menu:share:weiboApp",
+          onMenuShareQZone: "menu:share:QZone",
+          previewImage: "imagePreview",
+          getLocation: "geoLocation",
+          openProductSpecificView: "openProductViewWithPid",
+          addCard: "batchAddCard",
+          openCard: "batchViewCard",
+          chooseWXPay: "getBrandWCPayRequest",
+          openEnterpriseRedPacket: "getRecevieBizHongBaoRequest",
+          startSearchBeacons: "startMonitoringBeacons",
+          stopSearchBeacons: "stopMonitoringBeacons",
+          onSearchBeacons: "onBeaconsInRange",
+          consumeAndShareCard: "consumedShareCard",
+          openAddress: "editAddress"
+        }, a = function() {
+          var e2 = {};
+          for (var n2 in c)
+            e2[c[n2]] = n2;
+          return e2;
+        }(), i = o.document, t = i.title, r = navigator.userAgent.toLowerCase(), s = navigator.platform.toLowerCase(), d = !(!s.match("mac") && !s.match("win")), u = -1 != r.indexOf("wxdebugger"), l = -1 != r.indexOf("micromessenger"), p = -1 != r.indexOf("android"), f = -1 != r.indexOf("iphone") || -1 != r.indexOf("ipad"), m = (n = r.match(/micromessenger\/(\d+\.\d+\.\d+)/) || r.match(/micromessenger\/(\d+\.\d+)/)) ? n[1] : "", g = {
+          initStartTime: L(),
+          initEndTime: 0,
+          preVerifyStartTime: 0,
+          preVerifyEndTime: 0
+        }, h = {
+          version: 1,
+          appId: "",
+          initTime: 0,
+          preVerifyTime: 0,
+          networkType: "",
+          isPreVerifyOk: 1,
+          systemType: f ? 1 : p ? 2 : -1,
+          clientVersion: m,
+          url: encodeURIComponent(location.href)
+        }, v = {}, S = { _completes: [] }, y = { state: 0, data: {} };
+        O(function() {
+          g.initEndTime = L();
+        });
+        var I = false, _ = [], w = {
+          config: function(e2) {
+            B("config", v = e2);
+            var t2 = false !== v.check;
+            O(function() {
+              if (t2)
+                M(
+                  c.config,
+                  {
+                    verifyJsApiList: C(v.jsApiList),
+                    verifyOpenTagList: C(v.openTagList)
+                  },
+                  function() {
+                    S._complete = function(e4) {
+                      g.preVerifyEndTime = L(), y.state = 1, y.data = e4;
+                    }, S.success = function(e4) {
+                      h.isPreVerifyOk = 0;
+                    }, S.fail = function(e4) {
+                      S._fail ? S._fail(e4) : y.state = -1;
+                    };
+                    var t3 = S._completes;
+                    return t3.push(function() {
+                      !function() {
+                        if (!(d || u || v.debug || m < "6.0.2" || h.systemType < 0)) {
+                          var i3 = new Image();
+                          h.appId = v.appId, h.initTime = g.initEndTime - g.initStartTime, h.preVerifyTime = g.preVerifyEndTime - g.preVerifyStartTime, w.getNetworkType({
+                            isInnerInvoke: true,
+                            success: function(e4) {
+                              h.networkType = e4.networkType;
+                              var n3 = "https://open.weixin.qq.com/sdk/report?v=" + h.version + "&o=" + h.isPreVerifyOk + "&s=" + h.systemType + "&c=" + h.clientVersion + "&a=" + h.appId + "&n=" + h.networkType + "&i=" + h.initTime + "&p=" + h.preVerifyTime + "&u=" + h.url;
+                              i3.src = n3;
+                            }
+                          });
+                        }
+                      }();
+                    }), S.complete = function(e4) {
+                      for (var n3 = 0, i3 = t3.length; n3 < i3; ++n3)
+                        t3[n3]();
+                      S._completes = [];
+                    }, S;
+                  }()
+                ), g.preVerifyStartTime = L();
+              else {
+                y.state = 1;
+                for (var e3 = S._completes, n2 = 0, i2 = e3.length; n2 < i2; ++n2)
+                  e3[n2]();
+                S._completes = [];
+              }
+            }), w.invoke || (w.invoke = function(e3, n2, i2) {
+              o.WeixinJSBridge && WeixinJSBridge.invoke(e3, x(n2), i2);
+            }, w.on = function(e3, n2) {
+              o.WeixinJSBridge && WeixinJSBridge.on(e3, n2);
+            });
+          },
+          ready: function(e2) {
+            0 != y.state ? e2() : (S._completes.push(e2), !l && v.debug && e2());
+          },
+          error: function(e2) {
+            m < "6.0.2" || (-1 == y.state ? e2(y.data) : S._fail = e2);
+          },
+          checkJsApi: function(e2) {
+            M(
+              "checkJsApi",
+              { jsApiList: C(e2.jsApiList) },
+              (e2._complete = function(e3) {
+                if (p) {
+                  var n2 = e3.checkResult;
+                  n2 && (e3.checkResult = JSON.parse(n2));
+                }
+                e3 = function(e4) {
+                  var n3 = e4.checkResult;
+                  for (var i2 in n3) {
+                    var t2 = a[i2];
+                    t2 && (n3[t2] = n3[i2], delete n3[i2]);
+                  }
+                  return e4;
+                }(e3);
+              }, e2)
+            );
+          },
+          onMenuShareTimeline: function(e2) {
+            P(
+              c.onMenuShareTimeline,
+              {
+                complete: function() {
+                  M(
+                    "shareTimeline",
+                    {
+                      title: e2.title || t,
+                      desc: e2.title || t,
+                      img_url: e2.imgUrl || "",
+                      link: e2.link || location.href,
+                      type: e2.type || "link",
+                      data_url: e2.dataUrl || ""
+                    },
+                    e2
+                  );
+                }
+              },
+              e2
+            );
+          },
+          onMenuShareAppMessage: function(n2) {
+            P(
+              c.onMenuShareAppMessage,
+              {
+                complete: function(e2) {
+                  "favorite" === e2.scene ? M("sendAppMessage", {
+                    title: n2.title || t,
+                    desc: n2.desc || "",
+                    link: n2.link || location.href,
+                    img_url: n2.imgUrl || "",
+                    type: n2.type || "link",
+                    data_url: n2.dataUrl || ""
+                  }) : M(
+                    "sendAppMessage",
+                    {
+                      title: n2.title || t,
+                      desc: n2.desc || "",
+                      link: n2.link || location.href,
+                      img_url: n2.imgUrl || "",
+                      type: n2.type || "link",
+                      data_url: n2.dataUrl || ""
+                    },
+                    n2
+                  );
+                }
+              },
+              n2
+            );
+          },
+          onMenuShareQQ: function(e2) {
+            P(
+              c.onMenuShareQQ,
+              {
+                complete: function() {
+                  M(
+                    "shareQQ",
+                    {
+                      title: e2.title || t,
+                      desc: e2.desc || "",
+                      img_url: e2.imgUrl || "",
+                      link: e2.link || location.href
+                    },
+                    e2
+                  );
+                }
+              },
+              e2
+            );
+          },
+          onMenuShareWeibo: function(e2) {
+            P(
+              c.onMenuShareWeibo,
+              {
+                complete: function() {
+                  M(
+                    "shareWeiboApp",
+                    {
+                      title: e2.title || t,
+                      desc: e2.desc || "",
+                      img_url: e2.imgUrl || "",
+                      link: e2.link || location.href
+                    },
+                    e2
+                  );
+                }
+              },
+              e2
+            );
+          },
+          onMenuShareQZone: function(e2) {
+            P(
+              c.onMenuShareQZone,
+              {
+                complete: function() {
+                  M(
+                    "shareQZone",
+                    {
+                      title: e2.title || t,
+                      desc: e2.desc || "",
+                      img_url: e2.imgUrl || "",
+                      link: e2.link || location.href
+                    },
+                    e2
+                  );
+                }
+              },
+              e2
+            );
+          },
+          updateTimelineShareData: function(e2) {
+            M(
+              "updateTimelineShareData",
+              { title: e2.title, link: e2.link, imgUrl: e2.imgUrl },
+              e2
+            );
+          },
+          updateAppMessageShareData: function(e2) {
+            M(
+              "updateAppMessageShareData",
+              { title: e2.title, desc: e2.desc, link: e2.link, imgUrl: e2.imgUrl },
+              e2
+            );
+          },
+          startRecord: function(e2) {
+            M("startRecord", {}, e2);
+          },
+          stopRecord: function(e2) {
+            M("stopRecord", {}, e2);
+          },
+          onVoiceRecordEnd: function(e2) {
+            P("onVoiceRecordEnd", e2);
+          },
+          playVoice: function(e2) {
+            M("playVoice", { localId: e2.localId }, e2);
+          },
+          pauseVoice: function(e2) {
+            M("pauseVoice", { localId: e2.localId }, e2);
+          },
+          stopVoice: function(e2) {
+            M("stopVoice", { localId: e2.localId }, e2);
+          },
+          onVoicePlayEnd: function(e2) {
+            P("onVoicePlayEnd", e2);
+          },
+          uploadVoice: function(e2) {
+            M(
+              "uploadVoice",
+              {
+                localId: e2.localId,
+                isShowProgressTips: 0 == e2.isShowProgressTips ? 0 : 1
+              },
+              e2
+            );
+          },
+          downloadVoice: function(e2) {
+            M(
+              "downloadVoice",
+              {
+                serverId: e2.serverId,
+                isShowProgressTips: 0 == e2.isShowProgressTips ? 0 : 1
+              },
+              e2
+            );
+          },
+          translateVoice: function(e2) {
+            M(
+              "translateVoice",
+              {
+                localId: e2.localId,
+                isShowProgressTips: 0 == e2.isShowProgressTips ? 0 : 1
+              },
+              e2
+            );
+          },
+          chooseImage: function(e2) {
+            M(
+              "chooseImage",
+              {
+                scene: "1|2",
+                count: e2.count || 9,
+                sizeType: e2.sizeType || ["original", "compressed"],
+                sourceType: e2.sourceType || ["album", "camera"]
+              },
+              (e2._complete = function(e3) {
+                if (p) {
+                  var n2 = e3.localIds;
+                  try {
+                    n2 && (e3.localIds = JSON.parse(n2));
+                  } catch (e4) {
+                  }
+                }
+              }, e2)
+            );
+          },
+          getLocation: function(e2) {
+          },
+          previewImage: function(e2) {
+            M(c.previewImage, { current: e2.current, urls: e2.urls }, e2);
+          },
+          uploadImage: function(e2) {
+            M(
+              "uploadImage",
+              {
+                localId: e2.localId,
+                isShowProgressTips: 0 == e2.isShowProgressTips ? 0 : 1
+              },
+              e2
+            );
+          },
+          downloadImage: function(e2) {
+            M(
+              "downloadImage",
+              {
+                serverId: e2.serverId,
+                isShowProgressTips: 0 == e2.isShowProgressTips ? 0 : 1
+              },
+              e2
+            );
+          },
+          getLocalImgData: function(e2) {
+            false === I ? (I = true, M(
+              "getLocalImgData",
+              { localId: e2.localId },
+              (e2._complete = function(e3) {
+                if (I = false, 0 < _.length) {
+                  var n2 = _.shift();
+                  wx.getLocalImgData(n2);
+                }
+              }, e2)
+            )) : _.push(e2);
+          },
+          getNetworkType: function(e2) {
+            M(
+              "getNetworkType",
+              {},
+              (e2._complete = function(e3) {
+                e3 = function(e4) {
+                  var n2 = e4.errMsg;
+                  e4.errMsg = "getNetworkType:ok";
+                  var i2 = e4.subtype;
+                  if (delete e4.subtype, i2)
+                    e4.networkType = i2;
+                  else {
+                    var t2 = n2.indexOf(":"), o2 = n2.substring(t2 + 1);
+                    switch (o2) {
+                      case "wifi":
+                      case "edge":
+                      case "wwan":
+                        e4.networkType = o2;
+                        break;
+                      default:
+                        e4.errMsg = "getNetworkType:fail";
+                    }
+                  }
+                  return e4;
+                }(e3);
+              }, e2)
+            );
+          },
+          openLocation: function(e2) {
+            M(
+              "openLocation",
+              {
+                latitude: e2.latitude,
+                longitude: e2.longitude,
+                name: e2.name || "",
+                address: e2.address || "",
+                scale: e2.scale || 28,
+                infoUrl: e2.infoUrl || ""
+              },
+              e2
+            );
+          },
+          getLocation: function(e2) {
+            M(
+              c.getLocation,
+              { type: (e2 = e2 || {}).type || "wgs84" },
+              (e2._complete = function(e3) {
+                delete e3.type;
+              }, e2)
+            );
+          },
+          hideOptionMenu: function(e2) {
+            M("hideOptionMenu", {}, e2);
+          },
+          showOptionMenu: function(e2) {
+            M("showOptionMenu", {}, e2);
+          },
+          closeWindow: function(e2) {
+            M("closeWindow", {}, e2 = e2 || {});
+          },
+          hideMenuItems: function(e2) {
+            M("hideMenuItems", { menuList: e2.menuList }, e2);
+          },
+          showMenuItems: function(e2) {
+            M("showMenuItems", { menuList: e2.menuList }, e2);
+          },
+          hideAllNonBaseMenuItem: function(e2) {
+            M("hideAllNonBaseMenuItem", {}, e2);
+          },
+          showAllNonBaseMenuItem: function(e2) {
+            M("showAllNonBaseMenuItem", {}, e2);
+          },
+          scanQRCode: function(e2) {
+            M(
+              "scanQRCode",
+              {
+                needResult: (e2 = e2 || {}).needResult || 0,
+                scanType: e2.scanType || ["qrCode", "barCode"]
+              },
+              (e2._complete = function(e3) {
+                if (f) {
+                  var n2 = e3.resultStr;
+                  if (n2) {
+                    var i2 = JSON.parse(n2);
+                    e3.resultStr = i2 && i2.scan_code && i2.scan_code.scan_result;
+                  }
+                }
+              }, e2)
+            );
+          },
+          openAddress: function(e2) {
+            M(
+              c.openAddress,
+              {},
+              (e2._complete = function(e3) {
+                e3 = function(e4) {
+                  return e4.postalCode = e4.addressPostalCode, delete e4.addressPostalCode, e4.provinceName = e4.proviceFirstStageName, delete e4.proviceFirstStageName, e4.cityName = e4.addressCitySecondStageName, delete e4.addressCitySecondStageName, e4.countryName = e4.addressCountiesThirdStageName, delete e4.addressCountiesThirdStageName, e4.detailInfo = e4.addressDetailInfo, delete e4.addressDetailInfo, e4;
+                }(e3);
+              }, e2)
+            );
+          },
+          openProductSpecificView: function(e2) {
+            M(
+              c.openProductSpecificView,
+              {
+                pid: e2.productId,
+                view_type: e2.viewType || 0,
+                ext_info: e2.extInfo
+              },
+              e2
+            );
+          },
+          addCard: function(e2) {
+            for (var n2 = e2.cardList, i2 = [], t2 = 0, o2 = n2.length; t2 < o2; ++t2) {
+              var r2 = n2[t2], a2 = { card_id: r2.cardId, card_ext: r2.cardExt };
+              i2.push(a2);
+            }
+            M(
+              c.addCard,
+              { card_list: i2 },
+              (e2._complete = function(e3) {
+                var n3 = e3.card_list;
+                if (n3) {
+                  for (var i3 = 0, t3 = (n3 = JSON.parse(n3)).length; i3 < t3; ++i3) {
+                    var o3 = n3[i3];
+                    o3.cardId = o3.card_id, o3.cardExt = o3.card_ext, o3.isSuccess = !!o3.is_succ, delete o3.card_id, delete o3.card_ext, delete o3.is_succ;
+                  }
+                  e3.cardList = n3, delete e3.card_list;
+                }
+              }, e2)
+            );
+          },
+          chooseCard: function(e2) {
+            M(
+              "chooseCard",
+              {
+                app_id: v.appId,
+                location_id: e2.shopId || "",
+                sign_type: e2.signType || "SHA1",
+                card_id: e2.cardId || "",
+                card_type: e2.cardType || "",
+                card_sign: e2.cardSign,
+                time_stamp: e2.timestamp + "",
+                nonce_str: e2.nonceStr
+              },
+              (e2._complete = function(e3) {
+                e3.cardList = e3.choose_card_info, delete e3.choose_card_info;
+              }, e2)
+            );
+          },
+          openCard: function(e2) {
+            for (var n2 = e2.cardList, i2 = [], t2 = 0, o2 = n2.length; t2 < o2; ++t2) {
+              var r2 = n2[t2], a2 = { card_id: r2.cardId, code: r2.code };
+              i2.push(a2);
+            }
+            M(c.openCard, { card_list: i2 }, e2);
+          },
+          consumeAndShareCard: function(e2) {
+            M(
+              c.consumeAndShareCard,
+              { consumedCardId: e2.cardId, consumedCode: e2.code },
+              e2
+            );
+          },
+          chooseWXPay: function(e2) {
+            M(c.chooseWXPay, V(e2), e2);
+          },
+          openEnterpriseRedPacket: function(e2) {
+            M(c.openEnterpriseRedPacket, V(e2), e2);
+          },
+          startSearchBeacons: function(e2) {
+            M(c.startSearchBeacons, { ticket: e2.ticket }, e2);
+          },
+          stopSearchBeacons: function(e2) {
+            M(c.stopSearchBeacons, {}, e2);
+          },
+          onSearchBeacons: function(e2) {
+            P(c.onSearchBeacons, e2);
+          },
+          openEnterpriseChat: function(e2) {
+            M(
+              "openEnterpriseChat",
+              { useridlist: e2.userIds, chatname: e2.groupName },
+              e2
+            );
+          },
+          launchMiniProgram: function(e2) {
+            M(
+              "launchMiniProgram",
+              {
+                targetAppId: e2.targetAppId,
+                path: function(e3) {
+                  if ("string" == typeof e3 && 0 < e3.length) {
+                    var n2 = e3.split("?")[0], i2 = e3.split("?")[1];
+                    return n2 += ".html", void 0 !== i2 ? n2 + "?" + i2 : n2;
+                  }
+                }(e2.path),
+                envVersion: e2.envVersion
+              },
+              e2
+            );
+          },
+          openBusinessView: function(e2) {
+            M(
+              "openBusinessView",
+              {
+                businessType: e2.businessType,
+                queryString: e2.queryString || "",
+                envVersion: e2.envVersion
+              },
+              (e2._complete = function(n2) {
+                if (p) {
+                  var e3 = n2.extraData;
+                  if (e3)
+                    try {
+                      n2.extraData = JSON.parse(e3);
+                    } catch (e4) {
+                      n2.extraData = {};
+                    }
+                }
+              }, e2)
+            );
+          },
+          miniProgram: {
+            navigateBack: function(e2) {
+              e2 = e2 || {}, O(function() {
+                M(
+                  "invokeMiniProgramAPI",
+                  { name: "navigateBack", arg: { delta: e2.delta || 1 } },
+                  e2
+                );
+              });
+            },
+            navigateTo: function(e2) {
+              O(function() {
+                M(
+                  "invokeMiniProgramAPI",
+                  { name: "navigateTo", arg: { url: e2.url } },
+                  e2
+                );
+              });
+            },
+            redirectTo: function(e2) {
+              O(function() {
+                M(
+                  "invokeMiniProgramAPI",
+                  { name: "redirectTo", arg: { url: e2.url } },
+                  e2
+                );
+              });
+            },
+            switchTab: function(e2) {
+              O(function() {
+                M(
+                  "invokeMiniProgramAPI",
+                  { name: "switchTab", arg: { url: e2.url } },
+                  e2
+                );
+              });
+            },
+            reLaunch: function(e2) {
+              O(function() {
+                M(
+                  "invokeMiniProgramAPI",
+                  { name: "reLaunch", arg: { url: e2.url } },
+                  e2
+                );
+              });
+            },
+            postMessage: function(e2) {
+              O(function() {
+                M(
+                  "invokeMiniProgramAPI",
+                  { name: "postMessage", arg: e2.data || {} },
+                  e2
+                );
+              });
+            },
+            getEnv: function(e2) {
+              O(function() {
+                e2({ miniprogram: "miniprogram" === o.__wxjs_environment });
+              });
+            }
+          }
+        }, T = 1, k = {};
+        return i.addEventListener(
+          "error",
+          function(e2) {
+            if (!p) {
+              var n2 = e2.target, i2 = n2.tagName, t2 = n2.src;
+              if ("IMG" == i2 || "VIDEO" == i2 || "AUDIO" == i2 || "SOURCE" == i2) {
+                if (-1 != t2.indexOf("wxlocalresource://")) {
+                  e2.preventDefault(), e2.stopPropagation();
+                  var o2 = n2["wx-id"];
+                  if (o2 || (o2 = T++, n2["wx-id"] = o2), k[o2])
+                    return;
+                  k[o2] = true, wx.ready(function() {
+                    wx.getLocalImgData({
+                      localId: t2,
+                      success: function(e3) {
+                        n2.src = e3.localData;
+                      }
+                    });
+                  });
+                }
+              }
+            }
+          },
+          true
+        ), i.addEventListener(
+          "load",
+          function(e2) {
+            if (!p) {
+              var n2 = e2.target, i2 = n2.tagName;
+              n2.src;
+              if ("IMG" == i2 || "VIDEO" == i2 || "AUDIO" == i2 || "SOURCE" == i2) {
+                var t2 = n2["wx-id"];
+                t2 && (k[t2] = false);
+              }
+            }
+          },
+          true
+        ), e && (o.wx = o.jWeixin = w), w;
+      }
+      function M(n2, e2, i2) {
+        o.WeixinJSBridge ? WeixinJSBridge.invoke(n2, x(e2), function(e3) {
+          A(n2, e3, i2);
+        }) : B(n2, i2);
+      }
+      function P(n2, i2, t2) {
+        o.WeixinJSBridge ? WeixinJSBridge.on(n2, function(e2) {
+          t2 && t2.trigger && t2.trigger(e2), A(n2, e2, i2);
+        }) : B(n2, t2 || i2);
+      }
+      function x(e2) {
+        return (e2 = e2 || {}).appId = v.appId, e2.verifyAppId = v.appId, e2.verifySignType = "sha1", e2.verifyTimestamp = v.timestamp + "", e2.verifyNonceStr = v.nonceStr, e2.verifySignature = v.signature, e2;
+      }
+      function V(e2) {
+        return {
+          timeStamp: e2.timestamp + "",
+          nonceStr: e2.nonceStr,
+          package: e2.package,
+          paySign: e2.paySign,
+          signType: e2.signType || "SHA1"
+        };
+      }
+      function A(e2, n2, i2) {
+        "openEnterpriseChat" != e2 && "openBusinessView" !== e2 || (n2.errCode = n2.err_code), delete n2.err_code, delete n2.err_desc, delete n2.err_detail;
+        var t2 = n2.errMsg;
+        t2 || (t2 = n2.err_msg, delete n2.err_msg, t2 = function(e3, n3) {
+          var i3 = e3, t3 = a[i3];
+          t3 && (i3 = t3);
+          var o3 = "ok";
+          if (n3) {
+            var r2 = n3.indexOf(":");
+            "confirm" == (o3 = n3.substring(r2 + 1)) && (o3 = "ok"), "failed" == o3 && (o3 = "fail"), -1 != o3.indexOf("failed_") && (o3 = o3.substring(7)), -1 != o3.indexOf("fail_") && (o3 = o3.substring(5)), "access denied" != (o3 = (o3 = o3.replace(/_/g, " ")).toLowerCase()) && "no permission to execute" != o3 || (o3 = "permission denied"), "config" == i3 && "function not exist" == o3 && (o3 = "ok"), "" == o3 && (o3 = "fail");
+          }
+          return n3 = i3 + ":" + o3;
+        }(e2, t2), n2.errMsg = t2), (i2 = i2 || {})._complete && (i2._complete(n2), delete i2._complete), t2 = n2.errMsg || "", v.debug && !i2.isInnerInvoke && alert(JSON.stringify(n2));
+        var o2 = t2.indexOf(":");
+        switch (t2.substring(o2 + 1)) {
+          case "ok":
+            i2.success && i2.success(n2);
+            break;
+          case "cancel":
+            i2.cancel && i2.cancel(n2);
+            break;
+          default:
+            i2.fail && i2.fail(n2);
+        }
+        i2.complete && i2.complete(n2);
+      }
+      function C(e2) {
+        if (e2) {
+          for (var n2 = 0, i2 = e2.length; n2 < i2; ++n2) {
+            var t2 = e2[n2], o2 = c[t2];
+            o2 && (e2[n2] = o2);
+          }
+          return e2;
+        }
+      }
+      function B(e2, n2) {
+        if (!(!v.debug || n2 && n2.isInnerInvoke)) {
+          var i2 = a[e2];
+          i2 && (e2 = i2), n2 && n2._complete && delete n2._complete, console.log('"' + e2 + '",', n2 || "");
+        }
+      }
+      function L() {
+        return new Date().getTime();
+      }
+      function O(e2) {
+        l && (o.WeixinJSBridge ? e2() : i.addEventListener && i.addEventListener("WeixinJSBridgeReady", e2, false));
+      }
+    });
+  }
+});
+export default require_weixin_js_sdk();
+//# sourceMappingURL=weixin-js-sdk.js.map

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3 - 0
hn-shop-uniapp/.vite/deps/weixin-js-sdk.js.map


+ 174 - 0
hn-shop-uniapp/App.vue

@@ -0,0 +1,174 @@
+<script>
+	import store from '@/store'
+	import StoreModel from '@/common/model/Store'
+	import {
+		getSceneData
+	} from './core/app'
+	import {
+		isObject
+	} from './utils/util'
+	import storage from '@/utils/storage'
+
+	export default {
+		/**
+		 * 全局变量
+		 */
+		globalData: {},
+
+		onShow(path) {
+			// console.log('app - onShow', path)
+			// this.onStartupQuery(path)
+		},
+		/**
+		 * 初始化完成时触发
+		 */
+		onLaunch(path) {
+			// #ifdef MP
+			// console.log('app - onLaunch', path)
+			// #endif
+
+			this.onStartupQuery(path)
+
+			// #ifdef APP-PLUS
+			this.$navTo('pages/txmLogin/index')
+			// #endif
+
+			// // app启动参数
+			// this.onStartupQuery(isObject(query) ? query : {})
+		},
+
+		methods: {
+			// app启动参数
+			onStartupQuery(path) {
+				const query = path.query
+				// 微信鉴权
+				StoreModel.getApiData(query).then((res) => {
+					let param = query?.param ? query?.param.split(',') : ['', '']
+
+					if (res.resCode) {
+						if (param[1] === 'enterprise') {
+							console.log('******************企业注册页面******************')
+						} else if (param[1] === 'shopping') {
+							if (!storage.get('wx_qym')) {
+								this.$navTo('pages/authentication/index')
+								return
+							}
+							console.log('******************商场选购页面******************')
+						} else if (param[1] === 'activation') {
+							console.log('******************用户激活页面******************')
+						} else if (param[1] === 'tiaoxingma') {
+							console.log('******************条形码******************')
+						}
+
+						if (param[0]) {
+							console.log(param[0], 'padding')
+							this.$navTo(param[0])
+						} else {
+							console.log('没有指定路由,跳转默认路由', storage.get('wx_v'))
+							switch (storage.get('wx_v')) {
+								case 'enterprise':
+									if (storage.get('wx_userinfo').customertype == '代理商') {
+										this.$navTo('pages/firm/home/index')
+									} else {
+										// 如果已经是代理商则直接登录
+										this.$navTo('pages/firm/index')
+									}
+									break
+								case 'shopping':
+									if (!storage.get('wx_qym')) {
+										this.$navTo('pages/authentication/index')
+									} else {
+										this.$navTo('pages/index/index')
+									}
+									break
+								case 'activation':
+									this.$navTo('pages/activationcode/index')
+									break
+								case 'tiaoxingma':
+									this.$navTo('pages/tiaoxingma/index')
+									break
+								default:
+									console.log('没有模式路由')
+									this.$navTo('pages/empty')
+									break
+							}
+						}
+					} else {
+						this.$navTo('pages/empty')
+						let errMes = res.err || '错误!请退出重新打开'
+						uni.showModal({
+							title: '提示',
+							content: errMes,
+							showCancel: false
+						})
+					}
+
+					// 激活路由器
+					// this.$navTo('pages/activationcode/index')
+					// 填写二维码
+					// this.$navTo('pages/authentication/index')
+					// 
+					// this.$navTo('pages/txmLogin/index')
+					// this.$navTo('pages/tiaoxingma/index')
+					// this.$navTo('pages/firm/authentication/index')
+					// this.$navTo('pages/user/activatorder/index')
+				})
+			},
+
+			// 小程序主动更新
+			updateManager() {
+				const updateManager = uni.getUpdateManager()
+				updateManager.onCheckForUpdate((res) => {
+					// 请求完新版本信息的回调
+					// console.log(res.hasUpdate)
+				})
+				updateManager.onUpdateReady(() => {
+					uni.showModal({
+						title: '更新提示',
+						content: '新版本已经准备好,即将重启应用',
+						showCancel: false,
+						success(res) {
+							if (res.confirm) {
+								// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+								updateManager.applyUpdate()
+							}
+						}
+					})
+				})
+				updateManager.onUpdateFailed(() => {
+					// 新的版本下载失败
+					uni.showModal({
+						title: '更新提示',
+						content: '新版本下载失败',
+						showCancel: false
+					})
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	/* uView库样式 */
+	@import './uni_modules/vk-uview-ui/index.scss';
+	/* 项目基础样式 */
+	@import './app.scss';
+	/* iconfont图标库 */
+	@import './utils/iconfont.scss';
+
+	// 基础央视表
+	@import './colorui/main.css';
+	@import './colorui/icon.css';
+	@import './colorui/animation.css';
+	@import './colorui/reset.css';
+
+	// gs-logo样式
+	.gs-logo-box {
+		width: 70rpx;
+		height: 70rpx;
+		border: 6rpx solid #fff;
+		background: #fff;
+		border-radius: 100%;
+		overflow: hidden;
+	}
+</style>

+ 191 - 0
hn-shop-uniapp/LICENSE

@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "{}" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+Copyright 2018 信飞通
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 70 - 0
hn-shop-uniapp/README.md

@@ -0,0 +1,70 @@
+# 信飞通 [uni-app 端]
+
+#### 项目介绍
+
+信飞通是全新推出的一款轻量级、高性能、前后端分离的 ktboss 系统
+
+#### 源码下载
+
+1. 主商城端(又称后端、服务端,PHP 开发 用于管理后台和提供 api 接口)
+
+   下载地址:[https://gitee.com/xany/yoshop2.0](https://gitee.com/xany/yoshop2.0)
+
+2. 用户端(也叫客户端、前端,uniapp 开发 用于生成 H5 和微信小程序)
+
+   下载地址:[https://gitee.com/xany/yoshop2.0-uniapp](https://gitee.com/xany/yoshop2.0-uniapp)
+
+#### 如何使用 uni-app 端
+
+##### 一、导入 uniapp 项目
+
+    1. 首先下载HBuilderX并安装,地址:https://www.dcloud.io/hbuilderx.html
+    2. 打开HBuilderX -> 顶部菜单栏 -> 文件 -> 导入 -> 从本地目录导入 -> 选择uniapp端项目目录
+    3. 找到config.js文件,找到里面的apiUrl项,填入已搭建的后端url地址
+    4. 打开manifest.json文件,选择微信小程序配置,填写小程序的appid
+
+##### 二、本地调试
+
+    1. 打开HBuilderX -> 顶部菜单栏 -> 运行 -> 运行到浏览器 -> Chrome
+    2. 如果请求后端api时 提示跨域错误,可安装Chrome插件:【Allow CORS: Access-Control-Allow-Origin】,地址:https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf
+
+##### 三、打包发行(H5)
+
+    1. 打开HBuilderX -> 顶部菜单栏 -> 发行 -> 网站H5-手机版
+    2. 打包后的文件路径:/unpackage/dist/build/h5
+    3. 将打包完成的所有文件 复制到商城后端/pulic目录下,全部替换
+
+##### 四、打包发行(微信小程序)
+
+    1. 下载微信开发者工具并安装,地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
+    2. 打开HBuilderX -> 顶部菜单栏 -> 发行 -> 小程序-微信
+    3. 打包后的文件路径:/unpackage/dist/build/mp-weixin
+    5. 打开微信开发者工具 导入 打包完成的项目
+    6. 检查没有运行错误,在右上方上传小程序
+
+#### UNIAPP-页面展示
+
+![前端展示](https://images.gitee.com/uploads/images/2021/0316/215102_7bcb0802_2166072.png '前端展示.png')
+
+#### 系统演示
+
+![前端演示二维码](https://images.gitee.com/uploads/images/2021/0316/104516_3778337e_2166072.png '111.png')
+
+#### 版权须知
+
+1. 允许个人学习研究使用,支持二次开发,允许商业用途(仅限自运营)。
+2. 允许商业用途,但仅限自运营,如果商用必须保留版权信息,望自觉遵守。
+3. 不允许对程序代码以任何形式任何目的的再发行或出售,否则将追究侵权者法律责任。
+
+#### 微信 url
+
+https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx24533712759b8375&redirect_uri=https://api.hninpop.com/?versions=enterprise&response_type=code&scope=snsapi_base&state=STATE
+
+用户手机号绑定
+http://localhost:8010/pages/user/realname/index
+
+用户认证
+http://localhost:8010/pages/user/certificate/index
+
+我的订单
+http://localhost:8010/pages/order/index?dataType=comment

+ 14 - 0
hn-shop-uniapp/androidPrivacy.json

@@ -0,0 +1,14 @@
+{
+    "version" : "1",
+    "prompt" : "template",
+    "title" : "服务协议和隐私政策",
+    "message" : "  请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/>  你可阅读<a href=\"static/protocol.html\">《服务协议》</a>和<a href=\"static/privacy.html\">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
+    "buttonAccept" : "同意并接受",
+    "buttonRefuse" : "暂不同意",
+    "second" : {
+        "title" : "确认提示",
+        "message" : "  进入应用前,你需先同意<a href=\"static/protocol.html\">《服务协议》</a>和<a href=\"static/privacy.html\">《隐私政策》</a>,否则将退出应用。",
+        "buttonAccept" : "同意并继续",
+        "buttonRefuse" : "退出应用"
+    }
+}

+ 47 - 0
hn-shop-uniapp/api/address.js

@@ -0,0 +1,47 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'address/list',
+  defaultId: 'address/defaultId',
+  detail: 'address/detail',
+  add: 'address/add',
+  edit: 'address/edit',
+  setDefault: 'address/setDefault',
+  remove: 'address/remove'
+}
+
+// 收货地址列表
+export const list = (param) => {
+  return request.get(api.list, param)
+}
+
+// 默认收货地址ID
+export const defaultId = (param) => {
+  return request.get(api.defaultId, param)
+}
+
+// 收货地址详情
+export const detail = (addressId) => {
+  return request.get(api.detail, { addressId })
+}
+
+// 新增收货地址
+export const add = (data) => {
+  return request.post(api.add, { form: data })
+}
+
+// 编辑收货地址
+export const edit = (addressId, data) => {
+  return request.post(api.edit, { addressId, form: data })
+}
+
+// 设置默认收货地址
+export const setDefault = (addressId) => {
+  return request.post(api.setDefault, { addressId })
+}
+
+// 删除收货地址
+export const remove = (addressId) => {
+  return request.post(api.remove, { addressId })
+}

+ 203 - 0
hn-shop-uniapp/api/app.js

@@ -0,0 +1,203 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  userInfo: 'user/info',
+  assets: 'user/assets',
+  bindMobile: 'user/bindMobile',
+  personal: 'user/personal'
+}
+
+// 腾讯人脸核身
+export const wxHuman = (param, option) => {
+  return request.get('/api/wx/txy/detectauth', param, option)
+}
+
+// 腾讯人脸核身回调信息
+export const wxGetDetectInfo = (param, option) => {
+  return request.get('/api/wx/txy/getdetectinfo', param, option)
+}
+
+// 微信JSSDK参数
+export const getWxSign = (param, option) => {
+  return request.get('/api/wx/jsapiconfig?url=' + param, param, option)
+}
+
+// 绑定手机号
+export const bindMobile = (data, option) => {
+  return request.post('', data, option)
+}
+
+
+
+// 更新 客户信息手机号
+export const bindCustomer = (param, option) => {
+  param.pagevalue = 304
+  return request.get('/api/up', param, option)
+}
+
+// 获取 客户信息
+export const getCustomer = (param, option) => {
+  param.pagevalue = 305
+  return request.get('/api/query/list', param, option)
+}
+
+// 获取 用户与盒子 信息
+export const getYhinfo = (param, option) => {
+  param.pagevalue = 334
+  return request.get('/api/query/list', param, option)
+}
+
+// 获取 用户与订单 信息
+export const getUserDdinfo = (param, option) => {
+  param.pagevalue = 335
+  return request.get('/api/query/list', param, option)
+}
+
+// 获取 代理商信息
+export const getAgents = (param, option) => {
+  param.pagevalue = 307
+  param.rows = 1
+  param.pageindex = 1
+  return request.get('/api/query/list', param, option)
+}
+
+// 获取 代理商企业二维码内容
+export const createOpenid = (param, option) => {
+  return request.get('/api/wx/qy/qrcode/create', param, option)
+}
+
+// 查询商品列表
+export function getShopList(param, option) {
+  param.pagevalue = 297
+  return request.get('/api/query/list', param, option)
+}
+
+// 查询商品列表 - 连产品套餐的表
+export function getShopTaochanList(param, option) {
+  param.pagevalue = 355
+  return request.get('/api/query/list', param, option)
+}
+
+// 支付宝列表
+export function getPalList(param, option) {
+  return request.get('/api/query/list', param, option)
+}
+
+// 支付接口 - 通过人员,支付类型,返回支付二维码链接
+export function plFomr3(param, option) {
+  return request.get(`/p/${param.customerid}/${param.openid}/${param.paysortid}/${param.productid}`)
+}
+
+// 支付接口 - 续费 - 通过人员,支付类型,返回支付二维码链接
+export function plXufei(param, option) {
+  return request.get(`/x/${param.customerid}/${param.yhinfoid}/${param.openid}/${param.paysortid}/${param.productid}`)
+}
+
+// 支付接口 - 续费 - 通过人员,支付类型,返回支付二维码链接
+export function plLyqXufei(param, option) {
+  return request.get(`/x/${param.customerid}/${param.yhinfoid}/${param.openid}/${param.paysortid}`)
+}
+
+// 支付码 - 通过微信code获得
+export function openidCode(param, option) {
+  return request.get(`/api/wx/openid`, param, option)
+}
+
+//* *********** 图片上传 **************
+// 添加附件记录并返回路径
+export function addtoPathImg(param, option) {
+  return request.form('/api/attachment/addtoPath?pagevalue=300', param, option)
+}
+
+//* *********** 短信验证码 **************
+export function verifycodesmsbykt(param, option) {
+  return request.get(`/api/verifycodesmsbykt`, param, option)
+}
+
+//* *********** 企业端接口 **************
+
+// 企业用户登录
+export const companyLogin = (param, option) => {
+  param.pagevalue = 304
+  return request.get('/api/wx/createagents', param, option)
+}
+
+// 获取企业用户登录 验证码
+export const getSmsCode = (param, option) => {
+  return request.get('/api/verifycodesmsbyqy', param, option)
+}
+
+
+//* *********** 激活路由器端口 **************
+
+// 发送路由器激活验证短信 验证码
+export const verifycodesmsbylyq = (param, option) => {
+  return request.get('/api/verifycodesmsbylyq', param, option)
+}
+
+// 激活路由器
+export const activationlyq = (param, option) => {
+  return request.get('/api/wx/activationlyq', param, option)
+}
+
+
+// 更新代理商信息
+export const upDlsMsg = (param, option) => {
+  return request.get('/api/up?pagevalue=309', param, option)
+}
+// 获取代理商信息 - 列表
+export const getDlsMsg = (param, option) => {
+  return request.get('/api/query/list?pagevalue=310', param, option)
+}
+// 输入激活码
+export const bindqym = (param, option) => {
+  return request.get('/api/wx/bindqym', param, option)
+}
+
+// 订单开通认证
+export const personrz = (param, option) => {
+  return request.get('/api/wx/personrz', param, option)
+}
+
+//* *********** 通用接口 **************
+// 查询列表get
+export function _get_list(param, option) {
+  return request.get('/api/query/list', param, option)
+}
+
+// 查询详情get
+export function _get_view(param, option) {
+  param.pagevalue = 285
+  return request.get('/api/query/view', param, option)
+}
+
+// 更新数据get
+export function _get_up(param, option) {
+  param.pagevalue = 287
+  return request.get('/api/up', param, option)
+}
+
+// 删除get
+export function _get_del(param, option) {
+  param.pagevalue = 286
+  return request.get('/api/del', param, option)
+}
+
+// 查询详情get
+export function _post_view_common(param, option) {
+  return request.get('/api/query/view', param, option)
+}
+
+
+// app客户端
+
+// 获取验证码
+export function getCodeImg(param, option) {
+  return request.arraybuffer('/api/sys/getloginvifcode', param, option)
+}
+
+// 登录方法
+export function login(param) {
+  return request.form('/api/sys/login', param)
+}

+ 11 - 0
hn-shop-uniapp/api/article/category.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'article.category/list'
+}
+
+// 页面数据
+export function list() {
+  return request.get(api.list)
+}

+ 17 - 0
hn-shop-uniapp/api/article/index.js

@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'article/list',
+  detail: 'article/detail'
+}
+
+// 文章列表
+export function list(param, option) {
+  return request.get(api.list, param, option)
+}
+
+// 文章详情
+export function detail(articleId) {
+  return request.get(api.detail, { articleId })
+}

+ 11 - 0
hn-shop-uniapp/api/balance.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'balance.log/list'
+}
+
+// 余额账单明细列表
+export const list = (param) => {
+  return request.get(api.list, param)
+}

+ 11 - 0
hn-shop-uniapp/api/balance/log.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'balance.log/list'
+}
+
+// 余额账单明细
+export const list = (param) => {
+  return request.get(api.list, param)
+}

+ 17 - 0
hn-shop-uniapp/api/captcha.js

@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  image: 'captcha/image',
+  sendSmsCaptcha: 'captcha/sendSmsCaptcha'
+}
+
+// 图形验证码
+export function image() {
+  return request.get(api.image, {}, { load: false })
+}
+
+// 发送短信验证码
+export function sendSmsCaptcha(data) {
+  return request.post(api.sendSmsCaptcha, data)
+}

+ 35 - 0
hn-shop-uniapp/api/cart.js

@@ -0,0 +1,35 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'cart/list',
+  total: 'cart/total',
+  add: 'cart/add',
+  update: 'cart/update',
+  clear: 'cart/clear'
+}
+
+// 购物车列表
+export const list = () => {
+  return request.get(api.list, {}, { load: false })
+}
+
+// 购物车商品总数量
+export const total = () => {
+  return request.get(api.total, {}, { load: false })
+}
+
+// 加入购物车
+export const add = (goodsId, goodsSkuId, goodsNum) => {
+  return request.post(api.add, { goodsId, goodsSkuId, goodsNum })
+}
+
+// 更新购物车商品数量
+export const update = (goodsId, goodsSkuId, goodsNum) => {
+  return request.post(api.update, { goodsId, goodsSkuId, goodsNum }, { isPrompt: false })
+}
+
+// 删除购物车中指定记录
+export const clear = (cartIds = []) => {
+  return request.post(api.clear, { cartIds })
+}

+ 35 - 0
hn-shop-uniapp/api/cashier/index.js

@@ -0,0 +1,35 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  orderInfo: 'cashier/orderInfo',
+  orderPay: 'cashier/orderPay',
+  tradeQuery: 'cashier/tradeQuery',
+}
+
+/**
+ * 获取支付订单的信息
+ * @param {Number} orderId
+ * @param {Object} param
+ */
+export function orderInfo(orderId, param) {
+  return request.get(api.orderInfo, { orderId, ...param })
+}
+
+/**
+ * 确认支付
+ * @param {Number} orderId
+ * @param {Object} data
+ */
+export function orderPay(orderId, data) {
+  return request.post(api.orderPay, { orderId, ...data })
+}
+
+/**
+ * 交易查询
+ * 查询第三方支付订单是否付款成功
+ * @param {Object} param
+ */
+export function tradeQuery(param) {
+  return request.get(api.tradeQuery, param)
+}

+ 11 - 0
hn-shop-uniapp/api/category/index.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'category/list'
+}
+
+// 页面数据
+export function list() {
+  return request.get(api.list)
+}

+ 19 - 0
hn-shop-uniapp/api/checkout.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  order: 'checkout/order',
+  submit: 'checkout/submit',
+}
+
+// mode: 结算模式 (buyNow立即购买 cart购物车)
+
+// 结算台订单信息
+export const order = (mode, param) => {
+  return request.get(api.order, { mode, ...param })
+}
+
+// 结算台订单提交
+export const submit = (mode, data) => {
+  return request.post(api.submit, { mode, ...data }, { isPrompt: false })
+}

+ 23 - 0
hn-shop-uniapp/api/comment.js

@@ -0,0 +1,23 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'comment/list',
+  listRows: 'comment/listRows',
+  total: 'comment/total'
+}
+
+// 商品评价列表
+export const list = (goodsId, param, option) => {
+  return request.get(api.list, { ...param, goodsId }, option)
+}
+
+// 商品评价列表 (限制数量, 用于商品详情页展示)
+export const listRows = (goodsId, limit = 5) => {
+  return request.get(api.listRows, { goodsId, limit })
+}
+
+// 商品评分总数
+export const total = (goodsId) => {
+  return request.get(api.total, { goodsId })
+}

+ 16 - 0
hn-shop-uniapp/api/coupon.js

@@ -0,0 +1,16 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'coupon/list'
+}
+
+// 优惠券列表
+export const list = (param, option) => {
+  const options = {
+    isPrompt: true, //(默认 true 说明:本接口抛出的错误是否提示)
+    load: true, //(默认 true 说明:本接口是否提示加载动画)
+    ...option
+  }
+  return request.get(api.list, param, options)
+}

+ 11 - 0
hn-shop-uniapp/api/express.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'express/list'
+}
+
+// 物流公司列表
+export const list = (param) => {
+  return request.get(api.list, param)
+}

+ 37 - 0
hn-shop-uniapp/api/goods/index.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'goods/list',
+  detail: 'goods/detail',
+  basic: 'goods/basic',
+  specData: 'goods/specData',
+  skuInfo: 'goods/skuInfo'
+}
+
+// 商品列表
+export const list = (param, option) => {
+  return request.get(api.list, param, option)
+}
+
+// 商品详情(详细数据)
+export const detail = (goodsId, verifyStatus = true, param = {}) => {
+  verifyStatus = Number(verifyStatus)
+  return request.get(api.detail, { goodsId, verifyStatus, ...param })
+}
+
+// 商品详情(基本数据)
+export const basic = (goodsId, verifyStatus = true, param = {}) => {
+  verifyStatus = Number(verifyStatus)
+  return request.get(api.basic, { goodsId, verifyStatus, ...param })
+}
+
+// 获取商品规格数据
+export const specData = (goodsId) => {
+  return request.get(api.specData, { goodsId })
+}
+
+// 获取商品的指定SKU信息
+export const skuInfo = (goodsId, goodsSkuId, param) => {
+  return request.get(api.skuInfo, { goodsId, goodsSkuId, ...param })
+}

+ 11 - 0
hn-shop-uniapp/api/goods/service.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'goods.service/list'
+}
+
+// 商品评价列表
+export function list(goodsId) {
+  return request.get(api.list, { goodsId })
+}

+ 11 - 0
hn-shop-uniapp/api/help.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'help/list'
+}
+
+// 帮助中心列表
+export const list = (param) => {
+  return request.get(api.list, param)
+}

+ 12 - 0
hn-shop-uniapp/api/invoice/index.js

@@ -0,0 +1,12 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  create: '/api/orderinvoice/create'
+}
+
+// 创建发票
+export function addInvoice(param, option) {
+  param.pagevalue = 360
+  return request.get(api.create, param, option)
+}

+ 29 - 0
hn-shop-uniapp/api/login/index.js

@@ -0,0 +1,29 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  login: 'passport/login',
+  loginMpWx: 'passport/loginMpWx',
+  loginMpWxMobile: 'passport/loginMpWxMobile',
+  isPersonalMpweixin: 'passport/isPersonalMpweixin',
+}
+
+// 用户登录 (手机号+验证码)
+export function login(data) {
+  return request.post(api.login, data)
+}
+
+// 微信小程序快捷登录 (获取微信用户基本信息)
+export function loginMpWx(data, option) {
+  return request.post(api.loginMpWx, data, option)
+}
+
+// 是否需要填写昵称头像 (微信小程序端)
+export function isPersonalMpweixin(data, option) {
+  return request.post(api.isPersonalMpweixin, data, option)
+}
+
+// 微信小程序快捷登录 (授权手机号)
+export function loginMpWxMobile(data, option) {
+  return request.post(api.loginMpWxMobile, data, option)
+}

+ 22 - 0
hn-shop-uniapp/api/myCoupon.js

@@ -0,0 +1,22 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'myCoupon/list',
+  receive: 'myCoupon/receive'
+}
+
+// 我的优惠券列表
+export const list = (param, option) => {
+  const options = {
+    isPrompt: true, //(默认 true 说明:本接口抛出的错误是否提示)
+    load: true, //(默认 true 说明:本接口是否提示加载动画)
+    ...option
+  }
+  return request.get(api.list, param, options)
+}
+
+// 领取优惠券
+export const receive = (couponId, data, option) => {
+  return request.post(api.receive, { couponId, ...data }, option)
+}

+ 42 - 0
hn-shop-uniapp/api/order.js

@@ -0,0 +1,42 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  todoCounts: 'order/todoCounts',
+  list: 'order/list',
+  detail: 'order/detail',
+  express: 'order/express',
+  cancel: 'order/cancel',
+  receipt: 'order/receipt',
+  pay: 'order/pay'
+}
+
+// 当前用户待处理的订单数量
+export function todoCounts(param, option) {
+  return request.get(api.todoCounts, param, option)
+}
+
+// 我的订单列表
+export function list(param, option) {
+  return request.get(api.list, param, option)
+}
+
+// 订单详情
+export function detail(orderId, param) {
+  return request.get(api.detail, { orderId, ...param })
+}
+
+// 获取物流信息
+export function express(orderId, param) {
+  return request.get(api.express, { orderId, ...param })
+}
+
+// 取消订单
+export function cancel(orderId, data) {
+  return request.post(api.cancel, { orderId, ...data })
+}
+
+// 确认收货
+export function receipt(orderId, data) {
+  return request.post(api.receipt, { orderId, ...data })
+}

+ 17 - 0
hn-shop-uniapp/api/order/comment.js

@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'order.comment/list',
+  submit: 'order.comment/submit'
+}
+
+// 待评价订单商品列表
+export const list = (orderId, param) => {
+  return request.get(api.list, { orderId, ...param })
+}
+
+// 创建商品评价
+export const submit = (orderId, data) => {
+  return request.post(api.submit, { orderId, form: data })
+}

+ 13 - 0
hn-shop-uniapp/api/page.js

@@ -0,0 +1,13 @@
+import request from '@/utils/request'
+
+// api地址
+const apiUri = {
+  detail: 'page/detail'
+}
+
+// 页面数据
+export function detail(pageId) {
+  return request.get(apiUri.detail, {
+    pageId
+  })
+}

+ 11 - 0
hn-shop-uniapp/api/points/log.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'points.log/list'
+}
+
+// 积分明细列表
+export const list = (param) => {
+  return request.get(api.list, param)
+}

+ 27 - 0
hn-shop-uniapp/api/recharge.js

@@ -0,0 +1,27 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  center: 'recharge/center',
+  submit: 'recharge/submit',
+  tradeQuery: 'recharge/tradeQuery'
+}
+
+// 充值中心页面数据
+export const center = (param) => {
+  return request.get(api.center, param)
+}
+
+// 确认充值订单
+export const submit = (data) => {
+  return request.post(api.submit, { form: data })
+}
+
+/**
+ * 交易查询
+ * 查询第三方支付订单是否付款成功
+ * @param {Object} param
+ */
+export function tradeQuery(param) {
+  return request.get(api.tradeQuery, param)
+}

+ 11 - 0
hn-shop-uniapp/api/recharge/order.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'recharge.order/list'
+}
+
+// 我的充值记录列表
+export const list = (param) => {
+  return request.get(api.list, param)
+}

+ 35 - 0
hn-shop-uniapp/api/refund.js

@@ -0,0 +1,35 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  list: 'refund/list',
+  goods: 'refund/goods',
+  apply: 'refund/apply',
+  detail: 'refund/detail',
+  delivery: 'refund/delivery'
+}
+
+// 售后单列表
+export const list = (param, option) => {
+  return request.get(api.list, param, option)
+}
+
+// 订单商品详情
+export const goods = (orderGoodsId, param) => {
+  return request.get(api.goods, { orderGoodsId, ...param })
+}
+
+// 申请售后
+export const apply = (orderGoodsId, data) => {
+  return request.post(api.apply, { orderGoodsId, form: data })
+}
+
+// 售后单详情
+export const detail = (orderRefundId, param) => {
+  return request.get(api.detail, { orderRefundId, ...param })
+}
+
+// 用户发货
+export const delivery = (orderRefundId, data) => {
+  return request.post(api.delivery, { orderRefundId, form: data })
+}

+ 17 - 0
hn-shop-uniapp/api/region.js

@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  all: 'region/all',
+  tree: 'region/tree'
+}
+
+// 获取所有地区
+export const all = (param) => {
+  return request.get(api.all, param)
+}
+
+// 获取所有地区(树状)
+export const tree = (param) => {
+  return request.get(api.tree, param)
+}

+ 11 - 0
hn-shop-uniapp/api/setting.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  data: 'setting/data'
+}
+
+// 设置项详情
+export function data() {
+  return request.get(api.data)
+}

+ 78 - 0
hn-shop-uniapp/api/store.js

@@ -0,0 +1,78 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+	data: 'store/data'
+}
+
+const infoShop = {
+	"status": 200,
+	"message": "success",
+	"data": {
+		"storeInfo": {
+			"store_id": 10001,
+			"store_name": "信飞通",
+			"describe": "感谢您选择信飞通,信飞通是全新推出的一款轻量级、高性能、前后端分离的ktboss系统",
+			"logo_image_id": 0,
+			"image_url": null,
+			"logoImage": null
+		},
+		"client": "H5",
+		"setting": {
+			"app_theme": {
+				"gradualChange": 1,
+				"mainBg": "#fa2209",
+				"mainBg2": "#ff6335",
+				"mainText": "#ffffff",
+				"viceBg": "#ffb100",
+				"viceBg2": "#ffb900",
+				"viceText": "#ffffff"
+			},
+			"page_category_template": {
+				"style": 20,
+				"shareTitle": "全部分类",
+				"showAddCart": true,
+				"cartStyle": 1
+			},
+			"points": {
+				"points_name": "积分",
+				"describe": "a) 积分不可兑现、不可转让,仅可在本平台使用;\nb) 您在本平台参加特定活动也可使用积分,详细使用规则以具体活动时的规则为准;\nc) 积分的数值精确到个位(小数点后全部舍弃,不进行四舍五入)\nd) 买家在完成该笔交易(订单状态为“已签收”)后才能得到此笔交易的相应积分,如购买商品参加店铺其他优惠,则优惠的金额部分不享受积分获取;"
+			},
+			"recharge": {
+				"is_entrance": 1,
+				"is_custom": 1,
+				"describe": "1. 账户充值仅限微信在线方式支付,充值金额实时到账;\n2. 账户充值套餐赠送的金额即时到账;\n3. 账户余额有效期:自充值日起至用完即止;\n4. 若有其它疑问,可拨打客服电话400-000-1234"
+			},
+			"register": {
+				"registerMethod": 10,
+				"isManualBind": 1,
+				"isOauthMpweixin": 1,
+				"isOauthMobileMpweixin": 1
+			},
+			"customer": {
+				"enabled": 1,
+				"provider": "mpwxkf",
+				"config": {
+					"mpwxkf": []
+				}
+			}
+		},
+		"clientData": {
+			"h5": {
+				"setting": {
+					"enabled": true,
+					"baseUrl": "https:\/\/shop2.client.yiovo.com\/"
+				}
+			}
+		}
+	}
+}
+
+// 获取商城基础信息
+const data = () => {
+	return infoShop
+}
+
+export default {
+	infoShop
+}

+ 18 - 0
hn-shop-uniapp/api/upload.js

@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  image: 'upload/image'
+}
+
+// 图片上传
+export const image = (files, checkLogin = true) => {
+  // 文件上传大小, 2M
+  const maxSize = 1024 * 1024 * 2
+  // 执行上传
+  return new Promise((resolve, reject) => {
+    request.urlFileUpload({ name: 'file', files, maxSize, data: { test: 123, checkLogin: Number(checkLogin) } })
+      .then(result => resolve(result.map(item => item.data.fileInfo.file_id), result))
+      .catch(reject)
+  })
+}

+ 34 - 0
hn-shop-uniapp/api/user.js

@@ -0,0 +1,34 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  userInfo: 'user/info',
+  assets: 'user/assets',
+  bindMobile: 'user/bindMobile',
+  personal: 'user/personal'
+}
+
+// 当前登录的用户信息
+export const info = (param, option) => {
+  const options = {
+    isPrompt: true, //(默认 true 说明:本接口抛出的错误是否提示)
+    load: true, //(默认 true 说明:本接口是否提示加载动画)
+    ...option
+  }
+  return request.get(api.userInfo, param, options)
+}
+
+// 账户资产
+export const assets = (param, option) => {
+  return request.get(api.assets, param, option)
+}
+
+// 绑定手机号
+export const bindMobile = (data, option) => {
+  return request.post(api.bindMobile, data, option)
+}
+
+// 修改个人信息(头像昵称)
+export const personal = (data, option) => {
+  return request.post(api.personal, data, option)
+}

+ 11 - 0
hn-shop-uniapp/api/user/coupon.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// api地址
+const api = {
+  receive: 'user.coupon/receive'
+}
+
+// 优惠券列表
+export const receive = (data) => {
+  return request.post(api.receive, data)
+}

+ 43 - 0
hn-shop-uniapp/app.scss

@@ -0,0 +1,43 @@
+/* utils.scss */
+@import "/utils/utils.scss";
+
+page {
+  background: #fafafa;
+}
+
+@keyframes rotate {
+  0% {
+    transform: rotate(0deg) scale(1);
+  }
+
+  100% {
+    transform: rotate(360deg) scale(1);
+  }
+}
+
+/* #ifdef H5*/
+
+uni-page {
+  box-shadow: 0 1rpx 44rpx rgba(169, 169, 169, .3);
+}
+
+.uni-app--showlayout+uni-tabbar.uni-tabbar-bottom,
+.uni-app--showlayout+uni-tabbar.uni-tabbar-bottom .uni-tabbar,
+.uni-app--showlayout+uni-tabbar.uni-tabbar-top,
+.uni-app--showlayout+uni-tabbar.uni-tabbar-top .uni-tabbar {
+  left: var(--window-left);
+  right: var(--window-right);
+}
+  
+.footer-fixed {
+  left: var(--window-left) !important;
+  right: var(--window-right) !important;
+}
+
+.u-mask,.u-drawer {
+  left: var(--window-left) !important;
+  right: var(--window-right) !important;
+}
+
+/* #endif */
+

+ 184 - 0
hn-shop-uniapp/colorui/animation.css

@@ -0,0 +1,184 @@
+/* 
+  Animation 微动画  
+  基于ColorUI组建库的动画模块 by 文晓港 2019年3月26日19:52:28
+ */
+
+/* css 滤镜 控制黑白底色gif的 */
+.gif-black{  
+  mix-blend-mode: screen;  
+}
+.gif-white{  
+  mix-blend-mode: multiply; 
+}
+
+
+/* Animation css */
+[class*=animation-] {
+    animation-duration: .5s;
+    animation-timing-function: ease-out;
+    animation-fill-mode: both
+}
+
+.animation-fade {
+    animation-name: fade;
+    animation-duration: .8s;
+    animation-timing-function: linear
+}
+
+.animation-scale-up {
+    animation-name: scale-up
+}
+
+.animation-scale-down {
+    animation-name: scale-down
+}
+
+.animation-slide-top {
+    animation-name: slide-top
+}
+
+.animation-slide-bottom {
+    animation-name: slide-bottom
+}
+
+.animation-slide-left {
+    animation-name: slide-left
+}
+
+.animation-slide-right {
+    animation-name: slide-right
+}
+
+.animation-shake {
+    animation-name: shake
+}
+
+.animation-reverse {
+    animation-direction: reverse
+}
+
+@keyframes fade {
+    0% {
+        opacity: 0
+    }
+
+    100% {
+        opacity: 1
+    }
+}
+
+@keyframes scale-up {
+    0% {
+        opacity: 0;
+        transform: scale(.2)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}
+
+@keyframes scale-down {
+    0% {
+        opacity: 0;
+        transform: scale(1.8)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}
+
+@keyframes slide-top {
+    0% {
+        opacity: 0;
+        transform: translateY(-100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateY(0)
+    }
+}
+
+@keyframes slide-bottom {
+    0% {
+        opacity: 0;
+        transform: translateY(100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateY(0)
+    }
+}
+
+@keyframes shake {
+
+    0%,
+    100% {
+        transform: translateX(0)
+    }
+
+    10% {
+        transform: translateX(-9px)
+    }
+
+    20% {
+        transform: translateX(8px)
+    }
+
+    30% {
+        transform: translateX(-7px)
+    }
+
+    40% {
+        transform: translateX(6px)
+    }
+
+    50% {
+        transform: translateX(-5px)
+    }
+
+    60% {
+        transform: translateX(4px)
+    }
+
+    70% {
+        transform: translateX(-3px)
+    }
+
+    80% {
+        transform: translateX(2px)
+    }
+
+    90% {
+        transform: translateX(-1px)
+    }
+}
+
+@keyframes slide-left {
+    0% {
+        opacity: 0;
+        transform: translateX(-100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateX(0)
+    }
+}
+
+@keyframes slide-right {
+    0% {
+        opacity: 0;
+        transform: translateX(100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateX(0)
+    }
+}

+ 65 - 0
hn-shop-uniapp/colorui/components/cu-custom.vue

@@ -0,0 +1,65 @@
+<template>
+	<view>
+		<view class="cu-custom" :style="[{height:CustomBar + 'px'}]">
+			<view class="cu-bar fixed" :style="style" :class="[bgImage!=''?'none-bg text-white bg-img':'',bgColor]">
+				<view class="action" @tap="BackPage" v-if="isBack">
+					<text class="cuIcon-back"></text>
+					<slot name="backText"></slot>
+				</view>
+				<view class="content" :style="[{top:StatusBar + 'px'}]">
+					<slot name="content"></slot>
+				</view>
+				<slot name="right"></slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				StatusBar: this.StatusBar,
+				CustomBar: this.CustomBar
+			};
+		},
+		name: 'cu-custom',
+		computed: {
+			style() {
+				var StatusBar= this.StatusBar;
+				var CustomBar= this.CustomBar;
+				var bgImage = this.bgImage;
+				var style = `height:${CustomBar}px;padding-top:${StatusBar}px;`;
+				if (this.bgImage) {
+					style = `${style}background-image:url(${bgImage});`;
+				}
+				return style
+			}
+		},
+		props: {
+			bgColor: {
+				type: String,
+				default: ''
+			},
+			isBack: {
+				type: [Boolean, String],
+				default: false
+			},
+			bgImage: {
+				type: String,
+				default: ''
+			},
+		},
+		methods: {
+			BackPage() {
+				uni.navigateBack({
+					delta: 1
+				});
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 36 - 0
hn-shop-uniapp/colorui/icon.css


+ 3929 - 0
hn-shop-uniapp/colorui/main.css

@@ -0,0 +1,3929 @@
+/*
+  ColorUi for uniApp  v2.1.6 | by 文晓港 2019-05-31 10:44:24
+  仅供学习交流,如作它用所承受的法律责任一概与作者无关  
+  
+  *使用ColorUi开发扩展与插件时,请注明基于ColorUi开发 
+  
+  (QQ交流群:240787041)
+*/
+
+/* ==================
+        初始化
+ ==================== */
+body {
+	background-color: #f2f5f9;
+	font-size: 28upx;
+	color: #333333;
+	font-family: Helvetica Neue, Helvetica, sans-serif;
+}
+
+view,
+scroll-view,
+swiper,
+button,
+input,
+textarea,
+label,
+navigator,
+image {
+	box-sizing: border-box;
+}
+
+.round {
+	border-radius: 5000upx;
+}
+
+.radius {
+	border-radius: 6upx;
+}
+
+/* ==================
+          图片
+ ==================== */
+
+image {
+	max-width: 100%;
+	display: inline-block;
+	position: relative;
+	z-index: 0;
+}
+
+image.loading::before {
+	content: "";
+	background-color: #f5f5f5;
+	display: block;
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	z-index: -2;
+}
+
+image.loading::after {
+	content: "\e7f1";
+	font-family: "cuIcon";
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 32upx;
+	height: 32upx;
+	line-height: 32upx;
+	right: 0;
+	bottom: 0;
+	z-index: -1;
+	font-size: 32upx;
+	margin: auto;
+	color: #ccc;
+	-webkit-animation: cuIcon-spin 2s infinite linear;
+	animation: cuIcon-spin 2s infinite linear;
+	display: block;
+}
+
+.response {
+	width: 100%;
+}
+
+/* ==================
+         开关
+ ==================== */
+
+switch,
+checkbox,
+radio {
+	position: relative;
+}
+
+switch::after,
+switch::before {
+	font-family: "cuIcon";
+	content: "\e645";
+	position: absolute;
+	color: #ffffff !important;
+	top: 0%;
+	left: 0upx;
+	font-size: 26upx;
+	line-height: 26px;
+	width: 50%;
+	text-align: center;
+	pointer-events: none;
+	transform: scale(0, 0);
+	transition: all 0.3s ease-in-out 0s;
+	z-index: 9;
+	bottom: 0;
+	height: 26px;
+	margin: auto;
+}
+
+switch::before {
+	content: "\e646";
+	right: 0;
+	transform: scale(1, 1);
+	left: auto;
+}
+
+switch[checked]::after,
+switch.checked::after {
+	transform: scale(1, 1);
+}
+
+switch[checked]::before,
+switch.checked::before {
+	transform: scale(0, 0);
+}
+
+/* #ifndef MP-ALIPAY */
+radio::before,
+checkbox::before {
+	font-family: "cuIcon";
+	content: "\e645";
+	position: absolute;
+	color: #ffffff !important;
+	top: 50%;
+	margin-top: -8px;
+	right: 5px;
+	font-size: 32upx;
+	line-height: 16px;
+	pointer-events: none;
+	transform: scale(1, 1);
+	transition: all 0.3s ease-in-out 0s;
+	z-index: 9;
+}
+
+radio .wx-radio-input,
+checkbox .wx-checkbox-input,
+radio .uni-radio-input,
+checkbox .uni-checkbox-input {
+	margin: 0;
+	width: 24px;
+	height: 24px;
+}
+
+checkbox.round .wx-checkbox-input,
+checkbox.round .uni-checkbox-input {
+	border-radius: 100upx;
+}
+
+/* #endif */
+
+switch[checked]::before {
+	transform: scale(0, 0);
+}
+
+switch .wx-switch-input,
+switch .uni-switch-input {
+	border: none;
+	padding: 0 24px;
+	width: 48px;
+	height: 26px;
+	margin: 0;
+	border-radius: 100upx;
+}
+
+switch .wx-switch-input:not([class*="bg-"]),
+switch .uni-switch-input:not([class*="bg-"]) {
+	background: #8799a3 !important;
+}
+
+switch .wx-switch-input::after,
+switch .uni-switch-input::after {
+	margin: auto;
+	width: 26px;
+	height: 26px;
+	border-radius: 100upx;
+	left: 0upx;
+	top: 0upx;
+	bottom: 0upx;
+	position: absolute;
+	transform: scale(0.9, 0.9);
+	transition: all 0.1s ease-in-out 0s;
+}
+
+switch .wx-switch-input.wx-switch-input-checked::after,
+switch .uni-switch-input.uni-switch-input-checked::after {
+	margin: auto;
+	left: 22px;
+	box-shadow: none;
+	transform: scale(0.9, 0.9);
+}
+
+radio-group {
+	display: inline-block;
+}
+
+
+
+switch.radius .wx-switch-input::after,
+switch.radius .wx-switch-input,
+switch.radius .wx-switch-input::before,
+switch.radius .uni-switch-input::after,
+switch.radius .uni-switch-input,
+switch.radius .uni-switch-input::before {
+	border-radius: 10upx;
+}
+
+switch .wx-switch-input::before,
+radio.radio::before,
+checkbox .wx-checkbox-input::before,
+radio .wx-radio-input::before,
+switch .uni-switch-input::before,
+radio.radio::before,
+checkbox .uni-checkbox-input::before,
+radio .uni-radio-input::before {
+	display: none;
+}
+
+radio.radio[checked]::after,
+radio.radio .uni-radio-input-checked::after {
+	content: "";
+	background-color: transparent;
+	display: block;
+	position: absolute;
+	width: 8px;
+	height: 8px;
+	z-index: 999;
+	top: 0upx;
+	left: 0upx;
+	right: 0;
+	bottom: 0;
+	margin: auto;
+	border-radius: 200upx;
+	/* #ifndef MP */
+	border: 7px solid #ffffff !important;
+	/* #endif */
+
+	/* #ifdef MP */
+	border: 8px solid #ffffff !important;
+	/* #endif */
+}
+
+.switch-sex::after {
+	content: "\e71c";
+}
+
+.switch-sex::before {
+	content: "\e71a";
+}
+
+.switch-sex .wx-switch-input,
+.switch-sex .uni-switch-input {
+	background: #e54d42 !important;
+	border-color: #e54d42 !important;
+}
+
+.switch-sex[checked] .wx-switch-input,
+.switch-sex.checked .uni-switch-input {
+	background: #0081ff !important;
+	border-color: #0081ff !important;
+}
+
+switch.red[checked] .wx-switch-input.wx-switch-input-checked,
+checkbox.red[checked] .wx-checkbox-input,
+radio.red[checked] .wx-radio-input,
+switch.red.checked .uni-switch-input.uni-switch-input-checked,
+checkbox.red.checked .uni-checkbox-input,
+radio.red.checked .uni-radio-input {
+	background-color: #e54d42 !important;
+	border-color: #e54d42 !important;
+	color: #ffffff !important;
+}
+
+switch.orange[checked] .wx-switch-input,
+checkbox.orange[checked] .wx-checkbox-input,
+radio.orange[checked] .wx-radio-input,
+switch.orange.checked .uni-switch-input,
+checkbox.orange.checked .uni-checkbox-input,
+radio.orange.checked .uni-radio-input {
+	background-color: #f37b1d !important;
+	border-color: #f37b1d !important;
+	color: #ffffff !important;
+}
+
+switch.yellow[checked] .wx-switch-input,
+checkbox.yellow[checked] .wx-checkbox-input,
+radio.yellow[checked] .wx-radio-input,
+switch.yellow.checked .uni-switch-input,
+checkbox.yellow.checked .uni-checkbox-input,
+radio.yellow.checked .uni-radio-input {
+	background-color: #fbbd08 !important;
+	border-color: #fbbd08 !important;
+	color: #333333 !important;
+}
+
+switch.olive[checked] .wx-switch-input,
+checkbox.olive[checked] .wx-checkbox-input,
+radio.olive[checked] .wx-radio-input,
+switch.olive.checked .uni-switch-input,
+checkbox.olive.checked .uni-checkbox-input,
+radio.olive.checked .uni-radio-input {
+	background-color: #8dc63f !important;
+	border-color: #8dc63f !important;
+	color: #ffffff !important;
+}
+
+switch.green[checked] .wx-switch-input,
+switch[checked] .wx-switch-input,
+checkbox.green[checked] .wx-checkbox-input,
+checkbox[checked] .wx-checkbox-input,
+radio.green[checked] .wx-radio-input,
+radio[checked] .wx-radio-input,
+switch.green.checked .uni-switch-input,
+switch.checked .uni-switch-input,
+checkbox.green.checked .uni-checkbox-input,
+checkbox.checked .uni-checkbox-input,
+radio.green.checked .uni-radio-input,
+radio.checked .uni-radio-input {
+	background-color: #39b54a !important;
+	border-color: #39b54a !important;
+	color: #ffffff !important;
+	border-color: #39B54A !important;
+}
+
+switch.cyan[checked] .wx-switch-input,
+checkbox.cyan[checked] .wx-checkbox-input,
+radio.cyan[checked] .wx-radio-input,
+switch.cyan.checked .uni-switch-input,
+checkbox.cyan.checked .uni-checkbox-input,
+radio.cyan.checked .uni-radio-input {
+	background-color: #1cbbb4 !important;
+	border-color: #1cbbb4 !important;
+	color: #ffffff !important;
+}
+
+switch.blue[checked] .wx-switch-input,
+checkbox.blue[checked] .wx-checkbox-input,
+radio.blue[checked] .wx-radio-input,
+switch.blue.checked .uni-switch-input,
+checkbox.blue.checked .uni-checkbox-input,
+radio.blue.checked .uni-radio-input {
+	background-color: #0081ff !important;
+	border-color: #0081ff !important;
+	color: #ffffff !important;
+}
+
+switch.purple[checked] .wx-switch-input,
+checkbox.purple[checked] .wx-checkbox-input,
+radio.purple[checked] .wx-radio-input,
+switch.purple.checked .uni-switch-input,
+checkbox.purple.checked .uni-checkbox-input,
+radio.purple.checked .uni-radio-input {
+	background-color: #6739b6 !important;
+	border-color: #6739b6 !important;
+	color: #ffffff !important;
+}
+
+switch.mauve[checked] .wx-switch-input,
+checkbox.mauve[checked] .wx-checkbox-input,
+radio.mauve[checked] .wx-radio-input,
+switch.mauve.checked .uni-switch-input,
+checkbox.mauve.checked .uni-checkbox-input,
+radio.mauve.checked .uni-radio-input {
+	background-color: #9c26b0 !important;
+	border-color: #9c26b0 !important;
+	color: #ffffff !important;
+}
+
+switch.pink[checked] .wx-switch-input,
+checkbox.pink[checked] .wx-checkbox-input,
+radio.pink[checked] .wx-radio-input,
+switch.pink.checked .uni-switch-input,
+checkbox.pink.checked .uni-checkbox-input,
+radio.pink.checked .uni-radio-input {
+	background-color: #e03997 !important;
+	border-color: #e03997 !important;
+	color: #ffffff !important;
+}
+
+switch.brown[checked] .wx-switch-input,
+checkbox.brown[checked] .wx-checkbox-input,
+radio.brown[checked] .wx-radio-input,
+switch.brown.checked .uni-switch-input,
+checkbox.brown.checked .uni-checkbox-input,
+radio.brown.checked .uni-radio-input {
+	background-color: #a5673f !important;
+	border-color: #a5673f !important;
+	color: #ffffff !important;
+}
+
+switch.grey[checked] .wx-switch-input,
+checkbox.grey[checked] .wx-checkbox-input,
+radio.grey[checked] .wx-radio-input,
+switch.grey.checked .uni-switch-input,
+checkbox.grey.checked .uni-checkbox-input,
+radio.grey.checked .uni-radio-input {
+	background-color: #8799a3 !important;
+	border-color: #8799a3 !important;
+	color: #ffffff !important;
+}
+
+switch.gray[checked] .wx-switch-input,
+checkbox.gray[checked] .wx-checkbox-input,
+radio.gray[checked] .wx-radio-input,
+switch.gray.checked .uni-switch-input,
+checkbox.gray.checked .uni-checkbox-input,
+radio.gray.checked .uni-radio-input {
+	background-color: #f0f0f0 !important;
+	border-color: #f0f0f0 !important;
+	color: #333333 !important;
+}
+
+switch.black[checked] .wx-switch-input,
+checkbox.black[checked] .wx-checkbox-input,
+radio.black[checked] .wx-radio-input,
+switch.black.checked .uni-switch-input,
+checkbox.black.checked .uni-checkbox-input,
+radio.black.checked .uni-radio-input {
+	background-color: #333333 !important;
+	border-color: #333333 !important;
+	color: #ffffff !important;
+}
+
+switch.white[checked] .wx-switch-input,
+checkbox.white[checked] .wx-checkbox-input,
+radio.white[checked] .wx-radio-input,
+switch.white.checked .uni-switch-input,
+checkbox.white.checked .uni-checkbox-input,
+radio.white.checked .uni-radio-input {
+	background-color: #ffffff !important;
+	border-color: #ffffff !important;
+	color: #333333 !important;
+}
+
+/* ==================
+          边框
+ ==================== */
+
+/* -- 实线 -- */
+
+.solid,
+.solid-top,
+.solid-right,
+.solid-bottom,
+.solid-left,
+.solids,
+.solids-top,
+.solids-right,
+.solids-bottom,
+.solids-left,
+.dashed,
+.dashed-top,
+.dashed-right,
+.dashed-bottom,
+.dashed-left {
+	position: relative;
+}
+
+.solid::after,
+.solid-top::after,
+.solid-right::after,
+.solid-bottom::after,
+.solid-left::after,
+.solids::after,
+.solids-top::after,
+.solids-right::after,
+.solids-bottom::after,
+.solids-left::after,
+.dashed::after,
+.dashed-top::after,
+.dashed-right::after,
+.dashed-bottom::after,
+.dashed-left::after {
+	content: " ";
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	border-radius: inherit;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	pointer-events: none;
+	box-sizing: border-box;
+}
+
+.solid::after {
+	border: 1upx solid rgba(0, 0, 0, 0.1);
+}
+
+.solid-top::after {
+	border-top: 1upx solid rgba(0, 0, 0, 0.1);
+}
+
+.solid-right::after {
+	border-right: 1upx solid rgba(0, 0, 0, 0.1);
+}
+
+.solid-bottom::after {
+	border-bottom: 1upx solid rgba(0, 0, 0, 0.1);
+}
+
+.solid-left::after {
+	border-left: 1upx solid rgba(0, 0, 0, 0.1);
+}
+
+.solids::after {
+	border: 8upx solid #eee;
+}
+
+.solids-top::after {
+	border-top: 8upx solid #eee;
+}
+
+.solids-right::after {
+	border-right: 8upx solid #eee;
+}
+
+.solids-bottom::after {
+	border-bottom: 8upx solid #eee;
+}
+
+.solids-left::after {
+	border-left: 8upx solid #eee;
+}
+
+/* -- 虚线 -- */
+
+.dashed::after {
+	border: 1upx dashed #ddd;
+}
+
+.dashed-top::after {
+	border-top: 1upx dashed #ddd;
+}
+
+.dashed-right::after {
+	border-right: 1upx dashed #ddd;
+}
+
+.dashed-bottom::after {
+	border-bottom: 1upx dashed #ddd;
+}
+
+.dashed-left::after {
+	border-left: 1upx dashed #ddd;
+}
+
+/* -- 阴影 -- */
+
+.shadow[class*='white'] {
+	--ShadowSize: 0 1upx 6upx;
+}
+
+.shadow-lg {
+	--ShadowSize: 0upx 40upx 100upx 0upx;
+}
+
+.shadow-warp {
+	position: relative;
+	box-shadow: 0 0 10upx rgba(0, 0, 0, 0.1);
+}
+
+.shadow-warp:before,
+.shadow-warp:after {
+	position: absolute;
+	content: "";
+	top: 20upx;
+	bottom: 30upx;
+	left: 20upx;
+	width: 50%;
+	box-shadow: 0 30upx 20upx rgba(0, 0, 0, 0.2);
+	transform: rotate(-3deg);
+	z-index: -1;
+}
+
+.shadow-warp:after {
+	right: 20upx;
+	left: auto;
+	transform: rotate(3deg);
+}
+
+.shadow-blur {
+	position: relative;
+}
+
+.shadow-blur::before {
+	content: "";
+	display: block;
+	background: inherit;
+	filter: blur(10upx);
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	top: 10upx;
+	left: 10upx;
+	z-index: -1;
+	opacity: 0.4;
+	transform-origin: 0 0;
+	border-radius: inherit;
+	transform: scale(1, 1);
+}
+
+/* ==================
+          按钮
+ ==================== */
+
+.cu-btn {
+	position: relative;
+	border: 0upx;
+	display: inline-flex;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+	padding: 0 30upx;
+	font-size: 28upx;
+	height: 64upx;
+	line-height: 1;
+	text-align: center;
+	text-decoration: none;
+	overflow: visible;
+	margin-left: initial;
+	transform: translate(0upx, 0upx);
+	margin-right: initial;
+}
+
+.cu-btn::after {
+	display: none;
+}
+
+.cu-btn:not([class*="bg-"]) {
+	background-color: #f0f0f0;
+}
+
+.cu-btn[class*="line"] {
+	background-color: transparent;
+}
+
+.cu-btn[class*="line"]::after {
+	content: " ";
+	display: block;
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	border: 1upx solid currentColor;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	box-sizing: border-box;
+	border-radius: 12upx;
+	z-index: 1;
+	pointer-events: none;
+}
+
+.cu-btn.round[class*="line"]::after {
+	border-radius: 1000upx;
+}
+
+.cu-btn[class*="lines"]::after {
+	border: 6upx solid currentColor;
+}
+
+.cu-btn[class*="bg-"]::after {
+	display: none;
+}
+
+.cu-btn.sm {
+	padding: 0 20upx;
+	font-size: 20upx;
+	height: 48upx;
+}
+
+.cu-btn.lg {
+	padding: 0 40upx;
+	font-size: 32upx;
+	height: 80upx;
+}
+
+.cu-btn.cuIcon.sm {
+	width: 48upx;
+	height: 48upx;
+}
+
+.cu-btn.cuIcon {
+	width: 64upx;
+	height: 64upx;
+	border-radius: 500upx;
+	padding: 0;
+}
+
+button.cuIcon.lg {
+	width: 80upx;
+	height: 80upx;
+}
+
+.cu-btn.shadow-blur::before {
+	top: 4upx;
+	left: 4upx;
+	filter: blur(6upx);
+	opacity: 0.6;
+}
+
+.cu-btn.button-hover {
+	transform: translate(1upx, 1upx);
+}
+
+.block {
+	display: block;
+}
+
+.cu-btn.block {
+	display: flex;
+}
+
+.cu-btn[disabled] {
+	opacity: 0.6;
+	color: #ffffff;
+}
+
+/* ==================
+          徽章
+ ==================== */
+
+.cu-tag {
+	font-size: 24upx;
+	vertical-align: middle;
+	position: relative;
+	display: inline-flex;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+	padding: 0upx 16upx;
+	height: 48upx;
+	font-family: Helvetica Neue, Helvetica, sans-serif;
+	white-space: nowrap;
+}
+
+.cu-tag:not([class*="bg"]):not([class*="line"]) {
+	background-color: #f1f1f1;
+}
+
+.cu-tag[class*="line-"]::after {
+	content: " ";
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	border: 1upx solid currentColor;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	box-sizing: border-box;
+	border-radius: inherit;
+	z-index: 1;
+	pointer-events: none;
+}
+
+.cu-tag.radius[class*="line"]::after {
+	border-radius: 12upx;
+}
+
+.cu-tag.round[class*="line"]::after {
+	border-radius: 1000upx;
+}
+
+.cu-tag[class*="line-"]::after {
+	border-radius: 0;
+}
+
+.cu-tag+.cu-tag {
+	margin-left: 10upx;
+}
+
+.cu-tag.sm {
+	font-size: 20upx;
+	padding: 0upx 12upx;
+	height: 32upx;
+}
+
+.cu-capsule {
+	display: inline-flex;
+	vertical-align: middle;
+}
+
+.cu-capsule+.cu-capsule {
+	margin-left: 10upx;
+}
+
+.cu-capsule .cu-tag {
+	margin: 0;
+}
+
+.cu-capsule .cu-tag[class*="line-"]:last-child::after {
+	border-left: 0upx solid transparent;
+}
+
+.cu-capsule .cu-tag[class*="line-"]:first-child::after {
+	border-right: 0upx solid transparent;
+}
+
+.cu-capsule.radius .cu-tag:first-child {
+	border-top-left-radius: 6upx;
+	border-bottom-left-radius: 6upx;
+}
+
+.cu-capsule.radius .cu-tag:last-child::after,
+.cu-capsule.radius .cu-tag[class*="line-"] {
+	border-top-right-radius: 12upx;
+	border-bottom-right-radius: 12upx;
+}
+
+.cu-capsule.round .cu-tag:first-child {
+	border-top-left-radius: 200upx;
+	border-bottom-left-radius: 200upx;
+	text-indent: 4upx;
+}
+
+.cu-capsule.round .cu-tag:last-child::after,
+.cu-capsule.round .cu-tag:last-child {
+	border-top-right-radius: 200upx;
+	border-bottom-right-radius: 200upx;
+	text-indent: -4upx;
+}
+
+.cu-tag.badge {
+	border-radius: 200upx;
+	position: absolute;
+	top: -10upx;
+	right: -10upx;
+	font-size: 20upx;
+	padding: 0upx 10upx;
+	height: 28upx;
+	color: #ffffff;
+}
+
+.cu-tag.badge:not([class*="bg-"]) {
+	background-color: #dd514c;
+}
+
+.cu-tag:empty:not([class*="cuIcon-"]) {
+	padding: 0upx;
+	width: 16upx;
+	height: 16upx;
+	top: -4upx;
+	right: -4upx;
+}
+
+.cu-tag[class*="cuIcon-"] {
+	width: 32upx;
+	height: 32upx;
+	top: -4upx;
+	right: -4upx;
+}
+
+/* ==================
+          头像
+ ==================== */
+
+.cu-avatar {
+	font-variant: small-caps;
+	margin: 0;
+	padding: 0;
+	display: inline-flex;
+	text-align: center;
+	justify-content: center;
+	align-items: center;
+	background-color: #ccc;
+	color: #ffffff;
+	white-space: nowrap;
+	position: relative;
+	width: 64upx;
+	height: 64upx;
+	background-size: cover;
+	background-position: center;
+	vertical-align: middle;
+	font-size: 1.5em;
+}
+
+.cu-avatar.sm {
+	width: 48upx;
+	height: 48upx;
+	font-size: 1em;
+}
+
+.cu-avatar.lg {
+	width: 96upx;
+	height: 96upx;
+	font-size: 2em;
+}
+
+.cu-avatar.xl {
+	width: 128upx;
+	height: 128upx;
+	font-size: 2.5em;
+}
+
+.cu-avatar .avatar-text {
+	font-size: 0.4em;
+}
+
+.cu-avatar-group {
+	direction: rtl;
+	unicode-bidi: bidi-override;
+	padding: 0 10upx 0 40upx;
+	display: inline-block;
+}
+
+.cu-avatar-group .cu-avatar {
+	margin-left: -30upx;
+	border: 4upx solid #f1f1f1;
+	vertical-align: middle;
+}
+
+.cu-avatar-group .cu-avatar.sm {
+	margin-left: -20upx;
+	border: 1upx solid #f1f1f1;
+}
+
+/* ==================
+         进度条
+ ==================== */
+
+.cu-progress {
+	overflow: hidden;
+	height: 28upx;
+	background-color: #ebeef5;
+	display: inline-flex;
+	align-items: center;
+	width: 100%;
+}
+
+.cu-progress+view,
+.cu-progress+text {
+	line-height: 1;
+}
+
+.cu-progress.xs {
+	height: 10upx;
+}
+
+.cu-progress.sm {
+	height: 20upx;
+}
+
+.cu-progress view {
+	width: 0;
+	height: 100%;
+	align-items: center;
+	display: flex;
+	justify-items: flex-end;
+	justify-content: space-around;
+	font-size: 20upx;
+	color: #ffffff;
+	transition: width 0.6s ease;
+}
+
+.cu-progress text {
+	align-items: center;
+	display: flex;
+	font-size: 20upx;
+	color: #333333;
+	text-indent: 10upx;
+}
+
+.cu-progress.text-progress {
+	padding-right: 60upx;
+}
+
+.cu-progress.striped view {
+	background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+	background-size: 72upx 72upx;
+}
+
+.cu-progress.active view {
+	animation: progress-stripes 2s linear infinite;
+}
+
+@keyframes progress-stripes {
+	from {
+		background-position: 72upx 0;
+	}
+
+	to {
+		background-position: 0 0;
+	}
+}
+
+/* ==================
+          加载
+ ==================== */
+
+.cu-load {
+	display: block;
+	line-height: 3em;
+	text-align: center;
+}
+
+.cu-load::before {
+	font-family: "cuIcon";
+	display: inline-block;
+	margin-right: 6upx;
+}
+
+.cu-load.loading::before {
+	content: "\e67a";
+	animation: cuIcon-spin 2s infinite linear;
+}
+
+.cu-load.loading::after {
+	content: "加载中...";
+}
+
+.cu-load.over::before {
+	content: "\e64a";
+}
+
+.cu-load.over::after {
+	content: "没有更多了";
+}
+
+.cu-load.erro::before {
+	content: "\e658";
+}
+
+.cu-load.erro::after {
+	content: "加载失败";
+}
+
+.cu-load.load-cuIcon::before {
+	font-size: 32upx;
+}
+
+.cu-load.load-cuIcon::after {
+	display: none;
+}
+
+.cu-load.load-cuIcon.over {
+	display: none;
+}
+
+.cu-load.load-modal {
+	position: fixed;
+	top: 0;
+	right: 0;
+	bottom: 140upx;
+	left: 0;
+	margin: auto;
+	width: 260upx;
+	height: 260upx;
+	background-color: #ffffff;
+	border-radius: 10upx;
+	box-shadow: 0 0 0upx 2000upx rgba(0, 0, 0, 0.5);
+	display: flex;
+	align-items: center;
+	flex-direction: column;
+	justify-content: center;
+	font-size: 28upx;
+	z-index: 9999;
+	line-height: 2.4em;
+}
+
+.cu-load.load-modal [class*="cuIcon-"] {
+	font-size: 60upx;
+}
+
+.cu-load.load-modal image {
+	width: 70upx;
+	height: 70upx;
+}
+
+.cu-load.load-modal::after {
+	content: "";
+	position: absolute;
+	background-color: #ffffff;
+	border-radius: 50%;
+	width: 200upx;
+	height: 200upx;
+	font-size: 10px;
+	border-top: 6upx solid rgba(0, 0, 0, 0.05);
+	border-right: 6upx solid rgba(0, 0, 0, 0.05);
+	border-bottom: 6upx solid rgba(0, 0, 0, 0.05);
+	border-left: 6upx solid #f37b1d;
+	animation: cuIcon-spin 1s infinite linear;
+	z-index: -1;
+}
+
+.load-progress {
+	pointer-events: none;
+	top: 0;
+	position: fixed;
+	width: 100%;
+	left: 0;
+	z-index: 2000;
+}
+
+.load-progress.hide {
+	display: none;
+}
+
+.load-progress .load-progress-bar {
+	position: relative;
+	width: 100%;
+	height: 4upx;
+	overflow: hidden;
+	transition: all 200ms ease 0s;
+}
+
+.load-progress .load-progress-spinner {
+	position: absolute;
+	top: 10upx;
+	right: 10upx;
+	z-index: 2000;
+	display: block;
+}
+
+.load-progress .load-progress-spinner::after {
+	content: "";
+	display: block;
+	width: 24upx;
+	height: 24upx;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
+	border: solid 4upx transparent;
+	border-top-color: inherit;
+	border-left-color: inherit;
+	border-radius: 50%;
+	-webkit-animation: load-progress-spinner 0.4s linear infinite;
+	animation: load-progress-spinner 0.4s linear infinite;
+}
+
+@-webkit-keyframes load-progress-spinner {
+	0% {
+		-webkit-transform: rotate(0);
+		transform: rotate(0);
+	}
+
+	100% {
+		-webkit-transform: rotate(360deg);
+		transform: rotate(360deg);
+	}
+}
+
+@keyframes load-progress-spinner {
+	0% {
+		-webkit-transform: rotate(0);
+		transform: rotate(0);
+	}
+
+	100% {
+		-webkit-transform: rotate(360deg);
+		transform: rotate(360deg);
+	}
+}
+
+/* ==================
+          列表
+ ==================== */
+.grayscale {
+	filter: grayscale(1);
+}
+
+.cu-list+.cu-list {
+	margin-top: 30upx
+}
+
+.cu-list>.cu-item {
+	transition: all .6s ease-in-out 0s;
+	transform: translateX(0upx)
+}
+
+.cu-list>.cu-item.move-cur {
+	transform: translateX(-260upx)
+}
+
+.cu-list>.cu-item .move {
+	position: absolute;
+	right: 0;
+	display: flex;
+	width: 260upx;
+	height: 100%;
+	transform: translateX(100%)
+}
+
+.cu-list>.cu-item .move view {
+	display: flex;
+	flex: 1;
+	justify-content: center;
+	align-items: center
+}
+
+.cu-list.menu-avatar {
+	overflow: hidden;
+}
+
+.cu-list.menu-avatar>.cu-item {
+	position: relative;
+	display: flex;
+	padding-right: 10upx;
+	height: 140upx;
+	background-color: #ffffff;
+	justify-content: flex-end;
+	align-items: center
+}
+
+.cu-list.menu-avatar>.cu-item>.cu-avatar {
+	position: absolute;
+	left: 30upx
+}
+
+.cu-list.menu-avatar>.cu-item .flex .text-cut {
+	max-width: 510upx
+}
+
+.cu-list.menu-avatar>.cu-item .content {
+	position: absolute;
+	left: 146upx;
+	width: calc(100% - 96upx - 60upx - 120upx - 20upx);
+	line-height: 1.6em;
+}
+
+.cu-list.menu-avatar>.cu-item .content.flex-sub {
+	width: calc(100% - 96upx - 60upx - 20upx);
+}
+
+.cu-list.menu-avatar>.cu-item .content>view:first-child {
+	font-size: 30upx;
+	display: flex;
+	align-items: center
+}
+
+.cu-list.menu-avatar>.cu-item .content .cu-tag.sm {
+	display: inline-block;
+	margin-left: 10upx;
+	height: 28upx;
+	font-size: 16upx;
+	line-height: 32upx
+}
+
+.cu-list.menu-avatar>.cu-item .action {
+	width: 100upx;
+	text-align: center
+}
+
+.cu-list.menu-avatar>.cu-item .action view+view {
+	margin-top: 10upx
+}
+
+.cu-list.menu-avatar.comment>.cu-item .content {
+	position: relative;
+	left: 0;
+	width: auto;
+	flex: 1;
+}
+
+.cu-list.menu-avatar.comment>.cu-item {
+	padding: 30upx 30upx 30upx 120upx;
+	height: auto
+}
+
+.cu-list.menu-avatar.comment .cu-avatar {
+	align-self: flex-start
+}
+
+.cu-list.menu>.cu-item {
+	position: relative;
+	display: flex;
+	padding: 0 30upx;
+	min-height: 100upx;
+	background-color: #ffffff;
+	justify-content: space-between;
+	align-items: center
+}
+
+.cu-list.menu>.cu-item:last-child:after {
+	border: none
+}
+
+.cu-list.menu-avatar>.cu-item:after,
+.cu-list.menu>.cu-item:after {
+	position: absolute;
+	top: 0;
+	left: 0;
+	box-sizing: border-box;
+	width: 200%;
+	height: 200%;
+	border-bottom: 1upx solid #ddd;
+	border-radius: inherit;
+	content: " ";
+	transform: scale(.5);
+	transform-origin: 0 0;
+	pointer-events: none
+}
+
+.cu-list.menu>.cu-item.grayscale {
+	background-color: #f5f5f5
+}
+
+.cu-list.menu>.cu-item.cur {
+	background-color: #fcf7e9
+}
+
+.cu-list.menu>.cu-item.arrow {
+	padding-right: 90upx
+}
+
+.cu-list.menu>.cu-item.arrow:before {
+	position: absolute;
+	top: 0;
+	right: 30upx;
+	bottom: 0;
+	display: block;
+	margin: auto;
+	width: 30upx;
+	height: 30upx;
+	color: #8799a3;
+	content: "\e6a3";
+	text-align: center;
+	font-size: 34upx;
+	font-family: cuIcon;
+	line-height: 30upx
+}
+
+.cu-list.menu>.cu-item button.content {
+	padding: 0;
+	background-color: transparent;
+	justify-content: flex-start
+}
+
+.cu-list.menu>.cu-item button.content:after {
+	display: none
+}
+
+.cu-list.menu>.cu-item .cu-avatar-group .cu-avatar {
+	border-color: #ffffff
+}
+
+.cu-list.menu>.cu-item .content>view:first-child {
+	display: flex;
+	align-items: center
+}
+
+.cu-list.menu>.cu-item .content>text[class*=cuIcon] {
+	display: inline-block;
+	margin-right: 10upx;
+	width: 1.6em;
+	text-align: center
+}
+
+.cu-list.menu>.cu-item .content>image {
+	display: inline-block;
+	margin-right: 10upx;
+	width: 1.6em;
+	height: 1.6em;
+	vertical-align: middle
+}
+
+.cu-list.menu>.cu-item .content {
+	font-size: 30upx;
+	line-height: 1.6em;
+	flex: 1
+}
+
+.cu-list.menu>.cu-item .content .cu-tag.sm {
+	display: inline-block;
+	margin-left: 10upx;
+	height: 28upx;
+	font-size: 16upx;
+	line-height: 32upx
+}
+
+.cu-list.menu>.cu-item .action .cu-tag:empty {
+	right: 10upx
+}
+
+.cu-list.menu {
+	display: block;
+	overflow: hidden
+}
+
+.cu-list.menu.sm-border>.cu-item:after {
+	left: 30upx;
+	width: calc(200% - 120upx)
+}
+
+.cu-list.grid>.cu-item {
+	position: relative;
+	display: flex;
+	padding: 20upx 0 30upx;
+	transition-duration: 0s;
+	flex-direction: column
+}
+
+.cu-list.grid>.cu-item:after {
+	position: absolute;
+	top: 0;
+	left: 0;
+	box-sizing: border-box;
+	width: 200%;
+	height: 200%;
+	border-right: 1px solid rgba(0, 0, 0, .1);
+	border-bottom: 1px solid rgba(0, 0, 0, .1);
+	border-radius: inherit;
+	content: " ";
+	transform: scale(.5);
+	transform-origin: 0 0;
+	pointer-events: none
+}
+
+.cu-list.grid>.cu-item text {
+	display: block;
+	margin-top: 10upx;
+	color: #888;
+	font-size: 26upx;
+	line-height: 40upx
+}
+
+.cu-list.grid>.cu-item [class*=cuIcon] {
+	position: relative;
+	display: block;
+	margin-top: 20upx;
+	width: 100%;
+	font-size: 48upx
+}
+
+.cu-list.grid>.cu-item .cu-tag {
+	right: auto;
+	left: 50%;
+	margin-left: 20upx
+}
+
+.cu-list.grid {
+	background-color: #ffffff;
+	text-align: center
+}
+
+.cu-list.grid.no-border>.cu-item {
+	padding-top: 10upx;
+	padding-bottom: 20upx
+}
+
+.cu-list.grid.no-border>.cu-item:after {
+	border: none
+}
+
+.cu-list.grid.no-border {
+	padding: 20upx 10upx
+}
+
+.cu-list.grid.col-3>.cu-item:nth-child(3n):after,
+.cu-list.grid.col-4>.cu-item:nth-child(4n):after,
+.cu-list.grid.col-5>.cu-item:nth-child(5n):after {
+	border-right-width: 0
+}
+
+.cu-list.card-menu {
+	overflow: hidden;
+	margin-right: 30upx;
+	margin-left: 30upx;
+	border-radius: 20upx
+}
+
+
+/* ==================
+          操作条
+ ==================== */
+
+.cu-bar {
+	display: flex;
+	position: relative;
+	align-items: center;
+	min-height: 100upx;
+	justify-content: space-between;
+}
+
+.cu-bar .action {
+	display: flex;
+	align-items: center;
+	height: 100%;
+	justify-content: center;
+	max-width: 100%;
+}
+
+.cu-bar .action.border-title {
+	position: relative;
+	top: -10upx;
+}
+
+.cu-bar .action.border-title text[class*="bg-"]:last-child {
+	position: absolute;
+	bottom: -0.5rem;
+	min-width: 2rem;
+	height: 6upx;
+	left: 0;
+}
+
+.cu-bar .action.sub-title {
+	position: relative;
+	top: -0.2rem;
+}
+
+.cu-bar .action.sub-title text {
+	position: relative;
+	z-index: 1;
+}
+
+.cu-bar .action.sub-title text[class*="bg-"]:last-child {
+	position: absolute;
+	display: inline-block;
+	bottom: -0.2rem;
+	border-radius: 6upx;
+	width: 100%;
+	height: 0.6rem;
+	left: 0.6rem;
+	opacity: 0.3;
+	z-index: 0;
+}
+
+.cu-bar .action.sub-title text[class*="text-"]:last-child {
+	position: absolute;
+	display: inline-block;
+	bottom: -0.7rem;
+	left: 0.5rem;
+	opacity: 0.2;
+	z-index: 0;
+	text-align: right;
+	font-weight: 900;
+	font-size: 36upx;
+}
+
+.cu-bar.justify-center .action.border-title text:last-child,
+.cu-bar.justify-center .action.sub-title text:last-child {
+	left: 0;
+	right: 0;
+	margin: auto;
+	text-align: center;
+}
+
+.cu-bar .action:first-child {
+	margin-left: 30upx;
+	font-size: 30upx;
+}
+
+.cu-bar .action text.text-cut {
+	text-align: left;
+	width: 100%;
+}
+
+.cu-bar .cu-avatar:first-child {
+	margin-left: 20upx;
+}
+
+.cu-bar .action:first-child>text[class*="cuIcon-"] {
+	margin-left: -0.3em;
+	margin-right: 0.3em;
+}
+
+.cu-bar .action:last-child {
+	margin-right: 30upx;
+}
+
+.cu-bar .action>text[class*="cuIcon-"],
+.cu-bar .action>view[class*="cuIcon-"] {
+	font-size: 36upx;
+}
+
+.cu-bar .action>text[class*="cuIcon-"]+text[class*="cuIcon-"] {
+	margin-left: 0.5em;
+}
+
+.cu-bar .content {
+	position: absolute;
+	text-align: center;
+	width: calc(100% - 340upx);
+	left: 0;
+	right: 0;
+	bottom: 0;
+	top: 0;
+	margin: auto;
+	height: 60upx;
+	font-size: 32upx;
+	line-height: 60upx;
+	cursor: none;
+	pointer-events: none;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	overflow: hidden;
+}
+
+.cu-bar.ios .content {
+	bottom: 7px;
+	height: 30px;
+	font-size: 32upx;
+	line-height: 30px;
+}
+
+.cu-bar.btn-group {
+	justify-content: space-around;
+}
+
+.cu-bar.btn-group button {
+	padding: 20upx 32upx;
+}
+
+.cu-bar.btn-group button {
+	flex: 1;
+	margin: 0 20upx;
+	max-width: 50%;
+}
+
+.cu-bar .search-form {
+	background-color: #f5f5f5;
+	line-height: 64upx;
+	height: 64upx;
+	font-size: 24upx;
+	color: #333333;
+	flex: 1;
+	display: flex;
+	align-items: center;
+	margin: 0 30upx;
+}
+
+.cu-bar .search-form+.action {
+	margin-right: 30upx;
+}
+
+.cu-bar .search-form input {
+	flex: 1;
+	padding-right: 30upx;
+	height: 64upx;
+	line-height: 64upx;
+	font-size: 26upx;
+	background-color: transparent;
+}
+
+.cu-bar .search-form [class*="cuIcon-"] {
+	margin: 0 0.5em 0 0.8em;
+}
+
+.cu-bar .search-form [class*="cuIcon-"]::before {
+	top: 0upx;
+}
+
+.cu-bar.fixed,
+.nav.fixed {
+	position: fixed;
+	width: 100%;
+	top: 0;
+	z-index: 1024;
+	box-shadow: 0 1upx 6upx rgba(0, 0, 0, 0.1);
+}
+
+.cu-bar.foot {
+	position: fixed;
+	width: 100%;
+	bottom: 0;
+	z-index: 1024;
+	box-shadow: 0 -1upx 6upx rgba(0, 0, 0, 0.1);
+}
+
+.cu-bar.tabbar {
+	padding: 0;
+	height: calc(100upx + env(safe-area-inset-bottom) / 2);
+	padding-bottom: calc(env(safe-area-inset-bottom) / 2);
+}
+
+.cu-tabbar-height {
+	min-height: 100upx;
+	height: calc(100upx + env(safe-area-inset-bottom) / 2);
+}
+
+.cu-bar.tabbar.shadow {
+	box-shadow: 0 -1upx 6upx rgba(0, 0, 0, 0.1);
+}
+
+.cu-bar.tabbar .action {
+	font-size: 22upx;
+	position: relative;
+	flex: 1;
+	text-align: center;
+	padding: 0;
+	display: block;
+	height: auto;
+	line-height: 1;
+	margin: 0;
+	background-color: inherit;
+	overflow: initial;
+}
+
+.cu-bar.tabbar.shop .action {
+	width: 140upx;
+	flex: initial;
+}
+
+.cu-bar.tabbar .action.add-action {
+	position: relative;
+	z-index: 2;
+	padding-top: 50upx;
+}
+
+.cu-bar.tabbar .action.add-action [class*="cuIcon-"] {
+	position: absolute;
+	width: 70upx;
+	z-index: 2;
+	height: 70upx;
+	border-radius: 50%;
+	line-height: 70upx;
+	font-size: 50upx;
+	top: -35upx;
+	left: 0;
+	right: 0;
+	margin: auto;
+	padding: 0;
+}
+
+.cu-bar.tabbar .action.add-action::after {
+	content: "";
+	position: absolute;
+	width: 100upx;
+	height: 100upx;
+	top: -50upx;
+	left: 0;
+	right: 0;
+	margin: auto;
+	box-shadow: 0 -3upx 8upx rgba(0, 0, 0, 0.08);
+	border-radius: 50upx;
+	background-color: inherit;
+	z-index: 0;
+}
+
+.cu-bar.tabbar .action.add-action::before {
+	content: "";
+	position: absolute;
+	width: 100upx;
+	height: 30upx;
+	bottom: 30upx;
+	left: 0;
+	right: 0;
+	margin: auto;
+	background-color: inherit;
+	z-index: 1;
+}
+
+.cu-bar.tabbar .btn-group {
+	flex: 1;
+	display: flex;
+	justify-content: space-around;
+	align-items: center;
+	padding: 0 10upx;
+}
+
+.cu-bar.tabbar button.action::after {
+	border: 0;
+}
+
+.cu-bar.tabbar .action [class*="cuIcon-"] {
+	width: 100upx;
+	position: relative;
+	display: block;
+	height: auto;
+	margin: 0 auto 10upx;
+	text-align: center;
+	font-size: 40upx;
+}
+
+.cu-bar.tabbar .action .cuIcon-cu-image {
+	margin: 0 auto;
+}
+
+.cu-bar.tabbar .action .cuIcon-cu-image image {
+	width: 50upx;
+	height: 50upx;
+	display: inline-block;
+}
+
+.cu-bar.tabbar .submit {
+	align-items: center;
+	display: flex;
+	justify-content: center;
+	text-align: center;
+	position: relative;
+	flex: 2;
+	align-self: stretch;
+}
+
+.cu-bar.tabbar .submit:last-child {
+	flex: 2.6;
+}
+
+.cu-bar.tabbar .submit+.submit {
+	flex: 2;
+}
+
+.cu-bar.tabbar.border .action::before {
+	content: " ";
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	border-right: 1upx solid rgba(0, 0, 0, 0.1);
+	z-index: 3;
+}
+
+.cu-bar.tabbar.border .action:last-child:before {
+	display: none;
+}
+
+.cu-bar.input {
+	padding-right: 20upx;
+	background-color: #ffffff;
+}
+
+.cu-bar.input input {
+	overflow: initial;
+	line-height: 64upx;
+	height: 64upx;
+	min-height: 64upx;
+	flex: 1;
+	font-size: 30upx;
+	margin: 0 20upx;
+}
+
+.cu-bar.input .action {
+	margin-left: 20upx;
+}
+
+.cu-bar.input .action [class*="cuIcon-"] {
+	font-size: 48upx;
+}
+
+.cu-bar.input input+.action {
+	margin-right: 20upx;
+	margin-left: 0upx;
+}
+
+.cu-bar.input .action:first-child [class*="cuIcon-"] {
+	margin-left: 0upx;
+}
+
+.cu-custom {
+	display: block;
+	position: relative;
+}
+
+.cu-custom .cu-bar .content {
+	width: calc(100% - 440upx);
+}
+
+/* #ifdef MP-ALIPAY */
+.cu-custom .cu-bar .action .cuIcon-back {
+	opacity: 0;
+}
+
+/* #endif */
+
+.cu-custom .cu-bar .content image {
+	height: 60upx;
+	width: 240upx;
+}
+
+.cu-custom .cu-bar {
+	min-height: 0px;
+	/* #ifdef MP-WEIXIN */
+	padding-right: 220upx;
+	/* #endif */
+	/* #ifdef MP-ALIPAY */
+	padding-right: 150upx;
+	/* #endif */
+	box-shadow: 0upx 0upx 0upx;
+	z-index: 9999;
+}
+
+.cu-custom .cu-bar .border-custom {
+	position: relative;
+	background: rgba(0, 0, 0, 0.15);
+	border-radius: 1000upx;
+	height: 30px;
+}
+
+.cu-custom .cu-bar .border-custom::after {
+	content: " ";
+	width: 200%;
+	height: 200%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	border-radius: inherit;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	pointer-events: none;
+	box-sizing: border-box;
+	border: 1upx solid #ffffff;
+	opacity: 0.5;
+}
+
+.cu-custom .cu-bar .border-custom::before {
+	content: " ";
+	width: 1upx;
+	height: 110%;
+	position: absolute;
+	top: 22.5%;
+	left: 0;
+	right: 0;
+	margin: auto;
+	transform: scale(0.5);
+	transform-origin: 0 0;
+	pointer-events: none;
+	box-sizing: border-box;
+	opacity: 0.6;
+	background-color: #ffffff;
+}
+
+.cu-custom .cu-bar .border-custom text {
+	display: block;
+	flex: 1;
+	margin: auto !important;
+	text-align: center;
+	font-size: 34upx;
+}
+
+/* ==================
+         导航栏
+ ==================== */
+
+.nav {
+	white-space: nowrap;
+}
+
+::-webkit-scrollbar {
+	display: none;
+}
+
+.nav .cu-item {
+	height: 90upx;
+	display: inline-block;
+	line-height: 90upx;
+	margin: 0 10upx;
+	padding: 0 20upx;
+}
+
+.nav .cu-item.cur {
+	border-bottom: 4upx solid;
+}
+
+/* ==================
+         时间轴
+ ==================== */
+
+.cu-timeline {
+	display: block;
+	background-color: #ffffff;
+}
+
+.cu-timeline .cu-time {
+	width: 120upx;
+	text-align: center;
+	padding: 20upx 0;
+	font-size: 26upx;
+	color: #888;
+	display: block;
+}
+
+.cu-timeline>.cu-item {
+	padding: 30upx 30upx 30upx 120upx;
+	position: relative;
+	display: block;
+	z-index: 0;
+}
+
+.cu-timeline>.cu-item:not([class*="text-"]) {
+	color: #ccc;
+}
+
+.cu-timeline>.cu-item::after {
+	content: "";
+	display: block;
+	position: absolute;
+	width: 1upx;
+	background-color: #ddd;
+	left: 60upx;
+	height: 100%;
+	top: 0;
+	z-index: 8;
+}
+
+.cu-timeline>.cu-item::before {
+	font-family: "cuIcon";
+	display: block;
+	position: absolute;
+	top: 36upx;
+	z-index: 9;
+	background-color: #ffffff;
+	width: 50upx;
+	height: 50upx;
+	text-align: center;
+	border: none;
+	line-height: 50upx;
+	left: 36upx;
+}
+
+.cu-timeline>.cu-item:not([class*="cuIcon-"])::before {
+	content: "\e763";
+}
+
+.cu-timeline>.cu-item[class*="cuIcon-"]::before {
+	background-color: #ffffff;
+	width: 50upx;
+	height: 50upx;
+	text-align: center;
+	border: none;
+	line-height: 50upx;
+	left: 36upx;
+}
+
+.cu-timeline>.cu-item>.content {
+	padding: 30upx;
+	border-radius: 6upx;
+	display: block;
+	line-height: 1.6;
+}
+
+.cu-timeline>.cu-item>.content:not([class*="bg-"]) {
+	background-color: #f1f1f1;
+	color: #333333;
+}
+
+.cu-timeline>.cu-item>.content+.content {
+	margin-top: 20upx;
+}
+
+/* ==================
+         聊天
+ ==================== */
+
+.cu-chat {
+	display: flex;
+	flex-direction: column;
+}
+
+.cu-chat .cu-item {
+	display: flex;
+	padding: 30upx 30upx 70upx;
+	position: relative;
+}
+
+.cu-chat .cu-item>.cu-avatar {
+	width: 80upx;
+	height: 80upx;
+}
+
+.cu-chat .cu-item>.main {
+	max-width: calc(100% - 260upx);
+	margin: 0 40upx;
+	display: flex;
+	align-items: center;
+}
+
+.cu-chat .cu-item>image {
+	height: 320upx;
+}
+
+.cu-chat .cu-item>.main .content {
+	padding: 20upx;
+	border-radius: 6upx;
+	display: inline-flex;
+	max-width: 100%;
+	align-items: center;
+	font-size: 30upx;
+	position: relative;
+	min-height: 80upx;
+	line-height: 40upx;
+	text-align: left;
+}
+
+.cu-chat .cu-item>.main .content:not([class*="bg-"]) {
+	background-color: #ffffff;
+	color: #333333;
+}
+
+.cu-chat .cu-item .date {
+	position: absolute;
+	font-size: 24upx;
+	color: #8799a3;
+	width: calc(100% - 320upx);
+	bottom: 20upx;
+	left: 160upx;
+}
+
+.cu-chat .cu-item .action {
+	padding: 0 30upx;
+	display: flex;
+	align-items: center;
+}
+
+.cu-chat .cu-item>.main .content::after {
+	content: "";
+	top: 27upx;
+	transform: rotate(45deg);
+	position: absolute;
+	z-index: 100;
+	display: inline-block;
+	overflow: hidden;
+	width: 24upx;
+	height: 24upx;
+	left: -12upx;
+	right: initial;
+	background-color: inherit;
+}
+
+.cu-chat .cu-item.self>.main .content::after {
+	left: auto;
+	right: -12upx;
+}
+
+.cu-chat .cu-item>.main .content::before {
+	content: "";
+	top: 30upx;
+	transform: rotate(45deg);
+	position: absolute;
+	z-index: -1;
+	display: inline-block;
+	overflow: hidden;
+	width: 24upx;
+	height: 24upx;
+	left: -12upx;
+	right: initial;
+	background-color: inherit;
+	filter: blur(5upx);
+	opacity: 0.3;
+}
+
+.cu-chat .cu-item>.main .content:not([class*="bg-"])::before {
+	background-color: #333333;
+	opacity: 0.1;
+}
+
+.cu-chat .cu-item.self>.main .content::before {
+	left: auto;
+	right: -12upx;
+}
+
+.cu-chat .cu-item.self {
+	justify-content: flex-end;
+	text-align: right;
+}
+
+.cu-chat .cu-info {
+	display: inline-block;
+	margin: 20upx auto;
+	font-size: 24upx;
+	padding: 8upx 12upx;
+	background-color: rgba(0, 0, 0, 0.2);
+	border-radius: 6upx;
+	color: #ffffff;
+	max-width: 400upx;
+	line-height: 1.4;
+}
+
+/* ==================
+         卡片
+ ==================== */
+
+.cu-card {
+	display: block;
+	overflow: hidden;
+}
+
+.cu-card>.cu-item {
+	display: block;
+	background-color: #ffffff;
+	overflow: hidden;
+	border-radius: 10upx;
+	margin: 30upx;
+}
+
+.cu-card>.cu-item.shadow-blur {
+	overflow: initial;
+}
+
+.cu-card.no-card>.cu-item {
+	margin: 0upx;
+	border-radius: 0upx;
+}
+
+.cu-card .grid.grid-square {
+	margin-bottom: -20upx;
+}
+
+.cu-card.case .image {
+	position: relative;
+}
+
+.cu-card.case .image image {
+	width: 100%;
+}
+
+.cu-card.case .image .cu-tag {
+	position: absolute;
+	right: 0;
+	top: 0;
+}
+
+.cu-card.case .image .cu-bar {
+	position: absolute;
+	bottom: 0;
+	width: 100%;
+	background-color: transparent;
+	padding: 0upx 30upx;
+}
+
+.cu-card.case.no-card .image {
+	margin: 30upx 30upx 0;
+	overflow: hidden;
+	border-radius: 10upx;
+}
+
+.cu-card.dynamic {
+	display: block;
+}
+
+.cu-card.dynamic>.cu-item {
+	display: block;
+	background-color: #ffffff;
+	overflow: hidden;
+}
+
+.cu-card.dynamic>.cu-item>.text-content {
+	padding: 0 30upx 0;
+	max-height: 6.4em;
+	overflow: hidden;
+	font-size: 30upx;
+	margin-bottom: 20upx;
+}
+
+.cu-card.dynamic>.cu-item .square-img {
+	width: 100%;
+	height: 200upx;
+	border-radius: 6upx;
+}
+
+.cu-card.dynamic>.cu-item .only-img {
+	width: 100%;
+	height: 320upx;
+	border-radius: 6upx;
+}
+
+/* card.dynamic>.cu-item .comment {
+  padding: 20upx;
+  background-color: #f1f1f1;
+  margin: 0 30upx 30upx;
+  border-radius: 6upx;
+} */
+
+.cu-card.article {
+	display: block;
+}
+
+.cu-card.article>.cu-item {
+	padding-bottom: 30upx;
+}
+
+.cu-card.article>.cu-item .title {
+	font-size: 30upx;
+	font-weight: 900;
+	color: #333333;
+	line-height: 100upx;
+	padding: 0 30upx;
+}
+
+.cu-card.article>.cu-item .content {
+	display: flex;
+	padding: 0 30upx;
+}
+
+.cu-card.article>.cu-item .content>image {
+	width: 240upx;
+	height: 6.4em;
+	margin-right: 20upx;
+	border-radius: 6upx;
+}
+
+.cu-card.article>.cu-item .content .desc {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	justify-content: space-between;
+}
+
+.cu-card.article>.cu-item .content .text-content {
+	font-size: 28upx;
+	color: #888;
+	height: 4.8em;
+	overflow: hidden;
+}
+
+/* ==================
+         表单
+ ==================== */
+
+.cu-form-group {
+	background-color: #ffffff;
+	padding: 1upx 30upx;
+	display: flex;
+	align-items: center;
+	min-height: 100upx;
+	justify-content: space-between;
+}
+
+.cu-form-group+.cu-form-group {
+	border-top: 1upx solid #eee;
+}
+
+.cu-form-group .title {
+	text-align: justify;
+	padding-right: 30upx;
+	font-size: 30upx;
+	position: relative;
+	height: 60upx;
+	line-height: 60upx;
+}
+
+.cu-form-group input {
+	flex: 1;
+	font-size: 30upx;
+	color: #555;
+	padding-right: 20upx;
+}
+
+.cu-form-group>text[class*="cuIcon-"] {
+	font-size: 36upx;
+	padding: 0;
+	box-sizing: border-box;
+}
+
+.cu-form-group textarea {
+	margin: 32upx 0 30upx;
+	height: 4.6em;
+	width: 100%;
+	line-height: 1.2em;
+	flex: 1;
+	font-size: 28upx;
+	padding: 0;
+}
+
+.cu-form-group.align-start .title {
+	height: 1em;
+	margin-top: 32upx;
+	line-height: 1em;
+}
+
+.cu-form-group picker {
+	flex: 1;
+	padding-right: 40upx;
+	overflow: hidden;
+	position: relative;
+}
+
+.cu-form-group picker .picker {
+	line-height: 100upx;
+	font-size: 28upx;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	overflow: hidden;
+	width: 100%;
+	text-align: right;
+}
+
+.cu-form-group picker::after {
+	font-family: cuIcon;
+	display: block;
+	content: "\e6a3";
+	position: absolute;
+	font-size: 34upx;
+	color: #8799a3;
+	line-height: 100upx;
+	width: 60upx;
+	text-align: center;
+	top: 0;
+	bottom: 0;
+	right: -20upx;
+	margin: auto;
+}
+
+.cu-form-group textarea[disabled],
+.cu-form-group textarea[disabled] .placeholder {
+	color: transparent;
+}
+
+/* ==================
+         模态窗口
+ ==================== */
+
+.cu-modal {
+	position: fixed;
+	top: 0;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	z-index: 1110;
+	opacity: 0;
+	outline: 0;
+	text-align: center;
+	-ms-transform: scale(1.185);
+	transform: scale(1.185);
+	backface-visibility: hidden;
+	perspective: 2000upx;
+	background: rgba(0, 0, 0, 0.6);
+	transition: all 0.3s ease-in-out 0s;
+	pointer-events: none;
+}
+
+.cu-modal::before {
+	content: "\200B";
+	display: inline-block;
+	height: 100%;
+	vertical-align: middle;
+}
+
+.cu-modal.show {
+	opacity: 1;
+	transition-duration: 0.3s;
+	-ms-transform: scale(1);
+	transform: scale(1);
+	overflow-x: hidden;
+	overflow-y: auto;
+	pointer-events: auto;
+}
+
+.cu-dialog {
+	position: relative;
+	display: inline-block;
+	vertical-align: middle;
+	margin-left: auto;
+	margin-right: auto;
+	width: 680upx;
+	max-width: 100%;
+	background-color: #f8f8f8;
+	border-radius: 10upx;
+	overflow: hidden;
+}
+
+.cu-modal.bottom-modal::before {
+	vertical-align: bottom;
+}
+
+.cu-modal.bottom-modal .cu-dialog {
+	width: 100%;
+	border-radius: 0;
+}
+
+.cu-modal.bottom-modal {
+	margin-bottom: -1000upx;
+}
+
+.cu-modal.bottom-modal.show {
+	margin-bottom: 0;
+}
+
+.cu-modal.drawer-modal {
+	transform: scale(1);
+	display: flex;
+}
+
+.cu-modal.drawer-modal .cu-dialog {
+	height: 100%;
+	min-width: 200upx;
+	border-radius: 0;
+	margin: initial;
+	transition-duration: 0.3s;
+}
+
+.cu-modal.drawer-modal.justify-start .cu-dialog {
+	transform: translateX(-100%);
+}
+
+.cu-modal.drawer-modal.justify-end .cu-dialog {
+	transform: translateX(100%);
+}
+
+.cu-modal.drawer-modal.show .cu-dialog {
+	transform: translateX(0%);
+}
+.cu-modal .cu-dialog>.cu-bar:first-child .action{
+  min-width: 100rpx;
+  margin-right: 0;
+  min-height: 100rpx;
+}
+/* ==================
+         轮播
+ ==================== */
+swiper .a-swiper-dot {
+	display: inline-block;
+	width: 16upx;
+	height: 16upx;
+	background: rgba(0, 0, 0, .3);
+	border-radius: 50%;
+	vertical-align: middle;
+}
+
+swiper[class*="-dot"] .wx-swiper-dots,
+swiper[class*="-dot"] .a-swiper-dots,
+swiper[class*="-dot"] .uni-swiper-dots {
+	display: flex;
+	align-items: center;
+	width: 100%;
+	justify-content: center;
+}
+
+swiper.square-dot .wx-swiper-dot,
+swiper.square-dot .a-swiper-dot,
+swiper.square-dot .uni-swiper-dot {
+	background-color: #ffffff;
+	opacity: 0.4;
+	width: 10upx;
+	height: 10upx;
+	border-radius: 20upx;
+	margin: 0 8upx !important;
+}
+
+swiper.square-dot .wx-swiper-dot.wx-swiper-dot-active,
+swiper.square-dot .a-swiper-dot.a-swiper-dot-active,
+swiper.square-dot .uni-swiper-dot.uni-swiper-dot-active {
+	opacity: 1;
+	width: 30upx;
+}
+
+swiper.round-dot .wx-swiper-dot,
+swiper.round-dot .a-swiper-dot,
+swiper.round-dot .uni-swiper-dot {
+	width: 10upx;
+	height: 10upx;
+	position: relative;
+	margin: 4upx 8upx !important;
+}
+
+swiper.round-dot .wx-swiper-dot.wx-swiper-dot-active::after,
+swiper.round-dot .a-swiper-dot.a-swiper-dot-active::after,
+swiper.round-dot .uni-swiper-dot.uni-swiper-dot-active::after {
+	content: "";
+	position: absolute;
+	width: 10upx;
+	height: 10upx;
+	top: 0upx;
+	left: 0upx;
+	right: 0;
+	bottom: 0;
+	margin: auto;
+	background-color: #ffffff;
+	border-radius: 20upx;
+}
+
+swiper.round-dot .wx-swiper-dot.wx-swiper-dot-active,
+swiper.round-dot .a-swiper-dot.a-swiper-dot-active,
+swiper.round-dot .uni-swiper-dot.uni-swiper-dot-active {
+	width: 18upx;
+	height: 18upx;
+}
+
+.screen-swiper {
+	min-height: 375upx;
+}
+
+.screen-swiper image,
+.screen-swiper video,
+.swiper-item image,
+.swiper-item video {
+	width: 100%;
+	display: block;
+	height: 100%;
+	margin: 0;
+	pointer-events: none;
+}
+
+.card-swiper {
+	height: 420upx !important;
+}
+
+.card-swiper swiper-item {
+	width: 610upx !important;
+	left: 70upx;
+	box-sizing: border-box;
+	padding: 40upx 0upx 70upx;
+	overflow: initial;
+}
+
+.card-swiper swiper-item .swiper-item {
+	width: 100%;
+	display: block;
+	height: 100%;
+	border-radius: 10upx;
+	transform: scale(0.9);
+	transition: all 0.2s ease-in 0s;
+	overflow: hidden;
+}
+
+.card-swiper swiper-item.cur .swiper-item {
+	transform: none;
+	transition: all 0.2s ease-in 0s;
+}
+
+
+.tower-swiper {
+	height: 420upx;
+	position: relative;
+	max-width: 750upx;
+	overflow: hidden;
+}
+
+.tower-swiper .tower-item {
+	position: absolute;
+	width: 300upx;
+	height: 380upx;
+	top: 0;
+	bottom: 0;
+	left: 50%;
+	margin: auto;
+	transition: all 0.2s ease-in 0s;
+	opacity: 1;
+}
+
+.tower-swiper .tower-item.none {
+	opacity: 0;
+}
+
+.tower-swiper .tower-item .swiper-item {
+	width: 100%;
+	height: 100%;
+	border-radius: 6upx;
+	overflow: hidden;
+}
+
+/* ==================
+          步骤条
+ ==================== */
+
+.cu-steps {
+	display: flex;
+}
+
+scroll-view.cu-steps {
+	display: block;
+	white-space: nowrap;
+}
+
+scroll-view.cu-steps .cu-item {
+	display: inline-block;
+}
+
+.cu-steps .cu-item {
+	flex: 1;
+	text-align: center;
+	position: relative;
+	min-width: 100upx;
+}
+
+.cu-steps .cu-item:not([class*="text-"]) {
+	color: #8799a3;
+}
+
+.cu-steps .cu-item [class*="cuIcon-"],
+.cu-steps .cu-item .num {
+	display: block;
+	font-size: 40upx;
+	line-height: 80upx;
+}
+
+.cu-steps .cu-item::before,
+.cu-steps .cu-item::after,
+.cu-steps.steps-arrow .cu-item::before,
+.cu-steps.steps-arrow .cu-item::after {
+	content: "";
+	display: block;
+	position: absolute;
+	height: 0px;
+	width: calc(100% - 80upx);
+	border-bottom: 1px solid #ccc;
+	left: calc(0px - (100% - 80upx) / 2);
+	top: 40upx;
+	z-index: 0;
+}
+
+.cu-steps.steps-arrow .cu-item::before,
+.cu-steps.steps-arrow .cu-item::after {
+	content: "\e6a3";
+	font-family: 'cuIcon';
+	height: 30upx;
+	border-bottom-width: 0px;
+	line-height: 30upx;
+	top: 0;
+	bottom: 0;
+	margin: auto;
+	color: #ccc;
+}
+
+.cu-steps.steps-bottom .cu-item::before,
+.cu-steps.steps-bottom .cu-item::after {
+	bottom: 40upx;
+	top: initial;
+}
+
+.cu-steps .cu-item::after {
+	border-bottom: 1px solid currentColor;
+	width: 0px;
+	transition: all 0.3s ease-in-out 0s;
+}
+
+.cu-steps .cu-item[class*="text-"]::after {
+	width: calc(100% - 80upx);
+	color: currentColor;
+}
+
+.cu-steps .cu-item:first-child::before,
+.cu-steps .cu-item:first-child::after {
+	display: none;
+}
+
+.cu-steps .cu-item .num {
+	width: 40upx;
+	height: 40upx;
+	border-radius: 50%;
+	line-height: 40upx;
+	margin: 20upx auto;
+	font-size: 24upx;
+	border: 1px solid currentColor;
+	position: relative;
+	overflow: hidden;
+}
+
+.cu-steps .cu-item[class*="text-"] .num {
+	background-color: currentColor;
+}
+
+.cu-steps .cu-item .num::before,
+.cu-steps .cu-item .num::after {
+	content: attr(data-index);
+	position: absolute;
+	left: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+	margin: auto;
+	transition: all 0.3s ease-in-out 0s;
+	transform: translateY(0upx);
+}
+
+.cu-steps .cu-item[class*="text-"] .num::before {
+	transform: translateY(-40upx);
+	color: #ffffff;
+}
+
+.cu-steps .cu-item .num::after {
+	transform: translateY(40upx);
+	color: #ffffff;
+	transition: all 0.3s ease-in-out 0s;
+}
+
+.cu-steps .cu-item[class*="text-"] .num::after {
+	content: "\e645";
+	font-family: 'cuIcon';
+	color: #ffffff;
+	transform: translateY(0upx);
+}
+
+.cu-steps .cu-item[class*="text-"] .num.err::after {
+	content: "\e646";
+}
+
+/* ==================
+          布局
+ ==================== */
+
+/*  -- flex弹性布局 -- */
+
+.flex {
+	display: flex;
+}
+
+.basis-xs {
+	flex-basis: 20%;
+}
+
+.basis-sm {
+	flex-basis: 40%;
+}
+
+.basis-df {
+	flex-basis: 50%;
+}
+
+.basis-lg {
+	flex-basis: 60%;
+}
+
+.basis-xl {
+	flex-basis: 80%;
+}
+
+.flex-sub {
+	flex: 1;
+}
+
+.flex-twice {
+	flex: 2;
+}
+
+.flex-treble {
+	flex: 3;
+}
+
+.flex-direction {
+	flex-direction: column;
+}
+
+.flex-wrap {
+	flex-wrap: wrap;
+}
+
+.align-start {
+	align-items: flex-start;
+}
+
+.align-end {
+	align-items: flex-end;
+}
+
+.align-center {
+	align-items: center;
+}
+
+.align-stretch {
+	align-items: stretch;
+}
+
+.self-start {
+	align-self: flex-start;
+}
+
+.self-center {
+	align-self: flex-center;
+}
+
+.self-end {
+	align-self: flex-end;
+}
+
+.self-stretch {
+	align-self: stretch;
+}
+
+.align-stretch {
+	align-items: stretch;
+}
+
+.justify-start {
+	justify-content: flex-start;
+}
+
+.justify-end {
+	justify-content: flex-end;
+}
+
+.justify-center {
+	justify-content: center;
+}
+
+.justify-between {
+	justify-content: space-between;
+}
+
+.justify-around {
+	justify-content: space-around;
+}
+
+/* grid布局 */
+
+.grid {
+	display: flex;
+	flex-wrap: wrap;
+}
+
+.grid.grid-square {
+	overflow: hidden;
+}
+
+.grid.grid-square .cu-tag {
+	position: absolute;
+	right: 0;
+	top: 0;
+	border-bottom-left-radius: 6upx;
+	padding: 6upx 12upx;
+	height: auto;
+	background-color: rgba(0, 0, 0, 0.5);
+}
+
+.grid.grid-square>view>text[class*="cuIcon-"] {
+	font-size: 52upx;
+	position: absolute;
+	color: #8799a3;
+	margin: auto;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	flex-direction: column;
+}
+
+.grid.grid-square>view {
+	margin-right: 20upx;
+	margin-bottom: 20upx;
+	border-radius: 6upx;
+	position: relative;
+	overflow: hidden;
+}
+.grid.grid-square>view.bg-img image {
+	width: 100%;
+	height: 100%;
+	position: absolute;
+}
+.grid.col-1.grid-square>view {
+	padding-bottom: 100%;
+	height: 0;
+	margin-right: 0;
+}
+
+.grid.col-2.grid-square>view {
+	padding-bottom: calc((100% - 20upx)/2);
+	height: 0;
+	width: calc((100% - 20upx)/2);
+}
+
+.grid.col-3.grid-square>view {
+	padding-bottom: calc((100% - 40upx)/3);
+	height: 0;
+	width: calc((100% - 40upx)/3);
+}
+
+.grid.col-4.grid-square>view {
+	padding-bottom: calc((100% - 60upx)/4);
+	height: 0;
+	width: calc((100% - 60upx)/4);
+}
+
+.grid.col-5.grid-square>view {
+	padding-bottom: calc((100% - 80upx)/5);
+	height: 0;
+	width: calc((100% - 80upx)/5);
+}
+
+.grid.col-2.grid-square>view:nth-child(2n),
+.grid.col-3.grid-square>view:nth-child(3n),
+.grid.col-4.grid-square>view:nth-child(4n),
+.grid.col-5.grid-square>view:nth-child(5n) {
+	margin-right: 0;
+}
+
+.grid.col-1>view {
+	width: 100%;
+}
+
+.grid.col-2>view {
+	width: 50%;
+}
+
+.grid.col-3>view {
+	width: 33.33%;
+}
+
+.grid.col-4>view {
+	width: 25%;
+}
+
+.grid.col-5>view {
+	width: 20%;
+}
+
+/*  -- 内外边距 -- */
+
+.margin-0 {
+	margin: 0;
+}
+
+.margin-xs {
+	margin: 10upx;
+}
+
+.margin-sm {
+	margin: 20upx;
+}
+
+.margin {
+	margin: 30upx;
+}
+
+.margin-lg {
+	margin: 40upx;
+}
+
+.margin-xl {
+	margin: 50upx;
+}
+
+.margin-top-xs {
+	margin-top: 10upx;
+}
+
+.margin-top-sm {
+	margin-top: 20upx;
+}
+
+.margin-top {
+	margin-top: 30upx;
+}
+
+.margin-top-lg {
+	margin-top: 40upx;
+}
+
+.margin-top-xl {
+	margin-top: 50upx;
+}
+
+.margin-right-xs {
+	margin-right: 10upx;
+}
+
+.margin-right-sm {
+	margin-right: 20upx;
+}
+
+.margin-right {
+	margin-right: 30upx;
+}
+
+.margin-right-lg {
+	margin-right: 40upx;
+}
+
+.margin-right-xl {
+	margin-right: 50upx;
+}
+
+.margin-bottom-xs {
+	margin-bottom: 10upx;
+}
+
+.margin-bottom-sm {
+	margin-bottom: 20upx;
+}
+
+.margin-bottom {
+	margin-bottom: 30upx;
+}
+
+.margin-bottom-lg {
+	margin-bottom: 40upx;
+}
+
+.margin-bottom-xl {
+	margin-bottom: 50upx;
+}
+.margin-bottom-xxl {
+	margin-bottom: 140upx;
+}
+
+.margin-left-xs {
+	margin-left: 10upx;
+}
+
+.margin-left-sm {
+	margin-left: 20upx;
+}
+
+.margin-left {
+	margin-left: 30upx;
+}
+
+.margin-left-lg {
+	margin-left: 40upx;
+}
+
+.margin-left-xl {
+	margin-left: 50upx;
+}
+
+.margin-lr-xs {
+	margin-left: 10upx;
+	margin-right: 10upx;
+}
+
+.margin-lr-sm {
+	margin-left: 20upx;
+	margin-right: 20upx;
+}
+
+.margin-lr {
+	margin-left: 30upx;
+	margin-right: 30upx;
+}
+
+.margin-lr-lg {
+	margin-left: 40upx;
+	margin-right: 40upx;
+}
+
+.margin-lr-xl {
+	margin-left: 50upx;
+	margin-right: 50upx;
+}
+
+.margin-tb-xs {
+	margin-top: 10upx;
+	margin-bottom: 10upx;
+}
+
+.margin-tb-sm {
+	margin-top: 20upx;
+	margin-bottom: 20upx;
+}
+
+.margin-tb {
+	margin-top: 30upx;
+	margin-bottom: 30upx;
+}
+
+.margin-tb-lg {
+	margin-top: 40upx;
+	margin-bottom: 40upx;
+}
+
+.margin-tb-xl {
+	margin-top: 50upx;
+	margin-bottom: 50upx;
+}
+
+.padding-0 {
+	padding: 0;
+}
+
+.padding-xs {
+	padding: 10upx;
+}
+
+.padding-sm {
+	padding: 20upx;
+}
+
+.padding {
+	padding: 30upx;
+}
+
+.padding-lg {
+	padding: 40upx;
+}
+
+.padding-xl {
+	padding: 50upx;
+}
+
+.padding-top-xs {
+	padding-top: 10upx;
+}
+
+.padding-top-sm {
+	padding-top: 20upx;
+}
+
+.padding-top {
+	padding-top: 30upx;
+}
+
+.padding-top-lg {
+	padding-top: 40upx;
+}
+
+.padding-top-xl {
+	padding-top: 50upx;
+}
+
+.padding-right-xs {
+	padding-right: 10upx;
+}
+
+.padding-right-sm {
+	padding-right: 20upx;
+}
+
+.padding-right {
+	padding-right: 30upx;
+}
+
+.padding-right-lg {
+	padding-right: 40upx;
+}
+
+.padding-right-xl {
+	padding-right: 50upx;
+}
+
+.padding-bottom-xs {
+	padding-bottom: 10upx;
+}
+
+.padding-bottom-sm {
+	padding-bottom: 20upx;
+}
+
+.padding-bottom {
+	padding-bottom: 30upx;
+}
+
+.padding-bottom-lg {
+	padding-bottom: 40upx;
+}
+
+.padding-bottom-xl {
+	padding-bottom: 50upx;
+}
+
+.padding-left-xs {
+	padding-left: 10upx;
+}
+
+.padding-left-sm {
+	padding-left: 20upx;
+}
+
+.padding-left {
+	padding-left: 30upx;
+}
+
+.padding-left-lg {
+	padding-left: 40upx;
+}
+
+.padding-left-xl {
+	padding-left: 50upx;
+}
+.padding-left-xxl {
+	padding-left: 80upx;
+}
+
+.padding-lr-xs {
+	padding-left: 10upx;
+	padding-right: 10upx;
+}
+
+.padding-lr-sm {
+	padding-left: 20upx;
+	padding-right: 20upx;
+}
+
+.padding-lr {
+	padding-left: 30upx;
+	padding-right: 30upx;
+}
+
+.padding-lr-lg {
+	padding-left: 40upx;
+	padding-right: 40upx;
+}
+
+.padding-lr-xl {
+	padding-left: 50upx;
+	padding-right: 50upx;
+}
+
+.padding-tb-xs {
+	padding-top: 10upx;
+	padding-bottom: 10upx;
+}
+
+.padding-tb-sm {
+	padding-top: 20upx;
+	padding-bottom: 20upx;
+}
+
+.padding-tb {
+	padding-top: 30upx;
+	padding-bottom: 30upx;
+}
+
+.padding-tb-lg {
+	padding-top: 40upx;
+	padding-bottom: 40upx;
+}
+
+.padding-tb-xl {
+	padding-top: 50upx;
+	padding-bottom: 50upx;
+}
+
+/* -- 浮动 --  */
+
+.cf::after,
+.cf::before {
+	content: " ";
+	display: table;
+}
+
+.cf::after {
+	clear: both;
+}
+
+.fl {
+	float: left;
+}
+
+.fr {
+	float: right;
+}
+
+/* ==================
+          背景
+ ==================== */
+
+.line-red::after,
+.lines-red::after {
+	border-color: #e54d42;
+}
+
+.line-orange::after,
+.lines-orange::after {
+	border-color: #f37b1d;
+}
+
+.line-yellow::after,
+.lines-yellow::after {
+	border-color: #fbbd08;
+}
+
+.line-olive::after,
+.lines-olive::after {
+	border-color: #8dc63f;
+}
+
+.line-green::after,
+.lines-green::after {
+	border-color: #39b54a;
+}
+
+.line-cyan::after,
+.lines-cyan::after {
+	border-color: #1cbbb4;
+}
+
+.line-blue::after,
+.lines-blue::after {
+	border-color: #0081ff;
+}
+
+.line-purple::after,
+.lines-purple::after {
+	border-color: #6739b6;
+}
+
+.line-mauve::after,
+.lines-mauve::after {
+	border-color: #9c26b0;
+}
+
+.line-pink::after,
+.lines-pink::after {
+	border-color: #e03997;
+}
+
+.line-brown::after,
+.lines-brown::after {
+	border-color: #a5673f;
+}
+
+.line-grey::after,
+.lines-grey::after {
+	border-color: #8799a3;
+}
+
+.line-gray::after,
+.lines-gray::after {
+	border-color: #aaaaaa;
+}
+
+.line-black::after,
+.lines-black::after {
+	border-color: #333333;
+}
+
+.line-white::after,
+.lines-white::after {
+	border-color: #ffffff;
+}
+
+.bg-red {
+	background-color: #e54d42;
+	color: #ffffff;
+}
+
+.bg-orange {
+	background-color: #f37b1d;
+	color: #ffffff;
+}
+
+.bg-yellow {
+	background-color: #fbbd08;
+	color: #333333;
+}
+
+.bg-olive {
+	background-color: #8dc63f;
+	color: #ffffff;
+}
+
+.bg-green {
+	background-color: #39b54a;
+	color: #ffffff;
+}
+
+.bg-cyan {
+	background-color: #1cbbb4;
+	color: #ffffff;
+}
+
+.bg-blue {
+	background-color: #0081ff;
+	color: #ffffff;
+}
+
+.bg-purple {
+	background-color: #6739b6;
+	color: #ffffff;
+}
+
+.bg-mauve {
+	background-color: #9c26b0;
+	color: #ffffff;
+}
+
+.bg-pink {
+	background-color: #e03997;
+	color: #ffffff;
+}
+
+.bg-brown {
+	background-color: #a5673f;
+	color: #ffffff;
+}
+
+.bg-grey {
+	background-color: #8799a3;
+	color: #ffffff;
+}
+
+.bg-gray {
+	background-color: #f0f0f0;
+	color: #333333;
+}
+
+.bg-black {
+	background-color: #333333;
+	color: #ffffff;
+}
+
+.bg-white {
+	background-color: #ffffff;
+	color: #666666;
+}
+
+.bg-shadeTop {
+	background-image: linear-gradient(rgba(0, 0, 0, 1), rgba(0, 0, 0, 0.01));
+	color: #ffffff;
+}
+
+.bg-shadeBottom {
+	background-image: linear-gradient(rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 1));
+	color: #ffffff;
+}
+
+.bg-red.light {
+	color: #e54d42;
+	background-color: #fadbd9;
+}
+
+.bg-orange.light {
+	color: #f37b1d;
+	background-color: #fde6d2;
+}
+
+.bg-yellow.light {
+	color: #fbbd08;
+	background-color: #fef2ced2;
+}
+
+.bg-olive.light {
+	color: #8dc63f;
+	background-color: #e8f4d9;
+}
+
+.bg-green.light {
+	color: #39b54a;
+	background-color: #d7f0dbff;
+}
+
+.bg-cyan.light {
+	color: #1cbbb4;
+	background-color: #d2f1f0;
+}
+
+.bg-blue.light {
+	color: #0081ff;
+	background-color: #cce6ff;
+}
+
+.bg-purple.light {
+	color: #6739b6;
+	background-color: #e1d7f0;
+}
+
+.bg-mauve.light {
+	color: #9c26b0;
+	background-color: #ebd4ef;
+}
+
+.bg-pink.light {
+	color: #e03997;
+	background-color: #f9d7ea;
+}
+
+.bg-brown.light {
+	color: #a5673f;
+	background-color: #ede1d9;
+}
+
+.bg-grey.light {
+	color: #8799a3;
+	background-color: #e7ebed;
+}
+
+.bg-gradual-red {
+	background-image: linear-gradient(45deg, #f43f3b, #ec008c);
+	color: #ffffff;
+}
+
+.bg-gradual-orange {
+	background-image: linear-gradient(45deg, #ff9700, #ed1c24);
+	color: #ffffff;
+}
+
+.bg-gradual-green {
+	background-image: linear-gradient(45deg, #39b54a, #8dc63f);
+	color: #ffffff;
+}
+
+.bg-gradual-purple {
+	background-image: linear-gradient(45deg, #9000ff, #5e00ff);
+	color: #ffffff;
+}
+
+.bg-gradual-pink {
+	background-image: linear-gradient(45deg, #ec008c, #6739b6);
+	color: #ffffff;
+}
+
+.bg-gradual-blue {
+	background-image: linear-gradient(45deg, #0081ff, #1cbbb4);
+	color: #ffffff;
+}
+.bg-1a1a1a{
+	background-color: #1A1A1A;
+	color: #ffffff;
+}
+.bg-gdBlue{
+	background-color: #2695FE;
+	color: #ffffff;
+}
+
+.shadow[class*="-red"] {
+	box-shadow: 6upx 6upx 8upx rgba(204, 69, 59, 0.2);
+}
+
+.shadow[class*="-orange"] {
+	box-shadow: 6upx 6upx 8upx rgba(217, 109, 26, 0.2);
+}
+
+.shadow[class*="-yellow"] {
+	box-shadow: 6upx 6upx 8upx rgba(224, 170, 7, 0.2);
+}
+
+.shadow[class*="-olive"] {
+	box-shadow: 6upx 6upx 8upx rgba(124, 173, 55, 0.2);
+}
+
+.shadow[class*="-green"] {
+	box-shadow: 6upx 6upx 8upx rgba(48, 156, 63, 0.2);
+}
+
+.shadow[class*="-cyan"] {
+	box-shadow: 6upx 6upx 8upx rgba(28, 187, 180, 0.2);
+}
+
+.shadow[class*="-blue"] {
+	box-shadow: 6upx 6upx 8upx rgba(0, 102, 204, 0.2);
+}
+
+.shadow[class*="-purple"] {
+	box-shadow: 6upx 6upx 8upx rgba(88, 48, 156, 0.2);
+}
+
+.shadow[class*="-mauve"] {
+	box-shadow: 6upx 6upx 8upx rgba(133, 33, 150, 0.2);
+}
+
+.shadow[class*="-pink"] {
+	box-shadow: 6upx 6upx 8upx rgba(199, 50, 134, 0.2);
+}
+
+.shadow[class*="-brown"] {
+	box-shadow: 6upx 6upx 8upx rgba(140, 88, 53, 0.2);
+}
+
+.shadow[class*="-grey"] {
+	box-shadow: 6upx 6upx 8upx rgba(114, 130, 138, 0.2);
+}
+
+.shadow[class*="-gray"] {
+	box-shadow: 6upx 6upx 8upx rgba(114, 130, 138, 0.2);
+}
+
+.shadow[class*="-black"] {
+	box-shadow: 6upx 6upx 8upx rgba(26, 26, 26, 0.2);
+}
+
+.shadow[class*="-white"] {
+	box-shadow: 6upx 6upx 8upx rgba(26, 26, 26, 0.2);
+}
+
+.text-shadow[class*="-red"] {
+	text-shadow: 6upx 6upx 8upx rgba(204, 69, 59, 0.2);
+}
+
+.text-shadow[class*="-orange"] {
+	text-shadow: 6upx 6upx 8upx rgba(217, 109, 26, 0.2);
+}
+
+.text-shadow[class*="-yellow"] {
+	text-shadow: 6upx 6upx 8upx rgba(224, 170, 7, 0.2);
+}
+
+.text-shadow[class*="-olive"] {
+	text-shadow: 6upx 6upx 8upx rgba(124, 173, 55, 0.2);
+}
+
+.text-shadow[class*="-green"] {
+	text-shadow: 6upx 6upx 8upx rgba(48, 156, 63, 0.2);
+}
+
+.text-shadow[class*="-cyan"] {
+	text-shadow: 6upx 6upx 8upx rgba(28, 187, 180, 0.2);
+}
+
+.text-shadow[class*="-blue"] {
+	text-shadow: 6upx 6upx 8upx rgba(0, 102, 204, 0.2);
+}
+
+.text-shadow[class*="-purple"] {
+	text-shadow: 6upx 6upx 8upx rgba(88, 48, 156, 0.2);
+}
+
+.text-shadow[class*="-mauve"] {
+	text-shadow: 6upx 6upx 8upx rgba(133, 33, 150, 0.2);
+}
+
+.text-shadow[class*="-pink"] {
+	text-shadow: 6upx 6upx 8upx rgba(199, 50, 134, 0.2);
+}
+
+.text-shadow[class*="-brown"] {
+	text-shadow: 6upx 6upx 8upx rgba(140, 88, 53, 0.2);
+}
+
+.text-shadow[class*="-grey"] {
+	text-shadow: 6upx 6upx 8upx rgba(114, 130, 138, 0.2);
+}
+
+.text-shadow[class*="-gray"] {
+	text-shadow: 6upx 6upx 8upx rgba(114, 130, 138, 0.2);
+}
+
+.text-shadow[class*="-black"] {
+	text-shadow: 6upx 6upx 8upx rgba(26, 26, 26, 0.2);
+}
+
+.bg-img {
+	background-size: cover;
+	background-position: center;
+	background-repeat: no-repeat;
+}
+
+.bg-mask {
+	background-color: #333333;
+	position: relative;
+}
+
+.bg-mask::after {
+	content: "";
+	border-radius: inherit;
+	width: 100%;
+	height: 100%;
+	display: block;
+	background-color: rgba(0, 0, 0, 0.4);
+	position: absolute;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	top: 0;
+}
+
+.bg-mask view,
+.bg-mask cover-view {
+	z-index: 5;
+	position: relative;
+}
+
+.bg-video {
+	position: relative;
+}
+
+.bg-video video {
+	display: block;
+	height: 100%;
+	width: 100%;
+	-o-object-fit: cover;
+	object-fit: cover;
+	position: absolute;
+	top: 0;
+	z-index: 0;
+	pointer-events: none;
+}
+
+/* ==================
+          文本
+ ==================== */
+
+.text-xs {
+	font-size: 20upx;
+}
+
+.text-sm {
+	font-size: 24upx;
+}
+
+.text-df {
+	font-size: 28upx;
+}
+
+.text-lg {
+	font-size: 32upx;
+}
+
+.text-xl {
+	font-size: 36upx;
+}
+
+.text-xxl {
+	font-size: 44upx;
+}
+.text-xxxl {
+	font-size: 52upx;
+}
+
+.text-sl {
+	font-size: 80upx;
+}
+
+.text-xsl {
+	font-size: 120upx;
+}
+
+.text-Abc {
+	text-transform: Capitalize;
+}
+
+.text-ABC {
+	text-transform: Uppercase;
+}
+
+.text-abc {
+	text-transform: Lowercase;
+}
+
+.text-price::before {
+	content: "¥";
+	font-size: 80%;
+	margin-right: 4upx;
+}
+
+.text-cut {
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	overflow: hidden;
+}
+
+.text-bold {
+	font-weight: bold;
+}
+
+.text-center {
+	text-align: center;
+}
+
+.text-content {
+	line-height: 1.6;
+}
+
+.text-left {
+	text-align: left;
+}
+
+.text-right {
+	text-align: right;
+}
+
+.text-red,
+.line-red,
+.lines-red {
+	color: #e54d42;
+}
+
+.text-orange,
+.line-orange,
+.lines-orange {
+	color: #f37b1d;
+}
+
+.text-yellow,
+.line-yellow,
+.lines-yellow {
+	color: #fbbd08;
+}
+
+.text-olive,
+.line-olive,
+.lines-olive {
+	color: #8dc63f;
+}
+
+.text-green,
+.line-green,
+.lines-green {
+	color: #39b54a;
+}
+
+.text-cyan,
+.line-cyan,
+.lines-cyan {
+	color: #1cbbb4;
+}
+
+.text-blue,
+.line-blue,
+.lines-blue {
+	color: #0081ff;
+}
+
+.text-purple,
+.line-purple,
+.lines-purple {
+	color: #6739b6;
+}
+
+.text-mauve,
+.line-mauve,
+.lines-mauve {
+	color: #9c26b0;
+}
+
+.text-pink,
+.line-pink,
+.lines-pink {
+	color: #e03997;
+}
+
+.text-brown,
+.line-brown,
+.lines-brown {
+	color: #a5673f;
+}
+
+.text-grey,
+.line-grey,
+.lines-grey {
+	color: #8799a3;
+}
+
+.text-gray,
+.line-gray,
+.lines-gray {
+	color: #aaaaaa;
+}
+
+.text-black,
+.line-black,
+.lines-black {
+	color: #333333;
+}
+
+.text-white,
+.line-white,
+.lines-white {
+	color: #ffffff;
+}

+ 237 - 0
hn-shop-uniapp/colorui/reset.css

@@ -0,0 +1,237 @@
+/*
+ * @Date: 2019-11-18 16:41:12
+ * @LastEditTime : 2020-01-11 15:25:32
+ * @开    发: lancer
+ * @版    本: 1.00
+ * @页面描述: 重置样式表
+ */
+/****************************** 自定义公共类 ***********************************/
+/* 清除浮动 */
+.r_clearfix::before,
+.r_clearfix::after {
+  content: '';
+  display: table;
+  clear: both;
+}
+/* 怪异盒模型 */
+.r_moxing {
+  box-sizing: border-box;
+}
+/* 块元素 */
+.r_block {
+  display: block;
+}
+/* 盒模型布局一 */
+.r_flex {
+  display: flex;
+}
+/* 盒模型布局一 以列排序 */
+.r_flex_wrap {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+/* 盒模型布局一(左右居中对齐) */
+.r_flex_center {
+  display: flex;
+  align-items: center !important;
+  align-content: center !important;
+  justify-content: center !important;
+}
+/* 盒模型布局二(左右对齐) */
+.r_flex_space {
+  display: flex;
+  align-items: center;
+  align-content: center;
+  justify-content: space-between;
+}
+/* 盒模型布局三(左右对齐) */
+.r_flex_column {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  align-content: center;
+  justify-content: center;
+}
+
+/* 盒模型布局 仅上下对其 */
+.r_flex_align {
+  display: flex;
+  align-items: center;
+}
+
+/* 绝对定位上下左右居中 */
+.r_absolute {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  right: 0px;
+  bottom: 0px;
+  margin: auto;
+}
+/* 块元素左右居中 */
+.r_margin {
+  display: block;
+  margin: 0 auto;
+}
+/* 块元素百分百 */
+.r_center {
+  width: 100%;
+  height: 100%;
+}
+/* 动画延迟 */
+.r_transform {
+  transition: all 0.4s;
+}
+.r_transform5 {
+  transition: all 0.7s;
+}
+.r_transform4 {
+  transition: all 0.5s;
+}
+.r_transform3 {
+  transition: all 0.3s;
+}
+.r_transform2 {
+  transition: all 0.2s;
+}
+.r_transform1 {
+  transition: all 0.1s;
+}
+/* ios滚动不流畅 */
+.r_iosscroll {
+  overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
+}
+.r_text {
+  /**
+      思路:
+      1.设置inline-block属相
+      2.强制不换行
+      3.固定高度
+      4.隐藏超出部分
+      5.显示“……”
+    */
+  display: block;
+  white-space: nowrap;
+  width: 100%;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.r_filter {
+  filter: grayscale(100%);
+  -moz-filter: grayscale(100%);
+  -o-filter: grayscale(100%);
+  -webkit-filter: grayscale(1);
+}
+
+/* 基本间距、边距 */
+.r_margin_10 {
+  display: block;
+  margin: 0 10px;
+}
+.r_margin_14 {
+  display: block;
+  margin: 0 14px;
+}
+.r_margin_18 {
+  display: block;
+  margin: 0 18px;
+}
+.r_margin_20 {
+  display: block;
+  margin: 0 20px;
+}
+.r_margin_22 {
+  display: block;
+  margin: 0 22px;
+}
+.r_margin_24 {
+  display: block;
+  margin: 0 24px;
+}
+
+/* 字体大小 */
+.r_size_12 {
+  font-size: 12px;
+  line-height: 12px;
+}
+.r_size_14 {
+  font-size: 14px;
+  line-height: 14px;
+}
+.r_size_16 {
+  font-size: 16px;
+  line-height: 16px;
+}
+.r_size_18 {
+  font-size: 18px;
+  line-height: 18px;
+}
+.r_size_20 {
+  font-size: 20px;
+  line-height: 20px;
+}
+.r_size_22 {
+  font-size: 22px;
+  line-height: 22px;
+}
+.r_size_24 {
+  font-size: 24px;
+  line-height: 24px;
+}
+.r_size_26 {
+  font-size: 26px;
+  line-height: 26px;
+}
+.r_size_28 {
+  font-size: 28px;
+  line-height: 28px;
+}
+.r_size_30 {
+  font-size: 30px;
+  line-height: 30px;
+}
+.r_size_32 {
+  font-size: 32px;
+  line-height: 32px;
+}
+.r_size_34 {
+  font-size: 34px;
+  line-height: 34px;
+}
+.r_size_36 {
+  font-size: 36px;
+  line-height: 36px;
+}
+.r_size_38 {
+  font-size: 38px;
+  line-height: 38px;
+}
+.r_size_40 {
+  font-size: 40px;
+  line-height: 40px;
+}
+
+/* 字体颜色 */
+.r_red {
+  color: #fc2828;
+}
+.r_hui {
+  color: #808080;
+}
+.hot,
+.icon-red {
+  color: #ff0000;
+}
+.len {
+  color: #12cbec;
+}
+
+/* 无法选择文字 */
+.r_text_none {
+  user-select: none;
+}
+
+/****************************** 组件样式设置 ***********************************/

+ 7 - 0
hn-shop-uniapp/common/constant/index.js

@@ -0,0 +1,7 @@
+export const paginate = {
+  data: [], // 列表数据
+  current_page: 1, // 当前页码
+  last_page: 1, // 最大页码
+  per_page: 15, // 每页记录数
+  total: 0, // 总记录数
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
hn-shop-uniapp/common/data/region.js


+ 11 - 0
hn-shop-uniapp/common/enum/Client.js

@@ -0,0 +1,11 @@
+import Enum from './enum'
+
+/**
+ * 枚举类:客户端类型
+ * ClientEnum
+ */
+export default new Enum([
+  { key: 'APP', name: 'APP端', value: 'APP' },
+  { key: 'H5', name: 'H5端', value: 'H5' },
+  { key: 'MP_WEIXIN', name: '微信小程序端', value: 'MP-WEIXIN' }
+])

+ 10 - 0
hn-shop-uniapp/common/enum/coupon/ApplyRange.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:优惠券适用范围
+ * ApplyRangeEnum
+ */
+export default new Enum([
+  { key: 'ALL', name: '全部商品', value: 10 },
+  { key: 'SOME_GOODS', name: '指定商品', value: 20 }
+])

+ 10 - 0
hn-shop-uniapp/common/enum/coupon/CouponType.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:优惠券类型
+ * CouponTypeEnum
+ */
+export default new Enum([
+  { key: 'FULL_DISCOUNT', name: '满减券', value: 10 },
+  { key: 'DISCOUNT', name: '折扣券', value: 20 }
+])

+ 10 - 0
hn-shop-uniapp/common/enum/coupon/ExpireType.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:优惠券到期类型
+ * ExpireTypeEnum
+ */
+export default new Enum([
+  { key: 'RECEIVE', name: '领取后', value: 10 },
+  { key: 'FIXED_TIME', name: '固定时间', value: 20 }
+])

+ 5 - 0
hn-shop-uniapp/common/enum/coupon/index.js

@@ -0,0 +1,5 @@
+import ApplyRangeEnum from './ApplyRange'
+import ExpireTypeEnum from './ExpireType'
+import CouponTypeEnum from './CouponType'
+
+export { ApplyRangeEnum, CouponTypeEnum, ExpireTypeEnum }

+ 85 - 0
hn-shop-uniapp/common/enum/enum.js

@@ -0,0 +1,85 @@
+/**
+ * 枚举类
+ * Enum.IMAGE.name                => "图片"
+ * Enum.getNameByKey('IMAGE')     => "图片"
+ * Enum.getValueByKey('IMAGE')    => 10
+ * Enum.getNameByValue(10)        => "图片"
+ * Enum.getData()                 => [{key: "IMAGE", name: "图片", value: 10}]
+ */
+class Enum {
+  constructor (param) {
+    const keyArr = []
+    const valueArr = []
+
+    if (!Array.isArray(param)) {
+      throw new Error('param is not an array!')
+    }
+
+    param.map(element => {
+      if (!element.key || !element.name) {
+        return
+      }
+      // 保存key值组成的数组,方便A.getName(name)类型的调用
+      keyArr.push(element.key)
+      valueArr.push(element.value)
+      // 根据key生成不同属性值,以便A.B.name类型的调用
+      this[element.key] = element
+      if (element.key !== element.value) {
+        this[element.value] = element
+      }
+    })
+
+
+    // 保存源数组
+    this.data = param
+    this.keyArr = keyArr
+    this.valueArr = valueArr
+
+    // 防止被修改
+    // Object.freeze(this)
+  }
+
+  // 根据key得到对象
+  keyOf (key) {
+    return this.data[this.keyArr.indexOf(key)]
+  }
+
+  // 根据key得到对象
+  valueOf (key) {
+    return this.data[this.valueArr.indexOf(key)]
+  }
+
+  // 根据key获取name值
+  getNameByKey (key) {
+    const prop = this.keyOf(key)
+    if (!prop) {
+      throw new Error('No enum constant' + key)
+    }
+    return prop.name
+  }
+
+  // 根据value获取name值
+  getNameByValue (value) {
+    const prop = this.valueOf(value)
+    if (!prop) {
+      throw new Error('No enum constant' + value)
+    }
+    return prop.name
+  }
+
+  // 根据key获取value值
+  getValueByKey (key) {
+    const prop = this.keyOf(key)
+    if (!prop) {
+      throw new Error('No enum constant' + key)
+    }
+    return prop.key
+  }
+
+  // 返回源数组
+  getData () {
+    return this.data
+  }
+}
+
+export default Enum

+ 10 - 0
hn-shop-uniapp/common/enum/goods/GoodsType.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:商品类型
+ * GoodsTypeEnum
+ */
+export default new Enum([
+  { key: 'PHYSICAL', name: '实物商品', value: 10 },
+  { key: 'VIRTUAL', name: '虚拟商品', value: 20 }
+])

+ 10 - 0
hn-shop-uniapp/common/enum/goods/SpecType.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:商品规格类型
+ * SpecTypeEnum
+ */
+export default new Enum([
+  { key: 'SINGLE', name: '单规格', value: 10 },
+  { key: 'MULTI', name: '多规格', value: 20 }
+])

+ 4 - 0
hn-shop-uniapp/common/enum/goods/index.js

@@ -0,0 +1,4 @@
+import GoodsTypeEnum from './GoodsType'
+import SpecTypeEnum from './SpecType'
+
+export { GoodsTypeEnum, SpecTypeEnum }

+ 10 - 0
hn-shop-uniapp/common/enum/order/DeliveryStatus.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:订单发货状态
+ * DeliveryStatusEnum
+ */
+export default new Enum([
+  { key: 'NOT_DELIVERED', name: '未发货', value: 10 },
+  { key: 'DELIVERED', name: '已发货', value: 20 }
+])

+ 10 - 0
hn-shop-uniapp/common/enum/order/DeliveryType.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:配送方式
+ * DeliveryTypeEnum
+ */
+export default new Enum([
+  { key: 'EXPRESS', name: '快递配送', value: 10 },
+  { key: 'NOTHING', name: '无需配送', value: 30 }
+])

+ 9 - 0
hn-shop-uniapp/common/enum/order/OrderSource.js

@@ -0,0 +1,9 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:订单来源
+ * OrderSourceEnum
+ */
+export default new Enum([
+  { key: 'MASTER', name: '普通订单', value: 10 }
+])

+ 12 - 0
hn-shop-uniapp/common/enum/order/OrderStatus.js

@@ -0,0 +1,12 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:订单状态
+ * OrderStatusEnum
+ */
+export default new Enum([
+  { key: 'NORMAL', name: '进行中', value: 10 },
+  { key: 'CANCELLED', name: '已取消', value: 20 },
+  { key: 'APPLY_CANCEL', name: '待取消', value: 21 },
+  { key: 'COMPLETED', name: '已完成', value: 30 }
+])

+ 10 - 0
hn-shop-uniapp/common/enum/order/OrderType.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:订单类型
+ * OrderTypeEnum
+ */
+export default new Enum([
+  { key: 'PHYSICAL', name: '实物订单', value: 10 },
+  { key: 'VIRTUAL', name: '虚拟订单', value: 20 }
+])

+ 10 - 0
hn-shop-uniapp/common/enum/order/PayStatus.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:订单支付状态
+ * PayStatusEnum
+ */
+export default new Enum([
+  { key: 'PENDING', name: '待支付', value: 10 },
+  { key: 'SUCCESS', name: '已支付', value: 20 }
+])

+ 10 - 0
hn-shop-uniapp/common/enum/order/ReceiptStatus.js

@@ -0,0 +1,10 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:订单收货状态
+ * ReceiptStatusEnum
+ */
+export default new Enum([
+  { key: 'NOT_RECEIVED', name: '未收货', value: 10 },
+  { key: 'RECEIVED', name: '已收货', value: 20 }
+])

+ 11 - 0
hn-shop-uniapp/common/enum/order/delivery/DeliveryMethod.js

@@ -0,0 +1,11 @@
+import Enum from '../../enum'
+
+/**
+ * 枚举类:发货方式
+ * DeliveryMethodEnum
+ */
+export default new Enum([
+  { key: 'MANUAL', name: '手动发货', value: 10 },
+  { key: 'UNWANTED', name: '无需物流', value: 20 },
+  { key: 'EORDER', name: '电子面单', value: 30 }
+])

+ 3 - 0
hn-shop-uniapp/common/enum/order/delivery/index.js

@@ -0,0 +1,3 @@
+import DeliveryMethodEnum from './DeliveryMethod'
+
+export { DeliveryMethodEnum }

+ 17 - 0
hn-shop-uniapp/common/enum/order/index.js

@@ -0,0 +1,17 @@
+import DeliveryStatusEnum from './DeliveryStatus'
+import DeliveryTypeEnum from './DeliveryType'
+import OrderSourceEnum from './OrderSource'
+import OrderStatusEnum from './OrderStatus'
+import PayStatusEnum from './PayStatus'
+import ReceiptStatusEnum from './ReceiptStatus'
+import OrderTypeEnum from './OrderType'
+
+export {
+    DeliveryStatusEnum,
+    DeliveryTypeEnum,
+    OrderSourceEnum,
+    OrderStatusEnum,
+    PayStatusEnum,
+    ReceiptStatusEnum,
+    OrderTypeEnum
+}

+ 11 - 0
hn-shop-uniapp/common/enum/order/refund/AuditStatus.js

@@ -0,0 +1,11 @@
+import Enum from '../../enum'
+
+/**
+ * 枚举类:商家审核状态
+ * AuditStatusEnum
+ */
+export default new Enum([
+  { key: 'WAIT', name: '待审核', value: 0 },
+  { key: 'REVIEWED', name: '已同意', value: 10 },
+  { key: 'REJECTED', name: '已拒绝', value: 20 }
+])

+ 12 - 0
hn-shop-uniapp/common/enum/order/refund/RefundStatus.js

@@ -0,0 +1,12 @@
+import Enum from '../../enum'
+
+/**
+ * 枚举类:售后单状态
+ * RefundStatusEnum
+ */
+export default new Enum([
+  { key: 'NORMAL', name: '进行中', value: 0 },
+  { key: 'REJECTED', name: '已拒绝', value: 10 },
+  { key: 'COMPLETED', name: '已完成', value: 20 },
+  { key: 'CANCELLED', name: '已取消', value: 30 }
+])

+ 10 - 0
hn-shop-uniapp/common/enum/order/refund/RefundType.js

@@ -0,0 +1,10 @@
+import Enum from '../../enum'
+
+/**
+ * 枚举类:售后类型
+ * RefundTypeEnum
+ */
+export default new Enum([
+  { key: 'RETURN', name: '退货退款', value: 10 },
+  { key: 'EXCHANGE', name: '换货', value: 20 }
+])

+ 9 - 0
hn-shop-uniapp/common/enum/order/refund/index.js

@@ -0,0 +1,9 @@
+import AuditStatusEnum from './AuditStatus'
+import RefundStatusEnum from './RefundStatus'
+import RefundTypeEnum from './RefundType'
+
+export {
+    AuditStatusEnum,
+    RefundStatusEnum,
+    RefundTypeEnum
+}

+ 11 - 0
hn-shop-uniapp/common/enum/payment/Method.js

@@ -0,0 +1,11 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:支付方式类型
+ * PayMethodEnum
+ */
+export default new Enum([
+  { key: 'WECHAT', name: '微信支付', value: 'wechat' },
+  { key: 'ALIPAY', name: '支付宝支付', value: 'alipay' },
+  { key: 'BALANCE', name: '余额支付', value: 'balance' }
+])

+ 3 - 0
hn-shop-uniapp/common/enum/payment/index.js

@@ -0,0 +1,3 @@
+import PayMethodEnum from './Method'
+
+export { PayMethodEnum }

+ 37 - 0
hn-shop-uniapp/common/enum/setting/Key.js

@@ -0,0 +1,37 @@
+import Enum from '../enum'
+
+/**
+ * 枚举类:设置项索引
+ * SettingKeyEnum
+ */
+export default new Enum([{
+    key: 'REGISTER',
+    name: '账户注册设置',
+    value: 'register'
+  },
+  {
+    key: 'APP_THEME',
+    name: '店铺页面风格',
+    value: 'app_theme'
+  },
+  {
+    key: 'PAGE_CATEGORY_TEMPLATE',
+    name: '分类页模板',
+    value: 'page_category_template'
+  },
+  {
+    key: 'POINTS',
+    name: '积分设置',
+    value: 'points'
+  },
+  {
+    key: 'RECHARGE',
+    name: '充值设置',
+    value: 'recharge'
+  },
+  {
+    key: 'CUSTOMER',
+    name: '商城客服设置',
+    value: 'customer'
+  }
+])

+ 12 - 0
hn-shop-uniapp/common/enum/store/page/category/Style.js

@@ -0,0 +1,12 @@
+import Enum from '../../../enum'
+
+/**
+ * 枚举类:地址类型
+ * PageCategoryStyleEnum
+ */
+export default new Enum([
+  { key: 'ONE_LEVEL_BIG', name: '一级分类[大图]', value: 10 },
+  { key: 'ONE_LEVEL_SMALL', name: '一级分类[小图]', value: 11 },
+  { key: 'TWO_LEVEL', name: '二级分类', value: 20 },
+  { key: 'COMMODITY', name: '一级分类+商品', value: 30 }
+])

+ 3 - 0
hn-shop-uniapp/common/enum/store/page/category/index.js

@@ -0,0 +1,3 @@
+import PageCategoryStyleEnum from './Style'
+
+export { PageCategoryStyleEnum }

+ 77 - 0
hn-shop-uniapp/common/model/Region.js

@@ -0,0 +1,77 @@
+import * as Api from '@/api/region'
+import storage from '@/utils/storage'
+import regionData from '../data/region'
+
+const REGION_TREE = 'region_tree'
+
+/**
+ * 商品分类 model类
+ * RegionModel
+ */
+export default {
+
+  // 从服务端获取全部地区数据(树状)
+  getTreeDataFromApi() {
+    return new Promise((resolve, reject) => {
+      Api.tree().then(result => resolve(result.data.list))
+    })
+  },
+
+  // 获取所有地区(树状) 从storage中获取
+  getTreeData() {
+    return new Promise((resolve, reject) => {
+      // 判断缓存中是否存在
+      const data = storage.get(REGION_TREE)
+      // 从服务端获取全部地区数据
+      if (data) {
+        resolve(data)
+      } else {
+        this.getTreeDataFromApi().then(list => {
+          // 缓存24小时
+          storage.set(REGION_TREE, list, 24 * 60 * 60)
+          resolve(list)
+        })
+      }
+    })
+  },
+
+  // 同步获取所有地区(树状)
+  getTreeDataSync() {
+    return regionData
+  },
+
+  // 获取所有地区的总数
+  getCitysCount() {
+    return new Promise((resolve, reject) => {
+      // 获取所有地区(树状)
+      this.getTreeData().then(data => {
+        const cityIds = []
+        // 遍历省份
+        for (const pidx in data) {
+          const province = data[pidx]
+          // 遍历城市
+          for (const cidx in province.city) {
+            const cityItem = province.city[cidx]
+            cityIds.push(cityItem.id)
+          }
+        }
+        resolve(cityIds.length)
+      })
+    })
+  },
+
+  // 同步获取所有地区的总数
+  getCitysCountSync() {
+    const regionData = this.getTreeDataSync()
+    const cityIds = []
+    for (const pidx in regionData) {
+      const province = regionData[pidx]
+      for (const cidx in province.city) {
+        const cityItem = province.city[cidx]
+        cityIds.push(cityItem.id)
+      }
+    }
+    return cityIds.length
+  }
+
+}

+ 88 - 0
hn-shop-uniapp/common/model/Setting.js

@@ -0,0 +1,88 @@
+import store from '@/store'
+import Config from '@/core/config'
+import storage from '@/utils/storage'
+import * as SettingApi from '@/api/setting'
+import SettingKeyEnum from '@/common/enum/setting/Key'
+import platform from '@/core/platform'
+
+const CACHE_KEY = 'Setting'
+
+// 写入缓存, 到期时间10分钟
+const setStorage = data => {
+  const expireTime = 10 * 60
+  storage.set(CACHE_KEY, data, expireTime)
+}
+
+// 获取缓存中的数据
+const getStorage = () => {
+  return storage.get(CACHE_KEY)
+}
+
+// 获取后端接口商城设置 (最新)
+const getApiData = () => {
+  console.log('获取后端接口商城设置settingApi接口1',)
+  return new Promise((resolve, reject) => {
+    SettingApi.data()
+      .then(result => {
+        resolve(result.data.setting)
+      })
+  })
+}
+
+/**
+ * 获取商城设置
+ * 有缓存的情况下返回缓存, 没有缓存从后端api获取
+ * @param {bool} isCache 是否从缓存中获取 [优点不用每次请求后端api 缺点后台更新设置后需等待时效性]
+ */
+const data = (isCache = undefined) => {
+  // console.log('获取后端接口商城设置settingApi接口2',)
+
+  if (isCache == undefined) {
+    isCache = Config.get('enabledSettingCache')
+  }
+  return new Promise((resolve, reject) => {
+    const cacheData = getStorage()
+    if (isCache && cacheData) {
+      resolve(cacheData)
+    } else {
+      getApiData().then(data => {
+        setStorage(data)
+        resolve(data)
+      })
+    }
+  })
+}
+
+// 获取商城设置(指定项)
+const item = (key, isCache = undefined) => {
+  return new Promise((resolve, reject) => {
+    data(isCache).then(setting => resolve(setting[key]))
+  })
+}
+
+// 设置全局自定义主题
+const setAppTheme = () => {
+  return new Promise((resolve, reject) => {
+    item(SettingKeyEnum.APP_THEME.value).then(appTheme => {
+      store.dispatch('SetAppTheme', appTheme)
+      resolve()
+    })
+  })
+}
+
+// 是否显示客服按钮 (微信小程序客服只有在微信小程序端显示)
+const isShowCustomerBtn = async () => {
+  const setting = await item(SettingKeyEnum.CUSTOMER.value, true)
+  if (!setting.enabled) {
+    return false
+  }
+  return setting.provider === 'mpwxkf' && platform === 'MP-WEIXIN'
+}
+
+export default {
+  setStorage,
+  data,
+  item,
+  setAppTheme,
+  isShowCustomerBtn
+}

+ 280 - 0
hn-shop-uniapp/common/model/Store.js

@@ -0,0 +1,280 @@
+import store from '@/store'
+import StoreApi from '@/api/store'
+import {
+	openidCode,
+	getCustomer,
+	getAgents,
+	getYhinfo,
+	getUserDdinfo
+} from '@/api/app'
+import storage from '@/utils/storage'
+
+
+import SettingModel from '@/common/model/Setting'
+
+const CACHE_KEY = 'Store'
+
+// 写入缓存, 到期时间10分钟
+const setStorage = data => {
+	const expireTime = 10 * 60
+	storage.set(CACHE_KEY, data, expireTime)
+}
+
+// 获取缓存中的数据
+const getStorage = () => {
+	return storage.get(CACHE_KEY)
+}
+
+/**
+ * 普通用户
+ * 
+ * 微信公众号进入
+ * 从get参数获取code
+ * code鉴权 err: 拦截弹出
+ * 鉴权后获取 openid、userid
+ * 更新用户信息
+ * 
+ * 注册代理商
+ * 微信公众号进入
+ * 从get参数获取code
+ * code鉴权
+ * 鉴权后获取 openid、userid
+ * 更新用户信息
+ * 
+ * 注册通过
+ * 更新用户信息
+ * 
+ */
+
+// 获取后端接口鉴权设置
+const getApiData = async (data) => {
+	return new Promise(async (resolve, reject) => {
+		// console.log(data, 'getApiData - 鉴权')
+		let param = data?.param ? data?.param.split(',') : ['', '']
+		
+		// 临时调试 --start--
+		// let resCode = getWxOpen('')
+		// let param = 'pages/user/index,shopping'.split(',')
+		// 临时调试 --ebd--
+		
+		if (param[1]) {
+			storage.set('wx_v', param[1])
+		}
+		if (param[2]) {
+			console.log('扫描企业二维码进入,手动更新企业码')
+			storage.set('wx_qym', param[2])
+		}
+		// 获取 User Agent
+		let userAgent = navigator.userAgent.toLowerCase();
+		// 判断是否在微信中打开
+		const type = (userAgent.indexOf('micromessenger') === -1)
+		if (false) {
+			store.commit('SET_PLATTYPE', '其他')
+			store.commit('SET_VERIFYMSG', '错误!请在微信浏览器中打开')
+			resolve({
+				resCode: false,
+				err: '请在微信浏览器中打开'
+			})
+			return
+		} else {
+			store.commit('SET_PLATTYPE', '微信')
+		}
+
+		let wxCode = data.code
+		let resCode = ''
+		if (wxCode) {
+			// 鉴权
+			resCode = await getWxOpen(wxCode)
+			// 如果没有则存起来
+			if (!storage.get('WX_CODE')) {
+				storage.set('WX_CODE', wxCode)
+			}
+		} else if (!storage.get('wx_userid') || !storage.get('wx_openid')) {
+			store.commit('SET_VERIFYMSG', '错误,没有鉴权码!')
+		}
+
+		// 获取用户基础信息
+		// getCustomerData()
+
+		// 初始化商城数据
+		initStoreData(StoreApi.infoShop.data)
+		// console.log(StoreApi.infoShop.data, '初始化商城数据结束')
+
+		// 回调
+		resolve({
+			resCode: storage.get('WX_CODE') || resCode,
+			err: `${resCode ? '没有鉴权码' : '没有鉴权码!'}`,
+			infoShop: StoreApi.infoShop.data
+		})
+	})
+}
+
+// 初始化商城数据
+const initStoreData = data => {
+	// 将商城基本信息写入缓存
+	setStorage(data)
+	// 设置商城设置缓存
+	SettingModel.setStorage(data.setting)
+	// 设置全局自定义主题
+	SettingModel.setAppTheme()
+}
+
+// 鉴权
+const getWxOpen = async (wxCode) => {
+	return new Promise(async (resolve, reject) => {
+		try {
+			// 鉴权
+			const res = await openidCode({ code: wxCode, qym: storage.get('wx_qym') || '' })
+			
+			// 临时调试 --start--
+			// const res = {
+			// 	"Status": 0,
+			// 	"Data": {
+			// 		"subscribe": 1,
+			// 		"userid": "2024081412514638086221",
+			// 		"qym": "LGFMwT2m",
+			// 		"qyid": "2024080212130193375040",
+			// 		"appid": "wxefd01d28b7d41071",
+			// 		"weburl": "http://211.149.199.65:5007/",
+			// 		"openid": "oI0gr6inthqe1tc9Ex6nwpvGQ49E"
+			// 	},
+			// 	"Message": "操作成功",
+			// 	"OtherData": null
+			// }
+			// 临时调试 --end--
+			
+			if (res.Data.userid) {
+				storage.set('wx_openid', res.Data.openid)
+				storage.set('wx_userid', res.Data.userid)
+				storage.set('wx_qyid', res.Data.qyid)
+				storage.set('wx_qym', res.Data.qym)
+
+				// 如果目前用户是企业商,则拼写企业分销码
+				if (res.Data.qyid) {
+					let fxm =
+						`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${res.Data.appid}&redirect_uri=${res.Data.weburl}?param=pages/index/index,shopping,${res.Data.qym}&response_type=code&scope=snsapi_base&state=STATE`
+					storage.set('wx_fxm', fxm)
+				} else {
+					storage.set('wx_fxm', '')
+				}
+
+				// 获取用户基础信息
+				await getCustomerData()
+				resolve(true)
+			} else {
+				resolve(false)
+			}
+		} catch (err) {
+			resolve(false)
+		}
+	})
+
+}
+
+//  获取/更新 用户基础信息 
+const getCustomerData = async () => {
+	console.log('更新info信息', )
+	return new Promise(async (resolve, reject) => {
+		// 获取客户信息
+		const resCustomer = await getCustomer({
+			'customerid.value': storage.get('wx_userid')
+		})
+		console.log('获取客户信息')
+		if (resCustomer.Data) {
+			storage.set('wx_userinfo', resCustomer.Data.Data[0])
+		}
+
+		// 如果客户是代理商 获取代理商信息
+		if (resCustomer.Data.Data[0]?.customertype == '代理商') {
+			const resAgents = await getAgents({
+				'agentsid.value': storage.get('wx_qyid')
+			})
+			console.log('获取代理商信息', )
+			if (resAgents.Data) {
+				storage.set('wx_dlsinfo', resAgents.Data.Data[0])
+			}
+		}
+
+		if (storage.get('wx_v') === 'activation') {
+			// 获取用户 与盒子 信息
+			const resYhinfo = await getYhinfo({
+				'yhinfocustomerid.value': storage.get('wx_userid'),
+				'yhinfoopenid.value': storage.get('wx_openid')
+			})
+			console.log('获取用户与盒子信息', )
+			if (resCustomer.Data) {
+				storage.set('wx_yhinfo', resYhinfo.Data.Data)
+			}
+		} else {
+			// 获取用户 与订单 信息
+			const resYhinfo = await getUserDdinfo({
+				'yhinfocustomerid.value': storage.get('wx_userid'),
+				'yhinfoopenid.value': storage.get('wx_openid')
+			})
+			console.log('获取用户与订单信息', )
+			if (resCustomer.Data) {
+				storage.set('wx_yhinfo', resYhinfo.Data.Data)
+			}
+		}
+
+
+		resolve(true)
+	})
+
+}
+
+// 跳转用户端判断
+const routerNavTo = async (data) => {
+	console.log('开始跳转')
+	if (data?.versions === 'enterprise') {
+		console.log('******************企业注册页面******************')
+		setTimeout(() => {
+			this.$navTo('pages/firm/index')
+		}, 2000)
+	} else {
+		console.log('******************商场选购页面******************')
+		setTimeout(() => {
+			this.$navTo('pages/index/index')
+		}, 2000)
+	}
+}
+
+/**
+ * 获取商城基础信息
+ * 有缓存的情况下返回缓存, 没有缓存从后端api获取
+ */
+const data = (isCache = true) => {
+	return new Promise((resolve, reject) => {
+		const cacheData = getStorage()
+		if (isCache && cacheData) {
+			resolve(cacheData)
+		} else {
+			// length of data
+		}
+	})
+}
+
+// 获取商城基本信息
+const storeInfo = () => {
+	return new Promise((resolve, reject) => {
+		data().then(data => resolve(data.storeInfo))
+	})
+}
+
+// 获取H5端访问地址
+const h5Url = () => {
+	return new Promise((resolve, reject) => {
+		data().then(data => {
+			const h5Url = data.clientData.h5.setting.baseUrl
+			resolve(h5Url)
+		})
+	})
+}
+
+export default {
+	data,
+	storeInfo,
+	h5Url,
+	getApiData,
+	getCustomerData,
+}

+ 36 - 0
hn-shop-uniapp/components/add-cart-btn/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <view class="add-cart" @click.stop="handleAddCart">
+    <text class="icon iconfont" :class="[`icon-jiagou${btnStyle}`]"></text>
+  </view>
+</template>
+
+<script>
+  export default {
+    props: {
+      // 购物车按钮样式 1 2 3
+      btnStyle: {
+        type: Number,
+        default: 1
+      },
+    },
+    data() {
+      return {
+        value: false,
+        goodsInfo: {}
+      }
+    },
+    methods: {
+      handleAddCart() {
+        this.$emit('handleAddCart')
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .add-cart {
+    font-size: 38rpx;
+    padding: 0 20rpx;
+    color: $main-bg;
+  }
+</style>

+ 178 - 0
hn-shop-uniapp/components/add-cart-popup/index.vue

@@ -0,0 +1,178 @@
+<template>
+  <goods-sku-popup :modelValue="visible" @input="onChangeValue" border-radius="20" :localdata="localdata" :mode="2" :maskCloseAble="true"
+    :priceColor="appTheme.mainBg" :buyNowBackgroundColor="appTheme.mainBg" :addCartColor="appTheme.viceText" :addCartBackgroundColor="appTheme.viceBg"
+    :activedStyle="{ color: appTheme.mainBg, borderColor: appTheme.mainBg, backgroundColor: activedBtnBackgroundColor }"
+    @add-cart="addCart" buyNowText="立即购买" />
+</template>
+
+<script>
+  import { setCartTotalNum } from '@/core/app'
+  import { hex2rgba } from '@/utils/color'
+  import { SpecTypeEnum } from '@/common/enum/goods'
+  import * as CartApi from '@/api/cart'
+  import * as GoodsApi from '@/api/goods'
+  import GoodsSkuPopup from '@/components/goods-sku-popup'
+
+  export default {
+    components: {
+      GoodsSkuPopup
+    },
+    props: {
+      // 购物车按钮样式 1 2 3
+      btnStyle: {
+        type: Number,
+        default: 1
+      },
+    },
+    data() {
+      return {
+        // 是否显示
+        visible: false,
+        // 主商品信息
+        goods: {},
+        // SKU商品信息
+        localdata: {}
+      }
+    },
+    computed: {
+      // 规格按钮选中时的背景色
+      activedBtnBackgroundColor() {
+        return hex2rgba(this.appTheme.mainBg, 0.1)
+      }
+    },
+    methods: {
+
+      // 加入购物车事件
+      async handle(goods) {
+        this.goods = goods
+        if (goods.spec_type == SpecTypeEnum.SINGLE.value) {
+          this.singleEvent()
+        }
+        if (goods.spec_type == SpecTypeEnum.MULTI.value) {
+          this.multiEvent()
+        }
+      },
+
+      // 单规格商品事件
+      singleEvent() {
+        const { goods } = this
+        this.addCart({
+          goods_id: goods.goods_id,
+          goods_sku_id: '0',
+          buy_num: 1
+        })
+      },
+
+      // 多规格商品事件
+      async multiEvent() {
+        const app = this
+        const { goods } = app
+        // 获取商品的规格信息
+        const { data: { specData } } = await GoodsApi.specData(goods.goods_id)
+        goods.skuList = specData.skuList
+        goods.specList = specData.specList
+        // 整理SKU商品信息
+        app.localdata = {
+          _id: goods.goods_id,
+          name: goods.goods_name,
+          goods_thumb: goods.goods_image,
+          sku_list: app.getSkuList(),
+          spec_list: app.getSpecList()
+        }
+        this.visible = true
+      },
+
+      // 监听组件显示隐藏
+      onChangeValue(val) {
+        this.visible = val
+      },
+
+      // 整理商品SKU列表 (多规格)
+      getSkuList() {
+        const app = this
+        const { goods: { goods_name, goods_image, skuList } } = app
+        const skuData = []
+        skuList.forEach(item => {
+          skuData.push({
+            _id: item.id,
+            goods_sku_id: item.goods_sku_id,
+            goods_id: item.goods_id,
+            goods_name: goods_name,
+            image: item.image_url ? item.image_url : goods_image,
+            price: item.goods_price * 100,
+            stock: item.stock_num,
+            spec_value_ids: item.spec_value_ids,
+            sku_name_arr: app.getSkuNameArr(item.spec_value_ids)
+          })
+        })
+        return skuData
+      },
+
+      // 获取sku记录的规格值列表
+      getSkuNameArr(specValueIds) {
+        const app = this
+        const defaultData = ['默认']
+        const skuNameArr = []
+        if (specValueIds) {
+          specValueIds.forEach((valueId, groupIndex) => {
+            const specValueName = app.getSpecValueName(valueId, groupIndex)
+            skuNameArr.push(specValueName)
+          })
+        }
+        return skuNameArr.length ? skuNameArr : defaultData
+      },
+
+      // 获取指定的规格值名称
+      getSpecValueName(valueId, groupIndex) {
+        const app = this
+        const { goods: { specList } } = app
+        const res = specList[groupIndex].valueList.find(specValue => {
+          return specValue.spec_value_id == valueId
+        })
+        return res.spec_value
+      },
+
+      // 整理规格数据 (多规格)
+      getSpecList() {
+        const { goods: { specList } } = this
+        const defaultData = [{ name: '默认', list: [{ name: '默认' }] }]
+        const specData = []
+        specList.forEach(group => {
+          const children = []
+          group.valueList.forEach(specValue => {
+            children.push({ name: specValue.spec_value })
+          })
+          specData.push({
+            name: group.spec_name,
+            list: children
+          })
+        })
+        return specData.length ? specData : defaultData
+      },
+
+      // 加入购物车按钮
+      addCart(selectShop) {
+        const app = this
+        const { goods_id, goods_sku_id, buy_num } = selectShop
+        CartApi.add(goods_id, goods_sku_id, buy_num)
+          .then(result => {
+            // 显示成功
+            app.$toast(result.message, 1000, false)
+            // 隐藏当前弹窗
+            app.onChangeValue(false)
+            // 购物车商品总数量
+            const cartTotal = result.data.cartTotal
+            // 缓存购物车数量
+            setCartTotalNum(cartTotal)
+            // 传递给父级
+            app.$emit('addCart', cartTotal)
+          })
+      }
+
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 57 - 0
hn-shop-uniapp/components/avatar-image/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <view class="avatar-image">
+    <image class="image"
+      :style="{ width: `${width}rpx`, height: `${width}rpx`, borderWidth: `${borderWidth}rpx`, borderColor: borderColor }"
+      :src="url ? url : '/static/default-avatar.png'"></image>
+  </view>
+</template>
+
+<script>
+  export default {
+
+    /**
+     * 组件的属性列表
+     * 用于组件自定义设置
+     */
+    props: {
+      url: {
+        type: String,
+        default: ''
+      },
+      width: {
+        type: Number,
+        default: 90
+      },
+      borderWidth: {
+        type: Number,
+        default: 0
+      },
+      borderColor: {
+        type: String,
+        default: '#000000'
+      }
+    },
+
+    data() {
+      return {
+
+      }
+    },
+
+    methods: {
+
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .avatar-image {
+    .image {
+      display: block;
+      width: 60rpx;
+      height: 60rpx;
+      border-radius: 50%;
+      border-style: solid;
+    }
+  }
+</style>

+ 183 - 0
hn-shop-uniapp/components/countdown/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <view v-if="date" class="count-down">
+    <view :class="[`${theme}-theme`, `separator-${separator}`]">
+      <!-- <block v-if="dynamic.day != '00'">
+        <text class="dynamic-value">{{ dynamic.day }}</text>
+        <text class="separator">{{ separatorText.day }}</text>
+      </block> -->
+      <block v-if="dynamic.day > 0">
+        <text class="dynamic-value">{{ dynamic.day }}</text>
+        <text class="separator">{{ separatorText.day }}</text>
+      </block>
+      <text class="dynamic-value" :style="{ backgroundColor: customBgColor }">{{ dynamic.hou }}</text>
+      <text class="separator">{{ separatorText.hou }}</text>
+      <text class="dynamic-value" :style="{ backgroundColor: customBgColor }">{{ dynamic.min }}</text>
+      <text class="separator">{{ separatorText.min }}</text>
+      <text class="dynamic-value" :style="{ backgroundColor: customBgColor }">{{ dynamic.sec }}</text>
+      <text class="separator">{{ separatorText.sec }}</text>
+    </view>
+  </view>
+</template>
+
+<script>
+  import { formatDate } from '@/utils/util';
+
+  export default {
+    props: {
+      // 截止的时间
+      date: {
+        type: String,
+        default: ''
+      },
+      // 分隔符, colon为英文冒号,zh为中文
+      separator: {
+        type: String,
+        default: 'zh'
+      },
+      // 组件主题样式, text为纯文本,custom为带背景色
+      theme: {
+        type: String,
+        default: 'text'
+      },
+      // custom样式的背景色
+      customBgColor: {
+        type: String,
+        default: '#252525'
+      }
+    },
+    data() {
+      return {
+        // 倒计时数据
+        dynamic: {
+          day: '0',
+          hou: '00',
+          min: '00',
+          sec: '00'
+        },
+        // 分隔符文案
+        separatorText: {
+          day: '天',
+          hou: '时',
+          min: '分',
+          sec: '秒'
+        }
+      };
+    },
+    created() {
+      // 分隔符文案
+      this.setSeparatorText();
+      // 开始倒计时
+      this.onTime();
+    },
+    methods: {
+      // 分隔符文案
+      setSeparatorText() {
+        const sText = this.separatorText;
+        if (this.separator === 'colon') {
+          sText.day = ':'
+          sText.hou = sText.min = ':'
+          sText.sec = ''
+        }
+        this.separatorText = sText
+      },
+
+      // 开始倒计时
+      onTime(deep = 0) {
+        const app = this;
+        const dynamic = {};
+
+        // 获取当前时间,同时得到活动结束时间数组
+        const newTime = new Date().getTime();
+        // 对结束时间进行处理渲染到页面
+        const endTime = new Date(formatDate(app.date)).getTime();
+
+        // 如果活动未结束,对时间进行处理
+        if (endTime - newTime <= 0) {
+          return false;
+        }
+
+        const diffTime = (endTime - newTime) / 1000;
+        // 获取时、分、秒
+        const day = parseInt(diffTime / 86400),
+          hou = parseInt((diffTime % 86400) / 3600),
+          min = parseInt(((diffTime % 86400) % 3600) / 60),
+          sec = parseInt(((diffTime % 86400) % 3600) % 60);
+        dynamic.day = day;
+        dynamic.hou = app.timeFormat(hou);
+        dynamic.min = app.timeFormat(min);
+        dynamic.sec = app.timeFormat(sec);
+
+        // 渲染,然后每隔一秒执行一次倒计时函数
+        app.dynamic = dynamic;
+        // 判断倒计时是否结束
+        const isEnd = app.isEnd();
+        // 结束后执行回调函数
+        if (isEnd) {
+          deep > 0 && app.$emit('finish');
+        }
+        // 重复执行
+        if (!isEnd) {
+          setTimeout(() => {
+            app.onTime(++deep);
+          }, 100);
+        }
+      },
+
+      // 判断倒计时是否结束
+      isEnd() {
+        const { dynamic } = this;
+        return dynamic.day == '00' && dynamic.hou == '00' && dynamic.min == '00' && dynamic.sec == '00';
+      },
+
+      // 小于10的格式化函数
+      timeFormat(value) {
+        return value < 10 ? '0' + value : value;
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .item {
+    display: inline-block;
+    width: 22px;
+    margin-right: 5px;
+    color: #fff;
+    font-size: 12px;
+    text-align: center;
+    background-color: #1989fa;
+    border-radius: 2px;
+  }
+
+  .separator {
+    padding: 0 2rpx;
+  }
+
+  // 纯文本主题
+  .text-theme {
+
+    // 冒号分隔符
+    .separator-colon .separator {
+      padding: 0 5rpx;
+    }
+
+    .dynamic-value {
+      background: none !important;
+    }
+  }
+
+  // 背景主题
+  .custom-theme {
+    .dynamic-value {
+      background: #252525;
+      color: #fff;
+      padding: 2rpx 8rpx;
+      line-height: 40rpx;
+      border-radius: 8rpx;
+    }
+
+    .separator {
+      padding: 0 6rpx;
+    }
+  }
+</style>

+ 63 - 0
hn-shop-uniapp/components/customer-btn/index.vue

@@ -0,0 +1,63 @@
+<template>
+  <view v-if="isShow">
+    <!-- 微信小程序端、H5端、APP端 -->
+    <!-- #ifdef MP-WEIXIN || H5 || APP-PLUS -->
+    <button
+      class="btn-normal"
+      :open-type="setting.provider == 'mpwxkf' ? 'contact' : ''"
+      :show-message-card="showCard"
+      :send-message-path="cardPath"
+      :send-message-title="cardTitle"
+      :send-message-img="cardImage"
+      @click="handleContact()"
+    >
+      <slot></slot>
+    </button>
+    <!-- #endif -->
+  </view>
+</template>
+
+<script>
+import SettingKeyEnum from '@/common/enum/setting/Key'
+import SettingModel from '@/common/model/Setting'
+
+export default {
+  props: {
+    // 是否显示消息卡片
+    showCard: {
+      Type: Boolean,
+      default: false
+    },
+    // 消息卡片标题
+    cardTitle: {
+      Type: String,
+      default: ''
+    },
+    // 消息卡片图片
+    cardImage: {
+      Type: String,
+      default: ''
+    },
+    // 消息卡片点击跳转的路径
+    cardPath: {
+      Type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      isShow: false,
+      setting: {}
+    }
+  },
+  async created() {
+    // 是否显示在线客服按钮
+    // this.isShow = await SettingModel.isShowCustomerBtn()
+    // 商城客服设置
+    // this.setting = await SettingModel.item(SettingKeyEnum.CUSTOMER.value, true)
+  },
+  methods: {}
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 67 - 0
hn-shop-uniapp/components/empty/index.vue

@@ -0,0 +1,67 @@
+<template>
+  <view v-if="!isLoading" class="empty-content" :style="customStyle">
+    <view class="empty-icon">
+      <image class="image" src="/static/empty.png" mode="scaleToFill"></image>
+    </view>
+    <view class="tips">{{ tips }}</view>
+    <slot name="slot"></slot>
+  </view>
+</template>
+
+<script>
+  export default {
+
+    /**
+     * 组件的属性列表
+     * 用于组件自定义设置
+     */
+    props: {
+      // 正在加载
+      isLoading: {
+        type: Boolean,
+        default: false
+      },
+      // 自定义样式
+      customStyle: {
+        type: Object,
+        default () {
+          return {}
+        }
+      },
+      // 提示的问题
+      tips: {
+        type: String,
+        default: '亲,暂无相关数据'
+      }
+    },
+
+    data() {
+      return {}
+    },
+
+    methods: {
+
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .empty-content {
+    box-sizing: border-box;
+    width: 100%;
+    padding: 140rpx 50rpx;
+    text-align: center;
+
+    .tips {
+      font-size: 28rpx;
+      color: gray;
+      margin: 50rpx 0;
+    }
+
+    .empty-icon .image {
+      width: 280rpx;
+      height: 184rpx;
+    }
+
+  }
+</style>

+ 1377 - 0
hn-shop-uniapp/components/goods-sku-popup/index.vue

@@ -0,0 +1,1377 @@
+<template>
+  <view class="goods-sku-popup" catchtouchmove="true" :class="valueCom && complete ? 'show' : 'none'" @touchmove.stop.prevent="moveHandle"
+    @click.stop="stop">
+    <!-- 页面内容开始 -->
+    <view class="mask" @click="close('mask')"></view>
+    <view class="layer attr-content" :class="{'safe-area-inset-bottom':safeAreaInsetBottom }"
+      :style="{ borderRadius: borderRadius + 'rpx ' + borderRadius + 'rpx 0 0' }">
+      <view class="specification-wrapper">
+        <scroll-view class="specification-wrapper-content" scroll-y="true">
+          <view class="specification-header">
+            <view class="specification-left">
+              <image class="product-img" :src="selectShop.image ? selectShop.image : goodsInfo[goodsThumbName]"
+                :style="{ backgroundColor: goodsThumbBackgroundColor }" mode="aspectFill" @click="previewImage"></image>
+            </view>
+            <view class="specification-right">
+              <view class="price-content" :style="{ color: themeColorFn('priceColor') }">
+                <text class="sign">¥</text>
+                <text class="price" :class="priceCom.length > 16 ? 'price2' : ''">{{ priceCom }}</text>
+              </view>
+              <view class="inventory" v-if="!hideStock">{{ stockText }}:{{ stockCom }}</view>
+              <view class="inventory" v-else></view>
+              <view class="choose" v-show="isManyCom">
+                <text v-if="!selectArr.every(val => val == '')">已选:{{ selectArr.join(' ') }}</text>
+              </view>
+            </view>
+          </view>
+
+          <view class="specification-content">
+            <view v-show="isManyCom" class="specification-item" v-for="(item, index1) in goodsInfo[specListName]" :key="index1">
+              <view class="item-title">{{ item.name }}</view>
+              <view class="item-wrapper">
+                <view class="item-content" v-for="(item_value, index2) in item.list" :key="index2"
+                  :class="[item_value.ishow ? '' : 'noactived', subIndex[index1] == index2 ? 'actived' : '']" :style="[
+										item_value.ishow ? '' : themeColorFn('disableStyle'),
+										item_value.ishow ? themeColorFn('btnStyle') : '',
+										subIndex[index1] == index2 ? themeColorFn('activedStyle') : ''
+									]" @click="skuClick(item_value, index1, index2)">
+                  {{ item_value.name }}
+                </view>
+              </view>
+            </view>
+            <view class="number-box-view">
+              <view style="flex: 1;">数量</view>
+              <view style="flex: 4;text-align: right;">
+                <number-box v-model="selectNum" :min="minBuyNum || 1" :max="maxBuyNumCom" :step="stepBuyNum || 1"
+                  :step-strictly="stepStrictly" :positive-integer="true" @change="numChange" />
+              </view>
+            </view>
+          </view>
+        </scroll-view>
+        <view class="close" @click="close('close')" v-if="showClose != false">
+          <image class="close-item" :src="closeImage"></image>
+        </view>
+      </view>
+
+      <view class="btn-wrapper" v-if="outFoStock || mode == 4">
+        <view class="sure" style="color:#ffffff;background-color:#cccccc">{{ noStockText }}</view>
+      </view>
+      <view class="btn-wrapper" v-else-if="mode == 1">
+        <view class="sure add-cart" style="border-radius:38rpx 0rpx 0rpx 38rpx;" :style="{
+						color: themeColorFn('addCartColor'),
+						backgroundColor: themeColorFn('addCartBackgroundColor')
+					}" @click="addCart">
+          {{ addCartText }}
+        </view>
+
+        <view class="sure" style="border-radius:0rpx 38rpx 38rpx 0rpx;" :style="{
+						color: themeColorFn('buyNowColor'),
+						backgroundColor: themeColorFn('buyNowBackgroundColor')
+					}" @click="buyNow">
+          {{ buyNowText }}
+        </view>
+      </view>
+      <view class="btn-wrapper" v-else-if="mode == 2">
+        <view class="sure add-cart" :style="{
+						color: themeColorFn('addCartColor'),
+						backgroundColor: themeColorFn('addCartBackgroundColor')
+					}" @click="addCart">
+          {{ addCartText }}
+        </view>
+      </view>
+      <view class="btn-wrapper" v-else-if="mode == 3">
+        <view class="sure" :style="{
+						color: themeColorFn('buyNowColor'),
+						backgroundColor: themeColorFn('buyNowBackgroundColor')
+					}" @click="buyNow">
+          {{ buyNowText }}
+        </view>
+      </view>
+    </view>
+    <!-- 页面内容结束 -->
+  </view>
+</template>
+
+<script>
+  import NumberBox from './number-box'
+  var vk; // vk依赖
+  var goodsCache = {}; // 本地商品缓存
+  export default {
+    name: 'GoodsSkuPopup',
+    components: { NumberBox },
+    emits: ['update:modelValue', 'input', 'update-goods', 'open', 'close', 'add-cart', 'buy-now', 'cart', 'buy', 'num-change'],
+    props: {
+      // true 组件显示 false 组件隐藏
+      value: {
+        Type: Boolean,
+        default: false
+      },
+      modelValue: {
+        Type: Boolean,
+        default: false
+      },
+      // vk云函数路由模式参数开始-----------------------------------------------------------
+      // 商品id
+      goodsId: {
+        Type: String,
+        default: ''
+      },
+      // vk路由模式框架下的云函数地址
+      action: {
+        Type: String,
+        default: ''
+      },
+      // vk云函数路由模式参数结束-----------------------------------------------------------
+      // 该商品已抢完时的按钮文字
+      noStockText: {
+        Type: String,
+        default: '该商品已抢完'
+      },
+      // 库存文字
+      stockText: {
+        Type: String,
+        default: '库存'
+      },
+      // 商品表id的字段名
+      goodsIdName: {
+        Type: String,
+        default: '_id'
+      },
+      // sku表id的字段名
+      skuIdName: {
+        Type: String,
+        default: '_id'
+      },
+      // sku_list的字段名
+      skuListName: {
+        Type: String,
+        default: 'sku_list'
+      },
+      // spec_list的字段名
+      specListName: {
+        Type: String,
+        default: 'spec_list'
+      },
+      // 库存的字段名 默认 stock
+      stockName: {
+        Type: String,
+        default: 'stock'
+      },
+      // sku组合路径的字段名
+      skuArrName: {
+        Type: String,
+        default: 'sku_name_arr'
+      },
+      // 默认单规格时的规格组名称
+      defaultSingleSkuName: {
+        Type: String,
+        default: '默认'
+      },
+      // 模式 1:都显示  2:只显示购物车 3:只显示立即购买 4:显示缺货按钮 默认 1
+      mode: {
+        Type: Number,
+        default: 1
+      },
+      // 点击遮罩是否关闭组件 true 关闭 false 不关闭 默认true
+      maskCloseAble: {
+        Type: Boolean,
+        default: true
+      },
+      // 顶部圆角值
+      borderRadius: {
+        Type: [String, Number],
+        default: 0
+      },
+      // 商品缩略图字段名(未选择sku时)
+      goodsThumbName: {
+        Type: [String],
+        default: 'goods_thumb'
+      },
+      // 商品缩略图背景颜色,如#999999
+      goodsThumbBackgroundColor: {
+        Type: String,
+        default: '#999999'
+      },
+      // 最小购买数量 默认 1
+      minBuyNum: {
+        Type: [Number, String],
+        default: 1
+      },
+      // 最大购买数量 默认 100000
+      maxBuyNum: {
+        Type: [Number, String],
+        default: 100000
+      },
+      // 步进器步长 默认 1
+      stepBuyNum: {
+        Type: [Number, String],
+        default: 1
+      },
+      // 是否只能输入 step 的倍数
+      stepStrictly: {
+        Type: Boolean,
+        default: false
+      },
+      // 自定义获取商品信息的函数,支付宝小程序不支持该属性,请使用localdata属性
+      customAction: {
+        Type: [Function],
+        default: null
+      },
+      // 本地数据源
+      localdata: {
+        type: Object
+      },
+      // 价格的字体颜色
+      priceColor: {
+        Type: String
+      },
+      // 立即购买按钮的文字
+      buyNowText: {
+        Type: String,
+        default: '立即购买'
+      },
+      // 立即购买按钮的字体颜色
+      buyNowColor: {
+        Type: String
+      },
+      // 立即购买按钮的背景颜色
+      buyNowBackgroundColor: {
+        Type: String
+      },
+      // 加入购物车按钮的文字
+      addCartText: {
+        Type: String,
+        default: '加入购物车'
+      },
+      // 加入购物车按钮的字体颜色
+      addCartColor: {
+        Type: String
+      },
+      // 加入购物车按钮的背景颜色
+      addCartBackgroundColor: {
+        Type: String
+      },
+      // 不可点击时,按钮的样式
+      disableStyle: {
+        Type: Object,
+        default: null
+      },
+      // 按钮点击时的样式
+      activedStyle: {
+        Type: Object,
+        default: null
+      },
+      // 按钮常态的样式
+      btnStyle: {
+        Type: Object,
+        default: null
+      },
+      // 是否显示右上角关闭按钮
+      showClose: {
+        Type: Boolean,
+        default: true
+      },
+      // 关闭按钮的图片地址 https://img.alicdn.com/imgextra/i1/121022687/O1CN01ImN0O11VigqwzpLiK_!!121022687.png
+      closeImage: {
+        Type: String,
+        default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAqCAYAAADFw8lbAAAEyUlEQVR42sSZeWwNURTGp4OqtBo7sSXELragdkpQsRRJ1Zr4hyJiJ9YgxNIg1qANiT+E1i5IY0kVVWtQEbuEKLFGUSH27/ANN5PXmTvzupzkl/tm8t6b7517lnvvC0lKSjJ8WmnQAUSDFqABqALKgl8gD7wE90E2SAeXwFf1SxISErQeVtKHwCgwFsSDSIf3hYFKoCkYDBaDdyAViHdueHmoF6FtwDLQ23b/E7gM7oIcejIERIDaoBFoC8qA8mA8SQNz6W1XC9GY+nCQCCYAk/c+gF0gBZwH312+IxR0BCPBUIaH2A+wHsxHCHxx+gLT5QGN6a2JfG8uvVCDws9oiDQYlxkMGfHyQvARlADTwcXk5OT6foV2kS8ATXidymlcyen1a/Jjl9IJh3hPkjELYqO8Cu0KjjNZvtETw5jFBWXPmGSTGQKSeOn5iQ0kVLL0CINfPNcPbDMKyRCbGzEMBJ+ZD8cChYFdqGTqfsWT8otPGoVsEHsMwxDFs3shNsxJ6BrQ0Po8OGUUkVHsNCVml+cntB1jUWwn2GEUsTEMrASbDK+2CCQ0kYX6nfLLisMmKqUr0S60M+jG10vAm+JSCa8+x7CKlzHwaktV6DiObzUzPJIxFO1BQ12wGtTReO9GetVgY/kjNJzZbcWmTjHfxw51AsRqvL8eOAtmsJuFu3g1l+1ZLB5eDTVZ3K0P7tL0TkWOpSg61kVkBtuuNRthGs+wtJST5aQI7cEbkkRXNYVKgX6kIdYuUhYzMQwxN8tiExCLFqHNeSF9/aem0BzGp5PYQCJ7c/Gsk1RfuSD6U1dNpcDf9ZigTmKbMRZ9iVTsHscGJluW2FMf1SSQWGnBmaB6kCJVTVVNJZE++Cx9drEllS1KMCINpURFmEbBWA63Fz9s95cGIdJgp/zXmT4pZcOvSUzuZttTbblmnc3PIjjmidDXvKgdhMh0JdbzuCjWrbNOVovjS5P7bkPJ/mBESkz2BO0166ybNeJ431S2q+01NntuIq3E0amzjiZtk9tssWyTDzO4525bACK9NAUn68TtkNhpEXpOSagRml+S6iLSSeweHv242Qhl13rRyvoDvDlKyTQny/ZQJ+1iH7vVbEx7OR5UiKVIO7VicgvHCtwrudloMIV7/0uadVYW57O4Wvvi8v4pymlKkrpwvsDeLLZAY2pkwbAB3PSQfC+4cH7l4k1ZH8zkZRq8ecO+Z5rN40JJqnXFuGfaxPCTLjcn0OZOpnArXw8HY4paIbw5CcMgXq6HN2/mt6+XGLrN15tBryIUGavMpCTrfKcDCKkAceA9S8nhAOehhSUyhXpkBxxnP4YM1InugP7cBkjBPcqVUWFYCEROxXiQz5JlXV+IfKh7mpfJac+lZ6V87QXVClBkTc7YWsWTPSDyitfzUTlJlj8TbvE6jluDOdwZ+jX57GLO3ADeuyZrDYi86vV81FD2UVGsmT+5Zl0BnkhoseOEaogL46pqO4v/IqUEyalIR4h85BgjHv6+aUWRMbb7EstX6O0cpT1Gco0ry8fWygLDMjmDnQeBt3Qe7uVfkeugDwVLcsVzGsuwLXbV+I63XNAkG5r/hvgRqgqWs6pJPKrsbvz/Q6yyun0w/h6lP+BnzrCpfPMT2L8FGAA7k1GZ/vnaqAAAAABJRU5ErkJggg=='
+      },
+      // 是否隐藏库存显示
+      hideStock: {
+        Type: Boolean,
+        default: false
+      },
+      // 颜色主题
+      theme: {
+        Type: String,
+        default: 'default'
+      },
+      // 请求中的提示
+      actionTips: {
+        Type: String,
+        default: '请求中...'
+      },
+      // 默认选中的SKU
+      defaultSelect: {
+        Type: Object
+      },
+      // 是否使用缓存
+      useCache: {
+        Type: Boolean,
+        default: true
+      },
+      /**
+       * 默认商品,设置该值可快速展示商品
+       * 逻辑: 先展示 defaultGoods 信息,再取数据库,再更新页面(通常为更新库存)
+       */
+      defaultGoods: {
+        Type: Object
+      },
+      /**
+       * 金额是否需要除以100
+       * 1:金额会除以100
+       * 0:金额不会除以100
+       */
+      amountType: {
+        Type: Number,
+        default: 1
+      },
+      // 每次选择完SKU后,购买数量归1,如果有最小购买数量,则设置为最小购买数量
+      selectedInit: {
+        Type: Boolean,
+        default: false
+      },
+      // 是否开启底部安全区适配,默认true
+      safeAreaInsetBottom: {
+        Type: Boolean,
+        default: true
+      },
+    },
+    data() {
+      return {
+        complete: false, // 组件是否加载完成
+        goodsInfo: {}, // 商品信息
+        isShow: false, // true 显示 false 隐藏
+        initKey: true, // 是否需要初始化 true 是 false 否
+        // #ifndef MP-BAIDU
+        shopItemInfo: {}, // 存放要和选中的值进行匹配的数据(因百度小程序setData不支持中文字段,故不编译shopItemInfo变量)
+        // #endif
+        selectArr: [], // 存放被选中的值
+        subIndex: [], // 是否选中 因为不确定是多规格还是单规格,所以这里定义数组来判断
+        selectShop: {}, // 存放最后选中的商品
+        selectNum: this.minBuyNum || 1, // 选中数量
+        outFoStock: false, // 是否全部sku都缺货
+        openTime: 0,
+        themeColor: {
+          // 默认主题
+          default: {
+            priceColor: 'rgb(254, 86, 10)',
+            buyNowColor: '#ffffff',
+            buyNowBackgroundColor: 'rgb(254, 86, 10)',
+            addCartColor: '#ffffff',
+            addCartBackgroundColor: 'rgb(255, 148, 2)',
+            btnStyle: {
+              color: '#333333',
+              borderColor: '#f4f4f4',
+              backgroundColor: '#ffffff'
+            },
+            activedStyle: {
+              color: 'rgb(254, 86, 10)',
+              borderColor: 'rgb(254, 86, 10)',
+              backgroundColor: 'rgba(254,86,10,0.1)'
+            },
+            disableStyle: {
+              color: '#c3c3c3',
+              borderColor: '#f6f6f6',
+              backgroundColor: '#f6f6f6'
+            }
+          },
+          // 红黑主题
+          'red-black': {
+            priceColor: 'rgb(255, 68, 68)',
+            buyNowColor: '#ffffff',
+            buyNowBackgroundColor: 'rgb(255, 68, 68)',
+            addCartColor: '#ffffff',
+            addCartBackgroundColor: 'rgb(85, 85, 85)',
+            activedStyle: {
+              color: 'rgb(255, 68, 68)',
+              borderColor: 'rgb(255, 68, 68)',
+              backgroundColor: 'rgba(255,68,68,0.1)'
+            }
+          },
+          // 黑白主题
+          'black-white': {
+            priceColor: 'rgb(47, 47, 52)',
+            buyNowColor: '#ffffff',
+            buyNowBackgroundColor: 'rgb(47, 47, 52)',
+            addCartColor: 'rgb(47, 47, 52)',
+            addCartBackgroundColor: 'rgb(235, 236, 242)',
+            // btnStyle:{
+            // 	color:"rgb(47, 47, 52)",
+            // 	borderColor:"rgba(235,236,242,0.5)",
+            // 	backgroundColor:"rgba(235,236,242,0.5)",
+            // },
+            activedStyle: {
+              color: 'rgb(47, 47, 52)',
+              borderColor: 'rgba(47,47,52,0.12)',
+              backgroundColor: 'rgba(47,47,52,0.12)'
+            }
+          },
+          // 咖啡色主题
+          coffee: {
+            priceColor: 'rgb(195, 167, 105)',
+            buyNowColor: '#ffffff',
+            buyNowBackgroundColor: 'rgb(195, 167, 105)',
+            addCartColor: 'rgb(195, 167, 105)',
+            addCartBackgroundColor: 'rgb(243, 238, 225)',
+            activedStyle: {
+              color: 'rgb(195, 167, 105)',
+              borderColor: 'rgb(195, 167, 105)',
+              backgroundColor: 'rgba(195, 167, 105,0.1)'
+            }
+          },
+          // 浅绿色主题
+          green: {
+            priceColor: 'rgb(99, 190, 114)',
+            buyNowColor: '#ffffff',
+            buyNowBackgroundColor: 'rgb(99, 190, 114)',
+            addCartColor: 'rgb(99, 190, 114)',
+            addCartBackgroundColor: 'rgb(225, 244, 227)',
+            activedStyle: {
+              color: 'rgb(99, 190, 114)',
+              borderColor: 'rgb(99, 190, 114)',
+              backgroundColor: 'rgba(99, 190, 114,0.1)'
+            }
+          }
+        }
+      };
+    },
+    created() {
+      let that = this;
+      vk = that.vk;
+      if (that.valueCom) {
+        that.open();
+      }
+    },
+    mounted() {},
+    methods: {
+      // 初始化
+      init(notAutoClick) {
+        let that = this;
+        // 清空之前的数据
+        that.selectArr = [];
+        that.subIndex = [];
+        that.selectShop = {};
+        that.selectNum = that.minBuyNum || 1;
+        that.outFoStock = false;
+        that.shopItemInfo = {};
+        let specListName = that.specListName;
+        that.goodsInfo[specListName].map(item => {
+          that.selectArr.push('');
+          that.subIndex.push(-1);
+        });
+        that.checkItem(); // 计算sku里面规格形成路径
+        that.checkInpath(-1); // 传-1是为了不跳过循环
+        if (!notAutoClick) that.autoClickSku(); // 自动选择sku策略
+      },
+      // 使用vk路由模式框架获取商品信息
+      findGoodsInfo(obj = {}) {
+        let that = this;
+        let { useCache } = obj;
+        if (typeof vk == 'undefined') {
+          that.toast('custom-action必须是function', 'none');
+          return false;
+        }
+        let { actionTips } = that;
+        let actionTitle = '';
+        let actionAoading = false;
+        if (actionTips !== 'custom') {
+          actionTitle = useCache ? '' : '请求中...';
+        } else {
+          actionAoading = useCache ? false : true;
+        }
+        vk.callFunction({
+          url: that.action,
+          title: actionTitle,
+          loading: actionAoading,
+          data: {
+            goods_id: that.goodsId
+          },
+          success(data) {
+            that.updateGoodsInfo(data.goodsInfo);
+            // 更新缓存
+            goodsCache[that.goodsId] = data.goodsInfo;
+            that.$emit('update-goods', data.goodsInfo);
+          },
+          fail() {
+            that.updateValue(false);
+          }
+        });
+      },
+      updateValue(value) {
+        let that = this;
+        if (value) {
+          that.$emit('open', true);
+          that.$emit('input', true);
+          that.$emit('update:modelValue', true);
+        } else {
+          that.$emit('input', false);
+          that.$emit('close', 'close');
+          that.$emit('update:modelValue', false);
+        }
+      },
+      // 更新商品信息(库存、名称、图片)
+      updateGoodsInfo(goodsInfo) {
+        let that = this;
+        // goodsInfo.sku_list.map((item, index) => {
+        // 	item.sku_name_arr = ["20ml/瓶"];
+        // });
+        let { skuListName } = that;
+        if (JSON.stringify(that.goodsInfo) === '{}' || that.goodsInfo[that.goodsIdName] !== goodsInfo[that.goodsIdName]) {
+          that.goodsInfo = goodsInfo;
+          that.initKey = true;
+        } else {
+          that.goodsInfo[skuListName] = goodsInfo[skuListName];
+        }
+        if (that.initKey) {
+          that.initKey = false;
+
+          // 是否默认选中第一个SKU
+          // 条件是 单规格商品或者多规格单SKU商品
+          const skuList = that.goodsInfo[that.skuListName]
+          const autoClickSku = !that.isManyCom ? true : skuList.length === 1
+          that.init(!autoClickSku)
+        }
+        // 更新选中sku的库存信息
+        let select_sku_info = that.getListItem(that.goodsInfo[skuListName], that.skuIdName, that.selectShop[that.skuIdName]);
+        Object.assign(that.selectShop, select_sku_info);
+        that.defaultSelectSku();
+        that.complete = true;
+      },
+      async open() {
+        let that = this;
+        that.openTime = new Date().getTime();
+        let findGoodsInfoRun = true;
+        let skuListName = that.skuListName;
+        // 先获取缓存中的商品信息
+        let useCache = false;
+        let goodsInfo = goodsCache[that.goodsId];
+        if (goodsInfo && that.useCache) {
+          useCache = true;
+          that.updateGoodsInfo(goodsInfo);
+        } else {
+          that.complete = false;
+        }
+        if (that.customAction && typeof that.customAction === 'function') {
+          try {
+            goodsInfo = await that
+              .customAction({
+                useCache,
+                goodsId: that.goodsId,
+                goodsInfo,
+                close: function() {
+                  setTimeout(function() {
+                    that.close();
+                  }, 500);
+                }
+              })
+              .catch(err => {
+                setTimeout(function() {
+                  that.close();
+                }, 500);
+              });
+          } catch (err) {
+            let { message = '' } = err;
+            if (message.indexOf('.catch is not a function') > -1) {
+              that.toast('custom-action必须返回一个Promise', 'none');
+              setTimeout(function() {
+                that.close();
+              }, 500);
+              return false;
+            }
+          }
+          // 更新缓存
+          goodsCache[that.goodsId] = goodsInfo;
+          if (goodsInfo && typeof goodsInfo == 'object' && JSON.stringify(goodsInfo) != '{}') {
+            findGoodsInfoRun = false;
+            that.updateGoodsInfo(goodsInfo);
+            that.updateValue(true);
+          } else {
+            that.toast('未获取到商品信息', 'none');
+            that.$emit('input', false);
+            return false;
+          }
+        } else if (typeof that.localdata !== 'undefined' && that.localdata !== null) {
+          goodsInfo = that.localdata;
+          if (goodsInfo && typeof goodsInfo == 'object' && JSON.stringify(goodsInfo) != '{}') {
+            findGoodsInfoRun = false;
+            that.updateGoodsInfo(goodsInfo);
+            that.updateValue(true);
+          } else {
+            that.toast('未获取到商品信息', 'none');
+            that.$emit('input', false);
+            return false;
+          }
+        } else {
+          if (findGoodsInfoRun) that.findGoodsInfo({ useCache });
+        }
+      },
+      // 监听 - 弹出层收起
+      close(s) {
+        let that = this;
+        if (new Date().getTime() - that.openTime < 400) {
+          return false;
+        }
+        if (s == 'mask') {
+          if (that.maskCloseAble !== false) {
+            that.$emit('input', false);
+            that.$emit('close', 'mask');
+            that.$emit('update:modelValue', false);
+          }
+        } else {
+          that.$emit('input', false);
+          that.$emit('close', 'close');
+          that.$emit('update:modelValue', false);
+        }
+      },
+      moveHandle() {
+        //禁止父元素滑动
+      },
+      // sku按钮的点击事件
+      skuClick(value, index1, index2) {
+        let that = this;
+        if (value.ishow) {
+          if (that.selectArr[index1] != value.name) {
+            that.$set(that.selectArr, index1, value.name);
+            that.$set(that.subIndex, index1, index2);
+          } else {
+            that.$set(that.selectArr, index1, '');
+            that.$set(that.subIndex, index1, -1);
+          }
+          that.checkInpath(index1);
+          // 如果全部选完
+          that.checkSelectShop();
+        }
+      },
+      // 检测是否已经选完sku
+      checkSelectShop() {
+        let that = this;
+        // 如果全部选完
+        if (that.selectArr.every(item => item != '')) {
+          that.selectShop = that.shopItemInfo[that.getArrayToSting(that.selectArr)];
+          let stock = that.selectShop[that.stockName];
+          if (typeof stock !== 'undefined' && that.selectNum > stock) {
+            that.selectNum = stock;
+          }
+          if (that.selectNum > that.maxBuyNum) {
+            that.selectNum = that.maxBuyNum;
+          }
+          if (that.selectNum < that.minBuyNum) {
+            that.selectNum = that.minBuyNum;
+          }
+          if (that.selectedInit) {
+            that.selectNum = that.minBuyNum || 1;
+          }
+        } else {
+          that.selectShop = {};
+        }
+      },
+      // 检查路径
+      checkInpath(clickIndex) {
+        let that = this;
+        let specListName = that.specListName;
+        //console.time('筛选可选路径需要的时间是');
+        //循环所有属性判断哪些属性可选
+        //当前选中的兄弟节点和已选中属性不需要循环
+        let specList = that.goodsInfo[specListName];
+        for (let i = 0, len = specList.length; i < len; i++) {
+          if (i == clickIndex) {
+            continue;
+          }
+          let len2 = specList[i].list.length;
+          for (let j = 0; j < len2; j++) {
+            if (that.subIndex[i] != -1 && j == that.subIndex[i]) {
+              continue;
+            }
+            let choosed_copy = [...that.selectArr];
+            that.$set(choosed_copy, i, specList[i].list[j].name);
+            let choosed_copy2 = choosed_copy.filter(item => item !== '' && typeof item !== 'undefined');
+            if (that.shopItemInfo.hasOwnProperty(that.getArrayToSting(choosed_copy2))) {
+              specList[i].list[j].ishow = true;
+            } else {
+              specList[i].list[j].ishow = false;
+            }
+          }
+        }
+        that.$set(that.goodsInfo, specListName, specList);
+        // console.timeEnd('筛选可选路径需要的时间是');
+      },
+      // 计算sku里面规格形成路径
+      checkItem() {
+        let that = this;
+        // console.time('计算有多小种可选路径需要的时间是');
+        let { stockName } = that;
+        let skuListName = that.skuListName;
+        // 去除库存小于等于0的商品sku
+        let originalSkuList = that.goodsInfo[skuListName];
+        let skuList = [];
+        let stockNum = 0;
+        originalSkuList.map((skuItem, index) => {
+          if (skuItem[stockName] > 0) {
+            skuList.push(skuItem);
+            stockNum += skuItem[stockName];
+          }
+        });
+        if (stockNum <= 0) {
+          that.outFoStock = true;
+        }
+        // 计算有多小种可选路径
+        let result = skuList.reduce(
+          (arrs, items) => {
+            return arrs.concat(
+              items[that.skuArrName].reduce(
+                (arr, item) => {
+                  return arr.concat(
+                    arr.map(item2 => {
+                      // 利用对象属性的唯一性实现二维数组去重
+                      //console.log(1,that.shopItemInfo,that.getArrayToSting([...item2, item]),item2,item,items);
+                      if (!that.shopItemInfo.hasOwnProperty(that.getArrayToSting([...item2, item]))) {
+                        that.shopItemInfo[that.getArrayToSting([...item2, item])] = items;
+                      }
+                      return [...item2, item];
+                    })
+                  );
+                },
+                [
+                  []
+                ]
+              )
+            );
+          },
+          [
+            []
+          ]
+        );
+        // console.timeEnd('计算有多小种可选路径需要的时间是');
+      },
+      getArrayToSting(arr) {
+        let str = '';
+        arr.map((item, index) => {
+          item = item.replace(/\./g, '。');
+          if (index == 0) {
+            str += item;
+          } else {
+            str += ',' + item;
+          }
+        });
+        return str;
+      },
+      // 检测sku选项是否已全部选完,且有库存
+      checkSelectComplete(obj = {}) {
+        let that = this;
+        let clickTime = new Date().getTime();
+        if (that.clickTime && clickTime - that.clickTime < 400) {
+          return false;
+        }
+        that.clickTime = clickTime;
+        let { selectShop, selectNum, stockText, stockName } = that;
+        if (!selectShop || !selectShop[that.skuIdName]) {
+          that.toast('请先选择对应规格', 'none');
+          return false;
+        }
+        if (selectNum <= 0) {
+          that.toast('购买数量必须>0', 'none');
+          return false;
+        }
+        // 判断库存
+        if (selectNum > selectShop[stockName]) {
+          that.toast(stockText + '不足', 'none');
+          return false;
+        }
+        if (typeof obj.success == 'function') obj.success(selectShop);
+      },
+      // 加入购物车
+      addCart() {
+        let that = this;
+        that.checkSelectComplete({
+          success: function(selectShop) {
+            selectShop.buy_num = that.selectNum;
+            that.$emit('add-cart', selectShop);
+            that.$emit('cart', selectShop);
+            // setTimeout(function() {
+            // 	that.init();
+            // }, 300);
+          }
+        });
+      },
+      // 立即购买
+      buyNow() {
+        let that = this;
+        that.checkSelectComplete({
+          success: function(selectShop) {
+            selectShop.buy_num = that.selectNum;
+            that.$emit('buy-now', selectShop);
+            that.$emit('buy', selectShop);
+          }
+        });
+      },
+      // 弹窗
+      toast(title, icon) {
+        uni.showToast({
+          title: title,
+          icon: icon
+        });
+      },
+      // 获取对象数组中的某一个item,根据指定的键值
+      getListItem(list, key, value) {
+        let that = this;
+        let item;
+        for (let i in list) {
+          if (typeof value == 'object') {
+            if (JSON.stringify(list[i][key]) === JSON.stringify(value)) {
+              item = list[i];
+              break;
+            }
+          } else {
+            if (list[i][key] === value) {
+              item = list[i];
+              break;
+            }
+          }
+        }
+        return item;
+      },
+      getListIndex(list, key, value) {
+        let that = this;
+        let index = -1;
+        for (let i = 0; i < list.length; i++) {
+          if (list[i][key] === value) {
+            index = i;
+            break;
+          }
+        }
+        return index;
+      },
+      // 自动选择sku前提是只有一组sku,默认自动选择最前面的有库存的sku
+      autoClickSku() {
+        let that = this;
+        let { stockName } = that;
+        let skuList = that.goodsInfo[that.skuListName];
+        let specListArr = that.goodsInfo[that.specListName];
+        if (specListArr.length == 1) {
+          let specList = specListArr[0].list;
+          for (let i = 0; i < specList.length; i++) {
+            let sku = that.getListItem(skuList, that.skuArrName, [specList[i].name]);
+            if (sku && sku[stockName] > 0) {
+              that.skuClick(specList[i], 0, i);
+              break;
+            }
+          }
+        }
+      },
+      // 主题颜色
+      themeColorFn(name) {
+        let that = this;
+        let { theme, themeColor } = that;
+        let color = that[name] ? that[name] : themeColor[theme][name];
+        return color;
+      },
+      defaultSelectSku() {
+        let that = this;
+        let { defaultSelect } = that;
+        if (defaultSelect && defaultSelect.sku && defaultSelect.sku.length > 0) {
+          that.selectSku(defaultSelect);
+        }
+      },
+      /**
+      	 * 主动方法 - 设置sku
+      	that.$refs.skuPopup.selectSku({
+      		sku:["红色","256G","公开版"],
+      		num:5
+      	});
+      	 */
+      selectSku(obj = {}) {
+        let that = this;
+        let { sku: skuArr, num: selectNum } = obj;
+        let specListArr = that.goodsInfo[that.specListName];
+        if (skuArr && specListArr.length === skuArr.length) {
+          // 先清空
+          let skuClickArr = [];
+          let clickKey = true;
+          for (let index = 0; index < skuArr.length; index++) {
+            let skuName = skuArr[index];
+            let specList = specListArr[index].list;
+            let index1 = index;
+            let index2 = that.getListIndex(specList, 'name', skuName);
+            if (index2 == -1) {
+              clickKey = false;
+              break;
+            }
+            skuClickArr.push({
+              spec: specList[index2],
+              index1: index1,
+              index2: index2
+            });
+          }
+          if (clickKey) {
+            that.init(true);
+            skuClickArr.map(item => {
+              that.skuClick(item.spec, item.index1, item.index2);
+            });
+          }
+        }
+        if (selectNum > 0) that.selectNum = selectNum;
+      },
+      priceFilter(n = 0) {
+        let that = this;
+        if (typeof n == 'string') {
+          n = parseFloat(n);
+        }
+        if (that.amountType === 0) {
+          return n.toFixed(2);
+        } else {
+          return (n / 100).toFixed(2);
+        }
+      },
+      pushGoodsCache(goodsInfo) {
+        let that = this;
+        let { goodsIdName } = that;
+        goodsCache[goodsInfo[goodsIdName]] = goodsInfo;
+      },
+      // 用于阻止冒泡
+      stop() {},
+      // 图片预览
+      previewImage() {
+        let that = this;
+        let { selectShop, goodsInfo, goodsThumbName } = that;
+        let src = selectShop.image ? selectShop.image : goodsInfo[goodsThumbName];
+        if (src) {
+          uni.previewImage({
+            urls: [src]
+          });
+        }
+      },
+      getMaxStock() {
+        let maxStock = 0;
+        let that = this;
+        let { selectShop = {}, goodsInfo = {}, skuListName, stockName } = that;
+        if (selectShop[stockName]) {
+          maxStock = selectShop[stockName];
+        } else {
+          let skuList = goodsInfo[skuListName];
+          if (skuList && skuList.length > 0) {
+            let valueArr = [];
+            skuList.map((skuItem, index) => {
+              valueArr.push(skuItem[stockName]);
+            });
+            let max = Math.max(...valueArr);
+            maxStock = max;
+          }
+        }
+        return maxStock;
+      },
+      numChange(e) {
+        this.$emit("num-change", e.value);
+      }
+    },
+    // 计算属性
+    computed: {
+      valueCom() {
+        // #ifndef VUE3
+        return this.value;
+        // #endif
+
+        // #ifdef VUE3
+        return this.modelValue;
+        // #endif
+      },
+      // 最大购买数量
+      maxBuyNumCom() {
+        let that = this;
+        let maxStock = that.getMaxStock();
+        let max = that.maxBuyNum || 100000;
+        // 最大购买量不能超过当前商品的库存
+        if (max > maxStock) {
+          max = maxStock;
+        }
+        return max;
+      },
+      // 是否是多规格
+      isManyCom() {
+        let that = this;
+        let { goodsInfo, defaultSingleSkuName, specListName } = that;
+        let isMany = true;
+        if (
+          goodsInfo[specListName] &&
+          goodsInfo[specListName].length === 1 &&
+          goodsInfo[specListName][0].list.length === 1 &&
+          goodsInfo[specListName][0].name === defaultSingleSkuName
+        ) {
+          isMany = false;
+        }
+        return isMany;
+      },
+      // 默认价格区间计算
+      priceCom() {
+        let str = '';
+        let that = this;
+        let { selectShop = {}, goodsInfo = {}, skuListName, skuIdName } = that;
+        if (selectShop[skuIdName]) {
+          str = that.priceFilter(selectShop.price);
+        } else {
+          let skuList = goodsInfo[skuListName];
+          if (skuList && skuList.length > 0) {
+            let valueArr = [];
+            skuList.map((skuItem, index) => {
+              valueArr.push(skuItem.price);
+            });
+            let min = that.priceFilter(Math.min(...valueArr));
+            let max = that.priceFilter(Math.max(...valueArr));
+            if (min === max) {
+              str = min + '';
+            } else {
+              str = `${min} - ${max}`;
+            }
+          }
+        }
+        return str;
+      },
+      // 库存显示
+      stockCom() {
+        let str = '';
+        let that = this;
+        let { selectShop = {}, goodsInfo = {}, skuListName, stockName } = that;
+        if (selectShop[stockName]) {
+          str = selectShop[stockName];
+        } else {
+          let skuList = goodsInfo[skuListName];
+          if (skuList && skuList.length > 0) {
+            let valueArr = [];
+            skuList.map((skuItem, index) => {
+              valueArr.push(skuItem[stockName]);
+            });
+            let min = Math.min(...valueArr);
+            let max = Math.max(...valueArr);
+            if (min === max) {
+              str = min;
+            } else {
+              str = `${min} - ${max}`;
+            }
+          }
+        }
+        return str;
+      }
+    },
+    watch: {
+      valueCom(newVal, oldValue) {
+        let that = this;
+        if (newVal) {
+          that.open();
+        }
+      },
+      defaultGoods: {
+        immediate: true,
+        handler: function(newVal, oldValue) {
+          let that = this;
+          let { goodsIdName } = that;
+          if (typeof newVal === 'object' && newVal && newVal[goodsIdName] && !goodsCache[newVal[goodsIdName]]) {
+            that.pushGoodsCache(newVal);
+          }
+        }
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  /*  sku弹出层 */
+  .goods-sku-popup {
+    position: fixed;
+    left: var(--window-left);
+    right: var(--window-right);
+    bottom: var(--window-bottom);
+    top: var(--window-top);
+    z-index: 21;
+    overflow: hidden;
+
+    &.show {
+      display: block;
+
+      .mask {
+        animation: showPopup 0.2s linear both;
+      }
+
+      .layer {
+        animation: showLayer 0.2s linear both;
+        bottom: var(--window-bottom);
+      }
+    }
+
+    &.hide {
+      .mask {
+        animation: hidePopup 0.2s linear both;
+      }
+
+      .layer {
+        animation: hideLayer 0.2s linear both;
+      }
+    }
+
+    &.none {
+      display: none;
+    }
+
+    .mask {
+      position: fixed;
+      left: var(--window-left);
+      right: var(--window-right);
+      top: 0;
+      width: 100%;
+      height: 100%;
+      z-index: 1;
+      background-color: rgba(0, 0, 0, 0.65);
+    }
+
+    .layer {
+      display: flex;
+      flex-direction: column;
+      position: fixed;
+      left: var(--window-left);
+      right: var(--window-right);
+      bottom: var(--window-bottom);
+      z-index: 99;
+      border-radius: 10rpx 10rpx 0 0;
+      background-color: #fff;
+      // 设置ios刘海屏底部横线安全区域
+      padding-bottom: constant(safe-area-inset-bottom);
+      padding-bottom: env(safe-area-inset-bottom);
+
+      .specification-wrapper {
+        width: 100%;
+        padding: 30rpx 25rpx;
+        box-sizing: border-box;
+
+        .specification-wrapper-content {
+          width: 100%;
+          max-height: 900rpx;
+          min-height: 300rpx;
+
+          &::-webkit-scrollbar {
+            /*隐藏滚轮*/
+            display: none;
+          }
+
+          .specification-header {
+            width: 100%;
+            display: flex;
+            flex-direction: row;
+            position: relative;
+            margin-bottom: 40rpx;
+
+            .specification-left {
+              width: 180rpx;
+              height: 180rpx;
+              flex: 0 0 180rpx;
+
+              .product-img {
+                width: 180rpx;
+                height: 180rpx;
+              }
+            }
+
+            .specification-right {
+              flex: 1;
+              padding: 0 35rpx 10rpx 28rpx;
+              box-sizing: border-box;
+              display: flex;
+              flex-direction: column;
+              justify-content: flex-end;
+              font-weight: 500;
+
+              .price-content {
+                color: #fe560a;
+                margin-bottom: 10rpx;
+
+                .sign {
+                  font-size: 28rpx;
+                  margin-right: 4rpx;
+                }
+
+                .price {
+                  margin-left: 4rpx;
+                  font-size: 48rpx;
+                }
+
+                .price2 {
+                  margin-left: 4rpx;
+                  font-size: 36rpx;
+                }
+              }
+
+              .inventory {
+                font-size: 24rpx;
+                color: #525252;
+                margin-bottom: 14rpx;
+              }
+
+              .choose {
+                font-size: 24rpx;
+                color: #525252;
+                min-height: 32rpx;
+              }
+            }
+          }
+
+          .specification-content {
+            font-weight: 500;
+
+            .specification-item {
+              margin-bottom: 40rpx;
+
+              &:last-child {
+                margin-bottom: 0;
+              }
+
+              .item-title {
+                margin-bottom: 20rpx;
+                font-size: 28rpx;
+                color: #999999;
+              }
+
+              .item-wrapper {
+                display: flex;
+                flex-direction: row;
+                flex-flow: wrap;
+                margin-bottom: -20rpx;
+
+                .item-content {
+                  display: inline-block;
+                  padding: 10rpx 35rpx;
+                  font-size: 24rpx;
+                  border-radius: 10rpx;
+                  background-color: #ffffff;
+                  color: #333333;
+                  margin-right: 20rpx;
+                  margin-bottom: 20rpx;
+                  border: 2rpx solid #f4f4f4;
+                  box-sizing: border-box;
+
+                  &.actived {
+                    border-color: #fe560a;
+                    color: #fe560a;
+                  }
+
+                  &.noactived {
+                    // background-color: #f6f6f6;
+                    // border-color: #f6f6f6;
+                    // color: #c3c3c3;
+                    color: #c8c9cc;
+                    background: #f2f3f5;
+                    border-color: #f2f3f5;
+                  }
+                }
+              }
+            }
+
+            .number-box-view {
+              display: flex;
+              padding-top: 20rpx;
+            }
+          }
+        }
+
+        .close {
+          position: absolute;
+          top: 30rpx;
+          right: 25rpx;
+          width: 50rpx;
+          height: 50rpx;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+
+          .close-item {
+            width: 40rpx;
+            height: 40rpx;
+          }
+        }
+      }
+
+      .btn-wrapper {
+        display: flex;
+        width: 100%;
+        height: 120rpx;
+        flex: 0 0 120rpx;
+        align-items: center;
+        justify-content: space-between;
+        padding: 0 26rpx;
+        box-sizing: border-box;
+
+        .layer-btn {
+          width: 335rpx;
+          height: 76rpx;
+          border-radius: 38rpx;
+          color: #fff;
+          font-weight: 500;
+          font-size: 28rpx;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+
+          &.add-cart {
+            background: #ffbe46;
+          }
+
+          &.buy {
+            background: #fe560a;
+          }
+        }
+
+        .sure {
+          margin: 0 auto;
+          width: 95%;
+          max-width: 1200rpx;
+          height: 80rpx;
+          border-radius: 38rpx;
+          color: #fff;
+          font-weight: 500;
+          font-size: 28rpx;
+          background: #fe560a;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+        }
+
+        .sure.add-cart {
+          background: #ff9402;
+        }
+      }
+
+      .btn-wrapper.safe-area-inset-bottom {
+        padding-bottom: 0;
+        padding-bottom: constant(safe-area-inset-bottom);
+        padding-bottom: env(safe-area-inset-bottom);
+      }
+    }
+
+    @keyframes showPopup {
+      0% {
+        opacity: 0;
+      }
+
+      100% {
+        opacity: 1;
+      }
+    }
+
+    @keyframes hidePopup {
+      0% {
+        opacity: 1;
+      }
+
+      100% {
+        opacity: 0;
+      }
+    }
+
+    @keyframes showLayer {
+      0% {
+        transform: translateY(120%);
+      }
+
+      100% {
+        transform: translateY(0%);
+      }
+    }
+
+    @keyframes hideLayer {
+      0% {
+        transform: translateY(0);
+      }
+
+      100% {
+        transform: translateY(120%);
+      }
+    }
+  }
+</style>

+ 473 - 0
hn-shop-uniapp/components/goods-sku-popup/number-box/index.vue

@@ -0,0 +1,473 @@
+<!-- 步进器 -->
+<template>
+	<view class="number-box">
+		<view
+			class="u-icon-minus"
+			:class="{ 'u-icon-disabled': disabled || inputVal <= min }"
+			:style="{
+				background: bgColor,
+				height: inputHeight + 'rpx',
+				color: color,
+				fontSize: size + 'rpx',
+				minHeight: '1.4em'
+			}"
+			@click="emptyClick"
+			@touchstart.prevent="btnTouchStart('minus')"
+			@touchend.stop.prevent="clearTimer"
+		>
+			<view :style="'font-size:' + (Number(size) + 10) + 'rpx'" class="num-btn">-</view>
+		</view>
+		<input
+			v-model="inputVal"
+			:disabled="disabledInput || disabled"
+			:cursor-spacing="getCursorSpacing"
+			:class="{ 'u-input-disabled': disabled }"
+			class="u-number-input"
+			type="number"
+			:style="{
+				color: color,
+				fontSize: size + 'rpx',
+				background: bgColor,
+				height: inputHeight + 'rpx',
+				width: inputWidth + 'rpx'
+			}"
+			@blur="onBlur"
+			@click="showInput=true"
+		/>
+		<view
+			class="u-icon-plus"
+			:class="{ 'u-icon-disabled': disabled || inputVal >= max }"
+			:style="{
+				background: bgColor,
+				height: inputHeight + 'rpx',
+				color: color,
+				fontSize: size + 'rpx',
+				minHeight: '1.4em'
+			}"
+			@click="emptyClick"
+			@touchstart.prevent="btnTouchStart('plus')"
+			@touchend.stop.prevent="clearTimer"
+		>
+			<view :style="'font-size:' + (Number(size) + 10) + 'rpx'" class="num-btn">+</view>
+		</view>
+	</view>
+</template>
+<script>
+/**
+ * numberBox 步进器(此为uview组件改造)
+ * @description 该组件一般用于商城购物选择物品数量的场景。注意:该输入框只能输入大于或等于0的整数,不支持小数输入
+ * @tutorial https://www.uviewui.com/components/numberBox.html
+ * @property {Number} value 输入框初始值(默认1)
+ * @property {String} bg-color 输入框和按钮的背景颜色(默认#F2F3F5)
+ * @property {Number} min 用户可输入的最小值(默认0)
+ * @property {Number} max 用户可输入的最大值(默认99999)
+ * @property {Number} step 步长,每次加或减的值(默认1)
+ * @property {Number} stepFirst 步进值,首次增加或最后减的值(默认step值和一致)
+ * @property {Boolean} disabled 是否禁用操作,禁用后无法加减或手动修改输入框的值(默认false)
+ * @property {Boolean} disabled-input 是否禁止输入框手动输入值(默认false)
+ * @property {Boolean} positive-integer 是否只能输入正整数(默认true)
+ * @property {String | Number} size 输入框文字和按钮字体大小,单位rpx(默认26)
+ * @property {String} color 输入框文字和加减按钮图标的颜色(默认#323233)
+ * @property {String | Number} input-width 输入框宽度,单位rpx(默认80)
+ * @property {String | Number} input-height 输入框和按钮的高度,单位rpx(默认50)
+ * @property {String | Number} index 事件回调时用以区分当前发生变化的是哪个输入框
+ * @property {Boolean} long-press 是否开启长按连续递增或递减(默认true)
+ * @property {String | Number} press-time 开启长按触发后,每触发一次需要多久,单位ms(默认250)
+ * @property {String | Number} cursor-spacing 指定光标于键盘的距离,避免键盘遮挡输入框,单位rpx(默认200)
+ * @event {Function} change 输入框内容发生变化时触发,对象形式
+ * @event {Function} blur 输入框失去焦点时触发,对象形式
+ * @event {Function} minus 点击减少按钮时触发(按钮可点击情况下),对象形式
+ * @event {Function} plus 点击增加按钮时触发(按钮可点击情况下),对象形式
+ * @example <number-box :min="1" :max="100"></number-box>
+ */
+export default {
+	name: 'NumberBox',
+	emits: ['update:modelValue', 'input', 'change', 'blur', 'plus', 'minus'],
+	props: {
+		// 预显示的数字
+		value: {
+			type: Number,
+			default: 1
+		},
+		modelValue: {
+			type: Number,
+			default: 1
+		},
+		// 背景颜色
+		bgColor: {
+			type: String,
+			default: '#FFFFFF'
+		},
+		// 最小值
+		min: {
+			type: Number,
+			default: 0
+		},
+		// 最大值
+		max: {
+			type: Number,
+			default: 99999
+		},
+		// 步进值,每次加或减的值
+		step: {
+			type: Number,
+			default: 1
+		},
+		// 步进值,首次增加或最后减的值
+		stepFirst: {
+			type: Number,
+			default: 0
+		},
+		// 是否只能输入 step 的倍数
+		stepStrictly: {
+			type: Boolean,
+			default: false
+		},
+		// 是否禁用加减操作
+		disabled: {
+			type: Boolean,
+			default: false
+		},
+		// input的字体大小,单位rpx
+		size: {
+			type: [Number, String],
+			default: 26
+		},
+		// 加减图标的颜色
+		color: {
+			type: String,
+			default: '#323233'
+		},
+		// input宽度,单位rpx
+		inputWidth: {
+			type: [Number, String],
+			default: 80
+		},
+		// input高度,单位rpx
+		inputHeight: {
+			type: [Number, String],
+			default: 50
+		},
+		// index索引,用于列表中使用,让用户知道是哪个numberbox发生了变化,一般使用for循环出来的index值即可
+		index: {
+			type: [Number, String],
+			default: ''
+		},
+		// 是否禁用输入框,与disabled作用于输入框时,为OR的关系,即想要禁用输入框,又可以加减的话
+		// 设置disabled为false,disabledInput为true即可
+		disabledInput: {
+			type: Boolean,
+			default: false
+		},
+		// 输入框于键盘之间的距离
+		cursorSpacing: {
+			type: [Number, String],
+			default: 100
+		},
+		// 是否开启长按连续递增或递减
+		longPress: {
+			type: Boolean,
+			default: true
+		},
+		// 开启长按触发后,每触发一次需要多久
+		pressTime: {
+			type: [Number, String],
+			default: 250
+		},
+		// 是否只能输入大于或等于0的整数(正整数)
+		positiveInteger: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		valueCom(v1, v2) {
+			// 只有value的改变是来自外部的时候,才去同步inputVal的值,否则会造成循环错误
+			if (!this.changeFromInner) {
+				this.inputVal = v1;
+				// 因为inputVal变化后,会触发this.handleChange(),在其中changeFromInner会再次被设置为true,
+				// 造成外面修改值,也导致被认为是内部修改的混乱,这里进行this.$nextTick延时,保证在运行周期的最后处
+				// 将changeFromInner设置为false
+				this.$nextTick(function() {
+					this.changeFromInner = false;
+				});
+			}
+		},
+		inputVal(v1, v2) {
+			// 为了让用户能够删除所有输入值,重新输入内容,删除所有值后,内容为空字符串
+			if (v1 == '') return;
+			let value = 0;
+			// 首先判断是否数值,并且在min和max之间,如果不是,使用原来值
+			let tmp = this.isNumber(v1);
+			if (tmp && v1 >= this.min && v1 <= this.max) value = v1;
+			else value = v2;
+			// 判断是否只能输入大于等于0的整数
+			if (this.positiveInteger) {
+				// 小于0,或者带有小数点,
+				if (v1 < 0 || String(v1).indexOf('.') !== -1) {
+					value = v2;
+					// 双向绑定input的值,必须要使用$nextTick修改显示的值
+					this.$nextTick(() => {
+						this.inputVal = v2;
+					});
+				}
+			}
+			// 发出change事件
+			this.handleChange(value, 'change');
+		},
+		min(v1) {
+			if (v1 !== undefined && v1 != '' && this.valueCom < v1) {
+				this.$emit('input', v1);
+				this.$emit('update:modelValue', v1);
+			}
+		},
+		max(v1) {
+			if (v1 !== undefined && v1 != '' && this.valueCom > v1) {
+				this.$emit('input', v1);
+				this.$emit('update:modelValue', v1);
+			}
+		}
+	},
+	data() {
+		return {
+			inputVal: 1, // 输入框中的值,不能直接使用props中的value,因为应该改变props的状态
+			timer: null, // 用作长按的定时器
+			changeFromInner: false, // 值发生变化,是来自内部还是外部
+			innerChangeTimer: null ,// 内部定时器
+			showInput:false,
+		};
+	},
+	created() {
+		this.inputVal = Number(this.valueCom);
+	},
+	computed: {
+		valueCom() {
+			// #ifndef VUE3
+			return this.value;
+			// #endif
+
+			// #ifdef VUE3
+			return this.modelValue;
+			// #endif
+		},
+		getCursorSpacing() {
+			// 先将值转为px单位,再转为数值
+			return Number(uni.upx2px(this.cursorSpacing));
+		}
+	},
+	methods: {
+		// 空点击事件,主要用于解决PC端H5由于无click事件导致触摸位置不准确的问题
+		emptyClick(){
+			
+		},
+		// 触摸事件开始
+		btnTouchStart(callback) {
+			// 先执行一遍方法,否则会造成松开手时,就执行了clearTimer,导致无法实现功能
+			this[callback]();
+			// 如果没开启长按功能,直接返回
+			if (!this.longPress) return;
+			clearInterval(this.timer); //再次清空定时器,防止重复注册定时器
+			this.timer = null;
+			this.timer = setInterval(() => {
+				// 执行加或减函数
+				this[callback]();
+			}, this.pressTime);
+		},
+		// 清除定时器
+		clearTimer() {
+			this.$nextTick(() => {
+				clearInterval(this.timer);
+				this.timer = null;
+			});
+		},
+		// 减
+		minus() {
+			this.computeVal('minus');
+		},
+		// 加
+		plus() {
+			this.computeVal('plus');
+		},
+		// 为了保证小数相加减出现精度溢出的问题
+		calcPlus(num1, num2) {
+			let baseNum, baseNum1, baseNum2;
+			try {
+				baseNum1 = num1.toString().split('.')[1].length;
+			} catch (e) {
+				baseNum1 = 0;
+			}
+			try {
+				baseNum2 = num2.toString().split('.')[1].length;
+			} catch (e) {
+				baseNum2 = 0;
+			}
+			baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
+			let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2; //精度
+			return ((num1 * baseNum + num2 * baseNum) / baseNum).toFixed(precision);
+		},
+		// 为了保证小数相加减出现精度溢出的问题
+		calcMinus(num1, num2) {
+			let baseNum, baseNum1, baseNum2;
+			try {
+				baseNum1 = num1.toString().split('.')[1].length;
+			} catch (e) {
+				baseNum1 = 0;
+			}
+			try {
+				baseNum2 = num2.toString().split('.')[1].length;
+			} catch (e) {
+				baseNum2 = 0;
+			}
+			baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
+			let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2;
+			return ((num1 * baseNum - num2 * baseNum) / baseNum).toFixed(precision);
+		},
+		computeVal(type) {
+			uni.hideKeyboard();
+			if (this.disabled) return;
+			let value = 0;
+			// 新增stepFirst开始
+			// 减
+			if (type === 'minus') {
+				if (this.stepFirst > 0 && this.inputVal == this.stepFirst) {
+					value = this.min;
+				} else {
+					value = this.calcMinus(this.inputVal, this.step);
+				}
+			} else if (type === 'plus') {
+				if (this.stepFirst > 0 && this.inputVal < this.stepFirst) {
+					value = this.stepFirst;
+				} else {
+					value = this.calcPlus(this.inputVal, this.step);
+				}
+			}
+			if (this.stepStrictly) {
+				let strictly = value % this.step;
+				if (strictly > 0) {
+					value -= strictly;
+				}
+			}
+			if (value > this.max) {
+				value = this.max;
+			} else if (value < this.min) {
+				value = this.min;
+			}
+			// 新增stepFirst结束
+			this.inputVal = value;
+			this.handleChange(value, type);
+		},
+		// 处理用户手动输入的情况
+		onBlur(event) {
+			let val = 0;
+			let value = event.detail.value;
+			// 如果为非0-9数字组成,或者其第一位数值为0,直接让其等于min值
+			// 这里不直接判断是否正整数,是因为用户传递的props min值可能为0
+			if (!/(^\d+$)/.test(value) || value[0] == 0) val = this.min;
+			val = +value;
+
+			// 新增stepFirst开始
+			if (this.stepFirst > 0 && this.inputVal < this.stepFirst && this.inputVal > 0) {
+				val = this.stepFirst;
+			}
+			// 新增stepFirst结束
+			if (this.stepStrictly) {
+				let strictly = val % this.step;
+				if (strictly > 0) {
+					val -= strictly;
+				}
+			}
+			if (val > this.max) {
+				val = this.max;
+			} else if (val < this.min) {
+				val = this.min;
+			}
+			this.$nextTick(() => {
+				this.inputVal = val;
+			});
+			this.handleChange(val, 'blur');
+		},
+		handleChange(value, type) {
+			if (this.disabled) return;
+			// 清除定时器,避免造成混乱
+			if (this.innerChangeTimer) {
+				clearTimeout(this.innerChangeTimer);
+				this.innerChangeTimer = null;
+			}
+			// 发出input事件,修改通过v-model绑定的值,达到双向绑定的效果
+			this.changeFromInner = true;
+			// 一定时间内,清除changeFromInner标记,否则内部值改变后
+			// 外部通过程序修改value值,将会无效
+			this.innerChangeTimer = setTimeout(() => {
+				this.changeFromInner = false;
+			}, 150);
+			this.$emit('input', Number(value));
+			this.$emit('update:modelValue', Number(value));
+			this.$emit(type, {
+				// 转为Number类型
+				value: Number(value),
+				index: this.index
+			});
+		},
+		/**
+		 * 验证十进制数字
+		 */
+		isNumber(value) {
+			return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.number-box {
+	display: inline-flex;
+	align-items: center;
+	box-sizing: border-box;
+}
+
+.u-number-input {
+	position: relative;
+	text-align: center;
+	padding: 0;
+	margin: 0rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	border: 2rpx solid #f4f4f4;
+	border-left: 0;
+	border-right: 0;
+	box-sizing: border-box;
+}
+
+.u-icon-plus,
+.u-icon-minus {
+	width: 60rpx;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	border: 2rpx solid #f4f4f4;
+	box-sizing: border-box;
+}
+
+.u-icon-plus {
+	border-radius: 0 8rpx 8rpx 0;
+}
+
+.u-icon-minus {
+	border-radius: 8rpx 0 0 8rpx;
+}
+
+.u-icon-disabled {
+	color: #c8c9cc !important;
+	background-color: #f2f3f5 !important;
+}
+
+.u-input-disabled {
+	color: #c8c9cc !important;
+	background-color: #f2f3f5 !important;
+}
+.num-btn {
+	font-weight: 550;
+	line-height: 50rpx;
+}
+</style>

+ 43 - 0
hn-shop-uniapp/components/page/diyComponents/ICPLicense/index.vue

@@ -0,0 +1,43 @@
+<template>
+  <!-- 网站备案号 -->
+  <!-- #ifdef H5 -->
+  <view class="diy-ICPLicense"
+    :style="{ padding: `${itemStyle.paddingTop * 2}rpx ${itemStyle.paddingLeft * 2}rpx`, background: itemStyle.background }">
+    <view class="line" :style="{ textAlign: itemStyle.textAlign }">
+      <text :style="{ fontSize: `${itemStyle.fontSize * 2}rpx`, color: itemStyle.textColor }" target="_blank"
+        @click="onLink({ type: 'URL', param: { url: params.link } })">{{ params.text }}</text>
+    </view>
+  </view>
+  <!-- #endif -->
+</template>
+
+<script>
+  import mixin from '../mixin'
+
+  export default {
+
+    /**
+     * 组件的属性列表
+     * 用于组件自定义设置
+     */
+    props: {
+      itemStyle: Object,
+      params: Object,
+    },
+
+    mixins: [mixin],
+
+    /**
+     * 组件的方法列表
+     * 更新属性和数据的方法与更新页面数据的方法类似
+     */
+    methods: {
+
+    }
+
+  }
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 123 - 0
hn-shop-uniapp/components/page/diyComponents/article/index.vue

@@ -0,0 +1,123 @@
+<template>
+  <!-- 文章组 -->
+  <view class="diy-article">
+    <view class="article-item" :class="[`show-type__${item.show_type}`]" v-for="(item, index) in dataList" :key="index"
+      @click="onTargetDetail(item.article_id)">
+      <!-- 小图模式 -->
+      <block v-if="item.show_type == 10">
+        <view class="article-item__left flex-box">
+          <view class="article-item__title">
+            <text class="twoline-hide">{{ item.title }}</text>
+          </view>
+          <view class="article-item__footer m-top10">
+            <text class="article-views f-24 col-8">{{ item.show_views }}次浏览</text>
+          </view>
+        </view>
+        <view class="article-item__image">
+          <image class="image" mode="widthFix" :src="item.image_url"></image>
+        </view>
+      </block>
+      <!-- 大图模式 -->
+      <block v-if="item.show_type == 20">
+        <view class="article-item__title">
+          <text class="twoline-hide">{{ item.title }}</text>
+        </view>
+        <view class="article-item__image m-top20">
+          <image class="image" mode="widthFix" :src="item.image_url"></image>
+        </view>
+        <view class="article-item__footer m-top10">
+          <text class="article-views f-24 col-8">{{ item.show_views }}次浏览</text>
+        </view>
+      </block>
+    </view>
+  </view>
+</template>
+
+<script>
+  export default {
+    name: "Article",
+    /**
+     * 组件的属性列表
+     * 用于组件自定义设置
+     */
+    props: {
+      itemIndex: String,
+      params: Object,
+      dataList: Array
+    },
+
+    /**
+     * 组件的方法列表
+     * 更新属性和数据的方法与更新页面数据的方法类似
+     */
+    methods: {
+
+      /**
+       * 跳转文章详情页
+       */
+      onTargetDetail(id) {
+        uni.navigateTo({
+          url: '/pages/article/detail?articleId=' + id
+        })
+      }
+
+    }
+
+  }
+</script>
+
+<style lang="scss" scoped>
+  .diy-article {
+    background: #f7f7f7;
+
+    .article-item {
+      margin-bottom: 20rpx;
+      padding: 30rpx;
+      background: #fff;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+
+      .article-item__title {
+        max-height: 74rpx;
+        font-size: 28rpx;
+        line-height: 38rpx;
+        color: #333;
+      }
+
+      .article-item__image .image {
+        display: block;
+      }
+
+    }
+
+
+
+  }
+
+  /* 小图模式 */
+
+  .show-type__10 {
+    display: flex;
+
+    .article-item__left {
+      padding-right: 20rpx;
+    }
+
+    .article-item__title {
+      // min-height: 72rpx;
+    }
+
+    .article-item__image .image {
+      width: 240rpx;
+    }
+
+  }
+
+  /* 大图模式 */
+
+  .show-type__20 .article-item__image .image {
+    width: 100%;
+  }
+</style>

+ 154 - 0
hn-shop-uniapp/components/page/diyComponents/banner/index.vue

@@ -0,0 +1,154 @@
+<template>
+  <view class="diy-banner" :style="{height: `200px`}">
+    <!-- <view class="diy-banner" :style="{ height: `${imgHeights[imgCurrent]}px` }"> -->
+    <!-- 图片轮播 -->
+    <swiper :autoplay="autoplay" class="swiper-box" :duration="duration" :circular="true" :interval="itemStyle.interval * 1000" @change="_bindChange">
+      <swiper-item v-for="(dataItem, index) in dataList" :key="index">
+        <image mode="widthFix" class="slide-image" src="@/static/banner/01.jpg" @click="onLink(dataItem.link)" @load="_imagesHeight" />
+      </swiper-item>
+    </swiper>
+    <!-- 指示点 -->
+    <view class="indicator-dots" :class="itemStyle.btnShape">
+      <view class="dots-item" :class="{active: imgCurrent == index}" :style="{backgroundColor: itemStyle.btnColor}" v-for="(dataItem, index) in dataList" :key="index"></view>
+    </view>
+  </view>
+</template>
+
+<script>
+import mixin from '../mixin'
+
+export default {
+  name: 'Banner',
+
+  /**
+   * 组件的属性列表
+   * 用于组件自定义设置
+   */
+  props: {
+    itemIndex: String,
+    itemStyle: Object,
+    params: Object,
+    dataList: Array
+  },
+
+  mixins: [mixin],
+
+  /**
+   * 私有数据,组件的初始数据
+   * 可用于模版渲染
+   */
+  data() {
+    return {
+      windowWidth: 750,
+      indicatorDots: false, // 是否显示面板指示点
+      autoplay: true, // 是否自动切换
+      duration: 800, // 滑动动画时长
+      imgHeights: [], // 图片的高度
+      imgCurrent: 0 // 当前banne所在滑块指针
+    }
+  },
+
+  created() {
+    const app = this
+    uni.getSystemInfo({
+      success({windowWidth}) {
+        app.windowWidth = windowWidth > 750 ? 750 : windowWidth
+      }
+    })
+  },
+
+  /**
+   * 组件的方法列表
+   * 更新属性和数据的方法与更新页面数据的方法类似
+   */
+  methods: {
+    /**
+     * 计算图片高度
+     */
+    _imagesHeight({detail}) {
+      const app = this
+      // 获取图片真实宽度
+      const {width, height} = detail
+      // 宽高比
+      const ratio = width / height
+      // 计算的高度值
+      const viewHeight = app.windowWidth / ratio
+      // 把每一张图片的高度记录到数组里
+      app.imgHeights.push(viewHeight)
+    },
+
+    /**
+     * 记录当前指针
+     */
+    _bindChange(e) {
+      this.imgCurrent = e.detail.current
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.diy-banner {
+  position: relative;
+
+  // swiper组件
+  .swiper-box {
+    height: 100%;
+
+    .slide-image {
+      width: 100%;
+      height: 100%;
+      margin: 0 auto;
+      display: block;
+    }
+  }
+
+  /* 指示点 */
+  .indicator-dots {
+    width: 100%;
+    height: 28rpx;
+    padding: 0 20rpx;
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 20rpx;
+    opacity: 0.8;
+    display: flex;
+    justify-content: center;
+
+    .dots-item {
+      width: 16rpx;
+      height: 16rpx;
+      margin-right: 8rpx;
+      background-color: #fff;
+
+      &:last-child {
+        margin-right: 0;
+      }
+
+      &.active {
+        background-color: #313131 !important;
+      }
+    }
+
+    // 圆形
+    &.round .dots-item {
+      width: 16rpx;
+      height: 16rpx;
+      border-radius: 20rpx;
+    }
+
+    // 正方形
+    &.square .dots-item {
+      width: 16rpx;
+      height: 16rpx;
+    }
+
+    // 长方形
+    &.rectangle .dots-item {
+      width: 22rpx;
+      height: 14rpx;
+    }
+  }
+}
+</style>

+ 31 - 0
hn-shop-uniapp/components/page/diyComponents/blank/index.vue

@@ -0,0 +1,31 @@
+<template>
+  <!-- 辅助空白 -->
+  <view class="diy-blank" :style="{ height: `${itemStyle.height}px`, background: itemStyle.background }">
+  </view>
+</template>
+
+<script>
+  export default {
+
+    /**
+     * 组件的属性列表
+     * 用于组件自定义设置
+     */
+    props: {
+      itemStyle: Object
+    },
+
+    /**
+     * 组件的方法列表
+     * 更新属性和数据的方法与更新页面数据的方法类似
+     */
+    methods: {
+
+    }
+
+  }
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 206 - 0
hn-shop-uniapp/components/page/diyComponents/coupon/index.vue

@@ -0,0 +1,206 @@
+<template>
+  <!-- 优惠券组 -->
+  <view v-if="couponList.length" class="diy-coupon" :style="{ padding: `${itemStyle.paddingTop}px 0`, background: itemStyle.background }">
+    <scroll-view :scroll-x="true">
+      <view class="coupon-wrapper">
+        <view class="coupon-item" :class="{ disable: !dataItem.state.value }" v-for="(dataItem, index) in couponList" :key="index"
+          :style="{ marginRight: `${itemStyle.marginRight}px` }">
+          <text class="before" :style="{ background: itemStyle.background }"></text>
+          <view class="left-content" :style="{ background: itemStyle.couponBgColor }">
+            <view class="content-top">
+              <block v-if="dataItem.coupon_type == 10">
+                <text class="unit">¥</text>
+                <text class="price">{{ dataItem.reduce_price }}</text>
+              </block>
+              <text v-if="dataItem.coupon_type == 20" class="price">{{ dataItem.discount }}折</text>
+            </view>
+            <view class="content-bottom">
+              <text class="f-22">满{{ dataItem.min_price }}元可用</text>
+            </view>
+          </view>
+          <view class="right-receive" :style="{ background: itemStyle.receiveBgColor }" @click="handleReceive(index, dataItem)">
+            <block v-if="dataItem.state.value">
+              <text>立即</text>
+              <text>领取</text>
+            </block>
+            <text v-else>{{ dataItem.state.text }}</text>
+          </view>
+        </view>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  import * as MyCouponApi from '@/api/myCoupon'
+  import { cloneObj } from '@/utils/util'
+
+  export default {
+
+    /**
+     * 组件的属性列表
+     * 用于组件自定义设置
+     */
+    props: {
+      itemIndex: String,
+      itemStyle: Object,
+      params: Object,
+      dataList: Array
+    },
+
+    data() {
+      return {
+        // 优惠券列表
+        couponList: [],
+        // 防止重复提交
+        disable: false
+      }
+    },
+    watch: {
+      // 这里监听dataList并写入到data中, 因为领取事件不能直接修改props中的属性
+      dataList: {
+        handler(data) {
+          this.couponList = cloneObj(data)
+          // console.log(this.couponList)
+        },
+        immediate: true,
+        deep: true
+      }
+    },
+
+    mixins: [mixin],
+
+    /**
+     * 组件的方法列表
+     * 更新属性和数据的方法与更新页面数据的方法类似
+     */
+    methods: {
+
+      // 立即领取事件
+      handleReceive(index, item) {
+        const app = this
+        if (app.disable || !item.state.value) {
+          return
+        }
+        app.disable = true
+        MyCouponApi.receive(item.coupon_id, {}, { load: false })
+          .then(result => {
+            // 显示领取成功提示
+            app.$success(result.message)
+            // 将优惠券设置为已领取
+            app.setReceived(index, item)
+          })
+          .finally(() => app.disable = false)
+      },
+
+      // 将优惠券设置为已领取
+      setReceived(index, item) {
+        const app = this
+        // #ifdef VUE2
+        app.$set(app.couponList, index, {
+          ...item,
+          state: { value: 0, text: '已领取' }
+        })
+        // #endif
+        // #ifdef VUE3
+        app.couponList[index] = {
+          ...item,
+          state: { value: 0, text: '已领取' }
+        }
+        // #endif
+      }
+
+    }
+
+  }
+</script>
+
+<style lang="scss" scoped>
+  .diy-coupon {
+
+    .coupon-wrapper {
+      display: flex;
+      width: max-content;
+      height: 130rpx;
+      padding: 0 24rpx;
+
+      &::-webkit-scrollbar {
+        display: none;
+      }
+    }
+  }
+
+  .coupon-item {
+    flex-shrink: 0;
+    width: 300rpx;
+    height: 130rpx;
+    position: relative;
+    color: #fff;
+    overflow: hidden;
+    box-sizing: border-box;
+    margin-right: 40rpx;
+    display: flex;
+
+    &:last-child {
+      margin-right: 0 !important;
+    }
+
+    &.disable {
+      .left-content {
+        background: linear-gradient(-113deg, #bdbdbd, #a2a1a2) !important;
+      }
+
+      .right-receive {
+        background-color: #949494 !important;
+      }
+    }
+
+    .before {
+      content: "";
+      position: absolute;
+      z-index: 1;
+      width: 32rpx;
+      height: 32rpx;
+      top: 45%;
+      left: -16rpx;
+      transform: translateY(-50%);
+      border-radius: 80%;
+      background-color: #fff;
+    }
+
+    .left-content {
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      position: relative;
+      width: 70%;
+      height: 100%;
+      background-color: #E5004F;
+      font-size: 24rpx;
+
+      .content-top {
+        .unit {
+          font-size: 15px;
+        }
+
+        .price {
+          font-size: 44rpx;
+        }
+      }
+    }
+
+    .right-receive {
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      width: 30%;
+      height: 100%;
+      background-color: #4e4e4e;
+      text-align: center;
+      font-size: 24rpx;
+    }
+  }
+</style>

+ 39 - 0
hn-shop-uniapp/components/page/diyComponents/empty.vue

@@ -0,0 +1,39 @@
+<template>
+  <view class="diy-123123">
+
+  </view>
+</template>
+
+<script>
+  import mixin from '../mixin'
+  
+  export default {
+
+    /**
+     * 组件的属性列表
+     * 用于组件自定义设置
+     */
+    props: {
+      itemIndex: String,
+      itemStyle: Object,
+      params: Object,
+      dataList: Array
+    },
+
+    mixins: [mixin],
+
+    /**
+     * 组件的方法列表
+     * 更新属性和数据的方法与更新页面数据的方法类似
+     */
+    methods: {
+
+
+    }
+
+  }
+</script>
+
+<style lang="scss" scoped>
+  
+</style>

+ 290 - 0
hn-shop-uniapp/components/page/diyComponents/goods/index.vue

@@ -0,0 +1,290 @@
+<template>
+  <!-- 路由器商品列表组件 -->
+  <view class="diy-goods" :style="{background: itemStyle.background}">
+    <!-- <img class="" style="width: 100%" src="@/static/background/skm.jpg" alt="" /> -->
+    <view class="goods-list" :class="[`display__${itemStyle.display}`, `column__${itemStyle.column}`]">
+      <scroll-view :scroll-x="itemStyle.display === 'slide'">
+        <view class="goods-item" v-for="(dataItem, index) in dataList" :key="index" @click="buyNow(dataItem)">
+          <!-- 单列商品 -->
+          <block v-if="itemStyle.column === 1">
+            <view class="dis-flex">
+              <!-- 商品图片 -->
+              <view class="goods-item-left">
+                <image class="image" :src="dataItem.goods_image"></image>
+              </view>
+              <view class="goods-item-right">
+                <!-- 商品名称 -->
+                <view v-if="itemStyle.show.includes('goodsName')" class="goods-name">
+                  <text class="twoline-hide">{{ dataItem.goods_name }}</text>
+                </view>
+                <view class="goods-item-desc">
+                  <!-- 商品卖点 -->
+                  <view v-if="itemStyle.show.includes('sellingPoint')" class="desc-selling-point dis-flex">
+                    <text class="oneline-hide">{{ dataItem.selling_point }}</text>
+                  </view>
+                  <!-- 商品销量 -->
+                  <view v-if="itemStyle.show.includes('goodsSales')" class="desc-goods-sales dis-flex">
+                    <text>已售{{ dataItem.goods_sales }}件</text>
+                  </view>
+                  <!-- 商品价格 -->
+                  <view class="desc-footer">
+                    <text v-if="itemStyle.show.includes('goodsPrice')" class="price-x">¥{{ dataItem.goods_price_min }}</text>
+                    <text class="price-y col-9" v-if="itemStyle.show.includes('linePrice') && dataItem.line_price_min > 0">¥{{ dataItem.line_price_min }}</text>
+                  </view>
+                </view>
+              </view>
+            </view>
+          </block>
+          <!-- 多列商品 - new-->
+          <block v-else>
+            <!-- 商品图片 -->
+            <view class="goods-image">
+              <image class="image" mode="aspectFill" :src="dataItem.productpic"></image>
+              <!-- {{ dataItem }} -->
+
+              <view class="goods-text001" style="margin-top: 100rpx">时长:{{ dataItem.productlimit }} 月</view>
+              <view class="goods-text001">赠送:{{ dataItem.productlimit1 }}</view>
+              <view class="goods-text001">宽带:{{ dataItem.productdkband }}</view>
+              <view class="goods-text001">赠送时长:{{ dataItem.productlimit }}</view>
+            </view>
+            <view class="detail">
+              <!-- 商品标题 -->
+              <view v-if="true" class="goods-name twoline-hide">
+                <text class="twoline-hide">{{ dataItem.productname }}</text>
+                <text class="twoline-hide twoline-xiao">介绍:{{ dataItem.productmemo }}</text>
+              </view>
+              <!-- 商品价格 -->
+              <view class="detail-price oneline-hide">
+                <text class="goods-price f-30 col-m">¥{{ dataItem.productprice1 }}</text>
+                <text v-if="itemStyle.show.includes('linePrice') && dataItem.productprice1 > 0" class="line-price col-9 f-24">¥{{ dataItem.productprice }}</text>
+              </view>
+            </view>
+          </block>
+        </view>
+      </scroll-view>
+    </view>
+    <!-- 还没上架商品 -->
+    <empty v-if="dataList.length === 0" :isLoading="isLoading" :custom-style="{padding: '180rpx 50rpx'}" tips="还没上架商品">
+      <template v-slot:slot>
+        <view class="empty-ipt">
+          <text>敬请期待</text>
+        </view>
+      </template>
+    </empty>
+  </view>
+</template>
+
+<script>
+import Empty from '@/components/empty'
+
+export default {
+  name: 'Goods',
+  components: {
+    Empty
+  },
+  /**
+   * 组件的属性列表
+   * 用于组件自定义设置
+   */
+  props: {
+    itemIndex: String,
+    itemStyle: Object,
+    params: Object,
+    dataList: Array
+  },
+
+  /**
+   * 组件的方法列表
+   * 更新属性和数据的方法与更新页面数据的方法类似
+   */
+  methods: {
+    /**
+     * 跳转商品详情页
+     */
+    onTargetGoods(goodsId) {
+      this.$navTo(`pages/goods/detail`, {goodsId})
+    },
+    // 立即购买
+    buyNow(selectShop) {
+      console.log('选择商品:', selectShop)
+      // 跳转到订单结算页
+      this.$navTo('pages/checkout/index', {
+        mode: 'buyNow',
+        goodsId: selectShop.productid,
+        goodsNum: '3',
+        goodsSkuId: '2'
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.diy-goods {
+  .goods-list {
+    padding: 4rpx;
+    box-sizing: border-box;
+    .twoline-xiao {
+      color: #999;
+      font-size: 22rpx;
+    }
+
+    .goods-item {
+      box-sizing: border-box;
+      padding: 6rpx;
+      overflow: hidden;
+
+      .goods-image {
+        border-radius: 15rpx;
+        text-align: center;
+        color: #3a4b5f;
+        line-height: 40rpx;
+        position: relative;
+        width: 100%;
+        height: 0;
+        padding-bottom: 100%;
+        overflow: hidden;
+        background-image: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%);
+        &:after {
+          content: '';
+          display: block;
+          margin-top: 100%;
+        }
+
+        .image {
+          position: absolute;
+          width: 100%;
+          height: 100%;
+          top: 0;
+          left: 0;
+          -o-object-fit: cover;
+          object-fit: cover;
+        }
+      }
+
+      .detail {
+        padding: 8rpx;
+        background: #fff;
+
+        .goods-name {
+          min-height: 68rpx;
+          line-height: 1.3;
+          white-space: normal;
+          color: #484848;
+          font-size: 26rpx;
+          margin-bottom: 4rpx;
+        }
+
+        .detail-price {
+          .goods-price {
+            margin-right: 8rpx;
+          }
+
+          .line-price {
+            text-decoration: line-through;
+          }
+        }
+      }
+    }
+
+    &.display__slide {
+      white-space: nowrap;
+      font-size: 0;
+
+      .goods-item {
+        display: inline-block;
+      }
+    }
+
+    &.display__list {
+      .goods-item {
+        float: left;
+      }
+    }
+
+    &.column__2 {
+      .goods-item {
+        width: 50%;
+      }
+    }
+
+    &.column__3 {
+      .goods-item {
+        width: 33.33333%;
+      }
+    }
+
+    &.column__1 {
+      .goods-item {
+        width: 100%;
+        height: 280rpx;
+        margin-bottom: 12rpx;
+        padding: 20rpx;
+        box-sizing: border-box;
+        background: #fff;
+        line-height: 1.6;
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+      }
+
+      .goods-item-left {
+        display: flex;
+        width: 40%;
+        background: #fff;
+        align-items: center;
+
+        .image {
+          display: block;
+          width: 240rpx;
+          height: 240rpx;
+        }
+      }
+
+      .goods-item-right {
+        position: relative;
+        width: 60%;
+
+        .goods-name {
+          margin-top: 20rpx;
+          min-height: 68rpx;
+          line-height: 1.3;
+          white-space: normal;
+          color: #484848;
+          font-size: 26rpx;
+        }
+      }
+
+      .goods-item-desc {
+        margin-top: 8rpx;
+      }
+
+      .desc-selling-point {
+        width: 400rpx;
+        font-size: 24rpx;
+        color: #e49a3d;
+      }
+
+      .desc-goods-sales {
+        color: #999;
+        font-size: 24rpx;
+      }
+
+      .desc-footer {
+        font-size: 24rpx;
+
+        .price-x {
+          margin-right: 16rpx;
+          color: $main-bg;
+          font-size: 30rpx;
+        }
+
+        .price-y {
+          text-decoration: line-through;
+        }
+      }
+    }
+  }
+}
+</style>

Vissa filer visades inte eftersom för många filer har ändrats