lincl преди 2 години
родител
ревизия
7cb0287158
променени са 100 файла, в които са добавени 10895 реда и са изтрити 0 реда
  1. 5 0
      litemall-vue/.env.deployment
  2. 14 0
      litemall-vue/.env.development
  3. 5 0
      litemall-vue/.env.production
  4. 17 0
      litemall-vue/.eslintrc.js
  5. 22 0
      litemall-vue/.gitignore
  6. 5 0
      litemall-vue/.postcssrc.js
  7. 4 0
      litemall-vue/.prettierrc.js
  8. 15 0
      litemall-vue/babel.config.js
  9. 44 0
      litemall-vue/package.json
  10. BIN
      litemall-vue/public/favicon.ico
  11. 25 0
      litemall-vue/public/index.html
  12. 20 0
      litemall-vue/src/App.vue
  13. 453 0
      litemall-vue/src/api/api.js
  14. BIN
      litemall-vue/src/assets/images/ali_pay.png
  15. BIN
      litemall-vue/src/assets/images/avatar_default.png
  16. BIN
      litemall-vue/src/assets/images/goods_default.png
  17. BIN
      litemall-vue/src/assets/images/is_empty.png
  18. BIN
      litemall-vue/src/assets/images/mx_be_to.png
  19. BIN
      litemall-vue/src/assets/images/mx_start.png
  20. BIN
      litemall-vue/src/assets/images/not_enough.png
  21. BIN
      litemall-vue/src/assets/images/store_default.png
  22. BIN
      litemall-vue/src/assets/images/trumpet.png
  23. BIN
      litemall-vue/src/assets/images/user_head_bg.png
  24. BIN
      litemall-vue/src/assets/images/wx_pay.png
  25. 1 0
      litemall-vue/src/assets/scss/_mixin.scss
  26. 127 0
      litemall-vue/src/assets/scss/_vant-theme.scss
  27. 16 0
      litemall-vue/src/assets/scss/_var.scss
  28. 59 0
      litemall-vue/src/assets/scss/common.scss
  29. 44 0
      litemall-vue/src/assets/scss/global.scss
  30. 318 0
      litemall-vue/src/assets/scss/iconfont/iconfont.css
  31. BIN
      litemall-vue/src/assets/scss/iconfont/iconfont.ttf
  32. 17 0
      litemall-vue/src/assets/scss/mixin/_one-border.scss
  33. 34 0
      litemall-vue/src/components/Header/index.vue
  34. 88 0
      litemall-vue/src/components/Tabbar/index.vue
  35. 26 0
      litemall-vue/src/components/field-group/index.vue
  36. 104 0
      litemall-vue/src/components/field/index.vue
  37. 28 0
      litemall-vue/src/components/is-empty/index.vue
  38. 14 0
      litemall-vue/src/filter/index.js
  39. 41 0
      litemall-vue/src/main.js
  40. 24 0
      litemall-vue/src/mixin/scroll-fixed.js
  41. 22 0
      litemall-vue/src/router/home.js
  42. 41 0
      litemall-vue/src/router/index.js
  43. 80 0
      litemall-vue/src/router/items.js
  44. 44 0
      litemall-vue/src/router/login.js
  45. 38 0
      litemall-vue/src/router/order.js
  46. 112 0
      litemall-vue/src/router/user.js
  47. 3 0
      litemall-vue/src/store/getters.js
  48. 18 0
      litemall-vue/src/store/index.js
  49. 1 0
      litemall-vue/src/store/mutation-types.js
  50. 8 0
      litemall-vue/src/store/mutations.js
  51. 15 0
      litemall-vue/src/utils/auth.js
  52. 20 0
      litemall-vue/src/utils/local-storage.js
  53. 6 0
      litemall-vue/src/utils/location-param.js
  54. 58 0
      litemall-vue/src/utils/request.js
  55. 31 0
      litemall-vue/src/utils/scroll.js
  56. 12 0
      litemall-vue/src/utils/validate.js
  57. 434 0
      litemall-vue/src/views/home/tabbar-home.vue
  58. 141 0
      litemall-vue/src/views/items/brand-list/index.vue
  59. 139 0
      litemall-vue/src/views/items/brand/index.vue
  60. 147 0
      litemall-vue/src/views/items/category/index.vue
  61. 410 0
      litemall-vue/src/views/items/detail/index.vue
  62. 79 0
      litemall-vue/src/views/items/detail/popup-props.vue
  63. 105 0
      litemall-vue/src/views/items/groupon/index.vue
  64. 94 0
      litemall-vue/src/views/items/hot/index.vue
  65. 95 0
      litemall-vue/src/views/items/new/index.vue
  66. 168 0
      litemall-vue/src/views/items/search/index.vue
  67. 198 0
      litemall-vue/src/views/items/tabbar-catalog.vue
  68. 141 0
      litemall-vue/src/views/items/topic-list/index.vue
  69. 87 0
      litemall-vue/src/views/items/topic/index.vue
  70. 57 0
      litemall-vue/src/views/login/forget-reset/index.vue
  71. 81 0
      litemall-vue/src/views/login/forget-status/index.vue
  72. 80 0
      litemall-vue/src/views/login/forget/index.vue
  73. 184 0
      litemall-vue/src/views/login/login.vue
  74. 74 0
      litemall-vue/src/views/login/register-getCode/index.vue
  75. 105 0
      litemall-vue/src/views/login/register-status/index.vue
  76. 133 0
      litemall-vue/src/views/login/register-submit/index.vue
  77. 245 0
      litemall-vue/src/views/order/checkout.vue
  78. 196 0
      litemall-vue/src/views/order/order-detail/index.vue
  79. 94 0
      litemall-vue/src/views/order/payment-status/index.vue
  80. 214 0
      litemall-vue/src/views/order/payment/index.vue
  81. 263 0
      litemall-vue/src/views/order/tabbar-cart.vue
  82. 196 0
      litemall-vue/src/views/user/coupon-list/index.vue
  83. 3239 0
      litemall-vue/src/views/user/module-address-edit/area.json
  84. 66 0
      litemall-vue/src/views/user/module-address-edit/index.vue
  85. 73 0
      litemall-vue/src/views/user/module-address/index.vue
  86. 98 0
      litemall-vue/src/views/user/module-collect/index.vue
  87. 94 0
      litemall-vue/src/views/user/module-feedback/index.vue
  88. 38 0
      litemall-vue/src/views/user/module-help/index.vue
  89. 46 0
      litemall-vue/src/views/user/module-server/index.vue
  90. 214 0
      litemall-vue/src/views/user/order-list/index.vue
  91. 148 0
      litemall-vue/src/views/user/refund-list/index.vue
  92. 86 0
      litemall-vue/src/views/user/tabbar-user-coupon.vue
  93. 77 0
      litemall-vue/src/views/user/tabbar-user-header.vue
  94. 22 0
      litemall-vue/src/views/user/tabbar-user-module.vue
  95. 107 0
      litemall-vue/src/views/user/tabbar-user-order.vue
  96. 54 0
      litemall-vue/src/views/user/tabbar-user.vue
  97. 131 0
      litemall-vue/src/views/user/user-information-set/index.vue
  98. 111 0
      litemall-vue/src/views/user/user-information-set/set-mobile/index.vue
  99. 52 0
      litemall-vue/src/views/user/user-information-set/set-nickname/index.vue
  100. 0 0
      litemall-vue/src/views/user/user-information-set/set-password/index.vue

+ 5 - 0
litemall-vue/.env.deployment

@ -0,0 +1,5 @@
# just a flag
ENV = 'production'
# base api
VUE_APP_BASE_API = 'http://122.51.199.160:8080/'

+ 14 - 0
litemall-vue/.env.development

@ -0,0 +1,14 @@
# just a flag
ENV = 'development'
# base api
VUE_APP_BASE_API = '/wx'
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
# It only does one thing by converting all import() to require().
# This configuration can significantly increase the speed of hot updates,
# when you have a large number of pages.
# Detail:  https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 5 - 0
litemall-vue/.env.production

@ -0,0 +1,5 @@
# just a flag
ENV = 'production'
# base api
VUE_APP_BASE_API = 'https://www.cityihealth.com/litemall/wx'

+ 17 - 0
litemall-vue/.eslintrc.js

@ -0,0 +1,17 @@
module.exports = {
  root: true,
  env: {
    node: true
  },
  // extends: ['plugin:vue/essential', '@vue/prettier'],
  // rules: {
  //   camelcase: 'off',
  //   quotes: ['error', 'single'],
  //   indent: ['error', 2, { SwitchCase: 1 }],
  //   'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
  //   'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  // },
  parserOptions: {
    parser: 'babel-eslint'
  }
};

+ 22 - 0
litemall-vue/.gitignore

@ -0,0 +1,22 @@
.DS_Store
node_modules
/dist
/package-lock.json
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

+ 5 - 0
litemall-vue/.postcssrc.js

@ -0,0 +1,5 @@
module.exports = {
  plugins: {
    autoprefixer: {}
  }
};

+ 4 - 0
litemall-vue/.prettierrc.js

@ -0,0 +1,4 @@
module.exports = {
  tabWidth: 2,
  singleQuote: true
};

+ 15 - 0
litemall-vue/babel.config.js

@ -0,0 +1,15 @@
module.exports = {
  presets: ['@vue/app'],
  plugins: [
    'lodash',
    [
      'import',
      {
        libraryName: 'vant',
        libraryDirectory: 'es',
        style: true
      },
      'vant'
    ]
  ]
};

+ 44 - 0
litemall-vue/package.json

@ -0,0 +1,44 @@
{
  "name": "litemall-vue",
  "version": "0.1.0",
  "description": "litemall-vue basing on vant--mobile-mall 0.1.0",
  "author": "litemall <linlinjava@163.com>",
  "license": "MIT",
  "private": true,
  "scripts": {
    "dev": "vue-cli-service serve",
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build  --mode production",
    "build:dep": "vue-cli-service build --mode deployment",
    "build:prod": "vue-cli-service build --mode production",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "@chenfengyuan/vue-countdown": "^1.1.2",
    "axios": ">=0.21.1",
    "dayjs": "^1.7.7",
    "js-cookie": "2.2.0",
    "lodash": "^4.17.21",
    "vant": "^2.0.6",
    "vue": "^2.5.17",
    "vue-router": "^3.0.1",
    "vuelidate": "^0.7.4",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.0.5",
    "@vue/cli-plugin-eslint": "^3.0.5",
    "@vue/cli-service": "^3.0.5",
    "@vue/eslint-config-prettier": "^3.0.5",
    "babel-plugin-import": "^1.9.1",
    "babel-plugin-lodash": "^3.3.4",
    "sass": "^1.26.2",
    "sass-loader": "^7.1.0",
    "vue-template-compiler": "^2.5.17"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

BIN
litemall-vue/public/favicon.ico


+ 25 - 0
litemall-vue/public/index.html

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta content="black" name="apple-mobile-web-app-status-bar-style">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <link rel="icon" href="./favicon.ico" type="image/x-icon" />
  <title>litemall-vue</title>
</head>
<body>
  <noscript>
    <strong>Please enable it to continue.</strong>
  </noscript>
  <div id="app">
    <div class="lds-ball">
      <div></div>
    </div>
  </div>
  <!-- built files will be auto injected -->
</body>
</html>

+ 20 - 0
litemall-vue/src/App.vue

@ -0,0 +1,20 @@
<template>
<div id="app">
  <v-header></v-header>
  <keep-alive>
    <router-view class="view-router"  v-if="$route.meta.keepAlive"></router-view>
  </keep-alive>
  <router-view class="view-router" v-if="!$route.meta.keepAlive"></router-view>
  <router-view name="tabbar"></router-view>
</div>
</template>
<script>
import header from "@/components/Header";
export default {
  components:{
    'v-header': header
  }
}
</script>
<style lang="scss" src="./assets/scss/global.scss" />

+ 453 - 0
litemall-vue/src/api/api.js

@ -0,0 +1,453 @@
import request from '@/utils/request'
const IndexUrl= '/home/index'; //首页数据接口
export function getHome() {
  return request({
    url: IndexUrl,
    method: 'get'
  })
}
const CatalogList='/catalog/index'; //分类目录全部分类数据接口
export function catalogList() {
  return request({
    url: CatalogList,
    method: 'get'
  })
}
const CatalogCurrent='/catalog/current'; //分类目录当前分类数据接口
export function catalogCurrent(query) {
  return request({
    url: CatalogCurrent,
    method: 'get',
    params: query
  })
}
const AuthLoginByWeixin='/auth/login_by_weixin'; //微信登录
const AuthLoginByAccount='/auth/login'; //账号登录
export function authLoginByAccount(data) {
  return request({
    url: AuthLoginByAccount,
    method: 'post',
    data
  })
}
const AuthLogout='/auth/logout'; //账号登出
export function authLogout() {
  return request({
    url: AuthLogout,
    method: 'post'
  })
}
const AuthInfo='/auth/info'; //用户信息
export function authInfo() {
  return request({
    url: AuthInfo,
    method: 'get'
  })
}
const AuthProfile='/auth/profile'; //账号修改
export function authProfile(data) {
  return request({
    url: AuthProfile,
    method: 'post',
    data
  })
}
const AuthRegister='/auth/register'; //账号注册
export function authRegister(data) {
  return request({
    url: AuthRegister,
    method: 'post',
    data
  });
}
const AuthReset='/auth/reset'; //账号密码重置
export function authReset(data) {
  return request({
    url: AuthReset,
    method: 'post',
    data
  })
}
const AuthRegisterCaptcha='/auth/regCaptcha'; //注册验证码
export function authRegisterCaptcha(data) {
  return request({
    url: AuthRegisterCaptcha,
    method: 'post',
    data
  })
}
const AuthCaptcha='/auth/captcha'; //验证码
export function authCaptcha(data) {
  return request({
    url: AuthCaptcha,
    method: 'post',
    data
  })
}
const GoodsCount='/goods/count'; //统计商品总数
export function goodsCount() {
  return request({
    url: GoodsCount,
    method: 'get'
  })
}
export const GoodsList='/goods/list'; //获得商品列表
export function goodsList(query) {
  return request({
    url: GoodsList,
    method: 'get',
    params: query
  })
}
const GoodsCategory='/goods/category'; //获得分类数据
export function goodsCategory(query) {
  return request({
    url: GoodsCategory,
    method: 'get',
    params: query
  })
}
const GoodsDetail='/goods/detail'; //获得商品的详情
export function goodsDetail(query) {
  return request({
    url: GoodsDetail,
    method: 'get',
    params: query
  })
}
const GoodsRelated='/goods/related'; //商品详情页的关联商品(大家都在看)
const BrandList='/brand/list'; //品牌列表
export function brandList(query) {
  return request({
    url: BrandList,
    method: 'get',
    params: query
  })
}
const BrandDetail='/brand/detail'; //品牌详情
export function brandDetail(query) {
  return request({
    url: BrandDetail,
    method: 'get',
    params: query
  })
}
const CartList='/cart/index'; //获取购物车的数据
export function cartList(query) {
  return request({
    url: CartList,
    method: 'get',
    params: query
  })
}
const CartAdd='/cart/add'; // 添加商品到购物车
export function cartAdd(data) {
  return request({
    url: CartAdd,
    method: 'post',
    data
  })
}
const CartFastAdd='/cart/fastadd'; // 立即购买商品
export function cartFastAdd(data) {
  return request({
    url: CartFastAdd,
    method: 'post',
    data
  })
}
const CartUpdate='/cart/update'; // 更新购物车的商品
export function cartUpdate(data) {
  return request({
    url: CartUpdate,
    method: 'post',
    data
  })
}
const CartDelete='/cart/delete'; // 删除购物车的商品
export function cartDelete(data) {
  return request({
    url: CartDelete,
    method: 'post',
    data
  })
}
const CartChecked='/cart/checked'; // 选择或取消选择商品
export function cartChecked(data) {
  return request({
    url: CartChecked,
    method: 'post',
    data
  })
}
const CartGoodsCount='/cart/goodscount'; // 获取购物车商品件数
export function cartGoodsCount() {
  return request({
    url: CartGoodsCount,
    method: 'get'
  })
}
const CartCheckout='/cart/checkout'; // 下单前信息确认
export function cartCheckout(query) {
  return request({
    url: CartCheckout,
    method: 'get',
    params: query
  })
}
const CollectList='/collect/list'; //收藏列表
export function collectList(query) {
  return request({
    url: CollectList,
    method: 'get',
    params: query
  })
}
const CollectAddOrDelete='/collect/addordelete'; //添加或取消收藏
export function collectAddOrDelete(data) {
  return request({
    url: CollectAddOrDelete,
    method: 'post',
    data
  })
}
const CommentList='/comment/list'; //评论列表
const CommentCount='/comment/count'; //评论总数
const CommentPost='/comment/post'; //发表评论
const TopicList='/topic/list'; //专题列表
export function topicList(query) {
  return request({
    url: TopicList,
    method: 'get',
    params: query
  })
}
const TopicDetail='/topic/detail'; //专题详情
export function topicDetail(query) {
  return request({
    url: TopicDetail,
    method: 'get',
    params: query
  })
}
const TopicRelated='/topic/related'; //相关专题
export function topicRelated(query) {
  return request({
    url: TopicRelated,
    method: 'get',
    params: query
  })
}
const SearchIndex='/search/index'; //搜索关键字
const SearchResult='/search/result'; //搜索结果
const SearchHelper='/search/helper'; //搜索帮助
const SearchClearHistory='/search/clearhistory'; //搜索历史清楚
const AddressList='/address/list'; //收货地址列表
export function addressList(query) {
  return request({
    url: AddressList,
    method: 'get',
    params: query
  })
}
const AddressDetail='/address/detail'; //收货地址详情
export function addressDetail(query) {
  return request({
    url: AddressDetail,
    method: 'get',
    params: query
  })
}
const AddressSave='/address/save'; //保存收货地址
export function addressSave(data) {
  return request({
    url: AddressSave,
    method: 'post',
    data
  })
}
const AddressDelete='/address/delete'; //保存收货地址
export function addressDelete(data) {
  return request({
    url: AddressDelete,
    method: 'post',
    data
  })
}
const ExpressQuery='/express/query'; //物流查询
const OrderSubmit='/order/submit'; // 提交订单
export function orderSubmit(data) {
  return request({
    url: OrderSubmit,
    method: 'post',
    data
  })
}
const OrderPrepay='/order/prepay'; // 订单的预支付会话
export function orderPrepay(data) {
  return request({
    url: OrderPrepay,
    method: 'post',
    data
  })
}
const OrderH5pay = '/order/h5pay'; // h5支付
export function orderH5pay(data) {
  return request({
    url: OrderH5pay,
    method: 'post',
    data
  });
}
export const OrderList='/order/list'; //订单列表
export function orderList(query) {
  return request({
    url: OrderList,
    method: 'get',
    params: query
  })
}
const OrderDetail='/order/detail'; //订单详情
export function orderDetail(query) {
  return request({
    url: OrderDetail,
    method: 'get',
    params: query
  })
}
const OrderCancel='/order/cancel'; //取消订单
export function orderCancel(data) {
  return request({
    url: OrderCancel,
    method: 'post',
    data
  })
}
const OrderRefund='/order/refund'; //退款取消订单
export function orderRefund(data) {
  return request({
    url: OrderRefund,
    method: 'post',
    data
  })
}
const OrderDelete='/order/delete'; //删除订单
export function orderDelete(data) {
  return request({
    url: OrderDelete,
    method: 'post',
    data
  })
}
const OrderConfirm='/order/confirm'; //确认收货
export function orderConfirm(data) {
  return request({
    url: OrderConfirm,
    method: 'post',
    data
  })
}
const OrderGoods='/order/goods'; // 代评价商品信息
const OrderComment='/order/comment'; // 评价订单商品信息
const FeedbackAdd='/feedback/submit'; //添加反馈
export function feedbackAdd(data) {
  return request({
    url: FeedbackAdd,
    method: 'post',
    data
  })
}
const FootprintList='/footprint/list'; //足迹列表
const FootprintDelete='/footprint/delete'; //删除足迹
const GrouponList='/groupon/list'; //团购列表
export function grouponList(query) {
  return request({
    url: GrouponList,
    method: 'get',
    params: query
  })
}
const GroupOn='/groupon/query'; //团购API-查询
const GroupOnMy='/groupon/my'; //团购API-我的团购
const GroupOnDetail='/groupon/detail'; //团购API-详情
const GroupOnJoin='/groupon/join'; //团购API-详情
const CouponList='/coupon/list'; //优惠券列表
export function couponList(query) {
  return request({
    url: CouponList,
    method: 'get',
    params: query
  })
}
export const CouponMyList='/coupon/mylist'; //我的优惠券列表
export function couponMyList(query) {
  return request({
    url: CouponMyList,
    method: 'get',
    params: query
  })
}
const CouponSelectList='/coupon/selectlist'; //当前订单可用优惠券列表
export function couponSelectList(query) {
  return request({
    url: CouponSelectList,
    method: 'get',
    params: query
  })
}
const CouponReceive='/coupon/receive'; //优惠券领取
export function couponReceive(data) {
  return request({
    url: CouponReceive,
    method: 'post',
    data
  })
}
const CouponExchange='/coupon/exchange'; //优惠券兑换
const StorageUpload='/storage/upload'; //图片上传,
const UserIndex='/user/index'; //个人页面用户相关信息
export function userIndex() {
  return request({
    url: UserIndex,
    method: 'get'
  })
}
const IssueList='/issue/list'; //帮助信息
export function issueList() {
  return request({
    url: IssueList,
    method: 'get'
  })
}
export function getList(api, query) {
  return request({
    url: api,
    method: 'get',
    params: query
  })
}
export const REFUND_LIST = '';

BIN
litemall-vue/src/assets/images/ali_pay.png


BIN
litemall-vue/src/assets/images/avatar_default.png


BIN
litemall-vue/src/assets/images/goods_default.png


BIN
litemall-vue/src/assets/images/is_empty.png


BIN
litemall-vue/src/assets/images/mx_be_to.png


BIN
litemall-vue/src/assets/images/mx_start.png


BIN
litemall-vue/src/assets/images/not_enough.png


BIN
litemall-vue/src/assets/images/store_default.png


BIN
litemall-vue/src/assets/images/trumpet.png


BIN
litemall-vue/src/assets/images/user_head_bg.png


BIN
litemall-vue/src/assets/images/wx_pay.png


+ 1 - 0
litemall-vue/src/assets/scss/_mixin.scss

@ -0,0 +1 @@
@import "./mixin/one-border";

+ 127 - 0
litemall-vue/src/assets/scss/_vant-theme.scss

@ -0,0 +1,127 @@
//loading
.van-loading--gradient-circle, .van-loading--spinner{
	margin: 0 auto;
}
//tabber
div.van-tabbar-item--active {
	color: $red;
}
.van-tabbar-item__icon .van-icon{
	font-size: 18px;
}
//按钮组
a.van-goods-action__mini-btn i {
	font-size: 22px;
}
.van-goods-action .van-button--bottom-action {
	font-size: $font-size-normal;
}
.van-button--bottom-action.van-button--primary {
	background-color: $red;
}
.van-button--danger {
	background-color: $red;
}
.van-address-edit__buttons .van-button--primary{
	background-color: $red;
	border-color: $red;
}
// 多选
.van-checkbox__control:checked + i.van-icon-success {
	background-color: $red;
	border-color: $red;
}
//单选
.van-radio i.van-icon-checked {
	color: $red;
}
.payment .van-cell-group .van-radio__input{
	position: absolute;
	right: 10px;
	top: 50%;
	transform: translate3d(0, -50%, 0);
}
.van-radio .van-radio__input{
	height: 16px;
}
.van-radio i.van-icon{
	font-size: 16px;
}
span.van-radio__input, span.van-radio__label{
	vertical-align: unset;
	line-height: 16px;
}
.van-cell-group .van-radio__label{
	margin-left: 0;
}
//弹窗
.van-dialog__confirm,
.van-dialog__confirm:active {
	color: $red;
}
//商品卡片
.van-card__footer {
	width: 100%;
	padding-left: 130px;
	box-sizing: border-box;
}
div.van-card {
	font-size: $font-size-normal;
	background-color: #fff;
}
div.van-card__footer {
	font-size: 12px;	
	color: $font-color-gray;
}
//cell
.van-cell__title{
	vertical-align: middle;
}
//商品详情
.item_detail .van-picker__cancel {
	color: #000;
}
.item_detail .van-picker__confirm {
	color: $red;
}
//购物车
.tab-cart > .card-goods .van-checkbox {
	padding-left: 10px;
	box-sizing: border-box;
}
.tab-cart > .card-goods .van-card {
	flex: 1;
}
.tab-cart .van-card:not(:first-child){
	margin-top: 0;
	padding-top: 10px;
	padding-bottom: 10px;
}
//设置昵称
.set_nickname .van-field__control{
	text-align: right
}
//商品列表
.item_list .van-tab--disabled{
	color: #000;
}

+ 16 - 0
litemall-vue/src/assets/scss/_var.scss

@ -0,0 +1,16 @@
$red: #db3d3c;
$gray-deep: #999;
$gray: #bfbfbf;
$gray-shallow: #e5e5e5;
$gray-shallow-more: #f2f2f2;
$icon-bg: #f9f3e8;
//字体变量
$font-color-gray: $gray-deep;
$font-size-small: 12px;
$font-size-normal: 14px;
$font-size-big: 16px;
$border-color: $gray-shallow;
$bg-color: $gray-shallow-more;

+ 59 - 0
litemall-vue/src/assets/scss/common.scss

@ -0,0 +1,59 @@
.red{
	color: $red;
}
.text-desc{
	font-size:$font-size-small;
	color: $font-color-gray;
}
.float-r {
	float: right;
}
.float-l {
	float: left;
}
.clearfix {
  &:before,
  &:after {
    content: " "; // 1
    display: table; // 2
  }
  &:after {
    clear: both;
  }
}
.one_border {
	@include one-border;
}
.one_border:last-child::after {
	border-bottom-width: 0px;
}
.van-hairline--top-bottom::after {
  border-width: 1px 0;
  border-top-width: 0px;
  border-right-width: 0px;
  border-bottom-width: 0px;
  border-left-width: 0px;
}
.text-center{
	text-align: center;
}
.over-hide {
  overflow: hidden !important;
}
.no-pad-bottom {
  padding-bottom: 0 !important;
}
.height-fix42 {
  padding-bottom: 42px;
}

+ 44 - 0
litemall-vue/src/assets/scss/global.scss

@ -0,0 +1,44 @@
@import "./var";
@import "mixin";
@import "./common";
@import "./vant-theme";
* {
	box-sizing: border-box;
}
a {
	color: #000
}
input:-webkit-autofill {
	-webkit-box-shadow: 0 0 0 1000px white inset;
}
html {
	-webkit-overflow-scrolling: touch;
}
body {
	overflow-x: hidden;
	max-width: 700px;
	margin: 0 auto;
	font-size: 14px;
}
.view-router {
	background-color: $bg-color;
	height: 100vh;
	overflow-y: auto;
	padding-bottom: 50px;
	box-sizing: border-box;
}
.full-page {
	height: 100vh;
}
.scroll-wrap {
	overflow-y: scroll;
}

+ 318 - 0
litemall-vue/src/assets/scss/iconfont/iconfont.css

@ -0,0 +1,318 @@
@font-face {
	font-family: "iconfont";
	src: url('./iconfont.ttf') format('truetype');
}
@font-face {
	font-family: "vanIcon";
	src: url('https://img.yzcdn.cn/vant/vant-icon-66a14a.ttf') format('truetype');
}
.van-icon {
	position: relative;
	font-family: "iconfont", "vanIcon" !important;
	font-size: 14px;
	font-style: normal;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}
i.van-icon-huida:before {
	content: "\e678";
}
i.van-icon-wen:before {
	content: "\e715";
}
i.van-icon-wuliu:before {
	content: "\e640";
}
i.van-icon-hint:before {
	content: "\e62a";
}
i.van-icon-add:before {
	content: "\e64f";
}
i.van-icon-miaosha:before {
	content: "\e68f";
}
i.van-icon-lock:before {
	content: "\e60c";
}
i.van-icon-wode:before {
	content: "\e604";
}
i.van-icon-checked:before {
	content: "\e607";
}
i.van-icon-check:before {
	content: "\e628";
}
i.van-icon-leimu:before {
	content: "\e703";
}
i.van-icon-camera_full:before {
	content: "\e618";
}
i.van-icon-cart-full:before {
	content: "\e73c";
}
i.van-icon-cart:before {
	content: "\e73d";
}
i.van-icon-miaosha-copy:before {
	content: "\e601";
}
i.van-icon-id-card:before {
	content: "\e61e";
}
i.van-icon-compass-full:before {
	content: "\e7ac";
}
i.van-icon-fail:before {
	content: "\e609";
}
i.van-icon-success:before {
	content: "\e626";
}
i.van-icon-wangwang-full:before {
	content: "\e605";
}
i.van-icon-daifahuo:before {
	content: "\e642";
}
i.van-icon-arrowupcircle:before {
	content: "\e6cf";
}
i.van-icon-class-full:before {
	content: "\e7f8";
}
i.van-icon-fenxiang:before {
	content: "\e610";
}
i.van-icon-gold-bean:before {
	content: "\e629";
}
i.van-icon-coupon-due:before {
	content: "\e6dd";
}
i.van-icon-coupon-used:before {
	content: "\e6df";
}
i.van-icon-team:before {
	content: "\e65d";
}
i.van-icon-dingwei:before {
	content: "\e622";
}
i.van-icon-editor:before {
	content: "\e685";
}
i.van-icon-coupon:before {
	content: "\e60e";
}
i.van-icon-arrow-down:before {
	content: "\e61b";
}
i.van-icon-clear:before {
	content: "\e61f";
}
i.van-icon-laba:before {
	content: "\e73b";
}
i.van-icon-kefu:before {
	content: "\e616";
}
i.van-icon-tubiao215:before {
	content: "\e619";
}
i.van-icon-jijiangkaishi:before {
	content: "\e681";
}
i.van-icon-arrow:before {
	content: "\e66e";
}
i.van-icon-mobile:before {
	content: "\e72c";
}
i.van-icon-username:before {
	content: "\e84d";
}
i.van-icon-icon104:before {
	content: "\e665";
}
i.van-icon-list:before {
	content: "\e68e";
}
i.van-icon-set:before {
	content: "\e690";
}
i.van-icon-good:before {
	content: "\e699";
}
i.van-icon-search:before {
	content: "\e6a4";
}
i.van-icon-compass:before {
	content: "\e6a6";
}
i.van-icon-success-radius:before {
	content: "\e6b4";
}
i.van-icon-browse:before {
	content: "\e6c0";
}
i.van-icon-phone:before {
	content: "\e6c2";
}
i.van-icon-naozhong:before {
	content: "\e661";
}
i.van-icon-shiliangzhinengduixiang42:before {
	content: "\e675";
}
i.van-icon-dengdai:before {
	content: "\e60b";
}
i.van-icon-cancel:before {
	content: "\e61c";
}
i.van-icon-shouhouguanli:before {
	content: "\e608";
}
i.van-icon-invitation:before {
	content: "\e932";
}
i.van-icon-arrow-left:before {
	content: "\e625";
}
i.van-icon-clear-full:before {
	content: "\e658";
}
i.van-icon-wode1:before {
	content: "\e602";
}
i.van-icon-baoguo-shixin:before {
	content: "\e8fb";
}
i.van-icon-lajitong:before {
	content: "\e62f";
}
i.van-icon-daifukuan:before {
	content: "\e60a";
}
i.van-icon-eye-close:before {
	content: "\e60d";
}
i.van-icon-eye-open:before {
	content: "\e623";
}
i.van-icon-baoguo-kongxin:before {
	content: "\e61a";
}
i.van-icon-dingwei1:before {
	content: "\e7db";
}
i.van-icon-n4:before {
	content: "\e79a";
}
i.van-icon-jiaoyiwancheng:before {
	content: "\e686";
}
i.van-icon-jiaoyiguanbi:before {
	content: "\e687";
}
i.van-icon-qianshoutixing:before {
	content: "\e679";
}
i.van-icon-chulizhong:before {
	content: "\e68b";
}
i.van-icon-tuandui1:before {
	content: "\e63d";
}
i.van-icon-icon:before {
	content: "\e603";
}
i.van-icon-wangwang:before {
	content: "\e933";
}
i.van-icon-shoucang:before {
	content: "\e620";
}
i.van-icon-shoucang-full:before {
	content: "\e61d";
}

BIN
litemall-vue/src/assets/scss/iconfont/iconfont.ttf


+ 17 - 0
litemall-vue/src/assets/scss/mixin/_one-border.scss

@ -0,0 +1,17 @@
@mixin one-border($direction: bottom){
	position: relative;
	&::after {
		content: "";
		position: absolute;
		top: 0;
		left: 0;
		width: 200%;
		height: 200%;
		transform: scale(.5);
		transform-origin: 0 0;
		pointer-events: none;
		box-sizing: border-box;
		border-#{$direction}: 1px solid $border-color;
	}
}

+ 34 - 0
litemall-vue/src/components/Header/index.vue

@ -0,0 +1,34 @@
<template>
<div>
    <van-nav-bar :title="title" left-text="返回" left-arrow @click-left="goBack" v-show="showHeader"/>   
</div>
     
</template>
<script>
import { NavBar } from 'vant';
import { mapState } from 'vuex';
export default {
    name:"v-header",
    data(){
        return {
            title:"",
        };
    },
    computed: {
        showHeader:function(){
            let header=this.$store.getters.showHeader;
            this.title=this.$store.getters.titleHeader;
            return header
        }
    },
    methods: {
        goBack() {
            this.$router.back(-1);
        }
   },
   components: {
    [NavBar.name]:NavBar,
  }
}
</script>

+ 88 - 0
litemall-vue/src/components/Tabbar/index.vue

@ -0,0 +1,88 @@
<template>
	<van-tabbar v-model="active" style="z-index: 1999">
		<van-tabbar-item
			v-for="(tab, index) in tabbar"
			:icon="tab.icon"
			:to="tab.path"
			:dot="tab.dot"
			:info="tab.info"
			:key="index">
			{{tab.name}}
		</van-tabbar-item>
	</van-tabbar>
</template>
<script>
import { Tabbar, TabbarItem } from 'vant';
export default {
  data() {
    return {
      active: 0,
      tabbar: [
        {
          name: '精选',
          path: '/',
          pathName: 'home',
          icon: 'compass-full',
          dot: false,
          info: ''
        },
        {
          name: '分类',
          path: '/items',
          pathName: 'class',
          icon: 'class-full',
          dot: false,
          info: ''
        },
        {
          name: '购物车',
          path: '/order',
          pathName: 'cart',
          icon: 'cart-full',
          dot: false,
          info: ''
        },
        {
          name: '我的',
          path: '/user',
          pathName: 'user',
          icon: 'wode',
          dot: false,
          info: ''
        }
      ]
    };
  },
  watch: {
    $route: 'changeActive'
  },
  created() {
    const toName = this.$route.name;
    this.setActive(toName);
  },
  methods: {
    changeActive({ name }) {
      this.setActive(name);
    },
    setActive(name) {
      this.tabbar.forEach((tab, i) => {
        if (tab.pathName == name) {
          this.active = i;
          return false;
        }
      });
    }
  },
  components: {
    [Tabbar.name]: Tabbar,
    [TabbarItem.name]: TabbarItem
  }
};
</script>

+ 26 - 0
litemall-vue/src/components/field-group/index.vue

@ -0,0 +1,26 @@
<template>
	<div class="field_group">
		<slot></slot>
	</div>
</template>
<script>
export default {
  name: 'md-field-group'
};
</script>
<style lang="scss" scoped>
.field_group {
  padding-left: 15px;
  padding-right: 15px;
  > div {
    margin-bottom: 15px;
    &:last-child {
      margin-bottom: 0;
    }
  }
}
</style>

+ 104 - 0
litemall-vue/src/components/field/index.vue

@ -0,0 +1,104 @@
<template>
	<div class="md_field" :class="{md_field_hasIcon: !!icon, md_field_isError: isError}">
		<van-icon v-if="icon" :name="icon" class="md_feld_icon"/>
		<div class="md_field_control">
			<input
				:type="type"
				v-on="listeners"
				v-bind="$attrs"
				:value="value">
		</div>
		<div>
			<slot name="rightIcon">
				<van-icon :name="rightIcon" @click="rightClick" v-show="value" />
			</slot>
		</div>
	</div>
</template>
<script>
export default {
  name: 'md-field',
  props: {
    value: {},
    type: {
      type: String,
      default: 'text'
    },
    rightIcon: String,
    icon: String,
    isError: Boolean
  },
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        input: this.onInput
      };
    }
  },
  methods: {
    onInput(event) {
      this.$emit('input', event.target.value);
    },
    rightClick(event) {
      this.$emit('right-click', event);
    }
  }
};
</script>
<style lang="scss" scoped>
.md_field {
  position: relative;
  border: 1px solid $border-color;
  border-radius: 5px;
  padding-top: 10px;
  padding-bottom: 10px;
  padding-left: 10px;
  display: table;
  width: 100%;
  box-sizing: border-box;
  background-color: #fff;
  > div {
    display: table-cell;
  }
  > .md_field_control {
    padding-right: 10px;
    box-sizing: border-box;
    input {
      border: 0;
      line-height: 14px;
      font-size: 14px;
      width: 100%;
    }
  }
  .md_feld_icon {
    position: absolute;
    top: 50%;
    left: 10px;
    transform: translate(0, -50%);
  }
}
.md_field_hasIcon {
  padding-left: 40px;
}
.md_field_isError {
  color: $red;
  background-color: #fcf5f5;
  border: 1px solid $red;
  input {
    color: $red;
    background-color: #fcf5f5;
  }
  input:-webkit-autofill {
    -webkit-box-shadow: 0 0 0 1000px #fcf5f5 inset !important;
  }
}
</style>

