瀏覽代碼

first commit

wangfei 6 年之前
當前提交
608bcb9e67
共有 54 個文件被更改,包括 4018 次插入0 次删除
  1. 22
    0
      .gitignore
  2. 26
    0
      README.md
  3. 5
    0
      babel.config.js
  4. 58
    0
      package.json
  5. 二進制
      public/favicon.ico
  6. 18
    0
      public/index.html
  7. 32
    0
      src/App.vue
  8. 21
    0
      src/assets/iconfont.css
  9. 二進制
      src/assets/iconfont.eot
  10. 29
    0
      src/assets/iconfont.svg
  11. 二進制
      src/assets/iconfont.ttf
  12. 二進制
      src/assets/iconfont.woff
  13. 二進制
      src/assets/logo.png
  14. 46
    0
      src/components/XMFooter.vue
  15. 31
    0
      src/components/XMIcon.vue
  16. 137
    0
      src/components/XMPolygon.vue
  17. 21
    0
      src/components/XMRightLay.vue
  18. 26
    0
      src/components/XMSearchForm.vue
  19. 138
    0
      src/config/api.js
  20. 43
    0
      src/layout/default/MenuItem.vue
  21. 25
    0
      src/layout/default/MenuLink.vue
  22. 52
    0
      src/layout/default/User.vue
  23. 131
    0
      src/layout/default/index.vue
  24. 22
    0
      src/layout/index.vue
  25. 33
    0
      src/main.js
  26. 56
    0
      src/router.js
  27. 25
    0
      src/store/index.js
  28. 115
    0
      src/store/modules/activity.js
  29. 80
    0
      src/store/modules/apartment.js
  30. 48
    0
      src/store/modules/appointment.js
  31. 88
    0
      src/store/modules/building.js
  32. 50
    0
      src/store/modules/customer.js
  33. 22
    0
      src/store/modules/img.js
  34. 72
    0
      src/store/system.js
  35. 4
    0
      src/theme.scss
  36. 6
    0
      src/utils/comptype.js
  37. 6
    0
      src/utils/httpcode.js
  38. 112
    0
      src/utils/index.js
  39. 12
    0
      src/utils/message.js
  40. 7
    0
      src/utils/token.js
  41. 15
    0
      src/views/Dashboard.vue
  42. 143
    0
      src/views/Login.vue
  43. 215
    0
      src/views/activity/edit.vue
  44. 391
    0
      src/views/activity/list.vue
  45. 253
    0
      src/views/appointment/list.vue
  46. 430
    0
      src/views/building/edit.vue
  47. 274
    0
      src/views/building/list.vue
  48. 228
    0
      src/views/cms/list.vue
  49. 23
    0
      src/views/comment/list.vue
  50. 73
    0
      src/views/customer/detail.vue
  51. 199
    0
      src/views/customer/list.vue
  52. 138
    0
      src/views/index.js
  53. 3
    0
      src/views/index.vue
  54. 14
    0
      vue.config.js

+ 22
- 0
.gitignore 查看文件

@@ -0,0 +1,22 @@
1
+.DS_Store
2
+node_modules
3
+/dist
4
+package-lock.json
5
+
6
+# local env files
7
+.env.local
8
+.env.*.local
9
+
10
+# Log files
11
+npm-debug.log*
12
+yarn-debug.log*
13
+yarn-error.log*
14
+
15
+# Editor directories and files
16
+.idea
17
+.vscode
18
+*.suo
19
+*.ntvs*
20
+*.njsproj
21
+*.sln
22
+*.sw*

+ 26
- 0
README.md 查看文件

@@ -0,0 +1,26 @@
1
+# 全楼盘
2
+
3
+## Project setup
4
+```
5
+npm install
6
+```
7
+
8
+### Compiles and hot-reloads for development
9
+```
10
+npm run serve
11
+```
12
+
13
+### Compiles and minifies for production
14
+```
15
+npm run build
16
+```
17
+
18
+### Run your tests
19
+```
20
+npm run test
21
+```
22
+
23
+### Lints and fixes files
24
+```
25
+npm run lint
26
+```

+ 5
- 0
babel.config.js 查看文件

@@ -0,0 +1,5 @@
1
+module.exports = {
2
+  presets: [
3
+    '@vue/app'
4
+  ]
5
+}

+ 58
- 0
package.json 查看文件

@@ -0,0 +1,58 @@
1
+{
2
+  "name": "welcome",
3
+  "version": "0.1.0",
4
+  "private": true,
5
+  "scripts": {
6
+    "serve": "vue-cli-service serve",
7
+    "build": "vue-cli-service build",
8
+    "lint": "vue-cli-service lint"
9
+  },
10
+  "dependencies": {
11
+    "@fullcalendar/core": "^4.0.0-beta.4",
12
+    "@fullcalendar/daygrid": "^4.0.0-beta.4",
13
+    "axios": "^0.18.0",
14
+    "blueimp-md5": "^2.10.0",
15
+    "element-ui": "^2.6.1",
16
+    "normalize.css": "^8.0.1",
17
+    "nprogress": "^0.2.0",
18
+    "vue": "^2.6.6",
19
+    "vue-amap": "^0.5.8",
20
+    "vue-router": "^3.0.2",
21
+    "vuex": "^3.1.0"
22
+  },
23
+  "devDependencies": {
24
+    "@vue/cli-plugin-babel": "^3.0.5",
25
+    "@vue/cli-plugin-eslint": "^3.0.5",
26
+    "@vue/cli-service": "^3.0.5",
27
+    "babel-eslint": "^10.0.1",
28
+    "eslint": "^5.8.0",
29
+    "eslint-plugin-vue": "^5.0.0",
30
+    "node-sass": "^4.11.0",
31
+    "sass-loader": "^7.1.0",
32
+    "vue-template-compiler": "^2.5.21"
33
+  },
34
+  "eslintConfig": {
35
+    "root": true,
36
+    "env": {
37
+      "node": true
38
+    },
39
+    "extends": [
40
+      "plugin:vue/essential",
41
+      "eslint:recommended"
42
+    ],
43
+    "rules": {},
44
+    "parserOptions": {
45
+      "parser": "babel-eslint"
46
+    }
47
+  },
48
+  "postcss": {
49
+    "plugins": {
50
+      "autoprefixer": {}
51
+    }
52
+  },
53
+  "browserslist": [
54
+    "> 1%",
55
+    "last 2 versions",
56
+    "not ie <= 8"
57
+  ]
58
+}

二進制
public/favicon.ico 查看文件


+ 18
- 0
public/index.html 查看文件

@@ -0,0 +1,18 @@
1
+<!DOCTYPE html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="utf-8">
5
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
7
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
8
+    <link rel="stylesheet" href="//at.alicdn.com/t/font_1070150_8lyiyriedbr.css">
9
+    <title>全楼盘</title>
10
+  </head>
11
+  <body>
12
+    <noscript>
13
+      <strong>We're sorry but welcome doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
14
+    </noscript>
15
+    <div id="app"></div>
16
+    <!-- built files will be auto injected -->
17
+  </body>
18
+</html>

+ 32
- 0
src/App.vue 查看文件

@@ -0,0 +1,32 @@
1
+<template>
2
+  <div id="app">
3
+    <router-view></router-view>
4
+  </div>
5
+</template>
6
+
7
+<script>
8
+import { mapActions } from 'vuex'
9
+
10
+export default {
11
+  name: 'app',
12
+  methods: {
13
+    ...mapActions([
14
+      'getDicts'
15
+    ])
16
+  },
17
+  components: {
18
+  },
19
+  created() {
20
+    this.getDicts()
21
+  }
22
+}
23
+</script>
24
+
25
+<style>
26
+html, body, #app {
27
+  width: 100%;
28
+  height: 100%;
29
+
30
+  background-color: #f3f3f3;
31
+}
32
+</style>

+ 21
- 0
src/assets/iconfont.css 查看文件

@@ -0,0 +1,21 @@
1
+@font-face {font-family: "iconfont";
2
+  src: url('iconfont.eot?t=1552528558117'); /* IE9 */
3
+  src: url('iconfont.eot?t=1552528558117#iefix') format('embedded-opentype'), /* IE6-IE8 */
4
+  url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALYAAsAAAAABoAAAAKMAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCCcAqBCIEZATYCJAMICwYABCAFhG0HMRvABciemjwJUmbD2AB8U3gADCEePvb7de5zxCUxNPEsmhgiXgKheaislrCUuCF5Cf9/1/TfcmNSy5vikhwgqGLuuOlL0zEpQOMm3ITaXEGNFcopzF7XhQxMPPWJe6f/N4ACme+8y2WMQWNNmtQFGAdSQHtjtIXLJPGGsQte4HkC9YZVEXY7+sYgScaeFoh7VXZDUsEnk7FkJRQNazOIJ8hVyX7cDfAYfT/+w1IkgUSegl12cNnug6ZfmU4aWvtfcx4Q4CegzTlSLAIZcdIY24MEYyD1MdE4OFZx8CvzvxZYxVFNgv11dmEjGITiZ5J40kflTUBGd2aAlUlvIm0qFhbD4bFIZDwWm4xGJ6Z9wlte3ulBd8F5wcS57+9d45MXf33oFEpeGonf3yJHEAj9sPjzjSDlT9YvxgFZGrMPpx2iXypdWa/fsXZ5qb1+XB5OFO+DTYDaB7pGTSAgOLgoS3dmrn4lJQL8vIoceWofIgxzgOoE9uBHSQY2ZYbMtiSZJmlMJWagG6ioV48a7Gp3P1XfdVepUOm6N0dpKEOqMklm9EXkGiyjUNlAvQWdmxt0YJDIkoF52wCh1RUSzX4g1eqWzOh3yHX7RKE1EOqdRceeDaZDTdlhxM2JD3vmseqlfsm18ZS4eITIIcPN8rKIOUFYUA1iu8VWTnYSP2FLLAjOyA7OJSwxauIOcBkxDIoDjGrEyy0K54Eaq1VqupHFS01IsYMh3DjCB/OYh6m8KD8paM+lVD4/gpCFGNxYR0OdfwLBBKm9Y3YWtgFkp9o/qOFRrgmaIXPgOAkmYSgT1gFGEQYDhQWaR2kIL85CmRAJqGF1WklD1Zbttebf7YB6tiEJZyiC8oqG5kJuPwA=') format('woff2'),
5
+  url('iconfont.woff?t=1552528558117') format('woff'),
6
+  url('iconfont.ttf?t=1552528558117') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
7
+  url('iconfont.svg?t=1552528558117#iconfont') format('svg'); /* iOS 4.1- */
8
+}
9
+
10
+.iconfont {
11
+  font-family: "iconfont" !important;
12
+  font-size: 16px;
13
+  font-style: normal;
14
+  -webkit-font-smoothing: antialiased;
15
+  -moz-osx-font-smoothing: grayscale;
16
+}
17
+
18
+.icon-gouxuan:before {
19
+  content: "\e60f";
20
+}
21
+

二進制
src/assets/iconfont.eot 查看文件


+ 29
- 0
src/assets/iconfont.svg 查看文件

@@ -0,0 +1,29 @@
1
+<?xml version="1.0" standalone="no"?>
2
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
3
+<!--
4
+2013-9-30: Created.
5
+-->
6
+<svg>
7
+<metadata>
8
+Created by iconfont
9
+</metadata>
10
+<defs>
11
+
12
+<font id="iconfont" horiz-adv-x="1024" >
13
+  <font-face
14
+    font-family="iconfont"
15
+    font-weight="500"
16
+    font-stretch="normal"
17
+    units-per-em="1024"
18
+    ascent="896"
19
+    descent="-128"
20
+  />
21
+    <missing-glyph />
22
+    
23
+    <glyph glyph-name="gouxuan" unicode="&#58895;" d="M510.194376 831.24416c-246.595304 0-447.24416-200.649879-447.24416-447.245184 0-246.629073 200.614064-447.243137 447.24416-447.243137s447.24416 200.614064 447.24416 447.243137C957.438537 630.594281 756.82345 831.24416 510.194376 831.24416L510.194376 831.24416zM726.02091 457.77219 469.091236 198.048891c-0.064468-0.064468-0.191358-0.098237-0.260943-0.195451-0.092098-0.064468-0.092098-0.190335-0.190335-0.25378-2.051729-1.988284-4.587482-3.208065-6.962575-4.558829-1.183965-0.669242-2.148943-1.698688-3.40147-2.178619-3.848655-1.543146-7.92346-2.344395-12.002358-2.344395-4.107551 0-8.244778 0.801249-12.127202 2.408863-1.283226 0.543376-2.311649 1.635243-3.53143 2.310625-2.37407 1.346671-4.842285 2.536776-6.89913 4.552689-0.062422 0.064468-0.097214 0.195451-0.161682 0.25992-0.062422 0.094144-0.190335 0.094144-0.254803 0.192382L296.937364 328.102922c-12.353352 12.707416-12.06478 33.015951 0.641613 45.37135 12.70537 12.322653 32.985252 12.097526 45.373397-0.64366l103.546308-106.400309 233.92268 236.489132c12.45159 12.608156 32.79594 12.735046 45.37442 0.254803C738.33947 490.693997 738.467383 470.379323 726.02091 457.77219L726.02091 457.77219zM726.02091 457.77219"  horiz-adv-x="1024" />
24
+
25
+    
26
+
27
+
28
+  </font>
29
+</defs></svg>

二進制
src/assets/iconfont.ttf 查看文件


二進制
src/assets/iconfont.woff 查看文件


二進制
src/assets/logo.png 查看文件


+ 46
- 0
src/components/XMFooter.vue 查看文件

@@ -0,0 +1,46 @@
1
+<template>
2
+  <div class="footer">
3
+    <div>{{copyright}}</div>
4
+    <div>{{company}}</div>
5
+  </div>
6
+</template>
7
+
8
+<script>
9
+export default {
10
+  name: 'xm-footer',
11
+  props: [
12
+    'company',
13
+  ],
14
+  data () {
15
+    return {}
16
+  },
17
+  computed: {
18
+    copyright() {
19
+      const year = (new Date()).getFullYear()
20
+      return `CopyRight @ ${year}`
21
+    }
22
+  }
23
+}
24
+</script>
25
+
26
+<style lang="scss">
27
+.footer {
28
+  display:flex;
29
+  justify-content:center;
30
+  align-items:center;
31
+  width: 100%;
32
+  height: 100%;
33
+  color: #666;
34
+  // text-align: center;
35
+  font-size: 14px;
36
+  font-weight: 500;
37
+  text-shadow: 1px 1px #fff;
38
+
39
+  div {
40
+    & + div {
41
+      padding-left: 12px;
42
+    }
43
+  }
44
+}
45
+</style>
46
+

+ 31
- 0
src/components/XMIcon.vue 查看文件

@@ -0,0 +1,31 @@
1
+<template>
2
+  <i
3
+    :class="{ iconfont: true, [`xm-${name}`]: true}"
4
+    :style="{ color: color || undefined, fontSize }"></i>
5
+</template>
6
+
7
+<script>
8
+export default {
9
+  name: 'xm-icon',
10
+  props: [
11
+    'name',
12
+    'color',
13
+    'size',
14
+  ],
15
+  computed: {
16
+    fontSize() {
17
+      switch (this.size) {
18
+        case 'large':
19
+          return '64px'
20
+        case 'medium':
21
+          return '48px'
22
+        case 'small':
23
+          return '24px'
24
+        case 'min':
25
+        default:
26
+          return '16px'
27
+      }
28
+    }
29
+  }
30
+}
31
+</script>

+ 137
- 0
src/components/XMPolygon.vue 查看文件

