业务开发之用户管理(七)

云风网
云风笔记
云风知识库

首先从逻辑上,用户管理只限制admin用户显示

一、路由限制用户管理的访问权限

config/routes.ts添加access:admin权限限制

{
    name: 'userManage',
    icon: 'table',
    access: 'canAdmin',
    path: '/userManage',
    component: './userManage',
  }

二、页面水印处理

水印是 PageContainer 的功能,layout 只是透传给 PageContainer,屏蔽方法,找到文件src/app.tsx,直接注释代码

waterMarkProps: {
  content: initialState?.currentUser?.username
},

三、列表的增删改查功能接口联调以及页面代码修改

1.1、node开发查询用户接口,找到node项目router/user.js,添加查询用户列表接口代码。然后重启项目
//获取用户列表数据
router.get('/getUserList',commonFunc.authMiddle,(req, res) => {
    const page = parseInt(req.query.page) || 1;
    const limit = parseInt(req.query.limit) || 10;
    const name = req.query.username || '';
    userModel.find({ username: { $regex: name, $options: 'i' }}).countDocuments().then((allTotal)=>{
      userModel.find({ username: { $regex: name, $options: 'i' }}).skip((page - 1) * limit).limit(limit).then((data)=>{
        res.send({
          code: 200,
          msg: "获取成功!",
          data,
          total:allTotal
        });
      }).catch((err)=>{
        res.send(err)
      })
    })
})
//改变用户名称或者密码
router.post('/changeUser',commonFunc.authMiddle,(req, res) => {
  const token = req.headers.authorization
  if(token){
    var { _id,username,password } = req.body;
    userModel.find({ username: { $regex: username, $options: 'i' }}).then((data)=>{
      if (data.length != 0) { //功能存在
        userModel.updateOne(
          {
            _id: _id,
          },
          {
            $set: {
              username: username,
              password: password
            }
          }
        ).then(() => {
            res.send({
              code: 200,
              msg: "修改成功!",
            });
          })
          .catch(err => {
            res.send(err)
          });
      }else{
        res.send({ code: 400, msg: "用户不存在,修改失败!" });
      }
    }).catch((err)=>{
      res.send(err)
    })
  }else{
    res.send({
      code: 401,
      msg: "请先登录!"
    });
  }
})
//删除用户
router.delete('/delUser',commonFunc.authMiddle,(req, res) => {
  const token = req.headers.authorization
  if(token){
    //req 请求对象
    var { ids } = req.body;
    // 将字符串ID转换为ObjectId
    // const objectIds = ids.map(id => new ObjectId(id));
    userModel.deleteMany({ _id: { $in: ids } }).then(() => {
      res.send({
        code: 200,
        msg: "删除成功!",
      });
    })
    .catch(err => {
      res.send(err)
    });
    
  }else{
    res.send({
      code: 401,
      msg: "请先登录!"
    });
  }
})
1.2、改造用户管理前端代码src/pages/userManage/index.tsx
import { register, delUser, getUserList, updateUser } from '@/services/custom/api';
import { PlusOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns, ProDescriptionsItemProps } from '@ant-design/pro-components';
import {
  FooterToolbar,
  ModalForm,
  PageContainer,
  ProDescriptions,
  ProFormText,
  ProTable,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Button, Drawer,message,Form } from 'antd';
import React, { useRef, useState,useEffect } from 'react';
import type { FormValueType } from './components/UpdateForm';
import UpdateForm from './components/UpdateForm';

/**
 * @en-US Add node
 * @zh-CN 添加节点
 * @param fields
 */
const handleAdd = async (fields: API.RuleListItem) => {
  const hide = message.loading('正在添加');
  try {
    await register({ ...fields });
    hide();
    message.success('添加成功');
    return true;
  } catch (error) {
    hide();
    message.error('添加失败,请重试!');
    return false;
  }
};

/**
 * @en-US Update node
 * @zh-CN 更新节点
 *
 * @param fields
 */
const handleUpdate = async (fields: FormValueType) => {
  const hide = message.loading('Configuring');
  try {
    await updateUser({
      username: fields.username,
      password: fields.password,
      _id: fields._id,
    });
    hide();

    message.success('修改成功');
    return true;
  } catch (error) {
    hide();
    message.error('修改失败,请稍后再试');
    return false;
  }
};

/**
 *  Delete node
 * @zh-CN 删除节点
 *
 * @param selectedRows
 */
const handleRemove = async (selectedRows: API.RuleListItem[],actionRef:any) => {
  const hide = message.loading('正在删除');
  if (!selectedRows) return true;
  try {
    await delUser({
      ids: selectedRows.map((row) => row._id),
    });
    hide();
    message.success('删除成功');
    if(actionRef){
      actionRef.current?.reloadAndRest?.();
    }
    return true;
  } catch (error) {
    hide();
    message.error('删除失败,请重试');
    return false;
  }
};

