ソースを参照

新增修改密码功能和系统样式优化

shylock 2 年 前
コミット
9757e2aea4

+ 0 - 6
config/routes.ts

@@ -31,12 +31,6 @@
     path: '/list',
     component: './TableList',
   },
-  {
-    path: '/demo',
-    name: 'demo',
-    icon: 'smile',
-    component: './Demo',
-  },
   {
     path: '/',
     redirect: '/welcome',

+ 40 - 8
src/components/RightContent/AvatarDropdown.tsx

@@ -1,13 +1,15 @@
 import { outLogin } from '@/services/ant-design-pro/api';
-import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
+import { LockOutlined, LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
 import { Avatar, Menu, Spin } from 'antd';
 import type { ItemType } from 'antd/lib/menu/hooks/useItems';
 import { stringify } from 'querystring';
 import type { MenuInfo } from 'rc-menu/lib/interface';
-import React, { useCallback } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
 import { history, useModel } from 'umi';
 import HeaderDropdown from '../HeaderDropdown';
+import { currentUserInfo } from '@/services/setting';
 import styles from './index.less';
+import UpdatePassword from '@/components/RightContent/updatePassword';
 
 export type GlobalHeaderRightProps = {
   menu?: boolean;
@@ -33,6 +35,17 @@ const loginOut = async () => {
 
 const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
   const { initialState, setInitialState } = useModel('@@initialState');
+  const [avatar, setAvatar] = useState('');
+  const [visible, setVisible] = useState(false);
+
+  useEffect(() => {
+    const token = localStorage.getItem('token');
+    currentUserInfo(token).then((res) => {
+      if (res && res.record_id) {
+        setAvatar(res?.photo);
+      }
+    });
+  }, []);
 
   const onMenuClick = useCallback(
     (event: MenuInfo) => {
@@ -42,6 +55,10 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
         loginOut();
         return;
       }
+      if (key === 'updatePassword') {
+        setVisible(true);
+        return;
+      }
       history.push(`/account/${key}`);
     },
     [setInitialState],
@@ -87,6 +104,11 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
           },
         ]
       : []),
+    {
+      key: 'updatePassword',
+      icon: <LockOutlined />,
+      label: '修改密码',
+    },
     {
       key: 'logout',
       icon: <LogoutOutlined />,
@@ -99,12 +121,22 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
   );
 
   return (
-    <HeaderDropdown overlay={menuHeaderDropdown}>
-      <span className={`${styles.action} ${styles.account}`}>
-        <Avatar size="small" className={styles.avatar} src={currentUser.avatar} alt="avatar" />
-        <span className={`${styles.name} anticon`}>{currentUser.name}</span>
-      </span>
-    </HeaderDropdown>
+    <>
+      <HeaderDropdown overlay={menuHeaderDropdown}>
+        <span className={`${styles.action} ${styles.account}`}>
+          <Avatar size="small" className={styles.avatar} src={avatar} alt="avatar" />
+          <span className={`${styles.name} anticon`}>{currentUser.name}</span>
+        </span>
+      </HeaderDropdown>
+      {visible && (
+        <UpdatePassword
+          visible={visible}
+          onCancel={() => {
+            setVisible(false);
+          }}
+        />
+      )}
+    </>
   );
 };
 

+ 98 - 0
src/components/RightContent/updatePassword.tsx

