项目简介:
该项目为电商后台的管理系统。设计了登录页面。
管理人员需要通过输入正确的用户名和密码才能登录。登陆成功之后进入管理页面:
管理页面由五个子模块组成:用户管理,权限管理,商品管理,订单管理,数据统计;
每个子模块有若干子模块组成,用户管理下->用户列表,权限管理->角色列表,权限管理,商品管理->商品列表,分类参数,商品分配,订单管理->订单列表,数据统计->数据报表
登录页面
登录页面中对用户输入的内容进行预校验,如果不符合要求则,则不向后端发送请求,同事挂载路由守卫,防止强制跳转。同时设置令牌校验,避免重复登录。如果用户输入格式正确的用户名以及密码时,向后端发送请求,请求通过则跳转到管理页面,否则返回登录页面。
1. // 挂载路由导航守卫 2. router.beforeEach((to, from, next) => { 3. // to 将要访问的路径 4. // from 代表从哪个路径跳转而来 5. // next 是一个函数,表示放行 6. // next() 放行 next('/login') 强制跳转 7. 8. if (to.path === '/login') return next() 9. // 获取token 10. const tokenStr = window.sessionStorage.getItem('token') 11. if (!tokenStr) return next('/login') 12. next() 13. })
登录页面核心代码:
1. <template> 2. <div class="login_container"> 3. <div class="login_box"> 4. <!-- 头像区域 --> 5. <div class="avatar_box"> 6. <img src="../assets/logo.png" alt=""> 7. </div> 8. <!-- 登录表单区域 --> 9. <el-form ref="loginFormRef" :model="loginForm" :rules="loginFormRules" label-width="0px" class="login_form"> 10. <!-- 用户名 --> 11. <el-form-item prop="username"> 12. <el-input v-model="loginForm.username" prefix-icon="iconfont icon-user"></el-input> 13. </el-form-item> 14. <!-- 密码 --> 15. <el-form-item prop="password"> 16. <el-input v-model="loginForm.password" prefix-icon="iconfont icon-3702mima" type="password"></el-input> 17. </el-form-item> 18. <!-- 按钮区域 --> 19. <el-form-item class="btns"> 20. <el-button type="primary" @click="login">登录</el-button> 21. <el-button type="info" @click="resetLoginForm">重置</el-button> 22. </el-form-item> 23. </el-form> 24. </div> 25. </div> 26. </template> 27. 28. <script> 29. export default { 30. data() { 31. return { 32. // 这是登录表单的数据绑定对象 33. loginForm: { 34. username: 'admin', 35. password: '123456' 36. }, 37. // 这是表单的验证规则对象 38. loginFormRules: { 39. // 验证用户名是否合法 40. username: [ 41. { required: true, message: '请输入登录名称', trigger: 'blur' }, 42. { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' } 43. ], 44. // 验证密码是否合法 45. password: [ 46. { required: true, message: '请输入登录密码', trigger: 'blur' }, 47. { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' } 48. ] 49. } 50. } 51. }, 52. methods: { 53. // 点击重置按钮,重置登录表单 54. resetLoginForm() { 55. // console.log(this); 56. this.$refs.loginFormRef.resetFields() 57. }, 58. login() { 59. this.$refs.loginFormRef.validate(async valid => { 60. if (!valid) return 61. const { data: res } = await this.$http.post('login', this.loginForm) 62. if (res.meta.status !== 200) return this.$message.error('登录失败!') 63. this.$message.success('登录成功') 64. // 1. 将登录成功之后的 token,保存到客户端的 sessionStorage 中 65. // 1.1 项目中出了登录之外的其他API接口,必须在登录之后才能访问 66. // 1.2 token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中 67. window.sessionStorage.setItem('token', res.data.token) 68. // 2. 通过编程式导航跳转到后台主页,路由地址是 /home 69. this.$router.push('/home') 70. }) 71. } 72. } 73. } 74. </script> 75. 76. <style lang="less" scoped> 77. .login_container { 78. background-color: #2b4b6b; 79. height: 100%; 80. } 81. 82. .login_box { 83. width: 450px; 84. height: 300px; 85. background-color: #fff; 86. border-radius: 3px; 87. position: absolute; 88. left: 50%; 89. top: 50%; 90. transform: translate(-50%, -50%); 91. 92. .avatar_box { 93. height: 130px; 94. width: 130px; 95. border: 1px solid #eee; 96. border-radius: 50%; 97. padding: 10px; 98. box-shadow: 0 0 10px #ddd; 99. position: absolute; 100. left: 50%; 101. transform: translate(-50%, -50%); 102. background-color: #fff; 103. img { 104. width: 100%; 105. height: 100%; 106. border-radius: 50%; 107. background-color: #eee; 108. } 109. } 110. } 111. 112. .login_form { 113. position: absolute; 114. bottom: 0; 115. width: 100%; 116. padding: 0 20px; 117. box-sizing: border-box; 118. } 119. 120. .btns { 121. display: flex; 122. justify-content: flex-end; 123. } 124. </style>
菜单实现
管理页面有一个侧面的两级菜单,菜单的数据来自于后端,点击二级菜单会跳转到
相应的子页面中。在el-menu中设置router属性,即可通过index添加到路由上进行跳转。
1. <template> 2. <el-container class="home-container"> 3. <!-- 头部区域 --> 4. <el-header> 5. <div> 6. <img src="../assets/heima.png" alt=""> 7. <span>电商后台管理系统</span> 8. </div> 9. <el-button type="info" @click="logout">退出</el-button> 10. </el-header> 11. <!-- 页面主体区域 --> 12. <el-container> 13. <!-- 侧边栏 --> 14. <el-aside :width="isCollapse ? '64px' : '200px'"> 15. <div class="toggle-button" @click="toggleCollapse">|||</div> 16. <!-- 侧边栏菜单区域 --> 17. <el-menu background-color="#333744" text-color="#fff" active-text-color="#409EFF" unique-opened :collapse="isCollapse" :collapse-transition="false" router :default-active="activePath"> 18. <!-- 一级菜单 --> 19. <el-submenu :index="item.id + ''" v-for="item in menulist" :key="item.id"> 20. <!-- 一级菜单的模板区域 --> 21. <template slot="title"> 22. <!-- 图标 --> 23. <i :class="iconsObj[item.id]"></i> 24. <!-- 文本 --> 25. <span>{{item.authName}}</span> 26. </template> 27. 28. <!-- 二级菜单 --> 29. <el-menu-item :index="'/' + subItem.path" v-for="subItem in item.children" 30. :key="subItem.id" @click="saveNavState('/' + subItem.path)"> 31. <template slot="title"> 32. <!-- 图标 --> 33. <i class="el-icon-menu"></i> 34. <!-- 文本 --> 35. <span>{{subItem.authName}}</span> 36. </template> 37. </el-menu-item> 38. </el-submenu> 39. </el-menu> 40. </el-aside> 41. <!-- 右侧内容主体 --> 42. <el-main> 43. <!-- 路由占位符 --> 44. <router-view></router-view> 45. </el-main> 46. </el-container> 47. </el-container> 48. </template> 49. 50. <script> 51. export default { 52. data() { 53. return { 54. // 左侧菜单数据 55. menulist: [], 56. iconsObj: { 57. '125': 'iconfont icon-user', 58. '103': 'iconfont icon-tijikongjian', 59. '101': 'iconfont icon-shangpin', 60. '102': 'iconfont icon-danju', 61. '145': 'iconfont icon-baobiao' 62. }, 63. // 是否折叠 64. isCollapse: false, 65. // 被激活的链接地址 66. activePath: '' 67. } 68. }, 69. created() { 70. this.getMenuList() 71. this.activePath = window.sessionStorage.getItem('activePath') 72. }, 73. methods: { 74. logout() { 75. window.sessionStorage.clear() 76. this.$router.push('/login') 77. }, 78. // 获取所有的菜单 79. async getMenuList() { 80. const { data: res } = await this.$http.get('menus') 81. if (res.meta.status !== 200) return this.$message.error(res.meta.msg) 82. this.menulist = res.data 83. console.log(res) 84. }, 85. // 点击按钮,切换菜单的折叠与展开 86. toggleCollapse() { 87. this.isCollapse = !this.isCollapse 88. }, 89. // 保存链接的激活状态 90. saveNavState(activePath) { 91. window.sessionStorage.setItem('activePath', activePath) 92. this.activePath = activePath 93. } 94. } 95. } 96. </script> 97. 98. <style lang="less" scoped> 99. .home-container { 100. height: 100%; 101. } 102. .el-header { 103. background-color: #373d41; 104. display: flex; 105. justify-content: space-between; 106. padding-left: 0; 107. align-items: center; 108. color: #fff; 109. font-size: 20px; 110. > div { 111. display: flex; 112. align-items: center; 113. span { 114. margin-left: 15px; 115. } 116. } 117. } 118. 119. .el-aside { 120. background-color: #333744; 121. .el-menu { 122. border-right: none; 123. } 124. } 125. 126. .el-main { 127. background-color: #eaedf1; 128. } 129. 130. .iconfont { 131. margin-right: 10px; 132. } 133. 134. .toggle-button { 135. background-color: #4a5064; 136. font-size: 10px; 137. line-height: 24px; 138. color: #fff; 139. text-align: center; 140. letter-spacing: 0.2em; 141. cursor: pointer; 142. } 143. </style> 144.
用户管理
用户列表
用户管理下有用户列表,这里渲染了后端的用户列表,可以编辑用户信息,删除用户,为用户分配角色,还可以对用户是否禁用进行管理;除此之外,还添加了查询用户,添加用户,和分页功能。
核心代码:
1. <template> 2. <div> 3. <!-- 面包屑导航区域 --> 4. <el-breadcrumb separator-class="el-icon-arrow-right"> 5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> 6. <el-breadcrumb-item>用户管理</el-breadcrumb-item> 7. <el-breadcrumb-item>用户列表</el-breadcrumb-item> 8. </el-breadcrumb> 9. 10. <!-- 卡片视图区域 --> 11. <el-card> 12. <!-- 搜索与添加区域 --> 13. <el-row :gutter="20"> 14. <el-col :span="8"> 15. <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getUserList"> 16. <el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button> 17. </el-input> 18. </el-col> 19. <el-col :span="4"> 20. <el-button type="primary" @click="addDialogVisible = true">添加用户</el-button> 21. </el-col> 22. </el-row> 23. 24. <!-- 用户列表区域 --> 25. <el-table :data="userlist" border stripe> 26. <el-table-column type="index"></el-table-column> 27. <el-table-column label="姓名" prop="username"></el-table-column> 28. <el-table-column label="邮箱" prop="email"></el-table-column> 29. <el-table-column label="电话" prop="mobile"></el-table-column> 30. <el-table-column label="角色" prop="role_name"></el-table-column> 31. <el-table-column label="状态"> 32. <template slot-scope="scope"> 33. <el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)"> 34. </el-switch> 35. </template> 36. </el-table-column> 37. <el-table-column label="操作" width="180px"> 38. <template slot-scope="scope"> 39. <!-- 修改按钮 --> 40. <el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.id)"></el-button> 41. <!-- 删除按钮 --> 42. <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeUserById(scope.row.id)"></el-button> 43. <!-- 分配角色按钮 --> 44. <el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false"> 45. <el-button type="warning" icon="el-icon-setting" size="mini" @click="setRole(scope.row)"></el-button> 46. </el-tooltip> 47. </template> 48. </el-table-column> 49. </el-table> 50. 51. <!-- 分页区域 --> 52. <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[1, 2, 5, 10]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"> 53. </el-pagination> 54. </el-card> 55. 56. <!-- 添加用户的对话框 --> 57. <el-dialog title="添加用户" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed"> 58. <!-- 内容主体区域 --> 59. <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="70px"> 60. <el-form-item label="用户名" prop="username"> 61. <el-input v-model="addForm.username"></el-input> 62. </el-form-item> 63. <el-form-item label="密码" prop="password"> 64. <el-input v-model="addForm.password"></el-input> 65. </el-form-item> 66. <el-form-item label="邮箱" prop="email"> 67. <el-input v-model="addForm.email"></el-input> 68. </el-form-item> 69. <el-form-item label="手机" prop="mobile"> 70. <el-input v-model="addForm.mobile"></el-input> 71. </el-form-item> 72. </el-form> 73. <!-- 底部区域 --> 74. <span slot="footer" class="dialog-footer"> 75. <el-button @click="addDialogVisible = false">取 消</el-button> 76. <el-button type="primary" @click="addUser">确 定</el-button> 77. </span> 78. </el-dialog> 79. 80. <!-- 修改用户的对话框 --> 81. <el-dialog title="修改用户" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed"> 82. <el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="70px"> 83. <el-form-item label="用户名"> 84. <el-input v-model="editForm.username" disabled></el-input> 85. </el-form-item> 86. <el-form-item label="邮箱" prop="email"> 87. <el-input v-model="editForm.email"></el-input> 88. </el-form-item> 89. <el-form-item label="手机" prop="mobile"> 90. <el-input v-model="editForm.mobile"></el-input> 91. </el-form-item> 92. </el-form> 93. <span slot="footer" class="dialog-footer"> 94. <el-button @click="editDialogVisible = false">取 消</el-button> 95. <el-button type="primary" @click="editUserInfo">确 定</el-button> 96. </span> 97. </el-dialog> 98. 99. <!-- 分配角色的对话框 --> 100. <el-dialog title="分配角色" :visible.sync="setRoleDialogVisible" width="50%" @close="setRoleDialogClosed"> 101. <div> 102. <p>当前的用户:{{userInfo.username}}</p> 103. <p>当前的角色:{{userInfo.role_name}}</p> 104. <p>分配新角色: 105. <el-select v-model="selectedRoleId" placeholder="请选择"> 106. <el-option v-for="item in rolesList" :key="item.id" :label="item.roleName" :value="item.id"> 107. </el-option> 108. </el-select> 109. </p> 110. </div> 111. <span slot="footer" class="dialog-footer"> 112. <el-button @click="setRoleDialogVisible = false">取 消</el-button> 113. <el-button type="primary" @click="saveRoleInfo">确 定</el-button> 114. </span> 115. </el-dialog> 116. </div> 117. </template> 118. 119. <script> 120. export default { 121. data() { 122. // 验证邮箱的规则 123. var checkEmail = (rule, value, cb) => { 124. // 验证邮箱的正则表达式 125. const regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/ 126. 127. if (regEmail.test(value)) { 128. // 合法的邮箱 129. return cb() 130. } 131. 132. cb(new Error('请输入合法的邮箱')) 133. } 134. 135. // 验证手机号的规则 136. var checkMobile = (rule, value, cb) => { 137. // 验证手机号的正则表达式 138. const regMobile = /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/ 139. 140. if (regMobile.test(value)) { 141. return cb() 142. } 143. 144. cb(new Error('请输入合法的手机号')) 145. } 146. 147. return { 148. // 获取用户列表的参数对象 149. queryInfo: { 150. query: '', 151. // 当前的页数 152. pagenum: 1, 153. // 当前每页显示多少条数据 154. pagesize: 2 155. }, 156. userlist: [], 157. total: 0, 158. // 控制添加用户对话框的显示与隐藏 159. addDialogVisible: false, 160. // 添加用户的表单数据 161. addForm: { 162. username: '', 163. password: '', 164. email: '', 165. mobile: '' 166. }, 167. // 添加表单的验证规则对象 168. addFormRules: { 169. username: [ 170. { required: true, message: '请输入用户名', trigger: 'blur' }, 171. { 172. min: 3, 173. max: 10, 174. message: '用户名的长度在3~10个字符之间', 175. trigger: 'blur' 176. } 177. ], 178. password: [ 179. { required: true, message: '请输入密码', trigger: 'blur' }, 180. { 181. min: 6, 182. max: 15, 183. message: '用户名的长度在6~15个字符之间', 184. trigger: 'blur' 185. } 186. ], 187. email: [ 188. { required: true, message: '请输入邮箱', trigger: 'blur' }, 189. { validator: checkEmail, trigger: 'blur' } 190. ], 191. mobile: [ 192. { required: true, message: '请输入手机号', trigger: 'blur' }, 193. { validator: checkMobile, trigger: 'blur' } 194. ] 195. }, 196. // 控制修改用户对话框的显示与隐藏 197. editDialogVisible: false, 198. // 查询到的用户信息对象 199. editForm: {}, 200. // 修改表单的验证规则对象 201. editFormRules: { 202. email: [ 203. { required: true, message: '请输入用户邮箱', trigger: 'blur' }, 204. { validator: checkEmail, trigger: 'blur' } 205. ], 206. mobile: [ 207. { required: true, message: '请输入用户手机', trigger: 'blur' }, 208. { validator: checkMobile, trigger: 'blur' } 209. ] 210. }, 211. // 控制分配角色对话框的显示与隐藏 212. setRoleDialogVisible: false, 213. // 需要被分配角色的用户信息 214. userInfo: {}, 215. // 所有角色的数据列表 216. rolesList: [], 217. // 已选中的角色Id值 218. selectedRoleId: '' 219. } 220. }, 221. created() { 222. this.getUserList() 223. }, 224. methods: { 225. async getUserList() { 226. const { data: res } = await this.$http.get('users', { 227. params: this.queryInfo 228. }) 229. if (res.meta.status !== 200) { 230. return this.$message.error('获取用户列表失败!') 231. } 232. this.userlist = res.data.users 233. this.total = res.data.total 234. console.log(res) 235. }, 236. // 监听 pagesize 改变的事件 237. handleSizeChange(newSize) { 238. // console.log(newSize) 239. this.queryInfo.pagesize = newSize 240. this.getUserList() 241. }, 242. // 监听 页码值 改变的事件 243. handleCurrentChange(newPage) { 244. console.log(newPage) 245. this.queryInfo.pagenum = newPage 246. this.getUserList() 247. }, 248. // 监听 switch 开关状态的改变 249. async userStateChanged(userinfo) { 250. console.log(userinfo) 251. const { data: res } = await this.$http.put( 252. `users/${userinfo.id}/state/${userinfo.mg_state}` 253. ) 254. if (res.meta.status !== 200) { 255. userinfo.mg_state = !userinfo.mg_state 256. return this.$message.error('更新用户状态失败!') 257. } 258. this.$message.success('更新用户状态成功!') 259. }, 260. // 监听添加用户对话框的关闭事件 261. addDialogClosed() { 262. this.$refs.addFormRef.resetFields() 263. }, 264. // 点击按钮,添加新用户 265. addUser() { 266. this.$refs.addFormRef.validate(async valid => { 267. if (!valid) return 268. // 可以发起添加用户的网络请求 269. const { data: res } = await this.$http.post('users', this.addForm) 270. 271. if (res.meta.status !== 201) { 272. this.$message.error('添加用户失败!') 273. } 274. 275. this.$message.success('添加用户成功!') 276. // 隐藏添加用户的对话框 277. this.addDialogVisible = false 278. // 重新获取用户列表数据 279. this.getUserList() 280. }) 281. }, 282. // 展示编辑用户的对话框 283. async showEditDialog(id) { 284. // console.log(id) 285. const { data: res } = await this.$http.get('users/' + id) 286. 287. if (res.meta.status !== 200) { 288. return this.$message.error('查询用户信息失败!') 289. } 290. 291. this.editForm = res.data 292. this.editDialogVisible = true 293. }, 294. // 监听修改用户对话框的关闭事件 295. editDialogClosed() { 296. this.$refs.editFormRef.resetFields() 297. }, 298. // 修改用户信息并提交 299. editUserInfo() { 300. this.$refs.editFormRef.validate(async valid => { 301. if (!valid) return 302. // 发起修改用户信息的数据请求 303. const { data: res } = await this.$http.put( 304. 'users/' + this.editForm.id, 305. { 306. email: this.editForm.email, 307. mobile: this.editForm.mobile 308. } 309. ) 310. 311. if (res.meta.status !== 200) { 312. return this.$message.error('更新用户信息失败!') 313. } 314. 315. // 关闭对话框 316. this.editDialogVisible = false 317. // 刷新数据列表 318. this.getUserList() 319. // 提示修改成功 320. this.$message.success('更新用户信息成功!') 321. }) 322. }, 323. // 根据Id删除对应的用户信息 324. async removeUserById(id) { 325. // 弹框询问用户是否删除数据 326. const confirmResult = await this.$confirm( 327. '此操作将永久删除该用户, 是否继续?', 328. '提示', 329. { 330. confirmButtonText: '确定', 331. cancelButtonText: '取消', 332. type: 'warning' 333. } 334. ).catch(err => err) 335. 336. // 如果用户确认删除,则返回值为字符串 confirm 337. // 如果用户取消了删除,则返回值为字符串 cancel 338. // console.log(confirmResult) 339. if (confirmResult !== 'confirm') { 340. return this.$message.info('已取消删除') 341. } 342. 343. const { data: res } = await this.$http.delete('users/' + id) 344. 345. if (res.meta.status !== 200) { 346. return this.$message.error('删除用户失败!') 347. } 348. 349. this.$message.success('删除用户成功!') 350. this.getUserList() 351. }, 352. // 展示分配角色的对话框 353. async setRole(userInfo) { 354. this.userInfo = userInfo 355. 356. // 在展示对话框之前,获取所有角色的列表 357. const { data: res } = await this.$http.get('roles') 358. if (res.meta.status !== 200) { 359. return this.$message.error('获取角色列表失败!') 360. } 361. 362. this.rolesList = res.data 363. 364. this.setRoleDialogVisible = true 365. }, 366. // 点击按钮,分配角色 367. async saveRoleInfo() { 368. if (!this.selectedRoleId) { 369. return this.$message.error('请选择要分配的角色!') 370. } 371. 372. const { data: res } = await this.$http.put( 373. `users/${this.userInfo.id}/role`, 374. { 375. rid: this.selectedRoleId 376. } 377. ) 378. 379. if (res.meta.status !== 200) { 380. return this.$message.error('更新角色失败!') 381. } 382. 383. this.$message.success('更新角色成功!') 384. this.getUserList() 385. this.setRoleDialogVisible = false 386. }, 387. // 监听分配角色对话框的关闭事件 388. setRoleDialogClosed() { 389. this.selectedRoleId = '' 390. this.userInfo = {} 391. } 392. } 393. } 394. </script> 395. 396. <style lang="less" scoped> 397. </style>
权限管理
角色列表
角色列表中可以创建新的角色,创建的新的角色可以在用户管理中赋予用户,同时可以为已有的角色赋予权限
1. <template> 2. <div> 3. <!-- 面包屑导航区域 --> 4. <el-breadcrumb separator-class="el-icon-arrow-right"> 5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> 6. <el-breadcrumb-item>权限管理</el-breadcrumb-item> 7. <el-breadcrumb-item>角色列表</el-breadcrumb-item> 8. </el-breadcrumb> 9. 10. <!-- 卡片视图 --> 11. <el-card> 12. <!-- 添加角色按钮区域 --> 13. <el-row> 14. <el-col> 15. <el-button type="primary">添加角色</el-button> 16. </el-col> 17. </el-row> 18. 19. <!-- 角色列表区域 --> 20. <el-table :data="rolelist" border stripe> 21. <!-- 展开列 --> 22. <el-table-column type="expand"> 23. <template slot-scope="scope"> 24. <el-row :class="['bdbottom', i1 === 0 ? 'bdtop' : '', 'vcenter']" v-for="(item1, i1) in scope.row.children" :key="item1.id"> 25. <!-- 渲染一级权限 --> 26. <el-col :span="5"> 27. <el-tag closable @close="removeRightById(scope.row, item1.id)">{{item1.authName}}</el-tag> 28. <i class="el-icon-caret-right"></i> 29. </el-col> 30. <!-- 渲染二级和三级权限 --> 31. <el-col :span="19"> 32. <!-- 通过 for 循环 嵌套渲染二级权限 --> 33. <el-row :class="[i2 === 0 ? '' : 'bdtop', 'vcenter']" v-for="(item2, i2) in item1.children" :key="item2.id"> 34. <el-col :span="6"> 35. <el-tag type="success" closable @close="removeRightById(scope.row, item2.id)">{{item2.authName}}</el-tag> 36. <i class="el-icon-caret-right"></i> 37. </el-col> 38. <el-col :span="18"> 39. <el-tag type="warning" v-for="item3 in item2.children" :key="item3.id" closable @close="removeRightById(scope.row, item3.id)">{{item3.authName}}</el-tag> 40. </el-col> 41. </el-row> 42. </el-col> 43. </el-row> 44. 45. <!-- <pre> 46. {{scope.row}} 47. </pre> --> 48. </template> 49. </el-table-column> 50. <!-- 索引列 --> 51. <el-table-column type="index"></el-table-column> 52. <el-table-column label="角色名称" prop="roleName"></el-table-column> 53. <el-table-column label="角色描述" prop="roleDesc"></el-table-column> 54. <el-table-column label="操作" width="300px"> 55. <template slot-scope="scope"> 56. <el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button> 57. <el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button> 58. <el-button size="mini" type="warning" icon="el-icon-setting" @click="showSetRightDialog(scope.row)">分配权限</el-button> 59. </template> 60. </el-table-column> 61. </el-table> 62. </el-card> 63. 64. <!-- 分配权限的对话框 --> 65. <el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="50%" @close="setRightDialogClosed"> 66. <!-- 树形控件 --> 67. <el-tree :data="rightslist" :props="treeProps" show-checkbox node-key="id" default-expand-all :default-checked-keys="defKeys" ref="treeRef"></el-tree> 68. <span slot="footer" class="dialog-footer"> 69. <el-button @click="setRightDialogVisible = false">取 消</el-button> 70. <el-button type="primary" @click="allotRights">确 定</el-button> 71. </span> 72. </el-dialog> 73. </div> 74. </template> 75. 76. <script> 77. export default { 78. data() { 79. return { 80. // 所有角色列表数据 81. rolelist: [], 82. // 控制分配权限对话框的显示与隐藏 83. setRightDialogVisible: false, 84. // 所有权限的数据 85. rightslist: [], 86. // 树形控件的属性绑定对象 87. treeProps: { 88. label: 'authName', 89. children: 'children' 90. }, 91. // 默认选中的节点Id值数组 92. defKeys: [], 93. // 当前即将分配权限的角色id 94. roleId: '' 95. } 96. }, 97. created() { 98. this.getRolesList() 99. }, 100. methods: { 101. // 获取所有角色的列表 102. async getRolesList() { 103. const { data: res } = await this.$http.get('roles') 104. 105. if (res.meta.status !== 200) { 106. return this.$message.error('获取角色列表失败!') 107. } 108. 109. this.rolelist = res.data 110. 111. console.log(this.rolelist) 112. }, 113. // 根据Id删除对应的权限 114. async removeRightById(role, rightId) { 115. // 弹框提示用户是否要删除 116. const confirmResult = await this.$confirm( 117. '此操作将永久删除该文件, 是否继续?', 118. '提示', 119. { 120. confirmButtonText: '确定', 121. cancelButtonText: '取消', 122. type: 'warning' 123. } 124. ).catch(err => err) 125. 126. if (confirmResult !== 'confirm') { 127. return this.$message.info('取消了删除!') 128. } 129. 130. const { data: res } = await this.$http.delete( 131. `roles/${role.id}/rights/${rightId}` 132. ) 133. 134. if (res.meta.status !== 200) { 135. return this.$message.error('删除权限失败!') 136. } 137. 138. // this.getRolesList() 139. role.children = res.data 140. }, 141. // 展示分配权限的对话框 142. async showSetRightDialog(role) { 143. this.roleId = role.id 144. // 获取所有权限的数据 145. const { data: res } = await this.$http.get('rights/tree') 146. 147. if (res.meta.status !== 200) { 148. return this.$message.error('获取权限数据失败!') 149. } 150. 151. // 把获取到的权限数据保存到 data 中 152. this.rightslist = res.data 153. console.log(this.rightslist) 154. 155. // 递归获取三级节点的Id 156. this.getLeafKeys(role, this.defKeys) 157. 158. this.setRightDialogVisible = true 159. }, 160. // 通过递归的形式,获取角色下所有三级权限的id,并保存到 defKeys 数组中 161. getLeafKeys(node, arr) { 162. // 如果当前 node 节点不包含 children 属性,则是三级节点 163. if (!node.children) { 164. return arr.push(node.id) 165. } 166. 167. node.children.forEach(item => this.getLeafKeys(item, arr)) 168. }, 169. // 监听分配权限对话框的关闭事件 170. setRightDialogClosed() { 171. this.defKeys = [] 172. }, 173. // 点击为角色分配权限 174. async allotRights() { 175. const keys = [ 176. ...this.$refs.treeRef.getCheckedKeys(), 177. ...this.$refs.treeRef.getHalfCheckedKeys() 178. ] 179. 180. const idStr = keys.join(',') 181. 182. const { data: res } = await this.$http.post( 183. `roles/${this.roleId}/rights`, 184. { rids: idStr } 185. ) 186. 187. if (res.meta.status !== 200) { 188. return this.$message.error('分配权限失败!') 189. } 190. 191. this.$message.success('分配权限成功!') 192. this.getRolesList() 193. this.setRightDialogVisible = false 194. } 195. } 196. } 197. </script> 198. 199. <style lang="less" scoped> 200. .el-tag { 201. margin: 7px; 202. } 203. 204. .bdtop { 205. border-top: 1px solid #eee; 206. } 207. 208. .bdbottom { 209. border-bottom: 1px solid #eee; 210. } 211. 212. .vcenter { 213. display: flex; 214. align-items: center; 215. } 216. </style>
权限列表
权限列表对不同的权限做出展示,只发送一个请求即可获取所有需要的数据
1. <template> 2. <div> 3. <!-- 面包屑导航区域 --> 4. <el-breadcrumb separator-class="el-icon-arrow-right"> 5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> 6. <el-breadcrumb-item>权限管理</el-breadcrumb-item> 7. <el-breadcrumb-item>权限列表</el-breadcrumb-item> 8. </el-breadcrumb> 9. 10. <!-- 卡片视图 --> 11. <el-card> 12. <el-table :data="rightsList" border stripe> 13. <el-table-column type="index"></el-table-column> 14. <el-table-column label="权限名称" prop="authName"></el-table-column> 15. <el-table-column label="路径" prop="path"></el-table-column> 16. <el-table-column label="权限等级" prop="level"> 17. <template slot-scope="scope"> 18. <el-tag v-if="scope.row.level === '0'">一级</el-tag> 19. <el-tag type="success" v-else-if="scope.row.level === '1'">二级</el-tag> 20. <el-tag type="warning" v-else>三级</el-tag> 21. </template> 22. </el-table-column> 23. </el-table> 24. </el-card> 25. </div> 26. </template> 27. 28. <script> 29. export default { 30. data() { 31. return { 32. // 权限列表 33. rightsList: [] 34. } 35. }, 36. created() { 37. // 获取所有的权限 38. this.getRightsList() 39. }, 40. methods: { 41. // 获取权限列表 42. async getRightsList() { 43. const { data: res } = await this.$http.get('rights/list') 44. if (res.meta.status !== 200) { 45. return this.$message.error('获取权限列表失败!') 46. } 47. 48. this.rightsList = res.data 49. console.log(this.rightsList) 50. } 51. } 52. } 53. </script> 54. 55. <style lang="less" scoped> 56. </style>
商品管理
商品分类
1. <template> 2. <div> 3. <!-- 面包屑导航区域 --> 4. <el-breadcrumb separator-class="el-icon-arrow-right"> 5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> 6. <el-breadcrumb-item>商品管理</el-breadcrumb-item> 7. <el-breadcrumb-item>商品分类</el-breadcrumb-item> 8. </el-breadcrumb> 9. 10. <!-- 卡片视图区域 --> 11. <el-card> 12. <el-row> 13. <el-col> 14. <el-button type="primary" @click="showAddCateDialog">添加分类</el-button> 15. </el-col> 16. </el-row> 17. 18. <!-- 表格 --> 19. <tree-table class="treeTable" :data="catelist" :columns="columns" :selection-type="false" :expand-type="false" show-index index-text="#" border :show-row-hover="false"> 20. <!-- 是否有效 --> 21. <template slot="isok" slot-scope="scope"> 22. <i class="el-icon-success" v-if="scope.row.cat_deleted === false" style="color: lightgreen;"></i> 23. <i class="el-icon-error" v-else style="color: red;"></i> 24. </template> 25. <!-- 排序 --> 26. <template slot="order" slot-scope="scope"> 27. <el-tag size="mini" v-if="scope.row.cat_level===0">一级</el-tag> 28. <el-tag type="success" size="mini" v-else-if="scope.row.cat_level===1">二级</el-tag> 29. <el-tag type="warning" size="mini" v-else>三级</el-tag> 30. </template> 31. <!-- 操作 --> 32. <template slot="opt"> 33. <el-button type="primary" icon="el-icon-edit" size="mini">编辑</el-button> 34. <el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button> 35. </template> 36. </tree-table> 37. 38. <!-- 分页区域 --> 39. <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="querInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="querInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"> 40. </el-pagination> 41. </el-card> 42. 43. <!-- 添加分类的对话框 --> 44. <el-dialog title="添加分类" :visible.sync="addCateDialogVisible" width="50%" @close="addCateDialogClosed"> 45. <!-- 添加分类的表单 --> 46. <el-form :model="addCateForm" :rules="addCateFormRules" ref="addCateFormRef" label-width="100px"> 47. <el-form-item label="分类名称:" prop="cat_name"> 48. <el-input v-model="addCateForm.cat_name"></el-input> 49. </el-form-item> 50. <el-form-item label="父级分类:"> 51. <!-- options 用来指定数据源 --> 52. <!-- props 用来指定配置对象 --> 53. <el-cascader expand-trigger="hover" :options="parentCateList" :props="cascaderProps" v-model="selectedKeys" @change="parentCateChanged" clearable change-on-select> 54. </el-cascader> 55. </el-form-item> 56. </el-form> 57. <span slot="footer" class="dialog-footer"> 58. <el-button @click="addCateDialogVisible = false">取 消</el-button> 59. <el-button type="primary" @click="addCate">确 定</el-button> 60. </span> 61. </el-dialog> 62. </div> 63. </template> 64. 65. <script> 66. export default { 67. data() { 68. return { 69. // 查询条件 70. querInfo: { 71. type: 3, 72. pagenum: 1, 73. pagesize: 5 74. }, 75. // 商品分类的数据列表,默认为空 76. catelist: [], 77. // 总数据条数 78. total: 0, 79. // 为table指定列的定义 80. columns: [ 81. { 82. label: '分类名称', 83. prop: 'cat_name' 84. }, 85. { 86. label: '是否有效', 87. // 表示,将当前列定义为模板列 88. type: 'template', 89. // 表示当前这一列使用模板名称 90. template: 'isok' 91. }, 92. { 93. label: '排序', 94. // 表示,将当前列定义为模板列 95. type: 'template', 96. // 表示当前这一列使用模板名称 97. template: 'order' 98. }, 99. { 100. label: '操作', 101. // 表示,将当前列定义为模板列 102. type: 'template', 103. // 表示当前这一列使用模板名称 104. template: 'opt' 105. } 106. ], 107. // 控制添加分类对话框的显示与隐藏 108. addCateDialogVisible: false, 109. // 添加分类的表单数据对象 110. addCateForm: { 111. // 将要添加的分类的名称 112. cat_name: '', 113. // 父级分类的Id 114. cat_pid: 0, 115. // 分类的等级,默认要添加的是1级分类 116. cat_level: 0 117. }, 118. // 添加分类表单的验证规则对象 119. addCateFormRules: { 120. cat_name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }] 121. }, 122. // 父级分类的列表 123. parentCateList: [], 124. // 指定级联选择器的配置对象 125. cascaderProps: { 126. value: 'cat_id', 127. label: 'cat_name', 128. children: 'children' 129. }, 130. // 选中的父级分类的Id数组 131. selectedKeys: [] 132. } 133. }, 134. created() { 135. this.getCateList() 136. }, 137. methods: { 138. // 获取商品分类数据 139. async getCateList() { 140. const { data: res } = await this.$http.get('categories', { 141. params: this.querInfo 142. }) 143. 144. if (res.meta.status !== 200) { 145. return this.$message.error('获取商品分类失败!') 146. } 147. 148. console.log(res.data) 149. // 把数据列表,赋值给 catelist 150. this.catelist = res.data.result 151. // 为总数据条数赋值 152. this.total = res.data.total 153. }, 154. // 监听 pagesize 改变 155. handleSizeChange(newSize) { 156. this.querInfo.pagesize = newSize 157. this.getCateList() 158. }, 159. // 监听 pagenum 改变 160. handleCurrentChange(newPage) { 161. this.querInfo.pagenum = newPage 162. this.getCateList() 163. }, 164. // 点击按钮,展示添加分类的对话框 165. showAddCateDialog() { 166. // 先获取父级分类的数据列表 167. this.getParentCateList() 168. // 再展示出对话框 169. this.addCateDialogVisible = true 170. }, 171. // 获取父级分类的数据列表 172. async getParentCateList() { 173. const { data: res } = await this.$http.get('categories', { 174. params: { type: 2 } 175. }) 176. 177. if (res.meta.status !== 200) { 178. return this.$message.error('获取父级分类数据失败!') 179. } 180. 181. console.log(res.data) 182. this.parentCateList = res.data 183. }, 184. // 选择项发生变化触发这个函数 185. parentCateChanged() { 186. console.log(this.selectedKeys) 187. // 如果 selectedKeys 数组中的 length 大于0,证明选中的父级分类 188. // 反之,就说明没有选中任何父级分类 189. if (this.selectedKeys.length > 0) { 190. // 父级分类的Id 191. this.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1] 192. // 为当前分类的等级赋值 193. this.addCateForm.cat_level = this.selectedKeys.length 194. } else { 195. // 父级分类的Id 196. this.addCateForm.cat_pid = 0 197. // 为当前分类的等级赋值 198. this.addCateForm.cat_level = 0 199. } 200. }, 201. // 点击按钮,添加新的分类 202. addCate() { 203. this.$refs.addCateFormRef.validate(async valid => { 204. if (!valid) return 205. const { data: res } = await this.$http.post('categories', this.addCateForm) 206. 207. if (res.meta.status !== 201) { 208. return this.$message.error('添加分类失败!') 209. } 210. 211. this.$message.success('添加分类成功!') 212. this.getCateList() 213. this.addCateDialogVisible = false 214. }) 215. }, 216. // 监听对话框的关闭事件,重置表单数据 217. addCateDialogClosed() { 218. this.$refs.addCateFormRef.resetFields() 219. this.selectedKeys = [] 220. this.addCateForm.cat_level = 0 221. this.addCateForm.cat_pid = 0 222. } 223. } 224. } 225. </script> 226. 227. <style lang="less" scoped> 228. .treeTable { 229. margin-top: 15px; 230. } 231. 232. .el-cascader { 233. width: 100%; 234. } 235. </style>
商品列表
1. <template> 2. <div> 3. <!-- 面包屑导航区域 --> 4. <el-breadcrumb separator-class="el-icon-arrow-right"> 5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> 6. <el-breadcrumb-item>商品管理</el-breadcrumb-item> 7. <el-breadcrumb-item>商品列表</el-breadcrumb-item> 8. </el-breadcrumb> 9. 10. <!-- 卡片视图区域 --> 11. <el-card> 12. <el-row :gutter="20"> 13. <el-col :span="8"> 14. <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getGoodsList"> 15. <el-button slot="append" icon="el-icon-search" @click="getGoodsList"></el-button> 16. </el-input> 17. </el-col> 18. <el-col :span="4"> 19. <el-button type="primary" @click="goAddpage">添加商品</el-button> 20. </el-col> 21. </el-row> 22. 23. <!-- table表格区域 --> 24. <el-table :data="goodslist" border stripe> 25. <el-table-column type="index"></el-table-column> 26. <el-table-column label="商品名称" prop="goods_name"></el-table-column> 27. <el-table-column label="商品价格(元)" prop="goods_price" width="95px"></el-table-column> 28. <el-table-column label="商品重量" prop="goods_weight" width="70px"></el-table-column> 29. <el-table-column label="创建时间" prop="add_time" width="140px"> 30. <template slot-scope="scope"> 31. {{scope.row.add_time | dateFormat}} 32. </template> 33. </el-table-column> 34. <el-table-column label="操作" width="130px"> 35. <template slot-scope="scope"> 36. <el-button type="primary" icon="el-icon-edit" size="mini"></el-button> 37. <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeById(scope.row.goods_id)"></el-button> 38. </template> 39. </el-table-column> 40. </el-table> 41. 42. <!-- 分页区域 --> 43. <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[5, 10, 15, 20]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total" background> 44. </el-pagination> 45. </el-card> 46. </div> 47. </template> 48. 49. <script> 50. export default { 51. data() { 52. return { 53. // 查询参数对象 54. queryInfo: { 55. query: '', 56. pagenum: 1, 57. pagesize: 10 58. }, 59. // 商品列表 60. goodslist: [], 61. // 总数据条数 62. total: 0 63. } 64. }, 65. created() { 66. this.getGoodsList() 67. }, 68. methods: { 69. // 根据分页获取对应的商品列表 70. async getGoodsList() { 71. const { data: res } = await this.$http.get('goods', { 72. params: this.queryInfo 73. }) 74. 75. if (res.meta.status !== 200) { 76. return this.$message.error('获取商品列表失败!') 77. } 78. 79. this.$message.success('获取商品列表成功!') 80. console.log(res.data) 81. this.goodslist = res.data.goods 82. this.total = res.data.total 83. }, 84. handleSizeChange(newSize) { 85. this.queryInfo.pagesize = newSize 86. this.getGoodsList() 87. }, 88. handleCurrentChange(newPage) { 89. this.queryInfo.pagenum = newPage 90. this.getGoodsList() 91. }, 92. async removeById(id) { 93. const confirmResult = await this.$confirm( 94. '此操作将永久删除该商品, 是否继续?', 95. '提示', 96. { 97. confirmButtonText: '确定', 98. cancelButtonText: '取消', 99. type: 'warning' 100. } 101. ).catch(err => err) 102. 103. if (confirmResult !== 'confirm') { 104. return this.$message.info('已经取消删除!') 105. } 106. 107. const { data: res } = await this.$http.delete(`goods/${id}`) 108. 109. if (res.meta.status !== 200) { 110. return this.$message.error('删除失败!') 111. } 112. 113. this.$message.success('删除成功!') 114. this.getGoodsList() 115. }, 116. goAddpage() { 117. this.$router.push('/goods/add') 118. } 119. } 120. } 121. </script> 122. 123. <style lang="less" scoped> 124. </style>
增加商品
在商品分类中点击新增商品,则跳转到新增商品窗口
1. <template> 2. <div> 3. <!-- 面包屑导航区域 --> 4. <el-breadcrumb separator-class="el-icon-arrow-right"> 5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> 6. <el-breadcrumb-item>商品管理</el-breadcrumb-item> 7. <el-breadcrumb-item>添加商品</el-breadcrumb-item> 8. </el-breadcrumb> 9. 10. <!-- 卡片视图 --> 11. <el-card> 12. <!-- 提示区域 --> 13. <el-alert title="添加商品信息" type="info" center show-icon :closable="false"> 14. </el-alert> 15. <!-- 步骤条区域 --> 16. <el-steps :space="200" :active="activeIndex - 0" finish-status="success" align-center> 17. <el-step title="基本信息"></el-step> 18. <el-step title="商品参数"></el-step> 19. <el-step title="商品属性"></el-step> 20. <el-step title="商品图片"></el-step> 21. <el-step title="商品内容"></el-step> 22. <el-step title="完成"></el-step> 23. </el-steps> 24. 25. <!-- tab栏区域 --> 26. 27. <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top"> 28. <el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave" @tab-click="tabClicked"> 29. <el-tab-pane label="基本信息" name="0"> 30. <el-form-item label="商品名称" prop="goods_name"> 31. <el-input v-model="addForm.goods_name"></el-input> 32. </el-form-item> 33. <el-form-item label="商品价格" prop="goods_price"> 34. <el-input v-model="addForm.goods_price" type="number"></el-input> 35. </el-form-item> 36. <el-form-item label="商品重量" prop="goods_weight"> 37. <el-input v-model="addForm.goods_weight" type="number"></el-input> 38. </el-form-item> 39. <el-form-item label="商品数量" prop="goods_number"> 40. <el-input v-model="addForm.goods_number" type="number"></el-input> 41. </el-form-item> 42. <el-form-item label="商品分类" prop="goods_cat"> 43. <el-cascader expand-trigger="hover" :options="catelist" :props="cateProps" v-model="addForm.goods_cat" @change="handleChange"> 44. </el-cascader> 45. </el-form-item> 46. </el-tab-pane> 47. <el-tab-pane label="商品参数" name="1"> 48. <!-- 渲染表单的Item项 --> 49. <el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id"> 50. <!-- 复选框组 --> 51. <el-checkbox-group v-model="item.attr_vals"> 52. <el-checkbox :label="cb" v-for="(cb, i) in item.attr_vals" :key="i" border></el-checkbox> 53. </el-checkbox-group> 54. </el-form-item> 55. </el-tab-pane> 56. <el-tab-pane label="商品属性" name="2"> 57. <el-form-item :label="item.attr_name" v-for="item in onlyTableData" :key="item.attr_id"> 58. <el-input v-model="item.attr_vals"></el-input> 59. </el-form-item> 60. </el-tab-pane> 61. <el-tab-pane label="商品图片" name="3"> 62. <!-- action 表示图片要上传到的后台API地址 --> 63. <el-upload :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove" list-type="picture" :headers="headerObj" :on-success="handleSuccess"> 64. <el-button size="small" type="primary">点击上传</el-button> 65. </el-upload> 66. </el-tab-pane> 67. <el-tab-pane label="商品内容" name="4"> 68. <!-- 富文本编辑器组件 --> 69. <quill-editor v-model="addForm.goods_introduce"></quill-editor> 70. <!-- 添加商品的按钮 --> 71. <el-button type="primary" class="btnAdd" @click="add">添加商品</el-button> 72. </el-tab-pane> 73. </el-tabs> 74. </el-form> 75. 76. </el-card> 77. 78. <!-- 图片预览 --> 79. <el-dialog title="图片预览" :visible.sync="previewVisible" width="50%"> 80. <img :src="previewPath" alt="" class="previewImg"> 81. </el-dialog> 82. </div> 83. </template> 84. 85. <script> 86. import _ from 'lodash' 87. 88. export default { 89. data() { 90. return { 91. activeIndex: '0', 92. // 添加商品的表单数据对象 93. addForm: { 94. goods_name: '', 95. goods_price: 0, 96. goods_weight: 0, 97. goods_number: 0, 98. // 商品所属的分类数组 99. goods_cat: [], 100. // 图片的数组 101. pics: [], 102. // 商品的详情描述 103. goods_introduce: '', 104. attrs: [] 105. }, 106. addFormRules: { 107. goods_name: [ 108. { required: true, message: '请输入商品名称', trigger: 'blur' } 109. ], 110. goods_price: [ 111. { required: true, message: '请输入商品价格', trigger: 'blur' } 112. ], 113. goods_weight: [ 114. { required: true, message: '请输入商品重量', trigger: 'blur' } 115. ], 116. goods_number: [ 117. { required: true, message: '请输入商品数量', trigger: 'blur' } 118. ], 119. goods_cat: [ 120. { required: true, message: '请选择商品分类', trigger: 'blur' } 121. ] 122. }, 123. // 商品分类列表 124. catelist: [], 125. cateProps: { 126. label: 'cat_name', 127. value: 'cat_id', 128. children: 'children' 129. }, 130. // 动态参数列表数据 131. manyTableData: [], 132. // 静态属性列表数据 133. onlyTableData: [], 134. // 上传图片的URL地址 135. uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload', 136. // 图片上传组件的headers请求头对象 137. headerObj: { 138. Authorization: window.sessionStorage.getItem('token') 139. }, 140. previewPath: '', 141. previewVisible: false 142. } 143. }, 144. created() { 145. this.getCateList() 146. }, 147. methods: { 148. // 获取所有商品分类数据 149. async getCateList() { 150. const { data: res } = await this.$http.get('categories') 151. 152. if (res.meta.status !== 200) { 153. return this.$message.error('获取商品分类数据失败!') 154. } 155. 156. this.catelist = res.data 157. console.log(this.catelist) 158. }, 159. // 级联选择器选中项变化,会触发这个函数 160. handleChange() { 161. console.log(this.addForm.goods_cat) 162. if (this.addForm.goods_cat.length !== 3) { 163. this.addForm.goods_cat = [] 164. } 165. }, 166. beforeTabLeave(activeName, oldActiveName) { 167. // console.log('即将离开的标签页名字是:' + oldActiveName) 168. // console.log('即将进入的标签页名字是:' + activeName) 169. // return false 170. if (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) { 171. this.$message.error('请先选择商品分类!') 172. return false 173. } 174. }, 175. async tabClicked() { 176. // console.log(this.activeIndex) 177. // 证明访问的是动态参数面板 178. if (this.activeIndex === '1') { 179. const { data: res } = await this.$http.get( 180. `categories/${this.cateId}/attributes`, 181. { 182. params: { sel: 'many' } 183. } 184. ) 185. 186. if (res.meta.status !== 200) { 187. return this.$message.error('获取动态参数列表失败!') 188. } 189. 190. console.log(res.data) 191. res.data.forEach(item => { 192. item.attr_vals = 193. item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ') 194. }) 195. this.manyTableData = res.data 196. } else if (this.activeIndex === '2') { 197. const { data: res } = await this.$http.get( 198. `categories/${this.cateId}/attributes`, 199. { 200. params: { sel: 'only' } 201. } 202. ) 203. 204. if (res.meta.status !== 200) { 205. return this.$message.error('获取静态属性失败!') 206. } 207. 208. console.log(res.data) 209. this.onlyTableData = res.data 210. } 211. }, 212. // 处理图片预览效果 213. handlePreview(file) { 214. console.log(file) 215. this.previewPath = file.response.data.url 216. this.previewVisible = true 217. }, 218. // 处理移除图片的操作 219. handleRemove(file) { 220. // console.log(file) 221. // 1. 获取将要删除的图片的临时路径 222. const filePath = file.response.data.tmp_path 223. // 2. 从 pics 数组中,找到这个图片对应的索引值 224. const i = this.addForm.pics.findIndex(x => x.pic === filePath) 225. // 3. 调用数组的 splice 方法,把图片信息对象,从 pics 数组中移除 226. this.addForm.pics.splice(i, 1) 227. console.log(this.addForm) 228. }, 229. // 监听图片上传成功的事件 230. handleSuccess(response) { 231. console.log(response) 232. // 1. 拼接得到一个图片信息对象 233. const picInfo = { pic: response.data.tmp_path } 234. // 2. 将图片信息对象,push 到pics数组中 235. this.addForm.pics.push(picInfo) 236. console.log(this.addForm) 237. }, 238. // 添加商品 239. add() { 240. this.$refs.addFormRef.validate(async valid => { 241. if (!valid) { 242. return this.$message.error('请填写必要的表单项!') 243. } 244. // 执行添加的业务逻辑 245. // lodash cloneDeep(obj) 246. const form = _.cloneDeep(this.addForm) 247. form.goods_cat = form.goods_cat.join(',') 248. // 处理动态参数 249. this.manyTableData.forEach(item => { 250. const newInfo = { 251. attr_id: item.attr_id, 252. attr_value: item.attr_vals.join(' ') 253. } 254. this.addForm.attrs.push(newInfo) 255. }) 256. // 处理静态属性 257. this.onlyTableData.forEach(item => { 258. const newInfo = { attr_id: item.attr_id, attr_value: item.attr_vals } 259. this.addForm.attrs.push(newInfo) 260. }) 261. form.attrs = this.addForm.attrs 262. console.log(form) 263. 264. // 发起请求添加商品 265. // 商品的名称,必须是唯一的 266. const { data: res } = await this.$http.post('goods', form) 267. 268. if (res.meta.status !== 201) { 269. return this.$message.error('添加商品失败!') 270. } 271. 272. this.$message.success('添加商品成功!') 273. this.$router.push('/goods') 274. }) 275. } 276. }, 277. computed: { 278. cateId() { 279. if (this.addForm.goods_cat.length === 3) { 280. return this.addForm.goods_cat[2] 281. } 282. return null 283. } 284. } 285. } 286. </script> 287. 288. <style lang="less" scoped> 289. .el-checkbox { 290. margin: 0 10px 0 0 !important; 291. } 292. 293. .previewImg { 294. width: 100%; 295. } 296. 297. .btnAdd { 298. margin-top: 15px; 299. } 300. </style>
分类参数
在分类参数中选择一件商品,可以为其添加静态或者动态参数,这个参数可以展示在移动端商品的属性中。
1. <template> 2. <div> 3. <!-- 面包屑导航区域 --> 4. <el-breadcrumb separator-class="el-icon-arrow-right"> 5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> 6. <el-breadcrumb-item>商品管理</el-breadcrumb-item> 7. <el-breadcrumb-item>参数列表</el-breadcrumb-item> 8. </el-breadcrumb> 9. 10. <!-- 卡片视图区域 --> 11. <el-card> 12. <!-- 警告区域 --> 13. <el-alert show-icon title="注意:只允许为第三级分类设置相关参数!" type="warning" :closable="false"></el-alert> 14. 15. <!-- 选择商品分类区域 --> 16. <el-row class="cat_opt"> 17. <el-col> 18. <span>选择商品分类:</span> 19. <!-- 选择商品分类的级联选择框 --> 20. <el-cascader expand-trigger="hover" :options="catelist" :props="cateProps" v-model="selectedCateKeys" @change="handleChange"> 21. </el-cascader> 22. </el-col> 23. </el-row> 24. 25. <!-- tab 页签区域 --> 26. <el-tabs v-model="activeName" @tab-click="handleTabClick"> 27. <!-- 添加动态参数的面板 --> 28. <el-tab-pane label="动态参数" name="many"> 29. <!-- 添加参数的按钮 --> 30. <el-button type="primary" size="mini" :disabled="isBtnDisabled" @click="addDialogVisible=true">添加参数</el-button> 31. <!-- 动态参数表格 --> 32. <el-table :data="manyTableData" border stripe> 33. <!-- 展开行 --> 34. <el-table-column type="expand"> 35. <template slot-scope="scope"> 36. <!-- 循环渲染Tag标签 --> 37. <el-tag v-for="(item, i) in scope.row.attr_vals" :key="i" closable @close="handleClose(i, scope.row)">{{item}}</el-tag> 38. <!-- 输入的文本框 --> 39. <el-input class="input-new-tag" v-if="scope.row.inputVisible" v-model="scope.row.inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm(scope.row)" @blur="handleInputConfirm(scope.row)"> 40. </el-input> 41. <!-- 添加按钮 --> 42. <el-button v-else class="button-new-tag" size="small" @click="showInput(scope.row)">+ New Tag</el-button> 43. </template> 44. </el-table-column> 45. <!-- 索引列 --> 46. <el-table-column type="index"></el-table-column> 47. <el-table-column label="参数名称" prop="attr_name"></el-table-column> 48. <el-table-column label="操作"> 49. <template slot-scope="scope"> 50. <el-button size="mini" type="primary" icon="el-icon-edit" @click="showEditDialog(scope.row.attr_id)">编辑</el-button> 51. <el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">删除</el-button> 52. </template> 53. </el-table-column> 54. </el-table> 55. </el-tab-pane> 56. <!-- 添加静态属性的面板 --> 57. <el-tab-pane label="静态属性" name="only"> 58. <!-- 添加属性的按钮 --> 59. <el-button type="primary" size="mini" :disabled="isBtnDisabled" @click="addDialogVisible=true">添加属性</el-button> 60. <!-- 静态属性表格 --> 61. <el-table :data="onlyTableData" border stripe> 62. <!-- 展开行 --> 63. <el-table-column type="expand"> 64. <template slot-scope="scope"> 65. <!-- 循环渲染Tag标签 --> 66. <el-tag v-for="(item, i) in scope.row.attr_vals" :key="i" closable @close="handleClose(i, scope.row)">{{item}}</el-tag> 67. <!-- 输入的文本框 --> 68. <el-input class="input-new-tag" v-if="scope.row.inputVisible" v-model="scope.row.inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm(scope.row)" @blur="handleInputConfirm(scope.row)"> 69. </el-input> 70. <!-- 添加按钮 --> 71. <el-button v-else class="button-new-tag" size="small" @click="showInput(scope.row)">+ New Tag</el-button> 72. </template> 73. </el-table-column> 74. <!-- 索引列 --> 75. <el-table-column type="index"></el-table-column> 76. <el-table-column label="属性名称" prop="attr_name"></el-table-column> 77. <el-table-column label="操作"> 78. <template slot-scope="scope"> 79. <el-button size="mini" type="primary" icon="el-icon-edit" @click="showEditDialog(scope.row.attr_id)">编辑</el-button> 80. <el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">删除</el-button> 81. </template> 82. </el-table-column> 83. </el-table> 84. </el-tab-pane> 85. </el-tabs> 86. </el-card> 87. 88. <!-- 添加参数的对话框 --> 89. <el-dialog :title="'添加' + titleText" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed"> 90. <!-- 添加参数的对话框 --> 91. <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px"> 92. <el-form-item :label="titleText" prop="attr_name"> 93. <el-input v-model="addForm.attr_name"></el-input> 94. </el-form-item> 95. </el-form> 96. <span slot="footer" class="dialog-footer"> 97. <el-button @click="addDialogVisible = false">取 消</el-button> 98. <el-button type="primary" @click="addParams">确 定</el-button> 99. </span> 100. </el-dialog> 101. 102. <!-- 修改参数的对话框 --> 103. <el-dialog :title="'修改' + titleText" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed"> 104. <!-- 添加参数的对话框 --> 105. <el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px"> 106. <el-form-item :label="titleText" prop="attr_name"> 107. <el-input v-model="editForm.attr_name"></el-input> 108. </el-form-item> 109. </el-form> 110. <span slot="footer" class="dialog-footer"> 111. <el-button @click="editDialogVisible = false">取 消</el-button> 112. <el-button type="primary" @click="editParams">确 定</el-button> 113. </span> 114. </el-dialog> 115. </div> 116. </template> 117. 118. <script> 119. export default { 120. data() { 121. return { 122. // 商品分类列表 123. catelist: [], 124. // 级联选择框的配置对象 125. cateProps: { 126. value: 'cat_id', 127. label: 'cat_name', 128. children: 'children' 129. }, 130. // 级联选择框双向绑定到的数组 131. selectedCateKeys: [], 132. // 被激活的页签的名称 133. activeName: 'many', 134. // 动态参数的数据 135. manyTableData: [], 136. // 静态属性的数据 137. onlyTableData: [], 138. // 控制添加对话框的显示与隐藏 139. addDialogVisible: false, 140. // 添加参数的表单数据对象 141. addForm: { 142. attr_name: '' 143. }, 144. // 添加表单的验证规则对象 145. addFormRules: { 146. attr_name: [ 147. { required: true, message: '请输入参数名称', trigger: 'blur' } 148. ] 149. }, 150. // 控制修改对话框的显示与隐藏 151. editDialogVisible: false, 152. // 修改的表单数据对象 153. editForm: {}, 154. // 修改表单的验证规则对象 155. editFormRules: { 156. attr_name: [ 157. { required: true, message: '请输入参数名称', trigger: 'blur' } 158. ] 159. } 160. } 161. }, 162. created() { 163. this.getCateList() 164. }, 165. methods: { 166. // 获取所有的商品分类列表 167. async getCateList() { 168. const { data: res } = await this.$http.get('categories') 169. if (res.meta.status !== 200) { 170. return this.$message.error('获取商品分类失败!') 171. } 172. 173. this.catelist = res.data 174. 175. console.log(this.catelist) 176. }, 177. // 级联选择框选中项变化,会触发这个函数 178. handleChange() { 179. this.getParamsData() 180. }, 181. // tab 页签点击事件的处理函数 182. handleTabClick() { 183. console.log(this.activeName) 184. this.getParamsData() 185. }, 186. // 获取参数的列表数据 187. async getParamsData() { 188. // 证明选中的不是三级分类 189. if (this.selectedCateKeys.length !== 3) { 190. this.selectedCateKeys = [] 191. this.manyTableData = [] 192. this.onlyTableData = [] 193. return 194. } 195. 196. // 证明选中的是三级分类 197. console.log(this.selectedCateKeys) 198. // 根据所选分类的Id,和当前所处的面板,获取对应的参数 199. const { data: res } = await this.$http.get( 200. `categories/${this.cateId}/attributes`, 201. { 202. params: { sel: this.activeName } 203. } 204. ) 205. 206. if (res.meta.status !== 200) { 207. return this.$message.error('获取参数列表失败!') 208. } 209. 210. res.data.forEach(item => { 211. item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : [] 212. // 控制文本框的显示与隐藏 213. item.inputVisible = false 214. // 文本框中输入的值 215. item.inputValue = '' 216. }) 217. 218. console.log(res.data) 219. if (this.activeName === 'many') { 220. this.manyTableData = res.data 221. } else { 222. this.onlyTableData = res.data 223. } 224. }, 225. // 监听添加对话框的关闭事件 226. addDialogClosed() { 227. this.$refs.addFormRef.resetFields() 228. }, 229. // 点击按钮,添加参数 230. addParams() { 231. this.$refs.addFormRef.validate(async valid => { 232. if (!valid) return 233. const { data: res } = await this.$http.post( 234. `categories/${this.cateId}/attributes`, 235. { 236. attr_name: this.addForm.attr_name, 237. attr_sel: this.activeName 238. } 239. ) 240. 241. if (res.meta.status !== 201) { 242. return this.$message.error('添加参数失败!') 243. } 244. 245. this.$message.success('添加参数成功!') 246. this.addDialogVisible = false 247. this.getParamsData() 248. }) 249. }, 250. // 点击按钮,展示修改的对话框 251. async showEditDialog(attrId) { 252. // 查询当前参数的信息 253. const { data: res } = await this.$http.get( 254. `categories/${this.cateId}/attributes/${attrId}`, 255. { 256. params: { attr_sel: this.activeName } 257. } 258. ) 259. 260. if (res.meta.status !== 200) { 261. return this.$message.error('获取参数信息失败!') 262. } 263. 264. this.editForm = res.data 265. this.editDialogVisible = true 266. }, 267. // 重置修改的表单 268. editDialogClosed() { 269. this.$refs.editFormRef.resetFields() 270. }, 271. // 点击按钮,修改参数信息 272. editParams() { 273. this.$refs.editFormRef.validate(async valid => { 274. if (!valid) return 275. const { data: res } = await this.$http.put( 276. `categories/${this.cateId}/attributes/${this.editForm.attr_id}`, 277. { attr_name: this.editForm.attr_name, attr_sel: this.activeName } 278. ) 279. 280. if (res.meta.status !== 200) { 281. return this.$message.error('修改参数失败!') 282. } 283. 284. this.$message.success('修改参数成功!') 285. this.getParamsData() 286. this.editDialogVisible = false 287. }) 288. }, 289. // 根据Id删除对应的参数项 290. async removeParams(attrId) { 291. const confirmResult = await this.$confirm( 292. '此操作将永久删除该参数, 是否继续?', 293. '提示', 294. { 295. confirmButtonText: '确定', 296. cancelButtonText: '取消', 297. type: 'warning' 298. } 299. ).catch(err => err) 300. 301. // 用户取消了删除的操作 302. if (confirmResult !== 'confirm') { 303. return this.$message.info('已取消删除!') 304. } 305. 306. // 删除的业务逻辑 307. const { data: res } = await this.$http.delete( 308. `categories/${this.cateId}/attributes/${attrId}` 309. ) 310. 311. if (res.meta.status !== 200) { 312. return this.$message.error('删除参数失败!') 313. } 314. 315. this.$message.success('删除参数成功!') 316. this.getParamsData() 317. }, 318. // 文本框失去焦点,或摁下了 Enter 都会触发 319. async handleInputConfirm(row) { 320. if (row.inputValue.trim().length === 0) { 321. row.inputValue = '' 322. row.inputVisible = false 323. return 324. } 325. // 如果没有return,则证明输入的内容,需要做后续处理 326. row.attr_vals.push(row.inputValue.trim()) 327. row.inputValue = '' 328. row.inputVisible = false 329. // 需要发起请求,保存这次操作 330. this.saveAttrVals(row) 331. }, 332. // 将对 attr_vals 的操作,保存到数据库 333. async saveAttrVals(row) { 334. // 需要发起请求,保存这次操作 335. const { data: res } = await this.$http.put( 336. `categories/${this.cateId}/attributes/${row.attr_id}`, 337. { 338. attr_name: row.attr_name, 339. attr_sel: row.attr_sel, 340. attr_vals: row.attr_vals.join(' ') 341. } 342. ) 343. 344. if (res.meta.status !== 200) { 345. return this.$message.error('修改参数项失败!') 346. } 347. 348. this.$message.success('修改参数项成功!') 349. }, 350. // 点击按钮,展示文本输入框 351. showInput(row) { 352. row.inputVisible = true 353. // 让文本框自动获得焦点 354. // $nextTick 方法的作用,就是当页面上元素被重新渲染之后,才会指定回调函数中的代码 355. this.$nextTick(_ => { 356. this.$refs.saveTagInput.$refs.input.focus() 357. }) 358. }, 359. // 删除对应的参数可选项 360. handleClose(i, row) { 361. row.attr_vals.splice(i, 1) 362. this.saveAttrVals(row) 363. } 364. }, 365. computed: { 366. // 如果按钮需要被禁用,则返回true,否则返回false 367. isBtnDisabled() { 368. if (this.selectedCateKeys.length !== 3) { 369. return true 370. } 371. return false 372. }, 373. // 当前选中的三级分类的Id 374. cateId() { 375. if (this.selectedCateKeys.length === 3) { 376. return this.selectedCateKeys[2] 377. } 378. return null 379. }, 380. // 动态计算标题的文本 381. titleText() { 382. if (this.activeName === 'many') { 383. return '动态参数' 384. } 385. return '静态属性' 386. } 387. } 388. } 389. </script> 390. 391. <style lang="less" scoped> 392. .cat_opt { 393. margin: 15px 0; 394. } 395. 396. .el-tag { 397. margin: 10px; 398. } 399. 400. .input-new-tag { 401. width: 120px; 402. } 403. </style>
订单管理
订单列表
订单管理的实现和用户管理有很多类似的地方,都是向后端发送请求然后渲染到页面上
1. <template> 2. <div> 3. <!-- 面包屑导航区域 --> 4. <el-breadcrumb separator-class="el-icon-arrow-right"> 5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> 6. <el-breadcrumb-item>订单管理</el-breadcrumb-item> 7. <el-breadcrumb-item>订单列表</el-breadcrumb-item> 8. </el-breadcrumb> 9. 10. <!-- 卡片视图区域 --> 11. <el-card> 12. <el-row> 13. <el-col :span="8"> 14. <el-input placeholder="请输入内容"> 15. <el-button slot="append" icon="el-icon-search"></el-button> 16. </el-input> 17. </el-col> 18. </el-row> 19. 20. <!-- 订单列表数据 --> 21. <el-table :data="orderlist" border stripe> 22. <el-table-column type="index"></el-table-column> 23. <el-table-column label="订单编号" prop="order_number"></el-table-column> 24. <el-table-column label="订单价格" prop="order_price"></el-table-column> 25. <el-table-column label="是否付款" prop="pay_status"> 26. <template slot-scope="scope"> 27. <el-tag type="success" v-if="scope.row.pay_status === '1'">已付款</el-tag> 28. <el-tag type="danger" v-else>未付款</el-tag> 29. </template> 30. </el-table-column> 31. <el-table-column label="是否发货" prop="is_send"> 32. <template slot-scope="scope"> 33. <template> 34. {{scope.row.is_send}} 35. </template> 36. </template> 37. </el-table-column> 38. <el-table-column label="下单时间" prop="create_time"> 39. <template slot-scope="scope"> 40. {{scope.row.create_time | dateFormat}} 41. </template> 42. </el-table-column> 43. <el-table-column label="操作"> 44. <template> 45. <el-button size="mini" type="primary" icon="el-icon-edit" @click="showBox"></el-button> 46. <el-button size="mini" type="success" icon="el-icon-location" @click="showProgressBox"></el-button> 47. </template> 48. </el-table-column> 49. </el-table> 50. 51. <!-- 分页区域 --> 52. <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[5, 10, 15]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"> 53. </el-pagination> 54. </el-card> 55. 56. <!-- 修改地址的对话框 --> 57. <el-dialog title="修改地址" :visible.sync="addressVisible" width="50%" @close="addressDialogClosed"> 58. <el-form :model="addressForm" :rules="addressFormRules" ref="addressFormRef" label-width="100px"> 59. <el-form-item label="省市区/县" prop="address1"> 60. <el-cascader :options="cityData" v-model="addressForm.address1"></el-cascader> 61. </el-form-item> 62. <el-form-item label="详细地址" prop="address2"> 63. <el-input v-model="addressForm.address2"></el-input> 64. </el-form-item> 65. </el-form> 66. <span slot="footer" class="dialog-footer"> 67. <el-button @click="addressVisible = false">取 消</el-button> 68. <el-button type="primary" @click="addressVisible = false">确 定</el-button> 69. </span> 70. </el-dialog> 71. 72. <!-- 展示物流进度的对话框 --> 73. <el-dialog title="物流进度" :visible.sync="progressVisible" width="50%"> 74. <!-- 时间线 --> 75. <el-timeline> 76. <el-timeline-item v-for="(activity, index) in progressInfo" :key="index" :timestamp="activity.time"> 77. {{activity.context}} 78. </el-timeline-item> 79. </el-timeline> 80. </el-dialog> 81. </div> 82. </template> 83. 84. <script> 85. import cityData from './citydata.js' 86. 87. export default { 88. data() { 89. return { 90. queryInfo: { 91. query: '', 92. pagenum: 1, 93. pagesize: 10 94. }, 95. total: 0, 96. orderlist: [], 97. addressVisible: false, 98. addressForm: { 99. address1: [], 100. address2: '' 101. }, 102. addressFormRules: { 103. address1: [ 104. { required: true, message: '请选择省市区县', trigger: 'blur' } 105. ], 106. address2: [ 107. { required: true, message: '请填写详细地址', trigger: 'blur' } 108. ] 109. }, 110. cityData, 111. progressVisible: false, 112. progressInfo: [] 113. } 114. }, 115. created() { 116. this.getOrderList() 117. }, 118. methods: { 119. async getOrderList() { 120. const { data: res } = await this.$http.get('orders', { 121. params: this.queryInfo 122. }) 123. 124. if (res.meta.status !== 200) { 125. return this.$message.error('获取订单列表失败!') 126. } 127. 128. console.log(res) 129. this.total = res.data.total 130. this.orderlist = res.data.goods 131. }, 132. handleSizeChange(newSize) { 133. this.queryInfo.pagesize = newSize 134. this.getOrderList() 135. }, 136. handleCurrentChange(newPage) { 137. this.queryInfo.pagenum = newPage 138. this.getOrderList() 139. }, 140. // 展示修改地址的对话框 141. showBox() { 142. this.addressVisible = true 143. }, 144. addressDialogClosed() { 145. this.$refs.addressFormRef.resetFields() 146. }, 147. async showProgressBox() { 148. const { data: res } = await this.$http.get('/kuaidi/804909574412544580') 149. 150. if (res.meta.status !== 200) { 151. return this.$message.error('获取物流进度失败!') 152. } 153. 154. this.progressInfo = res.data 155. 156. this.progressVisible = true 157. console.log(this.progressInfo) 158. } 159. } 160. } 161. </script> 162. 163. <style lang="less" scoped> 164. @import '../../plugins/timeline/timeline.css'; 165. @import '../../plugins/timeline-item/timeline-item.css'; 166. 167. .el-cascader { 168. width: 100%; 169. } 170. </style>
数据统计
数据报表
数据统计部分用到了echarts,从后端获得数据后通过 _.merge()将数据组合在一起,最后渲染在页面上
1. <template> 2. <div> 3. <!-- 面包屑导航区域 --> 4. <el-breadcrumb separator-class="el-icon-arrow-right"> 5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> 6. <el-breadcrumb-item>数据统计</el-breadcrumb-item> 7. <el-breadcrumb-item>数据报表</el-breadcrumb-item> 8. </el-breadcrumb> 9. 10. <!-- 卡片视图区域 --> 11. <el-card> 12. <!-- 2. 为ECharts准备一个具备大小(宽高)的Dom --> 13. <div id="main" style="width: 750px;height:400px;"></div> 14. </el-card> 15. </div> 16. </template> 17. 18. <script> 19. // 1. 导入 echarts 20. import echarts from 'echarts' 21. import _ from 'lodash' 22. 23. export default { 24. data() { 25. return { 26. // 需要合并的数据 27. options: { 28. title: { 29. text: '用户来源' 30. }, 31. tooltip: { 32. trigger: 'axis', 33. axisPointer: { 34. type: 'cross', 35. label: { 36. backgroundColor: '#E9EEF3' 37. } 38. } 39. }, 40. grid: { 41. left: '3%', 42. right: '4%', 43. bottom: '3%', 44. containLabel: true 45. }, 46. xAxis: [ 47. { 48. boundaryGap: false 49. } 50. ], 51. yAxis: [ 52. { 53. type: 'value' 54. } 55. ] 56. } 57. } 58. }, 59. created() {}, 60. // 此时,页面上的元素,已经被渲染完毕了! 61. async mounted() { 62. // 3. 基于准备好的dom,初始化echarts实例 63. var myChart = echarts.init(document.getElementById('main')) 64. 65. const { data: res } = await this.$http.get('reports/type/1') 66. if (res.meta.status !== 200) { 67. return this.$message.error('获取折线图数据失败!') 68. } 69. 70. // 4. 准备数据和配置项 71. const result = _.merge(res.data, this.options) 72. 73. // 5. 展示数据 74. myChart.setOption(result) 75. }, 76. methods: {} 77. } 78. </script> 79. 80. <style lang="less" scoped> 81. </style>
项目git地址
目录中有后端以及sql文件,按照说明运行即可