├── .browserslistrc ├── .gitignore ├── README.md ├── babel.config.js ├── config.json ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ ├── images │ │ ├── 404.svg │ │ ├── bg.svg │ │ ├── default_avatar.png │ │ └── logo.svg │ └── style │ │ ├── base.css │ │ ├── element.css │ │ └── normalize.css ├── components │ └── content │ │ ├── add-form │ │ └── addForm.vue │ │ ├── base-echarts │ │ ├── baseEcharts.vue │ │ └── data │ │ │ ├── china.json │ │ │ └── geoCoordMap.js │ │ ├── edit-form │ │ └── editFrom.vue │ │ ├── el-card │ │ └── elCard.vue │ │ ├── nav-aside │ │ └── navAside.vue │ │ ├── nav-header │ │ └── navHeader.vue │ │ ├── search-form │ │ └── searchForm.vue │ │ └── table-form │ │ └── tableForm.vue ├── main.js ├── plugins │ ├── axios │ │ ├── axios.js │ │ └── index.js │ ├── element │ │ └── element.js │ └── socket │ │ └── socket.js ├── router │ └── index.js ├── store │ ├── global │ │ └── index.js │ ├── index.js │ └── login │ │ └── index.js ├── utils │ └── index.js └── views │ ├── 404.vue │ ├── login │ ├── index.vue │ └── web │ │ ├── login.vue │ │ └── register.vue │ └── main │ ├── admin │ ├── api │ │ └── admin-apis.js │ ├── config │ │ ├── feedback-config.js │ │ ├── order-config.js │ │ ├── shop-config.js │ │ └── user-config.js │ ├── index.vue │ ├── router │ │ └── admin-router.js │ └── web │ │ ├── feedback │ │ └── feedback.vue │ │ ├── order │ │ └── order.vue │ │ ├── shop │ │ └── shop.vue │ │ └── user │ │ └── user.vue │ ├── food │ ├── api │ │ └── food-apis.js │ ├── config │ │ └── food-config.js │ ├── index.vue │ ├── router │ │ └── shop-router.js │ └── web │ │ ├── add │ │ └── add.vue │ │ └── info │ │ ├── detailForm.vue │ │ └── info.vue │ ├── main.vue │ ├── order │ ├── api │ │ └── readme.md │ ├── index.vue │ ├── router │ │ └── order-router.js │ └── web │ │ ├── all │ │ └── all.vue │ │ └── today │ │ └── today.vue │ ├── overview │ ├── overview.vue │ └── router │ │ └── overview-router.js │ ├── profile │ ├── api │ │ └── user-apis.js │ ├── index.vue │ ├── router │ │ └── profile-router.js │ └── web │ │ ├── feedback │ │ ├── feedback.vue │ │ ├── myFeedback.vue │ │ └── 富文本.vue │ │ └── myself │ │ └── myself.vue │ └── shop │ ├── api │ └── shop-apis.js │ ├── config │ ├── echarts-config.js │ └── shop-config.js │ ├── index.vue │ ├── router │ └── shop-router.js │ └── web │ ├── bill │ └── bill.vue │ ├── comment │ └── comment.vue │ ├── info │ ├── drawerForm.vue │ └── info.vue │ └── register │ └── register.vue └── vue.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-front-cms 2 | 外卖点餐的后台管理系统, 对应的前台点餐系统: [uniapp-order](https://github.com/yg10323/uniapp-order) 和 后端接口: [koa-apis](https://github.com/yg10323/koa-apis) 3 | 4 | ## 安装依赖 5 | ``` 6 | npm install 7 | ``` 8 | ## 运行 9 | ``` 10 | npm start 11 | ``` 12 | ## 项目的一些介绍 13 | ### 一. 扫码登陆 14 | 扫码功能需配合 [uni-app的前台点餐项目](https://github.com/yg10323/uniapp-order) 使用 15 | 二维码由node后台生成, 扫码登陆功能由自己简单实现, 由socket.io、redis、BroadcastChannel配合完成 16 | 17 | ![image](https://user-images.githubusercontent.com/48284901/155941081-b598cf04-4308-4d34-b498-167f6aeb208e.png) 18 | ### 二. 卖家相关的功能 19 | #### 2.1 总览 20 | ![image](https://user-images.githubusercontent.com/48284901/155941920-f5f264a9-588e-4223-8a4a-aa2a80eadaf5.png) 21 | #### 2.2 店铺管理 22 | 店铺管理包括: 注册店铺、店铺信息查看编辑、店铺流水以及店铺评价 23 | ##### 2.2.1 注册店铺 24 | 一个卖家账号只允许注册一个店铺, 并且在注册店铺之前要进行实名登记(在个人中心中) 25 | (gif图像加载慢, 请耐心等待) 26 | 27 | ![注册店铺](https://user-images.githubusercontent.com/48284901/155942923-2b4f9e96-6d23-4cbe-9013-3096d9da722c.gif) 28 | ##### 2.2.2 店铺信息管理 29 | ![店铺信息](https://user-images.githubusercontent.com/48284901/155943328-62b9a574-eeac-4a3b-bedc-b2ab212018d7.gif) 30 | ##### 2.2.3 店铺流水 31 | 使用echarts做的图表展示 32 | 33 | ![image](https://user-images.githubusercontent.com/48284901/155943503-e282cd83-2b44-424f-b05d-f8614c598f3b.png) 34 | ##### 2.2.4 评价管理 35 | 卖家可对买家的评价进行评价 36 | 37 | ![image](https://user-images.githubusercontent.com/48284901/155943676-bb15889d-b1d1-4c70-b9b1-5e5652cabbd6.png) 38 | #### 2.3 食品管理 39 | 食品管理包括: 新增食品信息和食品信息查看、编辑 40 | ##### 2.3.1新增食品 41 | ![image](https://user-images.githubusercontent.com/48284901/155944057-8021c886-3b8b-45ee-84c7-a6ee1be811aa.png) 42 | ##### 2.3.2 食品信息管理 43 | ![食品信息管理](https://user-images.githubusercontent.com/48284901/155944383-3d64f8c5-bf92-4a14-be5f-028472398018.gif) 44 | #### 2.4 订单管理 45 | 订单管理包括: 今日订单和往日订单 46 | 因为数据的展示是一样的, 只是订单生成的日期不同, 所以下面图片只展示一个 47 | 48 | ![image](https://user-images.githubusercontent.com/48284901/155944547-f0bb782d-a83b-4b56-92c6-5f81f6b8590b.png) 49 | #### 2.5 个人中心 50 | 个人中心包括: 个人信息、工单反馈、个人工单 51 | ##### 2.5.1 个人信息 52 | 账号刚注册成功之后, 是需要实名认证以后才能注册店铺 53 | 54 | ![image](https://user-images.githubusercontent.com/48284901/155944814-510cac57-b1da-4909-ac2c-b66c97c53808.png) 55 | ##### 2.5.2 工单反馈 56 | ![image](https://user-images.githubusercontent.com/48284901/155944908-f9e1370f-4e00-40ae-90de-0799a54442bb.png) 57 | ##### 2.5.3 我的工单 58 | 可以与管理员端进行互相的信息反馈 59 | 60 | ![image](https://user-images.githubusercontent.com/48284901/155944965-9d878669-ec97-4fbc-93c8-9a6ca77acf5c.png) 61 | ### 3. 管理员相关的功能 62 | 管理员主要有四大功能: 用户管理、店铺管理、订单管理、工单管理 63 | #### 3.1 用户管理 64 | ![用户管理](https://user-images.githubusercontent.com/48284901/155985001-ae62e03c-e79e-41cf-b1fd-65032b838e09.gif) 65 | #### 3.2 店铺管理 66 | ![image](https://user-images.githubusercontent.com/48284901/155945739-35c6403e-e35f-4b74-ad50-03308bdb24bd.png) 67 | #### 3.3 订单管理 68 | ![image](https://user-images.githubusercontent.com/48284901/155945769-f6e274c0-a867-40ba-8ee3-c9a03b6c916b.png) 69 | #### 3.4 工单管理 70 | ![工单管理](https://user-images.githubusercontent.com/48284901/155945881-280dbb71-a952-49d7-9792-731a629c70df.gif) 71 | 72 | 73 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ], 5 | plugins: [ 6 | [ 7 | "component", 8 | { 9 | "libraryName": "element-ui", 10 | "styleLibraryName": "theme-chalk" 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "axios_config": { 3 | "host": "http://124.70.20.215", 4 | "port": 2140 5 | }, 6 | "web_socket_config": { 7 | "host": "http://124.70.20.215", 8 | "port": 1070 9 | } 10 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-front-cms", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "start": "npm run serve" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.24.0", 12 | "core-js": "^3.6.5", 13 | "echarts": "^5.2.2", 14 | "element-ui": "^2.15.6", 15 | "socket.io-client": "^4.4.0", 16 | "vue": "^2.6.11", 17 | "vue-router": "^3.2.0", 18 | "vuex": "^3.4.0" 19 | }, 20 | "devDependencies": { 21 | "@vue/cli-plugin-babel": "~4.5.0", 22 | "@vue/cli-plugin-router": "~4.5.0", 23 | "@vue/cli-plugin-vuex": "~4.5.0", 24 | "@vue/cli-service": "~4.5.0", 25 | "babel-plugin-component": "^1.1.1", 26 | "sass": "^1.26.5", 27 | "sass-loader": "^8.0.2", 28 | "vue-template-compiler": "^2.6.11" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yg10323/vue-cms/cd0fa2da6ea63bf7c908d4b84d891272c1b9fbba/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%= htmlWebpackPlugin.options.title %> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 15 | -------------------------------------------------------------------------------- /src/assets/images/bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 21 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/assets/images/default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yg10323/vue-cms/cd0fa2da6ea63bf7c908d4b84d891272c1b9fbba/src/assets/images/default_avatar.png -------------------------------------------------------------------------------- /src/assets/images/logo.svg: -------------------------------------------------------------------------------- 1 | 9 | 75 | 76 | 83 | 84 | 85 | 86 | 87 | 88 | 95 | 100 | 104 | 108 | 109 | 114 | 119 | 124 | 125 | 126 | 131 | 136 | 142 | 147 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 164 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /src/assets/style/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body, #app { 7 | height: 100%; 8 | width: 100%; 9 | } 10 | 11 | #app { 12 | background: url(../images/bg.svg); 13 | } 14 | 15 | li { 16 | list-style: none; 17 | } 18 | 19 | /* 滚动条样式 */ 20 | ::-webkit-scrollbar { 21 | /* display: none; */ 22 | width: .5rem; 23 | height: .5rem; 24 | background: hsla(0, 0%, 100%, 0.6); 25 | } 26 | 27 | ::-webkit-scrollbar-track { 28 | border-radius: 0; 29 | } 30 | 31 | ::-webkit-scrollbar-thumb { 32 | border-radius: 0; 33 | background-color: rgba(95, 95, 95, .4); 34 | transition: all .2s; 35 | border-radius: .5rem; 36 | } 37 | 38 | .v-modal { 39 | position: static !important; 40 | } -------------------------------------------------------------------------------- /src/assets/style/element.css: -------------------------------------------------------------------------------- 1 | /* 对element-ui的一些样式做全局修改 */ 2 | 3 | 4 | /* 水平滚动条 */ 5 | .el-scrollbar__wrap { 6 | overflow-x: hidden !important; 7 | } -------------------------------------------------------------------------------- /src/assets/style/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } 350 | -------------------------------------------------------------------------------- /src/components/content/add-form/addForm.vue: -------------------------------------------------------------------------------- 1 | 97 | 98 | 135 | 136 | -------------------------------------------------------------------------------- /src/components/content/base-echarts/baseEcharts.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 49 | 50 | -------------------------------------------------------------------------------- /src/components/content/base-echarts/data/geoCoordMap.js: -------------------------------------------------------------------------------- 1 | export const geoCoordMap = { 2 | '黑龙江': [127.9688, 45.368], 3 | '北京': [116.4551, 40.2539], 4 | "辽宁": [123.1238, 42.1216], 5 | "河北": [114.4995, 38.1006], 6 | "天津": [117.4219, 39.4189], 7 | "山西": [112.3352, 37.9413], 8 | "陕西": [109.1162, 34.2004], 9 | "甘肃": [103.5901, 36.3043], 10 | "宁夏": [106.3586, 38.1775], 11 | "青海": [101.4038, 36.8207], 12 | "新疆": [87.9236, 43.5883], 13 | "西藏": [91.11, 29.97], 14 | "四川": [103.9526, 30.7617], 15 | "重庆": [108.384366, 30.439702], 16 | "山东": [117.1582, 36.8701], 17 | "河南": [113.4668, 34.6234], 18 | "江苏": [118.8062, 31.9208], 19 | "安徽": [117.29, 32.0581], 20 | "湖北": [114.3896, 30.6628], 21 | "浙江": [119.5313, 29.8773], 22 | "福建": [119.4543, 25.9222], 23 | "江西": [116.0046, 28.6633], 24 | "湖南": [113.0823, 28.2568], 25 | "贵州": [106.6992, 26.7682], 26 | "云南": [102.9199, 25.4663], 27 | "广东": [113.12244, 23.009505], 28 | "广西": [108.479, 23.1152], 29 | "海南": [110.3893, 19.8516], 30 | '上海': [121.4648, 31.2891], 31 | }; -------------------------------------------------------------------------------- /src/components/content/edit-form/editFrom.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /src/components/content/el-card/elCard.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 27 | 28 | 40 | -------------------------------------------------------------------------------- /src/components/content/nav-aside/navAside.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 84 | 85 | 143 | 144 | -------------------------------------------------------------------------------- /src/components/content/nav-header/navHeader.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 63 | 64 | -------------------------------------------------------------------------------- /src/components/content/search-form/searchForm.vue: -------------------------------------------------------------------------------- 1 | 69 | 70 | 118 | 119 | -------------------------------------------------------------------------------- /src/components/content/table-form/tableForm.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 89 | 90 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | // import mavonEditor from 'mavon-editor' 6 | // import 'mavon-editor/dist/css/index.css' 7 | import registerElement from './plugins/element/element' 8 | import { SoClient } from './plugins/socket/socket' 9 | import { initStore } from './store' 10 | import api from './plugins/axios' 11 | import utils from './utils/index' 12 | 13 | Vue.config.productionTip = false 14 | 15 | initStore();//页面刷新后初始化vux中的数据 16 | registerElement(Vue);//注册element组件 17 | 18 | 19 | Vue.prototype.$socket = SoClient.getInstance; 20 | Vue.prototype.$api = api; 21 | Vue.prototype.$utils = utils 22 | // Vue.use(mavonEditor) 23 | 24 | 25 | new Vue({ 26 | router, 27 | store, 28 | render: h => h(App) 29 | }).$mount('#app') 30 | -------------------------------------------------------------------------------- /src/plugins/axios/axios.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import axios from "axios"; 3 | import VueRouter from "vue-router"; 4 | import { axios_config } from '../../../config.json' 5 | 6 | let loadingInstance; 7 | 8 | const instance = axios.create({ 9 | baseURL: `${axios_config.host}:${axios_config.port}`, 10 | timeout: 60 * 1000, 11 | headers: { 12 | "Content-Type": "application/json;charset=UTF-8" 13 | }, 14 | }); 15 | 16 | instance.interceptors.request.use( 17 | config => { 18 | const reqObj = config; 19 | // 在发送请求之前做些什么 20 | // const reg = /\{(.+?)\}/g; // 匹配{} 21 | // if (reg.test(reqObj.url)) { 22 | // reqObj.url = reqObj.url.replace(reg, Object.values(reqObj.path)[0]); 23 | // } 24 | // 发送请求时携带token 25 | if (localStorage.getItem("token")) { 26 | reqObj.headers.Authorization = JSON.parse(localStorage.getItem("token")); 27 | 28 | } 29 | return reqObj; 30 | }, 31 | err => Promise.reject(err) 32 | ); 33 | 34 | instance.interceptors.response.use( 35 | response => { 36 | // console.log(response) 37 | // if (response.headers.token) { 38 | // sessionStorage.setItem("token", response.headers.token); 39 | // } 40 | const res = response.data; 41 | // 对响应数据做点什么 42 | if (res.code !== 200) { 43 | // 401 未认证 44 | if (res.code === 401) { 45 | localStorage.clear(); 46 | setTimeout(() => location.reload(), 1000) 47 | } 48 | } 49 | return res; 50 | }, 51 | err => Promise.reject(err) 52 | ); 53 | 54 | Vue.prototype.$axios = instance 55 | export default instance; 56 | -------------------------------------------------------------------------------- /src/plugins/axios/index.js: -------------------------------------------------------------------------------- 1 | import _axiosInstance from './axios'; 2 | 3 | // 遍历,生成请求函数 4 | const trans = (apiObj) => { 5 | const obj = apiObj; 6 | Object.keys(obj).forEach((apiKey) => { 7 | const data = { 8 | ...obj[apiKey] 9 | } 10 | obj[apiKey] = (payload) => _axiosInstance({ 11 | method: data.method, 12 | url: '/api' + data.url, 13 | ...payload 14 | }); 15 | }); 16 | 17 | return obj; 18 | }; 19 | 20 | // -转驼峰 21 | function toCamelCase(str) { 22 | const pattern = /-([a-z])/g; 23 | return str.replace(pattern, (all, letter) => { 24 | // console.log(all, letter); 25 | return letter.toUpperCase(); 26 | }); 27 | 28 | } 29 | 30 | // https://webpack.docschina.org/guides/dependency-management/#requirecontext 31 | const apiAll = require.context('@/views', true, /-apis\.js$/); 32 | // console.log(apiAll); 33 | // key: 处理后的文件名, value: 文件内容中网络请求的方法名 34 | const moduleApis = {}; 35 | // keys属性 {Function} -匹配成功模块的名字组成的数组 36 | apiAll.keys().map((key) => { 37 | // 先将-去掉,然后将小写a转换成大写A,最后取到 .的下标 38 | const suffixIndex = toCamelCase(key.match(/.*\/(.*\..*)/)[1]).indexOf('.'); 39 | // 截取文件名 40 | const name = toCamelCase(key.match(/.*\/(.*\..*)/)[1]).substring(0, suffixIndex); 41 | moduleApis[name] = apiAll(key).default; 42 | return trans(moduleApis[name]); 43 | }); 44 | 45 | export default moduleApis; 46 | 47 | -------------------------------------------------------------------------------- /src/plugins/element/element.js: -------------------------------------------------------------------------------- 1 | // 导入组件 2 | import { 3 | Message, 4 | MessageBox, 5 | Notification, 6 | Button, 7 | Form, 8 | FormItem, 9 | Tabs, 10 | TabPane, 11 | Input, 12 | Empty, 13 | Container, 14 | Aside, 15 | Header, 16 | Main, 17 | Menu, 18 | MenuItem, 19 | MenuItemGroup, 20 | Submenu, 21 | Row, 22 | Col, 23 | Select, 24 | Option, 25 | DatePicker, 26 | Avatar, 27 | Dropdown, 28 | DropdownItem, 29 | DropdownMenu, 30 | Breadcrumb, 31 | BreadcrumbItem, 32 | Table, 33 | TableColumn, 34 | Pagination, 35 | Backtop, 36 | Divider, 37 | Link, 38 | Dialog, 39 | Scrollbar, 40 | Cascader, 41 | TimeSelect, 42 | Switch, 43 | Checkbox, 44 | CheckboxGroup, 45 | Radio, 46 | RadioGroup, 47 | Popover, 48 | Upload, 49 | Progress, 50 | Descriptions, 51 | DescriptionsItem, 52 | Tag, 53 | Result, 54 | Image, 55 | Collapse, 56 | CollapseItem, 57 | Drawer, 58 | Card, 59 | InputNumber, 60 | Popconfirm, 61 | Icon, 62 | RadioButton, 63 | Rate 64 | } from 'element-ui' 65 | 66 | const components = { 67 | Button, 68 | Form, 69 | FormItem, 70 | Tabs, 71 | TabPane, 72 | Input, 73 | Empty, 74 | Container, 75 | Aside, 76 | Header, 77 | Main, 78 | Menu, 79 | MenuItem, 80 | MenuItemGroup, 81 | Submenu, 82 | Row, 83 | Col, 84 | Select, 85 | Option, 86 | DatePicker, 87 | Avatar, 88 | Dropdown, 89 | DropdownItem, 90 | DropdownMenu, 91 | Breadcrumb, 92 | BreadcrumbItem, 93 | Table, 94 | TableColumn, 95 | Pagination, 96 | Backtop, 97 | Divider, 98 | Link, 99 | Dialog, 100 | Scrollbar, 101 | Cascader, 102 | TimeSelect, 103 | Switch, 104 | Checkbox, 105 | CheckboxGroup, 106 | Radio, 107 | RadioGroup, 108 | Popover, 109 | Upload, 110 | Progress, 111 | Descriptions, 112 | DescriptionsItem, 113 | Tag, 114 | Result, 115 | Image, 116 | Collapse, 117 | CollapseItem, 118 | Drawer, 119 | Card, 120 | InputNumber, 121 | Popconfirm, 122 | Icon, 123 | RadioButton, 124 | Rate 125 | } 126 | 127 | 128 | // 遍历注册, 将方法暴露 129 | export default function (Vue) { 130 | for (const el in components) { 131 | Vue.use(components[el]) 132 | } 133 | 134 | Vue.prototype.$message = Message 135 | Vue.prototype.$messageBox = MessageBox 136 | Vue.prototype.$notify = Notification 137 | } -------------------------------------------------------------------------------- /src/plugins/socket/socket.js: -------------------------------------------------------------------------------- 1 | import { io } from "socket.io-client"; 2 | import { web_socket_config } from '../../../config.json' 3 | 4 | // socket客户端 5 | export class SoClient { 6 | 7 | constructor(action) { 8 | this.host = web_socket_config.host; 9 | this.port = web_socket_config.port; 10 | this.action = action || null; 11 | this.client = io(`${this.host}:${this.port}`) 12 | this.client.on("connect", () => { 13 | this.stid = this.client.id; 14 | console.log('scoket连接成功 ' + this.stid); 15 | }) 16 | 17 | // 监听异常 18 | this.client.on('error', error => { 19 | console.log(error); 20 | }) 21 | 22 | // 监听服务端是否断开 23 | this.client.on('disconnect', () => { 24 | console.log(`${this.stid} 连接已断开`); 25 | }) 26 | 27 | this.initGetQrCode() 28 | 29 | // 启动心跳 30 | // this.heartCheck() 31 | 32 | return this.client; 33 | } 34 | 35 | // 用于某些刚连接好即需要通信的连接 36 | initGetQrCode() { 37 | if (this.action) { 38 | // console.log(222222222); 39 | this.client.emit(this.action); 40 | } 41 | } 42 | 43 | // 暂留 目前的服务器承受不住心跳包带来的压力, 所以暂时关闭心跳 44 | // 心跳包 45 | heartCheck() { 46 | this.heartTimer = setInterval(() => { 47 | this.client.emit('fromClient', JSON.stringify({ 48 | cmd: "heart", 49 | content: {} 50 | })) 51 | }, 1000); 52 | } 53 | 54 | // 重置心跳 55 | resetHeartCheck() { 56 | this.heartTimer && clearInterval(this.heartTimer); 57 | this.heartCheck(); 58 | } 59 | 60 | static getInstance(action) { 61 | return new SoClient(action); 62 | } 63 | }; 64 | 65 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | 5 | Vue.use(VueRouter) 6 | 7 | const routes = [ 8 | { 9 | path: '/', 10 | redirect: '/main' 11 | }, 12 | { 13 | path: "/main", 14 | name: "main", 15 | component: () => import('@/views/main/main') 16 | }, 17 | { 18 | path: '/login', 19 | name: 'login', 20 | component: () => import('@/views/login/index') 21 | }, 22 | 23 | { 24 | path: "*", 25 | name: "404", 26 | component: () => import('@/views/404') 27 | } 28 | ] 29 | 30 | const router = new VueRouter({ 31 | // mode: 'history', 32 | base: process.env.BASE_URL, 33 | routes 34 | }) 35 | 36 | 37 | // 避免冗余导航 (重复点击菜单栏报错问题) 38 | const originalPush = VueRouter.prototype.push; 39 | VueRouter.prototype.push = function push(location) { 40 | return originalPush.call(this, location).catch(err => err); 41 | }; 42 | 43 | // 全局导航守卫 44 | router.beforeEach((to, from, next) => { 45 | // 前往非登录页之前判断有无登录 46 | if (to.path !== '/login') { 47 | const token = localStorage.getItem('token') 48 | if (!token) { 49 | next({ path: '/login' }) 50 | } 51 | 52 | } 53 | 54 | // 重定向显示overview 55 | if (to.path === '/main') { 56 | const path = JSON.parse(localStorage.getItem('menus'))[0].path 57 | next({ path }) 58 | } 59 | 60 | next() 61 | }) 62 | 63 | 64 | export default router 65 | -------------------------------------------------------------------------------- /src/store/global/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | state: { 3 | isCollapse: false, 4 | defaultActive: '/main/overview', 5 | }, 6 | mutations: { 7 | // 控制侧边栏展开 8 | changeCollapse(state) { 9 | state.isCollapse = !state.isCollapse; 10 | }, 11 | // 用户退出后清空缓存,初始化侧边栏选中状态 12 | clearStorage(state) { 13 | localStorage.clear(); 14 | sessionStorage.clear(); 15 | state.defaultActive = '1000' 16 | }, 17 | // 侧边栏的选中状态 18 | setActive(state, active_id) { 19 | state.defaultActive = active_id; 20 | sessionStorage.setItem('defaultActive', active_id); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import login from './login' 4 | import global from './global' 5 | 6 | Vue.use(Vuex) 7 | 8 | const store = new Vuex.Store({ 9 | state: {}, 10 | mutations: {}, 11 | actions: {}, 12 | modules: { 13 | login, 14 | global 15 | } 16 | }) 17 | 18 | export default store 19 | 20 | 21 | export function initStore() { 22 | store.commit('loadLocalStorage') 23 | } 24 | -------------------------------------------------------------------------------- /src/store/login/index.js: -------------------------------------------------------------------------------- 1 | import router from '@/router' 2 | import utils from '@/utils' 3 | 4 | export default { 5 | state: { 6 | token: '', 7 | userInfo: {}, 8 | menus: [] 9 | }, 10 | mutations: { 11 | // 本地缓存token 12 | setToken(state, token) { 13 | state.token = token 14 | localStorage.setItem('token', JSON.stringify(token)) 15 | }, 16 | // 缓存用户信息 17 | setUserInfo(state, userInfo) { 18 | state.userInfo.account = userInfo.account 19 | state.userInfo.role_id = userInfo.role_id 20 | localStorage.setItem('userInfo', JSON.stringify(userInfo)) 21 | }, 22 | // 本地缓存菜单信息 23 | setMenus(state, menus) { 24 | state.menus = menus 25 | localStorage.setItem('menus', JSON.stringify(menus)) 26 | 27 | // 匹配出该角色对应的路由配置并添加路由 28 | const routes = utils.mapMenus(menus); 29 | routes.forEach(route => router.addRoute('main', route)) 30 | }, 31 | // 页面刷新时, 为vuex重新赋值 32 | loadLocalStorage(state) { 33 | const token = JSON.parse(localStorage.getItem('token')) 34 | if (token) { 35 | state.token = token 36 | } 37 | const userInfo = JSON.parse(localStorage.getItem('userInfo')) 38 | if (userInfo) { 39 | state.userInfo = userInfo 40 | } 41 | const menus = JSON.parse(localStorage.getItem('menus')) 42 | if (menus) { 43 | // state.menus = menus 44 | this.commit('setMenus', menus) 45 | } 46 | const defaultActive = sessionStorage.getItem('defaultActive') 47 | if (defaultActive) { 48 | this.commit('setActive', defaultActive) 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { geoCoordMap } from '../components/content/base-echarts/data/geoCoordMap' 3 | 4 | export default { 5 | /** 6 | * 根据角色动态匹配路由 7 | * @param {Array} menus 8 | * @returns Array roleRoutes 9 | */ 10 | mapMenus(menus) { 11 | const roleRoutes = []; 12 | const allRoutes = []; 13 | 14 | // 取出所有的路由配置信息 15 | const routeFiles = require.context('@/views', true, /-router\.js$/) 16 | routeFiles.keys().forEach(key => { 17 | const name = routeFiles(key).default.name 18 | const cpn = routeFiles(key).default 19 | // name: 一级菜单的name, cpn: 该name下所有的路由信息 20 | allRoutes.push({ name, cpn }) 21 | }) 22 | 23 | // 与后端返回的路由信息进行比对 24 | // 通过比对一级菜单的name 25 | for (const menu of menus) { 26 | const res = allRoutes.find(route => route.name === menu.name) 27 | if (res) { 28 | roleRoutes.push(res.cpn) 29 | } 30 | } 31 | 32 | return roleRoutes 33 | }, 34 | 35 | /** 36 | * 级联菜单数据格式化 37 | * @param {Array} data 38 | * @returns Array final 39 | */ 40 | setOptions(data) { 41 | const ps = new Map() 42 | const final = []; 43 | // 对一级菜单去重 44 | for (const obj of data) { 45 | ps.set(obj.op_id, obj.options) 46 | } 47 | // 将二级菜单添加至一级菜单中 48 | ps.forEach((value, key) => { 49 | const o = { 50 | value: key, 51 | label: value, 52 | children: [] 53 | }; 54 | 55 | for (const obj of data) { 56 | if (obj.op_id === key) { 57 | o.children.push({ 58 | value: obj.ch_id, 59 | label: obj.child 60 | }) 61 | } 62 | } 63 | 64 | final.push(o) 65 | }) 66 | 67 | return final 68 | }, 69 | 70 | /** 71 | * 对象递归删除空数据, 返回新对象 72 | * @param {any} data 73 | * @returns 74 | */ 75 | deepRemove(data) { 76 | // 对非对象进行过滤 77 | if (typeof data !== 'object') return data 78 | // 对null undefined 进行过滤 79 | if (data == undefined) return data 80 | // 对 RegExp Date Function 进行过滤 81 | if (data instanceof RegExp) return new RegExp(data) 82 | if (data instanceof Date) return new Date(data) 83 | if (data instanceof Function) return new Function(data) 84 | // 创建对应类型的实例 85 | let newData = new data.constructor() 86 | // 递归: 清除所有空串、undefined、null、以及对象属性值为空的属性 87 | for (let key in data) { 88 | if (data.hasOwnProperty(key)) { 89 | let res = this.deepRemove(data[key]) 90 | // 判断非空属性,并对其进行保留 (0也要保留) 91 | if ((res || res === 0) && JSON.stringify(res) !== '[]' && JSON.stringify(res) !== '{}') { 92 | newData[key] = res 93 | } 94 | } 95 | } 96 | 97 | return newData 98 | }, 99 | 100 | // 对deepRemove返回的数据进一步处理 101 | // 调用此函数 102 | removeEmpty(data) { 103 | const res = this.deepRemove(data) 104 | let finalData; 105 | // data如果是数组, 可能会出现empty的元素, 对此进行清除 106 | if (res instanceof Array) { 107 | finalData = res.filter(item => item || item === 0) 108 | } else { 109 | // data是对象,不做处理 110 | finalData = res 111 | } 112 | 113 | return finalData 114 | }, 115 | 116 | /** 117 | * 对象深拷贝 118 | * @param obj 119 | * @returns newObj 120 | */ 121 | deepClone(obj) { 122 | // 对非对象进行过滤 123 | if (typeof obj !== 'object') return obj 124 | 125 | // 对null undefined 进行过滤 126 | if (obj == undefined) return obj 127 | 128 | // 对 RegExp Date Function 进行过滤 129 | if (obj instanceof RegExp) return new RegExp(obj) 130 | if (obj instanceof Date) return new Date(obj) 131 | if (obj instanceof Function) return new Function(obj) 132 | 133 | // 利用constructor进行实例化, 不管是对象还是数组 134 | let newObj = new obj.constructor() 135 | for (let key in obj) { 136 | if (obj.hasOwnProperty(key)) { 137 | newObj[key] = this.deepClone(obj[key]) 138 | } 139 | } 140 | 141 | return newObj 142 | }, 143 | 144 | /** 145 | * 格式化时间 146 | * @param {*} timestamp 147 | * @returns 年-月-日 时:分:秒 148 | */ 149 | formatTime(timestamp) { 150 | const d = new Date(parseInt(timestamp)); //根据时间戳生成的时间对象 151 | const date = (d.getFullYear()) + "-" + 152 | (d.getMonth() + 1) + "-" + 153 | (d.getDate()) + " " + 154 | (d.getHours()) + ":" + 155 | (d.getMinutes()) + ":" + 156 | (d.getSeconds()); 157 | return date; 158 | }, 159 | 160 | /** 161 | * 合并相同订单(id在数据库经过排序) 162 | * @param {Object} orders 163 | * @returns Object data 164 | */ 165 | dealOrderData(orders) { 166 | let data = this.deepClone(orders); 167 | 168 | let current_id, next_id; 169 | let foodInfoArray = []; 170 | 171 | for (let i = 0; i < data.length; i++) { 172 | current_id = data[i].id; 173 | // next_id为undefined表示数组只有一个元素或遍历到了最后一个元素 174 | // 当遍历到最后一个元素的时候, 同样需要往foodInfoArray添加一次 175 | if (data[i + 1]) { 176 | next_id = data[i + 1].id; 177 | } 178 | else { 179 | foodInfoArray.push(data[i].food_info) 180 | data[i].food_info = foodInfoArray 181 | return data; 182 | } 183 | // 记录重复id的food_info 184 | // 当id不相同时, 将foodInfoArray赋值给food_info 185 | if (current_id === next_id) { 186 | foodInfoArray.push(data[i].food_info) 187 | data.splice(i, 1) 188 | i = i - 1;//i减一为了保证下次遍历从当前下标开始 189 | } else { 190 | foodInfoArray.push(data[i].food_info) 191 | data[i].food_info = foodInfoArray 192 | foodInfoArray = []; 193 | } 194 | 195 | } 196 | }, 197 | 198 | /** 199 | * 合并同一工单的回复 200 | * @param {Array} feedbackList 201 | * @returns new array 202 | */ 203 | dealFeedback(feedbackList) { 204 | let data = this.deepClone(feedbackList) 205 | for (let i = 0; i < data.length; i++) { 206 | data[i].children = []; 207 | for (let j = 0; j < feedbackList.length; j++) { 208 | if (data[i].id == feedbackList[j].id) { 209 | continue; 210 | } 211 | if (data[i].id == feedbackList[j].belong) { 212 | data[i].children.push(feedbackList[j]) 213 | feedbackList.splice(j, 1); 214 | data.splice(j, 1); 215 | j -= 1;//保证删除数据后的内层循环从当前j继续 216 | } 217 | } 218 | } 219 | return data 220 | }, 221 | 222 | /** 223 | * echarts地图城市经纬度匹配 224 | * @param {Array} data 225 | * @returns new Array 226 | */ 227 | convertData(data) { 228 | var res = []; 229 | for (var i = 0; i < data.length; i++) { 230 | var geoCoord = geoCoordMap[data[i].name]; 231 | if (geoCoord) { 232 | res.push({ 233 | name: data[i].name, 234 | value: geoCoord.concat(data[i].value) 235 | }); 236 | } 237 | } 238 | return res; 239 | } 240 | } -------------------------------------------------------------------------------- /src/views/404.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | -------------------------------------------------------------------------------- /src/views/login/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 37 | 38 | -------------------------------------------------------------------------------- /src/views/login/web/login.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 218 | 219 | -------------------------------------------------------------------------------- /src/views/login/web/register.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 130 | 131 | -------------------------------------------------------------------------------- /src/views/main/admin/api/admin-apis.js: -------------------------------------------------------------------------------- 1 | export default { 2 | getSellerMenu: { 3 | name: '获取role_id为2的菜单', 4 | url: "/role/2/menu", 5 | method: 'GET' 6 | }, 7 | getAdminMenu: { 8 | name: '获取role_id为1的菜单', 9 | url: "/role/1/menu", 10 | method: 'GET' 11 | }, 12 | adminLogin: { 13 | name: 'admin登录', 14 | url: '/role/login', 15 | method: 'POST' 16 | }, 17 | deleteAdmin: { 18 | name: '根据id删除admin', 19 | url: '/role/admin/delete', 20 | method: 'POST' 21 | }, 22 | deleteSeller: { 23 | name: '根据id删除seller', 24 | url: '/role/seller/delete', 25 | method: 'POST' 26 | }, 27 | deleteBuyer: { 28 | name: '根据id删除buyer', 29 | url: '/role/buyer/delete', 30 | method: 'POST' 31 | }, 32 | getUserByQuery: { 33 | name: '根据query获取admin/seller/buyer', 34 | url: '/role/user/by_query', 35 | method: 'POST' 36 | }, 37 | changeUserUsable: { 38 | name: '更改user的usable', 39 | url: '/role/user/usable', 40 | method: 'POST' 41 | }, 42 | addUser: { 43 | name: '添加user', 44 | url: '/role/user/add', 45 | method: 'POST' 46 | }, 47 | getShopList: { 48 | name: '获取店铺信息', 49 | url: '/role/shop/list', 50 | method: 'POST' 51 | }, 52 | changeShopUsable: { 53 | name: '更改shop的usable', 54 | url: '/role/shop/usable', 55 | method: 'POST' 56 | }, 57 | deleteShop: { 58 | name: '根据id删除shop', 59 | url: '/role/shop/delete', 60 | method: 'POST' 61 | }, 62 | addShop: { 63 | name: '添加店铺', 64 | url: '/role/shop/add', 65 | method: 'POST' 66 | }, 67 | getOrderList: { 68 | name: '获取订单信息', 69 | url: '/role/order/list', 70 | method: 'POST' 71 | }, 72 | changeOrderStatus: { 73 | name: '更改order的状态', 74 | url: '/role/order/status', 75 | method: 'POST' 76 | }, 77 | changeOrderUpdateFlag: { 78 | name: '更改order的update_flag', 79 | url: '/role/order/update_flag', 80 | method: 'POST' 81 | }, 82 | getFeedBackList: { 83 | name: '获取工单反馈', 84 | url: '/role/feedback/list', 85 | method: 'POST' 86 | }, 87 | replyFeedback: { 88 | name: '回复工单', 89 | url: '/role/feedback/reply', 90 | method: 'POST' 91 | } 92 | } -------------------------------------------------------------------------------- /src/views/main/admin/config/feedback-config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 搜索表单的配置 3 | FeedbackSearchFormConfig: [ 4 | { 5 | type: 'input', label: '卖家id', prop: 'seller_id', placeholder: '输入查询的id' 6 | }, 7 | { type: 'input', label: '反馈主题', prop: 'title', placeholder: '输入查询的主题' }, 8 | { 9 | type: 'datepicker', label: '日期范围', 10 | otherOptions: { 11 | type: 'daterange', 12 | 'range-separator': '至', 13 | 'start-placeholder': '开始日期', 14 | 'end-placeholder': '结束日期' 15 | }, 16 | prop: 'time' 17 | } 18 | ], 19 | // 表格表单的配置 20 | FeedbackTableFormConfig: { 21 | title: "反馈列表", 22 | isShowIndex: true, 23 | propList: [ 24 | { prop: "id", label: "id", minWidth: "40", slotName: "id" }, 25 | { prop: "seller_id", label: "卖家id", minWidth: "60", slotName: "seller_id" }, 26 | { prop: "title", label: "主题", minWidth: "80", slotName: "title" }, 27 | { prop: "content", label: "内容", minWidth: "120", slotName: "content" }, 28 | { prop: "feedback_id", label: "回复反馈id", minWidth: "60", slotName: "feedback_id" }, 29 | { prop: "belong", label: "所属主工单", minWidth: "60", slotName: "belong" }, 30 | { prop: "type", label: "类型", minWidth: "70", slotName: "type" }, 31 | { 32 | prop: "createTime", 33 | label: "创建时间", 34 | minWidth: "120", 35 | slotName: "createTime", 36 | }, 37 | { 38 | prop: "updateTime", 39 | label: "最后更改", 40 | minWidth: "120", 41 | slotName: "updateTime", 42 | }, 43 | { 44 | label: "操作", 45 | minWidth: "60", 46 | slotName: "handle", 47 | }, 48 | ], 49 | }, 50 | // 添加用户表单的配置 51 | FeedbackAddFormConfig: { 52 | options: ['feedback'], 53 | feedback: [ 54 | { 55 | type: 'textarea', label: '回复', prop: 'content', placeholder: '输入回复内容...', clearable: true, 56 | rows: 10, 57 | rules: [ 58 | { required: true, message: "内容不能为空", trigger: "blur" }, 59 | ] 60 | }, 61 | ], 62 | } 63 | } -------------------------------------------------------------------------------- /src/views/main/admin/config/order-config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 搜索表单的配置 3 | OrderSearchFormConfig: [ 4 | // { 5 | // type: 'input', label: '店铺名', prop: 'id', placeholder: '输入查询的id' 6 | // }, 7 | { type: 'input', label: '订单号', prop: 'order_number', placeholder: '输入查询的订单' }, 8 | { 9 | type: 'datepicker', label: '日期范围', 10 | otherOptions: { 11 | type: 'daterange', 12 | 'range-separator': '至', 13 | 'start-placeholder': '开始日期', 14 | 'end-placeholder': '结束日期' 15 | }, 16 | prop: 'time' 17 | } 18 | ], 19 | // 表格表单的配置 20 | OrderTableFormConfig: { 21 | title: "订单列表", 22 | isShowIndex: true, 23 | propList: [ 24 | { prop: "id", label: "id", minWidth: "40", slotName: "id" }, 25 | { prop: "buyer_id", label: "买家id", minWidth: "60", slotName: "buyer_id" }, 26 | { 27 | prop: "order_number", 28 | label: "订单编号", 29 | minWidth: "80", 30 | slotName: "order_number", 31 | }, 32 | { prop: "pay_mode", label: "支付方式", minWidth: "80", slotName: "pay_mode" }, 33 | { prop: "total_price", label: "订单金额", minWidth: "60", slotName: "total_price" }, 34 | { 35 | prop: "pay_price", 36 | label: "支付金额", 37 | minWidth: "70", 38 | slotName: "pay_price", 39 | }, 40 | { 41 | prop: "done", 42 | label: "订单状态", 43 | minWidth: "70", 44 | slotName: "done", 45 | }, 46 | { 47 | prop: "update_flag", 48 | label: "日期归属", 49 | minWidth: "60", 50 | slotName: "update_flag", 51 | }, 52 | { 53 | prop: "createTime", 54 | label: "下单时间", 55 | minWidth: "100", 56 | slotName: "createTime", 57 | }, 58 | { 59 | prop: "updateTime", 60 | label: "完成时间", 61 | minWidth: "100", 62 | slotName: "updateTime", 63 | }, 64 | { 65 | label: "操作", 66 | minWidth: "100", 67 | slotName: "handle", 68 | }, 69 | ], 70 | } 71 | } -------------------------------------------------------------------------------- /src/views/main/admin/config/shop-config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 搜索表单的配置 3 | ShopSearchFormConfig: [ 4 | { 5 | type: 'input', label: 'id', prop: 'id', placeholder: '输入查询的id' 6 | }, 7 | { type: 'input', label: '店铺名', prop: 'name', placeholder: '输入查询的店铺名' }, 8 | { 9 | type: 'datepicker', label: '日期范围', 10 | otherOptions: { 11 | type: 'daterange', 12 | 'range-separator': '至', 13 | 'start-placeholder': '开始日期', 14 | 'end-placeholder': '结束日期' 15 | }, 16 | prop: 'time' 17 | } 18 | ], 19 | // 表格表单的配置 20 | ShopTableFormConfig: { 21 | title: "店铺列表", 22 | isShowIndex: true, 23 | propList: [ 24 | { prop: "id", label: "id", minWidth: "40", slotName: "id" }, 25 | { prop: "name", label: "店铺名", minWidth: "80", slotName: "name" }, 26 | { 27 | prop: "address", 28 | label: "店铺地址", 29 | minWidth: "80", 30 | slotName: "address", 31 | }, 32 | { prop: "shop_avatar_url", label: "头像", minWidth: "80", slotName: "shop_avatar_url" }, 33 | { prop: "usable", label: "状态", minWidth: "60", slotName: "usable" }, 34 | { 35 | prop: "delivery", 36 | label: "配送", 37 | minWidth: "70", 38 | slotName: "delivery", 39 | }, 40 | { 41 | prop: "start_time", 42 | label: "营业时间", 43 | minWidth: "70", 44 | slotName: "start_time", 45 | }, 46 | { prop: "notice", label: "公告", minWidth: "60", slotName: "notice" }, 47 | { 48 | prop: "updateTime", 49 | label: "最后更改", 50 | minWidth: "120", 51 | slotName: "updateTime", 52 | }, 53 | { 54 | label: "操作", 55 | minWidth: "100", 56 | slotName: "handle", 57 | }, 58 | ], 59 | } 60 | } -------------------------------------------------------------------------------- /src/views/main/admin/config/user-config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 搜索表单的配置 3 | UserSearchFormConfig: [ 4 | { 5 | type: 'input', label: 'id', prop: 'id', placeholder: '输入查询的id' 6 | }, 7 | { type: 'input', label: '账号', prop: 'account', placeholder: '输入查询的账号' }, 8 | { 9 | type: 'select', label: '角色', prop: 'role', placeholder: '选择查询角色', 10 | options: [ 11 | // value需和数据库中的表名一致 12 | { label: '管理员', value: 'admin' }, 13 | { label: '卖家', value: 'seller' }, 14 | { label: '买家', value: 'buyer' }, 15 | ], 16 | rules: [ 17 | { required: true, message: "请选择要查询的角色", trigger: "blur" }, 18 | ] 19 | }, 20 | { 21 | type: 'datepicker', label: '日期范围', 22 | otherOptions: { 23 | type: 'daterange', 24 | 'range-separator': '至', 25 | 'start-placeholder': '开始日期', 26 | 'end-placeholder': '结束日期' 27 | }, 28 | prop: 'time' 29 | } 30 | ], 31 | // 表格表单的配置 32 | UserTableFormConfig: { 33 | title: "用户列表", 34 | isShowIndex: true, 35 | propList: [ 36 | { prop: "id", label: "id", minWidth: "40", slotName: "id" }, 37 | { prop: "account", label: "用户名", minWidth: "80", slotName: "account" }, 38 | { 39 | prop: "password", 40 | label: "密码", 41 | minWidth: "80", 42 | slotName: "password", 43 | }, 44 | { prop: "usable", label: "状态", minWidth: "60", slotName: "usable" }, 45 | { prop: "role_id", label: "角色", minWidth: "40", slotName: "role_id" }, 46 | { prop: "level", label: "等级", minWidth: "40", slotName: "level" }, 47 | { 48 | prop: "createTime", 49 | label: "创建时间", 50 | minWidth: "100", 51 | slotName: "createTime", 52 | }, 53 | { 54 | prop: "updateTime", 55 | label: "更新时间", 56 | minWidth: "100", 57 | slotName: "updateTime", 58 | }, 59 | { 60 | label: "操作", 61 | minWidth: "100", 62 | slotName: "handle", 63 | }, 64 | ], 65 | }, 66 | // 添加用户表单的配置 67 | UserAddFormConfig: { 68 | options: ['admin', "seller", "buyer"], 69 | admin: [ 70 | { 71 | type: 'input', label: '账号', prop: 'account', placeholder: '输入账号', clearable: true, 72 | rules: [ 73 | { required: true, message: "账号不能为空", trigger: "blur" }, 74 | ] 75 | }, 76 | { 77 | type: 'password', label: '密码', prop: 'password', placeholder: '输入密码', clearable: true, 78 | rules: [ 79 | { required: true, message: "密码不能为空", trigger: "blur" }, 80 | ] 81 | }, 82 | { 83 | type: 'select', label: '角色等级', prop: 'level', placeholder: '选择管理员等级', 84 | options: [ 85 | // value需和数据库中的表名一致 86 | { label: '等级1', value: '1' }, 87 | { label: '等级2', value: '2' }, 88 | ], 89 | rules: [ 90 | { required: true, message: "账号等级不能为空", trigger: "blur" }, 91 | ] 92 | }, 93 | ], 94 | seller: [ 95 | { 96 | type: 'input', label: '账号', prop: 'account', placeholder: '输入账号', clearable: true, 97 | rules: [ 98 | { required: true, message: "账号不能为空", trigger: "blur" }, 99 | ] 100 | }, 101 | { 102 | type: 'password', label: '密码', prop: 'password', placeholder: '输入密码', clearable: true, 103 | rules: [ 104 | { required: true, message: "密码不能为空", trigger: "blur" }, 105 | ] 106 | }, 107 | { 108 | type: 'input', label: '姓名', prop: 'name', placeholder: '输入姓名', clearable: true, 109 | rules: [ 110 | { required: true, message: "姓名不能为空", trigger: "blur" }, 111 | ] 112 | }, 113 | { 114 | type: 'input', label: '身份证号', prop: 'iid', placeholder: '输入身份证号', clearable: true, 115 | rules: [ 116 | { required: true, message: "身份证号不能为空", trigger: "blur" }, 117 | ] 118 | }, 119 | { 120 | type: 'input', label: '手机号', prop: 'phone', placeholder: '输入手机号', clearable: true, 121 | rules: [ 122 | { required: true, message: "手机号不能为空", trigger: "blur" }, 123 | ] 124 | }, 125 | ], 126 | buyer: [ 127 | { 128 | type: 'input', label: '账号', prop: 'account', placeholder: '输入账号', clearable: true, 129 | rules: [ 130 | { required: true, message: "账号不能为空", trigger: "blur" }, 131 | ] 132 | }, 133 | { 134 | type: 'password', label: '密码', prop: 'password', placeholder: '输入密码', clearable: true, 135 | rules: [ 136 | { required: true, message: "密码不能为空", trigger: "blur" }, 137 | ] 138 | }, 139 | { 140 | type: 'input', label: '手机号', prop: 'phone', placeholder: '输入手机号', clearable: true, 141 | rules: [ 142 | { required: true, message: "手机号不能为空", trigger: "blur" }, 143 | ] 144 | }, 145 | ], 146 | } 147 | } -------------------------------------------------------------------------------- /src/views/main/admin/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/views/main/admin/router/admin-router.js: -------------------------------------------------------------------------------- 1 | export default { 2 | path: 'admin', 3 | name: 'admin', 4 | meta: { name: '系统管理' }, 5 | component: () => import('@/views/main/admin/index.vue'), 6 | children: [ 7 | { 8 | path: 'user', 9 | name: 'user', 10 | meta: { name: '用户管理' }, 11 | component: () => import('@/views/main/admin/web/user/user.vue') 12 | }, 13 | { 14 | path: 'shop', 15 | name: 'shop', 16 | meta: { name: '店铺管理' }, 17 | component: () => import('@/views/main/admin/web/shop/shop.vue') 18 | }, 19 | { 20 | path: 'order', 21 | name: 'order', 22 | meta: { name: '订单管理' }, 23 | component: () => import('@/views/main/admin/web/order/order.vue') 24 | }, 25 | { 26 | path: 'feedback', 27 | name: 'feedback', 28 | meta: { name: '工单反馈' }, 29 | component: () => import('@/views/main/admin/web/feedback/feedback.vue') 30 | }, 31 | ] 32 | } -------------------------------------------------------------------------------- /src/views/main/admin/web/feedback/feedback.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 148 | 149 | -------------------------------------------------------------------------------- /src/views/main/admin/web/order/order.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 152 | 153 | -------------------------------------------------------------------------------- /src/views/main/admin/web/shop/shop.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 169 | 170 | -------------------------------------------------------------------------------- /src/views/main/admin/web/user/user.vue: -------------------------------------------------------------------------------- 1 | 90 | 91 | 245 | 246 | -------------------------------------------------------------------------------- /src/views/main/food/api/food-apis.js: -------------------------------------------------------------------------------- 1 | export default { 2 | addFood: { 3 | name: '添加食品', 4 | url: '/food/add', 5 | method: 'POST', 6 | }, 7 | getFoodClassify: { 8 | name: '获取店铺的食品分类', 9 | url: '/food/classify', 10 | method: 'GET' 11 | }, 12 | getFoodList: { 13 | name: '获取食品列表', 14 | url: '/food', 15 | method: 'GET' 16 | }, 17 | deleteFood: { 18 | name: '删除单个食品', 19 | url: '/food/delete', 20 | method: 'POST' 21 | }, 22 | updateFoodInfo: { 23 | name: '更新食品信息', 24 | url: '/food/update', 25 | method: 'POST' 26 | } 27 | } -------------------------------------------------------------------------------- /src/views/main/food/config/food-config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | add_config: { 4 | hasShop: '', 5 | 6 | // 显示食品分类 7 | options: [], 8 | 9 | // 图片验证 10 | food_avatar: true, 11 | 12 | // 除图片外的信息 13 | foodForm: { 14 | shop_id: '', 15 | name: '', 16 | cost: '', 17 | price: '', 18 | discount: '', 19 | extra: '', 20 | least: 1, 21 | single_point: false, 22 | foodClassify: [], 23 | }, 24 | // 最终上传的数据 25 | fileFormData: "", 26 | // 验证规则 27 | rules: { 28 | name: [ 29 | { required: true, message: "请输入食品名称", trigger: "blur" }, 30 | { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" }, 31 | ], 32 | cost: [ 33 | { required: true, message: "请输入食品成本", trigger: "change" }, 34 | ], 35 | price: [ 36 | { required: true, message: "请输入食品价格", trigger: "change" }, 37 | ], 38 | foodClassify: [ 39 | { required: true, message: "请选择/添加食品分类", trigger: "blur" }, 40 | ], 41 | discount: [ 42 | { required: true, message: "请输入食品折扣", trigger: "change" }, 43 | ], 44 | extra: [ 45 | { required: true, message: "请输入打包费", trigger: "change" }, 46 | ], 47 | 48 | }, 49 | 50 | // 请求头 51 | config: { 52 | headers: { 53 | "Content-Type": "multipart/form-data", 54 | }, 55 | } 56 | }, 57 | 58 | detail_config: { 59 | 60 | 61 | 62 | options: [], 63 | originData: "", 64 | 65 | fileFormData: "", 66 | 67 | foodForm: { 68 | name: '', 69 | cost: '', 70 | price: '', 71 | discount: '', 72 | extra: '', 73 | least: 0, 74 | single_point: false, 75 | foodClassify: [], 76 | desci: '', 77 | tips: '', 78 | package_content: '', 79 | main_material: '', 80 | secondary_material: '', 81 | meat_vegetable: '', 82 | weight: '', 83 | flavor: '', 84 | }, 85 | 86 | axios_config: { 87 | headers: { 88 | "Content-Type": "multipart/form-data", 89 | }, 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/views/main/food/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/views/main/food/router/shop-router.js: -------------------------------------------------------------------------------- 1 | export default { 2 | path: 'food', 3 | name: 'food', 4 | meta: { name: '食品中心' }, 5 | component: () => import('@/views/main/food/index.vue'), 6 | children: [ 7 | { 8 | path: 'add', 9 | name: 'add', 10 | meta: { name: '新增食品' }, 11 | component: () => import('@/views/main/food/web/add/add.vue') 12 | }, 13 | { 14 | path: 'info', 15 | name: 'foodInfo', 16 | meta: { name: '食品列表' }, 17 | component: () => import('@/views/main/food/web/info/info.vue') 18 | }, 19 | ] 20 | } -------------------------------------------------------------------------------- /src/views/main/food/web/add/add.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | 213 | 214 | -------------------------------------------------------------------------------- /src/views/main/food/web/info/detailForm.vue: -------------------------------------------------------------------------------- 1 | 178 | 179 | 271 | 272 | -------------------------------------------------------------------------------- /src/views/main/food/web/info/info.vue: -------------------------------------------------------------------------------- 1 | 103 | 104 | 174 | 175 | -------------------------------------------------------------------------------- /src/views/main/main.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 36 | 37 | -------------------------------------------------------------------------------- /src/views/main/order/api/readme.md: -------------------------------------------------------------------------------- 1 | order相关的api在shop-apis.js中 -------------------------------------------------------------------------------- /src/views/main/order/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/views/main/order/router/order-router.js: -------------------------------------------------------------------------------- 1 | export default { 2 | path: 'order', 3 | name: 'order', 4 | meta: { name: '订单中心' }, 5 | component: () => import('@/views/main/order/index.vue'), 6 | children: [ 7 | { 8 | path: 'today', 9 | name: 'today', 10 | meta: { name: '今日订单' }, 11 | component: () => import('@/views/main/order/web/today/today.vue') 12 | }, 13 | { 14 | path: 'all', 15 | name: 'all', 16 | meta: { name: '全部订单' }, 17 | component: () => import('@/views/main/order/web/all/all.vue') 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/views/main/order/web/all/all.vue: -------------------------------------------------------------------------------- 1 | 78 | 79 | 146 | 147 | -------------------------------------------------------------------------------- /src/views/main/order/web/today/today.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 147 | 148 | -------------------------------------------------------------------------------- /src/views/main/overview/overview.vue: -------------------------------------------------------------------------------- 1 | 137 | 138 | 143 | 144 | -------------------------------------------------------------------------------- /src/views/main/overview/router/overview-router.js: -------------------------------------------------------------------------------- 1 | export default { 2 | path: 'overview', 3 | name: 'overview', 4 | meta: { name: '系统总览' }, 5 | component: () => import('@/views/main/overview/overview.vue') 6 | } -------------------------------------------------------------------------------- /src/views/main/profile/api/user-apis.js: -------------------------------------------------------------------------------- 1 | export default { 2 | sellerLogin: { 3 | name: 'seller登录', 4 | url: "/seller/login", 5 | method: 'POST' 6 | }, 7 | sellerRegister: { 8 | name: "seller注册", 9 | url: "/seller/register", 10 | method: 'POST' 11 | }, 12 | getRealName: { 13 | name: '查询seller实名状态', 14 | url: "/seller/get_real_name", 15 | method: 'GET' 16 | }, 17 | createShop: { 18 | name: '创建店铺', 19 | url: '/seller/create_shop', 20 | method: 'POST' 21 | }, 22 | getSelfInfo: { 23 | name: '获取个人信息', 24 | url: '/seller/self_info', 25 | method: 'GET' 26 | }, 27 | updateAuthInfo: { 28 | name: "跟新实名信息", 29 | url: '/seller/update/auth', 30 | method: 'POST' 31 | }, 32 | deleteAccount: { 33 | name: '注销账号', 34 | url: '/seller/delete', 35 | method: 'DELETE' 36 | }, 37 | postFeedBack: { 38 | name: '工单反馈', 39 | url: '/seller/feedback/post', 40 | method: 'POST' 41 | }, 42 | getMyFeedback: { 43 | name: '获取我的工单', 44 | url: '/seller/feedback/self', 45 | method: 'GET' 46 | }, 47 | replyFeedback: { 48 | name: '回复工单', 49 | url: '/seller/feedback/reply', 50 | method: 'POST' 51 | }, 52 | replyEvaluate: { 53 | name: '回复评价', 54 | url: '/seller/evaluate/reply', 55 | method: 'POST' 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/views/main/profile/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/views/main/profile/router/profile-router.js: -------------------------------------------------------------------------------- 1 | export default { 2 | path: 'profile', 3 | name: 'profile', 4 | meta: { name: '个人中心' }, 5 | component: () => import('@/views/main/profile/index.vue'), 6 | children: [ 7 | { 8 | path: 'myself', 9 | name: 'myself', 10 | meta: { name: '我的信息' }, 11 | component: () => import('@/views/main/profile/web/myself/myself.vue') 12 | }, 13 | { 14 | path: 'feedback', 15 | name: 'feedback', 16 | meta: { name: '工单反馈' }, 17 | component: () => import('@/views/main/profile/web/feedback/feedback.vue') 18 | }, 19 | { 20 | path: 'my_feedback', 21 | name: 'my_feedback', 22 | meta: { name: '我的工单' }, 23 | component: () => import('@/views/main/profile/web/feedback/myFeedback.vue') 24 | }, 25 | ] 26 | } -------------------------------------------------------------------------------- /src/views/main/profile/web/feedback/feedback.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 74 | 75 | -------------------------------------------------------------------------------- /src/views/main/profile/web/feedback/myFeedback.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 112 | 113 | 138 | 139 | -------------------------------------------------------------------------------- /src/views/main/profile/web/feedback/富文本.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 65 | 66 | -------------------------------------------------------------------------------- /src/views/main/profile/web/myself/myself.vue: -------------------------------------------------------------------------------- 1 | 106 | 107 | 261 | 262 | -------------------------------------------------------------------------------- /src/views/main/shop/api/shop-apis.js: -------------------------------------------------------------------------------- 1 | export default { 2 | getClassify: { 3 | name: '获取店铺分类', 4 | url: "/shop/get_classify", 5 | method: 'GET', 6 | }, 7 | getShopExist: { 8 | name: '查询店铺名是否被注册', 9 | url: '/shop/shop_exist', 10 | method: 'POST', 11 | }, 12 | getHasShop: { 13 | name: '查询是否已经注册过店铺', 14 | url: '/shop/has_shop', 15 | method: 'GET', 16 | }, 17 | getShopInfo: { 18 | name: '获取店铺信息', 19 | url: '/shop/info', 20 | method: 'GET', 21 | }, 22 | updateShopInfo: { 23 | name: '更新店铺信息', 24 | url: '/shop/update', 25 | method: 'POST' 26 | }, 27 | updateActivities: { 28 | name: '更新店铺活动', 29 | url: '/shop/update/activities', 30 | method: 'POST' 31 | }, 32 | getOrdersToday: { 33 | name: '获取店铺今日订单', 34 | url: '/shop/orders/today', 35 | method: 'GET' 36 | }, 37 | getOrdersAll: { 38 | name: '获取店铺所有订单', 39 | url: '/shop/orders/all', 40 | method: 'GET' 41 | }, 42 | getBill: { 43 | name: '获取流水等信息', 44 | url: '/shop/bill', 45 | method: 'GET' 46 | }, 47 | getSold: { 48 | name: '获取店铺食品销售数量', 49 | url: "/shop/sold", 50 | method: 'GET' 51 | }, 52 | getMapData: { 53 | name: '获取订单的分布以及数量', 54 | url: '/shop/map_data', 55 | method: 'GET' 56 | }, 57 | getShopEvaluates: { 58 | name: '获取店铺评价', 59 | url: '/shop/evaluates', 60 | method: 'post' 61 | } 62 | } -------------------------------------------------------------------------------- /src/views/main/shop/config/echarts-config.js: -------------------------------------------------------------------------------- 1 | // 食品销售数 2 | export const soldOption = { 3 | tooltip: { 4 | trigger: "axis", 5 | axisPointer: { 6 | type: "shadow", 7 | }, 8 | }, 9 | grid: { 10 | left: "3%", 11 | right: "4%", 12 | bottom: "3%", 13 | containLabel: true, 14 | }, 15 | xAxis: [ 16 | { 17 | type: "category", 18 | data: [], 19 | axisTick: { 20 | alignWithLabel: true, 21 | }, 22 | }, 23 | ], 24 | yAxis: [ 25 | { 26 | type: "value", 27 | }, 28 | ], 29 | series: [ 30 | { 31 | name: "", 32 | type: "bar", 33 | barWidth: "60%", 34 | data: [], 35 | }, 36 | ], 37 | }; 38 | 39 | // 收入 40 | export const billOption = { 41 | title: { 42 | text: '收支明细', 43 | subtext: '汇总', 44 | left: 'center' 45 | }, 46 | tooltip: { 47 | trigger: 'item' 48 | }, 49 | legend: { 50 | orient: 'vertical', 51 | left: 'left' 52 | }, 53 | series: [ 54 | { 55 | type: 'pie', 56 | radius: '50%', 57 | data: [], 58 | emphasis: { 59 | itemStyle: { 60 | shadowBlur: 10, 61 | shadowOffsetX: 0, 62 | shadowColor: 'rgba(0, 0, 0, 0.5)' 63 | } 64 | } 65 | } 66 | ] 67 | }; 68 | 69 | export const mapOption = { 70 | backgroundColor: { 71 | type: 'linear', 72 | x: 0, 73 | y: 0, 74 | x2: 1, 75 | y2: 1, 76 | colorStops: [{ 77 | offset: 0, color: '#fff' // 0% 处的颜色 78 | }, { 79 | offset: 1, color: '#fff' // 100% 处的颜色 80 | }], 81 | globalCoord: false // 缺省为 false 82 | }, 83 | title: { 84 | top: 20, 85 | text: '订单在全国的数量分布', 86 | subtext: '鼠标左键拖动,中间放大缩小', 87 | x: 'center', 88 | textStyle: { 89 | color: '#ccc' 90 | } 91 | }, 92 | 93 | tooltip: { 94 | trigger: 'item', 95 | formatter: function (params) { 96 | if (typeof (params.value)[2] == "undefined") { 97 | return params.name + ' : ' + params.value; 98 | } else { 99 | return params.name + ' : ' + params.value[2]; 100 | } 101 | } 102 | }, 103 | /* legend: { 104 | orient: 'vertical', 105 | y: 'bottom', 106 | x: 'right', 107 | data:['pm2.5'], 108 | textStyle: { 109 | color: '#fff' 110 | } 111 | },*/ 112 | legend: { 113 | orient: 'vertical', 114 | y: 'bottom', 115 | x: 'right', 116 | data: ['pm2.5'], 117 | textStyle: { 118 | color: '#fff' 119 | } 120 | }, 121 | visualMap: { 122 | show: false, 123 | min: 0, 124 | max: 500, 125 | left: 'left', 126 | top: 'bottom', 127 | text: ['高', '低'], // 文本,默认为数值文本 128 | calculable: true, 129 | seriesIndex: [1], 130 | inRange: { 131 | 132 | } 133 | }, 134 | geo: { 135 | map: 'china', 136 | show: true, 137 | roam: true, 138 | label: { 139 | normal: { 140 | show: false 141 | }, 142 | emphasis: { 143 | show: false, 144 | } 145 | }, 146 | itemStyle: { 147 | normal: { 148 | areaColor: '#3a7fd5', 149 | borderColor: '#0a53e9',//线 150 | shadowColor: '#092f8f',//外发光 151 | shadowBlur: 20 152 | }, 153 | emphasis: { 154 | areaColor: '#0a2dae',//悬浮区背景 155 | } 156 | } 157 | }, 158 | series: [ 159 | { 160 | 161 | symbolSize: 5, 162 | label: { 163 | normal: { 164 | formatter: '{b}', 165 | position: 'right', 166 | show: true 167 | }, 168 | emphasis: { 169 | show: true 170 | } 171 | }, 172 | itemStyle: { 173 | normal: { 174 | color: '#fff' 175 | } 176 | }, 177 | name: 'light', 178 | type: 'scatter', 179 | coordinateSystem: 'geo', 180 | data: [], 181 | 182 | }, 183 | { 184 | type: 'map', 185 | map: 'china', 186 | geoIndex: 0, 187 | aspectScale: 0.75, //长宽比 188 | showLegendSymbol: false, // 存在legend时显示 189 | label: { 190 | normal: { 191 | show: false 192 | }, 193 | emphasis: { 194 | show: false, 195 | textStyle: { 196 | color: '#fff' 197 | } 198 | } 199 | }, 200 | roam: true, 201 | itemStyle: { 202 | normal: { 203 | areaColor: '#031525', 204 | borderColor: '#FFFFFF', 205 | }, 206 | emphasis: { 207 | areaColor: '#2B91B7' 208 | } 209 | }, 210 | animation: false, 211 | data: [] 212 | }, 213 | { 214 | name: 'Top 5', 215 | type: 'scatter', 216 | coordinateSystem: 'geo', 217 | symbol: 'pin', 218 | symbolSize: [50, 50], 219 | label: { 220 | normal: { 221 | show: true, 222 | textStyle: { 223 | color: '#fff', 224 | fontSize: 9, 225 | }, 226 | formatter(value) { 227 | return value.data.value[2] 228 | } 229 | } 230 | }, 231 | itemStyle: { 232 | normal: { 233 | color: '#D8BC58', //标志颜色 234 | } 235 | }, 236 | data: [], 237 | showEffectOn: 'render', 238 | rippleEffect: { 239 | brushType: 'stroke' 240 | }, 241 | hoverAnimation: true, 242 | zlevel: 1 243 | }, 244 | 245 | ] 246 | }; -------------------------------------------------------------------------------- /src/views/main/shop/config/shop-config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | realName: false,// 实名状态 3 | shopExist: false,// 店铺名是否已被注册 4 | hasShop: false,//是否已经注册过店铺 5 | options: [],// 展示级联菜单的数据 6 | dialogImageUrl: "",//dialog显示的图片 7 | dialogVisible: false, 8 | percentage: 0,//控制进度条 9 | fileFormData: "",// 向服务器提交的数据 10 | 11 | // 对图片进行表单验证 12 | status: { 13 | shop_avatar: true, 14 | shop_business: true, 15 | shop_health: true, 16 | }, 17 | // 收集除图片外的数据 18 | shopInfo: { 19 | seller_id: "", 20 | name: "", 21 | classify: { op_id: "", ch_id: "" }, 22 | address: "", 23 | start_time: "", 24 | end_time: "", 25 | mountain_plan: true, 26 | service: [], 27 | delivery: "", 28 | activities: [{ value: "" }], 29 | }, 30 | // 验证规则 31 | rules: { 32 | name: [ 33 | { required: true, message: "请输入店铺名称", trigger: "blur" }, 34 | { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" }, 35 | ], 36 | address: [ 37 | { required: true, message: "请输入店铺地址", trigger: "change" }, 38 | ], 39 | classify: [ 40 | { required: true, message: "请选择店铺分类", trigger: "blur" }, 41 | ], 42 | start_time: [ 43 | { 44 | required: true, 45 | message: "请选择时间", 46 | trigger: "change", 47 | }, 48 | ], 49 | end_time: [ 50 | { 51 | required: true, 52 | message: "请选择时间", 53 | trigger: "change", 54 | }, 55 | ], 56 | service: [ 57 | { 58 | type: "array", 59 | required: true, 60 | message: "请至少选择一个服务", 61 | trigger: "change", 62 | }, 63 | ], 64 | delivery: [ 65 | { required: true, message: "请选择配送形式", trigger: "change" }, 66 | ], 67 | }, 68 | 69 | // 请求头 70 | config: { 71 | headers: { 72 | "Content-Type": "multipart/form-data", 73 | }, 74 | }, 75 | 76 | // 回复评价的表单配置 77 | commentAddFormConfig: { 78 | options: ['reply'], 79 | reply: [ 80 | { 81 | type: 'textarea', label: '回复', prop: 'content', placeholder: '输入回复内容...', clearable: true, 82 | rows: 10, 83 | rules: [ 84 | { required: true, message: "内容不能为空", trigger: "blur" }, 85 | ] 86 | }, 87 | ], 88 | } 89 | } -------------------------------------------------------------------------------- /src/views/main/shop/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/views/main/shop/router/shop-router.js: -------------------------------------------------------------------------------- 1 | export default { 2 | path: 'shop', 3 | name: 'shop', 4 | meta: { name: '我的店铺' }, 5 | component: () => import('@/views/main/shop/index.vue'), 6 | children: [ 7 | { 8 | path: 'register', 9 | name: 'register', 10 | meta: { name: '注册店铺' }, 11 | component: () => import('@/views/main/shop/web/register/register.vue') 12 | }, 13 | { 14 | path: 'info', 15 | name: 'shopInfo', 16 | meta: { name: '店铺信息' }, 17 | component: () => import('@/views/main/shop/web/info/info.vue') 18 | }, 19 | { 20 | path: 'bill', 21 | name: 'bill', 22 | meta: { name: '店铺流水' }, 23 | component: () => import('@/views/main/shop/web/bill/bill.vue') 24 | }, 25 | { 26 | path: 'comment', 27 | name: 'comment', 28 | meta: { name: '店铺评价' }, 29 | component: () => import('@/views/main/shop/web/comment/comment.vue') 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /src/views/main/shop/web/bill/bill.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 109 | 110 | -------------------------------------------------------------------------------- /src/views/main/shop/web/comment/comment.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | 166 | 167 | -------------------------------------------------------------------------------- /src/views/main/shop/web/info/drawerForm.vue: -------------------------------------------------------------------------------- 1 | 131 | 132 | 236 | 237 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: process.env.NODE_ENV === 'production' ? './' : '/', 3 | productionSourceMap: process.env.NODE_ENV === 'production' ? false : true, 4 | devServer: { 5 | proxy: { 6 | '/api': { 7 | target: 'http://124.70.20.215:2140', 8 | }, 9 | }, 10 | // port: 8008 11 | }, 12 | }; 13 | --------------------------------------------------------------------------------