vue项目实战-电商后台管理系统

简介: vue项目实战-电商后台管理系统

项目简介:


该项目为电商后台的管理系统。设计了登录页面。

管理人员需要通过输入正确的用户名和密码才能登录。登陆成功之后进入管理页面:

管理页面由五个子模块组成:用户管理,权限管理,商品管理,订单管理,数据统计;

每个子模块有若干子模块组成,用户管理下->用户列表,权限管理->角色列表,权限管理,商品管理->商品列表,分类参数,商品分配,订单管理->订单列表,数据统计->数据报表

登录页面


登录页面中对用户输入的内容进行预校验,如果不符合要求则,则不向后端发送请求,同事挂载路由守卫,防止强制跳转。同时设置令牌校验,避免重复登录。如果用户输入格式正确的用户名以及密码时,向后端发送请求,请求通过则跳转到管理页面,否则返回登录页面。

路由导航守卫


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文件,按照说明运行即可

电商系统前端: 电商管理系统前端


学习资源


黑马程序员前端

目录
相关文章
|
4天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
vue学习第四章
|
4天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
vue学习第九章(v-model)
|
4天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
vue学习第十章(组件开发)
|
10天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
10天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
10天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
10天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
9天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
|
11天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
9天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉

相关实验场景

更多
下一篇
无影云桌面