@@ -0,0 +1,137 @@
1
+<template>
2
+  <canvas></canvas>
3
+</template>
4
+
5
+<script>
6
+export default {
7
+  name: 'xmpolygon',
8
+  props: [
9
+    'color',
10
+    'dots',
11
+  ],
12
+  data() {
13
+    return {
14
+      interval: null,
15
+    }
16
+  },
17
+  mounted() {
18
+    if (this.$el && !this.interval) {
19
+      this.interval = canvasMotion(this.$el, this.color, this.dots) 
20
+    }
21
+  },
22
+  destroyed() {
23
+    if (this.interval) {
24
+      clearInterval(this.interval);
25
+      window.onmousemove = null;
26
+    }
27
+  }
28
+}
29
+
30
+function canvasMotion(canvas, color = '#044281', dotNum = 800) {
31
+  const ctx = canvas.getContext('2d');
32
+
33
+  canvas.width = window.innerWidth;
34
+  canvas.height = window.innerHeight;
35
+  canvas.style.display = 'block';
36
+
37
+  ctx.fillStyle = color;
38
+  ctx.lineWidth = .2;
39
+  ctx.strokeStyle = color;
40
+
41
+  const mousePosition = {
42
+      x: 30 * canvas.width / 100,
43
+      y: 30 * canvas.height / 100
44
+  };
45
+
46
+  const dots = {
47
+      nb: dotNum,
48
+      distance: 60,
49
+      d_radius: 100,
50
+      array: []
51
+  };
52
+
53
+  function Dot(){
54
+      this.x = Math.random() * canvas.width;
55
+      this.y = Math.random() * canvas.height;
56
+
57
+      this.vx = -.5 + Math.random();
58
+      this.vy = -.5 + Math.random();
59
+
60
+      this.radius = Math.random();
61
+  }
62
+
63
+  Dot.prototype = {
64
+      create: function(){
65
+          ctx.beginPath();
66
+          ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
67
+          ctx.fill();
68
+      },
69
+
70
+      animate: function(){
71
+          for(let i = 0; i < dots.nb; i++){
72
+
73
+              const dot = dots.array[i];
74
+
75
+              if(dot.y < 0 || dot.y > canvas.height){
76
+                  // dot.vx = dot.vx;
77
+                  dot.vy = - dot.vy;
78
+              }
79
+              else if(dot.x < 0 || dot.x > canvas.width){
80
+                  dot.vx = - dot.vx;
81
+                  // dot.vy = dot.vy;
82
+              }
83
+              dot.x += dot.vx;
84
+              dot.y += dot.vy;
85
+          }
86
+      },
87
+
88
+      line: function(){
89
+          for(let i = 0; i < dots.nb; i++){
90
+              for(let j = 0; j < dots.nb; j++){
91
+                  const i_dot = dots.array[i];
92
+                  const j_dot = dots.array[j];
93
+
94
+                  if((i_dot.x - j_dot.x) < dots.distance 
95
+                    && (i_dot.y - j_dot.y) < dots.distance 
96
+                    && (i_dot.x - j_dot.x) > - dots.distance 
97
+                    && (i_dot.y - j_dot.y) > - dots.distance) {
98
+                      if((i_dot.x - mousePosition.x) < dots.d_radius 
99
+                        && (i_dot.y - mousePosition.y) < dots.d_radius 
100
+                        && (i_dot.x - mousePosition.x) > - dots.d_radius 
101
+                        && (i_dot.y - mousePosition.y) > - dots.d_radius) {
102
+                          ctx.beginPath();
103
+                          ctx.moveTo(i_dot.x, i_dot.y);
104
+                          ctx.lineTo(j_dot.x, j_dot.y);
105
+                          ctx.stroke();
106
+                          ctx.closePath();
107
+                      }
108
+                  }
109
+              }
110
+          }
111
+      }
112
+  };
113
+
114
+  function createDots(){
115
+      ctx.clearRect(0, 0, canvas.width, canvas.height);
116
+
117
+      for(let i = 0; i < dots.nb; i++){
118
+          dots.array.push(new Dot());
119
+          dots.array[i].create();
120
+      }
121
+
122
+      const dot = dots.array[dots.nb - 1];
123
+      dot.line();
124
+      dot.animate();
125
+  }
126
+
127
+  window.onmousemove = function(e) {
128
+      mousePosition.x = e.pageX;
129
+      mousePosition.y = e.pageY;
130
+  }
131
+
132
+  mousePosition.x = window.innerWidth / 2;
133
+  mousePosition.y = window.innerHeight / 2;
134
+
135
+  return setInterval(createDots, 1000/30);
136
+}
137
+</script>

+ 21
- 0
src/components/XMRightLay.vue 查看文件

@@ -0,0 +1,21 @@
1
+<template>
2
+  <div class="right-lay">
3
+    <div></div>
4
+    <slot></slot>
5
+  </div>
6
+</template>
7
+
8
+<style lang="scss" scoped>
9
+  .right-lay {
10
+    display: flex;
11
+    justify-items: end;
12
+
13
+    & > div:first-child {
14
+      flex: auto;
15
+    }
16
+
17
+    & > * {
18
+      flex: none;
19
+    }
20
+  }
21
+</style>

+ 26
- 0
src/components/XMSearchForm.vue 查看文件

@@ -0,0 +1,26 @@
1
+<template>
2
+  <el-form :inline="true" size="small" class="search-form">
3
+    <slot></slot>
4
+    <el-form-item>
5
+      <el-button type="primary" @click="submit">确定</el-button>
6
+    </el-form-item>
7
+  </el-form>
8
+</template>
9
+
10
+<script>
11
+export default {
12
+  name: 'search-form',
13
+  methods: {
14
+    submit() {
15
+      this.$emit('submit')
16
+    }
17
+  },
18
+}
19
+</script>
20
+
21
+<style lang="scss" scoped>
22
+.search-form {
23
+
24
+}
25
+</style>
26
+

+ 138
- 0
src/config/api.js 查看文件

@@ -0,0 +1,138 @@
1
+const commPrefix = '/api'
2
+
3
+const apis = {
4
+  user: {
5
+    login: {
6
+      method: 'post',
7
+      url: `${commPrefix}/user/login`
8
+    },
9
+    logout: {
10
+      method: 'post',
11
+      url: `${commPrefix}/user/loginOut`
12
+    },
13
+    updatepass: {
14
+      method: 'put',
15
+      url: `${commPrefix}/admin/password`
16
+    },
17
+    getuser: {
18
+      method: 'get',
19
+      url: `${commPrefix}/user/info`
20
+    }
21
+  },
22
+  building: {
23
+    list: {
24
+      method: 'get',
25
+      url: `${commPrefix}/buildinglist`
26
+    },
27
+    add: {
28
+      method: 'post',
29
+      url: `${commPrefix}/building/add`
30
+    },
31
+    edit: {
32
+      method: 'put',
33
+      url: `${commPrefix}/building/update`
34
+    },
35
+    updateStatus: {
36
+      method: 'put',
37
+      url: `${commPrefix}/building/update/status`
38
+    },
39
+    detail: {
40
+      method: 'get',
41
+      url: `${commPrefix}/buildingSelectId/:id`
42
+    },
43
+    delete: {
44
+      method: 'delete',
45
+      url: `${commPrefix}/building/delete/:id`
46
+    }
47
+  },
48
+  apartment: {
49
+    list: {
50
+      method: 'get',
51
+      url: `${commPrefix}/buildingApartment/buildingId/:buildingId`
52
+    },
53
+    detail: {
54
+      method: 'get',
55
+      url: `${commPrefix}/buildingApartment/:id`
56
+    },
57
+    add: {
58
+      method: 'post',
59
+      url: `${commPrefix}/buildingApartment/add`
60
+    },
61
+    edit: {
62
+      method: 'put',
63
+      url: `${commPrefix}/buildingApartment/update`
64
+    },
65
+    delete: {
66
+      method: 'delete',
67
+      url: `${commPrefix}/buildingApartment/:id`
68
+    }
69
+  },
70
+  appointment: {
71
+    list: {
72
+      method: 'get',
73
+      url: `${commPrefix}/appointmentlist`
74
+    },
75
+    writeoff: {
76
+      method: 'put',
77
+      url: `${commPrefix}/appointment/writeoff/:id`
78
+    }
79
+  },
80
+  file: {
81
+    upload: {
82
+      method: 'post',
83
+      url: `${commPrefix}/uploadImage`
84
+    }
85
+  },
86
+  activity: {
87
+    list: {
88
+      method: 'get',
89
+      url: `${commPrefix}/activity/list`
90
+    },
91
+    detail: {
92
+      method: 'get',
93
+      url: `${commPrefix}/activity/:id`
94
+    },
95
+    add: {
96
+      method: 'post',
97
+      url: `${commPrefix}/activity/add`
98
+    },
99
+    edit: {
100
+      method: 'put',
101
+      url: `${commPrefix}/activity/update`
102
+    },
103
+    delete: {
104
+      method: 'delete',
105
+      url: `${commPrefix}/activity/delete/:id`
106
+    },
107
+    enrolls: {
108
+      method: 'post',
109
+      url: `${commPrefix}/activity/enroll/list/:id`
110
+    },
111
+    public: {
112
+      method: 'put',
113
+      url: `${commPrefix}/activity/send/:id`
114
+    },
115
+    cancel: {
116
+      method: 'put',
117
+      url: `${commPrefix}/activity/cancel/:id`
118
+    }
119
+  },
120
+  dict: {
121
+    get: {
122
+      method: 'get',
123
+      url: `${commPrefix}/dict`
124
+    }
125
+  },
126
+  customer: {
127
+    list: {
128
+      method: 'get',
129
+      url: `${commPrefix}/customerList`
130
+    },
131
+    detail: {
132
+      method: 'get',
133
+      url: `${commPrefix}/customerSelectId/:id`
134
+    }
135
+  }
136
+}
137
+
138
+export default apis

+ 43
- 0
src/layout/default/MenuItem.vue 查看文件

@@ -0,0 +1,43 @@
1
+<template>
2
+  <el-submenu v-if="checkChildren(menu) && isShow(menu)" :index="menu.name">
3
+    <template slot="title">
4
+      <xm-icon v-if="menu.meta.icon" :name="menu.meta.icon"></xm-icon>
5
+      <span slot="title">{{ menu.meta.title }}</span>
6
+    </template>
7
+    <menu-item v-for="item in menu.children" :key="item.name" :menu="item" :validate="validate" />
8
+  </el-submenu>
9
+  <menu-link v-else-if="isShow(menu)" :to="menu.name">
10
+    <el-menu-item :index="menu.name">
11
+      <xm-icon v-if="menu.meta.icon" :name="menu.meta.icon"></xm-icon>
12
+      <span slot="title">{{ menu.meta.title }}</span>
13
+    </el-menu-item>
14
+  </menu-link>
15
+</template>
16
+
17
+<script>
18
+
19
+export default {
20
+  name: 'menu-item',
21
+  components: {
22
+    menuLink: () => import('./MenuLink.vue'),
23
+  },
24
+  props: [
25
+    'menu',
26
+    'validate',
27
+  ],
28
+  methods: {
29
+    checkChildren(menu) {
30
+      if (!menu.children || !menu.children.length) return false
31
+      
32
+      return menu.children.some(x => x.meta.menuShow)
33
+    },
34
+    isShow(item) {
35
+      if (!item.meta.menuShow) return false
36
+      if (!this.validate(item)) return false
37
+
38
+      return true
39
+    }
40
+  }
41
+}
42
+</script>
43
+

+ 25
- 0
src/layout/default/MenuLink.vue 查看文件

@@ -0,0 +1,25 @@
1
+<template>
2
+  <a class="noStyle" :href="to" v-if="isExternal(to)" target="_blank"><slot></slot></a>
3
+  <router-link class="noStyle" :to="{ name: to }" v-else><slot></slot></router-link>
4
+</template>
5
+
6
+<script>
7
+export default {
8
+  name: 'menu-link',
9
+  props: [
10
+    'to',
11
+  ],
12
+  methods: {
13
+    isExternal(path) {
14
+      return /^(https?:|mailto:|tel:)/.test(path)
15
+    }
16
+  }
17
+}
18
+</script>
19
+
20
+<style lang="scss" scoped>
21
+.noStyle {
22
+  text-decoration: none;
23
+}
24
+</style>
25
+

+ 52
- 0
src/layout/default/User.vue 查看文件

@@ -0,0 +1,52 @@
1
+<template>
2
+  <div class="userboard">
3
+    <!-- <el-badge :is-dot="!!(messages && messages.length)" class="message">
4
+      <xm-icon name="ring" :style="{ fontSize: '18px' }"></xm-icon>
5
+    </el-badge> -->
6
+    <el-dropdown @command="clickUser">
7
+      <span>
8
+        <xm-icon name="user" :style="{ fontSize: '18px' }"></xm-icon>
9
+        <xm-icon name="arrowdown"></xm-icon>
10
+      </span>
11
+      <el-dropdown-menu slot="dropdown">
12
+        <!-- <el-dropdown-item command="resetPassword">重置密码</el-dropdown-item> -->
13
+        <el-dropdown-item command="logout">登出</el-dropdown-item>
14
+      </el-dropdown-menu>
15
+    </el-dropdown>
16
+  </div>
17
+</template>
18
+
19
+<script>
20
+export default {
21
+  name: 'user-board',
22
+  props: [
23
+    'messages',
24
+    'user',
25
+  ],
26
+  methods: {
27
+    clickUser(cmd) {
28
+      switch (cmd) {
29
+        case 'resetPassword':
30
+          this.$emit('resetPassword')
31
+          break
32
+        case 'logout':
33
+          this.$emit('logout')
34
+          break
35
+        default:
36
+          // todo
37
+      }
38
+    }
39
+  }
40
+}
41
+</script>
42
+
43
+
44
+<style lang="scss" scoped>
45
+.userboard {
46
+  .message {
47
+    margin-right: 12px;
48
+    vertical-align: baseline;
49
+  }
50
+}
51
+</style>
52
+

+ 131
- 0
src/layout/default/index.vue 查看文件

@@ -0,0 +1,131 @@
1
+<template>
2
+  <el-container>
3
+    <el-aside class="siderbar" :style="{ width: '210px' }">
4
+      <div class="logo"></div>
5
+      <el-menu class="nav-menu" v-bind="menuParams">
6
+        <menu-item v-for="menu in menus" :key="menu.name" :menu="menu" :validate="isInMenuList" />
7
+      </el-menu>
8
+    </el-aside>
9
+    <el-container>
10
+      <el-header class="head">
11
+        <div class="h-item">&nbsp;</div>
12
+        <user class="h-item-noflex" :messages="[1]" @logout="logout" @resetPassword="resetPassword"></user>
13
+      </el-header>
14
+      <el-breadcrumb class="breadcrumb" separator-class="el-icon-arrow-right">
15
+        <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
16
+        <el-breadcrumb-item v-for="bread in breads" :key="bread.name">{{ (bread.meta || {}).title }}</el-breadcrumb-item>
17
+      </el-breadcrumb>
18
+      <el-main class="main">
19
+        <router-view></router-view>
20
+      </el-main>
21
+      <el-footer class="noPadding">
22
+        <xm-footer company="荟房科技" />
23
+      </el-footer>
24
+    </el-container>
25
+  </el-container>
26
+</template>
27
+
28
+<script>
29
+import { pages, pageArray } from '@/views'
30
+
31
+export default {
32
+  name: 'default-layout',
33
+  components: {
34
+    xmFooter: () => import('@/components/XMFooter.vue'),
35
+    menuItem: () => import('./MenuItem.vue'),
36
+    user: () => import('./User.vue'),
37
+  },
38
+  data () {
39
+    return {
40
+      menus: pages,
41
+      menuArray: pageArray,
42
+    }
43
+  },
44
+  computed: {
45
+    menuParams() {
46
+      return {
47
+        backgroundColor: '#24292e',
48
+        textColor: '#eee',
49
+        activeTextColor: '#FFD04B',
50
+        defaultActive: this.$route.name,
51
+      }
52
+    },
53
+    breads() {
54
+      const route = this.menuArray.filter(x => x.name === this.$route.name)[0] || {}
55
+      const p = (route.parents || []).map((rn) => {
56
+        return this.menuArray.filter(x => x.name === rn.name)[0];
57
+      })
58
+
59
+      return p.concat(route)
60
+    }
61
+  },
62
+  methods: {
63
+    isInMenuList(menu) {
64
+      return !!this.menuArray.filter(x => x.name === menu.name)[0]
65
+    },
66
+    resetPassword() {
67
+      window.console.log('------resetPassword------')
68
+    },
69
+    logout() {
70
+      this.$store.dispatch('logout').then(() => {
71
+        this.$router.push({
72
+          name: 'login',
73
+          query: {
74
+            from: this.$route.name,
75
+          }
76
+        })
77
+      })
78
+    }
79
+  }
80
+}
81
+</script>
82
+
83
+<style lang="scss" scoped>
84
+.noPadding {
85
+  padding: 0;
86
+}
87
+
88
+.head {
89
+  display: flex;
90
+  align-items: center;
91
+  justify-items: end;
92
+  background-color: #fafafa;
93
+  border-bottom: 1px solid #dfdfdf;
94
+
95
+  .h-item {
96
+    flex: auto;
97
+  }
98
+
99
+  .h-item-noflex {
100
+    flex: none;
101
+  }
102
+}
103
+
104
+.breadcrumb {
105
+  margin: 12px 20px;
106
+}
107
+
108
+.logo {
109
+  
110
+}
111
+
112
+.siderbar {
113
+  border-right: solid 1px #e6e6e6;
114
+  
115
+  color: #eee;
116
+  background-color: #24292e;
117
+
118
+  .nav-menu {
119
+    border: none;
120
+  }
121
+}
122
+
123
+.main {
124
+  /*160 = header + footer + margin */
125
+  min-height: calc(100vh - 160px);
126
+  position: relative;
127
+  overflow: hidden;
128
+  background-color: #fff;
129
+  margin: 0 20px;
130
+}
131
+</style>

+ 22
- 0
src/layout/index.vue 查看文件

@@ -0,0 +1,22 @@
1
+<template>
2
+  <keep-alive>
3
+    <component :is="currentComponent"></component>
4
+  </keep-alive>
5
+</template>
6
+
7
+<script>
8
+export default {
9
+  name: 'xm-layout',
10
+  props: [
11
+    'theme', // 有可能来自 store
12
+  ],
13
+  components: {
14
+    defaultLayout: () => import('./default'),
15
+  },
16
+  computed: {
17
+    currentComponent() {
18
+      return `${this.theme || 'default'}-layout`
19
+    }
20
+  }
21
+}
22
+</script>

