Browse Source

first commit

wjy 5 years ago
commit
3a64fbaa51
92 changed files with 16391 additions and 0 deletions
  1. 21 0
      .gitignore
  2. 21 0
      README.md
  3. 14 0
      babel.config.js
  4. 11141 0
      package-lock.json
  5. 50 0
      package.json
  6. 5 0
      postcss.config.js
  7. BIN
      public/favicon.ico
  8. 23 0
      public/index.html
  9. 88 0
      scripts/component.js
  10. 166 0
      scripts/template.js
  11. 140 0
      src/App.vue
  12. BIN
      src/assets/image/alipay.jpg
  13. BIN
      src/assets/image/banner/banner_1.jpg
  14. BIN
      src/assets/image/banner/banner_2.jpg
  15. BIN
      src/assets/image/banner/banner_3.jpg
  16. BIN
      src/assets/image/exit_03.png
  17. BIN
      src/assets/image/ico-10.png
  18. BIN
      src/assets/image/ico-11.png
  19. BIN
      src/assets/image/ico-12.png
  20. BIN
      src/assets/image/ico-13.png
  21. BIN
      src/assets/image/ico-14.png
  22. BIN
      src/assets/image/ico_01.png
  23. BIN
      src/assets/image/ico_02.png
  24. BIN
      src/assets/image/ico_03.png
  25. BIN
      src/assets/image/ico_04.png
  26. BIN
      src/assets/image/ico_05.png
  27. BIN
      src/assets/image/ico_06.png
  28. BIN
      src/assets/image/ico_07.png
  29. BIN
      src/assets/image/ico_08.png
  30. BIN
      src/assets/image/ico_09.png
  31. BIN
      src/assets/image/login_03.png
  32. BIN
      src/assets/image/login_07.png
  33. BIN
      src/assets/image/login_11.png
  34. BIN
      src/assets/image/login_15.png
  35. BIN
      src/assets/image/logo.png
  36. BIN
      src/assets/image/service.all.png
  37. BIN
      src/assets/image/service.half.png
  38. BIN
      src/assets/image/wxpay.png
  39. BIN
      src/assets/image/yen.png
  40. 92 0
      src/assets/scss/common.scss
  41. 138 0
      src/assets/scss/form1.scss
  42. 13 0
      src/assets/scss/variables.scss
  43. 54 0
      src/components/Carousel/Carousel.vue
  44. 50 0
      src/components/InfiniteScroll/InfiniteScroll.vue
  45. 29 0
      src/config/index.ts
  46. 164 0
      src/extend/Http.ts
  47. 159 0
      src/extend/HttpRequest.ts
  48. 402 0
      src/extend/Utils.ts
  49. 145 0
      src/main.ts
  50. 38 0
      src/router/index.ts
  51. 204 0
      src/router/router.ts
  52. 13 0
      src/shims-tsx.d.ts
  53. 4 0
      src/shims-vue.d.ts
  54. 24 0
      src/store/index.ts
  55. 36 0
      src/store/module/index.ts
  56. 41 0
      src/store/module/login.ts
  57. 42 0
      src/store/module/other.ts
  58. 42 0
      src/store/module/payment.ts
  59. 61 0
      src/store/module/social.ts
  60. 42 0
      src/store/module/user.ts
  61. 8 0
      src/types/components/carousel.interface.ts
  62. 0 0
      src/types/index.ts
  63. 14 0
      src/types/views/index.interface.ts
  64. 16 0
      src/types/views/login.interface.ts
  65. 13 0
      src/types/views/other.interface.ts
  66. 13 0
      src/types/views/payment.interface.ts
  67. 19 0
      src/types/views/social.interface.ts
  68. 13 0
      src/types/views/user.interface.ts
  69. 140 0
      src/views/index/calc.vue
  70. 0 0
      src/views/index/index.scss
  71. 39 0
      src/views/index/index.ts
  72. 75 0
      src/views/index/index.vue
  73. 151 0
      src/views/login/identify.vue
  74. 137 0
      src/views/login/login.scss
  75. 163 0
      src/views/login/login.ts
  76. 103 0
      src/views/login/login.vue
  77. 120 0
      src/views/order/detail.vue
  78. 150 0
      src/views/order/list.vue
  79. 146 0
      src/views/order/payment.ts
  80. 116 0
      src/views/order/payment.vue
  81. 205 0
      src/views/other/other.vue
  82. 139 0
      src/views/social/social.scss
  83. 135 0
      src/views/social/social.ts
  84. 202 0
      src/views/social/social.vue
  85. 180 0
      src/views/user/edit.vue
  86. 187 0
      src/views/user/index.vue
  87. 167 0
      src/views/user/info.vue
  88. 59 0
      src/views/user/invitation.vue
  89. 11 0
      src/views/user/inviter.vue
  90. 40 0
      tsconfig.json
  91. 125 0
      tslint.json
  92. 43 0
      vue.config.js

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+.DS_Store
+node_modules
+/dist
+
+# 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*

+ 21 - 0
README.md

@@ -0,0 +1,21 @@
+# hippo
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Lints and fixes files
+```
+npm run lint
+```

+ 14 - 0
babel.config.js

@@ -0,0 +1,14 @@
+module.exports = {
+    presets: [
+        '@vue/app'
+    ],
+    plugins: [
+        [
+            "component",
+            {
+                "libraryName": "element-ui",
+                "styleLibraryName": "theme-chalk"
+            }
+        ]
+    ]
+};

File diff suppressed because it is too large
+ 11141 - 0
package-lock.json


+ 50 - 0
package.json

@@ -0,0 +1,50 @@
+{
+  "name": "hippo",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint",
+    "component": "node scripts/component",
+    "page": "node scripts/template"
+  },
+  "dependencies": {
+    "axios": "^0.18.1",
+    "bootstrap": "^4.3.1",
+    "element-ui": "^2.4.5",
+    "jquery": "^3.4.1",
+    "swiper": "^4.5.0",
+    "vue": "^2.6.6",
+    "vue-awesome-swiper": "^3.1.3",
+    "vue-class-component": "^6.0.0",
+    "vue-infinite-scroll": "^2.0.2",
+    "vue-property-decorator": "^7.0.0",
+    "vue-router": "^3.0.1",
+    "vuex": "^3.0.1",
+    "vuex-class": "^0.3.1",
+    "weixin-jsapi": "^1.1.0"
+  },
+  "devDependencies": {
+    "@types/swiper": "^4.4.2",
+    "@vue/cli-plugin-babel": "^3.0.3",
+    "@vue/cli-plugin-typescript": "^3.0.3",
+    "@vue/cli-service": "^3.0.3",
+    "babel-plugin-component": "^1.1.1",
+    "node-sass": "^4.9.0",
+    "sass-loader": "^7.1.0",
+    "typescript": "^3.0.0",
+    "vue-cli-plugin-element": "^1.0.1",
+    "vue-template-compiler": "^2.5.21"
+  },
+  "postcss": {
+    "plugins": {
+      "autoprefixer": {}
+    }
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 8"
+  ]
+}

+ 5 - 0
postcss.config.js

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

BIN
public/favicon.ico


+ 23 - 0
public/index.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title>hippo</title>
+    <style>
+        body {
+            background: #dddddd;
+        }
+    </style>
+</head>
+<body>
+<noscript>
+    <strong>We're sorry but hippo doesn't work properly without JavaScript enabled. Please enable it to
+        continue.</strong>
+</noscript>
+<div id="app"></div>
+<!-- built files will be auto injected -->
+</body>
+</html>

+ 88 - 0
scripts/component.js

@@ -0,0 +1,88 @@
+/*
+ * @Description: 组件快速生成脚本
+ * @Date: 2018-12-06 10:26:23
+ * @LastEditTime: 2018-12-12 16:09:40
+ */
+
+const fs = require('fs');
+const path = require('path');
+const basePath = path.resolve(__dirname, '../src');
+
+const dirName = process.argv[2];
+const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1);
+if (!dirName) {
+  console.log('文件夹名称不能为空!');
+  console.log('示例:npm run tep ${capPirName}');
+  process.exit(0)
+}
+
+/**
+ * @msg: vue页面模版
+ */
+const VueTep = `<template>
+  <div class="${dirName}-wrap">
+    {{data.componentName}}
+  </div>
+</template>
+
+<script lang="ts">
+  import { Component, Vue, Prop } from "vue-property-decorator"
+  import { Getter, Action } from 'vuex-class'
+  import { ${capPirName}Data } from '@/types/components/${dirName}.interface'
+  // import {  } from "@/components" // 组件
+
+  @Component({})
+  export default class About extends Vue {
+    // prop
+    @Prop({
+      required: false,
+      default: ''
+    }) name!: string
+
+    // data
+    data: ${capPirName}Data = {
+      componentName: '${dirName}'
+    }
+
+    created() {
+      //
+    }
+    
+    activated() {
+      //
+    }
+
+    mounted() {
+      //
+    }
+
+  }
+</script>
+
+<style lang="scss">
+  @import "@/assets/scss/variables.scss";
+
+  .${dirName}-wrap {
+    width: 100%;
+  }
+</style>
+
+`;
+
+// interface 模版
+const interfaceTep = `// ${dirName}.Data 参数类型
+export interface ${capPirName}Data {
+  componentName: string
+}
+
+`;
+
+fs.mkdirSync(`${basePath}/components/${dirName}`); // mkdir
+
+process.chdir(`${basePath}/components/${dirName}`); // cd views
+fs.writeFileSync(`${dirName}.vue`, VueTep); // vue
+
+process.chdir(`${basePath}/types/components`); // cd components
+fs.writeFileSync(`${dirName}.interface.ts`, interfaceTep); // interface
+
+process.exit(0);

+ 166 - 0
scripts/template.js

@@ -0,0 +1,166 @@
+/*
+ * @Description: 页面快速生成脚本
+ * @Date: 2018-12-06 10:28:08
+ * @LastEditTime: 2018-12-12 17:02:36
+ */
+const fs = require('fs');
+const path = require('path');
+const basePath = path.resolve(__dirname, '../src');
+
+const dirName = process.argv[2];
+const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1);
+if (!dirName) {
+  console.log('文件夹名称不能为空!');
+  console.log('示例:npm run tep ${capPirName}');
+  process.exit(0)
+}
+
+/**
+ * @msg: vue页面模版
+ */
+const VueTep = `<template>
+  <div class="${dirName}-wrap">
+    {{data.pageName}}
+  </div>
+</template>
+
+<script lang="ts" src="./${dirName}.ts"></script>
+
+<style lang="scss">
+  @import './${dirName}.scss'
+</style>
+
+`;
+
+// ts 模版
+const tsTep = `import { Component, Vue } from "vue-property-decorator"
+import { Getter, Action } from "vuex-class"
+import { ${capPirName}Data } from '@/types/views/${dirName}.interface'
+// import {  } from "@/components" // 组件
+
+@Component({})
+export default class About extends Vue {
+  // Getter
+  // @Getter author
+  
+  // Action
+  // @Action GET_DATA_ASYN
+
+  // data
+  data: ${capPirName}Data = {
+    pageName: '${dirName}'
+  }
+
+  created() {
+    //
+  }
+  
+  activated() {
+    //
+  }
+
+  mounted() {
+    //
+  }
+
+  // 初始化函数
+  init() {
+    //
+  }
+    
+}
+`;
+
+// scss 模版
+const scssTep = `@import "@/assets/scss/variables.scss";
+
+.${dirName}-wrap {
+  width: 100%;
+}
+`;
+
+// interface 模版
+const interfaceTep = `// ${dirName}.Data 参数类型
+export interface ${capPirName}Data {
+  pageName: string
+}
+
+// VUEX ${dirName}.State 参数类型
+export interface ${capPirName}State {
+  author?: string
+}
+
+// GET_DATA_ASYN 接口参数类型
+// export interface DataOptions {}
+
+`;
+
+// vuex 模版
+const vuexTep = `
+import { ${capPirName}State } from '@/types/views/${dirName}.interface'
+import { GetterTree, MutationTree, ActionTree } from 'vuex'
+
+const state: ${capPirName}State = {
+  author: '三毛'
+}
+
+// 强制使用getter获取state
+const getters: GetterTree<${capPirName}State, any> = {
+  author: (state: ${capPirName}State) => state.author
+}
+
+// 更改state
+const mutations: MutationTree<${capPirName}State> = {
+  // 更新state都用该方法
+  UPDATE_STATE(state: ${capPirName}State, data: ${capPirName}State) {
+    for (const key in data) {
+      if (!data.hasOwnProperty(key)) { return }
+      state[key] = data[key]
+    }
+  }
+}
+
+const actions: ActionTree<${capPirName}State, any> = {
+  UPDATE_STATE_ASYN({ commit, state: ${capPirName}State }, data: ${capPirName}State) {
+    commit('UPDATE_STATE', data)
+  },
+  // GET_DATA_ASYN({ commit, state: LoginState }) {
+  //   ${capPirName}.getData()
+  // }
+}
+
+export default {
+  state,
+  getters,
+  mutations,
+  actions
+}
+
+`;
+
+// api 接口模版
+const apiTep = `import Api from '@/utils/request'
+
+export const getData = (data) => {
+  return Api.getData(data)
+}
+
+`;
+
+fs.mkdirSync(`${basePath}/views/${dirName}`); // mkdir
+
+process.chdir(`${basePath}/views/${dirName}`); // cd views
+fs.writeFileSync(`${dirName}.vue`, VueTep); // vue
+fs.writeFileSync(`${dirName}.ts`, tsTep); // ts
+fs.writeFileSync(`${dirName}.scss`, scssTep); // scss
+
+process.chdir(`${basePath}/types/views`); // cd types
+fs.writeFileSync(`${dirName}.interface.ts`, interfaceTep); // interface
+
+process.chdir(`${basePath}/store/module`); // cd store
+fs.writeFileSync(`${dirName}.ts`, vuexTep); // vuex
+
+process.chdir(`${basePath}/api`); // cd api
+fs.writeFileSync(`${dirName}.ts`, apiTep); // api
+
+process.exit(0);

+ 140 - 0
src/App.vue

@@ -0,0 +1,140 @@
+<template>
+    <div id="app">
+        <header class="bar bar-nav d-flex justify-content-between align-items-center p-2">
+            <el-button type="text" icon="el-icon-arrow-left" v-on:click="goBack"
+                       v-if="$route.meta.showBackLeftBtn"></el-button>
+            <h4 class="title">{{title}}</h4>
+            <el-button type="text"><span v-if="$route.meta.showBarRightBtn">编辑</span></el-button>
+        </header>
+        <keep-alive>
+            <router-view class="app-body" v-if="$route.meta.keepAlive"/>
+        </keep-alive>
+        <router-view class="app-body" v-if="!$route.meta.keepAlive"/>
+        <div class="tab-nav nav-bar" v-if="$route.meta.showTab">
+            <ul class="nav d-flex justify-content-around" id="tabs">
+                <li class="active">
+                    <i class="d-block text-center"><img src="./assets/image/ico_01.png" alt=""></i>
+                    <router-link to="/index" v-on:click.native="toggle">代缴服务</router-link>
+                </li>
+                <li class="">
+                    <i class="d-block text-center"><img src="./assets/image/ico_02.png" alt=""></i>
+                    <router-link to="/other" v-on:click.native="toggle">费用说明</router-link>
+                </li>
+                <li class="">
+                    <i class="d-block text-center"><img src="./assets/image/ico_03.png" alt=""></i>
+                    <router-link to="/user" v-on:click.native="toggle">个人中心</router-link>
+                </li>
+            </ul>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {Component,Vue} from "vue-property-decorator";
+    import router from "@/router";
+    import Utils from './extend/Utils';
+
+    @Component({})
+    export default class App extends Vue {
+
+        updated() {
+            // @ts-ignore
+            document.getElementById('tabs').querySelectorAll('li').forEach((item)=>{
+                item.classList.contains('active') && item.classList.remove('active');
+                item.children[1].getAttribute('href') === this.$route.path && item.classList.add('active');
+            });
+        }
+
+        public get title() {
+            return this.$route.meta.title;
+        }
+
+        public goBack() {
+            router.back();
+        }
+
+        public toggle(event: any) {
+            let target = event.target;
+            let li = target.parentElement;
+            let lis = li.parentElement.querySelectorAll("li");
+            lis.forEach((item: any) => {
+                item.classList.remove("active");
+            });
+            li.classList.add("active");
+            return false;
+        }
+
+    }
+</script>
+
+<style lang="scss">
+    @import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
+    @import '@/assets/scss/common.scss';
+
+    #app {
+        position: relative;
+        min-height: 100%;
+        background: white;
+        overflow-x: hidden;
+    }
+
+    #app > div:first-child {
+        padding-bottom: 4.5rem;
+        margin-bottom: 1rem;
+        overflow-x: hidden;
+        font-size: 1.2rem;
+    }
+
+    #carousel {
+        height: 16rem;
+        text-align: center;
+    }
+
+    .tab-nav {
+        position: fixed;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        height: auto;
+        border-bottom: 0;
+        border-top: 1px solid #ddd;
+        background: #fff;
+    }
+
+    .tab-nav li {
+        width: 33%;
+        border-bottom: 0;
+        padding: 0.5rem 0;
+    }
+
+    .tab-nav li.active {
+        border-top: 2px solid $orange;
+        border-bottom: 0;
+        background: rgba(228, 228, 228, .6);
+    }
+
+    .tab-nav li a {
+        height: auto;
+        font-size: 1rem;
+        padding-top: 0.5rem;
+    }
+
+    .tab-nav li img {
+        width: 1.6rem;
+        height: 1.6rem;
+    }
+
+    .app-body {
+        margin-top: 45px;
+        margin-bottom: 5rem;
+        overflow: hidden;
+    }
+
+    .bar-nav .title {
+        position: absolute;
+        left: 0;
+        right: 0;
+        text-align: center;
+    }
+
+</style>

BIN
src/assets/image/alipay.jpg


BIN
src/assets/image/banner/banner_1.jpg


BIN
src/assets/image/banner/banner_2.jpg


BIN
src/assets/image/banner/banner_3.jpg


BIN
src/assets/image/exit_03.png


BIN
src/assets/image/ico-10.png


BIN
src/assets/image/ico-11.png


BIN
src/assets/image/ico-12.png


BIN
src/assets/image/ico-13.png


BIN
src/assets/image/ico-14.png


BIN
src/assets/image/ico_01.png


BIN
src/assets/image/ico_02.png


BIN
src/assets/image/ico_03.png


BIN
src/assets/image/ico_04.png


BIN
src/assets/image/ico_05.png


BIN
src/assets/image/ico_06.png


BIN
src/assets/image/ico_07.png


BIN
src/assets/image/ico_08.png


BIN
src/assets/image/ico_09.png


BIN
src/assets/image/login_03.png


BIN
src/assets/image/login_07.png


BIN
src/assets/image/login_11.png


BIN
src/assets/image/login_15.png


BIN
src/assets/image/logo.png


BIN
src/assets/image/service.all.png


BIN
src/assets/image/service.half.png


BIN
src/assets/image/wxpay.png


BIN
src/assets/image/yen.png


+ 92 - 0
src/assets/scss/common.scss

