Chopper 5 роки тому
коміт
ec3e958037
100 змінених файлів з 7254 додано та 0 видалено
  1. 17 0
      .idea/codeStyles/Project.xml
  2. 5 0
      .idea/codeStyles/codeStyleConfig.xml
  3. 6 0
      .idea/inspectionProfiles/Project_Default.xml
  4. 12 0
      .idea/lili-shop-ui.iml
  5. 6 0
      .idea/misc.xml
  6. 8 0
      .idea/modules.xml
  7. 6 0
      .idea/vcs.xml
  8. 380 0
      .idea/workspace.xml
  9. 15 0
      .vscode/launch.json
  10. 6 0
      .vscode/settings.json
  11. 12 0
      buyer/.babelrc
  12. 9 0
      buyer/.editorconfig
  13. 4 0
      buyer/.eslintignore
  14. 33 0
      buyer/.eslintrc.js
  15. 15 0
      buyer/.gitignore
  16. 10 0
      buyer/.postcssrc.js
  17. 10 0
      buyer/.travis.yml
  18. 8 0
      buyer/Dockerfile
  19. 201 0
      buyer/LICENSE
  20. 0 0
      buyer/README-ZH.md
  21. 25 0
      buyer/README.md
  22. 41 0
      buyer/build/build.js
  23. 54 0
      buyer/build/check-versions.js
  24. 110 0
      buyer/build/utils.js
  25. 22 0
      buyer/build/vue-loader.conf.js
  26. 103 0
      buyer/build/webpack.base.conf.js
  27. 95 0
      buyer/build/webpack.dev.conf.js
  28. 145 0
      buyer/build/webpack.prod.conf.js
  29. 8 0
      buyer/config/dev.env.js
  30. 84 0
      buyer/config/index.js
  31. 5 0
      buyer/config/prod.env.js
  32. 1 0
      buyer/docker.sh
  33. 14 0
      buyer/index.html
  34. 51 0
      buyer/nginx.conf
  35. 90 0
      buyer/package.json
  36. 37 0
      buyer/src/App.vue
  37. 59 0
      buyer/src/api/account.js
  38. 60 0
      buyer/src/api/address.js
  39. 214 0
      buyer/src/api/cart.js
  40. 83 0
      buyer/src/api/common.js
  41. 73 0
      buyer/src/api/goods.js
  42. 35 0
      buyer/src/api/index.js
  43. 94 0
      buyer/src/api/login.js
  44. 443 0
      buyer/src/api/member.js
  45. 98 0
      buyer/src/api/order.js
  46. 48 0
      buyer/src/api/pay.js
  47. 48 0
      buyer/src/api/promotion.js
  48. 77 0
      buyer/src/api/shopentry.js
  49. 3 0
      buyer/src/assets/iconfont/iconfont.css
  50. BIN
      buyer/src/assets/iconfont/iconfont.eot
  51. 0 0
      buyer/src/assets/iconfont/iconfont.js
  52. 51 0
      buyer/src/assets/iconfont/iconfont.json
  53. 37 0
      buyer/src/assets/iconfont/iconfont.svg
  54. BIN
      buyer/src/assets/iconfont/iconfont.ttf
  55. BIN
      buyer/src/assets/iconfont/iconfont.woff
  56. BIN
      buyer/src/assets/iconfont/iconfont.woff2
  57. BIN
      buyer/src/assets/images/R168dc76973e0ae1cb3f6235b5a8cc0da.png
  58. BIN
      buyer/src/assets/images/arrow-left.png
  59. BIN
      buyer/src/assets/images/cart-coupon-icons02.png
  60. BIN
      buyer/src/assets/images/empty.png
  61. BIN
      buyer/src/assets/images/feedback.png
  62. BIN
      buyer/src/assets/images/festival_icon.png
  63. BIN
      buyer/src/assets/images/footer/ico_service.png
  64. BIN
      buyer/src/assets/images/geted.png
  65. BIN
      buyer/src/assets/images/goodsDetail/price-bg.png
  66. BIN
      buyer/src/assets/images/logo.png
  67. BIN
      buyer/src/assets/images/logo1.png
  68. BIN
      buyer/src/assets/images/pay-success.png
  69. BIN
      buyer/src/assets/images/seckillBg.png
  70. BIN
      buyer/src/assets/images/small-circle.png
  71. 92 0
      buyer/src/assets/styles/coupon.scss
  72. 125 0
      buyer/src/assets/styles/global.scss
  73. 58 0
      buyer/src/assets/styles/goodsList.scss
  74. 84 0
      buyer/src/assets/styles/theme.less
  75. 114 0
      buyer/src/assets/styles/theme.scss
  76. 149 0
      buyer/src/components/Search.vue
  77. 173 0
      buyer/src/components/addressManage/index.vue
  78. 42 0
      buyer/src/components/advertising/FixedTop.vue
  79. 2 0
      buyer/src/components/advertising/README.md
  80. 12 0
      buyer/src/components/card/README.md
  81. 149 0
      buyer/src/components/card/index.vue
  82. 110 0
      buyer/src/components/coupon/Coupon.vue
  83. 323 0
      buyer/src/components/drawer/Drawer.vue
  84. 155 0
      buyer/src/components/drawer/Main.vue
  85. 42 0
      buyer/src/components/drawer/README.md
  86. 63 0
      buyer/src/components/drawer/config.js
  87. 130 0
      buyer/src/components/drawer/drawer.scss
  88. 45 0
      buyer/src/components/empty/Main.vue
  89. 10 0
      buyer/src/components/empty/README.md
  90. 189 0
      buyer/src/components/footer/Footer.vue
  91. 54 0
      buyer/src/components/global.js
  92. 123 0
      buyer/src/components/goodsDetail/Promotion.vue
  93. 658 0
      buyer/src/components/goodsDetail/ShowGoods.vue
  94. 489 0
      buyer/src/components/goodsDetail/ShowGoodsDetail.vue
  95. 53 0
      buyer/src/components/goodsDetail/ShowGoodsQuestion.vue
  96. 85 0
      buyer/src/components/goodsDetail/ShowProductWarranty.vue
  97. 426 0
      buyer/src/components/header/Header.vue
  98. 102 0
      buyer/src/components/header/ShopHeader.vue
  99. 93 0
      buyer/src/components/header/hoverSearch.vue
  100. 33 0
      buyer/src/components/indexDecorate/modelForm.vue

+ 17 - 0
.idea/codeStyles/Project.xml

@@ -0,0 +1,17 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <option name="OTHER_INDENT_OPTIONS">
+      <value>
+        <option name="INDENT_SIZE" value="2" />
+        <option name="TAB_SIZE" value="2" />
+      </value>
+    </option>
+    <codeStyleSettings language="JavaScript">
+      <indentOptions>
+        <option name="INDENT_SIZE" value="2" />
+        <option name="CONTINUATION_INDENT_SIZE" value="0" />
+        <option name="TAB_SIZE" value="2" />
+      </indentOptions>
+    </codeStyleSettings>
+  </code_scheme>
+</component>

+ 5 - 0
.idea/codeStyles/codeStyleConfig.xml

@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>

+ 6 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
+  </profile>
+</component>

+ 12 - 0
.idea/lili-shop-ui.iml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/.tmp" />
+      <excludeFolder url="file://$MODULE_DIR$/temp" />
+      <excludeFolder url="file://$MODULE_DIR$/tmp" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
.idea/misc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/lili-shop-ui.iml" filepath="$PROJECT_DIR$/.idea/lili-shop-ui.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 380 - 0
.idea/workspace.xml

@@ -0,0 +1,380 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="BranchesTreeState">
+    <expand>
+      <path>
+        <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
+        <item name="LOCAL_ROOT" type="e8cecc67:BranchNodeDescriptor" />
+      </path>
+      <path>
+        <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
+        <item name="REMOTE_ROOT" type="e8cecc67:BranchNodeDescriptor" />
+      </path>
+      <path>
+        <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
+        <item name="REMOTE_ROOT" type="e8cecc67:BranchNodeDescriptor" />
+        <item name="GROUP_NODE:origin" type="e8cecc67:BranchNodeDescriptor" />
+      </path>
+    </expand>
+    <select />
+  </component>
+  <component name="ChangeListManager">
+    <list default="true" id="7e964aa0-753b-43f7-854a-2942a3e76fe4" name="默认更改列表" comment="店铺设置">
+      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
+<<<<<<< HEAD
+      <change beforePath="$PROJECT_DIR$/buyer/src/pages/home/orderCenter/AddAddress.vue" beforeDir="false" afterPath="$PROJECT_DIR$/buyer/src/pages/home/orderCenter/AddAddress.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/manager/src/config/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/manager/src/config/index.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/seller/src/config/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/seller/src/config/index.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/seller/src/views/statistics/goods/goodsStatistics.vue" beforeDir="false" afterPath="$PROJECT_DIR$/seller/src/views/statistics/goods/goodsStatistics.vue" afterDir="false" />
+=======
+      <change beforePath="$PROJECT_DIR$/manager/src/views/goods-unit/index.vue" beforeDir="false" afterPath="$PROJECT_DIR$/manager/src/views/goods-unit/index.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/manager/src/views/sensitiveWords/index.vue" beforeDir="false" afterPath="$PROJECT_DIR$/manager/src/views/sensitiveWords/index.vue" afterDir="false" />
+>>>>>>> new-lmr
+    </list>
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="FileTemplateManagerImpl">
+    <option name="RECENT_TEMPLATES">
+      <list>
+        <option value="Vue File" />
+        <option value="Vue Single File Component" />
+        <option value="JavaScript File" />
+      </list>
+    </option>
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
+  </component>
+  <component name="ProjectId" id="1gGPnr0TWcTGoYhLT7QHFe5MrX4" />
+  <component name="ProjectLevelVcsManager">
+    <ConfirmationsSetting value="2" id="Add" />
+  </component>
+  <component name="ProjectViewState">
+    <option name="hideEmptyMiddlePackages" value="true" />
+    <option name="showLibraryContents" value="true" />
+  </component>
+  <component name="PropertiesComponent">
+    <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
+    <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
+    <property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
+    <property name="WebServerToolWindowFactoryState" value="false" />
+<<<<<<< HEAD
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/manager/src/api" />
+=======
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/manager/src/views/sys/setting-manage" />
+>>>>>>> new-lmr
+    <property name="node.js.detected.package.eslint" value="true" />
+    <property name="node.js.detected.package.tslint" value="true" />
+    <property name="node.js.path.for.package.eslint" value="project" />
+    <property name="node.js.path.for.package.tslint" value="project" />
+    <property name="node.js.selected.package.eslint" value="(autodetect)" />
+    <property name="node.js.selected.package.tslint" value="(autodetect)" />
+    <property name="nodejs_package_manager_path" value="npm" />
+    <property name="settings.editor.selected.configurable" value="preferences.sourceCode.XML" />
+    <property name="ts.external.directory.path" value="$APPLICATION_HOME_DIR$/plugins/JavaScriptLanguage/jsLanguageServicesImpl/external" />
+  </component>
+  <component name="RecentsManager">
+    <key name="CopyFile.RECENT_KEYS">
+<<<<<<< HEAD
+      <recent name="$PROJECT_DIR$/manager/src/api" />
+      <recent name="$PROJECT_DIR$/manager/src/views/my-components" />
+=======
+      <recent name="$PROJECT_DIR$/manager/src/views/sys/setting-manage" />
+>>>>>>> new-lmr
+      <recent name="$PROJECT_DIR$/buyer/src/components/verify" />
+      <recent name="$PROJECT_DIR$/buyer/src/components/change" />
+    </key>
+    <key name="MoveFile.RECENT_KEYS">
+      <recent name="$PROJECT_DIR$/buyer/src/components/change" />
+      <recent name="$PROJECT_DIR$/buyer/src/pages/home/memberCenter" />
+      <recent name="$PROJECT_DIR$/buyer/src/pages/home/orderCenter" />
+      <recent name="$PROJECT_DIR$/buyer/src/pages/home" />
+      <recent name="$PROJECT_DIR$/buyer/src/components/home/order" />
+    </key>
+  </component>
+  <component name="RunManager">
+    <configuration name="Home.vue" type="JavascriptDebugType" temporary="true" nameIsGenerated="true" uri="http://localhost:63342/lili-shop-ui/buyer/src/page/user/Home.vue" useBuiltInWebServerPort="true">
+      <method v="2" />
+    </configuration>
+    <recent_temporary>
+      <list>
+        <item itemvalue="JavaScript Debug.Home.vue" />
+      </list>
+    </recent_temporary>
+  </component>
+  <component name="SvnConfiguration">
+    <configuration />
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="7e964aa0-753b-43f7-854a-2942a3e76fe4" name="默认更改列表" comment="" />
+      <created>1597738125477</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1597738125477</updated>
+      <workItem from="1597738127938" duration="6268000" />
+      <workItem from="1597825716012" duration="3768000" />
+      <workItem from="1597830090678" duration="7632000" />
+      <workItem from="1597972262326" duration="865000" />
+      <workItem from="1597974893526" duration="112000" />
+      <workItem from="1597975021121" duration="17000" />
+      <workItem from="1597975216701" duration="17254000" />
+      <workItem from="1598184961388" duration="14000" />
+      <workItem from="1598233017859" duration="10307000" />
+      <workItem from="1598259954059" duration="2720000" />
+      <workItem from="1598318520176" duration="726000" />
+      <workItem from="1598319433942" duration="106000" />
+      <workItem from="1598319554477" duration="13265000" />
+      <workItem from="1598406300597" duration="6030000" />
+      <workItem from="1598422866858" duration="7787000" />
+      <workItem from="1598491104901" duration="37245000" />
+      <workItem from="1606876680167" duration="61000" />
+      <workItem from="1611131536390" duration="5616000" />
+      <workItem from="1611537245379" duration="828000" />
+      <workItem from="1611538362285" duration="350000" />
+      <workItem from="1611560515629" duration="834000" />
+      <workItem from="1611650051294" duration="3000" />
+      <workItem from="1611818003333" duration="239000" />
+      <workItem from="1613989205923" duration="7005000" />
+      <workItem from="1614076082765" duration="8973000" />
+      <workItem from="1614142479169" duration="2129000" />
+      <workItem from="1614162982178" duration="8343000" />
+      <workItem from="1614228135182" duration="3456000" />
+      <workItem from="1614248866969" duration="8023000" />
+      <workItem from="1614313313338" duration="83000" />
+      <workItem from="1614313447954" duration="211000" />
+    </task>
+    <task id="LOCAL-00001" summary="管理端页面优化">
+      <created>1613996019944</created>
+      <option name="number" value="00001" />
+      <option name="presentableId" value="LOCAL-00001" />
+      <option name="project" value="LOCAL" />
+      <updated>1613996019944</updated>
+    </task>
+    <task id="LOCAL-00002" summary="修复修改商家地址报错问题">
+      <created>1613996389449</created>
+      <option name="number" value="00002" />
+      <option name="presentableId" value="LOCAL-00002" />
+      <option name="project" value="LOCAL" />
+      <updated>1613996389449</updated>
+    </task>
+    <task id="LOCAL-00003" summary="去掉id">
+      <created>1614076370139</created>
+      <option name="number" value="00003" />
+      <option name="presentableId" value="LOCAL-00003" />
+      <option name="project" value="LOCAL" />
+      <updated>1614076370139</updated>
+    </task>
+    <task id="LOCAL-00004" summary="选择物流规则不能选择物流模板">
+      <created>1614080544415</created>
+      <option name="number" value="00004" />
+      <option name="presentableId" value="LOCAL-00004" />
+      <option name="project" value="LOCAL" />
+      <updated>1614080544415</updated>
+    </task>
+    <task id="LOCAL-00005" summary="修改查询条件样式">
+      <created>1614143298555</created>
+      <option name="number" value="00005" />
+      <option name="presentableId" value="LOCAL-00005" />
+      <option name="project" value="LOCAL" />
+      <updated>1614143298555</updated>
+    </task>
+    <task id="LOCAL-00006" summary="修改样式">
+      <created>1614143902874</created>
+      <option name="number" value="00006" />
+      <option name="presentableId" value="LOCAL-00006" />
+      <option name="project" value="LOCAL" />
+      <updated>1614143902874</updated>
+    </task>
+    <task id="LOCAL-00007" summary="修改更新时间为NAN">
+      <created>1614144541858</created>
+      <option name="number" value="00007" />
+      <option name="presentableId" value="LOCAL-00007" />
+      <option name="project" value="LOCAL" />
+      <updated>1614144541858</updated>
+    </task>
+    <task id="LOCAL-00008" summary="修复app版本无法添加和修改">
+      <created>1614230386509</created>
+      <option name="number" value="00008" />
+      <option name="presentableId" value="LOCAL-00008" />
+      <option name="project" value="LOCAL" />
+      <updated>1614230386509</updated>
+    </task>
+    <task id="LOCAL-00009" summary="去掉打印">
+      <created>1614230407722</created>
+      <option name="number" value="00009" />
+      <option name="presentableId" value="LOCAL-00009" />
+      <option name="project" value="LOCAL" />
+      <updated>1614230407722</updated>
+    </task>
+    <task id="LOCAL-00010" summary="去掉多选">
+      <created>1614230544808</created>
+      <option name="number" value="00010" />
+      <option name="presentableId" value="LOCAL-00010" />
+      <option name="project" value="LOCAL" />
+      <updated>1614230544808</updated>
+    </task>
+    <task id="LOCAL-00011" summary="修改消息标题宽度">
+      <created>1614230627881</created>
+      <option name="number" value="00011" />
+      <option name="presentableId" value="LOCAL-00011" />
+      <option name="project" value="LOCAL" />
+      <updated>1614230627881</updated>
+    </task>
+    <task id="LOCAL-00012" summary="店铺相关js提交">
+      <created>1614231645143</created>
+      <option name="number" value="00012" />
+      <option name="presentableId" value="LOCAL-00012" />
+      <option name="project" value="LOCAL" />
+      <updated>1614231645143</updated>
+    </task>
+    <task id="LOCAL-00013" summary="店铺修改">
+      <created>1614231785359</created>
+      <option name="number" value="00013" />
+      <option name="presentableId" value="LOCAL-00013" />
+      <option name="project" value="LOCAL" />
+      <updated>1614231785359</updated>
+    </task>
+    <task id="LOCAL-00014" summary="优化页面">
+      <created>1614249105672</created>
+      <option name="number" value="00014" />
+      <option name="presentableId" value="LOCAL-00014" />
+      <option name="project" value="LOCAL" />
+      <updated>1614249105672</updated>
+    </task>
+    <task id="LOCAL-00015" summary="页面优化">
+      <created>1614258980903</created>
+      <option name="number" value="00015" />
+      <option name="presentableId" value="LOCAL-00015" />
+      <option name="project" value="LOCAL" />
+      <updated>1614258980903</updated>
+    </task>
+    <task id="LOCAL-00016" summary="页面优化">
+      <created>1614259068248</created>
+      <option name="number" value="00016" />
+      <option name="presentableId" value="LOCAL-00016" />
+      <option name="project" value="LOCAL" />
+      <updated>1614259068248</updated>
+    </task>
+    <task id="LOCAL-00017" summary="页面优化">
+      <created>1614313690732</created>
+      <option name="number" value="00017" />
+      <option name="presentableId" value="LOCAL-00017" />
+      <option name="project" value="LOCAL" />
+      <updated>1614313690732</updated>
+    </task>
+    <option name="localTasksCounter" value="18" />
+    <servers />
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="1" />
+  </component>
+  <component name="Vcs.Log.History.Properties">
+    <option name="COLUMN_ID_ORDER">
+      <list>
+        <option value="Default.Root" />
+        <option value="Default.Author" />
+        <option value="Default.Date" />
+        <option value="Default.Subject" />
+      </list>
+    </option>
+  </component>
+  <component name="Vcs.Log.Tabs.Properties">
+    <option name="TAB_STATES">
+      <map>
+        <entry key="1">
+          <value>
+            <State>
+              <option name="SHOW_ONLY_AFFECTED_CHANGES" value="true" />
+              <option name="FILTERS">
+                <map>
+                  <entry key="branch">
+                    <value>
+                      <list>
+                        <option value="HEAD" />
+                      </list>
+                    </value>
+                  </entry>
+                  <entry key="structure">
+                    <value>
+                      <list>
+                        <option value="dir:/Users/liushuai/Documents/workspace/lili-shop-ui/seller/src/router" />
+                      </list>
+                    </value>
+                  </entry>
+                </map>
+              </option>
+            </State>
+          </value>
+        </entry>
+        <entry key="MAIN">
+          <value>
+            <State>
+              <option name="COLUMN_ORDER" />
+            </State>
+          </value>
+        </entry>
+      </map>
+    </option>
+  </component>
+  <component name="VcsManagerConfiguration">
+    <MESSAGE value="店铺设置" />
+    <MESSAGE value="文件排序问题处理" />
+    <MESSAGE value="管理端页面优化" />
+    <MESSAGE value="修复修改商家地址报错问题" />
+    <MESSAGE value="去掉id" />
+    <MESSAGE value="选择物流规则不能选择物流模板" />
+    <MESSAGE value="修改查询条件样式" />
+    <MESSAGE value="修改样式" />
+    <MESSAGE value="修改更新时间为NAN" />
+    <MESSAGE value="修复app版本无法添加和修改" />
+    <MESSAGE value="去掉打印" />
+    <MESSAGE value="去掉多选" />
+    <MESSAGE value="修改消息标题宽度" />
+    <MESSAGE value="店铺相关js提交" />
+    <MESSAGE value="店铺修改" />
+    <MESSAGE value="优化页面" />
+    <MESSAGE value="页面优化" />
+    <option name="LAST_COMMIT_MESSAGE" value="页面优化" />
+  </component>
+  <component name="WindowStateProjectService">
+    <state x="2074" y="292" key="CommitChangelistDialog2" timestamp="1614313690605">
+      <screen x="0" y="23" width="3440" height="1333" />
+    </state>
+    <state x="2074" y="292" key="CommitChangelistDialog2/0.23.3440.1333@0.23.3440.1333" timestamp="1614313690605" />
+    <state x="1660" y="123" width="1572" height="1133" key="DiffContextDialog" timestamp="1614231764977">
+      <screen x="0" y="23" width="3440" height="1333" />
+    </state>
+    <state x="1660" y="123" width="1572" height="1133" key="DiffContextDialog/0.23.3440.1333@0.23.3440.1333" timestamp="1614231764977" />
+    <state x="2046" y="418" key="Vcs.Push.Dialog.v2" timestamp="1614313692405">
+      <screen x="0" y="23" width="3440" height="1333" />
+    </state>
+    <state x="2046" y="418" key="Vcs.Push.Dialog.v2/0.23.3440.1333@0.23.3440.1333" timestamp="1614313692405" />
+    <state x="2055" y="414" width="782" height="550" key="find.popup" timestamp="1614256597933">
+      <screen x="0" y="23" width="3440" height="1333" />
+    </state>
+    <state x="2055" y="414" width="782" height="550" key="find.popup/0.23.3440.1333@0.23.3440.1333" timestamp="1614256597933" />
+    <state x="2111" y="325" key="run.anything.popup" timestamp="1613991372498">
+      <screen x="0" y="23" width="3440" height="1333" />
+    </state>
+    <state x="2111" y="325" key="run.anything.popup/0.23.3440.1333@0.23.3440.1333" timestamp="1613991372498" />
+    <state x="2111" y="327" width="670" height="676" key="search.everywhere.popup" timestamp="1614254687317">
+      <screen x="0" y="23" width="3440" height="1333" />
+    </state>
+    <state x="2111" y="327" width="670" height="676" key="search.everywhere.popup/0.23.3440.1333@0.23.3440.1333" timestamp="1614254687317" />
+  </component>
+  <component name="XDebuggerManager">
+    <breakpoint-manager>
+      <breakpoints>
+        <line-breakpoint enabled="true" type="javascript">
+          <url>file://$PROJECT_DIR$/manager/src/views/sys/monitor/monitor.vue</url>
+          <line>5</line>
+          <option name="timeStamp" value="2" />
+        </line-breakpoint>
+      </breakpoints>
+    </breakpoint-manager>
+  </component>
+</project>

