tremble 4 年之前
當前提交
5d61322ab4
共有 59 個文件被更改,包括 15328 次插入0 次删除
  1. 3 0
      .browserslistrc
  2. 17 0
      .eslintrc.js
  3. 23 0
      .gitignore
  4. 24 0
      README.md
  5. 5 0
      babel.config.js
  6. 11884 0
      package-lock.json
  7. 29 0
      package.json
  8. 17 0
      public/index.html
  9. 18 0
      src/App.vue
  10. 76 0
      src/assets/font/iconfont.css
  11. 二進制
      src/assets/font/iconfont.eot
  12. 1 0
      src/assets/font/iconfont.js
  13. 128 0
      src/assets/font/iconfont.json
  14. 77 0
      src/assets/font/iconfont.svg
  15. 二進制
      src/assets/font/iconfont.ttf
  16. 二進制
      src/assets/font/iconfont.woff
  17. 二進制
      src/assets/font/iconfont.woff2
  18. 二進制
      src/assets/images/android.png
  19. 二進制
      src/assets/images/icon01.png
  20. 二進制
      src/assets/images/icon02.png
  21. 二進制
      src/assets/images/icon03.png
  22. 二進制
      src/assets/images/icon04.png
  23. 二進制
      src/assets/images/icon05.png
  24. 二進制
      src/assets/images/icon06.png
  25. 二進制
      src/assets/images/icon_mp3.png
  26. 二進制
      src/assets/images/img_login_banbg.jpg
  27. 二進制
      src/assets/images/index_logo@2x.png
  28. 二進制
      src/assets/images/ios.png
  29. 二進制
      src/assets/images/menu_icon_01@2x.png
  30. 二進制
      src/assets/images/menu_icon_02@2x.png
  31. 二進制
      src/assets/images/menu_icon_03@2x.png
  32. 二進制
      src/assets/images/qrcode.png
  33. 二進制
      src/assets/images/test.png
  34. 139 0
      src/assets/style/info.less
  35. 103 0
      src/assets/style/public.less
  36. 22 0
      src/assets/style/reset.less
  37. 59 0
      src/components/HelloWorld.vue
  38. 87 0
      src/components/crumb/index.vue
  39. 16 0
      src/components/vcenter/index.vue
  40. 94 0
      src/config/http.js
  41. 10 0
      src/config/route.js
  42. 54 0
      src/main.js
  43. 56 0
      src/mixins/index.js
  44. 48 0
      src/router/index.js
  45. 二進制
      src/theme/fonts/element-icons.ttf
  46. 二進制
      src/theme/fonts/element-icons.woff
  47. 1 0
      src/theme/index.css
  48. 9 0
      src/utils/index.js
  49. 218 0
      src/views/client/index.vue
  50. 173 0
      src/views/layout/aside.vue
  51. 74 0
      src/views/layout/header/index.vue
  52. 42 0
      src/views/layout/header/style.less
  53. 70 0
      src/views/layout/index.vue
  54. 219 0
      src/views/login/index.vue
  55. 697 0
      src/views/niche/index.vue
  56. 285 0
      src/views/order/index.vue
  57. 546 0
      src/views/position/index.vue
  58. 1 0
      style/config.json
  59. 3 0
      vue.config.js

+ 3 - 0
.browserslistrc

@@ -0,0 +1,3 @@
+> 1%
+last 2 versions
+not dead

+ 17 - 0
.eslintrc.js

@@ -0,0 +1,17 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true
+  },
+  'extends': [
+    'plugin:vue/essential',
+    'eslint:recommended'
+  ],
+  parserOptions: {
+    parser: 'babel-eslint'
+  },
+  rules: {
+    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
+  }
+}

+ 23 - 0
.gitignore

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

+ 24 - 0
README.md