const TableList: React.FC = () => {
  /**
   * @en-US Pop-up window of new window
   * @zh-CN 新建窗口的弹窗
   *  */
  const [createModalOpen, handleModalOpen] = useState<boolean>(false);
  /**
   * @en-US The pop-up window of the distribution update window
   * @zh-CN 分布更新窗口的弹窗
   * */
  const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);

  const [showDetail, setShowDetail] = useState<boolean>(false);

  const actionRef = useRef<ActionType>();
  const [currentRow, setCurrentRow] = useState<API.RuleListItem>();
  const [selectedRowsState, setSelectedRows] = useState<API.RuleListItem[]>([]);

  /**
   * @en-US International configuration
   * @zh-CN 国际化配置
   * */
  const intl = useIntl();
  const [form] = Form.useForm();

  const handleDel = async (selectedRows: API.RuleListItem[],actionRef: any) => {
    await handleRemove(selectedRows,actionRef);
  }
  useEffect(() => {
    form.resetFields();
    form.setFieldsValue({});
  });
  const columns: ProColumns<API.RuleListItem>[] = [
    {
      title: <FormattedMessage id="pages.searchTable.titleDesc" defaultMessage="Description" />,
      dataIndex: '_id',
      search:false,
      valueType: 'textarea'
    },
    {
      title: (
        <FormattedMessage
          id="pages.searchTable.updateForm.ruleName.nameLabel"
          defaultMessage="Rule name"
        />
      ),
      dataIndex: 'username',
      // render: (dom, entity) => {
      //   return (
      //     <a
      //       onClick={() => {
      //         setCurrentRow(entity);
      //         setShowDetail(true);
      //       }}
      //     >
      //       {dom}
      //     </a>
      //   );
      // },
    },

    {
      title: (
        <FormattedMessage
          id="pages.searchTable.password"
          defaultMessage="Number of service calls"
        />
      ),
      dataIndex: 'password',
      sorter: false,
      search:false,
      hideInForm: true,
      renderText: (val: string) =>
        `${val}`,
    },
    {
      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="Operating" />,
      dataIndex: 'option',
      valueType: 'option',
      render: (_, record) => [
        <a
          key="config"
          onClick={() => {
            setCurrentRow(record);
            handleUpdateModalOpen(true);
          }}
        >
          <FormattedMessage id="pages.searchTable.config" defaultMessage="Configuration" />
        </a>,
        <a
          key="del"
          onClick={() => {
            setCurrentRow(record);
            handleDel([record],actionRef)
          }}
        >
          <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
        </a>
      ],
    },
  ];

  return (
    <PageContainer>
      <ProTable<API.RuleListItem, API.PageParams>
        headerTitle={intl.formatMessage({
          id: 'pages.searchTable.title',
          defaultMessage: '用户列表',
        })}
        actionRef={actionRef}
        rowKey="_id"
        search={{labelWidth:150}}
        toolBarRender={() => [
          <Button
            type="primary"
            key="primary"
            onClick={() => {
              handleModalOpen(true);
            }}
          >
            <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="New" />
          </Button>
        ]}
        request={getUserList}
        columns={columns}
        rowSelection={{
          onChange: (_, selectedRows) => {
            setSelectedRows(selectedRows);
          },
        }}
      />
      {selectedRowsState?.length > 0 && (
        <FooterToolbar
          extra={
            <div>
              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="Chosen" />{' '}
              <a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a>{' '}
              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
            </div>
          }
        >
          <Button
            onClick={async () => {
              await handleRemove(selectedRowsState,'');
              setSelectedRows([]);
              actionRef.current?.reloadAndRest?.();
            }}
          >
            <FormattedMessage
              id="pages.searchTable.batchDeletion"
              defaultMessage="Batch deletion"
            />
          </Button>
        </FooterToolbar>
      )}
      <ModalForm
        form = {form}
        title={intl.formatMessage({
          id: 'pages.searchTable.createForm.newUser',
          defaultMessage: '新建用户',
        })}
        width="400px"
        open={createModalOpen}
        onOpenChange={handleModalOpen}
        onFinish={async (value) => {
          const success = await handleAdd(value as API.RuleListItem);
          if (success) {
            handleModalOpen(false);
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }
        }}
      >
        <ProFormText
          rules={[
            {
              required: true,
              message: (
                <FormattedMessage
                  id="pages.searchTable.ruleName"
                  defaultMessage="用户名是必填的"
                />
              ),
            },
          ]}

          label="用户名"
          placeholder="请输入用户名"
          width="md"
          name="username"
          initialValue={''}
        />
        <ProFormText
          rules={[
            {
              required: true,
              message: (
                <FormattedMessage
                  id="pages.login.password.placeholder"
                  defaultMessage="密码是必填的"
                />
              ),
            },
          ]}
          label="密码"
          placeholder="请输入密码"
          width="md"
          name="password"
          initialValue={''}
        />
      </ModalForm>
      <UpdateForm
        onSubmit={async (value) => {
          const success = await handleUpdate(value);
          if (success) {
            handleUpdateModalOpen(false);
            setCurrentRow(undefined);
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }
        }}
        onCancel={() => {
          handleUpdateModalOpen(false);
          if (!showDetail) {
            setCurrentRow(undefined);
          }
        }}
        updateModalOpen={updateModalOpen}
        values={currentRow || {}}
      />

      <Drawer
        width={600}
        open={showDetail}
        onClose={() => {
          setCurrentRow(undefined);
          setShowDetail(false);
        }}
        closable={false}
      >
        {currentRow?.name && (
          <ProDescriptions<API.RuleListItem>
            column={2}
            title={currentRow?.name}
            request={async () => ({
              data: currentRow || {},
            })}
            params={{
              id: currentRow?.name,
            }}
            columns={columns as ProDescriptionsItemProps<API.RuleListItem>[]}
          />
        )}
      </Drawer>
    </PageContainer>
  );
};