+ 28 - 0
litemall-vue/src/components/is-empty/index.vue

@ -0,0 +1,28 @@
<template>
	<div class="is_empty">
		<div>
			<img src="../../assets/images/is_empty.png" alt="无商品" width="20%">
		</div>
		<div>
			<slot></slot>
		</div>
	</div>
</template>
<script>
export default {
  name: 'is-empty'
};
</script>
<style lang="scss" scoped>
.is_empty {
  text-align: center;
  color: $font-color-gray;
  padding-top: 100px;
  > div {
    margin-bottom: 20px;
  }
}
</style>

+ 14 - 0
litemall-vue/src/filter/index.js

@ -0,0 +1,14 @@
import dayjs from 'dayjs';
import { isNumber } from 'lodash';
export const dateFormat = (value, format = 'YYYY-MM-DD') =>
  value ? dayjs(value * 1000).format(format) : '';
export const yuan = value =>
  isNumber(value) ? `¥${(value / 100).toFixed(2)}` : value;
export default {
  install(Vue) {
    Vue.filter('yuan', yuan);
    Vue.filter('dateFormat', dateFormat);
  }
};

+ 41 - 0
litemall-vue/src/main.js

@ -0,0 +1,41 @@
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import 'vant/lib/icon/local.css';
import '@/assets/scss/global.scss';
import '@/assets/scss/iconfont/iconfont.css';
import VueCountdown from '@chenfengyuan/vue-countdown';
import store from './store'
import filters from '@/filter';
Vue.component(VueCountdown.name, VueCountdown);
Vue.use(filters);
import { Lazyload, Icon, Cell, CellGroup, loading, Button, Toast } from 'vant';
Vue.use(Icon);
Vue.use(Cell);
Vue.use(CellGroup);
Vue.use(loading);
Vue.use(Button);
Vue.use(Toast);
Vue.use(Lazyload, {
  preLoad: 1.3,
  error: require('@/assets/images/goods_default.png'),
  loading: require('@/assets/images/goods_default.png'),
  attempt: 1,
  listenEvents: ['scroll'],
  lazyComponent: true
});
Vue.config.productionTip = false;
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app');

+ 24 - 0
litemall-vue/src/mixin/scroll-fixed.js

@ -0,0 +1,24 @@
// 滚动条记录, 适用于 keep-alive 组件
import { debounce } from 'lodash';
export default {
  data() {
    return {
      scrollTop: 0
    };
  },
  mounted() {
    const vm = this;
    vm.$el.addEventListener(
      'scroll',
      debounce(() => {
        vm.scrollTop = vm.$el.scrollTop;
      }, 50)
    );
  },
  activated() {
    this.$el.scrollTop = this.scrollTop;
  }
};

+ 22 - 0
litemall-vue/src/router/home.js

@ -0,0 +1,22 @@
const Tabbar = () => import('@/components/Tabbar/');
export default [
  {
    path: '/',
    name: 'home',
    components: {
      default: () => import('@/views/home/tabbar-home'),
      tabbar: Tabbar
    },
    meta: {
      keepAlive: true,
      showHeader:false
    },
  },
  {
    path: '*',
    redirect: {
      name: 'home'
    }
  }
];

+ 41 - 0
litemall-vue/src/router/index.js

@ -0,0 +1,41 @@
import Vue from 'vue';
import Router from 'vue-router';
import { getLocalStorage } from '@/utils/local-storage';
import home from './home';
import items from './items';
import user from './user';
import order from './order';
import login from './login';
import store from '../store/index';
Vue.use(Router);
const RouterModel = new Router({
  routes: [...home, ...items, ...user, ...order, ...login]
});
RouterModel.beforeEach((to, from, next) => {
  const { Authorization } = getLocalStorage(
    'Authorization'
  );
  if (!Authorization) {
    if (to.meta.login) {
      console.log("login");
      next({ name: 'login', query: { redirect: to.name } });
      return;
    }
  }
  console.log(to.meta,"meta");
  //页面顶部菜单拦截
  let emptyObj=JSON.stringify(to.meta) == "{}";
  let undefinedObj=typeof(to.meta.showHeader)=="undefined";
  if(!emptyObj&&!undefinedObj){
    store.commit("CHANGE_HEADER",to.meta);
  }else{
    store.commit("CHANGE_HEADER",{showHeader:true,title:""});
  }
  next();
});
export default RouterModel;

+ 80 - 0
litemall-vue/src/router/items.js

@ -0,0 +1,80 @@
const Tabbar = () => import('@/components/Tabbar/');
export default [
  {
    path: '/items',
    name: 'class',
    meta: {
      keepAlive: true,
      showHeader:false
    },
    components: {
      default:  () => import('@/views/items/tabbar-catalog'),
      tabbar: Tabbar
    }
  },
  {
    path: '/items/search',
    name: 'search',
    meta: {
      keepAlive: true,
      title:"搜索",
      showHeader:false
    },
    component: () => import('@/views/items/search')
  },
  {
    path: '/items/detail/:itemId',
    name: 'detail',
    props: true,
    component: () => import('@/views/items/detail')
  },
  {
    path: '/items/category',
    name: 'category',
    component: () => import('@/views/items/category'),
    props: route => route.query
  },
  {
    path: '/items/hot',
    name: 'hot',
    component: () => import('@/views/items/hot'),
    props: route => route.query
  },
  {
    path: '/items/new',
    name: 'new',
    component: () => import('@/views/items/new'),
    props: route => route.query
  },
  {
    path: '/items/groupon',
    name: 'groupon',
    component: () => import('@/views/items/groupon'),
    props: route => route.query
  },
  {
    path: '/items/brand/:brandId',
    name: 'brand',
    props: true,
    component: () => import('@/views/items/brand')
  },
  {
    path: '/items/brand-list',
    name: 'brandList',
    component: () => import('@/views/items/brand-list'),
    props: route => route.query
  },
  {
    path: '/items/topic/:topicId',
    name: 'topic',
    props: true,
    component: () => import('@/views/items/topic')
  },
  {
    path: '/items/topic-list',
    name: 'topicList',
    component: () => import('@/views/items/topic-list'),
    props: route => route.query
  }
];

+ 44 - 0
litemall-vue/src/router/login.js

@ -0,0 +1,44 @@
export default [
  {
    path: '/login',
    name: 'login',
    meta: {
      showHeader:false,
      title:"登录"
    },
    component: () => import('@/views/login/login')
  },
  {
    path: '/login/registerGetCode',
    name: 'registerGetCode',
    component: () => import('@/views/login/register-getCode')
  },
  {
    path: '/login/registerSubmit/:phone',
    name: 'registerSubmit',
    props: true,
    component: () => import('@/views/login/register-submit')
  },
  {
    path: '/login/registerStatus/:status',
    name: 'registerStatus',
    props: true,
    component: () => import('@/views/login/register-status')
  },
  {
    path: '/login/forget',
    name: 'forget',
    component: () => import('@/views/login/forget')
  },
  {
    path: '/login/forget/reset',
    name: 'forgetReset',
    component: () => import('@/views/login/forget-reset')
  },
  {
    path: '/login/forget/reset/:status',
    name: 'forgetStatus',
    props: true,
    component: () => import('@/views/login/forget-status')
  }
];

+ 38 - 0
litemall-vue/src/router/order.js

@ -0,0 +1,38 @@
const Tabbar = () => import('@/components/Tabbar/');
export default [
  {
    path: '/order',
    name: 'cart',
    meta: {
      login: true,
      showHeader:false,
      title:"购物车"
    },
    components: { 
      default: () => import('@/views/order/tabbar-cart'), 
      tabbar: Tabbar 
    }
  },
  {
    path: '/order/checkout',
    name: 'orderCheckout',
    component: () => import('@/views/order/checkout')
  },
  {
    path: '/order/order-detail',
    name: 'orderDetail',
    component: () => import('@/views/order/order-detail')
  },
  {
    path: '/order/payment',
    name: 'payment',
    component: () => import('@/views/order/payment')
  },
  {
    path: '/order/payment/:status',
    name: 'paymentStatus',
    component: () => import('@/views/order/payment-status'),
    props: true
  }
];

+ 112 - 0
litemall-vue/src/router/user.js

@ -0,0 +1,112 @@
const tab_user = () => import('@/views/user/tabbar-user');
const UserCollect = () => import('@/views/user/module-collect');
const UserAddress = () => import('@/views/user/module-address');
const UserAddressEdit = () => import('@/views/user/module-address-edit');
const UserServer = () => import('@/views/user/module-server');
const UserHelp = () => import('@/views/user/module-help');
const UserFeedback = () => import('@/views/user/module-feedback');
const UserInformation = () => import('@/views/user/user-information-set');
const UserInfo_SetMobile = () => import('@/views/user/user-information-set/set-mobile');
const UserInfo_SetNickname = () => import('@/views/user/user-information-set/set-nickname');
const UserInfo_SetPassword = () => import('@/views/user/user-information-set/set-password');
const UserOrderList = () => import('@/views/user/order-list');
const UserCouponList = () => import('@/views/user/coupon-list');
const UserRefundList = () => import('@/views/user/refund-list');
const Tabbar = () => import('@/components/Tabbar/');
export default [
  {
    path: '/user',
    name: 'user',
    meta: {
      keepAlive: true,
      login: true,
      showHeader:false,
      title:"购物车"
    },
    components: { default: tab_user, tabbar: Tabbar }
  },
  {
    path: '/user/collect',
    name: 'collect',
    meta: {
      login: true
    },
    component: UserCollect
  },
  {
    path: '/user/address',
    name: 'address',
    meta: {
      login: true
    },
    component: UserAddress
  },
  {
    path: '/user/address/edit',
    name: 'address-edit',
    props: true,
    meta: {
      login: true
    },
    component: UserAddressEdit
  },
  {
    path: '/user/server',
    name: 'user-server',
    component: UserServer
  },
  {
    path: '/user/help',
    name: 'user-help',
    component: UserHelp
  },
  {
    path: '/user/feedback',
    name: 'user-feedback',
    component: UserFeedback
  },  
  {
    path: '/user/information',
    name: 'user-information',
    meta: {
      login: true
    },
    component: UserInformation
  },
  {
    path: '/user/information/setMobile',
    name: 'user-info-setMobile',
    component: UserInfo_SetMobile
  },
  {
    path: '/user/information/setNickname',
    name: 'user-info-setNickname',
    component: UserInfo_SetNickname
  },
  {
    path: '/user/information/setPassword',
    name: 'user-info-setPassword',
    component: UserInfo_SetPassword
  },
  {
    path: '/user/order/list/:active',
    name: 'user-order-list',
    props: true,
    component: UserOrderList
  },
  {
    path: '/user/coupon/list/:active',
    name: 'user-coupon-list',
    props: true,
    component: UserCouponList
  },
  {
    path: '/user/refund/list',
    name: 'user-refund-list',
    component: UserRefundList
  }
];

+ 3 - 0
litemall-vue/src/store/getters.js

@ -0,0 +1,3 @@
export const showHeader = state => state.showHeader
export const titleHeader = state => state.title

+ 18 - 0
litemall-vue/src/store/index.js

@ -0,0 +1,18 @@
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters'
import mutations from './mutations'
Vue.use(Vuex)
const state = {
  showHeader:true,
  title:'' 
}
export default new Vuex.Store({
  strict: process.env.NODE_ENV !== 'production',
  state,
  getters,
  mutations
})

+ 1 - 0
litemall-vue/src/store/mutation-types.js

@ -0,0 +1 @@
export const CHANGE_HEADER= 'CHANGE_HEADER'

+ 8 - 0
litemall-vue/src/store/mutations.js

@ -0,0 +1,8 @@
import * as types from './mutation-types'
export default {
  [types.CHANGE_HEADER] (state,payload) {
    state.showHeader=payload.showHeader;
    state.title=payload.title;
  }
}

+ 15 - 0
litemall-vue/src/utils/auth.js

@ -0,0 +1,15 @@
import Cookies from 'js-cookie'
const TokenKey = 'X-Litemall-Admin-Token'
export function getToken() {
  return Cookies.get(TokenKey)
}
export function setToken(token) {
  return Cookies.set(TokenKey, token)
}
export function removeToken() {
  return Cookies.remove(TokenKey)
}

+ 20 - 0
litemall-vue/src/utils/local-storage.js

@ -0,0 +1,20 @@
export const getLocalStorage = (...args) => {
  const storage = {};
  args.forEach(arg => {
    storage[arg] = window.localStorage.getItem(arg) || null;
  });
  return storage;
};
export const setLocalStorage = data => {
  Object.keys(data).forEach(prop => {
    const el = data[prop];
    window.localStorage.setItem(prop, el);
  });
};
export const removeLocalStorage = (...args) => {
  args.forEach(arg => {
    window.localStorage.removeItem(arg);
  });
};

+ 6 - 0
litemall-vue/src/utils/location-param.js

@ -0,0 +1,6 @@
export default name => {
  const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`);
  const r = window.location.search.substr(1).match(reg);
  if (r != null) return decodeURIComponent(r[2]);
  return '';
};

+ 58 - 0
litemall-vue/src/utils/request.js

@ -0,0 +1,58 @@
import axios from 'axios'
import { Dialog, Toast } from 'vant';
// create an axios instance
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
  timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
    config => {
    if (!config.headers['X-Litemall-Token']) {
      config.headers['X-Litemall-Token'] = `${window.localStorage.getItem(
        'Authorization'
      ) || ''}`;
    }
    return config;
  },
  err => Promise.reject(err)
)
// response interceptor
service.interceptors.response.use(
  response => {
    const res = response.data
    if (res.errno === 501) {
        Toast.fail('请登录');
        setTimeout(() => {
          window.location = '#/login/'
        }, 1500)
      return Promise.reject('error')
    } else if (res.errno === 502) {
        Toast.fail('网站内部错误,请联系网站维护人员')
      return Promise.reject('error')
    } if (res.errno === 401) {
      Toast.fail('参数不对');
      return Promise.reject('error')
    } if (res.errno === 402) {
      Toast.fail('参数值不对');
      return Promise.reject('error')
    } else if (res.errno !== 0) {
      // 非5xx的错误属于业务错误,留给具体页面处理
      return Promise.reject(response)
    } else {
      return response
    }
  }, error => {
    console.log('err' + error)// for debug
    Dialog.alert({
        title: '警告',
        message: '登录连接超时'
      });
    return Promise.reject(error)
  })
export default service

+ 31 - 0
litemall-vue/src/utils/scroll.js

@ -0,0 +1,31 @@
export default {
  isAttached(element) {
    let currentNode = element.parentNode;
    while (currentNode) {
      if (currentNode.tagName === 'HTML') {
        return true;
      }
      if (currentNode.nodeType === 11) {
        return false;
      }
      currentNode = currentNode.parentNode;
    }
    return false;
  },
  getScrollLeft(element) {
    return 'scrollLeft' in element ? element.scrollLeft : element.pageXOffset;
  },
  getVisibleHeight(element) {
    return element === window
      ? element.innerHeight
      : element.getBoundingClientRect().height;
  },
  getVisibleWidth(element) {
    return element === window
      ? element.innerWidth
      : element.getBoundingClientRect().width;
  }
};

+ 12 - 0
litemall-vue/src/utils/validate.js

@ -0,0 +1,12 @@
export const idCard = /^[1-9]{1}[0-9]{14}$|^[1-9]{1}[0-9]{16}([0-9]|[xX])$/;
export const mobileReg = /^1[0-9]{10}$/;
export const address = val => {
  const value = val.trim();
  return value.length >= 5 && value.length <= 100;
};
export const userName = /^[a-zA-Z0-9_\u4e00-\u9fa5]{3,20}$/;
export const emailReg = /^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/;

+ 434 - 0
litemall-vue/src/views/home/tabbar-home.vue

@ -0,0 +1,434 @@
<template>
  <div class="tab_home">
    <div class="tal_class_searchBox">
      <van-search placeholder="点击前往搜索"
                  @click="$router.push({ name: 'search' })" />
      <div class="tal_class_searchMask"></div>
    </div>
    <van-swipe :autoplay="3000"
               indicator-color="white">
      <van-swipe-item v-for="(banner, index) in shopInfos.banner"
                      :key="index">
        <img :src="banner.url"
             style="height:230px">
      </van-swipe-item>
    </van-swipe>
    <div class="goods-channel">
      <div class="item"
           @click="changeTabbar(channel)"
           v-for="(channel, index) in shopInfos.channel"
           :key="index">
        <img :src="channel.iconUrl"
             background-size="cover" />
        <span>{{channel.name}}</span>
      </div>
    </div>
    <van-panel title="优惠券"
               style=" padding-bottom: 10px;">
      <div class="van-coupon-item"
           v-for="(coupon,index) in shopInfos.couponList"
           :key="index"
           @click="getCoupon(coupon.id)">
        <div class="van-coupon-item__content">
          <div class="van-coupon-item__head">
            <h2>
              <span>¥</span>
              {{coupon.discount}} 元
            </h2>
            <p>{{coupon.desc }} - {{coupon.tag}}</p>
          </div>
          <div class="van-coupon-item__body">
            <h2>{{coupon.name}}</h2>
            <p>有效期:{{coupon.days}} 天</p>
          </div>
        </div>
      </div>
    </van-panel>
    <van-panel>
      <van-card :thumb-link="goDetail(grouponGood.id)"
                v-for="(grouponGood ,index) in shopInfos.grouponList"
                :key="index"
                :title="grouponGood.name"
                :desc="grouponGood.brief"
                :origin-price="grouponGood.retailPrice"
                :price="grouponGood.grouponPrice +'.00'"
                :thumb="grouponGood.picUrl"
                @native-click="goDetail(grouponGood.id)">
        <div slot="tags"
             class="card__tags">
          <van-tag plain
                   type="primary">
            {{grouponGood.grouponMember}}人成团
          </van-tag>
          <van-tag plain
                   type="danger">
            {{grouponGood.grouponDiscount}}元再减
          </van-tag>
        </div>
      </van-card>
      <div slot='header'>
        <van-cell-group>
          <van-cell title="团购专区"
                    isLink>
            <router-link to="/items/groupon"
                         class="text-desc">更多团购商品</router-link>
          </van-cell>
        </van-cell-group>
      </div>
    </van-panel>
    <van-panel>
      <van-grid clickable
                :column-num="2">
        <van-grid-item v-for="(brand ,index) in shopInfos.brandList"
                       :key="index"
                       :text="brand.name"
                       :url="goBrand(brand.id)">
          <img :src="brand.picUrl"
               style="width: 80%;" />
          <div style="font-size:16px;"> {{ brand.name }}</div>
        </van-grid-item>
      </van-grid>
      <div slot='header'>
        <van-cell-group>
          <van-cell title="品牌商直供"
                    isLink>
            <router-link to="/items/brand-list"
                         class="text-desc">更多品牌商</router-link>
          </van-cell>
        </van-cell-group>
      </div>
    </van-panel>
    <van-panel>
      <van-row gutter>
        <van-col span="12"
                 v-for="(newGood ,index) in shopInfos.newGoodsList"
                 :key="index">
          <router-link :to="{ path: `/items/detail/${newGood.id}`}">
            <img :src="newGood.picUrl"
                 style="width:180px;height:180px;">
          </router-link>
          <span style="padding-left: 20px;position: relative;bottom: 10px; color: rgb(123, 116, 116);white-space: nowrap;">{{newGood.name}}</span>
          <span style="padding-left: 80px;position: relative;bottom: 10px; color:#ab956d">¥ {{newGood.retailPrice}}</span>
        </van-col>
      </van-row>
      <div slot='header'>
        <van-cell-group>
          <van-cell title="新品首发"
                    isLink>
            <router-link to="/items/new"
                         class="text-desc">更多新品首发</router-link>
          </van-cell>
        </van-cell-group>
      </div>
    </van-panel>
    <van-panel>
      <van-card :thumb-link="goDetail(groupGood.id)"
                v-for="(groupGood ,index) in shopInfos.hotGoodsList"
                :key="index"
                :title="groupGood.name"
                :desc="groupGood.brief"
                :origin-price="groupGood.counterPrice"
                :price="groupGood.retailPrice +'.00'"
                :thumb="groupGood.picUrl"
                @native-click="goDetail(groupGood.id)">
        <!-- <div slot="footer">添加日期 {{item.addTime}}</div> -->
      </van-card>
      <div slot='header'>
        <van-cell-group>
          <van-cell title="人气推荐"
                    isLink>
            <router-link to="/items/hot"
                         class="text-desc">更多人气推荐</router-link>
          </van-cell>
        </van-cell-group>
      </div>
    </van-panel>
<van-panel>
      <van-grid clickable
                :column-num="2">
        <van-grid-item v-for="(topic ,index) in shopInfos.topicList"
                       :key="index"
                       :url="goTopic(topic.id)">
          <img :src="topic.picUrl"
               style="width: 90%; max-height: 150px;" />
          <div style="font-size:14px;color:#ab956d;"> {{ topic.title }}</div>
          <div style="font-size:10px;color:#ab956d;"> {{ topic.subtitle }}</div>
        </van-grid-item>
      </van-grid>
      <div slot='header'>
        <van-cell-group>
          <van-cell title="专题精选"
                    isLink>
            <router-link to="/items/topic-list"
                         class="text-desc">更多专题精选</router-link>
          </van-cell>
        </van-cell-group>
      </div>
    </van-panel>
  </div>
</template>
<script>
import { getHome, goodsCategory, couponReceive } from '@/api/api';
import scrollFixed from '@/mixin/scroll-fixed';
import _ from 'lodash';
import {
  List,
  Swipe,
  SwipeItem,
  Tabbar,
  TabbarItem,
  Search,
  Panel,
  CouponCell,
  CouponList,
  Toast,
  Card,
  Grid,
  GridItem,
  Row,
  Col,
  Tag
} from 'vant';
export default {
  mixins: [scrollFixed],
  data() {
    return {
      shopInfos: [],
      isLoading: false
    };
  },
  created() {
    this.initViews();
  },
  methods: {
    goDetail(id) {
      return `#/items/detail/${id}`;
    },
    goBrand(id) {
      return `#/items/brand/${id}`;
    },
    goTopic(id) {
      return `#/items/topic/${id}`;
    },    
    getCoupon(id) {
      couponReceive({ couponId: id }).then(res => {
        Toast.success('领取成功');
      });
    },
    changeTabbar(o) {
      goodsCategory({ id: o.id }).then(res => {
        let categoryId = res.data.data.currentCategory.id;
        this.$router.replace({
          name: 'category',
          query: { itemClass: categoryId }
        });
      });
    },
    initViews() {
      getHome().then(res => {
        this.shopInfos = res.data.data;
      });
    }
  },
  components: {
    [Row.name]: Row,
    [Col.name]: Col,
    [Card.name]: Card,
    [Toast.name]: Toast,
    [CouponCell.name]: CouponCell,
    [CouponList.name]: CouponList,
    [Search.name]: Search,
    [Panel.name]: Panel,
    [List.name]: List,
    [Swipe.name]: Swipe,
    [SwipeItem.name]: SwipeItem,
    [Tabbar.name]: Tabbar,
    [TabbarItem.name]: TabbarItem,
    [Tag.name]: Tag,
    [Grid.name]: Grid,
    [GridItem.name]: GridItem
  }
};
</script>
<style lang="scss" scoped>
.interval_bot {
  margin-bottom: 10px;
}
.van-panel {
  margin-top: 20px;
}
.goods-channel {
  background: #fff;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  padding-bottom: 0px;
  padding-top: 10px;
}
.goods-channel .item {
  width: 50px;
  height: 50px;
  margin-left: 10px;
}
.goods-channel img {
  display: block;
  width: 20px;
  height: 20px;
  margin: 0 auto;
}
.goods-channel span {
  display: block;
  font-size: 14px;
  text-align: center;
  margin-top: 5px;
  margin-bottom: 10px;
  color: #333;
}
.van-coupon-cell--selected {
  color: #323233;
}
.van-coupon-list {
  height: 100%;
  position: relative;
  background-color: #f8f8f8;
}
.van-coupon-list__field {
  padding: 7px 15px;
}
.van-coupon-list__exchange {
  height: 32px;
  line-height: 30px;
}
.van-coupon-list__list {
  overflow-y: auto;
  padding: 15px 0;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  -webkit-overflow-scrolling: touch;
}
.van-coupon-list__close {
  left: 0;
  bottom: 0;
  position: absolute;
  font-weight: 500;
}
.van-coupon-list__empty {
  padding-top: 100px;
  text-align: center;
}
.van-coupon-list__empty p {
  color: #969799;
  margin: 15px 0;
  font-size: 14px;
  line-height: 20px;
}
.van-coupon-list__empty img {
  width: 80px;
  height: 84px;
}
.van-coupon-item {
  overflow: hidden;
  border-radius: 4px;
  margin: 0 15px 15px;
  background-color: #fff;
  -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
  box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
}
.van-coupon-item:active {
  background-color: #e8e8e8;
}
.van-coupon-item__content {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  height: 100px;
  padding: 24px 0 0 15px;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  border: 1px solid red;
}
.van-coupon-item h2,
.van-coupon-item p {
  margin: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.van-coupon-item h2 {
  height: 34px;
  font-weight: 500;
  line-height: 34px;
}
.van-coupon-item p {
  font-size: 12px;
  line-height: 16px;
  color: #969799;
}
.van-coupon-item__head {
  min-width: 90px;
}
.van-coupon-item__head h2 {
  color: #f44;
  font-size: 24px;
}
.van-coupon-item__head h2 span {
  font-size: 50%;
}
.van-coupon-item__body {
  -webkit-box-flex: 1;
  -ms-flex: 1;
  flex: 1;
  position: relative;
  border-radius: 0 4px 4px 0;
}
.van-coupon-item__body h2 {
  font-size: 16px;
}
.van-coupon-item__corner {
  top: 16px;
  right: 15px;
  position: absolute;
}
.van-coupon-item__corner .van-icon {
  border-color: #f44;
  background-color: #f44;
}
.van-coupon-item__reason {
  padding: 7px 15px;
  border-top: 1px dashed #ebedf0;
  background-color: #fafafa;
}
.van-coupon-item--disabled:active {
  background-color: #fff;
}
.van-coupon-item--disabled .van-coupon-item__content {
  height: 90px;
}
.van-coupon-item--disabled h2,
.van-coupon-item--disabled p,
.van-coupon-item--disabled span {
  color: #969799;
}
</style>

+ 141 - 0
litemall-vue/src/views/items/brand-list/index.vue

@ -0,0 +1,141 @@
<template>
  <div class="goods_brand_list">
    <van-list v-model="loading"
              :finished="finished"
              :immediate-check="false"
              finished-text="没有更多了"
              @load="getBrandList">
      <div class="brand-info"
           v-for="(brand, index) in list"
           :key="index"
           @click="itemClick(brand.id)">
        <div class="name">
          <img class="img"
               :src="brand.picUrl"
               background-size="cover" />
          <div class="info-box">
            <div class="txt">{{brand.name}}</div>
            <div class="line"></div>
            <div class="price">{{brand.floorPrice}}元起</div>
          </div>
        </div>
        <div class="desc">
          {{brand.desc}}
        </div>
      </div>
    </van-list>
  </div>
</template>
<script>
import { brandList } from '@/api/api';
import { List } from 'vant';
export default {
  data() {
    return {
      list: [],
      page: 0,
      limit: 10,
      loading: false,
      finished: false
    };
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      this.page = 0;
      this.list = [];
      this.getBrandList();
    },
    getBrandList() {
      this.page++;
      brandList({
        page: this.page,
        limit: this.limit
      }).then(res => {
        this.list.push(...res.data.data.list);
        this.loading = false;
        this.finished = res.data.data.page >= res.data.data.pages;
      });
    },
    itemClick(id) {
      this.$router.push(`/items/brand/${id}`);
    }
  },
  components: {
    [List.name]: List
  }
};
</script>
<style lang="scss" scoped>
.goods_brand_list {
  .brand-info {
    .name {
      width: 100%;
      height: 180px;
      position: relative;
      .img {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 180px;
      }
      .info-box {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 180px;
        text-align: center;
        display: flex;
        justify-content: center;
        align-items: center;
        display: block;
        .txt {
          margin-top: 60px;
          height: 25px;
          font-size: 25px;
          color: #fff;
        }
        .line {
          margin: 0 auto;
          margin-top: 16px;
          display: block;
          height: 2px;
          width: 300px;
          background: #fff;
        }
        .price{
          height: 25px;
          font-size: 25px;
          color: #fff;
        }
      }
    }
    .desc {
      background: #fff;
      width: 100%;
      height: auto;
      overflow: hidden;
      padding: 25px 20px;
      font-size: 20px;
      color: #666;
      line-height: 20px;
      text-align: center;
    }
  }
}
</style>

+ 139 - 0
litemall-vue/src/views/items/brand/index.vue

@ -0,0 +1,139 @@
<template>
  <div class="goods_brand">
    <div class="brand-info">
      <div class="name">
        <img class="img"
             :src="brand.picUrl"
             background-size="cover" />
        <div class="info-box">
          <div class="txt">{{brand.name}}</div>
          <div class="line"></div>
        </div>
      </div>
      <div class="desc">
        {{brand.desc}}
      </div>
    </div>
    <van-row gutter>
      <van-col span="12"
               v-for="(goods ,index) in brandGoods"
               :key="index">
        <router-link :to="{ path: `/items/detail/${goods.id}`}">
          <img :src="goods.picUrl"
               style="width:150px;height:150px;">
        </router-link>
        <div style="margin-left: 20px; rgb(123, 116, 116);">{{goods.name}}</div>
        <div style="margin-left: 20px; color:#ab956d">¥ {{goods.retailPrice}}</div>
      </van-col>
    </van-row>
  </div>
</template>
<script>
import { brandDetail, goodsList } from '@/api/api';
import { Card, Row, Col } from 'vant';
export default {
  props: {
    brandId: [String, Number]
  },
  data() {
    return {
      brand: {},
      brandGoods: []
    };
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      brandDetail({
        id: this.brandId
      }).then(res => {
        this.brand = res.data.data;
      });
      goodsList({
        brandId: this.brandId
      }).then(res => {
        this.brandGoods = res.data.data.list;
      });
    },
    itemClick(id) {
      this.$router.push(`/items/detail/${id}`);
    }
  },
  components: {
    [Card.name]: Card,
    [Row.name]: Row,
    [Col.name]: Col
  }
};
</script>
<style lang="scss" scoped>
.goods_brand {
  .brand-info {
    .name {
      width: 100%;
      height: 180px;
      position: relative;
      .img {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 180px;
      }
      .info-box {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 180px;
        text-align: center;
        display: flex;
        justify-content: center;
        align-items: center;
        display: block;
        .txt {
          display: block;
          margin-top: 60px;
          height: 35px;
          font-size: 35px;
          color: #fff;
        }
        .line {
          margin: 0 auto;
          margin-top: 16px;
          display: block;
          height: 2px;
          width: 145px;
          background: #fff;
        }
      }
    }
    .desc {
      background: #fff;
      width: 100%;
      height: auto;
      overflow: hidden;
      padding: 25px 20px;
      font-size: 20px;
      color: #666;
      line-height: 20px;
      text-align: center;
    }
  }
}
</style>

+ 147 - 0
litemall-vue/src/views/items/category/index.vue

@ -0,0 +1,147 @@
<template>
  <div class="item_list">
    <van-tabs v-model="navActive"
              @click="handleTabClick">
      <van-tab v-for="(nav, index) in navList"
               :title="nav.name"
               :key="index">
        <van-list v-model="loading"
                  :finished="finished"
                  :immediate-check="false"
                  finished-text="没有更多了"
                  @load="getGoodsList">
          <div class="h">
            <div class="name">{{currentCategory.name}}</div>
            <div class="desc">{{currentCategory.desc}}</div>
          </div>
          <van-card v-for="(item, i) in goodsList"
                    :key="i"
                    :desc="item.brief"
                    :title="item.name"
                    :thumb="item.picUrl"
                    :price="item.retailPrice"
                    :origin-price="item.counterPrice"
                    @click="itemClick(item.id)" />
        </van-list>
      </van-tab>
    </van-tabs>
  </div>