@@ -0,0 +1,24 @@
+# backstage
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Lints and fixes files
+```
+npm run lint
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 5 - 0
babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

文件差異過大導致無法顯示
+ 11884 - 0
package-lock.json


+ 29 - 0
package.json

@@ -0,0 +1,29 @@
+{
+  "name": "backstage",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "axios": "^0.21.0",
+    "core-js": "^3.8.0",
+    "element-ui": "^2.14.1",
+    "vue": "^2.6.12",
+    "vue-router": "^3.4.9"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "^4.5.9",
+    "@vue/cli-plugin-eslint": "^4.5.9",
+    "@vue/cli-plugin-router": "^4.5.9",
+    "@vue/cli-service": "^4.5.9",
+    "babel-eslint": "^10.1.0",
+    "eslint": "^6.8.0",
+    "eslint-plugin-vue": "^6.2.2",
+    "less": "^3.12.2",
+    "less-loader": "^5.0.0",
+    "vue-template-compiler": "^2.6.12"
+  }
+}

+ 17 - 0
public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico"> -->
+    <title>永念庭管理後台</title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 18 - 0
src/App.vue

@@ -0,0 +1,18 @@
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<script>
+import '@/assets/style/reset.less'
+import '@/assets/style/public.less'
+import '@/assets/font/iconfont.css'
+export default {
+  
+}
+</script>
+
+<style lang="less">
+
+</style>

文件差異過大導致無法顯示
+ 76 - 0
src/assets/font/iconfont.css


二進制
src/assets/font/iconfont.eot


文件差異過大導致無法顯示
+ 1 - 0
src/assets/font/iconfont.js


+ 128 - 0
src/assets/font/iconfont.json

@@ -0,0 +1,128 @@
+{
+  "id": "1713010",
+  "name": "消安管理后台",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "3232662",
+      "name": "正确 copy",
+      "font_class": "zhengquecopy",
+      "unicode": "e687",
+      "unicode_decimal": 59015
+    },
+    {
+      "icon_id": "4647621",
+      "name": "关闭",
+      "font_class": "guanbi",
+      "unicode": "e61a",
+      "unicode_decimal": 58906
+    },
+    {
+      "icon_id": "5093350",
+      "name": "重置",
+      "font_class": "zhongzhi",
+      "unicode": "e609",
+      "unicode_decimal": 58889
+    },
+    {
+      "icon_id": "7025373",
+      "name": "退出",
+      "font_class": "tuichu",
+      "unicode": "e611",
+      "unicode_decimal": 58897
+    },
+    {
+      "icon_id": "7025374",
+      "name": "下拉",
+      "font_class": "xl",
+      "unicode": "e612",
+      "unicode_decimal": 58898
+    },
+    {
+      "icon_id": "7040427",
+      "name": "下载",
+      "font_class": "tubiaozhizuomoban",
+      "unicode": "e613",
+      "unicode_decimal": 58899
+    },
+    {
+      "icon_id": "8747856",
+      "name": "增加",
+      "font_class": "add",
+      "unicode": "e648",
+      "unicode_decimal": 58952
+    },
+    {
+      "icon_id": "10726198",
+      "name": "icon_mp3",
+      "font_class": "icon_mp",
+      "unicode": "e653",
+      "unicode_decimal": 58963
+    },
+    {
+      "icon_id": "13682089",
+      "name": "per",
+      "font_class": "per",
+      "unicode": "e6bd",
+      "unicode_decimal": 59069
+    },
+    {
+      "icon_id": "13682090",
+      "name": "index",
+      "font_class": "index",
+      "unicode": "e6be",
+      "unicode_decimal": 59070
+    },
+    {
+      "icon_id": "13682091",
+      "name": "record",
+      "font_class": "record",
+      "unicode": "e6bf",
+      "unicode_decimal": 59071
+    },
+    {
+      "icon_id": "13682092",
+      "name": "power",
+      "font_class": "power",
+      "unicode": "e6c0",
+      "unicode_decimal": 59072
+    },
+    {
+      "icon_id": "13682093",
+      "name": "scene",
+      "font_class": "scene",
+      "unicode": "e6c1",
+      "unicode_decimal": 59073
+    },
+    {
+      "icon_id": "13682094",
+      "name": "feedback",
+      "font_class": "feedback",
+      "unicode": "e6c2",
+      "unicode_decimal": 59074
+    },
+    {
+      "icon_id": "13682095",
+      "name": "department",
+      "font_class": "department",
+      "unicode": "e6c3",
+      "unicode_decimal": 59075
+    },
+    {
+      "icon_id": "13682096",
+      "name": "statistics",
+      "font_class": "statistics",
+      "unicode": "e6c4",
+      "unicode_decimal": 59076
+    },
+    {
+      "icon_id": "13682097",
+      "name": "user",
+      "font_class": "user",
+      "unicode": "e6c5",
+      "unicode_decimal": 59077
+    }
+  ]
+}

文件差異過大導致無法顯示
+ 77 - 0
src/assets/font/iconfont.svg


二進制
src/assets/font/iconfont.ttf


二進制
src/assets/font/iconfont.woff


二進制
src/assets/font/iconfont.woff2


二進制
src/assets/images/android.png


二進制
src/assets/images/icon01.png


二進制
src/assets/images/icon02.png


二進制
src/assets/images/icon03.png


二進制
src/assets/images/icon04.png


二進制
src/assets/images/icon05.png


二進制
src/assets/images/icon06.png


二進制
src/assets/images/icon_mp3.png


二進制
src/assets/images/img_login_banbg.jpg


二進制
src/assets/images/index_logo@2x.png


二進制
src/assets/images/ios.png


二進制
src/assets/images/menu_icon_01@2x.png


二進制
src/assets/images/menu_icon_02@2x.png


二進制
src/assets/images/menu_icon_03@2x.png


二進制
src/assets/images/qrcode.png


二進制
src/assets/images/test.png


+ 139 - 0
src/assets/style/info.less

@@ -0,0 +1,139 @@
+.con {
+  .d-l{
+    // & /deep/ .el-form-item__content{
+    //   line-height: 30px;
+    //   .el-upload-list__item{
+    //     display: none;
+    //     &:last-child{
+    //       display: inline-block;
+    //     }
+    //   }
+    // }
+    display: inline-block;
+    width:45%;
+  }
+  .d-r{
+    width:45%;
+    float: right;
+    .el-date-editor.el-input, .el-date-editor.el-input__inner{
+      width: 100%;
+    }
+    .c-logo{
+      width: 150px;
+      height: 150px;
+      border: 1px solid #ccc;
+      border-radius: 4px;
+      background-color: #f5fafe;
+      img{
+        width: 100%;
+        height: 100%;
+      }
+    }
+    .c-zizhi{
+      width: 150px;
+      height: 150px;
+      border: 1px solid #ccc;
+      border-radius: 4px;
+      position: relative;
+      background-color: #f5fafe;
+      img{
+        width: 100%;
+        height: 100%;
+      }
+      .c-mask{
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        top: 0;
+        left: 0;
+        background: rgba(0, 0, 0, 0.5);
+        text-align: center;
+        display: table;
+        .icon-download{
+          text-align: center;
+          display: table-cell;
+          vertical-align: middle;
+          color: #fff;
+          font-size: 20px;
+        }
+      }
+    }
+  }
+  .ei-num{
+    .el-input-number{
+      width: 90%;
+    }
+  }
+  .ei-input{
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+    .input-con{
+      display: inline-block;
+      width: 45%;
+      margin: 2px 5px 2px 0;
+      height: 60px;
+      position: relative;
+      .el-input_err{
+        & /deep/ .el-input__inner{
+          border: 1px solid #f56c6c;
+        }
+      }
+      .el-input_success{
+        & /deep/ .el-input__inner{
+          border: 1px solid #67c23a;
+        }
+      }
+      span {
+        color: #f56c6c;
+        font-size: 12px;
+        line-height: 1;
+        padding-top: 4px;
+        position: absolute;
+        left: 0;
+      }
+    }
+
+  }
+ .h-header {
+    height: 80px;
+    background-color: #fff;
+    padding-left: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    div{
+      display: inline-block;
+      vertical-align: middle;
+    }
+    .h-input {
+      margin-left: 20px;
+      display: inline-block;
+    }
+    .fix-date{
+      div{
+        display: flex;
+      }
+    }
+  }
+
+  .h-body {
+    width: 100%;
+    margin: 30px 0 0;
+    padding-top: 20px;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 3px;
+    background-color: #fff;
+
+    .o-span {
+      color: #0175dc;
+      cursor: pointer;
+      margin-right: 10px;
+    }
+
+    .p-con {
+      width: 100%;
+      text-align: right;
+      padding: 25px 20px 30px;
+    }
+  }
+}

+ 103 - 0
src/assets/style/public.less

@@ -0,0 +1,103 @@
+#app, .layout, body, html{
+  width: 100%;
+  height: 100%;
+  color: #333;
+}
+table tr td .cell,table tr th .cell{
+  text-align: center;
+}
+
+::-webkit-scrollbar {
+  width: 5px;
+  height: 16px;
+  background-color: transparent;
+}
+
+::-webkit-scrollbar-track{
+    border-radius: 10px;
+    background-color: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.2);
+  background-color: #d4d4d4;
+}
+
+.plate{
+  .top-con{
+    background: #fff;
+    position: relative;
+    padding: 10px;
+    .top{
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      width: 60%;
+      .el-input{
+        margin-right: 10px;
+        flex: 1;
+      }
+      .el-select{
+        margin-right: 10px;
+        min-width: 240px;
+      }
+      .add{
+        position: absolute;
+        right: 10px;
+        top: 50%;
+        transform: translateY(-50%);
+      }
+    }
+  }
+  .body{
+    margin-top: 10px;
+  }
+  .add-con{
+    padding: 20px 50% 20px 0px;
+    background: #fff;
+    .btn{
+      padding: 10px 50px;
+    }
+   
+  }
+  .e-page{
+    background: #fff;
+    text-align: right;
+    padding: 20px;
+  }
+  .thumb {
+    display: block;
+    margin: 10px 0;
+    position: relative;
+    width: 300px;
+    > img {
+      max-width: 100%;
+    }
+    &:hover {
+      .el-icon-delete {
+        display: block;
+      }
+    }
+    .el-icon-delete {
+      position: absolute;
+      right: 5px;
+      top: 5px;
+      font-size: 20px;
+      color: #000;
+      cursor: pointer;
+      display: none;
+      text-shadow: 1px 1px 10px rgba(0,0,0,1);
+    }
+  }
+}
+
+.o-span {
+  color: #0175dc;
+  cursor: pointer;
+  margin-right: 10px;
+}
+
+.del{
+  color: #f56c6c;
+}

+ 22 - 0
src/assets/style/reset.less

@@ -0,0 +1,22 @@
+/* reset */
+html,body,h1,h2,h3,h4,h5,h6,div,dl,dt,dd,ul,ol,li,p,blockquote,pre,hr,figure,table,caption,th,td,form,fieldset,legend,input,button,textarea,menu{margin:0;padding:0;}
+header,footer,section,article,aside,nav,hgroup,address,figure,figcaption,menu,details{display:block;}
+table{border-collapse:collapse;border-spacing:0;}
+caption,th{text-align:left;font-weight:normal;}
+html,body,fieldset,img,iframe,abbr{border:0;}
+i,cite,em,var,address,dfn{font-style:normal;}
+[hidefocus],summary{outline:0;}
+li{list-style:none;}
+// h1,h2,h3,h4,h5,h6,small{font-size:100%;}
+sup,sub{font-size:83%;}
+pre,code,kbd,samp{font-family:inherit;}
+q:before,q:after{content:none;}
+textarea{overflow:auto;resize:none;}
+label,summary{cursor:default;}
+a,button{cursor:pointer;}
+h1,h2,h3,h4,h5,h6,em,strong,b{font-weight:bold;}
+del,ins,u,s,a,a:hover{text-decoration:none;}
+body,textarea,input,button,select,keygen,legend{font:14px/1.14 'Microsoft YaHei',\5b8b\4f53;outline:0;}
+body{background:#fff;}
+*{box-sizing: border-box}
+a{text-decoration: none;}

+ 59 - 0
src/components/HelloWorld.vue

@@ -0,0 +1,59 @@
+<template>
+  <div class="hello">
+    <h1>{{ msg }}</h1>
+    <p>
+      For a guide and recipes on how to configure / customize this project,<br>
+      check out the
+      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
+    </p>
+    <h3>Installed CLI Plugins</h3>
+    <ul>
+      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
+      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
+      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
+    </ul>
+    <h3>Essential Links</h3>
+    <ul>
+      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
+      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
+      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
+      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
+      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
+    </ul>
+    <h3>Ecosystem</h3>
+    <ul>
+      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
+      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
+      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
+      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
+      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
+    </ul>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'HelloWorld',
+  props: {
+    msg: String
+  }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped lang="less">
+h3 {
+  margin: 40px 0 0;
+}
+ul {
+  list-style-type: none;
+  padding: 0;
+}
+li {
+  display: inline-block;
+  margin: 0 10px;
+}
+a {
+  color: #42b983;
+}
+</style>

+ 87 - 0
src/components/crumb/index.vue

@@ -0,0 +1,87 @@
+<!--  -->
+<template>
+<div class='crumb-con'>
+  <vcenter>
+    <span>{{title}}</span>
+    <div class="fr">
+      <div v-for="(item,i) in data" :key="i">
+        <span style="cursor:pointer">{{item.name}}</span>
+        <span style="padding-right:3px;" v-if="data.length-1!==i">></span>
+      </div>
+    </div>
+  </vcenter>
+</div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from '@/components/vcenter'
+
+export default {
+// import引入的组件需要注入到对象中才能使用
+  props: {
+    data: {
+      default: () => {
+        return []
+      },
+      type: Array
+    },
+    title: {
+      default: () => {
+        return ''
+      },
+      type: String
+    }
+  },
+  components: {vcenter},
+  data () {
+    // 这里存放数据
+    return {
+
+    }
+  },
+  // 监听属性 类似于data概念
+  computed: {},
+  // 监控data中的数据变化
+  watch: {},
+  // 方法集合
+  methods: {
+
+  },
+  // 生命周期 - 创建完成(可以访问当前this实例)
+  created () {
+
+  },
+  // 生命周期 - 挂载完成(可以访问DOM元素)
+  mounted () {
+
+  },
+  beforeCreate () {}, // 生命周期 - 创建之前
+  beforeMount () {}, // 生命周期 - 挂载之前
+  beforeUpdate () {}, // 生命周期 - 更新之前
+  updated () {}, // 生命周期 - 更新之后
+  beforeDestroy () {}, // 生命周期 - 销毁之前
+  destroyed () {}, // 生命周期 - 销毁完成
+  activated () {} // 如果页面有keep-alive缓存功能,这个函数会触发
+}
+</script>
+<style lang="less" scoped>
+.crumb-con{
+  height: 60px;
+  width: 100%;
+  background-color: #fff;
+  box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.1);
+  font-size: 18px;
+  padding: 0 25px;
+  .fr{
+    color: #999;
+    float: right;
+    div{
+      display: inline-block;
+      font-size: 14px;
+    }
+  }
+}
+
+</style>

+ 16 - 0
src/components/vcenter/index.vue

@@ -0,0 +1,16 @@
+<template>
+  <table>
+    <tr>
+      <td>
+        <slot />
+      </td>
+    </tr>
+  </table>
+</template>
+
+<style scoped>
+table {
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 94 - 0
src/config/http.js

@@ -0,0 +1,94 @@
+import axios from 'axios'
+// import  qs from 'qs'
+
+import Vue from 'vue';
+let vue = new Vue()
+
+let loading = ''
+
+var isProduction = process.env.NODE_ENV === 'production'
+
+const serverName = isProduction ? '/platform-framework/' : 'http://vr.finehousing.co/platform-framework/'
+
+axios.defaults.baseURL = serverName
+axios.defaults.headers['X-Requested-with'] = 'XMLHttpRequest'
+axios.defaults.headers['token'] =  window.localStorage.getItem('token')
+
+axios.interceptors.request.use(function (config) {
+  if (config.method === 'post') {
+    config.data = {
+      ...config.data,
+      rnd: Math.random()
+    }
+    // config.data = qs.stringify(config.data)
+  } else if (config.method === 'get') {
+    config.params = {
+      ...config.params
+    }
+    // config.params = qs.stringify(config.params)
+  }
+
+  loading = vue.$loading({
+    lock: true,
+    text: '加載中',
+    spinner: 'el-icon-loading',
+    background: 'rgba(0, 0, 0, 0.7)'
+  });
+  
+  return config
+}, function (error) {
+  // 对请求错误做些什么
+  return Promise.reject(error)
+})
+
+// 配置response拦截器
+axios.interceptors.response.use(
+  response => {
+    loading.close()
+
+    let data = response.data
+    let code = Number(response.data.code)
+    switch (code) {
+      case -1:
+        break
+        case 4500:
+         
+          break
+        case 5001:
+          
+          break
+        case 5002:
+          
+          break
+        case 500:
+          vue.$message({
+            type: 'error',
+            message: `網絡錯誤,請稍後再試`
+          });
+          break
+      case 0:
+        break
+    }
+    // tryHideFullScreenLoading()
+    return data
+  },
+  error => {
+    console.log(error);
+    setTimeout(()=> {
+      localStorage.setItem("token", "");
+      localStorage.setItem("userInfo", "");
+      window.location.href = '/platform-framework/login.html';
+    }, 500);
+    if (error.response) {
+      switch (error.response.code) {
+        case 500:
+          break
+        case 3004:
+          
+          break
+      }
+    }
+    return Promise.reject(error)
+  }
+)
+export { serverName, axios }

+ 10 - 0
src/config/route.js

@@ -0,0 +1,10 @@
+const Menu = [
+    { text: '龛位管理', belong: '1', link: '/', name: 'niche' },
+    { text: '方位管理', belong: '2', link: '/position', name: 'position' },
+    { text: '訂單管理', belong: '3', link: '/order', name: 'order' },
+    { text: '客戶管理', belong: '4', link: '/client', name: 'client' },
+]
+
+export {
+    Menu
+}

+ 54 - 0
src/main.js

@@ -0,0 +1,54 @@
+import Vue from 'vue'
+import router from './router'
+import './theme/index.css'
+import ElementUI from 'element-ui';
+import 'element-ui/lib/theme-chalk/index.css';
+import App from './App.vue'
+import { axios, serverName } from './config/http'
+import { isImage } from "@/utils/index.js";
+
+
+let tmp = ['可預訂', '已預訂', '已售', '現貨', '预售', '转售', '已下架']
+Vue.use(ElementUI);
+Vue.prototype.$http = axios
+Vue.prototype.$serverName = serverName
+let temp = {}
+tmp.forEach((item, idx) => {
+  temp[idx] = item
+})
+Vue.prototype.$statusStr = temp
+Vue.prototype.isImage = isImage
+
+
+Vue.prototype.$statusArr = tmp.map((item, idx) => {
+  return {
+    id: idx,
+    name: item
+  }
+})
+Vue.prototype.$bus = new Vue()
+
+Vue.config.productionTip = false
+
+Date.prototype.Format = function (fmt) {
+  var o = {
+    "M+": this.getMonth() + 1, //月份
+    "d+": this.getDate(), //日
+    "h+": this.getHours(), //小时
+    "m+": this.getMinutes(), //分
+    "s+": this.getSeconds(), //秒
+    "q+": Math.floor((this.getMonth() + 3) / 3), //季度
+    "S": this.getMilliseconds() //毫秒
+  };
+  if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
+  for (var k in o)
+    if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
+  return fmt;
+}
+
+import './mixins'
+
+new Vue({
+  router,
+  render: h => h(App)
+}).$mount('#app')

+ 56 - 0
src/mixins/index.js

@@ -0,0 +1,56 @@
+import Vue from 'vue'
+
+Vue.mixin({
+    components: {
+    },
+  
+    
+    data() {
+        return {
+          $__loading:{
+            close(){
+          
+            }
+          },
+          loadOption:{
+            lock: true,
+            text: '上傳中',
+            spinner: 'el-icon-loading',
+            background: 'rgba(0, 0, 0, 0.7)'
+          }
+
+        }
+    },
+    methods: {
+      $beforeUpload(file) {
+        let type = this.isImage(file.name);
+        if (!type) {
+          this.$message.error("只允許上傳圖片");
+          return type;
+        }
+        const isLt2M = file.size / 1024 / 1024 < 5;
+  
+        if (!isLt2M) {
+          this.$message.error("上傳圖片大小不能超過 5MB!");
+          return isLt2M;
+        }
+        this.showLoading()
+      },
+      handleError(){
+        this.$alert("上傳失敗,請稍後再試", "提示", {
+          confirmButtonText: "確定",
+          callback: () => {
+            this.$__loading.close()
+          },
+        });
+      },
+      showLoading(){
+        this.$__loading = this.$loading(this.loadOption)
+      },
+      $inputnum(e){
+        console.log('e',e)
+        
+        e = (e.match(/^\d*(\.?\d{0,2})/g)[0]) || null
+      }
+    }
+})

+ 48 - 0
src/router/index.js

@@ -0,0 +1,48 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import { Menu } from '../config/route.js'
+Vue.use(VueRouter)
+
+const originalPush = VueRouter.prototype.push;
+VueRouter.prototype.push = function push(location) {
+  return originalPush.call(this, location).catch((err) => err);
+};
+
+
+let routes = [
+  {
+    path: '/',
+    component: () => import('@/views/layout/'),
+    children: []
+  },
+  {
+    path: '/login',
+    name: '登录',
+    component: () => import('@/views/login')
+  }
+];
+
+Menu.forEach((item) => {
+  routes[0].children.push({
+      name: item.name,
+      path: `${item.link}`,
+      meta: {
+          index: item.belong,
+          text:item.text
+      },
+      component: () => import(`../views/${item.name}/index.vue`)
+  })
+})
+
+const router = new VueRouter({
+  routes
+})
+
+
+router.beforeEach((to, from, next) => {
+  window.scrollTo(0, 0)
+  next()
+})
+
+
+export default router

二進制
src/theme/fonts/element-icons.ttf


二進制
src/theme/fonts/element-icons.woff


文件差異過大導致無法顯示
+ 1 - 0
src/theme/index.css


+ 9 - 0
src/utils/index.js

@@ -0,0 +1,9 @@
+
+module.exports = {
+
+isImage: function (fileName) {
+  if (typeof fileName !== 'string') return;
+  let name = fileName.toLowerCase();
+  return name.endsWith('.png') || name.endsWith('.jpeg') || name.endsWith('.jpg') || name.endsWith('.gif');
+}
+}

+ 218 - 0
src/views/client/index.vue

@@ -0,0 +1,218 @@
+<template>
+  <div class="plate">
+    <div class="top-con">
+      <div class="top" style="width:400px">
+        <el-input v-model="userName" placeholder="请输入客戶名字"></el-input>
+        <el-button type="primary" @click="getList">查询</el-button>
+        <el-upload
+          class="add"
+          ref="upload"
+          :show-file-list='false'
+          :action="uploadUrl"
+          :on-success="handleSuccess"
+          >
+          <el-button slot="trigger" type="primary">批量導入客戶</el-button>
+        </el-upload>
+      </div>
+    </div>
+    <div class="body">
+      <el-table height="65vh"
+        :data="tableData" style="width: 100%">
+        <el-table-column
+          v-for="(item, idx) in data"
+          :key="idx"
+          :prop="item.prop"
+          :label="item.label"
+        >
+          <template slot-scope="scope">
+            <img
+              style="width:80px;height:80px;border-radius:50%;"
+              v-if="item.prop === 'ico'"
+              :src="scope.row[item.prop]"
+              alt
+            />
+            <span v-else>{{ scope.row[item.prop] || "-" }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="e-page">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="pageNum"
+            :page-size="size"
+            layout="sizes, prev, pager, next"
+            :page-sizes="[15, 50, 100, 500]"
+            @size-change="handleSizeChange"
+            :total="total"
+          ></el-pagination>
+        </div>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    let data = [
+      {
+        prop: "userSn",
+        label: "客戶編號",
+      },
+      {
+        prop: "userName",
+        label: "客戶名稱",
+      },
+      {
+        prop: "phonePlaceCode",
+        label: "區號",
+      },
+      {
+        prop: "phoneNum",
+        label: "手機電話",
+      },
+      {
+        prop: "email",
+        label: "客戶郵箱",
+      },
+      {
+        prop: "addTime",
+        label: "客戶添加時間",
+      },
+      {
+        prop: "latestReserveTime",
+        label: "最新預訂時間",
+      }
+    ];
+
+  
+    return {
+      data,
+      tableData: [],
+      dialogFormVisible: false,
+      userName:'',
+      formLabelWidth: "120px",
+      pageNum:1,
+      size:15,
+      total:0,
+      uploadUrl:this.$serverName+'box/user/batchImport',
+    };
+  },
+
+  watch:{
+    pageNum(){
+        this.getList()
+    },
+    size(){
+      this.getList()
+    }
+  },
+  mounted(){
+    this.getList()
+  },
+  methods:{
+    handleCurrentChange (val) {
+      this.pageNum = val
+    },
+    handleSizeChange (val) {
+      this.size = val
+    },
+    handleSuccess(data){
+      if (data.code == 0) {
+        this.$alert('上傳成功', '提示', {
+          confirmButtonText: '确定',
+          callback: () => {
+            this.getList()
+          }
+        })
+      }
+      else {
+         this.$message({
+          type: "error",
+          message: data.msg,
+        });
+      }
+    },
+    async show(item){
+       let result = await this.$http.get(`box/order/getDetail`,
+        {
+          params:{
+            id:item.orderId
+          }
+        })
+      this.form = result.data
+      this.dialogFormVisible = true
+      this.editTitle = '編輯'
+    },
+
+    async save(){
+      let url = '/box/order/update'
+      let result = await this.$http.post(url,{
+          ...this.form
+      })
+      if (result.code == 0) {
+      this.$alert('操作成功', '提示', {
+        confirmButtonText: '确定',
+        callback: () => {
+          this.form={
+            isHandle: "",
+            updateTime:""
+          }
+          this.dialogFormVisible = false
+          this.getList()
+        }
+      })
+      }
+    },
+    submitForm() {
+        this.$refs.order.validate((valid) => {
+          if (valid) {
+            this.save()
+          } else {
+            return false;
+          }
+        });
+      },
+   
+    async getList(){
+      let result = await this.$http.get(`/box/user/getOrQuery`,{
+        params:{
+          userName:this.userName,
+          pageNum:this.pageNum,
+          pageSize:this.size
+        }
+      })
+
+      this.tableData = result.data.list.map(item=>{
+        item['isShowStr'] = item['isShow']?'顯示':'不顯示'
+        item['isHandleStr'] = item['isHandle']?'已處理':'未處理'
+        return item
+      })
+      this.total= result.data.totalNum
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.thumb{
+  display: block;
+  margin: 10px 0;
+  position: relative;
+  width: 120px;
+  >img{
+    max-width: 120px;
+  }
+  &:hover{
+    .el-icon-delete{
+      display: block;
+    }
+  }
+  .el-icon-delete{
+    position: absolute;
+    right: 5px;
+    top: 5px;
+    font-size: 20px;
+    cursor: pointer;
+    display: none;
+  }
+}
+</style>

+ 173 - 0
src/views/layout/aside.vue

@@ -0,0 +1,173 @@
+<template>
+<div class='aside'>
+  <div class="nav-menu">
+    <div class="navigator_menu">
+      <vcenter>
+        <span>导航面板</span>
+      </vcenter>
+    </div>
+    <ul class="aside-ul">
+      <li class="aside-li" v-for="(item,i) in aside" :key="i">
+        <div @click="clickNav(item)" :class="{'aside-li-div':true,'active':item.id&&item.id===activeIdx}" >
+          <vcenter>
+            <i class="iconfont" :class="item.icon" style="float:left;width:27px;"></i>
+            <span style="vertical-align: middle;">{{item.name}}</span>
+            <i v-if="item.subItem" style="font-size:12px;vertical-align: middle;position:relative;top:2px;" :class="{'iconfont':true, 'icon-arrow':true,'fr':true,rotate:item.isShow}"></i>
+          </vcenter>
+        </div>
+        <ul class="li-ul" v-if="item.subItem" :style="{maxHeight:item.isShow?'260px':'0'}">
+          <li @click="navigate(sub)" v-for="(sub, idx) in item.subItem" :class="{'active':sub.id&&sub.id===activeIdx}" :key="idx">
+            <vcenter>
+              <span>{{sub.name}}</span>
+              <span v-if="sub.id==='1-2' && a_tips" class="aside-tip">{{a_tips}}</span>
+            </vcenter>
+          </li>
+        </ul>
+      </li>
+    </ul>
+  </div>
+</div>
+</template>
+
+<script>
+import vcenter from '@/components/vcenter'
+
+const aside = [{
+  name: '龛位管理',
+  id: '1',
+  icon: 'iconindex',
+  url: '/'
+},
+{
+  name: '方位管理',
+  id: '2',
+  icon: 'icondepartment',
+  url: '/position'
+},
+{
+  name: '訂單管理',
+  id: '3',
+  icon: 'iconuser',
+  url: '/order'
+}, {
+  name: '客戶管理',
+  id: '4',
+  icon: 'iconper',
+  url: '/client'
+}]
+export default {
+  name: 'm-aside',
+  components: {vcenter},
+  data () {
+    return {
+      aside,
+      a_tips: 0,
+      currentItem: ''
+    }
+  },
+  watch: {
+    '$route.name': function () {
+
+    }
+  },
+  computed: {
+    activeIdx: {
+      get: function () {
+        return this.$route.meta.index
+      },
+      set: function () {
+      }
+    },
+    token () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  mounted () {
+   
+  },
+  methods: {
+    clickNav (item) {
+      if (item.id) {
+        this.activeIdx = item.id
+        this.currentItem = item
+        window.localStorage.setItem('currentItem', JSON.stringify(item))
+        this.$router.push(item.url)
+      } else {
+        item.isShow = !item.isShow
+      }
+    },
+    navigate (item) {
+      this.activeIdx = item.id
+      this.currentItem = item
+      this.$router.push(item.url)
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.aside {
+  width: 100%;
+  .navigator_menu{
+    height: 56px;
+    span{
+      padding-left: 20px;
+      font-size: 12px;
+      color: #999;
+    }
+  }
+  .aside-ul{
+    color: #999;
+    .aside-li{
+      cursor: pointer;
+      font-size: 14px;
+      .aside-li-div{
+        padding: 0 20px;
+        height: 56px;
+        .fr{
+          float: right;
+          transition: transform 0.3s ease;
+        }
+        .rotate{
+          transform: rotate(180deg);
+        }
+        &:hover{
+          background-color: #f5f5f5;
+        }
+      }
+      .active{
+        background-color: #f5f5f5;
+        color: #333;
+        .iconfont{
+          color: #333;
+        }
+      }
+      .li-ul{
+        max-height: 200px;
+        transition: max-height 0.3s ease;
+        overflow: hidden;
+        li{
+          height: 56px;
+          padding: 0 47px;
+          .aside-tip{
+            background: #ff0000;
+            color: #fff;
+            border-radius: 10px;
+            display: inline-block;
+            width: 16px;
+            height: 16px;
+            text-align: center;
+            font-size: 12px;
+            line-height: 16px;
+            margin-left: 4px;
+          }
+          &:hover{
+            background-color: #f5f5f5;
+          }
+        }
+      }
+    }
+  }
+}
+
+</style>

+ 74 - 0
src/views/layout/header/index.vue

@@ -0,0 +1,74 @@
+<!--  -->
+<template>
+  <div class="con">
+    <div class="logo">
+      <img :src="require('@/assets/images/index_logo@2x.png')" alt="" />
+      <span>永念庭管理後台</span>
+    </div>
+    <div class="setting">
+      <vcenter>
+        <div class="i-float">
+          <div class="user">
+            {{ userInfo.username }}
+          </div>
+          <el-button
+            @click="back"
+           type="warning" size="mini" icon="el-icon-s-home"
+            >返回首頁</el-button
+          >
+          <el-button
+            @click="logout"
+            type="danger"
+            size="mini"
+            icon="el-icon-switch-button"
+            >退出登錄</el-button
+          >
+        </div>
+      </vcenter>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import vcenter from "@/components/vcenter";
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: { vcenter },
+  data() {
+    let tmp = {}
+    try {
+      tmp = { ...JSON.parse(localStorage.getItem("userInfo"))||'' }
+    } catch (error) {
+      tmp={}
+    }
+    // 这里存放数据
+    return {
+      userInfo: tmp
+    };
+  },
+  methods: {
+    back(){
+      window.location.href = '/platform-framework/';
+    },
+    logout() {
+      this.$confirm("注:您确定要安全退出本次登录吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        setTimeout(()=> {
+          localStorage.setItem("token", "");
+          localStorage.setItem("userInfo", "");
+          window.location.href = '/platform-framework/login.html';
+        }, 500);
+      });
+    },
+  },
+  // 监听属性 类似于data概念
+};
+</script>
+<style lang="less" scoped>
+@import "./style.less";
+</style>

+ 42 - 0
src/views/layout/header/style.less

@@ -0,0 +1,42 @@
+.con{
+  background: #2F4050;
+  // background: -webkit-gradient(linear, left top, left bottom, from(#4491fa), to(#5653e4));
+  height: 70px;
+  display: inline-block;
+  width: 100%;
+  color: #fff;
+  padding: 0 30px;
+  position: relative;
+  .logo{
+    height: 100%;
+    display: flex;
+    align-items: center;
+    img{
+      width: 50px;
+    }
+    >span{
+      font-size: 24px;
+      margin-left: 25px;
+    }
+  }
+  .setting{
+    height: 100%;
+    position: absolute;
+    right: 10px;
+    top: 0;
+    .i-float{
+      display: flex;
+      align-items: center;
+      .user{
+        margin-right: 20px;
+      }
+      div{
+        margin-left: 20px;
+        cursor: pointer;
+        .iconfont{
+          padding-right:5px; 
+        }
+      }
+    }
+  }
+}

+ 70 - 0
src/views/layout/index.vue

@@ -0,0 +1,70 @@
+<template>
+<div class="layout">
+  <iheader />
+  <div class="main">
+    <div class="aside-con">
+      <iaside />
+    </div>
+    <div class="r-view">
+      <crumb :title="$route.meta.text"/>
+      <router-view class="r-main" />
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+import header from '@/views/layout/header'
+import aside from '@/views/layout/aside'
+import crumb from '@/components/crumb'
+
+export default {
+  components: {
+    iheader: header,
+    iaside: aside,
+    crumb
+  },
+  data () {
+    return {
+
+    }
+  },
+  computed: {
+    userId () {
+      return window.localStorage.getItem('token')
+    }
+  },
+  mounted () {
+    // if (!this.userId) {
+    //   this.$router.push('/login')
+    // }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.main {
+  width: 100%;
+  height: calc(100% - 72px);
+  background: #f2f2f2;
+  .aside-con {
+    border-right: 1px solid #ebeef5;
+    background: #fff;
+    position: fixed;
+    height: 100%;
+    width: 235px;
+    z-index: 1;
+    box-shadow: 0 0 12px 0 rgba(0, 0, 0, .1);
+  }
+  .r-view {
+    height: 100%;
+    padding-left: 235px;
+    overflow: hidden;
+    .r-main{
+      padding: 10px;
+      height: calc(100% - 65px);
+      overflow: auto;
+    }
+  }
+}
+</style>

+ 219 - 0
src/views/login/index.vue

@@ -0,0 +1,219 @@
+<template>
+<div class="l-con">
+  <div class="l-top">
+    <h1>管理系统</h1>
+  </div>
+  <div class="l_center">
+    <vcenter>
+      <div class="lc-l">
+        <img :src="logoImg" alt="">
+        <div class="text_01">全自动精准建模,智能测距</div>
+        <div class="text_02">多功能编辑后台,让空间延伸表达</div>
+        <div class="text_03">简单、高效</div>
+      </div>
+      <div class="lc-r">
+        <div class="lc-title">
+          <span>后台登录</span>
+        </div>
+        <div class="lc-form">
+           <el-form label-position="top" :model="ruleForm2" status-icon :rules="rules2" ref="ruleForm2" label-width="100px" class="demo-ruleForm">
+              <el-form-item prop="username">
+                <el-input type="username" placeholder="账号" v-model="ruleForm2.username" auto-complete="off"></el-input>
+              </el-form-item>
+              <el-form-item prop="pass" id='no-bottom'>
+                <el-input type="password" placeholder="密码" @keyup.enter.native="submitForm('ruleForm2')" v-model="ruleForm2.pass" auto-complete="off"></el-input>
+              </el-form-item>
+              <el-form-item>
+                <el-checkbox  v-model="checked">记住密码(在公共场所电脑请勿勾选)</el-checkbox>
+              </el-form-item>
+            </el-form>
+            <div class="lc-btn">
+              <el-button type="primary" v-loading.fullscreen.lock="fullscreenLoading" @click="submitForm('ruleForm2')">登录</el-button>
+            </div>
+        </div>
+      </div>
+    </vcenter>
+  </div>
+  <div class="l_footer">
+
+  </div>
+</div>
+</template>
+
+<script>
+import vcenter from '@/components/vcenter'
+export default {
+  name: 'login',
+  components: {
+    vcenter
+  },
+  data () {
+    var validateUser = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error('用户名不能为空'))
+      } else if (value.length < 3) {
+        callback(new Error('用户名不合法'))
+      } else {
+        callback()
+      }
+    }
+
+    var validatePass = (rule, value, callback) => {
+      if (value === '') {
+        return callback(new Error('请输入密码'))
+      } else if (value.length < 4) {
+        callback(new Error('密码不能小于4位'))
+      } else {
+        callback()
+      }
+    }
+
+    return {
+      fullscreenLoading: false,
+      checked: true,
+      logoImg: require('@/assets/images/index_logo@2x.png'),
+      ruleForm2: {
+        username: '',
+        pass: ''
+      },
+      rules2: {
+        username: [{
+          validator: validateUser,
+          trigger: 'blur'
+        }],
+        pass: [{
+          validator: validatePass,
+          trigger: 'blur'
+        }]
+      }
+    }
+  },
+
+  mounted () {
+    this.ruleForm2.username = window.localStorage.getItem('zfb_username') || ''
+    this.ruleForm2.pass = window.localStorage.getItem('zfb_pass') || ''
+  },
+  methods: {
+    login () {
+      this.$http.post('/admin/login', {
+        userName: this.ruleForm2.username,
+        password: this.ruleForm2.pass
+      }).then(res => {
+        this.fullscreenLoading = true
+        if (res.status === 5001 || res.status === 5002) return
+        if (res.status === 2000) {
+          window.localStorage.setItem('token', String(res.data.token))
+          window.localStorage.setItem('menu', JSON.stringify(res.data.permission))
+          window.localStorage.setItem('roleType', res.data.role[0])
+
+          if (this.checked) {
+            window.localStorage.setItem('zfb_username', this.ruleForm2.username)
+            window.localStorage.setItem('zfb_pass', this.ruleForm2.pass)
+          } else {
+            window.localStorage.setItem('zfb_username', '')
+            window.localStorage.setItem('zfb_pass', '')
+          }
+          this.$router.push('/')
+        } else {
+          this.fullscreenLoading = false
+          this.$message({
+            type: "error",
+            message: res.msg,
+          });
+        }
+      })
+    },
+    submitForm (formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          this.login()
+        } else {
+          return false
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.l-con{
+  width: 100%;
+  .l-top{
+    color: #333;
+    text-align: center;
+    padding: 50px 0;
+  }
+  .l_center {
+    height: 600px;
+    width: 100%;
+    padding: 0 10% 0 20%;
+    background: url('~@/assets/images/img_login_banbg.jpg') center center no-repeat;
+    .lc-l{
+      display: inline-block;
+      color: #fff;
+      img{
+        width: 150px;
+      }
+
+      .text_01{
+        font-size: 60px;
+        font-weight: bold;
+        margin-top: 70px;
+      }
+      .text_02{
+        font-size: 40px;
+        margin: 30px 0;
+      }
+      .text_03{
+        font-size: 20px;
+      }
+
+    }
+    .lc-r{
+      float: right;
+      width: 345px;
+      height: 370px;
+      background-color: #fff;
+      padding: 28px 30px;
+      .lc-title{
+        text-align: center;
+        border-bottom: 2px solid #0175dc;
+        padding-bottom: 20px;
+        span{
+          color: #333;
+          font-size: 16px;
+        }
+      }
+      .lc-form{
+        margin-top: 40px;
+        .lc-btn{
+          width: 100%;
+          .el-button--primary{
+            width: 100%;
+          }
+        }
+      }
+    }
+  }
+  .l_footer{
+    font-size: 14px;
+    text-align: center;
+    margin-top: 45px;
+    line-height: 1.5;
+    .sub{
+      font-size: 12px;
+      color: #999;
+      margin-top: 10px;
+    }
+  }
+}
+
+@media screen and (max-width: 1400px) {
+    .l-con{
+      .l_center{
+        padding: 0 100px;
+      }
+    }
+}
+</style>

+ 697 - 0
src/views/niche/index.vue

@@ -0,0 +1,697 @@
+<template>
+  <div class="plate">
+    <div class="top-con" v-if="!dialogFormVisible">
+      <div class="top">
+        <el-input v-model="boxName" placeholder="請輸入龕位名稱"></el-input>
+        <el-input v-model="boxSn" placeholder="請輸入龕位編碼"></el-input>
+        <el-select
+          style="width: 90px"
+          v-model="saleStatus"
+          placeholder="請選擇龕位狀態"
+        >
+          <el-option label="全部" value></el-option>
+          <el-option
+            v-for="(item, i) in statusSelect"
+            :key="i"
+            :label="item.name"
+            :value="item.id"
+          ></el-option>
+        </el-select>
+        <el-button type="primary" @click="getList">查詢</el-button>
+        <el-button
+          @click="(dialogFormVisible = true), (editTitle = '新增')"
+          type="primary"
+          class="add"
+          >新增</el-button
+        >
+      </div>
+    </div>
+    <div class="body">
+      <template v-if="!dialogFormVisible">
+        <el-table height="65vh" :data="tableData" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+            :width="item.width"
+          >
+            <template slot-scope="scope">
+              <img
+                style="width: 80px; height: 80px; border-radius: 50%"
+                v-if="item.prop === 'ico'"
+                :src="scope.row[item.prop]"
+              />
+              <span v-html="scope.row[item.prop] || '-'" v-else></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope">
+              <span class="o-span" @click="show(scope.row, 'edit')">修改</span>
+              <span class="o-span del" @click="del(scope.row)">刪除</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-page">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="pageNum"
+            :page-size="size"
+            layout="sizes, prev, pager, next"
+            :page-sizes="[15, 50, 100, 500]"
+            @size-change="handleSizeChange"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </template>
+
+      <template v-else>
+        <div class="add-con">
+          <el-form :model="form" :rules="rules" ref="niche">
+            <el-form-item
+              key="boxSn"
+              label="龕位編碼"
+              prop="boxSn"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                maxlength="20"
+                show-word-limit
+                v-model="form.boxSn"
+                placeholder="請輸入龕位編碼"
+                autocomplete="off"
+              ></el-input>
+            </el-form-item>
+            <el-form-item
+              label="龕位名稱"
+              prop="boxName"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                maxlength="10"
+                show-word-limit
+                v-model="form.boxName"
+                placeholder="請輸入龕位名稱"
+                autocomplete="off"
+              ></el-input>
+            </el-form-item>
+            <el-form-item
+              label="方位名稱"
+              prop="blockId"
+              :label-width="formLabelWidth"
+            >
+              <el-select v-model="form.blockId" placeholder="請選擇活動區域">
+                <el-option
+                  v-for="(item, i) in posiList"
+                  :key="i"
+                  :label="item.positionName"
+                  :value="item.positionId"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item
+              label="所屬墻面"
+              prop="wallId"
+              :label-width="formLabelWidth"
+            >
+              <el-select v-model="form.wallId" placeholder="請選擇活動區域">
+                <el-option
+                  v-for="(item, i) in wallList"
+                  :key="i"
+                  :label="item.positionName"
+                  :value="item.positionId"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+
+            <el-form-item
+              label="墻面橫列"
+              prop="xposition"
+              :label-width="formLabelWidth"
+            >
+              <el-select v-model="form.xposition" placeholder="請選擇活動區域">
+                <el-option
+                  v-for="(item, i) in row_column.row"
+                  :key="i"
+                  :label="item"
+                  :value="item"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+            
+            <el-form-item
+              label="墻面縱列"
+              prop="yposition"
+              :label-width="formLabelWidth"
+            >
+              <el-select v-model="form.yposition" placeholder="請選擇活動區域">
+                <el-option
+                  v-for="(item, i) in row_column.column"
+                  :key="i"
+                  :label="item"
+                  :value="item"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          
+
+            <el-form-item
+              label="龕位價格"
+              prop="price"
+              :label-width="formLabelWidth"
+            >
+              <el-input
+                oninput="value = (value.match(/^\d*(\.?\d{0,2})/g)[0])"
+                v-model="form.price"
+                placeholder="請輸入龕位價格"
+                autocomplete="off"
+              >
+              <span style="font-size:10px" slot="suffix">
+                HK$
+              </span>
+              </el-input>
+            </el-form-item>
+            <el-form-item
+              label="管理費"
+              prop="managementFee"
+              :label-width="formLabelWidth"
+            >
+              <div style="display:flex">
+                <el-select v-model="form.feeType" placeholder="請選擇收費類型" style="margin-right:10px">
+                  <el-option label="按月收費" :value="1"></el-option>
+                  <el-option label="按年收費" :value="2"></el-option>
+                </el-select>
+                <el-input
+                  oninput="value = (value.match(/^\d*(\.?\d{0,2})/g)[0])"
+                  v-model="form.managementFee"
+                  placeholder="請輸入管理費"
+                  autocomplete="off"
+                >
+                <span style="font-size:10px" slot="suffix">
+                  HK$
+                </span>
+                </el-input>
+              </div>
+            </el-form-item>
+            <el-form-item
+              label="龕位樣式"
+              prop="boxStylePic"
+              :label-width="formLabelWidth"
+            >
+              <el-upload
+                class="upload-demo"
+                :action="uploadUrl"
+                :show-file-list="false"
+                :on-success="handleStyleSuccess"
+                :before-upload="$beforeUpload"
+                :on-error="handleError"
+              >
+              <el-input
+                  v-model="form.boxStylePic"
+                  v-show="false"
+                >
+                </el-input>
+                <el-button size="small" type="primary">點擊上傳</el-button>
+                <div slot="tip" class="el-upload__tip">
+                  <div v-if="form.boxStylePic" class="thumb">
+                    <img class="upl-img" :src="form.boxStylePic" alt="" />
+                    <i
+                      class="el-icon-delete"
+                      @click="form.boxStylePic = ''"
+                    ></i>
+                  </div>
+                  <p>1、支持png、jpg和gif圖片格式;</p>
+                  <p>2、最大可上傳5M的圖片。</p>
+                  <p>3、推薦大小:25 * 25 像素</p>
+                </div>
+              </el-upload>
+            </el-form-item>
+            <el-form-item
+              label="龕位方位"
+              :label-width="formLabelWidth"
+            >
+            <el-input
+                  v-model="form.boxPositionPic"
+                  v-show="false"
+                >
+                </el-input>
+              <el-upload
+                class="upload-demo"
+                :action="uploadUrl"
+                :show-file-list="false"
+                :on-success="handlePositionSuccess"
+                :before-upload="$beforeUpload"
+                :on-error="handleError"
+
+              >
+                <el-button size="small" type="primary">點擊上傳</el-button>
+                <div slot="tip" class="el-upload__tip">
+                  <div v-if="form.boxPositionPic" class="thumb">
+                    <img class="upl-img" :src="form.boxPositionPic" alt="" />
+                    <i
+                      class="el-icon-delete"
+                      @click="form.boxPositionPic = ''"
+                    ></i>
+                  </div>
+                  <p>1、支持png、jpg和gif圖片格式;</p>
+                  <p>2、最大可上傳5M的圖片。</p>
+                </div>
+              </el-upload>
+            </el-form-item>
+
+            <el-form-item label="出售狀態" :label-width="formLabelWidth">
+              <el-radio-group v-model="form.saleStatus">
+                <el-radio :label="item.id" v-for="(item,i) in statusSelect" :key="i">{{item.name}}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+
+          </el-form>
+          <div class="btn">
+             <el-button type="primary" @click="submitForm">保 存</el-button>
+              <el-button @click="cancel">取 消</el-button>
+          </div>
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
+<script>
+let XYposition = "";
+let BOXSN = "";
+
+export default {
+  data() {
+    let data = [
+      {
+        prop: "boxSn",
+        label: "龕位編碼",
+      },
+      {
+        prop: "boxName",
+        label: "龕位名稱",
+      },
+      {
+        prop: "blockName",
+        label: "方位名稱",
+      },
+      {
+        prop: "wallName",
+        label: "所屬墻面",
+      },
+      {
+        prop: "position",
+        label: "所屬位置",
+      },
+      {
+        prop: "price",
+        label: "龕位價格(HK$)",
+        width: 180
+      },
+      {
+        prop: "managementFee",
+        label: "龕位管理費(HK$)",
+        width: 180
+      },
+      {
+        prop: "updateTime",
+        label: "修改日期",
+      },
+      {
+        prop: "statusStr",
+        label: "預約狀態",
+      },
+      {
+        prop: "saleStatusStr",
+        label: "出售狀態",
+      },
+      // {
+      //   prop: "isShowStr",
+      //   label: "顯示狀態",
+      // },
+    ];
+
+    var checkBoxSn = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error("請填寫龕位編碼"));
+      } else if (BOXSN == value) {
+        return callback();
+      } else {
+        setTimeout(async () => {
+          await this.$http
+            .get(`/box/checkBoxSn?boxSn=${value}`)
+            .then((res) => {
+              if (res.code === 0) {
+                res.data.used
+                  ? callback(new Error("龕位編碼已存在"))
+                  : callback();
+              } else {
+                this.$message({
+                  type: "error",
+                  message: res.msg,
+                });
+              }
+            });
+        });
+      }
+    };
+
+      var checkXY = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error(rule.msg));
+      }
+      else if (XYposition.x == this.form.xposition&&XYposition.y == this.form.yposition) {
+        return callback();
+      }
+      else if(this.form.xposition&&this.form.yposition){
+        setTimeout(async () => {
+          await this.$http
+            .get(`/box/checkBoxXYpos?wallId=${this.form.wallId}&xPosition=${this.form.xposition}&yPosition=${this.form.yposition}`)
+            .then((res) => {
+              if (res.code === 0) {
+                res.data.used
+                  ? callback(new Error(rule.t))
+                  : callback();
+              } else {
+                this.$message({
+                  type: "error",
+                  message: res.msg,
+                });
+              }
+            });
+        });
+      }
+      else {
+        callback();
+      }
+    };
+
+    let rules = {
+      boxSn: [{ required: true, validator: checkBoxSn, trigger: "blur" }],
+      boxName: [{ required: true, message: "請填寫龕位名稱", trigger: "blur" }],
+      blockId: [{ required: true, message: "請選擇所屬方位", trigger: "blur" }],
+      wallId: [{ required: true, message: "請選擇所屬墻面", trigger: "blur" }],
+      xposition: [{ required: true, validator: checkXY, t:'已存在龕位,請重新選擇', msg: "請選擇墻面橫列", trigger: "change" }],
+      yposition: [{ required: true, validator: checkXY, t:'已存在龕位,請重新選擇', msg: "請選擇墻面縱列", trigger: "change" }],
+      price: [{ required: true, message: "請填寫龕位價格", trigger: "blur" }],
+      managementFee: [
+        { required: true, message: "請填寫管理費價格", trigger: "blur" },
+      ],
+      boxStylePic: [
+        { required: true, message: "請上傳龕位樣式", trigger: "change" },
+      ]
+    };
+    return {
+      status: "",
+      statusSelect:[{
+        id:3,
+        name:'現貨'
+      },{
+        id:4,
+        name:'預售'
+      },{
+        id:5,
+        name:'轉售'
+      },{
+        id:2,
+        name:'已售'
+      },{
+        id:1,
+        name:'已預訂'
+      }],
+      data,
+      rules,
+      tableData: [],
+      fileList: [],
+      isChangeWall: true,
+      isChangeRC:true,
+      dialogFormVisible: false,
+      editTitle: "編輯",
+      boxName: "",
+      boxSn: "",
+      saleStatus: "",
+      form: {
+        boxSn: "",
+        boxName: "",
+        blockId: "",
+        blockName: "",
+        wallId: "",
+        wallName: "",
+        saleStatus: 3,
+        status: 0,
+        isShow: 1,
+        managementFee: "",
+        feeType:2,
+        price: "",
+        boxStylePic: "",
+        boxPositionPic: "",
+        yposition:'',
+        xposition:''
+      },
+      uploadUrl: this.$serverName + "sys/oss/upload",
+      formLabelWidth: "120px",
+      pageNum: 1,
+      size: 15,
+      total: 0,
+      posiList: [],
+    };
+  },
+
+  computed: {
+    wallList() {
+      let tmp = this.posiList.find((item) => {
+        return this.form.blockId == item.positionId;
+      });
+      return tmp ? tmp.childList : [];
+    },
+    row_column(){
+      let tmp = this.wallList.find((item) => {
+        return this.form.wallId == item.positionId;
+      });
+      return  {
+        row:tmp?tmp.rowNum:0,
+        column:tmp?tmp.columnNum:0
+      }
+    }
+  },
+  watch: {
+    "form.blockId": function () {
+      if (this.isChangeWall) {
+        this.form.wallId = "";
+      }
+      this.isChangeWall = true;
+    },
+    "form.wallId": function () {
+      if (this.isChangeRC) {
+        this.form.xposition = "";
+        this.form.yposition = "";
+      }
+      this.isChangeRC = true;
+    },
+    pageNum(){
+        this.getList()
+    },
+    size(){
+      this.getList()
+    }
+  },
+  mounted() {
+    this.getPosiList();
+    this.getList();
+  },
+  methods: {
+    handleSizeChange (val) {
+      this.size = val
+    },
+    handleCurrentChange (val) {
+      this.pageNum = val
+    },
+    reset(){
+      this.form = {
+        boxSn: "",
+        boxName: "",
+        blockId: "",
+        blockName: "",
+        wallId: "",
+        wallName: "",
+        saleStatus: 3,
+        status: 0,
+        isShow: 1,
+        managementFee: "",
+        feeType:2,
+        price: "",
+        boxStylePic: "",
+        boxPositionPic: "",
+        yposition:'',
+        xposition:''
+      }
+    },
+    cancel(){
+      this.dialogFormVisible = false
+      this.reset()
+    },
+    
+    handleRemove(file, fileList) {
+      console.log(file, fileList);
+    },
+    handlePreview(file) {
+      console.log(file);
+    },
+    handleStyleSuccess(file) {
+      this.form.boxStylePic = file.link;
+      this.$__loading.close()
+    },
+
+    handlePositionSuccess(file) {
+      this.form.boxPositionPic = file.link;
+      this.$__loading.close()
+    },
+
+    del(item) {
+      this.$confirm("此操作將刪除該數據, 是否繼續?", "提示", {
+        confirmButtonText: "確定",
+        cancelButtonText: "取消",
+        type: "warning"
+      })
+        .then(() => {
+          this.$http
+            .post(`/box/batchDelete?ids=${item.boxId}`, {})
+            .then((res) => {
+              if (res.code === 0) {
+                this.$alert("刪除成功", "提示", {
+                  confirmButtonText: "確定",
+                  callback: () => {
+                    this.reset()
+                    this.getList();
+                  },
+                });
+              } else {
+                this.$message({
+                  type: "error",
+                  message: res.msg,
+                });
+              }
+            });
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消刪除",
+          });
+        });
+    },
+
+    async show(item) {
+      this.isChangeWall = false;
+      this.isChangeRC = false;
+
+      let result = await this.$http.get(`box/getDetail`, {
+        params: {
+          id: item.boxId,
+        },
+      });
+      this.form = result.data;
+      this.dialogFormVisible = true;
+      this.editTitle = "編輯";
+      XYposition = {
+        x:this.form.xposition,
+        y:this.form.yposition
+      },
+      BOXSN = this.form.boxSn
+    },
+
+    async save() {
+      let url = this.editTitle != "編輯" ? "/box/add" : "/box/update";
+      let tmp =  this.wallList.find(item=>item.positionId==this.form.wallId)
+      let tmp2 =  this.posiList.find(item=>item.positionId==this.form.blockId)
+
+      this.form.wallName = tmp.positionName
+      this.form.blockName = tmp2.positionName
+
+
+      let result = await this.$http.post(url, {
+        ...this.form,
+      });
+      if (result.code == 0) {
+        this.$alert("操作成功", "提示", {
+          confirmButtonText: "確定",
+          callback: () => {
+            this.reset()
+            this.dialogFormVisible = false;
+            this.getList();
+          },
+        });
+      }else{
+        this.$message({
+          type: "error",
+          message: result.msg,
+        });
+      }
+    },
+    submitForm() {
+      this.$refs.niche.validate((valid) => {
+        if (valid) {
+          this.save();
+        } else {
+          return false;
+        }
+      });
+    },
+    async getPosiList() {
+      let result = await this.$http.get(`box/position/getList`, {
+        params: {
+          name: "",
+          pageNum: 1,
+          pageSize: 1000,
+        },
+      });
+      this.posiList = result.data.list;
+    },
+    async getList() {
+      let result = await this.$http.get(`/box/getOrQuery`, {
+        params: {
+          boxName: this.boxName,
+          boxSn: this.boxSn,
+          saleStatus: this.saleStatus,
+          pageNum: this.pageNum,
+          pageSize: this.size,
+        },
+      });
+
+      this.tableData = result.data.list.map((item) => {
+        item["isShowStr"] = item["isShow"] ? "顯示" : "不顯示";
+        item["saleStatusStr"] = this.$statusStr[item["saleStatus"]];
+        item["statusStr"] = this.$statusStr[item["status"]];
+
+        item["position"] = `橫列:第${item['xposition']||' - '}橫<br/>縱列:第${item['yposition']||' - '}列`;
+        return item;
+      });
+      this.total= result.data.totalNum
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.thumb {
+  display: block;
+  margin: 10px 0;
+  position: relative;
+  width: 120px;
+  > img {
+    max-width: 120px;
+  }
+  &:hover {
+    .el-icon-delete {
+      display: block;
+    }
+  }
+  .el-icon-delete {
+    position: absolute;
+    right: 5px;
+    top: 5px;
+    font-size: 20px;
+    cursor: pointer;
+    display: none;
+  }
+}
+</style>

+ 285 - 0
src/views/order/index.vue

@@ -0,0 +1,285 @@
+<template>
+  <div class="plate">
+    <div class="top-con">
+      <div class="top">
+        <el-input v-model="boxName" placeholder="请输入龛位名称"></el-input>
+        <el-input v-model="boxSn" placeholder="请输入龛位编码"></el-input>
+        <el-select style="width:90px;" v-model='saleStatus' placeholder="请选择龛位状态">
+          <el-option label="全部" value></el-option>
+          <el-option label="待處理" value='0'></el-option>
+          <el-option label="已處理" value='1'></el-option>
+
+        </el-select>
+        <el-button type="primary" @click="getList">查询</el-button>
+      </div>
+    </div>
+    <div class="body">
+      <el-table height="65vh"
+      :data="tableData" style="width: 100%">
+        <el-table-column
+          v-for="(item, idx) in data"
+          :key="idx"
+          :prop="item.prop"
+          :label="item.label"
+          :width="item.width"
+        >
+          <template slot-scope="scope">
+            <img
+              style="width:80px;height:80px;border-radius:50%;"
+              v-if="item.prop === 'ico'"
+              :src="scope.row[item.prop]"
+              alt
+            />
+            <span v-else>{{ scope.row[item.prop] || "-" }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+              <span class="o-span" @click="show(scope.row, 'edit')">編輯訂單狀態</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="e-page">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="pageNum"
+            :page-size="size"
+            layout="sizes, prev, pager, next"
+            :page-sizes="[15, 50, 100, 500]"
+            @size-change="handleSizeChange"
+            :total="total"
+          ></el-pagination>
+        </div>
+    </div>
+    <el-dialog :close-on-click-modal="false" :title="editTitle" :visible.sync="dialogFormVisible" width="40%">
+      <div>
+        <el-form :model="form" :rules="rules" ref="order">
+          <el-form-item porp="isHandle" label="處理狀態" :label-width="formLabelWidth">
+            <el-radio-group v-model="form.isHandle">
+              <el-radio :label="0">待處理</el-radio>
+              <el-radio :label="1">已處理</el-radio>
+            </el-radio-group>
+          </el-form-item>
+
+           <el-form-item  porp="handleTime" label="處理時間" :label-width="formLabelWidth">
+             <el-date-picker
+              v-model="form.handleTime"
+              type="datetime"
+              :value-format="format"
+              placeholder="选择日期时间">
+            </el-date-picker>
+          </el-form-item>
+        </el-form>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">保 存</el-button>
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    let data = [
+      {
+        prop: "orderSn",
+        label: "訂單編號",
+        width:200
+      },
+      {
+        prop: "positionName",
+        label: "所屬區域",
+      },
+      {
+        prop: "wallName",
+        label: "所屬墻面",
+      },
+      {
+        prop: "boxSn",
+        label: "龕位編碼",
+      },
+      {
+        prop: "boxName",
+        label: "龕位名稱",
+      },
+      {
+        prop: "boxPrice",
+        label: "龕位金額(HK$)",
+      },
+      {
+        prop: "boxUserName",
+        label: "客戶名稱",
+      },
+      {
+        prop: "boxUserEmail",
+        label: "客戶郵箱",
+      },
+      {
+        prop: "reservationDate",
+        label: "預定日期",
+      },
+      {
+        prop: "handleTime",
+        label: "處理時間",
+      },
+      {
+        prop: "isHandleStr",
+        label: "處理状态",
+      },
+      //  {
+      //   prop: "statusStr",
+      //   label: "預約狀態",
+      // },
+    ];
+
+    let rules= {
+          isHandle: [
+            { required: true, message: '请選擇處理狀態', trigger: 'blur' }
+          ],
+          handleTime: [
+            { required: true, message: '请填写處理時間', trigger: 'blur' }
+          ]
+        }
+    let FORMAT = 'yyyy-MM-dd hh:mm:ss'
+    console.log((new Date()).Format(FORMAT));
+    return {
+      data,
+      rules,
+      tableData: [],
+      dialogFormVisible: false,
+      editTitle: "訂單處理",
+      boxName:'',
+      boxSn:'',
+      saleStatus:'',
+      format:FORMAT,
+      form: {
+        isHandle: "",
+        handleTime: '',
+      },
+      formLabelWidth: "120px",
+      pageNum: 1,
+      size: 15,
+      total: 0
+    };
+  },
+
+  computed:{
+      wallList(){
+        let tmp =  this.posiList.find(item=>{
+         return this.form.blockId == item.positionId
+        })
+        return tmp?tmp.childList:[]
+      }
+  },
+  watch:{
+    'form.blockId':function () {
+        this.form.wallId = ''
+    },
+    pageNum(){
+        this.getList()
+    },
+    size(){
+      this.getList()
+    }
+  },
+  mounted(){
+    this.getList()
+  },
+  methods:{
+    handleSizeChange (val) {
+      this.size = val
+    },
+    handleCurrentChange (val) {
+      this.pageNum = val
+    },
+    async show(item){
+       let result = await this.$http.get(`box/order/getDetail`,
+        {
+          params:{
+            id:item.orderId
+          }
+        })
+      this.form = result.data
+      this.form.handleTime =  this.form.handleTime|| (new Date()).Format(this.format)
+      this.dialogFormVisible = true
+      this.editTitle = '訂單處理'
+    },
+
+    async save(){
+      let url = '/box/order/update'
+      let result = await this.$http.post(url,{
+          ...this.form
+      })
+      if (result.code == 0) {
+      this.$alert('操作成功', '提示', {
+        confirmButtonText: '确定',
+        callback: () => {
+          this.form={
+            isHandle: "",
+            updateTime:""
+          }
+          this.dialogFormVisible = false
+          this.getList()
+        }
+      })
+      }
+    },
+    submitForm() {
+        this.$refs.order.validate((valid) => {
+          if (valid) {
+            this.save()
+          } else {
+            return false;
+          }
+        });
+      },
+   
+    async getList(){
+      let result = await this.$http.get(`/box/order/getOrQuery`,{
+        params:{
+          boxName:this.boxName,
+          boxSn:this.boxSn,
+          status:this.saleStatus,
+          pageNum:this.pageNum,
+          pageSize:this.size
+        }
+      })
+
+      this.tableData = result.data.list.map(item=>{
+        item['isShowStr'] = item['isShow']?'顯示':'不顯示'
+        item["statusStr"] = this.$statusStr[item["status"]];
+        item['isHandleStr'] = item['status']==2?'客戶已取消':(item['isHandle']?'已處理':'待處理')
+        return item
+      })
+      this.total = result.data.totalNum
+
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.thumb{
+  display: block;
+  margin: 10px 0;
+  position: relative;
+  width: 120px;
+  >img{
+    max-width: 120px;
+  }
+  &:hover{
+    .el-icon-delete{
+      display: block;
+    }
+  }
+  .el-icon-delete{
+    position: absolute;
+    right: 5px;
+    top: 5px;
+    font-size: 20px;
+    cursor: pointer;
+    display: none;
+  }
+}
+</style>

+ 546 - 0
src/views/position/index.vue

@@ -0,0 +1,546 @@
+<template>
+  <div class="plate">
+    <div class="top-con" v-if="!dialogFormVisible">
+      <div class="top">
+        <el-input v-model="name" placeholder="请输入一級方位名称"></el-input>
+        <el-button @click="getList" type="primary">查询</el-button>
+        <el-button
+          @click="(dialogFormVisible = true), (editTitle = '新增')"
+          type="primary"
+          class="add"
+          >新增</el-button
+        >
+      </div>
+    </div>
+    <div class="body">
+      <template v-if="!dialogFormVisible">
+        <el-table
+          :data="tableData"
+          row-key="positionId"
+          :tree-props="{ children: 'childList' }"
+          style="width: 100%"
+        >
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+          >
+            <template slot-scope="scope">
+              <img
+                style="width: 80px; height: 80px; border-radius: 50%"
+                v-if="item.prop === 'ico'"
+                :src="scope.row[item.prop]"
+                alt
+              />
+              <span v-else>{{ scope.row[item.prop] || "-" }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope">
+              <span class="o-span" @click="show(scope.row, 'edit')">修改</span>
+              <span class="o-span del" @click="del(scope.row)">刪除</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-page">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="pageNum"
+            :page-size="size"
+            layout="sizes, prev, pager, next"
+            :page-sizes="[15, 50, 100, 500]"
+            @size-change="handleSizeChange"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </template>
+      <template v-else>
+        <div class="add-con">
+          <el-form :model="form" :rules="rules" ref="position">
+            <el-form-item
+              label="級別"
+              prop="level"
+              key="level"
+              :label-width="formLabelWidth"
+            >
+              <el-radio-group
+                :disabled="editTitle == '編輯'"
+                v-model="form.level"
+              >
+                <el-radio :label="1">一級</el-radio>
+                <el-radio :label="2">二級</el-radio>
+              </el-radio-group>
+            </el-form-item>
+
+            <template v-if="form.level == 1">
+              <el-form-item
+                label="方位名稱"
+                prop="positionName"
+                key="positionName1"
+                :label-width="formLabelWidth"
+              >
+                <el-input
+                  maxlength="10"
+                  show-word-limit
+                  v-model="form.positionName"
+                  placeholder="请输入方位名稱"
+                  autocomplete="off"
+                ></el-input>
+              </el-form-item>
+            </template>
+
+            <template v-if="form.level == 2">
+              <el-form-item
+                prop="parentId"
+                key="parentId"
+                label="一級方位"
+                :label-width="formLabelWidth"
+              >
+                <el-select v-model="form.parentId" placeholder="请选择一級方位">
+                  <el-option
+                    v-for="(item, i) in tableData"
+                    :key="i"
+                    :label="item.positionName"
+                    :value="item.positionId"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+
+              <el-form-item
+                label="所属墙面"
+                key="positionName2"
+                prop="positionName"
+                :label-width="formLabelWidth"
+              >
+                <el-input
+                  maxlength="10"
+                  show-word-limit
+                  v-model="form.positionName"
+                  placeholder="请输入所属墙面名稱"
+                  autocomplete="off"
+                ></el-input>
+              </el-form-item>
+
+              <el-form-item
+                prop="hotId"
+                key="hotId"
+                label="选择绑定热点"
+                :label-width="formLabelWidth"
+              >
+                <el-select v-model="form.hotId" placeholder="选择可绑定的热点">
+                  <el-option
+                    v-for="(item, i) in hotspotList"
+                    :key="i"
+                    :label="item.label"
+                    :value="item.sid"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+
+              <el-form-item
+                label="熱點圖片"
+                key="stylePic"
+                prop="stylePic"
+                :label-width="formLabelWidth"
+              >
+                <el-input v-model="form.stylePic" v-show="false"> </el-input>
+                <el-upload
+                  class="upload-demo"
+                  :action="uploadUrl"
+                  :show-file-list="false"
+                  :on-success="handleStyleSuccess"
+                  :before-upload="$beforeUpload"
+                >
+                  <el-button size="small" type="primary">点击上传</el-button>
+                </el-upload>
+                <div class="el-upload__tip">
+                  <div v-if="form.stylePic" class="thumb">
+                    <img class="upl-img" :src="form.stylePic" alt="" />
+                    <i class="el-icon-delete" @click="form.stylePic = ''"></i>
+                  </div>
+                  <p>1、支持png、jpg和gif图片格式;</p>
+                  <p>2、最大可上传5M的图片。</p>
+                  <p>3、推薦大小:25*25像素。</p>
+                </div>
+              </el-form-item>
+
+              <el-form-item
+                label="熱點顯示價格"
+                key="lowestPrice"
+                prop="lowestPrice"
+                :label-width="formLabelWidth"
+              >
+                <el-input
+                  v-model="form.lowestPrice"
+                  oninput="value = (value.match(/^\d*(\.?\d{0,2})/g)[0])"
+                  placeholder="请填写熱點顯示的價格"
+                  autocomplete="off"
+                >
+                  <span style="font-size: 10px" slot="suffix"> HK$ </span>
+                </el-input>
+              </el-form-item>
+
+              <el-form-item
+                prop="rowNum"
+                key="rowNum"
+                label="墙面橫數"
+                :label-width="formLabelWidth"
+              >
+                <el-input
+                  v-model="form.rowNum"
+                  placeholder="请输入墙面龕位橫數"
+                  autocomplete="off"
+                ></el-input>
+              </el-form-item>
+
+              <el-form-item
+                label="墙面縱數"
+                key="columnNum"
+                prop="columnNum"
+                :label-width="formLabelWidth"
+              >
+                <el-input
+                  v-model="form.columnNum"
+                  placeholder="请输入墙面龕位縱數"
+                  autocomplete="off"
+                ></el-input>
+              </el-form-item>
+            </template>
+
+            <el-form-item label="排序" :label-width="formLabelWidth">
+              <el-input
+                v-model="form.sort"
+                placeholder="请输入排序"
+                autocomplete="off"
+              ></el-input>
+            </el-form-item>
+
+            <!-- <el-form-item label="显示状态" :label-width="formLabelWidth">
+              <el-radio-group v-model="form.isShow">
+                <el-radio :label="1">顯示</el-radio>
+                <el-radio :label="0">不顯示</el-radio>
+              </el-radio-group>
+            </el-form-item> -->
+          </el-form>
+          <div class="btn">
+            <el-button type="primary" @click="submitForm">保 存</el-button>
+            <el-button @click="cancel">取 消</el-button>
+          </div>
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
+<script>
+const senceCode = "tSrGwEa0n";
+
+let cacheHotId = "";
+
+let rules1 = "";
+let rules2 = "";
+export default {
+  data() {
+    var checkHotId = (rule, value, callback) => {
+      console.log(cacheHotId, value);
+      if (!value) {
+        return callback(new Error("請選擇熱點"));
+      } else if (cacheHotId == value) {
+        return callback();
+      } else {
+        setTimeout(async () => {
+          await this.$http
+            .get(`/box/position/checkHot?hotId=${value}`)
+            .then((res) => {
+              if (res.code === 0) {
+                res.data.bindFlag
+                  ? callback(new Error("熱點已被綁定"))
+                  : callback();
+              } else {
+                this.$message({
+                  type: "error",
+                  message: res.msg,
+                });
+              }
+            });
+        });
+      }
+    };
+
+    rules1 = {
+      positionName: [
+        { required: true, message: "请填写方位名稱", trigger: "blur" },
+      ],
+      level: [{ required: true, message: "请選擇級別", trigger: "blur" }],
+    };
+    rules2 = {
+      positionName: [
+        { required: true, message: "请填写所屬墻面", trigger: "blur" },
+      ],
+      level: [{ required: true, message: "请選擇級別", trigger: "blur" }],
+      parentId: [
+        { required: true, message: "请選擇一級方位", trigger: "change" },
+      ],
+      rowNum: [
+        { required: true, message: "请填写墙面橫數", trigger: "blur" },
+      ],
+      columnNum: [
+        { required: true, message: "请選擇墙面縱數", trigger: "blur" },
+      ],
+      lowestPrice: [
+        { required: true, message: "请填写熱點顯示價格", trigger: "blur" },
+      ],
+      hotId: [{ required: true, validator: checkHotId, trigger: "change" }],
+      stylePic: [
+        { required: true, message: "请上傳熱點圖片", trigger: "change" },
+      ],
+    };
+    let data = [
+      {
+        prop: "levelStr",
+        label: "級別",
+      },
+      {
+        prop: "positionName",
+        label: "方位名稱",
+      },
+      {
+        prop: "wallName",
+        label: "所屬墻面",
+      },
+      {
+        prop: "rowNum",
+        label: "橫數",
+      },
+      {
+        prop: "columnNum",
+        label: "縱數",
+      },
+      {
+        prop: "updateTime",
+        label: "修改日期",
+      },
+      // {
+      //   prop: "isShowStr",
+      //   label: "顯示狀態",
+      // },
+    ];
+
+    return {
+      data,
+      name: "",
+      tableData: [],
+      uploadUrl: this.$serverName + "sys/oss/upload",
+      fileList: [],
+      hotspotList: [],
+      dialogFormVisible: false,
+      editTitle: "編輯",
+      form: {
+        positionName: "",
+        wallName: "",
+        level: 1,
+        lowestPrice: "",
+        stylePic: "",
+        sort: "",
+        parentId: "",
+        hotId: "",
+        hotTag: "",
+        rowNum: "",
+        columnNum: "",
+        isShow: 1,
+      },
+      pageNum: 1,
+      size: 15,
+      total: 0,
+      formLabelWidth: "120px",
+      bindFlag: 0,
+    };
+  },
+  mounted() {
+    this.getList();
+    this.getHotSpotList();
+  },
+  computed: {
+    rules() {
+      return this.form.level == 2 ? rules2 : rules1;
+    },
+  },
+  watch: {
+    pageNum() {
+      this.getList();
+    },
+    size() {
+      this.getList();
+    },
+    "form.hotId": function (newVal) {
+      if (newVal) {
+        this.$http.get(`/box/position/checkHot?hotId=${newVal}`).then((res) => {
+          if (res.code === 0) {
+            this.bindFlag = res.data.bindFlag;
+          } else {
+            this.$message({
+              type: "error",
+              message: res.msg,
+            });
+          }
+        });
+      }
+    },
+  },
+  methods: {
+    handleSizeChange(val) {
+      this.size = val;
+    },
+    handleCurrentChange(val) {
+      this.pageNum = val;
+    },
+    reset() {
+      this.form = {
+        positionName: "",
+        wallName: "",
+        level: 1,
+        lowestPrice: "",
+        stylePic: "",
+        sort: "",
+        parentId: "",
+        hotId: "",
+        hotTag: "",
+        rowNum: "",
+        columnNum: "",
+        isShow: 1,
+      };
+    },
+    cancel() {
+      this.dialogFormVisible = false;
+      this.reset();
+    },
+    handleStyleSuccess(file) {
+      this.form.stylePic = file.link;
+      this.$__loading.close();
+    },
+
+    async save() {
+      let url =
+        this.editTitle != "編輯" ? `/box/position/add` : "/box/position/update";
+      let tmp = this.hotspotList.find((item) => item.sid == this.form.hotId);
+      // if (this.form.level == 2) {
+      //   let tmp2 =  this.tableData.find(item=>item.positionId==this.form.parentId)
+      //   this.form.positionName = tmp2.positionName
+      // }
+      this.form.hotTag = tmp ? tmp.label : "";
+      let result = await this.$http.post(url, {
+        ...this.form,
+      });
+      if (result.code == 0) {
+        this.$alert("操作成功", "提示", {
+          confirmButtonText: "确定",
+          callback: () => {
+            this.reset();
+            this.dialogFormVisible = false;
+            this.getList();
+          },
+        });
+      } else {
+        this.$message({
+          type: "error",
+          message: result.msg,
+        });
+      }
+    },
+    del(item) {
+      this.$confirm("此操作将删除该数据, 是否继续?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.$http
+            .post(`/box/position/deleteBatch?ids=${item.positionId}`, {})
+            .then((res) => {
+              if (res.code === 0) {
+                this.$alert("删除成功", "提示", {
+                  confirmButtonText: "确定",
+                  callback: () => {
+                    this.getList();
+                  },
+                });
+              } else {
+                this.$message({
+                  type: "error",
+                  message: res.msg,
+                });
+              }
+            });
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消删除",
+          });
+        });
+    },
+    async show(item) {
+      let result = await this.$http.get(`/box/position/getDetail`, {
+        params: {
+          positionId: item.positionId,
+        },
+      });
+      this.form = result.data;
+      this.dialogFormVisible = true;
+      cacheHotId = this.form.hotId;
+      this.editTitle = "編輯";
+    },
+
+    submitForm() {
+      this.$refs.position.validate((valid) => {
+        if (valid) {
+          this.save();
+        } else {
+          return false;
+        }
+      });
+    },
+    async getList() {
+      let result = await this.$http.get(`box/position/getList`, {
+        params: {
+          name: this.name,
+          pageNum: this.pageNum,
+          pageSize: this.size,
+        },
+      });
+
+      this.tableData = result.data.list.map((item) => {
+        item["levelStr"] = "L" + item["level"];
+        item["isShowStr"] = item["isShow"] ? "顯示" : "不顯示";
+        item["childList"].forEach((i) => {
+          i["levelStr"] = "L" + i["level"];
+          i["wallName"] = i["positionName"];
+          (i["positionName"] = ""),
+            (i["isShowStr"] = i["isShow"] ? "顯示" : "不顯示");
+        });
+        return item;
+      });
+      this.total = result.data.totalNum;
+    },
+
+    async getHotSpotList() {
+      let result = await this.$http.get(
+        `https://4dkk.4dage.com/data/data${senceCode}/hot.json`
+      );
+      this.hotspotList = result;
+    },
+    handleRemove(file, fileList) {
+      console.log(file, fileList);
+    },
+    handlePreview(file) {
+      console.log(file);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.top {
+  width: 20% !important;
+}
+</style>

+ 1 - 0
style/config.json

@@ -0,0 +1 @@
+{"global":{"$--color-primary":"#2F4050"},"local":{}}

+ 3 - 0
vue.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  publicPath: "./"
+};