@@ -0,0 +1,92 @@
+@import "@/assets/scss/variables.scss";
+
+:focus {
+  outline: none;
+}
+
+body, html {
+  height: 100%;
+  font-size: 12px;
+  font-family: SimHei,Arial;
+}
+
+a:active, a:focus, a:hover {
+  color: $green;
+  text-decoration: none;
+}
+
+.hide {
+  display: none;
+}
+
+.nav-bar .nav {
+  overflow: hidden;
+}
+
+.nav-bar li a {
+  color: #757575;
+  text-align: center;
+  display: block;
+  height: 30px;
+  box-sizing: border-box;
+}
+
+.nav-bar li.active a {
+  color: $orange;
+}
+
+.jumbotron{
+  background: #ffffff;
+}
+
+
+.bar-nav {
+  top: 0;
+}
+
+.bar {
+  position: fixed;
+  z-index: 10;
+  right: 0;
+  left: 0;
+  height: 44px;
+  padding-right: 10px;
+  padding-left: 10px;
+  border-bottom: 0;
+  background-color: #f7f7f7;
+  -webkit-backface-visibility: hidden;
+  backface-visibility: hidden;
+}
+
+.bar-nav .el-button:first-child {
+  font-size: 2rem;
+}
+
+.bar-nav .el-button:last-child {
+  font-size: 1.5rem;
+}
+
+.bar-nav .el-button{
+  position: relative;
+  z-index: 100;
+}
+
+button:focus {
+  outline: 0;
+}
+
+.btn.disable {
+  background: #ddd;
+  border-color: #ddd;
+  color: #333;
+}
+
+.form-control.is-valid-fail {
+  border-color: $red;
+  box-shadow: 0 0 0 0.2rem rgba(255, 77, 59, .25);
+}
+
+.form-control.is-valid-success {
+  border-color: $success;
+  box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
+}

+ 138 - 0
src/assets/scss/form1.scss

@@ -0,0 +1,138 @@
+.form-group {
+  padding: 0 20px
+}
+
+.form-group.inline .control-label {
+  float: left;
+  font-size: 1.6rem
+}
+
+.form-group.inline .control-group {
+  overflow: hidden;
+  padding: 5px 0;
+  font-size: 0
+}
+
+.control-group .error {
+  position: relative
+}
+
+.control-group .error input, .control-group .error select {
+  text-shadow: none
+}
+
+.control-group span.hint {
+  display: none
+}
+
+.control-group .control-label {
+  width: 35%;
+  vertical-align: top;
+  line-height: 35px
+}
+
+.control-group .controls {
+  vertical-align: top;
+  margin-left: 35%;
+  text-align: left;
+}
+
+.control-group .controls label {
+  margin-right: 4px;
+  display: inline-block;
+  line-height: 1;
+  vertical-align: middle
+}
+
+.control-group .controls .grey-f {
+  vertical-align: top
+}
+
+.control-group .controls span {
+  font-size: 1.4rem;
+  margin-left: 3px;
+  vertical-align: top
+}
+
+.control-group input[type=text], .control-group input[type=date], .control-group input[type=number] {
+  width: 100%;
+  font-size: 1.4rem
+}
+
+.control-group input.half-control, .control-group input.s-control, .control-group select.half-control, .control-group select.s-control {
+  width: 50%;
+}
+
+.control-group input.triple-control, .control-group select.triple-control {
+  width: 33%
+}
+
+.control-group select {
+  padding: 0 15px 0 0;
+  width: 100%;
+  font-size: 1.4rem;
+  //background: #fff url(../weixin_image/a-down.svg) right 1.4rem no-repeat !important;
+  background-size: 7px 7px !important
+}
+
+.control-group select:active, .control-group select:focus {
+  outline: 0;
+  border: 1px solid transparent
+}
+
+
+.form-group.inline {
+  padding: 0
+}
+
+.form-group.inline .control-group {
+  padding: 5px 20px
+}
+
+.form-group.inline .control-label {
+  width: 30% !important;
+  height: 35px;
+  line-height: 33px
+}
+
+.form-group.inline .controls {
+  margin-left: 30% !important;
+  width: 70%
+}
+
+.control-group + .control-group span.hint {
+  left: 8px;
+  width: 100%
+}
+
+input.s-control + .hint + button, input.s-control + button {
+  width: 45%;
+  height: 35px;
+  line-height: 35px;
+  padding: 0;
+  margin-left: 5%;
+  min-width: 60px
+}
+
+.control-group {
+  margin-bottom: 15px
+}
+
+.control-group img {
+  width: 45%;
+  margin-left: 5%;
+  height: 100%
+}
+
+.control-group .error span.hint {
+  display: block;
+  color: #f78f8f;
+  font-size: 1.2rem;
+  line-height: initial;
+  margin-top: 0.2rem;
+}
+
+.col-form-label{
+  margin-left: 0;
+  margin-right: 0;
+}

+ 13 - 0
src/assets/scss/variables.scss

@@ -0,0 +1,13 @@
+$green: #7bab5e;
+$orange: #ff9c3b;
+$blue: #6f8cc0;
+$gray: #e4e4e4;
+
+$indigo:  #6610f2 !default;
+$purple:  #6f42c1 !default;
+$pink:    #e83e8c !default;
+$red:     #ff4d3b !default;
+$yellow:  #ffc107 !default;
+$success: #28a745 !default;
+$teal:    #20c997 !default;
+//$cyan:    #17a2b8 !default;

+ 54 - 0
src/components/Carousel/Carousel.vue

@@ -0,0 +1,54 @@
+<template>
+    <div class="swiper-container swiper-container-initialized swiper-container-horizontal">
+        <div class="swiper-wrapper">
+            <div class="swiper-slide"><img src="" alt="Slide 3"></div>
+            <div class="swiper-slide"><img src="" alt="Slide 3"></div>
+            <div class="swiper-slide"><img src="" alt="Slide 3"></div>
+        </div>
+        <!-- Add Pagination -->
+        <div class="swiper-pagination swiper-pagination-bullets">
+            <span class="swiper-pagination-bullet swiper-pagination-bullet-active"></span>
+            <span class="swiper-pagination-bullet"></span>
+            <span class="swiper-pagination-bullet"></span>
+        </div>
+        <span class="swiper-notification" aria-live="assertive" aria-atomic="true"></span></div>
+</template>
+
+<script lang="ts">
+    import {Component, Prop, Vue} from "vue-property-decorator";
+    import Swiper from "swiper";
+
+    @Component
+    export default class Carousel extends Vue {
+        @Prop() private banners!: string;
+        private swiper!: Swiper;
+
+        private created() {
+            // empty
+        }
+
+        private mounted() {
+            this.swiper = new Swiper(".swiper-container", {
+                loop: true, autoplay: true,
+                pagination: {
+                    el: '.swiper-pagination',
+                },
+            });
+            document.querySelectorAll(".swiper-slide").forEach((element: any) => {
+                let r = Math.floor(Math.random() * 256);
+                let g = Math.floor(Math.random() * 256);
+                let b = Math.floor(Math.random() * 256);
+                element.style.background = `rgb(${r},${g},${b})`;
+            });
+        }
+
+        private methods() {
+            // empty
+        }
+    }
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+    @import '../../../node_modules/swiper/dist/css/swiper.min.css';
+</style>

+ 50 - 0
src/components/InfiniteScroll/InfiniteScroll.vue

@@ -0,0 +1,50 @@
+<!--<template>-->
+    <!--<div id="wrap">-->
+        <!--<slot></slot>-->
+        <!--<div class="scroll-txt" v-if="scoll">加载中</div>-->
+        <!--<div class="scroll-txt" v-if="!scoll">没有更多数据啦!</div>-->
+    <!--</div>-->
+<!--</template>-->
+
+<!--<script lang="ts">-->
+    <!--import {Component, Prop, Vue} from "vue-property-decorator";-->
+
+    <!--@Component-->
+    <!--export default class InfiniteScroll extends Vue {-->
+        <!--@Prop() private loading: boolean = false;-->
+        <!--@Prop() private scoll: boolean = true;-->
+
+        <!--private time: number = 0;-->
+        <!--private firstTime: boolean = true;-->
+
+        <!--mounted() {-->
+            <!--this.time = new Date().getTime();-->
+            <!--window.addEventListener("scroll", this.scrollFn);-->
+        <!--}-->
+
+        <!--beforeDestroy() {-->
+            <!--window.removeEventListener("scroll", this.scrollFn);-->
+        <!--}-->
+
+        <!--scrollFn() {-->
+            <!--let page = document.querySelector("#wrap") as HTMLElement;-->
+            <!--if (page.offsetHeight - window.pageYOffset - window.screen.height < 100) {-->
+                <!--let curTime = new Date().getTime();-->
+                <!--if (curTime - this.time >= 1000 || this.firstTime) {-->
+                    <!--// 时间差>=1秒直接执行-->
+                    <!--this.firstTime = false;-->
+                    <!--this.loading && this.$emit("onload");-->
+                    <!--this.time = curTime;-->
+                <!--}-->
+            <!--}-->
+        <!--}-->
+    <!--}-->
+<!--</script>-->
+
+<!--<style scoped>-->
+    <!--.scroll-txt {-->
+        <!--text-align: center;-->
+        <!--padding: 10px;-->
+        <!--color: #999999;-->
+    <!--}-->
+<!--</style>-->

+ 29 - 0
src/config/index.ts

@@ -0,0 +1,29 @@
+export const online: string = 'http://hm.gyfledu.com';
+
+export const sandbox: string = 'http://www.hippo.com';
+
+export const host: string = online;
+
+export const params: any = {};
+
+export const http: any = {
+    login: host + '/dev/login',
+    getsocialconfig: host + '/dev/getSocialConfig',
+    getverifycode: host + '/pc/login/getCode',
+    sns: host + '/dev/notify/login',
+    userinfo: host + '/dev/user/getInfo',
+    baseinfo: host + '/dev/user/saveBase',
+    getinvitationlist: host + '/dev/user/getInvitation',
+    calcbill: host + '/dev/getCalcBill',
+    createorder: host + '/dev/order/save',
+    payorder: host + '/dev/order/payment',
+    getorderlist: host + '/dev/order/list',
+    getorderdetail: host + '/dev/order/detail',
+    getconfig: host + '/dev/getConfig',
+    getpayinfo: host + '/dev/getpayinfo',
+    sociallimit: host + '/dev/getSocialLimit',
+    accesstoken: host + '/dev/getAccessToken',
+    getyouhui: host + '/dev/user/getDiscount',
+    getweixin: host + '/dev/order/payment',
+    getalipay: host + '/dev/order/payment',
+};

+ 164 - 0
src/extend/Http.ts