</template>
<script>
import { goodsCategory, goodsList } from '@/api/api';
import { Card, List, Tab, Tabs } from 'vant';
export default {
  name: 'Item-list',
  props: {
    itemClass: {
      type: [String, Number],
      default: ''
    }
  },
  data() {
    return {
      categoryId: this.itemClass,
      goodsList: [],
      page: 0,
      limit: 10,
      currentCategory: {},
      navList: [],
      navActive: 0,
      loading: false,
      finished: false
    };
  },
  created() {
    this.init();
  },
  methods: {
    handleTabClick(index) {
      this.categoryId = this.navList[index].id;
      this.$router.replace({
        name: 'category',
        query: { itemClass: this.categoryId }
      });
      this.init();
    },
    init() {
      goodsCategory({ id: this.categoryId }).then(res => {
        this.navList = res.data.data.brotherCategory;
        this.currentCategory = res.data.data.currentCategory;
        // 当id是L1分类id时,这里需要重新设置成L1分类的一个子分类的id
        if (res.data.data.parentCategory.id == this.categoryId) {
          this.categoryId = res.data.data.currentCategory.id;
        }
        for (let i = 0; i < this.navList.length; i++) {
          if (this.navList[i].id == this.categoryId) {
            this.navActive = i;
            break;
          }
        }
        this.page = 0;
        this.goodsList = [];
        this.getGoodsList();
      });
    },
    getGoodsList() {
      this.page++;
      goodsList({
        categoryId: this.categoryId,
        page: this.page,
        limit: this.limit
      }).then(res => {
        this.goodsList.push(...res.data.data.list);
        this.loading = false;
        this.finished = res.data.data.page >= res.data.data.pages;
      });
    },
    itemClick(id) {
      this.$router.push(`/items/detail/${id}`);
    }
  },
  components: {
    [List.name]: List,
    [Card.name]: Card,
    [Tab.name]: Tab,
    [Tabs.name]: Tabs
  }
};
</script>
<style lang="scss" scoped>
.item_list {
  background-color: #fff;
}
.h {
  height: 100px;
  width: 100%;
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.h .name {
  display: block;
  height: 30px;
  margin-bottom: 10px;
  font-size: 20px;
  color: #333;
}
.h .desc {
  display: block;
  height: 24px;
  font-size: 16px;
  color: #999;
}
</style>

+ 410 - 0
litemall-vue/src/views/items/detail/index.vue

@ -0,0 +1,410 @@
<template>
  <div class="item_detail">
    <van-swipe :autoplay="3000">
      <van-swipe-item v-for="(image, index) in goods.info.gallery" :key="index">
        <img v-lazy="image" width="100%">
      </van-swipe-item>
    </van-swipe>
    <van-cell-group class="item_cell_group" v-if="goods">
      <van-cell class="item_info">
        <div>
          <span class="item_price">{{ goods.info.retailPrice*100 | yuan }}</span>
          <span class="item_market_price">{{goods.info.counterPrice*100 | yuan}}</span>
        </div>
        <div class="item-title">
          {{ goods.info.name }}
        </div>
        <div class="item_intro">{{goods.info.brief}}</div>
      </van-cell>
    </van-cell-group>
  <div class="item_cell_group">
    <van-cell-group>
      <van-cell
        title="规格"
        isLink
        value="请选择"
        @click.native="skuClick"
      />
      <van-cell title="属性" isLink @click.native="propsPopup = true"/>
      <van-cell title="运费" value="满88免邮费"/>
    </van-cell-group>
    <van-sku
      v-model="showSku"
      :sku="sku"
      :hide-stock="true"
      :goods="skuGoods"
      :goodsId="goods.info.id"
      @buy-clicked="buyGoods"
      @add-cart="addCart"
    />
    <van-popup v-model="propsPopup" position="bottom">
      <popup-props :propsStr="props_str"></popup-props>
    </van-popup>
  </div>
    <div class="item_desc">
      <div class="item_desc_title">商品详情</div>
      <div class="item_desc_wrap" v-if="goods.info.detail" v-html="goods.info.detail"></div>
      <div class="item_desc_wrap" v-else style="text-align: center;">
        <p>无详情</p>
      </div>
    </div>
    <van-goods-action>
      <van-goods-action-icon @click="toCart" icon="cart-o" :info="(cartInfo > 0) ? cartInfo : ''"/>
      <van-goods-action-icon @click="addCollect" icon="star-o" :style="(goods.userHasCollect !== 0) ? 'color: #f7b444;':''"/>
      <van-goods-action-button type="warning" @click="skuClick" text="加入购物车"/>
      <van-goods-action-button type="danger" @click="skuClick" text="立即购买"/>
    </van-goods-action>
  </div>
</template>
<script>
import { goodsDetail, cartGoodsCount, collectAddOrDelete, cartAdd, cartFastAdd } from '@/api/api';
import { Sku, Swipe, SwipeItem, GoodsAction, GoodsActionButton, GoodsActionIcon, Popup } from 'vant';
import { setLocalStorage } from '@/utils/local-storage';
import popupProps from './popup-props';
import _ from 'lodash';
export default {
  props: {
    itemId: [String, Number]
  },
  data() {
    const isLogin = !!localStorage.getItem('Authorization');
    return {
      isLogin,
      goods: {
        userHasCollect: 0,
        info: {
          gallery: []
        }
      },
      sku: {
        tree: [],
        list: [],
        price: '1.00' // 默认价格(单位元)
      },
      skuGoods: {
        // 商品标题
        title: '',
        // 默认商品 sku 缩略图
        picture: ''
      },
      cartInfo: 0,
      selectSku: {
        selectedNum: 1,
        selectedSkuComb: {
          sku_str: 'aa'
        }
      },
      propsPopup: false,
      showSku: false
    };
  },
  computed: {
    props_str() {
      let props_arr = [];
      _.each(this.goods.attribute, json => {
        props_arr.push([json['attribute'], json['value']]);
      });
      return props_arr || [];
    }
  },
  created() {
    this.initData();
  },
  methods: {
    skuClick() {
      this.showSku = true;
    },
    initData() {
      goodsDetail({ id: this.itemId }).then(res => {
        this.goods = res.data.data;
        this.skuAdapter();
      });
      cartGoodsCount().then(res => {
        this.cartInfo = res.data.data;
      });
    },
    toCart() {
      this.$router.push({
        name: 'cart'
      });
    },
    addCollect() {
      collectAddOrDelete({ valueId: this.itemId, type: 0 }).then(res => {
        if (this.goods.userHasCollect === 1) {
          this.goods.userHasCollect = 0;
        } else {
          this.goods.userHasCollect = 1;
          this.$toast({
            message: '收藏成功',
            duration: 1500
          });
        }
      });
    },
    getProductId(s1, s2) {
      var productId;
      var s1_name;
      var s2_name;
      _.each(this.goods.specificationList, specification => {
        _.each(specification.valueList, specValue => {
          if (specValue.id === s1) {
            s1_name = specValue.value;
          } else if (specValue.id === s2) {
            s2_name = specValue.value;
          }
        });
      });
      _.each(this.goods.productList, v => {
        let result = _.without(v.specifications, s1_name, s2_name);
        if (result.length === 0) {
          productId = v.id;
        }
      });
      return productId;
    },
    getProductIdByOne(s1) {
      var productId;
      var s1_name;
      _.each(this.goods.specificationList, specification => {
        _.each(specification.valueList, specValue => {
          if (specValue.id === s1) {
            s1_name = specValue.value;
            return;
          }
        });
      });
      _.each(this.goods.productList, v => {
        let result = _.without(v.specifications, s1_name);
        if (result.length === 0) {
          productId = v.id;
        }
      });
      return productId;
    },
    addCart(data) {
      let that = this;
      let params = {
        goodsId: data.goodsId,
        number: data.selectedNum,
        productId: 0
      };
      if (_.has(data.selectedSkuComb, 's3')) {
        this.$toast({
          message: '目前仅支持两规格',
          duration: 1500
        });
        return;
      } else if (_.has(data.selectedSkuComb, 's2')) {
        params.productId = this.getProductId(
          data.selectedSkuComb.s1,
          data.selectedSkuComb.s2
        );
      } else {
        params.productId = this.getProductIdByOne(data.selectedSkuComb.s1);
      }
      cartAdd(params).then(() => {
        this.cartInfo = this.cartInfo + data.selectedNum;
        this.$toast({
          message: '已添加至购物车',
          duration: 1500
        });
        that.showSku = false;
      });
    },
    buyGoods(data) {
      let that = this;
      let params = {
        goodsId: data.goodsId,
        number: data.selectedNum,
        productId: 0
      };
      if (_.has(data.selectedSkuComb, 's3')) {
        this.$toast({
          message: '目前仅支持两规格',
          duration: 1500
        });
        return;
      } else if (_.has(data.selectedSkuComb, 's2')) {
        params.productId = this.getProductId(
          data.selectedSkuComb.s1,
          data.selectedSkuComb.s2
        );
      } else {
        params.productId = this.getProductIdByOne(data.selectedSkuComb.s1);
      }
      cartFastAdd(params).then(res => {
        let cartId = res.data.data;
        setLocalStorage({ CartId: cartId });
        that.showSku = false;
        this.$router.push('/order/checkout');
      });
    },
    skuAdapter() {
      const tree = this.setSkuTree();
      const list = this.setSkuList();
      const skuInfo = {
        price: parseInt(this.goods.info.retailPrice), // 未选择规格时的价格
        stock_num: 0, // TODO 总库存
        collection_id: '', // 无规格商品skuId取collection_id,否则取所选sku组合对应的id
        none_sku: false, // 是否无规格商品
        hide_stock: true
      };
      this.sku = {
        tree,
        list,
        ...skuInfo
      };
      this.skuGoods = {
        title: this.goods.info.name,
        picture: this.goods.info.picUrl
      };
    },
    setSkuList() {
      var sku_list = [];
      _.each(this.goods.productList, v => {
        var sku_list_obj = {};
        _.each(v.specifications, (specificationName, index) => {
          sku_list_obj['s' + (~~index + 1)] = this.findSpecValueIdByName(
            specificationName
          );
        });
        sku_list_obj.price = v.price * 100;
        sku_list_obj.stock_num = v.number;
        sku_list.push(sku_list_obj);
      });
      return sku_list;
    },
    findSpecValueIdByName(name) {
      let id = 0;
      _.each(this.goods.specificationList, specification => {
        _.each(specification.valueList, specValue => {
          if (specValue.value === name) {
            id = specValue.id;
            return;
          }
        });
        if (id !== 0) {
          return;
        }
      });
      return id;
    },
    setSkuTree() {
      let that = this;
      let specifications = [];
      _.each(this.goods.specificationList, (v, k) => {
        let values = [];
        _.each(v.valueList, vv => {
          vv.name = vv.value;
          values.push({
            id: vv.id,
            name: vv.value,
            imUrl: vv.picUrl
          });
        });
        specifications.push({
          k: v.name,
          v: values,
          k_s: 's' + (~~k + 1)
        });
      });
      return specifications;
    }
  },
  components: {
    [Popup.name]: Popup,
    [Swipe.name]: Swipe,
    [SwipeItem.name]: SwipeItem,
    [Sku.name]: Sku,
    [GoodsAction.name]: GoodsAction,
    [GoodsActionButton.name]: GoodsActionButton,
    [GoodsActionIcon.name]: GoodsActionIcon,
    [popupProps.name]: popupProps
  }
};
</script>
<style lang="scss" scoped>
.item_detail {
  img {
    max-width: 100%;
  }
}
.item_cell_group {
  margin-bottom: 15px;
}
.item_price {
  font-size: 20px;
  color: $red;
  margin-right: 10px;
}
.item_market_price {
  color: $font-color-gray;
  text-decoration: line-through;
  font-size: $font-size-small;
}
.item-title {
  line-height: 1.4;
}
.item_dispatch {
  font-size: $font-size-small;
  color: $font-color-gray;
}
.item_intro {
  line-height: 18px;
  margin: 5px 0;
  font-size: $font-size-small;
  color: $font-color-gray;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}
.item_desc {
  background-color: #fff;
  ::v-deep p {
    padding: 0 10px;
    margin-block-start: 0 !important;
    margin-block-end: 0 !important;
  }
  ::v-deep img {
    max-width: 100%;
    display: block;
  }
}
.item_desc_title {
  @include one-border;
  padding: 10px 0;
  text-align: center;
}
</style>

+ 79 - 0
litemall-vue/src/views/items/detail/popup-props.vue

@ -0,0 +1,79 @@
<template>
  <div class="popup_wrap">
    <van-icon name="clear" class="cancel_popup" @click.native="$parent.value = false"></van-icon>
    <div class="popup_header">商品属性</div>
    <div class="popup_content">
      <van-cell-group>
        <van-cell v-for="(str, i) in propsStr" :key="i">
          <van-row>
            <van-col span="8">{{str[0]}}</van-col>
            <van-col span="16">{{str[1]}}</van-col>
          </van-row>
        </van-cell>
      </van-cell-group>
    </div>
  </div>
</template>
<script>
import { Row, Col } from 'vant';
export default {
  name: 'popup-props',
  props: {
    propsStr: {
      type: Array,
      default: () => []
    }
  },
  components: {
    [Col.name]: Col,
    [Row.name]: Row
  }
};
</script>
<style lang="scss" scoped>
.popup_wrap {
  position: relative;
  padding-bottom: 30px;
  box-sizing: border-box;
  .popup_header {
    padding: 15px 0 30px 0;
    text-align: center;
  }
  .popup_content {
    min-height: 150px;
    max-height: 400px;
    box-sizing: border-box;
    overflow-x: hidden;
    overflow-y: scroll;
    padding: 0 10px;
    line-height: 30px;
    &::-webkit-scrollbar {
      background-color: #fff;
      width: 5px;
    }
    &::-webkit-scrollbar-thumb {
      border-radius: 3px;
      background-color: #bebebe;
    }
    ol {
      padding-left: 15px;
      list-style: decimal;
    }
  }
  .cancel_popup {
    position: absolute;
    right: 15px;
    top: 15px;
    z-index: 9;
    font-size: 18px;
  }
}
</style>

+ 105 - 0
litemall-vue/src/views/items/groupon/index.vue

@ -0,0 +1,105 @@
<template>
  <div class="goods_groupon">
    <div class="banner">
      <div class="title">团购列表</div>
    </div>
    <van-list v-model="loading"
              :finished="finished"
              :immediate-check="false"
              finished-text="没有更多了"
              @load="getGrouponList">
      <van-card v-for="(item, i) in list"
                :key="i"
                :desc="item.brief"
                :title="item.name"
                :thumb="item.picUrl"
                :price="item.retailPrice"
                :origin-price="item.counterPrice"
                @click="itemClick(item.id)">
        <div slot="tags">
          <van-tag plain
                   type="primary">
            {{item.grouponMember}}人成团
          </van-tag>
          <van-tag plain
                   type="danger"
                   style="margin-left:5px;">
            {{item.grouponDiscount}}元再减
          </van-tag>
        </div>
      </van-card>
    </van-list>
  </div>
</template>
<script>
import { grouponList } from '@/api/api';
import { Card, Tag, List } from 'vant';
import scrollFixed from '@/mixin/scroll-fixed';
export default {
  mixins: [scrollFixed],
  data() {
    return {
      list: [],
      page: 0,
      limit: 10,
      loading: false,
      finished: false
    };
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      this.page = 0;
      this.list = [];
      this.getGrouponList();
    },
    getGrouponList() {
      this.page++;
      grouponList({
        page: this.page,
        limit: this.limit
      }).then(res => {
        this.list.push(...res.data.data.list);
        this.loading = false;
        this.finished = res.data.data.page >= res.data.data.pages;
      });
    },
    itemClick(id) {
      this.$router.push(`/items/detail/${id}`);
    }
  },
  components: {
    [List.name]: List,
    [Tag.name]: Tag,
    [Card.name]: Card
  }
};
</script>
<style lang="scss" scoped>
.goods_groupon {
  padding: 20px;
  .banner {
    height: 250px;
    background-image: url('http://yanxuan.nosdn.127.net/8976116db321744084774643a933c5ce.png');
    background-size: cover;
    margin-bottom: 20px;
    .title {
      text-align: center;
      line-height: 200px;
      color: #ffffff;
      font-size: 40px;
    }
  }
}
</style>

+ 94 - 0
litemall-vue/src/views/items/hot/index.vue

@ -0,0 +1,94 @@
<template>
  <div class="goods_hot">
    <div class="banner">
      <div class="title">大家都在买</div>
    </div>
    <van-list v-model="loading"
              :finished="finished"
              :immediate-check="false"
              finished-text="没有更多了"
              @load="getHotList">
      <van-card v-for="(item, i) in list"
                :key="i"
                :desc="item.brief"
                :title="item.name"
                :thumb="item.picUrl"
                :price="item.retailPrice"
                :origin-price="item.counterPrice"
                @click="itemClick(item.id)">
      </van-card>
    </van-list>
  </div>
</template>
<script>
import { goodsList } from '@/api/api';
import { Card, List } from 'vant';
import scrollFixed from '@/mixin/scroll-fixed';
export default {
  mixins: [scrollFixed],
  data() {
    return {
      list: [],
      page: 0,
      limit: 10,
      loading: false,
      finished: false
    };
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      this.page = 0;
      this.list = [];
      this.getHotList();
    },
    getHotList() {
      this.page++;
      goodsList({
        isHot: true,
        page: this.page,
        limit: this.limit
      }).then(res => {
        this.list.push(...res.data.data.list);
        this.loading = false;
        this.finished = res.data.data.page >= res.data.data.pages;
      });
    },
    itemClick(id) {
      this.$router.push(`/items/detail/${id}`);
    }
  },
  components: {
    [List.name]: List,
    [Card.name]: Card
  }
};
</script>
<style lang="scss" scoped>
.goods_hot {
  padding: 20px;
  .banner {
    height: 250px;
    background-image: url('http://yanxuan.nosdn.127.net/8976116db321744084774643a933c5ce.png');
    background-size: cover;
    margin-bottom: 20px;
    .title {
      text-align: center;
      line-height: 200px;
      color: #ffffff;
      font-size: 40px;
    }
  }
}
</style>

+ 95 - 0
litemall-vue/src/views/items/new/index.vue

@ -0,0 +1,95 @@
<template>
  <div class="goods_new">
    <div class="banner">
      <div class="title">新品首发</div>
    </div>
    <van-list v-model="loading"
              :finished="finished"
              :immediate-check="false"
              finished-text="没有更多了"
              @load="getNewtList">
      <van-card v-for="(item, i) in list"
                :key="i"
                :desc="item.brief"
                :title="item.name"
                :thumb="item.picUrl"
                :price="item.retailPrice"
                :origin-price="item.counterPrice"
                @click="itemClick(item.id)">
      </van-card>
    </van-list>
  </div>
</template>
<script>
import { goodsList } from '@/api/api';
import { Card, List } from 'vant';
import scrollFixed from '@/mixin/scroll-fixed';
export default {
  mixins: [scrollFixed],
  data() {
    return {
      list: [],
      page: 0,
      limit: 10,
      loading: false,
      finished: false
    };
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      this.page = 0;
      this.list = [];
      this.getNewtList();
    },
    getNewtList() {
      this.page++;
      goodsList({
        isNew: true,
        page: this.page,
        limit: this.limit
      }).then(res => {
        this.list.push(...res.data.data.list);
        this.loading = false;
        this.finished = res.data.data.page >= res.data.data.pages;
      });
    },
    itemClick(id) {
      this.$router.push(`/items/detail/${id}`);
    }
  },
  components: {
    [List.name]: List,
    [Card.name]: Card
  }
};
</script>
<style lang="scss" scoped>
.goods_new {
  padding: 20px;
  .banner {
    height: 250px;
    background-image: url('http://yanxuan.nosdn.127.net/8976116db321744084774643a933c5ce.png');
    background-size: cover;
    margin-bottom: 20px;
    .title {
      text-align: center;
      line-height: 200px;
      color: #ffffff;
      font-size: 40px;
    }
  }
}
</style>

+ 168 - 0
litemall-vue/src/views/items/search/index.vue

@ -0,0 +1,168 @@
<template>
  <div class="item_search">
    <form action="/search"
          @submit="disabledSubmit">
      <van-search placeholder="请输入商品名称"
                  v-model="keyword"
                  @search="enterSearch"
                  autofocus />
    </form>
    <div class="item_search_content">
      <div class="item_search_text clearfix">
        <div class="float-l">历史搜索</div>
        <div class="float-r"
             @click="clearHistory">
          <van-icon name="lajitong"
                    style="font-size: 12px;margin-right: 3px" />
          清空历史记录
        </div>
      </div>
      <div class="item_search_history">
        <van-tag plain
                 v-for="(his, i) in wordHistory"
                 :key="i"
                 @click="clickSearch(his)">{{his}}</van-tag>
      </div>
    </div>
    <van-list v-model="loading"
              :finished="finished"
              :immediate-check="false"
              @load="loadMore">
      <van-card v-for="(item, i) in list"
                :key="i"
                :desc="item.brief"
                :title="item.name"
                :thumb="item.picUrl"
                :price="item.retailPrice"
                :origin-price="item.counterPrice"
                @click="itemClick(item.id)" />
    </van-list>
    <is-empty v-if="isEmpty">抱歉,没有找到符合条件商品</is-empty>
  </div>
</template>
<script>
import { Card, Search, Tag, List } from 'vant';
import { goodsList } from '@/api/api';
import IsEmpty from '@/components/is-empty/';
export default {
  data() {
    return {
      keyword: '',
      focusStatus: true,
      wordHistory: [],
      list: [],
      page: 1,
      limit: 10,
      pages: 0,
      loading: false,
      finished: false,
      isEmpty: false
    };
  },
  methods: {
    enterSearch() {
      this.reset();
      this.searchGoods();
    },
    clickSearch(word) {
      this.keyword = word.trim();
      this.reset();
      this.searchGoods();
    },
    reset() {
      this.list = [];
      this.page = 1;
      this.limit = 10;
      this.total = 0;
      this.loading = false;
      this.finished = false;
      this.isEmpty = false;
    },
    pushHistoryTolocal(keyword) {
      const wordHistory = this.wordHistory;
      const historyKeyWord = this.getKeyWordHistory();
      if (!!keyword.trim() && historyKeyWord.indexOf(keyword) < 0) {
        wordHistory.push(keyword);
        window.localStorage.setItem('keyword', wordHistory.join('|'));
      }
    },
    getKeyWordHistory() {
      const listWord = window.localStorage.getItem('keyword');
      return listWord ? listWord.split('|') : [];
    },
    clearHistory() {
      this.$dialog
        .confirm({
          message: '是否清空历史记录'
        })
        .then(() => {
          window.localStorage.setItem('keyword', '');
          this.wordHistory = [];
        });
    },
    disabledSubmit() {
      return false;
    },
    searchGoods() {
      goodsList({
        keyword: this.keyword,
        page: this.page,
        limit: this.limit,
        categoryId: 0
      }).then(res => {
        var data = res.data.data;
        this.list.push(...data.list);
        this.page = data.page;
        this.limit = data.limit;
        this.pages = data.pages;
      });
    },
    async loadMore() {
      this.loading = false;
      this.page += 1;
      await this.searchGoods();
      this.loading = false;
      if (this.pages <= this.page) {
        this.finished = true;
      }
    },
    itemClick(id) {
      this.$router.push(`/items/detail/${id}`);
    }
  },
  activated() {
    this.wordHistory = this.getKeyWordHistory();
  },
  components: {
    [Search.name]: Search,
    [Card.name]: Card,
    [Tag.name]: Tag,
    [List.name]: List,
    [IsEmpty.name]: IsEmpty
  }
};
</script>
<style lang="scss" scoped>
.item_search {
  background-color: #fff;
}
.item_search_content {
  padding: 15px 10px 0;
}
.item_search_text {
  font-size: $font-size-normal;
  color: $font-color-gray;
  margin-bottom: 20px;
}
.item_search_history > span {
  margin-right: 10px;
  margin-bottom: 10px;
}
</style>

+ 198 - 0
litemall-vue/src/views/items/tabbar-catalog.vue

@ -0,0 +1,198 @@
<template>
  <div class="tab_class">
    <div class="tal_class_searchBox">
      <van-search placeholder="点击前往搜索"/>
      <div class="tal_class_searchMask" @click="$router.push({ name: 'search' })"></div>
    </div>
    <div class="class_tree clearfix">
    <ul class="class_tree_nav">
      <li
        v-for="(item, index) in categoryList"
        :key="index"
        :class="{active_nav: currentCategory.id == item.id}"
        @click="changeCatalog(item.id)"
      >{{item.name}}</li>
    </ul>
    <div class="class_tree_content">
      <div class="class_tree_all">
        <img style="width:250px" v-lazy="currentCategory.picUrl">
      </div>
      <div class="box">
        <span>{{currentCategory.desc}}</span>
      </div>
      <div class="class_tree_items_wrap clearfix">
        <div @click="toItemList(item.id)" :key="i" v-for="(item, i) in currentSubCategoryList">
          <div class="class_tree_item_img">
            <img :src="item.picUrl" :alt="item.name">
          </div>
          <div class="class_tree_item_name">{{item.name}}</div>
        </div>
      </div>
    </div>
  </div>
  </div>
</template>
<script>
import { catalogList, catalogCurrent } from '@/api/api';
import { Search } from 'vant';
export default {
  data() {
    return {
      categoryList: [],
      currentCategory: {},
      currentSubCategoryList: []
    };
  },
  created() {
    this.initData();
  },
  methods: {
    initData() {
      catalogList().then(res => {
        let data = res.data.data;
        this.categoryList = data.categoryList;
        this.currentCategory = res.data.data.currentCategory;
        this.currentSubCategoryList = data.currentSubCategory;
      });
    },
    changeCatalog(id) {
      catalogCurrent({ id: id}).then(res => {
        let data = res.data.data;
        this.currentCategory = data.currentCategory;        
        this.currentSubCategoryList = data.currentSubCategory;
      });
    },
    toItemList(id) {
      this.$router.push({
        name: 'category',
        query: { keyword: '', itemClass: id }
      });
    }
  },
  components: {
    [Search.name]: Search
  }
};
</script>
<style lang="scss" scoped>
@import '../../assets/scss/mixin';
.tab_class {
  overflow: hidden;
  background-color: #fff;
}
.height-fix {
  padding-bottom: 42px;
}
.tal_class_searchBox {
  position: relative;
}
.tal_class_searchMask {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 9;
}
.box {
  width: 250px;
  height: 20px;
  text-align: center;
  font-family: PingFangSC-Light, helvetica, 'Heiti SC';
  font-size: 13px;
  position: absolute;
  top: 95px;
}
.box span {
  line-height: 20px;
}
.class_tree {
  position: relative;
  background-color: #fff;
  overflow-x: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
  height: 100%;
  box-sizing: border-box;
}
.class_tree_nav {
  float: left;
  width: 100px;
  height: 100%;
  background-color: #fff;
  overflow: scroll;
  > li {
    @include one-border;
    height: 40px;
    line-height: 40px;
    text-align: center;
    border-left: 2px solid $bg-color;
  }
  > li.active_nav {
    background-color: #fff;
    border-left: 2px solid $red;
    color: $red;
  }  
}
.class_tree_content {
  margin-left: 100px;
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  .class_tree_all {
    text-align: right;
    padding-right: 10px;
    height: 40px;
    line-height: 40px;
    color: $font-color-gray;
    font-size: $font-size-small;
  }
  .van-icon-arrow {
    font-size: $font-size-small;
  }
  .class_tree_items_wrap {
    padding: 10px 20px;
    margin-right: -3%;
    margin-top: 70px;
    text-align: center;
    > div {
      float: left;
      padding-right: 3%;
      box-sizing: border-box;
      width: 33.333%;
      margin-bottom: 20px;
    }
    img {
      max-width: 100%;
    }
    .class_tree_item_img {
      display: inline-block;
      max-width: 100%;
      width: 70px;
      height: 70px;
    }
    .class_tree_item_name {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
}
</style>

+ 141 - 0
litemall-vue/src/views/items/topic-list/index.vue

@ -0,0 +1,141 @@
<template>
  <div class="goods_topic_list">
    <van-list v-model="loading"
              :finished="finished"
              :immediate-check="false"
              finished-text="没有更多了"
              @load="getTopicList">
      <div class="topic-info"
           v-for="(topic, index) in list"
           :key="index"
           @click="itemClick(topic.id)">
        <div class="name">
          <img class="img"
               :src="topic.picUrl"
               background-size="cover" />
          <div class="info-box">
            <div class="txt">{{topic.title}}</div>
            <div class="line"></div>
            <div class="price">阅读次数:{{topic.readCount}}</div>
          </div>
        </div>
        <div class="desc">
          {{topic.subtitle}}
        </div>
      </div>
    </van-list>
  </div>
</template>
<script>
import { topicList } from '@/api/api';
import { List } from 'vant';
export default {
  data() {
    return {
      list: [],
      page: 0,
      limit: 10,
      loading: false,
      finished: false
    };
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      this.page = 0;
      this.list = [];
      this.getTopicList();
    },
    getTopicList() {
      this.page++;
      topicList({
        page: this.page,
        limit: this.limit
      }).then(res => {
        this.list.push(...res.data.data.list);
        this.loading = false;
        this.finished = res.data.data.page >= res.data.data.pages;
      });
    },
    itemClick(id) {
      this.$router.push(`/items/topic/${id}`);
    }
  },
  components: {
    [List.name]: List
  }
};
</script>
<style lang="scss" scoped>
.goods_topic_list {
  .topic-info {
    .name {
      width: 100%;
      height: 180px;
      position: relative;
      .img {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 180px;
      }
      .info-box {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 180px;
        text-align: center;
        display: flex;
        justify-content: center;
        align-items: center;
        display: block;
        .txt {
          margin-top: 60px;
          height: 25px;
          font-size: 25px;
          color: #fff;
        }
        .line {
          margin: 0 auto;
          margin-top: 16px;
          display: block;
          height: 2px;
          width: 300px;
          background: #fff;
        }
        .price{
          height: 25px;
          font-size: 25px;
          color: #fff;
        }
      }
    }
    .desc {
      background: #fff;
      width: 100%;
      height: auto;
      overflow: hidden;
      padding: 25px 20px;
      font-size: 20px;
      color: #666;
      line-height: 20px;
      text-align: center;
    }
  }
}
</style>

+ 87 - 0
litemall-vue/src/views/items/topic/index.vue

@ -0,0 +1,87 @@
<template>
  <div class="goods_topic">
    <div class="topic-detail"
         v-html="topic.content">
    </div>
    <van-row gutter>
      <van-col span="12"
               v-for="(goods ,index) in topicGoods"
               :key="index">
        <router-link :to="{ path: `/items/detail/${goods.id}`}">
          <img :src="goods.picUrl"
               style="width:150px;height:150px;">
        </router-link>
        <div style="margin-left: 20px; rgb(123, 116, 116);">{{goods.name}}</div>
        <div style="margin-left: 20px; color:#ab956d">¥ {{goods.retailPrice}}</div>
      </van-col>
    </van-row>
  </div>
</template>
<script>
import { topicDetail, topicRelated } from '@/api/api';
import { Card, Row, Col } from 'vant';
export default {
  props: {
    topicId: [String, Number]
  },
  data() {
    return {
      topic: {},
      topicGoods: [],
      topicRelated: []
    };
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      topicDetail({
        id: this.topicId
      }).then(res => {
        this.topic = res.data.data.topic;
        this.topicGoods = res.data.data.goods;
      });
      topicRelated({
        id: this.topicId
      }).then(res => {
        this.topicRelated = res.data.data;
      });
    },
    itemClick(id) {
      this.$router.push(`/items/detail/${id}`);
    }
  },
  components: {
    [Card.name]: Card,
    [Row.name]: Row,
    [Col.name]: Col
  }
};
</script>
<style lang="scss" scoped>
.goods_topic {
  .topic-detail {
    ::v-deep p {
      padding: 0 10px;
      margin-block-start: 0 !important;
      margin-block-end: 0 !important;
    }
    ::v-deep img {
      max-width: 100%;
      width: 100% !important;
      height: 100% !important;
      display: block;
    }
  }
}
</style>

+ 57 - 0
litemall-vue/src/views/login/forget-reset/index.vue

@ -0,0 +1,57 @@
<template>
	<md-field-group class="foget_view">
		<md-field
			v-model="password"
			icon="lock"
			:is-error="isErrow"
			placeholder="请输入新密码"/>
		<md-field
			v-model="passwordRepeat"
			type="password"
			icon="lock"
			:is-error="isErrow"
			placeholder="请再次输入密码" />
		<div class="red" v-show="isErrow">两次密码输入不一致</div>
		<div class="foget_submit">
			<van-button size="large" type="danger" @click="submitCode">重置</van-button>
		</div>
	</md-field-group>
</template>
<script>
import field from '@/components/field/';
import fieldGroup from '@/components/field-group/';
export default {
  data() {
    return {
      isErrow: true,
      password: '',
      passwordRepeat: ''
    };
  },
  methods: {
    submitCode() {}
  },
  components: {
    [field.name]: field,
    [fieldGroup.name]: fieldGroup
  }
};
</script>
<style lang="scss" scoped>
div.foget_view {
  background-color: #fff;
  padding-top: 30px;
}
div.foget_submit {
  padding-top: 30px;
  padding-bottom: 20px;
}
</style>

+ 81 - 0
litemall-vue/src/views/login/forget-status/index.vue

@ -0,0 +1,81 @@
<template>
	<div class="payment_status">
		<div class="status_top">
			<van-icon :name="statusIcon" :class="statusClass" />
			<div>{{statusText}}</div>
		</div>
		<div class="status_text"><span class="red">3秒</span>后返回到登录页, 您也可以<router-link to="/login" class="red">点此登录</router-link></div>
	</div>
</template>
<script>
export default {
  name: 'payment-status',
  props: {
    status: String
  },
  data() {
    return {
      isSuccess: true
    };
  },
  computed: {
    statusText() {
      return this.isSuccess ? '修改成功' : '修改失败';
    },
    statusIcon() {
      return this.isSuccess ? 'checked' : 'fail';
    },
    statusClass() {
      return this.isSuccess ? 'success_icon' : 'fail_icon';
    }
  },
  activated() {
    this.isSuccess = this.status === 'success';
  }
};
</script>
<style lang="scss" scopd>
.payment_status {
  padding-top: 30px;
  box-sizing: border-box;
  background-color: #fff;
  text-align: center;
}
.status_top {
  margin-bottom: 15px;
  i {
    margin-bottom: 5px;
  }
  > div {
    font-size: 18px;
  }
}
.status_text {
  color: $font-color-gray;
  margin-bottom: 50px;
}
.status_icon {
  font-size: 80px;
}
i.success_icon {
  @extend .status_icon;
  color: #06bf04;
}
i.fail_icon {
  @extend .status_icon;
  color: #f44;
}
</style>

