소스 검색

用户编辑

zhangjie 1 년 전
부모
커밋
09007440d2
6개의 변경된 파일239개의 추가작업 그리고 111개의 파일을 삭제
  1. 0 2
      src/components/Layout.vue
  2. 6 6
      src/components/StatusTag.vue
  3. 175 0
      src/features/userManagement/ModifyUser.vue
  4. 19 95
      src/features/userManagement/UserManagement.vue
  5. 8 8
      src/utils/formRules.ts
  6. 31 0
      src/utils/utils.ts

+ 0 - 2
src/components/Layout.vue

@@ -90,8 +90,6 @@ watch(
 );
 
 function toMenu(menu: MenuItem) {
-  console.log(menu, curMenu.value, route.name);
-
   curMenu.value = menu.url;
 
   if (route.name === menu.url) return;

+ 6 - 6
src/components/StatusTag.vue

@@ -8,16 +8,16 @@ import $filters from "@/filters";
 
 const configs = {
   enable: {
-    themeDict: { true: "success", false: "danger" },
+    themeDict: { true: "success", false: "error" },
     valFilter: $filters.booleanEnableDisableFilter,
   },
   projectStatus: {
     themeDict: {
-      NONE: "",
-      PROCESSING: "",
+      NONE: "blue",
+      PROCESSING: "blue",
       FINISH: "success",
-      FAIL: "danger",
-      STOPING: "",
+      FAIL: "error",
+      STOPING: "warning",
     },
     valFilter: $filters.projectStatusFilter,
   },
@@ -42,6 +42,6 @@ const theme = computed(() => {
 });
 const label = computed(() => {
   // @ts-ignore
-  return valFilter(props.value as any);
+  return valFilter(props.value);
 });
 </script>

+ 175 - 0
src/features/userManagement/ModifyUser.vue

@@ -0,0 +1,175 @@
+<template>
+  <a-modal v-model:open="visible" :title="title" :width="500">
+    <a-form
+      ref="formRef"
+      :labelCol="{ style: { width: '90px' } }"
+      :model="formData"
+      :rules="rules"
+    >
+      <a-form-item v-if="store.isSuperAdmin" label="学校" name="rootOrgId">
+        <RootOrgSelect
+          v-model:value="formData.rootOrgId"
+          :disabled="isEdit"
+          selectFirst
+        />
+      </a-form-item>
+      <a-form-item label="姓名" name="name">
+        <a-input v-model:value="formData.name"></a-input>
+      </a-form-item>
+      <a-form-item label="登录名" name="loginName">
+        <a-input
+          v-model:value="formData.loginName"
+          :disabled="isEdit"
+        ></a-input>
+      </a-form-item>
+      <a-form-item v-if="!isEdit" label="登录密码" name="password">
+        <a-input v-model:value="formData.password"></a-input>
+      </a-form-item>
+      <a-form-item label="角色" name="role">
+        <RoleSelect
+          v-model:value="formData.role"
+          :rootOrgId="formData.rootOrgId"
+        />
+      </a-form-item>
+      <a-form-item label="状态" name="enable">
+        <a-radio-group v-model:value="formData.enable">
+          <a-radio :value="true">启用</a-radio>
+          <a-radio :value="false">禁用</a-radio>
+        </a-radio-group>
+      </a-form-item>
+    </a-form>
+
+    <template #footer>
+      <a-button type="primary" :disabled="loading" @click="confirm"
+        >确认</a-button
+      >
+      <a-button @click="close">取消</a-button>
+    </template>
+  </a-modal>
+</template>
+
+<script setup lang="ts">
+import useModal from "@/hooks/modal";
+import { nextTick, reactive, ref, watch } from "vue";
+import type { Rule, FormInstance } from "ant-design-vue/es/form";
+import { password } from "@/utils/formRules";
+import useLoading from "@/hooks/loading";
+import { updateUser } from "@/api/userManagementPage";
+import { message } from "ant-design-vue";
+import { useMainStore } from "@/store";
+import { objAssign, objModifyAssign } from "@/utils/utils";
+import { User } from "@/types";
+
+const store = useMainStore();
+
+/* modal */
+const { visible, open, close } = useModal();
+defineExpose({ open, close });
+
+const props = defineProps<{
+  rowData: User;
+}>();
+const emit = defineEmits(["modified"]);
+
+const isEdit = $computed(() => !!props.rowData.id);
+const title = $computed(() => `${isEdit ? "编辑" : "新增"}用户`);
+
+const defaultFormData = {
+  id: undefined,
+  code: "",
+  name: "",
+  loginName: "",
+  password: "",
+  enable: true,
+  role: "",
+  rootOrgId: null,
+};
+type FormDataType = typeof defaultFormData;
+
+const formRef = ref<FormInstance>();
+const formData = reactive<FormDataType>({ ...defaultFormData });
+const rules: Record<string, Rule[]> = {
+  rootOrgId: [
+    {
+      required: true,
+      message: "请选择学校",
+    },
+  ],
+  name: [
+    {
+      required: true,
+      message: "请输入姓名",
+    },
+    {
+      max: 30,
+      message: "最多30个字符",
+    },
+  ],
+  loginName: [
+    {
+      required: true,
+      message: "请输入登录名",
+    },
+    {
+      pattern: /^[a-zA-Z0-9]{4}$/,
+      message: "请输入登录名",
+    },
+    {
+      max: 30,
+      message: "最多30个字符",
+    },
+  ],
+  password: password,
+  role: [
+    {
+      required: true,
+      message: "请选择角色",
+    },
+  ],
+  enable: [
+    {
+      required: true,
+      message: "请选择状态",
+    },
+  ],
+};
+
+/* confirm */
+const { loading, setLoading } = useLoading();
+async function confirm() {
+  const err = await formRef.value?.validate();
+  if (err) return;
+
+  setLoading(true);
+  const datas = objAssign(formData, {});
+  const res = await updateUser(datas).catch(() => false);
+  setLoading(false);
+  if (!res) return;
+  message.success("修改成功!");
+  emit("modified", datas);
+  close();
+}
+
+// init modal
+watch(
+  () => visible.value,
+  (val) => {
+    if (!val) {
+      formRef.value?.clearValidate();
+      return;
+    }
+    nextTick(() => {
+      modalBeforeOpen();
+    });
+  },
+  { immediate: true }
+);
+
+function modalBeforeOpen() {
+  if (props.rowData.id) {
+    objModifyAssign(formData, props.rowData);
+  } else {
+    objModifyAssign(formData, defaultFormData);
+  }
+}
+</script>

+ 19 - 95
src/features/userManagement/UserManagement.vue

@@ -24,7 +24,7 @@
 
     <div class="part-box">
       <a-space class="part-action" :size="6">
-        <a-button type="text" @click="newUser">
+        <a-button type="text" @click="toAdd">
           <template #icon>
             <svg-icon name="add"></svg-icon>
           </template>
@@ -101,7 +101,7 @@
           </template>
           <template v-if="column.dataIndex === 'action'">
             <div class="action-cell">
-              <a-button type="text" @click="showModal(record)">编辑</a-button>
+              <a-button type="text" @click="toEdit(record)">编辑</a-button>
               <a-button
                 type="text"
                 :danger="record.enable"
@@ -118,52 +118,6 @@
       </a-table>
     </div>
 
-    <a-modal
-      v-model:open="visible"
-      title="用户信息页"
-      okText="确定"
-      cancelText="取消"
-      :width="438"
-      @ok="handleOk"
-    >
-      <a-form :labelCol="{ span: 4 }">
-        <a-form-item v-if="store.isSuperAdmin" label="学校">
-          <RootOrgSelect
-            v-model:value="userObj.rootOrgId"
-            :disabled="!!userObj.id"
-            selectFirst
-          />
-        </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
-            v-model:value="userObj.loginName"
-            :disabled="!!userObj.id"
-          ></a-input>
-        </a-form-item>
-        <a-form-item label="登录密码">
-          <a-input
-            v-model:value="userObj.password"
-            :disabled="!!userObj.id"
-          ></a-input>
-        </a-form-item>
-        <a-form-item label="角色">
-          <RoleSelect
-            v-model:value="userObj.role"
-            :rootOrgId="userObj.rootOrgId"
-          />
-        </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:open="importModalVisible"
       title="批量用户导入"
@@ -173,10 +127,10 @@
     >
       <a-form>
         <a-form-item v-show="store.isSuperAdmin" label="学校">
-          <RootOrgSelect
+          <!-- <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" />
@@ -188,6 +142,9 @@
         </a-form-item>
       </a-form>
     </a-modal>
+
+    <!-- ModifyUser -->
+    <ModifyUser ref="modifyUserRef" :row-data="curRow" @modified="search" />
   </div>
 </template>
 
@@ -198,14 +155,13 @@ import {
   importUsers,
   resetPasswords,
   toggleUsers,
-  updateUser,
 } from "@/api/userManagementPage";
-import router from "@/router";
 import { useMainStore } from "@/store";
 import { User } from "@/types";
 import { downloadFileURL } from "@/utils/utils";
 import { message, Modal } from "ant-design-vue";
-import { watch, onMounted, ref, reactive, toRaw, h } from "vue";
+import { watch, onMounted, h, ref } from "vue";
+import ModifyUser from "./ModifyUser.vue";
 
 const store = useMainStore();
 store.currentLocation = "";
@@ -316,44 +272,16 @@ onMounted(async () => {
   await search();
 });
 
-const visible = ref<boolean>(false);
-
-const showModal = (record: User) => {
-  Object.assign(userObj, record);
-  visible.value = true;
-};
-
-const handleOk = async () => {
-  await updateUser(toRaw(userObj));
-  visible.value = false;
-  await search();
-  void message.success({ content: "操作成功" });
-};
-
-const initUser = <User>(<unknown>{
-  id: undefined,
-  code: "",
-  name: "",
-  loginName: "",
-  password: "",
-  enable: true,
-  role: "",
-  rootOrgId: store.isSuperAdmin ? null : store.userInfo.rootOrgId,
-});
-let userObj = reactive({ ...initUser });
-
-const newUser = () => {
-  Object.assign(userObj, initUser);
-  userObj.rootOrgId = rootOrgId as number;
-  console.log(userObj.rootOrgId);
-
-  showModal(userObj);
-};
-
-// let curRoleCode = $ref("");
-// function roleChange(data: RoleOption | null) {
-//   curRoleCode = data ? data.code : "";
-// }
+const modifyUserRef = ref();
+const curRow = ref<User>({} as User);
+function toAdd() {
+  curRow.value = {} as User;
+  modifyUserRef.value?.open();
+}
+function toEdit(row: User) {
+  curRow.value = row;
+  modifyUserRef.value?.open();
+}
 
 function checkEmpty(selectIds: number[]): boolean {
   if (selectIds && selectIds.length > 0) {
@@ -435,10 +363,6 @@ async function downloadTpl() {
   await downloadFileURL("/api/ess/user/template");
 }
 
-async function handleUserPrivilege(userId: number) {
-  await router.push("/basic/user/privilege/" + userId);
-}
-
 const handleRowSelect = (selectedRowKeys: (string | number)[]) => {
   selectIds = selectedRowKeys as number[];
 };

+ 8 - 8
src/utils/formRules.ts

@@ -3,7 +3,7 @@ import type { Rule } from "ant-design-vue/es/form";
 export const username: Rule[] = [
   {
     required: true,
-    match: /^[a-zA-Z0-9][a-zA-Z0-9_]{2,19}$/,
+    pattern: /^[a-zA-Z0-9][a-zA-Z0-9_]{2,19}$/,
     message: "用户名必须以字母或数字开头,长度为3-20位,允许字母数字下划线",
   },
 ];
@@ -11,7 +11,7 @@ export const username: Rule[] = [
 export const password: Rule[] = [
   {
     required: true,
-    match: /^[a-zA-Z0-9_]{6,20}$/,
+    pattern: /^[a-zA-Z0-9_]{6,20}$/,
     message: "密码只能由数字、字母和下划线组成,长度6-20个字符",
   },
 ];
@@ -19,7 +19,7 @@ export const password: Rule[] = [
 export const phone: Rule[] = [
   {
     required: true,
-    match: /^1\d{10}$/,
+    pattern: /^1\d{10}$/,
     message: "请输入合适的手机号码",
   },
 ];
@@ -27,7 +27,7 @@ export const phone: Rule[] = [
 export const smscode: Rule[] = [
   {
     required: true,
-    match: /^[a-zA-Z0-9]{4}$/,
+    pattern: /^[a-zA-Z0-9]{4}$/,
     message: "请输入4位短信验证码",
   },
 ];
@@ -35,9 +35,9 @@ export const smscode: Rule[] = [
 export const strictPassword: Rule[] = [
   {
     required: true,
-    validator: (value, callback) => {
+    validator: (_rule: Rule, value: string) => {
       if (!value) {
-        return callback(`请输入密码`);
+        return Promise.reject(`请输入密码`);
       }
       // // 禁止使用相同的数字或字符作为密码
       // const reg2 = /^[a-zA-Z0-9].+$/;
@@ -67,12 +67,12 @@ export const strictPassword: Rule[] = [
       const reg1 =
         /^(?![\d]+$)(?![a-z]+$)(?![A-Z]+$)(?![!@#$%^&*]+$)[\da-zA-Z!@#$%^&*]{8,20}$/g;
       if (!reg1.test(value)) {
-        return callback(
+        return Promise.reject(
           `密码应至少包含数字、大小写字母及特殊宇符(!@#$%^&*)中的两种,长度8-20位`
         );
       }
 
-      return callback();
+      return Promise.resolve();
     },
   },
 ];

+ 31 - 0
src/utils/utils.ts

@@ -53,3 +53,34 @@ export function downloadByLink(url: string) {
 export function goBack() {
   router.back();
 }
+
+/**
+ * 将目标对象中有的属性值与源对象中的属性值合并
+ * @param {Object} target 目标对象
+ * @param {Object} sources 源对象
+ */
+export function objAssign<T extends object>(
+  target: object,
+  sources: object
+): T {
+  const targ = { ...target };
+  Object.keys(targ).forEach((k) => {
+    targ[k] = Object.prototype.hasOwnProperty.call(sources, k)
+      ? sources[k]
+      : targ[k];
+  });
+
+  return targ;
+}
+/**
+ * 使用目标对象中有的属性值修改源对象中的属性值
+ * @param {Object} target 目标对象
+ * @param {Object} sources 源对象
+ */
+export function objModifyAssign(target: object, sources: object) {
+  Object.keys(target).forEach((k) => {
+    if (Object.prototype.hasOwnProperty.call(sources, k)) {
+      target[k] = sources[k];
+    }
+  });
+}