Commit c8affe6c authored by 文旺-丰林-DEV's avatar 文旺-丰林-DEV

Merge branch 'uploadFile' into 'master'

Upload file

See merge request !4
parents 6284bf37 13f7e567
...@@ -328,3 +328,4 @@ build ...@@ -328,3 +328,4 @@ build
node_modules node_modules
*.txt
This source diff could not be displayed because it is too large. You can view the blob instead.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BBM.ChatGLM.Dtos
{
public class ImportUsersResult
{
public List<Dtos.Results> Results { get; set; }
public int FailNum { get; set; }
public int SuccessNum { get; set; }
}
}
...@@ -8,7 +8,13 @@ namespace BBM.ChatGLM.Dtos ...@@ -8,7 +8,13 @@ namespace BBM.ChatGLM.Dtos
{ {
public class Results public class Results
{ {
public int? SuccessNum { get; set; } = 0; public string userName { get; set; }
public string phone { get; set; }
public string role { get; set; }
public string state { get; set; }
public string failureReason { get; set; }
public int? SuccessNum { get; set; } = 0;
public int? FailNum { get; set; } = 0; public int? FailNum { get; set; } = 0;
} }
} }
using BBM.ChatGLM.Dtos; using BBM.ChatGLM.Dtos;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
...@@ -17,5 +19,7 @@ namespace BBM.ChatGLM.User ...@@ -17,5 +19,7 @@ namespace BBM.ChatGLM.User
Task<PagedResultDto<UserDto>> PageAsync(UserPageInput input); Task<PagedResultDto<UserDto>> PageAsync(UserPageInput input);
Task<List<string>> GetRolesAsync(); Task<List<string>> GetRolesAsync();
Task<string> GetTokenAsync(); Task<string> GetTokenAsync();
Task<ImportUsersResult> CreateUsersByExcel([FromForm] IFormFile file);
} }
} }
using BBM.ChatGLM.Dtos; using BBM.ChatGLM.Dtos;
using Lion.AbpPro.BasicManagement.Roles; using Lion.AbpPro.BasicManagement.Roles;
using Lion.AbpPro.BasicManagement.Users; using Lion.AbpPro.BasicManagement.Users;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using NetTopologySuite.Mathematics; using NetTopologySuite.Mathematics;
using OfficeOpenXml;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical;
using System.ComponentModel.DataAnnotations;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Identity; using Volo.Abp.Identity;
...@@ -120,5 +125,161 @@ namespace BBM.ChatGLM.User ...@@ -120,5 +125,161 @@ namespace BBM.ChatGLM.User
throw new BusinessException("BBM.ChatGLM:100012"); throw new BusinessException("BBM.ChatGLM:100012");
} }
} }
[Authorize(ChatGLMPermissions.UsersManagement.Create)]
public async Task<ImportUsersResult> CreateUsersByExcel([FromForm] IFormFile file)
{
/**
* [FromForm]
* 操作步骤
* 1、React中,使用antd的Upload组件来上传Excel表格数据,并且在上传之前需要将表格数据转换成FormData。
* 2、在ASP.NET中,建立一个Controller用于接收上传的文件,并且使用[HttpPost]属性来指定使用POST方法接收请求。
* 3、在Controller中,使用IFormFile接口来接收上传的Excel文件,然后使用EPPlus等工具来读取Excel文件数据
* 4、将读取到的Excel数据进行处理,例如存储到数据库中或者进行数据分析等操作
*/
List<Dtos.Results> results = new List<Dtos.Results>();
Dtos.Results xiangying = new Dtos.Results();
var failNum = 0;
if (file == null)
{
xiangying = FailReason("", "", "", "", "Excel文件为空");
results.Add(xiangying);
return new ImportUsersResult
{
Results = new List<Dtos.Results> { },
FailNum = 0,
SuccessNum = 0
};
}
string directoryPath = Environment.CurrentDirectory + @"\Excels";
if (!Directory.Exists(directoryPath))
Directory.CreateDirectory(directoryPath);
var filePath = Path.Combine(directoryPath, file.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
using (var package = new ExcelPackage(new FileInfo(filePath)))
{
var worksheet = package.Workbook.Worksheets["sheet1"];
var totalRows = worksheet.Dimension.Rows;
var row = 2;
var userName = "";
var phoneNumber = "";
var userRole = "";
while (row <= totalRows)
{
try
{
userName = worksheet.Cells[row, 1].Value?.ToString().Replace(" ", "") ?? "";
phoneNumber = worksheet.Cells[row, 2].Value?.ToString().Replace(" ", "") ?? "";
userRole = worksheet.Cells[row, 3].Value?.ToString().Replace(" ", "") ?? "";
userName = Regex.Replace(userName, @"\s", "");
phoneNumber = Regex.Replace(phoneNumber, @"\s", "");
userRole = Regex.Replace(userRole, @"\s", "");
if (string.IsNullOrEmpty(userName))
{
failNum++;
xiangying = FailReason(userName, phoneNumber, userRole, "失败", "用户名不正确");
}
else if (userName.Length > 20)
{
failNum++;
xiangying = FailReason(userName, phoneNumber, userRole, "失败", "用户名超出长度限制");
}
else if (!Regex.IsMatch(userName, @"^[\u4e00-\u9fa5a-zA-Z]+$"))
{
failNum++;
xiangying = FailReason(userName, phoneNumber, userRole, "失败", "用户名包含特殊符号");
}
else if (string.IsNullOrEmpty(phoneNumber))
{
failNum++;
xiangying = FailReason(userName, phoneNumber, userRole, "失败", "手机号码不正确");
}
else if (!Regex.IsMatch(phoneNumber, @"^(133|153|189|180|181|177|173|199|174|141
|139|138|137|136|135|134|159|158|157|150|151|152|147|188|187|182|183|184|178|198
|130|131|132|146|156|155|166|186|185|145|175|176
|170|171
|123)\d{8}$"))
{
failNum++;
xiangying = FailReason(userName, phoneNumber, userRole, "失败", "手机号码格式不正确");
}
else if (phoneNumber.Length > 11)
{
failNum++;
xiangying = FailReason(userName, phoneNumber, userRole, "失败", "手机号码超出长度限制");
}
else if (userRole != "普通用户" && userRole != "管理员")
{
failNum++;
xiangying = FailReason(userName, phoneNumber, userRole, "失败", "角色输入不正确");
}
else
{
var password = "Zs." + phoneNumber.Substring(phoneNumber.Length - 6);
if (userRole == "管理员")
{
userRole = "admin";
}
UserCreateInput userInput = new UserCreateInput();
userInput.Name = userName;
userInput.UserName = userName;
userInput.Password = password;
userInput.Role = userRole;
userInput.IsActive = true;
var res = await CreateAsync(userInput);
xiangying = FailReason(userName, phoneNumber, userRole, "成功", "");
}
}
catch (Exception ex)
{
failNum++;
xiangying = FailReason(userName, phoneNumber, userRole, "失败", "已存在当前用户,重复添加失败");
}
finally
{
row++;
results.Add(xiangying);
}
}
return new ImportUsersResult
{
Results = results,
FailNum = failNum,
SuccessNum = (totalRows - 1) - failNum
};
}
}
public Dtos.Results FailReason(string userName, string phone, string role, string state, string reason)
{
Dtos.Results results = new Dtos.Results();
results.userName = userName;
results.phone = phone;
results.role = role;
results.state = state;
results.failureReason = reason;
return results;
}
} }
} }
...@@ -66,105 +66,9 @@ namespace BBM.ChatGLM.Controllers ...@@ -66,105 +66,9 @@ namespace BBM.ChatGLM.Controllers
} }
[HttpPost("excelFile")] [HttpPost("excelFile")]
public async Task<IActionResult> UploadExcel([FromForm] IFormFile file) public async Task<ImportUsersResult> CreateUsersByExcel([FromForm] IFormFile file)
{ {
/** return await _usersAppService.CreateUsersByExcel(file);
* [FromForm]
* 操作步骤
* 1、React中,使用antd的Upload组件来上传Excel表格数据,并且在上传之前需要将表格数据转换成FormData。
* 2、在ASP.NET中,建立一个Controller用于接收上传的文件,并且使用[HttpPost]属性来指定使用POST方法接收请求。
* 3、在Controller中,使用IFormFile接口来接收上传的Excel文件,然后使用EPPlus等工具来读取Excel文件数据
* 4、将读取到的Excel数据进行处理,例如存储到数据库中或者进行数据分析等操作
*/
Dtos.Results xiangying = new Dtos.Results();
var failNum = 0;
if (file == null)
return BadRequest("Excel文件为空");
string directoryPath = Environment.CurrentDirectory + @"\Excels";
if (!Directory.Exists(directoryPath))
Directory.CreateDirectory(directoryPath);
var filePath = Path.Combine(directoryPath, file.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
using (var package = new ExcelPackage(new FileInfo(filePath)))
{
var worksheet = package.Workbook.Worksheets["sheet1"];
var totalRows = worksheet.Dimension.Rows;
var row = 2;
var userName = "";
var phoneNumber = "";
var userRole = "";
while (row <= totalRows)
{
try
{
userName = worksheet.Cells[row, 1].Value?.ToString() ?? "";
phoneNumber = worksheet.Cells[row, 2].Value?.ToString() ?? "";
userRole = worksheet.Cells[row, 3].Value?.ToString() ?? "";
if (userName == "" || phoneNumber == "" || userName.Length>50 || userRole == "" || (userRole != "普通用户" && userRole != "管理员"))
{
failNum++;
}
else
{
if (!Regex.IsMatch(phoneNumber, @"^(133|153|189|180|181|177|173|199|174|141
|139|138|137|136|135|134|159|158|157|150|151|152|147|188|187|182|183|184|178|198
|130|131|132|146|156|155|166|186|185|145|175|176
|170|171
|123)\d{8}$"))
{
failNum++;
}
else
{
var password = "Zs" + phoneNumber.Substring(phoneNumber.Length - 6);
if (userRole == "管理员")
{
userRole = "admin";
}
UserCreateInput userInput = new UserCreateInput();
userInput.Name = userName;
userInput.UserName = userName;
userInput.Password = password;
userInput.Role = userRole;
userInput.IsActive = true;
await _usersAppService.CreateAsync(userInput);
continue;
}
}
}
catch (Exception ex)
{
failNum++;
}
finally
{
row++;
}
}
xiangying.SuccessNum = (totalRows-1) - failNum;
xiangying.FailNum = failNum;
package.Dispose();
}
return Ok(xiangying);
} }
} }
......
...@@ -34,6 +34,20 @@ import styles from './index.less'; ...@@ -34,6 +34,20 @@ import styles from './index.less';
import { Exclamtion } from '@/components/Icons/Icons'; import { Exclamtion } from '@/components/Icons/Icons';
import { LoadingOutlined } from '@ant-design/icons'; import { LoadingOutlined } from '@ant-design/icons';
import UserModal from './userModal';
interface IUserResult {
userName: string;
role: string;
state: string;
phone: string;
failureReason: string
}
export interface ICreateUserResult {
results: IUserResult[],
failNum: number,
successNum: number,
}
const { Item } = Form; const { Item } = Form;
const { Password } = Input; const { Password } = Input;
...@@ -71,6 +85,8 @@ const UserManage = () => { ...@@ -71,6 +85,8 @@ const UserManage = () => {
const [uploadButtonDisable, setUploadButtonDisable] = useState(false); const [uploadButtonDisable, setUploadButtonDisable] = useState(false);
const [accountForm] = Form.useForm(); const [accountForm] = Form.useForm();
const [resetPasswordForm] = Form.useForm(); const [resetPasswordForm] = Form.useForm();
const [isModalOpen, setIsModalOpen] = useState(false);
const [users, setUsers] = useState<ICreateUserResult>();
useEffect(() => { useEffect(() => {
getRole().then((res) => { getRole().then((res) => {
...@@ -295,16 +311,18 @@ const UserManage = () => { ...@@ -295,16 +311,18 @@ const UserManage = () => {
const handleUpload = (file) => { const handleUpload = (file) => {
setUploadButtonDisable(true); setUploadButtonDisable(true);
postExcelFile(file).then((res) => { postExcelFile(file).then(data => {
setUploadButtonDisable(false); setUploadButtonDisable(false);
actionRef.current?.reload(); setUsers(data);
message.success(`批量添加用户: 成功人数:${res.successNum} 失败人数:${res.failNum}人`); setTimeout(() => {
setIsModalOpen(true);
}, 100)
}); });
}; };
const downloadExcelTemplate = () => { const downloadExcelTemplate = () => {
postDowload(); postDowload();
}; }
return ( return (
<PageContainer> <PageContainer>
...@@ -478,18 +496,13 @@ const UserManage = () => { ...@@ -478,18 +496,13 @@ const UserManage = () => {
headerTitle="账号列表" headerTitle="账号列表"
toolBarRender={() => [ toolBarRender={() => [
<Button> <Button>
<a href="https://baibaomen.oss-cn-hangzhou.aliyuncs.com/zhensheng/excelTemplate.xlsx"> <a href='https://baibaomen.oss-cn-hangzhou.aliyuncs.com/zhensheng/excelTemplate.xlsx'>批量添加用户Excel模板</a></Button>,
批量添加用户模板下载 <Upload
</a> beforeUpload={handleUpload}
</Button>, disabled={uploadButtonDisable}
<Upload beforeUpload={handleUpload} disabled={uploadButtonDisable} showUploadList={false}> showUploadList={false}
{uploadButtonDisable ? ( >
<Button> {uploadButtonDisable ? <Button color='white'><Loading /></Button> : <Button icon={<UploadOutlined /> }>Excel表格添加账号</Button>}
<Loading />
</Button>
) : (
<Button icon={<UploadOutlined />}>批量添加账号</Button>
)}
</Upload>, </Upload>,
<Button <Button
key="button" key="button"
...@@ -504,6 +517,7 @@ const UserManage = () => { ...@@ -504,6 +517,7 @@ const UserManage = () => {
</Button>, </Button>,
]} ]}
/> />
<UserModal isModalOpen={isModalOpen} data={users} onClose={()=>{ setIsModalOpen(false) }}></UserModal>
</PageContainer> </PageContainer>
); );
}; };
......
import { Modal, Row, Table, Tag, Col } from 'antd';
import { ICreateUserResult } from '.';
const UserModal = (props: {data?: ICreateUserResult, isModalOpen:boolean, onClose: () => void }) => {
const { data, isModalOpen, onClose } = props;
const columns = [
{
title: '用户名',
dataIndex: 'userName',
key: 'userName',
},
{
title: '手机号码',
dataIndex: 'phone',
key: 'phone',
},
{
title: '用户角色',
dataIndex: 'role',
key: 'role',
},
{
title: '状态',
dataIndex: 'state',
key: 'state',
render: (_, record: any) => {
let color = record?.state === '失败' ? 'volcano' : 'green';
return <Tag color={color}> {record?.state} </Tag>;
}
},
{
title: '原因',
dataIndex: 'failureReason',
key: 'failureReason',
},
];
return (
<>
{
data && <Row>
<Modal title="导入结果" width={'50vw'} open={isModalOpen} onCancel={onClose} onOk={onClose}>
<Row>
<Col span={24}>
共导入用户&nbsp;<Tag style={{ margin:'0 4px' }}>{data.successNum + data.failNum}</Tag>个。 成功<Tag style={{ margin:'0 4px' }} color='green'>{data.successNum}</Tag>&nbsp;,失败<Tag style={{ margin:'0 4px' }} color='volcano'>{data.failNum}</Tag>个。
</Col>
</Row>
<br></br>
<Table columns={columns} dataSource={data.results} pagination={false} />
</Modal>
</Row>
}
</>
);
};
export default UserModal;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment