😎1、统一系统logo文件上传模式 2、增加轮播图上传管理 3、ai组件改成按需加载 4、升级依赖

This commit is contained in:
zuohuaijun 2025-07-28 13:06:28 +08:00
parent ad69c7e5b5
commit 17c3fb6a98
21 changed files with 474 additions and 395 deletions

View File

@ -4,7 +4,7 @@
"Upload": { "Upload": {
"Path": "upload/{yyyy}/{MM}/{dd}", // "Path": "upload/{yyyy}/{MM}/{dd}", //
"MaxSize": 51200, // KB1024*50 "MaxSize": 51200, // KB1024*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- "EnableMd5": false, // MDF5-
"EnableSaveDb": false // "EnableSaveDb": false //
}, },

View File

@ -28,9 +28,9 @@
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" /> <PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" Aliases="BouncyCastleV2" /> <PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" Aliases="BouncyCastleV2" />
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.0.7" /> <PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.0.7" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" 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.105" /> <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.106" />
<PackageReference Include="Furion.Pure" Version="4.9.7.105" /> <PackageReference Include="Furion.Pure" Version="4.9.7.106" />
<PackageReference Include="Hardware.Info" Version="101.0.1.1" /> <PackageReference Include="Hardware.Info" Version="101.0.1.1" />
<PackageReference Include="Hashids.net" Version="1.7.0" /> <PackageReference Include="Hashids.net" Version="1.7.0" />
<PackageReference Include="IPTools.China" Version="1.6.0" /> <PackageReference Include="IPTools.China" Version="1.6.0" />
@ -74,7 +74,7 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="AspNet.Security.OAuth.Gitee" Version="8.3.0" /> <PackageReference Include="AspNet.Security.OAuth.Gitee" Version="8.3.0" />
<PackageReference Include="AspNet.Security.OAuth.Weixin" 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.DataProtection.StackExchangeRedis" Version="8.0.11" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" 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" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="8.0.11" />

View File

@ -66,6 +66,11 @@ public class UploadFileInput
/// </summary> /// </summary>
/// <example></example> /// <example></example>
public string AllowSuffix { get; set; } public string AllowSuffix { get; set; }
/// <summary>
/// 业务数据Id
/// </summary>
public long DataId { get; set; }
} }
/// <summary> /// <summary>

View File

@ -21,7 +21,7 @@ public class SysFileService : IDynamicApiController, ITransient
private readonly UploadOptions _uploadOptions; private readonly UploadOptions _uploadOptions;
private readonly INamedServiceProvider<ICustomFileProvider> _namedServiceProvider; private readonly INamedServiceProvider<ICustomFileProvider> _namedServiceProvider;
private readonly ICustomFileProvider _customFileProvider; 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, public SysFileService(UserManager userManager,
SqlSugarRepository<SysFile> sysFileRep, SqlSugarRepository<SysFile> sysFileRep,
@ -373,6 +373,41 @@ public class SysFileService : IDynamicApiController, ITransient
return sysFile; 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集合导航属性 #region SysFile集合导航属性
/// <summary> /// <summary>

View File

@ -17,14 +17,9 @@ public class SysInfoInput
public long TenantId { get; set; } public long TenantId { get; set; }
/// <summary> /// <summary>
/// 图标Data URI scheme base64 编码) /// 系统图标
/// </summary> /// </summary>
public string LogoBase64 { get; set; } public string Logo { get; set; }
/// <summary>
/// 图标文件名
/// </summary>
public string LogoFileName { get; set; }
/// <summary> /// <summary>
/// 主标题 /// 主标题
@ -99,7 +94,7 @@ public class SysInfoInput
public bool SecondVer { get; set; } = false; public bool SecondVer { get; set; } = false;
/// <summary> /// <summary>
/// 轮播图 /// 轮播图Id集合
/// </summary> /// </summary>
public List<IFormFile> CarouselFiles { get; set; } public List<long> CarouselFileIds { get; set; }
} }

View File