+ 15 - 0
.vscode/launch.json

@@ -0,0 +1,15 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "pwa-chrome",
+            "request": "launch",
+            "name": "Launch Chrome against localhost",
+            "url": "http://localhost:9999",
+            "webRoot": "${workspaceFolder}"
+        }
+    ]
+}

+ 6 - 0
.vscode/settings.json

@@ -0,0 +1,6 @@
+{
+  "cSpell.ignoreWords": [
+    "cancel",
+    "role"
+  ]
+}

+ 12 - 0
buyer/.babelrc

@@ -0,0 +1,12 @@
+{
+  "presets": [
+    ["env", {
+      "modules": false,
+      "targets": {
+        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
+      }
+    }],
+    "stage-2"
+  ],
+  "plugins": ["transform-vue-jsx", "transform-runtime"]
+}

+ 9 - 0
buyer/.editorconfig

@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true

+ 4 - 0
buyer/.eslintignore

@@ -0,0 +1,4 @@
+/build/
+/config/
+/dist/
+/*.js

+ 33 - 0
buyer/.eslintrc.js

@@ -0,0 +1,33 @@
+// https://eslint.org/docs/user-guide/configuring
+
+module.exports = {
+  root: true,
+  parserOptions: {
+    parser: 'babel-eslint'
+  },
+  env: {
+    browser: true,
+  },
+  extends: [
+    // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
+    // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
+    'plugin:vue/essential',
+    // https://github.com/standard/standard/blob/master/docs/RULES-en.md
+    'standard'
+  ],
+  // required to lint *.vue files
+  plugins: [
+    'vue'
+  ],
+  // add your custom rules here
+  rules: {
+    "semi": [0, "never"],
+    // allow async-await
+    'generator-star-spacing': 'off',
+    // allow debugger during development
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+    "vue/no-parsing-error": [2, {
+      "x-invalid-end-tag": false
+    }]
+  }
+}

+ 15 - 0
buyer/.gitignore

@@ -0,0 +1,15 @@
+.DS_Store
+node_modules/
+/dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# or directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+package-lock.json

+ 10 - 0
buyer/.postcssrc.js

@@ -0,0 +1,10 @@
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+  "plugins": {
+    "postcss-import": {},
+    "postcss-url": {},
+    // to  target browsers: use "browserslist" field in package.json
+    "autoprefixer": {}
+  }
+}

+ 10 - 0
buyer/.travis.yml

@@ -0,0 +1,10 @@
+language: node_js
+
+node_js:
+  - '8'
+
+install:
+  - npm i npm install && npm install
+
+script:
+  - npm run build

+ 8 - 0
buyer/Dockerfile

@@ -0,0 +1,8 @@
+FROM nginx:alpine
+
+RUN mkdir -p /app/
+COPY ./dist /app/
+COPY ./nginx.conf /etc/nginx/nginx.conf
+
+
+EXPOSE 80

+ 201 - 0
buyer/LICENSE

@@ -0,0 +1,201 @@
+                                 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:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) 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
+
+      (d) 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 [yyyy] [name of copyright owner]
+
+   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.

+ 0 - 0
buyer/README-ZH.md


+ 25 - 0
buyer/README.md

@@ -0,0 +1,25 @@
+### 全局css src/assets/styles
+
+### 工具类 src/plugins
+
+### 顶部广告页 src/components/advertising
+
+### 全部商品分类  components/nav
+
+### 底部导航栏   components/footer
+
+### 发票模态框  components/invoiceModal
+
+### 商品详情   pages/GoodsDetail.vue
+
+#### 商品详情组件  components/goodsDetail
+
+### 全部商品分类  pages/AllCategories.vue
+
+### 意见反馈 pages/Feedback.vue
+
+### 卡片的封装  components/card
+
+### 购物车    pages/Cart.vue
+
+

+ 41 - 0
buyer/build/build.js

@@ -0,0 +1,41 @@
+'use strict'
+require('./check-versions')()
+
+process.env.NODE_ENV = 'production'
+
+const ora = require('ora')
+const rm = require('rimraf')
+const path = require('path')
+const chalk = require('chalk')
+const webpack = require('webpack')
+const config = require('../config')
+const webpackConfig = require('./webpack.prod.conf')
+
+const spinner = ora('building for production...')
+spinner.start()
+
+rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
+  if (err) throw err
+  webpack(webpackConfig, (err, stats) => {
+    spinner.stop()
+    if (err) throw err
+    process.stdout.write(stats.toString({
+      colors: true,
+      modules: false,
+      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
+      chunks: false,
+      chunkModules: false
+    }) + '\n\n')
+
+    if (stats.hasErrors()) {
+      console.log(chalk.red('  Build failed with errors.\n'))
+      process.exit(1)
+    }
+
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+  })
+})

+ 54 - 0
buyer/build/check-versions.js

@@ -0,0 +1,54 @@
+'use strict'
+const chalk = require('chalk')
+const semver = require('semver')
+const packageConfig = require('../package.json')
+const shell = require('shelljs')
+
+function exec (cmd) {
+  return require('child_process').execSync(cmd).toString().trim()
+}
+
+const versionRequirements = [
+  {
+    name: 'node',
+    currentVersion: semver.clean(process.version),
+    versionRequirement: packageConfig.engines.node
+  }
+]
+
+if (shell.which('npm')) {
+  versionRequirements.push({
+    name: 'npm',
+    currentVersion: exec('npm --version'),
+    versionRequirement: packageConfig.engines.npm
+  })
+}
+
+module.exports = function () {
+  const warnings = []
+
+  for (let i = 0; i < versionRequirements.length; i++) {
+    const mod = versionRequirements[i]
+
+    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
+      warnings.push(mod.name + ': ' +
+        chalk.red(mod.currentVersion) + ' should be ' +
+        chalk.green(mod.versionRequirement)
+      )
+    }
+  }
+
+  if (warnings.length) {
+    console.log('')
+    console.log(chalk.yellow('To use this template, you must update following to modules:'))
+    console.log()
+
+    for (let i = 0; i < warnings.length; i++) {
+      const warning = warnings[i]
+      console.log('  ' + warning)
+    }
+
+    console.log()
+    process.exit(1)
+  }
+}

+ 110 - 0
buyer/build/utils.js

@@ -0,0 +1,110 @@
+'use strict'
+const path = require('path')
+const config = require('../config')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const packageConfig = require('../package.json')
+
+exports.assetsPath = function (_path) {
+  const assetsSubDirectory = process.env.NODE_ENV === 'production'
+    ? config.build.assetsSubDirectory
+    : config.dev.assetsSubDirectory
+
+  return path.posix.join(assetsSubDirectory, _path)
+}
+
+exports.cssLoaders = function (options) {
+  options = options || {}
+
+  const cssLoader = {
+    loader: 'css-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
+  const postcssLoader = {
+    loader: 'postcss-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
+  // generate loader string to be used with extract text plugin
+  function generateLoaders (loader, loaderOptions) {
+    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
+
+    if (loader) {
+      loaders.push({
+        loader: loader + '-loader',
+        options: Object.assign({}, loaderOptions, {
+          sourceMap: options.sourceMap
+        })
+      })
+    }
+
+    // Extract CSS when that option is specified
+    // (which is the case during production build)
+    if (options.extract) {
+      return ExtractTextPlugin.extract({
+        use: loaders,
+        fallback: 'vue-style-loader',
+        publicPath: '../../'
+      })
+    } else {
+      return ['vue-style-loader'].concat(loaders)
+    }
+  }
+
+  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
+  return {
+    css: generateLoaders(),
+    postcss: generateLoaders(),
+    less: generateLoaders('less' , { javascriptEnabled : true }),
+    sass: generateLoaders('sass', { indentedSyntax: true }),
+    scss: generateLoaders('sass').concat(
+      {
+        loader: 'sass-resources-loader',
+        options: {
+            //你自己的scss全局文件的路径
+          resources: path.resolve(__dirname, '../src/assets/styles/global.scss')
+        }
+      }
+    ),
+    stylus: generateLoaders('stylus'),
+    styl: generateLoaders('stylus')
+  }
+}
+
+// Generate loaders for standalone style files (outside of .vue)
+exports.styleLoaders = function (options) {
+  const output = []
+  const loaders = exports.cssLoaders(options)
+
+  for (const extension in loaders) {
+    const loader = loaders[extension]
+    output.push({
+      test: new RegExp('\\.' + extension + '$'),
+      use: loader
+    })
+  }
+
+  return output
+}
+
+exports.createNotifierCallback = () => {
+  const notifier = require('node-notifier')
+
+  return (severity, errors) => {
+    if (severity !== 'error') return
+
+    const error = errors[0]
+    const filename = error.file && error.file.split('!').pop()
+
+    notifier.notify({
+      title: packageConfig.name,
+      message: severity + ': ' + error.name,
+      subtitle: filename || '',
+      icon: path.join(__dirname, 'logo.png')
+    })
+  }
+}

+ 22 - 0
buyer/build/vue-loader.conf.js

@@ -0,0 +1,22 @@
+'use strict'
+const utils = require('./utils')
+const config = require('../config')
+const isProduction = process.env.NODE_ENV === 'production'
+const sourceMapEnabled = isProduction
+  ? config.build.productionSourceMap
+  : config.dev.cssSourceMap
+
+module.exports = {
+  loaders: utils.cssLoaders({
+    sourceMap: sourceMapEnabled,
+    extract: isProduction
+  }),
+  cssSourceMap: sourceMapEnabled,
+  cacheBusting: config.dev.cacheBusting,
+  transformToRequire: {
+    video: ['src', 'poster'],
+    source: 'src',
+    img: 'src',
+    image: 'xlink:href'
+  }
+}

+ 103 - 0
buyer/build/webpack.base.conf.js

@@ -0,0 +1,103 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const config = require('../config')
+const vueLoaderConfig = require('./vue-loader.conf')
+
+function resolve (dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+const createLintingRule = () => ({
+  test: /\.(js|vue)$/,
+  loader: 'eslint-loader',
+  enforce: 'pre',
+  include: [resolve('src'), resolve('test')],
+  options: {
+    formatter: require('eslint-friendly-formatter'),
+    emitWarning: !config.dev.showEslintErrorsInOverlay
+  }
+})
+
+module.exports = {
+  context: path.resolve(__dirname, '../'),
+  entry: {
+    app: './src/main.js'
+  },
+  output: {
+    path: config.build.assetsRoot,
+    filename: '[name].js',
+    publicPath: process.env.NODE_ENV === 'production'
+      ? config.build.assetsPublicPath
+      : config.dev.assetsPublicPath
+  },
+  resolve: {
+    extensions: ['.js', '.vue', '.json'],
+    alias: {
+      'vue$': 'vue/dist/vue.esm.js',
+      '@': resolve('src'),
+    }
+  },
+  module: {
+    rules: [
+      // ...(config.dev.useEslint ? [createLintingRule()] : []),
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        options: vueLoaderConfig
+      },
+      {
+        test: /\.scss$/,
+        loaders: ['style', 'css', 'sass']
+      },
+      // {
+
+      //   test: /\.less$/,
+        
+      //   loader: "style-loader!css-loader!less-loader",
+        
+      //   },
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
+      },
+      {
+        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('img/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('media/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+        }
+      }
+    ]
+  },
+  node: {
+    // prevent webpack from injecting useless setImmediate polyfill because Vue
+    // source contains it (although only uses it if it's native).
+    setImmediate: false,
+    // prevent webpack from injecting mocks to Node native modules
+    // that does not make sense for the client
+    dgram: 'empty',
+    fs: 'empty',
+    net: 'empty',
+    tls: 'empty',
+    child_process: 'empty'
+  }
+}

+ 95 - 0
buyer/build/webpack.dev.conf.js

@@ -0,0 +1,95 @@
+'use strict'
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const path = require('path')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+const portfinder = require('portfinder')
+
+const HOST = process.env.HOST
+const PORT = process.env.PORT && Number(process.env.PORT)
+
+const devWebpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
+  },
+  // cheap-module-eval-source-map is faster for development
+  devtool: config.dev.devtool,
+
+  // these devServer options should be customized in /config/index.js
+  devServer: {
+    clientLogLevel: 'warning',
+    historyApiFallback: {
+      rewrites: [
+        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
+      ],
+    },
+    hot: true,
+    contentBase: false, // since we use CopyWebpackPlugin.
+    compress: true,
+    host: HOST || config.dev.host,
+    port: PORT || config.dev.port,
+    open: config.dev.autoOpenBrowser,
+    overlay: config.dev.errorOverlay
+      ? { warnings: false, errors: true }
+      : false,
+    publicPath: config.dev.assetsPublicPath,
+    proxy: config.dev.proxyTable,
+    quiet: true, // necessary for FriendlyErrorsPlugin
+    watchOptions: {
+      poll: config.dev.poll,
+    }
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': require('../config/dev.env')
+    }),
+    new webpack.HotModuleReplacementPlugin(),
+    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
+    new webpack.NoEmitOnErrorsPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true
+    }),
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.dev.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+module.exports = new Promise((resolve, reject) => {
+  portfinder.basePort = process.env.PORT || config.dev.port
+  portfinder.getPort((err, port) => {
+    if (err) {
+      reject(err)
+    } else {
+      // publish the new Port, necessary for e2e tests
+      process.env.PORT = port
+      // add port to devServer config
+      devWebpackConfig.devServer.port = port
+
+      // Add FriendlyErrorsPlugin
+      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
+        compilationSuccessInfo: {
+          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
+        },
+        onErrors: config.dev.notifyOnErrors
+        ? utils.createNotifierCallback()
+        : undefined
+      }))
+
+      resolve(devWebpackConfig)
+    }
+  })
+})

+ 145 - 0
buyer/build/webpack.prod.conf.js

@@ -0,0 +1,145 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
+
+const env = require('../config/prod.env')
+
+const webpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({
+      sourceMap: config.build.productionSourceMap,
+      extract: false,
+      usePostCSS: true
+    })
+  },
+  devtool: config.build.productionSourceMap ? config.build.devtool : false,
+  output: {
+    path: config.build.assetsRoot,
+    filename: utils.assetsPath('js/[name].[chunkhash].js'),
+    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new UglifyJsPlugin({
+      uglifyOptions: {
+        compress: {
+          warnings: false
+        }
+      },
+      sourceMap: config.build.productionSourceMap,
+      parallel: true
+    }),
+    // extract css into its own file
+    new ExtractTextPlugin({
+      filename: utils.assetsPath('css/[name].[contenthash].css'),
+      // Setting the following option to `false` will not extract CSS from codesplit chunks.
+      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
+      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
+      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
+      allChunks: true,
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: config.build.productionSourceMap
+        ? { safe: true, map: { inline: false } }
+        : { safe: true }
+    }),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: config.build.index,
+      template: 'index.html',
+      inject: true,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // keep module.id stable when vendor modules does not change
+    new webpack.HashedModuleIdsPlugin(),
+    // enable scope hoisting
+    new webpack.optimize.ModuleConcatenationPlugin(),
+    // split vendor js into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'vendor',
+      minChunks (module) {
+        // any required modules inside node_modules are extracted to vendor
+        return (
+          module.resource &&
+          /\.js$/.test(module.resource) &&
+          module.resource.indexOf(
+            path.join(__dirname, '../node_modules')
+          ) === 0
+        )
+      }
+    }),
+    // extract webpack runtime and module manifest to its own file in order to
+    // prevent vendor hash from being updated whenever app bundle is updated
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'manifest',
+      minChunks: Infinity
+    }),
+    // This instance extracts shared chunks from code splitted chunks and bundles them
+    // in a separate chunk, similar to the vendor chunk
+    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'app',
+      async: 'vendor-async',
+      children: true,
+      minChunks: 3
+    }),
+
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+if (config.build.productionGzip) {
+  const CompressionWebpackPlugin = require('compression-webpack-plugin')
+
+  webpackConfig.plugins.push(
+    new CompressionWebpackPlugin({
+      asset: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: new RegExp(
+        '\\.(' +
+        config.build.productionGzipExtensions.join('|') +
+        ')$'
+      ),
+      threshold: 10240,
+      minRatio: 0.8
+    })
+  )
+}
+
+if (config.build.bundleAnalyzerReport) {
+  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+}
+
+module.exports = webpackConfig

+ 8 - 0
buyer/config/dev.env.js

@@ -0,0 +1,8 @@
+'use strict'
+const merge = require('webpack-merge')
+const prodEnv = require('./prod.env')
+
+module.exports = merge(prodEnv, {
+  NODE_ENV: '"development"',
+  BASE_URL:''
+})

+ 84 - 0
buyer/config/index.js

@@ -0,0 +1,84 @@
+'use strict'
+// Template version: 1.3.1
+// see http://vuejs-templates.github.io/webpack for documentation.
+
+const path = require('path')
+// const api = 'http://www.baidu.com'
+module.exports = {
+  dev: {
+
+    // Paths
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+    proxyTable: {
+      // '/api': {
+      //   target:api,
+      //   changeOrigin:true,
+      //   pathRewrite:{
+      //     '^/api':''
+      //   }
+      // }
+    },
+
+    // Various Dev Server settings
+    host: '0.0.0.0', // can be overwritten by process.env.HOST
+    port: 8889, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
+    autoOpenBrowser: false,
+    errorOverlay: true,
+    notifyOnErrors: true,
+    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
+
+    // Use Eslint Loader?
+    // If true, your code will be linted during bundling and
+    // linting errors and warnings will be shown in the console.
+    useEslint: false,
+    // If true, eslint errors and warnings will also be shown in the error overlay
+    // in the browser.
+    showEslintErrorsInOverlay: false,
+
+    /**
+     * Source Maps
+     */
+
+    // https://webpack.js.org/configuration/devtool/#development
+    devtool: 'cheap-module-eval-source-map',
+
+    // If you have problems debugging vue-files in devtools,
+    // set this to false - it *may* help
+    // https://vue-loader.vuejs.org/en/options.html#cachebusting
+    cacheBusting: true,
+
+    cssSourceMap: true
+  },
+
+  build: {
+    // Template for index.html
+    index: path.resolve(__dirname, '../dist/index.html'),
+
+    // Paths
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+
+    /**
+     * Source Maps
+     */
+
+    productionSourceMap: true,
+    // https://webpack.js.org/configuration/devtool/#production
+    devtool: '#source-map',
+
+    // Gzip off by default as many popular static hosts such as
+    // Surge or Netlify already gzip all static assets for you.
+    // Before setting to `true`, make sure to:
+    // npm install --save-dev compression-webpack-plugin
+    productionGzip: false,
+    productionGzipExtensions: ['js', 'css'],
+
+    // Run the build command with an extra argument to
+    // View the bundle analyzer report after build finishes:
+    // `npm run build --report`
+    // Set to `true` or `false` to always turn it on or off
+    bundleAnalyzerReport: process.env.npm_config_report
+  }
+}

+ 5 - 0
buyer/config/prod.env.js

@@ -0,0 +1,5 @@
+'use strict'
+module.exports = {
+  NODE_ENV: '"production"',
+  BASE_URL:'http://www.baidu.com'
+}

+ 1 - 0
buyer/docker.sh

@@ -0,0 +1 @@
+docker build -t registry.cn-beijing.aliyuncs.com/lili-images/buyer-ui-1.0.1:0.0.1 .

+ 14 - 0
buyer/index.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <link rel="icon" href="./static/logo.ico" type="image/x-icon">
+    <!-- <script src = 'https://webapi.amap.com/maps?v=2.0&key=b440952723253aa9fe483e698057bf7d'></script> -->
+    <title>LILI</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 51 - 0
buyer/nginx.conf

@@ -0,0 +1,51 @@
+#这个文件给docker用的
+#user  nobody;
+worker_processes  1;
+
+#error_log  logs/error.log;
+#error_log  logs/error.log  notice;
+#error_log  logs/error.log  info;
+
+#pid        logs/nginx.pid;
+
+
+events {
+    worker_connections  1024;
+}
+
+
+http {
+    include       mime.types;
+    default_type  application/octet-stream;
+
+    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
+    #                  '$status $body_bytes_sent "$http_referer" '
+    #                  '"$http_user_agent" "$http_x_forwarded_for"';
+
+    #access_log  logs/access.log  main;
+
+    sendfile        on;
+    #tcp_nopush     on;
+
+    #keepalive_timeout  0;
+    keepalive_timeout  65;
+    client_max_body_size 10m;
+    gzip on;
+    gzip_min_length  5k;
+    gzip_buffers     4 16k;
+    gzip_http_version 1.0;
+    gzip_comp_level 4;
+    gzip_types       text/plain application/x-javascript application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
+    gzip_vary on;
+
+    server {
+        listen       80;
+        server_name  localhost;
+
+        location / {
+            root /app;
+            try_files $uri $uri/ /index.html $uri/ =404;
+            index  index.html index.htm;
+        }
+    }
+}

+ 90 - 0
buyer/package.json

@@ -0,0 +1,90 @@
+{
+  "name": "lilishop-vue",
+  "version": "1.0.0",
+  "private": true,
+  "scripts": {
+    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
+    "start": "npm run dev",
+    "lint": "eslint --ext .js,.vue src",
+    "build": "node build/build.js"
+  },
+  "dependencies": {
+    "@amap/amap-jsapi-loader": "0.0.7",
+    "axios": "^0.19.2",
+    "js-cookie": "^2.2.1",
+    "less": "^3.12.2",
+    "less-loader": "^5.0.0",
+    "psl": "^1.8.0",
+    "qs": "^6.9.4",
+    "swiper": "^6.4.1",
+    "uuid": "^8.3.2",
+    "v-distpicker": "^1.0.17",
+    "view-design": "^4.3.2",
+    "vue": "^2.5.2",
+    "vue-awesome": "^4.0.2",
+    "vue-awesome-swiper": "^4.1.1",
+    "vue-piczoom": "^1.0.6",
+    "vue-qr": "^2.3.0",
+    "vue-router": "^3.0.1",
+    "vuex": "^3.0.1"
+  },
+  "devDependencies": {
+    "autoprefixer": "^7.1.2",
+    "babel-core": "^6.22.1",
+    "babel-eslint": "^8.2.1",
+    "babel-helper-vue-jsx-merge-props": "^2.0.3",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-syntax-jsx": "^6.18.0",
+    "babel-plugin-transform-runtime": "^6.22.0",
+    "babel-plugin-transform-vue-jsx": "^3.5.0",
+    "babel-preset-env": "^1.3.2",
+    "babel-preset-stage-2": "^6.22.0",
+    "chalk": "^2.0.1",
+    "copy-webpack-plugin": "^4.0.1",
+    "css-loader": "^0.28.0",
+    "eslint": "^4.15.0",
+    "eslint-config-standard": "^10.2.1",
+    "eslint-friendly-formatter": "^3.0.0",
+    "eslint-loader": "^1.7.1",
+    "eslint-plugin-import": "^2.7.0",
+    "eslint-plugin-node": "^5.2.0",
+    "eslint-plugin-promise": "^3.4.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "eslint-plugin-vue": "^4.0.0",
+    "extract-text-webpack-plugin": "^3.0.0",
+    "file-loader": "^1.1.4",
+    "friendly-errors-webpack-plugin": "^1.6.1",
+    "html-webpack-plugin": "^2.30.1",
+    "node-notifier": "^5.1.2",
+    "node-sass": "^4.14.1",
+    "optimize-css-assets-webpack-plugin": "^3.2.0",
+    "ora": "^1.2.0",
+    "portfinder": "^1.0.13",
+    "postcss-import": "^11.0.0",
+    "postcss-loader": "^2.0.8",
+    "postcss-url": "^7.2.1",
+    "rimraf": "^2.6.0",
+    "sass-loader": "^7.3.1",
+    "sass-resources-loader": "^2.0.3",
+    "semver": "^5.3.0",
+    "shelljs": "^0.7.6",
+    "uglifyjs-webpack-plugin": "^1.1.1",
+    "url-loader": "^0.5.8",
+    "vue-loader": "^13.3.0",
+    "vue-style-loader": "^3.0.1",
+    "vue-template-compiler": "^2.5.2",
+    "webpack": "^3.6.0",
+    "webpack-bundle-analyzer": "^2.9.0",
+    "webpack-dev-server": "^2.9.1",
+    "webpack-merge": "^4.1.0"
+  },
+  "engines": {
+    "node": ">= 6.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 8"
+  ]
+}

