Skip to content

本篇主要探讨用户管理功能,接口部分依然是使用 Apifox mock 模拟。

用户 api

在 src/api/user.ts 中添加用户相关 CRUD 接口,代码如下:

typescript
//src/api/user.ts 
import request from "@/api/config/request";
// 从 "./type" 模块中导入 ApiResponse 类型,用于定义接口响应数据的结构
import type { ApiResponse } from "./type";
import type { IRole } from "./role";
/**
 * 定义用户登录所需的数据结构
 * @interface IUserLoginData
 * @property {string} username - 用户登录使用的用户名
 * @property {string} password - 用户登录使用的密码
 */
export interface IUserLoginData {
  username: string;
  password: string;
}
/**
 * 定义登录接口响应的数据结构
 * @interface ILoginResponseData
 * @property {string} token - 登录成功后返回的令牌,用于后续请求的身份验证
 */
export interface ILoginResponseData {
  token: string;
}
/**
 * 登录接口
 * @param {IUserLoginData} data - 用户登录所需的数据,包含用户名和密码
 * @returns {Promise<ApiResponse<ILoginResponseData>>} - 返回一个 Promise 对象,该对象解析为包含登录响应数据的 ApiResponse 类型
 */
export const login = (
  data: IUserLoginData
): Promise<ApiResponse<ILoginResponseData>> => {
  return request.post("/4642164-4292760-default/287017559", data);
};
//test
export const logout_error = (): Promise<ApiResponse<ILoginResponseData>> => {
  return request.post("/4642164-4292760-default/auth/401");
};
//个人中心接口
export interface Profile {
  id: number;
  username: string;
  email: string;
  mobile: string;
  isSuper: boolean;
  status: boolean;
  avatar: string;
  description: string;
  roles: IRole[];
  roleIds?: number[]; // 修改用户的时候,后端接受只要id
}
export interface IUsers {
  users: Profile[];
  count: number;
}
// 查询参数
export interface IUserQuery {
  pageNum?: number;
  pageSize?: number;
  mobile?: string;
  status?: boolean;
  username?: string;
}
// 获取用户列表的接口
export const getUsers = (params: IUserQuery): Promise<ApiResponse<IUsers>> => {
  const {
    pageNum = 0,
    pageSize = 10,
    username = "",
    status,
    mobile = ""
  } = params;
  return request.get("/user", {
    params: {
      pageNum,
      pageSize,
      username,
      status,
      mobile
    }
  });
};
// 删除用户
export const removeUser = (id: number): Promise<ApiResponse> => {
  return request.delete(`/user/${id}`);
};
// 添加用户
export const addUser = (data: Profile): Promise<ApiResponse> => {
  return request.post("/auth/register", data);
};
// 编辑用户
export const updateUser = (id: number, data: Profile): Promise<ApiResponse> => {
  return request.put(`/user/${id}`, data);
};

用户 Store

在 src/stores/user.ts 中添加用户相关方法,代码如下:

