Commit 0f7bb16e authored by “wuhongyu”'s avatar “wuhongyu”

wuhongyu_增加Excel批量添加用户

parent 2e2e9a60
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
2023-05-07 15:47:29.631 +08:00 [ERR] An error occurred using the connection to database 'BBMChatGLM' on server ''.
2023-05-07 15:47:33.046 +08:00 [ERR] An exception occurred while iterating over the results of a query for context type 'Volo.Abp.BackgroundJobs.EntityFrameworkCore.BackgroundJobsDbContext'.
System.InvalidOperationException: An exception has been raised that is likely due to a transient failure.
---> System.TimeoutException: The operation has timed out.
at Npgsql.Util.NpgsqlTimeout.CheckAndGetTimeLeft()
at Npgsql.Util.NpgsqlTimeout.CheckAndApply(NpgsqlConnector connector)
at Npgsql.Internal.NpgsqlConnector.RawOpen(SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
at Npgsql.Internal.NpgsqlConnector.<Open>g__OpenCore|204_1(NpgsqlConnector conn, SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
at Npgsql.Internal.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.PoolingDataSource.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.PoolingDataSource.<Get>g__RentAsync|28_0(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlConnection.<Open>g__OpenAsync|45_0(Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
System.InvalidOperationException: An exception has been raised that is likely due to a transient failure.
---> System.TimeoutException: The operation has timed out.
at Npgsql.Util.NpgsqlTimeout.CheckAndGetTimeLeft()
at Npgsql.Util.NpgsqlTimeout.CheckAndApply(NpgsqlConnector connector)
at Npgsql.Internal.NpgsqlConnector.RawOpen(SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
at Npgsql.Internal.NpgsqlConnector.<Open>g__OpenCore|204_1(NpgsqlConnector conn, SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
at Npgsql.Internal.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.PoolingDataSource.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.PoolingDataSource.<Get>g__RentAsync|28_0(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlConnection.<Open>g__OpenAsync|45_0(Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
2023-05-07 15:47:37.237 +08:00 [ERR] An exception has been raised that is likely due to a transient failure.
System.InvalidOperationException: An exception has been raised that is likely due to a transient failure.
---> System.TimeoutException: The operation has timed out.
at Npgsql.Util.NpgsqlTimeout.CheckAndGetTimeLeft()
at Npgsql.Util.NpgsqlTimeout.CheckAndApply(NpgsqlConnector connector)
at Npgsql.Internal.NpgsqlConnector.RawOpen(SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
at Npgsql.Internal.NpgsqlConnector.<Open>g__OpenCore|204_1(NpgsqlConnector conn, SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
at Npgsql.Internal.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.PoolingDataSource.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.PoolingDataSource.<Get>g__RentAsync|28_0(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlConnection.<Open>g__OpenAsync|45_0(Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
at Volo.Abp.BackgroundJobs.EntityFrameworkCore.EfCoreBackgroundJobRepository.GetWaitingListAsync(Int32 maxResultCount, CancellationToken cancellationToken)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Volo.Abp.BackgroundJobs.BackgroundJobStore.GetWaitingJobsAsync(Int32 maxResultCount)
at Volo.Abp.BackgroundJobs.BackgroundJobWorker.DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
at Volo.Abp.BackgroundJobs.BackgroundJobWorker.DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
at Volo.Abp.BackgroundWorkers.AsyncPeriodicBackgroundWorkerBase.DoWorkAsync(CancellationToken cancellationToken)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BBM.ChatGLM.Dtos
{
public class Results
{
public int? SuccessNum { get; set; } = 0;
public int? FailNum { get; set; } = 0;
}
}
......@@ -3,6 +3,7 @@ using BBM.ChatGLM.User;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using OfficeOpenXml;
using System.Text.RegularExpressions;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Identity;
......@@ -63,9 +64,10 @@ namespace BBM.ChatGLM.Controllers
}
[HttpPost("excelFile")]
public async Task<IActionResult> UploadExcel(IFormFile file)
public async Task<IActionResult> UploadExcel([FromForm] IFormFile file)
{
/**
* [FromForm]
* 操作步骤
* 1、React中,使用antd的Upload组件来上传Excel表格数据,并且在上传之前需要将表格数据转换成FormData。
* 2、在ASP.NET中,建立一个Controller用于接收上传的文件,并且使用[HttpPost]属性来指定使用POST方法接收请求。
......@@ -73,22 +75,16 @@ namespace BBM.ChatGLM.Controllers
* 4、将读取到的Excel数据进行处理,例如存储到数据库中或者进行数据分析等操作
*/
Console.WriteLine("\n\n\n\n");
Console.WriteLine("react + antd 访问 \t");
Console.WriteLine("comeback welcome UploadExcel(IFormFile excel)");
Dtos.Results xiangying = new Dtos.Results();
var failNum = 0;
var failNum = 0; //添加失败的人数变量
if (file == null || file.Length == 0)
if (file == null)
return BadRequest("Excel文件为空");
var fileName = file.FileName;
Console.WriteLine(fileName);
var fileName = file.FileName;
var filePath = Path.Combine("C:\\Excel", fileName);
Console.WriteLine(filePath);
using (var stream = new FileStream(filePath, FileMode.Create))
{
......@@ -97,46 +93,74 @@ namespace BBM.ChatGLM.Controllers
using (var package = new ExcelPackage(new FileInfo(filePath)))
{
var worksheet = package.Workbook.Worksheets["sheet1"]; //reader space Name 可能要更灵活-通过指定的空间名称
var worksheet = package.Workbook.Worksheets["sheet1"];
var totalRows = worksheet.Dimension.Rows;
var row = 2; //First line is UserName And Phone
var row = 2;
var userName = "";
var phoneNumber = "";
var userRole = "";
while (row <= totalRows)
{
// 处理Excel数据
var userName = worksheet.Cells[row, 1].Value?.ToString() ?? "";
var phoneNumber = worksheet.Cells[row, 2].Value?.ToString() ?? "";
var password = "Zs"+ phoneNumber.Substring(phoneNumber.Length - 6); //1、可能需要加密操作... 2、以及判断账户是否在数据库重复
UserCreateInput userInput = new UserCreateInput();
userInput.UserName = userName;
userInput.Password = password;
userInput.Name = "";
userInput.Role = "";
userInput.IsActive = false;
failNum++;
//try
//{
// //添加用户方法
//}
//catch (Exception ex)
//{
// failNum ++; //账户添加 失败一次 数量+1
// Console.WriteLine(ex.Message);
// continue;
//}
row++;
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;
var userDto = await _usersAppService.CreateAsync(userInput);
continue;
}
}
}
catch (Exception ex)
{
failNum++;
}
finally
{
row++;
}
}
xiangying.SuccessNum = (totalRows-1) - failNum;
xiangying.FailNum = failNum;
package.Dispose();
}
return Ok(failNum==0?"用户新增成功":failNum);
//return Ok("Excel文件上传成功");
return Ok(xiangying);
}
}
......
......@@ -6,24 +6,27 @@ import {
postResetUser,
postEditUser,
postExcelFile,
postDowload
} from '@/services/chatGLM/api';
import { PlusOutlined,UploadOutlined } from '@ant-design/icons';
import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
import type { ActionType } from '@ant-design/pro-components';
import { PageContainer } from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components';
import type { UploadProps } from 'antd';
import { Button, Col, Divider, Form, Input, Modal, Radio, Row, Switch, message ,Upload,Table } from 'antd';
import { Button, Col, Divider, Form, Input, Modal, Radio, Row, Switch, message, Upload, Spin } from 'antd';
import classNames from 'classnames';
import { isEmpty } from 'lodash';
import React,{ useRef, useState, useEffect } from 'react';
import React, { useRef, useState, useEffect } from 'react';
import styles from './index.less';
import { Exclamtion } from '@/components/Icons/Icons';
import { LoadingOutlined } from '@ant-design/icons';
const { Item } = Form;
const { Password } = Input;
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />;
const Loading: React.FC = () => <Spin indicator={antIcon} />;
const layout = {
labelCol: { span: 4 },
......@@ -53,6 +56,7 @@ const UserManage = () => {
const [loading, setLoading] = useState(false);
const [ressetLoading, setResetLoading] = useState(false);
const [accountModalName, setAccountModalName] = useState('新建账号');
const [uploadButtonDisable, setUploadButtonDisable] = useState(false);
const [accountForm] = Form.useForm();
const [resetPasswordForm] = Form.useForm();
......@@ -70,9 +74,6 @@ const UserManage = () => {
}
};
const columns: any[] = [
{
title: '用户姓名',
......@@ -178,25 +179,6 @@ const UserManage = () => {
},
];
const props = {
name: 'file',
action: 'https://glm-mangement-api.baibaomen.com/User/excelFile',
headers: {
'access-control-allow-origin': '*',
'content-type': 'application/json; charset=utf-8'
},
onChange(info) {
console.log(info);
// if (info.file.status !== 'uploading') {
// console.log(info.file, info.fileList);
// }
// if (info.file.status === 'done') {
// message.success(`${info.file.name} 上传成功!`);
// } else if (info.file.status === 'error') {
// message.error(`${info.file.name} 上传失败!`);
// }
},
};
const handleOk = () => {
setIsModalAccountOpen(false);
......@@ -242,13 +224,9 @@ const UserManage = () => {
const onHandleClickAccountFinish = (values: any) => {
if (accountModalName === '添加账号') {
alert("2");
createdAccount(values);
}
// if (accountModalName === 'Excel添加账号'){
// createAccountExcel(values);
// }
if (accountModalName === '编辑账号') {
editAccountFinish(values);
}
......@@ -271,17 +249,6 @@ const UserManage = () => {
return Promise.reject(new Error('密码必须包含数字、大小写字母且长度为8-16位'));
};
const validateUserName = (_, value: any) => {
// const phoneNumberRegex = /^1[3456789]\d{9}$/;
//|| phoneNumberRegex.test(value)
if (!value ) {
return Promise.resolve();
}
return Promise.reject(new Error('请输入有效的姓名'));
};
const handleClickOpenModalDelete = () => {
postDeleteUser({
id: deleteMessage.id,
......@@ -315,6 +282,19 @@ const UserManage = () => {
});
};
const handleUpload = (file) => {
setUploadButtonDisable(true);
postExcelFile(file).then(res => {
setUploadButtonDisable(false);
message.success(`批量添加用户: 成功人数:${res.successNum} 失败人数:${res.failNum}人`);
})
};
const downloadExcelTemplate = () => {
postDowload();
}
return (
<PageContainer>
<Modal
......@@ -422,45 +402,9 @@ const UserManage = () => {
>
<Password />
</Item>
<Item name={'Role'} label="角色" rules={[{ required: true, message: '请选择你的角色' }]}>
<Radio.Group>
{roles.map((item) => {
return (
<Radio key={item} value={`${item}`}>
{item}
</Radio>
);
})}
</Radio.Group>
</Item>
<Item name={'Name'} label="姓名" rules={[{ required: true, message: '请输入你的姓名' }]}>
<Input placeholder="" />
</Item>
<Item label="是否启用" name={'isActive'} valuePropName="checked">
<Switch checked />
</Item>
<Divider />
<Row justify={'end'} gutter={8}>
<Col>
<Item key={'cancelButton'} noStyle={true}>
<Button key="back" onClick={handleModalCancel}>
取消
</Button>
</Item>
</Col>
<Col>
<Item key={'saveButton'} noStyle={true}>
<Button key="submit" type="primary" loading={loading} htmlType="submit">
保存
</Button>
</Item>
</Col>
</Row>
</>
)}
{/* <Item name={'Role'} label="角色" rules={[{ required: true, message: '请选择你的角色' }]}>
<Item name={'Role'} label="角色" rules={[{ required: true, message: '请选择你的角色' }]}>
<Radio.Group>
{roles.map((item) => {
return (
......@@ -493,11 +437,9 @@ const UserManage = () => {
</Button>
</Item>
</Col>
</Row> */}
</Row>
</Form>
</Modal>
<ProTable
request={async (params = { pageSize: 10, current: 0 }, sort, filter) => {
console.log(sort, filter, params);
......@@ -524,8 +466,13 @@ const UserManage = () => {
options={false}
headerTitle="账号列表"
toolBarRender={() => [
<Upload {...props}>
<Button icon={<UploadOutlined />}>Excel表格添加账号</Button>
<Button onClick={downloadExcelTemplate}>批量添加用户Excel模板</Button>,
<Upload
beforeUpload={handleUpload}
disabled={uploadButtonDisable}
showUploadList={false}
>
{uploadButtonDisable ? <Button><Loading /></Button> : <Button icon={<UploadOutlined />}>Excel表格添加账号</Button>}
</Upload>,
<Button
key="button"
......@@ -537,11 +484,11 @@ const UserManage = () => {
type="primary"
>
添加账号
</Button>,
</Button>
]}
/>
</PageContainer>
);
};
export default UserManage;
export default UserManage;
\ No newline at end of file
......@@ -4,19 +4,38 @@ interface ILoginParams {
name: string;
password: string;
}
const request = extend({});
const host = 'https://glm-mangement-api.baibaomen.com';
let host = "";
if (process.env.NODE_ENV === 'development') {
host = 'http://localhost:44315'
} else {
host = 'https://glm-mangement-api.baibaomen.com'
}
request.interceptors.request.use((url, options) => {
const token = JSON.parse(localStorage.getItem('user') || '{}')?.token;
const authHeader = {
Authorization: `Bearer ${token || ''}`,
'Content-Type': 'application/json',
__tenant: '3a0a90fe-9a0d-70f4-200d-a80a41fb6195',
};
let authHeader = null;
if (url == host + "/User/excelFile"){
authHeader= {
Authorization: `Bearer ${token || ''}`,
headers:{'Content-Type': 'multipart/form-data'},
__tenant: '3a0a90fe-9a0d-70f4-200d-a80a41fb6195',
};
}else{
authHeader= {
Authorization: `Bearer ${token || ''}`,
'Content-Type': 'application/json',
__tenant: '3a0a90fe-9a0d-70f4-200d-a80a41fb6195',
};
}
return {
url: url,
options: { ...options, interceptors: true, headers: authHeader },
options: { ...options, interceptors: true ,headers: authHeader},
};
});
......@@ -45,7 +64,7 @@ export interface IPromptWord {
}
export function GetChatGLMToken(): Promise<string> {
return request(host + '/user/token', {
method: 'POST',
method: 'POST'
});
}
......@@ -54,7 +73,6 @@ export function GetPromptList(): Promise<{ items: IPromptWord[] }> {
method: 'POST',
headers: {
Authorization: 'Bearer ' + JSON.parse(localStorage.getItem('user') || '')?.token,
'Content-Type': 'application/json',
},
body: JSON.stringify({ pageIndex: 0, pageSize: 10 }),
});
......@@ -164,6 +182,36 @@ export const postEditUser = (params?: any): Promise<string> => {
});
};
export const postExcelFile = (file) => {
const formData = new FormData();
formData.append('file', file);
return request(host + '/User/excelFile', {
method: 'POST',
body: formData
}).then(response => {
return response;
})
.catch(error => {
throw error;
});
};
export const postDowload = () => {
return request('Template.xlsx', {
method: 'GET',
responseType: 'blob', // 将响应类型设置为二进制流
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'excelTemplate.xlsx');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
}
export const createSession = (params: any): Promise<any> => {
return request(host + '/ChatSessions/create', {
method: 'POST',
......
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