+ 37 - 0
buyer/src/App.vue

@@ -0,0 +1,37 @@
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<script>
+import {v4 as uuidv4} from 'uuid';
+import storage from '@/plugins/storage';
+import {getLogo} from '@/api/common.js';
+export default {
+  name: 'App',
+  mounted () {
+    let uuid = storage.getItem('uuid');
+    if (!uuid) {
+      uuid = uuidv4();
+      storage.setItem('uuid', uuid);
+    }
+    if (!this.Cookies.getItem('logo')) {
+      setTimeout(() => {
+        getLogo().then(res => {
+          if (res.success) {
+            let logoObj = JSON.parse(res.result.settingValue)
+            this.Cookies.setItem('logo', logoObj.buyerSideLogo)
+          }
+        })
+      }, 1000)
+    }
+  }
+};
+</script>
+<style lang="scss">
+#app{
+  min-height: 100%;
+  @include background_color($light_background_color);
+}
+</style>

+ 59 - 0
buyer/src/api/account.js

@@ -0,0 +1,59 @@
+// import {
+//   getRequest,
+//   postRequest,
+//   putRequest,
+//   deleteRequest,
+//   importRequest,
+//   getRequestWithNoToken
+// } from '@/axios/index.js';
+import request, {Method} from '@/plugins/request.js'
+
+// 获取密码状态
+export function getPwdStatus (params) {
+  return request({
+    url: '/buyer/members/wallet/check',
+    method: Method.GET,
+    needToken: true,
+    params
+  })
+}
+
+// 设置密码
+export function setPwd (params) {
+  return request({
+    url: '/buyer/members/wallet/set-password',
+    method: Method.POST,
+    needToken: true,
+    data: params
+  })
+}
+
+// 设置支付密码
+export function setUpdatePwdOrdinary (params) {
+  return request({
+    url: '/buyer/members/wallet/update-password/ordinary',
+    method: Method.GET,
+    needToken: true,
+    data: params
+  })
+}
+
+// 修改会员资料
+export function editMemberInfo (params) {
+  return request({
+    url: '/buyer/members/editOwn',
+    method: Method.PUT,
+    needToken: true,
+    data: params
+  })
+}
+
+// 修改密码
+export function editPwd (params) {
+  return request({
+    url: `/buyer/members/modifyPass`,
+    method: Method.PUT,
+    needToken: true,
+    data: params
+  })
+}

+ 60 - 0
buyer/src/api/address.js

@@ -0,0 +1,60 @@
+import request, {
+  Method, commonUrl
+} from '@/plugins/request.js';
+
+// 会员收货地址列表
+export function memberAddress () {
+  return request({
+    url: '/buyer/memberAddress',
+    needToken: true,
+    method: Method.GET
+  });
+}
+
+// 添加收货地址
+export function newMemberAddress (params) {
+  return request({
+    url: '/buyer/memberAddress',
+    needToken: true,
+    method: Method.POST,
+    data: params
+  });
+}
+
+// 编辑收货地址
+export function editMemberAddress (params) {
+  return request({
+    url: '/buyer/memberAddress',
+    needToken: true,
+    method: Method.PUT,
+    params
+  });
+}
+
+// 删除收货地址
+export function delMemberAddress (id) {
+  return request({
+    url: `/buyer/memberAddress/delById/${id}`,
+    needToken: true,
+    method: Method.DELETE
+  });
+}
+
+// 根据id获取会员地址详情
+export function getAddrDetail (id) {
+  return request({
+    url: `/buyer/memberAddress/get/${id}`,
+    needToken: true,
+    method: Method.GET
+  });
+}
+
+// 传给后台citycode 获取城市街道等id
+export function handleRegion (params) {
+  return request({
+    url: `${commonUrl}/common/region/region`,
+    needToken: true,
+    method: Method.GET,
+    params
+  });
+}

+ 214 - 0
buyer/src/api/cart.js