typescript
//src/stores/user.ts
import type { IUserLoginData, IUserQuery, IUsers, Profile } from "@/api/user";
import {
  login as loginApi,
  getUsers as getUsersApi, // 获取用户
  addUser as addUserApi,
  removeUser as removeUserApi,
  updateUser as updateUserApi
} from "@/api/user";
import { setToken, removeToken } from "@/utils/auth";
import { useTagsView } from "./tagsView";
import type { IRole } from "@/api/role";
export type IProfileQuery = Profile & {
  pageNum?: number;
  pageSize?: number;
};
export const useUserStore = defineStore("user", () => {
  const state = reactive({
    token: "",
    users: [] as IUsers["users"], // 用户列表
    count: 0, // 用户个数
    roles: [] as IRole[],
    userInfo: {} as Profile
  });
  const tagsViewStore = useTagsView();
  const login = async (userInfo: IUserLoginData) => {
    try {
      const { username, password } = userInfo;
      const response = await loginApi({ username: username.trim(), password });
      const { data } = response;
      state.token = data.token;
      setToken(data.token);
    } catch (e) {
      return Promise.reject(e);
    }
  };
  const logout = () => {
    state.token = "";
    removeToken();
    // 所有的信息都应该清空
    tagsViewStore.delAllView(); // ...
  };
  // 获取全部用户
  const getAllUsers = async (params: IUserQuery) => {
    const res = await getUsersApi(params);
    const { data } = res;
    state.users = data.users;
    state.count = data.count;
  };
  // 添加用户
  const addUser = async (data: IProfileQuery) => {
    const { pageSize, pageNum, ...params } = data;
    const res = await addUserApi(params);
    if (res.code === 0) {
      getAllUsers({
        pageSize,
        pageNum
      });
    }
  };
  // 删除用户
  const removeUser = async (data: IProfileQuery) => {
    const { pageSize, pageNum, id } = data;
    const res = await removeUserApi(id);
    if (res.code === 0) {
      getAllUsers({
        pageSize,
        pageNum
      });
    }
  };
  //编辑用户
  const editUser = async (data: IProfileQuery) => {
    const { pageSize, pageNum, ...params } = data;
    const res = await updateUserApi(params.id, params);
    if (res.code === 0) {
      getAllUsers({
        pageSize,
        pageNum
      });
    }
  };
  return {
    login,
    state,
    logout,
    getAllUsers,
    editUser,
    removeUser,
    addUser
  };
});

用户管理界面实现

3.1 editorUser 组件封装

在 src/views/system/user/components/editorUser.vue 中,实现用户新增编辑组件,代码如下:

html
//src/views/system/user/components/editorUser.vue
<template>
  <divclass="editor-container"p-20px>
    <el-form
      ref="editFormRef"
      :model="editData"
      :rules="menuFormRules"
      label-width="80px"
    >
      <el-form-itemlabel="用户名"prop="username">
        <el-inputv-model="editData.username"placeholder="请输入用户名" />
      </el-form-item>
      <el-form-itemlabel="手机"prop="mobile">
        <el-input
          v-model="editData.mobile"
          placeholder="请输入手机"
          maxlength="11"
        />
      </el-form-item>
      <el-form-itemlabel="邮箱"prop="email">
        <el-inputv-model="editData.email"placeholder="请输入邮箱" />
      </el-form-item>
      <el-form-itemlabel="状态"prop="status">
        <el-switchv-model="editData.status" />
      </el-form-item>
      <el-form-itemlabel="角色分配"prop="roleIds">
        <el-selectmultiplev-model="editData.roleIds"placeholder="请选择角色">
          <el-option
            v-for="item in editData.roles"
            :key="item.id"
            :label="item.name"
            :value="item.id"
          >
          </el-option>
        </el-select>
      </el-form-item>
      <el-form-itemlabel="说明"prop="description">
        <el-input
          type="textarea"
          :rows="3"
          v-model="editData.description"
          placeholder="请输入说明"
        />
      </el-form-item>
      <el-form-item>
        <el-buttontype="primary" @click="submitMenuForm">提交</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<scriptlang="ts"setup>