@@ -0,0 +1,98 @@
+import { message, Form, Input, Modal } from 'antd';
+import { history, useModel } from 'umi';
+import md5 from 'js-md5';
+import { updatePwd } from '@/services/ant-design-pro/api';
+
+/**
+ * 修改密码
+ * @param props
+ * @constructor
+ */
+const UpdatePassword = (props: any) => {
+  const { initialState, setInitialState } = useModel('@@initialState');
+  const { visible, onCancel } = props;
+  const [form] = Form.useForm();
+
+  const handleCancel = () => {
+    if (onCancel) {
+      onCancel();
+    }
+  };
+
+  const onFinish = () => {
+    form.validateFields().then((values) => {
+      // const reg = /^(?=.*\d)(?=.*[a-zA-Z])(?=.*[~!@#$%^&*])[\da-zA-Z~!@#$%^&*]{8,}$/;
+      // if (!reg.test(values.new_password)) {
+      //   message.warning('密码必须为数字、字母和特殊字符(!@#$%^&*)的组合,且不能小于8位');
+      //   return;
+      // }
+      const formData = {
+        old_password: md5(values.old_password),
+        new_password: md5(values.new_password),
+      };
+      updatePwd(formData).then((res: any) => {
+        if (res.status === 'OK') {
+          message.success('密码更新成功!');
+          console.log(initialState);
+          setInitialState((s) => ({ ...s, currentUser: undefined }));
+          history.push(`/user/login`);
+        }
+      });
+    });
+  };
+
+  const formItemLayout = {
+    labelCol: {
+      span: 6,
+    },
+    wrapperCol: {
+      span: 16,
+    },
+  };
+
+  return (
+    <Modal title="修改密码" open={visible} width={500} onOk={onFinish} onCancel={handleCancel}>
+      <Form form={form}>
+        <Form.Item
+          {...formItemLayout}
+          name="old_password"
+          label="旧密码"
+          hasFeedback
+          rules={[{ required: true, message: '请输入旧密码' }]}
+        >
+          <Input type="password" placeholder="请输入旧密码" />
+        </Form.Item>
+        <Form.Item
+          {...formItemLayout}
+          name="new_password"
+          label="新密码"
+          hasFeedback
+          rules={[{ required: true, message: '请输入新密码' }]}
+        >
+          <Input type="password" placeholder="请输入新密码" />
+        </Form.Item>
+        <Form.Item
+          {...formItemLayout}
+          name="confirm_new_password"
+          label="确认新密码"
+          hasFeedback
+          rules={[
+            { required: true, message: '请输入确认新密码' },
+            ({ getFieldValue }) => ({
+              validator(_, value) {
+                if (!value || getFieldValue('new_password') === value) {
+                  return Promise.resolve();
+                }
+                return Promise.reject(new Error('两次密码输入不一致!'));
+              },
+            }),
+          ]}
+        >
+          <Input type="password" placeholder="请再次输入新密码" />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};
+
+export default UpdatePassword;

+ 2 - 2
src/pages/404.tsx

@@ -6,10 +6,10 @@ const NoFoundPage: React.FC = () => (
   <Result
     status="404"
     title="404"
-    subTitle="Sorry, the page you visited does not exist."
+    subTitle="抱歉,您访问的页面不存在"
     extra={
       <Button type="primary" onClick={() => history.push('/')}>
-        Back Home
+        返回首页
       </Button>
     }
   />

+ 0 - 231
src/pages/Demo/index.tsx

@@ -1,231 +0,0 @@
-import { Card, Tabs, Input, Typography, Space, Form, Button, Table, Tag, Modal } from 'antd';
-import type { ColumnsType } from 'antd/es/table';
-import React from 'react';
-import { useState } from 'react';
-import { postData, getData } from '@/services/demo';
-import { ExclamationCircleOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons';
-import moment from 'moment';
-
-const { Text } = Typography;
-const { Search } = Input;
-
-interface DataType {
-  key: string;
-  name: string;
-  age: number;
-  address: string;
-  record_id: string;
-  tags: string[];
-}
-
-const DemoPage: React.FC = () => {
-  const [currentKey, setCurrentKey] = useState('1');
-  const [tabText1, setTabText1] = useState('Tab1');
-  const [tabText2, setTabText2] = useState('Tab2');
-  const [tabText3, setTabText3] = useState('Tab3');
-  const [form] = Form.useForm();
-
-  //  tab标签切换
-  const handleOnChange = (key: string) => {
-    setCurrentKey(key);
-  };
-
-  //  tab下的搜索
-  async function onSearch(text: string) {
-    switch (currentKey) {
-      case '1': {
-        const result = await postData(text);
-        const res = result.data.text;
-        setTabText1(res);
-        break;
-      }
-      case '2': {
-        const result = await getData(text);
-        const res = result.data.text;
-        setTabText2(res);
-        break;
-      }
-      case '3': {
-        const result = await postData(text);
-        const res = result.data.text;
-        setTabText3(res);
-        break;
-      }
-      default: {
-        console.log('On Search: Error Key');
-        break;
-      }
-    }
-  }
-
-  //  删除
-  const toDelete = (record: DataType) => {
-    Modal.confirm({
-      title: '确认删除吗?',
-      icon: <ExclamationCircleOutlined />,
-      okText: '确认',
-      okType: 'danger',
-      cancelText: '取消',
-      onOk() {
-        console.log('OK');
-      },
-      onCancel() {
-        console.log('Cancel', record);
-      },
-    });
-  };
-
-  const columns: ColumnsType<DataType> = [
-    {
-      title: '序号',
-      align: 'center',
-      key: 'index',
-      render: (_: any, row: any, index: number) => index + 1,
-    },
-    {
-      title: '名称',
-      dataIndex: 'name',
-      key: 'name',
-      render: (
-        text:
-          | boolean
-          | React.ReactChild
-          | React.ReactFragment
-          | React.ReactPortal
-          | null
-          | undefined,
-      ) => <a>{text}</a>,
-    },
-    {
-      title: '数字',
-      dataIndex: 'age',
-      key: 'age',
-    },
-    {
-      title: '地址',
-      dataIndex: 'address',
-      key: 'address',
-    },
-    {
-      title: '日期',
-      dataIndex: 'date',
-      key: 'date',
-      render: (data) => data && moment(data).format('YYYY-MM-DD HH:ss:mm'),
-    },
-    {
-      title: '标签们',
-      key: 'tags',
-      dataIndex: 'tags',
-      render: (_, { tags }) => (
-        <>
-          {tags.map((tag) => {
-            let color = tag.length > 5 ? 'geekblue' : 'green';
-            if (tag === 'loser') {
-              color = 'volcano';
-            }
-            return (
-              <Tag color={color} key={tag}>
-                {tag.toUpperCase()}
-              </Tag>
-            );
-          })}
-        </>
-      ),
-    },
-    {
-      title: '操作',
-      key: 'action',
-      render: (_, record) => (
-        <Space size="middle">
-          <a>编辑</a>
-          <a
-            onClick={() => {
-              toDelete(record);
-            }}
-          >
-            删除
-          </a>
-        </Space>
-      ),
-    },
-  ];
-
-  const data = [
-    {
-      record_id: '1',
-      key: '1',
-      name: '111',
-      age: 31,
-      address: '地址地址地址1',
-      date: '2022-10-07T10:44:21',
-      tags: ['loser', 'xixixi'],
-    },
-    {
-      record_id: '2',
-      key: '2',
-      name: '222',
-      age: 32,
-      address: '地址地址地址2',
-      date: '2022-10-07T10:44:21',
-      tags: ['loser', 'hahaha'],
-    },
-  ];
-
-  return (
-    <div className="site-card-border-less-wrapper">
-      <Card title="demo">
-        <Form form={form} layout="inline">
-          <Form.Item name="first" label="第一个">
-            <Input placeholder="请输入..." />
-          </Form.Item>
-          <Form.Item name="first" label="第二个">
-            <Input placeholder="请输入..." />
-          </Form.Item>
-          <Form.Item style={{ marginBottom: '10px' }}>
-            <Space>
-              <Button type="primary" htmlType="submit">
-                <SearchOutlined />
-                查询
-              </Button>
-              <Button htmlType="button">
-                <ReloadOutlined />
-                重置
-              </Button>
-            </Space>
-          </Form.Item>
-        </Form>
-        <Tabs defaultActiveKey="1" activeKey={currentKey} onChange={handleOnChange}>
-          <Tabs.TabPane tab="Demo_Tab_1" key="1" />
-          <Tabs.TabPane tab="Demo_Tab_2" key="2" />
-          <Tabs.TabPane tab="Demo_Tab_3" key="3" />
-        </Tabs>
-        {currentKey === '1' && (
-          <Space direction="vertical">
-            <Search placeholder="input search text1" onSearch={onSearch} enterButton />
-            <Text> {tabText1} </Text>
-          </Space>
-        )}
-        {currentKey === '2' && (
-          <Space direction="vertical">
-            <Search placeholder="input search text2" onSearch={onSearch} enterButton />
-            <Text> {tabText2} </Text>
-          </Space>
-        )}
-        {currentKey === '3' && (
-          <Space direction="vertical">
-            <Search placeholder="input search text3" onSearch={onSearch} enterButton />
-            <Text> {tabText3} </Text>
-          </Space>
-        )}
-        <Table
-          columns={columns}
-          dataSource={data}
-          pagination={false}
-          rowKey={(record) => record.record_id}
-        />
-      </Card>
-    </div>
-  );
-};
-
-export default DemoPage;

+ 85 - 0
src/pages/setting/UserManagement/check.tsx

@@ -0,0 +1,85 @@
+import React, { useEffect, useState } from 'react';
+import { Descriptions, Modal, Image } from 'antd';
+import { queryUserDetail } from '@/services/setting';
+import moment from 'moment';
+
+interface userCheckPros {
+  visible: boolean;
+  id: string;
+  onCallback: () => void;
+}
+
+interface detailType {
+  user_name: string;
+  real_name: string;
+  phone: string;
+  photo: string;
+  created_at: string;
+  updated_at: string;
+  status: string;
+}
+
+/**
+ * 用户查看
+ * @param props
+ * @constructor
+ */
+const Check: React.FC<userCheckPros> = (props) => {
+  const { visible, onCallback, id } = props;
+  const [dataDetail, setDataDetail] = useState<detailType>({
+    phone: '',
+    photo: '',
+    real_name: '',
+    user_name: '',
+    created_at: '',
+    status: '',
+    updated_at: '',
+  });
+
+  useEffect(() => {
+    queryUserDetail(id).then((res) => {
+      if (res && res.record_id) {
+        setDataDetail(res);
+      }
+    });
+  }, []);
+
+  // 确定
+  const onOk = () => {
+    onCallback();
+  };
+
+  // 取消
+  const onCancel = () => {
+    onCallback();
+  };
+
+  return (
+    <Modal title="查看" open={visible} onOk={onOk} onCancel={onCancel} width={1000}>
+      <Descriptions title="用户信息" bordered column={2}>
+        <Descriptions.Item label="用户姓名">{dataDetail?.user_name}</Descriptions.Item>
+        <Descriptions.Item label="真实姓名">{dataDetail?.real_name}</Descriptions.Item>
+        <Descriptions.Item label="手机号">{dataDetail?.phone}</Descriptions.Item>
+        <Descriptions.Item label="状态">
+          <span style={{ color: `${{ 1: '#00a650', 2: 'red' }[dataDetail?.status]}` }}>
+            {{ 1: '启用', 2: '停用' }[dataDetail?.status]}
+          </span>
+        </Descriptions.Item>
+        <Descriptions.Item label="创建时间">
+          {dataDetail?.created_at
+            ? moment(dataDetail?.created_at).format('YYYY-MM-DD HH:ss:mm')
+            : '暂无'}
+        </Descriptions.Item>
+        <Descriptions.Item label="更新时间">
+          {dataDetail?.updated_at
+            ? moment(dataDetail?.updated_at).format('YYYY-MM-DD HH:ss:mm')
+            : '暂无'}
+        </Descriptions.Item>
+        <Descriptions.Item label="头像">
+          <Image width={100} src={dataDetail?.photo} />
+        </Descriptions.Item>
+      </Descriptions>
+    </Modal>
+  );
+};
+export default Check;

+ 26 - 4
src/pages/setting/UserManagement/index.tsx

@@ -6,6 +6,7 @@ import moment from 'moment';
 import Edit from '@/pages/setting/UserManagement/edit';
 import { disableUser, enableUser, queryUserList } from '@/services/setting';
 import { PageContainer } from '@ant-design/pro-components';
+import Check from '@/pages/setting/UserManagement/check';
 
 interface DataType {
   key: string;
@@ -30,6 +31,8 @@ const UserManagement: React.FC = () => {
   const [dataList, setDataList] = useState([]);
   const [pagination, setPagination] = useState({ total: 0, current: 1, pageSize: 10 });
   const [loading, setLoading] = useState(false);
+  const [checkVisible, setCheckVisible] = useState(false);
+  const [checkId, setCheckId] = useState('');
 
   // 获取列表数据
   const getListData = () => {
@@ -143,6 +146,17 @@ const UserManagement: React.FC = () => {
     });
   };
 
+  //  查看
+  const toCheck = (record: DataType) => {
+    setCheckVisible(true);
+    setCheckId(record?.record_id);
+  };
+
+  // 查看回调
+  const checkCallback = () => {
+    setCheckVisible(false);
+  };
+
   const columns: ColumnsType<DataType> = [
     {
       title: '序号',
@@ -154,7 +168,7 @@ const UserManagement: React.FC = () => {
       title: '头像',
       dataIndex: 'photo',
       key: 'photo',
-      render: (v) => v && <Image width={50} src={`http://192.168.0.224:18199${v}`} alt="头像" />,
+      render: (v) => v && <Image width={50} src={v} alt="头像" />,
     },
     {
       title: '用户姓名',
@@ -183,9 +197,9 @@ const UserManagement: React.FC = () => {
         ),
     },
     {
-      title: '更新时间',
-      dataIndex: 'updated_at',
-      key: 'updated_at',
+      title: '创建时间',
+      dataIndex: 'created_at',
+      key: 'created_at',
       render: (v) => v && moment(v).format('YYYY-MM-DD HH:ss'),
     },
     {
@@ -220,6 +234,13 @@ const UserManagement: React.FC = () => {
               停用
             </a>
           )}
+          <a
+            onClick={() => {
+              toCheck(record);
+            }}
+          >
+            查看
+          </a>
         </Space>
       ),
     },
@@ -283,6 +304,7 @@ const UserManagement: React.FC = () => {
           onChange={tableChange}
         />
         {visible && <Edit visible={visible} editCallback={editCallback} params={editData} />}
+        {checkVisible && <Check visible={checkVisible} id={checkId} onCallback={checkCallback} />}
       </Card>
     </PageContainer>
   );

+ 1 - 1
src/pages/user/Login/index.less

@@ -2,7 +2,7 @@
 
 .container {
   height: 100%;
-  background: url('../../../../public/assets/login_left.png') top right no-repeat;
+  background: url('/assets/login_left.png') top right no-repeat;
   .loginBox {
     .logo_style {
       width: 150px;

+ 0 - 1
src/pages/user/Login/index.tsx

@@ -107,7 +107,6 @@ const Login: React.FC = () => {
             >
               欢迎登录
             </div>
-            {/*<img src="/assets/logo.png" alt="logo" className={styles.logoStyle} />*/}
           </div>
           <Form layout="vertical" onFinish={handleSubmit}>
             <FormItem

+ 11 - 0
src/services/ant-design-pro/api.ts

@@ -33,6 +33,17 @@ export async function login(body: API.LoginParams, options?: { [key: string]: an
   });
 }
 
+/**
+ * 修改密码
+ * @param params
+ */
+export async function updatePwd(params: any) {
+  return request('/web/v1/login/password', {
+    method: 'PUT',
+    data: params,
+  });
+}
+
 /** 此处后端没有提供注释 GET /api/notices */
 export async function getNotices(options?: { [key: string]: any }) {
   return request<API.NoticeIconList>('/api/notices', {

+ 0 - 21
src/services/demo.ts

@@ -1,21 +0,0 @@
-import { request } from 'umi';
-
-export async function postData(params: any) {
-  return request('/api/postData', {
-    method: 'POST',
-    data: params,
-  });
-}
-
-//export async function getData(params:any){
-//return request(`/api/getData?${stringify(params)}`);
-//}
-export async function getData(params: any) {
-  // return request(`/api/getData?${stringify(params)}`);
-  return request('/api/getData', {
-    method: 'GET',
-    params: {
-      ...params,
-    },
-  });
-}

+ 19 - 0
src/services/setting/index.ts

@@ -9,6 +9,25 @@ export async function queryUserList(param: object) {
   return request(`/web/v1/users?${stringify(param)}`);
 }
 
+/**
+ * 查询用户详情信息
+ * @param id
+ */
+export async function queryUserDetail(id: string) {
+  return request(`/web/v1/users/${id}`);
+}
+
+/** 获取当前的用户信息 */
+export async function currentUserInfo(param: any) {
+  return request<{
+    record_id: string;
+    photo: string;
+  }>('/web/v1/users/current', {
+    method: 'GET',
+    headers: { Authorization: param },
+  });
+}
+
 /**
  * 新增用户信息
  * @param params