image.png
image.png
添加、编辑

用的是同一个编辑面板

image.png
删除
image.png

1-1 user.vue

添加用户就等于注册用户,目前密码默认都是6个1

src/views/system/user/index.vue

  1. <template>
  2. <div class="user-container">
  3. <h2>用户管理</h2>
  4. <el-form :inline="true" :model="formQuery" ref="queryFormRef">
  5. <el-form-item label="用户名" prop="username">
  6. <el-input v-model="formQuery.username" placeholder="请输入用户名"></el-input>
  7. </el-form-item>
  8. <el-form-item label="手机号" prop="mobile">
  9. <el-input v-model="formQuery.mobile" placeholder="请输入手机号"></el-input>
  10. </el-form-item>
  11. <el-form-item label="状态" prop="status">
  12. <el-select v-model="formQuery.status" placeholder="状态">
  13. <el-option label="全部" value="all"></el-option>
  14. <el-option label="禁用" :value="0"></el-option>
  15. <el-option label="正常" :value="1"></el-option>
  16. </el-select>
  17. </el-form-item>
  18. <el-form-item>
  19. <el-button type="primary" @click="handleSubmitQuery">查询</el-button>
  20. <el-button type="default" @click="handleResetFeilds">重置</el-button>
  21. </el-form-item>
  22. </el-form>
  23. <div class="role-list">
  24. <el-button
  25. type="primary"
  26. plain
  27. icon="el-icon-plus"
  28. @click="handleAddUser"
  29. >添加用户</el-button>
  30. <el-table
  31. :data="users"
  32. max-height="400"
  33. >
  34. <el-table-column
  35. prop="username"
  36. label="用户名"
  37. >
  38. </el-table-column>
  39. <el-table-column
  40. prop="mobile"
  41. label="手机"
  42. >
  43. </el-table-column>
  44. <el-table-column
  45. prop="email"
  46. label="邮箱"
  47. >
  48. </el-table-column>
  49. <el-table-column
  50. prop="status"
  51. label="状态"
  52. :formatter="formatter"
  53. >
  54. </el-table-column>
  55. <el-table-column
  56. prop="createdAt"
  57. label="创建时间"
  58. >
  59. </el-table-column>
  60. <el-table-column
  61. label="操作"
  62. fixed="right"
  63. width="150px"
  64. >
  65. <template #default="scope">
  66. <el-button
  67. type="text"
  68. size="mini"
  69. @click="handleEditUser(scope.$index, scope.row)">编辑</el-button>
  70. <el-button
  71. type="text"
  72. size="mini"
  73. @click="handleDeleteUser(scope.$index, scope.row)">删除</el-button>
  74. </template>
  75. </el-table-column>
  76. </el-table>
  77. <div class="user-pagination">
  78. <el-pagination
  79. @size-change="handleSizeChange"
  80. @current-change="handleCurrentChange"
  81. background
  82. :total="total"
  83. :page-sizes="[1, 5, 10, 20]"
  84. :page-size="pageSize"
  85. layout="total, prev, pager, next, sizes,jumper"
  86. ></el-pagination>
  87. </div>
  88. </div>
  89. <!-- 新增角色 编辑角色面板 -->
  90. <right-panel v-model="panelVisible" :title="panelTitle" :size="330">
  91. <editor-user
  92. :type="editType"
  93. :data="editData"
  94. @submit="handleSubmitUser"
  95. />
  96. </right-panel>
  97. </div>
  98. </template>
  99. <script lang="ts">
  100. import { computed, defineComponent, onMounted, reactive, ref, getCurrentInstance } from 'vue'
  101. import { ElForm } from 'element-plus'
  102. import { Profile } from '@/store/modules/user'
  103. import { useStore } from '@/store'
  104. import RightPanel from '@/components/RightPanel/index.vue'
  105. import EditorUser from './components/editorUser.vue'
  106. type ElFormInstance = InstanceType<typeof ElForm>
  107. export default defineComponent({
  108. name: 'User',
  109. components: {
  110. EditorUser,
  111. RightPanel
  112. },
  113. setup() {
  114. const { proxy } = getCurrentInstance()!
  115. const store = useStore()
  116. // 查询表单ref
  117. const queryFormRef = ref<ElFormInstance | null>(null)
  118. // 查询条件
  119. const formQuery = reactive({
  120. username: '',
  121. status: 'all',
  122. mobile: ''
  123. })
  124. // 用户列表
  125. const users = computed(() => store.state.user.users)
  126. // 用户总条数
  127. const total = computed(() => store.state.user.count)
  128. // 分页相关状态
  129. const pageNum = ref(0)
  130. const pageSize = ref(1)
  131. // 获取用户列表 支持分页
  132. const getUserList = () => {
  133. store.dispatch('user/getAllUsers', {
  134. pageNum: pageNum.value,
  135. pageSize: pageSize.value,
  136. ...formQuery
  137. })
  138. }
  139. onMounted(() => { // 初始加载数据
  140. getUserList()
  141. })
  142. // 查询
  143. const handleSubmitQuery = () => {
  144. getUserList()
  145. }
  146. // 重置
  147. const handleResetFeilds = () => {
  148. (queryFormRef.value as ElFormInstance).resetFields()
  149. getUserList()
  150. }
  151. // 分页
  152. const handleSizeChange = (val: number) => {
  153. pageSize.value = val
  154. getUserList()
  155. }
  156. const handleCurrentChange = (val: number) => {
  157. pageNum.value = val - 1 // 页码后端是从0开始的
  158. getUserList()
  159. }
  160. // 格式化status
  161. const formatter = (row: Profile) => {
  162. return row.status ? '正常' : '禁用'
  163. }
  164. // 用户新增
  165. const editData = ref<Profile | undefined>(undefined)
  166. // 控制面板显示
  167. const panelVisible = ref(false)
  168. // 操作类型 0编辑 1新增
  169. const editType = ref(1)
  170. // panel title
  171. const panelTitle = computed(() => editType.value === 1 ? '新增用户' : '编辑用户')
  172. // 获取角色 添加和编辑用户都需要分配角色 这里是必选
  173. store.dispatch('role/getRoles')
  174. const roles = computed(() => store.state.role.roles)
  175. // 添加用户
  176. const handleAddUser = () => {
  177. editType.value = 1
  178. editData.value = {} as Profile
  179. editData.value.roles = roles.value // 所有角色列表
  180. editData.value.roleIds = [] // 所选角色id列表
  181. panelVisible.value = true
  182. }
  183. // 编辑用户
  184. const handleEditUser = (index: number, row: Profile) => {
  185. editType.value = 0
  186. editData.value = { ...row }
  187. // 获取当前编辑用户 现有角色列表
  188. editData.value.roleIds = row.roles.map(item => item.id)
  189. editData.value.roles = roles.value // 所有角色列表
  190. panelVisible.value = true
  191. }
  192. // 新增编辑 统一派发方法
  193. const dispatchAction = async (action: string, data: Partial<Profile>, message: string) => {
  194. await store.dispatch(action, {
  195. ...data,
  196. pageSize: pageSize.value,
  197. pageNum: pageNum.value
  198. }).then(() => {
  199. (queryFormRef.value as ElFormInstance).resetFields()
  200. proxy?.$message.success(message)
  201. panelVisible.value = false
  202. })
  203. }
  204. // 新增用户
  205. const addNewUser = (data: Profile) => {
  206. dispatchAction('user/addUser', data, '用户添加成功')
  207. }
  208. // 编辑用户
  209. const editUser = (data: Profile) => {
  210. dispatchAction('user/editUser', data, '用户编辑成功')
  211. }
  212. // 删除用户
  213. const handleDeleteUser = (index: number, row: Profile) => {
  214. proxy?.$confirm(`您确认要删除用户${row.username}吗?`, '删除确认', {
  215. type: 'warning'
  216. }).then(async () => {
  217. await dispatchAction('user/removeUser', {
  218. id: row.id
  219. }, '用户删除成功')
  220. }).catch((err: Error) => {
  221. console.log('err', err)
  222. proxy?.$message({
  223. type: 'info',
  224. message: '已取消删除'
  225. })
  226. })
  227. }
  228. // 提交用户信息
  229. const handleSubmitUser = (data: Profile) => {
  230. if (editType.value === 1) { // 新增
  231. addNewUser(data)
  232. } else if (editType.value === 0) { // 编辑
  233. editUser(data)
  234. }
  235. }
  236. return {
  237. users,
  238. total,
  239. pageSize,
  240. formQuery,
  241. queryFormRef,
  242. formatter,
  243. editData,
  244. panelVisible,
  245. editType,
  246. panelTitle,
  247. handleSubmitUser,
  248. handleAddUser,
  249. handleEditUser,
  250. handleDeleteUser,
  251. handleSizeChange,
  252. handleCurrentChange,
  253. handleSubmitQuery,
  254. handleResetFeilds
  255. }
  256. }
  257. })
  258. </script>
  259. <style lang="scss" scoped>
  260. .user-container {
  261. padding: 30px;
  262. .user-pagination {
  263. margin-top: 10px;
  264. text-align: right;
  265. }
  266. }
  267. </style>