@ -26,7 +26,6 @@ public class SysTenantService : IDynamicApiController, ITransient
private readonly SysRoleMenuService _sysRoleMenuService; private readonly SysRoleMenuService _sysRoleMenuService;
private readonly SysConfigService _sysConfigService; private readonly SysConfigService _sysConfigService;
private readonly SysCacheService _sysCacheService; private readonly SysCacheService _sysCacheService;
private readonly SysFileService _sysFileService;
private readonly IEventPublisher _eventPublisher; private readonly IEventPublisher _eventPublisher;
public SysTenantService(SqlSugarRepository<SysTenant> sysTenantRep, public SysTenantService(SqlSugarRepository<SysTenant> sysTenantRep,
@ -43,7 +42,6 @@ public class SysTenantService : IDynamicApiController, ITransient
SysRoleMenuService sysRoleMenuService, SysRoleMenuService sysRoleMenuService,
SysConfigService sysConfigService, SysConfigService sysConfigService,
SysCacheService sysCacheService, SysCacheService sysCacheService,
SysFileService sysFileService,
IEventPublisher eventPublisher) IEventPublisher eventPublisher)
{ {
_sysTenantRep = sysTenantRep; _sysTenantRep = sysTenantRep;
@ -60,7 +58,6 @@ public class SysTenantService : IDynamicApiController, ITransient
_sysRoleMenuService = sysRoleMenuService; _sysRoleMenuService = sysRoleMenuService;
_sysConfigService = sysConfigService; _sysConfigService = sysConfigService;
_sysCacheService = sysCacheService; _sysCacheService = sysCacheService;
_sysFileService = sysFileService;
_eventPublisher = eventPublisher; _eventPublisher = eventPublisher;
} }
@ -571,8 +568,8 @@ public class SysTenantService : IDynamicApiController, ITransient
if (string.IsNullOrWhiteSpace(tenant.Title)) if (string.IsNullOrWhiteSpace(tenant.Title))
tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == SqlSugarConst.DefaultTenantId); 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 forceChangePassword = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysForceChangePassword); // 强制修改密码
var passwordExpirationTime = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysPasswordExpirationTime); // 密码有效期 var passwordExpirationTime = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysPasswordExpirationTime); // 密码有效期
@ -601,7 +598,7 @@ public class SysTenantService : IDynamicApiController, ITransient
ForceChangePassword = forceChangePassword, ForceChangePassword = forceChangePassword,
PasswordExpirationTime = passwordExpirationTime, PasswordExpirationTime = passwordExpirationTime,
PublicKey = publicKey, PublicKey = publicKey,
//CarouselFiles = carouselFiles, CarouselFiles = carouselFiles,
I18NSwitch = i18NSwitch, I18NSwitch = i18NSwitch,
IdleTimeout = idleTimeout, IdleTimeout = idleTimeout,
OnlineNotice = onlineNotice, OnlineNotice = onlineNotice,
@ -616,52 +613,18 @@ public class SysTenantService : IDynamicApiController, ITransient
[DisplayName("保存系统信息")] [DisplayName("保存系统信息")]
public async Task SaveSysInfo(SysInfoInput input) public async Task SaveSysInfo(SysInfoInput input)
{ {
if (input.TenantId < 1) input.TenantId = input.TenantId < 1 ? long.Parse(App.User?.FindFirst(ClaimConst.TenantId)?.Value ?? "0") : input.TenantId;
input.TenantId = long.Parse(App.User?.FindFirst(ClaimConst.TenantId)?.Value ?? "0"); var tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == input.TenantId) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
var tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == input.TenantId);
_ = tenant ?? throw Oops.Oh(ErrorCodeEnum.D1002);
var originLogo = tenant.Logo;
tenant = input.Adapt<SysTenant>(); tenant = input.Adapt<SysTenant>();
tenant.Id = input.TenantId; tenant.Id = input.TenantId;
// logo 不为空才保存 // 先清空轮播图再更新
if (!string.IsNullOrEmpty(input.LogoBase64)) 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)
var oldSysLogoRelativeFilePath = tenant.Logo ?? ""; .Select(u => u.Id).ToListAsync();
var oldSysLogoAbsoluteFilePath = Path.Combine(App.WebHostEnvironment.WebRootPath, oldSysLogoRelativeFilePath.TrimStart('/')); foreach (var fileId in carouselFileIds)
await App.GetRequiredService<SysFileService>().DeleteFile(new BaseIdInput { Id = fileId });
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;
}
await _sysTenantRep.AsUpdateable(tenant).UpdateColumns(u => new await _sysTenantRep.AsUpdateable(tenant).UpdateColumns(u => new
{ {
@ -684,40 +647,4 @@ public class SysTenantService : IDynamicApiController, ITransient
// 更新租户缓存 // 更新租户缓存
await CacheTenant(); 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;
}
} }

View File

@ -2,7 +2,7 @@
"name": "admin.net.pro", "name": "admin.net.pro",
"type": "module", "type": "module",
"version": "2.4.33", "version": "2.4.33",
"lastBuildTime": "2025.07.27", "lastBuildTime": "2025.07.28",
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架", "description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
"author": "zuohuaijun", "author": "zuohuaijun",
"license": "MIT", "license": "MIT",
@ -74,7 +74,7 @@
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-demi": "0.14.10", "vue-demi": "0.14.10",
"vue-draggable-plus": "^0.6.0", "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-grid-layout": "3.0.0-beta1",
"vue-i18n": "^11.1.11", "vue-i18n": "^11.1.11",
"vue-json-pretty": "^2.5.0", "vue-json-pretty": "^2.5.0",
@ -82,8 +82,8 @@
"vue-router": "^4.5.1", "vue-router": "^4.5.1",
"vue-signature-pad": "^3.0.2", "vue-signature-pad": "^3.0.2",
"vue3-tree-org": "^4.2.2", "vue3-tree-org": "^4.2.2",
"vxe-pc-ui": "^4.7.28", "vxe-pc-ui": "^4.7.30",
"vxe-table": "^4.14.7", "vxe-table": "^4.14.8",
"xe-utils": "^3.7.8", "xe-utils": "^3.7.8",
"xlsx-js-style": "^1.2.0" "xlsx-js-style": "^1.2.0"
}, },
@ -96,10 +96,10 @@
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^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", "@vitejs/plugin-vue-jsx": "^5.0.1",
"@vue/compiler-sfc": "^3.5.18", "@vue/compiler-sfc": "^3.5.18",
"code-inspector-plugin": "^1.0.0", "code-inspector-plugin": "^1.0.2",
"eslint": "^9.32.0", "eslint": "^9.32.0",
"eslint-plugin-vue": "^10.3.0", "eslint-plugin-vue": "^10.3.0",
"globals": "^16.3.0", "globals": "^16.3.0",