+ 33
- 0
src/main.js 查看文件

@@ -0,0 +1,33 @@
1
+import Vue from 'vue'
2
+import Element from 'element-ui'
3
+import XMIcon from '@/components/XMIcon.vue'
4
+import XMRightLay from '@/components/XMRightLay.vue'
5
+import XMSearchForm from '@/components/XMSearchForm.vue'
6
+import App from './App.vue'
7
+import router from './router'
8
+import store from './store'
9
+import 'normalize.css/normalize.css'
10
+import './theme.scss'
11
+import './assets/iconfont.css'
12
+import VueAMap from 'vue-amap'
13
+
14
+Vue.use(Element)
15
+
16
+Vue.config.productionTip = false
17
+Vue.component('xm-icon', XMIcon)
18
+Vue.component('xm-rtl', XMRightLay)
19
+Vue.component('xm-search', XMSearchForm)
20
+Vue.use(VueAMap)
21
+
22
+VueAMap.initAMapApiLoader({
23
+  key: 'f0d1d4f82432504003ebf46e5e36ff03',
24
+  plugin: ['AMap.Autocomplete', 'AMap.PlaceSearch', 'AMap.Scale', 'AMap.OverView', 'AMap.ToolBar', 'AMap.MapType', 'AMap.PolyEditor', 'AMap.CircleEditor'],
25
+  // 默认高德 sdk 版本为 1.4.4
26
+  v: '1.4.4'
27
+})
28
+
29
+new Vue({
30
+  render: h => h(App),
31
+  router,
32
+  store,
33
+}).$mount('#app')

+ 56
- 0
src/router.js 查看文件

@@ -0,0 +1,56 @@
1
+import Vue from 'vue'
2
+import VueRouter from 'vue-router'
3
+import NProgress from 'nprogress' // progress bar
4
+import 'nprogress/nprogress.css'
5
+import store from './store'
6
+import { pages } from './views'
7
+
8
+Vue.use(VueRouter)
9
+
10
+const routes = [
11
+  {
12
+    path: '/',
13
+    name: 'index',
14
+    component: () => import('@/layout/index.vue'),
15
+    children: [
16
+      ...pages,
17
+    ]
18
+  },
19
+  {
20
+    path: '/login',
21
+    name: 'login',
22
+    component: () => import('@/views/Login.vue')
23
+  }
24
+]
25
+
26
+const router = new VueRouter({ routes })
27
+
28
+// 校验用户权限
29
+router.beforeEach((to, from, next) => {
30
+  NProgress.start()
31
+  if (to.name === 'login') {
32
+    next()
33
+    return
34
+  }
35
+  store.dispatch('checkLogin').then((isLogin) => {
36
+    if (isLogin) {
37
+      if (to.name === 'login') {
38
+        next({ name: 'index' })
39
+      } else {
40
+        next()
41
+      }
42
+    } else {
43
+      if (to.name === 'login') {
44
+        next()
45
+      } else {
46
+        next({ name: 'login', query: { from: to.name } })
47
+      }
48
+    }
49
+  })
50
+})
51
+
52
+router.afterEach(() => {
53
+  NProgress.done()
54
+})
55
+
56
+export default router

+ 25
- 0
src/store/index.js 查看文件

@@ -0,0 +1,25 @@
1
+import Vue from 'vue'
2
+import Vuex from 'vuex'
3
+import system from './system'
4
+import building from './modules/building'
5
+import appointment from './modules/appointment'
6
+import activity from './modules/activity'
7
+import img from './modules/img'
8
+import customer from './modules/customer'
9
+import apartment from './modules/apartment'
10
+
11
+Vue.use(Vuex)
12
+
13
+const store = new Vuex.Store({
14
+  ...system,
15
+  modules: {
16
+    building,
17
+    appointment,
18
+    activity,
19
+    img,
20
+    customer,
21
+    apartment
22
+  }
23
+})
24
+
25
+export default store

+ 115
- 0
src/store/modules/activity.js 查看文件

@@ -0,0 +1,115 @@
1
+import lodash from 'lodash'
2
+import { interact, replaceApiParams } from '../../utils'
3
+import apis from '../../config/api'
4
+
5
+export default {
6
+  namespaced: true,
7
+  state: {
8
+    activitys: {},
9
+    detail: {},
10
+    enrolls: {}
11
+  },
12
+  mutations: {
13
+    updateList (state, payload) {
14
+      state.activitys = payload
15
+    },
16
+    updateEnrollsList (state, payload) {
17
+      state.enrolls = payload
18
+    },
19
+    updateDetail (state, payload) {
20
+      state.detail = payload
21
+    }
22
+  },
23
+  actions: {
24
+    setDetailNull ({ commit }) {
25
+      commit('updateDetail', {})
26
+    },
27
+    getActivitys ({ commit }, payload) {
28
+      return new Promise((resolve, reject) => {
29
+        const api = lodash.get(apis, 'activity.list')
30
+        interact(api, payload).then((data) => {
31
+          commit('updateList', data)
32
+          resolve(data)
33
+        }).catch(({ message }) => {
34
+          if (typeof message === 'string') {
35
+            reject(message)
36
+          }
37
+        })
38
+      })
39
+    },
40
+    getDetail ({ commit }, payload) {
41
+      return new Promise((resolve, reject) => {
42
+        const api = replaceApiParams(lodash.get(apis, 'activity.detail'), payload)
43
+        interact(api).then((data) => {
44
+          commit('updateDetail', data)
45
+          resolve(data)
46
+        }).catch(({ message }) => {
47
+          if (typeof message === 'string') {
48
+            reject(message)
49
+          }
50
+        })
51
+      })
52
+    },
53
+    addActivity (_, payload) {
54
+      const { onSuccess } = payload
55
+      const api = lodash.get(apis, 'activity.add')
56
+      interact(api, payload.detail).then((data) => {
57
+        onSuccess(data)
58
+      })
59
+    },
60
+    editActivity (_, payload) {
61
+      const { onSuccess } = payload
62
+      const api = lodash.get(apis, 'activity.edit')
63
+      interact(api, payload.detail).then((data) => {
64
+        onSuccess(data)
65
+      })
66
+    },
67
+    deleteActivity (_, payload) {
68
+      return new Promise((resolve, reject) => {
69
+        const api = replaceApiParams(lodash.get(apis, 'activity.delete'), payload)
70
+        interact(api).then(() => {
71
+          resolve()
72
+        }).catch(() => {
73
+          reject()
74
+        })
75
+      })
76
+    },
77
+    getEnrolls ({ commit }, payload) {
78
+      return new Promise((resolve, reject) => {
79
+        const api = replaceApiParams(lodash.get(apis, 'activity.enrolls'), payload)
80
+        interact(api, payload).then((data) => {
81
+          commit('updateEnrollsList', data)
82
+          resolve(data)
83
+        }).catch(({ message }) => {
84
+          if (typeof message === 'string') {
85
+            reject(message)
86
+          }
87
+        })
88
+      })
89
+    },
90
+    publicActivity (_, payload) {
91
+      return new Promise((resolve, reject) => {
92
+        const api = replaceApiParams(lodash.get(apis, 'activity.public'), payload)
93
+        interact(api).then((data) => {
94
+          resolve(data)
95
+        }).catch(({ message }) => {
96
+          if (typeof message === 'string') {
97
+            reject(message)
98
+          }
99
+        })
100
+      })
101
+    },
102
+    cancelActivity (_, payload) {
103
+      return new Promise((resolve, reject) => {
104
+        const api = replaceApiParams(lodash.get(apis, 'activity.cancel'), payload)
105
+        interact(api).then((data) => {
106
+          resolve(data)
107
+        }).catch(({ message }) => {
108
+          if (typeof message === 'string') {
109
+            reject(message)
110
+          }
111
+        })
112
+      })
113
+    }
114
+  }
115
+}

+ 80
- 0
src/store/modules/apartment.js 查看文件

@@ -0,0 +1,80 @@
1
+import lodash from 'lodash'
2
+import { interact, replaceApiParams } from '../../utils'
3
+import apis from '../../config/api'
4
+
5
+export default {
6
+  namespaced: true,
7
+  state: {
8
+    apartmentts: [],
9
+    detail: {}
10
+  },
11
+  mutations: {
12
+    updateList (state, payload) {
13
+      state.apartments = payload
14
+    },
15
+    updateDetail (state, payload) {
16
+      state.detail = payload
17
+    }
18
+  },
19
+  actions: {
20
+    setDetailNull ({ commit }) {
21
+      commit('updateDetail', {})
22
+    },
23
+    getApartments ({ commit }, payload) {
24
+      return new Promise((resolve, reject) => {
25
+        const api = replaceApiParams(lodash.get(apis, 'apartment.list'), payload)
26
+        interact(api).then((data) => {
27
+          commit('updateList', data)
28
+          resolve(data)
29
+        }).catch(({ message }) => {
30
+          if (typeof message === 'string') {
31
+            reject(message)
32
+          }
33
+        })
34
+      })
35
+    },
36
+    getApartmentDetail ({ commit }, payload) {
37
+      return new Promise((resolve, reject) => {
38
+        const api = replaceApiParams(lodash.get(apis, 'apartment.detail'), payload)
39
+        interact(api).then((data) => {
40
+          commit('updateDetail', data)
41
+          resolve(data)
42
+        }).catch(({ message }) => {
43
+          if (typeof message === 'string') {
44
+            reject(message)
45
+          }
46
+        })
47
+      })
48
+    },
49
+    addApartment (_, payload) {
50
+      return new Promise((resolve, reject) => {
51
+        const api = lodash.get(apis, 'apartment.add')
52
+        interact(api, payload).then((data) => {
53
+          resolve(data)
54
+        }).catch(() => {
55
+          reject()
56
+        })
57
+      })
58
+    },
59
+    editApartment (_, payload) {
60
+      return new Promise((resolve, reject) => {
61
+        const api = lodash.get(apis, 'apartment.edit')
62
+        interact(api, payload).then((data) => {
63
+          resolve(data)
64
+        }).catch(() => {
65
+          reject()
66
+        })
67
+      })
68
+    },
69
+    deleteApartment (_, payload) {
70
+      return new Promise((resolve, reject) => {
71
+        const api = replaceApiParams(lodash.get(apis, 'apartment.delete'), payload)
72
+        interact(api).then(() => {
73
+          resolve()
74
+        }).catch(() => {
75
+          reject()
76
+        })
77
+      })
78
+    },
79
+  }
80
+}

+ 48
- 0
src/store/modules/appointment.js 查看文件

@@ -0,0 +1,48 @@
1
+import lodash from 'lodash'
2
+import { interact, replaceApiParams } from '../../utils'
3
+import apis from '../../config/api'
4
+
5
+export default {
6
+  namespaced: true,
7
+  state: {
8
+    appointments: {},
9
+    detail: {}
10
+  },
11
+  mutations: {
12
+    updateList (state, payload) {
13
+      state.appointments = payload
14
+    },
15
+    updateDetail (state, payload) {
16
+      state.detail = payload
17
+    }
18
+  },
19
+  actions: {
20
+    setDetailNull ({ commit }) {
21
+      commit('updateDetail', {})
22
+    },
23
+    getAppointments ({ commit }, payload) {
24
+      return new Promise((resolve, reject) => {
25
+        const api = lodash.get(apis, 'appointment.list')
26
+        interact(api, payload).then((data) => {
27
+          commit('updateList', data)
28
+          resolve(data)
29
+        }).catch(({ message }) => {
30
+          if (typeof message === 'string') {
31
+            reject(message)
32
+          }
33
+        })
34
+      })
35
+    },
36
+    appointWriteOff (_, payload) {
37
+      return new Promise((resolve, reject) => {
38
+        const {id, detail} = payload
39
+        const api = replaceApiParams(lodash.get(apis, 'appointment.writeoff'), {id})
40
+        interact(api, detail).then((data) => {
41
+          resolve(data)
42
+        }).catch(({ message }) => {
43
+            reject(message)
44
+        })
45
+      })
46
+    }
47
+  }
48
+}

+ 88
- 0
src/store/modules/building.js 查看文件

@@ -0,0 +1,88 @@
1
+import lodash from 'lodash'
2
+import { interact, replaceApiParams } from '../../utils'
3
+import apis from '../../config/api'
4
+
5
+export default {
6
+  namespaced: true,
7
+  state: {
8
+    buildings: {},
9
+    detail: {}
10
+  },
11
+  mutations: {
12
+    updateList (state, payload) {
13
+      state.buildings = payload
14
+    },
15
+    updateDetail (state, payload) {
16
+      state.detail = payload
17
+    }
18
+  },
19
+  actions: {
20
+    setDetailNull ({ commit }) {
21
+      commit('updateDetail', {})
22
+    },
23
+    getBuildings ({ commit }, payload) {
24
+      return new Promise((resolve, reject) => {
25
+        const api = lodash.get(apis, 'building.list')
26
+        interact(api, payload).then((data) => {
27
+          commit('updateList', data)
28
+          resolve(data)
29
+        }).catch(({ message }) => {
30
+          if (typeof message === 'string') {
31
+            reject(message)
32
+          }
33
+        })
34
+      })
35
+    },
36
+    getDetail ({ commit }, payload) {
37
+      return new Promise((resolve, reject) => {
38
+        const api = replaceApiParams(lodash.get(apis, 'building.detail'), payload)
39
+        interact(api).then((data) => {
40
+          commit('updateDetail', data)
41
+          resolve(data)
42
+        }).catch(({ message }) => {
43
+          if (typeof message === 'string') {
44
+            reject(message)
45
+          }
46
+        })
47
+      })
48
+    },
49
+    addBuilding (_, payload) {
50
+      const { onSuccess } = payload
51
+      const api = lodash.get(apis, 'building.add')
52
+      interact(api, payload.detail).then((data) => {
53
+        onSuccess(data)
54
+      })
55
+    },
56
+    editBuilding (_, payload) {
57
+      const { onSuccess } = payload
58
+      const api = lodash.get(apis, 'building.edit')
59
+      interact(api, payload.detail).then((data) => {
60
+        onSuccess(data)
61
+      })
62
+    },
63
+    delBuilding (_, payload) {
64
+      return new Promise((resolve, reject) => {
65
+        const api = replaceApiParams(lodash.get(apis, 'building.delete'), payload)
66
+        interact(api).then((data) => {
67
+          resolve(data)
68
+        }).catch(({ message }) => {
69
+          if (typeof message === 'string') {
70
+            reject(message)
71
+          }
72
+        })
73
+      })
74
+    },
75
+    updateBuildingStatus (_, payload) {
76
+      return new Promise((resolve, reject) => {
77
+        const api = lodash.get(apis, 'building.updateStatus')
78
+        interact(api, payload).then((data) => {
79
+          resolve(data)
80
+        }).catch(({ message }) => {
81
+          if (typeof message === 'string') {
82
+            reject(message)
83
+          }
84
+        })
85
+      })
86
+    }
87
+  }
88
+}

+ 50
- 0
src/store/modules/customer.js 查看文件

@@ -0,0 +1,50 @@
1
+import lodash from 'lodash'
2
+import { interact, replaceApiParams } from '../../utils'
3
+import apis from '../../config/api'
4
+
5
+export default {
6
+  namespaced: true,
7
+  state: {
8
+    customers: {},
9
+    detail: {}
10
+  },
11
+  mutations: {
12
+    updateList (state, payload) {
13
+      state.customers = payload
14
+    },
15
+    updateDetail (state, payload) {
16
+      state.detail = payload
17
+    }
18
+  },
19
+  actions: {
20
+    setDetailNull ({ commit }) {
21
+      commit('updateDetail', {})
22
+    },
23
+    getCustomers ({ commit }, payload) {
24
+      return new Promise((resolve, reject) => {
25
+        const api = lodash.get(apis, 'customer.list')
26
+        interact(api, payload).then((data) => {
27
+          commit('updateList', data)
28
+          resolve(data)
29
+        }).catch(({ message }) => {
30
+          if (typeof message === 'string') {
31
+            reject(message)
32
+          }
33
+        })
34
+      })
35
+    },
36
+    getDetail ({ commit }, payload) {
37
+      return new Promise((resolve, reject) => {
38
+        const api = replaceApiParams(lodash.get(apis, 'customer.detail'), payload)
39
+        interact(api).then((data) => {
40
+          commit('updateDetail', data)
41
+          resolve(data)
42
+        }).catch(({ message }) => {
43
+          if (typeof message === 'string') {
44
+            reject(message)
45
+          }
46
+        })
47
+      })
48
+    }
49
+  }
50
+}

+ 22
- 0
src/store/modules/img.js 查看文件