+ 80 - 0
litemall-vue/src/views/login/forget/index.vue

@ -0,0 +1,80 @@
<template>
	<md-field-group class="foget_view">
		<md-field
			v-model="mobile"
			icon="mobile"
			placeholder="请输入手机号"/>
		<md-field
			v-model="code"
			icon="lock"
			placeholder="请输入短信验证码"
		>
			<div slot="rightIcon" @click="getCode" class="getCode red">
				<countdown v-if="counting" :time="60000" @end="countdownend">
				  <template slot-scope="props">{{ +props.seconds || 60 }}秒后获取</template>
				</countdown>
				<span v-else>获取验证码</span>
			</div>
		</md-field >
		<div class="foget_submit">
			<van-button size="large" type="danger" @click="submitCode">下一步</van-button>
		</div>
	</md-field-group>
</template>
<script>
import field from '@/components/field/';
import fieldGroup from '@/components/field-group/';
export default {
  data() {
    return {
      counting: false,
      mobile: '',
      code: ''
    };
  },
  methods: {
    submitCode() {
      this.$router.push({ name: 'forgetReset' });
    },
    getCode() {
      this.counting = true;
    },
    countdownend() {
      this.counting = false;
    }
  },
  components: {
    [field.name]: field,
    [fieldGroup.name]: fieldGroup
  }
};
</script>
<style lang="scss" scoped>
@import '../../../assets/scss/mixin';
div.foget_view {
  background-color: #fff;
  padding-top: 30px;
}
div.foget_submit {
  padding-top: 30px;
  padding-bottom: 20px;
}
.getCode {
  @include one-border(left);
  text-align: center;
}
.time_down {
  color: $red;
}
</style>

+ 184 - 0
litemall-vue/src/views/login/login.vue

@ -0,0 +1,184 @@
<template>
	<div class="login">
    	<div class="store_header">
		<div class="store_avatar">
			<img src="../../assets/images/avatar_default.png" alt="头像" width="55" height="55">
		</div>
		<div class="store_name">litemall-vue</div>
	</div>
    <md-field-group>
      <md-field
        v-model="account"
        icon="username"
        placeholder="请输入测试账号 user123"
        right-icon="clear-full"
        name="user"
        data-vv-as="帐号"
        @right-click="clearText"
      />
      <md-field
        v-model="password"
        icon="lock"
        placeholder="请输入测试密码 user123"
        :type="visiblePass ? 'text' : 'password'"
        :right-icon="visiblePass ? 'eye-open' : 'eye-close'"
        data-vv-as="密码"
        name="password"
        @right-click="visiblePass = !visiblePass"
      />
      <div class="clearfix">
        <div class="float-l">
          <router-link to="/login/registerGetCode">免费注册</router-link>
        </div>
        <div class="float-r">
          <router-link to="/login/forget">忘记密码</router-link>
        </div>
      </div>
      <van-button size="large" type="danger" :loading="isLogining" @click="loginSubmit">登录</van-button>
    </md-field-group>
      <div class="text-desc text-center bottom_positon">技术支持: litemall</div>
	</div>
</template>
<script>
import field from '@/components/field/';
import fieldGroup from '@/components/field-group/';
import { authLoginByAccount } from '@/api/api';
import { setLocalStorage } from '@/utils/local-storage';
import { emailReg, mobileReg } from '@/utils/validate';
import { Toast } from 'vant';
export default {
  name: 'login-request',
  components: {
    [field.name]: field,
    [fieldGroup.name]: fieldGroup,
    Toast
  },
  data() {
    return {
      account: '',
      password: '',
      visiblePass: false,
      isLogining: false,
      userInfo: {}
    };
  },
  methods: {
    clearText() {
      this.account = '';
    },
    validate() {
    },
    login() {
      let loginData = this.getLoginData();
      authLoginByAccount(loginData).then(res => {
        this.userInfo = res.data.data.userInfo;
        setLocalStorage({
          Authorization: res.data.data.token,
          avatar: this.userInfo.avatarUrl,
          nickName: this.userInfo.nickName
        });
        this.routerRedirect();
      }).catch(error => {
        Toast.fail(error.data.errmsg);
      });
    },
    loginSubmit() {
      this.isLogining = true;
      try {
        this.validate();
        this.login();
        this.isLogining = false;
      } catch (err) {
        console.log(err.message);
        this.isLogining = false;
      }
    },
    routerRedirect() {
      // const { query } = this.$route;
      // this.$router.replace({
      //   name: query.redirect || 'home',
      //   query: query
      // });
      window.location = '#/user/';
    },
    getLoginData() {
      const password = this.password;
      const account = this.getUserType(this.account);
      return {
        [account]: this.account,
        password: password
      };
    },
    getUserType(account) {
      const accountType = mobileReg.test(account)
        ? 'mobile'
        : emailReg.test(account)
        ? 'email'
        : 'username';
      return accountType;
    }
  }
};
</script>
<style lang="scss" scoped>
@import '../../assets/scss/mixin';
.login {
  position: relative;
  background-color: #fff;
}
.store_header {
  text-align: center;
  padding: 30px 0;
  .store_avatar img {
    border-radius: 50%;
  }
  .store_name {
    padding-top: 5px;
    font-size: 16px;
  }
}
.register {
  padding-top: 40px;
  color: $font-color-gray;
  a {
    color: $font-color-gray;
  }
  > div {
    width: 50%;
    box-sizing: border-box;
    padding: 0 20px;
  }
  .connect {
    @include one-border(right);
    text-align: right;
  }
}
.bottom_positon {
  position: absolute;
  bottom: 30px;
  width: 100%;
}
</style>

+ 74 - 0
litemall-vue/src/views/login/register-getCode/index.vue

@ -0,0 +1,74 @@
<template>
	<md-field-group class="register_view">
		<div>我们将发送验证码到您的手机</div>
		<md-field
			v-model="mobile"
			icon="mobile"
			placeholder="请输入手机号"/>
		<div class="register_submit">
			<van-button size="large" type="danger" @click="submitCode">下一步</van-button>
		</div>
		<div class="register_footer">
			已有账号?
			<router-link to="/login" class="red">登录</router-link>
		</div>
	</md-field-group>
</template>
<script>
import field from '@/components/field/';
import fieldGroup from '@/components/field-group/';
import { mobileReg } from '@/utils/validate';
export default {
  data() {
    return {
      mobile: ''
    };
  },
  methods: {
    submitCode() {
      if(this.mobile === ''){
        return
      }
      if(!mobileReg.test(this.mobile)){
        this.mobile = ''
        return
      }
    	try {
        this.$router.push({
          name: 'registerSubmit',
          params: { phone: this.mobile}
        });
      } catch (error) {
        console.log(error.message);
      }
    }
  },
  components: {
    [field.name]: field,
    [fieldGroup.name]: fieldGroup
  }
};
</script>
<style lang="scss" scoped>
div.register_view {
  background-color: #fff;
  padding-top: 30px;
}
div.register_submit {
  padding-top: 30px;
  padding-bottom: 20px;
}
.register_footer {
  text-align: right;
  color: $font-color-gray;
}
</style>

+ 105 - 0
litemall-vue/src/views/login/register-status/index.vue

@ -0,0 +1,105 @@
<template>
	<div class="payment_status">
		<div class="status_top">
			<van-icon :name="statusIcon" :class="statusClass" />
			<div>{{statusText}}</div>
		</div>
		<div class="status_text">
			<span class="red">
				<countdown v-if="counting" :time="3000" @end="countDownEnd">
					<template slot-scope="props">{{ +props.seconds || 3 }}</template>秒
				</countdown>
			</span>
			后返回到登录页, 您也可以
			<router-link to="/login" class="red">点此登录</router-link>
		</div>
	</div>
</template>
<script>
import field from '@/components/field/';
import fieldGroup from '@/components/field-group/';
export default {
	name: 'payment-status',
	props: {
		status: String
	},
	data() {
		return {
			counting: true,
			isSuccess: true
		};
	},
	methods:{
		countDownEnd() {
			this.counting = false;
			window.location = '#/login/';
		}
	},
	computed: {
		statusText() {
			return this.isSuccess ? '注册成功' : '注册失败';
		},
		statusIcon() {
			return this.isSuccess ? 'checked' : 'fail';
		},
		statusClass() {
			return this.isSuccess ? 'success_icon' : 'fail_icon';
		}
	},
	activated() {
		this.isSuccess = this.status === 'success';
	},
	components: {
		[field.name]: field,
		[fieldGroup.name]: fieldGroup
	}
};
</script>
<style lang="scss" scopd>
.payment_status {
  padding-top: 30px;
  box-sizing: border-box;
  background-color: #fff;
  text-align: center;
}
.status_top {
  margin-bottom: 15px;
  i {
    margin-bottom: 5px;
  }
  > div {
    font-size: 18px;
  }
}
.status_text {
  color: $font-color-gray;
  margin-bottom: 50px;
}
.status_icon {
  font-size: 80px;
}
i.success_icon {
  @extend .status_icon;
  color: #06bf04;
}
i.fail_icon {
  @extend .status_icon;
  color: #f44;
}
</style>

+ 133 - 0
litemall-vue/src/views/login/register-submit/index.vue

@ -0,0 +1,133 @@
<template>
	<md-field-group class="register_submit">
		<md-field v-model="code" icon="mobile" placeholder="请输入验证码">
			<div slot="rightIcon" @click="getCode" class="getCode red">
				<countdown v-if="counting" :time="60000" @end="countDownEnd">
				  <template slot-scope="props">{{ +props.seconds || 60 }}秒后获取</template>
				</countdown>
				<span v-else>获取验证码</span>
			</div>
		</md-field>
		<md-field v-model="username" icon="username" placeholder="请输入用户名"/>
		<md-field v-model="password" icon="lock" placeholder="请输入密码"/>
		<md-field v-model="repeatPassword" icon="lock" placeholder="请再次确认密码"/>
		<div class="register_submit_btn">
			<van-button type="danger" size="large" @click="registerSubmit">确定</van-button>
		</div>
	</md-field-group>
</template>
<script>
import field from '@/components/field/';
import fieldGroup from '@/components/field-group/';
import { authRegisterCaptcha } from '@/api/api';
import { authRegister } from '@/api/api';
import {Toast} from "vant";
export default {
	props: {
		phone: String
	},
	data() {
		return {
		  counting: true,
		  code: '',
		  username: '',
		  password: '',
		  repeatPassword: ''
		};
	},
	mounted:function(){
		this.getCode();
	},
  methods: {
    registerSubmit() {
      if(this.username === '' || this.code === ''){
        return
      }
      if(this.password === '' || this.repeatPassword === ''){
        return
      }
      if(this.password !== this.repeatPassword){
        this.password = ''
        this.repeatPassword = ''
        return
      }
      let data = this.getRegisterData();
      authRegister(data).then(res => {
        this.$router.push({
          name: 'registerStatus',
          params: { status: 'success' }
        });
      }).catch (error => {
        Toast.fail(error.data.errmsg);
        if (error.data.errno == 705) {
          window.location = '#/login/';
        }
      });
    },
    getCode() {
      this.counting = true;
      let data = {
        mobile: this.phone
      };
      authRegisterCaptcha(data).then(res => {
        this.counting = true;
      }).catch(error => {
        alert(error.data.errmsg);
        this.counting = true;
      });
    },
    getRegisterData() {
      const password = this.password;
      const code = this.code;
      const repeatPassword = this.repeatPassword;
      const mobile = this.phone;
      const username = this.username;
      return {
        code: code,
        username: username,
        password: password,
        repeatPassword: repeatPassword,
        mobile: mobile
      };
    },
    countDownEnd() {
      this.counting = false;
    }
  },
  components: {
    [field.name]: field,
    [fieldGroup.name]: fieldGroup
  }
};
</script>
<style lang="scss" scoped>
@import '../../../assets/scss/mixin';
.register_submit {
  padding-top: 40px;
  background-color: #fff;
}
.register_submit_btn {
  padding-top: 30px;
}
.getCode {
  @include one-border(left);
  text-align: center;
}
.time_down {
  color: $red;
}
</style>

+ 245 - 0
litemall-vue/src/views/order/checkout.vue

@ -0,0 +1,245 @@
<template>
<div class="order">
  <van-cell-group>
      <van-cell v-if="checkedAddress" isLink @click="goAddressList()" title="收货地址">
      <div slot="label">
        <div>
         <span>{{ checkedAddress.name }} </span>
         <span>{{ checkedAddress.tel }} </span>
      </div>
      <div>
        {{ checkedAddress.addressDetail }}
      </div>
      </div>
    </van-cell>
  </van-cell-group>
  <van-cell-group>
    <van-cell class="order-coupon" title="优惠券" is-link :value="getCouponValue()" @click="getCoupons" />
  </van-cell-group>
<!-- 优惠券列表 -->
<van-popup v-model="showList" position="bottom">
  <van-coupon-list
    :coupons="coupons"
    :chosen-coupon="chosenCoupon"
    :disabled-coupons="disabledCoupons"
    @change="onChange"
    @exchange="onExchange"
  />
</van-popup>
    <van-card
      v-for="item in checkedGoodsList"
      :key="item.id"
      :title="item.goodsName"
      :num="item.number"
      :price="item.price +'.00'"
      :thumb="item.picUrl"
    >
      <div slot="desc">
        <div class="van-card__desc">
          <van-tag plain style="margin-right:6px;" v-for="(spec, index) in item.specifications" :key="index">
            {{spec}}
          </van-tag>
        </div>
      </div>
    </van-card>
    <van-cell-group>
      <van-cell title="商品金额">
        <span class="red">{{goodsTotalPrice * 100 | yuan}}</span>
      </van-cell>
      <van-cell title="邮费">
        <span class="red">{{ freightPrice * 100| yuan}}</span>
      </van-cell>
      <van-cell title="优惠券">
        <span class="red">-{{ couponPrice * 100| yuan}}</span>
      </van-cell>
      <van-field v-model="message" placeholder="请输入备注" label="订单备注">
      <template slot="icon">{{message.length}}/50</template>
      </van-field>      
    </van-cell-group>
    <van-submit-bar
      :price="actualPrice*100"
      label="总计:"
      buttonText="提交订单"
      :disabled="isDisabled"
      @submit="onSubmit"
    />
</div>
</template>
<script>
import { Card, Tag, ard, Field, SubmitBar, Toast  } from 'vant';
import { CouponCell, CouponList, Popup } from 'vant';
import { cartCheckout, orderSubmit, couponSelectList} from '@/api/api';
import { getLocalStorage, setLocalStorage } from '@/utils/local-storage';
import dayjs from 'dayjs';
export default {
  data() {
    return {
      checkedGoodsList: [],
      checkedAddress: {},
      availableCouponLength: 0, // 可用的优惠券数量
      goodsTotalPrice: 0, //商品总价
      freightPrice: 0, //快递费
      couponPrice: 0, //优惠券的价格
      grouponPrice: 0, //团购优惠价格
      orderTotalPrice: 0, //订单总价
      actualPrice: 0, //实际需要支付的总价
      message: '',
      isDisabled: false,
      showList: false,
      chosenCoupon: -1,
      coupons: [],
      disabledCoupons: [] 
    };
  },
  created() {
    this.init();
  },
  methods: {
    onSubmit() {     
      const {AddressId, CartId, CouponId, UserCouponId} = getLocalStorage('AddressId', 'CartId', 'CouponId', 'UserCouponId');
      if (AddressId === null || AddressId === "0") {
        Toast.fail('请设置收货地址');
        return;
      }
      this.isDisabled = true;
      orderSubmit({
        addressId: AddressId,
        cartId: CartId,
        couponId: CouponId,
        userCouponId: UserCouponId,
        grouponLinkId: 0,
        grouponRulesId: 0,
        message: this.message
      }).then(res => {
        
        // 下单成功,重置下单参数。
        setLocalStorage({AddressId: 0, CartId: 0, CouponId: 0});
        let orderId = res.data.data.orderId;
        this.$router.push({
          name: 'payment',
          params: { orderId: orderId }
        });
      }).catch(error => {
        this.isDisabled = false;
        this.$toast("下单失败");
      })
    },
    goAddressList() {
      this.$router.push({
        path: '/user/address'
      });
    },
    getCouponValue() {
      if(this.couponPrice !== 0 ){
        return "-¥" + this.couponPrice + ".00元"
      }
      if(this.availableCouponLength !== 0){
        return this.availableCouponLength + "张可用"
      }
      return '没有可用优惠券'
    },
    getCoupons() {
      const {AddressId, CartId, CouponId} = getLocalStorage('AddressId', 'CartId', 'CouponId');
      couponSelectList({cartId: CartId, grouponRulesId: 0}).then(res => {
        var cList = res.data.data.list;
        this.coupons = []
        this.disabledCoupons = [];
        for(var i = 0; i < cList.length; i++){
          var c = cList[i]
          var coupon = {
            id: c.id,
            cid: c.cid,
            name: c.name,
            condition: '满' + c.min + '元可用',
            value: c.discount * 100,
            description: c.desc,
            startAt: new Date(c.startTime).getTime()/1000,
            endAt: new Date(c.endTime).getTime()/1000,
            valueDesc: c.discount,
            unitDesc: '元'            
          }
          if (c.available) {
            this.coupons.push(coupon);
          } else {
            this.disabledCoupons.push(coupon);
          }
        }
        
        this.showList = true
      })
    },
    init() {
      const {AddressId, CartId, CouponId, UserCouponId} = getLocalStorage('AddressId', 'CartId', 'CouponId', 'UserCouponId');
      cartCheckout({cartId: CartId, addressId: AddressId, couponId: CouponId, userCouponId: UserCouponId, grouponRulesId: 0}).then(res => {
          var data = res.data.data
          this.checkedGoodsList = data.checkedGoodsList;
          this.checkedAddress= data.checkedAddress;
          this.availableCouponLength= data.availableCouponLength;
          this.actualPrice= data.actualPrice;
          this.couponPrice= data.couponPrice;
          this.grouponPrice= data.grouponPrice;
          this.freightPrice= data.freightPrice;
          this.goodsTotalPrice= data.goodsTotalPrice;
          this.orderTotalPrice= data.orderTotalPrice;
          setLocalStorage({AddressId: data.addressId, CartId: data.cartId, CouponId: data.couponId, UserCouponId: data.userCouponId});
      });
    },
    onChange(index) {
      this.showList = false;
      this.chosenCoupon = index;
      
      if(index === -1 ){
        setLocalStorage({CouponId: -1, UserCouponId: -1});
      }
      else{
        const couponId = this.coupons[index].cid;
        const userCouponId = this.coupons[index].id;
        setLocalStorage({CouponId: couponId, UserCouponId: userCouponId});
      }
      this.init()
    },
    onExchange() {
      this.$toast("兑换暂不支持");
    }    
  },
  components: {
    [Toast.name]: Toast ,
    [SubmitBar.name]: SubmitBar,
    [Card.name]: Card,
    [Field.name]: Field,
    [Tag.name]: Tag,
    [CouponCell.name]: CouponCell,
    [CouponList.name]: CouponList,
    [Popup.name]: Popup
  }
};
</script>
<style lang="scss" scoped>
.order-coupon {
  margin-top: 10px;
}
</style>

+ 196 - 0
litemall-vue/src/views/order/order-detail/index.vue

@ -0,0 +1,196 @@
<template>
  <div class="order_detail">
    <div class="order-goods">
      <van-card v-for="item in orderGoods"
                :key="item.id"
                :title="item.goodsName"
                desc="暂无描述"
                :num="item.number"
                :price="item.price +'.00'"
                :thumb="item.picUrl"></van-card>
      <van-cell-group>
        <van-cell title="商品金额">
          <span class="red">{{orderInfo.goodsPrice * 100 | yuan}}</span>
        </van-cell>
        <van-cell title="快递费用">
          <span class="red">{{orderInfo.freightPrice * 100 | yuan}}</span>
        </van-cell>
      </van-cell-group>
    </div>
    <van-cell-group style="margin-top: 20px;">
      <van-cell icon="dingwei"
                :title="`${orderInfo.consignee}  ${orderInfo.mobile}`"
                :label="orderInfo.address" />
    </van-cell-group>
    <van-cell-group style="margin-top: 20px;">
      <van-cell title="下单时间">
        <span>{{orderInfo.addTime }}</span>
      </van-cell>
      <van-cell title="订单编号">
        <span>{{orderInfo.orderSn }}</span>
      </van-cell>
      <van-cell title="订单备注">
        <span>{{orderInfo.remark }}</span>
      </van-cell>
      <van-cell title="实付款:">
        <span class="red">{{orderInfo.actualPrice * 100 | yuan}}</span>
      </van-cell>
      <!-- 订单动作 -->
      <van-cell>
        <van-button size="small"
                    v-if="handleOption.cancel"
                    @click="cancelOrder(orderInfo.id)"
                    style=" float:right"
                    round
                    type="danger">取消订单</van-button>
        <van-button size="small"
                    v-if="handleOption.pay"
                    @click="payOrder(orderInfo.id)"
                    style=" float:right"
                    round
                    type="danger">去支付</van-button>
        <van-button size="small"
                    v-if="handleOption.delete"
                    @click="deleteOrder(orderInfo.id)"
                    style=" float:right"
                    type="danger">删除订单</van-button>
        <van-button size="small"
                    v-if="handleOption.confirm"
                    @click="confirmOrder(orderInfo.id)"
                    style=" float:right"
                    type="danger">确认收货</van-button>
        <van-button size="small"
                    v-if="handleOption.refund"
                    @click="refundOrder(orderInfo.id)"
                    style=" float:right"
                    type="danger">退款</van-button>
      </van-cell>
    </van-cell-group>
    <van-cell-group v-if="showExp()"
                    style="margin-top: 20px;">
      <van-cell title="快递公司">
        <span>{{orderInfo.expCode }}</span>
      </van-cell>
      <van-cell title="快递编号">
        <span>{{orderInfo.expNo }}</span>
      </van-cell>
    </van-cell-group>
  </div>
</template>
<script>
import { Card, Field, SubmitBar, Button, Cell, CellGroup, Dialog } from 'vant';
import _ from 'lodash';
import {
  orderDetail,
  orderDelete,
  orderConfirm,
  orderCancel,
  orderRefund
} from '@/api/api';
export default {
  data() {
    return {
      isSubmit: false,
      isDisabled: false,
      orderInfo: {},
      orderGoods: [],
      handleOption: {},
      expressInfo: {}
    };
  },
  created() {
    this.init();
  },
  methods: {
    showExp() {
      return _.has(this.orderInfo, 'expNo');
    },
    deleteOrder(id) {
      let that = this;
      this.$dialog
        .confirm({ message: '确定要删除该订单吗?' })
        .then(() => {
          orderDelete({ orderId: id }).then(() => {
            this.$toast('已删除订单');
            this.$router.go(-1);
          });
        })
        .catch(() => {});
    },
    cancelOrder(id) {
      this.$dialog
        .confirm({ message: '确定要取消该订单吗?' })
        .then(() => {
          orderDelete({ orderId: id }).then(() => {
            this.init();
            this.$toast('已取消该订单');
          });
        })
        .catch(() => {});
    },
    confirmOrder(id) {
      this.$dialog
        .confirm({
          message: '请确认收到货物, 确认收货后无法撤销!'
        })
        .then(() => {
          orderConfirm({ orderId: id }).then(() => {
            this.init();
            this.$toast('已确认收货');
          });
        })
        .catch(() => {});
    },
    refundOrder(id) {
      this.$dialog
        .confirm({ message: '确定要申请退款吗?' })
        .then(() => {
          orderRefund({ orderId: id }).then(() => {
            this.init();
            this.$toast('已申请订单退款');
          });
        })
        .catch(() => {});
    },
    commentOrder(id) {},
    toPay(id) {
      this.$router.push({ name: 'payment', params: { orderId: id } });
    },
    init() {
      let orderId = this.$route.query.orderId;
      orderDetail({ orderId: orderId }).then(res => {
        var data = res.data.data;
        this.orderInfo = data.orderInfo;
        this.orderGoods = data.orderGoods;
        this.handleOption = data.orderInfo.handleOption;
        this.expressInfo = data.expressInfo;
      });
    }
  },
  components: {
    [Dialog.name]: Dialog,
    [CellGroup.name]: CellGroup,
    [Cell.name]: Cell,
    [Button.name]: Button,
    [SubmitBar.name]: SubmitBar,
    [Card.name]: Card,
    [Field.name]: Field
  }
};
</script>
<style lang="scss" scoped>
.order_detail {
  padding-bottom: 70px;
}
</style>

+ 94 - 0
litemall-vue/src/views/order/payment-status/index.vue

@ -0,0 +1,94 @@
<template>
  <div class="payment_status">
    <div class="status_top">
      <van-icon :name="statusIcon" :class="statusClass"/>
      <div>{{statusText}}</div>
    </div>
    <div class="status_text" v-if="isSuccess">
      <span class="red">3秒</span>跳转订单
    </div>
    <div class="status_text" v-else>系统繁忙, 支付遇到问题, 请您稍后再试!</div>
    <div class="status_goLink">
      <router-link class="red" :to="{name: 'user'}">查看订单
        <van-icon name="arrow"/>
      </router-link>
    </div>
  </div>
</template>
<script>
export default {
  name: 'payment-status',
  props: {
    status: String
  },
  created() {
    setTimeout(() => {
      this.$router.push({ path: '/user/order/list/0' });
    }, 3000);
  },
  data() {
    return {
      isSuccess: true
    };
  },
  computed: {
    statusText() {
      return this.isSuccess ? '支付成功' : '支付失败';
    },
    statusIcon() {
      return this.isSuccess ? 'checked' : 'fail';
    },
    statusClass() {
      return this.isSuccess ? 'success_icon' : 'fail_icon';
    }
  },
  activated() {
    this.isSuccess = this.status === 'success';
  }
};
</script>
<style lang="scss" scopd>
.payment_status {
  padding-top: 30px;
  box-sizing: border-box;
  background-color: #fff;
  text-align: center;
}
.status_top {
  margin-bottom: 15px;
  i {
    margin-bottom: 5px;
  }
  > div {
    font-size: 18px;
  }
}
.status_text {
  color: $font-color-gray;
  margin-bottom: 50px;
}
.status_icon {
  font-size: 80px;
}
i.success_icon {
  @extend .status_icon;
  color: #06bf04;
}
i.fail_icon {
  @extend .status_icon;
  color: #f44;
}
</style>

+ 214 - 0
litemall-vue/src/views/order/payment/index.vue

@ -0,0 +1,214 @@
<template>
  <div class="payment">
    <div class="time_down payment_group">
      请在
      <span class="red">半小时内</span>
      完成付款,否则系统自动取消订单
    </div>
    <van-cell-group class="payment_group">
      <van-cell title="订单编号" :value="order.orderInfo.orderSn"/>
      <van-cell title="实付金额">
        <span class="red">{{order.orderInfo.actualPrice *100 | yuan}}</span>
      </van-cell>
    </van-cell-group>
    <div class="pay_way_group">
      <div class="pay_way_title">选择支付方式</div>
      <van-radio-group v-model="payWay">
        <van-cell-group>
          <van-cell>
            <template slot="title">
              <img src="../../../assets/images/ali_pay.png" alt="支付宝" width="82" height="29">
            </template>
            <van-radio name="ali"/>
          </van-cell>
          <van-cell>
            <template slot="title">
              <img src="../../../assets/images/wx_pay.png" alt="微信支付" width="113" height="23">
            </template>            
            <van-radio name="wx"/>
          </van-cell>
        </van-cell-group>
      </van-radio-group>
    </div>
    <van-button class="pay_submit" @click="pay" type="primary" bottomAction>去支付</van-button>
  </div>
</template>
<script>
import { Radio, RadioGroup, Dialog } from 'vant';
import { orderDetail, orderPrepay, orderH5pay } from '@/api/api';
import _ from 'lodash';
import { getLocalStorage, setLocalStorage } from '@/utils/local-storage';
export default {
  name: 'payment',
  data() {
    return {
      payWay: 'wx',
      order: {
        orderInfo: {},
        orderGoods: []
      },
      orderId: 0
    };
  },
  created() {
    if (_.has(this.$route.params, 'orderId')) {
      this.orderId = this.$route.params.orderId;
      this.getOrder(this.orderId);
    }
  },
  methods: {
    getOrder(orderId) {
      orderDetail({orderId: orderId}).then(res => {
        this.order = res.data.data;
      });
    },
    pay() {
      
      Dialog.alert({
        message: '你选择了' + (this.payWay === 'wx' ? '微信支付' : '支付宝支付')
      }).then(() => {
        if (this.payWay === 'wx') {
          let ua = navigator.userAgent.toLowerCase();
          let isWeixin = ua.indexOf('micromessenger') != -1;
          if (isWeixin) {
            orderPrepay({ orderId: this.orderId })
              .then(res => {
                let data = res.data.data;
                let prepay_data = JSON.stringify({
                  appId: data.appId,
                  timeStamp: data.timeStamp,
                  nonceStr: data.nonceStr,
                  package: data.packageValue,
                  signType: 'MD5',
                  paySign: data.paySign
                });
                setLocalStorage({ prepay_data: prepay_data });
                if (typeof WeixinJSBridge == 'undefined') {
                  if (document.addEventListener) {
                    document.addEventListener(
                      'WeixinJSBridgeReady',
                      this.onBridgeReady,
                      false
                    );
                  } else if (document.attachEvent) {
                    document.attachEvent(
                      'WeixinJSBridgeReady',
                      this.onBridgeReady
                    );
                    document.attachEvent(
                      'onWeixinJSBridgeReady',
                      this.onBridgeReady
                    );
                  }
                } else {
                  this.onBridgeReady();
                }
              })
              .catch(err => {
                Dialog.alert({ message: err.data.errmsg });
                that.$router.replace({
                  name: 'paymentStatus',
                  params: {
                    status: 'failed'
                  }
                });
              });
          } else {
            orderH5pay({ orderId: this.orderId })
              .then(res => {
                let data = res.data.data;
                window.location.replace(
                  data.mwebUrl +
                  '&redirect_url=' +
                  encodeURIComponent(
                    window.location.origin +
                    '/#/?orderId=' +
                    this.orderId +
                    '&tip=yes'
                  )
                );
              })
              .catch(err => {
                Dialog.alert({ message: err.data.errmsg });
              });
          }
        } else {
          //todo : alipay
        }
      });
    },
    onBridgeReady() {
      let that = this;
      let data = getLocalStorage('prepay_data');
      // eslint-disable-next-line no-undef
      WeixinJSBridge.invoke(
        'getBrandWCPayRequest',
        JSON.parse(data.prepay_data),
        function(res) {
          if (res.err_msg == 'get_brand_wcpay_request:ok') {
            that.$router.replace({
              name: 'paymentStatus',
              params: {
                status: 'success'
              }
            });
          } else if (res.err_msg == 'get_brand_wcpay_request:cancel') {
            that.$router.replace({
              name: 'paymentStatus',
              params: {
                status: 'cancel'
              }
            });
          } else {
            that.$router.replace({
              name: 'paymentStatus',
              params: {
                status: 'failed'
              }
            });
          }
        }
      );
    }
  },
  components: {
    [Radio.name]: Radio,
    [RadioGroup.name]: RadioGroup,
    [Dialog.name]: Dialog
  }
};
</script>
<style lang="scss" scoped>
.payment_group {
  margin-bottom: 10px;
}
.time_down {
  background-color: #fffeec;
  padding: 10px 15px;
}
.pay_submit {
  position: fixed;
  bottom: 0;
  width: 100%;
}
.pay_way_group img {
  vertical-align: middle;
}
.pay_way_title {
  padding: 15px;
  background-color: #fff;
}
</style>

+ 263 - 0
litemall-vue/src/views/order/tabbar-cart.vue

@ -0,0 +1,263 @@
<template>
  <div class="tab-cart">
    <div class="editor_head" v-show="goods.length">
      <van-icon :name="isEditor ? 'success' : 'editor'"/>
      <span @click="isEditor = !isEditor">{{isEditor ? '完成' : '编辑'}}</span>
    </div>
    <van-checkbox-group @change="toggle" class="card-goods" v-model="checkedGoods">
      <div v-for="(item, i) in goods" :key="i" class="card-goods__item">
        <van-checkbox :key="item.id" :name="item.id" v-model="item.checked"></van-checkbox>
        <van-card :title="item.goodsName" :price="item.price" :num="item.number" :thumb="item.picUrl">
          <div slot="desc">
            <div class="van-card__desc">
              <van-tag plain style="margin-right:6px;" v-for="(spec, index) in item.specifications" :key="index">
                {{spec}}
              </van-tag>
            </div>
          </div>
          <div slot="footer" v-if="isEditor">
            <van-stepper v-model="item.number" @change="stepperEvent(item,arguments)" disableInput/>
          </div>
          <div slot="footer" v-else>添加日期 {{item.addTime}}</div>
        </van-card>
        <div class="cart_delete" v-if="isEditor" @click="deleteCart(i)">删除</div>
      </div>
    </van-checkbox-group>
    <is-empty v-if="!goods.length">您的购物车空空如也~</is-empty>
    <van-submit-bar
      style="bottom: 50px"
      :price="totalPrice"
      :disabled="!checkedGoods.length"
      :buttonText="submitBarText"
      :loading="isSubmit"
      label="总计"
      @submit="cartSubmit"
    >
      <van-checkbox v-model="checkedAll" @click="setCheckAll" style="padding: 0 10px;">全选</van-checkbox>
    </van-submit-bar>
  </div>