View File

@ -518,6 +518,65 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
options: localVarRequestOptions, 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 🔖 * @summary Base64 🔖
@ -574,10 +633,11 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
* @param {string} [fileAlias] * @param {string} [fileAlias]
* @param {boolean} [isPublic] * @param {boolean} [isPublic]
* @param {string} [allowSuffix] * @param {string} [allowSuffix]
* @param {number} [dataId]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @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`; const localVarPath = `/api/sysFile/uploadFile`;
// use dummy base URL string because the URL constructor only accepts absolute URLs. // use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com'); const localVarUrlObj = new URL(localVarPath, 'https://example.com');
@ -620,6 +680,10 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
localVarFormParams.append('AllowSuffix', allowSuffix as any); localVarFormParams.append('AllowSuffix', allowSuffix as any);
} }
if (dataId !== undefined) {
localVarFormParams.append('DataId', dataId as any);
}
localVarHeaderParameter['Content-Type'] = 'multipart/form-data'; localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
const query = new URLSearchParams(localVarUrlObj.search); const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) { for (const key in localVarQueryParameter) {
@ -691,6 +755,64 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
options: localVarRequestOptions, 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 🔖 * @summary 🔖
@ -945,6 +1067,21 @@ export const SysFileApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs); 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 🔖 * @summary Base64 🔖
@ -967,11 +1104,12 @@ export const SysFileApiFp = function(configuration?: Configuration) {
* @param {string} [fileAlias] * @param {string} [fileAlias]
* @param {boolean} [isPublic] * @param {boolean} [isPublic]
* @param {string} [allowSuffix] * @param {string} [allowSuffix]
* @param {number} [dataId]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultSysFile>>> { 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, options); const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).apiSysFileUploadFilePostForm(file, fileType, fileAlias, isPublic, allowSuffix, dataId, options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs); return axios.request(axiosRequestArgs);
@ -991,6 +1129,21 @@ export const SysFileApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs); 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 🔖 * @summary 🔖
@ -1129,6 +1282,17 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
async apiSysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSysFile>> { async apiSysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSysFile>> {
return SysFileApiFp(configuration).apiSysFileUploadAvatarPostForm(file, options).then((request) => request(axios, basePath)); 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 🔖 * @summary Base64 🔖
@ -1147,11 +1311,12 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
* @param {string} [fileAlias] * @param {string} [fileAlias]
* @param {boolean} [isPublic] * @param {boolean} [isPublic]
* @param {string} [allowSuffix] * @param {string} [allowSuffix]
* @param {number} [dataId]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSysFile>> { 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, options).then((request) => request(axios, basePath)); 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>> { async apiSysFileUploadFilesPostForm(files?: Array<Blob>, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListSysFile>> {
return SysFileApiFp(configuration).apiSysFileUploadFilesPostForm(files, options).then((request) => request(axios, basePath)); 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 🔖 * @summary 🔖
@ -1304,6 +1480,18 @@ export class SysFileApi extends BaseAPI {
public async apiSysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSysFile>> { public async apiSysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSysFile>> {
return SysFileApiFp(this.configuration).apiSysFileUploadAvatarPostForm(file, options).then((request) => request(this.axios, this.basePath)); 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 🔖 * @summary Base64 🔖
@ -1323,12 +1511,13 @@ export class SysFileApi extends BaseAPI {
* @param {string} [fileAlias] * @param {string} [fileAlias]
* @param {boolean} [isPublic] * @param {boolean} [isPublic]
* @param {string} [allowSuffix] * @param {string} [allowSuffix]
* @param {number} [dataId]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
* @memberof SysFileApi * @memberof SysFileApi
*/ */
public async apiSysFileUploadFilePostForm(file?: Blob, fileType?: string, fileAlias?: string, isPublic?: boolean, allowSuffix?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSysFile>> { 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, options).then((request) => request(this.axios, this.basePath)); 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>> { 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)); 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 🔖 * @summary 🔖

View File

@ -25,7 +25,6 @@ import { AdminNETResultListSysUser } from '../models';
import { AdminNETResultObject } from '../models'; import { AdminNETResultObject } from '../models';
import { AdminNETResultSqlSugarPagedListTenantOutput } from '../models'; import { AdminNETResultSqlSugarPagedListTenantOutput } from '../models';
import { AdminNETResultString } from '../models'; import { AdminNETResultString } from '../models';
import { AdminNETResultSysFile } from '../models';
import { DeleteTenantInput } from '../models'; import { DeleteTenantInput } from '../models';
import { PageTenantInput } from '../models'; import { PageTenantInput } from '../models';
import { RoleMenuInput } from '../models'; import { RoleMenuInput } from '../models';
@ -703,58 +702,6 @@ export const SysTenantApiAxiosParamCreator = function (configuration?: Configura
options: localVarRequestOptions, 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 🔖 * @summary 🔖
@ -1006,20 +953,6 @@ export const SysTenantApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs); 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 🔖 * @summary 🔖
@ -1181,16 +1114,6 @@ export const SysTenantApiFactory = function (configuration?: Configuration, base
async apiSysTenantUpdatePost(body?: UpdateTenantInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> { async apiSysTenantUpdatePost(body?: UpdateTenantInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
return SysTenantApiFp(configuration).apiSysTenantUpdatePost(body, options).then((request) => request(axios, basePath)); 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 🔖 * @summary 🔖
@ -1363,17 +1286,6 @@ export class SysTenantApi extends BaseAPI {
public async apiSysTenantUpdatePost(body?: UpdateTenantInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> { public async apiSysTenantUpdatePost(body?: UpdateTenantInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
return SysTenantApiFp(this.configuration).apiSysTenantUpdatePost(body, options).then((request) => request(this.axios, this.basePath)); 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 🔖 * @summary 🔖

View File

@ -503,7 +503,6 @@ export * from './sys-report-group';
export * from './sys-report-layout-config'; export * from './sys-report-layout-config';
export * from './sys-report-param'; export * from './sys-report-param';
export * from './sys-schedule'; export * from './sys-schedule';
export * from './sys-tenant-upload-carousel-file-body';
export * from './sys-upgrade'; export * from './sys-upgrade';
export * from './sys-user'; export * from './sys-user';
export * from './sys-user-ext-org'; export * from './sys-user-ext-org';
@ -553,7 +552,9 @@ export * from './update-schedule-input';
export * from './update-sys-ldap-input'; export * from './update-sys-ldap-input';
export * from './update-tenant-input'; export * from './update-tenant-input';
export * from './update-user-input'; export * from './update-user-input';
export * from './upload-carousel-tenant-id-body';
export * from './upload-file-from-base64-input'; export * from './upload-file-from-base64-input';
export * from './upload-logo-tenant-id-body';
export * from './user-menu-input'; export * from './user-menu-input';
export * from './user-output'; export * from './user-output';
export * from './user-role-input'; export * from './user-role-input';

View File

@ -59,4 +59,12 @@ export interface SysFileUploadFileBody {
* @memberof SysFileUploadFileBody * @memberof SysFileUploadFileBody
*/ */
allowSuffix?: string; allowSuffix?: string;
/**
* Id
*
* @type {number}
* @memberof SysFileUploadFileBody
*/
dataId?: number;
} }

View File

@ -29,20 +29,12 @@ export interface SysInfoInput {
tenantId?: number; tenantId?: number;
/** /**
* Data URI scheme base64 *
* *
* @type {string} * @type {string}
* @memberof SysInfoInput * @memberof SysInfoInput
*/ */
logoBase64?: string | null; logo?: string | null;
/**
*
*
* @type {string}
* @memberof SysInfoInput
*/
logoFileName?: string | null;
/** /**
* *
@ -149,10 +141,10 @@ export interface SysInfoInput {
secondVer?: boolean; secondVer?: boolean;
/** /**
* * Id集合
* *
* @type {Array<Blob>} * @type {Array<number>}
* @memberof SysInfoInput * @memberof SysInfoInput
*/ */
carouselFiles?: Array<Blob> | null; carouselFileIds?: Array<number> | null;
} }

View File

@ -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>;
}

View File

@ -16,13 +16,13 @@
* *
* *
* @export * @export
* @interface SysTenantUploadCarouselFileBody * @interface UploadLogoTenantIdBody
*/ */
export interface SysTenantUploadCarouselFileBody { export interface UploadLogoTenantIdBody {
/** /**
* @type {Blob} * @type {Blob}
* @memberof SysTenantUploadCarouselFileBody * @memberof UploadLogoTenantIdBody
*/ */
file: Blob; file: Blob;
} }

View File

@ -27,7 +27,7 @@ interface iVxeOption {
sortConfig?: VxeTablePropTypes.SortConfig<any>; sortConfig?: VxeTablePropTypes.SortConfig<any>;
showFooter?: boolean; showFooter?: boolean;
footerData?: VxeTablePropTypes.FooterData; footerData?: VxeTablePropTypes.FooterData;
footerMethod?: VxeTablePropTypes.FooterMethod<D>; footerMethod?: VxeTablePropTypes.FooterMethod<any>;
remoteCustom?: boolean; remoteCustom?: boolean;
} }

View File

@ -23,8 +23,6 @@ import 'vform3-builds/dist/designer.style.css';
import { setupVXETable } from '/@/hooks/setupVXETableHook'; import { setupVXETable } from '/@/hooks/setupVXETableHook';
// 自定义字典组件 // 自定义字典组件
import sysDict from '/@/components/sysDict/sysDict.vue'; import sysDict from '/@/components/sysDict/sysDict.vue';
// AI组件
import ElementPlusX from 'vue-element-plus-x';
// 关闭自动打印 // 关闭自动打印
import { disAutoConnect } from 'vue-plugin-hiprint'; import { disAutoConnect } from 'vue-plugin-hiprint';
disAutoConnect(); disAutoConnect();
@ -36,4 +34,4 @@ other.elSvg(app);
// 注册全局字典组件 // 注册全局字典组件
app.component('GSysDict', sysDict); 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');

View File

@ -100,6 +100,7 @@ declare interface ThemeConfigState {
icp: string; // Icp备案号 icp: string; // Icp备案号
icpUrl: string; // Icp地址 icpUrl: string; // Icp地址
version?: string; // 版本号 version?: string; // 版本号
carouselFiles?: any[]; // 轮播图集合
secondVer?: boolean; // 是否开启二级验证 secondVer?: boolean; // 是否开启二级验证
captcha?: boolean; // 是否开启验证码 captcha?: boolean; // 是否开启验证码
forceChangePassword?: boolean; // 是否开启强制修改密码 forceChangePassword?: boolean; // 是否开启强制修改密码

View File

@ -42,6 +42,8 @@ export async function loadSysInfo(tenantid: number) {
themeConfig.value.copyright = data.copyright; themeConfig.value.copyright = data.copyright;
// 版本号 // 版本号
themeConfig.value.version = data.version; themeConfig.value.version = data.version;
// 轮播图
themeConfig.value.carouselFiles = data.carouselFiles;
// 全局主题 // 全局主题
themeConfig.value.primary = data.themeColor; themeConfig.value.primary = data.themeColor;
// 布局切换 // 布局切换

View File

@ -2,8 +2,7 @@
<div style="flex: 1; background: #f3f4f6; overflow: auto"> <div style="flex: 1; background: #f3f4f6; overflow: auto">
<el-container> <el-container>
<!-- 侧边栏 --> <!-- 侧边栏 -->
<el-aside v-show="!isFold" :class="isFold ? 'sidebar-fold' : 'expand-sidebar'" <el-aside v-show="!isFold" :class="isFold ? 'sidebar-fold' : 'expand-sidebar'" style="background: #f3f4f6; border-right: 1px solid #f0f0f0; display: flex; flex-direction: column">
style="background: #f3f4f6; border-right: 1px solid #f0f0f0; display: flex; flex-direction: column">
<div class="chat-action"> <div class="chat-action">
<el-tooltip :content="$t('message.chat.newChat')" placement="top"> <el-tooltip :content="$t('message.chat.newChat')" placement="top">
<div class="chat-action-item" @click.stop="handleNewChat"> <div class="chat-action-item" @click.stop="handleNewChat">
@ -16,23 +15,28 @@
</div> </div>
</el-tooltip> </el-tooltip>
</div> </div>
<div <div style="display: flex; flex-direction: column; align-items: center; padding-bottom: 8px; padding-top: 0px; margin-top: 0px">
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" />
<el-avatar :size="60" style="background-color: #f3f4f6" src="/chat.png" fit="fill" <div style="margin-top: 10px; font-weight: bold; font-size: 18px; color: #333">{{ $t('message.chat.title') }}</div>
class="avatar-with-shadow" />
<div style="margin-top: 10px; font-weight: bold; font-size: 18px; color: #333">{{
$t('message.chat.title') }}</div>
</div> </div>
<div style="flex: 1; overflow-y: auto; width: 100%"> <div style="flex: 1; overflow-y: auto; width: 100%">
<div style="display: flex; flex-direction: column; gap: 12px; height: 100%"> <div style="display: flex; flex-direction: column; gap: 12px; height: 100%">
<Conversations style="border-radius: 0%; width: 255px" v-model:active="activeHistoryKey" <Conversations
:items="sideBarHistoryList" row-key="key" :show-tooltip="true" showToTopBtn style="border-radius: 0%; width: 255px"
:label-max-width="210" :load-more="loadMoreHistoryItems" v-model:active="activeHistoryKey"
:load-more-loading="isHistoryListLoading" showBuiltInMenu @change="handleChange"> :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 }"> <template #menu="{ item }">
<div class="menu-buttons"> <div class="menu-buttons">
<el-button v-for="menuItem in actionMenuItems" :key="menuItem.key" link <el-button v-for="menuItem in actionMenuItems" :key="menuItem.key" link size="default" @click="handleMenuClick(menuItem.key, item)">
size="default" @click="handleMenuClick(menuItem.key, item)">
<el-icon v-if="menuItem.icon"> <el-icon v-if="menuItem.icon">
<component :is="menuItem.icon" /> <component :is="menuItem.icon" />
</el-icon> </el-icon>
@ -47,7 +51,8 @@
<!-- 主体内容 --> <!-- 主体内容 -->
<el-container> <el-container>
<el-main style=" <el-main
style="
flex: 1; flex: 1;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -60,12 +65,12 @@
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
background: #fff; background: #fff;
"> "
>
<div class="main_action_toolbar" style="position: absolute; top: 0px; width: 500px; left: 20px"> <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"> <div v-if="isFold" class="chat-action-item main_action_item">
<el-tooltip :content="$t('message.chat.expandChat')" placement="top"> <el-tooltip :content="$t('message.chat.expandChat')" placement="top">
<Expand style="width: 1.5em; height: 1.5em; color: #333" <Expand style="width: 1.5em; height: 1.5em; color: #333" @click.stop="handleExpandChat" />
@click.stop="handleExpandChat" />
</el-tooltip> </el-tooltip>
</div> </div>
<div v-if="isFold" class="chat-action-item main_action_item" @click="handleNewChat"> <div v-if="isFold" class="chat-action-item main_action_item" @click="handleNewChat">
@ -83,12 +88,10 @@
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item v-for="item in modellList.models" :key="item.modelName" <el-dropdown-item v-for="item in modellList.models" :key="item.modelName" @click="handleChangeModel(item)">
@click="handleChangeModel(item)">
<div class="model-item"> <div class="model-item">
<span>{{ item.providerName }}/{{ item.modelName }}</span> <span>{{ item.providerName }}/{{ item.modelName }}</span>
<el-icon v-if="item.modelName == modellList.currentModel" <el-icon v-if="item.modelName == modellList.currentModel" style="color: #409eff">
style="color: #409eff">
<Select /> <Select />
</el-icon> </el-icon>
</div> </div>
@ -99,46 +102,35 @@
</div> </div>
</div> </div>
<div v-if="isNew" class="new_chat_title"> <div v-if="isNew" class="new_chat_title">
<div style="margin-top: 60px; font-size: 36px; font-weight: bold; letter-spacing: 2px">Hello, {{ <div style="margin-top: 60px; font-size: 36px; font-weight: bold; letter-spacing: 2px">Hello, {{ userName }}</div>
userName }} <div style="margin-top: 20px; width: 100%; text-align: center; color: #999; font-size: 16px; font-weight: bold; letter-spacing: 2px">
</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') }} {{ $t('message.chat.subTitle') }}
</div> </div>
</div> </div>
<div v-else class="chat_content"> <div v-else class="chat_content">
<BubbleList class="chat_content_list" ref="chatRef" :list="chatList" maxHeight="100%" <BubbleList class="chat_content_list" ref="chatRef" :list="chatList" maxHeight="100%" style="padding: 0 60px 100px 60px" @complete="handleBubbleComplete" :triggerIndices="triggerIndices">
style="padding: 0 60px 100px 60px" @complete="handleBubbleComplete"
:triggerIndices="triggerIndices">
<template #content="{ item }"> <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"/> <Typewriter
<XMarkdown v-if="chatList.length > 0 && chatList[chatList.length - 1].key === item.key && isSenderLoading"
v-else :content="item.content"
:markdown="item.content" :is-markdown="true"
default-theme-mode="light" :typing="true"
class="markdown-body" @finish="handleTypeingComplete"
/> />
<XMarkdown v-else :markdown="item.content" default-theme-mode="light" class="markdown-body" />
</template> </template>
<template #header="{ item }"> <template #header="{ item }">
<div v-if="item.role == 'assistant' && deepThinkingVisible && item.key == chatList[chatList.length - 1].key" <div v-if="item.role == 'assistant' && deepThinkingVisible && item.key == chatList[chatList.length - 1].key" class="header-wrapper">
class="header-wrapper"> <Thinking max-width="100%" buttonWidth="250px" autoCollapse :content="deepThinkingMessage" :status="deepThinkingStatus" backgroundColor="#fff9e6" color="#000">
<Thinking max-width="100%" buttonWidth="250px" autoCollapse
:content="deepThinkingMessage" :status="deepThinkingStatus"
backgroundColor="#fff9e6" color="#000">
<template #label="{ status }"> <template #label="{ status }">
<span v-if="status === 'start'">{{ $t('message.chat.startThinking') <span v-if="status === 'start'">{{ $t('message.chat.startThinking') }}</span>
}}</span> <span v-else-if="status === 'thinking'">{{ $t('message.chat.thinking') }}</span>
<span v-else-if="status === 'thinking'">{{ $t('message.chat.thinking') <span v-else-if="status === 'end'">{{ $t('message.chat.thinkingDone') }}</span>
}}</span> <span v-else-if="status === 'error'">{{ $t('message.chat.thinkingFailed') }}</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>
<template #content="{ content }"> <template #content="{ content }">
<span> <span>
<XMarkdown :markdown="content" default-theme-mode="light" class="vp-raw" /> <XMarkdown :markdown="content" default-theme-mode="light" class="vp-raw" />
</span> </span>
</template> </template>
</Thinking> </Thinking>
@ -146,15 +138,12 @@
</template> </template>
<template #footer="{ item }"> <template #footer="{ item }">
<div v-if="item.role == 'assistant'" class="footer-container"> <div v-if="item.role == 'assistant'" class="footer-container">
<el-button type="info" text :icon="CopyDocument" size="small" <el-button type="info" text :icon="CopyDocument" size="small" @click="handleCopy(item)" />
@click="handleCopy(item)" />
<el-button type="info" text size="small"> <el-button type="info" text size="small">
<el-icon v-if="!isPlaying || playAudioKey != item.key" <el-icon v-if="!isPlaying || playAudioKey != item.key" @click="handlePlay(item)">
@click="handlePlay(item)">
<VideoPlay /> <VideoPlay />
</el-icon> </el-icon>
<el-icon v-if="isPlaying && playAudioKey == item.key" <el-icon v-if="isPlaying && playAudioKey == item.key" @click="handlePause(item)">
@click="handlePause(item)">
<VideoPause /> <VideoPause />
</el-icon> </el-icon>
</el-button> </el-button>
@ -163,9 +152,18 @@
</BubbleList> </BubbleList>
</div> </div>
<div :class="isNew ? 'chat_new_input_style' : 'chat_edit_input_style'"> <div :class="isNew ? 'chat_new_input_style' : 'chat_edit_input_style'">
<sender ref="senderRef" variant="updown" clearable allow-speech :loading="isSenderLoading" <sender
:read-only="isSenderLoading" :auto-size="{ minRows: 1, maxRows: 5 }" v-model="senderInput" ref="senderRef"
@submit="handleSend" :placeholder="$t('message.chat.inputPlaceholder')"> 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> <template #prefix>
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap"> <div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap">
<el-button round plain> <el-button round plain>
@ -174,9 +172,11 @@
</el-icon> </el-icon>
</el-button> </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" 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> <el-icon>
<ElementPlus /> <ElementPlus />
</el-icon> </el-icon>
@ -197,12 +197,10 @@
<div class="ai-rename-icon ai-rename-drag" @mousedown="startDrag"> <div class="ai-rename-icon ai-rename-drag" @mousedown="startDrag">
<el-avatar :size="48" src="/chat.png" /> <el-avatar :size="48" src="/chat.png" />
</div> </div>
<el-input v-model="renameInput" @keyup.enter="confirmRename" ref="renameInputRef" <el-input v-model="renameInput" @keyup.enter="confirmRename" ref="renameInputRef" class="ai-rename-input" />
class="ai-rename-input" />
</div> </div>
<div class="ai-dialog-footer"> <div class="ai-dialog-footer">
<el-button @click="cancelRename" class="ai-btn-cancel">{{ $t('message.chat.cancel') <el-button @click="cancelRename" class="ai-btn-cancel">{{ $t('message.chat.cancel') }}</el-button>
}}</el-button>
<el-button type="primary" @click="confirmRename" class="ai-btn-confirm"> <el-button type="primary" @click="confirmRename" class="ai-btn-confirm">
{{ $t('message.chat.confirm') }} {{ $t('message.chat.confirm') }}
</el-button> </el-button>
@ -222,12 +220,10 @@ import { useI18n } from 'vue-i18n';
import { franc } from 'franc'; import { franc } from 'franc';
import { v4 as uuidv4 } from 'uuid'; 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 { 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 { ConversationItem } from 'vue-element-plus-x/types/components/Conversations/types';
import type { TypewriterInstance } from 'vue-element-plus-x/types/components/Typewriter/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 { getAPI } from '/@/utils/axios-utils';
import { LLMChatApi } from '/@/api-services/api'; import { LLMChatApi } from '/@/api-services/api';
@ -311,11 +307,11 @@ const initSSEConnection = () => {
monitorSSEConnectionHandler = setInterval(() => { monitorSSEConnectionHandler = setInterval(() => {
isSSEConnectionClosed = Date.now() - lastSseConnectionTime > SSE_CONNECTION_TIMEOUT; isSSEConnectionClosed = Date.now() - lastSseConnectionTime > SSE_CONNECTION_TIMEOUT;
if (isSSEConnectionClosed) { if (isSSEConnectionClosed) {
console.log("SSE connection timed out, reconnecting"); console.log('SSE connection timed out, reconnecting');
try { try {
initSSEConnectionCore(); initSSEConnectionCore();
} catch (err) { } catch (err) {
console.log("SSE connection timed out, reconnecting failed", err); console.log('SSE connection timed out, reconnecting failed', err);
} }
} }
}, SSE_CONNECTION_TIMEOUT); }, SSE_CONNECTION_TIMEOUT);
@ -327,7 +323,7 @@ const checkSSEConnectionStatus = () => {
return false; return false;
} }
return true; return true;
} };
// sse // sse
const initSSEConnectionCore = () => { const initSSEConnectionCore = () => {
closeSSEConnection(); closeSSEConnection();
@ -378,12 +374,10 @@ const initSSEConnectionCore = () => {
lastSseConnectionTime = Date.now(); lastSseConnectionTime = Date.now();
}); });
eventSource.onerror = () => { eventSource.onerror = () => {
console.log('SSE connection error'); console.log('SSE connection error');
}; };
eventSource.onopen = (event) => { eventSource.onopen = (event) => {
console.log('SSE connection opened:', event); console.log('SSE connection opened:', event);
}; };
@ -401,10 +395,10 @@ const handleBubbleComplete = (instance: TypewriterInstance, index: number) => {
chatRef?.value?.scrollToBottom(); chatRef?.value?.scrollToBottom();
}; };
const handleTypeingComplete = ()=>{ const handleTypeingComplete = () => {
isSenderLoading.value = false; isSenderLoading.value = false;
chatRef?.value?.scrollToBottom(); chatRef?.value?.scrollToBottom();
} };
//#endregion sse //#endregion sse
const handleSend = async () => { const handleSend = async () => {
@ -688,8 +682,6 @@ const loadMoreHistoryItems = async () => {
isHistoryListLoading.value = false; isHistoryListLoading.value = false;
}; };
const handleMenuClick = (menuKey: string, item: any) => { const handleMenuClick = (menuKey: string, item: any) => {
switch (menuKey) { switch (menuKey) {
case 'rename': case 'rename':
@ -1129,9 +1121,9 @@ onBeforeUnmount(() => {
} }
.vp-raw { .vp-raw {
background-color: #f0f0f0; background-color: #f0f0f0;
padding: 2px 20px; padding: 2px 20px;
border-radius: 4px; border-radius: 4px;
line-height: 2em; line-height: 2em;
} }
.markdown-body { .markdown-body {
font-size: 16px !important; font-size: 16px !important;

View File

@ -10,15 +10,22 @@
</div> </div>
</div> </div>
<el-carousel height="500px" class="login-carousel"> <el-carousel height="500px" class="login-carousel">
<el-carousel-item> <template v-if="themeConfig.carouselFiles == undefined || themeConfig.carouselFiles.length < 1">
<img :src="loginIconTwo" class="login-icon-group-icon" /> <el-carousel-item>
</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="loginIconTwo1" class="login-icon-group-icon" />
<el-carousel-item> </el-carousel-item>
<img :src="loginIconTwo2" 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> </el-carousel>
</div> </div>
<div class="login-right flex"> <div class="login-right flex">
@ -95,6 +102,7 @@ import { Local } from '/@/utils/storage';
// //
import { languageList, getCountryCode } from '/@/i18n'; import { languageList, getCountryCode } from '/@/i18n';
import '/node_modules/flag-icons/css/flag-icons.min.css'; import '/node_modules/flag-icons/css/flag-icons.min.css';
import { template } from 'xe-utils';
// //
const Account = defineAsyncComponent(() => import('/@/views/login/component/account.vue')); const Account = defineAsyncComponent(() => import('/@/views/login/component/account.vue'));

View File

@ -9,7 +9,7 @@
<template #label> <template #label>
<el-icon><ele-School /></el-icon> <el-icon><ele-School /></el-icon>
</template> </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" /> <img v-if="state.sysInfo.logo" :src="state.sysInfo.logo" class="avatar" />
<SvgIcon v-else class="avatar-uploader-icon" name="ele-Plus" :size="28" /> <SvgIcon v-else class="avatar-uploader-icon" name="ele-Plus" :size="28" />
</el-upload> </el-upload>
@ -152,13 +152,21 @@
<template #label> <template #label>
<el-icon><ele-Picture /></el-icon> <el-icon><ele-Picture /></el-icon>
</template> </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-icon><ele-Plus /></el-icon>
</el-upload> </el-upload>
</el-descriptions-item> </el-descriptions-item>
<template #extra> <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> </template>
</el-descriptions> </el-descriptions>
</el-card> </el-card>
@ -182,63 +190,67 @@
</el-row> </el-row>
</div> </div>
</el-dialog> </el-dialog>
<el-dialog v-model="state.dialogImagePreviewVisible">
<img w-full :src="state.dialogImagePreviewUrl" alt="" />
</el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts" name="sysInfoSetting"> <script setup lang="ts" name="sysInfoSetting">
import { nextTick, onMounted, reactive, ref } from 'vue'; import { nextTick, onMounted, reactive, ref } from 'vue';
import { ElMessage, ElMessageBox, UploadInstance } from 'element-plus'; import { ElMessage, UploadFile, UploadFiles, UploadInstance } from 'element-plus';
import { fileToBase64 } from '/@/utils/base64Conver';
import { loadSysInfo } from '/@/utils/sysInfo'; import { loadSysInfo } from '/@/utils/sysInfo';
import chineseColors from '/@/layout/navBars/topBar/colors.json'; import chineseColors from '/@/layout/navBars/topBar/colors.json';
import { getAPI } from '/@/utils/axios-utils'; 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 host = window.location.host;
const uploadRef = ref<UploadInstance>(); const uploadRef = ref<UploadInstance>();
const state = reactive({ const state = reactive({
isLoading: false, isLoading: false,
sysInfo: {} as SysInfoInput | any, // sysInfo: {} as any, //
logoFile: undefined as any, // logo logoFile: undefined as any, // logo
dialogChineseColorVisible: false, // dialogChineseColorVisible: false, //
colorName: '飞燕草蓝', // colorName: '飞燕草蓝', //
dialogImagePreviewVisible: false, // carouselFileList: [] as any, //
dialogImagePreviewUrl: '', //
isDelete: false, //
carouselFileList: [] as any, //
}); });
// //
onMounted(async () => { 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 () => { 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 { try {
state.isLoading = true; 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); await getAPI(SysTenantApi).apiSysTenantSaveSysInfoPost(state.sysInfo);
state.logoFile = undefined;
ElMessage.success('保存成功'); ElMessage.success('保存成功');
//
state.logoFile = undefined;
// //
await loadSysInfo(state.sysInfo.tenantId); await loadSysInfo(state.sysInfo.tenantId);
} finally { } finally {
@ -248,65 +260,27 @@ const saveSysInfo = async () => {
} }
}; };
// // logo
const loadSysInfoData = async () => { const handleUploadLogoChange = (file: any) => {
try { uploadRef.value!.clearFiles();
state.isLoading = true; state.logoFile = file;
const res = await getAPI(SysTenantApi).apiSysTenantSysInfoTenantIdGet(0); state.sysInfo.logo = URL.createObjectURL(state.logoFile.raw); // logo
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;
});
}
}; };
// 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) => { const handleCarouselChange = async (file: UploadFile, files: UploadFiles) => {
await getAPI(SysTenantApi).apiSysTenantUploadCarouselFilePostForm(e.file); state.carouselFileList = files;
}; };
// //
const beforeRemoveCarouselFile = (file: any, fileList: any) => { const handleCarouselRemove = (file: UploadFile) => {
const result = new Promise((resolve, reject) => { let index = state.carouselFileList.indexOf(file);
ElMessageBox.confirm(`确定删除此轮播图?`, '提示', { state.carouselFileList.splice(index, 1);
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 previewCarouselFile = (file: any) => { const handleCarouselPreview = (file: UploadFile) => {
state.dialogImagePreviewUrl = file.url!; window.open(file.url);
state.dialogImagePreviewVisible = true;
}; };
// //