@@ -0,0 +1,22 @@
1
+import lodash from 'lodash'
2
+import { interact } from '../../utils'
3
+import apis from '../../config/api'
4
+
5
+export default {
6
+  namespaced: true,
7
+  state: {},
8
+  actions: {
9
+    uploadImg (_, payload) {
10
+      return new Promise((resolve, reject) => {
11
+        const api = lodash.get(apis, 'file.upload')
12
+        interact(api, payload).then((data) => {
13
+          resolve(data)
14
+        }).catch(({ message }) => {
15
+          if (typeof message === 'string') {
16
+            reject(message)
17
+          }
18
+        })
19
+      })
20
+    },
21
+  }
22
+}

+ 72
- 0
src/store/system.js 查看文件

@@ -0,0 +1,72 @@
1
+import lodash from 'lodash'
2
+import { interact } from '../utils'
3
+import apis from '../config/api'
4
+
5
+export default {
6
+  state: {
7
+    app: {
8
+      logo: '',
9
+      name: '荟房科技',
10
+      version: 1.0,
11
+    },
12
+    menus: [],
13
+    user: {},
14
+    dicts: {}
15
+  },
16
+  mutations: {
17
+    updateUser(state, user) {
18
+      state.user = {
19
+        ...state.user,
20
+        ...user,
21
+      }
22
+    },
23
+    updateDicts(state, dicts) {
24
+      state.dicts = dicts
25
+    }
26
+  },
27
+  actions: {
28
+    checkLogin({ state, commit }) {
29
+      return new Promise((resolve, reject) => {
30
+        if (state.user.username) {
31
+          resolve(state.user)
32
+          return
33
+        }
34
+        const api = lodash.get(apis, 'user.getuser')
35
+        interact(api).then((data) => {
36
+          commit('updateUser', data)
37
+          resolve(data)
38
+        }).catch(() => {
39
+          reject('获取人员信息失败')
40
+        })
41
+      })
42
+    },
43
+    login({ commit }, user) {
44
+      return new Promise((resolve, reject) => {
45
+        const api = lodash.get(apis, 'user.login')
46
+        interact(api, user).then((data) => {
47
+          commit('updateUser', data)
48
+          resolve()
49
+        }).catch(() => {
50
+          reject('账户密码不正确')
51
+        })
52
+      })
53
+    },
54
+    logout({ commit }) {
55
+      const api = lodash.get(apis, 'user.logout')
56
+      interact(api).then(() => {
57
+        commit('updateUser', {})
58
+      })
59
+    },
60
+    getDicts({ commit }) {
61
+      return new Promise((resolve, reject) => {
62
+        const api = lodash.get(apis, 'dict.get')
63
+        interact(api).then((data) => {
64
+          commit('updateDicts', data)
65
+          resolve(data)
66
+        }).catch(() => {
67
+          reject('获取字典信息失败')
68
+        })
69
+      })
70
+    }
71
+  }
72
+}

+ 4
- 0
src/theme.scss 查看文件

@@ -0,0 +1,4 @@
1
+$--color-primary: #044281;
2
+
3
+$--font-path: '~element-ui/lib/theme-chalk/fonts';
4
+@import "~element-ui/packages/theme-chalk/src/index";

+ 6
- 0
src/utils/comptype.js 查看文件

@@ -0,0 +1,6 @@
1
+export const CTYP_GROUP = 'G-';
2
+export const CTYP_SINGLE_LINE = 'single-line';
3
+export const CTYP_MULTI_LINE = 'multi-line';
4
+export const CTYP_IMAGE = 'image';
5
+export const CTYP_MAP = 'map';
6
+export const CTYP_CONTACT_FORM = `${CTYP_GROUP}contact_form`;

+ 6
- 0
src/utils/httpcode.js 查看文件

@@ -0,0 +1,6 @@
1
+export default {
2
+  HTTP_OK: 200,
3
+  HTTP_MULTIPLECHOICES: 300,
4
+  HTTP_BADREQUEST: 400,
5
+  HTTP_UNAUTHORIZED: 401
6
+}

+ 112
- 0
src/utils/index.js 查看文件

@@ -0,0 +1,112 @@
1
+import axios from 'axios'
2
+import httpCode from './httpcode'
3
+import { error } from './message'
4
+
5
+// 替换 url 中的参数
6
+export const replaceApiParams = (api, params) => {
7
+  if (typeof params !== 'object') return api
8
+
9
+  const { url: origin } = api
10
+  if (!origin) return undefined
11
+
12
+  const url = origin.split('/').map((it) => {
13
+    if (it.indexOf(':') === 0) {
14
+      const theKey = it.substr(1)
15
+      return Object.prototype.hasOwnProperty.call(params, theKey)
16
+        ? params[theKey]
17
+        : it
18
+    }
19
+
20
+    return it
21
+  }).join('/')
22
+
23
+  return { ...api, url }
24
+}
25
+
26
+// 远程交互, 支持使用过滤器处理反馈结果先
27
+export const request = filters => (api, dt) => {
28
+  const defaultData = (conf) => {
29
+    const { data } = conf
30
+    if (typeof data !== 'object') return conf
31
+    if (data instanceof window.FormData) return conf
32
+
33
+    const ft = new window.FormData()
34
+    Object.keys(data).forEach((k) => {
35
+      ft.append(k, data[k])
36
+    })
37
+
38
+    return { ...conf, data: ft }
39
+  }
40
+
41
+  const config = (api.method.toLowerCase() === 'get')
42
+    ? { ...api, params: dt }
43
+    : defaultData({ ...api, data: dt, headers: {'Content-Type': 'application/json;charset=utf-8', ...(api.headers || {})} })
44
+  return new Promise((resolve, reject) => {
45
+    axios(config)
46
+      .then(({ data }) => {
47
+        // 与后端约定的返回数据结构
48
+        const { code, message: result, data: returnval } = data
49
+        if (code === httpCode.HTTP_UNAUTHORIZED) {
50
+          const router = require('../router').default
51
+          router.push({name: 'login'})
52
+          return undefined
53
+        }
54
+
55
+        if (filters && filters.success) {
56
+          // 如果过滤器返回失败
57
+          if (!filters.success(code, result)) {
58
+            if (filters.fail) {
59
+              filters.fail(data)
60
+            } else {
61
+              reject(data)
62
+            }
63
+          }
64
+        }
65
+
66
+        resolve(returnval)
67
+      })
68
+      .catch((err) => {
69
+        // 网络错误, 直接抛出
70
+        if (filters && filters.fail) {
71
+          filters.fail(err)
72
+        } else {
73
+          throw err
74
+        }
75
+      })
76
+  })
77
+}
78
+
79
+// 远程交互, 可直接使用的
80
+export const interact = request({
81
+  success: (code, result) => {
82
+    if (code < httpCode.HTTP_OK || code >= httpCode.HTTP_MULTIPLECHOICES) {
83
+      error(result)
84
+      return false
85
+    }
86
+    return true
87
+  }
88
+})
89
+
90
+// 未定义的就使用默认值
91
+const ifUndifined = (val, def) => (val === undefined ? def : val)
92
+const withDefault = (val, def) => ({ ...def, ...ifUndifined(val, def) })
93
+
94
+// 扩展 store
95
+// 暂时不支持 modules
96
+export const extendsStore = (base, extend) => {
97
+  const {
98
+    namespaced: baseNamespaced = true,
99
+    state: baseState = {},
100
+    getters: baseGetters = {},
101
+    mutations: baseMutations = {},
102
+    actions: baseActions = {}
103
+  } = base
104
+
105
+  return {
106
+    namespaced: ifUndifined(extend.namespaced, baseNamespaced),
107
+    state: withDefault(extend.state, baseState),
108
+    getters: withDefault(extend.getters, baseGetters),
109
+    mutations: withDefault(extend.mutations, baseMutations),
110
+    actions: withDefault(extend.actions, baseActions)
111
+  }
112
+}

+ 12
- 0
src/utils/message.js 查看文件

@@ -0,0 +1,12 @@
1
+import { Message, MessageBox } from 'element-ui'
2
+
3
+export const alert = message => Message({ showClose: true, message })
4
+export const success = message => Message({ showClose: true, message, type: 'success' })
5
+export const warning = message => Message({ showClose: true, message, type: 'warning' })
6
+export const error = message => Message({ showClose: true, message, type: 'error' })
7
+
8
+export const confirm = message => MessageBox.confirm(message, '提示', {
9
+  confirmButtonText: '确定',
10
+  cancelButtonText: '取消',
11
+  type: 'warning'
12
+})

+ 7
- 0
src/utils/token.js 查看文件

@@ -0,0 +1,7 @@
1
+export function setToken(tk) {
2
+  window.localStorage.setItem('xm-token', tk)
3
+}
4
+
5
+export function getToken() {
6
+  return window.localStorage.getItem('xm-token')
7
+}

+ 15
- 0
src/views/Dashboard.vue 查看文件

@@ -0,0 +1,15 @@
1
+<template>
2
+<div></div>
3
+</template>
4
+
5
+<script>
6
+export default {
7
+  data () {
8
+    return {}
9
+  },
10
+}
11
+</script>
12
+
13
+<style lang="scss" scoped>
14
+
15
+</style>

+ 143
- 0
src/views/Login.vue 查看文件

@@ -0,0 +1,143 @@
1
+<template>
2
+  <div class="login-form">
3
+    <xm-polygon color="#044281" :dots="800" class="xm-polygon" />
4
+    <el-container class="login-layout">
5
+      <el-main>
6
+        <div class="head">
7
+          <div class="logo"></div>
8
+          <div class="title">
9
+            <h2>欢迎使用荟房科技后台系统</h2>
10
+          </div>
11
+        </div>
12
+        <div class="body">
13
+          <el-form size="medium" :model="formdata">
14
+            <el-form-item>
15
+              <el-input class="large-input" v-model="formdata.username">
16
+                <xm-icon name="user" size="small" slot="prefix"/>
17
+              </el-input>
18
+            </el-form-item>
19
+            <el-form-item>
20
+              <el-input class="large-input" show-password v-model="formdata.password">
21
+                <xm-icon name="password" size="small" slot="prefix"/>
22
+              </el-input>
23
+            </el-form-item>
24
+            <el-form-item>
25
+              <div class="form-action">
26
+                <el-button type="primary" @click="submit">登录</el-button>
27
+              </div>
28
+            </el-form-item>
29
+          </el-form>
30
+        </div>
31
+      </el-main>
32
+      <el-footer class="foot">
33
+        <xm-footer company="荟房科技" />
34
+      </el-footer>
35
+    </el-container>
36
+  </div>
37
+</template>
38
+
39
+<script>
40
+import { mapActions } from 'vuex'
41
+import md5 from 'blueimp-md5'
42
+
43
+export default {
44
+  name: 'login',
45
+  components: {
46
+    xmFooter: () => import('@/components/XMFooter.vue'),
47
+    xmPolygon: () => import('@/components/XMPolygon.vue'),
48
+  },
49
+  data() {
50
+    return {
51
+      formdata: {
52
+        username: '',
53
+        password: '',
54
+      },
55
+    }
56
+  },
57
+  computed: {
58
+    fromRoute() {
59
+      return this.$route.query.from || 'index'
60
+    }
61
+  },
62
+  methods: {
63
+    ...mapActions([
64
+      'login',
65
+    ]),
66
+    submit() {
67
+      this.login(JSON.stringify({ userName: this.formdata.username, password: md5(this.formdata.password) })).then(() => {
68
+        this.$router.push({ name: this.fromRoute })
69
+      }).catch((err) => {
70
+        this.$message({
71
+          showClose: true,
72
+          message: err,
73
+          type: 'error'
74
+        });
75
+      })
76
+    }
77
+  }
78
+}
79
+</script>
80
+
81
+<style lang="scss" scoped>
82
+.login-form {
83
+  width: 100%;
84
+  height: 100%;
85
+
86
+  .login-layout {
87
+    padding: 0;
88
+    height: 100%;
89
+    z-index: 100;
90
+    position: relative;
91
+
92
+    .head {
93
+      margin: 10em auto 4em;
94
+      text-align: center;
95
+    }
96
+
97
+    .body {
98
+      width: 400px;
99
+      margin: 0 auto;
100
+
101
+      .form-action {
102
+        margin: 24px 0;
103
+
104
+        button {
105
+          width: 100%;
106
+          font-size: 16px;
107
+          padding: 0.64em 20px;
108
+        }
109
+      }
110
+    }
111
+
112
+    .foot {
113
+      padding: 0;
114
+    }
115
+  }
116
+
117
+  .xm-polygon {
118
+    position: absolute;
119
+    width: 100%;
120
+    height: 100%;
121
+    z-index: 0;
122
+
123
+    top: 0;
124
+    bottom: 0;
125
+    left: 0;
126
+    right: 0;
127
+  }
128
+}
129
+</style>
130
+
131
+<style lang="scss">
132
+.login-form {
133
+  .large-input {
134
+    font-size: 18px;
135
+
136
+    .el-input__inner {
137
+      height: 2em;
138
+      line-height: 2.2em;
139
+    }
140
+  }
141
+}
142
+</style>
143
+

+ 215
- 0
src/views/activity/edit.vue 查看文件

@@ -0,0 +1,215 @@
1
+<template>
2
+  <el-form ref="form" :model="activity" label-width="160px">
3
+    <el-form-item label="所属楼盘">
4
+      <el-select v-model="activity.buildingId" placeholder="请选择">
5
+        <el-option v-for="(build,i) in buildings.list || []" :key="i" :label="build.buildingName" :value="build.buildingId"></el-option>
6
+      </el-select>
7
+    </el-form-item>
8
+    <el-form-item label="活动标题">
9
+      <el-input v-model="activity.title"></el-input>
10
+    </el-form-item>
11
+    <el-form-item label="活动描述">
12
+      <el-input v-model="activity.desc"></el-input>
13
+    </el-form-item>
14
+    <el-form-item label="活动主图">
15
+      <el-upload
16
+        class="avatar-uploader"
17
+        :action="upFileUrl"
18
+        name='uploadFiles'
19
+        :show-file-list="false"
20
+        :before-upload="beforeImgUpload"
21
+        :on-success="handleAvatarSuccess">
22
+        <img v-if="activity.imgUrl" :src="activity.imgUrl" class="avatar">
23
+        <i v-else class="el-icon-plus avatar-uploader-icon"></i>
24
+      </el-upload>
25
+    </el-form-item>
26
+    <el-form-item label="活动时间">
27
+      <el-date-picker
28
+        v-model="activity.activityDate"
29
+        type="date"
30
+        placeholder="选择日期">
31
+      </el-date-picker>
32
+    </el-form-item>
33
+    <el-form-item label="报名最多人数">
34
+      <el-input v-model="activity.maxNum"></el-input>
35
+    </el-form-item>
36
+    <el-form-item label="报名开始时间">
37
+      <el-date-picker
38
+        v-model="activity.beginDate"
39
+        type="date"
40
+        placeholder="选择日期">
41
+      </el-date-picker>
42
+    </el-form-item>
43
+    <el-form-item label="报名截止时间">
44
+      <el-date-picker
45
+        v-model="activity.endDate"
46
+        type="date"
47
+        placeholder="选择日期">
48
+      </el-date-picker>
49
+    </el-form-item>
50
+    <el-form-item label="内容">
51
+      <div id="websiteEditorElem" style="height: 400px"></div>
52
+    </el-form-item>
53
+    <el-form-item>
54
+      <el-button type="primary" @click="onSubmit">保存</el-button>
55
+      <el-button @click="onCancel">取消</el-button>
56
+    </el-form-item>
57
+  </el-form>
58
+</template>
59
+
60
+<script>
61
+import { createNamespacedHelpers } from 'vuex'
62
+import apis from '../../config/api'
63
+import E from 'wangeditor'
64
+
65
+const { mapState: mapActivityState, mapActions: mapActivityActions, mapMutations: mapActivityMutations } = createNamespacedHelpers('activity')
66
+const { mapState: mapBuildingState, mapActions: mapBuildingActions } = createNamespacedHelpers('building')
67
+const { mapActions: mapImgActions } = createNamespacedHelpers('img')
68
+
69
+export default {
70
+  data () {
71
+    return {
72
+      upFileUrl: apis.file.upload.url,
73
+    }
74
+  },
75
+  computed: {
76
+    ...mapActivityState({
77
+      detail: x => x.detail,
78
+    }),
79
+    activity: {
80
+      get () {
81
+        return this.detail
82
+      },
83
+      set (val) {
84
+        return this.updateDetail(val)
85
+      }
86
+    },
87
+    ...mapBuildingState({
88
+      buildings: x => x.buildings
89
+    }),
90
+  },
91
+  methods: {
92
+    ...mapBuildingActions([
93
+      'getBuildings'
94
+    ]),
95
+    ...mapActivityMutations([
96
+      'updateDetail'
97
+    ]),
98
+    ...mapActivityActions([
99
+      'getDetail',
100
+      'addActivity',
101
+      'editActivity'
102
+    ]),
103
+    ...mapImgActions([
104
+      'uploadImg'
105
+    ]),
106
+    beforeImgUpload (file) {
107
+      if (file.type !== 'image/jpeg') {
108
+        this.$message.error('上传图片只能是 JPG 格式!')
109
+        return false
110
+      }
111
+      // if (file.size / 1024 > 300) {
112
+      //   this.$message.error('图片大小不允许超过300k!')
113
+      //   return false
114
+      // }
115
+      this.loading = this.$loading({
116
+        lock: true,
117
+        text: '上传中...',
118
+        spinner: 'el-icon-loading',
119
+        background: 'rgba(0, 0, 0, 0.7)'
120
+      })
121
+
122
+      return true
123
+    },
124
+    handleAvatarSuccess (res) {
125
+      this.updateDetail({...this.activity, imgUrl: res.data[0]})
126
+      this.loading.close()
127
+    },
128
+    onSubmit () {
129
+      if (!this.detail.activityId) {
130
+        // 新增
131
+        this.addActivity({
132
+          onSuccess: this.onCancel,
133
+          detail: JSON.stringify(this.detail)
134
+        })
135
+      } else {
136
+        // 修改
137
+        this.editActivity({
138
+          onSuccess: this.onCancel,
139
+          detail: JSON.stringify(this.detail)
140
+        })
141
+      }
142
+    },
143
+    onCancel () {
144
+      this.$router.push({name: 'activitylist'})
145
+    }
146
+  },
147
+  mounted () {
148
+    const _that = this
149
+    const phoneEditor = new E('#websiteEditorElem')
150
+    phoneEditor.customConfig.onchange = function (html) {
151
+      _that.activity = {..._that.activity, context: html}
152
+    }
153
+    // phoneEditor.customConfig.uploadImgServer = this.upFileUrl
154
+    // phoneEditor.customConfig.uploadFileName = 'uploadFiles'
155
+
156
+    phoneEditor.customConfig.customUploadImg = function (files, insert) {
157
+      _that.uploadImg({
158
+        uploadFiles: files[0]
159
+      }).then(data => {
160
+        insert(data[0])
161
+      })
162
+    }
163
+    phoneEditor.customConfig.menus = [
164
+      'head',  // 标题
165
+      'bold',  // 粗体
166
+      'fontSize',  // 字号
167
+      'fontName',  // 字体
168
+      'italic',  // 斜体
169
+      'underline',  // 下划线
170
+      'strikeThrough',  // 删除线
171
+      'foreColor',  // 文字颜色
172
+      'backColor',  // 背景颜色
173
+      'justify',  // 对齐方式
174
+      'image',  // 插入图片
175
+    ]
176
+    phoneEditor.create()
177
+    this.getBuildings({
178
+      pageNum: 1,
179
+      pageSize: 100,
180
+    }).then(() => {
181
+      if ((this.$route.query.id || '') !== '') {
182
+        this.getDetail({id: this.$route.query.id}).then(data => {
183
+          phoneEditor.txt.html(data.context)
184
+        })
185
+      }
186
+    })
187
+  }
188
+}
189
+</script>
190
+
191
+<style lang="scss">
192
+  .avatar-uploader .el-upload {
193
+    border: 1px dashed #d9d9d9;
194
+    border-radius: 6px;
195
+    cursor: pointer;
196
+    position: relative;
197
+    overflow: hidden;
198
+  }
199
+  .avatar-uploader .el-upload:hover {
200
+    border-color: #409EFF;
201
+  }
202
+  .avatar-uploader-icon {
203
+    font-size: 28px;
204
+    color: #8c939d;
205
+    width: 178px;
206
+    height: 178px;
207
+    line-height: 178px;
208
+    text-align: center;
209
+  }
210
+  .avatar {
211
+    width: 178px;
212
+    height: 178px;
213
+    display: block;
214
+  }
215
+</style>