</template>
<script>
import { Checkbox, CheckboxGroup, Card, SubmitBar, Stepper, Tag } from 'vant';
import { cartList, cartUpdate, cartChecked, cartDelete} from '@/api/api';
import { setLocalStorage } from '@/utils/local-storage';
import isEmpty from '@/components/is-empty/';
import _ from 'lodash';
export default {
  data() {
    return {
      isEditor: false,
      checkedAll: false,
      isSubmit: false,
      checkedGoods: [],
      allGoods: [],
      goods: []
    };
  },
  created() {
    this.init();
  },
  computed: {
    submitBarText() {
      return this.isEditor ? '删除' : '结算';
    },
    totalPrice() {
      return this.goods.reduce(
        (total, item) =>
          total +
          (this.checkedGoods.indexOf(item.id) !== -1
            ? item.price * item.number * 100
            : 0),
        0
      );
    }
  },
  methods: {
    stepperEvent(item, arg) {
      let number = arg[0];
      cartUpdate({
        number: number,
        goodsId: item.goodsId,
        id: item.id,
        productId: item.productId
      });
    },
    init() {
      cartList().then(res => {
        this.goods = res.data.data.cartList;
        this.allGoods = this.getAllList();
        this.checkedGoods = this.getCheckedList(this.goods);
      });
    },
    getAllList() {
      let result = [];
      _.each(this.goods, v => {
        result.push(v.id);
      });
      return result;
    },
    getCheckedList(goods) {
      let result = [];
      _.each(goods, v => {
        if (v.checked) {
          result.push(v.id);
        }
      });
      return result;
    },
    cartSubmit(data) {
      let productIds = [];
      let checkedGoods = this.checkedGoods;
      _.each(checkedGoods, id => {
        productIds.push(
          _.find(this.goods, vv => {
            return id === vv.id;
          }).productId
        );
      });
      if (this.isEditor) {
        this.$dialog
          .confirm({
            message: '确定删除所选商品吗?',
            cancelButtonText: '再想想'
          })
          .then(() => {
            this.deleteNext(productIds);
          });
      } else {
        this.isSubmit = true;
        setLocalStorage({AddressId: 0, CartId: 0, CouponId: 0});
        this.$router.push('/order/checkout');
      }
    },
    setCheckAll(val) {
      if (this.checkedGoods.length === this.allGoods.length) {
        this.checkedGoods = [];
      } else {
        this.checkedGoods = this.allGoods;
      }
    },
    deleteCart(o) {
      let productId = this.goods[o].productId;
      this.$dialog
        .confirm({ message: '确定删除所选商品吗', cancelButtonText: '再想想' })
        .then(() => {
          this.$nextTick(() => {
            this.deleteNext(productId);
          });
        });
    },
    toggle(index) {
      let addProductIds = [];
      _.each(index, v => {
        let productId = _.find(this.goods, result => {
          return result.id === v;
        }).productId;
        addProductIds.push(productId);
      });
      let delProductIds = [];
      _.each(_.difference(this.allGoods, index), v => {
        let productId = _.find(this.goods, result => {
          return result.id === v;
        }).productId;
        delProductIds.push(productId);
      });
      if (delProductIds.length > 0) {
        cartChecked({productIds: delProductIds, isChecked: 0});
      }
      if (addProductIds.length > 0) {
        cartChecked({productIds: addProductIds, isChecked: 1});
      }
      if(index.length === this.allGoods.length){
        this.checkedAll = true
      }
      else{
        this.checkedAll = false
      }
    },
    deleteNext(o) {
      let productIds = [];
      if (o instanceof Array) {
        productIds = o;
      } else {
        productIds.push(o);
      }
      cartDelete({productIds: productIds}).then(res => {
        this.goods = res.data.data.cartList;
        this.allGoods = this.getAllList();
        this.checkedGoods = this.getCheckedList(this.goods);
      });
    }
  },
  components: {
    [Card.name]: Card,
    [Tag.name]: Tag,
    [Stepper.name]: Stepper,
    [isEmpty.name]: isEmpty,
    [Checkbox.name]: Checkbox,
    [SubmitBar.name]: SubmitBar,
    [CheckboxGroup.name]: CheckboxGroup
  }
};
</script>
<style lang="scss" scoped>
@import '../../assets/scss/mixin';
.tab-cart {
  padding-bottom: 50px;
  box-sizing: border-box;
}
.editor_head {
  @include one-border;
  text-align: right;
  padding: 10px;
  font-size: $font-size-normal;
  background-color: #fff;
}
.card-goods {
  background-color: $bg-color;
  .card-goods__item {
    display: flex;
    align-items: center;
    margin-bottom: 10px;
    background-color: #fff;
  }
  .cart_delete {
    line-height: 100px;
    padding: 0 10px;
    color: #fff;
    background-color: $red;
  }
  .card-goods__footer {
    font-size: $font-size-normal;
    color: $font-color-gray;
  }
}
.clear_invalid {
  width: 120px;
  color: $font-color-gray;
  border: 1px solid $font-color-gray;
  margin: 0 auto;
  text-align: center;
  padding: 5px 3px;
  margin-top: 20px;
  border-radius: 3px;
}
</style>

+ 196 - 0
litemall-vue/src/views/user/coupon-list/index.vue

@ -0,0 +1,196 @@
<template>
  <div class="coupon_list">
    <van-tabs v-model="activeIndex"
              type="card"
              sticky
              @click="handleTabClick">
      <van-tab v-for="(tabTitle, tabIndex) in tabTitles"
               :title="tabTitle"
               :key="tabIndex">
        <van-list v-model="loading"
                  :finished="finished"
                  :immediate-check="false"
                  finished-text="没有更多了"
                  @load="getCouponList">
          <van-panel style=" padding-bottom: 10px;">
            <div class="van-coupon-item"
                 v-for="(coupon,index) in couponList"
                 :key="index">
              <div class="van-coupon-item__content">
                <div class="van-coupon-item__head">
                  <h2>
                    <span>¥</span>
                    {{coupon.discount}} 元
                  </h2>
                  <p>{{coupon.desc }} - {{coupon.tag}}</p>
                </div>
                <div class="van-coupon-item__body">
                  <h2>{{coupon.name}}</h2>
                  <p>有效期: 至 {{coupon.endTime}}</p>
                </div>
              </div>
            </div>
          </van-panel>
        </van-list>
      </van-tab>
    </van-tabs>
  </div>