import type { Profile } from "@/api/user";
import type { FormInstance, FormItemRule } from "element-plus";
import type { PropType } from "vue";
const props = defineProps({
  type: {
    // 操作类型 0编辑 1新增
    type: Number,
    required: true
  },
  data: {
    type: Object as PropType<Profile>
  }
});
const emit = defineEmits(["submit"]);
const editFormRef = ref<FormInstance | null>(null);
const editData = ref<Partial<Profile>>({
  username: "",
  email: "",
  mobile: "",
  description: "",
  status: true
});
// 验证规则
const validateMobile = (
  rule: unknown,
  value: string,
  callback: (arg?: Error) => void
) => {
  if (!isNaN(Number(value)) && value.length === 11) {
    callback();
  }
  callback(new Error("请输入正确格式手机号!"));
};
const menuFormRules = {
  username: {
    required: true,
    message: "请输入用户名",
    trigger: "blur"
  },
  email: [
    {
      required: true,
      message: "请输入邮箱",
      trigger: "blur"
    },
    {
      type: "email",
      message: "请输入正确的邮箱地址",
      trigger: ["blur", "change"]
    }
  ] as FormItemRule[],
  mobile: [
    {
      required: true,
      message: "请输入手机",
      trigger: "blur"
    },
    {
      message: "请输入正确11位手机号",
      trigger: "blur",
      validator: validateMobile
    }
  ],
  roleIds: {
    required: true,
    message: "请至少选择一个角色!",
    trigger: "blur"
  }
};
const defaultProps = {
  username: "",
  email: "",
  mobile: "",
  description: "",
  status: true
};
watchEffect(() => {
  if (props.data) {
    // 移除之前表单效验结果
    editFormRef.value?.clearValidate();
    editData.value = { ...defaultProps, ...props.data };
  }
});
// 提交编辑菜单
const submitMenuForm = () => {
  (editFormRef.value as FormInstance).validate((valid) => {
    if (valid) {
      emit("submit", editData.value);
    }
  });
};
</script>

3.2 用户管理页面

在 src/views/system/user/index.vue 中,编写用户管理静态页面,代码如下:

html
//src/views/system/user/index.vue
<template>
  <divclass="user-container"p-30px>
    <h2>用户管理</h2>
    <el-buttontype="primary"plain @click="handleAddUser"class="mb">
      添加用户</el-button
    >
    <el-form:inline="true":model="formQuery"ref="queryFormRef">
      <el-form-itemlabel="用户名"prop="username">
        <el-input
          v-model="formQuery.username"
          placeholder="请输入用户名"
        ></el-input>
      </el-form-item>
      <el-form-itemlabel="手机号"prop="mobile">
        <el-input
          v-model="formQuery.mobile"
          placeholder="请输入手机号"
        ></el-input>
      </el-form-item>
      <el-form-itemlabel="状态"prop="status"w-200px>
        <el-selectv-model="formQuery.status"placeholder="状态">
          <el-optionlabel="全部"value="all"></el-option>
          <el-optionlabel="禁用":value="0"></el-option>
          <el-optionlabel="正常":value="1"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-buttontype="primary" @click="handleSubmitQuery">查询</el-button>
        <el-buttontype="default" @click="handleResetFeilds">重置</el-button>
      </el-form-item>
    </el-form>
    <divclass="user-list">
      <el-table:data="users"max-height="400">
        <el-table-columnprop="username"label="用户名"></el-table-column>
        <el-table-columnprop="mobile"label="手机"></el-table-column>
        <el-table-columnprop="email"label="邮箱"></el-table-column>
        <el-table-columnprop="status"label="状态":formatter="formatter">
        </el-table-column>
        <el-table-columnprop="createdAt"label="创建时间"></el-table-column>
        <el-table-columnlabel="操作"fixed="right"width="150px">
          <template #default="scope">
            <el-button
              size="small"
              link
              @click="handleEditUser(scope.$index, scope.row)"
              >编辑</el-button
            >
            <el-button
              size="small"
              link
              @click="handleDeleteUser(scope.$index, scope.row)"
              >删除</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <divclass="user-container">
        <divclass="user-list">
          <!--用户展示-->
          <divclass="user-pagination">
            <el-pagination
              @size-change="handleSizeChange"
              @current-change="handleCurrentChange"
              background
              :total="total"
              :page-sizes="[1, 5, 10, 20]"
              :page-size="pageSize"
              layout="total, prev, pager, next, sizes,jumper"
            ></el-pagination>
          </div>
        </div>
      </div>
    </div>
    <right-panelv-model="panelVisible":title="panelTitle":size="330">
      <editor-user
        :type="editType"
        :data="editData"
        @submit="handleSubmitUser"
      />
    </right-panel>
  </div>