+ 391
- 0
src/views/activity/list.vue 查看文件

@@ -0,0 +1,391 @@
1
+<template>
2
+  <div>
3
+    <div class="system-table-search">
4
+      <div class="flex-h">
5
+        <div class="flex-item flex-h">
6
+          <el-button size="mini" type="success" @click='addActivity'>新增活动</el-button>
7
+        </div>
8
+        <ul>
9
+          <li>
10
+            <el-input v-model="name" placeholder="活动名称"></el-input>
11
+          </li>
12
+          <li>
13
+            <el-date-picker
14
+              v-model="beginDate"
15
+              type="date"
16
+              value-format="yyyy-MM-dd"
17
+              placeholder="活动开始时间">
18
+            </el-date-picker>
19
+          </li>
20
+          <li>
21
+            <el-date-picker
22
+              v-model="endDate"
23
+              type="date"
24
+              value-format="yyyy-MM-dd"
25
+              placeholder="活动截止日期">
26
+            </el-date-picker>
27
+          </li>
28
+        </ul>
29
+        <el-button
30
+          size="mini"
31
+          type="primary" @click="search">搜索</el-button>
32
+      </div>
33
+      <div class="moreFilter"></div>
34
+    </div>
35
+    <el-table
36
+      :data="activitys.records || []"
37
+      style="width: 100%">
38
+      <el-table-column
39
+        type="index"
40
+        width="50">
41
+        <template slot-scope="scope">
42
+          <span>{{ GetIndex(scope.$index) }}</span>
43
+        </template>
44
+      </el-table-column>
45
+      <el-table-column
46
+        label="活动主图">
47
+        <template slot-scope="scope">
48
+          <div class="header">
49
+            <img :src="scope.row.imgUrl" alt="" />
50
+          </div>
51
+        </template>
52
+      </el-table-column>
53
+      <el-table-column
54
+        prop="title"
55
+        label="活动标题">
56
+      </el-table-column>
57
+      <el-table-column
58
+        prop="desc"
59
+        label="活动描述">
60
+      </el-table-column>
61
+      <el-table-column
62
+        label="活动时间"
63
+        width="120"
64
+        prop="activityDate"
65
+      >
66
+        <template slot-scope="scope">
67
+          <span>{{(scope.row.activityDate || '').split(' ')[0]}}</span>
68
+        </template>
69
+      </el-table-column>
70
+      <el-table-column
71
+        label="报名开始时间"
72
+        width="120"
73
+        prop="beginDate"
74
+      >
75
+        <template slot-scope="scope">
76
+          <span>{{(scope.row.beginDate || '').split(' ')[0]}}</span>
77
+        </template>
78
+      </el-table-column>
79
+      <el-table-column
80
+        label="报名截止时间"
81
+        width="120"
82
+        prop="endDate"
83
+      >
84
+        <template slot-scope="scope">
85
+          <span>{{(scope.row.endDate || '').split(' ')[0]}}</span>
86
+        </template>
87
+      </el-table-column>
88
+      <el-table-column
89
+        label="活动最多人数"
90
+        width="120"
91
+        prop="maxNum"
92
+      >
93
+      </el-table-column>
94
+      <el-table-column
95
+        label="已报名人数"
96
+        width="120"
97
+        prop="signUpNumber"
98
+      >
99
+      </el-table-column>
100
+      <el-table-column
101
+        label="发布状态">
102
+        <template slot-scope="scope">
103
+          <span>{{scope.row.status == 1 ? '已发布' : '未发布'}}</span>
104
+        </template>
105
+      </el-table-column>
106
+      <el-table-column
107
+        fixed="right"
108
+        label="操作"
109
+        width="180">
110
+        <template slot-scope="scope">
111
+          <el-button type="text" @click="handleEdit(scope.row)" size="small">编辑</el-button>
112
+          <el-button type="text" @click="handlePulic(scope.row)" size="small" v-if="scope.row.status === 0">发布</el-button>
113
+          <el-button type="text" @click="handleShowEnroll(scope.row)" size="small" v-if="scope.row.status === 1">活动报名信息</el-button>
114
+          <el-button type="text" @click="handleUnPulic(scope.row)" size="small" v-if="scope.row.status === 1">取消发布</el-button>
115
+          <el-button @click="handleDel(scope.row)" type="text" size="small">删除</el-button>
116
+        </template>
117
+      </el-table-column>
118
+    </el-table>
119
+    <el-pagination
120
+      small
121
+      style="margin-top:10px;"
122
+      layout="prev, pager, next"
123
+      :current-page.sync="currentPage"
124
+      :pageSize="pageSize"
125
+      :total="activitys.total || 0"
126
+      @current-change="getList"
127
+    >
128
+    </el-pagination>
129
+    <el-dialog title="报名信息" width="66%" :visible.sync="showEnroll">
130
+      <div class="system-table-search">
131
+        <div class="flex-h">
132
+          <div class="flex-item flex-h">
133
+          </div>
134
+          <ul>
135
+            <li>
136
+              <el-input v-model="customerName" placeholder="姓名或昵称"></el-input>
137
+            </li>
138
+            <li>
139
+              <el-input v-model="customerPhone" placeholder="手机号"></el-input>
140
+            </li>
141
+          </ul>
142
+          <el-button
143
+            size="mini"
144
+            type="primary" @click="searchCustomer">搜索</el-button>
145
+        </div>
146
+        <div class="moreFilter"></div>
147
+      </div>
148
+      <el-table
149
+      :data="enrolls.records || []"
150
+      style="width: 100%">
151
+        <el-table-column
152
+          label="头像">
153
+          <template slot-scope="scope">
154
+            <div class="header">
155
+              <img :src="scope.row.avatar" alt="" />
156
+            </div>
157
+          </template>
158
+        </el-table-column>
159
+        <el-table-column
160
+          prop="buildingName"
161
+          label="案场">
162
+        </el-table-column>
163
+        <el-table-column
164
+          prop="customerName"
165
+          label="姓名">
166
+        </el-table-column>
167
+        <el-table-column
168
+          prop="name"
169
+          label="昵称">
170
+        </el-table-column>
171
+        <el-table-column
172
+          prop="idNum"
173
+          label="证件号">
174
+        </el-table-column>
175
+        <el-table-column
176
+          prop="phone"
177
+          label="电话">
178
+        </el-table-column>
179
+        <el-table-column
180
+          prop="enrollDate"
181
+          label="报名时间">
182
+        </el-table-column>
183
+      </el-table>
184
+      <el-pagination
185
+        small
186
+        style="margin-top:10px;"
187
+        layout="prev, pager, next"
188
+        :current-page.sync="enrollcurrentPage"
189
+        :pageSize="enrollPageSize"
190
+        :total="enrolls.total || 0"
191
+        @current-change="getEnrollsList"
192
+      >
193
+      </el-pagination>
194
+      <div slot="footer" class="dialog-footer">
195
+        <el-button type="primary" @click="showEnroll = false">关 闭</el-button>
196
+      </div>
197
+    </el-dialog>
198
+
199
+  </div>
200
+</template>
201
+
202
+<script>
203
+import { createNamespacedHelpers } from 'vuex';
204
+
205
+const {mapState: mapActivityState, mapActions: mapActivityActions} = createNamespacedHelpers('activity')
206
+const {mapState: mapBuildingState, mapActions: mapBuildingActions} = createNamespacedHelpers('building')
207
+
208
+export default {
209
+  data() {
210
+    return {
211
+      pageSize: 20,
212
+      currentPage: 1,
213
+      name: '',
214
+      beginDate: '',
215
+      endDate: '',
216
+      enrollPageSize: 20,
217
+      enrollcurrentPage: 1,
218
+      enrolls: {},
219
+      showEnroll: false,
220
+      customerName: '',
221
+      customerPhone: ''
222
+    }
223
+  },
224
+  computed: {
225
+    ...mapActivityState({
226
+      activitys: x => x.activitys
227
+    }),
228
+    ...mapBuildingState({
229
+      buildings: x => x.buildings
230
+    })
231
+  },
232
+  methods: {
233
+    ...mapActivityActions([
234
+      'getActivitys',
235
+      'setDetailNull',
236
+      'updateActivityStatus',
237
+      'deleteActivity',
238
+      'publicActivity',
239
+      'cancelActivity',
240
+      'getEnrolls'
241
+    ]),
242
+    ...mapBuildingActions([
243
+      'getBuildings',
244
+    ]),
245
+    GetIndex (inx) {
246
+      return (this.currentPage - 1) * this.pageSize + inx + 1
247
+    },
248
+    FormatDate (date) {
249
+      if (date) {
250
+        return date.split('T')[0] === '0001-01-01' ? '' : date.split('T')[0] + '  ' + date.split('T')[1]
251
+      } else {
252
+        return ''
253
+      }
254
+    },
255
+    getList () {
256
+      this.getActivitys({
257
+        pageNum: this.currentPage,
258
+        pageSize: this.pageSize,
259
+        name: this.name,
260
+        beginDate: this.beginDate,
261
+        endDate: this.endDate
262
+      })
263
+    },
264
+    getBuildingName (id) {
265
+      return ((this.buildings.list || []).filter(x => x.buildingId === id)[0] || {}).buildingName || ''
266
+    },
267
+    search () {
268
+      this.currentPage = 1
269
+      this.getList()
270
+    },
271
+    addActivity () {
272
+      this.$router.push({name: 'activityedit'})
273
+    },
274
+    handleEdit (row) {
275
+      this.setDetailNull()
276
+      this.$router.push({name: 'activityedit', query: {id: row.activityId}})
277
+    },
278
+    handlePulic (row) {
279
+      this.$confirm('确认发布此数据?', '提示', {
280
+        confirmButtonText: '确定',
281
+        cancelButtonText: '取消',
282
+        type: 'warning'
283
+      }).then(() => {
284
+        this.publicActivity({
285
+          id: row.activityId
286
+        }).then(() => {
287
+          this.getList()
288
+        })
289
+      })
290
+    },
291
+    handleUnPulic (row) {
292
+      this.$confirm('确认取消发布此数据?', '提示', {
293
+        confirmButtonText: '确定',
294
+        cancelButtonText: '取消',
295
+        type: 'warning'
296
+      }).then(() => {
297
+        this.cancelActivity({
298
+          id: row.activityId}).then(() => {
299
+          this.getList()
300
+        })
301
+      })
302
+    },
303
+    handleDel (row) {
304
+      this.$confirm('确认删除此数据?', '提示', {
305
+        confirmButtonText: '确定',
306
+        cancelButtonText: '取消',
307
+        type: 'warning'
308
+      }).then(() => {
309
+        if (row.status === 1) {
310
+          this.$message.error('当前活动处于发布状态,不允许删除!')
311
+          return false
312
+        }
313
+        this.deleteActivity({
314
+          id: row.activityId
315
+        }).then(() => {
316
+          this.getList()
317
+        })
318
+      })
319
+    },
320
+    handleShowEnroll (row) {
321
+      this.enrollcurrentPage = 1
322
+      this.actionId = row.activityId
323
+      this.getEnrollsList()
324
+    },
325
+    getEnrollsList () {
326
+      this.getEnrolls({
327
+        id: this.actionId,
328
+        pageNum: this.enrollcurrentPage,
329
+        pageSize: this.enrollPageSize,
330
+        customerName: this.customerName,
331
+        customerPhone: this.customerPhone
332
+      }).then((data) => {
333
+        this.enrolls = data
334
+        this.showEnroll = true
335
+      })
336
+    },
337
+    searchCustomer () {
338
+      this.getEnrollsList()
339
+    }
340
+  },
341
+  created () {
342
+    this.getBuildings({
343
+      pageNum: 1,
344
+      pageSize: 100,
345
+    }).then(() => {
346
+      this.getList()
347
+    })
348
+  }
349
+}
350
+</script>
351
+
352
+<style lang="scss" scoped>
353
+.header{
354
+  width: 50px;
355
+  height: 50px;
356
+  img{
357
+    width: 100%;
358
+    height: 100%;
359
+  }
360
+}
361
+
362
+.system-table-search{
363
+  width: calc(100% - 40px);
364
+  margin: 20px auto 0;
365
+}
366
+
367
+.system-table-search li{
368
+  margin-right: 20px;
369
+}
370
+
371
+.system-table-search ul{
372
+  font-size: 0;
373
+  white-space: nowrap;
374
+}
375
+
376
+.system-table-search ul>li{
377
+  display: inline-block;
378
+}
379
+
380
+.flex-h {
381
+  display: flex;
382
+  display: -webkit-flex;
383
+}
384
+
385
+.flex-item {
386
+  flex: 1;
387
+  -webkit-flex: 1;
388
+  position: relative;
389
+  overflow: hidden;
390
+}
391
+</style>

+ 253
- 0
src/views/appointment/list.vue 查看文件