</template>
<script>
import { couponMyList } from '@/api/api';
import { Tab, Tabs, Panel, Card, List, CouponCell, CouponList } from 'vant';
import _ from 'lodash';
export default {
  name: 'coupon-list',
  props: {
    active: {
      type: [String, Number],
      default: 0
    }
  },
  created() {
    this.init();
  },
  data() {
    return {
      activeIndex: Number(this.active),
      tabTitles: ['未使用', '已使用', '已过期'],
      couponList: [],
      page: 0,
      limit: 10,
      loading: false,
      finished: false
    };
  },
  methods: {
    init() {
      this.page = 0;
      this.couponList = [];
      this.getCouponList();
    },
    getCouponList() {
      this.page++;
      couponMyList({
        status: this.activeIndex,
        page: this.page,
        limit: this.limit
      }).then(res => {
        this.couponList.push(...res.data.data.list);
        this.loading = false;
        this.finished = res.data.data.page >= res.data.data.pages;
      });
    },
    handleTabClick() {
      this.page = 0;
      this.couponList = [];
      this.getCouponList();
    }
  },
  components: {
    [Tab.name]: Tab,
    [Tabs.name]: Tabs,
    [Panel.name]: Panel,
    [Card.name]: Card,
    [List.name]: List,
    CouponCell,
    CouponList
  }
};
</script>
<style lang="scss" scoped>
.van-coupon-item {
  overflow: hidden;
  border-radius: 4px;
  margin: 0 15px 15px;
  background-color: #fff;
  -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
  box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
}
.van-coupon-item:active {
  background-color: #e8e8e8;
}
.van-coupon-item__content {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  height: 100px;
  padding: 24px 0 0 15px;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  border: 1px solid red;
}
.van-coupon-item h2,
.van-coupon-item p {
  margin: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.van-coupon-item h2 {
  height: 34px;
  font-weight: 500;
  line-height: 34px;
}
.van-coupon-item p {
  font-size: 12px;
  line-height: 16px;
  color: #969799;
}
.van-coupon-item__head {
  min-width: 90px;
}
.van-coupon-item__head h2 {
  color: #f44;
  font-size: 24px;
}
.van-coupon-item__head h2 span {
  font-size: 50%;
}
.van-coupon-item__body {
  -webkit-box-flex: 1;
  -ms-flex: 1;
  flex: 1;
  position: relative;
  border-radius: 0 4px 4px 0;
  margin-left: 20px;
}
.van-coupon-item__body h2 {
  font-size: 16px;
}
.van-coupon-item__corner {
  top: 16px;
  right: 15px;
  position: absolute;
}
.van-coupon-item__corner .van-icon {
  border-color: #f44;
  background-color: #f44;
}
.van-coupon-item__reason {
  padding: 7px 15px;
  border-top: 1px dashed #ebedf0;
  background-color: #fafafa;
}
.van-coupon-item--disabled:active {
  background-color: #fff;
}
.van-coupon-item--disabled .van-coupon-item__content {
  height: 90px;
}
.van-coupon-item--disabled h2,
.van-coupon-item--disabled p,
.van-coupon-item--disabled span {
  color: #969799;
}
</style>

+ 3239 - 0
litemall-vue/src/views/user/module-address-edit/area.json

@ -0,0 +1,3239 @@
{
  "province_list": {
    "110000": "北京市",
    "120000": "天津市",
    "130000": "河北省",
    "140000": "山西省",
    "150000": "内蒙古自治区",
    "210000": "辽宁省",
    "220000": "吉林省",
    "230000": "黑龙江省",
    "310000": "上海市",
    "320000": "江苏省",
    "330000": "浙江省",
    "340000": "安徽省",
    "350000": "福建省",
    "360000": "江西省",
    "370000": "山东省",
    "410000": "河南省",
    "420000": "湖北省",
    "430000": "湖南省",
    "440000": "广东省",
    "450000": "广西壮族自治区",
    "460000": "海南省",
    "500000": "重庆市",
    "510000": "四川省",
    "520000": "贵州省",
    "530000": "云南省",
    "540000": "西藏自治区",
    "610000": "陕西省",
    "620000": "甘肃省",
    "630000": "青海省",
    "640000": "宁夏回族自治区",
    "650000": "新疆维吾尔自治区"
  },
  "city_list": {
    "110100": "市辖区",
    "120100": "市辖区",
    "130100": "石家庄市",
    "130200": "唐山市",
    "130300": "秦皇岛市",
    "130400": "邯郸市",
    "130500": "邢台市",
    "130600": "保定市",
    "130700": "张家口市",
    "130800": "承德市",
    "130900": "沧州市",
    "131000": "廊坊市",
    "131100": "衡水市",
    "139000": "省直辖县级行政区划",
    "140100": "太原市",
    "140200": "大同市",
    "140300": "阳泉市",
    "140400": "长治市",
    "140500": "晋城市",
    "140600": "朔州市",
    "140700": "晋中市",
    "140800": "运城市",
    "140900": "忻州市",
    "141000": "临汾市",
    "141100": "吕梁市",
    "150100": "呼和浩特市",
    "150200": "包头市",
    "150300": "乌海市",
    "150400": "赤峰市",
    "150500": "通辽市",
    "150600": "鄂尔多斯市",
    "150700": "呼伦贝尔市",
    "150800": "巴彦淖尔市",
    "150900": "乌兰察布市",
    "152200": "兴安盟",
    "152500": "锡林郭勒盟",
    "152900": "阿拉善盟",
    "210100": "沈阳市",
    "210200": "大连市",
    "210300": "鞍山市",
    "210400": "抚顺市",
    "210500": "本溪市",
    "210600": "丹东市",
    "210700": "锦州市",
    "210800": "营口市",
    "210900": "阜新市",
    "211000": "辽阳市",
    "211100": "盘锦市",
    "211200": "铁岭市",
    "211300": "朝阳市",
    "211400": "葫芦岛市",
    "220100": "长春市",
    "220200": "吉林市",
    "220300": "四平市",
    "220400": "辽源市",
    "220500": "通化市",
    "220600": "白山市",
    "220700": "松原市",
    "220800": "白城市",
    "222400": "延边朝鲜族自治州",
    "230100": "哈尔滨市",
    "230200": "齐齐哈尔市",
    "230300": "鸡西市",
    "230400": "鹤岗市",
    "230500": "双鸭山市",
    "230600": "大庆市",
    "230700": "伊春市",
    "230800": "佳木斯市",
    "230900": "七台河市",
    "231000": "牡丹江市",
    "231100": "黑河市",
    "231200": "绥化市",
    "232700": "大兴安岭地区",
    "310100": "市辖区",
    "320100": "南京市",
    "320200": "无锡市",
    "320300": "徐州市",
    "320400": "常州市",
    "320500": "苏州市",
    "320600": "南通市",
    "320700": "连云港市",
    "320800": "淮安市",
    "320900": "盐城市",
    "321000": "扬州市",
    "321100": "镇江市",
    "321200": "泰州市",
    "321300": "宿迁市",
    "330100": "杭州市",
    "330200": "宁波市",
    "330300": "温州市",
    "330400": "嘉兴市",
    "330500": "湖州市",
    "330600": "绍兴市",
    "330700": "金华市",
    "330800": "衢州市",
    "330900": "舟山市",
    "331000": "台州市",
    "331100": "丽水市",
    "340100": "合肥市",
    "340200": "芜湖市",
    "340300": "蚌埠市",
    "340400": "淮南市",
    "340500": "马鞍山市",
    "340600": "淮北市",
    "340700": "铜陵市",
    "340800": "安庆市",
    "341000": "黄山市",
    "341100": "滁州市",
    "341200": "阜阳市",
    "341300": "宿州市",
    "341500": "六安市",
    "341600": "亳州市",
    "341700": "池州市",
    "341800": "宣城市",
    "350100": "福州市",
    "350200": "厦门市",
    "350300": "莆田市",
    "350400": "三明市",
    "350500": "泉州市",
    "350600": "漳州市",
    "350700": "南平市",
    "350800": "龙岩市",
    "350900": "宁德市",
    "360100": "南昌市",
    "360200": "景德镇市",
    "360300": "萍乡市",
    "360400": "九江市",
    "360500": "新余市",
    "360600": "鹰潭市",
    "360700": "赣州市",
    "360800": "吉安市",
    "360900": "宜春市",
    "361000": "抚州市",
    "361100": "上饶市",
    "370100": "济南市",
    "370200": "青岛市",
    "370300": "淄博市",
    "370400": "枣庄市",
    "370500": "东营市",
    "370600": "烟台市",
    "370700": "潍坊市",
    "370800": "济宁市",
    "370900": "泰安市",
    "371000": "威海市",
    "371100": "日照市",
    "371200": "莱芜市",
    "371300": "临沂市",
    "371400": "德州市",
    "371500": "聊城市",
    "371600": "滨州市",
    "371700": "菏泽市",
    "410100": "郑州市",
    "410200": "开封市",
    "410300": "洛阳市",
    "410400": "平顶山市",
    "410500": "安阳市",
    "410600": "鹤壁市",
    "410700": "新乡市",
    "410800": "焦作市",
    "410900": "濮阳市",
    "411000": "许昌市",
    "411100": "漯河市",
    "411200": "三门峡市",
    "411300": "南阳市",
    "411400": "商丘市",
    "411500": "信阳市",
    "411600": "周口市",
    "411700": "驻马店市",
    "419000": "省直辖县级行政区划",
    "420100": "武汉市",
    "420200": "黄石市",
    "420300": "十堰市",
    "420500": "宜昌市",
    "420600": "襄阳市",
    "420700": "鄂州市",
    "420800": "荆门市",
    "420900": "孝感市",
    "421000": "荆州市",
    "421100": "黄冈市",
    "421200": "咸宁市",
    "421300": "随州市",
    "422800": "恩施土家族苗族自治州",
    "429000": "省直辖县级行政区划",
    "430100": "长沙市",
    "430200": "株洲市",
    "430300": "湘潭市",
    "430400": "衡阳市",
    "430500": "邵阳市",
    "430600": "岳阳市",
    "430700": "常德市",
    "430800": "张家界市",
    "430900": "益阳市",
    "431000": "郴州市",
    "431100": "永州市",
    "431200": "怀化市",
    "431300": "娄底市",
    "433100": "湘西土家族苗族自治州",
    "440100": "广州市",
    "440200": "韶关市",
    "440300": "深圳市",
    "440400": "珠海市",
    "440500": "汕头市",
    "440600": "佛山市",
    "440700": "江门市",
    "440800": "湛江市",
    "440900": "茂名市",
    "441200": "肇庆市",
    "441300": "惠州市",
    "441400": "梅州市",
    "441500": "汕尾市",
    "441600": "河源市",
    "441700": "阳江市",
    "441800": "清远市",
    "441900": "东莞市",
    "442000": "中山市",
    "445100": "潮州市",
    "445200": "揭阳市",
    "445300": "云浮市",
    "450100": "南宁市",
    "450200": "柳州市",
    "450300": "桂林市",
    "450400": "梧州市",
    "450500": "北海市",
    "450600": "防城港市",
    "450700": "钦州市",
    "450800": "贵港市",
    "450900": "玉林市",
    "451000": "百色市",
    "451100": "贺州市",
    "451200": "河池市",
    "451300": "来宾市",
    "451400": "崇左市",
    "460100": "海口市",
    "460200": "三亚市",
    "460300": "三沙市",
    "460400": "儋州市",
    "469000": "省直辖县级行政区划",
    "500100": "市辖区",
    "500200": "县",
    "510100": "成都市",
    "510300": "自贡市",
    "510400": "攀枝花市",
    "510500": "泸州市",
    "510600": "德阳市",
    "510700": "绵阳市",
    "510800": "广元市",
    "510900": "遂宁市",
    "511000": "内江市",
    "511100": "乐山市",
    "511300": "南充市",
    "511400": "眉山市",
    "511500": "宜宾市",
    "511600": "广安市",
    "511700": "达州市",
    "511800": "雅安市",
    "511900": "巴中市",
    "512000": "资阳市",
    "513200": "阿坝藏族羌族自治州",
    "513300": "甘孜藏族自治州",
    "513400": "凉山彝族自治州",
    "520100": "贵阳市",
    "520200": "六盘水市",
    "520300": "遵义市",
    "520400": "安顺市",
    "520500": "毕节市",
    "520600": "铜仁市",
    "522300": "黔西南布依族苗族自治州",
    "522600": "黔东南苗族侗族自治州",
    "522700": "黔南布依族苗族自治州",
    "530100": "昆明市",
    "530300": "曲靖市",
    "530400": "玉溪市",
    "530500": "保山市",
    "530600": "昭通市",
    "530700": "丽江市",
    "530800": "普洱市",
    "530900": "临沧市",
    "532300": "楚雄彝族自治州",
    "532500": "红河哈尼族彝族自治州",
    "532600": "文山壮族苗族自治州",
    "532800": "西双版纳傣族自治州",
    "532900": "大理白族自治州",
    "533100": "德宏傣族景颇族自治州",
    "533300": "怒江傈僳族自治州",
    "533400": "迪庆藏族自治州",
    "540100": "拉萨市",
    "540200": "日喀则市",
    "540300": "昌都市",
    "540400": "林芝市",
    "540500": "山南市",
    "542400": "那曲地区",
    "542500": "阿里地区",
    "610100": "西安市",
    "610200": "铜川市",
    "610300": "宝鸡市",
    "610400": "咸阳市",
    "610500": "渭南市",
    "610600": "延安市",
    "610700": "汉中市",
    "610800": "榆林市",
    "610900": "安康市",
    "611000": "商洛市",
    "620100": "兰州市",
    "620200": "嘉峪关市",
    "620300": "金昌市",
    "620400": "白银市",
    "620500": "天水市",
    "620600": "武威市",
    "620700": "张掖市",
    "620800": "平凉市",
    "620900": "酒泉市",
    "621000": "庆阳市",
    "621100": "定西市",
    "621200": "陇南市",
    "622900": "临夏回族自治州",
    "623000": "甘南藏族自治州",
    "630100": "西宁市",
    "630200": "海东市",
    "632200": "海北藏族自治州",
    "632300": "黄南藏族自治州",
    "632500": "海南藏族自治州",
    "632600": "果洛藏族自治州",
    "632700": "玉树藏族自治州",
    "632800": "海西蒙古族藏族自治州",
    "640100": "银川市",
    "640200": "石嘴山市",
    "640300": "吴忠市",
    "640400": "固原市",
    "640500": "中卫市",
    "650100": "乌鲁木齐市",
    "650200": "克拉玛依市",
    "650400": "吐鲁番市",
    "650500": "哈密市",
    "652300": "昌吉回族自治州",
    "652700": "博尔塔拉蒙古自治州",
    "652800": "巴音郭楞蒙古自治州",
    "652900": "阿克苏地区",
    "653000": "克孜勒苏柯尔克孜自治州",
    "653100": "喀什地区",
    "653200": "和田地区",
    "654000": "伊犁哈萨克自治州",
    "654200": "塔城地区",
    "654300": "阿勒泰地区",
    "659000": "自治区直辖县级行政区划"
  },
  "county_list": {
    "110101": "东城区",
    "110102": "西城区",
    "110105": "朝阳区",
    "110106": "丰台区",
    "110107": "石景山区",
    "110108": "海淀区",
    "110109": "门头沟区",
    "110111": "房山区",
    "110112": "通州区",
    "110113": "顺义区",
    "110114": "昌平区",
    "110115": "大兴区",
    "110116": "怀柔区",
    "110117": "平谷区",
    "110118": "密云区",
    "110119": "延庆区",
    "120101": "和平区",
    "120102": "河东区",
    "120103": "河西区",
    "120104": "南开区",
    "120105": "河北区",
    "120106": "红桥区",
    "120110": "东丽区",
    "120111": "西青区",
    "120112": "津南区",
    "120113": "北辰区",
    "120114": "武清区",
    "120115": "宝坻区",
    "120116": "滨海新区",
    "120117": "宁河区",
    "120118": "静海区",
    "120119": "蓟州区",
    "130102": "长安区",
    "130104": "桥西区",
    "130105": "新华区",
    "130107": "井陉矿区",
    "130108": "裕华区",
    "130109": "藁城区",
    "130110": "鹿泉区",
    "130111": "栾城区",
    "130121": "井陉县",
    "130123": "正定县",
    "130125": "行唐县",
    "130126": "灵寿县",
    "130127": "高邑县",
    "130128": "深泽县",
    "130129": "赞皇县",
    "130130": "无极县",
    "130131": "平山县",
    "130132": "元氏县",
    "130133": "赵县",
    "130183": "晋州市",
    "130184": "新乐市",
    "130202": "路南区",
    "130203": "路北区",
    "130204": "古冶区",
    "130205": "开平区",
    "130207": "丰南区",
    "130208": "丰润区",
    "130209": "曹妃甸区",
    "130223": "滦县",
    "130224": "滦南县",
    "130225": "乐亭县",
    "130227": "迁西县",
    "130229": "玉田县",
    "130281": "遵化市",
    "130283": "迁安市",
    "130302": "海港区",
    "130303": "山海关区",
    "130304": "北戴河区",
    "130306": "抚宁区",
    "130321": "青龙满族自治县",
    "130322": "昌黎县",
    "130324": "卢龙县",
    "130402": "邯山区",
    "130403": "丛台区",
    "130404": "复兴区",
    "130406": "峰峰矿区",
    "130421": "邯郸县",
    "130423": "临漳县",
    "130424": "成安县",
    "130425": "大名县",
    "130426": "涉县",
    "130427": "磁县",
    "130428": "肥乡县",
    "130429": "永年县",
    "130430": "邱县",
    "130431": "鸡泽县",
    "130432": "广平县",
    "130433": "馆陶县",
    "130434": "魏县",
    "130435": "曲周县",
    "130481": "武安市",
    "130502": "桥东区",
    "130503": "桥西区",
    "130521": "邢台县",
    "130522": "临城县",
    "130523": "内丘县",
    "130524": "柏乡县",
    "130525": "隆尧县",
    "130526": "任县",
    "130527": "南和县",
    "130528": "宁晋县",
    "130529": "巨鹿县",
    "130530": "新河县",
    "130531": "广宗县",
    "130532": "平乡县",
    "130533": "威县",
    "130534": "清河县",
    "130535": "临西县",
    "130581": "南宫市",
    "130582": "沙河市",
    "130602": "竞秀区",
    "130606": "莲池区",
    "130607": "满城区",
    "130608": "清苑区",
    "130609": "徐水区",
    "130623": "涞水县",
    "130624": "阜平县",
    "130626": "定兴县",
    "130627": "唐县",
    "130628": "高阳县",
    "130629": "容城县",
    "130630": "涞源县",
    "130631": "望都县",
    "130632": "安新县",
    "130633": "易县",
    "130634": "曲阳县",
    "130635": "蠡县",
    "130636": "顺平县",
    "130637": "博野县",
    "130638": "雄县",
    "130681": "涿州市",
    "130683": "安国市",
    "130684": "高碑店市",
    "130702": "桥东区",
    "130703": "桥西区",
    "130705": "宣化区",
    "130706": "下花园区",
    "130708": "万全区",
    "130709": "崇礼区",
    "130722": "张北县",
    "130723": "康保县",
    "130724": "沽源县",
    "130725": "尚义县",
    "130726": "蔚县",
    "130727": "阳原县",
    "130728": "怀安县",
    "130730": "怀来县",
    "130731": "涿鹿县",
    "130732": "赤城县",
    "130802": "双桥区",
    "130803": "双滦区",
    "130804": "鹰手营子矿区",
    "130821": "承德县",
    "130822": "兴隆县",
    "130823": "平泉县",
    "130824": "滦平县",
    "130825": "隆化县",
    "130826": "丰宁满族自治县",
    "130827": "宽城满族自治县",
    "130828": "围场满族蒙古族自治县",
    "130902": "新华区",
    "130903": "运河区",
    "130921": "沧县",
    "130922": "青县",
    "130923": "东光县",
    "130924": "海兴县",
    "130925": "盐山县",
    "130926": "肃宁县",
    "130927": "南皮县",
    "130928": "吴桥县",
    "130929": "献县",
    "130930": "孟村回族自治县",
    "130981": "泊头市",
    "130982": "任丘市",
    "130983": "黄骅市",
    "130984": "河间市",
    "131002": "安次区",
    "131003": "广阳区",
    "131022": "固安县",
    "131023": "永清县",
    "131024": "香河县",
    "131025": "大城县",
    "131026": "文安县",
    "131028": "大厂回族自治县",
    "131081": "霸州市",
    "131082": "三河市",
    "131102": "桃城区",
    "131103": "冀州区",
    "131121": "枣强县",
    "131122": "武邑县",
    "131123": "武强县",
    "131124": "饶阳县",
    "131125": "安平县",
    "131126": "故城县",
    "131127": "景县",
    "131128": "阜城县",
    "131182": "深州市",
    "139001": "定州市",
    "139002": "辛集市",
    "140105": "小店区",
    "140106": "迎泽区",
    "140107": "杏花岭区",
    "140108": "尖草坪区",
    "140109": "万柏林区",
    "140110": "晋源区",
    "140121": "清徐县",
    "140122": "阳曲县",
    "140123": "娄烦县",
    "140181": "古交市",
    "140202": "城区",
    "140203": "矿区",
    "140211": "南郊区",
    "140212": "新荣区",
    "140221": "阳高县",
    "140222": "天镇县",
    "140223": "广灵县",
    "140224": "灵丘县",
    "140225": "浑源县",
    "140226": "左云县",
    "140227": "大同县",
    "140302": "城区",
    "140303": "矿区",
    "140311": "郊区",
    "140321": "平定县",
    "140322": "盂县",
    "140402": "城区",
    "140411": "郊区",
    "140421": "长治县",
    "140423": "襄垣县",
    "140424": "屯留县",
    "140425": "平顺县",
    "140426": "黎城县",
    "140427": "壶关县",
    "140428": "长子县",
    "140429": "武乡县",
    "140430": "沁县",
    "140431": "沁源县",
    "140481": "潞城市",
    "140502": "城区",
    "140521": "沁水县",
    "140522": "阳城县",
    "140524": "陵川县",
    "140525": "泽州县",
    "140581": "高平市",
    "140602": "朔城区",
    "140603": "平鲁区",
    "140621": "山阴县",
    "140622": "应县",
    "140623": "右玉县",
    "140624": "怀仁县",
    "140702": "榆次区",
    "140721": "榆社县",
    "140722": "左权县",
    "140723": "和顺县",
    "140724": "昔阳县",
    "140725": "寿阳县",
    "140726": "太谷县",
    "140727": "祁县",
    "140728": "平遥县",
    "140729": "灵石县",
    "140781": "介休市",
    "140802": "盐湖区",
    "140821": "临猗县",
    "140822": "万荣县",
    "140823": "闻喜县",
    "140824": "稷山县",
    "140825": "新绛县",
    "140826": "绛县",
    "140827": "垣曲县",
    "140828": "夏县",
    "140829": "平陆县",
    "140830": "芮城县",
    "140881": "永济市",
    "140882": "河津市",
    "140902": "忻府区",
    "140921": "定襄县",
    "140922": "五台县",
    "140923": "代县",
    "140924": "繁峙县",
    "140925": "宁武县",
    "140926": "静乐县",
    "140927": "神池县",
    "140928": "五寨县",
    "140929": "岢岚县",
    "140930": "河曲县",
    "140931": "保德县",
    "140932": "偏关县",
    "140981": "原平市",
    "141002": "尧都区",
    "141021": "曲沃县",
    "141022": "翼城县",
    "141023": "襄汾县",
    "141024": "洪洞县",
    "141025": "古县",
    "141026": "安泽县",
    "141027": "浮山县",
    "141028": "吉县",
    "141029": "乡宁县",
    "141030": "大宁县",
    "141031": "隰县",
    "141032": "永和县",
    "141033": "蒲县",
    "141034": "汾西县",
    "141081": "侯马市",
    "141082": "霍州市",
    "141102": "离石区",
    "141121": "文水县",
    "141122": "交城县",
    "141123": "兴县",
    "141124": "临县",
    "141125": "柳林县",
    "141126": "石楼县",
    "141127": "岚县",
    "141128": "方山县",
    "141129": "中阳县",
    "141130": "交口县",
    "141181": "孝义市",
    "141182": "汾阳市",
    "150102": "新城区",
    "150103": "回民区",
    "150104": "玉泉区",
    "150105": "赛罕区",
    "150121": "土默特左旗",
    "150122": "托克托县",
    "150123": "和林格尔县",
    "150124": "清水河县",
    "150125": "武川县",
    "150202": "东河区",
    "150203": "昆都仑区",
    "150204": "青山区",
    "150205": "石拐区",
    "150206": "白云鄂博矿区",
    "150207": "九原区",
    "150221": "土默特右旗",
    "150222": "固阳县",
    "150223": "达尔罕茂明安联合旗",
    "150302": "海勃湾区",
    "150303": "海南区",
    "150304": "乌达区",
    "150402": "红山区",
    "150403": "元宝山区",
    "150404": "松山区",
    "150421": "阿鲁科尔沁旗",
    "150422": "巴林左旗",
    "150423": "巴林右旗",
    "150424": "林西县",
    "150425": "克什克腾旗",
    "150426": "翁牛特旗",
    "150428": "喀喇沁旗",
    "150429": "宁城县",
    "150430": "敖汉旗",
    "150502": "科尔沁区",
    "150521": "科尔沁左翼中旗",
    "150522": "科尔沁左翼后旗",
    "150523": "开鲁县",
    "150524": "库伦旗",
    "150525": "奈曼旗",
    "150526": "扎鲁特旗",
    "150581": "霍林郭勒市",
    "150602": "东胜区",
    "150603": "康巴什区",
    "150621": "达拉特旗",
    "150622": "准格尔旗",
    "150623": "鄂托克前旗",
    "150624": "鄂托克旗",
    "150625": "杭锦旗",
    "150626": "乌审旗",
    "150627": "伊金霍洛旗",
    "150702": "海拉尔区",
    "150703": "扎赉诺尔区",
    "150721": "阿荣旗",
    "150722": "莫力达瓦达斡尔族自治旗",
    "150723": "鄂伦春自治旗",
    "150724": "鄂温克族自治旗",
    "150725": "陈巴尔虎旗",
    "150726": "新巴尔虎左旗",
    "150727": "新巴尔虎右旗",
    "150781": "满洲里市",
    "150782": "牙克石市",
    "150783": "扎兰屯市",
    "150784": "额尔古纳市",
    "150785": "根河市",
    "150802": "临河区",
    "150821": "五原县",
    "150822": "磴口县",
    "150823": "乌拉特前旗",
    "150824": "乌拉特中旗",
    "150825": "乌拉特后旗",
    "150826": "杭锦后旗",
    "150902": "集宁区",
    "150921": "卓资县",
    "150922": "化德县",
    "150923": "商都县",
    "150924": "兴和县",
    "150925": "凉城县",
    "150926": "察哈尔右翼前旗",
    "150927": "察哈尔右翼中旗",
    "150928": "察哈尔右翼后旗",
    "150929": "四子王旗",
    "150981": "丰镇市",
    "152201": "乌兰浩特市",
    "152202": "阿尔山市",
    "152221": "科尔沁右翼前旗",
    "152222": "科尔沁右翼中旗",
    "152223": "扎赉特旗",
    "152224": "突泉县",
    "152501": "二连浩特市",
    "152502": "锡林浩特市",
    "152522": "阿巴嘎旗",
    "152523": "苏尼特左旗",
    "152524": "苏尼特右旗",
    "152525": "东乌珠穆沁旗",
    "152526": "西乌珠穆沁旗",
    "152527": "太仆寺旗",
    "152528": "镶黄旗",
    "152529": "正镶白旗",
    "152530": "正蓝旗",
    "152531": "多伦县",
    "152921": "阿拉善左旗",
    "152922": "阿拉善右旗",
    "152923": "额济纳旗",
    "210102": "和平区",
    "210103": "沈河区",
    "210104": "大东区",
    "210105": "皇姑区",
    "210106": "铁西区",
    "210111": "苏家屯区",
    "210112": "浑南区",
    "210113": "沈北新区",
    "210114": "于洪区",
    "210115": "辽中区",
    "210123": "康平县",
    "210124": "法库县",
    "210181": "新民市",
    "210202": "中山区",
    "210203": "西岗区",
    "210204": "沙河口区",
    "210211": "甘井子区",
    "210212": "旅顺口区",
    "210213": "金州区",
    "210214": "普兰店区",
    "210224": "长海县",
    "210281": "瓦房店市",
    "210283": "庄河市",
    "210302": "铁东区",
    "210303": "铁西区",
    "210304": "立山区",
    "210311": "千山区",
    "210321": "台安县",
    "210323": "岫岩满族自治县",
    "210381": "海城市",
    "210402": "新抚区",
    "210403": "东洲区",
    "210404": "望花区",
    "210411": "顺城区",
    "210421": "抚顺县",
    "210422": "新宾满族自治县",
    "210423": "清原满族自治县",
    "210502": "平山区",
    "210503": "溪湖区",
    "210504": "明山区",
    "210505": "南芬区",
    "210521": "本溪满族自治县",
    "210522": "桓仁满族自治县",
    "210602": "元宝区",
    "210603": "振兴区",
    "210604": "振安区",
    "210624": "宽甸满族自治县",
    "210681": "东港市",
    "210682": "凤城市",
    "210702": "古塔区",
    "210703": "凌河区",
    "210711": "太和区",
    "210726": "黑山县",
    "210727": "义县",
    "210781": "凌海市",
    "210782": "北镇市",
    "210802": "站前区",
    "210803": "西市区",
    "210804": "鲅鱼圈区",
    "210811": "老边区",
    "210881": "盖州市",
    "210882": "大石桥市",
    "210902": "海州区",
    "210903": "新邱区",
    "210904": "太平区",
    "210905": "清河门区",
    "210911": "细河区",
    "210921": "阜新蒙古族自治县",
    "210922": "彰武县",
    "211002": "白塔区",
    "211003": "文圣区",
    "211004": "宏伟区",
    "211005": "弓长岭区",
    "211011": "太子河区",
    "211021": "辽阳县",
    "211081": "灯塔市",
    "211102": "双台子区",
    "211103": "兴隆台区",
    "211104": "大洼区",
    "211122": "盘山县",
    "211202": "银州区",
    "211204": "清河区",
    "211221": "铁岭县",
    "211223": "西丰县",
    "211224": "昌图县",
    "211281": "调兵山市",
    "211282": "开原市",
    "211302": "双塔区",
    "211303": "龙城区",
    "211321": "朝阳县",
    "211322": "建平县",
    "211324": "喀喇沁左翼蒙古族自治县",
    "211381": "北票市",
    "211382": "凌源市",
    "211402": "连山区",
    "211403": "龙港区",
    "211404": "南票区",
    "211421": "绥中县",
    "211422": "建昌县",
    "211481": "兴城市",
    "220102": "南关区",
    "220103": "宽城区",
    "220104": "朝阳区",
    "220105": "二道区",
    "220106": "绿园区",
    "220112": "双阳区",
    "220113": "九台区",
    "220122": "农安县",
    "220182": "榆树市",
    "220183": "德惠市",
    "220202": "昌邑区",
    "220203": "龙潭区",
    "220204": "船营区",
    "220211": "丰满区",
    "220221": "永吉县",
    "220281": "蛟河市",
    "220282": "桦甸市",
    "220283": "舒兰市",
    "220284": "磐石市",
    "220302": "铁西区",
    "220303": "铁东区",
    "220322": "梨树县",
    "220323": "伊通满族自治县",
    "220381": "公主岭市",
    "220382": "双辽市",
    "220402": "龙山区",
    "220403": "西安区",
    "220421": "东丰县",
    "220422": "东辽县",
    "220502": "东昌区",
    "220503": "二道江区",
    "220521": "通化县",
    "220523": "辉南县",
    "220524": "柳河县",
    "220581": "梅河口市",
    "220582": "集安市",
    "220602": "浑江区",
    "220605": "江源区",
    "220621": "抚松县",
    "220622": "靖宇县",
    "220623": "长白朝鲜族自治县",
    "220681": "临江市",
    "220702": "宁江区",
    "220721": "前郭尔罗斯蒙古族自治县",
    "220722": "长岭县",
    "220723": "乾安县",
    "220781": "扶余市",
    "220802": "洮北区",
    "220821": "镇赉县",
    "220822": "通榆县",
    "220881": "洮南市",
    "220882": "大安市",
    "222401": "延吉市",
    "222402": "图们市",
    "222403": "敦化市",
    "222404": "珲春市",
    "222405": "龙井市",
    "222406": "和龙市",
    "222424": "汪清县",
    "222426": "安图县",
    "230102": "道里区",
    "230103": "南岗区",
    "230104": "道外区",
    "230108": "平房区",
    "230109": "松北区",
    "230110": "香坊区",
    "230111": "呼兰区",
    "230112": "阿城区",
    "230113": "双城区",
    "230123": "依兰县",
    "230124": "方正县",
    "230125": "宾县",
    "230126": "巴彦县",
    "230127": "木兰县",
    "230128": "通河县",
    "230129": "延寿县",
    "230183": "尚志市",
    "230184": "五常市",
    "230202": "龙沙区",
    "230203": "建华区",
    "230204": "铁锋区",
    "230205": "昂昂溪区",
    "230206": "富拉尔基区",
    "230207": "碾子山区",
    "230208": "梅里斯达斡尔族区",
    "230221": "龙江县",
    "230223": "依安县",
    "230224": "泰来县",
    "230225": "甘南县",
    "230227": "富裕县",
    "230229": "克山县",
    "230230": "克东县",
    "230231": "拜泉县",
    "230281": "讷河市",
    "230302": "鸡冠区",
    "230303": "恒山区",
    "230304": "滴道区",
    "230305": "梨树区",
    "230306": "城子河区",
    "230307": "麻山区",
    "230321": "鸡东县",
    "230381": "虎林市",
    "230382": "密山市",
    "230402": "向阳区",
    "230403": "工农区",
    "230404": "南山区",
    "230405": "兴安区",
    "230406": "东山区",
    "230407": "兴山区",
    "230421": "萝北县",
    "230422": "绥滨县",
    "230502": "尖山区",
    "230503": "岭东区",
    "230505": "四方台区",
    "230506": "宝山区",
    "230521": "集贤县",
    "230522": "友谊县",
    "230523": "宝清县",
    "230524": "饶河县",
    "230602": "萨尔图区",
    "230603": "龙凤区",
    "230604": "让胡路区",
    "230605": "红岗区",
    "230606": "大同区",
    "230621": "肇州县",
    "230622": "肇源县",
    "230623": "林甸县",
    "230624": "杜尔伯特蒙古族自治县",
    "230702": "伊春区",
    "230703": "南岔区",
    "230704": "友好区",
    "230705": "西林区",
    "230706": "翠峦区",
    "230707": "新青区",
    "230708": "美溪区",
    "230709": "金山屯区",
    "230710": "五营区",
    "230711": "乌马河区",
    "230712": "汤旺河区",
    "230713": "带岭区",
    "230714": "乌伊岭区",
    "230715": "红星区",
    "230716": "上甘岭区",
    "230722": "嘉荫县",
    "230781": "铁力市",
    "230803": "向阳区",
    "230804": "前进区",
    "230805": "东风区",
    "230811": "郊区",
    "230822": "桦南县",
    "230826": "桦川县",
    "230828": "汤原县",
    "230881": "同江市",
    "230882": "富锦市",
    "230883": "抚远市",
    "230902": "新兴区",
    "230903": "桃山区",
    "230904": "茄子河区",
    "230921": "勃利县",
    "231002": "东安区",
    "231003": "阳明区",
    "231004": "爱民区",
    "231005": "西安区",
    "231025": "林口县",
    "231081": "绥芬河市",
    "231083": "海林市",
    "231084": "宁安市",
    "231085": "穆棱市",
    "231086": "东宁市",
    "231102": "爱辉区",
    "231121": "嫩江县",
    "231123": "逊克县",
    "231124": "孙吴县",
    "231181": "北安市",
    "231182": "五大连池市",
    "231202": "北林区",
    "231221": "望奎县",
    "231222": "兰西县",
    "231223": "青冈县",
    "231224": "庆安县",
    "231225": "明水县",
    "231226": "绥棱县",
    "231281": "安达市",
    "231282": "肇东市",
    "231283": "海伦市",
    "232721": "呼玛县",
    "232722": "塔河县",
    "232723": "漠河县",
    "310101": "黄浦区",
    "310104": "徐汇区",
    "310105": "长宁区",
    "310106": "静安区",
    "310107": "普陀区",
    "310109": "虹口区",
    "310110": "杨浦区",
    "310112": "闵行区",
    "310113": "宝山区",
    "310114": "嘉定区",
    "310115": "浦东新区",
    "310116": "金山区",
    "310117": "松江区",
    "310118": "青浦区",
    "310120": "奉贤区",
    "310151": "崇明区",
    "320102": "玄武区",
    "320104": "秦淮区",
    "320105": "建邺区",
    "320106": "鼓楼区",
    "320111": "浦口区",
    "320113": "栖霞区",
    "320114": "雨花台区",
    "320115": "江宁区",
    "320116": "六合区",
    "320117": "溧水区",
    "320118": "高淳区",
    "320205": "锡山区",
    "320206": "惠山区",
    "320211": "滨湖区",
    "320213": "梁溪区",
    "320214": "新吴区",
    "320281": "江阴市",
    "320282": "宜兴市",
    "320302": "鼓楼区",
    "320303": "云龙区",
    "320305": "贾汪区",
    "320311": "泉山区",
    "320312": "铜山区",
    "320321": "丰县",
    "320322": "沛县",
    "320324": "睢宁县",
    "320381": "新沂市",
    "320382": "邳州市",
    "320402": "天宁区",
    "320404": "钟楼区",
    "320411": "新北区",
    "320412": "武进区",
    "320413": "金坛区",
    "320481": "溧阳市",
    "320505": "虎丘区",
    "320506": "吴中区",
    "320507": "相城区",
    "320508": "姑苏区",
    "320509": "吴江区",
    "320581": "常熟市",
    "320582": "张家港市",
    "320583": "昆山市",
    "320585": "太仓市",
    "320602": "崇川区",
    "320611": "港闸区",
    "320612": "通州区",
    "320621": "海安县",
    "320623": "如东县",
    "320681": "启东市",
    "320682": "如皋市",
    "320684": "海门市",
    "320703": "连云区",
    "320706": "海州区",
    "320707": "赣榆区",
    "320722": "东海县",
    "320723": "灌云县",
    "320724": "灌南县",
    "320803": "淮安区",
    "320804": "淮阴区",
    "320812": "清江浦区",
    "320813": "洪泽区",
    "320826": "涟水县",
    "320830": "盱眙县",
    "320831": "金湖县",
    "320902": "亭湖区",
    "320903": "盐都区",
    "320904": "大丰区",
    "320921": "响水县",
    "320922": "滨海县",
    "320923": "阜宁县",
    "320924": "射阳县",
    "320925": "建湖县",
    "320981": "东台市",
    "321002": "广陵区",
    "321003": "邗江区",
    "321012": "江都区",
    "321023": "宝应县",
    "321081": "仪征市",
    "321084": "高邮市",
    "321102": "京口区",
    "321111": "润州区",
    "321112": "丹徒区",
    "321181": "丹阳市",
    "321182": "扬中市",
    "321183": "句容市",
    "321202": "海陵区",
    "321203": "高港区",
    "321204": "姜堰区",
    "321281": "兴化市",
    "321282": "靖江市",
    "321283": "泰兴市",
    "321302": "宿城区",
    "321311": "宿豫区",
    "321322": "沭阳县",
    "321323": "泗阳县",
    "321324": "泗洪县",
    "330102": "上城区",
    "330103": "下城区",
    "330104": "江干区",
    "330105": "拱墅区",
    "330106": "西湖区",
    "330108": "滨江区",
    "330109": "萧山区",
    "330110": "余杭区",
    "330111": "富阳区",
    "330122": "桐庐县",
    "330127": "淳安县",
    "330182": "建德市",
    "330185": "临安市",
    "330203": "海曙区",
    "330204": "江东区",
    "330205": "江北区",
    "330206": "北仑区",
    "330211": "镇海区",
    "330212": "鄞州区",
    "330225": "象山县",
    "330226": "宁海县",
    "330281": "余姚市",
    "330282": "慈溪市",
    "330283": "奉化市",
    "330302": "鹿城区",
    "330303": "龙湾区",
    "330304": "瓯海区",
    "330305": "洞头区",
    "330324": "永嘉县",
    "330326": "平阳县",
    "330327": "苍南县",
    "330328": "文成县",
    "330329": "泰顺县",
    "330381": "瑞安市",
    "330382": "乐清市",
    "330402": "南湖区",
    "330411": "秀洲区",
    "330421": "嘉善县",
    "330424": "海盐县",
    "330481": "海宁市",
    "330482": "平湖市",
    "330483": "桐乡市",
    "330502": "吴兴区",
    "330503": "南浔区",
    "330521": "德清县",
    "330522": "长兴县",
    "330523": "安吉县",
    "330602": "越城区",
    "330603": "柯桥区",
    "330604": "上虞区",
    "330624": "新昌县",
    "330681": "诸暨市",
    "330683": "嵊州市",
    "330702": "婺城区",
    "330703": "金东区",
    "330723": "武义县",
    "330726": "浦江县",
    "330727": "磐安县",
    "330781": "兰溪市",
    "330782": "义乌市",
    "330783": "东阳市",
    "330784": "永康市",
    "330802": "柯城区",
    "330803": "衢江区",
    "330822": "常山县",
    "330824": "开化县",
    "330825": "龙游县",
    "330881": "江山市",
    "330902": "定海区",
    "330903": "普陀区",
    "330921": "岱山县",
    "330922": "嵊泗县",
    "331002": "椒江区",
    "331003": "黄岩区",
    "331004": "路桥区",
    "331021": "玉环县",
    "331022": "三门县",
    "331023": "天台县",
    "331024": "仙居县",
    "331081": "温岭市",
    "331082": "临海市",
    "331102": "莲都区",
    "331121": "青田县",
    "331122": "缙云县",
    "331123": "遂昌县",
    "331124": "松阳县",
    "331125": "云和县",
    "331126": "庆元县",
    "331127": "景宁畲族自治县",
    "331181": "龙泉市",
    "340102": "瑶海区",
    "340103": "庐阳区",
    "340104": "蜀山区",
    "340111": "包河区",
    "340121": "长丰县",
    "340122": "肥东县",
    "340123": "肥西县",
    "340124": "庐江县",
    "340181": "巢湖市",
    "340202": "镜湖区",
    "340203": "弋江区",
    "340207": "鸠江区",
    "340208": "三山区",
    "340221": "芜湖县",
    "340222": "繁昌县",
    "340223": "南陵县",
    "340225": "无为县",
    "340302": "龙子湖区",
    "340303": "蚌山区",
    "340304": "禹会区",
    "340311": "淮上区",
    "340321": "怀远县",
    "340322": "五河县",
    "340323": "固镇县",
    "340402": "大通区",
    "340403": "田家庵区",
    "340404": "谢家集区",
    "340405": "八公山区",
    "340406": "潘集区",
    "340421": "凤台县",
    "340422": "寿县",
    "340503": "花山区",
    "340504": "雨山区",
    "340506": "博望区",
    "340521": "当涂县",
    "340522": "含山县",
    "340523": "和县",
    "340602": "杜集区",
    "340603": "相山区",
    "340604": "烈山区",
    "340621": "濉溪县",
    "340705": "铜官区",
    "340706": "义安区",
    "340711": "郊区",
    "340722": "枞阳县",
    "340802": "迎江区",
    "340803": "大观区",
    "340811": "宜秀区",
    "340822": "怀宁县",
    "340824": "潜山县",
    "340825": "太湖县",
    "340826": "宿松县",
    "340827": "望江县",
    "340828": "岳西县",
    "340881": "桐城市",
    "341002": "屯溪区",
    "341003": "黄山区",
    "341004": "徽州区",
    "341021": "歙县",
    "341022": "休宁县",
    "341023": "黟县",
    "341024": "祁门县",
    "341102": "琅琊区",
    "341103": "南谯区",
    "341122": "来安县",
    "341124": "全椒县",
    "341125": "定远县",
    "341126": "凤阳县",
    "341181": "天长市",
    "341182": "明光市",
    "341202": "颍州区",
    "341203": "颍东区",
    "341204": "颍泉区",
    "341221": "临泉县",
    "341222": "太和县",
    "341225": "阜南县",
    "341226": "颍上县",
    "341282": "界首市",
    "341302": "埇桥区",
    "341321": "砀山县",
    "341322": "萧县",
    "341323": "灵璧县",
    "341324": "泗县",
    "341502": "金安区",
    "341503": "裕安区",
    "341504": "叶集区",
    "341522": "霍邱县",
    "341523": "舒城县",
    "341524": "金寨县",
    "341525": "霍山县",
    "341602": "谯城区",
    "341621": "涡阳县",
    "341622": "蒙城县",
    "341623": "利辛县",
    "341702": "贵池区",
    "341721": "东至县",
    "341722": "石台县",
    "341723": "青阳县",
    "341802": "宣州区",
    "341821": "郎溪县",
    "341822": "广德县",
    "341823": "泾县",
    "341824": "绩溪县",
    "341825": "旌德县",
    "341881": "宁国市",
    "350102": "鼓楼区",
    "350103": "台江区",
    "350104": "仓山区",
    "350105": "马尾区",
    "350111": "晋安区",
    "350121": "闽侯县",
    "350122": "连江县",
    "350123": "罗源县",
    "350124": "闽清县",
    "350125": "永泰县",
    "350128": "平潭县",
    "350181": "福清市",
    "350182": "长乐市",
    "350203": "思明区",
    "350205": "海沧区",
    "350206": "湖里区",
    "350211": "集美区",
    "350212": "同安区",
    "350213": "翔安区",
    "350302": "城厢区",
    "350303": "涵江区",
    "350304": "荔城区",
    "350305": "秀屿区",
    "350322": "仙游县",
    "350402": "梅列区",
    "350403": "三元区",
    "350421": "明溪县",
    "350423": "清流县",
    "350424": "宁化县",
    "350425": "大田县",
    "350426": "尤溪县",
    "350427": "沙县",
    "350428": "将乐县",
    "350429": "泰宁县",
    "350430": "建宁县",
    "350481": "永安市",
    "350502": "鲤城区",
    "350503": "丰泽区",
    "350504": "洛江区",
    "350505": "泉港区",
    "350521": "惠安县",
    "350524": "安溪县",
    "350525": "永春县",
    "350526": "德化县",
    "350527": "金门县",
    "350581": "石狮市",
    "350582": "晋江市",
    "350583": "南安市",
    "350602": "芗城区",
    "350603": "龙文区",
    "350622": "云霄县",
    "350623": "漳浦县",
    "350624": "诏安县",
    "350625": "长泰县",
    "350626": "东山县",
    "350627": "南靖县",
    "350628": "平和县",
    "350629": "华安县",
    "350681": "龙海市",
    "350702": "延平区",
    "350703": "建阳区",
    "350721": "顺昌县",
    "350722": "浦城县",
    "350723": "光泽县",
    "350724": "松溪县",
    "350725": "政和县",
    "350781": "邵武市",
    "350782": "武夷山市",
    "350783": "建瓯市",
    "350802": "新罗区",
    "350803": "永定区",
    "350821": "长汀县",
    "350823": "上杭县",
    "350824": "武平县",
    "350825": "连城县",
    "350881": "漳平市",
    "350902": "蕉城区",
    "350921": "霞浦县",
    "350922": "古田县",
    "350923": "屏南县",
    "350924": "寿宁县",
    "350925": "周宁县",
    "350926": "柘荣县",
    "350981": "福安市",
    "350982": "福鼎市",
    "360102": "东湖区",
    "360103": "西湖区",
    "360104": "青云谱区",
    "360105": "湾里区",
    "360111": "青山湖区",
    "360112": "新建区",
    "360121": "南昌县",
    "360123": "安义县",
    "360124": "进贤县",
    "360202": "昌江区",
    "360203": "珠山区",
    "360222": "浮梁县",
    "360281": "乐平市",
    "360302": "安源区",
    "360313": "湘东区",
    "360321": "莲花县",
    "360322": "上栗县",
    "360323": "芦溪县",
    "360402": "濂溪区",
    "360403": "浔阳区",
    "360421": "九江县",
    "360423": "武宁县",
    "360424": "修水县",
    "360425": "永修县",
    "360426": "德安县",
    "360428": "都昌县",
    "360429": "湖口县",
    "360430": "彭泽县",
    "360481": "瑞昌市",
    "360482": "共青城市",
    "360483": "庐山市",
    "360502": "渝水区",
    "360521": "分宜县",
    "360602": "月湖区",
    "360622": "余江县",
    "360681": "贵溪市",
    "360702": "章贡区",
    "360703": "南康区",
    "360721": "赣县",
    "360722": "信丰县",
    "360723": "大余县",
    "360724": "上犹县",
    "360725": "崇义县",
    "360726": "安远县",
    "360727": "龙南县",
    "360728": "定南县",
    "360729": "全南县",
    "360730": "宁都县",
    "360731": "于都县",
    "360732": "兴国县",
    "360733": "会昌县",
    "360734": "寻乌县",
    "360735": "石城县",
    "360781": "瑞金市",
    "360802": "吉州区",
    "360803": "青原区",
    "360821": "吉安县",
    "360822": "吉水县",
    "360823": "峡江县",
    "360824": "新干县",
    "360825": "永丰县",
    "360826": "泰和县",
    "360827": "遂川县",
    "360828": "万安县",
    "360829": "安福县",
    "360830": "永新县",
    "360881": "井冈山市",
    "360902": "袁州区",
    "360921": "奉新县",
    "360922": "万载县",
    "360923": "上高县",
    "360924": "宜丰县",
    "360925": "靖安县",
    "360926": "铜鼓县",
    "360981": "丰城市",
    "360982": "樟树市",
    "360983": "高安市",
    "361002": "临川区",
    "361021": "南城县",
    "361022": "黎川县",
    "361023": "南丰县",
    "361024": "崇仁县",
    "361025": "乐安县",
    "361026": "宜黄县",
    "361027": "金溪县",
    "361028": "资溪县",
    "361029": "东乡县",
    "361030": "广昌县",
    "361102": "信州区",
    "361103": "广丰区",
    "361121": "上饶县",
    "361123": "玉山县",
    "361124": "铅山县",
    "361125": "横峰县",
    "361126": "弋阳县",
    "361127": "余干县",
    "361128": "鄱阳县",
    "361129": "万年县",
    "361130": "婺源县",
    "361181": "德兴市",
    "370102": "历下区",
    "370103": "市中区",
    "370104": "槐荫区",
    "370105": "天桥区",
    "370112": "历城区",
    "370113": "长清区",
    "370124": "平阴县",
    "370125": "济阳县",
    "370126": "商河县",
    "370181": "章丘市",
    "370202": "市南区",
    "370203": "市北区",
    "370211": "黄岛区",
    "370212": "崂山区",
    "370213": "李沧区",
    "370214": "城阳区",
    "370281": "胶州市",
    "370282": "即墨市",
    "370283": "平度市",
    "370285": "莱西市",
    "370302": "淄川区",
    "370303": "张店区",
    "370304": "博山区",
    "370305": "临淄区",
    "370306": "周村区",
    "370321": "桓台县",
    "370322": "高青县",
    "370323": "沂源县",
    "370402": "市中区",
    "370403": "薛城区",
    "370404": "峄城区",
    "370405": "台儿庄区",
    "370406": "山亭区",
    "370481": "滕州市",
    "370502": "东营区",
    "370503": "河口区",
    "370505": "垦利区",
    "370522": "利津县",
    "370523": "广饶县",
    "370602": "芝罘区",
    "370611": "福山区",
    "370612": "牟平区",
    "370613": "莱山区",
    "370634": "长岛县",
    "370681": "龙口市",
    "370682": "莱阳市",
    "370683": "莱州市",
    "370684": "蓬莱市",
    "370685": "招远市",
    "370686": "栖霞市",
    "370687": "海阳市",
    "370702": "潍城区",
    "370703": "寒亭区",
    "370704": "坊子区",
    "370705": "奎文区",
    "370724": "临朐县",
    "370725": "昌乐县",
    "370781": "青州市",
    "370782": "诸城市",
    "370783": "寿光市",
    "370784": "安丘市",
    "370785": "高密市",
    "370786": "昌邑市",
    "370811": "任城区",
    "370812": "兖州区",
    "370826": "微山县",
    "370827": "鱼台县",
    "370828": "金乡县",
    "370829": "嘉祥县",
    "370830": "汶上县",
    "370831": "泗水县",
    "370832": "梁山县",
    "370881": "曲阜市",
    "370883": "邹城市",
    "370902": "泰山区",
    "370911": "岱岳区",
    "370921": "宁阳县",
    "370923": "东平县",
    "370982": "新泰市",
    "370983": "肥城市",
    "371002": "环翠区",
    "371003": "文登区",
    "371082": "荣成市",
    "371083": "乳山市",
    "371102": "东港区",
    "371103": "岚山区",
    "371121": "五莲县",
    "371122": "莒县",
    "371202": "莱城区",
    "371203": "钢城区",
    "371302": "兰山区",
    "371311": "罗庄区",
    "371312": "河东区",
    "371321": "沂南县",
    "371322": "郯城县",
    "371323": "沂水县",
    "371324": "兰陵县",
    "371325": "费县",
    "371326": "平邑县",
    "371327": "莒南县",
    "371328": "蒙阴县",
    "371329": "临沭县",
    "371402": "德城区",
    "371403": "陵城区",
    "371422": "宁津县",
    "371423": "庆云县",
    "371424": "临邑县",
    "371425": "齐河县",
    "371426": "平原县",
    "371427": "夏津县",
    "371428": "武城县",
    "371481": "乐陵市",
    "371482": "禹城市",
    "371502": "东昌府区",
    "371521": "阳谷县",
    "371522": "莘县",
    "371523": "茌平县",
    "371524": "东阿县",
    "371525": "冠县",
    "371526": "高唐县",
    "371581": "临清市",
    "371602": "滨城区",
    "371603": "沾化区",
    "371621": "惠民县",
    "371622": "阳信县",
    "371623": "无棣县",
    "371625": "博兴县",
    "371626": "邹平县",
    "371702": "牡丹区",
    "371703": "定陶区",
    "371721": "曹县",
    "371722": "单县",
    "371723": "成武县",
    "371724": "巨野县",
    "371725": "郓城县",
    "371726": "鄄城县",
    "371728": "东明县",
    "410102": "中原区",
    "410103": "二七区",
    "410104": "管城回族区",
    "410105": "金水区",
    "410106": "上街区",
    "410108": "惠济区",
    "410122": "中牟县",
    "410181": "巩义市",
    "410182": "荥阳市",
    "410183": "新密市",
    "410184": "新郑市",
    "410185": "登封市",
    "410202": "龙亭区",
    "410203": "顺河回族区",
    "410204": "鼓楼区",
    "410205": "禹王台区",
    "410211": "金明区",
    "410212": "祥符区",
    "410221": "杞县",
    "410222": "通许县",
    "410223": "尉氏县",
    "410225": "兰考县",
    "410302": "老城区",
    "410303": "西工区",
    "410304": "瀍河回族区",
    "410305": "涧西区",
    "410306": "吉利区",
    "410311": "洛龙区",
    "410322": "孟津县",
    "410323": "新安县",
    "410324": "栾川县",
    "410325": "嵩县",
    "410326": "汝阳县",
    "410327": "宜阳县",
    "410328": "洛宁县",
    "410329": "伊川县",
    "410381": "偃师市",
    "410402": "新华区",
    "410403": "卫东区",
    "410404": "石龙区",
    "410411": "湛河区",
    "410421": "宝丰县",
    "410422": "叶县",
    "410423": "鲁山县",
    "410425": "郏县",
    "410481": "舞钢市",
    "410482": "汝州市",
    "410502": "文峰区",
    "410503": "北关区",
    "410505": "殷都区",
    "410506": "龙安区",
    "410522": "安阳县",
    "410523": "汤阴县",
    "410526": "滑县",
    "410527": "内黄县",
    "410581": "林州市",
    "410602": "鹤山区",
    "410603": "山城区",
    "410611": "淇滨区",
    "410621": "浚县",
    "410622": "淇县",
    "410702": "红旗区",
    "410703": "卫滨区",
    "410704": "凤泉区",
    "410711": "牧野区",
    "410721": "新乡县",
    "410724": "获嘉县",
    "410725": "原阳县",
    "410726": "延津县",
    "410727": "封丘县",
    "410728": "长垣县",
    "410781": "卫辉市",
    "410782": "辉县市",
    "410802": "解放区",
    "410803": "中站区",
    "410804": "马村区",
    "410811": "山阳区",
    "410821": "修武县",
    "410822": "博爱县",
    "410823": "武陟县",
    "410825": "温县",
    "410882": "沁阳市",
    "410883": "孟州市",
    "410902": "华龙区",
    "410922": "清丰县",
    "410923": "南乐县",
    "410926": "范县",
    "410927": "台前县",
    "410928": "濮阳县",
    "411002": "魏都区",
    "411023": "许昌县",
    "411024": "鄢陵县",
    "411025": "襄城县",
    "411081": "禹州市",
    "411082": "长葛市",
    "411102": "源汇区",
    "411103": "郾城区",
    "411104": "召陵区",
    "411121": "舞阳县",
    "411122": "临颍县",
    "411202": "湖滨区",
    "411203": "陕州区",
    "411221": "渑池县",
    "411224": "卢氏县",
    "411281": "义马市",
    "411282": "灵宝市",
    "411302": "宛城区",
    "411303": "卧龙区",
    "411321": "南召县",
    "411322": "方城县",
    "411323": "西峡县",
    "411324": "镇平县",
    "411325": "内乡县",
    "411326": "淅川县",
    "411327": "社旗县",
    "411328": "唐河县",
    "411329": "新野县",
    "411330": "桐柏县",
    "411381": "邓州市",
    "411402": "梁园区",
    "411403": "睢阳区",
    "411421": "民权县",
    "411422": "睢县",
    "411423": "宁陵县",
    "411424": "柘城县",
    "411425": "虞城县",
    "411426": "夏邑县",
    "411481": "永城市",
    "411502": "浉河区",
    "411503": "平桥区",
    "411521": "罗山县",
    "411522": "光山县",
    "411523": "新县",
    "411524": "商城县",
    "411525": "固始县",
    "411526": "潢川县",
    "411527": "淮滨县",
    "411528": "息县",
    "411602": "川汇区",
    "411621": "扶沟县",
    "411622": "西华县",
    "411623": "商水县",
    "411624": "沈丘县",
    "411625": "郸城县",
    "411626": "淮阳县",
    "411627": "太康县",
    "411628": "鹿邑县",
    "411681": "项城市",
    "411702": "驿城区",
    "411721": "西平县",
    "411722": "上蔡县",
    "411723": "平舆县",
    "411724": "正阳县",
    "411725": "确山县",
    "411726": "泌阳县",
    "411727": "汝南县",
    "411728": "遂平县",
    "411729": "新蔡县",
    "419001": "济源市",
    "420102": "江岸区",
    "420103": "江汉区",
    "420104": "硚口区",
    "420105": "汉阳区",
    "420106": "武昌区",
    "420107": "青山区",
    "420111": "洪山区",
    "420112": "东西湖区",
    "420113": "汉南区",
    "420114": "蔡甸区",
    "420115": "江夏区",
    "420116": "黄陂区",
    "420117": "新洲区",
    "420202": "黄石港区",
    "420203": "西塞山区",
    "420204": "下陆区",
    "420205": "铁山区",
    "420222": "阳新县",
    "420281": "大冶市",
    "420302": "茅箭区",
    "420303": "张湾区",
    "420304": "郧阳区",
    "420322": "郧西县",
    "420323": "竹山县",
    "420324": "竹溪县",
    "420325": "房县",
    "420381": "丹江口市",
    "420502": "西陵区",
    "420503": "伍家岗区",
    "420504": "点军区",
    "420505": "猇亭区",
    "420506": "夷陵区",
    "420525": "远安县",
    "420526": "兴山县",
    "420527": "秭归县",
    "420528": "长阳土家族自治县",
    "420529": "五峰土家族自治县",
    "420581": "宜都市",
    "420582": "当阳市",
    "420583": "枝江市",
    "420602": "襄城区",
    "420606": "樊城区",
    "420607": "襄州区",
    "420624": "南漳县",
    "420625": "谷城县",
    "420626": "保康县",
    "420682": "老河口市",
    "420683": "枣阳市",
    "420684": "宜城市",
    "420702": "梁子湖区",
    "420703": "华容区",
    "420704": "鄂城区",
    "420802": "东宝区",
    "420804": "掇刀区",
    "420821": "京山县",
    "420822": "沙洋县",
    "420881": "钟祥市",
    "420902": "孝南区",
    "420921": "孝昌县",
    "420922": "大悟县",
    "420923": "云梦县",
    "420981": "应城市",
    "420982": "安陆市",
    "420984": "汉川市",
    "421002": "沙市区",
    "421003": "荆州区",
    "421022": "公安县",
    "421023": "监利县",
    "421024": "江陵县",
    "421081": "石首市",
    "421083": "洪湖市",
    "421087": "松滋市",
    "421102": "黄州区",
    "421121": "团风县",
    "421122": "红安县",
    "421123": "罗田县",
    "421124": "英山县",
    "421125": "浠水县",
    "421126": "蕲春县",
    "421127": "黄梅县",
    "421181": "麻城市",
    "421182": "武穴市",
    "421202": "咸安区",
    "421221": "嘉鱼县",
    "421222": "通城县",
    "421223": "崇阳县",
    "421224": "通山县",
    "421281": "赤壁市",
    "421303": "曾都区",
    "421321": "随县",
    "421381": "广水市",
    "422801": "恩施市",
    "422802": "利川市",
    "422822": "建始县",
    "422823": "巴东县",
    "422825": "宣恩县",
    "422826": "咸丰县",
    "422827": "来凤县",
    "422828": "鹤峰县",
    "429004": "仙桃市",
    "429005": "潜江市",
    "429006": "天门市",
    "429021": "神农架林区",
    "430102": "芙蓉区",
    "430103": "天心区",
    "430104": "岳麓区",
    "430105": "开福区",
    "430111": "雨花区",
    "430112": "望城区",
    "430121": "长沙县",
    "430124": "宁乡县",
    "430181": "浏阳市",
    "430202": "荷塘区",
    "430203": "芦淞区",
    "430204": "石峰区",
    "430211": "天元区",
    "430221": "株洲县",
    "430223": "攸县",
    "430224": "茶陵县",
    "430225": "炎陵县",
    "430281": "醴陵市",
    "430302": "雨湖区",
    "430304": "岳塘区",
    "430321": "湘潭县",
    "430381": "湘乡市",
    "430382": "韶山市",
    "430405": "珠晖区",
    "430406": "雁峰区",
    "430407": "石鼓区",
    "430408": "蒸湘区",
    "430412": "南岳区",
    "430421": "衡阳县",
    "430422": "衡南县",
    "430423": "衡山县",
    "430424": "衡东县",
    "430426": "祁东县",
    "430481": "耒阳市",
    "430482": "常宁市",
    "430502": "双清区",
    "430503": "大祥区",
    "430511": "北塔区",
    "430521": "邵东县",
    "430522": "新邵县",
    "430523": "邵阳县",
    "430524": "隆回县",
    "430525": "洞口县",
    "430527": "绥宁县",
    "430528": "新宁县",
    "430529": "城步苗族自治县",
    "430581": "武冈市",
    "430602": "岳阳楼区",
    "430603": "云溪区",
    "430611": "君山区",
    "430621": "岳阳县",
    "430623": "华容县",
    "430624": "湘阴县",
    "430626": "平江县",
    "430681": "汨罗市",
    "430682": "临湘市",
    "430702": "武陵区",
    "430703": "鼎城区",
    "430721": "安乡县",
    "430722": "汉寿县",
    "430723": "澧县",
    "430724": "临澧县",
    "430725": "桃源县",
    "430726": "石门县",
    "430781": "津市市",
    "430802": "永定区",
    "430811": "武陵源区",
    "430821": "慈利县",
    "430822": "桑植县",
    "430902": "资阳区",
    "430903": "赫山区",
    "430921": "南县",
    "430922": "桃江县",
    "430923": "安化县",
    "430981": "沅江市",
    "431002": "北湖区",
    "431003": "苏仙区",
    "431021": "桂阳县",
    "431022": "宜章县",
    "431023": "永兴县",
    "431024": "嘉禾县",
    "431025": "临武县",
    "431026": "汝城县",
    "431027": "桂东县",
    "431028": "安仁县",
    "431081": "资兴市",
    "431102": "零陵区",
    "431103": "冷水滩区",
    "431121": "祁阳县",
    "431122": "东安县",
    "431123": "双牌县",
    "431124": "道县",
    "431125": "江永县",
    "431126": "宁远县",
    "431127": "蓝山县",
    "431128": "新田县",
    "431129": "江华瑶族自治县",
    "431202": "鹤城区",
    "431221": "中方县",
    "431222": "沅陵县",
    "431223": "辰溪县",
    "431224": "溆浦县",
    "431225": "会同县",
    "431226": "麻阳苗族自治县",
    "431227": "新晃侗族自治县",
    "431228": "芷江侗族自治县",
    "431229": "靖州苗族侗族自治县",
    "431230": "通道侗族自治县",
    "431281": "洪江市",
    "431302": "娄星区",
    "431321": "双峰县",
    "431322": "新化县",
    "431381": "冷水江市",
    "431382": "涟源市",
    "433101": "吉首市",
    "433122": "泸溪县",
    "433123": "凤凰县",
    "433124": "花垣县",
    "433125": "保靖县",
    "433126": "古丈县",
    "433127": "永顺县",
    "433130": "龙山县",
    "440103": "荔湾区",
    "440104": "越秀区",
    "440105": "海珠区",
    "440106": "天河区",
    "440111": "白云区",
    "440112": "黄埔区",
    "440113": "番禺区",
    "440114": "花都区",
    "440115": "南沙区",
    "440117": "从化区",
    "440118": "增城区",
    "440203": "武江区",
    "440204": "浈江区",
    "440205": "曲江区",
    "440222": "始兴县",
    "440224": "仁化县",
    "440229": "翁源县",
    "440232": "乳源瑶族自治县",
    "440233": "新丰县",
    "440281": "乐昌市",
    "440282": "南雄市",
    "440303": "罗湖区",
    "440304": "福田区",
    "440305": "南山区",
    "440306": "宝安区",
    "440307": "龙岗区",
    "440308": "盐田区",
    "440402": "香洲区",
    "440403": "斗门区",
    "440404": "金湾区",
    "440507": "龙湖区",
    "440511": "金平区",
    "440512": "濠江区",
    "440513": "潮阳区",
    "440514": "潮南区",
    "440515": "澄海区",
    "440523": "南澳县",
    "440604": "禅城区",
    "440605": "南海区",
    "440606": "顺德区",
    "440607": "三水区",
    "440608": "高明区",
    "440703": "蓬江区",
    "440704": "江海区",
    "440705": "新会区",
    "440781": "台山市",
    "440783": "开平市",
    "440784": "鹤山市",
    "440785": "恩平市",
    "440802": "赤坎区",
    "440803": "霞山区",
    "440804": "坡头区",
    "440811": "麻章区",
    "440823": "遂溪县",
    "440825": "徐闻县",
    "440881": "廉江市",
    "440882": "雷州市",
    "440883": "吴川市",
    "440902": "茂南区",
    "440904": "电白区",
    "440981": "高州市",
    "440982": "化州市",
    "440983": "信宜市",
    "441202": "端州区",
    "441203": "鼎湖区",
    "441204": "高要区",
    "441223": "广宁县",
    "441224": "怀集县",
    "441225": "封开县",
    "441226": "德庆县",
    "441284": "四会市",
    "441302": "惠城区",
    "441303": "惠阳区",
    "441322": "博罗县",
    "441323": "惠东县",
    "441324": "龙门县",
    "441402": "梅江区",
    "441403": "梅县区",
    "441422": "大埔县",
    "441423": "丰顺县",
    "441424": "五华县",
    "441426": "平远县",
    "441427": "蕉岭县",
    "441481": "兴宁市",
    "441502": "城区",
    "441521": "海丰县",
    "441523": "陆河县",
    "441581": "陆丰市",
    "441602": "源城区",
    "441621": "紫金县",
    "441622": "龙川县",
    "441623": "连平县",
    "441624": "和平县",
    "441625": "东源县",
    "441702": "江城区",
    "441704": "阳东区",
    "441721": "阳西县",
    "441781": "阳春市",
    "441802": "清城区",
    "441803": "清新区",
    "441821": "佛冈县",
    "441823": "阳山县",
    "441825": "连山壮族瑶族自治县",
    "441826": "连南瑶族自治县",
    "441881": "英德市",
    "441882": "连州市",
    "441900": "东莞市",
    "442000": "中山市",
    "445102": "湘桥区",
    "445103": "潮安区",
    "445122": "饶平县",
    "445202": "榕城区",
    "445203": "揭东区",
    "445222": "揭西县",
    "445224": "惠来县",
    "445281": "普宁市",
    "445302": "云城区",
    "445303": "云安区",
    "445321": "新兴县",
    "445322": "郁南县",
    "445381": "罗定市",
    "450102": "兴宁区",
    "450103": "青秀区",
    "450105": "江南区",
    "450107": "西乡塘区",
    "450108": "良庆区",
    "450109": "邕宁区",
    "450110": "武鸣区",
    "450123": "隆安县",
    "450124": "马山县",
    "450125": "上林县",
    "450126": "宾阳县",
    "450127": "横县",
    "450202": "城中区",
    "450203": "鱼峰区",
    "450204": "柳南区",
    "450205": "柳北区",
    "450206": "柳江区",
    "450222": "柳城县",
    "450223": "鹿寨县",
    "450224": "融安县",
    "450225": "融水苗族自治县",
    "450226": "三江侗族自治县",
    "450302": "秀峰区",
    "450303": "叠彩区",
    "450304": "象山区",
    "450305": "七星区",
    "450311": "雁山区",
    "450312": "临桂区",
    "450321": "阳朔县",
    "450323": "灵川县",
    "450324": "全州县",
    "450325": "兴安县",
    "450326": "永福县",
    "450327": "灌阳县",
    "450328": "龙胜各族自治县",
    "450329": "资源县",
    "450330": "平乐县",
    "450331": "荔浦县",
    "450332": "恭城瑶族自治县",
    "450403": "万秀区",
    "450405": "长洲区",
    "450406": "龙圩区",
    "450421": "苍梧县",
    "450422": "藤县",
    "450423": "蒙山县",
    "450481": "岑溪市",
    "450502": "海城区",
    "450503": "银海区",
    "450512": "铁山港区",
    "450521": "合浦县",
    "450602": "港口区",
    "450603": "防城区",
    "450621": "上思县",
    "450681": "东兴市",
    "450702": "钦南区",
    "450703": "钦北区",
    "450721": "灵山县",
    "450722": "浦北县",
    "450802": "港北区",
    "450803": "港南区",
    "450804": "覃塘区",
    "450821": "平南县",
    "450881": "桂平市",
    "450902": "玉州区",
    "450903": "福绵区",
    "450921": "容县",
    "450922": "陆川县",
    "450923": "博白县",
    "450924": "兴业县",
    "450981": "北流市",
    "451002": "右江区",
    "451021": "田阳县",
    "451022": "田东县",
    "451023": "平果县",
    "451024": "德保县",
    "451026": "那坡县",
    "451027": "凌云县",
    "451028": "乐业县",
    "451029": "田林县",
    "451030": "西林县",
    "451031": "隆林各族自治县",
    "451081": "靖西市",
    "451102": "八步区",
    "451103": "平桂区",
    "451121": "昭平县",
    "451122": "钟山县",
    "451123": "富川瑶族自治县",
    "451202": "金城江区",
    "451221": "南丹县",
    "451222": "天峨县",
    "451223": "凤山县",
    "451224": "东兰县",
    "451225": "罗城仫佬族自治县",
    "451226": "环江毛南族自治县",
    "451227": "巴马瑶族自治县",
    "451228": "都安瑶族自治县",
    "451229": "大化瑶族自治县",
    "451281": "宜州市",
    "451302": "兴宾区",
    "451321": "忻城县",
    "451322": "象州县",
    "451323": "武宣县",
    "451324": "金秀瑶族自治县",
    "451381": "合山市",
    "451402": "江州区",
    "451421": "扶绥县",
    "451422": "宁明县",
    "451423": "龙州县",
    "451424": "大新县",
    "451425": "天等县",
    "451481": "凭祥市",
    "460105": "秀英区",
    "460106": "龙华区",
    "460107": "琼山区",
    "460108": "美兰区",
    "460201": "市辖区",
    "460202": "海棠区",
    "460203": "吉阳区",
    "460204": "天涯区",
    "460205": "崖州区",
    "460321": "西沙群岛",
    "460322": "南沙群岛",
    "460323": "中沙群岛的岛礁及其海域",
    "460400": "儋州市",
    "469001": "五指山市",
    "469002": "琼海市",
    "469005": "文昌市",
    "469006": "万宁市",
    "469007": "东方市",
    "469021": "定安县",
    "469022": "屯昌县",
    "469023": "澄迈县",
    "469024": "临高县",
    "469025": "白沙黎族自治县",
    "469026": "昌江黎族自治县",
    "469027": "乐东黎族自治县",
    "469028": "陵水黎族自治县",
    "469029": "保亭黎族苗族自治县",
    "469030": "琼中黎族苗族自治县",
    "500101": "万州区",
    "500102": "涪陵区",
    "500103": "渝中区",
    "500104": "大渡口区",
    "500105": "江北区",
    "500106": "沙坪坝区",
    "500107": "九龙坡区",
    "500108": "南岸区",
    "500109": "北碚区",
    "500110": "綦江区",
    "500111": "大足区",
    "500112": "渝北区",
    "500113": "巴南区",
    "500114": "黔江区",
    "500115": "长寿区",
    "500116": "江津区",
    "500117": "合川区",
    "500118": "永川区",
    "500119": "南川区",
    "500120": "璧山区",
    "500151": "铜梁区",
    "500152": "潼南区",
    "500153": "荣昌区",
    "500154": "开州区",
    "500228": "梁平县",
    "500229": "城口县",
    "500230": "丰都县",
    "500231": "垫江县",
    "500232": "武隆县",
    "500233": "忠县",
    "500235": "云阳县",
    "500236": "奉节县",
    "500237": "巫山县",
    "500238": "巫溪县",
    "500240": "石柱土家族自治县",
    "500241": "秀山土家族苗族自治县",
    "500242": "酉阳土家族苗族自治县",
    "500243": "彭水苗族土家族自治县",
    "510104": "锦江区",
    "510105": "青羊区",
    "510106": "金牛区",
    "510107": "武侯区",
    "510108": "成华区",
    "510112": "龙泉驿区",
    "510113": "青白江区",
    "510114": "新都区",
    "510115": "温江区",
    "510116": "双流区",
    "510121": "金堂县",
    "510124": "郫县",
    "510129": "大邑县",
    "510131": "蒲江县",
    "510132": "新津县",
    "510181": "都江堰市",
    "510182": "彭州市",
    "510183": "邛崃市",
    "510184": "崇州市",
    "510185": "简阳市",
    "510302": "自流井区",
    "510303": "贡井区",
    "510304": "大安区",
    "510311": "沿滩区",
    "510321": "荣县",
    "510322": "富顺县",
    "510402": "东区",
    "510403": "西区",
    "510411": "仁和区",
    "510421": "米易县",
    "510422": "盐边县",
    "510502": "江阳区",
    "510503": "纳溪区",
    "510504": "龙马潭区",
    "510521": "泸县",
    "510522": "合江县",
    "510524": "叙永县",
    "510525": "古蔺县",
    "510603": "旌阳区",
    "510623": "中江县",
    "510626": "罗江县",
    "510681": "广汉市",
    "510682": "什邡市",
    "510683": "绵竹市",
    "510703": "涪城区",
    "510704": "游仙区",
    "510705": "安州区",
    "510722": "三台县",
    "510723": "盐亭县",
    "510725": "梓潼县",
    "510726": "北川羌族自治县",
    "510727": "平武县",
    "510781": "江油市",
    "510802": "利州区",
    "510811": "昭化区",
    "510812": "朝天区",
    "510821": "旺苍县",
    "510822": "青川县",
    "510823": "剑阁县",
    "510824": "苍溪县",
    "510903": "船山区",
    "510904": "安居区",
    "510921": "蓬溪县",
    "510922": "射洪县",
    "510923": "大英县",
    "511002": "市中区",
    "511011": "东兴区",
    "511024": "威远县",
    "511025": "资中县",
    "511028": "隆昌县",
    "511102": "市中区",
    "511111": "沙湾区",
    "511112": "五通桥区",
    "511113": "金口河区",
    "511123": "犍为县",
    "511124": "井研县",
    "511126": "夹江县",
    "511129": "沐川县",
    "511132": "峨边彝族自治县",
    "511133": "马边彝族自治县",
    "511181": "峨眉山市",
    "511302": "顺庆区",
    "511303": "高坪区",
    "511304": "嘉陵区",
    "511321": "南部县",
    "511322": "营山县",
    "511323": "蓬安县",
    "511324": "仪陇县",
    "511325": "西充县",
    "511381": "阆中市",
    "511402": "东坡区",
    "511403": "彭山区",
    "511421": "仁寿县",
    "511423": "洪雅县",
    "511424": "丹棱县",
    "511425": "青神县",
    "511502": "翠屏区",
    "511503": "南溪区",
    "511521": "宜宾县",
    "511523": "江安县",
    "511524": "长宁县",
    "511525": "高县",
    "511526": "珙县",
    "511527": "筠连县",
    "511528": "兴文县",
    "511529": "屏山县",
    "511602": "广安区",
    "511603": "前锋区",
    "511621": "岳池县",
    "511622": "武胜县",
    "511623": "邻水县",
    "511681": "华蓥市",
    "511702": "通川区",
    "511703": "达川区",
    "511722": "宣汉县",
    "511723": "开江县",
    "511724": "大竹县",
    "511725": "渠县",
    "511781": "万源市",
    "511802": "雨城区",
    "511803": "名山区",
    "511822": "荥经县",
    "511823": "汉源县",
    "511824": "石棉县",
    "511825": "天全县",
    "511826": "芦山县",
    "511827": "宝兴县",
    "511902": "巴州区",
    "511903": "恩阳区",
    "511921": "通江县",
    "511922": "南江县",
    "511923": "平昌县",
    "512002": "雁江区",
    "512021": "安岳县",
    "512022": "乐至县",
    "513201": "马尔康市",
    "513221": "汶川县",
    "513222": "理县",
    "513223": "茂县",
    "513224": "松潘县",
    "513225": "九寨沟县",
    "513226": "金川县",
    "513227": "小金县",
    "513228": "黑水县",
    "513230": "壤塘县",
    "513231": "阿坝县",
    "513232": "若尔盖县",
    "513233": "红原县",
    "513301": "康定市",
    "513322": "泸定县",
    "513323": "丹巴县",
    "513324": "九龙县",
    "513325": "雅江县",
    "513326": "道孚县",
    "513327": "炉霍县",
    "513328": "甘孜县",
    "513329": "新龙县",
    "513330": "德格县",
    "513331": "白玉县",
    "513332": "石渠县",
    "513333": "色达县",
    "513334": "理塘县",
    "513335": "巴塘县",
    "513336": "乡城县",
    "513337": "稻城县",
    "513338": "得荣县",
    "513401": "西昌市",
    "513422": "木里藏族自治县",
    "513423": "盐源县",
    "513424": "德昌县",
    "513425": "会理县",
    "513426": "会东县",
    "513427": "宁南县",
    "513428": "普格县",
    "513429": "布拖县",
    "513430": "金阳县",
    "513431": "昭觉县",
    "513432": "喜德县",
    "513433": "冕宁县",
    "513434": "越西县",
    "513435": "甘洛县",
    "513436": "美姑县",
    "513437": "雷波县",
    "520102": "南明区",
    "520103": "云岩区",
    "520111": "花溪区",
    "520112": "乌当区",
    "520113": "白云区",
    "520115": "观山湖区",
    "520121": "开阳县",
    "520122": "息烽县",
    "520123": "修文县",
    "520181": "清镇市",
    "520201": "钟山区",
    "520203": "六枝特区",
    "520221": "水城县",
    "520222": "盘县",
    "520302": "红花岗区",
    "520303": "汇川区",
    "520304": "播州区",
    "520322": "桐梓县",
    "520323": "绥阳县",
    "520324": "正安县",
    "520325": "道真仡佬族苗族自治县",
    "520326": "务川仡佬族苗族自治县",
    "520327": "凤冈县",
    "520328": "湄潭县",
    "520329": "余庆县",
    "520330": "习水县",
    "520381": "赤水市",
    "520382": "仁怀市",
    "520402": "西秀区",
    "520403": "平坝区",
    "520422": "普定县",
    "520423": "镇宁布依族苗族自治县",
    "520424": "关岭布依族苗族自治县",
    "520425": "紫云苗族布依族自治县",
    "520502": "七星关区",
    "520521": "大方县",
    "520522": "黔西县",
    "520523": "金沙县",
    "520524": "织金县",
    "520525": "纳雍县",
    "520526": "威宁彝族回族苗族自治县",
    "520527": "赫章县",
    "520602": "碧江区",
    "520603": "万山区",
    "520621": "江口县",
    "520622": "玉屏侗族自治县",
    "520623": "石阡县",
    "520624": "思南县",
    "520625": "印江土家族苗族自治县",
    "520626": "德江县",
    "520627": "沿河土家族自治县",
    "520628": "松桃苗族自治县",
    "522301": "兴义市",
    "522322": "兴仁县",
    "522323": "普安县",
    "522324": "晴隆县",
    "522325": "贞丰县",
    "522326": "望谟县",
    "522327": "册亨县",
    "522328": "安龙县",
    "522601": "凯里市",
    "522622": "黄平县",
    "522623": "施秉县",
    "522624": "三穗县",
    "522625": "镇远县",
    "522626": "岑巩县",
    "522627": "天柱县",
    "522628": "锦屏县",
    "522629": "剑河县",
    "522630": "台江县",
    "522631": "黎平县",
    "522632": "榕江县",
    "522633": "从江县",
    "522634": "雷山县",
    "522635": "麻江县",
    "522636": "丹寨县",
    "522701": "都匀市",
    "522702": "福泉市",
    "522722": "荔波县",
    "522723": "贵定县",
    "522725": "瓮安县",
    "522726": "独山县",
    "522727": "平塘县",
    "522728": "罗甸县",
    "522729": "长顺县",
    "522730": "龙里县",
    "522731": "惠水县",
    "522732": "三都水族自治县",
    "530102": "五华区",
    "530103": "盘龙区",
    "530111": "官渡区",
    "530112": "西山区",
    "530113": "东川区",
    "530114": "呈贡区",
    "530122": "晋宁县",
    "530124": "富民县",
    "530125": "宜良县",
    "530126": "石林彝族自治县",
    "530127": "嵩明县",
    "530128": "禄劝彝族苗族自治县",
    "530129": "寻甸回族彝族自治县",
    "530181": "安宁市",
    "530302": "麒麟区",
    "530303": "沾益区",
    "530321": "马龙县",
    "530322": "陆良县",
    "530323": "师宗县",
    "530324": "罗平县",
    "530325": "富源县",
    "530326": "会泽县",
    "530381": "宣威市",
    "530402": "红塔区",
    "530403": "江川区",
    "530422": "澄江县",
    "530423": "通海县",
    "530424": "华宁县",
    "530425": "易门县",
    "530426": "峨山彝族自治县",
    "530427": "新平彝族傣族自治县",
    "530428": "元江哈尼族彝族傣族自治县",
    "530502": "隆阳区",
    "530521": "施甸县",
    "530523": "龙陵县",
    "530524": "昌宁县",
    "530581": "腾冲市",
    "530602": "昭阳区",
    "530621": "鲁甸县",
    "530622": "巧家县",
    "530623": "盐津县",
    "530624": "大关县",
    "530625": "永善县",
    "530626": "绥江县",
    "530627": "镇雄县",
    "530628": "彝良县",
    "530629": "威信县",
    "530630": "水富县",
    "530702": "古城区",
    "530721": "玉龙纳西族自治县",
    "530722": "永胜县",
    "530723": "华坪县",
    "530724": "宁蒗彝族自治县",
    "530802": "思茅区",
    "530821": "宁洱哈尼族彝族自治县",
    "530822": "墨江哈尼族自治县",
    "530823": "景东彝族自治县",
    "530824": "景谷傣族彝族自治县",
    "530825": "镇沅彝族哈尼族拉祜族自治县",
    "530826": "江城哈尼族彝族自治县",
    "530827": "孟连傣族拉祜族佤族自治县",
    "530828": "澜沧拉祜族自治县",
    "530829": "西盟佤族自治县",
    "530902": "临翔区",
    "530921": "凤庆县",
    "530922": "云县",
    "530923": "永德县",
    "530924": "镇康县",
    "530925": "双江拉祜族佤族布朗族傣族自治县",
    "530926": "耿马傣族佤族自治县",
    "530927": "沧源佤族自治县",
    "532301": "楚雄市",
    "532322": "双柏县",
    "532323": "牟定县",
    "532324": "南华县",
    "532325": "姚安县",
    "532326": "大姚县",
    "532327": "永仁县",
    "532328": "元谋县",
    "532329": "武定县",
    "532331": "禄丰县",
    "532501": "个旧市",
    "532502": "开远市",
    "532503": "蒙自市",
    "532504": "弥勒市",
    "532523": "屏边苗族自治县",
    "532524": "建水县",
    "532525": "石屏县",
    "532527": "泸西县",
    "532528": "元阳县",
    "532529": "红河县",
    "532530": "金平苗族瑶族傣族自治县",
    "532531": "绿春县",
    "532532": "河口瑶族自治县",
    "532601": "文山市",
    "532622": "砚山县",
    "532623": "西畴县",
    "532624": "麻栗坡县",
    "532625": "马关县",
    "532626": "丘北县",
    "532627": "广南县",
    "532628": "富宁县",
    "532801": "景洪市",
    "532822": "勐海县",
    "532823": "勐腊县",
    "532901": "大理市",
    "532922": "漾濞彝族自治县",
    "532923": "祥云县",
    "532924": "宾川县",
    "532925": "弥渡县",
    "532926": "南涧彝族自治县",
    "532927": "巍山彝族回族自治县",
    "532928": "永平县",
    "532929": "云龙县",
    "532930": "洱源县",
    "532931": "剑川县",
    "532932": "鹤庆县",
    "533102": "瑞丽市",
    "533103": "芒市",
    "533122": "梁河县",
    "533123": "盈江县",
    "533124": "陇川县",
    "533301": "泸水市",
    "533323": "福贡县",
    "533324": "贡山独龙族怒族自治县",
    "533325": "兰坪白族普米族自治县",
    "533401": "香格里拉市",
    "533422": "德钦县",
    "533423": "维西傈僳族自治县",
    "540102": "城关区",
    "540103": "堆龙德庆区",
    "540121": "林周县",
    "540122": "当雄县",
    "540123": "尼木县",
    "540124": "曲水县",
    "540126": "达孜县",
    "540127": "墨竹工卡县",
    "540202": "桑珠孜区",
    "540221": "南木林县",
    "540222": "江孜县",
    "540223": "定日县",
    "540224": "萨迦县",
    "540225": "拉孜县",
    "540226": "昂仁县",
    "540227": "谢通门县",
    "540228": "白朗县",
    "540229": "仁布县",
    "540230": "康马县",
    "540231": "定结县",
    "540232": "仲巴县",
    "540233": "亚东县",
    "540234": "吉隆县",
    "540235": "聂拉木县",
    "540236": "萨嘎县",
    "540237": "岗巴县",
    "540302": "卡若区",
    "540321": "江达县",
    "540322": "贡觉县",
    "540323": "类乌齐县",
    "540324": "丁青县",
    "540325": "察雅县",
    "540326": "八宿县",
    "540327": "左贡县",
    "540328": "芒康县",
    "540329": "洛隆县",
    "540330": "边坝县",
    "540402": "巴宜区",
    "540421": "工布江达县",
    "540422": "米林县",
    "540423": "墨脱县",
    "540424": "波密县",
    "540425": "察隅县",
    "540426": "朗县",
    "540502": "乃东区",
    "540521": "扎囊县",
    "540522": "贡嘎县",
    "540523": "桑日县",
    "540524": "琼结县",
    "540525": "曲松县",
    "540526": "措美县",
    "540527": "洛扎县",
    "540528": "加查县",
    "540529": "隆子县",
    "540530": "错那县",
    "540531": "浪卡子县",
    "542421": "那曲县",
    "542422": "嘉黎县",
    "542423": "比如县",
    "542424": "聂荣县",
    "542425": "安多县",
    "542426": "申扎县",
    "542427": "索县",
    "542428": "班戈县",
    "542429": "巴青县",
    "542430": "尼玛县",
    "542431": "双湖县",
    "542521": "普兰县",
    "542522": "札达县",
    "542523": "噶尔县",
    "542524": "日土县",
    "542525": "革吉县",
    "542526": "改则县",
    "542527": "措勤县",
    "610102": "新城区",
    "610103": "碑林区",
    "610104": "莲湖区",
    "610111": "灞桥区",
    "610112": "未央区",
    "610113": "雁塔区",
    "610114": "阎良区",
    "610115": "临潼区",
    "610116": "长安区",
    "610117": "高陵区",
    "610122": "蓝田县",
    "610124": "周至县",
    "610125": "户县",
    "610202": "王益区",
    "610203": "印台区",
    "610204": "耀州区",
    "610222": "宜君县",
    "610302": "渭滨区",
    "610303": "金台区",
    "610304": "陈仓区",
    "610322": "凤翔县",
    "610323": "岐山县",
    "610324": "扶风县",
    "610326": "眉县",
    "610327": "陇县",
    "610328": "千阳县",
    "610329": "麟游县",
    "610330": "凤县",
    "610331": "太白县",
    "610402": "秦都区",
    "610403": "杨陵区",
    "610404": "渭城区",
    "610422": "三原县",
    "610423": "泾阳县",
    "610424": "乾县",
    "610425": "礼泉县",
    "610426": "永寿县",
    "610427": "彬县",
    "610428": "长武县",
    "610429": "旬邑县",
    "610430": "淳化县",
    "610431": "武功县",
    "610481": "兴平市",
    "610502": "临渭区",
    "610503": "华州区",
    "610522": "潼关县",
    "610523": "大荔县",
    "610524": "合阳县",
    "610525": "澄城县",
    "610526": "蒲城县",
    "610527": "白水县",
    "610528": "富平县",
    "610581": "韩城市",
    "610582": "华阴市",
    "610602": "宝塔区",
    "610603": "安塞区",
    "610621": "延长县",
    "610622": "延川县",
    "610623": "子长县",
    "610625": "志丹县",
    "610626": "吴起县",
    "610627": "甘泉县",
    "610628": "富县",
    "610629": "洛川县",
    "610630": "宜川县",
    "610631": "黄龙县",
    "610632": "黄陵县",
    "610702": "汉台区",
    "610721": "南郑县",
    "610722": "城固县",
    "610723": "洋县",
    "610724": "西乡县",
    "610725": "勉县",
    "610726": "宁强县",
    "610727": "略阳县",
    "610728": "镇巴县",
    "610729": "留坝县",
    "610730": "佛坪县",
    "610802": "榆阳区",
    "610803": "横山区",
    "610821": "神木县",
    "610822": "府谷县",
    "610824": "靖边县",
    "610825": "定边县",
    "610826": "绥德县",
    "610827": "米脂县",
    "610828": "佳县",
    "610829": "吴堡县",
    "610830": "清涧县",
    "610831": "子洲县",
    "610902": "汉滨区",
    "610921": "汉阴县",
    "610922": "石泉县",
    "610923": "宁陕县",
    "610924": "紫阳县",
    "610925": "岚皋县",
    "610926": "平利县",
    "610927": "镇坪县",
    "610928": "旬阳县",
    "610929": "白河县",
    "611002": "商州区",
    "611021": "洛南县",
    "611022": "丹凤县",
    "611023": "商南县",
    "611024": "山阳县",
    "611025": "镇安县",
    "611026": "柞水县",
    "620102": "城关区",
    "620103": "七里河区",
    "620104": "西固区",
    "620105": "安宁区",
    "620111": "红古区",
    "620121": "永登县",
    "620122": "皋兰县",
    "620123": "榆中县",
    "620201": "嘉峪关市",
    "620302": "金川区",
    "620321": "永昌县",
    "620402": "白银区",
    "620403": "平川区",
    "620421": "靖远县",
    "620422": "会宁县",
    "620423": "景泰县",
    "620502": "秦州区",
    "620503": "麦积区",
    "620521": "清水县",
    "620522": "秦安县",
    "620523": "甘谷县",
    "620524": "武山县",
    "620525": "张家川回族自治县",
    "620602": "凉州区",
    "620621": "民勤县",
    "620622": "古浪县",
    "620623": "天祝藏族自治县",
    "620702": "甘州区",
    "620721": "肃南裕固族自治县",
    "620722": "民乐县",
    "620723": "临泽县",
    "620724": "高台县",
    "620725": "山丹县",
    "620802": "崆峒区",
    "620821": "泾川县",
    "620822": "灵台县",
    "620823": "崇信县",
    "620824": "华亭县",
    "620825": "庄浪县",
    "620826": "静宁县",
    "620902": "肃州区",
    "620921": "金塔县",
    "620922": "瓜州县",
    "620923": "肃北蒙古族自治县",
    "620924": "阿克塞哈萨克族自治县",
    "620981": "玉门市",
    "620982": "敦煌市",
    "621002": "西峰区",
    "621021": "庆城县",
    "621022": "环县",
    "621023": "华池县",
    "621024": "合水县",
    "621025": "正宁县",
    "621026": "宁县",
    "621027": "镇原县",
    "621102": "安定区",
    "621121": "通渭县",
    "621122": "陇西县",
    "621123": "渭源县",
    "621124": "临洮县",
    "621125": "漳县",
    "621126": "岷县",
    "621202": "武都区",
    "621221": "成县",
    "621222": "文县",
    "621223": "宕昌县",
    "621224": "康县",
    "621225": "西和县",
    "621226": "礼县",
    "621227": "徽县",
    "621228": "两当县",
    "622901": "临夏市",
    "622921": "临夏县",
    "622922": "康乐县",
    "622923": "永靖县",
    "622924": "广河县",
    "622925": "和政县",
    "622926": "东乡族自治县",
    "622927": "积石山保安族东乡族撒拉族自治县",
    "623001": "合作市",
    "623021": "临潭县",
    "623022": "卓尼县",
    "623023": "舟曲县",
    "623024": "迭部县",
    "623025": "玛曲县",
    "623026": "碌曲县",
    "623027": "夏河县",
    "630102": "城东区",
    "630103": "城中区",
    "630104": "城西区",
    "630105": "城北区",
    "630121": "大通回族土族自治县",
    "630122": "湟中县",
    "630123": "湟源县",
    "630202": "乐都区",
    "630203": "平安区",
    "630222": "民和回族土族自治县",
    "630223": "互助土族自治县",
    "630224": "化隆回族自治县",
    "630225": "循化撒拉族自治县",
    "632221": "门源回族自治县",
    "632222": "祁连县",
    "632223": "海晏县",
    "632224": "刚察县",
    "632321": "同仁县",
    "632322": "尖扎县",
    "632323": "泽库县",
    "632324": "河南蒙古族自治县",
    "632521": "共和县",
    "632522": "同德县",
    "632523": "贵德县",
    "632524": "兴海县",
    "632525": "贵南县",
    "632621": "玛沁县",
    "632622": "班玛县",
    "632623": "甘德县",
    "632624": "达日县",
    "632625": "久治县",
    "632626": "玛多县",
    "632701": "玉树市",
    "632722": "杂多县",
    "632723": "称多县",
    "632724": "治多县",
    "632725": "囊谦县",
    "632726": "曲麻莱县",
    "632801": "格尔木市",
    "632802": "德令哈市",
    "632821": "乌兰县",
    "632822": "都兰县",
    "632823": "天峻县",
    "640104": "兴庆区",
    "640105": "西夏区",
    "640106": "金凤区",
    "640121": "永宁县",
    "640122": "贺兰县",
    "640181": "灵武市",
    "640202": "大武口区",
    "640205": "惠农区",
    "640221": "平罗县",
    "640302": "利通区",
    "640303": "红寺堡区",
    "640323": "盐池县",
    "640324": "同心县",
    "640381": "青铜峡市",
    "640402": "原州区",
    "640422": "西吉县",
    "640423": "隆德县",
    "640424": "泾源县",
    "640425": "彭阳县",
    "640502": "沙坡头区",
    "640521": "中宁县",
    "640522": "海原县",
    "650102": "天山区",
    "650103": "沙依巴克区",
    "650104": "新市区",
    "650105": "水磨沟区",
    "650106": "头屯河区",
    "650107": "达坂城区",
    "650109": "米东区",
    "650121": "乌鲁木齐县",
    "650202": "独山子区",
    "650203": "克拉玛依区",
    "650204": "白碱滩区",
    "650205": "乌尔禾区",
    "650402": "高昌区",
    "650421": "鄯善县",
    "650422": "托克逊县",
    "650502": "伊州区",
    "650521": "巴里坤哈萨克自治县",
    "650522": "伊吾县",
    "652301": "昌吉市",
    "652302": "阜康市",
    "652323": "呼图壁县",
    "652324": "玛纳斯县",
    "652325": "奇台县",
    "652327": "吉木萨尔县",
    "652328": "木垒哈萨克自治县",
    "652701": "博乐市",
    "652702": "阿拉山口市",
    "652722": "精河县",
    "652723": "温泉县",
    "652801": "库尔勒市",
    "652822": "轮台县",
    "652823": "尉犁县",
    "652824": "若羌县",
    "652825": "且末县",
    "652826": "焉耆回族自治县",
    "652827": "和静县",
    "652828": "和硕县",
    "652829": "博湖县",
    "652901": "阿克苏市",
    "652922": "温宿县",
    "652923": "库车县",
    "652924": "沙雅县",
    "652925": "新和县",
    "652926": "拜城县",
    "652927": "乌什县",
    "652928": "阿瓦提县",
    "652929": "柯坪县",
    "653001": "阿图什市",
    "653022": "阿克陶县",
    "653023": "阿合奇县",
    "653024": "乌恰县",
    "653101": "喀什市",
    "653121": "疏附县",
    "653122": "疏勒县",
    "653123": "英吉沙县",
    "653124": "泽普县",
    "653125": "莎车县",
    "653126": "叶城县",
    "653127": "麦盖提县",
    "653128": "岳普湖县",
    "653129": "伽师县",
    "653130": "巴楚县",
    "653131": "塔什库尔干塔吉克自治县",
    "653201": "和田市",
    "653221": "和田县",
    "653222": "墨玉县",
    "653223": "皮山县",
    "653224": "洛浦县",
    "653225": "策勒县",
    "653226": "于田县",
    "653227": "民丰县",
    "654002": "伊宁市",
    "654003": "奎屯市",
    "654004": "霍尔果斯市",
    "654021": "伊宁县",
    "654022": "察布查尔锡伯自治县",
    "654023": "霍城县",
    "654024": "巩留县",
    "654025": "新源县",
    "654026": "昭苏县",
    "654027": "特克斯县",
    "654028": "尼勒克县",
    "654201": "塔城市",
    "654202": "乌苏市",
    "654221": "额敏县",
    "654223": "沙湾县",
    "654224": "托里县",
    "654225": "裕民县",
    "654226": "和布克赛尔蒙古自治县",
    "654301": "阿勒泰市",
    "654321": "布尔津县",
    "654322": "富蕴县",
    "654323": "福海县",
    "654324": "哈巴河县",
    "654325": "青河县",
    "654326": "吉木乃县",
    "659001": "石河子市",
    "659002": "阿拉尔市",
    "659003": "图木舒克市",
    "659004": "五家渠市",
    "659006": "铁门关市"
  }
}

+ 66 - 0
litemall-vue/src/views/user/module-address-edit/index.vue

@ -0,0 +1,66 @@
<template>
  <div>
    <van-nav-bar title="编辑地址" left-text="返回" left-arrow @click-left="goback"/>
    <van-address-edit
      style="background-color: #fff;"
      :areaList="areaList"
      :addressInfo="addressInfo"
      show-set-default
      show-delete
      @save="onSave"
      @delete="onDelete"
    />
  </div>
</template>
<script>
import { AddressEdit, NavBar } from 'vant';
import areaList from './area.json';
import { addressDetail, addressSave, addressDelete } from '@/api/api';
import { removeLocalStorage } from '@/utils/local-storage';
export default {
  name: 'address-edit',
  data() {
    return {
      areaList,
      addressId: 0,
      addressInfo: {}
    };
  },
  created() {
    this.addressId = this.$route.query.addressId;
    if (this.addressId !== -1 && this.addressId !== 0) {
      this.init();
    }
  },
  methods: {
    init() {
      addressDetail({id: this.addressId}).then(res => {
        this.addressInfo = res.data.data;
      });
    },
    onSave(content) {
      addressSave(content).then(res => {
        this.$toast('成功');
        this.$router.go(-1);
      });
    },
    onDelete(content) {
      addressDelete({ id: content.id });
      removeLocalStorage('AddressId')
      this.$router.go(-1);
    },
    goback() {
      this.$router.go(-1);
    }
  },
  components: {
    [NavBar.name]: NavBar,
    [AddressEdit.name]: AddressEdit
  }
};
</script>

+ 73 - 0
litemall-vue/src/views/user/module-address/index.vue

@ -0,0 +1,73 @@
<template>
  <div>
    <van-address-list v-model="chosenAddressId" :list="addressList" @add="onAdd" @edit="onEdit" @select="onSelect"/>
  </div>
</template>
<script>
import { addressList, addressDetail, addressSave, addressDelete } from '@/api/api';
import { AddressList, NavBar } from 'vant';
import { setLocalStorage } from '@/utils/local-storage';
export default {
  data() {
    return {
      chosenAddressId: -1,
      addressList: []
    };
  },
  created() {
    this.loadAddress();
  },
  methods: {
    onAdd() {
      this.$router.push({ name: 'address-edit', query: { addressId: -1 } });
    },
    onEdit(item, index) {
      this.$router.push({ name: 'address-edit', query: { addressId: item.id } });
    },
    onSelect(item, index) {
      setLocalStorage({ AddressId: item.id });
      this.$router.go(-1);
    },         
    goback() {
      this.$router.go(-1);
    },
    loadAddress() {
      addressList().then(res => {
        var list = res.data.data.list;
        for(var i = 0; i < list.length; i++ ){
          var item = list[i]
          this.addressList.push({
            id: item.id,
            name: item.name,
            tel: item.tel,
            address: item.province + item.city + item.county + " " + item.addressDetail
          })
        }
      })
    }
  },
  components: {
    [NavBar.name]: NavBar,
    [AddressList.name]: AddressList 
  }
};
</script>
<style lang="scss" scoped>
.addressGroup {
  margin-bottom: 10px;
  &:last-child {
    margin-bottom: 0;
  }
}
.bottom_btn {
  position: fixed;
  bottom: 0;
}
</style>

+ 98 - 0
litemall-vue/src/views/user/module-collect/index.vue

@ -0,0 +1,98 @@
<template>
  <div class="user_collect">
    <van-list v-model="loading"
              :finished="finished"
              :immediate-check="false"
              finished-text="没有更多了"
              @load="getCollectList">
      <van-card v-for="(item, i) in list"
                :key="i"
                :desc="item.brief"
                :title="item.name"
                :thumb="item.picUrl"
                :price="item.retailPrice"
                :origin-price="item.counterPrice"
                @click="itemClick(item.valueId)">
        <div slot="footer">
          <van-button size="mini"
                      icon="lajitong"
                      @click.stop="cancelCollect($event, i,item)">删除</van-button>
        </div>
      </van-card>
    </van-list>
    <is-empty v-if="list.length === 0">没有商品收藏</is-empty>
  </div>
</template>
<script>
import { collectList, collectAddOrDelete } from '@/api/api';
import IsEmpty from '@/components/is-empty/';
import { Card, Search, List } from 'vant';
import scrollFixed from '@/mixin/scroll-fixed';
export default {
  mixins: [scrollFixed],
  data() {
    return {
      list: [],
      page: 0,
      limit: 10,
      loading: false,
      finished: false
    };
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      this.page = 0;
      this.list = [];
      this.getCollectList()
    },
    getCollectList() {
      this.page++;
      collectList({ type: 0, page: this.page, limit: this.limit }).then(res => {
        this.list.push(...res.data.data.list);
        this.loading = false;
        this.finished = res.data.data.page >= res.data.data.pages;
      });
    },
    cancelCollect(event, i, item) {
      this.$dialog.confirm({ message: '是否取消收藏该商品' }).then(() => {
        collectAddOrDelete({ valueId: item.valueId, type: 0 }).then(res => {
          this.list.splice(i, 1);
        });
      });
    },
    itemClick(id) {
      this.$router.push(`/items/detail/${id}`);
    }
  },
  components: {
    [Search.name]: Search,
    [IsEmpty.name]: IsEmpty,
    [List.name]: List,
    [Card.name]: Card
  }
};
</script>
<style lang="scss" scoped>
.clear_invalid {
  width: 120px;
  color: $font-color-gray;
  border: 1px solid $font-color-gray;
  margin: 0 auto;
  text-align: center;
  padding: 5px 3px;
  margin-top: 20px;
  border-radius: 3px;
}
</style>

