去年进入新部门之前,自己负责的工作是做客服系统的工单工作,工单建立数据稍微复杂一点,还要一步步的节点审核,走一个完整的流程就要耗费很长时间。本来以为这是比较正常的,进入新部门后,发现这个系统有个非常好用的功能,就是连续点击右上角的头像超过10下,就会出现一个切换账号的对话框,在这里就可以快捷实现用户账号的切换。用了一段时间,发现是真的香。下面就直入正题,开始介绍。
这个账号切换功能会比较适用于一下场景:
适用场景举例
场景1:查看项目数据
- 问题:自身账号没有项目数据,项目数据构建复杂。
- 解决方案:通过切换到拥有项目数据的账号,可以直接查看和分析数据,无需重新构建数据集,节省时间和资源。
场景2:项目流转和审批
- 问题:项目需要不同审批人处理,频繁沟通影响效率。
- 解决方案:直接切换到审批人的账号进行审批处理,减少沟通成本,提高工作效率。如果系统支持数据转交功能,也可以将任务转交给自己处理,进一步简化流程。
场景3:多角色系统开发
- 问题:开发过程中需要频繁切换角色权限,配置繁琐。
- 解决方案:使用账号切换功能,快速在超级管理员和普通角色间切换,便于开发和测试,同时避免频繁修改角色配置。
场景4:问题定位和数据检查
- 问题:业务、测试或UI报告问题,但数据繁杂,难以快速定位。
- 解决方案:切换到报告问题的业务或测试账号,直接查看相关数据,快速定位问题。对于UI设计,可以切换到数据丰富的账号进行界面走查,确保设计符合实际数据展示需求。
当然适用的场景可能不仅仅是这些,适用范围会更加广泛,我只是简单介绍一些比较常用的场景。
注意
- 账号切换并不是所有人都可以切换,只有开发人员、测试和UI人员才可以切换,生产环境更快限制会更加严格,只有个别负责人才能够账号切换,毕竟信息安全更加重要。
优劣势
这里我们介绍账号切换功能的优劣势,这里仅仅是简单介绍:
优势:
- 提高效率:
- 用户无需反复登录退出,即可快速切换至所需账号,节省时间成本。
- 提升安全性:
- 避免因频繁登录导致密码泄露的风险。
劣势:
- 安全性风险:
- 频繁切换账号可能会触及系统的使用限制,建议合理安排切换频率,以免造成不必要的麻烦。
具体实现
业务介绍
系统的右上角有用户的头像,每个页面都有,可以全局配置,可以通过用户连续点击10次,页面出现一个对话框,对话框里包含两个输入框,第一个是用户姓名输入框,并且右侧有个查询按钮,第二个输入框是工号,用户可以通过姓名来查询对应的工号,如果姓名对应工号为空,则提示查不到该用户,如果只有一个结果,则直接回填到工号输入框,如果有多个结果,则第二个工号输入框变为单选选择框,选择数据为姓名-工号,对应值为工号数据,姓名输入框为选填,提交时不进行参数传递,第二个工号必填,如果知道工号,也可以输入工号,直接进行账号切换。工号作为唯一参数传递给后端,请求成功后,替换本地的用户信息,重新加载页面,实现账号切换登录。
实现账号切换的具体步骤可以分为前端和后端两部分。这里我们只介绍前端部分,后端只需要提供两个接口:
- 根据姓名查询工号的接口,毕竟工号不是很好记,可以通过姓名来查找对应的工号,如果公司大的话,同名同姓的人会比较多。
- 第二个结果是根据工号,返回工号对应的token信息,直接替换本地的用户信息。
这里后端是实现这个功能的关键,可能有些系统的后端并没有这样的权限。
组件封装
以下是封装后的React组件代码:
import React, { useState, useEffect } from 'react';
import { Modal, Input, Select, Button, Form, Row, Col } from 'antd';
import axios from 'axios';
const SwitchAccountDialog = ({ setIsVisible, setNameCount }) => {
const [form] = Form.useForm();
const [searchResults, setSearchResults] = useState([]);
const [workNumber, setWorkNumber] = useState('');
// 重置工号信息
const nameOnChange = (e) => {
const value = e.target.value;
if (value) {
// 当姓名输入框的值变化时,重置工号信息
setWorkNumber('');
setSearchResults([]);
}
};
const handleOk = async () => {
try {
const values = await form.validateFields();
const workNo = values.workNumber.split('-')[1]; // 从"姓名-工号"中提取工号
const response = await axios.get('/api/switch_account', { params: { workNumber: workNo } });
if (response.data.success) {
localStorage.setItem('token', JSON.stringify(response.data.userInfo)); // 存储用户信息到localStorage
setNameCount(0); // 重置点击次数
setIsVisible(false); // 关闭对话框
window.location.reload(); // 重新加载页面
}
} catch (error) {
console.error('Error:', error);
Modal.warning({ title: '提示', content: '账号切换出错' });
}
};
const handleCancel = () => {
setNameCount(0); // 重置点击次数
setIsVisible(false); // 关闭对话框
};
const handleSearch = async () => {
const { name } = form.getFieldsValue();
if (!name) {
Modal.warning({ title: '提示', content: '请输入姓名' });
return;
}
try {
const response = await axios.get('/api/search', { params: { name } });
const results = response.data;
if (results.length === 0) {
Modal.warning({ title: '提示', content: '查不到该用户' });
} else {
setSearchResults(results.map(item => ({
key: `${item.name}-${item.work_no}`,
value: `${item.name}-${item.work_no}`,
label: `${item.name}-${item.work_no}`
})));
}
} catch (error) {
console.error('Error:', error);
Modal.warning({ title: '提示', content: '查询出错' });
}
};
return (
<Modal
title="输入信息"
centered
onOk={handleOk}
onCancel={handleCancel}
maskClosable={false}
>
<Form form={form} layout="vertical">
<Row gutter={16}>
<Col span={18}>
<Form.Item
name="name"
label="姓名"
>
<Input onChange={nameOnChange} />
</Form.Item>
</Col>
<Col span={6}>
<Button type="primary" onClick={handleSearch} block>
查询工号
</Button>
</Col>
</Row>
<Row gutter={16}>
<Col span={24}>
<Form.Item
name="workNumber"
label="工号"
>
{searchResults.length > 0 ? (
<Select
value={workNumber}
onChange={setWorkNumber}
placeholder="选择工号"
>
{searchResults.map(item => (
<Select.Option key={item.key} value={item.value}>
{item.label}
</Select.Option>
))}
</Select>
) : (
<Input placeholder="请输入工号" />
)}
</Form.Item>
</Col>
</Row>
</Form>
</Modal>
);
};
export default SwitchAccountDialog;
组件使用示例
账号切换我们一般会封装在公共页面区域,以便实现全局的功能配置。
同时增加工号的权限控制和生产和其他环境的权限控制:
import React, { useState, useEffect } from 'react';
import SwitchAccountDialog from './SwitchAccountDialog';
import { Avatar } from 'antd';
import { useUser } from './UserContext'; // 引入UserContext的钩子
const isProductionEnvironment = process.env.REACT_APP_ENVIRONMENT === 'production';
const allowedWorkNumbers = isProductionEnvironment
? ['123', '456', '789'] // 生产环境的权限工号
: ['001', '002', '003']; // 其他环境的权限工号
const App = () => {
const [isDialogVisible, setIsDialogVisible] = useState(false);
const [clickCount, setClickCount] = useState(0);
const { user } = useUser(); // 从UserContext获取用户信息
const clickThreshold = 10; // 用户需要连续点击头像的次数
useEffect(() => {
// 当用户工号在权限数组内且点击次数达到阈值时显示对话框
if (allowedWorkNumbers.includes(user.workNumber) && clickCount >= clickThreshold) {
setIsDialogVisible(true);
} else {
setIsDialogVisible(false);
}
}, [user.workNumber, clickCount, allowedWorkNumbers]);
const handleDialogCancel = () => {
setIsDialogVisible(false);
setClickCount(0); // 对话框关闭时重置点击次数
};
return (
<div style={{ textAlign: 'center', padding: '100px' }}>
<Avatar
icon="user"
style={{ cursor: 'pointer' }}
onClick={() => setClickCount(prevCount => prevCount + 1)}
/>
{isDialogVisible && (
<SwitchAccountDialog
setIsVisible={setIsDialogVisible}
setNameCount={setClickCount}
userWorkNumber={user.workNumber} // 传递用户工号给对话框组件
/>
)}
</div>
);
};
export default App;
这样,就实现了一个完整的功能,允许用户通过连续点击头像来触发账号切换对话框,并且在对话框关闭时重置点击次数。同时,用户工号从store
中获取,而不是从localStorage
,提高了安全性和数据一致性。
- 从安全性和数据一致性的角度考虑,最好从后端服务或前端的
store
(如Redux、Context API等)中获取用户工号,而不是从localStorage
。