@@ -0,0 +1,253 @@
1
+<template>
2
+  <div>
3
+    <div class="system-table-search">
4
+      <div class="flex-h">
5
+        <div class="flex-item flex-h"></div>
6
+        <ul>
7
+          <li>
8
+            <el-select v-model="buildingId" placeholder="请选择楼盘">
9
+              <el-option
10
+                v-for="item in buildings.list || []"
11
+                :key="item.buildingId"
12
+                :label="item.buildingName"
13
+                :value="item.buildingId">
14
+              </el-option>
15
+            </el-select>
16
+          </li>
17
+          <li>
18
+            <el-input v-model="username" placeholder="预约人"></el-input>
19
+          </li>
20
+          <li>
21
+            <el-input v-model="phone" placeholder="联系电话"></el-input>
22
+          </li>
23
+          <li>
24
+            <el-select v-model="status" placeholder="核销状态">
25
+              <el-option
26
+                label="未核销"
27
+                :value="1">
28
+              </el-option>
29
+              <el-option
30
+                label="已核销"
31
+                :value="2">
32
+              </el-option>
33
+            </el-select>
34
+          </li>
35
+        </ul>
36
+        <el-button
37
+          size="mini"
38
+          type="primary" @click="search">搜索</el-button>
39
+      </div>
40
+      <div class="moreFilter"></div>
41
+    </div>
42
+    <el-table
43
+      :data="appointments.list || []"
44
+      style="width: 100%"
45
+      >
46
+      <el-table-column
47
+        type="index"
48
+        width="50">
49
+        <template slot-scope="scope">
50
+          <span>{{ GetIndex(scope.$index) }}</span>
51
+        </template>
52
+      </el-table-column>
53
+      <el-table-column
54
+        label="预约楼盘"
55
+        width="180">
56
+        <template slot-scope="scope">
57
+          <span>{{getBuildingName(scope.row.buildingId)}}</span>
58
+        </template>
59
+      </el-table-column>
60
+      <el-table-column
61
+        prop="customerName"
62
+        label="用户姓名">
63
+         <template slot-scope="scope">
64
+          <span>{{scope.row.customerName}}</span>
65
+        </template>
66
+      </el-table-column>
67
+      <el-table-column
68
+        prop="phone"
69
+        label="手机号">
70
+      </el-table-column>
71
+      <el-table-column
72
+        prop="appointmentDate"
73
+        label="预约时间">
74
+      </el-table-column>
75
+      <el-table-column
76
+        prop="remark"
77
+        label="备注">
78
+      </el-table-column>
79
+      <el-table-column
80
+        prop="createDate"
81
+        label="创建时间">
82
+      </el-table-column>
83
+      <el-table-column
84
+        label="核销状态"
85
+        >
86
+        <template slot-scope="scope">
87
+          <span :title="getHxTitle(scope.row)">{{scope.row.status == 2 ? '已核销' : '未核销'}}</span>
88
+        </template>
89
+      </el-table-column>
90
+      <el-table-column
91
+        fixed="right"
92
+        label="操作"
93
+        width="100">
94
+        <template slot-scope="scope">
95
+          <el-button type="text" v-if="scope.row.status != 2" @click="handleHx(scope.row)" size="small">核销</el-button>
96
+        </template>
97
+      </el-table-column>
98
+    </el-table>
99
+    <el-pagination
100
+      small
101
+      style="margin-top:10px;"
102
+      layout="prev, pager, next"
103
+      :current-page.sync="currentPage"
104
+      :pageSize="pageSize"
105
+      :total="appointments.total || 0"
106
+      @current-change="getList"
107
+    >
108
+    </el-pagination>
109
+    <el-dialog title="核销备注" :visible.sync="dialogHxVisible">
110
+      <el-input type="textarea" rows="10" v-model="remark"></el-input>
111
+      <div slot="footer" class="dialog-footer">
112
+        <el-button @click="dialogHxVisible = false">取 消</el-button>
113
+        <el-button type="primary" @click="appointHx()">确 定</el-button>
114
+      </div>
115
+    </el-dialog>
116
+  </div>
117
+</template>
118
+
119
+<script>
120
+import { createNamespacedHelpers } from 'vuex';
121
+
122
+const {mapState: mapBuildingState, mapActions: mapBuildingActions} = createNamespacedHelpers('building')
123
+const {mapState: mapAppointmentState, mapActions: mapAppointmentActions} = createNamespacedHelpers('appointment')
124
+
125
+
126
+export default {
127
+  data() {
128
+    return {
129
+      pageSize: 20,
130
+      currentPage: 1,
131
+      buildingId: '',
132
+      username: '',
133
+      phone: '',
134
+      status: '',
135
+      remark: '',
136
+      dialogHxVisible: false,
137
+      currentRow: {}
138
+    }
139
+  },
140
+  computed: {
141
+    ...mapBuildingState({
142
+      buildings: x => x.buildings
143
+    }),
144
+    ...mapAppointmentState({
145
+      appointments: x => x.appointments
146
+    })
147
+  },
148
+  methods: {
149
+    ...mapBuildingActions([
150
+      'getBuildings'
151
+    ]),
152
+    ...mapAppointmentActions([
153
+      'getAppointments',
154
+      'appointWriteOff'
155
+    ]),
156
+    GetIndex (inx) {
157
+      return (this.currentPage - 1) * this.pageSize + inx + 1
158
+    },
159
+    FormatDate (date) {
160
+      if (date) {
161
+        return date.split('T')[0] === '0001-01-01' ? '' : date.split('T')[0] + '  ' + date.split('T')[1]
162
+      } else {
163
+        return ''
164
+      }
165
+    },
166
+    getBuildingName (id) {
167
+      return ((this.buildings.list || []).filter(x => x.buildingId === id)[0] || {}).buildingName
168
+    },
169
+    getList () {
170
+      this.getAppointments({
171
+        pageNum: this.currentPage,
172
+        pageSize: this.pageSize,
173
+        buildingId: this.buildingId,
174
+        username: this.username,
175
+        phone: this.phone,
176
+        status: this.status
177
+      })
178
+    },
179
+    search () {
180
+      this.currentPage = 1
181
+      this.getList()
182
+    },
183
+    handleHx (row) {
184
+      this.currentRow = row
185
+      this.dialogHxVisible = true
186
+    },
187
+    appointHx () {
188
+      this.appointWriteOff({
189
+        id: this.currentRow.appointmentId,
190
+        detail: JSON.stringify({writeoffRemark: this.remark})
191
+      }).then(() => {
192
+        this.getList()
193
+        this.dialogHxVisible = false
194
+      })
195
+    },
196
+    getHxTitle (row) {
197
+      return `核销时间:${row.writeoffDate}
198
+备注:${row.writeoffRemark}`
199
+    }
200
+  },
201
+  created () {
202
+    this.getBuildings({
203
+      pageNum: 1,
204
+      pageSize: 100
205
+    }).then(() => {
206
+      this.getList()
207
+    })
208
+  }
209
+}
210
+</script>
211
+
212
+<style lang="scss" scoped>
213
+.list{
214
+    .header{
215
+      width: 50px;
216
+      height: 50px;
217
+      img{
218
+        width: 100%;
219
+        height: 100%;
220
+      }
221
+    }
222
+  }
223
+
224
+.system-table-search{
225
+  width: calc(100% - 40px);
226
+  margin: 20px auto 0;
227
+}
228
+
229
+.system-table-search li{
230
+  margin-right: 20px;
231
+}
232
+
233
+.system-table-search ul{
234
+  font-size: 0;
235
+  white-space: nowrap;
236
+}
237
+
238
+.system-table-search ul>li{
239
+  display: inline-block;
240
+}
241
+
242
+.flex-h {
243
+  display: flex;
244
+  display: -webkit-flex;
245
+}
246
+
247
+.flex-item {
248
+  flex: 1;
249
+  -webkit-flex: 1;
250
+  position: relative;
251
+  overflow: hidden;
252
+}
253
+</style>

+ 430
- 0
src/views/building/edit.vue 查看文件

@@ -0,0 +1,430 @@
1
+<template>
2
+  <el-tabs v-model="activeName">
3
+    <el-tab-pane label="基础信息" name="detail">
4
+      <el-form ref="form" :model="building" label-width="160px">
5
+        <el-form-item label="楼盘编号">
6
+          <el-input v-model="building.code"></el-input>
7
+        </el-form-item>
8
+        <el-form-item label="楼盘名称">
9
+          <el-input v-model="building.buildingName"></el-input>
10
+        </el-form-item>
11
+        <el-form-item label="别名">
12
+          <el-input v-model="building.name"></el-input>
13
+        </el-form-item>
14
+        <el-form-item label="均价">
15
+          <el-input v-model="building.price"></el-input>
16
+        </el-form-item>
17
+        <el-form-item label="开盘时间">
18
+          <el-date-picker
19
+            v-model="building.openingDate"
20
+            type="date"
21
+            placeholder="选择日期">
22
+          </el-date-picker>
23
+        </el-form-item>
24
+        <el-form-item label="电话">
25
+          <el-input v-model="building.tel"></el-input>
26
+        </el-form-item>
27
+        <el-form-item label="物业类型">
28
+          <el-select v-model="buildingProperty" multiple placeholder="请选择">
29
+            <el-option v-for="(t,i) in propertyType" :key="i" :label="t.name" :value="t.id"></el-option>
30
+          </el-select>
31
+        </el-form-item>
32
+        <el-form-item label="项目主图">
33
+          <el-upload
34
+            :action="upFileUrl"
35
+            name='uploadFiles'
36
+            list-type="picture-card"
37
+            :file-list="imgList"
38
+            :show-file-list="true"
39
+            :before-upload="beforeImgUpload"
40
+            :on-success="handleAvatarSuccess"
41
+            :on-remove="handleRemove">
42
+            <i class="el-icon-plus"></i>
43
+          </el-upload>
44
+          <el-dialog :visible.sync="dialogVisible">
45
+            <img width="100%" :src="dialogImageUrl" alt="">
46
+          </el-dialog>
47
+        </el-form-item>
48
+        <el-form-item label="项目地址">
49
+          <el-input v-model="building.address"></el-input>
50
+        </el-form-item>
51
+        <el-form-item label="项目坐标">
52
+          <el-input v-model="building.coordinate" :disabled="true"></el-input>
53
+        </el-form-item>
54
+        <el-form-item label="项目地址">
55
+          <div class="search">
56
+            <el-amap-search-box class="search-box" :search-option="searchOption" :on-search-result="onSearchResult"></el-amap-search-box>
57
+          </div>
58
+          <div class="flex-item">
59
+            <div style="width: 100%;">
60
+              <el-amap ref="map" vid="amapDemo" :center="mapCenter" :zoom="12" :events="events" class="amap-demo">
61
+                <el-amap-marker v-for="(item,index) in markers" :key="index" :position="item" ></el-amap-marker>
62
+              </el-amap>
63
+            </div>
64
+          </div>  
65
+        </el-form-item>
66
+        <el-form-item>
67
+          <el-button type="primary" @click="onSubmit">保存</el-button>
68
+          <el-button @click="onCancel">取消</el-button>
69
+        </el-form-item>
70
+      </el-form>
71
+    </el-tab-pane>
72
+    <el-tab-pane label="户型设置" name="apartment" v-if="detail.buildingId">
73
+      <div class="system-table-search">
74
+        <div class="flex-h">
75
+          <div class="flex-item flex-h">
76
+            <el-button size="mini" type="success" @click='addHx'>新增户型</el-button>
77
+          </div>
78
+        </div>
79
+      </div>
80
+      <el-table
81
+        :data="aparments"
82
+        style="width: 100%">
83
+        <el-table-column
84
+          prop="apartmentName"
85
+          label="户型名称"
86
+        >
87
+        </el-table-column>
88
+        <el-table-column
89
+          prop="marketStatus"
90
+          label="销售状态"
91
+        >
92
+          <template slot-scope="scope">
93
+            <span>{{getSaleTypeName(scope.row.marketStatus)}}</span>
94
+          </template>
95
+        </el-table-column>
96
+        <el-table-column
97
+          prop="remark"
98
+          label="备注"
99
+        >
100
+        </el-table-column>
101
+        <el-table-column
102
+          prop="createDate"
103
+          label="创建时间"
104
+        >
105
+        </el-table-column>
106
+        <el-table-column
107
+          fixed="right"
108
+          label="操作"
109
+          width="180">
110
+          <template slot-scope="scope">
111
+            <el-button type="text" @click="handleEdit(scope.row)" size="small">编辑</el-button>
112
+            <el-button @click="handleDel(scope.row)" type="text" size="small">删除</el-button>
113
+          </template>
114
+        </el-table-column>
115
+      </el-table>
116
+      <el-dialog title="户型信息" :visible.sync="showHx">
117
+        <el-form ref="form" :model="aparmentInfo" label-width="160px">
118
+          <el-form-item label="户型名称">
119
+            <el-input v-model="aparmentInfo.apartmentName"></el-input>
120
+          </el-form-item>
121
+          <el-form-item label="销售状态">
122
+            <el-select v-model="aparmentInfo.marketStatus" placeholder="请选择">
123
+              <el-option v-for="(type,i) in saleType" :key="i" :label="type.name" :value="type.id"></el-option>
124
+            </el-select>
125
+          </el-form-item>
126
+          <el-form-item label="户型图">
127
+            <el-upload
128
+              :action="upFileUrl"
129
+              name='uploadFiles'
130
+              list-type="picture-card"
131
+              :file-list="aparmentImg"
132
+              :show-file-list="true"
133
+              :before-upload="beforeImgUpload"
134
+              :on-success="handleAparmentSuccess"
135
+              :on-remove="handleAparmentRemove">
136
+              <i class="el-icon-plus"></i>
137
+            </el-upload>
138
+            <el-dialog :visible.sync="dialogApartVisible">
139
+              <img width="100%" :src="dialogApartImageUrl" alt="">
140
+            </el-dialog>
141
+          </el-form-item>
142
+          <el-form-item label="备注">
143
+            <el-input type="textarea" v-model="aparmentInfo.remark"></el-input>
144
+          </el-form-item>
145
+          <el-form-item>
146
+            <el-button type="primary" @click="saveAparment">保存</el-button>
147
+            <el-button @click="showHx = false">取消</el-button>
148
+          </el-form-item>
149
+        </el-form>
150
+      </el-dialog>
151
+    </el-tab-pane>
152
+  </el-tabs>
153
+</template>
154
+
155
+<script>
156
+import { createNamespacedHelpers } from 'vuex'
157
+import apis from '../../config/api'
158
+
159
+const { mapState: mapBuildingState, mapActions: mapBuildingActions, mapMutations: mapBuildingMutations } = createNamespacedHelpers('building')
160
+import { mapState } from 'vuex'
161
+const { mapActions: mapApartmentActions } = createNamespacedHelpers('apartment')
162
+
163
+export default {
164
+  data () {
165
+    var _self = this
166
+    return {
167
+      upFileUrl: apis.file.upload.url,
168
+      commPrefix: '',
169
+      imgurl: '',
170
+      loadingZm: false,
171
+      activeName: 'detail',
172
+      dialogImageUrl: '',
173
+      dialogVisible: false,
174
+      dialogApartImageUrl: '',
175
+      dialogApartVisible: false,
176
+      showHx: false,
177
+      markers: [],
178
+      imgList: [],
179
+      buildingProperty: [],
180
+      aparments: [],
181
+      aparmentInfo: {},
182
+      aparmentImg: [],
183
+      searchOption: {
184
+        city: '南京',
185
+        citylimit: false
186
+      },
187
+      mapCenter: [118.789509, 32.019989],
188
+      events: {
189
+        click: (e) => {
190
+          _self.updateDetail({..._self.building, coordinate: e.lnglat.lat + ',' + e.lnglat.lng})
191
+        }
192
+      },
193
+    }
194
+  },
195
+  computed: {
196
+    ...mapBuildingState({
197
+      detail: x => x.detail,
198
+    }),
199
+    ...mapState({
200
+      dicts: x =>  x.dicts,
201
+    }),
202
+    building: {
203
+      get () {
204
+        return this.detail
205
+      },
206
+      set (val) {
207
+        return this.updateDetail(val)
208
+      }
209
+    },
210
+    propertyType () {
211
+      return (this.dicts || []).filter(x => x.type === 'propertyType')
212
+    },
213
+    saleType () {
214
+      return (this.dicts || []).filter(x => x.type === 'saleType')
215
+    }
216
+  },
217
+  methods: {
218
+    ...mapBuildingMutations([
219
+      'updateDetail'
220
+    ]),
221
+    ...mapBuildingActions([
222
+      'getDetail',
223
+      'addBuilding',
224
+      'editBuilding'
225
+    ]),
226
+    ...mapApartmentActions([
227
+      'getApartments',
228
+      'getApartmentDetail',
229
+      'addApartment',
230
+      'editApartment',
231
+      'deleteApartment'
232
+    ]),
233
+    getSaleTypeName (id) {
234
+      return ((this.dicts || []).filter(x => x.type === 'saleType' && x.id == id) [0] || {}).name
235
+    },
236
+    onCancel () {
237
+      this.$router.push({name: 'buildinglist'})
238
+    },
239
+    handleRemove (file, fileList) {
240
+      this.imgList = fileList
241
+    },
242
+    handleAparmentRemove (file, fileList) {
243
+      this.aparmentImg = fileList
244
+    },
245
+    onSearchResult (pois) { // 搜索地图
246
+      let latSum = 0
247
+      let lngSum = 0
248
+      if (pois.length > 0) {
249
+        pois.forEach(poi => {
250
+          let { lng, lat } = poi
251
+          lngSum += lng
252
+          latSum += lat
253
+          this.markers.push([poi.lng, poi.lat])
254
+        })
255
+        let center = {
256
+          lng: lngSum / pois.length,
257
+          lat: latSum / pois.length
258
+        }
259
+        this.mapCenter = [center.lng, center.lat]
260
+      }
261
+    },
262
+    beforeImgUpload (file) {
263
+      if (file.type !== 'image/jpeg') {
264
+        this.$message.error('上传图片只能是 JPG 格式!')
265
+        return false
266
+      }
267
+      // if (file.size / 1024 > 300) {
268
+      //   this.$message.error('图片大小不允许超过300k!')
269
+      //   return false
270
+      // }
271
+      this.loading = this.$loading({
272
+        lock: true,
273
+        text: '上传中...',
274
+        spinner: 'el-icon-loading',
275
+        background: 'rgba(0, 0, 0, 0.7)'
276
+      })
277
+
278
+      return true
279
+    },
280
+    handleAvatarSuccess (res) {
281
+      this.imgList = [...this.imgList, {
282
+        url: res.data[0]
283
+      }]
284
+      this.loading.close()
285
+    },
286
+    handleAparmentSuccess (res) {
287
+      this.aparmentImg = [...this.aparmentImg, {
288
+        url: res.data[0]
289
+      }]
290
+      this.loading.close()
291
+    },
292
+    onSubmit () {
293
+      const imgs = this.imgList.map((x, i) => {
294
+        return {
295
+          imgType: 'banner',
296
+          url: x.url,
297
+          orderNo: (i + 1)
298
+        }
299
+      })
300
+      const building = {...this.building, img: imgs, propertyType: this.buildingProperty.join(',')}
301
+      if (!building.buildingId) {
302
+        // 新增
303
+        this.addBuilding({
304
+          onSuccess: this.onCancel,
305
+          detail: JSON.stringify(building)
306
+        })
307
+      } else {
308
+        // 修改
309
+        this.editBuilding({
310
+          onSuccess: this.onCancel,
311
+          detail: JSON.stringify(building)
312
+        })
313
+      }
314
+    },
315
+    FormatDate (date) {
316
+      return (date || '').split('T')[0] === '0001-01-01' ? '' : (date || '').split('T')[0]
317
+    },
318
+    saveAparment () {
319
+      const imgs = this.aparmentImg.map((x, i) => {
320
+        return {
321
+          imgType: 'aparment',
322
+          url: x.url,
323
+          orderNo: (i + 1)
324
+        }
325
+      })
326
+      if (!this.aparmentInfo.apartmentId) {
327
+        this.addApartment(JSON.stringify({
328
+          ...this.aparmentInfo,
329
+          img: imgs
330
+        })).then(() => {
331
+          this.getAparmentList()
332
+          this.showHx = false
333
+        })
334
+      } else {
335
+        this.editApartment(JSON.stringify({
336
+          ...this.aparmentInfo,
337
+          img: imgs
338
+        })).then(() => {
339
+          this.getAparmentList()
340
+          this.showHx = false
341
+        })
342
+      }
343
+    },
344
+    addHx () {
345
+      this.aparmentImg = []
346
+      this.aparmentInfo = {buildingId: this.building.buildingId}
347
+      this.showHx = true
348
+    },
349
+    getAparmentList () {
350
+      this.getApartments({buildingId: this.$route.query.id}).then(data => {
351
+        this.aparments = data
352
+      })
353
+    },
354
+    handleEdit (row) {
355
+      this.aparmentImg = []
356
+      this.getApartmentDetail({
357
+        id: row.apartmentId
358
+      }).then(data => {
359
+        this.aparmentImg = data.buildingImgList.map(x => {
360
+          return {
361
+            name: x.imgId,
362
+            url: x.url
363
+          }
364
+        })
365
+        this.aparmentInfo = data
366
+        this.showHx = true
367
+      })
368
+    },
369
+    handleDel (row) {
370
+      
371
+    }
372
+  },
373
+  created () {
374
+    if ((this.$route.query.id || '') !== '') {
375
+      this.getAparmentList()
376
+      this.getDetail({id: this.$route.query.id}).then(data => {
377
+        this.buildingProperty = data.propertyType.split(',')
378
+        this.imgList = data.buildingImg.map(x => {
379
+          return {
380
+            name: x.imgId,
381
+            url: x.url
382
+          }
383
+        })
384
+      })
385
+    }
386
+  }
387
+}
388
+</script>
389
+
390
+<style lang="scss" scoped>
391
+.header{
392
+  width: 50px;
393
+  height: 50px;
394
+  img{
395
+    width: 100%;
396
+    height: 100%;
397
+  }
398
+}
399
+.avatar-uploader .el-upload {
400
+  border: 1px dashed #d9d9d9;
401
+  border-radius: 6px;
402
+  cursor: pointer;
403
+  position: relative;
404
+  overflow: hidden;
405
+}
406
+.avatar-uploader .el-upload:hover {
407
+  border-color: #409EFF;
408
+}
409
+.avatar-uploader-icon {
410
+  font-size: 28px;
411
+  color: #8c939d;
412
+  width: 178px;
413
+  height: 178px;
414
+  line-height: 178px;
415
+  text-align: center;
416
+}
417
+.avatar {
418
+  width: 178px;
419
+  height: 178px;
420
+  display: block;
421
+}
422
+
423
+.flex-item .amap-demo{
424
+  height: 300px;
425
+}
426
+
427
+.el-select{
428
+  width: 100% !important;
429
+}
430
+</style>