创建editorUser组件

/src/views/system/user/components/editorUser.vue

  1. <template>
  2. <div class="editor-container">
  3. <el-form
  4. ref="editFormRef"
  5. :model="editData"
  6. :rules="menuFormRules"
  7. label-width="80px"
  8. >
  9. <el-form-item label="用户名" prop="username">
  10. <el-input v-model="editData.username" placeholder="请输入用户名" />
  11. </el-form-item>
  12. <el-form-item label="手机" prop="mobile">
  13. <el-input
  14. v-model="editData.mobile"
  15. placeholder="请输入手机"
  16. maxlength="11"
  17. />
  18. </el-form-item>
  19. <el-form-item label="邮箱" prop="email">
  20. <el-input v-model="editData.email" placeholder="请输入邮箱" />
  21. </el-form-item>
  22. <el-form-item label="状态" prop="status">
  23. <el-switch v-model="editData.status" />
  24. </el-form-item>
  25. <el-form-item label="角色分配" prop="roleIds">
  26. <el-select multiple v-model="editData.roleIds" placeholder="请选择角色">
  27. <el-option
  28. v-for="item in editData.roles"
  29. :key="item.id"
  30. :label="item.name"
  31. :value="item.id"
  32. >
  33. </el-option>
  34. </el-select>
  35. </el-form-item>
  36. <el-form-item label="说明" prop="description">
  37. <el-input
  38. type="textarea"
  39. :rows="3"
  40. v-model="editData.description"
  41. placeholder="请输入说明"
  42. />
  43. </el-form-item>
  44. <el-form-item>
  45. <el-button type="primary" @click="submitMenuForm" :loading="loading"
  46. >提交</el-button
  47. >
  48. </el-form-item>
  49. </el-form>
  50. </div>
  51. </template>
  52. <script lang="ts">
  53. import { defineComponent, PropType, ref, watchEffect } from 'vue'
  54. import { ElForm } from 'element-plus'
  55. import { Profile } from '@/store/modules/user'
  56. type FormInstance = InstanceType<typeof ElForm>
  57. export default defineComponent({
  58. name: 'EditorMenu',
  59. props: {
  60. type: { // 操作类型 0编辑 1新增
  61. type: Number,
  62. required: true
  63. },
  64. data: {
  65. type: Object as PropType<Profile>
  66. }
  67. },
  68. emits: ['submit'],
  69. setup(props, { emit }) {
  70. const loading = ref(false)
  71. const editFormRef = ref<FormInstance | null>(null)
  72. const editData = ref<Partial<Profile>>({
  73. username: '',
  74. email: '',
  75. mobile: '',
  76. description: '',
  77. status: true
  78. })
  79. // 验证规则
  80. const validateMobile = (
  81. rule: unknown,
  82. value: string,
  83. callback: (arg?: Error) => void
  84. ) => {
  85. if (!isNaN(Number(value)) && value.length === 11) {
  86. callback()
  87. }
  88. callback(new Error('请输入正确格式手机号!'))
  89. }
  90. const validateRoles = (
  91. rule: unknown,
  92. value: number[],
  93. callback: (arg?: Error) => void
  94. ) => {
  95. if (value.length <= 0) {
  96. callback(new Error('请至少选择一个角色!'))
  97. }
  98. callback()
  99. }
  100. const menuFormRules = {
  101. username: {
  102. required: true,
  103. message: '请输入用户名',
  104. trigger: 'blur'
  105. },
  106. email: [
  107. {
  108. required: true,
  109. message: '请输入邮箱',
  110. trigger: 'blur'
  111. },
  112. {
  113. type: 'email',
  114. message: '请输入正确的邮箱地址',
  115. trigger: ['blur', 'change']
  116. }
  117. ],
  118. mobile: [
  119. {
  120. required: true,
  121. message: '请输入手机',
  122. trigger: 'blur'
  123. },
  124. {
  125. message: '请输入正确11位手机号',
  126. trigger: 'blur',
  127. validator: validateMobile
  128. }
  129. ],
  130. roleIds: {
  131. required: true,
  132. message: '请至少选择一个角色!',
  133. // validator: validateRoles,
  134. trigger: 'blur'
  135. }
  136. }
  137. const defaultProps = {
  138. username: '',
  139. email: '',
  140. mobile: '',
  141. description: '',
  142. status: true
  143. }
  144. watchEffect(() => {
  145. if (props.data) {
  146. // 移除之前表单效验结果
  147. editFormRef.value?.clearValidate()
  148. editData.value = { ...defaultProps, ...props.data }
  149. }
  150. })
  151. // 提交编辑菜单
  152. const submitMenuForm = () => {
  153. (editFormRef.value as FormInstance).validate(valid => {
  154. if (valid) {
  155. emit('submit', editData.value)
  156. }
  157. })
  158. }
  159. return {
  160. editData,
  161. submitMenuForm,
  162. editFormRef,
  163. menuFormRules,
  164. loading
  165. }
  166. }
  167. })
  168. </script>
  169. <style>
  170. .editor-container {
  171. padding: 20px;
  172. }
  173. </style>