+ 94 - 0
litemall-vue/src/views/user/module-feedback/index.vue

@ -0,0 +1,94 @@
<template>
  <div>
<van-cell-group title="反馈类型">
    <van-cell class="order-coupon" :title="type" is-link arrow-direction="down" @click="showList = true" />
</van-cell-group>
<van-cell-group title="反馈内容">
  <van-field v-model="content" 
    clearable autosize center
    placeholder="对我们网站、商品、服务,你还有什么建议吗?你还希望在商城上买到什么?请告诉我们..." 
    type="textarea"
    rows="10"
    size="large"
    />
</van-cell-group>
<van-cell-group title="联系方式">
   <van-field size="large" v-model="mobile" placeholder="请输入联系电话,方便我们与您联系" />
   </van-cell-group>
<van-button size="large" type="primary" @click="submit">提交</van-button>
<van-popup v-model="showList" position="bottom">
<van-picker :columns="types" @change="onType" />
</van-popup>
  </div>
</template>
<script>
import { Field , Picker, Popup, Button } from 'vant';
import { feedbackAdd } from '@/api/api';
export default {
  data() {
    return {
      mobile: '',
      content: '',
      showList: false,
      types:['商品相关', '功能异常', '优化建议', '其他'],
      type: ''
    };
  },
  created() {
  },
  methods: {
    onType(picker, value, index) {
      this.type = value
      this.showList = false
    },
    submit() {
      if(this.mobile === ''){
        this.$toast("请输入联系电话");
        return;
      }
      if(this.type === ''){
        this.$toast("请选择反馈类型");
        return;
      }
      if(this.content === ''){
        this.$toast("请输入反馈内容");
        return;
      }      
      feedbackAdd({ mobile: this.mobile, feedType: this.type, content: this.content}).then(res => {
        this.$toast("感谢您的宝贵意见!");
        this.$router.go(-1);
      })
    }
  },
  components: {
    [Field.name]: Field,
    [Popup.name]: Popup,
    [Button.name]: Button,
    [Picker.name]: Picker
  }
};
</script>
<style lang="scss" scoped>
.addressGroup {
  margin-bottom: 10px;
  &:last-child {
    margin-bottom: 0;
  }
}
.bottom_btn {
  position: fixed;
  bottom: 0;
}
</style>