+ 274
- 0
src/views/building/list.vue 查看文件

@@ -0,0 +1,274 @@
1
+<template>
2
+  <div>
3
+    <div class="system-table-search">
4
+      <div class="flex-h">
5
+        <div class="flex-item flex-h">
6
+          <el-button size="mini" type="success" @click='addBuilding'>新增楼盘</el-button>
7
+        </div>
8
+        <ul>
9
+          <li>
10
+            <el-input v-model="code" placeholder="楼盘编号"></el-input>
11
+          </li>
12
+          <li>
13
+            <el-input v-model="name" placeholder="楼盘名称"></el-input>
14
+          </li>
15
+        </ul>
16
+        <el-button
17
+          size="mini"
18
+          type="primary" @click="search">搜索</el-button>
19
+      </div>
20
+      <div class="moreFilter"></div>
21
+    </div>
22
+    <el-table
23
+      :data="buildings.list || []"
24
+      style="width: 100%">
25
+      <el-table-column
26
+        type="index"
27
+        width="50">
28
+        <template slot-scope="scope">
29
+          <span>{{ GetIndex(scope.$index) }}</span>
30
+        </template>
31
+      </el-table-column>
32
+      <el-table-column
33
+        prop="code"
34
+        label="楼盘编号">
35
+      </el-table-column>
36
+      <el-table-column
37
+        prop="buildingName"
38
+        label="楼盘名称"
39
+        width="180">
40
+      </el-table-column>
41
+      <el-table-column
42
+        prop="price"
43
+        label="均价"
44
+        width="180">
45
+        <template slot-scope="scope">
46
+          <span style="color:red">{{scope.row.price}}</span>
47
+        </template>
48
+      </el-table-column>
49
+      <el-table-column
50
+        prop="openingDate"
51
+        label="开盘时间">
52
+        <template slot-scope="scope">
53
+          <span>{{(scope.row.openingDate || '').split(' ')[0]}}</span>
54
+        </template>
55
+      </el-table-column>
56
+      <el-table-column
57
+        prop="address"
58
+        label="项目地址">
59
+      </el-table-column>
60
+      <el-table-column
61
+        prop="propertyType"
62
+        label="物业类型">
63
+        <template slot-scope="scope">
64
+          <span>{{getPropertyType(scope.row.propertyType)}}</span>
65
+        </template>
66
+      </el-table-column>
67
+      <el-table-column
68
+        prop="tel"
69
+        label="销售电话">
70
+      </el-table-column>
71
+      <el-table-column
72
+        prop="createDate"
73
+        label="录入时间">
74
+        <template slot-scope="scope">
75
+          <span>{{(scope.row.createDate || '').split(' ')[0]}}</span>
76
+        </template>
77
+      </el-table-column>
78
+      <el-table-column
79
+        label="发布状态">
80
+        <template slot-scope="scope">
81
+          <span>{{scope.row.status == 1 ? '已发布' : '未发布'}}</span>
82
+        </template>
83
+      </el-table-column>
84
+      <el-table-column
85
+        fixed="right"
86
+        label="操作"
87
+        width="180">
88
+        <template slot-scope="scope">
89
+          <el-button type="text" @click="handleEdit(scope.row)" size="small">编辑</el-button>
90
+          <el-button type="text" @click="handlePulic(scope.row)" size="small" v-if="scope.row.status === 0">发布</el-button>
91
+          <el-button type="text" @click="handleUnPulic(scope.row)" size="small" v-if="scope.row.status === 1">取消发布</el-button>
92
+          <el-button @click="handleDel(scope.row)" type="text" size="small">删除</el-button>
93
+        </template>
94
+      </el-table-column>
95
+    </el-table>
96
+    <el-pagination
97
+      small
98
+      style="margin-top:10px;"
99
+      layout="prev, pager, next"
100
+      :current-page.sync="currentPage"
101
+      :pageSize="pageSize"
102
+      :total="buildings.total || 0"
103
+      @current-change="getList"
104
+    >
105
+    </el-pagination>
106
+  </div>
107
+</template>
108
+
109
+<script>
110
+import { createNamespacedHelpers } from 'vuex';
111
+
112
+const {mapState: mapBuildingState, mapActions: mapBuildingActions} = createNamespacedHelpers('building')
113
+import { mapState } from 'vuex'
114
+
115
+export default {
116
+  data() {
117
+    return {
118
+      pageSize: 20,
119
+      currentPage: 1,
120
+      code: '',
121
+      name: ''
122
+    }
123
+  },
124
+  computed: {
125
+    ...mapBuildingState({
126
+      buildings: x => x.buildings
127
+    }),
128
+    ...mapState({
129
+      dicts: x =>  x.dicts,
130
+    }),
131
+  },
132
+  methods: {
133
+    ...mapBuildingActions([
134
+      'setDetailNull',
135
+      'getBuildings',
136
+      'updateBuildingStatus',
137
+      'delBuilding'
138
+    ]),
139
+    getPropertyType (types) {
140
+      let typeNames = ''
141
+      let typeArr = types.split(',')
142
+      const _that = this
143
+      typeArr.map(x => {
144
+        const name = ((_that.dicts || []).filter(d => d.type === 'propertyType' && d.id == x)[0] || {}).name
145
+        if (name) {
146
+          typeNames = typeNames + (typeNames===''?'':',') + name
147
+        }
148
+      })
149
+      return typeNames
150
+    },
151
+    GetIndex (inx) {
152
+      return (this.currentPage - 1) * this.pageSize + inx + 1
153
+    },
154
+    // FormatDate (date) {
155
+    //   if (date) {
156
+    //     return date.split(' ')[0] === '0001-01-01' ? '' : date.split(' ')[0]
157
+    //   } else {
158
+    //     return ''
159
+    //   }
160
+    // },
161
+    getList () {
162
+      this.getBuildings({
163
+        pageNum: this.currentPage,
164
+        pageSize: this.pageSize,
165
+        code: this.code,
166
+        name: this.name
167
+      })
168
+    },
169
+    search () {
170
+      this.currentPage = 1
171
+      this.getList()
172
+    },
173
+    addBuilding () {
174
+      this.setDetailNull()
175
+      this.$router.push({name: 'buildingedit'})
176
+    },
177
+    handleEdit (row) {
178
+      this.setDetailNull()
179
+      this.$router.push({name: 'buildingedit', query: {id: row.buildingId}})
180
+    },
181
+    handlePulic (row) {
182
+      this.$confirm('确认发布此数据?', '提示', {
183
+        confirmButtonText: '确定',
184
+        cancelButtonText: '取消',
185
+        type: 'warning'
186
+      }).then(() => {
187
+        this.updateBuildingStatus(JSON.stringify({
188
+          id: row.buildingId,
189
+          status: 1
190
+        })).then(() => {
191
+          this.getList()
192
+        })
193
+      })
194
+    },
195
+    handleUnPulic (row) {
196
+      this.$confirm('确认取消发布此数据?', '提示', {
197
+        confirmButtonText: '确定',
198
+        cancelButtonText: '取消',
199
+        type: 'warning'
200
+      }).then(() => {
201
+        this.updateBuildingStatus(JSON.stringify({
202
+          id: row.buildingId,
203
+          status: 0
204
+        })).then(() => {
205
+          this.getList()
206
+        })
207
+      })
208
+    },
209
+    handleDel (row) {
210
+      this.$confirm('确认删除此数据?', '提示', {
211
+        confirmButtonText: '确定',
212
+        cancelButtonText: '取消',
213
+        type: 'warning'
214
+      }).then(() => {
215
+        if (row.status === 1) {
216
+          this.$message.error('当前楼盘处于发布状态,不允许删除!')
217
+          return false
218
+        }
219
+        this.delBuilding({
220
+          id: row.buildingId
221
+        }).then(() => {
222
+          this.getList()
223
+        })
224
+      })
225
+    }
226
+  },
227
+  created () {
228
+    this.getList()
229
+  }
230
+}
231
+</script>
232
+
233
+<style lang="scss" scoped>
234
+.list{
235
+    .header{
236
+      width: 50px;
237
+      height: 50px;
238
+      img{
239
+        width: 100%;
240
+        height: 100%;
241
+      }
242
+    }
243
+  }
244
+
245
+.system-table-search{
246
+  width: calc(100% - 40px);
247
+  margin: 20px auto 0;
248
+}
249
+
250
+.system-table-search li{
251
+  margin-right: 20px;
252
+}
253
+
254
+.system-table-search ul{
255
+  font-size: 0;
256
+  white-space: nowrap;
257
+}
258
+
259
+.system-table-search ul>li{
260
+  display: inline-block;
261
+}
262
+
263
+.flex-h {
264
+  display: flex;
265
+  display: -webkit-flex;
266
+}
267
+
268
+.flex-item {
269
+  flex: 1;
270
+  -webkit-flex: 1;
271
+  position: relative;
272
+  overflow: hidden;
273
+}
274
+</style>

+ 228
- 0
src/views/cms/list.vue 查看文件