1-2 相关store

至于上面页面中 需要依赖角色数据,至于角色的sotre放在角色那一章节

src/store/modules/user.ts

  1. import { Module, MutationTree, ActionTree } from 'vuex'
  2. import { IRootState } from '@/store'
  3. import { getUserInfo, getUsers, login, addUser, updateUser, removeUser } from '@/api/user'
  4. import { setToken, removeToken, getToken } from '@/utils/auth'
  5. // login params
  6. export interface IUserInfo {
  7. username: string;
  8. password: string;
  9. }
  10. interface Role {
  11. id: number;
  12. name: string;
  13. description: string;
  14. }
  15. export interface Profile {
  16. avatar: string;
  17. email: string;
  18. id: number;
  19. isSuper: boolean;
  20. mobile: string;
  21. status: boolean;
  22. username: string;
  23. description: string;
  24. roles: Role[];
  25. roleIds?: number[]
  26. }
  27. // 定义state类型
  28. export interface IUserState {
  29. token: string | null;
  30. userInfo: Profile | null;
  31. users: Profile[];
  32. count: number;
  33. roles: Role[] | null
  34. }
  35. // 查询user参数类型
  36. export interface IUserQuery {
  37. pageNum?: number;
  38. pageSize?: number;
  39. mobile?: string;
  40. status?: boolean;
  41. username?: string;
  42. }
  43. // 用户编辑/添加查询类型
  44. export type IProfileQuery = Profile & {
  45. pageNum?: number;
  46. pageSize?: number;
  47. }
  48. // mutations类型
  49. type IMutations = MutationTree<IUserState>
  50. // actions类型
  51. type IActions = ActionTree<IUserState, IRootState>
  52. // 定义state
  53. const state: IUserState = {
  54. token: getToken(),
  55. userInfo: null,
  56. users: [],
  57. count: 0,
  58. roles: null
  59. }
  60. // 定义mutation
  61. const mutations: IMutations = {
  62. SET_TOKEN(state, token: string) {
  63. state.token = token
  64. },
  65. SET_USER_INFO(state, data: Profile) {
  66. state.userInfo = data
  67. },
  68. SET_USERS(state, data: Profile[]) {
  69. state.users = data
  70. },
  71. SET_COUNT(state, data: number) {
  72. state.count = data
  73. },
  74. SET_ROLES(state, data: Role[]) {
  75. state.roles = data
  76. }
  77. }
  78. // 定义action
  79. const actions: IActions = {
  80. login({ commit }, userInfo: IUserInfo) {
  81. const { username, password } = userInfo
  82. return new Promise((resolve, reject) => {
  83. login({ username: username.trim(), password }).then(response => {
  84. const { data } = response
  85. commit('SET_TOKEN', data.token)
  86. setToken(data.token) // localStorage中保存token
  87. resolve(data)
  88. }).catch(error => {
  89. reject(error)
  90. })
  91. })
  92. },
  93. logout({ commit, dispatch }) {
  94. // 退出登录接口我这里就不写了
  95. return new Promise<void>((resolve) => {
  96. // 清空store里token
  97. commit('SET_TOKEN', '')
  98. // 清空localStorage里的token
  99. removeToken()
  100. // 清除所有tag views
  101. dispatch('tagsView/delAllViews', null, { root: true })
  102. resolve()
  103. })
  104. },
  105. resetToken({ commit }) {
  106. return new Promise<void>((resolve) => {
  107. // 清空store里token
  108. commit('SET_TOKEN', '')
  109. // 清空localStorage里的token
  110. removeToken()
  111. resolve()
  112. })
  113. },
  114. getAllUsers({ commit }, params: IUserQuery = {}) {
  115. return new Promise<void>((resolve, reject) => {
  116. getUsers(params).then(res => {
  117. const { data } = res
  118. commit('SET_USERS', data.users)
  119. commit('SET_COUNT', data.count)
  120. resolve()
  121. }).catch(reject)
  122. })
  123. },
  124. addUser({ dispatch }, data: IProfileQuery) {
  125. return new Promise<void>((resolve, reject) => {
  126. const { pageSize, pageNum, ...params } = data
  127. addUser(params).then(res => {
  128. if (res.code === 0) {
  129. dispatch('getAllUsers', {
  130. pageSize,
  131. pageNum
  132. })
  133. }
  134. resolve()
  135. }).catch(reject)
  136. })
  137. },
  138. editUser({ dispatch }, data: IProfileQuery) {
  139. return new Promise<void>((resolve, reject) => {
  140. const { pageSize, pageNum, ...params } = data
  141. updateUser(params.id, params).then(res => {
  142. if (res.code === 0) {
  143. dispatch('getAllUsers', {
  144. pageSize,
  145. pageNum
  146. })
  147. }
  148. resolve()
  149. }).catch(reject)
  150. })
  151. },
  152. removeUser({ dispatch }, data: IProfileQuery) {
  153. return new Promise<void>((resolve, reject) => {
  154. const { pageSize, pageNum, id } = data
  155. removeUser(id).then(res => {
  156. if (res.code === 0) {
  157. dispatch('getAllUsers', {
  158. pageSize,
  159. pageNum
  160. })
  161. }
  162. resolve()
  163. }).catch(reject)
  164. })
  165. },
  166. getUserInfo({ commit }) {
  167. return new Promise((resolve, reject) => {
  168. getUserInfo().then(response => {
  169. const { data } = response
  170. const { roles, ...info } = data
  171. commit('SET_USER_INFO', info)
  172. commit('SET_ROLES', roles)
  173. resolve(roles)
  174. }).catch(reject)
  175. })
  176. }
  177. }
  178. // 定义user module
  179. const user: Module<IUserState, IRootState> = {
  180. namespaced: true,
  181. state,
  182. mutations,
  183. actions
  184. }
  185. export default user

