😎1、统一系统logo文件上传模式 2、增加轮播图上传管理 3、ai组件改成按需加载 4、升级依赖
This commit is contained in:
parent
ad69c7e5b5
commit
17c3fb6a98
@ -4,7 +4,7 @@
|
||||
"Upload": {
|
||||
"Path": "upload/{yyyy}/{MM}/{dd}", // 文件上传目录
|
||||
"MaxSize": 51200, // 文件最大限制KB:1024*50
|
||||
"ContentType": [ "image/jpg", "image/png", "image/jpeg", "image/gif", "image/bmp", "text/plain", "text/xml", "application/pdf", "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "video/mp4", "application/wps-office.docx", "application/wps-office.xlsx", "application/wps-office.pptx", "application/vnd.android.package-archive", "application/octet-stream" ],
|
||||
"ContentType": [ "image/jpg", "image/png", "image/jpeg", "image/gif", "image/bmp", "image/svg+xml", "text/plain", "text/xml", "application/pdf", "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "video/mp4", "application/wps-office.docx", "application/wps-office.xlsx", "application/wps-office.pptx", "application/vnd.android.package-archive", "application/octet-stream" ],
|
||||
"EnableMd5": false, // 启用文件MDF5验证-防止重复上传
|
||||
"EnableSaveDb": false // 启用文件存储到数据库
|
||||
},
|
||||
|
||||
@ -28,9 +28,9 @@
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" Aliases="BouncyCastleV2" />
|
||||
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.0.7" />
|
||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.105" />
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.105" />
|
||||
<PackageReference Include="Furion.Pure" Version="4.9.7.105" />
|
||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.106" />
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.106" />
|
||||
<PackageReference Include="Furion.Pure" Version="4.9.7.106" />
|
||||
<PackageReference Include="Hardware.Info" Version="101.0.1.1" />
|
||||
<PackageReference Include="Hashids.net" Version="1.7.0" />
|
||||
<PackageReference Include="IPTools.China" Version="1.6.0" />
|
||||
@ -74,7 +74,7 @@
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||
<PackageReference Include="AspNet.Security.OAuth.Gitee" Version="8.3.0" />
|
||||
<PackageReference Include="AspNet.Security.OAuth.Weixin" Version="8.3.0" />
|
||||
<PackageReference Include="Lazy.Captcha.Core" Version="2.2.0" />
|
||||
<PackageReference Include="Lazy.Captcha.Core" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="8.0.11" />
|
||||
|
||||
@ -66,6 +66,11 @@ public class UploadFileInput
|
||||
/// </summary>
|
||||
/// <example></example>
|
||||
public string AllowSuffix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 业务数据Id
|
||||
/// </summary>
|
||||
public long DataId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -21,7 +21,7 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
private readonly UploadOptions _uploadOptions;
|
||||
private readonly INamedServiceProvider<ICustomFileProvider> _namedServiceProvider;
|
||||
private readonly ICustomFileProvider _customFileProvider;
|
||||
private readonly string _imageType = ".jpeg.jpg.png.bmp.gif.tif";
|
||||
private readonly string _imageType = ".jpeg.jpg.png.bmp.gif.tif.svg";
|
||||
|
||||
public SysFileService(UserManager userManager,
|
||||
SqlSugarRepository<SysFile> sysFileRep,
|
||||
@ -373,6 +373,41 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
return sysFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传Logo 🔖
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <param name="tenantId"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("上传Logo")]
|
||||
public async Task<SysFile> UploadLogo([Required] IFormFile file, [Required] long tenantId)
|
||||
{
|
||||
// 先清空
|
||||
var sysFile = await _sysFileRep.GetFirstAsync(u => u.FileType == "Logo" && u.DataId == tenantId);
|
||||
if (sysFile != null)
|
||||
await DeleteFile(new BaseIdInput { Id = sysFile.Id });
|
||||
|
||||
return await UploadFile(new UploadFileInput { File = file, AllowSuffix = _imageType, FileType = "Logo", DataId = tenantId }, $"upload/system/{tenantId}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传轮播图 🔖
|
||||
/// </summary>
|
||||
/// <param name="files"></param>
|
||||
/// <param name="tenantId"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("上传轮播图")]
|
||||
public async Task<List<SysFile>> UploadCarousel([Required] List<IFormFile> files, [Required] long tenantId)
|
||||
{
|
||||
var sysFileList = new List<SysFile>();
|
||||
foreach (var file in files)
|
||||
{
|
||||
var tFile = await UploadFile(new UploadFileInput { File = file, AllowSuffix = _imageType, FileType = "Carousel", DataId = tenantId }, $"upload/system/{tenantId}");
|
||||
sysFileList.Add(tFile);
|
||||
}
|
||||
return sysFileList;
|
||||
}
|
||||
|
||||
#region 统一实体与文件关联时,业务应用实体只需要定义一个SysFile集合导航属性,业务增加和更新、删除分别调用即可
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -17,14 +17,9 @@ public class SysInfoInput
|
||||
public long TenantId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标(Data URI scheme base64 编码)
|
||||
/// 系统图标
|
||||
/// </summary>
|
||||
public string LogoBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标文件名
|
||||
/// </summary>
|
||||
public string LogoFileName { get; set; }
|
||||
public string Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主标题
|
||||
@ -99,7 +94,7 @@ public class SysInfoInput
|
||||
public bool SecondVer { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 轮播图
|
||||
/// 轮播图Id集合
|
||||
/// </summary>
|
||||
public List<IFormFile> CarouselFiles { get; set; }
|
||||
public List<long> CarouselFileIds { get; set; }
|
||||
}
|
||||
@ -26,7 +26,6 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
private readonly SysRoleMenuService _sysRoleMenuService;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
private readonly SysFileService _sysFileService;
|
||||
private readonly IEventPublisher _eventPublisher;
|
||||
|
||||
public SysTenantService(SqlSugarRepository<SysTenant> sysTenantRep,
|
||||
@ -43,7 +42,6 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
SysRoleMenuService sysRoleMenuService,
|
||||
SysConfigService sysConfigService,
|
||||
SysCacheService sysCacheService,
|
||||
SysFileService sysFileService,
|
||||
IEventPublisher eventPublisher)
|
||||
{
|
||||
_sysTenantRep = sysTenantRep;
|
||||
@ -60,7 +58,6 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
_sysRoleMenuService = sysRoleMenuService;
|
||||
_sysConfigService = sysConfigService;
|
||||
_sysCacheService = sysCacheService;
|
||||
_sysFileService = sysFileService;
|
||||
_eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
@ -571,8 +568,8 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
if (string.IsNullOrWhiteSpace(tenant.Title))
|
||||
tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == SqlSugarConst.DefaultTenantId);
|
||||
|
||||
//// 获取首页轮播图列表
|
||||
//var carouselFiles = await _fileRep.GetListAsync(u => u.BelongId == tenant.Id && u.RelationId == tenant.Id && u.FileType == "Carousel");
|
||||
// 获取首页轮播图列表
|
||||
var carouselFiles = await _sysTenantRep.ChangeRepository<SqlSugarRepository<SysFile>>().GetListAsync(u => u.DataId == tenant.Id && u.FileType == "Carousel");
|
||||
|
||||
var forceChangePassword = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysForceChangePassword); // 强制修改密码
|
||||
var passwordExpirationTime = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysPasswordExpirationTime); // 密码有效期
|
||||
@ -601,7 +598,7 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
ForceChangePassword = forceChangePassword,
|
||||
PasswordExpirationTime = passwordExpirationTime,
|
||||
PublicKey = publicKey,
|
||||
//CarouselFiles = carouselFiles,
|
||||
CarouselFiles = carouselFiles,
|
||||
I18NSwitch = i18NSwitch,
|
||||
IdleTimeout = idleTimeout,
|
||||
OnlineNotice = onlineNotice,
|
||||
@ -615,53 +612,19 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
[UnitOfWork]
|
||||
[DisplayName("保存系统信息")]
|
||||
public async Task SaveSysInfo(SysInfoInput input)
|
||||
{
|
||||
if (input.TenantId < 1)
|
||||
input.TenantId = long.Parse(App.User?.FindFirst(ClaimConst.TenantId)?.Value ?? "0");
|
||||
var tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == input.TenantId);
|
||||
_ = tenant ?? throw Oops.Oh(ErrorCodeEnum.D1002);
|
||||
|
||||
var originLogo = tenant.Logo;
|
||||
{
|
||||
input.TenantId = input.TenantId < 1 ? long.Parse(App.User?.FindFirst(ClaimConst.TenantId)?.Value ?? "0") : input.TenantId;
|
||||
var tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == input.TenantId) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
|
||||
tenant = input.Adapt<SysTenant>();
|
||||
tenant.Id = input.TenantId;
|
||||
|
||||
// logo 不为空才保存
|
||||
if (!string.IsNullOrEmpty(input.LogoBase64))
|
||||
{
|
||||
// 旧图标文件相对路径
|
||||
var oldSysLogoRelativeFilePath = tenant.Logo ?? "";
|
||||
var oldSysLogoAbsoluteFilePath = Path.Combine(App.WebHostEnvironment.WebRootPath, oldSysLogoRelativeFilePath.TrimStart('/'));
|
||||
|
||||
var groups = Regex.Match(input.LogoBase64, @"data:image/(?<type>.+?);base64,(?<data>.+)").Groups;
|
||||
//var type = groups["type"].Value;
|
||||
var base64Data = groups["data"].Value;
|
||||
var binData = Convert.FromBase64String(base64Data);
|
||||
// 根据文件名取扩展名
|
||||
var ext = string.IsNullOrWhiteSpace(input.LogoFileName) ? ".png" : Path.GetExtension(input.LogoFileName);
|
||||
// 本地图标保存路径
|
||||
var path = $"upload/{input.TenantId}/";
|
||||
var fileName = $"logo{ext}".ToLower();
|
||||
var absoluteFilePath = Path.Combine(App.WebHostEnvironment.WebRootPath, path, fileName);
|
||||
|
||||
// 删除已存在文件
|
||||
if (File.Exists(oldSysLogoAbsoluteFilePath))
|
||||
File.Delete(oldSysLogoAbsoluteFilePath);
|
||||
|
||||
// 创建文件夹
|
||||
var absoluteFileDir = Path.GetDirectoryName(absoluteFilePath);
|
||||
if (!Directory.Exists(absoluteFileDir))
|
||||
Directory.CreateDirectory(absoluteFileDir);
|
||||
|
||||
// 保存图标文件
|
||||
await File.WriteAllBytesAsync(absoluteFilePath, binData);
|
||||
|
||||
// 保存图标配置
|
||||
tenant.Logo = $"/{path}/{fileName}";
|
||||
}
|
||||
else
|
||||
{
|
||||
tenant.Logo = originLogo;
|
||||
}
|
||||
tenant.Id = input.TenantId;
|
||||
|
||||
// 先清空轮播图再更新
|
||||
var carouselFileIds = await _sysTenantRep.ChangeRepository<SqlSugarRepository<SysFile>>().AsQueryable()
|
||||
.WhereIF(input.CarouselFileIds != null && input.CarouselFileIds.Count != 0, u => !input.CarouselFileIds.Contains(u.Id))
|
||||
.Where(u => u.FileType == "Carousel" && u.DataId == input.TenantId)
|
||||
.Select(u => u.Id).ToListAsync();
|
||||
foreach (var fileId in carouselFileIds)
|
||||
await App.GetRequiredService<SysFileService>().DeleteFile(new BaseIdInput { Id = fileId });
|
||||
|
||||
await _sysTenantRep.AsUpdateable(tenant).UpdateColumns(u => new
|
||||
{
|
||||
@ -684,40 +647,4 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
// 更新租户缓存
|
||||
await CacheTenant();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传轮播图单文件 🔖
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("上传轮播图单文件")]
|
||||
public async Task<SysFile> UploadCarouselFile([Required] IFormFile file)
|
||||
{
|
||||
var tenantId = long.Parse(App.User?.FindFirst(ClaimConst.TenantId)?.Value ?? "0");
|
||||
if (tenantId < 1) tenantId = SqlSugarConst.DefaultTenantId;
|
||||
var tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == tenantId);
|
||||
if (tenant == null) return null;
|
||||
|
||||
if (file == null)
|
||||
throw Oops.Oh(ErrorCodeEnum.D8000);
|
||||
|
||||
// 本地轮播图保存路径
|
||||
var path = $"upload/{tenantId}/carousel";
|
||||
var absoluteDirPath = Path.Combine(App.WebHostEnvironment.WebRootPath, path);
|
||||
|
||||
// 创建文件夹
|
||||
if (!Directory.Exists(absoluteDirPath))
|
||||
Directory.CreateDirectory(absoluteDirPath);
|
||||
|
||||
// 保存轮播图文件
|
||||
var sysFile = await _sysFileService.UploadFile(new UploadFileInput { File = file, FileType = "Carousel" });
|
||||
|
||||
//// 保存轮播图配置
|
||||
//sysFile.BelongId = tenant.Id;
|
||||
//sysFile.RelationId = tenant.Id;
|
||||
|
||||
await _sysFileService.UpdateFile(sysFile);
|
||||
|
||||
return sysFile;
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "admin.net.pro",
|
||||
"type": "module",
|
||||
"version": "2.4.33",
|
||||
"lastBuildTime": "2025.07.27",
|
||||
"lastBuildTime": "2025.07.28",
|
||||
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
|
||||
"author": "zuohuaijun",
|
||||
"license": "MIT",
|
||||
@ -74,7 +74,7 @@
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-demi": "0.14.10",
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
"vue-element-plus-x": "^1.3.0",
|
||||
"vue-element-plus-x": "^1.3.1",
|
||||
"vue-grid-layout": "3.0.0-beta1",
|
||||
"vue-i18n": "^11.1.11",
|
||||
"vue-json-pretty": "^2.5.0",
|
||||
@ -82,8 +82,8 @@
|
||||
"vue-router": "^4.5.1",
|
||||
"vue-signature-pad": "^3.0.2",
|
||||
"vue3-tree-org": "^4.2.2",
|
||||
"vxe-pc-ui": "^4.7.28",
|
||||
"vxe-table": "^4.14.7",
|
||||
"vxe-pc-ui": "^4.7.30",
|
||||
"vxe-table": "^4.14.8",
|
||||
"xe-utils": "^3.7.8",
|
||||
"xlsx-js-style": "^1.2.0"
|
||||
},
|
||||
@ -96,10 +96,10 @@
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
||||
"@typescript-eslint/parser": "^8.38.0",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
||||
"@vue/compiler-sfc": "^3.5.18",
|
||||
"code-inspector-plugin": "^1.0.0",
|
||||
"code-inspector-plugin": "^1.0.2",
|
||||
"eslint": "^9.32.0",
|
||||
"eslint-plugin-vue": "^10.3.0",
|
||||
"globals": "^16.3.0",
|
||||
|
||||
@ -518,6 +518,65 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传轮播图 🔖
|
||||
* @param {number} tenantId
|
||||
* @param {Array<Blob>} [files]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
apiSysFileUploadCarouselTenantIdPostForm: async (tenantId: number, files?: Array<Blob>, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'tenantId' is not null or undefined
|
||||
if (tenantId === null || tenantId === undefined) {
|
||||
throw new RequiredError('tenantId','Required parameter tenantId was null or undefined when calling apiSysFileUploadCarouselTenantIdPostForm.');
|
||||
}
|
||||
const localVarPath = `/api/sysFile/uploadCarousel/{tenantId}`
|
||||
.replace(`{${"tenantId"}}`, encodeURIComponent(String(tenantId)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
const localVarFormParams = new FormData();
|
||||
|
||||
// authentication Bearer required
|
||||
// http bearer authentication required
|
||||
if (configuration && configuration.accessToken) {
|
||||
const accessToken = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken()
|
||||
: await configuration.accessToken;
|
||||
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
|
||||
}
|
||||
|
||||
if (files) {
|
||||
files.forEach((element) => {
|
||||
localVarFormParams.append('files', element as any);
|
||||
})
|
||||
}
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
|
||||
const query = new URLSearchParams(localVarUrlObj.search);
|
||||
for (const key in localVarQueryParameter) {
|
||||
query.set(key, localVarQueryParameter[key]);
|
||||
}
|
||||
for (const key in options.params) {
|
||||
query.set(key, options.params[key]);
|
||||
}
|
||||
localVarUrlObj.search = (new URLSearchParams(query)).toString();
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = localVarFormParams;
|
||||
|
||||
return {
|
||||
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传文件Base64 🔖
|
||||
@ -574,10 +633,11 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
|
||||
* @param {string} [fileAlias]
|
||||
* @param {boolean} [isPublic]
|
||||
* @param {string} [allowSuffix]
|
||||
* @param {number} [dataId]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
apiSysFileUploadFilePostForm: async (file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
apiSysFileUploadFilePostForm: async (file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, dataId?: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/sysFile/uploadFile`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
|
||||
@ -620,6 +680,10 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
|
||||
localVarFormParams.append('AllowSuffix', allowSuffix as any);
|
||||
}
|
||||
|
||||
if (dataId !== undefined) {
|
||||
localVarFormParams.append('DataId', dataId as any);
|
||||
}
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
|
||||
const query = new URLSearchParams(localVarUrlObj.search);
|
||||
for (const key in localVarQueryParameter) {
|
||||
@ -691,6 +755,64 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传Logo 🔖
|
||||
* @param {number} tenantId
|
||||
* @param {Blob} [file]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
apiSysFileUploadLogoTenantIdPostForm: async (tenantId: number, file?: Blob, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'tenantId' is not null or undefined
|
||||
if (tenantId === null || tenantId === undefined) {
|
||||
throw new RequiredError('tenantId','Required parameter tenantId was null or undefined when calling apiSysFileUploadLogoTenantIdPostForm.');
|
||||
}
|
||||
const localVarPath = `/api/sysFile/uploadLogo/{tenantId}`
|
||||
.replace(`{${"tenantId"}}`, encodeURIComponent(String(tenantId)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
const localVarFormParams = new FormData();
|
||||
|
||||
// authentication Bearer required
|
||||
// http bearer authentication required
|
||||
if (configuration && configuration.accessToken) {
|
||||
const accessToken = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken()
|
||||
: await configuration.accessToken;
|
||||
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
|
||||
}
|
||||
|
||||
|
||||
if (file !== undefined) {
|
||||
localVarFormParams.append('file', file as any);
|
||||
}
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
|
||||
const query = new URLSearchParams(localVarUrlObj.search);
|
||||
for (const key in localVarQueryParameter) {
|
||||
query.set(key, localVarQueryParameter[key]);
|
||||
}
|
||||
for (const key in options.params) {
|
||||
query.set(key, options.params[key]);
|
||||
}
|
||||
localVarUrlObj.search = (new URLSearchParams(query)).toString();
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = localVarFormParams;
|
||||
|
||||
return {
|
||||
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传电子签名 🔖
|
||||
@ -945,6 +1067,21 @@ export const SysFileApiFp = function(configuration?: Configuration) {
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传轮播图 🔖
|
||||
* @param {number} tenantId
|
||||
* @param {Array<Blob>} [files]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysFileUploadCarouselTenantIdPostForm(tenantId: number, files?: Array<Blob>, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultListSysFile>>> {
|
||||
const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).apiSysFileUploadCarouselTenantIdPostForm(tenantId, files, options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传文件Base64 🔖
|
||||
@ -967,11 +1104,12 @@ export const SysFileApiFp = function(configuration?: Configuration) {
|
||||
* @param {string} [fileAlias]
|
||||
* @param {boolean} [isPublic]
|
||||
* @param {string} [allowSuffix]
|
||||
* @param {number} [dataId]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultSysFile>>> {
|
||||
const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).apiSysFileUploadFilePostForm(file, fileType, fileAlias, isPublic, allowSuffix, options);
|
||||
async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, dataId?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultSysFile>>> {
|
||||
const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).apiSysFileUploadFilePostForm(file, fileType, fileAlias, isPublic, allowSuffix, dataId, options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
return axios.request(axiosRequestArgs);
|
||||
@ -991,6 +1129,21 @@ export const SysFileApiFp = function(configuration?: Configuration) {
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传Logo 🔖
|
||||
* @param {number} tenantId
|
||||
* @param {Blob} [file]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysFileUploadLogoTenantIdPostForm(tenantId: number, file?: Blob, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultSysFile>>> {
|
||||
const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).apiSysFileUploadLogoTenantIdPostForm(tenantId, file, options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传电子签名 🔖
|
||||
@ -1129,6 +1282,17 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
|
||||
async apiSysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysFileApiFp(configuration).apiSysFileUploadAvatarPostForm(file, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传轮播图 🔖
|
||||
* @param {number} tenantId
|
||||
* @param {Array<Blob>} [files]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysFileUploadCarouselTenantIdPostForm(tenantId: number, files?: Array<Blob>, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListSysFile>> {
|
||||
return SysFileApiFp(configuration).apiSysFileUploadCarouselTenantIdPostForm(tenantId, files, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传文件Base64 🔖
|
||||
@ -1147,11 +1311,12 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
|
||||
* @param {string} [fileAlias]
|
||||
* @param {boolean} [isPublic]
|
||||
* @param {string} [allowSuffix]
|
||||
* @param {number} [dataId]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysFileApiFp(configuration).apiSysFileUploadFilePostForm(file, fileType, fileAlias, isPublic, allowSuffix, options).then((request) => request(axios, basePath));
|
||||
async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, dataId?: number, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysFileApiFp(configuration).apiSysFileUploadFilePostForm(file, fileType, fileAlias, isPublic, allowSuffix, dataId, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
@ -1163,6 +1328,17 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
|
||||
async apiSysFileUploadFilesPostForm(files?: Array<Blob>, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListSysFile>> {
|
||||
return SysFileApiFp(configuration).apiSysFileUploadFilesPostForm(files, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传Logo 🔖
|
||||
* @param {number} tenantId
|
||||
* @param {Blob} [file]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysFileUploadLogoTenantIdPostForm(tenantId: number, file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysFileApiFp(configuration).apiSysFileUploadLogoTenantIdPostForm(tenantId, file, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传电子签名 🔖
|
||||
@ -1304,6 +1480,18 @@ export class SysFileApi extends BaseAPI {
|
||||
public async apiSysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysFileApiFp(this.configuration).apiSysFileUploadAvatarPostForm(file, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 上传轮播图 🔖
|
||||
* @param {number} tenantId
|
||||
* @param {Array<Blob>} [files]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysFileApi
|
||||
*/
|
||||
public async apiSysFileUploadCarouselTenantIdPostForm(tenantId: number, files?: Array<Blob>, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListSysFile>> {
|
||||
return SysFileApiFp(this.configuration).apiSysFileUploadCarouselTenantIdPostForm(tenantId, files, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 上传文件Base64 🔖
|
||||
@ -1323,12 +1511,13 @@ export class SysFileApi extends BaseAPI {
|
||||
* @param {string} [fileAlias]
|
||||
* @param {boolean} [isPublic]
|
||||
* @param {string} [allowSuffix]
|
||||
* @param {number} [dataId]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysFileApi
|
||||
*/
|
||||
public async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysFileApiFp(this.configuration).apiSysFileUploadFilePostForm(file, fileType, fileAlias, isPublic, allowSuffix, options).then((request) => request(this.axios, this.basePath));
|
||||
public async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, dataId?: number, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysFileApiFp(this.configuration).apiSysFileUploadFilePostForm(file, fileType, fileAlias, isPublic, allowSuffix, dataId, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
@ -1341,6 +1530,18 @@ export class SysFileApi extends BaseAPI {
|
||||
public async apiSysFileUploadFilesPostForm(files?: Array<Blob>, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListSysFile>> {
|
||||
return SysFileApiFp(this.configuration).apiSysFileUploadFilesPostForm(files, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 上传Logo 🔖
|
||||
* @param {number} tenantId
|
||||
* @param {Blob} [file]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysFileApi
|
||||
*/
|
||||
public async apiSysFileUploadLogoTenantIdPostForm(tenantId: number, file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysFileApiFp(this.configuration).apiSysFileUploadLogoTenantIdPostForm(tenantId, file, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 上传电子签名 🔖
|
||||
|
||||
@ -25,7 +25,6 @@ import { AdminNETResultListSysUser } from '../models';
|
||||
import { AdminNETResultObject } from '../models';
|
||||
import { AdminNETResultSqlSugarPagedListTenantOutput } from '../models';
|
||||
import { AdminNETResultString } from '../models';
|
||||
import { AdminNETResultSysFile } from '../models';
|
||||
import { DeleteTenantInput } from '../models';
|
||||
import { PageTenantInput } from '../models';
|
||||
import { RoleMenuInput } from '../models';
|
||||
@ -703,58 +702,6 @@ export const SysTenantApiAxiosParamCreator = function (configuration?: Configura
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传轮播图单文件 🔖
|
||||
* @param {Blob} [file]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
apiSysTenantUploadCarouselFilePostForm: async (file?: Blob, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/sysTenant/uploadCarouselFile`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
const localVarFormParams = new FormData();
|
||||
|
||||
// authentication Bearer required
|
||||
// http bearer authentication required
|
||||
if (configuration && configuration.accessToken) {
|
||||
const accessToken = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken()
|
||||
: await configuration.accessToken;
|
||||
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
|
||||
}
|
||||
|
||||
|
||||
if (file !== undefined) {
|
||||
localVarFormParams.append('file', file as any);
|
||||
}
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
|
||||
const query = new URLSearchParams(localVarUrlObj.search);
|
||||
for (const key in localVarQueryParameter) {
|
||||
query.set(key, localVarQueryParameter[key]);
|
||||
}
|
||||
for (const key in options.params) {
|
||||
query.set(key, options.params[key]);
|
||||
}
|
||||
localVarUrlObj.search = (new URLSearchParams(query)).toString();
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = localVarFormParams;
|
||||
|
||||
return {
|
||||
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 获取租户下的用户列表 🔖
|
||||
@ -1006,20 +953,6 @@ export const SysTenantApiFp = function(configuration?: Configuration) {
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传轮播图单文件 🔖
|
||||
* @param {Blob} [file]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysTenantUploadCarouselFilePostForm(file?: Blob, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultSysFile>>> {
|
||||
const localVarAxiosArgs = await SysTenantApiAxiosParamCreator(configuration).apiSysTenantUploadCarouselFilePostForm(file, options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 获取租户下的用户列表 🔖
|
||||
@ -1181,16 +1114,6 @@ export const SysTenantApiFactory = function (configuration?: Configuration, base
|
||||
async apiSysTenantUpdatePost(body?: UpdateTenantInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
|
||||
return SysTenantApiFp(configuration).apiSysTenantUpdatePost(body, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 上传轮播图单文件 🔖
|
||||
* @param {Blob} [file]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysTenantUploadCarouselFilePostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysTenantApiFp(configuration).apiSysTenantUploadCarouselFilePostForm(file, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 获取租户下的用户列表 🔖
|
||||
@ -1363,17 +1286,6 @@ export class SysTenantApi extends BaseAPI {
|
||||
public async apiSysTenantUpdatePost(body?: UpdateTenantInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
|
||||
return SysTenantApiFp(this.configuration).apiSysTenantUpdatePost(body, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 上传轮播图单文件 🔖
|
||||
* @param {Blob} [file]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysTenantApi
|
||||
*/
|
||||
public async apiSysTenantUploadCarouselFilePostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSysFile>> {
|
||||
return SysTenantApiFp(this.configuration).apiSysTenantUploadCarouselFilePostForm(file, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 获取租户下的用户列表 🔖
|
||||
|
||||
@ -503,7 +503,6 @@ export * from './sys-report-group';
|
||||
export * from './sys-report-layout-config';
|
||||
export * from './sys-report-param';
|
||||
export * from './sys-schedule';
|
||||
export * from './sys-tenant-upload-carousel-file-body';
|
||||
export * from './sys-upgrade';
|
||||
export * from './sys-user';
|
||||
export * from './sys-user-ext-org';
|
||||
@ -553,7 +552,9 @@ export * from './update-schedule-input';
|
||||
export * from './update-sys-ldap-input';
|
||||
export * from './update-tenant-input';
|
||||
export * from './update-user-input';
|
||||
export * from './upload-carousel-tenant-id-body';
|
||||
export * from './upload-file-from-base64-input';
|
||||
export * from './upload-logo-tenant-id-body';
|
||||
export * from './user-menu-input';
|
||||
export * from './user-output';
|
||||
export * from './user-role-input';
|
||||
|
||||
@ -59,4 +59,12 @@ export interface SysFileUploadFileBody {
|
||||
* @memberof SysFileUploadFileBody
|
||||
*/
|
||||
allowSuffix?: string;
|
||||
|
||||
/**
|
||||
* 业务数据Id
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SysFileUploadFileBody
|
||||
*/
|
||||
dataId?: number;
|
||||
}
|
||||
|
||||
@ -29,20 +29,12 @@ export interface SysInfoInput {
|
||||
tenantId?: number;
|
||||
|
||||
/**
|
||||
* 图标(Data URI scheme base64 编码)
|
||||
* 系统图标
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SysInfoInput
|
||||
*/
|
||||
logoBase64?: string | null;
|
||||
|
||||
/**
|
||||
* 图标文件名
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SysInfoInput
|
||||
*/
|
||||
logoFileName?: string | null;
|
||||
logo?: string | null;
|
||||
|
||||
/**
|
||||
* 主标题
|
||||
@ -149,10 +141,10 @@ export interface SysInfoInput {
|
||||
secondVer?: boolean;
|
||||
|
||||
/**
|
||||
* 轮播图
|
||||
* 轮播图Id集合
|
||||
*
|
||||
* @type {Array<Blob>}
|
||||
* @type {Array<number>}
|
||||
* @memberof SysInfoInput
|
||||
*/
|
||||
carouselFiles?: Array<Blob> | null;
|
||||
carouselFileIds?: Array<number> | null;
|
||||
}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Admin.NET 通用权限开发平台
|
||||
* 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
|
||||
*
|
||||
* OpenAPI spec version: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export
|
||||
* @interface UploadCarouselTenantIdBody
|
||||
*/
|
||||
export interface UploadCarouselTenantIdBody {
|
||||
|
||||
/**
|
||||
* @type {Array<Blob>}
|
||||
* @memberof UploadCarouselTenantIdBody
|
||||
*/
|
||||
files: Array<Blob>;
|
||||
}
|
||||
@ -16,13 +16,13 @@
|
||||
*
|
||||
*
|
||||
* @export
|
||||
* @interface SysTenantUploadCarouselFileBody
|
||||
* @interface UploadLogoTenantIdBody
|
||||
*/
|
||||
export interface SysTenantUploadCarouselFileBody {
|
||||
export interface UploadLogoTenantIdBody {
|
||||
|
||||
/**
|
||||
* @type {Blob}
|
||||
* @memberof SysTenantUploadCarouselFileBody
|
||||
* @memberof UploadLogoTenantIdBody
|
||||
*/
|
||||
file: Blob;
|
||||
}
|
||||
@ -27,7 +27,7 @@ interface iVxeOption {
|
||||
sortConfig?: VxeTablePropTypes.SortConfig<any>;
|
||||
showFooter?: boolean;
|
||||
footerData?: VxeTablePropTypes.FooterData;
|
||||
footerMethod?: VxeTablePropTypes.FooterMethod<D>;
|
||||
footerMethod?: VxeTablePropTypes.FooterMethod<any>;
|
||||
remoteCustom?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@ -23,8 +23,6 @@ import 'vform3-builds/dist/designer.style.css';
|
||||
import { setupVXETable } from '/@/hooks/setupVXETableHook';
|
||||
// 自定义字典组件
|
||||
import sysDict from '/@/components/sysDict/sysDict.vue';
|
||||
// AI组件
|
||||
import ElementPlusX from 'vue-element-plus-x';
|
||||
// 关闭自动打印
|
||||
import { disAutoConnect } from 'vue-plugin-hiprint';
|
||||
disAutoConnect();
|
||||
@ -36,4 +34,4 @@ other.elSvg(app);
|
||||
// 注册全局字典组件
|
||||
app.component('GSysDict', sysDict);
|
||||
|
||||
app.use(pinia).use(i18n).use(router).use(ElementPlus).use(setupVXETable).use(VueGridLayout).use(VForm3).use(VueSignaturePad).use(vue3TreeOrg).use(ElementPlusX).mount('#app');
|
||||
app.use(pinia).use(i18n).use(router).use(ElementPlus).use(setupVXETable).use(VueGridLayout).use(VForm3).use(VueSignaturePad).use(vue3TreeOrg).mount('#app');
|
||||
|
||||
1
Web/src/types/pinia.d.ts
vendored
1
Web/src/types/pinia.d.ts
vendored
@ -100,6 +100,7 @@ declare interface ThemeConfigState {
|
||||
icp: string; // Icp备案号
|
||||
icpUrl: string; // Icp地址
|
||||
version?: string; // 版本号
|
||||
carouselFiles?: any[]; // 轮播图集合
|
||||
secondVer?: boolean; // 是否开启二级验证
|
||||
captcha?: boolean; // 是否开启验证码
|
||||
forceChangePassword?: boolean; // 是否开启强制修改密码
|
||||
|
||||
@ -42,6 +42,8 @@ export async function loadSysInfo(tenantid: number) {
|
||||
themeConfig.value.copyright = data.copyright;
|
||||
// 版本号
|
||||
themeConfig.value.version = data.version;
|
||||
// 轮播图
|
||||
themeConfig.value.carouselFiles = data.carouselFiles;
|
||||
// 全局主题
|
||||
themeConfig.value.primary = data.themeColor;
|
||||
// 布局切换
|
||||
|
||||
@ -2,8 +2,7 @@
|
||||
<div style="flex: 1; background: #f3f4f6; overflow: auto">
|
||||
<el-container>
|
||||
<!-- 侧边栏 -->
|
||||
<el-aside v-show="!isFold" :class="isFold ? 'sidebar-fold' : 'expand-sidebar'"
|
||||
style="background: #f3f4f6; border-right: 1px solid #f0f0f0; display: flex; flex-direction: column">
|
||||
<el-aside v-show="!isFold" :class="isFold ? 'sidebar-fold' : 'expand-sidebar'" style="background: #f3f4f6; border-right: 1px solid #f0f0f0; display: flex; flex-direction: column">
|
||||
<div class="chat-action">
|
||||
<el-tooltip :content="$t('message.chat.newChat')" placement="top">
|
||||
<div class="chat-action-item" @click.stop="handleNewChat">
|
||||
@ -16,23 +15,28 @@
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div
|
||||
style="display: flex; flex-direction: column; align-items: center; padding-bottom: 8px; padding-top: 0px; margin-top: 0px">
|
||||
<el-avatar :size="60" style="background-color: #f3f4f6" src="/chat.png" fit="fill"
|
||||
class="avatar-with-shadow" />
|
||||
<div style="margin-top: 10px; font-weight: bold; font-size: 18px; color: #333">{{
|
||||
$t('message.chat.title') }}</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; padding-bottom: 8px; padding-top: 0px; margin-top: 0px">
|
||||
<el-avatar :size="60" style="background-color: #f3f4f6" src="/chat.png" fit="fill" class="avatar-with-shadow" />
|
||||
<div style="margin-top: 10px; font-weight: bold; font-size: 18px; color: #333">{{ $t('message.chat.title') }}</div>
|
||||
</div>
|
||||
<div style="flex: 1; overflow-y: auto; width: 100%">
|
||||
<div style="display: flex; flex-direction: column; gap: 12px; height: 100%">
|
||||
<Conversations style="border-radius: 0%; width: 255px" v-model:active="activeHistoryKey"
|
||||
:items="sideBarHistoryList" row-key="key" :show-tooltip="true" showToTopBtn
|
||||
:label-max-width="210" :load-more="loadMoreHistoryItems"
|
||||
:load-more-loading="isHistoryListLoading" showBuiltInMenu @change="handleChange">
|
||||
<Conversations
|
||||
style="border-radius: 0%; width: 255px"
|
||||
v-model:active="activeHistoryKey"
|
||||
:items="sideBarHistoryList"
|
||||
row-key="key"
|
||||
:show-tooltip="true"
|
||||
showToTopBtn
|
||||
:label-max-width="210"
|
||||
:load-more="loadMoreHistoryItems"
|
||||
:load-more-loading="isHistoryListLoading"
|
||||
showBuiltInMenu
|
||||
@change="handleChange"
|
||||
>
|
||||
<template #menu="{ item }">
|
||||
<div class="menu-buttons">
|
||||
<el-button v-for="menuItem in actionMenuItems" :key="menuItem.key" link
|
||||
size="default" @click="handleMenuClick(menuItem.key, item)">
|
||||
<el-button v-for="menuItem in actionMenuItems" :key="menuItem.key" link size="default" @click="handleMenuClick(menuItem.key, item)">
|
||||
<el-icon v-if="menuItem.icon">
|
||||
<component :is="menuItem.icon" />
|
||||
</el-icon>
|
||||
@ -47,7 +51,8 @@
|
||||
|
||||
<!-- 主体内容 -->
|
||||
<el-container>
|
||||
<el-main style="
|
||||
<el-main
|
||||
style="
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -60,12 +65,12 @@
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
background: #fff;
|
||||
">
|
||||
"
|
||||
>
|
||||
<div class="main_action_toolbar" style="position: absolute; top: 0px; width: 500px; left: 20px">
|
||||
<div v-if="isFold" class="chat-action-item main_action_item">
|
||||
<el-tooltip :content="$t('message.chat.expandChat')" placement="top">
|
||||
<Expand style="width: 1.5em; height: 1.5em; color: #333"
|
||||
@click.stop="handleExpandChat" />
|
||||
<Expand style="width: 1.5em; height: 1.5em; color: #333" @click.stop="handleExpandChat" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div v-if="isFold" class="chat-action-item main_action_item" @click="handleNewChat">
|
||||
@ -83,12 +88,10 @@
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="item in modellList.models" :key="item.modelName"
|
||||
@click="handleChangeModel(item)">
|
||||
<el-dropdown-item v-for="item in modellList.models" :key="item.modelName" @click="handleChangeModel(item)">
|
||||
<div class="model-item">
|
||||
<span>{{ item.providerName }}/{{ item.modelName }}</span>
|
||||
<el-icon v-if="item.modelName == modellList.currentModel"
|
||||
style="color: #409eff">
|
||||
<el-icon v-if="item.modelName == modellList.currentModel" style="color: #409eff">
|
||||
<Select />
|
||||
</el-icon>
|
||||
</div>
|
||||
@ -99,46 +102,35 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isNew" class="new_chat_title">
|
||||
<div style="margin-top: 60px; font-size: 36px; font-weight: bold; letter-spacing: 2px">Hello, {{
|
||||
userName }}
|
||||
</div>
|
||||
<div
|
||||
style="margin-top: 20px; width: 100%; text-align: center; color: #999; font-size: 16px; font-weight: bold; letter-spacing: 2px">
|
||||
<div style="margin-top: 60px; font-size: 36px; font-weight: bold; letter-spacing: 2px">Hello, {{ userName }}</div>
|
||||
<div style="margin-top: 20px; width: 100%; text-align: center; color: #999; font-size: 16px; font-weight: bold; letter-spacing: 2px">
|
||||
{{ $t('message.chat.subTitle') }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="chat_content">
|
||||
<BubbleList class="chat_content_list" ref="chatRef" :list="chatList" maxHeight="100%"
|
||||
style="padding: 0 60px 100px 60px" @complete="handleBubbleComplete"
|
||||
:triggerIndices="triggerIndices">
|
||||
<BubbleList class="chat_content_list" ref="chatRef" :list="chatList" maxHeight="100%" style="padding: 0 60px 100px 60px" @complete="handleBubbleComplete" :triggerIndices="triggerIndices">
|
||||
<template #content="{ item }">
|
||||
<Typewriter v-if="chatList.length > 0 && chatList[chatList.length - 1].key === item.key && isSenderLoading" :content="item.content" :is-markdown="true" :typing="true" @finish="handleTypeingComplete"/>
|
||||
<XMarkdown
|
||||
v-else
|
||||
:markdown="item.content"
|
||||
default-theme-mode="light"
|
||||
class="markdown-body"
|
||||
<Typewriter
|
||||
v-if="chatList.length > 0 && chatList[chatList.length - 1].key === item.key && isSenderLoading"
|
||||
:content="item.content"
|
||||
:is-markdown="true"
|
||||
:typing="true"
|
||||
@finish="handleTypeingComplete"
|
||||
/>
|
||||
<XMarkdown v-else :markdown="item.content" default-theme-mode="light" class="markdown-body" />
|
||||
</template>
|
||||
<template #header="{ item }">
|
||||
<div v-if="item.role == 'assistant' && deepThinkingVisible && item.key == chatList[chatList.length - 1].key"
|
||||
class="header-wrapper">
|
||||
<Thinking max-width="100%" buttonWidth="250px" autoCollapse
|
||||
:content="deepThinkingMessage" :status="deepThinkingStatus"
|
||||
backgroundColor="#fff9e6" color="#000">
|
||||
<div v-if="item.role == 'assistant' && deepThinkingVisible && item.key == chatList[chatList.length - 1].key" class="header-wrapper">
|
||||
<Thinking max-width="100%" buttonWidth="250px" autoCollapse :content="deepThinkingMessage" :status="deepThinkingStatus" backgroundColor="#fff9e6" color="#000">
|
||||
<template #label="{ status }">
|
||||
<span v-if="status === 'start'">{{ $t('message.chat.startThinking')
|
||||
}}</span>
|
||||
<span v-else-if="status === 'thinking'">{{ $t('message.chat.thinking')
|
||||
}}</span>
|
||||
<span v-else-if="status === 'end'">{{ $t('message.chat.thinkingDone')
|
||||
}}</span>
|
||||
<span v-else-if="status === 'error'">{{ $t('message.chat.thinkingFailed')
|
||||
}}</span>
|
||||
<span v-if="status === 'start'">{{ $t('message.chat.startThinking') }}</span>
|
||||
<span v-else-if="status === 'thinking'">{{ $t('message.chat.thinking') }}</span>
|
||||
<span v-else-if="status === 'end'">{{ $t('message.chat.thinkingDone') }}</span>
|
||||
<span v-else-if="status === 'error'">{{ $t('message.chat.thinkingFailed') }}</span>
|
||||
</template>
|
||||
<template #content="{ content }">
|
||||
<span>
|
||||
<XMarkdown :markdown="content" default-theme-mode="light" class="vp-raw" />
|
||||
<XMarkdown :markdown="content" default-theme-mode="light" class="vp-raw" />
|
||||
</span>
|
||||
</template>
|
||||
</Thinking>
|
||||
@ -146,15 +138,12 @@
|
||||
</template>
|
||||
<template #footer="{ item }">
|
||||
<div v-if="item.role == 'assistant'" class="footer-container">
|
||||
<el-button type="info" text :icon="CopyDocument" size="small"
|
||||
@click="handleCopy(item)" />
|
||||
<el-button type="info" text :icon="CopyDocument" size="small" @click="handleCopy(item)" />
|
||||
<el-button type="info" text size="small">
|
||||
<el-icon v-if="!isPlaying || playAudioKey != item.key"
|
||||
@click="handlePlay(item)">
|
||||
<el-icon v-if="!isPlaying || playAudioKey != item.key" @click="handlePlay(item)">
|
||||
<VideoPlay />
|
||||
</el-icon>
|
||||
<el-icon v-if="isPlaying && playAudioKey == item.key"
|
||||
@click="handlePause(item)">
|
||||
<el-icon v-if="isPlaying && playAudioKey == item.key" @click="handlePause(item)">
|
||||
<VideoPause />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
@ -163,9 +152,18 @@
|
||||
</BubbleList>
|
||||
</div>
|
||||
<div :class="isNew ? 'chat_new_input_style' : 'chat_edit_input_style'">
|
||||
<sender ref="senderRef" variant="updown" clearable allow-speech :loading="isSenderLoading"
|
||||
:read-only="isSenderLoading" :auto-size="{ minRows: 1, maxRows: 5 }" v-model="senderInput"
|
||||
@submit="handleSend" :placeholder="$t('message.chat.inputPlaceholder')">
|
||||
<sender
|
||||
ref="senderRef"
|
||||
variant="updown"
|
||||
clearable
|
||||
allow-speech
|
||||
:loading="isSenderLoading"
|
||||
:read-only="isSenderLoading"
|
||||
:auto-size="{ minRows: 1, maxRows: 5 }"
|
||||
v-model="senderInput"
|
||||
@submit="handleSend"
|
||||
:placeholder="$t('message.chat.inputPlaceholder')"
|
||||
>
|
||||
<template #prefix>
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap">
|
||||
<el-button round plain>
|
||||
@ -174,9 +172,11 @@
|
||||
</el-icon>
|
||||
</el-button>
|
||||
|
||||
<div :class="{ isDeepThinking }"
|
||||
<div
|
||||
:class="{ isDeepThinking }"
|
||||
style="display: flex; align-items: center; gap: 4px; padding: 4px 12px; border: 1px solid silver; border-radius: 15px; cursor: pointer; font-size: 12px"
|
||||
@click="handleDeepThinking">
|
||||
@click="handleDeepThinking"
|
||||
>
|
||||
<el-icon>
|
||||
<ElementPlus />
|
||||
</el-icon>
|
||||
@ -197,12 +197,10 @@
|
||||
<div class="ai-rename-icon ai-rename-drag" @mousedown="startDrag">
|
||||
<el-avatar :size="48" src="/chat.png" />
|
||||
</div>
|
||||
<el-input v-model="renameInput" @keyup.enter="confirmRename" ref="renameInputRef"
|
||||
class="ai-rename-input" />
|
||||
<el-input v-model="renameInput" @keyup.enter="confirmRename" ref="renameInputRef" class="ai-rename-input" />
|
||||
</div>
|
||||
<div class="ai-dialog-footer">
|
||||
<el-button @click="cancelRename" class="ai-btn-cancel">{{ $t('message.chat.cancel')
|
||||
}}</el-button>
|
||||
<el-button @click="cancelRename" class="ai-btn-cancel">{{ $t('message.chat.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="confirmRename" class="ai-btn-confirm">
|
||||
{{ $t('message.chat.confirm') }}
|
||||
</el-button>
|
||||
@ -222,12 +220,10 @@ import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { franc } from 'franc';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { BubbleList, Conversations, Sender, Thinking, Typewriter, XMarkdown } from 'vue-element-plus-x';
|
||||
import type { BubbleListItemProps, BubbleListProps } from 'vue-element-plus-x/types/components/BubbleList/types';
|
||||
import type { ConversationItem } from 'vue-element-plus-x/types/components/Conversations/types';
|
||||
import type { TypewriterInstance } from 'vue-element-plus-x/types/components/Typewriter/types';
|
||||
// import 'vue-element-plus-x/styles/prism-coy.min.css'
|
||||
// import 'vue-element-plus-x/styles/prism.min.css'
|
||||
|
||||
|
||||
import { getAPI } from '/@/utils/axios-utils';
|
||||
import { LLMChatApi } from '/@/api-services/api';
|
||||
@ -311,13 +307,13 @@ const initSSEConnection = () => {
|
||||
monitorSSEConnectionHandler = setInterval(() => {
|
||||
isSSEConnectionClosed = Date.now() - lastSseConnectionTime > SSE_CONNECTION_TIMEOUT;
|
||||
if (isSSEConnectionClosed) {
|
||||
console.log("SSE connection timed out, reconnecting");
|
||||
console.log('SSE connection timed out, reconnecting');
|
||||
try {
|
||||
initSSEConnectionCore();
|
||||
} catch (err) {
|
||||
console.log("SSE connection timed out, reconnecting failed", err);
|
||||
console.log('SSE connection timed out, reconnecting failed', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, SSE_CONNECTION_TIMEOUT);
|
||||
};
|
||||
|
||||
@ -327,7 +323,7 @@ const checkSSEConnectionStatus = () => {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
// 初始化sse连接核心代码
|
||||
const initSSEConnectionCore = () => {
|
||||
closeSSEConnection();
|
||||
@ -378,12 +374,10 @@ const initSSEConnectionCore = () => {
|
||||
lastSseConnectionTime = Date.now();
|
||||
});
|
||||
|
||||
|
||||
eventSource.onerror = () => {
|
||||
console.log('SSE connection error');
|
||||
};
|
||||
|
||||
|
||||
eventSource.onopen = (event) => {
|
||||
console.log('SSE connection opened:', event);
|
||||
};
|
||||
@ -401,10 +395,10 @@ const handleBubbleComplete = (instance: TypewriterInstance, index: number) => {
|
||||
chatRef?.value?.scrollToBottom();
|
||||
};
|
||||
|
||||
const handleTypeingComplete = ()=>{
|
||||
const handleTypeingComplete = () => {
|
||||
isSenderLoading.value = false;
|
||||
chatRef?.value?.scrollToBottom();
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion sse客户端
|
||||
const handleSend = async () => {
|
||||
@ -688,8 +682,6 @@ const loadMoreHistoryItems = async () => {
|
||||
isHistoryListLoading.value = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleMenuClick = (menuKey: string, item: any) => {
|
||||
switch (menuKey) {
|
||||
case 'rename':
|
||||
@ -1129,9 +1121,9 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.vp-raw {
|
||||
background-color: #f0f0f0;
|
||||
padding: 2px 20px;
|
||||
border-radius: 4px;
|
||||
line-height: 2em;
|
||||
padding: 2px 20px;
|
||||
border-radius: 4px;
|
||||
line-height: 2em;
|
||||
}
|
||||
.markdown-body {
|
||||
font-size: 16px !important;
|
||||
|
||||
@ -10,15 +10,22 @@
|
||||
</div>
|
||||
</div>
|
||||
<el-carousel height="500px" class="login-carousel">
|
||||
<el-carousel-item>
|
||||
<img :src="loginIconTwo" class="login-icon-group-icon" />
|
||||
</el-carousel-item>
|
||||
<el-carousel-item>
|
||||
<img :src="loginIconTwo1" class="login-icon-group-icon" />
|
||||
</el-carousel-item>
|
||||
<el-carousel-item>
|
||||
<img :src="loginIconTwo2" class="login-icon-group-icon" />
|
||||
</el-carousel-item>
|
||||
<template v-if="themeConfig.carouselFiles == undefined || themeConfig.carouselFiles.length < 1">
|
||||
<el-carousel-item>
|
||||
<img :src="loginIconTwo" class="login-icon-group-icon" />
|
||||
</el-carousel-item>
|
||||
<el-carousel-item>
|
||||
<img :src="loginIconTwo1" class="login-icon-group-icon" />
|
||||
</el-carousel-item>
|
||||
<el-carousel-item>
|
||||
<img :src="loginIconTwo2" class="login-icon-group-icon" />
|
||||
</el-carousel-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-carousel-item v-for="(item, key) in themeConfig.carouselFiles" :key="key">
|
||||
<img :src="item.url" class="login-icon-group-icon" />
|
||||
</el-carousel-item>
|
||||
</template>
|
||||
</el-carousel>
|
||||
</div>
|
||||
<div class="login-right flex">
|
||||
@ -95,6 +102,7 @@ import { Local } from '/@/utils/storage';
|
||||
// 引入多语言
|
||||
import { languageList, getCountryCode } from '/@/i18n';
|
||||
import '/node_modules/flag-icons/css/flag-icons.min.css';
|
||||
import { template } from 'xe-utils';
|
||||
|
||||
// 引入组件
|
||||
const Account = defineAsyncComponent(() => import('/@/views/login/component/account.vue'));
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<template #label>
|
||||
<el-icon><ele-School /></el-icon> 系统图标
|
||||
</template>
|
||||
<el-upload ref="uploadRef" class="avatar-uploader" :showFileList="false" :autoUpload="false" accept=".jpg,.png,.svg" action :limit="1" :onChange="handleUploadChange">
|
||||
<el-upload ref="uploadRef" class="avatar-uploader" :showFileList="false" :autoUpload="false" accept=".jpg,.png,.svg" action :limit="1" :onChange="handleUploadLogoChange">
|
||||
<img v-if="state.sysInfo.logo" :src="state.sysInfo.logo" class="avatar" />
|
||||
<SvgIcon v-else class="avatar-uploader-icon" name="ele-Plus" :size="28" />
|
||||
</el-upload>
|
||||
@ -152,13 +152,21 @@
|
||||
<template #label>
|
||||
<el-icon><ele-Picture /></el-icon> 首页轮播图
|
||||
</template>
|
||||
<el-upload :file-list="state.carouselFileList" list-type="picture-card" :http-request="uploadCarouselFile" :on-preview="previewCarouselFile" :before-remove="beforeRemoveCarouselFile">
|
||||
<el-upload
|
||||
:file-list="state.carouselFileList"
|
||||
list-type="picture-card"
|
||||
:autoUpload="false"
|
||||
accept=".jpg,.png,.svg"
|
||||
:onChange="handleCarouselChange"
|
||||
:on-preview="handleCarouselPreview"
|
||||
:before-remove="handleCarouselRemove"
|
||||
>
|
||||
<el-icon><ele-Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-descriptions-item>
|
||||
|
||||
<template #extra>
|
||||
<el-button type="primary" icon="ele-SuccessFilled" @click="saveSysInfo">保存</el-button>
|
||||
<el-button type="primary" icon="ele-SuccessFilled" @click="saveSysInfo">保存配置</el-button>
|
||||
</template>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
@ -182,63 +190,67 @@
|
||||
</el-row>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="state.dialogImagePreviewVisible">
|
||||
<img w-full :src="state.dialogImagePreviewUrl" alt="" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="sysInfoSetting">
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessage, ElMessageBox, UploadInstance } from 'element-plus';
|
||||
import { fileToBase64 } from '/@/utils/base64Conver';
|
||||
import { ElMessage, UploadFile, UploadFiles, UploadInstance } from 'element-plus';
|
||||
import { loadSysInfo } from '/@/utils/sysInfo';
|
||||
import chineseColors from '/@/layout/navBars/topBar/colors.json';
|
||||
|
||||
import { getAPI } from '/@/utils/axios-utils';
|
||||
import { SysFileApi, SysInfoInput, SysTenantApi } from '/@/api-services';
|
||||
import { SysFileApi, SysTenantApi } from '/@/api-services';
|
||||
|
||||
const host = window.location.host;
|
||||
const uploadRef = ref<UploadInstance>();
|
||||
const state = reactive({
|
||||
isLoading: false,
|
||||
sysInfo: {} as SysInfoInput | any, // 系统信息数据
|
||||
sysInfo: {} as any, // 系统信息数据
|
||||
logoFile: undefined as any, // logo上传文件
|
||||
dialogChineseColorVisible: false, // 中国传统颜色弹窗
|
||||
colorName: '飞燕草蓝', // 主题颜色名称
|
||||
dialogImagePreviewVisible: false, // 预览图片弹窗
|
||||
dialogImagePreviewUrl: '', // 预览图片地址
|
||||
isDelete: false, // 是否已删除图片
|
||||
carouselFileList: [] as any, // 轮播图片文件列表
|
||||
carouselFileList: [] as any, // 轮播图文件列表
|
||||
});
|
||||
|
||||
// 页面初始化
|
||||
onMounted(async () => {
|
||||
await loadSysInfoData();
|
||||
state.isLoading = true;
|
||||
state.sysInfo = await getAPI(SysTenantApi)
|
||||
.apiSysTenantSysInfoTenantIdGet(0)
|
||||
.then((res) => res.data.result ?? []);
|
||||
if (state.sysInfo.carouselFiles) state.carouselFileList = state.sysInfo.carouselFiles;
|
||||
state.isLoading = false;
|
||||
});
|
||||
|
||||
// 通过onChange方法获得文件列表
|
||||
const handleUploadChange = (file: any) => {
|
||||
uploadRef.value!.clearFiles();
|
||||
state.logoFile = file;
|
||||
state.sysInfo.logo = URL.createObjectURL(state.logoFile.raw); // 显示预览logo
|
||||
};
|
||||
|
||||
// 保存
|
||||
const saveSysInfo = async () => {
|
||||
// 如果有选择图标,则转换为 base64
|
||||
if (state.logoFile) {
|
||||
state.sysInfo.logoBase64 = (await fileToBase64(state.logoFile.raw)) as string;
|
||||
state.sysInfo.logoFileName = state.logoFile.raw.name;
|
||||
}
|
||||
|
||||
try {
|
||||
state.isLoading = true;
|
||||
if (state.logoFile != undefined && state.logoFile.raw != undefined) {
|
||||
var res = await getAPI(SysFileApi).apiSysFileUploadLogoTenantIdPostForm(state.sysInfo.tenantId, state.logoFile.raw);
|
||||
state.sysInfo.logo = res.data.result?.url;
|
||||
}
|
||||
if (state.carouselFileList != undefined && state.carouselFileList.length > 0) {
|
||||
// 已有的轮播图
|
||||
state.sysInfo.carouselFileIds = state.carouselFileList.filter((file: any) => file.raw == undefined && file.id != undefined).map((file: any) => file.id);
|
||||
|
||||
// 新增的轮播图
|
||||
var carouselFiles = state.carouselFileList.filter((file: any) => file.raw != undefined).map((file: any) => file.raw);
|
||||
if (carouselFiles.length > 0) {
|
||||
var sysFileIds = await getAPI(SysFileApi)
|
||||
.apiSysFileUploadCarouselTenantIdPostForm(state.sysInfo.tenantId, carouselFiles)
|
||||
.then((res) => res.data.result?.map((file: any) => file.id) ?? []);
|
||||
state.sysInfo.carouselFileIds.push(...sysFileIds);
|
||||
}
|
||||
console.log(state.sysInfo.carouselFileIds);
|
||||
}
|
||||
await getAPI(SysTenantApi).apiSysTenantSaveSysInfoPost(state.sysInfo);
|
||||
state.logoFile = undefined;
|
||||
ElMessage.success('保存成功');
|
||||
|
||||
// 置空上传文件
|
||||
state.logoFile = undefined;
|
||||
|
||||
// 更新系统信息
|
||||
await loadSysInfo(state.sysInfo.tenantId);
|
||||
} finally {
|
||||
@ -248,65 +260,27 @@ const saveSysInfo = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 加载系统信息数据
|
||||
const loadSysInfoData = async () => {
|
||||
try {
|
||||
state.isLoading = true;
|
||||
const res = await getAPI(SysTenantApi).apiSysTenantSysInfoTenantIdGet(0);
|
||||
if (res.data!.type !== 'success') return;
|
||||
|
||||
state.sysInfo = res.data.result;
|
||||
if (state.sysInfo.carouselFiles) state.carouselFileList = state.sysInfo.carouselFiles;
|
||||
} finally {
|
||||
nextTick(() => {
|
||||
state.isLoading = false;
|
||||
});
|
||||
}
|
||||
// 上传系统logo
|
||||
const handleUploadLogoChange = (file: any) => {
|
||||
uploadRef.value!.clearFiles();
|
||||
state.logoFile = file;
|
||||
state.sysInfo.logo = URL.createObjectURL(state.logoFile.raw); // 显示预览logo
|
||||
};
|
||||
|
||||
// 图片转file
|
||||
// const onlineImageToFile = async (imageUrl: string | URL | Request, fileName: string) => {
|
||||
// try {
|
||||
// const response = await fetch(imageUrl);
|
||||
// const blob = await response.blob();
|
||||
// const file = new File([blob], fileName, { type: blob.type });
|
||||
// return file;
|
||||
// } catch (error) {
|
||||
// return null;
|
||||
// }
|
||||
// };
|
||||
|
||||
// 上传轮播图文件
|
||||
const uploadCarouselFile = async (e: any) => {
|
||||
await getAPI(SysTenantApi).apiSysTenantUploadCarouselFilePostForm(e.file);
|
||||
const handleCarouselChange = async (file: UploadFile, files: UploadFiles) => {
|
||||
state.carouselFileList = files;
|
||||
};
|
||||
|
||||
// 删除轮播图文件
|
||||
const beforeRemoveCarouselFile = (file: any, fileList: any) => {
|
||||
const result = new Promise((resolve, reject) => {
|
||||
ElMessageBox.confirm(`确定删除此轮播图?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(async () => {
|
||||
state.isDelete = true;
|
||||
let index = fileList.indexOf(file);
|
||||
await getAPI(SysFileApi).apiSysFileDeletePost(fileList[index]);
|
||||
fileList.splice(index, 1);
|
||||
state.carouselFileList.splice(index, 1);
|
||||
})
|
||||
.catch(() => {
|
||||
reject(false);
|
||||
});
|
||||
});
|
||||
return result;
|
||||
const handleCarouselRemove = (file: UploadFile) => {
|
||||
let index = state.carouselFileList.indexOf(file);
|
||||
state.carouselFileList.splice(index, 1);
|
||||
};
|
||||
|
||||
// 预览轮播图
|
||||
const previewCarouselFile = (file: any) => {
|
||||
state.dialogImagePreviewUrl = file.url!;
|
||||
state.dialogImagePreviewVisible = true;
|
||||
const handleCarouselPreview = (file: UploadFile) => {
|
||||
window.open(file.url);
|
||||
};
|
||||
|
||||
// 选择主题色
|
||||
|
||||
Loading…
Reference in New Issue
Block a user