@@ -0,0 +1,228 @@
1
+<template>
2
+  <div>
3
+    <div class="system-table-search">
4
+      <div class="flex-h">
5
+        <div class="flex-item flex-h">
6
+          <el-button size="mini" type="success" @click='addBuilding'>新增楼盘</el-button>
7
+        </div>
8
+        <ul>
9
+          <li>
10
+            <el-input v-model="code" placeholder="楼盘编号"></el-input>
11
+          </li>
12
+          <li>
13
+            <el-input v-model="name" placeholder="楼盘名称"></el-input>
14
+          </li>
15
+        </ul>
16
+        <el-button
17
+          size="mini"
18
+          type="primary" @click="search">搜索</el-button>
19
+      </div>
20
+      <div class="moreFilter"></div>
21
+    </div>
22
+    <el-table
23
+      :data="buildings.list || []"
24
+      style="width: 100%">
25
+      <el-table-column
26
+        type="index"
27
+        width="50">
28
+        <template slot-scope="scope">
29
+          <span>{{ GetIndex(scope.$index) }}</span>
30
+        </template>
31
+      </el-table-column>
32
+      <el-table-column
33
+        prop="code"
34
+        label="楼盘编号">
35
+      </el-table-column>
36
+      <el-table-column
37
+        prop="buildingName"
38
+        label="楼盘名称"
39
+        width="180">
40
+      </el-table-column>
41
+      <el-table-column
42
+        prop="price"
43
+        label="均价"
44
+        width="180">
45
+        <template slot-scope="scope">
46
+          <span style="color:#red">{{scope.row.price}}</span>
47
+        </template>
48
+      </el-table-column>
49
+      <el-table-column
50
+        prop="openingDate"
51
+        label="开盘时间">
52
+      </el-table-column>
53
+      <el-table-column
54
+        prop="address"
55
+        label="项目地址">
56
+      </el-table-column>
57
+      <el-table-column
58
+        prop="propertyType"
59
+        label="物业类型">
60
+      </el-table-column>
61
+      <el-table-column
62
+        prop="tel"
63
+        label="销售电话">
64
+      </el-table-column>
65
+      <el-table-column
66
+        prop="createDate"
67
+        label="录入时间">
68
+      </el-table-column>
69
+      <el-table-column
70
+        label="发布状态">
71
+        <template slot-scope="scope">
72
+          <span>{{scope.row.status == 1 ? '已发布' : '未发布'}}</span>
73
+        </template>
74
+      </el-table-column>
75
+      <el-table-column
76
+        fixed="right"
77
+        label="操作"
78
+        width="180">
79
+        <template slot-scope="scope">
80
+          <el-button type="text" @click="handleEdit(scope.row)" size="small">编辑</el-button>
81
+          <el-button type="text" @click="handlePulic(scope.row)" size="small" v-if="scope.row.status === 0">发布</el-button>
82
+          <el-button type="text" @click="handleUnPulic(scope.row)" size="small" v-if="scope.row.status === 1">取消发布</el-button>
83
+          <el-button @click="handleDel(scope.row)" type="text" size="small">删除</el-button>
84
+        </template>
85
+      </el-table-column>
86
+    </el-table>
87
+    <el-pagination
88
+      small
89
+      style="margin-top:10px;"
90
+      layout="prev, pager, next"
91
+      :current-page.sync="currentPage"
92
+      :pageSize="pageSize"
93
+      :total="buildings.total || 0"
94
+      @current-change="getList"
95
+    >
96
+    </el-pagination>
97
+  </div>
98
+</template>
99
+
100
+<script>
101
+import { createNamespacedHelpers } from 'vuex';
102
+
103
+const {mapState: mapBuildingState, mapActions: mapBuildingActions} = createNamespacedHelpers('building')
104
+
105
+export default {
106
+  data() {
107
+    return {
108
+      pageSize: 20,
109
+      currentPage: 1,
110
+      code: '',
111
+      name: ''
112
+    }
113
+  },
114
+  computed: {
115
+    ...mapBuildingState({
116
+      buildings: x => x.buildings
117
+    })
118
+  },
119
+  methods: {
120
+    ...mapBuildingActions([
121
+      'getBuildings',
122
+      'updateBuildingStatus',
123
+      'delBuilding'
124
+    ]),
125
+    GetIndex (inx) {
126
+      return (this.currentPage - 1) * this.pageSize + inx + 1
127
+    },
128
+    FormatDate (date) {
129
+      if (date) {
130
+        return date.split('T')[0] === '0001-01-01' ? '' : date.split('T')[0] + '  ' + date.split('T')[1]
131
+      } else {
132
+        return ''
133
+      }
134
+    },
135
+    getList () {
136
+      this.getBuildings({
137
+        pageNum: this.currentPage,
138
+        pageSize: this.pageSize,
139
+        code: this.code,
140
+        name: this.name
141
+      })
142
+    },
143
+    search () {
144
+      this.currentPage = 1
145
+      this.getList()
146
+    },
147
+    addBuilding () {
148
+      this.$router.push({name: 'buildingedit'})
149
+    },
150
+    handleEdit () {
151
+      
152
+    },
153
+    handlePulic (row) {
154
+      this.updateBuildingStatus(JSON.stringify({
155
+        id: row.buildingId,
156
+        status: 1
157
+      })).then(() => {
158
+        this.getList()
159
+      })
160
+    },
161
+    handleUnPulic (row) {
162
+      this.updateBuildingStatus(JSON.stringify({
163
+        id: row.buildingId,
164
+        status: 0
165
+      })).then(() => {
166
+        this.getList()
167
+      })
168
+    },
169
+    handleDel (row) {
170
+      if (row.status === 1) {
171
+        this.$message.error('当前楼盘发布状态,不允许删除!')
172
+        return false
173
+      }
174
+      this.delBuilding({
175
+        id: row.buildingId
176
+      }).then(() => {
177
+        this.getList()
178
+      })
179
+    }
180
+  },
181
+  created () {
182
+    this.getList()
183
+  }
184
+}
185
+</script>
186
+
187
+<style lang="scss" scoped>
188
+.list{
189
+    .header{
190
+      width: 50px;
191
+      height: 50px;
192
+      img{
193
+        width: 100%;
194
+        height: 100%;
195
+      }
196
+    }
197
+  }
198
+
199
+.system-table-search{
200
+  width: calc(100% - 40px);
201
+  margin: 20px auto 0;
202
+}
203
+
204
+.system-table-search li{
205
+  margin-right: 20px;
206
+}
207
+
208
+.system-table-search ul{
209
+  font-size: 0;
210
+  white-space: nowrap;
211
+}
212
+
213
+.system-table-search ul>li{
214
+  display: inline-block;
215
+}
216
+
217
+.flex-h {
218
+  display: flex;
219
+  display: -webkit-flex;
220
+}
221
+
222
+.flex-item {
223
+  flex: 1;
224
+  -webkit-flex: 1;
225
+  position: relative;
226
+  overflow: hidden;
227
+}
228
+</style>

+ 23
- 0
src/views/comment/list.vue 查看文件

@@ -0,0 +1,23 @@
1
+<template>
2
+  <el-tabs v-model="activeName">
3
+    <el-tab-pane label="活动评论" name="activity">
4
+    </el-tab-pane>
5
+    <el-tab-pane label="项目动态评论" name="dynamic">
6
+    </el-tab-pane>
7
+  </el-tabs>
8
+</template>
9
+
10
+<script>
11
+export default {
12
+  data () {
13
+    return {
14
+      activeName: 'activity'
15
+    }
16
+  },
17
+
18
+}
19
+</script>
20
+
21
+<style lang="scss" scoped>
22
+
23
+</style>

+ 73
- 0
src/views/customer/detail.vue 查看文件

@@ -0,0 +1,73 @@
1
+<template>
2
+  <el-form ref="form" :model="detail" label-width="160px">
3
+    <el-form-item label="头像">
4
+      <div class="header">
5
+        <img :src="detail.avatar" alt="" />
6
+      </div>
7
+    </el-form-item>
8
+    <el-form-item label="微信昵称">
9
+      <span>{{detail.name}}</span>
10
+    </el-form-item>
11
+    <el-form-item label="姓名">
12
+      <span>{{detail.customerName}}</span>
13
+    </el-form-item>
14
+    <el-form-item label="手机号">
15
+      <span>{{detail.phone}}</span>
16
+    </el-form-item>
17
+    <el-form-item label="证件号码">
18
+      <span>{{detail.idNum}}</span>
19
+    </el-form-item>
20
+    <el-form-item label="注册时间">
21
+      <span>{{detail.createDate}}</span>
22
+    </el-form-item>
23
+    <el-form-item>
24
+      <el-button @click="onCancel">返回</el-button>
25
+    </el-form-item>
26
+  </el-form>
27
+</template>
28
+
29
+<script>
30
+import { createNamespacedHelpers } from 'vuex';
31
+
32
+const {mapState: mapCustomerState, mapActions: mapCustomerActions} = createNamespacedHelpers('customer')
33
+
34
+export default {
35
+  data () {
36
+    return {}
37
+  },
38
+  computed: {
39
+    ...mapCustomerState({
40
+      detail: x => x.detail
41
+    })
42
+  },
43
+  methods: {
44
+    ...mapCustomerActions([
45
+      'getDetail'
46
+    ]),
47
+    onCancel() {
48
+      this.$router.push({name: 'customerlist'})
49
+    }
50
+  },
51
+  created() {
52
+    if ((this.$route.query.id || '') === '') {
53
+      this.$message.error('未找到对应的用户信息!')
54
+      this.$router.push({name: 'customerlist'})
55
+      return
56
+    }
57
+    this.getDetail({
58
+      id: this.$route.query.id
59
+    })
60
+  }
61
+}
62
+</script>
63
+
64
+<style lang="scss" scoped>
65
+.header{
66
+  width: 100px;
67
+  height: 100px;
68
+  img{
69
+    width: 100%;
70
+    height: 100%;
71
+  }
72
+}
73
+</style>

+ 199
- 0
src/views/customer/list.vue 查看文件

@@ -0,0 +1,199 @@
1
+<template>
2
+  <div>
3
+    <div class="system-table-search">
4
+      <div class="flex-h">
5
+        <div class="flex-item flex-h">
6
+        </div>
7
+        <ul>
8
+          <li>
9
+            <el-input v-model="name" placeholder="姓名或昵称"></el-input>
10
+          </li>
11
+          <li>
12
+            <el-input v-model="phone" placeholder="手机号"></el-input>
13
+          </li>
14
+          <li>
15
+            <el-input v-model="cardNo" placeholder="证件号"></el-input>
16
+          </li>
17
+          <li>
18
+            <el-date-picker
19
+              v-model="beginDate"
20
+              type="date"
21
+              value-format="yyyy-MM-dd"
22
+              placeholder="入会起始日期">
23
+            </el-date-picker>
24
+          </li>
25
+          <li>
26
+            <el-date-picker
27
+              v-model="endDate"
28
+              type="date"
29
+              value-format="yyyy-MM-dd"
30
+              placeholder="截止日期">
31
+            </el-date-picker>
32
+          </li>
33
+        </ul>
34
+        <el-button
35
+          size="mini"
36
+          type="primary" @click="search">搜索</el-button>
37
+      </div>
38
+      <div class="moreFilter"></div>
39
+    </div>
40
+    <el-table
41
+      :data="customers.list || []"
42
+      style="width: 100%">
43
+      <el-table-column
44
+        type="index"
45
+        width="50">
46
+        <template slot-scope="scope">
47
+          <span>{{ GetIndex(scope.$index) }}</span>
48
+        </template>
49
+      </el-table-column>
50
+      <el-table-column
51
+        label="头像">
52
+        <template slot-scope="scope">
53
+          <div class="header">
54
+            <img :src="scope.row.avatar" alt="" />
55
+          </div>
56
+        </template>
57
+      </el-table-column>
58
+      <el-table-column
59
+        prop="customerName"
60
+        label="用户姓名">
61
+      </el-table-column>
62
+      <el-table-column
63
+        prop="name"
64
+        label="昵称">
65
+      </el-table-column>
66
+      <el-table-column
67
+        prop="phone"
68
+        label="电话">
69
+      </el-table-column>
70
+      <el-table-column
71
+        label="创建时间"
72
+        width="180"
73
+        prop="createDate"
74
+      >
75
+      </el-table-column>
76
+      <el-table-column
77
+        fixed="right"
78
+        label="操作"
79
+        width="180">
80
+        <template slot-scope="scope">
81
+          <el-button type="text" @click="handleEdit(scope.row)" size="small">详情</el-button>
82
+        </template>
83
+      </el-table-column>
84
+    </el-table>
85
+    <el-pagination
86
+      small
87
+      style="margin-top:10px;"
88
+      layout="prev, pager, next"
89
+      :current-page.sync="currentPage"
90
+      :pageSize="pageSize"
91
+      :total="customers.total || 0"
92
+      @current-change="getList"
93
+    >
94
+    </el-pagination>
95
+  </div>
96
+</template>
97
+
98
+<script>
99
+import { createNamespacedHelpers } from 'vuex';
100
+
101
+const {mapState: mapCustomerState, mapActions: mapCustomerActions} = createNamespacedHelpers('customer')
102
+
103
+export default {
104
+  data() {
105
+    return {
106
+      pageSize: 20,
107
+      currentPage: 1,
108
+      name: '',
109
+      phone: '',
110
+      cardNo: '',
111
+      beginDate: '',
112
+      endDate: ''
113
+    }
114
+  },
115
+  computed: {
116
+    ...mapCustomerState({
117
+      customers: x => x.customers
118
+    }),
119
+  },
120
+  methods: {
121
+    ...mapCustomerActions([
122
+      'getCustomers',
123
+      'setDetailNull',
124
+    ]),
125
+    GetIndex (inx) {
126
+      return (this.currentPage - 1) * this.pageSize + inx + 1
127
+    },
128
+    FormatDate (date) {
129
+      if (date) {
130
+        return date.split('T')[0] === '0001-01-01' ? '' : date.split('T')[0] + '  ' + date.split('T')[1]
131
+      } else {
132
+        return ''
133
+      }
134
+    },
135
+    getList () {
136
+      this.getCustomers({
137
+        pageNum: this.currentPage,
138
+        pageSize: this.pageSize,
139
+        name: this.name,
140
+        phone: this.phone,
141
+        cardNo: this.cardNo,
142
+        beginDate: this.beginDate,
143
+        endDate: this.endDate
144
+      })
145
+    },
146
+    search () {
147
+      this.currentPage = 1
148
+      this.getList()
149
+    },
150
+    handleEdit (row) {
151
+      this.$router.push({name: 'customeredit', query: {id: row.customerId}})
152
+    },
153
+  },
154
+  created () {
155
+    this.getList()
156
+  }
157
+}
158
+</script>
159
+
160
+<style lang="scss" scoped>
161
+.header{
162
+  width: 50px;
163
+  height: 50px;
164
+  img{
165
+    width: 100%;
166
+    height: 100%;
167
+  }
168
+}
169
+
170
+.system-table-search{
171
+  width: calc(100% - 40px);
172
+  margin: 20px auto 0;
173
+}
174
+
175
+.system-table-search li{
176
+  margin-right: 20px;
177
+}
178
+
179
+.system-table-search ul{
180
+  font-size: 0;
181
+  white-space: nowrap;
182
+}
183
+
184
+.system-table-search ul>li{
185
+  display: inline-block;
186
+}
187
+
188
+.flex-h {
189
+  display: flex;
190
+  display: -webkit-flex;
191
+}
192
+
193
+.flex-item {
194
+  flex: 1;
195
+  -webkit-flex: 1;
196
+  position: relative;
197
+  overflow: hidden;
198
+}
199
+</style>

+ 138
- 0
src/views/index.js 查看文件

@@ -0,0 +1,138 @@
1
+
2
+const pages = [  
3
+  {
4
+    path: '',
5
+    name: 'dashboard',
6
+    component: () => import('./Dashboard.vue'),
7
+    meta: {
8
+      menuShow: true,
9
+      title: 'Dashboard',
10
+    },
11
+  },
12
+  {
13
+    path: 'building',
14
+    name: 'building',
15
+    component: () => import('./index.vue'),
16
+    meta: {
17
+      menuShow: true,
18
+      title: '楼盘设置',
19
+    },
20
+    children: [
21
+      {
22
+        path: 'buildinglist',
23
+        name: 'buildinglist',
24
+        component: () => import('./building/list.vue'),
25
+        meta: {
26
+          menuShow: true,
27
+          title: '楼盘列表',
28
+        },
29
+      },
30
+      {
31
+        path: 'buildingedit',
32
+        name: 'buildingedit',
33
+        component: () => import('./building/edit.vue'),
34
+        meta: {
35
+          menuShow: false,
36
+        },
37
+      },
38
+      {
39
+        path: 'appointment',
40
+        name: 'appointment',
41
+        component: () => import('./appointment/list.vue'),
42
+        meta: {
43
+          menuShow: true,
44
+          title: '预约信息',
45
+        },
46
+      },
47
+    ]
48
+  },
49
+  {
50
+    path: 'activity',
51
+    name: 'activity',
52
+    component: () => import('./index.vue'),
53
+    meta: {
54
+      menuShow: true,
55
+      title: '活动设置',
56
+    },
57
+    children: [
58
+      {
59
+        path: 'activitylist',
60
+        name: 'activitylist',
61
+        component: () => import('./activity/list.vue'),
62
+        meta: {
63
+          menuShow: true,
64
+          title: '活动列表',
65
+        },
66
+      },
67
+      {
68
+        path: 'activityedit',
69
+        name: 'activityedit',
70
+        component: () => import('./activity/edit.vue'),
71
+        meta: {
72
+          menuShow: false
73
+        },
74
+      },
75
+    ]
76
+  },
77
+  {
78
+    path: 'customer',
79
+    name: 'customer',
80
+    component: () => import('./index.vue'),
81
+    meta: {
82
+      menuShow: true,
83
+      title: '会员管理',
84
+    },
85
+    children: [
86
+      {
87
+        path: 'customerlist',
88
+        name: 'customerlist',
89
+        component: () => import('./customer/list.vue'),
90
+        meta: {
91
+          menuShow: true,
92
+          title: '会员列表',
93
+        },
94
+      },
95
+      {
96
+        path: 'customeredit',
97
+        name: 'customeredit',
98
+        component: () => import('./customer/detail.vue'),
99
+        meta: {
100
+          menuShow: false
101
+        },
102
+      },
103
+    ]
104
+  },
105
+  {
106
+    path: 'comment',
107
+    name: 'comment',
108
+    component: () => import('./index.vue'),
109
+    meta: {
110
+      menuShow: true,
111
+      title: '评论管理',
112
+    },
113
+    children: [
114
+      {
115
+        path: 'commentlist',
116
+        name: 'commentlist',
117
+        component: () => import('./comment/list.vue'),
118
+        meta: {
119
+          menuShow: true,
120
+          title: '评论列表',
121
+        },
122
+      }
123
+    ]
124
+  }
125
+]
126
+
127
+const flatten = (rts, parents = []) => {
128
+  return rts.reduce((acc, rt) => {
129
+    const chs = rt.children && rt.children.length ?
130
+      flatten(rt.children, parents.concat(rt)) : []
131
+    
132
+    return [...acc, { ...rt, parents }, ...chs]
133
+  }, [])
134
+}
135
+
136
+const pageArray = flatten(pages)
137
+
138
+export { pages, pageArray }

+ 3
- 0
src/views/index.vue 查看文件

@@ -0,0 +1,3 @@
1
+<template>
2
+  <router-view></router-view>
3
+</template>

+ 14
- 0
vue.config.js 查看文件

@@ -0,0 +1,14 @@
1
+module.exports = {
2
+  publicPath: './',
3
+  devServer: {
4
+    proxy: {
5
+      '/api': {
6
+        target: 'http://192.168.0.164:8080',
7
+        changeOrigin: true,
8
+        pathRewrite: {
9
+          '^/api': '/'
10
+        },
11
+      },
12
+    }
13
+  }
14
+}