1-3 用户相关api

src/api/user.ts

  1. import request from '@/api/config/request'
  2. import { IUserQuery, Profile } from '@/store/modules/user'
  3. import { ApiResponse } from './type'
  4. interface UserLoginData {
  5. username: string;
  6. password: string;
  7. }
  8. interface LoginResponseData {
  9. token: string;
  10. }
  11. export const login = (data: UserLoginData): Promise<ApiResponse<LoginResponseData>> => {
  12. return request.post(
  13. '/auth/login',
  14. data
  15. )
  16. }
  17. // 获取用户信息
  18. interface UserBody {
  19. token: string;
  20. }
  21. export const getUserInfo = (data?: UserBody): Promise<ApiResponse<Profile>> => {
  22. return request.post('/auth/info', data)
  23. }
  24. // 获取用户列表
  25. interface IUsers {
  26. users: Profile[];
  27. count: number;
  28. }
  29. export const getUsers = (params: IUserQuery): Promise<ApiResponse<IUsers>> => {
  30. const { pageNum = 0, pageSize = 10, username = '', status, mobile = '' } = params
  31. return request.get('/user', {
  32. params: {
  33. pageNum,
  34. pageSize,
  35. username,
  36. status,
  37. mobile
  38. }
  39. })
  40. }
  41. // 删除用户
  42. export const removeUser = (id: number): Promise<ApiResponse> => {
  43. return request.delete(`/user/${id}`)
  44. }
  45. // 添加用户
  46. export const addUser = (data: Profile): Promise<ApiResponse> => {
  47. return request.post('/auth/register', data)
  48. }
  49. // 编辑用户
  50. export const updateUser = (id: number, data: Profile): Promise<ApiResponse> => {
  51. return request.put(`/user/${id}`, data)
  52. }