+ 38 - 0
litemall-vue/src/views/user/module-help/index.vue

@ -0,0 +1,38 @@
<template>
  <div>
 <van-collapse :accordion="true" v-model="activeNames">
  <van-collapse-item :title="issue.question" :name="index" v-for="(issue, index) in issueList" :key="index">
    {{issue.answer}}  
  </van-collapse-item>
</van-collapse>
  </div>
</template>
<script>
import { Collapse, CollapseItem } from 'vant';
import { issueList } from '@/api/api';
export default {
  data() {
    return {
      activeNames: [-1],
      issueList: []
    };
  },
  created() {
    this.getIssueList();
  },
  methods: {
    getIssueList() {
      issueList().then(res => {
        this.issueList = res.data.data.list
      })
    }
  },
  components: {
    [Collapse.name]: Collapse,
    [CollapseItem.name]: CollapseItem
  }
};
</script>

+ 46 - 0
litemall-vue/src/views/user/module-server/index.vue

@ -0,0 +1,46 @@
<template>
  <div>
    <van-cell-group>
      <van-cell title="联系客服" @click="showKefu = true" isLink></van-cell>
      <van-cell title="意见反馈" to="/user/feedback" isLink></van-cell>
      <van-cell title="常见问题" to="/user/help" isLink/>
    </van-cell-group>
    <van-popup v-model="showKefu">
      <van-cell-group>
        <van-cell title="项目名称" value="litemall" />
        <van-cell title="项目地址" value="Github"  url="https://github.com/linlinjava/litemall"/>
        <van-cell title="项目地址" value="Gitee"  url="https://gitee.com/linlinjava/litemall"/>
        <van-cell title="联系电话" value="021-xxxx-xxxx" />
        <van-cell title="联系QQ" value="738696120" />
        <van-cell title="当前版本" value="V1.0" />
        <van-cell title="开源协议" value="MIT" />
      </van-cell-group>
    </van-popup>
  </div>
</template>
<script>
import { Popup, Cell, CellGroup } from 'vant';
export default {
  data() {
    return {
      showKefu: false
    };
  },
  components: {
    [Popup.name]: Popup,
    [Cell.name]: Cell, 
    [CellGroup.name]: CellGroup    
  }
};
</script>
<style scoped lang="scss">
  .van-popup {
    width: 80%;
    padding: 20px;
    box-sizing: border-box;
  }
</style>

+ 214 - 0
litemall-vue/src/views/user/order-list/index.vue

@ -0,0 +1,214 @@
<template>
  <div class="order_list">
    <van-tabs v-model="activeIndex"
              :swipe-threshold="5"
              @click="handleTabClick">
      <van-tab v-for="(tabTitle, index) in tabTitles"
               :title="tabTitle"
               :key="index">
        <van-list v-model="loading"
                  :finished="finished"
                  :immediate-check="false"
                  finished-text="没有更多了"
                  @load="getOrderList">
          <van-panel v-for="(el, i) in orderList"
                     :key="i"
                     :title="'订单编号: ' + el.orderSn"
                     :status="el.orderStatusText"
                     @click.native="toOrderDetail(el.id)">
            <van-card v-for="(goods, goodsI) in el.goodsList"
                      :key="goodsI"
                      :title="goods.goodsName"
                      :num="goods.number"
                      :thumb="goods.picUrl">
              <div slot="desc">
                <div class="desc">
                  <van-tag plain
                           style="margin-right:6px;"
                           v-for="(spec, index) in goods.specifications"
                           :key="index">
                    {{spec}}
                  </van-tag>
                </div>
              </div>
            </van-card>
            <div class="total">合计: {{el.actualPrice * 100 | yuan}}(含运费{{el.post_fee | yuan}})</div>
            <div slot="footer"
                 class="footer_btn">
              <van-button size="small"
                          v-if="el.handleOption.cancel"
                          @click.stop="cancelOrder(el.id)">取消订单</van-button>
              <van-button size="small"
                          v-if="el.handleOption.pay"
                          type="danger"
                          @click.stop="toPay(el.id)">去支付</van-button>
              <van-button size="small"
                          v-if="el.handleOption.refund"
                          type="danger"
                          @click.stop="refundOrder(el.id)">退款</van-button>
              <van-button size="small"
                          v-if="el.handleOption.confirm"
                          type="danger"
                          @click.stop="confirmOrder(el.id)">确认收货</van-button>
              <van-button size="small"
                          v-if="el.handleOption.delete"
                          @click.stop="delOrder(el.id)">删除订单</van-button>
              <van-button size="small"
                          v-if="el.handleOption.comment"
                          @click.stop="commentOrder(el.id)">去评价</van-button>
            </div>
          </van-panel>
        </van-list>
      </van-tab>
    </van-tabs>
  </div>
</template>
<script>
import { orderList, orderDelete, orderConfirm, orderCancel, orderRefund } from '@/api/api';
import _ from 'lodash';
import { Tab, Tabs, Panel, Card, List, Tag } from 'vant';
export default {
  name: 'order-list',
  props: {
    active: {
      type: [String, Number],
      default: 0
    }
  },
  created() {
    this.init();
  },
  data() {
    return {
      activeIndex: Number(this.active),
      tabTitles: ['全部', '待付款', '待发货', '待收货', '待评价'],
      orderList: [],
      page: 0,
      limit: 10,
      loading: false,
      finished: false
    };
  },
  methods: {
    init() {
      this.page = 0;
      this.orderList = [];
      this.getOrderList();
    },
    getOrderList() {
      this.page++;
      orderList({
        showType: this.activeIndex,
        page: this.page,
        limit: this.limit
      }).then(res => {
        this.orderList.push(...res.data.data.list);
        this.loading = false;
        this.finished = res.data.data.page >= res.data.data.pages;
      });
    },
    delOrder(id) {
      let that = this;
      this.$dialog
        .confirm({ message: '确定要删除该订单吗?' })
        .then(() => {
          orderDelete({ orderId: id }).then(() => {
            this.init();
            this.$toast('已删除订单');
          });
        })
        .catch(() => {});
    },
    cancelOrder(id) {
      this.$dialog
        .confirm({ message: '确定要取消该订单吗?' })
        .then(() => {
          orderCancel({ orderId: id }).then(() => {
            this.init();
            this.$toast('已取消该订单');
          });
        })
        .catch(() => {});
    },
    refundOrder(id) {
      this.$dialog
        .confirm({ message: '确定要申请退款吗?' })
        .then(() => {
          orderRefund({ orderId: id }).then(() => {
            this.init();
            this.$toast('已申请订单退款');
          });
        })
        .catch(() => {});
    },    
    confirmOrder(id) {
      this.$dialog
        .confirm({
          message: '请确认收到货物, 确认收货后无法撤销!'
        })
        .then(() => {
          orderConfirm({ orderId: id }).then(() => {
            this.init();
            this.$toast('已确认收货');
          });
        })
        .catch(() => {});
    },
    commentOrder(id) {},
    toPay(id) {
      this.$router.push({ name: 'payment', params: { orderId: id } });
    },
    handleTabClick() {
      this.page = 0;
      this.orderList = [];
      this.getOrderList();
    },
    toOrderDetail(id) {
      this.$router.push({
        path: '/order/order-detail',
        query: { orderId: id }
      });
    }
  },
  components: {
    [Tab.name]: Tab,
    [Tabs.name]: Tabs,
    [Panel.name]: Panel,
    [Card.name]: Card,
    [List.name]: List,
    [Tag.name]: Tag
  }
};
</script>
<style lang="scss" scoped>
.order_list {
  .van-panel {
    margin-top: 20px;
  }
  .van-card {
    background-color: #fff;
  }
  .total {
    text-align: right;
    padding: 10px;
  }
  .footer_btn {
    text-align: right;
    .van-button {
      margin-left: 10px;
    }
  }
}
</style>

+ 148 - 0
litemall-vue/src/views/user/refund-list/index.vue

@ -0,0 +1,148 @@
<template>
  <div class="refund_list">
    <van-tabs sticky :active="activeIndex" :swipe-threshold="5" @click="handleTabClick">
      <van-tab v-for="(tab, tabIndex) in tabsItem" :title="tab.name" :key="tabIndex">
        <van-list v-model="loading"
                  :finished="finished"
                  :immediate-check="false"
                  finished-text="没有更多了"
                  @load="getRefundList">
          <van-panel
            v-for="(el, i) in tab.items"
            class="order_list--panel"
            :key="i"
            :title="'订单编号: ' + el.id"
            :status="getStatusText(el.status)"
          >
            <div>
              <van-card
                class="order_list--van-card"
                :key="i"
                :title="el.orderItem.item_name"
                :desc="el.orderItem.sku_props_str"
                :num="10000"
                :price="(el.orderItem.price / 100).toFixed(2)"
                :thumb="el.orderItem.pic_url"
              />
              <div
                class="order_list--total"
              >合计: {{el.refund_fee | yuan}}(含运费{{el.refund_post_fee | yuan}})</div>
            </div>
            <div slot="footer" style="text-align: right;">
              <van-button
                size="small"
                @click="refund_handle(i)"
              >{{ el.status == 10 ? "撤销申请" : "查看详情"}}</van-button>
            </div>
          </van-panel>
        </van-list>
      </van-tab>
    </van-tabs>
  </div>
</template>
<script>
import { REFUND_LIST } from '@/api/api';
import { Tab, Tabs, Panel, Card, List } from 'vant';
const STATUS_TEXT = {
  10: '退款中',
  50: '退款关闭',
  60: '退款成功'
};
export default {
  name: 'order-list',
  data() {
    return {
      listApi: REFUND_LIST,
      activeIndex: 0,
      items: [],
      page: 0,
      limit: 10,
      loading: false,
      finished: false,
      tabsItem: [
        {
          name: '全部',
          status: 0,
          items: []
        },
        {
          name: '退款中',
          status: 10,
          items: []
        },
        {
          name: '退款成功',
          status: 60,
          items: []
        }
      ]
    };
  },
  methods: {
    onLoad(i, items) {
      this.tabsItem[i].items.push(...items);
    },
    refund_handle(i) {
      const item = this.items[i];
      if (item.status == 10) {
        this.$dialog
          .confirm({
            message: '撤销后将不能再次发起申请,确定要撤销该申请吗?'
          })
          .then(() => {
            this.$toast('已撤销该退款申请');
            this.items[i].status = 50;
          });
      } else {
        // 跳转退款详情
      }
    },
    handleTabClick(index) {
      if (this.activeIndex != index) {
        this.activeIndex = index;
      }
    },
    getStatusText(status) {
      return STATUS_TEXT[status] || '';
    },
    getRefundList(){
    }
  },
  components: {
    [Tab.name]: Tab,
    [Tabs.name]: Tabs,
    [Panel.name]: Panel,
    [Card.name]: Card,
    [List.name]: List
  }
};
</script>
<style lang="scss" scoped>
.refund_list {
  padding-bottom: 0;
  &--footer_btn {
    text-align: right;
  }
  &--panel {
    margin-bottom: 10px;
  }
  &--van-card {
    background-color: #fafafa;
  }
  &--total {
    text-align: right;
    padding: 10px;
  }
}
</style>

+ 86 - 0
litemall-vue/src/views/user/tabbar-user-coupon.vue

@ -0,0 +1,86 @@
<template>
	<div>
		<van-cell-group>
			<van-cell title="我的优惠券" isLink>
				<router-link to="/user/coupon/list/0" class="text-desc">全部优惠券</router-link>
			</van-cell>
		</van-cell-group>
		<van-row class="coupon_status">
			<van-col span="8">
				<div class="coupon_status_icon" @click="$router.push({path: '/user/coupon/list/0'})">
					<van-icon name="coupon" />
				</div>
				<div>待使用</div>
			</van-col>
			<van-col span="8">
				<div class="coupon_status_icon" @click="$router.push({path: '/user/coupon/list/1'})">
					<van-icon name="coupon-used" />
				</div>
				<div>已使用</div>
			</van-col>
			<van-col span="8">
				<div class="coupon_status_icon" @click="$router.push({path: '/user/coupon/list/2'})">
					<van-icon name="coupon-due" />
				</div>
				<div>过期关闭</div>
			</van-col>
		</van-row>
	</div>
</template>
<script>
import { Row, Col } from 'vant';
export default {
  name: 'coupon-group',
  components: {
    [Row.name]: Row,
    [Col.name]: Col
  }
};
</script>
<style scoped lang="scss">
@import '../../assets/scss/mixin';
.coupon_status {
  background-color: #fff;
  text-align: center;
  padding: 10px 0;
  font-size: 12px;
  > div {
    @include one-border;
    &::after {
      top: 50%;
      left: 50%;
      border-bottom: 0;
      border-right: 1px solid $border-color;
      height: 150%;
      transform: scale(0.5) translate3d(-50%, -50%, 0);
      transform-origin: 0 0;
    }
    &:last-child::after {
      border: 0;
    }
  }
  .coupon_status_icon {
    position: relative;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: inline-block;
    i {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate3d(-50%, -50%, 0);
      font-size: 24px;
      color: #000;
    }
  }
}
</style>

+ 77 - 0
litemall-vue/src/views/user/tabbar-user-header.vue

@ -0,0 +1,77 @@
<template>
  <div class="user_header" :style="{backgroundImage: `url(${background_image})`}">
    <van-icon name="set" class="user_set" @click="toSetting"/>
    <div class="user_avatar">
      <img :src="avatar" alt="头像" width="55" height="55">
    </div>
    <div>{{nickName}}</div>
  </div>
</template>
<script>
import avatar_default from '@/assets/images/avatar_default.png';
import bg_default from '@/assets/images/user_head_bg.png';
import { getLocalStorage } from '@/utils/local-storage';
export default {
  name: 'user-header',
  props: {
    isLogin: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      nickName: '昵称',
      avatar: avatar_default,
      background_image: bg_default
    };
  },
  activated() {
    this.getUserInfo();
  },
  methods: {
    getUserInfo() {
      const infoData = getLocalStorage(
        'nickName',
        'avatar'
      );
      this.avatar = infoData.avatar || avatar_default;
      this.nickName = infoData.nickName || '昵称';
    },
    toSetting() {
      this.$router.push({ name: 'user-information' });
    }
  }
};
</script>
<style lang="scss" scoped>
.user_header {
  background-repeat: no-repeat;
  background-size: cover;
  height: 130px;
  text-align: center;
  color: #fff;
  padding-top: 30px;
}
.user_set {
  position: absolute;
  top: 10px;
  right: 10px;
  font-size: 24px;
}
.user_avatar {
  margin-bottom: 10px;
  img {
    border: 0;
    border-radius: 50%;
  }
}
</style>

+ 22 - 0
litemall-vue/src/views/user/tabbar-user-module.vue

@ -0,0 +1,22 @@
<template>
  <div class="user_module">
    <van-cell-group>
      <van-cell icon="shoucang" title="我的收藏" to="/user/collect" isLink/>
      <van-cell icon="dingwei" title="收货地址" to="/user/address" isLink/>
      <van-cell icon="kefu" title="服务中心" to="/user/server" isLink/>
    </van-cell-group>
  </div>
</template>
<script>
export default {
  name: 'user-module'
};
</script>
<style scoped lang="scss">
.user_module {
  background-color: #fff;
}
</style>

+ 107 - 0
litemall-vue/src/views/user/tabbar-user-order.vue

@ -0,0 +1,107 @@
<template>
  <div>
    <van-cell-group>
      <van-cell title="我的订单" isLink>
        <router-link to="/user/order/list/0" class="text-desc">全部订单</router-link>
      </van-cell>
    </van-cell-group>
    <van-row class="order_status">
      <van-col span="6">
        <div class="order_status_icon" @click="$router.push({path: '/user/order/list/1'})">
          <van-icon name="daifukuan" :info="order.unpaid > 0 ? order.unpaid : ''"/>
        </div>
        <div>待付款</div>
      </van-col>
      <van-col span="6">
        <div class="order_status_icon" @click="$router.push({path: '/user/order/list/2'})">
          <van-icon name="daifahuo" :info="order.unship > 0 ? order.unship : ''"/>
        </div>
        <div>待发货</div>
      </van-col>
      <van-col span="6">
        <div class="order_status_icon" @click="$router.push({path: '/user/order/list/3'})">
          <van-icon name="wuliu" :info="order.unrecv > 0 ? order.unrecv : ''"/>
        </div>
        <div>待收货</div>
      </van-col>
      <van-col span="6">
        <div class="order_status_icon" @click="$router.push({path: '/user/order/list/4'})">
          <van-icon name="shouhouguanli" :info="order.uncomment > 0 ? order.uncomment : ''"/>
        </div>
        <div>已完成</div>
      </van-col>
    </van-row>
  </div>
</template>
<script>
import { Row, Col } from 'vant';
import { userIndex } from '@/api/api';
export default {
  name: 'order-group',
  data() {
    return {
      order: []
    };
  },
  created() {
    this.init();
  },
  methods: {
    init() {
      userIndex().then(res => {
      this.order = res.data.data.order;
      });
    }
  },
  components: {
    [Row.name]: Row,
    [Col.name]: Col
  }
};
</script>
<style scoped lang="scss">
@import '../../assets/scss/mixin';
.order_status {
  background-color: #fff;
  text-align: center;
  padding: 10px 0;
  font-size: 12px;
  > div {
    @include one-border;
    &::after {
      top: 50%;
      left: 50%;
      border-bottom: 0;
      border-right: 1px solid $border-color;
      height: 150%;
      transform: scale(0.5) translate3d(-50%, -50%, 0);
      transform-origin: 0 0;
    }
    &:last-child::after {
      border: 0;
    }
  }
  .order_status_icon {
    position: relative;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: inline-block;
    i {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate3d(-50%, -50%, 0);
      font-size: 24px;
      color: #000;
    }
  }
}
</style>

+ 54 - 0
litemall-vue/src/views/user/tabbar-user.vue

@ -0,0 +1,54 @@
<template>
  <div class="tabbar-user">
    <user-header :isLogin="isLogin"/>
    <order-group/>
    <coupon-group/>
    <user-module/>
  </div>
</template>
<script>
import userHeader from './tabbar-user-header';
import orderGroup from './tabbar-user-order';
import couponGroup from './tabbar-user-coupon';
import userModule from './tabbar-user-module';
export default {
  data() {
    return {
      isLogin: false
    };
  },
  activated() {
    this.getLoginStatus();
  },
  methods: {
    getLoginStatus() {
      this.isLogin =
        !!localStorage.getItem('Authorization');
    }
  },
  components: {
    [userHeader.name]: userHeader,
    [orderGroup.name]: orderGroup,
    [couponGroup.name]: couponGroup,
    [userModule.name]: userModule
  }
};
</script>
<style scoped lang="scss">
.tabbar-user {
  > div {
    margin-bottom: 10px;
  }
  &__quit {
    border: 0;
    border-radius: 0;
  }
}
</style>

+ 131 - 0
litemall-vue/src/views/user/user-information-set/index.vue

@ -0,0 +1,131 @@
<template>
  <div class="user_information">
    <van-cell-group>
      <van-cell title="头像" class="cell_middle">
        <van-uploader :afterRead="avatarAfterRead">
          <div class="user_avatar_upload">
            <img
              :src="avatar + '?x-oss-process=image/resize,m_fill,h_50,w_50'"
              alt="你的头像"
              v-if="avatar"
            >
            <van-icon name="camera_full" v-else></van-icon>
          </div>
        </van-uploader>
      </van-cell>
      <van-cell title="昵称" to="/user/information/setNickname" :value="nickName" isLink/>
      <van-cell title="性别" :value="genderText" @click="showSex = true" isLink/>
      <van-cell title="密码设置" to="/user/information/setPassword" isLink/>
      <van-cell title="手机号" to="/user/information/setMobile" :value="mobile" isLink></van-cell>
    </van-cell-group>
    <van-button size="large" class="user_quit" @click="loginOut">退出当前账户</van-button>
    <van-popup v-model="showSex" position="bottom">
      <van-picker
        showToolbar
        :columns="sexColumns"
        title="选择性别"
        @cancel="showSex = false"
        @confirm="onSexConfirm"
      />
    </van-popup>
  </div>
</template>
<script>
import { Uploader, Picker, Popup, Button } from 'vant';
import { removeLocalStorage } from '@/utils/local-storage';
import { getLocalStorage } from '@/utils/local-storage';
import { authInfo, authLogout, authProfile } from '@/api/api';
export default {
  data() {
    return {
      sexColumns: [
        {
          values: ['保密', '男', '女'],
          defaultIndex: 0
        }
      ],
      showSex: false,
      avatar: '',
      nickName: '',
      gender: 0,
      mobile: ''
    };
  },
  computed: {
    genderText() {
      const text = ['保密', '男', '女'];
      return text[this.gender] || '';
    }
  },
  created() {
    this.getUserInfo();
  },
  methods: {
    avatarAfterRead(file) {
      console.log(file);
    },
    onSexConfirm(value, index) {
      this.showSex = false;
    },
    getUserInfo() {
      authInfo().then(res => {
        this.avatar = res.data.data.avatar;
        this.nickName = res.data.data.nickName;
        this.gender = res.data.data.gender;
        this.mobile = res.data.data.mobile;
      })
    },
    loginOut() {
      authLogout().then(res => {
        removeLocalStorage('Authorization')
        removeLocalStorage('avatar')
        removeLocalStorage('nickName')
        this.$router.push({ name: 'home' });
      });
    }
  },
  components: {
    [Button.name]: Button,
    [Uploader.name]: Uploader,
    [Picker.name]: Picker,
    [Popup.name]: Popup
  }
};
</script>
<style lang="scss" scoped>
.user_information {
  .user_avatar_upload {
    position: relative;
    width: 50px;
    height: 50px;
    border: 1px solid $border-color;
    img {
      max-width: 100%;
      max-height: 100%;
    }
    i {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      font-size: 20px;
      color: $border-color;
    }
  }
  .user_quit {
    margin-top: 20px;
  }
}
</style>

+ 111 - 0
litemall-vue/src/views/user/user-information-set/set-mobile/index.vue

@ -0,0 +1,111 @@
<template>
	<div>
		<van-cell-group>
			<van-field
				label="登录密码"
				v-model="password"
				type="password"
				placeholder="请输入登录密码"
				 />
			<van-field
				label="新手机号"
				v-model="mobile"
				placeholder="请输入新手机号"
				/>
			<van-field
				label="验证码"
				v-model="code"
				@click-icon="getCode"
				placeholder="请输入验证码">
				<span slot="icon"
					class="verifi_code red"
					:class="{verifi_code_counting: counting}"
					@click="getCode">
					<countdown v-if="counting" :time="60000" @end="countdownend">
					  <template slot-scope="props">{{ +props.seconds || 60 }}秒后获取</template>
					</countdown>
					<span v-else>获取验证码</span>
				</span>
			</van-field>
		</van-cell-group>
		<div class="bottom_btn">
			<van-button size="large" type="danger" @click="saveMobile">保存</van-button>
		</div>
	</div>
</template>
<script>
import { authCaptcha } from '@/api/api';
import { Field } from 'vant';
export default {
  data: () => ({
    password: '',
    mobile: '',
    code: '',
    counting: false
  }),
  methods: {
    getCode() {
      if (!this.counting && this.vuelidate()) {
        authCaptcha({
          mobile: this.mobile,
          type: 'bind-mobile'
        }).then(() => {
          this.$toast.success('发送成功');
          this.counting = true;
        }).catch(error => {
          this.$toast.fail(error.data.errmsg);
          this.counting = false;
        })
      }
    },
    countdownend() {
      this.counting = false;
    },
    vuelidate() {
      if(this.mobile === ''){
        this.$toast.fail('请输入号码');
        return false;
      }
      return true;
    },
    saveMobile() {
      console.log('保存手机号');
    }
  },
  components: {
    [Field.name]: Field
  }
};
</script>
<style lang="scss" scoped>
@import '../../../../assets/scss/var';
@import '../../../../assets/scss/mixin';
.bottom_btn {
  padding: 30px 15px 0 15px;
}
.verifi_code {
  @include one-border;
  padding-left: 10px;
  &::after {
    border-bottom: 0;
    border-left: 1px solid $border-color;
  }
  &_counting {
    color: $font-color-gray;
  }
}
</style>

+ 52 - 0
litemall-vue/src/views/user/user-information-set/set-nickname/index.vue

@ -0,0 +1,52 @@
<template>
  <div class="set_nickname">
    <van-cell-group>
      <van-field v-model="nickName" label="昵称" />
    </van-cell-group>
    <div class="bottom_btn">
      <van-button size="large" type="danger" @click="saveNick">保存</van-button>
    </div>
  </div>
</template>
<script>
import { authProfile } from '@/api/api';
import { Field } from 'vant';
export default {
  data() {
    return {
      nickName: ''
    };
  },
  created() {
    this.getNick();
  },
  methods: {
    getNick() {
      this.nickName = localStorage.getItem('nickName') || '';
    },
    saveNick() {
      authProfile({ nickname: this.nickName }).then(res => {
        localStorage.setItem('nickName', this.nickName);
        this.$dialog.alert({ message: '保存成功' }).then(() => {
          this.$router.go(-1);
        });
      });
    }
  },
  components: {
    [Field.name]: Field
  }
};
</script>
<style scoped>
.bottom_btn {
  padding: 30px 15px 0 15px;
}
</style>

+ 0 - 0
litemall-vue/src/views/user/user-information-set/set-password/index.vue


Някои файлове не бяха показани, защото твърде много файлове са промени