@@ -0,0 +1,214 @@
+import request, {
+  Method
+} from '@/plugins/request.js';
+
+/**
+ * 清空购物车
+ */
+export function clearCart () {
+  return request({
+    url: '/buyer/trade/carts',
+    method: Method.DELETE,
+    needToken: true
+  });
+}
+
+/**
+ * 获取购物车页面购物车详情
+ */
+export function cartGoodsAll () {
+  return request({
+    url: '/buyer/trade/carts/all',
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+/**
+ * 获取购物车商品数量
+ */
+export function cartCount () {
+  return request({
+    url: '/buyer/trade/carts/count',
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+/**
+ * 获取结算页面购物车详情
+ */
+export function cartGoodsPay (params) {
+  return request({
+    url: '/buyer/trade/carts/checked',
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 向购物车添加一个商品
+ * @param skuId skuId
+ * @param  num  购买数量
+ */
+export function addCartGoods (params) {
+  return request({
+    url: '/buyer/trade/carts',
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 创建交易
+ * @param client 客户端:H5/移动端 PC/PC端,WECHAT_MP/小程序端,APP/移动应用端
+ * @param way 购物车购买:CART/立即购买:BUY_NOW / 积分购买:POINT
+ * @param remark 备注  非必填
+ */
+export function createTrade (data) {
+  return request({
+    url: '/buyer/trade/carts/create/trade',
+    method: Method.POST,
+    needToken: true,
+    headers: {'Content-Type': 'application/json'},
+    data
+  });
+}
+
+/**
+ * 选择优惠券
+ * @param memberCouponId 优惠券id
+ * @param way 购物车购买:CART/立即购买:BUY_NOW/ 积分购买:POINT
+ * @param used 使用true 弃用 false
+ */
+export function selectCoupon (params) {
+  return request({
+    url: '/buyer/trade/carts/select/coupon',
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 可用优惠券数量
+ */
+export function couponNum (params) {
+  return request({
+    url: '/buyer/trade/carts/coupon/num',
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+/**
+ * 选择收货地址
+ * @param shippingAddressId 地址id
+ * @param way 购物车类型
+ */
+export function selectAddr (params) {
+  return request({
+    url: `/buyer/trade/carts/shippingAddress`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 选中购物车所有商品
+ * @param checked 设置选中 0,1
+ */
+export function setCheckedAll (params) {
+  return request({
+    url: `/buyer/trade/carts/sku/checked`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 批量设置某商家的商品为选中或不选中
+ * @param checked 是否选中
+ * @param storeId   商家id
+ */
+export function setCheckedSeller (params) {
+  return request({
+    url: `/buyer/trade/carts/store/${params.storeId}`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 选中购物车中单个产品
+ * @param skuId 产品id
+ * @param checked 设置选中0,1
+ */
+export function setCheckedGoods (params) {
+  return request({
+    url: `/buyer/trade/carts/sku/checked/${params.skuId}`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 更新购物车中单个产品数量
+ * @param skuId 产品id
+ * @param num   产品数量
+ */
+export function setCartGoodsNum (params) {
+  return request({
+    url: `/buyer/trade/carts/sku/num/${params.skuId}`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 删除购物车中一个或多个产品
+ * @param skuIds 产品id数组
+ */
+export function delCartGoods (params) {
+  return request({
+    url: `/buyer/trade/carts/sku/remove`,
+    method: Method.DELETE,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 选择配送方式
+ * @param shippingMethod SELF_PICK_UP(自提),LOCAL_TOWN_DELIVERY(同城配送),LOGISTICS(物流)
+ * @param way 购物方式
+ */
+export function shippingMethod (params) {
+  return request({
+    url: `/buyer/trade/carts/shippingMethod`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 选择发票
+ * @param receiptId 发票Id
+ * @param way 购物方式
+ */
+export function receiptSelect (params) {
+  return request({
+    url: `/buyer/trade/carts/select/receipt`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}

+ 83 - 0
buyer/src/api/common.js

@@ -0,0 +1,83 @@
+import request, {Method, commonUrl} from '@/plugins/request.js';
+
+/**
+ * 获取拼图验证
+ */
+export function getVerifyImg (verificationEnums) {
+  return request({
+    url: `${commonUrl}/common/slider/${verificationEnums}`,
+    method: Method.GET,
+    needToken: false
+  });
+}
+/**
+ * 验证码校验
+ */
+export function postVerifyImg (params) {
+  return request({
+    url: `${commonUrl}/common/slider/${params.verificationEnums}`,
+    method: Method.POST,
+    needToken: false,
+    params
+  });
+}
+/**
+ * 发送短信验证码
+ */
+export function sendSms (params) {
+  return request({
+    url: `${commonUrl}/common/sms/${params.verificationEnums}/${params.mobile}`,
+    method: Method.GET,
+    needToken: false,
+    params
+  });
+}
+/**
+ * 发送短信验证码
+ */
+export function getLogo () {
+  return request({
+    url: `${commonUrl}/common/logo`,
+    method: Method.GET,
+    needToken: false
+  });
+}
+// 地区数据,用于三级联动
+export function getRegion (id) {
+  return request({
+    url: `${commonUrl}/common/region/item/${id}`,
+    needToken: true,
+    method: Method.GET
+  });
+}
+
+/**
+ * 分页获取文章列表
+ * @param cateId 文章分类id
+ */
+export function articleList (params) {
+  return request({
+    url: `/buyer/article`,
+    method: Method.GET,
+    params
+  });
+}
+
+/**
+ * 获取帮助中心文章分类列表
+ * @param cateId 文章分类id
+ */
+export function articleCateList () {
+  return request({
+    url: `/buyer/article/articleCategory/list`,
+    method: Method.GET
+  });
+}
+
+// 通过id获取文章
+export function articleDetail (id) {
+  return request({
+    url: `/buyer/article/get/${id}`,
+    method: Method.GET
+  });
+}

+ 73 - 0
buyer/src/api/goods.js

@@ -0,0 +1,73 @@
+import request, {
+  Method
+} from '@/plugins/request.js';
+
+// 商品列表
+export function goodsList (params) {
+  return request({
+    url: '/buyer/goods/es',
+    method: Method.GET,
+    needToken: false,
+    params
+  });
+}
+
+// 商品分类 筛选条件  品牌, 尺寸等
+export function filterList (params) {
+  return request({
+    url: '/buyer/goods/es/related',
+    method: Method.GET,
+    needToken: false,
+    params
+  });
+}
+
+// id获取商品详情
+export function goodsDetail (goodsId) {
+  return request({
+    url: `/buyer/goods/get/${goodsId}`,
+    method: Method.GET,
+    needToken: false
+  });
+}
+
+// id获取商品Sku详情
+export function goodsSkuDetail (params) {
+  return request({
+    url: `/buyer/goods/sku/${params.goodsId}/${params.skuId}`,
+    method: Method.GET,
+    needToken: false,
+    params
+  });
+}
+
+// 获取所有商品分类
+export function getCategory (parentId) {
+  return request({
+    url: `/buyer/category/get/${parentId}`,
+    method: Method.GET,
+    needToken: false
+  });
+}
+
+// 当前拼团活动未成团会员
+export function pintuanMembers (pintuanId) {
+  return request({
+    url: `/buyer/promotion/pintuan/${pintuanId}/members`,
+    method: Method.GET,
+    needToken: false
+  });
+}
+
+/**
+ * @param {Number} start 搜索起始下标
+ * @param {Number} end 搜索结束下标
+ */
+export function hotWords (params) {
+  return request({
+    url: `/buyer/goods/hot-words`,
+    method: Method.GET,
+    needToken: false,
+    params
+  });
+}

+ 35 - 0
buyer/src/api/index.js

@@ -0,0 +1,35 @@
+import request, {Method} from '@/plugins/request.js'
+
+// 获取首页楼层装修数据
+export function indexData (params) {
+  return request({
+    url: '/buyer/pageData/getIndex',
+    method: Method.GET,
+    needToken: false,
+    params
+  })
+}
+
+/**
+ * 楼层装修数据
+ * @param pageClientType 客户端类型,可用值:PC,H5,WECHAT_MP,APP
+ * @param pageType 页面类型,可用值:INDEX,STORE,SPECIAL
+ */
+export function pageData (params) {
+  return request({
+    url: `/buyer/pageData`,
+    method: Method.GET,
+    needToken: false,
+    params
+  })
+}
+/**
+ * 刷新token
+ */
+export function handleRefreshToken (token) {
+  return request({
+    url: `/buyer/members/refresh/${token}`,
+    method: Method.GET,
+    needToken: false
+  })
+}

+ 94 - 0
buyer/src/api/login.js

@@ -0,0 +1,94 @@
+
+import request, {Method, buyerUrl} from '@/plugins/request.js';
+
+/**
+ * 注册
+ */
+export function regist (params) {
+  return request({
+    url: '/buyer/members/register',
+    method: Method.POST,
+    needToken: false,
+    data: params
+  });
+}
+
+/**
+ * 账号密码登录
+ */
+export function login (params) {
+  return request({
+    url: '/buyer/members/userLogin',
+    method: Method.POST,
+    needToken: false,
+    data: params,
+    headers: { 'clientType': 'PC' }
+  });
+}
+
+/**
+ * 手机号验证码登录
+ */
+export function smsLogin (params) {
+  return request({
+    url: '/buyer/members/smsLogin',
+    method: Method.POST,
+    needToken: false,
+    data: params,
+    headers: { 'clientType': 'PC' }
+  });
+}
+
+/**
+ * 获取用户信息
+ */
+export function getMemberMsg (params) {
+  return request({
+    url: '/buyer/members',
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 第三方登录 支付宝,微博,qq,微信
+ */
+export function webLogin (type) {
+  window.open(`${buyerUrl}/buyer/connect/login/web/${type}`, 'blank');
+}
+
+/**
+ * 第三方登录成功 回调接口
+ */
+export function loginCallback (uuid) {
+  return request({
+    url: `/buyer/connect/result?state=${uuid}`,
+    method: Method.GET,
+    needToken: false
+  });
+}
+
+/**
+ * 忘记密码  验证手机验证码
+ */
+export function validateCode (params) {
+  return request({
+    url: `/buyer/members/resetByMobile`,
+    method: Method.POST,
+    needToken: false,
+    params
+  });
+}
+
+/**
+ * 忘记密码 重置密码
+ */
+export function resetPassword (params) {
+  return request({
+    url: `/buyer/members/resetPassword`,
+    method: Method.POST,
+    needToken: false,
+    params
+  });
+}

+ 443 - 0
buyer/src/api/member.js

@@ -0,0 +1,443 @@
+import request, {Method} from '@/plugins/request.js';
+
+// 查询账户余额
+export function getMembersWallet () {
+  return request({
+    url: '/buyer/members/wallet',
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+// 查询余额列表
+export function getDepositLog (params) {
+  return request({
+    url: '/buyer/wallet/log',
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+// 查询充值记录
+export function getRecharge (params) {
+  return request({
+    url: '/buyer/member/recharge',
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+// 查询提现记录
+export function getWithdrawApply (params) {
+  return request({
+    url: '/buyer/member/withdrawApply',
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+// 在线充值
+export function recharge (params) {
+  return request({
+    url: '/buyer/trade/recharge',
+    method: Method.POST,
+    needToken: true,
+    data: params
+  });
+}
+
+// 提现
+export function withdrawalApply (params) {
+  return request({
+    url: '/buyer/members/wallet/withdrawal',
+    method: Method.POST,
+    needToken: true,
+    data: params
+  });
+}
+
+// 收藏商品、店铺
+export function collectGoods (type, id) {
+  return request({
+    url: `/buyer/member/collection/add/${type}/${id}`,
+    method: Method.POST,
+    needToken: true
+  });
+}
+
+// 取消 收藏商品、店铺
+export function cancelCollect (type, id) {
+  return request({
+    url: `/buyer/member/collection/delete/${type}/${id}`,
+    method: Method.DELETE,
+    needToken: true
+  });
+}
+
+// 查看是否收藏
+export function isCollection (type, goodsId) {
+  return request({
+    url: `/buyer/member/collection/isCollection/${type}/${goodsId}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+// 会员收藏列表
+export function collectList (params) {
+  return request({
+    url: `/buyer/member/collection/${params.type}`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+// 单个商品评价
+export function goodsComment (params) {
+  return request({
+    url: `/buyer/memberEvaluation/${params.goodsId}/goodsEvaluation`,
+    method: Method.GET,
+    needToken: false,
+    params
+  });
+}
+
+// 商品各评价类别数量
+export function goodsCommentNum (goodsId) {
+  return request({
+    url: `/buyer/memberEvaluation/${goodsId}/evaluationNumber`,
+    method: Method.GET,
+    needToken: false
+  });
+}
+
+// 添加会员评价
+export function addEvaluation (params) {
+  return request({
+    url: `/buyer/memberEvaluation`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+// 会员评价详情
+export function evaluationDetail (id) {
+  return request({
+    url: `/buyer/memberEvaluation/get/${id}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+// 发票分页列表
+export function receiptList () {
+  return request({
+    url: `/buyer/trade/receipt`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+// 保存发票信息
+export function saveReceipt (params) {
+  return request({
+    url: `/buyer/trade/receipt`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+// 获取可领取优惠券列表
+export function couponList (params) {
+  return request({
+    url: `/buyer/promotion/coupon`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+// 会员优惠券列表
+export function memberCouponList (params) {
+  return request({
+    url: `/buyer/promotion/coupon/getCoupons`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+// 会员优惠券列表
+export function canUseCouponList (params) {
+  return request({
+    url: `/buyer/promotion/coupon/canUse`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+// 领取优惠券
+export function receiveCoupon (couponId) {
+  return request({
+    url: `/buyer/promotion/coupon/receive/${couponId}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+// 获取申请售后列表
+export function afterSaleList (params) {
+  return request({
+    url: `/buyer/afterSale/page`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+// 获取申请售后页面信息
+export function afterSaleInfo (sn) {
+  return request({
+    url: `/buyer/afterSale/applyAfterSaleInfo/${sn}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+// 获取申请售后、投诉原因
+export function afterSaleReason (serviceType) {
+  return request({
+    url: `/buyer/afterSale/get/afterSaleReason/${serviceType}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+// 获取申请售后详情
+export function afterSaleDetail (sn) {
+  return request({
+    url: `/buyer/afterSale/get/${sn}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+// 售后日志
+export function afterSaleLog (sn) {
+  return request({
+    url: `/buyer/afterSale/get/getAfterSaleLog/${sn}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+// 申请售后
+export function applyAfterSale (params) {
+  return request({
+    url: `/buyer/afterSale/save/${params.orderItemSn}`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+// 取消售后申请
+export function cancelAfterSale (afterSaleSn) {
+  return request({
+    url: `/buyer/afterSale/cancel/${afterSaleSn}`,
+    method: Method.POST,
+    needToken: true
+  });
+}
+
+// 投诉商品
+export function handleComplain (data) {
+  return request({
+    url: `/buyer/complain`,
+    method: Method.POST,
+    needToken: true,
+    data
+  });
+}
+// 分页获取我的投诉列表
+export function complainList (params) {
+  return request({
+    url: `/buyer/complain`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 获取投诉详情
+ */
+export function getComplainDetail (id) {
+  return request({
+    url: `/buyer/complain/${id}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+/**
+ * 取消投诉
+ */
+export function clearComplain (id) {
+  return request({
+    url: `/buyer/complain/status/${id}`,
+    method: Method.PUT,
+    needToken: true
+  });
+}
+
+/**
+ * 获取当前会员分销信息
+ */
+export function distribution () {
+  return request({
+    url: `/buyer/distribution`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+/**
+ * 申请成为分销商
+ * @param idNumber 身份证号
+ * @param name  名字
+ */
+export function applyDistribution (params) {
+  return request({
+    url: `/buyer/distribution`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 获取分销商订单列表
+ */
+export function getDistOrderList (params) {
+  return request({
+    url: `/buyer/distribution/distributionOrder`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 获取分销商商品列表
+ */
+export function getDistGoodsList (params) {
+  return request({
+    url: `/buyer/distributionGoods`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 选择分销商品
+ * @param distributionGoodsId 分销商品id
+ */
+export function selectDistGoods (distributionGoodsId) {
+  return request({
+    url: `/buyer/distributionGoods/checked/${distributionGoodsId}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+/**
+ * 分销商提现历史
+ */
+export function distCashHistory (params) {
+  return request({
+    url: `/buyer/distribution/cash`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 分销商提现
+ */
+export function distCash (params) {
+  return request({
+    url: `/buyer/distribution/cash`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 我的足迹
+ * @param {Number} pageNumber 页码
+ * @param {Number} pageSize 页数
+ */
+export function tracksList (params) {
+  return request({
+    url: `/buyer/footprint`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 清空足迹
+ */
+export function clearTracks () {
+  return request({
+    url: `/buyer/footprint`,
+    method: Method.DELETE,
+    needToken: true
+  });
+}
+
+/**
+ * 根据id删除足迹
+ * @param {String} ids id集合
+ */
+export function clearTracksById (ids) {
+  return request({
+    url: `/buyer/footprint/delByIds/${ids}`,
+    method: Method.DELETE,
+    needToken: true
+  });
+}
+
+/**
+ * 获取会员积分
+ */
+export function memberPoint (params) {
+  return request({
+    url: `/buyer/member/memberPointsHistory/getMemberPointsHistoryVO`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 积分历史
+ */
+export function memberPointHistory (params) {
+  return request({
+    url: `/buyer/member/memberPointsHistory/getByPage`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}

+ 98 - 0
buyer/src/api/order.js

@@ -0,0 +1,98 @@
+import request, {
+  Method
+} from '@/plugins/request.js';
+
+// 查询会员订单列表
+export function getOrderList (params) {
+  return request({
+    url: `/buyer/orders`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 订单明细
+ * @param {orderSn} 订单编号
+ */
+export function orderDetail (orderSn) {
+  return request({
+    url: `/buyer/orders/${orderSn}`,
+    method: Method.GET,
+    needToken: true
+  });
+}
+
+/**
+ * 取消订单
+ * @param {orderSn} 订单编号
+ * @param reason 取消订单原因
+ */
+export function cancelOrder (params) {
+  return request({
+    url: `/buyer/orders/${params.orderSn}/cancel`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 删除订单
+ * @param {orderSn} 订单编号
+ */
+export function delOrder (orderSn) {
+  return request({
+    url: `/buyer/orders/${orderSn}`,
+    method: Method.DELETE,
+    needToken: true
+  });
+}
+
+/**
+ * 确认收货
+ * @param {orderSn} 订单编号
+ */
+export function sureReceived (orderSn) {
+  return request({
+    url: `/buyer/orders/${orderSn}/receiving`,
+    method: Method.POST,
+    needToken: true
+  });
+}
+
+/**
+ * 查询物流
+ * @param {orderSn} 订单编号
+ */
+export function getTraces (orderSn) {
+  return request({
+    url: `/buyer/orders/getTraces/${orderSn}`,
+    method: Method.POST,
+    needToken: true
+  });
+}
+
+/**
+ * 评价列表
+ *
+ */
+export function evolutionList (params) {
+  return request({
+    url: `/buyer/memberEvaluation`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+// 添加交易投诉对话
+export function communication (params) {
+  return request({
+    url: `/buyer/complain/communication`,
+    method: Method.POST,
+    needToken: true,
+    params
+  });
+}

+ 48 - 0
buyer/src/api/pay.js

@@ -0,0 +1,48 @@
+import request, {
+  Method
+} from '@/plugins/request.js';
+
+/**
+ * 获取支付详情
+ * @param orderType 交易类型,可用值:TRADE,ORDER,RECHARGE
+ * @param sn   订单编号
+ * @param clientType  调起方式,PC
+ */
+export function tradeDetail (params) {
+  return request({
+    url: '/buyer/cashier/tradeDetail',
+    needToken: true,
+    method: Method.GET,
+    params
+  });
+}
+
+/**
+ * 支付
+ * @param orderType 交易类型,可用值:TRADE,ORDER,RECHARGE
+ * @param paymentMethod 支付方式 可用值:ALIPAY,WECHAT
+ * @param payClient  调起方式 可用值:APP,NATIVE,JSAPI,H5
+ * @param sn   订单编号
+ */
+export function pay (params) {
+  return request({
+    url: `/buyer/cashier/pay/${params.paymentMethod}/${params.paymentClient}`,
+    needToken: true,
+    method: Method.GET,
+    params
+  });
+}
+
+/**
+ * 支付结果查询
+ * @param orderType 交易类型,可用值:TRADE,ORDER,RECHARGE
+ * @param sn   订单编号
+ */
+export function payCallback (params) {
+  return request({
+    url: `/buyer/cashier/result`,
+    needToken: true,
+    method: Method.GET,
+    params
+  });
+}

+ 48 - 0
buyer/src/api/promotion.js

@@ -0,0 +1,48 @@
+import request, {
+  Method
+} from '@/plugins/request.js';
+
+/**
+ * 获取当天限时抢购信息
+ */
+export function seckillByDay (params) {
+  return request({
+    url: '/buyer/promotion/seckill',
+    method: Method.GET,
+    params
+  });
+}
+
+/**
+ * 获取某个时刻限时抢购信息
+ */
+export function seckillByTimeline (timeline) {
+  return request({
+    url: `/buyer/promotion/seckill/${timeline}`,
+    method: Method.GET
+  });
+}
+
+/**
+ * 获取积分商品分类列表
+ */
+export function pointGoodsCategory (params) {
+  return request({
+    url: `/buyer/promotion/pointsGoods/category`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}
+
+/**
+ * 获取积分商品列表
+ */
+export function pointGoods (params) {
+  return request({
+    url: `/buyer/promotion/pointsGoods`,
+    method: Method.GET,
+    needToken: true,
+    params
+  });
+}

+ 77 - 0
buyer/src/api/shopentry.js

@@ -0,0 +1,77 @@
+import request, {
+  Method
+} from '@/plugins/request.js'
+
+// 店铺分页列表
+export function shopList (params) {
+  return request({
+    url: '/buyer/store',
+    needToken: true,
+    method: Method.GET,
+    params
+  })
+}
+
+// 申请店铺第一步-填写企业信息
+export function applyFirst (params) {
+  return request({
+    url: '/buyer/store/apply/first',
+    needToken: true,
+    method: Method.PUT,
+    params
+  })
+}
+
+// 申请店铺第一步-填写企业信息
+export function applySecond (params) {
+  return request({
+    url: '/buyer/store/apply/second',
+    needToken: true,
+    method: Method.PUT,
+    params
+  })
+}
+
+// 申请店铺第一步-填写企业信息
+export function applyThird (params) {
+  return request({
+    url: '/buyer/store/apply/third',
+    needToken: true,
+    method: Method.PUT,
+    params
+  })
+}
+
+// 店铺详情
+export function getDetailById (id) {
+  return request({
+    url: `/buyer/store/get/detail/${id}`,
+    needToken: true,
+    method: Method.GET
+  })
+}
+// 店铺分类
+export function getCateById (id) {
+  return request({
+    url: `/buyer/store/label/get/${id}`,
+    needToken: true,
+    method: Method.GET
+  })
+}
+//  店铺入驻协议
+export function agreement () {
+  return request({
+    url: `/buyer/article/get/1349291301250293760`,
+    needToken: true,
+    method: Method.GET
+  })
+}
+
+//  获取当前登录会员的店铺信息
+export function applyStatus () {
+  return request({
+    url: `/buyer/store/apply`,
+    needToken: true,
+    method: Method.GET
+  })
+}

Різницю між файлами не показано, бо вона завелика
+ 3 - 0
buyer/src/assets/iconfont/iconfont.css


BIN
buyer/src/assets/iconfont/iconfont.eot


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
buyer/src/assets/iconfont/iconfont.js


+ 51 - 0
buyer/src/assets/iconfont/iconfont.json

@@ -0,0 +1,51 @@
+{
+  "id": "2274088",
+  "name": "lili shop",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "829012",
+      "name": "QQ",
+      "font_class": "qq",
+      "unicode": "e6e6",
+      "unicode_decimal": 59110
+    },
+    {
+      "icon_id": "2450774",
+      "name": "微信",
+      "font_class": "weixin",
+      "unicode": "e643",
+      "unicode_decimal": 58947
+    },
+    {
+      "icon_id": "7010369",
+      "name": "支付宝",
+      "font_class": "zhifubao",
+      "unicode": "e601",
+      "unicode_decimal": 58881
+    },
+    {
+      "icon_id": "10615183",
+      "name": "服务评价-客服",
+      "font_class": "kefu",
+      "unicode": "e673",
+      "unicode_decimal": 58995
+    },
+    {
+      "icon_id": "13090705",
+      "name": "下一步",
+      "font_class": "xiayibu",
+      "unicode": "e600",
+      "unicode_decimal": 58880
+    },
+    {
+      "icon_id": "14510226",
+      "name": "微博",
+      "font_class": "weibo",
+      "unicode": "eb0e",
+      "unicode_decimal": 60174
+    }
+  ]
+}

Різницю між файлами не показано, бо вона завелика
+ 37 - 0
buyer/src/assets/iconfont/iconfont.svg


BIN
buyer/src/assets/iconfont/iconfont.ttf


BIN
buyer/src/assets/iconfont/iconfont.woff


BIN
buyer/src/assets/iconfont/iconfont.woff2


BIN
buyer/src/assets/images/R168dc76973e0ae1cb3f6235b5a8cc0da.png


BIN
buyer/src/assets/images/arrow-left.png


BIN
buyer/src/assets/images/cart-coupon-icons02.png


BIN
buyer/src/assets/images/empty.png


BIN
buyer/src/assets/images/feedback.png


BIN
buyer/src/assets/images/festival_icon.png


BIN
buyer/src/assets/images/footer/ico_service.png


BIN
buyer/src/assets/images/geted.png


BIN
buyer/src/assets/images/goodsDetail/price-bg.png


BIN
buyer/src/assets/images/logo.png


BIN
buyer/src/assets/images/logo1.png


BIN
buyer/src/assets/images/pay-success.png


BIN
buyer/src/assets/images/seckillBg.png


BIN
buyer/src/assets/images/small-circle.png


+ 92 - 0
buyer/src/assets/styles/coupon.scss

@@ -0,0 +1,92 @@
+.coupon-list {
+  display: flex;
+  flex-wrap: wrap;
+}
+.coupon-item {
+  width: 380px;
+  height: 145px;
+  margin-bottom: 20px;
+  margin-right: 10px;
+  margin-left: 10px;
+  position: relative;
+  border: 1px solid #eee;
+  .c-left {
+    width: 100%;
+    padding: 20px;
+    >div, > p {
+      margin-bottom: 10px;
+    }
+    >div {
+      .price{
+        color: $theme_color;
+        font-size: 20px;
+      }
+      .describe{
+        background-color: #fff4ec;
+        color: $theme_color;
+        padding: 0 5px;
+        margin-left: 10px;
+        font-size: 13px;
+      }
+    }
+    p:nth-of-type(1){
+      font-weight: bold;
+    }
+    p:nth-of-type(2){
+      color: #999;
+    }
+  }
+  b{
+    position: absolute;
+    z-index: 2;
+    top: 0;
+    right: 50px;
+    display: block;
+    width: 3px;
+    height: 100%;
+  }
+  .c-right {
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 52px;
+    height: 100%;
+    background-color: $theme_color;
+    color: #fff;
+    font-size: 16px;
+    padding: 20px;
+    &:hover{
+      font-weight: bold;
+    }
+  }
+  i{
+    position: absolute;
+    width: 20px;
+    height: 20px;
+    right: 42px;
+    border: 1px solid #eee;
+    background-color: #fff;
+    border-radius: 20px;
+    &:after{
+      content: '';
+      position: absolute;
+      width: 25px;
+      height: 20px;
+      left: -2px;
+      background-color: #fff;
+
+    }
+  }
+  i.circle-top{
+    top: -10px;
+    &::after{
+      top: -11px;
+    }
+  }
+  i.circle-bottom{
+    bottom: -10px;
+    &::after{
+      bottom: -11px;
+    }
+  }
+}

+ 125 - 0
buyer/src/assets/styles/global.scss

@@ -0,0 +1,125 @@
+/*
+ * @Author: LMR
+ * @Date: 2020-08-11 10:12:34
+ * @Last Modified by: LMR
+ * @Last Modified time: 2020-08-18 16:00:10
+ */
+
+//引入主题色
+@import "./theme.scss";
+
+.global_color{
+  color: $theme_color;
+}
+.global_background_color{
+  background-color: $theme_color;
+}
+.global_text_left {
+    text-align: left;
+}
+.global_text_right {
+    text-align: right;
+}
+.global_float_left {
+    float: left;
+}
+.global_float_right {
+    float: right;
+}
+.clearfix::after{
+    content: '';
+    display: block;
+    clear: both;
+}
+.width_1200{width: 1200px;}
+.width_800{width: 800px;}
+.width_400{width: 400px;}
+.width_300{width: 300px;}
+.width_200{width: 200px;}
+.width_100{width: 100px;}
+
+.fontsize_12{font-size: 12px;}
+.fontsize_14{font-size: 14px;}
+.fontsize_16{font-size: 16px;}
+.fontsize_18{font-size: 18px;}
+
+.mb_20{margin-bottom: 20px;}
+.mt_20{margin-top: 20px;}
+.ml_20{margin-left: 20px;}
+.mr_20{margin-right: 20px;}
+
+.mb_10{margin-bottom: 10px;}
+.mt_10{margin-top: 10px;}
+.ml_10{margin-left: 10px;}
+.mr_10{margin-right: 10px;}
+
+.pb_20{padding-bottom: 20px;}
+.pt_20{padding-top: 20px;}
+.pl_20{padding-left: 20px;}
+.pr_20{padding-right: 20px;}
+
+.pb_10{padding-bottom: 10px;}
+.pt_10{padding-top: 10px;}
+.pl_10{padding-left: 10px;}
+.pr_10{padding-right: 10px;}
+
+.color999{
+    color: #999;
+}
+
+html,
+body {
+    height: 100%;
+    width: 100%;
+    font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
+    // overflow: hidden;
+    margin: 0;
+    padding: 0;
+
+}
+li{
+    list-style: none;
+}
+.hover-color:hover{
+    color: $theme_color!important;
+    cursor: pointer;
+}
+.hover-pointer{
+    cursor: pointer;
+}
+.center{
+    margin: 0 auto;
+}
+.relative{
+    position: relative;
+}
+.ellipsis{
+    overflow: hidden;
+    text-overflow:ellipsis;
+    white-space: nowrap;
+}
+.promotion-decorate{
+    width: 200px;
+    text-align: center;
+    font-size: 25px;
+    position: relative;
+    font-weight: bold;
+    margin: 30px auto 30px;
+    &::before,&::after{
+      content: '';
+      display: inline-block;
+      width: 25px;
+      height: 20px;
+      background-size: 50px 20px;
+      background-repeat: no-repeat;
+      background-position: 0 0;
+      position: absolute;
+      top: 10px;
+      left: -3px;
+    }
+    &::after{
+      background-position: -24px 0;
+      right: -3px;
+      left: auto;
+    }
+  }

+ 58 - 0
buyer/src/assets/styles/goodsList.scss

@@ -0,0 +1,58 @@
+.text-danger {
+  color: $theme_color;
+}
+.seckill-price{
+  margin-right: 5px;
+  font-size: 25px;
+  font-weight: bold;
+}
+.goods-list {
+  display: flex;
+  flex-wrap: wrap;
+  width: 1200px;
+  margin: 0 auto;
+}
+.goods-show-info {
+  width: 235px;
+  padding: 6px;
+  margin: 10px 0px;
+  margin-left: 5px;
+  position: relative;
+  border: 1px solid #fff;
+  cursor: pointer;
+  background-color: #fff;
+}
+.goods-show-info:hover {
+  border: 1px solid #ccc;
+  box-shadow: 0px 0px 15px #ccc;
+}
+.goods-show-price {
+  margin-top: 6px;
+}
+.goods-show-detail {
+  font-size: 12px;
+  margin: 6px 0px;
+}
+.goods-show-num {
+  font-size: 12px;
+  margin-bottom: 6px;
+  color: #666;
+}
+.goods-show-num {
+  font-size: 12px;
+  margin-bottom: 6px;
+  color: #009688;
+}
+.goods-show-num span {
+  color: #005aa0;
+  font-weight: bold;
+}
+.goods-show-seller {
+  font-size: 12px;
+  color: $theme_color;
+}
+.goods-page {
+  margin:10px auto ;
+  text-align: right;
+  width: 1200px;
+}

+ 84 - 0
buyer/src/assets/styles/theme.less

@@ -0,0 +1,84 @@
+@import '~view-design/src/styles/index.less';
+
+@primary-color: #ed3f14;
+
+
+@table-thead-bg               : #f8f8f9;
+@table-td-stripe-bg           : #f8f8f9;
+@table-td-hover-bg            : #ededed;
+@table-td-highlight-bg        : #ededed;
+
+
+
+//
+//.colorPaletteMixin() {
+//  @functions: ~`(function() {
+//  var hueStep = 2;
+//  var saturationStep = 0.16;
+//  var saturationStep2 = 0.05;
+//  var brightnessStep1 = 0.05;
+//  var brightnessStep2 = 0.15;
+//  var lightColorCount = 5;
+//  var darkColorCount = 4;
+//
+//  var getHue = function(hsv, i, isLight) {
+//    var hue;
+//    if (hsv.h >= 60 && hsv.h <= 240) {
+//      hue = isLight ? hsv.h - hueStep * i : hsv.h + hueStep * i;
+//    } else {
+//      hue = isLight ? hsv.h + hueStep * i : hsv.h - hueStep * i;
+//    }
+//    if (hue < 0) {
+//      hue += 360;
+//    } else if (hue >= 360) {
+//      hue -= 360;
+//    }
+//    return Math.round(hue);
+//  };
+//  var getSaturation = function(hsv, i, isLight) {
+//    var saturation;
+//    if (isLight) {
+//      saturation = hsv.s - saturationStep * i;
+//    } else if (i === darkColorCount) {
+//      saturation = hsv.s + saturationStep;
+//    } else {
+//      saturation = hsv.s + saturationStep2 * i;
+//    }
+//    if (saturation > 1) {
+//      saturation = 1;
+//    }
+//    if (isLight && i === lightColorCount && saturation > 0.1) {
+//      saturation = 0.1;
+//    }
+//    if (saturation < 0.06) {
+//      saturation = 0.06;
+//    }
+//    return Number(saturation.toFixed(2));
+//  };
+//  var getValue = function(hsv, i, isLight) {
+//    var value;
+//    if (isLight) {
+//      value = hsv.v + brightnessStep1 * i;
+//    }else{
+//      value = hsv.v - brightnessStep2 * i
+//    }
+//    if (value > 1) {
+//      value = 1;
+//    }
+//    return Number(value.toFixed(2))
+//  };
+//
+//  this.colorPalette = function(color, index) {
+//    var isLight = index <= 6;
+//    var hsv = tinycolor(color).toHsv();
+//    var i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
+//    return tinycolor({
+//      h: getHue(hsv, i, isLight),
+//      s: getSaturation(hsv, i, isLight),
+//      v: getValue(hsv, i, isLight),
+//    }).toHexString();
+//  };
+//})()`;
+//}
+
+//.colorPaletteMixin();

+ 114 - 0
buyer/src/assets/styles/theme.scss

@@ -0,0 +1,114 @@
+/*
+ * @Author: LMR
+ * @Date: 2020-08-14 11:04:12
+ * @Last Modified by: LMR
+ * @Last Modified time: 2020-08-18 14:21:41
+ */
+
+
+// 主题颜色
+
+// 明亮主题颜色
+$primary_color: #2d8cf0;
+$primary_light_color: #0f1011;
+$primary_dark_color: #2b85e4;
+$success_color: #19be6b;
+$warning_color: #ff9900;
+$error_color: #ed3f14;
+$handle-btn-color: #438cde;
+
+$theme_color: #ed3f14;
+
+$border_color: #dddee1;
+$title_color: #8c8c8c;
+$light_title_color: #1c2438;
+$light_content_color: #495060;
+$light_sub_color: #80848f;
+$light_background_color: #f8f8f9;
+$light_white_background_color :#fff;
+// 暗黑主题颜色
+$dark_background_color: #141414;
+$dark_sub_background_color: #1d1d1d; //稍微浅一点的
+$dark_content_color: #d5d5d5;
+
+
+
+/***** 封装一些方法可用于 黑暗主题 ,明亮主题 *****/
+
+
+// 背景颜色
+@mixin background_color($color) {
+  /*通过该函数设置字体颜色,后期方便统一管理;*/
+  background-color: $color;
+  transition: 0.35s;
+  [data-theme="dark"] & {
+    background-color: $dark_background_color;
+  }
+  [data-theme="light"] & {
+    background-color: $light_background_color;
+  }
+}
+// 辅助背景颜色
+@mixin sub_background_color($color) {
+  /*通过该函数设置字体颜色,后期方便统一管理;*/
+  background-color: $color;
+  transition: 0.35s;
+  [data-theme="dark"] & {
+    background-color: $dark_sub_background_color;
+  }
+  [data-theme="light"] & {
+    background-color: $light_background_color;
+  }
+}
+
+
+@mixin white_background_color() {
+  /*通过该函数设置字体颜色,后期方便统一管理;*/
+  background-color: $light_white_background_color;
+  transition: 0.35s;
+  [data-theme="dark"] & {
+    background-color: $dark_sub_background_color;
+  }
+  [data-theme="light"] & {
+    background-color: $light_white_background_color;
+  }
+}
+
+
+// 正文颜色
+@mixin content_color($color) {
+  /*通过该函数设置字体颜色,后期方便统一管理;*/
+  color: $color;
+  [data-theme="dark"] & {
+    color: $dark_content_color;
+  }
+  [data-theme="light"] & {
+    color: $light_content_color;
+  }
+}
+
+// 辅助颜色
+@mixin sub_color($color) {
+  /*通过该函数设置字体颜色,后期方便统一管理;*/
+  color: $color;
+  [data-theme="dark"] & {
+    color: $dark_content_color;
+  }
+  [data-theme="light"] & {
+    color: $light_sub_color;
+  }
+}
+
+// 标题颜色
+@mixin title_color($color) {
+  /*通过该函数设置字体颜色,后期方便统一管理;*/
+  color: $color;
+  [data-theme="dark"] & {
+    color: $dark_content_color;
+  }
+  [data-theme="light"] & {
+    color: $light_title_color;
+  }
+}
+
+

+ 149 - 0
buyer/src/components/Search.vue

@@ -0,0 +1,149 @@
+<template>
+  <div>
+    <div class="container">
+      <img
+        :src="logoImg"
+        v-if="showLogo"
+        class="logo-img"
+        alt=""
+        @click="$router.push('/')"
+      />
+      <i-input
+        v-model="searchData"
+        size="large"
+        class="search"
+        placeholder="输入你想查找的商品"
+        @keyup.enter.native="search"
+      >
+        <Button v-if="!store"  slot="append" @click="search">搜索</Button>
+      </i-input>
+      <div v-if="store" class="btn-div">
+        <Button class="store-search" type="warning" @click="searchStore">搜本店</Button>
+        <Button class="store-search" type="primary" @click="search">搜全站</Button>
+      </div>
+      <template v-if="showTag">
+        <div style="height:12px" v-if="promotionTags.length === 0"></div>
+        <div v-else>
+          <Tag
+            v-for="(item, index) in promotionTags"
+            :key="index"
+          >
+            <span class="hover-color" @click="selectTags(item)">{{ item }}</span>
+          </Tag>
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script>
+import {getLogo} from '@/api/common.js'
+import {hotWords} from '@/api/goods.js'
+export default {
+  name: 'search',
+  props: {
+    showTag: { // 是否展示搜索栏下方热门搜索
+      type: Boolean,
+      default: true
+    },
+    showLogo: { // 是否展示左侧logo
+      type: Boolean,
+      default: true
+    },
+    store: { // 是否为店铺页面
+      type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      searchData: '', // 搜索内容
+      logoImg: '', // pc端展示logo
+      promotionTags: [] // 热门搜索列表
+    };
+  },
+  methods: {
+    selectTags (item) {
+      this.searchData = item;
+      this.search();
+    },
+    search () {
+      this.$router.push({
+        path: '/goodsList',
+        query: { keyword: this.searchData }
+      });
+    },
+    searchStore () {
+      this.$emit('search', this.searchData)
+    }
+  },
+  mounted () {
+    if (!this.Cookies.getItem('logo')) {
+      getLogo().then(res => {
+        if (res.success) {
+          let logoObj = JSON.parse(res.result.settingValue)
+          this.Cookies.setItem('logo', logoObj.buyerSideLogo)
+          this.logoImg = logoObj.buyerSideLogo
+        }
+      })
+    } else {
+      this.logoImg = this.Cookies.getItem('logo')
+    }
+    this.searchData = this.$route.query.keyword
+    hotWords({start: 1, end: 5}).then(res => {
+      if (res.success) this.promotionTags = res.result
+    })
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.container {
+  margin: 30px auto;
+  width: 460px;
+  position: relative;
+}
+.search {
+  margin: 10px 0px 5px 0;
+  /deep/ .ivu-input.ivu-input-large {
+    border: 2px solid $theme_color;
+    font-size: 12px;
+    height: 34px;
+    &:focus {
+      box-shadow: none;
+    }
+  }
+  /deep/ .ivu-input-group-append {
+    border: 1px solid $theme_color;
+    border-left: none;
+    height: 30px;
+    background-color: $theme_color;
+    color: #ffffff;
+    button {
+      font-size: 14px;
+      font-weight: 600;
+      line-height: 1;
+    }
+  }
+}
+.logo-img {
+  position: absolute;
+  left: -360px;
+  top: -9px;
+  width: 150px;
+  cursor: pointer;
+}
+.store-search{
+  padding: 0 9px;
+  border-radius: 0;
+  &:nth-child(2){
+    margin-left: -5px;
+  }
+}
+.btn-div{
+  position: relative;
+  height: 0px;
+  top: -38px;
+  left: 336px;
+}
+</style>

+ 173 - 0
buyer/src/components/addressManage/index.vue

@@ -0,0 +1,173 @@
+<template>
+  <div>
+    <Modal v-model="showAddr" width="800" title="收件人地址">
+      <Form
+        :model="formData"
+        ref="form"
+        label-position="left"
+        :label-width="100"
+        :rules="ruleInline"
+      >
+        <FormItem label="收件人" prop="name">
+          <i-input v-model="formData.name" style="width: 600px"></i-input>
+        </FormItem>
+        <FormItem label="收件地区" prop="address">
+          <i-input
+            v-model="formData.address"
+            disabled
+            style="width: 600px"
+          ></i-input>
+          <Button type="primary" size="small" @click="$refs.map.showMap = true">选择</Button>
+        </FormItem>
+        <FormItem label="详细地址" prop="detail">
+          <i-input v-model="formData.detail" style="width: 600px"></i-input>
+        </FormItem>
+        <FormItem label="手机号码" prop="mobile">
+          <i-input v-model="formData.mobile" style="width: 600px"></i-input>
+        </FormItem>
+        <FormItem label="地址别名">
+          <i-input
+            v-model="formData.alias"
+            length
+            :maxlength="4"
+            placeholder="请输入地址别名,例如公司"
+            style="width: 600px"
+          ></i-input>
+        </FormItem>
+        <FormItem label="默认地址">
+          <i-switch v-model="formData.isDefault" />
+        </FormItem>
+      </Form>
+      <div class="mt_20" slot="footer">
+        <Button @click="hide">取消</Button>
+        <Button type="primary" class="mr_10" :loading="loading" @click="save">保存收货地址</Button>
+      </div>
+    </Modal>
+    <lili-map ref="map" @getAddress="getAddress"></lili-map>
+  </div>
+</template>
+
+<script>
+import liliMap from '@/components/map';
+import {
+  newMemberAddress,
+  editMemberAddress,
+  getAddrDetail
+} from '@/api/address';
+export default {
+  name: 'addressManage',
+  props: {
+    id: {
+      defalut: '',
+      type: String
+    }
+  },
+  data () {
+    return {
+      showAddr: false, // 控制模态框显隐
+      formData: { // 表单数据
+        isDefault: false
+      },
+      ruleInline: { // 验证规则
+        name: [{ required: true, message: '请输入收件人姓名', trigger: 'blur' }],
+        address: [{ required: true, message: '请输入地址', trigger: 'change' }],
+        detail: [
+          { required: true, message: '请输入详细地址', trigger: 'blur' }
+        ],
+        mobile: [
+          { required: true, message: '手机号不能为空', trigger: 'blur' },
+          {
+            type: 'string',
+            pattern: /^1[3|4|5|6|7|8][0-9]{9}$/,
+            message: '手机号格式出错',
+            trigger: 'blur'
+          }
+        ]
+      },
+      loading: false, // 提交的加载状态
+      mapMsg: {} // 地图信息
+    };
+  },
+  methods: {
+    save () {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          const params = JSON.parse(JSON.stringify(this.formData));
+          params.consigneeAddressPath = params.address.replace(/\s/g, ',');
+          delete params.address;
+          this.loading = true;
+          if (this.id) {
+            editMemberAddress(params).then((res) => {
+              this.loading = false;
+              if (res.code === 200) {
+                this.$Message.success('编辑地址成功');
+                this.$emit('change', true);
+                this.hide();
+              }
+            }).catch(() => { this.loading = false; });
+          } else {
+            newMemberAddress(params).then((res) => {
+              this.loading = false;
+              if (res.code === 200) {
+                this.$Message.success('新增地址成功');
+                this.$emit('change', true);
+                this.hide();
+              }
+            }).catch(() => { this.loading = false; });
+          }
+        }
+      });
+    },
+    getAddrById (id) {
+      // 获取地址详情
+      getAddrDetail(id).then((res) => {
+        if (res.code === 200) {
+          console.log(res);
+          const data = res.result;
+          data.address = res.result.consigneeAddressPath.replace(/,/g, ' ');
+          this.formData = data;
+        }
+      });
+    },
+    getAddress (item) {
+      // 获取地图选择信息
+      console.log(item);
+      this.mapMsg = item;
+      this.$set(this.formData, 'address', item.addr);
+      this.$set(this.formData, 'consigneeAddressIdPath', item.addrId);
+      this.$set(this.formData, 'detail', item.detail);
+      this.formData.lat = item.position.lat;
+      this.formData.lon = item.position.lng;
+    },
+    show () {
+      this.showAddr = true;
+    },
+    hide () {
+      this.showAddr = false;
+    }
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler: function (v) {
+        console.log(v);
+        if (v) {
+          this.getAddrById(v);
+        } else {
+          this.formData = {}
+          this.$refs.form.resetFields();
+        }
+      }
+    }
+  },
+  components: {
+    liliMap
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.add-box {
+  margin: 40px 0;
+}
+</style>

+ 42 - 0
buyer/src/components/advertising/FixedTop.vue

@@ -0,0 +1,42 @@
+<template>
+  <!-- 头部广告 -->
+  <div class="advertising" v-if="show" :style="{'background-color': data.bgColor}">
+    <img :src="data.img" class="hover-pointer" @click="linkTo(data.url)"/>
+    <Icon type="md-close-circle" size="20" @click="show = false" />
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    data: { // 传入的广告信息
+      type: Object,
+      default: null
+    }
+  },
+  data () {
+    return {
+      show: true // 是否显示头部广告
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+.advertising {
+  height: 80px;
+  width: 100%;
+  background-color: $theme_color;
+  text-align: center;
+  overflow: hidden;
+  position: relative;
+  > img {
+    width: 1200px;
+    height: 100%;
+  }
+  *:nth-child(2){
+    position: relative;
+    right: 36px;
+    top: -57px;
+    cursor: pointer;
+  }
+}
+</style>

+ 2 - 0
buyer/src/components/advertising/README.md

@@ -0,0 +1,2 @@
+# 广告
+## FixedTop 顶部广告

+ 12 - 0
buyer/src/components/card/README.md

@@ -0,0 +1,12 @@
+## 自定义card -
+
+### 参数
+>_Title 卡片头部
+
+>_More 右侧栏显示内容 -- 默认不显示
+
+>_Src 右侧内容显示之后的src路径
+
+>_Tabs 传入数组  --  默认无
+
+>_Change 点击数组返回的index

+ 149 - 0
buyer/src/components/card/index.vue

@@ -0,0 +1,149 @@
+<template>
+  <Card class="_Card" :bordered="false" :dis-hover="true">
+    <div slot="title" class="cardTitle">
+      <span :style="{fontSize:`${_Size}px`}">{{_Title}}</span>
+
+      <div v-if="_Tabs" class="cardTabs">
+        <div @click="tabsChange(index)" :class="{active:(isActive==index)}" class="cardTabsItem" :style="{fontSize:`${_Size-2}px`}" v-for="(item,index) in _Tabs"
+             :key="index">
+          {{item}}
+        </div>
+      </div>
+    </div>
+    <div slot="extra" class="cardExtra" v-if="_More" @click="callBack()">
+      {{_More}}
+    </div>
+    <div>
+
+    </div>
+  </Card>
+</template>
+
+<script>
+export default {
+  name: 'index',
+  props:
+    {
+      _Tabs: { // 可点击的tab栏
+        type: null,
+        default: ''
+      },
+      // 头部
+      _Title: { // 标题
+        type: null,
+        default: '卡片头部'
+      },
+      // 右侧更多
+      _More: {
+        type: null,
+        default: false
+      },
+      _Size: { // 文字大小
+        type: Number,
+        default: 16
+      },
+      // 点击更多触发跳转
+      _Src: {
+        type: null,
+        default: function (val) {
+          if (this._More) {
+            return val;
+          } else {
+            return false;
+          }
+        }
+      }
+    },
+  data () {
+    return {
+      isActive: 0 // 已激活tab栏下标
+    };
+  },
+  mounted () {},
+  methods: {
+    // 点击右侧的回调
+    callBack () {
+      let _this = this;
+      if (this._Src !== '' || this._Src != null) {
+        this.$router.push({
+          path: _this._Src
+        });
+      }
+    },
+    // 点击tab的回调
+    tabsChange (index) {
+    //  处理并返回index
+      this.isActive = index;
+      this.$emit('_Change', index);
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+  .cardTitle {
+    display: flex;
+    cursor: pointer;
+  }
+  .active{
+    color: $theme_color;
+    position: relative;
+    &::before{
+      content: '';
+      position: absolute;
+      width: 100%;
+      height: 3px;
+      bottom: 0;
+      left: 0;
+      background: $theme_color;
+    }
+  }
+
+  .cardTabs {
+    display: flex;
+    padding: 0 12px;
+
+    > .cardTabsItem {
+      padding: 0 12px;
+
+    }
+
+    > .cardTabsItem:hover {
+
+      color: $theme_color;
+    }
+  }
+
+  /deep/ .ivu-card, .ivu-card-head, ._Card {
+    margin-bottom: 20px;
+    @include white_background_color();
+  }
+
+  /deep/ .ivu-card-head {
+    position: relative;
+    padding: 0 14px;
+    height: 50px;
+    line-height: 50px;
+
+    &::before {
+      content: '';
+      width: 3px;
+      height: 50%;
+      top: 25%;
+      background: $theme_color;
+      position: absolute;
+      left: 0;
+    }
+  }
+
+  .cardExtra {
+    color: $theme_color;
+    cursor: pointer;
+  }
+
+  /deep/ .ivu-card-body {
+    padding: 0 !important;
+    display: none;
+  }
+
+</style>

+ 110 - 0
buyer/src/components/coupon/Coupon.vue

@@ -0,0 +1,110 @@
+<template>
+  <div class="wrapper">
+    <empty v-if="list.length==0" />
+    <ul class="coupon-list" v-else>
+      <li v-for="(item, index) in list" class="coupon-item" :key="index">
+        <div class="c-left">
+          <div>
+            <span v-if="item.couponType === 'PRICE'" class="fontsize_12 global_color">¥<span class="price">{{item.price | unitPrice}}</span></span>
+            <span v-if="item.couponType === 'DISCOUNT'" class="fontsize_12 global_color"><span class="price">{{item.discount}}</span>折</span>
+            <span class="describe">满{{item.consumeThreshold}}元可用</span>
+          </div>
+          <p>使用范围:{{useScope(item.scopeType, item.storeName)}}</p>
+          <p>有效期:{{item.endTime}}</p>
+        </div>
+        <b></b>
+        <a class="c-right" @click="go(item)">立即使用</a>
+        <i class="circle-top"></i>
+        <i class="circle-bottom"></i>
+      </li>
+    </ul>
+    <Page :total="total" @on-change="changePageNum"
+      v-if="list.length && total > params.pageNumber"
+      class="pageration"
+      @on-page-size-change="changePageSize"
+      :page-size="params.pageSize"
+      show-sizer>
+    </Page>
+    <Spin v-if="loading" fix></Spin>
+  </div>
+</template>
+
+<script>
+import { memberCouponList } from '@/api/member.js';
+export default {
+  data () {
+    return {
+      loading: false, // 列表加载状态
+      params: { // 请求参数
+        pageNumber: 1,
+        pageSize: 10
+      },
+      total: 0, // 优惠券总数
+      list: [] // 优惠券列表
+    };
+  },
+  methods: {
+    getList () {
+      this.loading = true
+      memberCouponList(this.params).then(res => {
+        this.loading = false
+        if (res.success) {
+          this.list = res.result.records
+          this.total = res.result.total
+        }
+      })
+    },
+
+    go (item) { // 根据使用条件跳转商品列表页面
+      if (item.storeId !== 'platform') {
+        this.$router.push({path: '/merchant', query: {id: item.storeId}})
+      } else {
+        if (item.scopeType === 'PORTION_GOODS_CATEGORY') {
+          this.$router.push({path: '/goodsList', query: {categoryId: item.scopeId}})
+        } else {
+          this.$router.push({path: '/goodsList'})
+        }
+      }
+    },
+
+    changePageNum (val) {
+      this.params.pageNumber = val;
+      this.getList()
+    },
+
+    changePageSize (val) {
+      this.pageNumber = 1;
+      this.params.pageSize = val;
+      this.getList()
+    },
+
+    useScope (type, storeName) {
+      let shop = '平台';
+      let goods = '全部商品'
+      if (storeName !== 'platform') shop = storeName
+      switch (type) {
+        case 'ALL':
+          goods = '全部商品'
+          break;
+        case 'PORTION_GOODS':
+          goods = '部分商品'
+          break;
+        case 'PORTION_GOODS_CATEGORY':
+          goods = '部分分类商品'
+          break;
+      }
+      return `${shop}${goods}可用`
+    }
+  },
+  mounted () {
+    this.getList()
+  }
+};
+</script>
+
+<style scoped lang="scss">
+@import '../../assets/styles/coupon.scss';
+.pageration{
+    text-align: right;
+}
+</style>

+ 323 - 0
buyer/src/components/drawer/Drawer.vue

@@ -0,0 +1,323 @@
+<template>
+  <div class="content-drawer">
+    <div v-if="title === '购物车'" class="cart-con">
+      <ul>
+        <li v-for="(goods,goodsIndex) in cartList" :key="goodsIndex">
+          <div>
+            <img :src="goods.goodsSku.thumbnail" width="90" height="90" alt="">
+          </div>
+          <div>
+            <p class="hover-color" @click="linkTo(`/goodsDetail?skuId=${goods.goodsSku.id}&goodsId=${goods.goodsSku.goodsId}`)">{{goods.goodsSku.goodsName}}</p>
+            <p class="price">{{goods.goodsSku.price | unitPrice('¥')}}<span>&nbsp; x{{goods.num}}</span></p>
+          </div>
+          <span class="del hover-color" @click="delGoods(goods.goodsSku.id)">删除</span>
+        </li>
+      </ul>
+      <Button size="large" class="mt_10" type="primary" @click="linkTo('/cart')" long>去购物车结算</Button>
+    </div>
+
+    <div v-else-if="title === '我的订单'" class="order-con">
+      <ul>
+        <li v-for="(order,orderIndex) in orderList" :key="orderIndex">
+          <div class="order-status"><span>{{filterOrderStatus(order.orderStatus)}}</span><span>{{order.createTime}}</span></div>
+          <div class="goods-img">
+            <img :src="img.image"
+              @click="linkTo(`/goodsDetail?skuId=${img.skuId}&goodsId=${img.goodsId}`)"
+              v-for="(img,imgIndex) in order.orderItems"
+              :key="imgIndex" width="40" height="40" alt="">
+          </div>
+          <div class="order-handle"><span>{{ order.flowPrice | unitPrice("¥") }}</span><span class="hover-color" @click="linkTo(`home/OrderDetail?sn=${order.sn}`)">查看订单</span></div>
+        </li>
+      </ul>
+      <Button type="primary" @click="linkTo('/home/MyOrder')" long>查看全部订单</Button>
+    </div>
+
+    <div v-else-if="title === '优惠券'" class="coupon-con">
+      <ul class="coupon-list">
+          <li v-for="(coupon, index) in couponList" class="coupon-item" :key="index">
+            <div class="c-left">
+              <div>
+                <span v-if="coupon.couponType === 'PRICE'" class="fontsize_12 global_color">¥<span class="price">{{coupon.price | unitPrice}}</span></span>
+                <span v-if="coupon.couponType === 'DISCOUNT'" class="fontsize_12 global_color"><span class="price">{{coupon.discount}}</span>折</span>
+                <span class="describe">满{{coupon.consumeThreshold}}元可用</span>
+              </div>
+              <p>使用范围:{{useScope(coupon.scopeType, coupon.storeName)}}</p>
+              <p>有效期:{{coupon.endTime}}</p>
+            </div>
+            <b></b>
+            <a class="c-right" @click="receive(coupon)">立即领取</a>
+            <i class="circle-top"></i>
+            <i class="circle-bottom"></i>
+          </li>
+        </ul>
+    </div>
+    <div v-else-if="title === '我的足迹'" class="tracks-con">
+      <ul>
+        <li v-for="(track,trackIndex) in tracksList" :key="trackIndex">
+          <img :src="track.thumbnail" :alt="track.thumbnail" @click="linkTo(`/goodsDetail?skuId=${track.id}&goodsId=${track.goodsId}`)" width="100" height="100">
+          <div @click="addToCart(track.id)">加入购物车</div>
+          <p class="global_color">{{track.price | unitPrice('¥')}}</p>
+        </li>
+      </ul>
+      <div class="hover-color" style="text-align:center;" @click="linkTo('/home/MyTracks')">查看更多>></div>
+    </div>
+    <div v-else-if="title === '我的收藏'" class="collect-con">
+      <ul>
+        <li v-for="(collect,collectIndex) in collectList" :key="collectIndex">
+          <img :src="collect.image" :alt="collect.image" @click="linkTo(`/goodsDetail?skuId=${collect.skuId}&goodsId=${collect.goodsId}`)" width="100" height="100">
+          <div @click="addToCart(collect.skuId)">加入购物车</div>
+          <span class="del-icon" @click.stop="cancelCollect(collect.skuId)">
+            <Icon type="md-trash" />
+          </span>
+          <p class="global_color">{{collect.price | unitPrice('¥')}}</p>
+        </li>
+      </ul>
+      <div class="hover-color" style="text-align:center;" @click="linkTo('/home/Favorites')">查看更多>></div>
+    </div>
+    <Spin v-if="loading" fix></Spin>
+  </div>
+</template>
+
+<script>
+import {cartGoodsAll, delCartGoods, addCartGoods, cartCount} from '@/api/cart.js'
+import { getOrderList } from '@/api/order';
+import {couponList, receiveCoupon, tracksList, collectList, cancelCollect} from '@/api/member.js'
+export default {
+  name: 'Drawer',
+  props: {
+    title: {
+      default: '',
+      type: String
+    }
+  },
+  watch: {
+    title (val) {
+      switch (val) {
+        case '购物车':
+          this.getCartList()
+          break;
+        case '我的订单':
+          this.getOrderList()
+          break;
+        case '我的足迹':
+          this.getTracksList()
+          break;
+        case '优惠券':
+          this.getCouponList()
+          break;
+        case '我的收藏':
+          this.getCollectList()
+          break;
+      }
+    }
+  },
+  data () {
+    return {
+      loading: false, // 控制spin显隐
+      cartList: [], // 购物车列表
+      couponList: [], // 优惠券列表
+      orderList: [], // 订单列表
+      collectList: [], // 收藏列表
+      tracksList: [], // 足迹列表
+      orderStatusList: [ // 订单状态
+        {
+          name: '未付款',
+          status: 'UNPAID'
+        },
+        {
+          name: '已付款',
+          status: 'PAID'
+        },
+        {
+          name: '待发货',
+          status: 'UNDELIVERED'
+        },
+        {
+          name: '已发货',
+          status: 'DELIVERED'
+        },
+        {
+          name: '已完成',
+          status: 'COMPLETED'
+        },
+        {
+          name: '待核验',
+          status: 'TAKE'
+        },
+        {
+          name: '已取消',
+          status: 'CANCELLED'
+        }
+      ]
+    };
+  },
+  components: {},
+  mounted () {},
+  methods: {
+    getCartList () { // 获取购物车列表
+      this.loading = true
+      cartGoodsAll().then(res => {
+        this.loading = false
+        this.cartList = res.result.skuList
+      })
+    },
+    // 删除商品
+    delGoods (id) {
+      delCartGoods({ skuIds: id }).then((res) => {
+        if (res.code === 200) {
+          this.$Message.success('删除成功');
+          this.getCartList();
+          cartCount().then(res => {
+            this.$store.commit('SET_CARTNUM', res.result)
+            this.Cookies.setItem('cartNum', res.result)
+          })
+        } else {
+          this.$Message.error(res.message);
+        }
+      });
+    },
+    filterOrderStatus (status) { // 获取订单状态中文
+      const ob = this.orderStatusList.filter(e => { return e.status === status });
+      return ob[0].name
+    },
+    receive (item) { // 领取优惠券
+      receiveCoupon(item.id).then(res => {
+        if (res.success) {
+          this.$Modal.confirm({
+            title: '领取优惠券',
+            content: '<p>优惠券领取成功,可到我的优惠券页面查看</p>',
+            okText: '我的优惠券',
+            cancelText: '立即使用',
+            onOk: () => {
+              this.$router.push('/home/Coupons')
+            },
+            onCancel: () => {
+              if (item.storeId !== 'platform') {
+                this.$router.push({path: '/merchant', query: {id: item.storeId}})
+              } else {
+                if (item.scopeType === 'PORTION_GOODS_CATEGORY') {
+                  this.$router.push({path: '/goodsList', query: {categoryId: item.scopeId}})
+                } else {
+                  this.$router.push({path: '/goodsList'})
+                }
+              }
+            }
+          });
+        }
+      })
+    },
+    useScope (type, storeName) { // 判断优惠券使用范围
+      let shop = '平台';
+      let goods = '全部商品'
+      if (storeName !== 'platform') shop = storeName
+      switch (type) {
+        case 'ALL':
+          goods = '全部商品'
+          break;
+        case 'PORTION_GOODS':
+          goods = '部分商品'
+          break;
+        case 'PORTION_GOODS_CATEGORY':
+          goods = '部分分类商品'
+          break;
+      }
+      return `${shop}${goods}可用`
+    },
+    addToCart (id) { // 添加商品到购物车
+      const params = {
+        num: 1,
+        skuId: id
+      }
+      this.loading = true;
+      addCartGoods(params).then(res => {
+        this.loading = false;
+        if (res.code === 200) {
+          this.$Message.success('商品已成功添加到购物车')
+        } else {
+          this.$Message.warning(res.message);
+        }
+      }).catch(() => { this.loading = false });
+    },
+    getCouponList () { // 获取优惠券列表
+      // this.loading = true;
+      const params = {
+        pageNumber: 1,
+        pageSize: 10
+      }
+      couponList(params).then(res => {
+        this.loading = false
+        if (res.success) {
+          this.couponList = res.result.records
+        }
+      }).catch(() => { this.loading = false })
+    },
+    getOrderList () { // 获取订单列表
+      this.loading = true
+      const params = {
+        pageNumber: 1,
+        pageSize: 10,
+        tag: 'ALL'
+      }
+      getOrderList(params).then(res => {
+        this.loading = false
+        if (res.success) {
+          this.orderList = res.result.records;
+        }
+      });
+    },
+    getCollectList () { // 获取收藏列表
+      const params = {
+        pageNumber: 1,
+        pageSize: 10,
+        type: 'GOODS'
+      }
+      this.loading = true
+      collectList(params).then(res => {
+        this.loading = false
+        this.collectList = res.result.records
+      })
+    },
+    cancelCollect (id) { // 取消商品收藏
+      cancelCollect('GOODS', id).then(res => {
+        if (res.success) {
+          this.$Message.success('取消收藏成功')
+          this.getCollectList();
+        }
+      })
+    },
+    getTracksList () { // 获取足迹列表
+      const params = {
+        pageNumber: 1,
+        pageSize: 20
+      }
+      this.loading = true
+      tracksList(params).then(res => {
+        this.tracksList = res.result
+        this.loading = false
+      }).catch(() => { this.loading = false })
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+@import '../../assets/styles/coupon.scss';
+@import './drawer.scss';
+.coupon-item{
+  overflow: hidden;
+  background-color: #fff;
+  height: 120px;
+  .c-left{
+    padding: 15px;
+  }
+  .c-right{
+    width: 38px;
+    padding: 13px;
+    font-size: 14px;
+  }
+  i{
+    right: 30px;
+    background-color: #eee;
+  }
+}
+</style>

+ 155 - 0
buyer/src/components/drawer/Main.vue

@@ -0,0 +1,155 @@
+<template>
+  <div>
+    <div class="wrapper" :style="{right:handleDrawer ? '300px' : '0px'}">
+
+      <div class="barItem" @mouseenter="showCartNum(item)" @click="clickBar(item)" v-for="(item,index) in resetConfig.menuList" :key="index">
+        <Tooltip placement="left" :content="item.title">
+          <Icon size="20" :type="item.icon"/>
+          <p class="barTitle" v-if="item.titleShow"> {{item.title}}</p>
+          <div class="circle" v-if="item.title === '购物车'">
+            {{cartNum < 100 ? cartNum : 99}}
+          </div>
+        </Tooltip>
+      </div>
+
+    </div>
+    <Drawer width="300" class="popup" :title="drawerData.title" :mask="resetConfig.mask" :closable="resetConfig.closable"
+      v-model="handleDrawer">
+      <drawerPage :title="drawerData.title" />
+    </Drawer>
+  </div>
+</template>
+
+<script>
+import Storage from '@/plugins/storage.js';
+import Configuration from './config';
+import drawerPage from './Drawer'
+import {cartCount} from '@/api/cart.js'
+export default {
+  name: 'Main',
+  data () {
+    return {
+      resetConfig: Configuration, // 菜单项
+      handleDrawer: false, // 是否可展开
+      drawerData: '' // 菜单基础数据
+    }
+  },
+  components: {drawerPage},
+  computed: {
+    userInfo () {
+      return Storage.getItem('userInfo');
+    },
+    cartNum () {
+      return this.$store.state.cartNum
+    }
+  },
+  methods: {
+    showCartNum (item) {
+      if (this.userInfo && item.title === '购物车') {
+        this.getCartList()
+      }
+    },
+    clickBar (val) {
+      if (!this.userInfo) {
+        this.$Modal.confirm({
+          title: '请登录',
+          content: '<p>请登录后执行此操作</p>',
+          okText: '立即登录',
+          cancelText: '继续浏览',
+          onOk: () => {
+            this.$router.push({
+              path: '/login',
+              query: {
+                rePath: this.$router.history.current.path,
+                query: JSON.stringify(this.$router.history.current.query)
+              }
+            });
+          }
+        });
+      } else {
+        if (val.display) {
+          this.handleDrawer = true
+          this.drawerData = val
+        } else {
+          this.handleDrawer = false
+          switch (val.title) {
+            case '会员中心':
+              this.openBlank('/home')
+              break;
+            case '我的资产':
+              this.openBlank('/home/MoneyManagement')
+              break;
+          }
+        }
+      }
+    },
+    openBlank (path) {
+      let routerUrl = this.$router.resolve({
+        path: path
+      })
+      window.open(routerUrl.href, '_blank')
+    },
+    getCartList () { // 获取购物车列表
+      cartCount().then(res => {
+        this.$store.commit('SET_CARTNUM', res.result)
+        this.Cookies.setItem('cartNum', res.result)
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+  .wrapper {
+    background-color: #000!important;
+  }
+  .barItem {
+    text-align: center;
+    padding: 13px 0;
+
+    cursor: pointer;
+    color: #fff;
+    &:hover{
+      background-color: $theme_color;
+      .circle{
+        color: $theme_color;
+        background-color: #fff;
+      }
+    }
+  }
+
+  .barTitle {
+    writing-mode: vertical-lr;
+    letter-spacing: 2px;
+    padding: 4px 0;
+
+  }
+
+  .circle {
+    width: 20px;
+    height: 20px;
+    border-radius: 50%;
+    color: #fff;
+    background: $theme_color;
+  }
+
+  .wrapper {
+    width: 40px;
+    position: fixed;
+    transition: .35s;
+    height: 100%;
+    z-index: 9999;
+    background: $dark_background_color;
+    top: 0;
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+  }
+  /deep/.popup .ivu-drawer-body{
+    padding: 0!important;
+    background-color: #eee;
+  }
+  /deep/.popup .ivu-drawer-wrap{
+    z-index: 3001;
+  }
+</style>

+ 42 - 0
buyer/src/components/drawer/README.md

@@ -0,0 +1,42 @@
+# 右侧侧边栏组件
+> 本组件依赖于iview的组件基础上进行封装
+>项目结构
+>* Main  -- 组件用于挂在右侧的横栏
+>* drawer -- 右侧横栏的内容
+>* config -- 用于设置大小
+
+
+#### config设置
+```
+/**
+menuList // 组件的menu
+display //是否显示此menu
+badge //显示徽标数
+titleShow //是否显示title
+*/
+
+//实例代码
+ width : 50, //bar的大小
+ menuList:[
+   {
+      icon
+   }
+ ]
+
+
+```
+
+
+
+> 账户信息
+> 购物车
+> 我的订单
+> 优惠券
+> 我的资产
+> 我的足迹
+> 我的收藏
+> 邮箱订阅
+
+
+## 如何使用
+1.

+ 63 - 0
buyer/src/components/drawer/config.js

@@ -0,0 +1,63 @@
+const config = {
+  closable: true, // 是否显示右上角关闭按钮
+  mask: true, // 是否显示遮罩层
+
+  menuList: [{
+    icon: 'md-person', // menu的icon
+    title: '会员中心', // menu的标题
+    titleShow: false,
+    path: '', // menu点击的路径
+    display: false // 是否显示此menu
+  },
+  {
+    icon: 'ios-cart', // menu的icon
+    title: '购物车', // menu的标题
+    path: '', // menu点击的路径
+    display: true, // 是否显示此menu
+    badge: 12,
+    titleShow: true
+  },
+  {
+    icon: 'md-clipboard', // menu的icon
+    title: '我的订单', // menu的标题
+    path: '', // menu点击的路径
+    display: true, // 是否显示此menu
+    badge: '',
+    titleShow: false
+  },
+  {
+    icon: 'md-pricetag', // menu的icon
+    title: '优惠券', // menu的标题
+    path: '', // menu点击的路径
+    display: true, // 是否显示此menu
+    badge: '',
+    titleShow: false
+  },
+  {
+    icon: 'logo-usd', // menu的icon
+    title: '我的资产', // menu的标题
+    path: '', // menu点击的路径
+    display: false, // 是否显示此menu
+    badge: '',
+    titleShow: false
+  },
+  {
+    icon: 'ios-eye', // menu的icon
+    title: '我的足迹', // menu的标题
+    path: '', // menu点击的路径
+    display: true, // 是否显示此menu
+    badge: '',
+    titleShow: false
+  },
+  {
+    icon: 'md-star', // menu的icon
+    title: '我的收藏', // menu的标题
+    path: '', // menu点击的路径
+    display: true, // 是否显示此menu
+    badge: '',
+    titleShow: false
+  }
+  ]
+}
+
+export default config

+ 130 - 0
buyer/src/components/drawer/drawer.scss

@@ -0,0 +1,130 @@
+.content-drawer {
+  height: 100%;
+  font-size: 12px;
+}
+// 购物车样式
+.cart-con{
+  position: relative;
+  background: #fff;
+  height: 100%;
+  ul>li{
+    font-size: 12px;
+    border-bottom: 1px dashed #999;
+    margin: 0 10px;
+    display: flex;
+    padding: 10px 0;
+    align-items: center;
+    position: relative;
+    p{margin-bottom: 10px;}
+    .del{
+      position: absolute;
+      right: 10px;
+      bottom: 30px;
+    }
+
+    .price {
+      color: $theme_color;
+      span{color: #999;}
+    }
+
+    &:last-child{border: none;}
+  }
+  
+}
+// 订单样式
+.order-con{
+  ul>li {
+    margin: 10px;
+    background-color: #fff;
+    .order-status {
+      display: flex;
+      background-color: #666;
+      border-radius: 3px 3px 0 0;
+      color: #fff;
+      justify-content: space-between;
+      padding: 0 10px;
+    }
+
+    .goods-img {
+      padding-left: 10px;
+      padding-top: 10px;
+      img{
+        border: 1px solid #eee;
+        margin-right: 10px;
+        
+        &:hover{
+          cursor: pointer;
+        }
+      }
+    }
+    
+    .order-handle{
+      display: flex;
+      justify-content: space-between;
+      padding:5px 10px;
+      border-top: 1px solid #eee;
+      span:nth-child(1){
+        color: $theme_color;
+      }
+    }
+  }
+}
+// 优惠券样式
+.coupon-con{
+  margin-top: 10px;
+}
+// 足迹样式
+.tracks-con,.collect-con{
+  ul{
+    display: flex;
+    flex-wrap: wrap;
+    padding: 10px;
+  }
+  li {
+    background-color: #fff;
+    margin: 10px;
+    width: 120px;
+    position: relative;
+    text-align: center;
+    &:hover{
+      div,.del-icon{
+        display: block;
+      }
+
+    }
+
+    img{
+      cursor: pointer;
+    }
+
+    div{
+      display: none;
+      position: absolute;
+      bottom: 18px;
+      width: 100%;
+      background-color: #666;
+      color: #fff;
+      &:hover{
+        background-color: $theme_color;
+        cursor: pointer;
+      }
+    }
+    .del-icon{
+      display: none;
+      font-size: 20px;
+      position: absolute;
+      width: 30px;
+      height: 30px;
+      line-height: 30px;
+      text-align: center;
+      right: 0;
+      top: 0;
+      cursor: pointer;
+      color: $theme_color;
+    }
+  }
+}
+// 我的收藏样式
+.collect-con{
+
+}

+ 45 - 0
buyer/src/components/empty/Main.vue

@@ -0,0 +1,45 @@
+<template>
+  <div class="empty">
+    <img
+      class="empty-img"
+      :style="{ width: _Size + 'px' }"
+      src="../../assets/images/empty.png"
+      alt=""
+    />
+    <p v-if="_Title">{{ _Title }}</p>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Main',
+  data () {
+    return {
+
+    };
+  },
+  props: {
+    _Title: { // 描述内容
+      type: null,
+      default: '暂无更多'
+    },
+    _Size: { // 图片大小
+      type: Number,
+      default: 150
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.empty {
+  margin: 30px 0;
+  text-align: center;
+  width: 100%;
+
+}
+p {
+  cursor: pointer;
+  @include sub_color($light_sub_color);
+}
+</style>

+ 10 - 0
buyer/src/components/empty/README.md

@@ -0,0 +1,10 @@
+# 空状态封装
+
+```
+// _Title
+
+<empty></empty>  //默认显示
+
+q
+<empty _Title='暂无更多了' ></empty>   //自定义返回标题
+```

+ 189 - 0
buyer/src/components/footer/Footer.vue

@@ -0,0 +1,189 @@
+<template>
+  <div>
+    <footer class="footer">
+      <div class="clearfix"></div>
+      <div class="icon-row">
+        <div class="footer-icon">
+          <h5 class="footer-icon-child"></h5>
+          <span class="footer-icon-text">品类齐全,轻松购物</span>
+        </div>
+        <div class="footer-icon">
+          <h5 class="footer-icon-child footer-icon-child-2"></h5>
+          <span class="footer-icon-text">多仓直发,极速配送</span>
+        </div>
+        <div class="footer-icon">
+          <h5 class="footer-icon-child footer-icon-child-3"></h5>
+          <span class="footer-icon-text">正品行货,精致服务</span>
+        </div>
+        <div class="footer-icon">
+          <h5 class="footer-icon-child footer-icon-child-4"></h5>
+          <span class="footer-icon-text">天天低价,畅选无忧</span>
+        </div>
+      </div>
+      <div class="service-intro">
+        <div class="servece-type">
+          <div class="servece-type-info" v-for="(guide, index) in guideArr" :key="index">
+            <ul>
+              <li v-for="(item, index) in guide" :key="index" @click="goArticle">{{item}}</li>
+            </ul>
+          </div>
+        </div>
+        <div class="clearfix"></div>
+        <div class="friend-link">
+          <div class="friend-link-item">
+            <ul>
+              <li v-for="(link, index) in moreLink" :key="index" @click="goArticle">
+                <span class="link-item" :class="{'link-last-item': index === 4}">{{link}}</span>
+              </li>
+            </ul>
+          </div>
+        </div>
+        <div class="clearfix"></div>
+        <div class="copyright">
+          <p>Copyright © LILI</p>
+        </div>
+      </div>
+    </footer>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Footer',
+  data () {
+    return {
+      guideArr: [ // 导航链接
+        [ '购物指南', '购物流程', '会员介绍', '生活旅行', '常见问题', '大家电', '联系客服' ],
+        [ '配送方式', '上门自提', '211限时达', '配送服务查询', '收取标准', '海外配送' ],
+        [ '支付方式', '货到付款', '在线支付', '分期付款', '邮局汇款', '公司转账' ],
+        [ '售后服务', '售后政策', '价格保护', '退款说明', '返修/退换货', '取消订单' ]
+      ],
+      moreLink: ['关于我们', '联系我们', '联系客服', '商家帮助', '隐私政策'] // 更多链接
+    };
+  },
+  methods: {
+    goArticle () { // 跳转
+      let routeUrl = this.$router.resolve({
+        path: '/article'
+      })
+      window.open(routeUrl.href, '_blank')
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+/*****************************底 部 开 始*****************************/
+.footer {
+  width: 100%;
+  height: 450px;
+  padding-top: 30px;
+
+  @include background_color($light_background_color);
+
+}
+.icon-row {
+  margin: 15px auto;
+  padding-top: 8px;
+  width: 1000px;
+  height: 64px;
+}
+.footer-icon {
+  margin-left: 17px;
+  margin-right: 17px;
+  float: left;
+}
+.footer-icon-child {
+  margin-top: 10px;
+  overflow: hidden;
+  position: absolute;
+  width: 36px;
+  height: 42px;
+  background-image: url("../../assets/images/footer/ico_service.png");
+  text-indent: -999px;
+}
+.footer-icon-child-2 {
+  background-position: 0 -43px;
+}
+.footer-icon-child-3 {
+  background-position: 0 -86px;
+}
+.footer-icon-child-4 {
+  background-position: 0 -129px;
+}
+ .footer-icon-text{
+  margin-left: 45px;
+  font-size: 18px;
+  font-weight: bold;
+  line-height: 64px;
+}
+.service-intro {
+  width: 100%;
+  border-top: 1px solid $border_color;
+}
+.servece-type {
+  margin: 15px auto;
+  height: 200px;
+  width: 800px;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+
+.servece-type-info ul {
+  list-style: none;
+}
+.servece-type-info li {
+  font-size: 14px;
+  cursor: pointer;
+  line-height: 26px;
+}
+.servece-type-info li:first-child {
+  font-size: 16px;
+  line-height: 28px;
+  font-weight: bold;
+}
+
+.friend-link {
+  display: flex;
+  align-items: center;
+  width: 908px;
+  height: 30px;
+  margin: 0px auto;
+  border-top: 1px solid $border_color;
+}
+.friend-link-item {
+  margin: 0px auto;
+}
+.friend-link-item ul {
+  list-style: none;
+}
+.friend-link-item li {
+  padding: 5px 0px;
+  float: left;
+}
+.link-item {
+  padding: 0px 8px;
+  cursor: pointer;
+  border-right: 1px solid $border_color;
+}
+.link-last-item {
+  border: none;
+}
+.copyright {
+  width: 100%;
+  line-height: 30px;
+  text-align: center;
+}
+.copyright a{
+  color: #232323;
+  font-size: 20px;
+}
+.footer-icon-text{
+  @include title_color($light_title_color)
+}
+.copyright,.friend-link,.servece-type-info {
+  @include sub_color($light_sub_color)
+}
+/*****************************底 部 结 束*****************************/
+</style>

+ 54 - 0
buyer/src/components/global.js

@@ -0,0 +1,54 @@
+// 全局组件配置
+
+import empty from './empty/Main' // 空状态组件
+import drawer from './drawer/Main' // 右侧bar
+import Header from '@/components/header/Header'; // 头部组件
+import FixedTopPage from '@/components/advertising/FixedTop'; // 顶部广告
+import Footer from '@/components/footer/Footer'; // 底部栏
+import Search from '@/components/Search' // 搜索框
+import card from '@/components/card' // 个人中心 卡片
+import cateNav from '@/components/nav/cateNav' // 个人中心 卡片
+
+empty.install = function (Vue) {
+  Vue.component('empty', empty);
+};
+
+drawer.install = function (Vue) {
+  Vue.component('drawer', drawer);
+};
+
+Header.install = function (Vue) {
+  Vue.component('BaseHeader', Header);
+};
+
+FixedTopPage.install = function (Vue) {
+  Vue.component('FixedTopPage', FixedTopPage);
+};
+
+Footer.install = function (Vue) {
+  Vue.component('BaseFooter', Footer);
+};
+
+Search.install = function (Vue) {
+  Vue.component('Search', Search);
+};
+
+card.install = function (Vue) {
+  Vue.component('card', card)
+}
+
+cateNav.install = function (Vue) {
+  Vue.component('cateNav', cateNav)
+}
+
+// 引用本js中所有的组件
+export function InstallAll (Vue) {
+  Vue.use(empty)
+  Vue.use(drawer)
+  Vue.use(Header)
+  Vue.use(FixedTopPage)
+  Vue.use(Footer)
+  Vue.use(Search)
+  Vue.use(card)
+  Vue.use(cateNav)
+}

+ 123 - 0
buyer/src/components/goodsDetail/Promotion.vue

@@ -0,0 +1,123 @@
+<template>
+  <div>
+    <div class="wrapper" v-if="type === 'goodsDetail'">
+      <div class="wr-l"><Icon size="23" type="ios-alarm-outline" /> 秒杀活动</div>
+      <div class="count-down" v-if="end === ''">
+      <p>倒计时:</p><span>{{ hours }}</span><span>{{ minutes }}</span><span>{{ seconds }}</span>
+      </div>
+      <div v-else>{{end}}</div>
+    </div>
+    <span v-else class="cart-promotion">
+      <span v-if="end === ''">据活动结束:<span>{{ hours }}</span> : <span>{{ minutes }}</span> : <span>{{ seconds }}</span></span>
+      <span v-else>活动已结束</span>
+    </span>
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    time: { // 传入的初始时间
+      default: 1718977559428
+    },
+    type: {
+      default: 'goodsDetail', // 设置两个值,goodsDetail和cart,样式不同
+      type: String
+    }
+  },
+  data () {
+    return {
+      end: '', // 结束状态
+      hours: '', // 小时
+      minutes: '', // 分钟
+      seconds: '', // 秒
+      interval: '' // 定时器
+    };
+  },
+  mounted () {
+    this.init()
+  },
+  methods: {
+    countDown (val) {
+      function addZero (i) {
+        return i < 10 ? '0' + i : i + '';
+      }
+      var nowtime = new Date();
+      var endtime = new Date(val);
+      var lefttime = parseInt((endtime.getTime() - nowtime.getTime()) / 1000);
+      var h = parseInt((lefttime / (60 * 60)) % 24);
+      var m = parseInt((lefttime / 60) % 60);
+      var s = parseInt(lefttime % 60);
+      h = addZero(h);
+      m = addZero(m);
+      s = addZero(s);
+      this.hours = h;
+      this.minutes = m
+      this.seconds = s;
+      if (lefttime <= 0) {
+        this.end = `活动已结束`;
+        clearInterval(this.interval)
+      }
+    },
+    init () {
+      this.interval = setInterval((item) => {
+        this.countDown(this.time);
+      }, 1000);
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+.cart-promotion{
+  font-size: 13px;
+  color: #999;
+  margin-left: 10px;
+}
+.wrapper {
+  background-image: linear-gradient(266deg, #ff0b33, #ff4257, #ff5f7c, #fa78a2);
+  height: 32px;
+  color: #fff;
+  line-height: 32px;
+  font-size: 16px;
+  padding: 0 10px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+.wr-r{
+  font-size: 13px;
+}
+.count-down {
+  margin-right: -20px;
+  p{
+    float: left;
+    line-height: 20px;
+  }
+  > span {
+    position: relative;
+    float: left;
+    width: 20px;
+    height: 20px;
+    text-align: center;
+    background-color: #2f3430;
+    margin-right: 20px;
+    color: white;
+    font-size: 14px;
+    line-height: 20px;
+    &::after {
+      content: ":";
+      display: block;
+      position: absolute;
+      right: -20px;
+      font-weight: bolder;
+      font-size: 14px;
+      width: 20px;
+      height: 100%;
+      top: 0;
+    }
+  }
+  > span:last-child::after {
+    content: "";
+  }
+}
+
+</style>

+ 658 - 0
buyer/src/components/goodsDetail/ShowGoods.vue

@@ -0,0 +1,658 @@
+<template>
+  <div class="wrapper">
+    <div class="item-detail-show">
+      <!-- 详情左侧展示数据、图片,收藏、举报 -->
+      <div class="item-detail-left">
+        <!-- 大图、放大镜 -->
+        <div class="item-detail-big-img">
+          <pic-zoom :url="imgList[imgIndex].url" :scale="2"></pic-zoom>
+        </div>
+        <div class="item-detail-img-row">
+          <div
+            class="item-detail-img-small"
+            @mouseover="imgIndex = index"
+            v-for="(item, index) in imgList"
+            :key="index"
+          >
+            <img :src="item.url" />
+          </div>
+        </div>
+
+        <div class="goodsConfig mt_10">
+          <span @click="collect" ><Icon type="ios-heart" :color="isCollected ? '#ed3f14' : '#666'" />{{isCollected?'已收藏':'收藏'}}</span>
+          <!-- <span>举报</span> -->
+        </div>
+      </div>
+      <!-- 右侧商品信息、活动信息、操作展示 -->
+      <div class="item-detail-right">
+        <div class="item-detail-title">
+          <p>
+            {{ skuDetail.goodsName }}
+          </p>
+        </div>
+        <!-- 限时秒杀 -->
+        <Promotion v-if="promotionMap['SECKILL']" :time="promotionMap['SECKILL'].endTime"></Promotion>
+        <!-- 商品详细 价格、优惠券、促销 -->
+        <div class="item-detail-price-row">
+          <div class="item-price-left">
+            <!-- 商品原价 -->
+            <div class="item-price-row" v-if="!skuDetail.promotionPrice">
+              <p>
+                <span class="item-price-title">价 &nbsp;&nbsp;&nbsp;&nbsp;格</span>
+                <span class="item-price">{{skuDetail.price | unitPrice("¥")}}</span>
+              </p>
+            </div>
+            <!-- 秒杀价格 -->
+            <div class="item-price-row" v-if="skuDetail.promotionPrice">
+              <p>
+                <span class="item-price-title" v-if="promotionMap['SECKILL']">秒 &nbsp;杀&nbsp;价</span>
+                <span class="item-price">{{skuDetail.promotionPrice | unitPrice("¥")}}</span>
+                <span class="item-price-old">{{skuDetail.price | unitPrice("¥")}}</span>
+              </p>
+            </div>
+            <!-- 优惠券展示 -->
+            <div class="item-price-row"  v-if="promotionMap['COUPON'].length">
+              <p>
+                <span class="item-price-title">优 惠 券</span>
+                <span
+                  class="item-coupon"
+                  v-for="(item, index) in promotionMap['COUPON']"
+                  :key="index"
+                  @click="receiveCoupon(item.id)"
+                  >
+                  <span v-if="item.couponType == 'PRICE'">满{{ item.consumeThreshold }}减{{item.price}}</span>
+                  <span v-if="item.couponType == 'DISCOUNT'">满{{ item.consumeThreshold }}打{{item.couponDiscount}}折</span>
+                  </span>
+              </p>
+            </div>
+             <!-- 满减展示 -->
+            <div class="item-price-row" v-if="promotionMap['FULL_DISCOUNT']">
+              <p>
+                <span class="item-price-title">促&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;销</span>
+                <span class="item-promotion">满减</span>
+                <span class="item-desc-pintuan" v-if="promotionMap['FULL_DISCOUNT'].fullMinus">满{{ promotionMap['FULL_DISCOUNT'].fullMoney }}元,立减现金{{ promotionMap['FULL_DISCOUNT'].fullMinus}}元</span>
+                <span class="item-desc-pintuan" v-if="promotionMap['FULL_DISCOUNT'].fullRate">满{{ promotionMap['FULL_DISCOUNT'].fullMoney }}元,立享{{ promotionMap['FULL_DISCOUNT'].fullRate}}折</span>
+              </p>
+            </div>
+          </div>
+          <div class="item-price-right">
+            <div class="item-remarks-sum">
+              <p>累计评价</p>
+              <p>
+                <span class="item-remarks-num">{{ skuDetail.commentNum || 0 }} 条</span>
+              </p>
+            </div>
+          </div>
+        </div>
+        <!-- 选择颜色 -->
+        <div class="item-select" v-for="(sku, index) in formatList" :key="sku.id">
+          <div class="item-select-title">
+            <p>{{ sku.name }}</p>
+          </div>
+          <div class="item-select-column">
+            <div class="item-select-row" v-for="(item) in sku.values" :key="item.id">
+              <div class="item-select-box" @click="select(index, sku.id, item.id)"
+                :class="{ 'item-select-box-active': item.id === currentSelceted[index] }"
+              >
+                <div class="item-select-intro">
+                  <p>{{ item.value }}</p>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <br />
+        <div class="add-buy-car-box">
+          <div class="item-select">
+            <div class="item-select-title">
+              <p>数量</p>
+            </div>
+            <div class="item-select-row">
+              <InputNumber :min="1" :disabled="skuDetail.quantity === 0" v-model="count"></InputNumber>
+              <span class="inventory"> 库存{{skuDetail.quantity}}</span>
+            </div>
+          </div>
+          <div class="item-select">
+            <div class="item-select-title">
+              <p>重量</p>
+            </div>
+            <div class="item-select-row">
+              <span class="inventory"> {{skuDetail.weight}}kg</span>
+            </div>
+          </div>
+          <div class="add-buy-car">
+            <Button type="error" :loading="loading" :disabled="skuDetail.quantity === 0" @click="addShoppingCartBtn">加入购物车</Button>
+            <Button type="warning" :loading="loading1" :disabled="skuDetail.quantity === 0" @click="buyNow">立即购买</Button>
+          </div>
+        </div>
+      </div>
+
+      <!-- <div class="item-detail-see">
+        <Divider>更多推荐</Divider>
+        <Row>
+          <Col :span="24" class="see-Item">
+            <img class="see-Img" src="https://demo.dscmall.cn/storage/images/201703/thumb_img/0_thumb_G_1489099128797.jpg" alt="" />
+            <p>
+              名龙堂i7 6700升7700 GTX1060 6G台式电脑主机DIY游戏组装整机
+              升6GB独显 送正版WIN10 一年上门
+            </p>
+            <p class="global_color">¥2500.00</p>
+          </Col>
+          <Col :span="24" class="see-Item">
+            <img
+              class="see-Img"
+              src="https://demo.dscmall.cn/storage/images/201703/thumb_img/0_thumb_G_1489099128797.jpg"
+              alt=""
+            />
+            <p>
+              名龙堂i7 6700升7700 GTX1060 6G台式电脑主机DIY游戏组装整机
+              升6GB独显 送正版WIN10 一年上门
+            </p>
+            <p class="global_color">¥2500.00</p>
+          </Col>
+        </Row>
+      </div> -->
+    </div>
+  </div>
+</template>
+
+<script>
+import Promotion from './Promotion.vue';
+import PicZoom from 'vue-piczoom'; // 图片放大 https://github.com/826327700/vue-piczoom
+import { collectGoods, isCollection, receiveCoupon, cancelCollect } from '@/api/member.js';
+import { addCartGoods } from '@/api/cart.js';
+export default {
+  name: 'ShowGoods',
+  props: {
+    detail: {
+      type: Object,
+      default: null
+    }
+  },
+  data () {
+    return {
+      count: 1, // 商品数量
+      imgIndex: 0, // 展示图片下标
+      currentSelceted: [], // 当前商品sku
+      imgList: this.detail.data.specList[0].specImage, // 商品图片列表
+      skuDetail: this.detail.data, // sku详情
+      goodsSpecList: this.detail.specs, // 商品spec
+      promotionMap: { // 活动状态
+        SECKILL: null,
+        FULL_DISCOUNT: null,
+        COUPON: []
+      }, // 促销活动
+      formatList: [], // 选择商品品类的数组
+      loading: false, // 立即购买loading
+      loading1: false, // 加入购物车loading
+      isCollected: false // 是否收藏
+    };
+  },
+  components: {
+    PicZoom,
+    Promotion
+  },
+  methods: {
+    select (index, id, valueId) { // 选择规格
+      this.$set(this.currentSelceted, index, valueId);
+
+      let selectedSkuId = this.goodsSpecList.find((i) => {
+        let matched = true;
+        let specValues = i.specValues.filter((j) => j.specName !== 'images');
+        for (let n = 0; n < specValues.length; n++) {
+          if (specValues[n].specValueId !== this.currentSelceted[n]) {
+            matched = false;
+            return;
+          }
+        }
+        if (matched) {
+          return i;
+        }
+      });
+      console.log(selectedSkuId);
+      this.$router.push({
+        path: '/goodsDetail',
+        query: { skuId: selectedSkuId.skuId, goodsId: this.skuDetail.goodsId }
+      });
+    },
+
+    addShoppingCartBtn () { // 添加购物车
+      const params = {
+        num: this.count,
+        skuId: this.skuDetail.id
+      };
+      this.loading = true;
+      addCartGoods(params).then(res => {
+        this.loading = false;
+        if (res.code === 200) {
+          this.$router.push({path: '/shoppingCart', query: {detail: this.skuDetail, count: this.count}});
+        } else {
+          this.$Message.warning(res.message);
+        }
+      });
+    },
+    buyNow () { // 立即购买
+      const params = {
+        num: this.count,
+        skuId: this.skuDetail.id,
+        cartType: 'BUY_NOW'
+      };
+      this.loading1 = true;
+      addCartGoods(params).then(res => {
+        this.loading1 = false;
+        if (res.code === 200) {
+          this.$router.push({path: '/pay', query: {way: 'BUY_NOW'}});
+        } else {
+          this.$Message.warning(res.message);
+        }
+      });
+    },
+    async collect () { // 收藏商品
+      if (this.isCollected) {
+        let cancel = await cancelCollect('GOODS', this.skuDetail.id)
+        if (cancel.success) {
+          this.$Message.success('取消收藏成功')
+          this.isCollected = false
+        }
+      } else {
+        let collect = await collectGoods('GOODS', this.skuDetail.id);
+        if (collect.code === 200) {
+          this.isCollected = true;
+          this.$Message.success('收藏商品成功,可以前往个人中心我的收藏查看');
+        }
+      }
+    },
+    formatSku (list) {
+      // 格式化数据
+
+      let arr = [{}];
+
+      list.forEach((item, index) => {
+        item.specValues.forEach((spec, specIndex) => {
+          let id = spec.specNameId;
+          let name = spec.specName;
+          let values = {
+            id: spec.specValueId,
+            value: spec.specValue,
+            quantity: item.quantity
+          };
+          if (name === 'images') {
+            return;
+          }
+
+          arr.forEach((arrItem, arrIndex) => {
+            if (
+              arrItem.name === name &&
+              arrItem.values &&
+              !arrItem.values.find((i) => i.id === values.id)
+            ) {
+              arrItem.values.push(values);
+            }
+
+            let keys = arr.map((key) => {
+              return key.name;
+            });
+            if (!keys.includes(name)) {
+              arr.push({
+                id: id,
+                name: name,
+                values: [values]
+              });
+            }
+          });
+        });
+      });
+      arr.shift();
+      this.formatList = arr;
+
+      let cur = list.filter((i) => i.skuId === this.$route.query.skuId)[0];
+      if (cur) {
+        cur.specValues.filter((i) => i.specName !== 'images')
+          .forEach((value, _index) => {
+            this.currentSelceted[_index] = value.specValueId;
+          });
+      }
+      this.skuList = list;
+    },
+    receiveCoupon (id) { // 领取优惠券
+      receiveCoupon(id).then(res => {
+        if (res.success) {
+          this.$Message.success('优惠券领取成功')
+        } else {
+          this.$Message.warning(res.message)
+        }
+      })
+    },
+    promotion () { // 格式化促销活动,返回当前促销的对象
+      let keysArr = Object.keys(this.detail.promotionMap);
+      if (keysArr.length === 0) return false;
+
+      for (let i = 0; i < keysArr.length; i++) {
+        let key = keysArr[i].split('-')[0]
+        if (key === 'COUPON') {
+          this.promotionMap[key].push(this.detail.promotionMap[keysArr[i]])
+        } else {
+          this.promotionMap[key] = this.detail.promotionMap[keysArr[i]]
+        }
+      }
+    }
+  },
+  mounted () {
+    if (this.Cookies.getItem('userInfo')) {
+      isCollection('GOODS', this.skuDetail.id).then(res => {
+        if (res.success && res.result) {
+          this.isCollected = true;
+        }
+      })
+    }
+    
+    this.formatSku(this.goodsSpecList);
+    this.promotion()
+    document.title = this.skuDetail.goodsName
+  }
+};
+</script>
+
+<style scoped lang="scss">
+/******************商品图片及购买详情开始******************/
+.item-detail-see {
+  width: 175px;
+  margin-left: 30px;
+}
+.inventory {
+  padding-left: 4px;
+  @include sub_color($light_sub_color);
+}
+
+.global_color {
+  text-align: center;
+}
+
+.see-Img {
+  width: 100%;
+  height: 175px;
+}
+
+.see-Item {
+  > p {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+}
+
+.Report {
+  color: $theme_color !important;
+}
+
+.wrapper {
+  @include white_background_color();
+}
+.item-sale-flex {
+  width: 29%;
+  padding: 0 3%;
+}
+.item-sale {
+  margin: 10px 0;
+  > h3 {
+    width: 13%;
+    text-align: center;
+    font-size: 20px;
+    line-height: 60px;
+    box-sizing: border-box;
+    border-right: 1px solid $border_color;
+  }
+  height: 60px;
+  justify-content: center;
+  align-items: center;
+  display: flex;
+  width: 1200px;
+  margin: 0 auto;
+  margin-bottom: 10px;
+  border: 1px solid $border_color;
+  background: #f7f7f7;
+}
+
+.item-detail-show {
+  width: 1200px;
+  margin: 0 auto;
+  padding: 30px;
+  display: flex;
+  flex-direction: row;
+}
+
+.item-detail-left {
+  width: 350px;
+  margin-right: 30px;
+}
+
+.item-detail-big-img {
+  width: 350px;
+  height: 350px;
+  box-shadow: 0px 0px 8px $border_color;
+  cursor: pointer;
+}
+
+.item-detail-big-img img {
+  width: 100%;
+}
+
+.item-detail-img-row {
+  margin-top: 15px;
+  display: flex;
+}
+
+.item-detail-img-small {
+  width: 68px;
+  height: 68px;
+  box-shadow: 0px 0px 8px #ccc;
+  cursor: pointer;
+  margin-left: 5px;
+}
+
+.item-detail-img-small img {
+  height: 100%;
+  width: 100%;
+}
+
+/*商品选购详情*/
+.item-detail-right {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
+
+.item-detail-title p {
+  @include content_color($light_content_color);
+  font-weight: bold;
+  font-size: 20px;
+  padding: 8px 0;
+}
+
+.item-detail-express {
+  font-size: 14px;
+  padding: 2px 3px;
+  border-radius: 3px;
+  background-color: $theme_color;
+  color: #fff;
+}
+
+/*商品标签*/
+.item-detail-tag {
+  padding: 8px 0;
+  font-size: 12px;
+  color: $theme_color;
+}
+
+/*价格详情等*/
+.item-detail-price-row {
+  padding: 10px;
+  display: flex;
+  // width: 555px;
+  flex-direction: row;
+  justify-content: space-between;
+  // @include background_color($light_background_color);
+  background: url("../../assets/images/goodsDetail/price-bg.png");
+}
+
+.item-price-left {
+  display: flex;
+  flex-direction: column;
+}
+
+.item-price-title {
+  color: #999999;
+  font-size: 14px;
+  margin-right: 15px;
+}
+
+.item-price-row {
+  margin: 5px 0px;
+}
+
+.item-price {
+  color: $theme_color;
+  font-size: 20px;
+  cursor: pointer;
+}
+.item-price-old {
+  color: gray;
+  text-decoration: line-through;
+  font-size: 14px;
+  margin-left: 5px;
+}
+
+.item-coupon {
+  margin-right: 5px;
+  padding: 3px;
+  color: $theme_color;
+  font-size: 12px;
+  background-color: #ffdedf;
+  border: 1px dotted $theme_color;
+  cursor: pointer;
+}
+.item-promotion {
+  margin-right: 5px;
+  padding: 3px;
+  color: $theme_color;
+  font-size: 12px;
+  border: 1px solid $theme_color;
+}
+.item-remarks-sum {
+  padding-left: 8px;
+  border-left: 1px solid $border_color;
+}
+
+.item-remarks-sum p {
+  color: #999999;
+  font-size: 12px;
+  line-height: 10px;
+  text-align: center;
+}
+
+.item-remarks-num {
+  line-height: 18px;
+  color: #005eb7;
+}
+
+.item-select {
+  display: flex;
+  flex-direction: row;
+  margin-top: 15px;
+}
+
+.item-select-title {
+  @include content_color($light_content_color);
+  font-size: 14px;
+  margin-right: 15px;
+  width: 60px;
+}
+
+.item-select-column {
+  display: flex;
+  flex-wrap: wrap;
+  flex: 1;
+}
+
+.item-select-row {
+  // display: flex;
+  // flex-direction: row;
+  // flex-wrap: wrap;
+  margin-bottom: 8px;
+}
+
+.item-select-box {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+
+.item-select-img {
+  width: 36px;
+}
+
+.item-select-box {
+  padding: 5px;
+  margin-right: 8px;
+  @include background_color($light_background_color);
+  border: 0.5px solid $border_color;
+  cursor: pointer;
+  @include content_color($light_content_color);
+}
+
+.item-select-box:hover {
+  border: 0.5px solid $theme_color;
+}
+
+.item-select-box-active {
+  border: 0.5px solid $theme_color;
+}
+
+.item-select-box-disabled {
+  background-color: gray;
+}
+
+.item-select-img img {
+  width: 100%;
+}
+
+.item-select-intro p {
+  margin: 0px;
+  padding: 5px;
+}
+
+.item-select-class {
+  padding: 5px;
+  margin-right: 8px;
+  @include sub_background_color($light_background_color);
+  border: 0.5px solid #ccc;
+  cursor: pointer;
+}
+
+.item-select-class:hover {
+  border: 0.5px solid $theme_color;
+}
+
+.add-buy-car-box {
+  width: 100%;
+  margin-top: 15px;
+  border-top: 1px dotted $border_color;
+}
+
+.add-buy-car {
+  margin-top: 15px;
+}
+
+.goodsConfig {
+  display: flex;
+  justify-content: space-between;
+  > span {
+    padding-right: 10px;
+    &:hover{
+      cursor: pointer;
+      color: $theme_color;
+    }
+  }
+}
+/******************商品图片及购买详情结束******************/
+</style>

+ 489 - 0
buyer/src/components/goodsDetail/ShowGoodsDetail.vue

@@ -0,0 +1,489 @@
+<template>
+  <div>
+    <div class="item-intro-show">
+      <!-- <div class="item-intro-recommend">
+        <div class="item-recommend-title">
+          <p>店铺热销</p>
+        </div>
+        <div class="item-intro-recommend-column">
+          <div class="item-recommend-column" v-for="(item, index) in hotList" :key="index">
+            <div class="item-recommend-img">
+              <img :src="item.img" alt="">
+            </div>
+            <div class="item-recommend-intro">
+              <span>
+                <span class="item-recommend-top-num">{{index + 1}}</span> 热销{{item.sale}}件</span>
+              <span class="item-recommend-price">¥{{item.price | unitPrice}}</span>
+            </div>
+          </div>
+        </div>
+      </div> -->
+      <div class="item-intro-detail" ref="itemIntroDetail">
+        <div class="item-intro-nav item-tabs">
+          <Tabs :animated="false" @on-click="tabClick">
+            <TabPane label="商品介绍">
+              <div class="item-intro-img" ref="itemIntroGoods">
+                <div v-html="skuDetail.intro" v-if="skuDetail.intro"></div>
+                <div v-else style="margin:20px;">暂无商品介绍</div>
+              </div>
+            </TabPane>
+            <TabPane label="商品评价">
+              <div class="remarks-container" ref="itemGoodsComment">
+                <div class="remarks-analyse-box">
+                  <div class="remarks-analyse-goods">
+                    <i-circle :percent="skuDetail.grade || 100" stroke-color="#5cb85c">
+                      <span class="remarks-analyse-num">{{skuDetail.grade || 100}}%</span>
+                      <p class="remarks-analyse-title">好评率</p>
+                    </i-circle>
+                  </div>
+                </div>
+                <div class="remarks-bar">
+                  <span @click="searchByGrade('')" :class="{selectedBar: commentParams.grade === ''}">全部({{commentTypeNum.all}})</span>
+                  <span @click="searchByGrade('GOOD')" :class="{selectedBar: commentParams.grade === 'GOOD'}">好评({{commentTypeNum.good}})</span>
+                  <span @click="searchByGrade('MODERATE')" :class="{selectedBar: commentParams.grade === 'MODERATE'}">中评({{commentTypeNum.moderate}})</span>
+                  <span @click="searchByGrade('WORSE')" :class="{selectedBar: commentParams.grade === 'WORSE'}">差评({{commentTypeNum.worse}})</span>
+                </div>
+                <div style="text-align: center;margin-top: 20px;" v-if="commentList.length === 0">
+                  暂无评价数据
+                </div>
+                <div class="remarks-box" v-for="(item,index) in commentList" :key="index" v-else>
+                  <div class="remarks-user">
+                    <Avatar :src="item.memberProfile" />
+                    <span class="remarks-user-name">{{item.memberName | secrecyMobile}}</span>
+                  </div>
+                  <div class="remarks-content-box">
+                    <p>
+                      <Rate disabled :value="Number(item.descriptionScore)" allow-half class="remarks-star"></Rate>
+                    </p>
+                    <p class="remarks-content">{{item.content}}</p>
+                    <div class="comment-img" v-if="item.haveImage">
+                      <div v-for="(img, imgIndex) in item.image.split(',')"
+                       @click="previewImg(img, item)"
+                       :class="{borderColor:img === item.previewImg}"
+                       :key="imgIndex">
+                        <img :src="img" alt="">
+                      </div>
+                    </div>
+                    <div class="preview-img"  v-if="item.previewImg"  @click.prevent="hidePreviewImg(item)">
+                      <div>
+                        <span @click.stop="rotatePreviewImg(0, item)"><Icon type="md-refresh" />左转</span>
+                        <span @click.stop="rotatePreviewImg(1, item)"><Icon type="md-refresh" />右转</span>
+                      </div>
+                      <img :src="item.previewImg" :style="{transform:`rotate(${item.deg}deg)`}" width="198" alt="">
+                    </div>
+                    <p class="remarks-sub">
+                      <span class="remarks-item">{{item.goodsName}}</span>
+                      <span class="remarks-time">{{item.createTime}}</span>
+                    </p>
+                  </div>
+                </div>
+                <div class="remarks-page">
+                  <Page :total="commentTotal" size="small"
+                    @on-change="changePageNum"
+                    @on-page-size-change="changePageSize"
+                    :page-size="commentParams.pageSize"
+                    ></Page>
+                </div>
+              </div>
+            </TabPane>
+            <!-- <TabPane label="商品问答">
+              <ShowGoodsQuestion/>
+            </TabPane> -->
+          </Tabs>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import ShowGoodsQuestion from '@/components/goodsDetail/ShowGoodsQuestion';
+import { goodsComment, goodsCommentNum } from '@/api/member.js';
+export default {
+  name: 'ShowGoodsDetail',
+  props: {
+    detail: { // 商品详情
+      type: Object,
+      default: null
+    }
+  },
+  data () {
+    return {
+      commentList: [], // 评论列表
+      commentParams: { // 评论传参
+        pageNumber: 1,
+        pageSize: 10,
+        grade: '',
+        goodsId: ''
+      },
+      commentTypeNum: {}, // 评论数量,包括好中差分别的数量
+      commentTotal: 0, // 评论总数
+      onceFlag: true // 只调用一次
+    };
+  },
+  computed: {
+    skuDetail () {
+      return this.detail.data;
+    }
+  },
+  methods: {
+    changeHeight (name) {
+      let heightCss = window.getComputedStyle(this.$refs[name]).height;
+      heightCss = parseInt(heightCss.substr(0, heightCss.length - 2)) + 89;
+      this.$refs.itemIntroDetail.style.height = heightCss + 'px';
+    },
+    changePageNum (val) {
+      this.commentParams.pageNumber = val;
+      this.getList();
+    },
+    changePageSize (val) {
+      this.commentParams.pageNumber = 1;
+      this.commentParams.pageSize = val;
+      this.getList();
+    },
+    getList () { // 获取评论列表
+      this.commentParams.goodsId = this.skuDetail.goodsId;
+      goodsComment(this.commentParams).then(res => {
+        if (res.code === 200) {
+          this.commentList = res.result.records;
+          this.commentTotal = res.result.total;
+        }
+      });
+      goodsCommentNum(this.skuDetail.goodsId).then(res => {
+        if (res.code === 200) {
+          this.commentTypeNum = res.result;
+        }
+      });
+    },
+    searchByGrade (grade) {
+      this.$set(this.commentParams, 'grade', grade);
+      this.commentParams.pageNumber = 1;
+      this.getList();
+    },
+    tabClick (name) {
+      if (name === 0) {
+        this.$nextTick(() => {
+          this.changeHeight('itemIntroGoods')
+        });
+      } else {
+        this.$nextTick(() => {
+          this.changeHeight('itemGoodsComment')
+        });
+      }
+    },
+    previewImg (img, item) { // 预览图片
+      this.$set(item, 'previewImg', img);
+      this.$nextTick(() => {
+        this.changeHeight('itemGoodsComment')
+      });
+    },
+    hidePreviewImg (item) { // 隐藏预览图片
+      this.$set(item, 'previewImg', '');
+      this.$nextTick(() => {
+        this.changeHeight('itemGoodsComment')
+      });
+    },
+    rotatePreviewImg (type, item) { // 图片旋转
+      if (type) {
+        if (item.deg) {
+          this.$set(item, 'deg', item.deg + 90);
+        } else {
+          this.$set(item, 'deg', 90);
+        }
+      } else {
+        if (item.deg) {
+          this.$set(item, 'deg', item.deg - 90);
+        } else {
+          this.$set(item, 'deg', -90);
+        }
+      }
+    },
+    handleScroll () {
+      if (this.onceFlag) {
+        this.$nextTick(() => {
+          this.changeHeight('itemIntroGoods')
+        });
+        this.onceFlag = false
+      }
+    }
+  },
+  mounted () {
+    this.$nextTick(() => {
+      setTimeout(this.changeHeight('itemIntroGoods'), 2000);
+    });
+    window.addEventListener('scroll', this.handleScroll)
+    this.getList();
+  },
+  components: {
+    ShowGoodsQuestion
+  }
+};
+</script>
+
+<style scoped lang="scss">
+/***************商品详情介绍和推荐侧边栏开始***************/
+.item-intro-show{
+
+  width: 1200px;
+  margin: 15px auto;
+  display: flex;
+  flex-direction: row;
+
+}
+.item-intro-recommend{
+  width: 200px;
+  display: flex;
+  flex-direction: column;
+}
+.item-intro-recommend-column{
+  display: flex;
+  flex-direction: column;
+  box-shadow: 0px 0px 5px #999;
+}
+.item-recommend-title{
+  width: 100%;
+  height: 38px;
+  font-size: 16px;
+  line-height: 38px;
+  color: #fff;
+  background-color: $theme_color;
+  box-shadow: 0px 0px 5px $theme_color;
+  text-align: center;
+}
+.item-recommend-column{
+  margin-top: 15px;
+}
+.item-recommend-intro{
+  padding: 5px 15px;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  font-size: 12px;
+  color: #999;
+  cursor: pointer;
+}
+.item-recommend-img{
+  width: 80%;
+  margin: 0px auto;
+  cursor: pointer;
+}
+.item-recommend-img img{
+  width: 100%;
+}
+.item-recommend-top-num{
+  color: #fff;
+  margin: 0px 2px;
+  padding: 1px 5px;
+  border-radius: 12px;
+  background-color: $theme_color;
+}
+.item-recommend-price{
+  color: $theme_color;
+  font-weight: bolder;
+}
+.item-intro-detail{
+  margin: 0  30px;
+  // min-height: 1500px;
+  width: 100%;
+}
+.item-intro-nav{
+  width: 100%;
+  height: 38px;
+  background-color: #F7F7F7;
+  // border-bottom: 1px solid $theme_color;
+}
+.item-intro-nav ul{
+  margin: 0px;
+  padding: 0px;
+  list-style: none;
+}
+.item-intro-nav li{
+  float: left;
+  height: 100%;
+  width: 120px;
+  line-height: 38px;
+  text-align: center;
+  color: $theme_color;
+}
+.item-intro-nav li:first-child{
+  background-color: $theme_color;
+  color: #fff;
+}
+.item-intro-img {
+  width: 100%;
+  min-height: 300px;
+}
+.item-intro-img img{
+  max-width: 1000px;
+}
+/************* 商品参数 *************/
+.item-param-container {
+  display: flex;
+  flex-wrap: wrap;
+  flex-direction: row;
+  justify-content: space-between;
+}
+.item-param-box {
+  padding: 5px;
+  padding-left: 30px;
+  width: 240px;
+  height: 36px;
+  font-size: 14px;
+  /* text-align: center; */
+  /* background-color: #ccc; */
+}
+.item-param-title {
+  color: #232323;
+}
+.item-param-content {
+  color: #999;
+}
+.remarks-title {
+  padding-left: 15px;
+  height: 36px;
+  font-size: 16px;
+  font-weight: bolder;
+  line-height: 36px;
+  color: #666666;
+  background-color: #F7F7F7;
+}
+.remarks-analyse-box {
+  padding: 15px;
+  display: flex;
+  align-items: center;
+}
+.remarks-analyse-goods {
+  margin-left: 15px;
+  margin-right: 15px;
+}
+.remarks-analyse-num {
+  font-size: 26px;
+}
+.remarks-analyse-title {
+  font-size: 12px;
+  line-height: 20px;
+}
+.remarks-bar {
+  padding-left: 15px;
+  height: 36px;
+  line-height: 36px;
+  color: #666666;
+  background-color: #F7F7F7;
+  .selectedBar{
+    color: $theme_color;
+  }
+}
+.remarks-bar span {
+  margin-right: 15px;
+  &:hover{
+    color: $theme_color;
+    cursor: pointer;
+  }
+}
+.remarks-box {
+  padding: 15px;
+  display: flex;
+  flex-direction: row;
+  border-bottom: 1px #ccc dotted;
+}
+.remarks-user {
+  width: 180px;
+}
+.remarks-user-name {
+  padding-left: 15px;
+}
+.remarks-content-box {
+  width: calc(100% - 180px);
+  .comment-img{
+    display: flex;
+    .borderColor{
+      border-color: $theme_color;
+    }
+    div{
+      border: 1px solid #999;
+      margin-right: 5px;
+      width: 50px;
+      height: 50px;
+      img{width: 100%;}
+    }
+  }
+  .preview-img{
+    position: relative;
+    border: 1px solid #eee;
+    margin: 10px 0;
+    width: 200px;
+
+    div{
+      position: absolute;
+      top: 3px;
+      left: 3px;
+      z-index: 3;
+      span{
+        display: inline-block;
+        background-color: rgba(0,0,0,.5);
+        padding:3px 5px;
+        color: #fff;
+        border-radius: 4px;
+        cursor: pointer;
+      }
+      span:nth-child(1) .ivu-icon {
+        transform: rotateY(180deg);
+      }
+    }
+
+    img:hover{
+      cursor: url(require('../../../static/small.cur')),auto;
+    }
+  }
+}
+
+.remarks-content {
+  font-size: 14px;
+  color: #232323;
+  line-height: 28px;
+}
+.remarks-sub {
+  margin-top: 5px;
+  color: #ccc;
+}
+.remarks-time {
+  margin-left: 15px;
+}
+.remarks-page {
+  margin: 15px;
+  display: flex;
+  justify-content:flex-end;
+}
+/***************商品详情介绍和推荐侧边栏结束***************/
+/* 改变便签页样式 */
+.ivu-tabs-ink-bar {
+  background-color: $theme_color !important;
+}
+/deep/.ivu-tabs-bar{
+  border: none;
+}
+.item-tabs > .ivu-tabs > .ivu-tabs-bar .ivu-tabs-tab{
+  border-radius: 0px;
+  color: #999;
+  height: 38px;
+  // background: #F7F7F7;
+}
+.item-tabs > .ivu-tabs > .ivu-tabs-bar .ivu-tabs-tab-active{
+  color: #fff;
+  background-color: $theme_color;
+}
+.item-tabs > .ivu-tabs > .ivu-tabs-bar .ivu-tabs-tab-active:before{
+  content: '';
+  display: block;
+  width: 100%;
+  height: 1px;
+  color: #fff;
+  background: #F7F7F7;
+  position: absolute;
+  top: 0;
+  left: 0;
+}
+.ivu-rate-star-full:before, .ivu-rate-star-half .ivu-rate-star-content:before {
+  color: $theme_color;
+}
+</style>

+ 53 - 0
buyer/src/components/goodsDetail/ShowGoodsQuestion.vue

@@ -0,0 +1,53 @@
+<template>
+  <div class="wrapper">
+    <div v-if="true" class="question-list">
+      <div class="-item" v-for="index in 6" :key="index">
+        <!-- 提问 -->
+        <div class="-item-put -item-div">
+          <div class="-item-div-l blod">
+            <Tag color="warning">问</Tag>
+            有屏幕调节亮度吗
+          </div>
+          <div class="-item-div-r">2020年10月21日17:03:35</div>
+        </div>
+        <!-- 解答 -->
+        <div class="-item-reply -item-div">
+          <div class="-item-div-l">
+            <Tag color="success">答</Tag>
+            能调节,点屏幕上方有一条调整带,可正负2调整。
+          </div>
+          <div class="-item-div-r">2020年10月21日17:03:35</div>
+        </div>
+      </div>
+    </div>
+    <div v-else class="question-empty">
+      <empty></empty>
+    </div>
+  </div>
+</template>
+<script>
+export default {};
+</script>
+<style scoped lang="scss">
+.-item-div {
+  padding: 10px 0;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin: 10px 0;
+}
+.-item-div-l {
+  display: flex;
+  @include content_color($light_content_color);
+}
+.-item-div-r {
+  @include sub_color($light_content_color);
+}
+.blod {
+  font-weight: bold;
+}
+.-item {
+  margin: 10px 0;
+  border-bottom: 1px solid $border_color;
+}
+</style>

+ 85 - 0
buyer/src/components/goodsDetail/ShowProductWarranty.vue

@@ -0,0 +1,85 @@
+<template>
+  <div>
+    <div class="remarks-title">
+      <span>售后保障</span>
+    </div>
+    <div class="item-protect-container">
+      <div class="item-protect-box">
+        <p class="item-protect-title-box">
+          <Avatar style="background-color: #e4393c" icon="ribbon-a" />
+          <span class="item-protect-title">卖家服务</span>
+        </p>
+        <p class="item-protect-detail">
+          高品质敢承诺:7天无理由退货,30天免费换新,质量问题商家承担来回运费换新;如需发票,请在确认收货无误后联系商家开出。(注*发票不随货品一同发出)
+        </p>
+      </div>
+      <div class="item-protect-box">
+        <p class="item-protect-title-box">
+          <Avatar style="background-color: #e4393c" icon="cash" />
+          <span class="item-protect-title">平台承诺</span>
+        </p>
+        <p class="item-protect-detail">
+          平台卖家销售并发货的商品,由平台卖家提供发票和相应的售后服务。请您放心购买!<br> 注:因厂家会在没有任何提前通知的情况下更改产品包装、产地或者一些附件,本司不能确保客户收到的货物与商城图片、产地、附件说明完全一致。只能确保为原厂正货!并且保证与当时市场上同样主流新品一致。若本商城没有及时更新,请大家谅解!
+        </p>
+      </div>
+      <div class="item-protect-box">
+        <p class="item-protect-title-box">
+          <Avatar style="background-color: #e4393c" icon="locked" />
+          <span class="item-protect-title">正品行货</span>
+        </p>
+        <p class="item-protect-detail">
+          BIT商城向您保证所售商品均为正品行货,BIT自营商品开具机打发票或电子发票。
+        </p>
+      </div>
+      <div class="item-protect-box">
+        <p class="item-protect-title-box">
+          <Avatar style="background-color: #e4393c" icon="settings" />
+          <span class="item-protect-title">全国联保</span>
+        </p>
+        <p class="item-protect-detail">
+          凭质保证书及BIT商城发票,可享受全国联保服务(奢侈品、钟表除外;奢侈品、钟表由BIT联系保修,享受法定三包售后服务),与您亲临商场选购的商品享受相同的质量保证。BIT商城还为您提供具有竞争力的商品价格和运费政策,请您放心购买!<br><br> 注:因厂家会在没有任何提前通知的情况下更改产品包装、产地或者一些附件,本司不能确保客户收到的货物与商城图片、产地、附件说明完全一致。只能确保为原厂正货!并且保证与当时市场上同样主流新品一致。若本商城没有及时更新,请大家谅解!
+        </p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ShowProductWarranty'
+};
+</script>
+
+<style scoped lang="scss">
+.remarks-title {
+  padding-left: 15px;
+  height: 36px;
+  font-size: 16px;
+  font-weight: bolder;
+  line-height: 36px;
+  color: #666666;
+  background-color: #F7F7F7;
+}
+.item-protect-container {
+  padding: 15px;
+}
+.item-protect-box {
+  margin-bottom: 30px;
+}
+.item-protect-title-box {
+  display: flex;
+  align-items: center;
+}
+.item-protect-title {
+  padding-left: 15px;
+  font-size: 20px;
+  font-weight: bolder;
+  color: $theme_color;
+}
+.item-protect-detail {
+  padding-top: 5px;
+  padding-left: 46px;
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 426 - 0
buyer/src/components/header/Header.vue

@@ -0,0 +1,426 @@
+<template>
+  <div class="box">
+    <div class="nav">
+      <ul class="location">
+        <li><router-link to="/" v-if="$route.path !== '/'" class="home-page" ><Icon type="md-home" />首页</router-link></li>
+        <li>
+          <Dropdown placement="bottom-start">
+            <a href="javascript:void(0)">
+              <Icon type="ios-pin" class="icon"></Icon>
+              {{ city }}
+            </a>
+            <DropdownMenu slot="list">
+              <div class="city">
+                <p v-for="(items, index) in cityArr" :key="index">
+                  <span
+                    v-for="(item, index) in items"
+                    class="city-item"
+                    :key="index"
+                    @click="changeCity(item)"
+                    >{{ item }}</span>
+                </p>
+              </div>
+            </DropdownMenu>
+          </Dropdown>
+        </li>
+      </ul>
+      <ul class="detail">
+        <!-- <li class="first hover-pointer" @click="handleClickTheme()">切换主题</li> -->
+        <li class="first" v-show="!userInfo.username">
+          <router-link :to="`/login?rePath=${$route.path}&query=${JSON.stringify($route.query)}`">
+            <span style="border:none" class="tipsLogin">请登录</span>
+          </router-link>
+        </li>
+        <li v-show="!!userInfo.username">
+          <Dropdown>
+            <p class="username-p">
+              <Avatar class="person-icon" :src="userInfo.face" icon="person" size="small" />
+              <span class="username">{{ userInfo.nickName? userInfo.nickName : userInfo.username | secrecyMobile }}</span>
+            </p>
+            <DropdownMenu slot="list">
+              <div class="my-page">
+                <div class="my-info" @click="myInfo">
+                  <Icon type="md-home"></Icon>
+                  <p>我的主页</p>
+                </div>
+                <div class="sign-out" @click="signOutFun">
+                  <Icon type="md-exit"></Icon>
+                  <p>退出登陆</p>
+                </div>
+              </div>
+            </DropdownMenu>
+          </Dropdown>
+        </li>
+        <li class="hover-color" @click="goUserCenter('home/MyOrder')"><span class="nav-item">我的订单</span></li>
+        <li class="hover-color" @click="goUserCenter('home/MyTracks')"><span class="nav-item">我的足迹</span></li>
+        <li v-if="$route.name !== 'Cart'" style="position:relative;" @mouseenter="getCartList">
+          <i class="cart-badge" v-show="cartNum">{{cartNum < 100 ? cartNum : '99'}}</i>
+          <Dropdown placement="bottom-start">
+            <router-link to="cart" target="_blank">
+              <Icon
+                size="18"
+                class="cart-icon"
+                type="ios-cart-outline"
+              ></Icon>
+              购物车
+            </router-link>
+
+            <DropdownMenu slot="list">
+              <div class="shopping-cart-null" style="width:200px" v-show="shoppingCart.length <= 0">
+                <Icon type="ios-cart-outline" class="cart-null-icon"></Icon>
+                <span>你的购物车没有宝贝哦</span>
+                <span>赶快去添加商品吧~</span>
+              </div>
+              <div class="shopping-cart-list" v-show="shoppingCart.length > 0">
+                <div
+                  class="shopping-cart-box"
+                  v-for="(item, index) in shoppingCart"
+                  @click="goToPay"
+                  :key="index"
+                >
+                  <div class="shopping-cart-img">
+                    <img :src="item.goodsSku.thumbnail" class="hover-pointer" />
+                  </div>
+                  <div class="shopping-cart-info">
+                    <div class="shopping-cart-title ">
+                      <p class="hover-pointer goods-title">{{ item.goodsSku.goodsName }}</p>
+                    </div>
+                    <div class="shopping-cart-detail">
+                      <p>
+                        数量:
+                        <span class="shopping-cart-text">{{ item.num }}</span>
+                        价钱:
+                        <span class="shopping-cart-text">{{ item.purchasePrice | unitPrice('¥') }}</span>
+                      </p>
+                    </div>
+                  </div>
+                </div>
+                <div class="go-to-buy">
+                  <Button type="error" size="small" @click="goToPay">去结账</Button>
+                </div>
+              </div>
+            </DropdownMenu>
+          </Dropdown>
+        </li>
+        <li>
+          <span class="nav-item" @click="shopEntry">店铺入驻</span>
+        </li>
+        <!-- <li>
+          <router-link to="/feedback">意见反馈</router-link>
+        </li>-->
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script>
+import storage from '@/plugins/storage.js';
+import {cartGoodsAll, cartCount} from '@/api/cart.js'
+export default {
+  name: 'M-Header',
+  created () {
+    if (storage.getItem('userInfo')) {
+      this.userInfo = JSON.parse(storage.getItem('userInfo'));
+    }
+  },
+
+  data () {
+    return {
+      // 主题颜色切换
+      themeType: 'light',
+      city: '珠海', // 展示城市
+      cityArr: [
+        ['北京', '上海', '天津', '重庆', '广州'],
+        ['深圳', '河南', '辽宁', '吉林', '江苏'],
+        ['江西', '四川', '海南', '贵州', '云南'],
+        ['西藏', '陕西', '甘肃', '青海', '珠海']
+      ],
+      userInfo: {}, // 用户信息
+      shoppingCart: [] // 购物车
+    };
+  },
+  computed: {
+    cartNum () {
+      return this.$store.state.cartNum
+    }
+  },
+  methods: {
+    handleClickTheme () {
+      this.themeType === 'light'
+        ? (this.themeType = 'dark')
+        : (this.themeType = 'light');
+      window.document.documentElement.setAttribute(
+        'data-theme',
+        this.themeType
+      );
+    },
+    changeCity (city) {
+      this.city = city;
+    },
+    goToPay () {
+      let url = this.$router.resolve({
+        path: '/cart'
+      })
+      window.open(url.href, '_blank')
+    },
+    myInfo () {
+      let url = this.$router.resolve({
+        path: '/home'
+      })
+      window.open(url.href, '_blank')
+    },
+    signOutFun () {
+      storage.removeItem('accessToken');
+      storage.removeItem('refreshToken');
+      storage.removeItem('userInfo');
+      this.$router.push('/login');
+    },
+    goUserCenter (path) { // 跳转我的订单,我的足迹
+      if (this.userInfo.username) {
+        this.$router.push({path: path})
+      } else {
+        this.$Modal.confirm({
+          title: '请登录',
+          content: '<p>请登录后执行此操作</p>',
+          okText: '立即登录',
+          cancelText: '继续浏览',
+          onOk: () => {
+            this.$router.push({
+              path: '/login',
+              query: {
+                rePath: this.$router.history.current.path,
+                query: JSON.stringify(this.$router.history.current.query)
+              }
+            });
+          }
+        });
+      }
+    },
+    shopEntry () { // 店铺入驻
+      if (storage.getItem('accessToken')) {
+        let routeUrl = this.$router.resolve({
+          path: '/shopEntry',
+          query: {id: 1}
+        });
+        window.open(routeUrl.href, '_blank');
+      } else {
+        this.$router.push('login');
+      }
+    },
+    getCartList () { // 获取购物车列表
+      if (this.userInfo.username) {
+        cartCount().then(res => {
+          this.$store.commit('SET_CARTNUM', res.result)
+          this.Cookies.setItem('cartNum', res.result)
+        })
+        cartGoodsAll().then(res => {
+          this.shoppingCart = res.result.skuList
+        })
+      }
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.shopping-cart-detail,
+.shopping-cart-text,
+.shopping-cart-info,
+.nav a,
+.location,
+.first,
+.username,
+.shopping-cart-null span {
+  @include sub_color($light_sub_color);
+}
+.tipsLogin {
+  color: $theme_color;
+}
+
+.box {
+  width: 100%;
+  // height: 35px;
+  @include background_color($light_white_background_color);
+}
+.nav {
+  margin: 0 auto;
+  width: 1200px;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+.nav ul {
+  list-style: none;
+}
+.nav li {
+  float: left;
+  font-size: 14px;
+  line-height: 35px;
+  margin-right: 15px;
+  font-weight: bold;
+}
+.nav a,.nav-item {
+  text-decoration: none;
+  padding-left: 15px;
+  border-left: 1px solid #ccc;
+  color: #999;
+  cursor: pointer;
+}
+.location a {
+  border-left: none;
+}
+.nav a:hover {
+  color: $theme_color;
+}
+
+.icon {
+  color: gray;
+  vertical-align: middle;
+}
+
+.first a:first-child {
+  padding-left: 3px;
+  border-left: none;
+}
+.city {
+  padding: 10px 15px;
+}
+.city-item {
+  font-weight: bold;
+  cursor: pointer;
+  padding: 5px;
+}
+.city-item:hover {
+  color: $theme_color;
+}
+.person-icon {
+  color: $theme_color;
+  background-color: #f0cdb2;
+}
+
+.shopping-cart-list {
+  padding: 10px 15px;
+  box-sizing: border-box;
+  height: 300px;
+  overflow: scroll;
+}
+.shopping-cart-box {
+  margin: 8px 0px;
+  margin-top: 15px;
+  padding-bottom: 15px;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  border-bottom: 1px #ccc dotted;
+}
+.shopping-cart-box:first-child {
+  margin-top: 8px;
+}
+.shopping-cart-img {
+  margin-right: 15px;
+  width: 40px;
+  height: 40px;
+}
+.shopping-cart-img img {
+  width: 100%;
+}
+.shopping-cart-info {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  align-content: space-between;
+  width: 200px;
+  overflow: hidden;
+  font-size: 12px;
+  line-height: 20px;
+}
+
+.go-to-buy {
+  display: flex;
+  justify-content: flex-end;
+}
+.shopping-cart-null {
+  padding: 15px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+.cart-null-icon {
+  font-size: 38px;
+  margin-bottom: 15px;
+}
+.shopping-cart-null span {
+  font-size: 12px;
+  line-height: 16px;
+}
+.username-p {
+  cursor: pointer;
+}
+.my-page {
+  padding: 3px 5px;
+  width: 180px;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.my-page a {
+  margin: 0px;
+  padding: 0px;
+  border: none;
+}
+.my-info {
+  padding: 5px;
+  width: 50%;
+  height: 100%;
+  text-align: center;
+  cursor: pointer;
+}
+.my-info:hover {
+  box-shadow: 0px 0px 5px #ccc;
+}
+.my-info i {
+  font-size: 28px;
+}
+.my-info p {
+  font-size: 12px;
+}
+.sign-out {
+  padding: 5px;
+  width: 50%;
+  height: 100%;
+  text-align: center;
+  cursor: pointer;
+}
+.sign-out:hover {
+  box-shadow: 0px 0px 5px $border_color;
+}
+.sign-out i {
+  font-size: 28px;
+}
+.sign-out p {
+  font-size: 12px;
+}
+.cart-icon{
+  padding: 0 6px;
+}
+.goods-title:hover {
+  color: $theme_color;
+}
+.cart-badge {
+  position: absolute;
+  right: -8px;
+  font-style: normal;
+  background-color: $theme_color;
+  color: #fff;
+  font-size: 12px;
+  width: 17px;
+  height: 17px;
+  border-radius: 10px;
+  line-height: 17px;
+  text-align: center;
+  z-index: 3;
+  top: 3px;
+}
+</style>

+ 102 - 0
buyer/src/components/header/ShopHeader.vue

@@ -0,0 +1,102 @@
+<template>
+  <div class="shop-box">
+    <div class="shop-container">
+      <div class="shop-title">
+        <div class="shop-title-content">
+          <p><router-link :to="`/merchant?id=${skuDetail.storeId}`">{{ skuDetail.storeName }}</router-link></p>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ShopHeader',
+  props: {
+    detail: {
+      type: Object,
+      default: null
+    }
+  },
+  computed: {
+    skuDetail () {
+      return this.detail;
+    }
+  }
+};
+</script>
+
+<style scoped>
+/* 店铺介绍 */
+.shop-box {
+  width: 100%;
+  height: 50px;
+  background-color: #484848;
+}
+
+.shop-container {
+  width: 1200px;
+  height: 100%;
+  margin: 0px auto;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+  color: #fff;
+}
+
+.shop-title {
+  display: flex;
+  flex-direction: row;
+}
+
+.shop-title-icon {
+  font-size: 46px;
+}
+
+.shop-title-content {
+  padding-top: 8px;
+  margin-left: 15px;
+  display: flex;
+}
+
+.shop-title-content p {
+  line-height: 26px;
+  font-size: 20px;
+
+}
+.shop-title-content p:nth-child(2) {
+  font-size: 16px;
+  margin-left: 20px;
+}
+.shop-title-content a {
+  color: #fff;
+}
+
+.shop-another-item {
+  display: flex;
+  flex-direction: row;
+}
+
+.shop-another-item-detail {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  margin-left: 15px;
+}
+
+.shop-another-item-img {
+  height: 80px;
+  border-radius: 40px;
+  overflow: hidden;
+}
+
+.shop-another-item-img img {
+  width: 80px;
+}
+
+.shop-anoter-item-intro {
+  margin-left: 15px;
+}
+</style>

+ 93 - 0
buyer/src/components/header/hoverSearch.vue

@@ -0,0 +1,93 @@
+<template>
+  <div class="scroll-show">
+    <div class="content clearfix">
+      <cateNav class="cate" :showNavBar="false"></cateNav>
+      <Search class="search-con" :showLogo="false" :showTag="false"></Search>
+      <Icon type="ios-cart-outline" @click="goCartList" class="cart-icon"  @mouseenter.native="getCartList" />
+      <i class="cart-badge">{{cartNum < 100 ? cartNum : '99'}}</i>
+    </div>
+  </div>
+</template>
+<script>
+import {cartCount} from '@/api/cart.js'
+import storage from '@/plugins/storage.js';
+export default {
+  data () {
+    return {
+      userInfo: {} // 用户信息
+    }
+  },
+  computed: {
+    cartNum () { // 购物车数量
+      return this.$store.state.cartNum
+    }
+  },
+  methods: {
+    goCartList () {
+      let routerUrl = this.$router.resolve({
+        path: '/cart'
+      })
+      window.open(routerUrl.href, '_blank')
+    },
+    getCartList () { // 获取购物车列表
+      if (storage.getItem('userInfo')) {
+        cartCount().then(res => {
+          this.$store.commit('SET_CARTNUM', res.result)
+          this.Cookies.setItem('cartNum', res.result)
+        })
+      }
+    }
+  },
+  mounted () {
+    if (storage.getItem('userInfo')) {
+      this.userInfo = JSON.parse(storage.getItem('userInfo'));
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+  .content{
+    width: 1200px;
+    height: 40px;
+    margin: 10px auto;
+    position: relative;
+  }
+  .cate {
+    float: left;
+    width: 200px!important;
+  }
+  .search-con{
+    float: left;
+    width: 800px;
+    overflow: hidden;
+    margin-top: -27px;
+  }
+  .cart-icon {
+    width: 30px;
+    float: left;
+    font-size: 25px;
+    margin-top: 8px;
+    color: $theme_color;
+    z-index: 1;
+    position: relative;
+    &:hover{
+      cursor: pointer;
+    }
+  }
+  .cart-badge {
+    position: absolute;
+    font-style: normal;
+    right: 165px;
+    display: block;
+    background-color: $theme_color;
+    color: #fff;
+    font-size: 12px;
+    width: 17px;
+    height: 17px;
+    border-radius: 10px;
+    line-height: 17px;
+    text-align: center;
+    z-index: 5;
+    top: 3px;
+  }
+</style>

+ 33 - 0
buyer/src/components/indexDecorate/modelForm.vue

@@ -0,0 +1,33 @@
+<template>
+  <div class="model-form">
+    <div class="model-content">
+      <template v-for="(element, index) in data.list">
+        <model-form-item
+          v-if="element && element.key"
+          :key="element.key"
+          :element="element"
+          :index="index"
+          :data="data"
+        ></model-form-item>
+      </template>
+    </div>
+  </div>
+</template>
+<script>
+import ModelFormItem from './modelFormItem.vue';
+export default {
+  name: 'modelForm',
+  components: {
+    ModelFormItem
+  },
+  props: ['data']
+};
+</script>
+<style lang="scss" scoped>
+.model-content {
+  width: 1200px;
+  margin: 0 auto;
+  background: #fff;
+  min-height: 1200px;
+}
+</style>

Деякі файли не було показано, через те що забагато файлів було змінено