Просмотр исходного кода

!1511 修复部分已知的bug
Merge pull request !1511 from 喵你个汪/next

zuohuaijun 1 год назад
Родитель
Сommit
1f071e7f78

+ 12 - 0
Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs

@@ -318,6 +318,18 @@ public enum ErrorCodeEnum
     /// </summary>
     [ErrorCodeItemMetadata("字典状态错误")]
     D3005,
+    
+    /// <summary>
+    /// 字典编码不能以Enum结尾
+    /// </summary>
+    [ErrorCodeItemMetadata("字典编码不能以Enum结尾")]
+    D3006,
+    
+    /// <summary>
+    /// 禁止修改枚举类型的字典编码
+    /// </summary>
+    [ErrorCodeItemMetadata("禁止修改枚举类型的字典编码")]
+    D3007,
 
     /// <summary>
     /// 菜单已存在

+ 14 - 6
Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs

@@ -33,15 +33,21 @@ public class EnumToDictJob : IJob
         
         using var serviceScope = _scopeFactory.CreateScope();
         var db = serviceScope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
+        
+        var sysEnumService = serviceScope.ServiceProvider.GetRequiredService<SysEnumService>();
+        var sysDictTypeList = GetDictByEnumType(sysEnumService.GetEnumTypeList());
+
+        // 校验枚举类命名规范,字典相关功能中需要通过后缀判断是否为枚举类型
+        foreach (var dictType in sysDictTypeList.Where(x => !x.Code.EndsWith("Enum"))) Log.Warning($"系统枚举转换字典的枚举类名称必须以Enum结尾: {dictType.Code} ({dictType.Name})");
+        sysDictTypeList = sysDictTypeList.Where(x => x.Code.EndsWith("Enum")).ToList();
+
         try
         {
             await db.BeginTranAsync();
-
-            var sysEnumService = serviceScope.ServiceProvider.GetRequiredService<SysEnumService>();
-            var sysDictTypeList = GetDictByEnumType(sysEnumService.GetEnumTypeList());
             var storageable1 = await db.Storageable(sysDictTypeList)
+                .WhereColumns(it => new { it.Code })
+                .SplitInsert(it => !it.Any())
                 .SplitUpdate(it => it.Any())
-                .SplitInsert(_ => true)
                 .ToStorageAsync();
             await storageable1.BulkCopyAsync();
             await storageable1.BulkUpdateAsync();
@@ -49,8 +55,9 @@ public class EnumToDictJob : IJob
             Log.Information($"系统枚举类转字典类型数据: 共{storageable1.TotalList.Count}条");
         
             var storageable2 = await db.Storageable(sysDictTypeList.SelectMany(x => x.Children).ToList())
+                .WhereColumns(it => new { it.DictTypeId, it.Code })
+                .SplitInsert(it => !it.Any())
                 .SplitUpdate(it => it.Any())
-                .SplitInsert(_ => true)
                 .ToStorageAsync();
             await storageable2.BulkCopyAsync();
             await storageable2.BulkUpdateAsync();
@@ -74,6 +81,7 @@ public class EnumToDictJob : IJob
     /// <returns></returns>
     private List<SysDictType> GetDictByEnumType(List<EnumTypeOutput> enumTypeList)
     {
+        var orderNo = 1;
         var list = new List<SysDictType>();
         foreach (var type in enumTypeList)
         {
@@ -86,7 +94,7 @@ public class EnumToDictJob : IJob
             };
             dictType.Children = type.EnumEntities.Select(x => new SysDictData
             {
-                Id = dictType.Id + x.Value + OrderOffset,
+                Id = dictType.Id + orderNo++,
                 DictTypeId = dictType.Id,
                 Name = x.Name,
                 Value = x.Describe,

+ 7 - 3
Admin.NET/Admin.NET.Core/Service/Dict/SysDictTypeService.cs

@@ -71,6 +71,8 @@ public class SysDictTypeService : IDynamicApiController, ITransient
     [DisplayName("添加字典类型")]
     public async Task AddDictType(AddDictTypeInput input)
     {
+        if (input.Code.ToLower().EndsWith("enum")) throw Oops.Oh(ErrorCodeEnum.D3006);
+        
         var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code);
         if (isExist) throw Oops.Oh(ErrorCodeEnum.D3001);
 
@@ -87,10 +89,12 @@ public class SysDictTypeService : IDynamicApiController, ITransient
     [DisplayName("更新字典类型")]
     public async Task UpdateDictType(UpdateDictTypeInput input)
     {
-        var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Id == input.Id);
-        if (!isExist) throw Oops.Oh(ErrorCodeEnum.D3000);
+        var dict = await _sysDictTypeRep.GetFirstAsync(x => x.Id == input.Id);
+        if (dict == null) throw Oops.Oh(ErrorCodeEnum.D3000);
+        
+        if (dict.Code.ToLower().EndsWith("enum") && input.Code != dict.Code) throw Oops.Oh(ErrorCodeEnum.D3007);
 
-        isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code && u.Id != input.Id);
+        var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code && u.Id != input.Id);
         if (isExist) throw Oops.Oh(ErrorCodeEnum.D3001);
 
         _sysCacheService.Remove($"{CacheConst.KeyDict}{input.Code}");

+ 1 - 1
Admin.NET/Admin.NET.Web.Entry/wwwroot/template/index.vue.vm

@@ -2,7 +2,7 @@
 import { ref, reactive, onMounted } from "vue";
 import { auth } from '/@@/utils/authFunction';
 import { ElMessageBox, ElMessage } from "element-plus";
-import { downloadStreamFile } from "/@@/utils/downloadFile";
+import { downloadStreamFile } from "/@@/utils/download";
 @if(Model.PrintType == "custom") {
 @:// 推荐设置操作 width 为 200
 @:import { hiprint } from 'vue-plugin-hiprint';

+ 0 - 28
Web/src/utils/constHelper.ts

@@ -1,28 +0,0 @@
-import type { App } from 'vue';
-import { useUserInfo } from '/@/stores/userInfo';
-
-export function setupConstFilter(app: App) {
-	// 全局过滤器  在vue文件中调用  $filters.codeToName(code,type)
-	app.config.globalProperties.$filters = {
-		codeToName(code: any, type: any) {
-			return codeToName(code, type);
-		},
-	};
-}
-
-// 常量值转换
-export function codeToName(code: any, type: any) {
-	const userStore = useUserInfo();
-	try {
-		const name = userStore.constList.find((x: any) => x.code === type).data.result.find((x: any) => x.code === code)?.name;
-		return name;
-	} catch (error) {
-		return code;
-	}
-}
-
-export function getConstType(type: any) {
-	const userStore = useUserInfo();
-	const constType = userStore.constList.filter((x: any) => x.code === type)[0].data.result;
-	return constType;
-} 

+ 15 - 2
Web/src/utils/download.ts

@@ -1,5 +1,6 @@
 import { AxiosResponseHeaders, RawAxiosResponseHeaders } from 'axios';
 import { dataURLtoBlob, urlToBase64 } from './base64Conver';
+import * as url from "node:url";
 
 /**
  * Download online pictures
@@ -55,7 +56,7 @@ export function downloadByData(data: BlobPart, filename: string, mime?: string,
  * Download file according to file address
  * @param {*} sUrl
  */
-export function downloadByUrl({ url, target = '_blank', fileName }: { url: string; target?: TargetContext; fileName?: string }): boolean {
+export function downloadByUrl({ url, target = '_blank', fileName }: { url: string; target?: string; fileName?: string }): boolean {
 	const isChrome = window.navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
 	const isSafari = window.navigator.userAgent.toLowerCase().indexOf('safari') > -1;
 
@@ -87,7 +88,7 @@ export function downloadByUrl({ url, target = '_blank', fileName }: { url: strin
 	return true;
 }
 
-export function openWindow(url: string, opt?: { target?: TargetContext | string; noopener?: boolean; noreferrer?: boolean }) {
+export function openWindow(url: string, opt?: { target?: string; noopener?: boolean; noreferrer?: boolean }) {
 	const { target = '__blank', noopener = true, noreferrer = true } = opt || {};
 	const feature: string[] = [];
 
@@ -106,3 +107,15 @@ export function getFileName(headers: RawAxiosResponseHeaders | AxiosResponseHead
 	}
 	return fileName;
 }
+
+/**
+ * 文件流下载
+ * @param res
+ * @param fileName 文件名
+ */
+export function downloadStreamFile(res: any, fileName: string | undefined) {
+	const contentType = res.headers['content-type'];
+	fileName = fileName || getFileName(res.headers['content-disposition']);
+	const blob = res.data instanceof Blob ? res.data : new Blob([res.data], { type: contentType });
+	downloadByUrl({ url: window.URL.createObjectURL(blob), fileName });
+}

+ 0 - 31
Web/src/utils/downloadFile.ts

@@ -1,31 +0,0 @@
-/**
- * 下载文件
- * @param url 下载链接
- * @param filename 文件名
- */
-export function downloadFile(url: string, filename: string | undefined = undefined) {
-    const urlSplit = url.split('/');
-    filename = filename || urlSplit[urlSplit.length - 1];
-
-    const link = document.createElement('a');
-    link.setAttribute('download', filename);
-    link.href = url;
-
-    document.body.appendChild(link);
-    link.click();
-
-    document.body.removeChild(link);
-    window.URL.revokeObjectURL(url);
-}
-
-/**
- * 文件流下载
- * @param res
- */
-export function downloadStreamFile(res: any) {
-    const contentType = res.headers['content-type'];
-    const contentDisposition = res.headers['content-disposition'];
-    const filename = decodeURIComponent(contentDisposition.split('; ')[1].split('=')[1])
-    const blob = res.data instanceof Blob ? res.data : new Blob([res.data], { type: contentType });
-    downloadFile(window.URL.createObjectURL(blob), filename);
-}

+ 1 - 1
Web/src/views/system/database/component/genEntity.vue

@@ -22,7 +22,7 @@
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
 						<el-form-item label="基类" prop="baseClassName">
 							<el-select v-model="state.ruleForm.baseClassName" clearable class="w100">
-								<el-option v-for="item in getDictDataByCode('code_gen_base_class') ?? []" :key="item.code" :label="item.value" :value="item.code" />
+								<el-option v-for="item in getDictDataByCode('code_gen_base_class')" :key="item.code" :label="item.value" :value="item.code" />
 							</el-select>
 						</el-form-item>
 					</el-col>

+ 1 - 1
Web/src/views/system/job/component/editJobDetail.vue

@@ -24,7 +24,7 @@
 							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
 								<el-form-item label="创建类型">
 									<el-radio-group v-model="state.ruleForm.createType" :disabled="isEdit">
-										<el-radio :value="item.code" v-show="item.code == JobCreateTypeEnum.NUMBER_0 ? isEdit : true" v-for="(item, index) in getDictDataByCode('JobCreateTypeEnum') ?? []" :key="index">{{item.value}}</el-radio>
+										<el-radio :value="item.code" v-show="item.code == JobCreateTypeEnum.NUMBER_0 ? isEdit : true" v-for="(item, index) in getDictDataByCode('JobCreateTypeEnum')" :key="index">{{item.value}}</el-radio>
 									</el-radio-group>
 								</el-form-item>
 							</el-col>

+ 1 - 1
Web/src/views/system/menu/component/editMenu.vue

@@ -22,7 +22,7 @@
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
 						<el-form-item label="菜单类型" prop="type" :rules="[{ required: true, message: '菜单类型不能为空', trigger: 'blur' }]">
 							<el-radio-group v-model="state.ruleForm.type">
-								<el-radio :value="item.code" v-for="(item, index) in getDictDataByCode('MenuTypeEnum') ?? []" :key="index">{{item.value}}</el-radio>
+								<el-radio :value="item.code" v-for="(item, index) in getDictDataByCode('MenuTypeEnum')" :key="index">{{item.value}}</el-radio>
 							</el-radio-group>
 						</el-form-item>
 					</el-col>

+ 1 - 1
Web/src/views/system/notice/component/editNotice.vue

@@ -17,7 +17,7 @@
 					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						<el-form-item label="类型" prop="type" :rules="[{ required: true, message: '类型不能为空', trigger: 'blur' }]">
 							<el-select v-model="state.ruleForm.type" placeholder="类型" filterable allow-create default-first-option style="width: 100%">
-                <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('NoticeTypeEnum') ?? []" :key="index" />
+                <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('NoticeTypeEnum')" :key="index" />
 							</el-select>
 						</el-form-item>
 					</el-col>

+ 1 - 1
Web/src/views/system/notice/index.vue

@@ -7,7 +7,7 @@
 				</el-form-item>
 				<el-form-item label="类型">
 					<el-select v-model="state.queryParams.type" placeholder="类型" clearable>
-            <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('NoticeTypeEnum') ?? []" :key="index" />
+            <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('NoticeTypeEnum')" :key="index" />
 					</el-select>
 				</el-form-item>
 				<el-form-item>

+ 1 - 1
Web/src/views/system/openAccess/component/generateSign.vue

@@ -24,7 +24,7 @@
 							<el-input v-model="state.ruleForm.url" placeholder="接口请求地址" class="input-with-select" clearable>
 								<template #prepend>
 									<el-select v-model="state.ruleForm.method" placeholder="请求方法" style="width: 100px">
-                    <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('HttpMethodEnum') ?? []" :key="index" />
+                    <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('HttpMethodEnum')" :key="index" />
 									</el-select>
 								</template>
 							</el-input>

+ 1 - 1
Web/src/views/system/org/component/editOrg.vue

@@ -37,7 +37,7 @@
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
 						<el-form-item label="机构类型">
 							<el-select v-model="state.ruleForm.type" filterable clearable class="w100">
-                <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('org_type') ?? []" :key="index" />
+                <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('org_type')" :key="index" />
 							</el-select>
 						</el-form-item>
 					</el-col>

+ 1 - 1
Web/src/views/system/org/index.vue

@@ -15,7 +15,7 @@
 						</el-form-item> -->
 						<el-form-item label="机构类型">
 							<el-select v-model="state.queryParams.type" filterable clearable>
-                <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('org_type') ?? []" :key="index" />
+                <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('org_type')" :key="index" />
 							</el-select>
 						</el-form-item>
 						<el-form-item>

+ 1 - 1
Web/src/views/system/print/component/editPrint.vue

@@ -51,7 +51,7 @@
 					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						<el-form-item label="打印类型">
 							<el-radio-group v-model="state.ruleForm.printType">
-                <el-radio :value="item.code" v-for="(item, index) in getDictDataByCode('PrintTypeEnum') ?? []" :key="index">{{item.value}}</el-radio>
+                <el-radio :value="item.code" v-for="(item, index) in getDictDataByCode('PrintTypeEnum')" :key="index">{{item.value}}</el-radio>
 							</el-radio-group>
 						</el-form-item>
 					</el-col>

+ 1 - 1
Web/src/views/system/role/component/grantData.vue

@@ -12,7 +12,7 @@
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl1="24" class="mb20">
 						<el-form-item label="数据范围:">
 							<el-select v-model="state.ruleForm.dataScope" placeholder="数据范围" style="width: 100%">
-                <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('DataScopeEnum') ?? []" :key="index" />
+                <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('DataScopeEnum')" :key="index" />
 							</el-select>
 						</el-form-item>
 					</el-col>

+ 1 - 1
Web/src/views/system/tenant/component/editTenant.vue

@@ -12,7 +12,7 @@
 					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 						<el-form-item label="租户类型" :rules="[{ required: true, message: '租户类型不能为空', trigger: 'blur' }]">
 							<el-radio-group v-model="state.ruleForm.tenantType" :disabled="state.ruleForm.id != undefined">
-                <el-radio :value="item.code" v-for="(item, index) in getDictDataByCode('TenantTypeEnum') ?? []" :key="index">{{item.value}}</el-radio>
+                <el-radio :value="item.code" v-for="(item, index) in getDictDataByCode('TenantTypeEnum')" :key="index">{{item.value}}</el-radio>
 							</el-radio-group>
 						</el-form-item>
 					</el-col>

+ 4 - 4
Web/src/views/system/user/component/editUser.vue

@@ -42,7 +42,7 @@
 								<el-form-item label="账号类型" prop="accountType" :rules="[{ required: true, message: '账号类型不能为空', trigger: 'blur' }]">
 									<el-select v-model="state.ruleForm.accountType" placeholder="账号类型" collapse-tags collapse-tags-tooltip class="w100">
                     <el-option
-                        v-for="(item, index) in getDictDataByCode('AccountTypeEnum').filter(x => x.name != 'SuperAdmin') ?? []"
+                        v-for="(item, index) in getDictDataByCode('AccountTypeEnum').filter(x => x.name != 'SuperAdmin')"
                         :disabled="item.name == 'SysAdmin' && ![888, 999].some(x => x == userInfos.accountType)"
                         :label="item.value"
                         :value="item.code"
@@ -139,7 +139,7 @@
 							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 								<el-form-item label="证件类型" prop="cardType">
 									<el-select v-model="state.ruleForm.cardType" placeholder="证件类型" class="w100">
-                    <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('CardTypeEnum') ?? []" :key="index" />
+                    <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('CardTypeEnum')" :key="index" />
 									</el-select>
 								</el-form-item>
 							</el-col>
@@ -156,7 +156,7 @@
 							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 								<el-form-item label="性别">
 									<el-radio-group v-model="state.ruleForm.sex">
-                    <el-radio :value="item.code" v-for="(item, index) in getDictDataByCode('GenderEnum') ?? []" :key="index">{{item.value}}</el-radio>
+                    <el-radio :value="item.code" v-for="(item, index) in getDictDataByCode('GenderEnum').sort((a: any, b: any) => a.value.length - b.value.length)" :key="index">{{item.value}}</el-radio>
                   </el-radio-group>
 								</el-form-item>
 							</el-col>
@@ -183,7 +183,7 @@
 							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 								<el-form-item label="文化程度">
 									<el-select v-model="state.ruleForm.cultureLevel" placeholder="文化程度" class="w100">
-                    <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('CultureLevelEnum') ?? []" :key="index" />
+                    <el-option :label="item.value" :value="item.code" v-for="(item, index) in getDictDataByCode('CultureLevelEnum')" :key="index" />
 									</el-select>
 								</el-form-item>
 							</el-col>