export default TableList;

其中编辑组件代码components/UpdateForm

import {
  ModalForm,
  ProFormText
} from '@ant-design/pro-components';
import { useIntl,FormattedMessage } from '@umijs/max';
import { Form } from 'antd';
import React from 'react';
import { useEffect } from 'react';
export type FormValueType = {
  username?: string;
  password?: string;
  _id?: string;
} & Partial<API.RuleListItem>;

export type UpdateFormProps = {
  onCancel: (flag?: boolean, formVals?: FormValueType) => void;
  onSubmit: (values: FormValueType) => Promise<void>;
  updateModalOpen: boolean;
  values: Partial<API.RuleListItem>;
};
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
  const intl = useIntl();
  const [form] = Form.useForm();
  useEffect(() => {
    form.resetFields();
    form.setFieldsValue(props.values);
  });
  return (
    <ModalForm
      title={intl.formatMessage({
        id: 'pages.searchTable.updateForm.ruleConfig',
        defaultMessage: '编辑配置',
      })}
      form = {form}
      width="400px"
      open={props.updateModalOpen}
      onOpenChange={async (value) => {
        if(!value){
          props.onCancel(value);
        }
      }}
      onFinish={async (value:any) => {
        value._id = props.values._id
        props.onSubmit(value)
      }}
    >
      <ProFormText
        rules={[
          {
            required: true,
            message: (
              <FormattedMessage
                id="pages.searchTable.ruleName"
                defaultMessage="请输入用户名"
              />
            ),
          }
        ]}
        placeholder="请输入用户名"
        label="用户名"
        width="md"
        name="username"
        initialValue={props.values.username}
      />
      <ProFormText
        rules={[
          {
            required: true,
            message: (
              <FormattedMessage
                id="pages.login.password.placeholder"
                defaultMessage="请输入密码"
              />
            ),
          }
        ]}
        placeholder="请输入密码"
        label="密码"
        width="md"
        name="password"
        initialValue={props.values.password}
      />
    </ModalForm>

  );
};

export default UpdateForm;

src/service/custom/typings.d.ts代码改造为

 type PageParams = {
    page?: number;
    limit?: number;
  };

  type UserListItem = {
    _id?: string;
    username?: string;
    password?: string;
  };

  type UserList = {
    data?: UserListItem[];
    /** 列表的内容总数 */
    total?: number;
    success?: boolean;
  };

src/service/custom/api.ts添加接口请求方法定义

/** 获取用户列表 GET /api/user/getUserList */
export async function getUserList(
  params: {
    // query
    /** 当前的页码 */
    page?: number;
    /** 页面的容量 */
    limit?: number;
  },
  options?: { [key: string]: any },
) {
  return request<API.UserList>('/api/user/getUserList', {
    method: 'GET',
    params: {
      ...params,
    },
    ...(options || {}),
  });
}

/** 更新用户信息 PUT /api/user/changeUser */
export async function updateUser(options?: { [_id: string]: any }) {
  return request<API.UserListItem>('/api/user/changeUser', {
    method: 'POST',
    data: {
      ...(options || {}),
    },
  });
}


/** 删除用户 DELETE /api/user/delUser */
export async function delUser(options?: { [key: string]: any }) {
  return request<Record<string, any>>('/api/user/delUser', {
    method: 'delete',
    data: {
      ...(options || {}),
    },
  });
}

四、注意要点

1、ModalForm下的ProFormText设置默认值initialValue只更新一次的问题
解决方法:
import { Form } from 'antd';
import { useEffect } from 'react';
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
  const intl = useIntl();
  const [form] = Form.useForm();
  useEffect(() => {
    form.resetFields();
    form.setFieldsValue(props.values);
  });
  return (
    <ModalForm
      form = {form}
    >
      <ProFormText
        initialValue={props.values.username}
      />
    </ModalForm>
  )
 }
2、隐藏不想要显示搜索框的列数据
解决方法:

columns中配置search:false

const columns: ProColumns<API.UserListItem>[] = [
    {
      title: <FormattedMessage id="pages.searchTable.titleDesc" defaultMessage="Description" />,
      dataIndex: '_id',
      search:false,
      valueType: 'textarea'
    }
 ]

五、最终效果如下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值