浏览代码

用户管理

Michael Wang 3 年之前
父节点
当前提交
beecb9d72d

+ 2 - 0
components.d.ts

@@ -23,7 +23,9 @@ declare module 'vue' {
     ATextarea: typeof import('ant-design-vue/es')['Textarea']
     Layout: typeof import('./src/components/Layout.vue')['default']
     QmButton: typeof import('./src/components/QmButton.vue')['default']
+    RoleSelect: typeof import('./src/components/RoleSelect.vue')['default']
     RootOrgSelect: typeof import('./src/components/RootOrgSelect.vue')['default']
+    'RootOrgSelect copy': typeof import('./src/components/RootOrgSelect copy.vue')['default']
     StateSelect: typeof import('./src/components/StateSelect.vue')['default']
     'StateSelect copy': typeof import('./src/components/StateSelect copy.vue')['default']
   }

+ 2 - 2
src/api/roleManagementPage.ts

@@ -1,9 +1,9 @@
 import { httpApp } from "@/plugins/axiosApp";
 
 /** 机构分页查询 */
-export function getRoleList(rootOrgId: string) {
+export function getRoleList(rootOrgId: number) {
   return httpApp.post(
     "/api/ess/role/list/all",
-    new URLSearchParams({ rootOrgId })
+    new URLSearchParams({ rootOrgId: rootOrgId + "" })
   );
 }

+ 5 - 8
src/api/subOrgPage.ts

@@ -5,7 +5,7 @@ export function getSubOrgList(params: {
   code?: string;
   name?: string;
   enable?: boolean;
-  rootOrgId: string;
+  rootOrgId: number;
   pageNo?: number;
   pageSize?: number;
 }) {
@@ -18,9 +18,8 @@ export function updateSubOrg(params: {
   enable?: boolean;
   id?: number;
   name?: string;
-  rootOrgId?: string;
+  rootOrgId?: number;
 }) {
-  // params.rootId = params.rootOrgId;
   return httpApp.post("/api/ess/org/save", params);
 }
 
@@ -37,22 +36,20 @@ export function delOrg(ids: number[]) {
   return httpApp.post(
     `/api/ess/org/delete`,
     new URLSearchParams([["ids", ids.join(",")]])
-    // { ids: ids.join(",") },
-    // { headers: { "content-type": "application/x-www-form-urlencoded" } }
   );
 }
 
 /** 导入机构 */
-export function importOrg(rootOrgId: string, file: File) {
+export function importOrg(rootOrgId: number, file: File) {
   const f = new FormData();
-  f.append("rootOrgId", rootOrgId);
+  f.append("rootOrgId", rootOrgId + "");
   f.append("file", file);
   return httpApp.post(`/api/ess/org/import`, f);
 }
 
 /** 导出机构 */
 export function exportOrg(params: {
-  rootOrgId: string;
+  rootOrgId: number;
   code: string;
   name: string;
   enable?: boolean;

+ 62 - 0
src/api/userManagementPage.ts

@@ -0,0 +1,62 @@
+import { httpApp } from "@/plugins/axiosApp";
+
+/** 用户分页查询 */
+export function getUserList(params: {
+  loginName?: string;
+  name?: string;
+  enable?: boolean;
+  roleId: number;
+  rootOrgId: number;
+  pageNo?: number;
+  pageSize?: number;
+}) {
+  return httpApp.post("/api/ess/user/page", params);
+}
+
+/** 更新用户 */
+export function updateUser(params: {
+  loginName?: string;
+  password?: string;
+  enable?: boolean;
+  id?: number;
+  roleId?: number;
+  name?: string;
+  rootOrgId?: number;
+}) {
+  return httpApp.post("/api/ess/user/save", params);
+}
+
+/** 禁用、启用用户 */
+export function toggleUsers(enable: boolean, ids: number[]) {
+  return httpApp.post(
+    `/api/ess/user/${enable ? "enable" : "disable"}`,
+    new URLSearchParams([["ids", ids.join(",")]])
+  );
+}
+
+/** 删除用户 */
+export function resetPasswords(ids: number[]) {
+  return httpApp.post(
+    `/api/ess/user/password`,
+    new URLSearchParams([["password", ids.join(",")]])
+  );
+}
+
+/** 导入用户 */
+export function importUsers(rootOrgId: number, file: File) {
+  const f = new FormData();
+  f.append("rootOrgId", rootOrgId + "");
+  f.append("file", file);
+  return httpApp.post(`/api/ess/user/import`, f);
+}
+
+/** 导出用户 */
+export function exportUsers(params: {
+  rootOrgId: number;
+  roleId?: number;
+  loginName?: string;
+  name?: string;
+  enable?: boolean;
+}) {
+  return httpApp.post(`/api/ess/user/export`, params);
+}

+ 5 - 1
src/components/Layout.vue

@@ -21,7 +21,11 @@
               角色管理
             </router-link>
           </a-menu-item>
-          <a-menu-item key="14">用户管理</a-menu-item>
+          <a-menu-item key="14">
+            <router-link active-class="active-route" to="/basic/user">
+              用户管理
+            </router-link>
+          </a-menu-item>
           <a-menu-item key="15">科目管理</a-menu-item>
         </a-sub-menu>
         <a-sub-menu key="sub2">

+ 53 - 0
src/components/RoleSelect.vue

@@ -0,0 +1,53 @@
+<template>
+  <a-select
+    placeholder="请选择角色"
+    allowClear
+    :value="valueStr"
+    @change="handleChange"
+    style="width: 140px"
+  >
+    <a-select-option
+      v-for="(item, index) in optionList"
+      :key="index"
+      :value="item.id"
+    >
+      {{ item.name }}
+    </a-select-option>
+  </a-select>
+</template>
+
+<script setup lang="ts">
+import { getRoleList } from "@/api/roleManagementPage";
+import { onMounted, computed, watch } from "vue-demi";
+
+const props = defineProps<{
+  value?: null | number;
+  rootOrgId?: null | number;
+}>();
+const emit = defineEmits(["update:value"]);
+
+let optionList = $ref<{ id: number; name: string }[]>([]);
+
+onMounted(async () => {
+  await fetchData();
+});
+
+watch(() => [props.rootOrgId], fetchData);
+
+async function fetchData() {
+  if (typeof props.rootOrgId === "number") {
+    const res = await getRoleList(props.rootOrgId);
+    optionList = res.data;
+  }
+}
+const valueStr = computed(() => {
+  let res: undefined | number = props.value ?? undefined;
+  // if (typeof res === "number") res = props.value;
+  return res as undefined | number;
+});
+
+function handleChange(v: string) {
+  // console.log(typeof v);
+  emit("update:value", v);
+}
+</script>

+ 9 - 3
src/components/RootOrgSelect.vue

@@ -2,7 +2,7 @@
   <a-select
     placeholder="请选择顶级机构"
     allowClear
-    :value="props.value"
+    :value="valueStr"
     @change="handleChange"
     style="width: 140px"
   >
@@ -18,7 +18,7 @@
 
 <script setup lang="ts">
 import { getRootOrgList } from "@/api/rootOrgPage";
-import { onMounted } from "vue-demi";
+import { onMounted, computed } from "vue-demi";
 
 const props = defineProps({ value: null });
 const emit = defineEmits(["update:value"]);
@@ -30,8 +30,14 @@ onMounted(async () => {
   optionList = res.data.content;
 });
 
+const valueStr = computed(() => {
+  let res: undefined | number | string = props.value ?? undefined;
+  // if (typeof res === "number") res = props.value + "";
+  return res as undefined | string;
+});
+
 function handleChange(v: string) {
-  console.log(v);
+  // console.log(v);
   emit("update:value", v);
 }
 </script>

+ 1 - 3
src/features/roleManagement/RoleManagement.vue

@@ -19,18 +19,16 @@
 <script setup lang="ts">
 import { getRoleList } from "@/api/roleManagementPage";
 import { useMainStore } from "@/store";
-import { message } from "ant-design-vue";
 import { watch, onMounted, ref, reactive, toRaw } from "vue-demi";
 
 const store = useMainStore();
 store.currentLocation = "基础管理 / 角色管理";
 
-let rootOrgId = $ref("");
+let rootOrgId = $ref(undefined as unknown as number);
 
 let data = $ref([]);
 let pageSize = $ref(10);
 let pageNo = $ref(1);
-let totalElements = $ref(0);
 
 async function search() {
   await fetchData();

+ 13 - 3
src/features/subOrg/SubOrg.vue

@@ -71,9 +71,8 @@
       cancel-text="取消"
     >
       <a-form>
-        <a-form-item label="顶级机构">
+        <a-form-item v-show="store.isSuperAdmin" label="顶级机构">
           <RootOrgSelect
-            v-show="store.isSuperAdmin"
             :disabled="!!orgObj.id"
             v-model:value="orgObj.rootOrgId"
           />
@@ -135,7 +134,7 @@ import { watch, onMounted, ref, reactive, toRaw } from "vue-demi";
 const store = useMainStore();
 store.currentLocation = "基础管理 / 机构管理";
 
-let rootOrgId = $ref("");
+let rootOrgId = $ref(undefined as unknown as number);
 let code = $ref("");
 let name = $ref("");
 let enable = $ref(undefined as undefined | boolean);
@@ -248,12 +247,23 @@ const newOrg = async () => {
   showModal(orgObj);
 };
 
+function checkEmpty(): boolean {
+  if (selectIds && selectIds.length > 0) {
+    return false;
+  } else {
+    message.warn({ content: "请先选择行" });
+    return true;
+  }
+}
+
 async function handleToggleOrg(enable: boolean, ids: number[]) {
+  if (checkEmpty()) return;
   await toggleSubOrg(enable, ids);
   await search();
 }
 
 async function handleDelOrg(ids: number[]) {
+  if (checkEmpty()) return;
   Modal.confirm({
     title: "提示",
     content: "确认删除?",

+ 332 - 0
src/features/userManagement/UserManagement.vue

@@ -0,0 +1,332 @@
+<template>
+  <div>
+    <div class="tw-bg-white tw-p-5 tw-rounded-xl tw-mb-5">
+      <RootOrgSelect v-if="store.isSuperAdmin" v-model:value="rootOrgId" />
+
+      <span class="tw-mr-4"></span>
+      <a-input
+        v-model:value="name"
+        class="tw-mr-4"
+        style="width: 178px"
+        placeholder="姓名"
+        allowClear
+      ></a-input>
+      <span class="tw-mr-4"></span>
+      <a-input
+        v-model:value="loginName"
+        style="width: 178px"
+        placeholder="登录名"
+        allowClear
+      ></a-input>
+      <span class="tw-mr-4"></span>
+      <RoleSelect v-model:value="roleId" :root-org-id="rootOrgId" />
+      <StateSelect v-model:value="enable" />
+      <span class="tw-mr-4"></span>
+      <a-button @click="search">查询</a-button>
+
+      <div class="tw-mt-4">
+        <a-button @click="newUser">新增</a-button>
+        <a-button @click="importModalVisible = true">批量导入</a-button>
+        <a-button @click="handleExport">批量导出</a-button>
+        <a-button @click="handleToggleUsers(true, selectIds)"
+          >批量启用</a-button
+        >
+        <a-button @click="handleToggleUsers(false, selectIds)"
+          >批量禁用</a-button
+        >
+        <a-button @click="handleResetUsers(selectIds)">批量重置密码</a-button>
+      </div>
+    </div>
+
+    <div class="tw-bg-white tw-p-5 tw-rounded-xl">
+      <a-table
+        row-key="id"
+        :columns="columns"
+        :data-source="data"
+        :row-selection="rowSelection"
+        :pagination="{
+          pageSize: pageSize,
+          current: pageNo,
+          total: totalElements,
+          showTotal: (total: number) => `总数:${total}`,
+          onChange: (pageNoChanged: number, pageSizeChanged: number) => {
+            pageNo = pageNoChanged; 
+            pageSize = pageSizeChanged;
+          }
+        }"
+      >
+        <template #rootOrgName="{ record }">
+          <a>{{ record.rootOrgName }}({{ record.rootOrgCode }})</a>
+        </template>
+        <template #enable="{ text }">
+          <a>{{ $filters.booleanEnableDisableFilter(text) }}</a>
+        </template>
+        <template #action="{ record }">
+          <span>
+            <a-button @click="showModal(record)">编辑</a-button>
+            <a-button @click="handleToggleUsers(!record.enable, [record.id])">
+              {{ record.enable ? "禁用" : "启用" }}
+            </a-button>
+            <a-button @click="handleResetUsers([record.id])">重置密码</a-button>
+          </span>
+        </template>
+      </a-table>
+    </div>
+
+    <a-modal
+      v-model:visible="visible"
+      title="用户信息页"
+      @ok="handleOk"
+      ok-text="确定"
+      cancel-text="取消"
+    >
+      <a-form>
+        <a-form-item v-show="store.isSuperAdmin" label="顶级机构">
+          <RootOrgSelect
+            :disabled="!!userObj.id"
+            v-model:value="userObj.rootOrgId"
+          />
+        </a-form-item>
+        <a-form-item label="姓名">
+          <a-input v-model:value="userObj.name"></a-input>
+        </a-form-item>
+        <a-form-item label="登录名">
+          <a-input
+            :disabled="!!userObj.id"
+            v-model:value="userObj.loginName"
+          ></a-input>
+        </a-form-item>
+        <a-form-item label="登录密码">
+          <a-input
+            :disabled="!!userObj.id"
+            v-model:value="userObj.password"
+          ></a-input>
+        </a-form-item>
+        <a-form-item label="角色">
+          <RoleSelect
+            :root-org-id="userObj.rootOrgId"
+            v-model:value="userObj.roleId"
+          />
+        </a-form-item>
+        <a-form-item label="状态">
+          <a-radio-group v-model:value="userObj.enable">
+            <a-radio :value="true">启用</a-radio>
+            <a-radio :value="false">禁用</a-radio>
+          </a-radio-group>
+        </a-form-item>
+      </a-form>
+    </a-modal>
+
+    <a-modal
+      v-model:visible="importModalVisible"
+      title="批量用户导入"
+      @ok="handleImport"
+      ok-text="确定"
+      cancel-text="取消"
+    >
+      <a-form>
+        <a-form-item label="顶级机构">
+          <RootOrgSelect
+            v-show="store.isSuperAdmin"
+            v-model:value="userObj.rootOrgId"
+          />
+        </a-form-item>
+        <a-form-item label="文件地址">
+          <input id="file-input" :multiple="false" type="file" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {
+  exportUsers,
+  getUserList,
+  importUsers,
+  resetPasswords,
+  toggleUsers,
+  updateUser,
+} from "@/api/userManagementPage";
+import { useMainStore } from "@/store";
+import { message, Modal } from "ant-design-vue";
+import { watch, onMounted, ref, reactive, toRaw } from "vue-demi";
+
+const store = useMainStore();
+store.currentLocation = "基础管理 / 用户管理";
+
+let rootOrgId = $ref(undefined as unknown as number);
+let roleId = $ref(undefined as unknown as number);
+let loginName = $ref("");
+let name = $ref("");
+let enable = $ref(undefined as undefined | boolean);
+
+let data = $ref([]);
+let pageSize = $ref(10);
+let pageNo = $ref(1);
+let totalElements = $ref(0);
+
+async function search() {
+  await fetchData();
+}
+
+watch(() => [pageNo, pageSize], fetchData);
+
+async function fetchData() {
+  const res = await getUserList({
+    loginName,
+    name,
+    enable,
+    roleId,
+    rootOrgId,
+    pageSize,
+    pageNo,
+  });
+  // console.log(res);
+  data = res.data.content;
+  pageNo = res.data.pageNo;
+  pageSize = res.data.pageSize;
+  totalElements = res.data.totalElements;
+}
+
+const columns = [
+  {
+    title: "顶级机构",
+    dataIndex: "rootOrgName",
+    width: 150,
+    slots: { customRender: "rootOrgName" },
+  },
+  {
+    title: "姓名",
+    dataIndex: "name",
+    width: 150,
+  },
+  {
+    title: "登录名",
+    dataIndex: "loginName",
+    width: 150,
+  },
+  {
+    title: "角色",
+    dataIndex: "roleName",
+  },
+  {
+    title: "状态",
+    dataIndex: "enable",
+    slots: { customRender: "enable" },
+  },
+  {
+    title: "创建时间",
+    dataIndex: "createTime",
+  },
+  {
+    title: "创建人",
+    dataIndex: "creator",
+  },
+  {
+    title: "更新时间",
+    dataIndex: "updateTime",
+  },
+  {
+    title: "更新人",
+    dataIndex: "updater",
+  },
+  {
+    title: "Action",
+    key: "action",
+    slots: { customRender: "action" },
+  },
+];
+
+onMounted(async () => {
+  rootOrgId = store.userInfo.rootOrgId;
+  await search();
+});
+
+const visible = ref<boolean>(false);
+
+const showModal = (record: any) => {
+  Object.assign(userObj, record);
+  visible.value = true;
+};
+
+const handleOk = async (e: MouseEvent) => {
+  await updateUser(toRaw(userObj));
+  visible.value = false;
+  await search();
+};
+
+const initUser = {
+  id: undefined,
+  code: "",
+  name: "",
+  loginName: "",
+  password: "",
+  enable: true,
+  roleId,
+  rootOrgId: store.userInfo.rootOrgId,
+};
+const userObj = reactive({ ...initUser });
+
+const newUser = async () => {
+  Object.assign(userObj, initUser);
+  showModal(userObj);
+};
+
+function checkEmpty(): boolean {
+  if (selectIds && selectIds.length > 0) {
+    return false;
+  } else {
+    message.warn({ content: "请先选择行" });
+    return true;
+  }
+}
+async function handleToggleUsers(enable: boolean, ids: number[]) {
+  if (checkEmpty()) return;
+  await toggleUsers(enable, ids);
+  await search();
+}
+
+async function handleResetUsers(ids: number[]) {
+  if (checkEmpty()) return;
+  Modal.confirm({
+    title: "提示",
+    content: "确认重置?",
+    cancelText: "取消",
+    okText: "确定",
+    onOk: async () => {
+      await resetPasswords(ids);
+      await search();
+    },
+  });
+}
+
+/** <handleImport> */
+let importModalVisible = ref<boolean>(false);
+async function handleImport() {
+  const files = (document.querySelector("#file-input") as HTMLInputElement)
+    .files;
+  const fileToImport = files && files[0];
+  if (!fileToImport) {
+    message.warn({ content: "请选择文件" });
+    return;
+  }
+
+  await importUsers(rootOrgId, fileToImport);
+  message.success({ content: "导入成功" });
+}
+/** </handleImport> */
+
+async function handleExport() {
+  await exportUsers({ rootOrgId, name, loginName, roleId, enable });
+  message.success({ content: "导出成功" });
+}
+
+let selectIds = $ref<number[]>([]);
+const rowSelection = {
+  onChange: (selectedRowKeys: (string | number)[]) => {
+    console.log(`selectedRowKeys: ${selectedRowKeys}`);
+    selectIds = selectedRowKeys as number[];
+  },
+};
+</script>

+ 1 - 1
src/plugins/axiosNotice.ts

@@ -18,4 +18,4 @@ export const notifyInvalidTokenThrottled = throttle(() => {
     );
   }, 3000);
   console.log("登录失效");
-}, 1000);
+}, 5000);

+ 4 - 0
src/router/index.ts

@@ -27,6 +27,10 @@ const routes = [
         path: "role",
         component: () => import("@/features/roleManagement/RoleManagement.vue"),
       },
+      {
+        path: "user",
+        component: () => import("@/features/userManagement/UserManagement.vue"),
+      },
     ],
     component: Layout,
   },

+ 1 - 1
src/store/index.ts

@@ -13,7 +13,7 @@ export const useMainStore = defineStore("main", {
         userId: -1,
         identity: "",
         displayName: "",
-        rootOrgId: "-1",
+        rootOrgId: -1,
         rootOrgName: "",
         token: "",
         roleList: [] as Role[],