</template>
<scriptlang="ts"setup>
import type { IUserQuery, Profile } from "@/api/user";
import { useRoleStore } from "@/stores/role";
import { type IProfileQuery, useUserStore } from "@/stores/user";
import type { FormInstance } from "element-plus";
const store = useUserStore();
// 用户列表
const users = computed(() => store.state.users);
// 分页相关状态
const pageNum = ref(0);
const pageSize = ref(10);
// 获取用户列表 支持分页
const getUserList = () => {
  store.getAllUsers({
    pageNum: pageNum.value,
    pageSize: pageSize.value,
    ...formQuery
    // ... 搜索条件
  } as unknown as IUserQuery);
};
// 格式化status
const formatter = (row: Profile) => {
  return row.status ? "正常" : "禁用";
};
// 不使用watchEffect
onMounted(() => {
  getUserList();
});
// 删除用户
const { proxy } = getCurrentInstance()!;
const handleDeleteUser = async (index: number, row: Profile) => {
  try {
    await proxy?.$confirm(`您确认要删除用户${row.username}吗?`, "删除确认", {
      type: "warning"
    });
    await store.removeUser({
      id: row.id,
      pageNum: pageNum.value,
      pageSize: pageSize.value
    } as IProfileQuery);
    proxy?.$message.success("用户删除成功");
  } catch {
    proxy?.$message({
      type: "info",
      message: "已取消删除"
    });
  }
};
const handleEditUser = (index: number, row: Profile) => {
  editType.value = 0;
  editData.value = { ...row };
  // 获取当前编辑用户 现有角色列表
  editData.value.roleIds = row.roles.map((item) => item.id);
  editData.value.roles = roles.value!; // 所有角色列表
  panelVisible.value = true;
};
// 用户总条数
const total = computed(() => store.state.count);
// 分页
const handleSizeChange = (val: number) => {
  pageSize.value = val;
  getUserList();
};
const handleCurrentChange = (val: number) => {
  pageNum.value = val - 1; // 页码后端是从0开始的
  getUserList();
};
// 查询参数
const formQuery = reactive({
  username: "",
  status: "all",
  mobile: ""
});
const handleSubmitQuery = () => {
  getUserList();
};
// 重置
const queryFormRef = useTemplateRef<FormInstance | null>("queryFormRef");
const handleResetFeilds = () => {
  (queryFormRef.value as FormInstance).resetFields();
  getUserList();
};
const editData = ref<Profile | undefined>(undefined);
// 控制面板显示
const panelVisible = ref(false);
const editType = ref(1);
const panelTitle = computed(() =>
  editType.value === 1 ? "新增用户" : "编辑用户"
);
const storeRole = useRoleStore();
storeRole.getRoles({
  pageNum: pageNum.value,
  pageSize: pageSize.value,
  flag: 0
});
const roles = computed(() => storeRole.state.roles); // 角色
const handleAddUser = () => {
  editType.value = 1;
  editData.value = {} as Profile;
  editData.value.roles = roles.value; // 所有角色列表
  editData.value.roleIds = []; // 所选角色id列表
  panelVisible.value = true;
};
const editUser = async (data: IProfileQuery) => {
  store.editUser({
    ...data,
    pageSize: pageSize.value,
    pageNum: pageNum.value
  });
  (queryFormRef.value as FormInstance).resetFields();
  proxy?.$message.success("用户编辑成功");
  panelVisible.value = false;
};
const handleSubmitUser = (data: Profile) => {
  if (editType.value === 1) {
    // 新增
    addNewUser(data);
  } else {
    editUser(data);
  }
};
const addNewUser = (data: Profile) => {
  store.addUser({
    ...data,
    pageSize: pageSize.value,
    pageNum: pageNum.value
  });
  (queryFormRef.value as FormInstance).resetFields();
  proxy?.$message.success("用户添加成功");
  panelVisible.value = false;
};
</script>

npm run dev 启动后,页面效果如下:

图片

以上,就是用户管理的全部内容。