@@ -0,0 +1,164 @@
+import Utils from '@/extend/Utils';
+import HttpRequest from '@/extend/HttpRequest';
+import {host, http} from '@/config';
+import router from '@/router';
+
+export default class Http {
+
+    public static getInstance(...args: any[]): any {
+        let Class: any = this;
+        if (!Class._instance) {
+            let argsLen: number = args.length;
+            if (argsLen === 0) {
+                Class._instance = new Class();
+            } else {
+                Class._instance = new Class(...args);
+            }
+        }
+        return Class._instance;
+    }
+
+    public httpClient: HttpRequest = HttpRequest.getInstance();
+
+    async login(params: any) {
+        return this.httpClient.request({
+            url: http.login,
+            method: 'POST',
+            data: params,
+        });
+    }
+
+    async sns(params: any) {
+        return this.httpClient.request({
+            url: http.sns,
+            data: params,
+        });
+    }
+
+    async userinfo(params: any = null) {
+        return this.httpClient.request({
+            url: http.userinfo,
+            method: 'POST',
+            data: params,
+        });
+    }
+
+    async saveBase(params: any = null) {
+        return this.httpClient.request({
+            url: http.baseinfo,
+            method: 'POST',
+            data: params,
+        });
+    }
+
+    async socialLimit(params: any = null) {
+        return this.httpClient.request({
+            url: http.sociallimit,
+            data: params,
+        });
+    }
+
+    async calcBill(params: any = null) {
+        return this.httpClient.request({
+            url: http.calcbill,
+            data: params,
+        });
+    }
+
+    async saveOrder(params: any = null) {
+        return this.httpClient.request({
+            url: http.createorder,
+            method: 'POST',
+            data: params,
+        });
+    }
+
+    async payOrder(params: any = null) {
+        return this.httpClient.request({
+            url: http.payorder,
+            method: 'POST',
+            data: params,
+        });
+    }
+
+    async getConfig(params: any = null) {
+        return this.httpClient.request({
+            url: http.getconfig,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+    async getPayInfo(params: any = null) {
+        return this.httpClient.request({
+            url: http.getpayinfo,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+    async getOrderList(params: any = null) {
+        return this.httpClient.request({
+            url: http.getorderlist,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+    async getOrderDetail(params: any = null) {
+        return this.httpClient.request({
+            url: http.getorderdetail,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+    async getInvitationList(params: any = null) {
+        return this.httpClient.request({
+            url: http.getinvitationlist,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+    async getSocialConfig(params: any = null) {
+        return this.httpClient.request({
+            url: http.getsocialconfig,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+    async getVerifyCode(params: any = null) {
+        return this.httpClient.request({
+            url: http.getverifycode,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+    async getDiscountListt(params: any = null) {
+        return this.httpClient.request({
+            url: http.getyouhui,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+    async getWeixin(params: any = null) {
+        return this.httpClient.request({
+            url: http.getweixin,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+    async getAlipay(params: any = null) {
+        return this.httpClient.request({
+            url: http.getalipay,
+            method: 'GET',
+            data: params,
+        });
+    }
+
+}

+ 159 - 0
src/extend/HttpRequest.ts

@@ -0,0 +1,159 @@
+import axios, {AxiosResponse, AxiosRequestConfig, AxiosInstance} from 'axios';
+import {host, params, http} from '@/config';
+import router from '@/router';
+import Utils from '@/extend/Utils';
+import {Loading} from 'element-ui';
+
+export default class HttpRequest {
+
+    public static getInstance(...args: any[]): any {
+        let Class: any = this;
+        if (!Class._instance) {
+            let argsLen: number = args.length;
+            if (argsLen === 0) {
+                Class._instance = new Class();
+            } else {
+                Class._instance = new Class(...args);
+            }
+        }
+        return Class._instance;
+    }
+
+    public loadingInstance: any;
+
+    public queue: any; // 请求的url集合
+    public redirect: number;
+    public status: number;
+
+    public constructor() {
+        this.queue = {};
+        this.redirect = 0;
+        this.status = 0;
+    }
+
+    public async request(options: AxiosRequestConfig) {
+        let opts = this.getOpts(options);
+        const instance = axios.create();
+        await this.interceptors(instance, opts);
+        return instance(opts);
+    }
+
+    private interceptors(instance: any, options: AxiosRequestConfig) {
+        // 请求拦截
+        instance.interceptors.request.use((config: AxiosRequestConfig) => {
+            // 添加全局的loading...
+            this.loadingInstance = Loading.service({
+                lock: true,
+                text: 'Loading',
+                spinner: 'el-icon-loading',
+                background: 'rgba(0, 0, 0, 0.7)',
+                fullscreen: false
+            });
+            if (!Object.keys(this.queue).length) {
+                // show loading
+            }
+            if (options && options.url) {
+                this.queue[options.url] = true;
+            }
+            return config;
+        }, (error: any) => {
+            console.log(error);
+        });
+        // 响应拦截
+        instance.interceptors.response.use((res: AxiosResponse) => {
+            this.loadingInstance.close();
+            if (options && options.url) {
+                this.complete(options.url);
+            }
+            const {data, status} = res;
+            if (status === 200 && data && data.code === 1) {
+                return data;
+            } // 请求成功
+            this.redirect = 0;
+            return this.fail(res, options); // 失败回调
+        }, (error: any) => {
+            if (options && options.url) {
+                this.complete(options.url);
+            }
+            console.log(error);
+        });
+    }
+
+    private getOpts(options: AxiosRequestConfig): AxiosRequestConfig {
+        let opts: any = {
+            method: 'GET', timeout: 5000, headers: {
+                'content-type': 'application/json', // 默认值
+                'Authorization': this.getToken('access_token'),
+            }, baseURL: host
+        };
+        opts = Object.assign(opts, options);
+        if (opts.method !== 'GET') {
+            Object.assign(opts, {data: opts.data});
+        } else {
+            Object.assign(opts, {params: opts.data});
+        }
+        return opts;
+    }
+
+    private getToken(name: string) {
+        let token = localStorage.getItem(name);
+        console.log(token);
+        if (token) {
+            return token;
+        } else {
+            return '';
+        }
+    }
+
+    private clearToken() {
+        localStorage.removeItem('access_token');
+        localStorage.removeItem('refresh_token');
+    }
+
+    private async getAcessToken() {
+        let res = await this.request({
+            url: http.accesstoken, method: 'GET', data: {refresh_token: this.getToken('refresh_token')}, baseURL: host,
+        });
+        // @ts-ignore
+        console.log(res);
+        localStorage.setItem('access_token', res.data.access_token);
+    }
+
+    private async fail(res: AxiosResponse, options: AxiosRequestConfig) {
+        let errStr = '网络繁忙!';
+
+        if (this.redirect > 3) {
+            return '网络繁忙';
+        }
+
+        switch (res.data.code.toString()) {
+            case '20002':
+            case '20003':
+            case '20004':
+            case '20005':
+                console.info(res.data.msg);
+                await this.getAcessToken();
+                this.redirect++;
+                delete options.headers;
+                return this.request(options);
+            case '20001':
+            case '20006':
+                this.clearToken();
+                console.log('需要登录');
+                return router.replace({name: 'login'});
+            default:
+                return {
+                    err: console.error({
+                        code: res.data.errcode || res.data.code, msg: res.data.errmsg || errStr
+                    })
+                };
+        }
+    }
+
+    private complete(url: string) {
+        delete this.queue[url];
+        if (!Object.keys(this.queue).length) {
+            // hide loading
+        }
+    }
+}

+ 402 - 0
src/extend/Utils.ts

@@ -0,0 +1,402 @@
+/*
+ * @Description: 公共函数
+ * @Author: asheng
+ * @Date: 2018-12-07 11:36:27
+ * @LastEditors: asheng
+ * @LastEditTime: 2018-12-12 15:22:04
+ */
+
+export default class Utils {
+
+    public static getInstance(...args: any[]): any {
+        let Class: any = this;
+        if (!Class._instance) {
+            let argsLen: number = args.length;
+            if (argsLen === 0) {
+                Class._instance = new Class();
+            } else {
+                Class._instance = new Class(...args);
+            }
+        }
+        return Class._instance;
+    }
+
+    /**
+     * 数组去重
+     * @param arr
+     */
+    public uniqueArray(arr: any): any[] {
+        let newA = arr;
+        newA.sort();
+        let result = [newA[0]];
+        for (let i = 1; i < newA.length; i++) {
+            if (newA[i] !== result[result.length - 1]) {
+                result.push(newA[i]);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 二维数组转置
+     * @param arr
+     */
+    public transposition(arr: any[][]): any[][] {
+        let newArr: any[][] = [];
+        let len0 = arr.length;
+        let len1 = arr[0].length;
+        for (let i = 0; i < len0; i++) {
+            for (let j = 0; j < len1; j++) {
+                if (newArr[j] === undefined) {
+                    newArr[j] = [];
+                    newArr[j] = [];
+                }
+                newArr[j][i] = arr[i][j];
+            }
+        }
+        return newArr;
+    }
+
+    /**
+     * 转驼峰命名
+     * @param {string} str
+     * @returns {string}
+     */
+    public nameToTF(str: string): string {
+        let arr: string[] = [];
+        if (str.indexOf('_') !== -1) {
+            arr = str.split('_');
+        } else if (str.indexOf('-') !== -1) {
+            arr = str.split('-');
+        }
+
+        for (let i = 1; i < arr.length; i++) {
+            arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
+        }
+        return arr.join('');
+    }
+
+    /**
+     * 随机数组
+     * @param len
+     * @param start
+     * @param end
+     * @returns {Array<number>}
+     */
+    public random(len: number, start: number, end: number): number[] {
+        let arr = [];
+
+        while (arr.length < len) {
+            let num = this.randomNum(start, end);
+            // if (arr.indexOf(num) == -1) {
+            //     arr.push(num);
+            // }
+            // @ts-ignore
+            arr.push(num);
+        }
+        return arr;
+    }
+
+    /**
+     * 随机数
+     * @param start
+     * @param end
+     * @returns {number}
+     */
+    public randomNum(start: number, end: number) {
+        let span = end - start;
+        return Math.round(Math.random() * span + start);
+    }
+
+    /**
+     * 洗牌
+     * @param arr
+     */
+    public sortRand(arr: any[][]) {
+        let len0 = arr.length;
+        let len1 = arr[0].length;
+        for (let i = 0; i < arr.length * arr[0].length; i++) {
+            let x1 = this.randomNum(0, len0 - 1);
+            let y1 = this.randomNum(0, len1 - 1);
+            let x2 = this.randomNum(0, len0 - 1);
+            let y2 = this.randomNum(0, len1 - 1);
+            [arr[x1][y1], arr[x2][y2]] = [arr[x2][y2], arr[x1][y1]];
+        }
+        return arr;
+    }
+
+    /**
+     * 获取url中参数
+     * @param url
+     */
+    public getParams(url: string) {
+        if (url.indexOf('?') === -1) {
+            return {};
+        }
+        const keyValueArr = url.split('?')[1].split('&');
+        let paramObj: any = {};
+        keyValueArr.forEach(item => {
+            const keyValue = item.split('=');
+            paramObj[keyValue[0]] = keyValue[1];
+        });
+        return paramObj;
+    }
+
+    /**
+     * 判断一个对象是否存在key,
+     * 如果传入第二个参数key,则是判断这个obj对象是否存在key这个属性
+     * 如果没有传入key这个参数,则判断obj对象是否有键值对
+     * @param obj
+     * @param key
+     */
+    public hasKey(obj: any, key: string | number) {
+        if (key) {
+            return key in obj;
+        } else {
+            const keysArr = Object.keys(obj);
+            return keysArr.length;
+        }
+    };
+
+    /**
+     * 时间格式化
+     * @param fmt
+     * @param date
+     */
+    public date(fmt: any, date?: Date) {
+        let time = '';
+        let datetime: any = date;
+        if (!datetime) {
+            datetime = new Date();
+        }
+        const o: any = {
+            'Y+': datetime.getFullYear(), 'm+': datetime.getMonth() + 1, // 月份
+            'd+': datetime.getDate(), // 日
+            'H+': datetime.getHours(), // 小时
+            'i+': datetime.getMinutes(), // 分
+            's+': datetime.getSeconds(), // 秒
+            'q+': Math.floor((datetime.getMonth() + 3) / 3), // 季度
+            'ms': datetime.getMilliseconds() // 毫秒
+        };
+        for (const k in o) {
+            if (new RegExp('(' + k + ')').test(fmt)) {
+                time = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
+            }
+        }
+        return time;
+    };
+
+    /**
+     * 中文字符串截取
+     * @param str
+     * @param sliceLen
+     */
+    public sliceStr(str: any, sliceLen: number) {
+        if (!str) {
+            return '';
+        }
+        let realLength = 0;
+        const len = str.length;
+        let charCode = -1;
+        for (let i = 0; i < len; i++) {
+            charCode = str.charCodeAt(i);
+            if (charCode >= 0 && charCode <= 128) {
+                realLength += 1;
+            } else {
+                realLength += 2;
+            }
+            if (realLength > sliceLen) {
+                return `${str.slice(0, i)}...`;
+            }
+        }
+
+        return str;
+    }
+
+    /**
+     * 对象克隆
+     * @param jsonObj
+     */
+    public objClone(jsonObj: any) {
+        let buf: any;
+        if (jsonObj instanceof Array) {
+            buf = [];
+            let i = jsonObj.length;
+            while (i--) {
+                buf[i] = this.objClone(jsonObj[i]);
+            }
+            return buf;
+        } else if (jsonObj instanceof Object) {
+            buf = {};
+            for (let k in jsonObj) {
+                buf[k] = this.objClone(jsonObj[k]);
+            }
+            return buf;
+        } else {
+            return jsonObj;
+        }
+    }
+
+    /**
+     * 验证手机号
+     * @param mobile
+     */
+    public isMobile(mobile: string) {
+        return /^1[3|4|5|6|7|8|9][0-9]\d{8}$/i.test(mobile);
+    }
+
+    /**
+     * 判断目标是否在数组中
+     * @param needle
+     * @param array
+     */
+    public inArray(needle: any, array: any[]) {
+        for (let i in array) {
+            if (array[i] === needle) {
+                return i;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 获取当前相对路径
+     */
+    public getUrlRelativePath() {
+        let url = document.location.toString();
+        let arrUrl = url.split('//');
+
+        let start = arrUrl[1].indexOf('/');
+        // stop省略,截取从start开始到结尾的所有字符
+        let relUrl = arrUrl[1].substring(start);
+        if (relUrl.indexOf('?') !== -1) {
+            relUrl = relUrl.split('?')[0];
+        }
+        return relUrl;
+    }
+
+    /**
+     * 表单序列化
+     * @param formBox
+     * @param type
+     */
+    public serialize(formBox: HTMLFormElement, type: string = 'json') {
+        let res: any = {};
+        for (let item of formBox.elements) {
+            // @ts-ignore
+            if (item.disabled) {
+                continue;
+            }
+            // @ts-ignore
+            switch (item.type) {
+                case 'file':    // 文件输入类型
+                case 'submit':  // 提交按钮
+                case 'button':  // 一般按钮
+                case 'image':   // 图像形式的提交按钮
+                case 'reset':   // 重置按钮
+                case undefined: // 未定义
+                    break;
+                // select控件
+                case 'select-one':
+                case 'select-multiple':
+                    // @ts-ignore
+                    if (item.name && item.name.length) {
+                        // @ts-ignore
+                        for (let option of item.options) {
+                            let value = '';
+                            if (option.selected) {
+                                // @ts-ignore
+                                res[item.name] = option.hasAttribute('value') ? option.value : option.text;
+                                break;
+                            }
+                        }
+                    }
+                    break;
+
+                // 单选,复选框
+                case 'radio':
+                case 'checkbox':
+                    // 这里有个取巧 的写法,这里的判断是跟下面的default相互对应。
+                    // 如果放在其他地方,则需要额外的判断取值
+                    // @ts-ignore
+                    if (!item.checked) {
+                        break;
+                    }
+                default:
+                    // 一般表单控件处理
+                    // @ts-ignore
+                    if (item.name && item.name.length) {
+                        // @ts-ignore
+                        res[item.name] = item.value;
+                    }
+            }
+        }
+        if (type === 'formData') {
+            res = encodeURI(Object.keys(res).map((i) => {
+                return i + '=' + res[i];
+            }).join('&'));
+        }
+        return res;
+    }
+
+    /**
+     * 从父元素中删除
+     * @param el
+     */
+    public removeFromParent(el: HTMLElement) {
+        if (el.parentElement) {
+            el.parentElement.removeChild(el);
+        }
+    }
+
+    /**
+     * 添加子元素
+     * @param parentEl
+     * @param text
+     */
+    public append(parentEl: HTMLElement, text: string | HTMLElement) {
+        if (typeof text === 'string') {
+            let temp = document.createElement('div');
+            temp.innerHTML = text;
+            let frag = document.createDocumentFragment();
+            while (temp.firstChild) {
+                frag.appendChild(temp.firstChild);
+            }
+            parentEl.appendChild(frag);
+        } else if (text instanceof HTMLElement) {
+            parentEl.appendChild(text);
+        }
+    }
+
+    /**
+     * 数字零填充
+     * @param num
+     * @param n
+     */
+    public prefixInteger(num: number, n: number = 2) {
+        return (Array(n).join('0') + num).slice(-n);
+    }
+
+    public setCookie(cname: string, cvalue: string, exdays: number) {
+        let d = new Date();
+        d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
+        let expires = 'expires=' + d.toUTCString();
+        console.info(cname + '=' + cvalue + '; ' + expires);
+        document.cookie = cname + '=' + cvalue + '; ' + expires;
+    }
+
+    public getCookie(cname: string) {
+        let reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)');
+        let arr = document.cookie.match(reg);
+        if (arr) {
+            return decodeURI(arr[2]);
+        } else {
+            return null;
+        }
+
+    }
+
+    public clearCookie() {
+        this.setCookie('username', '', -1);
+    }
+}

+ 145 - 0
src/main.ts

@@ -0,0 +1,145 @@
+import Vue from 'vue';
+import App from './App.vue';
+import router from './router';
+import store from './store';
+import 'element-ui/lib/theme-chalk/index.css';
+
+import {
+    Pagination,
+    Dialog,
+    Autocomplete,
+    Dropdown,
+    DropdownMenu,
+    DropdownItem,
+    Menu,
+    Submenu,
+    MenuItem,
+    MenuItemGroup,
+    Input,
+    InputNumber,
+    Radio,
+    RadioGroup,
+    RadioButton,
+    Checkbox,
+    CheckboxButton,
+    CheckboxGroup,
+    Switch,
+    Select,
+    Option,
+    OptionGroup,
+    Button,
+    ButtonGroup,
+    Table,
+    TableColumn,
+    DatePicker,
+    TimeSelect,
+    TimePicker,
+    Popover,
+    Tooltip,
+    Breadcrumb,
+    BreadcrumbItem,
+    Form,
+    FormItem,
+    Tabs,
+    TabPane,
+    Tag,
+    Tree,
+    Alert,
+    Slider,
+    Row,
+    Col,
+    Upload,
+    Progress,
+    Badge,
+    Card,
+    Rate,
+    Steps,
+    Step,
+    Carousel,
+    CarouselItem,
+    Collapse,
+    CollapseItem,
+    Cascader,
+    ColorPicker,
+    Transfer,
+    Container,
+    Header,
+    Aside,
+    Main,
+    Footer,
+    Loading,
+    MessageBox,
+    Message,
+    Notification
+} from 'element-ui';
+
+Vue.use(Pagination);
+Vue.use(Dialog);
+Vue.use(Autocomplete);
+Vue.use(Dropdown);
+Vue.use(DropdownMenu);
+Vue.use(DropdownItem);
+Vue.use(Menu);
+Vue.use(Submenu);
+Vue.use(MenuItem);
+Vue.use(MenuItemGroup);
+Vue.use(Input);
+Vue.use(InputNumber);
+Vue.use(Radio);
+Vue.use(RadioGroup);
+Vue.use(RadioButton);
+Vue.use(Checkbox);
+Vue.use(CheckboxButton);
+Vue.use(CheckboxGroup);
+Vue.use(Switch);
+Vue.use(Select);
+Vue.use(Option);
+Vue.use(OptionGroup);
+Vue.use(Button);
+Vue.use(ButtonGroup);
+Vue.use(Table);
+Vue.use(TableColumn);
+Vue.use(DatePicker);
+Vue.use(TimeSelect);
+Vue.use(TimePicker);
+Vue.use(Popover);
+Vue.use(Tooltip);
+Vue.use(Breadcrumb);
+Vue.use(BreadcrumbItem);
+Vue.use(Form);
+Vue.use(FormItem);
+Vue.use(Tabs);
+Vue.use(TabPane);
+Vue.use(Tag);
+Vue.use(Tree);
+Vue.use(Alert);
+Vue.use(Slider);
+Vue.use(Row);
+Vue.use(Col);
+Vue.use(Upload);
+Vue.use(Progress);
+Vue.use(Badge);
+Vue.use(Card);
+Vue.use(Rate);
+Vue.use(Steps);
+Vue.use(Step);
+Vue.use(Carousel);
+Vue.use(CarouselItem);
+Vue.use(Collapse);
+Vue.use(CollapseItem);
+Vue.use(Cascader);
+Vue.use(ColorPicker);
+Vue.use(Transfer);
+Vue.use(Container);
+Vue.use(Header);
+Vue.use(Aside);
+Vue.use(Main);
+Vue.use(Footer);
+
+Vue.config.productionTip = false;
+
+new Vue({
+    router,
+    store,
+    render: (h) => h(App),
+}).$mount('#app');

+ 38 - 0
src/router/index.ts

@@ -0,0 +1,38 @@
+import Vue from 'vue';
+import Router from 'vue-router';
+import routes from './router';
+import Utils from '@/extend/Utils';
+import HttpRequest from '@/extend/HttpRequest';
+
+Vue.use(Router);
+
+const router = new Router({
+    routes,
+    // mode: 'history'
+});
+
+// 验证路由内 name
+const verify_router_list: string[] = ['index', 'other', 'login'];
+
+// 跳转之前
+router.beforeEach((to, from, next) => {
+    // let token = HttpRequest.getInstance().getToken('refresh_token');
+    // if (Utils.getInstance().inArray(to.name, verify_router_list) === false && !token) {
+    //     // 跳转路径在验证路由内且未登录,跳转至登录
+    //     next({name: 'login', replace: true});
+    // } else if (token && to.name === 'login') {
+    //     // 已登录且要跳转的页面是登录页
+    //     next({name: 'index', replace: true});
+    // } else {
+    //     next();
+    // }
+    next();
+});
+
+
+// 跳转之后
+router.afterEach(to => {
+    //
+});
+
+export default router;

+ 204 - 0
src/router/router.ts

@@ -0,0 +1,204 @@
+/**
+ * meta 可配置参数
+ * @param {boolean} icon 页面icon
+ * @param {boolean} keepAlive 是否缓存页面
+ * @param {string} title 页面标题
+ */
+export default [
+    {
+        path: '/',
+        redirect: '/index'
+    },
+    {
+        path: '/login',
+        name: 'login',
+        component: () => import('@/views/login/login.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: false,
+            showBackLeftBtn: true,
+            showBarRightBtn: false,
+            title: '登录页'
+        }
+    },
+    {
+        path: '/index',
+        name: 'index',
+        component: () => import('@/views/index/index.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: true,
+            showBackLeftBtn: false,
+            showBarRightBtn: false,
+            title: '首页'
+        }
+    },
+    {
+        path: '/calc',
+        name: 'calc',
+        component: () => import('@/views/index/calc.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: true,
+            showBackLeftBtn: false,
+            showBarRightBtn: false,
+            title: '社保计算器'
+        }
+    },
+    {
+        path: '/other',
+        name: 'other',
+        component: () => import('@/views/other/other.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: true,
+            showBackLeftBtn: false,
+            showBarRightBtn: false,
+            title: '缴费说明'
+        }
+    },
+    {
+        path: '/user',
+        component: () => import('@/views/user/index.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: true,
+            showBackLeftBtn: false,
+            showBarRightBtn: false,
+            title: '个人中心'
+        },
+    },
+    {
+        path: '/user/index',
+        name: 'uindex',
+        component: () => import('@/views/user/index.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: true,
+            showBackLeftBtn: true,
+            showBarRightBtn: false,
+            title: '个人中心'
+        }
+    },
+    {
+        path: '/user/info',
+        name: 'uinfo',
+        component: () => import('@/views/user/info.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: false,
+            showBackLeftBtn: true,
+            showBarRightBtn: false,
+            title: '个人信息'
+        }
+    },
+    {
+        path: '/user/edit',
+        name: 'uedit',
+        component: () => import('@/views/user/edit.vue'),
+        meta: {
+            icon: '',
+            keepAlive: false,
+            showTab: false,
+            showBackLeftBtn: true,
+            showBarRightBtn: false,
+            title: '修改信息'
+        }
+    },
+    {
+        path: '/user/invitation',
+        name: 'uinvitation',
+        component: () => import('@/views/user/invitation.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: false,
+            showBackLeftBtn: true,
+            showBarRightBtn: false,
+            title: '好友列表'
+        }
+    },
+    // {
+    //     path: '/user/inviter',
+    //     name: 'inviter',
+    //     component: () => import('@/views/user/inviter.vue'),
+    //     meta: {
+    //         icon: '',
+    //         keepAlive: true,
+    //         showTab: true,
+    //         showBackLeftBtn: true,
+    //         showBarRightBtn: false,
+    //         title: '邀请'
+    //     }
+    // },
+    {
+        path: '/social',
+        name: 'social',
+        component: () => import('@/views/social/social.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: false,
+            showBackLeftBtn: true,
+            showBarRightBtn: false,
+            title: '订单信息'
+        }
+    },
+    {
+        path: '/order',
+        name: 'order',
+        component: () => import('@/views/order/list.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: false,
+            showBackLeftBtn: true,
+            showBarRightBtn: false,
+            title: '订单'
+        },
+    },
+    {
+        path: '/order/list',
+        name: 'olist',
+        component: () => import('@/views/order/list.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: false,
+            showBackLeftBtn: true,
+            showBarRightBtn: false,
+            title: '订单列表'
+        }
+    }, {
+        path: '/order/detail',
+        name: 'odetail',
+        component: () => import('@/views/order/detail.vue'),
+        meta: {
+            icon: '',
+            keepAlive: false,
+            showTab: false,
+            showBackLeftBtn: true,
+            showBarRightBtn: false,
+            title: '订单详情'
+        }
+    },  {
+        path: '/order/payment',
+        name: 'opayment',
+        component: () => import('@/views/order/payment.vue'),
+        meta: {
+            icon: '',
+            keepAlive: true,
+            showTab: false,
+            showBackLeftBtn: false,
+            showBarRightBtn: false,
+            title: '收银台'
+        }
+    },
+];

+ 13 - 0
src/shims-tsx.d.ts

@@ -0,0 +1,13 @@
+import Vue, { VNode } from 'vue';
+
+declare global {
+  namespace JSX {
+    // tslint:disable no-empty-interface
+    interface Element extends VNode {}
+    // tslint:disable no-empty-interface
+    interface ElementClass extends Vue {}
+    interface IntrinsicElements {
+      [elem: string]: any;
+    }
+  }
+}

+ 4 - 0
src/shims-vue.d.ts

@@ -0,0 +1,4 @@
+declare module '*.vue' {
+  import Vue from 'vue';
+  export default Vue;
+}

+ 24 - 0
src/store/index.ts

@@ -0,0 +1,24 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+// modules
+import Login from './module/login'
+import Index from './module/index';
+
+Vue.use(Vuex);
+
+export default new Vuex.Store({
+  state: {
+    //
+  },
+  mutations: {
+    //
+  },
+  actions: {
+    //
+  },
+  modules: {
+    Login,
+    Index
+  }
+})

+ 36 - 0
src/store/module/index.ts

@@ -0,0 +1,36 @@
+import { IndexState } from '@/types/views/index.interface';
+import { GetterTree, MutationTree, ActionTree } from 'vuex';
+
+const state: IndexState = {
+  author: '三毛'
+};
+
+// 强制使用getter获取state
+const getters: GetterTree<IndexState, any> = {
+  author: (state: IndexState) => state.author
+};
+
+// 更改state
+const mutations: MutationTree<IndexState> = {
+  // 更新state都用该方法
+  UPDATE_STATE(state: IndexState, data: IndexState) {
+    for (let key in data) {
+      if (!data.hasOwnProperty(key)) { return }
+      state[key] = data[key];
+    }
+  }
+};
+
+const actions: ActionTree<IndexState, any> = {
+  UPDATE_STATE_ASYN({ commit }, data: IndexState) {
+    commit('UPDATE_STATE', data)
+  },
+};
+
+export default {
+  state,
+  getters,
+  mutations,
+  actions
+}
+

+ 41 - 0
src/store/module/login.ts

@@ -0,0 +1,41 @@
+import { LoginState } from '@/types/views/login.interface'
+import { GetterTree, MutationTree, ActionTree } from 'vuex'
+
+const state: LoginState = {
+  // author: undefined
+};
+
+// 强制使用getter获取state
+const getters: GetterTree<LoginState, any> = {
+  // author: (state: LoginState) => state.author
+};
+
+// 更改state
+const mutations: MutationTree<LoginState> = {
+  // 更新state都用该方法
+  UPDATE_STATE(state: LoginState, data: LoginState) {
+    for (const key in data) {
+      if (!data.hasOwnProperty(key)) { return }
+      state[key] = data[key]
+    }
+  }
+};
+
+const actions: ActionTree<LoginState, any> = {
+  UPDATE_STATE_ASYN({ commit, state: LoginState }, data: LoginState) {
+    commit('UPDATE_STATE', data)
+  },
+  // GET_DATA_ASYN({ commit, state: LoginState }) {
+  //   LoginApi.getData().then(res => {
+  //     commit('UPDATE_STATE')
+  //   })
+  // }
+};
+
+export default {
+  state,
+  getters,
+  mutations,
+  actions
+}
+

+ 42 - 0
src/store/module/other.ts

@@ -0,0 +1,42 @@
+import {OtherState} from '@/types/views/other.interface';
+import {GetterTree, MutationTree, ActionTree} from 'vuex';
+
+const state: OtherState = {
+    author: '三毛'
+};
+
+// 强制使用getter获取state
+const getters: GetterTree<OtherState, any> = {
+    author: (state: OtherState) => state.author
+};
+
+// 更改state
+const mutations: MutationTree<OtherState> = {
+    // 更新state都用该方法
+    UPDATE_STATE(state: OtherState, data: OtherState) {
+        for (const key in data) {
+            if (!data.hasOwnProperty(key)) {
+                return;
+            }
+            // @ts-ignore
+            state[key] = data[key];
+        }
+    }
+};
+
+const actions: ActionTree<OtherState, any> = {
+    UPDATE_STATE_ASYN({commit, state: OtherState}, data: OtherState) {
+        commit('UPDATE_STATE', data);
+    },
+    // GET_DATA_ASYN({ commit, state: LoginState }) {
+    //   Other.getData()
+    // }
+};
+
+export default {
+    state,
+    getters,
+    mutations,
+    actions
+};
+

+ 42 - 0
src/store/module/payment.ts

@@ -0,0 +1,42 @@
+import {PaymentState} from '@/types/views/payment.interface';
+import {GetterTree, MutationTree, ActionTree} from 'vuex';
+
+const state: PaymentState = {
+    author: '三毛'
+};
+
+// 强制使用getter获取state
+const getters: GetterTree<PaymentState, any> = {
+    author: (state: PaymentState) => state.author
+};
+
+// 更改state
+const mutations: MutationTree<PaymentState> = {
+    // 更新state都用该方法
+    UPDATE_STATE(state: PaymentState, data: PaymentState) {
+        for (const key in data) {
+            if (!data.hasOwnProperty(key)) {
+                return;
+            }
+            // @ts-ignore
+            state[key] = data[key];
+        }
+    }
+};
+
+const actions: ActionTree<PaymentState, any> = {
+    UPDATE_STATE_ASYN({commit, state: PaymentState}, data: PaymentState) {
+        commit('UPDATE_STATE', data);
+    },
+    // GET_DATA_ASYN({ commit, state: LoginState }) {
+    //   Payment.getData()
+    // }
+};
+
+export default {
+    state,
+    getters,
+    mutations,
+    actions
+};
+

+ 61 - 0
src/store/module/social.ts

@@ -0,0 +1,61 @@
+import {SocialState} from '@/types/views/social.interface';
+import {GetterTree, MutationTree, ActionTree} from 'vuex';
+
+const state: SocialState = {
+    author: '三毛',
+    isfund: false,
+    serviceChargeData: [
+        {
+            monthly: '月付:100元/月',
+            seasonal: '季付:270元/季',
+            halfYearly: '半年付:480元/半年',
+            yearly: '年付:600元/年',
+        },
+        {
+            monthly: '月付:150元/月',
+            seasonal: '季付:405元/季',
+            halfYearly: '半年付:720元/半年',
+            yearly: '年付:900元/年',
+        }
+    ],
+};
+
+// 强制使用getter获取state
+const getters: GetterTree<SocialState, any> = {
+    author: (state: SocialState) => state.author,
+    serviceChargeData: (state: SocialState) => state.serviceChargeData,
+    service: (state: SocialState) => {
+        return state.isfund ? state.serviceChargeData[1] : state.serviceChargeData[0];
+    }
+};
+
+// 更改state
+const mutations: MutationTree<SocialState> = {
+    // 更新state都用该方法
+    update(state: SocialState, data: SocialState) {
+        for (const key in data) {
+            if (!data.hasOwnProperty(key)) {
+                return;
+            }
+            // @ts-ignore
+            state[key] = data[key];
+        }
+    }
+};
+
+const actions: ActionTree<SocialState, any> = {
+    updateAsyn({commit, state: SocialState}, data: SocialState) {
+        commit('update', data);
+    },
+    // GET_DATA_ASYN({ commit, state: LoginState }) {
+    //   Social.getData()
+    // }
+};
+
+export default {
+    state,
+    getters,
+    mutations,
+    actions
+};
+

+ 42 - 0
src/store/module/user.ts

@@ -0,0 +1,42 @@
+import {UserState} from '@/types/views/user.interface';
+import {GetterTree, MutationTree, ActionTree} from 'vuex';
+
+const state: UserState = {
+    author: '三毛'
+};
+
+// 强制使用getter获取state
+const getters: GetterTree<UserState, any> = {
+    author: (state: UserState) => state.author
+};
+
+// 更改state
+const mutations: MutationTree<UserState> = {
+    // 更新state都用该方法
+    UPDATE_STATE(state: UserState, data: UserState) {
+        for (const key in data) {
+            if (!data.hasOwnProperty(key)) {
+                return;
+            }
+            // @ts-ignore
+            state[key] = data[key];
+        }
+    }
+};
+
+const actions: ActionTree<UserState, any> = {
+    UPDATE_STATE_ASYN({commit, state: UserState}, data: UserState) {
+        commit('UPDATE_STATE', data);
+    },
+    // GET_DATA_ASYN({ commit, state: LoginState }) {
+    //   User.getData()
+    // }
+};
+
+export default {
+    state,
+    getters,
+    mutations,
+    actions
+};
+

+ 8 - 0
src/types/components/carousel.interface.ts

@@ -0,0 +1,8 @@
+// Carousel.Data 参数类型
+import Swiper from 'swiper';
+
+export interface CarouselData {
+    componentName: string,
+    swiper: Swiper,
+}
+

+ 0 - 0
src/types/index.ts


+ 14 - 0
src/types/views/index.interface.ts

@@ -0,0 +1,14 @@
+// index.Data 参数类型
+export interface IndexData {
+  pageName: string
+}
+
+// VUEX index.State 参数类型
+export interface IndexState {
+    [key: string]: any;
+    author?: string
+}
+
+// GET_DATA_ASYN 接口参数类型
+// export interface DataOptions {}
+

+ 16 - 0
src/types/views/login.interface.ts

@@ -0,0 +1,16 @@
+// login.Data 参数类型
+export interface LoginData {
+    [key: string]: any;
+    verify: string,
+    infonam: string,
+}
+
+// VUEX login.State 参数类型
+export interface LoginState {
+    [key: string]: any;
+    author?: any
+}
+
+// GET_DATA_ASYN 接口参数类型
+// export interface DataOptions {}
+

+ 13 - 0
src/types/views/other.interface.ts

@@ -0,0 +1,13 @@
+// other.Data 参数类型
+export interface OtherData {
+  pageName: string
+}
+
+// VUEX other.State 参数类型
+export interface OtherState {
+  author?: string
+}
+
+// GET_DATA_ASYN 接口参数类型
+// export interface DataOptions {}
+

+ 13 - 0
src/types/views/payment.interface.ts

@@ -0,0 +1,13 @@
+// payment.Data 参数类型
+export interface PaymentData {
+  pageName: string
+}
+
+// VUEX payment.State 参数类型
+export interface PaymentState {
+  author?: string
+}
+
+// GET_DATA_ASYN 接口参数类型
+// export interface DataOptions {}
+

+ 19 - 0
src/types/views/social.interface.ts

@@ -0,0 +1,19 @@
+// social.Data 参数类型
+export interface SocialData {
+    pageName: string;
+    socialBasic: number;
+    fundBasic?: number;
+    serviceChargeType: string;
+}
+
+// VUEX social.State 参数类型
+export interface SocialState {
+    author?: string;
+    isfund: boolean;
+    serviceChargeData: [any, any];
+    serviceCharge?: any;
+}
+
+// GET_DATA_ASYN 接口参数类型
+// export interface DataOptions {}
+

+ 13 - 0
src/types/views/user.interface.ts

@@ -0,0 +1,13 @@
+// user.Data 参数类型
+export interface UserData {
+  pageName: string
+}
+
+// VUEX user.State 参数类型
+export interface UserState {
+  author?: string
+}
+
+// GET_DATA_ASYN 接口参数类型
+// export interface DataOptions {}
+

+ 140 - 0
src/views/index/calc.vue

@@ -0,0 +1,140 @@
+<template>
+    <div class="detail-wrap small">
+        <div class="form-group row">
+            <label class="col-3 col-form-label">社保户别</label>
+            <div class="col-9">
+                <el-radio v-model="data.social_type" v-on:change="socialLimit" label="1">城镇</el-radio>
+                <el-radio v-model="data.social_type" v-on:change="socialLimit" label="2">农村</el-radio>
+            </div>
+        </div>
+        <div class="form-group row">
+            <label class="col-3 col-form-label">社保缴纳</label>
+            <div class="col-9">
+                <input type="number" class="form-control" v-model="data.social_value"
+                       :min="data.social_basic.pension[0]"
+                       :max="data.social_basic.pension[1]" required>
+            </div>
+        </div>
+        <el-switch v-model="data.is_fund" active-text="公积金缴纳">
+        </el-switch>
+        <div id="fund" class="form-group row">
+            <label class="col-3 col-form-label">公积金缴纳</label>
+            <div class="col-9" v-if="data.is_fund">
+                <input type="number" class="form-control" v-model="data.fund_value"
+                       :min="data.social_basic.fund[0]"
+                       :max="data.social_basic.fund[1]"
+                       required>
+            </div>
+        </div>
+        <div class="form-group row">
+            <label class="col-3 col-form-label">缴费明细</label>
+            <div class="col-9">
+                <ul class="list-group list-group-flush">
+                    <li class="list-group-item d-flex justify-content-between align-items-center"
+                        v-for="value in data.social_calc">
+                        {{value[1]}}
+                        <span class="badge badge-pill">&yen;{{value[0]}}</span>
+                    </li>
+                </ul>
+                <div class="mt-2 d-flex justify-content-between align-items-center"
+                     v-if="data.subtotal_calc">
+                    <label class="col-form-label">社保小计</label>
+                    <span class="badge">&yen;{{data.subtotal_calc}}</span>
+                </div>
+                <div class="mt-2 d-flex justify-content-between align-items-center"
+                     v-if="data.is_fund&&data.fund_calc">
+                    <label class="col-form-label">公积金</label>
+                    <span class="badge">&yen;{{data.fund_calc}}</span>
+                </div>
+            </div>
+        </div>
+        <div class="form-group">
+            <a href="javascript:void(0)" class="btn btn-success btn-block"
+               v-on:click="calcBill">开始计算</a>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {Component, Vue} from "vue-property-decorator";
+    import Http from "../../extend/Http";
+    import router from "../../router";
+    import Utils from "../../extend/Utils";
+
+    @Component({})
+    export default class OrderList extends Vue {
+
+        public data: any = {
+            is_fund: true,
+            is_discounts: false,
+            social_calc: null,
+            service_calc: 0,
+            fund_calc: 0,
+            subtotal_calc: 0,
+            total_calc: 0,
+            social_basic: {pension: 3400, fund: 3400},
+            social_type: null,
+            social_value: 3387,
+            fund_value: 3387,
+        };
+
+        async created() {
+            this.socialLimit();
+        }
+
+        async socialLimit(event?: any) {
+            if (this.data.social_type != null) {
+                let res = await Http.getInstance().socialLimit({
+                    socialType: this.data.social_type
+                });
+                this.data.social_basic = res.data;
+            }
+        }
+
+        async calcBill(event?: any) {
+            let res = await Http.getInstance().calcBill({
+                'bill_type': 'calc',
+                'social_type': this.data.social_type,
+                'social_basic': this.data.social_value,
+                'fund_basic': this.data.fund_value,
+            });
+            this.data.social_calc = res.data.social;
+            this.data.service_calc = res.data.service;
+            this.data.fund_calc = res.data.fund;
+            this.data.subtotal_calc = res.data.subtotal;
+            this.data.total_calc = res.data.total;
+            this.data.is_discounts = res.data.discounts;
+        }
+
+    }
+</script>
+
+<style lang="scss">
+    .detail-wrap {
+        width: 100%;
+        padding: 2rem;
+    }
+
+    hr {
+        margin: 0.5rem 0;
+    }
+
+    .list-group-flush {
+        border-bottom: 1px solid rgba(0, 0, 0, .125);
+    }
+
+    .list-group-flush .list-group-item {
+        border-top: 0;
+        background: transparent;
+    }
+
+    .list-group-flush .list-group-item:last-child {
+        border-bottom: 0;
+    }
+
+    .subtotal {
+        padding: .75rem 1.25rem;
+        padding-left: 2.5rem;
+    }
+</style>
+

+ 0 - 0
src/views/index/index.scss


+ 39 - 0
src/views/index/index.ts

@@ -0,0 +1,39 @@
+import {Component, Vue} from 'vue-property-decorator';
+import {Getter, Action} from 'vuex-class';
+import {IndexData} from '@/types/views/index.interface';
+import Carousel from '@/components/Carousel/Carousel.vue';
+// import {  } from "@/components" // 组件
+
+@Component({
+    components: {
+    },
+})
+export default class Index extends Vue {
+    // Getter
+    // @Getter author
+
+    // Action
+    // @Action GET_DATA_ASYN: any;
+
+    // data
+    data: any = {
+    };
+
+    created() {
+        // this.GET_DATA_ASYN();
+    }
+
+    activated() {
+        //
+    }
+
+    mounted() {
+        //
+    }
+
+    // 初始化函数
+    init() {
+        //
+    }
+
+}

+ 75 - 0
src/views/index/index.vue

@@ -0,0 +1,75 @@
+<template>
+    <div class="index-wrap">
+        <el-carousel :interval="4000" indicator-position="none">
+            <el-carousel-item>
+                <img src="../../assets/image/banner/banner_1.jpg" alt="">
+            </el-carousel-item>
+            <el-carousel-item>
+                <img src="../../assets/image/banner/banner_2.jpg" alt="">
+            </el-carousel-item>
+            <el-carousel-item>
+                <img src="../../assets/image/banner/banner_3.jpg" alt="">
+            </el-carousel-item>
+        </el-carousel>
+        <section class="index-main">
+            <router-link class="btn-lg btn-calc text-left" to="/calc">
+                <img src="../../assets/image/ico_04.png" alt="">
+                <span>社保计算机</span></router-link>
+            <router-link class="btn-lg btn-social text-left" to="/social">
+                <img src="../../assets/image/ico_05.png" alt="">
+                <span>社保代缴</span>
+            </router-link>
+            <router-link class="btn-lg btn-social text-left" to="/social?is_fund=true">
+                <img src="../../assets/image/ico_06.png" alt="">
+                <span>社保及公积金代缴</span>
+            </router-link>
+        </section>
+    </div>
+</template>
+<script lang="ts" src="./index.ts"></script>
+
+<style lang="scss">
+    @import '@/assets/scss/variables.scss';
+
+    .index-wrap {
+        width: 100%;
+        overflow-x: hidden;
+    }
+
+    section.index-main a.btn-lg {
+        display: block;
+        color: #ffffff;
+        margin: 1.25rem;
+        height: 5.8rem;
+        line-height: 4.5rem;
+        padding-left: 4rem;
+        border-radius: 1rem;
+    }
+
+    section a img {
+        width: 4.2rem;
+        margin-right: 3rem;
+    }
+
+    .btn-calc {
+        background: $orange;
+    }
+
+    .btn-social {
+        background: $green;
+    }
+
+    .btn-fund {
+        background: $blue;
+    }
+
+    .el-carousel__container{
+        height: 200px;
+    }
+
+    .el-carousel__item img{
+        width: 100%;
+        height: auto;
+    }
+</style>
+

+ 151 - 0
src/views/login/identify.vue

@@ -0,0 +1,151 @@
+<template>
+    <div class="s-canvas" :width="contentWidth" :height="contentHeight">
+        <canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas>
+    </div>
+</template>
+<script>
+    export default {
+        name: "SIdentify",
+        props: {
+            identifyCode: {
+                type: String,
+                default: "1234"
+            },
+            fontSizeMin: {
+                type: Number,
+                default: 30
+            },
+            fontSizeMax: {
+                type: Number,
+                default: 40
+            },
+            backgroundColorMin: {
+                type: Number,
+                default: 180
+            },
+            backgroundColorMax: {
+                type: Number,
+                default: 240
+            },
+            colorMin: {
+                type: Number,
+                default: 50
+            },
+            colorMax: {
+                type: Number,
+                default: 160
+            },
+            lineColorMin: {
+                type: Number,
+                default: 40
+            },
+            lineColorMax: {
+                type: Number,
+                default: 180
+            },
+            dotColorMin: {
+                type: Number,
+                default: 0
+            },
+            dotColorMax: {
+                type: Number,
+                default: 255
+            },
+            contentWidth: {
+                type: Number,
+                default: 100
+            },
+            contentHeight: {
+                type: Number,
+                default: 38
+            }
+        },
+        methods: {
+            // 生成一个随机数
+            randomNum(min, max) {
+                return Math.floor(Math.random() * (max - min) + min)
+            },
+            // 生成一个随机的颜色
+            randomColor(min, max) {
+                let r = this.randomNum(min, max)
+                let g = this.randomNum(min, max)
+                let b = this.randomNum(min, max)
+                return "rgb(" + r + "," + g + "," + b + ")"
+            },
+            drawPic() {
+                let canvas = document.getElementById("s-canvas")
+                let ctx = canvas.getContext("2d")
+                ctx.textBaseline = "bottom"
+                // 绘制背景
+                ctx.fillStyle = this.randomColor(
+                    this.backgroundColorMin,
+                    this.backgroundColorMax
+                );
+                ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
+                // 绘制文字
+                for (let i = 0; i < this.identifyCode.length; i++) {
+                    this.drawText(ctx, this.identifyCode[i], i)
+                }
+                this.drawLine(ctx)
+                this.drawDot(ctx)
+            },
+            drawText(ctx, txt, i) {
+                ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax);
+                ctx.font =
+                    this.randomNum(this.fontSizeMin, this.fontSizeMax) + "px SimHei";
+                let x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1));
+                let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5);
+                var deg = this.randomNum(-45, 45);
+                // 修改坐标原点和旋转角度
+                ctx.translate(x, y);
+                ctx.rotate((deg * Math.PI) / 180);
+                ctx.fillText(txt, 0, 0);
+                // 恢复坐标原点和旋转角度
+                ctx.rotate((-deg * Math.PI) / 180);
+                ctx.translate(-x, -y);
+            },
+            drawLine(ctx) {
+                // 绘制干扰线
+                for (let i = 0; i < 2; i++) {
+                    ctx.strokeStyle = this.randomColor(
+                        this.lineColorMin,
+                        this.lineColorMax
+                    );
+                    ctx.beginPath();
+                    ctx.moveTo(
+                        this.randomNum(0, this.contentWidth),
+                        this.randomNum(0, this.contentHeight)
+                    );
+                    ctx.lineTo(
+                        this.randomNum(0, this.contentWidth),
+                        this.randomNum(0, this.contentHeight)
+                    );
+                    ctx.stroke();
+                }
+            },
+            drawDot(ctx) {
+                // 绘制干扰点
+                for (let i = 0; i < 100; i++) {
+                    ctx.fillStyle = this.randomColor(0, 255);
+                    ctx.beginPath()
+                    ctx.arc(
+                        this.randomNum(0, this.contentWidth),
+                        this.randomNum(0, this.contentHeight),
+                        1,
+                        0,
+                        2 * Math.PI
+                    )
+                    ctx.fill()
+                }
+            }
+        },
+        watch: {
+            identifyCode() {
+                this.drawPic()
+            }
+        },
+        mounted() {
+            this.drawPic()
+        }
+    }
+</script>

+ 137 - 0
src/views/login/login.scss

@@ -0,0 +1,137 @@
+.login-wrap {
+  width: 100%;
+  overflow-x: hidden;
+  background: white;
+  margin-top: 10rem;
+}
+
+.password_login {
+  margin-top: 0
+}
+.pwd .hint {
+  width: auto;
+  left: auto
+}
+
+.captcha_login .control-group .btn {
+  width: 45%;
+  height: 3.5rem;
+  margin-left: 5%;
+  min-width: 6rem;
+  line-height: 3.5rem;
+}
+.header.tc img {
+  width: 65%;
+  margin: 2rem 0;
+}
+  //.row{
+  //  display: block;
+  //}
+.col-code{
+  width: 16rem;
+}
+.form-group{
+  border-bottom: 1px solid #cccccc;
+}
+.two-imgcode{
+  position: relative;
+}
+.imginput{
+  width: 60% !important;
+}
+.col-5{
+  max-width: 92%;
+}
+.col-6{
+  flex: 0 0 27%;
+}
+.ml-2{
+  margin-left: 0.5rem !important ;
+  background: #04b954;
+  border: none;
+  font-size: 1rem;
+  padding-top: 0.7rem;
+  border-radius: 2rem;
+  height: 3rem;
+}
+.mt-3{
+  border: none;
+}
+.m-4{
+  margin-left: 2rem !important;
+}
+.btn-primary{
+  background: #04b954;
+  border: none;
+  font-size:1.6rem ;
+  border-radius: 2rem;
+  margin-top: 4rem !important;
+  height: 3.6rem;
+  padding-top: 0.6rem !important;
+}
+.col-phone{
+  width: 100%;
+  height: 4rem;
+}
+.col-imgcard{
+  width: 93%;
+  height: 4rem;
+  right: 0;
+  //position: relative;
+}
+.col-imgcard{
+  //display: flex;
+  //justify-content: center;
+  //align-items: center;
+}
+.code-btn{
+  margin-top: 0.8rem !important;
+  position: absolute;
+  right: 0rem;
+  width: 8.8rem;
+  bottom: 0.1rem;
+}
+.code-aaa{
+  position: relative;
+}
+.col-7{
+  width: 15rem;
+}
+.col-code{
+  position: absolute;
+  left: 1.8rem;
+  bottom: 0.1rem;
+  width: 60%;
+}
+.phones{
+  width: 1.8rem ;
+  height: 2.5rem;
+}
+.phoness{
+  width: 1.8rem ;
+  height: 2.5rem;
+  margin-top: 1rem;
+}
+.imgcode{
+  font-size: 2rem;
+  margin-bottom: 1rem;
+  position: absolute;
+  top: 0.75rem;
+  right:0rem;
+}
+.form-control{
+  display: inline;
+  font-size:1.2rem;
+  width: 90%;
+  border:none;
+  outline: none;
+  margin-top: 1rem;
+  margin-left: 1rem;
+}
+.phone-input{
+  width: 62% !important;
+}
+.fromdata-aaa{
+  width:30rem;
+}
+

+ 163 - 0
src/views/login/login.ts

@@ -0,0 +1,163 @@
+import {Component, Vue, Watch} from 'vue-property-decorator';
+import {LoginData} from '@/types/views/login.interface';
+import Sidentify from './identify.vue'
+import {host, http} from '@/config';
+import router from '@/router';
+import Utils from '@/extend/Utils';
+import Http from '@/extend/Http';
+import qs from 'qs'
+
+@Component({
+    components: {
+        Sidentify
+    }
+})
+export default class Login extends Vue {
+
+    // data
+    data: any = {
+        verifyImg: host + '/captcha.html',
+        infonam: 'hint',
+        type: 'mobile',
+        identifyCodes: '1234567890',
+        identifyCode: '',
+        mobile: '',
+        imgVerify: '',
+        codeVerify: '',
+        errorType: {
+            mobile: '',
+            imgVerify: '',
+            codeVerify: '',
+        },
+        verifyBtnActive: true,
+        verifyBtnText: '获取验证码',
+    };
+    private timers: any;
+    private urlParams: any;
+
+    @Watch('data.mobile') mobileWatch(newVal: string, oldVal: string) {
+        this.data.errorType.mobile = Utils.getInstance().isMobile(newVal);
+    }
+
+    @Watch('data.imgVerify') imgVerifyWatch(newVal: string, oldVal: string) {
+        this.data.errorType.imgVerify = /^[0-9a-zA-Z]{4}$/.test(this.data.imgVerify);
+    }
+
+    @Watch('data.codeVerify') codeVerifyWatch(newVal: string, oldVal: string) {
+        this.data.errorType.codeVerify = /^[0-9]{4}$/.test(this.data.codeVerify);
+    }
+
+    // 验证码
+   randomNum (min, max) {
+        return Math.floor(Math.random() * (max - min) + min)
+    }
+   refreshCode () {
+        this.data.identifyCode = ''
+        this.makeCode(this.data.identifyCodes, 4)
+    }
+   makeCode (o,l) {
+        for (let i = 0; i < l; i++) {
+            this.data.identifyCode += this.data.identifyCodes[this.randomNum(0, this.data.identifyCodes.length)]
+        }
+    }
+
+    async created() {
+
+        if (!localStorage.getItem('refresh_token')) {
+            let ua = window.navigator.userAgent.toLowerCase();
+            this.urlParams = Utils.getInstance().getParams(location.href);
+
+            if (ua.match(/MicroMessenger/i) && !this.urlParams.code) {
+                this.data.type = 'weixin';
+                location.href = host + '/dev?platform=weixin&callback=' + location.href;
+            } else if (this.urlParams.code) {
+                let data = await Http.getInstance().sns(Object.assign({platform: 'weixin'}, this.urlParams));
+                this.loginCallBack(data);
+            }
+        }
+        this.data.identifyCode = ''
+        this.makeCode(this.data.identifyCodes, 4)
+        // this.refreshCode()
+    }
+
+    async loginform(event: any) {
+        this.data.errorType = [];
+        if (!Utils.getInstance().isMobile(this.data.mobile)) {
+            this.data.errorType.push('mobile');
+            return false;
+        }
+        if (/^[0-9a-zA-Z]{4}$/.test(this.data.imgVerify) === false) {
+            this.data.errorType.push('imgVerify');
+            return false;
+        }
+        if (/^[0-9]{4}$/.test(this.data.codeVerify) === false) {
+            this.data.errorType.push('codeVerify');
+            return false;
+        }
+        let post = Object.assign(this.urlParams,Utils.getInstance().serialize(document.getElementById('form-data')));
+
+        let data = await Http.getInstance().login(post);
+
+        this.loginCallBack(data);
+        return false;
+    }
+
+    async loginCallBack(data: any) {
+        if (data.code === 1) {
+            for (let index in data.data) {
+                localStorage.setItem(index, data.data[index]);
+            }
+            router.replace('user');
+        } else if (data.code === '20201') {
+            this.data.errorType.push('mobile');
+        } else if (data.code.toString() === '20202') {
+            this.data.errorType.push('imgVerify');
+        } else if (data.code.toString() === '20203') {
+            this.data.errorType.push('codeVerify');
+        } else {
+            alert(data.msg);
+        }
+    }
+
+    reloadVerify(event: any) {
+        let src = event.target.getAttribute('src');
+        if (src && src.indexOf('?') > 0) {
+            event.target.setAttribute('src', src + '&random=' + Math.random());
+        } else if (src) {
+            event.target.setAttribute('src', src.replace(/\?.*$/, '') + '?' + Math.random());
+        }
+    }
+
+    getMobileVerify(event: any) {
+        if (!Utils.getInstance().isMobile(this.data.mobile)) {
+            this.data.errorType.push('mobile');
+            alert('手机号有误')
+            return false;
+
+        }
+        if( this.data.imgVerify != this.data.identifyCode ){
+            alert('验证码有误')
+            return false
+        }
+        Http.getInstance().getVerifyCode({
+            mobile:  this.data.mobile
+        });
+        this.data.verifyBtnActive = false;
+        this.data.verifyBtnText = '60 s';
+        this.timers = this.countDown();
+    }
+
+    private countDown() {
+        return setInterval(() => {
+            let number = parseInt(this.data.verifyBtnText, 10);
+            if (number <= 0) {
+                clearInterval(this.timers);
+                this.data.verifyBtnActive = true;
+                this.data.verifyBtnText = '获取验证码';
+            } else {
+                this.data.verifyBtnText = number - 1 + ' s';
+            }
+        }, 1000);
+    }
+
+}

+ 103 - 0
src/views/login/login.vue

@@ -0,0 +1,103 @@
+<template>
+    <div class="login-wrap text-center container" v-if="data.type!='weixin'">
+        <!--<div class="nav-bar">-->
+        <!--<ul class="nav">-->
+        <!--<li class="active"><a href="#captcha_login"  v-on:click="tab">验证码登录</a></li>-->
+        <!--<li class=""><a href="#password_login"  v-on:click="tab">密码登录</a></li>-->
+        <!--</ul>-->
+        <!--</div>-->
+        <header class="header tc">
+            <img src="../../assets/image/login_03.png" class="d-ib logo"></header>
+        <div id="captcha_login" class="row login-item">
+            <!--添加员工信息-->
+            <form action="" class="text-left m-4 p-3 fromdata-aaa" id="form-data">
+                <div class="form-group row">
+<!--                    <label for="mobile" class="col-4 col-form-label">手机号码</label>-->
+                    <div class="col-phone">
+                        <img class="phones"  src="../../assets/image/login_07.png" alt="">
+                        <input type="text" class="form-control phone-input" placeholder="请输入手机号" id="mobile" name="username"
+                               v-model="data.mobile"
+                               v-bind:class="{'is-valid-success': data.errorType.mobile === true, 'is-valid-fail': data.errorType.mobile === false}"
+                        >
+                    </div>
+                </div>
+                <div class="form-group row two-imgcode">
+<!--                    <label for="verify_img" class="col-4 col-form-label">图形验证码</label>-->
+                    <img  class="phoness" src="../../assets/image/login_11.png" alt="">
+                    <div class="col-imgcard">
+
+                        <input type="text" class="d-inline-block col-5 form-control imginput" placeholder="请输入图形验证码" id="verify_img" name="verify"
+                               v-model="data.imgVerify"
+                               v-bind:class="{'is-valid-success': data.errorType.imgVerify === true, 'is-valid-fail': data.errorType.imgVerify === false}"
+                        >
+<!--                        <img class="d-ib fr  imgcode" id="" :src="data.verifyImg" v-on:click="reloadVerify">-->
+                       <span class="d-ib fr  imgcode"   v-on:click="refreshCode">
+                           <Sidentify :identifyCode="data.identifyCode"></Sidentify>
+                       </span>
+                    </div>
+                </div>
+                <div class="form-group row code-aaa" >
+                    <img  class="phoness" src="../../assets/image/login_15.png" alt="">
+                    <div class="col-code">
+                        <input type="text" class="d-inline-block col-5 form-control" placeholder="请输入短信验证码" id="verify_code" name="password"
+                               v-model="data.codeVerify"
+                               v-bind:class="{'is-valid-success': data.errorType.codeVerify === true, 'is-valid-fail': data.errorType.codeVerify === false}"
+                        >
+                    </div>
+                    <a href="javascript:void(0);" class="btn btn-info ml-2 col-6 code-btn"
+                       v-on:click="getMobileVerify"
+                       v-bind:class="{'disable': !data.verifyBtnActive}"
+                    >{{data.verifyBtnText}}</a>
+
+                </div>
+                <div class="form-group row mt-3">
+                    <div class="col-12 text-center">
+                        <a href="javascript:void(0);" class="btn btn-block btn-primary mt-2 p-3" v-on:click="loginform">注册/登录</a>
+                    </div>
+                </div>
+            </form>
+        </div>
+        <!--<p class="text-center">用户注册代表同意<a href="/GrHelp/registerContract">《用户注册协议》</a></p>-->
+        <!--<div id="password_login" class="row login-item hide">
+            &lt;!&ndash;添加员工信息&ndash;&gt;
+            <div class="form-group inline">
+                <form class="" id="form-data2" method="post">
+                    <div class="control-group bg-w ">
+                        <label class="control-label">
+                            <i class="icon-mobile m-f"></i>
+                            <span class="v-m">+86</span>
+                        </label>
+                        <div class="controls">
+                            <input type="tel" value="" placeholder="手机号" name="mobile" id="pwd_mobile"
+                                   oninput="if(value.length>11)value=value.slice(0,11)">
+                            <input type="hidden" value="" name="openid">
+                            <input type="hidden" value="" name="unionid">
+                            <input type="hidden" value="app" name="state">
+
+                            <input type="hidden" value="is_password" name="type">
+                        </div>
+                    </div>
+                    <div class="control-group bg-w">
+                        <label class="control-label">
+                            <i class="icon-shield m-f"></i>
+                        </label>
+                        <div class="controls pwd">
+                            <input type="password" value="" id="password" name="password" class="" placeholder="密码">
+                        </div>
+                    </div>
+                </form>
+            </div>
+            &lt;!&ndash;添加员工完毕 &ndash;&gt;
+            <div class="btn-group">
+                <a href="javascript:void(0);" id="psLogin" class="btn tn full-w">登录</a>
+            </div>
+        </div>-->
+    </div>
+</template>
+
+<script lang="ts" src="./login.ts"></script>
+
+<style lang="scss">
+    @import "login";
+</style>
+

+ 120 - 0
src/views/order/detail.vue

@@ -0,0 +1,120 @@
+<template>
+    <div class="detail-wrap small">
+        <div class="detail social-detail btn-light">
+            <h6 class="d-flex justify-content-between align-items-center">
+                <label class="badge m-0 p-0">订单编号:{{data.order_sn}}</label>
+                <label class="badge">{{data.status}}</label>
+            </h6>
+            <hr>
+            <h6 class="d-flex justify-content-between align-items-center pb-2">
+                <label class="small">下单时间</label>
+                <label class="small">{{data.create_timestamp}}</label>
+            </h6>
+        </div>
+        <div class="detail social-detail btn-light">
+            <h6 class="d-flex justify-content-between align-items-center small">
+                社保
+                <label class="p-0 m-0 small">缴费基数:&yen;<span class="badge">{{data.social_basic}}</span></label>
+            </h6>
+            <hr>
+            <ul class="list-group list-group-flush pl-4">
+                <li class="list-group-item d-flex justify-content-between align-items-center"
+                    v-for="item in data.charge_details.social">
+                    {{item[1]}}
+                    <span class="badge">&yen;{{item[0]}}</span>
+                </li>
+            </ul>
+            <p class="subtotal d-flex justify-content-between align-items-center">
+                社保小计
+                <span class="badge">&yen;{{data.charge_details.subtotal}}</span>
+            </p>
+        </div>
+        <div class="detail social-detail btn-light" v-if="data.charge_details.fund">
+            <h6 class="d-flex justify-content-between align-items-center small">
+                公积金
+                <label class="p-0 m-0 small">缴费基数:&yen;<span class="badge">{{data.fund_basic}}</span></label>
+            </h6>
+            <hr>
+            <p class="d-flex justify-content-between align-items-center small pb-3">
+                公积金
+                <span class="badge">&yen;{{data.charge_details.fund}}</span>
+            </p>
+        </div>
+        <div class="detail social-detail btn-light">
+            <h6 class="d-flex justify-content-between align-items-center small">
+                小计
+                <label class="p-0 m-0 small">&yen;<span class="badge">{{data.charge_details.total}}</span></label>
+            </h6>
+            <h6 class="d-flex justify-content-between align-items-center small">
+                本次服务费
+                <span class="badge">&yen;{{data.service_charge}}</span>
+            </h6>
+            <hr>
+            <h6 class="d-block badge pb-3 text-right">
+                应付总计:&yen;{{data.total_charge}}
+            </h6>
+            <h6 class="d-block badge pb-3 text-right">
+                实付款:&yen;{{data.payment}}
+            </h6>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {Component, Vue} from "vue-property-decorator";
+    import Http from "../../extend/Http";
+    import router from "../../router";
+
+    @Component({})
+    export default class OrderList extends Vue {
+
+        public data: any = {};
+
+        async created() {
+            let res = await Http.getInstance().getOrderDetail({
+                order_sn: '201904081235302513625396',
+                // order_sn: this.$route.params["order_sn"],
+            });
+            this.data = res.data;
+            this.data.charge_details.fund = this.data.charge_details.fund == 0 ? false : this.data.charge_details.fund;
+            this.data["status"] = this.$route.params["status"];
+            this.data["payment"] = this.$route.params["payment"];
+            this.data["create_timestamp"] = this.$route.params["create_timestamp"];
+        }
+
+    }
+</script>
+
+<style lang="scss">
+    .detail-wrap {
+        width: 100%;
+    }
+
+    hr {
+        margin: 0.5rem 0;
+    }
+
+    .detail {
+        padding: 1.5rem;
+        padding-bottom: 0;
+    }
+
+    .list-group-flush {
+        border-bottom: 1px solid rgba(0, 0, 0, .125);
+    }
+
+    .list-group-flush .list-group-item {
+        border-top: 0;
+        background: transparent;
+    }
+
+    .list-group-flush .list-group-item:last-child {
+        border-bottom: 0;
+    }
+
+    .subtotal {
+        padding: .75rem 1.25rem;
+        padding-left: 2.5rem;
+    }
+</style>
+

+ 150 - 0
src/views/order/list.vue

@@ -0,0 +1,150 @@
+<template>
+    <div class="list-wrap">
+        <!--<div class="nav-bar order-status-tab">-->
+        <!--<ul class="nav d-flex justify-content-around">-->
+        <!--<li class="active"><a href="#order_pay_yes" data-status="2" v-on:click="tab">已付款</a></li>-->
+        <!--<li class=""><a href="#order_pay_no" data-status="1" v-on:click="tab">未付款</a></li>-->
+        <!--</ul>-->
+        <!--</div>-->
+        <div class="list">
+            <ul class="list-group list-group-flush">
+                <li class="list-group-item m-2 mb-0" v-for="(info,key) in data.list">
+                    <div class="header d-flex align-items-center">
+                        <span>{{info['order_sn']}}</span>
+                        <span class="ml-auto">{{info['create_timestamp']}}</span>
+                    </div>
+                    <hr>
+                    <div class="section">
+                        <p class="d-flex align-items-center">
+                            <span>办理进度</span>
+                            <span class="ml-auto">{{info['status']}}</span>
+                        </p>
+                        <p class="d-flex align-items-center">
+                            <span>实付金额</span>
+                            <span class="ml-auto">&yen;{{info['payment']}}</span>
+                        </p>
+                    </div>
+                    <hr>
+                    <div class="footer d-flex align-items-center">
+                        <a href="https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzUxMzYyMjYzOA==&scene=126&bizpsid=0#wechat_redirect" class="btn btn-info">联系客服</a>
+                        <a class="btn btn-info ml-auto" :data-id="info['order_sn']" :data-key="key"
+                           v-on:click="goDetail">订单详情</a>
+                    </div>
+                </li>
+            </ul>
+            <div class="more">
+                <a href="javascript:void(0);" class="btn btn-light btn-block m-2 pt-3 pb-3" v-on:click="loadMore"
+                   v-if="hasMore">点击加载更多</a>
+                <p class="text-center" v-if="!hasMore">没有更多了</p>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {Component, Vue} from "vue-property-decorator";
+    import Http from "../../extend/Http";
+    import router from "../../router";
+
+    @Component({})
+    export default class OrderList extends Vue {
+
+        public data: any = {
+            list: [],
+        };
+
+        // private status: number = 2;
+        private page: number = 0;
+        private hasMore: boolean = true;
+
+        created() {
+            this.page = 1;
+            this.loadMore();
+        }
+
+        async loadMore() {
+            let res = await Http.getInstance().getOrderList({
+                page: this.page,
+            });
+            console.log(res);
+            this.hasMore = res.data.length >9 ;
+            this.data.list = [].concat(this.data.list.concat(res.data));
+            this.page++;
+        }
+
+        goDetail(event: any) {
+            let data = this.data.list[event.target.getAttribute("data-key")];
+            router.push({
+                name: "odetail", params: {
+                    order_sn: data["order_sn"],
+                    status: data["status"],
+                    payment: data["payment"],
+                    create_timestamp: data["create_timestamp"],
+                }
+            });
+        }
+
+        //
+        // tab(event: any) {
+        //     let href = event.target.href;
+        //     let li = event.target.parentElement;
+        //     let lis = li.parentElement.querySelectorAll("li");
+        //     let tab = document.querySelector(href.slice(href.indexOf("#")));
+        //     let tabs = tab.parentElement.querySelectorAll(".login-iten");
+        //     this.status = event.target.getAttribute("data-status");
+        //     this.data.list = Http.getInstance().getOrderList({
+        //         status: this.status,
+        //     });
+        //     lis.forEach((item: any) => {
+        //         item.classList.remove("active");
+        //     });
+        //     li.classList.add("active");
+        //     tabs.forEach((item: any) => {
+        //         item.classList.add("hide");
+        //     });
+        //     tab.classList.remove("hide");
+        //     return false;
+        // }
+    }
+</script>
+
+<style lang="scss">
+    @import "@/assets/scss/variables.scss";
+
+    .list-wrap {
+    }
+
+    .order-status-tab {
+        height: auto;
+        border-bottom: 0;
+    }
+
+    .order-status-tab .nav {
+        overflow: inherit;
+    }
+
+    .order-status-tab li {
+        width: 50%;
+        border-bottom: 2px solid #ffffff;
+    }
+
+    .order-status-tab li a {
+        height: auto;
+        padding: .5rem;
+    }
+
+    .order-status-tab li.active {
+        border-bottom: 2px solid $green;
+    }
+
+    .list .list-group-item {
+        border-radius: 0.4rem;
+        padding: .5rem;
+        margin: .5rem;
+        margin-bottom: 0;
+    }
+
+    .footer {
+        color: #ffffff;
+    }
+</style>

+ 146 - 0
src/views/order/payment.ts

@@ -0,0 +1,146 @@
+import {Component, Vue} from 'vue-property-decorator';
+import Http from '../../extend/Http';
+import Utils from '../../extend/Utils';
+import wx from 'weixin-jsapi';
+import router from '@/router';
+
+@Component({
+    components: {}
+})
+
+export default class UserIndex extends Vue {
+
+    public data: any = {
+        hour: 0,
+        minute: 0,
+        second: 0,
+        orderSn: '0',
+        totalCharge: '0',
+        payment: 'weixin',
+        model: false,
+        html:''
+    };
+    private closeTime: string = '0';
+    private timer: number = 0;
+
+    async created() {
+        console.log(router);
+    }
+
+    mounted() {
+        let info = JSON.parse(localStorage.getItem('order_paying') as string);
+        this.closeTime = info.close_timestamp;
+        this.data.totalCharge = info.total_charge;
+        this.data.orderSn = info.order_sn;
+        // this.countTime(this.closeTime);
+    }
+
+    async payment(platform: string) {
+        // let res = await Http.getInstance().payOrder({
+        //     order_sn: this.data.orderSn, platform: platform, remark: '订单支付'
+        // });
+        // let res = await Http.getInstance().getWeixin({
+        //     order_sn: this.data.orderSn,
+        //     platform: 'weixin',
+        //     pay:'wappay'
+        // });
+        let res = await Http.getInstance().getWeixin({
+            order_sn: this.data.orderSn,
+            platform: 'weixin',
+            pay:'wappay'
+        });
+        console.log(res)
+        window.location.replace(res.data.mweb_url+'&redirect_url='+encodeURIComponent(window.location.href+'&tip=yes'))
+
+        // console.log(this.data.orderSn)
+        // this.data.model = true;
+        // this.data.payment = platform;
+        // switch (platform) {
+        //     case 'weixin':
+        //         // this.wxpay(res.data);
+        //         this.data.payment = 'weixin';
+        //         break;
+        //     case 'alipay':
+        //         // this.alipay(res.data);
+        //         break;
+        //     case 'union':
+        //         // this.unionpay(res.data);
+        //         break;
+        // }
+    }
+    async alipayment(platform: string) {
+
+        let res = await Http.getInstance().getAlipay({
+            order_sn: this.data.orderSn,
+            platform: 'alipay',
+            pay:'wappay'
+        });
+        console.log(res.data)
+        this.data.html = res.data
+        let form= res.data;
+        const div = document.createElement('div')
+        div.innerHTML = form//此处form就是后台返回接收到的数据
+        document.body.appendChild(div)
+        document.forms[0].submit()
+    }
+
+    private countTime(datetime: any): any {
+        this.timer = setInterval(() => {
+            // 获取当前时间
+            let now = new Date().getTime();
+            // 设置截止时间
+            let end = new Date(datetime).getTime();
+            // 时间差
+            let left = end - now;
+            if (left > 0) {
+                this.data.hour = Utils.getInstance().prefixInteger(Math.floor(left / 3600000));
+                this.data.minute = Utils.getInstance().prefixInteger(Math.floor((left / 60000) % 60));
+                this.data.second = Utils.getInstance().prefixInteger(Math.floor((left / 1000) % 60));
+            } else {
+                clearInterval(this.timer);
+                router.replace({
+                    name: 'olist',
+                });
+            }
+        }, 1000);
+    }
+
+    private async wxpay(data: any) {
+        let info = JSON.parse(localStorage.getItem('order_paying') || '');
+        console.log('info');
+        let res = await Http.getInstance().getConfig({
+            platform: 'weixin', order_sn: info.order_sn, url: location.href.slice(0, -7)
+        });
+        res.data.jsApiList = ['chooseWXPay'];
+        console.log(res);
+        wx.config(res.data);
+        wx.ready(() => {
+            wx.chooseWXPay({
+                timestamp: data.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
+                nonceStr: data.nonceStr, // 支付签名随机串,不长于 32 位
+                package: data.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
+                signType: data.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
+                paySign: data.paySign, // 支付签名
+                success: (res: any) => {
+                    alert(res);
+                    if (res) {
+                        localStorage.removeItem('order_paying');
+                        router.replace('orderList');
+                    } else {
+                    }
+                }
+            });
+        });
+        wx.error((err: any) => {
+            alert(JSON.stringify(err));
+        });
+    }
+
+    private alipay(data: any) {
+
+    }
+
+    private unionpay(data: any) {
+
+    }
+}

+ 116 - 0
src/views/order/payment.vue

@@ -0,0 +1,116 @@
+<template>
+    <div class="payment-wrap">
+        <div class="jumbotron jumbotron-fluid">
+            <div class="container text-center">
+                <p class="order-info">
+                    <img src="../../assets/image/ico_09.png" alt="">
+                    提交订单完成,请尽快完成付款!
+                </p>
+                <!--<h5 class="time-down m-3">{{data.hour}}:{{data.minute}}:{{data.second}}</h5>-->
+                <h5 class="pay-number d-inline-block mb-3">待付金额:&yen;<span>{{data.totalCharge}}</span></h5>
+                <p class="order-sn">订单编号:<span>{{data.orderSn}}</span></p>
+            </div>
+        </div>
+        <div class="btn-box form-group m-3 row">
+            <a href="https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzUxMzYyMjYzOA==&scene=126&bizpsid=0#wechat_redirect" class="btn btn-block btn-light p-3">联系客服</a>
+        </div>
+        <div class="payment mt-4">
+            <h6 class="m-0"><img src="../../assets/image/ico-10.png" alt=""></h6>
+            <ul class="list-group list-group-flush">
+                <li class="list-group-item d-flex align-items-center" v-on:click="payment('weixin')">
+                    <img src="../../assets/image/ico-11.png" alt="">
+                </li>
+                <li class="list-group-item d-flex align-items-center" v-on:click="alipayment('alipay')">
+                    <img src="../../assets/image/ico-13.png" alt="">
+                </li>
+                <!--<li class="list-group-item d-flex align-items-center" v-on:click="payment('union')">-->
+                    <!--<img src="../../assets/image/ico-12.png" alt="">-->
+                <!--</li>-->
+            </ul>
+        </div>
+        <div class="model" v-bind:class="{'active': data.model}">
+            <img src="../../assets/image/wxpay.png" alt="" v-if="data.payment == 'weixin'">
+            <img src="../../assets/image/alipay.jpg" alt="" v-if="data.payment == 'alipay'">
+        </div>
+    </div>
+</template>
+
+<script lang="ts" src="./payment.ts"></script>
+
+
+<style lang="scss">
+    @import "@/assets/scss/variables.scss";
+
+    .payment-wrap {
+        width: 100%;
+    }
+
+    .jumbotron {
+        background: #ffffff;
+    }
+
+    .icon {
+        display: block;
+        width: 2rem;
+        height: 2rem;
+        line-height: 2rem;
+        text-align: center;
+        background: #f78f8f;
+    }
+
+    .order-info {
+        font-size: 1.25rem;
+    }
+
+    .order-info img {
+        width: 3rem;
+    }
+
+    .time-down {
+        color: #ccb187;
+        font-size: 2rem;
+        font-weight: bolder;
+    }
+
+    .pay-number {
+        color: $green;
+        border: 1px solid $green;
+        padding: 0.5rem 2.5rem;
+        border-radius: 0.5rem;
+        font-weight: bolder;
+    }
+
+    .order-sn {
+        position: absolute;
+        color: #8e8e8e;
+    }
+
+    .btn-box .btn {
+        color: #8e8e8e;
+    }
+
+    .payment img {
+        width: 60%;
+    }
+
+    .model {
+        position: absolute;
+        top: 0;
+        right: 0;
+        left: 0;
+        bottom: 0;
+        text-align: center;
+        background: rgba(0, 0, 0, .6);
+        display: none;
+    }
+
+    .model.active {
+        display: block;
+    }
+
+    .model img {
+        width: 80%;
+        margin-top: 15%;
+    }
+</style>
+

+ 205 - 0
src/views/other/other.vue

@@ -0,0 +1,205 @@
+<template>
+    <div class="list-wrap">
+        <div class="card service" v-if="data.is_activity == 'off'">
+            <div class="card-header">
+                <h5><img src="../../assets/image/yen.png" alt="">代缴服务费介绍</h5>
+            </div>
+            <div class="card-body">
+                <table class="table">
+                    <thead>
+                    <tr>
+                        <th>类别</th>
+                        <th>社保</th>
+                        <th>社保+公积金</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    <tr v-for="item in data.service">
+                        <td>{{item[2]}}</td>
+                        <td>&yen;{{item[0]}}/人</td>
+                        <td>&yen;{{item[1]}}/人</td>
+                    </tr>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+        <div class="service" v-else>
+            <div class="card">
+                <div class="card-header">
+                    <h5><img src="../../assets/image/yen.png" alt="">社保代缴服务费</h5>
+                </div>
+                <div class="card-body d-flex justify-content-between align-items-center">
+                    <div class="service-container active d-flex justify-content-between align-items-center align-content-center flex-wrap">
+                        <div class="d-flex flex-column justify-content-center" v-for="item in data.service">
+                            <p><span class="price">&yen;{{item[0]*0.5}}</span>/人</p>
+                            <span>{{item[2]}}</span>
+                        </div>
+                    </div>
+                    <div class="service-container d-flex justify-content-between align-items-center align-content-center flex-wrap">
+                        <div class="d-flex flex-column justify-content-center" v-for="item in data.service">
+                            <p><span class="price">&yen;{{item[0]}}</span>/人</p>
+                            <span>{{item[2]}}</span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="card">
+                <div class="card-header">
+                    <h5><img src="../../assets/image/yen.png" alt="">社保+公积金代缴服务费</h5>   
+                </div>
+                <div class="card-body d-flex justify-content-between align-items-center">
+                    <div class="service-container active d-flex justify-content-between align-items-center align-content-center flex-wrap">
+                        <div class="d-flex flex-column justify-content-center" v-for="item in data.service">
+                            <p><span class="price">&yen;{{item[1]*0.5}}</span>/人</p>
+                            <span>{{item[2]}}</span>
+                        </div>
+                    </div>
+                    <div class="service-container d-flex justify-content-between align-items-center align-content-center flex-wrap">
+                        <div class="d-flex flex-column justify-content-center" v-for="item in data.service">
+                            <p><span class="price">&yen;{{item[1]}}</span>/人</p>
+                            <span>{{item[2]}}</span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="card">
+            <div class="card-header">
+                <h5><img src="../../assets/image/yen.png" alt="">最低缴纳介绍</h5>
+            </div>
+            <div class="card-body">
+                <table class="table">
+                    <thead>
+                    <tr>
+                        <th>类别</th>
+                        <th>最低缴纳</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    <tr>
+                        <td>社保</td>
+                        <td><p>城镇:&yen;{{data.basic.social[1]}}/人</p>
+                            <p>农村:&yen;{{data.basic.social[0]}}/人</p></td>
+                    </tr>
+                    <tr>
+                        <td>公积金</td>
+                        <td>&yen;{{data.basic.fund}}/人</td>
+                    </tr>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {Component, Vue} from "vue-property-decorator";
+    import Http from "../../extend/Http";
+    import router from "../../router";
+
+    @Component({})
+    export default class OrderList extends Vue {
+
+        public data: any = {
+            "service": {
+                "monthly": [100, 150, "月付"],
+                "seasonal": [270, 405, "季付"],
+                "halfYearly": [480, 720, "半年付"],
+                "yearly": [600, 900, "年付"]
+            }, "basic": {"social": [3387, 3387], "fund": 2273}, "is_activity": 0.5
+        };
+
+        async created() {
+            let res = await Http.getInstance().getSocialConfig();
+            this.data = res.data;
+        }
+
+    }
+</script>
+
+<style lang="scss">
+    .card {
+        margin-top: .5rem;
+    }
+
+    .card-header {
+        border: 0;
+        padding: 1.2rem .8rem;
+    }
+
+    .card-header h5 {
+        font-size: 1.5rem;
+        font-weight: bold;
+        margin-bottom: 0;
+    }
+
+    .card-header img {
+        width: 1.5rem;
+        margin-right: .5rem;
+        vertical-align: text-top;
+    }
+
+    .card-body {
+        padding: 0 2.75rem 2.5rem;
+    }
+
+    .card p {
+        margin: 0;
+    }
+
+    .table {
+        margin-bottom: 0;
+        border: 1px solid #EBEBEB;
+        text-align: center;
+    }
+
+    .table td, .table th {
+        border: 0;
+        border-right: 1px solid #EBEBEB;
+        border-bottom: 1px solid #EBEBEB;
+    }
+
+    .table thead th {
+        border: 0;
+        border-right: 1px solid #EBEBEB;
+        border-bottom: 1px solid #EBEBEB;
+        font-size: 1.3rem;
+    }
+
+    .table tbody td {
+        color: #777777;
+        font-size: 1.2rem;
+    }
+
+    .service-container.active{
+        width: 60%;
+        background: url('../../assets/image/service.half.png') no-repeat center center;
+        background-size: contain;
+        padding-bottom: 1rem;
+    }
+
+    .service-container.active .price{
+        color: #F16511;
+        font-size: 1.2rem;
+    }
+
+    .service-container>div {
+        width: 50%;
+        text-align: center;
+    }
+
+    .service-container{
+        width: 40%;
+        min-height: 12rem;
+        background: url('../../assets/image/service.all.png') no-repeat center center;
+        background-size: contain;
+        padding: 0 1rem;
+    }
+
+    .service .card-body {
+        padding: 0 1rem 2rem;
+    }
+
+
+</style>
+

+ 139 - 0
src/views/social/social.scss

@@ -0,0 +1,139 @@
+@import "@/assets/scss/variables.scss";
+
+.social-wrap {
+  width: 100%;
+  padding: 1.5rem;
+  background: #ffffff;
+}
+
+.social-wrap .form-group {
+  margin: 1.6rem 0;
+  font-size: 16px;
+}
+
+.form-group.btn-box {
+  background: $gray;
+  margin: 2rem -1.5rem -4.5rem;
+  padding: 3rem;
+  overflow: hidden;
+}
+
+.btn-box .btn {
+  margin: 1rem 0;
+  color: white;
+  background: $green;
+  border-radius: 0.5rem;
+}
+
+
+.btn-box .btn:last-child {
+  margin: 1rem 0;
+  color: #8e8e8e;
+  background: #ffffff;
+  border-radius: 0.5rem;
+}
+
+#app {
+  background: $gray;
+}
+.badge-pill{
+  font-size: 1.5rem;
+  font-weight: 400;
+}
+.social-wrap input, .social-wrap select {
+  //background: $gray;
+}
+
+.swich-box .box {
+  width: 60px;
+  height: 25px;
+  background: #ccc;
+  border-radius: 20px;
+  transition: all .5s ease;
+}
+
+.swich-box input {
+  display: none;
+}
+
+.swich-box .box span {
+  display: inline-block;
+  height: 25px;
+  width: 25px;
+  border-radius: 15px;
+  background: #fff;
+  box-shadow: 1px 1px 1px #ddd;
+  transform: translateX(0px);
+  transition: all .5s ease;
+}
+
+.swich-on .box {
+  background: forestgreen;
+  transition: all .5s ease;
+}
+
+.swich-on .box span {
+  transform: translateX(36px);
+  transition: all .5s ease;
+}
+.detail{
+  font-size: 15px;
+}
+.detail .list-group, .detail .list-group-item{
+  background: $gray;
+}
+
+.col-form-label{
+  padding-right: 0;
+  padding-left: 0;
+}
+
+.social-wrap .needs-validation .form-control {
+  background-image: none;
+}
+.youhui{
+  .pingtai{
+    padding: 10px 0 0;
+    font-size: 16px;
+    color: #999999;
+  }
+  .pingtai-2{
+    font-size: 16px;
+  }
+  .fuwuinput{
+    background: white !important;
+  }
+   .youhui-box{
+    //border: 1px solid #e8e8e8;
+     position:relative;
+    img{
+      width: 178px;
+      height: 33px;
+      margin: 10px 0;
+    }
+    .youhui-text{
+      //padding-left:  10px;
+      font-size: 16px;
+      padding-top: 1rem;
+      position: relative;
+        .youinput{
+          width: 15rem;
+          position: absolute;
+          right: 1rem;
+          top: 1.5rem;
+          background: white;
+        }
+    }
+    .youhui-p{
+      padding-left:  10px;
+      font-size: 16px;
+      color: #d30000;
+      margin-top: 2rem;
+      //padding-right: 20px;
+    }
+     .youhui-dec{
+       margin-top: 1rem;
+       display: inline-block;
+     }
+   }
+}

+ 135 - 0
src/views/social/social.ts

@@ -0,0 +1,135 @@
+import {Component, Vue} from 'vue-property-decorator';
+import Utils from '@/extend/Utils';
+import Http from '@/extend/Http';
+import router from '@/router';
+// import {  } from "@/components" // 组件
+
+const serviceChargeData: any = [
+    {
+    monthly: '月付:100元/月', seasonal: '季付:270元/季', halfYearly: '半年付:480元/半年', yearly: '年付:600元/年',
+},
+    {
+    monthly: '月付:150元/月', seasonal: '季付:405元/季', halfYearly: '半年付:720元/半年', yearly: '年付:900元/年',
+}
+];
+
+@Component({})
+export default class Social extends Vue {
+
+    public data: any = {
+        is_fund: true,
+        is_discounts: false,
+        social_calc: null,
+        service_calc: 0,
+        fund_calc: 0,
+        shouxu:0,
+        subtotal_calc: 0,
+        total_calc: 0,
+        social_basic: {pension: 3400, fund: 3400},
+        social_type: null,
+        service_charge: null,
+        discountList:'',
+        value:'',
+        is_balance:0,
+        blance:'',
+    };
+
+    private service_charge_type: string = 'monthly';
+
+
+    async created() {
+        let params = Utils.getInstance().getParams(location.href);
+        this.data.is_fund = !!params.is_fund;
+        this.data.social_type = localStorage.getItem('social_type');
+        if (this.data.social_type != null) {
+            let res = await Http.getInstance().socialLimit({
+                socialType: this.data.social_type
+            });
+            this.data.social_basic = res.data;
+        }
+        this.data.social_type = this.data.social_type - 1;
+        this.data.service_charge = serviceChargeData[this.data.social_type];
+    }
+
+
+
+
+    updated() {
+        if (this.data.social_calc == null) {
+            this.calcBill();
+        }
+
+    }
+    async changebalance(event: any){
+        // console.log(event)
+        console.log(this.data.is_balance)
+    }
+    async saveInfo(event: any) {
+        console.log(event);
+        console.log(event.target.form);
+        let form = event.target.form;
+        if (form.checkValidity() === false) {
+            form.classList.add('was-validated');
+            event.preventDefault();
+            event.stopPropagation();
+            return false;
+        }
+        let res = await Http.getInstance().saveBase(Utils.getInstance().serialize(form));
+        // @ts-ignore
+        this.data.social_type = res.data.social_type;
+        this.data.service_charge = serviceChargeData[res.data.social_type];
+        localStorage.setItem('social_type', this.data.social_type);
+        localStorage.setItem('service_charge', this.data.service_charge);
+        localStorage.setItem('access_token', res.data.access_token);
+        localStorage.setItem('refresh_token', res.data.refresh_token);
+
+        res = await Http.getInstance().socialLimit({
+            socialType: this.data.social_type
+        });
+        this.data.social_basic = res.data;
+        return false;
+    }
+
+    async calcBill(event?: any) {
+        let form = event ? event.target.form : document.getElementById('social-detail');
+        if (form.checkValidity() === false) {
+            form.classList.add('was-validated');
+            return false;
+        }
+        let res = await Http.getInstance().calcBill(Utils.getInstance().serialize(form));
+        console.log(res)
+        this.data.social_calc = res.data.social;
+        this.data.service_calc = res.data.service;
+        this.data.fund_calc = res.data.fund;
+        this.data.subtotal_calc = res.data.subtotal;
+        this.data.total_calc = res.data.total;
+        this.data.is_discounts = res.data.discounts;
+        let ress = await Http.getInstance().userinfo();
+        this.data.blance = ress.data.balance
+        let resss = await Http.getInstance().getDiscountListt();
+        this.data.discountList = resss.data;
+        console.log(this.data.discountList)
+
+    }
+
+
+    async saveOrder(event: any) {
+        let form = event.target.form;
+        console.log(form)
+        if (form.checkValidity() === false) {
+            form.classList.add('was-validated');
+            event.preventDefault();
+            event.stopPropagation();
+            return false;
+        }
+        console.log(Utils.getInstance().serialize(form))
+        let res = await Http.getInstance().saveOrder(Utils.getInstance().serialize(form));
+        localStorage.setItem('order_paying', JSON.stringify(res.data));
+        router.replace({
+            name: 'opayment',
+            params: {
+                order_sn: res.data.order_sn
+            }
+        });
+    }
+}

+ 202 - 0
src/views/social/social.vue

@@ -0,0 +1,202 @@
+<template>
+    <div class="social-wrap">
+        <section class="social-basic" v-if="data.social_type == null">
+            <form action="" id="social-basic" class="needs-validation" novalidate>
+                <div class="form-group row">
+                    <label for="realname" class="col-3 col-form-label">姓名</label>
+                    <div class="col-8">
+                        <input type="text" class="form-control" id="realname" name="realname"
+                               value="" required pattern="^[\u4e00-\u9fa5]{2,4}$">
+                        <div class="invalid-feedback">
+                            请输入有效的姓名
+                        </div>
+                    </div>
+                    <b class="text-danger">*</b>
+                </div>
+                <div class="form-group row">
+                    <label for="socialType" class="col-3 col-form-label">社保户别</label>
+                    <div class="col-8">
+                        <select id="socialType" name="socialType" class="form-control">
+                            <option value="1" selected="">城镇</option>
+                            <option value="2">农村</option>
+                        </select>
+                        <div class="invalid-feedback">
+                            请输入有效的社保户别
+                        </div>
+                    </div>
+                    <b class="text-danger">*</b>
+                </div>
+                <div class="form-group row">
+                    <label for="socialCity" class="col-3 col-form-label">缴费城市</label>
+                    <div class="col-8">
+                        <input id="socialCity" name="socialCity" class="form-control" type="text" value="北京" readonly>
+                    </div>
+                    <b class="text-danger">*</b>
+                </div>
+                <div class="form-group row">
+                    <label for="idCard" class="col-3 col-form-label">身份证号</label>
+                    <div class="col-8">
+                        <input type="text" class="form-control" id="idCard" name="idCard" value="" required
+                               pattern="(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)">
+                        <div class="invalid-feedback">
+                            请输入有效的身份证号
+                        </div>
+                    </div>
+                    <b class="text-danger">*</b>
+                </div>
+                <div class="form-group row">
+                    <label for="mobile" class="col-3 col-form-label">手机号码</label>
+                    <div class="col-8">
+                        <input type="text" class="form-control" id="mobile" name="mobile" value="" required
+                               pattern="^1[3-9]\d{9}$">
+                        <div class="invalid-feedback">
+                            请输入有效的手机号码
+                        </div>
+                    </div>
+                    <b class="text-danger">*</b>
+                </div>
+                <div class="form-group row">
+                    <label for="email" class="col-3 col-form-label">电子邮箱</label>
+                    <div class="col-8">
+                        <input type="text" class="form-control" id="email" name="email" value=""
+                               pattern="[^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$|]">
+                        <div class="invalid-feedback">
+                            请输入有效的电子邮箱
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group row">
+                    <label for="addr" class="col-3 col-form-label">联系地址</label>
+                    <div class="col-8">
+                        <input type="text" class="form-control" id="addr" name="addr" value="">
+                    </div>
+                </div>
+                <div class="text-right" style="color: #6b6b6b">
+                    <small class="small">输入您的个人信息,<b class="text-danger">*</b>号为必填内容</small>
+                </div>
+                <div class="btn-box form-group mt-3 row">
+                    <input type="hidden" name="is_edit" value="">
+                    <a href="https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzUxMzYyMjYzOA==&scene=126&bizpsid=0#wechat_redirect" class="btn btn-block btn-primary p-3">联系客服</a>
+                    <button type="button" class="btn btn-block btn-primary p-3" v-on:click="saveInfo">下一步</button>
+                </div>
+            </form>
+        </section>
+        <section class="social-detail" v-if="data.social_type != null">
+            <form action="" id="social-detail" class="needs-validation" novalidate>
+                <input type="hidden" name="bill_type" value="calc">
+                <div class="form-group row">
+                    <label for="socialBasic" class="col-5 col-form-label">社保缴纳额度</label>
+                    <div class="col-7">
+                        <input type="number" class="form-control" id="socialBasic" name="socialBasic"
+                               v-on:change="calcBill"
+                               :value="data.social_basic.pension[0]" :min="data.social_basic.pension[0]"
+                               :max="data.social_basic.pension[1]" required>
+                        <div class="invalid-feedback">
+                            请输入介于{{data.social_basic.pension[0]}},{{data.social_basic.pension[1]}}之间的整数
+                        </div>
+                    </div>
+                </div>
+                <div id="fund" class="form-group row" v-if="data.is_fund">
+                    <label for="fundBasic" class="col-5 col-form-label">公积金缴纳额度</label>
+                    <div class="col-7">
+                        <input type="number" class="form-control fuwuinput" id="fundBasic" name="fundBasic" v-on:change="calcBill"
+                               :value="data.social_basic.fund[0]" :min="data.social_basic.fund[0]"
+                               :max="data.social_basic.fund[1]"
+                               required>
+                        <div class="invalid-feedback">
+                            请输入介于{{data.social_basic.fund[0]}},{{data.social_basic.fund[1]}}之间的整数
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group row">
+                    <label for="serviceCharge" class="col-5 col-form-label">服务费</label>
+                    <div class="col-7">
+                        <select id="serviceCharge" class="form-control fuwuinput" name="serviceType">
+                            <option v-for="(value, key) in data.service_charge" :value="key"
+                                    :selected="key === 'monthly'">
+                                {{value}}
+                            </option>
+                        </select>
+                    </div>
+                    <b class="text-danger col-12 text-right" v-if="data.is_discounts">5折优惠</b>
+                </div>
+                <div class="detail">
+                    <label class="col-form-label">缴费明细</label>
+                    <ul class="list-group list-group-flush pl-4">
+                        <li class="list-group-item d-flex justify-content-between align-items-center"
+                            v-for="value in data.social_calc">
+                            {{value[1]}}
+                            <span class="badge badge-pill">&yen;{{value[0]}}</span>
+                        </li>
+                    </ul>
+                </div>
+                <div class="youhui">
+<!--                    <p class="pingtai">*平台优惠可以选择优惠方式进行支付</p>-->
+<!--                    <p class="pingtai-2">平台优惠(二选一)</p>-->
+                    <div class="youhui-box">
+                        <p class="youhui-text">
+                            <span class="youhui-dec">优惠券</span>
+                            <select  class="form-control youinput" name="discount">
+                                <option value="0">未选择优惠券</option>
+                                <option v-for="item in data.discountList" :value="item.id">
+                                    {{item.money}}
+                                </option>
+                            </select>
+                        </p>
+<!--                        <p class="youhui-p">-->
+<!--                            <span>余额可抵扣 -¥{{data.blance}}</span>-->
+<!--                            <el-switch-->
+<!--                                    v-model="data.is_balance"-->
+<!--                                    :value="data.is_balance"-->
+<!--                                    name="is_balance"-->
+<!--                                    active-color="#2277e1"-->
+<!--                                    inactive-color="#c2c2c2"-->
+<!--                                    @change="changebalance()"-->
+<!--                                    style="position: absolute;right: 1rem;">-->
+<!--                            </el-switch>-->
+<!--                        </p>-->
+                    </div>
+                </div>
+                <div class="detail mt-2 pr-3 d-flex justify-content-between align-items-center">
+                    <label class="col-form-label">社保小计</label>
+                    <span class="badge">&yen;{{data.subtotal_calc}}</span>
+                </div>
+                <div class="detail mt-2 pr-3 d-flex justify-content-between align-items-center"
+                     v-if="data.is_fund">
+                    <label class="col-form-label">公积金</label>
+                    <span class="badge">&yen;{{data.fund_calc}}</span>
+                </div>
+                <div class="detail mt-2 pr-3 d-flex justify-content-between align-items-center"
+                     >
+                    <label class="col-form-label">手续费</label>
+                    <span class="badge">&yen;{{data.shoxu}}</span>
+                </div>
+                <div class="detail mt-2 pr-3 d-flex justify-content-between align-items-center"
+                >
+                    <label class="col-form-label">服务费</label>
+                    <span class="badge">&yen;{{data.shoxu}}</span>
+                </div>
+                <div class="detail mt-2 pr-3 d-flex justify-content-between align-items-center">
+                    <label class="col-form-label">总计</label>
+                    <span class="badge">&yen;{{data.total_calc}}</span>
+                </div>
+                <div class="text-right" style="color: #6b6b6b">
+                    <small class="small">建议联系客服后再缴费</small>
+                </div>
+                <div class="btn-box form-group mt-3 row">
+
+                    <button type="button" class="btn btn-block btn-primary p-3" v-on:click="saveOrder">去缴费</button>
+                    <a href="https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzUxMzYyMjYzOA==&scene=126&bizpsid=0#wechat_redirect" class="btn btn-block btn-primary p-3">联系客服</a>
+                    <!--<router-link to="/order/payment/2" class="btn btn-block p-3">去交费</router-link>-->
+                </div>
+            </form>
+        </section>
+    </div>
+</template>
+
+<script lang="ts" src="./social.ts"></script>
+
+<style lang="scss" scoped>
+    @import './social.scss';
+</style>
+

+ 180 - 0
src/views/user/edit.vue

@@ -0,0 +1,180 @@
+<template>
+    <div class="info-wrap">
+        <header class="bar bar-nav d-flex justify-content-between align-items-center p-2">
+            <el-button type="text" icon="el-icon-arrow-left" v-on:click="goBack"></el-button>
+            <h4 class="title">{{data.title}}</h4>
+            <el-button type="text" v-on:click="barFunc"><span>确定</span></el-button>
+        </header>
+        <form class="content needs-validation p-2 pt-3" id="editInfo">
+            <div v-if="data.key==='realname'">
+                <el-input v-model="data.value" name="realname" required pattern="^[\u4e00-\u9fa5]{2,4}$"></el-input>
+                <div class="invalid-feedback">
+                    请输入有效的姓名
+                </div>
+            </div>
+            <div v-if="data.key==='social_type'">
+                <el-radio v-model="data.value" name="social_type" label="1" border size="medium">城镇</el-radio>
+                <el-radio v-model="data.value" name="social_type" label="2" border size="medium">农村</el-radio>
+            </div>
+            <div v-if="data.key==='gender'">
+                <el-radio v-model="data.value" name="gender" label="1" border size="medium">男</el-radio>
+                <el-radio v-model="data.value" name="gender" label="2" border size="medium">女</el-radio>
+            </div>
+            <div v-if="data.key==='social_city'">
+                <el-input v-model="data.value" name="socialCity" required
+                          pattern="(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)"></el-input>
+                <div class="invalid-feedback">
+                    请输入有效的身份证号
+                </div>
+            </div>
+            <div v-if="data.key==='mobile'">
+                <el-input v-model="data.value" name="mobile" required pattern="^1[3-9]\d{9}$"></el-input>
+                <div class="invalid-feedback">
+                    请输入有效的手机号码
+                </div>
+            </div>
+            <div v-if="data.key==='addr'">
+                <el-input v-model="data.value" name="addr" required></el-input>
+                <div class="invalid-feedback">
+                    请输入有效的联系地址
+                </div>
+            </div>
+            <div v-if="data.key==='email'">
+                <el-input v-model="data.value" name="email" required
+                          pattern="[^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$|]"></el-input>
+                <div class="invalid-feedback">
+                    请输入有效的电子邮箱
+                </div>
+            </div>
+        </form>
+    </div>
+</template>
+
+<script lang="ts">
+    import {Component, Vue} from "vue-property-decorator";
+    import Http from "../../extend/Http";
+    import Utils from "../../extend/Utils";
+    import wx from "weixin-jsapi";
+    import router from "../../router";
+    import {Message} from "element-ui";
+
+    @Component({
+        components: {}
+    })
+    export default class UserEdit extends Vue {
+
+        public data: any = {
+            title: "修改信息", key: "string", value: "string",
+        };
+
+        async created() {
+            this.data.title = "修改" + this.$route.params["label"];
+            this.data.key = this.$route.params["key"];
+            this.data.value = this.$route.params["value"];
+        }
+
+        mounted() {
+        }
+
+
+        async barFunc() {
+            let form = document.querySelector("#editInfo") as HTMLFormElement;
+            console.log(form);
+            if (form.checkValidity() === false) {
+                form.classList.add("was-validated");
+                return false;
+
+            }
+            let res = await Http.getInstance().saveBase(Object.assign(Utils.getInstance().serialize(form), {is_edit: true}));
+            res.data.social_type && localStorage.setItem("social_type", res.data.social_type);
+            // @ts-ignore
+            Message({
+                message: "修改成功", type: "success", onClose: () => router.back()
+            });
+        }
+
+        public goBack() {
+            router.back();
+        }
+
+        private async wxInit() {
+            let url = location.href.split("#")[0]; // 获取锚点之前的链接
+            let links = "m.wercf.com";
+            let title = "河马代缴";
+            let desc = "了解更多知识,请关注“河马代缴”公众号";
+            let imgUrl = "";
+            let res = await Http.getInstance().getConfig({
+                platform: "weixin", url: url,
+            });
+            res.jsApiList = ["onMenuShareTimeline", "onMenuShareAppMessage", "onMenuShareQQ", "onMenuShareWeibo", "onMenuShareQZone"];
+            wx.config(res);
+            wx.ready(() => {
+                wx.onMenuShareTimeline({
+                    title: title, // 分享标题
+                    link: links, // 分享链接
+                    imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+                // 微信分享菜单测试
+                wx.onMenuShareAppMessage({
+                    title: title, // 分享标题
+                    link: links, // 分享链接
+                    desc: title, // 分享链接
+                    type: "", // 分享链接
+                    dataUrl: "", imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+                wx.onMenuShareQQ({
+                    title: title, // 分享标题
+                    link: links, // 分享链接
+                    desc: title, // 分享链接
+                    type: "", // 分享链接
+                    dataUrl: "", imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+                wx.onMenuShareWeibo({
+                    title: title, // 分享标题
+                    desc: desc, // 分享描述
+                    link: links, // 分享链接
+                    imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+            });
+            wx.error((err: any) => {
+                alert(JSON.stringify(err));
+            });
+        }
+    }
+</script>
+
+<style>
+    .avator {
+        width: 3.5rem;
+        height: 3.5rem;
+        border-radius: 3.5rem;
+    }
+
+    .list-group-item {
+        border-color: #eee;
+    }
+
+    .list-group-item a {
+        color: #212529;
+    }
+
+    .was-validated .invalid-feedback {
+        display: block;
+    }
+
+    .el-message {
+        margin-top: 8rem;
+    }
+</style>

+ 187 - 0
src/views/user/index.vue

@@ -0,0 +1,187 @@
+<template>
+    <div class="user-wrap">
+        <div class="tuichu">
+            <img src="../../assets/image/exit_03.png" alt="" @click="exit">
+        </div>
+        <el-card class="jumbotron jumbotron-fluid">
+            <img class="avator" :src="avatar">
+            <div style="padding: 14px;">
+                <span>{{nickname}}</span>
+                <!--<div class="bottom clearfix">-->
+                    <!--<el-button type="text" class="button">操作按钮</el-button>-->
+                <!--</div>-->
+            </div>
+        </el-card>
+        <div class="info">
+            <div class="list-group list-group-flush">
+                <router-link class="list-group-item" to="/user/info">
+                    我的信息
+                </router-link>
+                <router-link class="list-group-item" to="/order/list">
+                    我的订单
+                </router-link>
+                <a class="list-group-item text-center" v-on:click="wxInit">
+                    <div class="invite d-inline-block pt-3">
+                        邀请好友
+                        <p class="small m-0">(邀请好友返50元红包)</p>
+                    </div>
+                </a>
+            </div>
+        </div>
+        <div class="query row mt-4">
+            <a class="col-6 text-center" href="http://www.bjrbj.gov.cn/sscsearch/xml/login?cityid=110000">
+                <img src="../../assets/image/ico_07.png" alt="">
+            </a>
+            <a class="col-6 text-center" href="https://grwsyw.bjgjj.gov.cn/ish/">
+                <img src="../../assets/image/ico_08.png" alt="">
+            </a>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {Component, Vue} from "vue-property-decorator";
+    import Http from "../../extend/Http";
+    import Utils from "../../extend/Utils";
+    import wx from "weixin-jsapi";
+
+    @Component({
+        components: {}
+    })
+    export default class UserIndex extends Vue {
+
+        private avatar = localStorage.getItem("avatar");
+        private nickname = localStorage.getItem("nickname");
+
+        async created() {
+            console.log(this.avatar);
+            if (!this.avatar) {
+                console.log(this.avatar);
+                let res = await Http.getInstance().userinfo();
+                this.avatar = res.data.avatar as string;
+                this.nickname = res.data.nickname as string;
+                localStorage.setItem("avatar", this.avatar);
+                localStorage.setItem("nickname", this.nickname);
+            }
+        }
+        async exit(){
+            // localStorage.removeItem()
+            let  storage = window.localStorage;
+            storage.clear()
+            this.$router.push('/login')
+        }
+        private async wxInit() {
+            let url = location.href.split("#")[0] + '?inviter_code=' + localStorage.getItem('inviter_code'); // 获取锚点之前的链接
+            let links = "m.wercf.com";
+            let title = "河马代缴";
+            let desc = "了解更多知识,请关注“河马代缴”公众号";
+            let imgUrl = "";
+            let res = await Http.getInstance().getConfig({
+                platform: "weixin", url: url,
+            });
+            res.jsApiList = ["onMenuShareTimeline", "onMenuShareAppMessage", "onMenuShareQQ", "onMenuShareWeibo", "onMenuShareQZone"];
+            wx.config(res);
+            wx.ready(() => {
+                wx.onMenuShareTimeline({
+                    title: title, // 分享标题
+                    link: links, // 分享链接
+                    imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+                // 微信分享菜单测试
+                wx.onMenuShareAppMessage({
+                    title: title, // 分享标题
+                    link: links, // 分享链接
+                    desc: title, // 分享链接
+                    type: "", // 分享链接
+                    dataUrl: "", imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+                wx.onMenuShareQQ({
+                    title: title, // 分享标题
+                    link: links, // 分享链接
+                    desc: title, // 分享链接
+                    type: "", // 分享链接
+                    dataUrl: "", imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+                wx.onMenuShareWeibo({
+                    title: title, // 分享标题
+                    desc: desc, // 分享描述
+                    link: links, // 分享链接
+                    imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+            });
+            wx.error((err: any) => {
+                alert(JSON.stringify(err));
+            });
+        }
+    }
+</script>
+
+<style lang="scss" scoped>
+    @import '@/assets/scss/variables.scss';
+
+    .user-wrap {
+        width: 100%;
+        overflow-x: hidden;
+        position: relative;
+    }
+
+    .jumbotron {
+        text-align: center;
+        background: $blue;
+    }
+
+    .avator {
+        width: 6rem;
+        height: 6rem;
+        border-radius: 6rem;
+    }
+
+    .info .list-group-item {
+        margin: 0.6rem 1.25rem;
+        height: 5rem;
+        line-height: 5rem;
+        padding: 0;
+        color: #ffffff;
+        text-align: center;
+        border-radius: 0.8rem;
+    }
+
+    .list-group-item {
+        background: $blue;
+    }
+
+    .list-group-item:last-child {
+        background: $orange;
+    }
+
+    .invite {
+        display: inline-block !important;
+        line-height: normal;
+    }
+
+    .query img {
+        width: 75%;
+    }
+
+    .list-group-flush {
+        border-bottom: 0;
+    }
+    .tuichu{
+        position: absolute;
+        right: 2rem;
+        top: 1rem;
+    }
+</style>
+

+ 167 - 0
src/views/user/info.vue

@@ -0,0 +1,167 @@
+<template>
+    <div class="info-wrap">
+        <el-card class="text-center">
+            <img class="avator" :src="avatar">
+            <div style="padding: 14px;">
+                <span>{{nickname}}</span>
+            </div>
+        </el-card>
+        <ul class="list-group list-group-flush">
+            <li v-for="(item,key) in data"
+                v-on:click="item.canEdit && edit(item,key)"
+                class="list-group-item d-flex justify-content-between align-items-center">
+                <label class="control-label">{{item.label}}</label>
+                <div class="badge">
+                    <span>{{item.value}}</span>
+                    <span v-if="item.canEdit" class="ml-2">&gt;</span>
+                    <span v-else="item.canEdit" class="ml-2"></span>
+                </div>
+            </li>
+            <li class="list-group-item">
+                <router-link to="/user/invitation" class=" d-flex justify-content-between align-items-center">
+                    <label class="control-label">邀请列表</label>
+                    <span class="badge">
+                    <span class="ml-2">&gt;</span>
+                </span>
+                </router-link>
+            </li>
+        </ul>
+    </div>
+</template>
+
+<script lang="ts">
+    import {Component, Vue} from "vue-property-decorator";
+    import Http from "../../extend/Http";
+    import Utils from "../../extend/Utils";
+    import wx from "weixin-jsapi";
+    import router from "../../router";
+
+    @Component({
+        components: {}
+    })
+    export default class UserIndex extends Vue {
+
+        public data: any = {};
+
+        public avatar: string = "";
+        public nickname: string = "";
+
+        async created() {
+            let res = await Http.getInstance().userinfo();
+            this.data.inviter_code = {
+                label: "邀请码", value: res.data.inviter_code, canEdit: false
+            };
+            this.data.realname = {
+                label: "真实姓名", value: res.data.realname, canEdit: true
+            };
+            this.data.gender = {
+                label: "性别", value: res.data.gender == 1 ? "男" : "女", canEdit: true
+            };
+            this.data.social_type = {
+                label: "户别", value: res.data.social_type == 1 ? "农村" : res.data.social_type == 2 ? "城镇": '',
+                canEdit: true
+            };
+            this.data.id_card = {
+                label: "身份证", value: res.data.id_card, canEdit: true
+            };
+            this.data.mobile = {
+                label: "手机号码", value: res.data.mobile, canEdit: true
+            };
+            this.data.email = {
+                label: "邮箱", value: res.data.email, canEdit: true
+            };
+
+            this.data.addr = {
+                label: "联系地址", value: res.data.addr, canEdit: true
+            };
+            this.avatar = res.data.avatar;
+            this.nickname = res.data.nickname;
+            localStorage.setItem("avatar", res.data.avatar);
+            localStorage.setItem("nickname", res.data.nickname);
+        }
+
+        mounted() {
+        }
+
+        edit(params: any, key: string) {
+            router.push({
+                name: "uedit", params: Object.assign(params, {key: key})
+            });
+        }
+
+        private async wxInit() {
+            let url = location.href.split("#")[0]; // 获取锚点之前的链接
+            let links = "m.wercf.com";
+            let title = "河马代缴";
+            let desc = "了解更多知识,请关注“河马代缴”公众号";
+            let imgUrl = "";
+            let res = await Http.getInstance().getConfig({
+                platform: "weixin", url: url,
+            });
+            res.jsApiList = ["onMenuShareTimeline", "onMenuShareAppMessage", "onMenuShareQQ", "onMenuShareWeibo", "onMenuShareQZone"];
+            wx.config(res);
+            wx.ready(() => {
+                wx.onMenuShareTimeline({
+                    title: title, // 分享标题
+                    link: links, // 分享链接
+                    imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+                // 微信分享菜单测试
+                wx.onMenuShareAppMessage({
+                    title: title, // 分享标题
+                    link: links, // 分享链接
+                    desc: title, // 分享链接
+                    type: "", // 分享链接
+                    dataUrl: "", imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+                wx.onMenuShareQQ({
+                    title: title, // 分享标题
+                    link: links, // 分享链接
+                    desc: title, // 分享链接
+                    type: "", // 分享链接
+                    dataUrl: "", imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+                wx.onMenuShareWeibo({
+                    title: title, // 分享标题
+                    desc: desc, // 分享描述
+                    link: links, // 分享链接
+                    imgUrl: imgUrl, // 分享图标
+                    success: () => {
+                    }, cancel: () => {
+                    }
+                });
+            });
+            wx.error((err: any) => {
+                alert(JSON.stringify(err));
+            });
+        }
+    }
+</script>
+
+<style>
+    .avator {
+        width: 3.5rem;
+        height: 3.5rem;
+        border-radius: 3.5rem;
+    }
+
+    .list-group-item {
+        border-color: #eee;
+    }
+
+    .list-group-item a {
+        color: #212529;
+    }
+    .tuichu{
+
+    }
+</style>

+ 59 - 0
src/views/user/invitation.vue

@@ -0,0 +1,59 @@
+<template>
+    <div class="invitation-wrap">
+        <div class="list-group list-group-flush">
+            <div class="list-group-item d-flex justify-content-between align-items-center"
+                 v-for="item in data.list">
+                <div class="user">
+                    <img class="avator" width="40" :src="item.avatar" alt="">
+                    <span class="name ml-2">{{item.nickname}}</span>
+                </div>
+                <div class="status">
+                    {{item.status}}
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {Component, Vue} from "vue-property-decorator";
+    import Http from "../../extend/Http";
+    import Utils from "../../extend/Utils";
+    import wx from "weixin-jsapi";
+    import router from "../../router";
+
+    @Component({
+        components: {}
+    })
+    export default class UserIndex extends Vue {
+
+        public data: any = {
+            list: [],
+        };
+
+
+        async created() {
+            let res = await Http.getInstance().getInvitationList();
+            res.data.forEach((item: any) => {
+                this.data.list.push({
+                    avatar: item.avatar,
+                    nickname: item.nickname,
+                    status: item.status == 1 ? "已邀请" : item.status == 2 ? "邀请成功,奖励发放中" : "已完成",
+                });
+            });
+        }
+    }
+</script>
+
+
+<style scoped>
+    .avator {
+        width: 2.5rem;
+        height: 2.5rem;
+        border-radius: 3rem;
+    }
+
+    .list-group-item a {
+        color: #212529;
+    }
+</style>

+ 11 - 0
src/views/user/inviter.vue

@@ -0,0 +1,11 @@
+<template>
+     <div>
+         <img src="" alt="">
+     </div>
+</template>
+<script>
+
+</script>
+<style>
+
+</style>

+ 40 - 0
tsconfig.json

@@ -0,0 +1,40 @@
+{
+  "compilerOptions": {
+    "target": "esnext",
+    "module": "esnext",
+    "strict": true,
+    "jsx": "preserve",
+    "importHelpers": true,
+    "moduleResolution": "node",
+    "experimentalDecorators": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "sourceMap": true,
+    "noImplicitAny": false,
+    "baseUrl": ".",
+    "types": [
+      "webpack-env"
+    ],
+    "paths": {
+      "@/*": [
+        "src/*"
+      ]
+    },
+    "lib": [
+      "esnext",
+      "dom",
+      "dom.iterable",
+      "scripthost"
+    ]
+  },
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue",
+    "tests/**/*.ts",
+    "tests/**/*.tsx"
+  ],
+  "exclude": [
+    "node_modules"
+  ]
+}

+ 125 - 0
tslint.json

@@ -0,0 +1,125 @@
+{
+  "defaultSeverity": "warning",
+  "extends": [
+    "tslint:recommended"
+  ],
+  "linterOptions": {
+    "exclude": [
+      "node_modules/**"
+    ]
+  },
+  "rules": {
+    "quotemark": false,
+    // 字符串文字需要单引号或双引号。
+    "indent": false,
+    // 使用制表符或空格强制缩进。
+    "member-access": false,
+    // 需要类成员的显式可见性声明。
+    "interface-name": false,
+    // 接口名要求大写开头
+    "ordered-imports": false,
+    // 要求将import语句按字母顺序排列并进行分组。
+    "object-literal-sort-keys": false,
+    // 检查对象文字中键的排序。
+    "no-consecutive-blank-lines": false,
+    // 不允许连续出现一个或多个空行。
+    "no-shadowed-variable": false,
+    // 不允许隐藏变量声明。
+    "no-trailing-whitespace": false,
+    // 不允许在行尾添加尾随空格。
+    "semicolon": false,
+    // 是否分号结尾
+    "trailing-comma": false,
+    // 是否强象添加逗号
+    "eofline": false,
+    // 是否末尾另起一行
+    "prefer-conditional-expression": false,
+    // for (... in ...)语句必须用if语句过滤
+    "curly": true,
+    //for if do while 要有括号
+    "forin": false,
+    //用for in 必须用if进行过滤
+    "import-blacklist": true,
+    //允许使用import require导入具体的模块
+    "no-arg": true,
+    //不允许使用 argument.callee
+    "no-bitwise": true,
+    //不允许使用按位运算符
+    "no-console": false,
+    //不能使用console
+    "no-construct": true,
+    //不允许使用 String/Number/Boolean的构造函数
+    "no-debugger": true,
+    //不允许使用debugger
+    "no-duplicate-super": true,
+    //构造函数两次用super会发出警告
+    "no-empty": false,
+    //不允许空的块
+    "no-eval": true,
+    //不允许使用eval
+    "no-floating-promises": false,
+    //必须正确处理promise的返回函数
+    "no-for-in-array": false,
+    //不允许使用for in 遍历数组
+    "no-implicit-dependencies": false,
+    //不允许在项目的package.json中导入未列为依赖项的模块
+    "no-inferred-empty-object-type": false,
+    //不允许在函数和构造函数中使用{}的类型推断
+    "no-invalid-template-strings": true,
+    //警告在非模板字符中使用${
+    "no-invalid-this": true,
+    //不允许在非class中使用 this关键字
+    "no-misused-new": true,
+    //禁止定义构造函数或new class
+    "no-null-keyword": false,
+    //不允许使用null关键字
+    "no-object-literal-type-assertion": false,
+    //禁止object出现在类型断言表达式中
+    "no-return-await": true,
+    //不允许return await
+    "arrow-parens": false,
+    //箭头函数定义的参数需要括号
+    "adjacent-overload-signatures": false,
+    //  Enforces function overloads to be consecutive.
+    "ban-comma-operator": true,
+    //禁止逗号运算符。
+    "no-any": false,
+    //不需使用any类型
+    "no-empty-interface": true,
+    //禁止空接口 {}
+    "no-internal-module": true,
+    //不允许内部模块
+    "no-magic-numbers": false,
+    //不允许在变量赋值之外使用常量数值。当没有指定允许值列表时,默认允许-1,0和1
+    "no-namespace": [
+      //不允许使用内部modules和命名空间
+      true,
+      "allow-declarations"
+    ],
+    "no-non-null-assertion": true,
+    //不允许使用!后缀操作符的非空断言。
+    "no-parameter-reassignment": false,
+    //不允许重新分配参数
+    "no-reference": true,
+    // 禁止使用/// <reference path=> 导入 ,使用import代替
+    "no-unnecessary-type-assertion": false,
+    //如果类型断言没有改变表达式的类型就发出警告
+    "no-var-requires": false,
+    //不允许使用var module = require("module"),用 import foo = require('foo')导入
+    "prefer-for-of": true,
+    //建议使用for(..of)
+    "promise-function-async": false,
+    //要求异步函数返回promise
+    "max-classes-per-file": [
+      // 一个脚本最多几个申明类
+      true,
+      2
+    ],
+    "max-line-length": false,
+    "variable-name": false,
+    "prefer-const": false,
+    // 提示可以用const的地方
+    "object-literal-shorthand": false
+    // 必须使用 a = {b} 而不是 a = {b: b}
+  }
+}

+ 43 - 0
vue.config.js

@@ -0,0 +1,43 @@
+const path = require('path');
+const webpack = require('webpack');
+
+const resolve = dir => {
+    return path.join(__dirname, dir)
+};
+
+// 线上打包路径,请根据项目实际线上情况
+const BASE_URL = process.env.NODE_ENV === 'production' ? './' : './';
+
+module.exports = {
+    publicPath: BASE_URL,
+    outputDir: 'dist', // 打包生成的生产环境构建文件的目录
+    assetsDir: '', // 放置生成的静态资源路径,默认在outputDir
+    indexPath: 'index.html', // 指定生成的 index.html 输入路径,默认outputDir
+    pages: undefined, // 构建多页
+    productionSourceMap: false, // 开启 生产环境的 source map?
+    chainWebpack: config => {
+        // 配置路径别名
+        config.resolve.alias
+            .set('@', resolve('src'))
+            .set('_c', resolve('src/components'))
+    },
+    css: {
+        modules: false, // 启用 CSS modules
+        extract: true, // 是否使用css分离插件
+        sourceMap: false, // 开启 CSS source maps?
+        loaderOptions: {} // css预设器配置项
+    },
+    devServer: {
+        port: 8080, // 端口
+        // proxy: 'https://www.easy-mock.com' // 设置代理
+    },
+    configureWebpack: {
+        plugins: [
+            new webpack.ProvidePlugin({
+                $: "jquery",
+                jQuery: "jquery",
+                "windows.jQuery": "jquery"
+            })
+        ]
+    }
+};