Преглед изворни кода

新增用户过期时间操作

pm пре 2 месеци
родитељ
комит
ccf86bf413

+ 24 - 5
src/components/UserManagement.tsx

@@ -17,6 +17,7 @@ import FaceOrFingerForm, { FaceOrFingerFormRef } from './user/FaceOrFingerForm';
 import { useTranslation } from 'react-i18next';
 import PermissionWrapper from './PermissionWrapper';
 import { hasPermission } from '../utils/permission';
+import { formatDateWithFormat } from '../utils/formatTime';
 
 interface UserManagementProps {
   subMenu: string;
@@ -102,6 +103,7 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
     const resetParams = {
       pageNo: 1,
       pageSize: 10,
+      nickname: '',
       username: '',
       mobile: '',
       status: undefined as number | undefined,
@@ -123,6 +125,17 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
     formRef.current?.open(type, id);
   };
 
+  const handleUserFormSuccess = (type: 'create' | 'update') => {
+    if (type === 'create') {
+      const nextParams = { ...queryParams, pageNo: 1 };
+      setQueryParams(nextParams);
+      getList(nextParams);
+      return;
+    }
+
+    getList();
+  };
+
   // 用户导入
   const handleImport = () => {
     importFormRef.current?.open();
@@ -271,7 +284,7 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
     {
       title: t('table.userId'),
       dataIndex: 'id',
-      width: '8%',
+      width: '6%',
     },
     {
       title: t('table.username'),
@@ -340,7 +353,7 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
     },
     {
       title: t('table.facePhoto'),
-      width: '8%',
+      width: '6%',
       render: (_: any, record: UserVO) => (
         <Button
           type="link"
@@ -353,7 +366,7 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
     },
     {
       title: t('table.status'),
-      width: '8%',
+      width: '6%',
       render: (_: any, record: UserVO) => (
         <Switch
           checked={record.status === 0}
@@ -367,6 +380,12 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
         />
       ),
     },
+    {
+      title: t('table.expireTime'),
+      dataIndex: 'expireTime',
+      width: '10%',
+      render: (text: string | number) => text ? formatDateWithFormat(text) : '-',
+    },
     {
       title: t('table.operation'),
       width: '10%',
@@ -436,7 +455,7 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
                 trigger={['hover']}
                 getPopupContainer={() => document.body}
                 placement="bottomRight"
-                popupStyle={{ zIndex: 1050 }}
+                overlayStyle={{ zIndex: 1050 }}
                 mouseEnterDelay={0.1}
                 mouseLeaveDelay={0.1}
               >
@@ -643,7 +662,7 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
       </div>
 
       {/* 相关子组件 */}
-      <UserForm ref={formRef} onSuccess={getList} />
+      <UserForm ref={formRef} onSuccess={handleUserFormSuccess} />
       <UserImportForm ref={importFormRef} onSuccess={getList} />
       <UserAssignRoleForm ref={assignRoleFormRef} onSuccess={getList} />
       <FaceOrFingerForm ref={faceOrFingerFormRef} onSuccess={getList} />

+ 58 - 7
src/components/user/UserForm.tsx

@@ -1,5 +1,5 @@
 import React, { useState, useImperativeHandle, forwardRef } from 'react';
-import { Modal, Form, Input, Select, TreeSelect, Row, Col, message } from 'antd';
+import { Modal, Form, Input, Select, TreeSelect, Row, Col, message, DatePicker } from 'antd';
 import { userApi } from '../../api/user';
 import { deptApi } from '../../api/dept';
 import { postApi, PostVO } from '../../api/Post';
@@ -8,11 +8,27 @@ import { getIntDictOptions, DICT_TYPE, setDictOptions, DictDataType } from '../.
 import { handleTree } from '../../utils/tree';
 import { useTranslation } from 'react-i18next';
 import { dictDataApi } from '../../api/DictData';
+import dayjs, { Dayjs } from 'dayjs';
 
 const { TextArea } = Input;
+const LONG_TERM_EXPIRE_TIME = '2099-12-31 00:00:00';
+
+const toDayjsValue = (value?: Date | string | number): Dayjs | undefined => {
+  if (!value && value !== 0) {
+    return undefined;
+  }
+
+  const normalizedValue =
+    typeof value === 'string' && /^\d+$/.test(value.trim())
+      ? Number(value)
+      : value;
+
+  const dayjsValue = dayjs(normalizedValue);
+  return dayjsValue.isValid() ? dayjsValue : undefined;
+};
 
 interface UserFormProps {
-  onSuccess?: () => void;
+  onSuccess?: (type: 'create' | 'update') => void;
 }
 
 export interface UserFormRef {
@@ -143,6 +159,7 @@ const UserForm = forwardRef<UserFormRef, UserFormProps>(({ onSuccess }, ref) =>
           workstationIds: userData.postIds || userData.workstationIds || [],
           deptId: userData.deptId,
           sex: sexValue, // 使用处理后的性别值
+          expireTime: toDayjsValue(userData.expireTime),
         });
         
         console.log('设置表单值 - 性别:', { 
@@ -173,13 +190,17 @@ const UserForm = forwardRef<UserFormRef, UserFormProps>(({ onSuccess }, ref) =>
   const submitForm = async () => {
     try {
       const values = await form.validateFields();
+      const normalizedValues = {
+        ...values,
+        expireTime: values.expireTime ? values.expireTime.format('YYYY-MM-DD HH:mm:ss') : undefined,
+      };
       
       setFormLoading(true);
       try {
         if (formType === 'create') {
           // 新增模式下,处理字段名映射:workstationIds -> postIds
           const createData: any = {
-            ...values,
+            ...normalizedValues,
           };
           // 将 workstationIds 映射为 postIds
           if (createData.workstationIds) {
@@ -196,9 +217,9 @@ const UserForm = forwardRef<UserFormRef, UserFormProps>(({ onSuccess }, ref) =>
             // 保留原始数据中的字段(这些字段用户不能编辑但需要传递)
             ...(originalUserData || {}),
             // 用表单数据覆盖可编辑字段
-            ...values,
+            ...normalizedValues,
             // 确保 id 存在
-            id: originalUserData?.id || values.id,
+            id: originalUserData?.id || normalizedValues.id,
           };
           
           // 如果密码为空,则移除密码字段
@@ -235,7 +256,7 @@ const UserForm = forwardRef<UserFormRef, UserFormProps>(({ onSuccess }, ref) =>
           message.success(t('form.updateSuccess'));
         }
         setDialogVisible(false);
-        onSuccess?.();
+        onSuccess?.(formType);
       } catch (error: any) {
         message.error(error.message || t('form.operationFailed'));
       } finally {
@@ -418,7 +439,37 @@ const UserForm = forwardRef<UserFormRef, UserFormProps>(({ onSuccess }, ref) =>
             </Form.Item>
           </Col>
           <Col span={12}>
-            <Form.Item label={t('form.remark')} name="remark">
+            <Form.Item
+              label={t('form.expireTime')}
+              name="expireTime"
+              rules={[{ required: true, message: t('form.expireTimeRequired') }]}
+            >
+              <DatePicker
+                style={{ width: '100%' }}
+                format="YYYY-MM-DD HH:mm:ss"
+                showTime={{ format: 'HH:mm:ss' }}
+                placeholder={t('form.expireTimePlaceholder')}
+                renderExtraFooter={() => (
+                  <div
+                    style={{ color: '#1677ff', cursor: 'pointer' }}
+                    onClick={() => form.setFieldValue('expireTime', dayjs(LONG_TERM_EXPIRE_TIME))}
+                  >
+                    {t('form.longTermValid')}
+                  </div>
+                )}
+              />
+            </Form.Item>
+          </Col>
+        </Row>
+
+        <Row gutter={16}>
+          <Col span={24}>
+            <Form.Item
+              label={t('form.remark')}
+              name="remark"
+              labelCol={{ span: 3 }}
+              wrapperCol={{ span: 21 }}
+            >
               <TextArea rows={3} placeholder={t('form.remarkPlaceholder')} />
             </Form.Item>
           </Col>

+ 5 - 0
src/locales/en.json

@@ -529,6 +529,7 @@
     "mobile": "Mobile",
     "department": "Department",
     "facePhoto": "Face Photo",
+    "expireTime": "Expire Time",
     "keyName": "Key Name",
     "keyNfc": "Key NFC",
     "keySpec": "Key Model",
@@ -676,6 +677,10 @@
     "userSex": "Gender",
     "cardNumber": "Card Number",
     "cardNumberPlaceholder": "Please enter card number (optional)",
+    "expireTime": "Expire Time",
+    "expireTimePlaceholder": "Please select expire time",
+    "expireTimeRequired": "Please select expire time",
+    "longTermValid": "Long Term Valid",
     "keyName": "Key Name",
     "keyNamePlaceholder": "Please enter key name",
     "keyNameRequired": "Please enter key name",

+ 5 - 0
src/locales/zh.json

@@ -531,6 +531,7 @@
     "mobile": "手机号码",
     "department": "部门",
     "facePhoto": "人脸照片",
+    "expireTime": "过期时间",
     "keyName": "钥匙名称",
     "keyNfc": "钥匙NFC",
     "keySpec": "钥匙型号",
@@ -678,6 +679,10 @@
     "userSex": "用户性别",
     "cardNumber": "卡号",
     "cardNumberPlaceholder": "请输入卡号",
+    "expireTime": "过期时间",
+    "expireTimePlaceholder": "请选择过期时间",
+    "expireTimeRequired": "请选择过期时间",
+    "longTermValid": "长期有效",
     "keyName": "钥匙名称",
     "keyNamePlaceholder": "请输入钥匙名称",
     "keyNameRequired": "请输入钥匙名称",

+ 1 - 0
src/types/index.ts

@@ -60,6 +60,7 @@ export interface UserVO {
   remark?: string;
   loginDate?: Date | string;
   createTime?: Date | string;
+  expireTime?: Date | string | number;
   cardNfc?: string;
 }
 

+ 14 - 2
src/utils/formatTime.ts

@@ -27,7 +27,13 @@ export function formatDate(
       dateObj = new Date(date);
     }
   } else if (typeof date === 'string') {
-    dateObj = new Date(date);
+    const trimmedDate = date.trim();
+    if (/^\d+$/.test(trimmedDate)) {
+      const timestamp = Number(trimmedDate);
+      dateObj = new Date(trimmedDate.length <= 10 ? timestamp * 1000 : timestamp);
+    } else {
+      dateObj = new Date(date);
+    }
   } else {
     dateObj = date;
   }
@@ -116,7 +122,13 @@ export function formatDateWithFormat(
     // 如果是数字,可能是时间戳(秒或毫秒)
     d = new Date(date > 1000000000000 ? date : date * 1000);
   } else if (typeof date === 'string') {
-    d = new Date(date);
+    const trimmedDate = date.trim();
+    if (/^\d+$/.test(trimmedDate)) {
+      const timestamp = Number(trimmedDate);
+      d = new Date(trimmedDate.length <= 10 ? timestamp * 1000 : timestamp);
+    } else {
+      d = new Date(date);
+    }
   } else {
     return '';
   }