#
Junjie
2025-02-14 ac4341ea6b66ae02427d39d35f41d42d78b2eb2e
zy-asrs-flow/src/pages/system/user/index.jsx
@@ -1,4 +1,3 @@
import React, { useState, useRef, useEffect } from 'react';
import { Button, message, Modal, Row, Col, Card, Tree, Input, Tag, Skeleton } from 'antd';
import {
@@ -6,102 +5,108 @@
    PageContainer,
    ProTable,
    LightFilter,
    TableDropdown
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { PlusOutlined, ExportOutlined, DownOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import Http from '@/utils/http';
import Edit from './components/edit'
import Pwd from './components/pwd'
import AssignRole from './components/assignRole'
import { TextFilter, SelectFilter, DatetimeRangeFilter, LinkFilter } from '@/components/TableSearch'
import { transformTreeData, getTreeAllKeys } from '@/utils/tree-util'
import { statusMap } from '@/utils/enum-util'
import { repairBug } from '@/utils/common-util';
const handleSave = async (val) => {
    const hide = message.loading('正在添加');
const TABLE_KEY = "pro-table-user";
const handleSave = async (val, intl) => {
    const hide = message.loading(intl.formatMessage({ id: 'page.adding', defaultMessage: '正在添加' }));
    try {
        const resp = await Http.doPost('api/user/save', val);
        if (resp.code === 200) {
            message.success('添加成功');
            message.success(intl.formatMessage({ id: 'page.add.success', defaultMessage: '添加成功' }));
            return true;
        } else {
            message.error(resp.msg);
            return false;
        }
    } catch (error) {
        message.error('添加失败请重试!');
        message.error(intl.formatMessage({ id: 'page.add.fail', defaultMessage: '添加失败请重试!' }));
        return false;
    } finally {
        hide();
    }
};
const handleUpdate = async (val) => {
    const hide = message.loading('正在更新');
const handleUpdate = async (val, intl) => {
    const hide = message.loading(intl.formatMessage({ id: 'page.updating', defaultMessage: '正在更新' }));
    try {
        const resp = await Http.doPost('api/user/update', val);
        if (resp.code === 200) {
            message.success('更新成功');
            message.success(intl.formatMessage({ id: 'page.update.success', defaultMessage: '更新成功' }));
            return true;
        } else {
            message.error(resp.msg);
            return false;
        }
    } catch (error) {
        message.error('配置失败请重试!');
        message.error(intl.formatMessage({ id: 'page.update.fail', defaultMessage: '更新失败请重试!' }));
        return false;
    } finally {
        hide();
    }
};
const handleRemove = async (rows) => {
const handleRemove = async (rows, intl) => {
    if (!rows) return true;
    const hide = message.loading('正在删除');
    const hide = message.loading(intl.formatMessage({ id: 'page.deleting', defaultMessage: '正在删除' }));
    try {
        const resp = await Http.doPost('api/user/remove/' + rows.map((row) => row.id).join(','));
        if (resp.code === 200) {
            message.success('删除成功');
            message.success(intl.formatMessage({ id: 'page.delete.success', defaultMessage: '删除成功' }));
            return true;
        } else {
            message.error(resp.msg);
            return false;
        }
    } catch (error) {
        message.error('删除失败,请重试');
        message.error(intl.formatMessage({ id: 'page.delete.fail', defaultMessage: '删除失败,请重试!' }));
        return false;
    } finally {
        hide();
    }
};
const handleExport = async () => {
    const hide = message.loading('正在导出');
const handleExport = async (intl) => {
    const hide = message.loading(intl.formatMessage({ id: 'page.exporting', defaultMessage: '正在导出' }));
    try {
        const resp = await Http.doPostBlob('api/user/export');
        const blob = new Blob([resp], { type: 'application/vnd.ms-excel' });
        window.location.href = window.URL.createObjectURL(blob);
        message.success('导出成功');
        message.success(intl.formatMessage({ id: 'page.export.success', defaultMessage: '导出成功' }));
        return true;
    } catch (error) {
        message.error('导出失败,请重试');
        message.error(intl.formatMessage({ id: 'page.export.fail', defaultMessage: '导出失败,请重试' }));
        return false;
    } finally {
        hide();
    }
};
const handlePwd = async (val) => {
    const hide = message.loading('正在重置');
const handlePwd = async (val, intl) => {
    const hide = message.loading(intl.formatMessage({ id: 'page.resetting', defaultMessage: '正在重置' }));
    try {
        const resp = await Http.doPost('api/user/reset/pwd', val);
        if (resp.code === 200) {
            message.success('重置成功');
            message.success(intl.formatMessage({ id: 'page.reset.success', defaultMessage: '重置成功' }));
            return true;
        } else {
            message.error(resp.msg);
            return false;
        }
    } catch (error) {
        message.error('重置失败请重试!');
        message.error(intl.formatMessage({ id: 'page.reset.fail', defaultMessage: '重置失败请重试!' }));
        return false;
    } finally {
        hide();
@@ -110,6 +115,7 @@
const Main = () => {
    const intl = useIntl();
    const formTableRef = useRef();
    const actionRef = useRef();
    const [selectedRows, setSelectedRows] = useState([]);
@@ -124,6 +130,8 @@
    const [pwdModalVisible, setPwdModalVisible] = useState(false);
    const [assignModalVisible, setAssignModalVisible] = useState(false);
    const loadDeptTreeData = (param) => {
        setDeptTreeLoading(true);
        Http.doPostPromise('/api/dept/tree', param, (res) => {
@@ -136,12 +144,13 @@
            const treeAllKeys = getTreeAllKeys(treeData);
            setDeptExpandedKeys(treeAllKeys);
        }).catch((err) => {
            console.error(err);
            setDeptTreeLoading(false);
        })
    }
    useEffect(() => {
        const handleResize = () => setBoxHeight(window.innerHeight - 368);
        const handleResize = () => setBoxHeight(window.innerHeight - 320);
        window.addEventListener('resize', handleResize);
        handleResize();
@@ -152,43 +161,70 @@
    const columns = [
        {
            title: 'No',
            title: intl.formatMessage({
                id: 'page.table.no',
                defaultMessage: 'No'
            }),
            dataIndex: 'index',
            valueType: 'indexBorder',
            width: 30,
            width: 48,
        },
        {
            title: '名称',
            dataIndex: 'nickname',
            title: '部门',
            dataIndex: 'deptId$',
            valueType: 'text',
            hidden: false,
            width: 100,
            filterDropdown: (props) => <TextFilter
                name='nickname'
                {...props}
                actionRef={actionRef}
                setSearchParam={setSearchParam}
            />,
            render: (_, record) => {
                if (_ && _ !== '-') {
                    return <Tag color={""}>{_}</Tag>
                } else {
                    return '-'
                }
            },
            // filterDropdown: (props) => <LinkFilter
            //     name='deptId'
            //     major='dept'
            //     {...props}
            //     actionRef={actionRef}
            //     setSearchParam={setSearchParam}
            // />,
        },
        {
            title: '登录账号',
            dataIndex: 'username',
            valueType: 'text',
            hidden: false,
            width: 100,
            copyable: true,
            filterDropdown: (props) => <TextFilter
                name='username'
                {...props}
                actionRef={actionRef}
                setSearchParam={setSearchParam}
            />,
            render: (_, record) => {
                return <span style={{ fontWeight: "bold" }}>{_}</span>
            }
        },
        {
            title: '名称',
            dataIndex: 'nickname',
            valueType: 'text',
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='nickname'
                {...props}
                actionRef={actionRef}
                setSearchParam={setSearchParam}
            />,
            render: (_, record) => {
                return <span style={{ fontWeight: "bold" }}>{_}</span>
            }
        },
        {
            title: '密码',
            dataIndex: 'password',
            valueType: 'password',
            hidden: true,
            width: 100,
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='password'
                {...props}
@@ -200,8 +236,7 @@
            title: '头像',
            dataIndex: 'avatar',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='avatar'
                {...props}
@@ -213,8 +248,7 @@
            title: '工号',
            dataIndex: 'code',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='code'
                {...props}
@@ -227,7 +261,6 @@
            dataIndex: 'sex$',
            valueType: 'text',
            hidden: false,
            width: 80,
            filterDropdown: (props) => <SelectFilter
                name='sex'
                {...props}
@@ -245,7 +278,6 @@
            dataIndex: 'phone',
            valueType: 'text',
            hidden: false,
            width: 100,
            filterDropdown: (props) => <TextFilter
                name='phone'
                {...props}
@@ -257,8 +289,7 @@
            title: '邮箱',
            dataIndex: 'email',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='email'
                {...props}
@@ -270,8 +301,7 @@
            title: '邮箱验证',
            dataIndex: 'emailVerified$',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <SelectFilter
                name='emailVerified'
                {...props}
@@ -284,32 +314,10 @@
            />,
        },
        {
            title: '所属部门',
            dataIndex: 'deptId$',
            valueType: 'text',
            hidden: false,
            width: 100,
            render: (_, record) => {
                if (_ && _ !== '-') {
                    return <Tag color={""}>{_}</Tag>
                } else {
                    return '-'
                }
            },
            // filterDropdown: (props) => <LinkFilter
            //     name='deptId'
            //     major='dept'
            //     {...props}
            //     actionRef={actionRef}
            //     setSearchParam={setSearchParam}
            // />,
        },
        {
            title: '真实姓名',
            dataIndex: 'realName',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='realName'
                {...props}
@@ -321,8 +329,7 @@
            title: '身份证号',
            dataIndex: 'idCard',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='idCard'
                {...props}
@@ -334,8 +341,7 @@
            title: '出生日期',
            dataIndex: 'birthday',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='birthday'
                {...props}
@@ -347,8 +353,7 @@
            title: '个人简介',
            dataIndex: 'introduction',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='introduction'
                {...props}
@@ -361,7 +366,6 @@
            dataIndex: 'status$',
            valueType: 'text',
            hidden: false,
            width: 80,
            render: (_, record) => {
                const status = statusMap[record.status]
                return <Tag color={status.color}>{status.text}</Tag>
@@ -381,8 +385,7 @@
            title: '添加时间',
            dataIndex: 'createTime$',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <DatetimeRangeFilter
                name='createTime'
                {...props}
@@ -394,8 +397,7 @@
            title: '修改时间',
            dataIndex: 'updateTime$',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <DatetimeRangeFilter
                name='updateTime'
                {...props}
@@ -407,8 +409,7 @@
            title: '修改人员',
            dataIndex: 'updateBy$',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <LinkFilter
                name='updateBy'
                major='user'
@@ -421,8 +422,7 @@
            title: '备注',
            dataIndex: 'memo',
            valueType: 'text',
            hidden: true,
            width: 140,
            hidden: false,
            filterDropdown: (props) => <TextFilter
                name='memo'
                {...props}
@@ -434,39 +434,42 @@
        {
            title: '操作',
            dataIndex: 'option',
            width: 150,
            valueType: 'option',
            width: 180,
            render: (_, record) => [
                <Button
                    type="link"
                    key="edit"
                    style={{ padding: '2px 3px' }}
                    onClick={() => {
                        setModalVisible(true);
                        setCurrentRow(record);
                    }}
                >
                    编辑
                    <FormattedMessage id='page.edit' defaultMessage='编辑' />
                </Button>,
                <Button
                    type="link"
                    key="pwd"
                    style={{ padding: '2px 2px' }}
                    onClick={() => {
                        setPwdModalVisible(true);
                        setCurrentRow(record);
                    }}
                >
                    重置密码
                    <FormattedMessage id='page.reset.pwd' defaultMessage='重置密码' />
                </Button>,
                <Button
                    type="link"
                    danger
                    style={{ padding: '4px' }}
                    key="remove"
                    onClick={async () => {
                        Modal.confirm({
                            title: '删除',
                            content: '确定删除该项吗?',
                            title: intl.formatMessage({ id: 'page.delete', defaultMessage: '删除' }),
                            content: intl.formatMessage({ id: 'page.delete.confirm', defaultMessage: '确定删除该项吗?' }),
                            onOk: async () => {
                                const success = await handleRemove([record]);
                                const success = await handleRemove([record], intl);
                                if (success) {
                                    if (actionRef.current) {
                                        actionRef.current.reload();
@@ -476,8 +479,21 @@
                        });
                    }}
                >
                    删除
                    <FormattedMessage id='page.delete' defaultMessage='删除' />
                </Button>,
                <TableDropdown
                    key="actionGroup"
                    name="dropdown"
                    onSelect={(key) => {
                        if (key === 'assign') {
                            setAssignModalVisible(true);
                            setCurrentRow(record);
                        }
                    }}
                    menus={[
                        { key: 'assign', name: intl.formatMessage({ id: 'page.assign.role', defaultMessage: '分配角色' }) },
                    ]}
                />,
            ],
        },
    ];
@@ -490,10 +506,13 @@
        >
            <Row gutter={[16, 24]}>
                <Col lg={6} md={24}>
                    <Card title="部门" style={{ width: '100%', height: 'calc(100vh - 200px)' }}>
                    <Card
                        title={intl.formatMessage({ id: 'system.dept', defaultMessage: '部门' })}
                        style={{ width: '100%', height: 'calc(100vh - 160px)' }}
                    >
                        <Input
                            style={{ marginBottom: 10 }}
                            placeholder="Search"
                            placeholder={intl.formatMessage({ id: 'commont.enter', defaultMessage: '请输入' })}
                            onChange={(e) => {
                                const { value } = e.target;
                                loadDeptTreeData({
@@ -501,11 +520,11 @@
                                })
                            }}
                        />
                        <div style={{ height: 'calc(100vh - 350px)', overflowY: 'auto' }}>
                        <div style={{ height: 'calc(100vh - 325px)', overflowY: 'auto' }}>
                            {deptTreeLoading ? (
                                <Skeleton active />
                            ) : (
                                < Tree
                                <Tree
                                    showLine
                                    blockNode
                                    defaultExpandAll    // 异步加载失效
@@ -534,9 +553,10 @@
                        formRef={formTableRef}
                        columns={columns}
                        cardBordered
                        scroll={{ y: boxHeight }}
                        size='small'
                        // scroll={{ y: boxHeight }}
                        dateFormatter="string"
                        pagination={{ pageSize: 20 }}
                        pagination={{ pageSize: 12 }}
                        search={false}
                        toolbar={{
                            search: {
@@ -563,11 +583,11 @@
                                    hidden={selectedRows?.length === 0}
                                    onClick={async () => {
                                        Modal.confirm({
                                            title: '是否确认删除所选数据项?',
                                            title: intl.formatMessage({ id: 'page.delete', defaultMessage: '删除' }),
                                            content: intl.formatMessage({ id: 'page.delete.confirm', defaultMessage: '确定删除该项吗?' }),
                                            icon: <ExclamationCircleOutlined />,
                                            content: '请谨慎操作',
                                            async onOk() {
                                                const success = await handleRemove(selectedRows);
                                                const success = await handleRemove(selectedRows, intl);
                                                if (success) {
                                                    setSelectedRows([]);
                                                    actionRef.current.reload();
@@ -578,7 +598,7 @@
                                    }}
                                >
                                    <DeleteOutlined />
                                    删除
                                    <FormattedMessage id='page.delete' defaultMessage='删除' />
                                </Button>,
                                <Button
                                    type="primary"
@@ -588,16 +608,16 @@
                                    }}
                                >
                                    <PlusOutlined />
                                    添加
                                    <FormattedMessage id='page.add' defaultMessage='添加' />
                                </Button>,
                                <Button
                                    key="export"
                                    onClick={async () => {
                                        handleExport();
                                        handleExport(intl);
                                    }}
                                >
                                    <ExportOutlined />
                                    导出
                                    <FormattedMessage id='page.export' defaultMessage='导出' />
                                </Button>,
                            ],
                        }}
@@ -617,9 +637,23 @@
                            }
                        }}
                        columnsState={{
                            persistenceKey: 'pro-table-user',
                            persistenceKey: TABLE_KEY,
                            persistenceType: 'localStorage',
                            defaultValue: {
                                password: { show: repairBug(TABLE_KEY, 'password', false) },
                                avatar: { show: repairBug(TABLE_KEY, 'avatar', false) },
                                code: { show: repairBug(TABLE_KEY, 'code', false) },
                                phone: { show: repairBug(TABLE_KEY, 'phone', false) },
                                email: { show: repairBug(TABLE_KEY, 'email', false) },
                                emailVerified$: { show: repairBug(TABLE_KEY, 'emailVerified$', false) },
                                realName: { show: repairBug(TABLE_KEY, 'realName', false) },
                                idCard: { show: repairBug(TABLE_KEY, 'idCard', false) },
                                birthday: { show: repairBug(TABLE_KEY, 'birthday', false) },
                                introduction: { show: repairBug(TABLE_KEY, 'introduction', false) },
                                createTime$: { show: repairBug(TABLE_KEY, 'createTime$', false) },
                                updateTime$: { show: repairBug(TABLE_KEY, 'updateTime$', false) },
                                updateBy$: { show: repairBug(TABLE_KEY, 'updateBy$', false) },
                                memo: { show: repairBug(TABLE_KEY, 'memo', false) },
                                option: { fixed: 'right', disable: true },
                            },
                            onChange(value) {
@@ -627,60 +661,85 @@
                        }}
                    />
                </Col>
                <Edit
                    open={modalVisible}
                    values={currentRow || {}}
                    treeData={deptTreeData}
                    onCancel={
                        () => {
                            setModalVisible(false);
                            setCurrentRow(undefined);
                        }
                    }
                    onSubmit={async (values) => {
                        let ok = false;
                        if (values.id) {
                            ok = await handleUpdate({ ...values })
                        } else {
                            ok = await handleSave({ ...values })
                        }
                        if (ok) {
                            setModalVisible(false);
                            setCurrentRow(undefined);
                            if (actionRef.current) {
                                actionRef.current.reload();
                            }
                        }
                    }
                    }
                />
                <Pwd
                    open={pwdModalVisible}
                    values={currentRow || {}}
                    onCancel={
                        () => {
                            setPwdModalVisible(false);
                            setCurrentRow(undefined);
                        }
                    }
                    onSubmit={async (values) => {
                        let ok = false;
                        if (values.id) {
                            ok = await handlePwd({ ...values })
                        }
                        if (ok) {
                            setPwdModalVisible(false);
                            setCurrentRow(undefined);
                            if (actionRef.current) {
                                actionRef.current.reload();
                            }
                        }
                    }
                    }
                />
            </Row>
            <Edit
                open={modalVisible}
                values={currentRow || {}}
                treeData={deptTreeData}
                onCancel={
                    () => {
                        setModalVisible(false);
                        setCurrentRow(undefined);
                    }
                }
                onSubmit={async (values) => {
                    let ok = false;
                    if (values.id) {
                        ok = await handleUpdate({ ...values }, intl)
                    } else {
                        ok = await handleSave({ ...values }, intl)
                    }
                    if (ok) {
                        setModalVisible(false);
                        setCurrentRow(undefined);
                        if (actionRef.current) {
                            actionRef.current.reload();
                        }
                    }
                }
                }
            />
            <Pwd
                open={pwdModalVisible}
                values={currentRow || {}}
                onCancel={
                    () => {
                        setPwdModalVisible(false);
                        setCurrentRow(undefined);
                    }
                }
                onSubmit={async (values) => {
                    let ok = false;
                    if (values.id) {
                        ok = await handlePwd({ ...values }, intl)
                    }
                    if (ok) {
                        setPwdModalVisible(false);
                        setCurrentRow(undefined);
                        if (actionRef.current) {
                            actionRef.current.reload();
                        }
                    }
                }
                }
            />
            <AssignRole
                open={assignModalVisible}
                values={currentRow || {}}
                onCancel={
                    () => {
                        setAssignModalVisible(false);
                        setCurrentRow(undefined);
                    }
                }
                onSubmit={async (values) => {
                    let ok = false;
                    if (values.id) {
                        ok = await handleUpdate({ ...values }, intl)
                    }
                    if (ok) {
                        setAssignModalVisible(false);
                        setCurrentRow(undefined);
                        if (actionRef.current) {
                            actionRef.current.reload();
                        }
                    }
                }}
            />
        </PageContainer>
    );
};