diff --git a/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/AssemblyDataHelper.cs b/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/AssemblyDataHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..bab823e99c24d66498ece65b1d023ddc4f06a73c --- /dev/null +++ b/Yi.Abp.Net8/framework/Yi.Framework.Core/Helper/AssemblyDataHelper.cs @@ -0,0 +1,83 @@ +using System.Collections.Specialized; + +namespace Yi.Framework.Core.Helper; + +/// +/// 数据装配帮助类 +/// +public static class AssemblyDataHelper +{ + /// + /// 装配数据 + /// + /// 数据 + /// 装配键值,键为原始数据列,值为目标列 + /// 装配的原始字典 + /// + public static async Task AssemblyData(List dataList, NameValueCollection keyValues, + Func, Task>> dataDicFunc) + { + HashSet idList = []; + var t = typeof(T); + foreach (var data in dataList) + { + foreach (var m in keyValues.AllKeys) + { + var p = t.GetProperty(m); + if (p != null) + { + var v = p.GetValue(data); + if (v is Guid guid) + { + idList.Add(guid); + } + + continue; + } + + var f = t.GetField(m); + if (f != null) + { + var v = f.GetValue(data); + if (v is Guid g) + { + idList.Add(g); + } + } + } + } + + var userDic = await dataDicFunc.Invoke(idList); + foreach (var data in dataList) + { + foreach (var m in keyValues.AllKeys) + { + var p = t.GetProperty(m); + if (p != null) + { + var v = p.GetValue(data); + if (v is Guid guid) + { + var k = t.GetProperty(keyValues[m]); + var val = userDic[guid]; + k.SetValue(data, val ?? v); + } + + continue; + } + + var f = t.GetField(m); + if (f != null) + { + var v = f.GetValue(data); + if (v is Guid guid1) + { + var k = t.GetField(keyValues[m]); + var value = userDic[guid1]; + k.SetValue(data, value ?? v); + } + } + } + } + } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/framework/Yi.Framework.Ddd.Application.Contracts/IBackUser.cs b/Yi.Abp.Net8/framework/Yi.Framework.Ddd.Application.Contracts/IBackUser.cs new file mode 100644 index 0000000000000000000000000000000000000000..a5d04497e28a9538020025b7873899c86e7fe8f9 --- /dev/null +++ b/Yi.Abp.Net8/framework/Yi.Framework.Ddd.Application.Contracts/IBackUser.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Auditing; + +namespace Yi.Framework.Ddd.Application.Contracts; + +public interface IBackUser : IAuditedObject +{ + /// + /// 创建的用户名 + /// + public string CreateUserNikeName { get; set; } + + /// + /// 更新的用户名 + /// + public string UpdateUserNikeName { get; set; } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IUserService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IUserService.cs index 343765e4ca4bb3715d5dcb832a7b68bb5e21a9ef..88691bec5147c89c28f169b9ad0a6b55c6431da5 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IUserService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IUserService.cs @@ -1,3 +1,4 @@ +using System.Collections.Specialized; using Yi.Framework.Ddd.Application.Contracts; using Yi.Framework.Rbac.Application.Contracts.Dtos.User; @@ -8,5 +9,20 @@ namespace Yi.Framework.Rbac.Application.Contracts.IServices /// public interface IUserService : IYiCrudAppService { + /// + /// 装配返回数据中的创建用户和修改用户 + /// + /// + /// 返回数据列表 + public Task AssemblyBackUser(List dataList) where T : class, IBackUser; + + /// + /// 装配用户,用来处理没有实现IBackUser接口,但需要返回用户信息 + /// 例如 AuditUserId列 对应 AuditUserNikeName这种 + /// + /// + /// + /// key为原始id列,value为填充列 + public Task AssemblyBackUser(List dataList, NameValueCollection keyValues) where T : class; } } diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/UserService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/UserService.cs index 70597f3b071d759ce50072e2bba1aa9cd1ca29cd..e3b5308eb32b6b19369c2feb1228bd7044461b05 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/UserService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/System/UserService.cs @@ -1,3 +1,4 @@ +using System.Collections.Specialized; using Microsoft.AspNetCore.Mvc; using SqlSugar; using TencentCloud.Tcr.V20190924.Models; @@ -8,7 +9,9 @@ using Volo.Abp.EventBus.Local; using Volo.Abp.Users; using Yi.Framework.Bbs.Domain.Shared.Enums; using Yi.Framework.Bbs.Domain.Shared.Etos; +using Yi.Framework.Core.Helper; using Yi.Framework.Ddd.Application; +using Yi.Framework.Ddd.Application.Contracts; using Yi.Framework.Rbac.Application.Contracts.Dtos.User; using Yi.Framework.Rbac.Application.Contracts.IServices; using Yi.Framework.Rbac.Domain.Authorization; @@ -237,5 +240,53 @@ namespace Yi.Framework.Rbac.Application.Services.System { return base.PostImportExcelAsync(input); } + + + [RemoteService(false)] + public async Task AssemblyBackUser(List dataList) where T : class, IBackUser + { + HashSet useridList = []; + foreach (var data in dataList) + { + if (data.CreatorId != null) useridList.Add(data.CreatorId.Value); + if (data.LastModifierId != null) useridList.Add(data.LastModifierId.Value); + } + + var userDic = await GetByNameKeyByIdListAsync(useridList); + foreach (var data in dataList) + { + if (data.CreatorId != null) + { + data.CreateUserNikeName = + userDic.ContainsKey(data.CreatorId.Value) ? userDic[data.CreatorId.Value] : ""; + } + + if (data.LastModifierId != null) + { + data.UpdateUserNikeName = userDic.ContainsKey(data.LastModifierId.Value) + ? userDic[data.LastModifierId.Value] + : ""; + } + } + } + + [RemoteService(false)] + public async Task AssemblyBackUser(List dataList, NameValueCollection keyValues) where T : class + { + await AssemblyDataHelper.AssemblyData(dataList, keyValues, GetByNameKeyByIdListAsync); + } + + /// + /// 用id查询用户,装备配为字典,键为id,值为Nike + /// + /// guid列表 + /// + private async Task> GetByNameKeyByIdListAsync(IEnumerable idList) + { + var users = await _repository.GetListAsync(u => idList.Contains(u.Id)); + Dictionary userDic = new(); + users.ForEach(x => userDic.Add(x.Id, x.Nick)); + return userDic; + } } } \ No newline at end of file diff --git a/Yi.Pure.Vue3/eslint.config.js b/Yi.Pure.Vue3/eslint.config.js index b4d48fbc6d2a31d829b8499eff822f9ce5b4a9a9..844b8bd6a039ae6416874a9d1e9d7d6fc2f0baff 100644 --- a/Yi.Pure.Vue3/eslint.config.js +++ b/Yi.Pure.Vue3/eslint.config.js @@ -8,174 +8,174 @@ import * as parserTypeScript from "@typescript-eslint/parser"; import pluginTypeScript from "@typescript-eslint/eslint-plugin"; export default defineFlatConfig([ - { - ...js.configs.recommended, - ignores: [ - "**/.*", - "dist/*", - "*.d.ts", - "public/*", - "src/assets/**", - "src/**/iconfont/**" - ], - languageOptions: { - globals: { - // index.d.ts - RefType: "readonly", - EmitType: "readonly", - TargetContext: "readonly", - ComponentRef: "readonly", - ElRef: "readonly", - ForDataType: "readonly", - AnyFunction: "readonly", - PropType: "readonly", - Writable: "readonly", - Nullable: "readonly", - NonNullable: "readonly", - Recordable: "readonly", - ReadonlyRecordable: "readonly", - Indexable: "readonly", - DeepPartial: "readonly", - Without: "readonly", - Exclusive: "readonly", - TimeoutHandle: "readonly", - IntervalHandle: "readonly", - Effect: "readonly", - ChangeEvent: "readonly", - WheelEvent: "readonly", - ImportMetaEnv: "readonly", - Fn: "readonly", - PromiseFn: "readonly", - ComponentElRef: "readonly", - parseInt: "readonly", - parseFloat: "readonly" - } - }, - plugins: { - prettier: pluginPrettier - }, - rules: { - ...configPrettier.rules, - ...pluginPrettier.configs.recommended.rules, - "no-debugger": "off", - "no-unused-vars": [ - "error", - { - argsIgnorePattern: "^_", - varsIgnorePattern: "^_" - } - ], - "prettier/prettier": [ - "error", - { - endOfLine: "auto" - } - ] - } - }, - { - files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"], - languageOptions: { - parser: parserTypeScript, - parserOptions: { - sourceType: "module" - } - }, - plugins: { - "@typescript-eslint": pluginTypeScript - }, - rules: { - ...pluginTypeScript.configs.strict.rules, - "@typescript-eslint/ban-types": "off", - "@typescript-eslint/no-redeclare": "error", - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/prefer-as-const": "warn", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-import-type-side-effects": "error", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/consistent-type-imports": [ - "error", - { disallowTypeAnnotations: false, fixStyle: "inline-type-imports" } - ], - "@typescript-eslint/prefer-literal-enum-member": [ - "error", - { allowBitwiseExpressions: true } - ], - "@typescript-eslint/no-unused-vars": [ - "error", - { - argsIgnorePattern: "^_", - varsIgnorePattern: "^_" - } - ] - } - }, - { - files: ["**/*.d.ts"], - rules: { - "eslint-comments/no-unlimited-disable": "off", - "import/no-duplicates": "off", - "unused-imports/no-unused-vars": "off" - } - }, - { - files: ["**/*.?([cm])js"], - rules: { - "@typescript-eslint/no-require-imports": "off", - "@typescript-eslint/no-var-requires": "off" - } - }, - { - files: ["**/*.vue"], - languageOptions: { - globals: { - $: "readonly", - $$: "readonly", - $computed: "readonly", - $customRef: "readonly", - $ref: "readonly", - $shallowRef: "readonly", - $toRef: "readonly" - }, - parser: parserVue, - parserOptions: { - ecmaFeatures: { - jsx: true - }, - extraFileExtensions: [".vue"], - parser: "@typescript-eslint/parser", - sourceType: "module" - } - }, - plugins: { - vue: pluginVue - }, - processor: pluginVue.processors[".vue"], - rules: { - ...pluginVue.configs.base.rules, - ...pluginVue.configs["vue3-essential"].rules, - ...pluginVue.configs["vue3-recommended"].rules, - "no-undef": "off", - "no-unused-vars": "off", - "vue/no-v-html": "off", - "vue/require-default-prop": "off", - "vue/require-explicit-emits": "off", - "vue/multi-word-component-names": "off", - "vue/no-setup-props-reactivity-loss": "off", - "vue/html-self-closing": [ - "error", - { - html: { - void: "always", - normal: "always", - component: "always" - }, - svg: "always", - math: "always" - } - ] - } - } + // { + // ...js.configs.recommended, + // ignores: [ + // "**/.*", + // "dist/*", + // "*.d.ts", + // "public/*", + // "src/assets/**", + // "src/**/iconfont/**" + // ], + // languageOptions: { + // globals: { + // // index.d.ts + // RefType: "readonly", + // EmitType: "readonly", + // TargetContext: "readonly", + // ComponentRef: "readonly", + // ElRef: "readonly", + // ForDataType: "readonly", + // AnyFunction: "readonly", + // PropType: "readonly", + // Writable: "readonly", + // Nullable: "readonly", + // NonNullable: "readonly", + // Recordable: "readonly", + // ReadonlyRecordable: "readonly", + // Indexable: "readonly", + // DeepPartial: "readonly", + // Without: "readonly", + // Exclusive: "readonly", + // TimeoutHandle: "readonly", + // IntervalHandle: "readonly", + // Effect: "readonly", + // ChangeEvent: "readonly", + // WheelEvent: "readonly", + // ImportMetaEnv: "readonly", + // Fn: "readonly", + // PromiseFn: "readonly", + // ComponentElRef: "readonly", + // parseInt: "readonly", + // parseFloat: "readonly" + // } + // }, + // plugins: { + // prettier: pluginPrettier + // }, + // rules: { + // ...configPrettier.rules, + // ...pluginPrettier.configs.recommended.rules, + // "no-debugger": "off", + // "no-unused-vars": [ + // "error", + // { + // argsIgnorePattern: "^_", + // varsIgnorePattern: "^_" + // } + // ], + // "prettier/prettier": [ + // "error", + // { + // endOfLine: "auto" + // } + // ] + // } + // }, + // { + // files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"], + // languageOptions: { + // parser: parserTypeScript, + // parserOptions: { + // sourceType: "module" + // } + // }, + // plugins: { + // "@typescript-eslint": pluginTypeScript + // }, + // rules: { + // ...pluginTypeScript.configs.strict.rules, + // "@typescript-eslint/ban-types": "off", + // "@typescript-eslint/no-redeclare": "error", + // "@typescript-eslint/ban-ts-comment": "off", + // "@typescript-eslint/no-explicit-any": "off", + // "@typescript-eslint/prefer-as-const": "warn", + // "@typescript-eslint/no-empty-function": "off", + // "@typescript-eslint/no-non-null-assertion": "off", + // "@typescript-eslint/no-import-type-side-effects": "error", + // "@typescript-eslint/explicit-module-boundary-types": "off", + // "@typescript-eslint/consistent-type-imports": [ + // "error", + // { disallowTypeAnnotations: false, fixStyle: "inline-type-imports" } + // ], + // "@typescript-eslint/prefer-literal-enum-member": [ + // "error", + // { allowBitwiseExpressions: true } + // ], + // "@typescript-eslint/no-unused-vars": [ + // "error", + // { + // argsIgnorePattern: "^_", + // varsIgnorePattern: "^_" + // } + // ] + // } + // }, + // { + // files: ["**/*.d.ts"], + // rules: { + // "eslint-comments/no-unlimited-disable": "off", + // "import/no-duplicates": "off", + // "unused-imports/no-unused-vars": "off" + // } + // }, + // { + // files: ["**/*.?([cm])js"], + // rules: { + // "@typescript-eslint/no-require-imports": "off", + // "@typescript-eslint/no-var-requires": "off" + // } + // }, + // { + // files: ["**/*.vue"], + // languageOptions: { + // globals: { + // $: "readonly", + // $$: "readonly", + // $computed: "readonly", + // $customRef: "readonly", + // $ref: "readonly", + // $shallowRef: "readonly", + // $toRef: "readonly" + // }, + // parser: parserVue, + // parserOptions: { + // ecmaFeatures: { + // jsx: true + // }, + // extraFileExtensions: [".vue"], + // parser: "@typescript-eslint/parser", + // sourceType: "module" + // } + // }, + // plugins: { + // vue: pluginVue + // }, + // processor: pluginVue.processors[".vue"], + // rules: { + // ...pluginVue.configs.base.rules, + // ...pluginVue.configs["vue3-essential"].rules, + // ...pluginVue.configs["vue3-recommended"].rules, + // "no-undef": "off", + // "no-unused-vars": "off", + // "vue/no-v-html": "off", + // "vue/require-default-prop": "off", + // "vue/require-explicit-emits": "off", + // "vue/multi-word-component-names": "off", + // "vue/no-setup-props-reactivity-loss": "off", + // "vue/html-self-closing": [ + // "error", + // { + // html: { + // void: "always", + // normal: "always", + // component: "always" + // }, + // svg: "always", + // math: "always" + // } + // ] + // } + // } ]); diff --git a/Yi.Pure.Vue3/src/api/account.ts b/Yi.Pure.Vue3/src/api/account.ts index c3afcf621b31ad2d372d30e9fd9cbc7fbb9e0166..4fe8abbbdd41db94f8dc7f2f3688c65e6ac94a5d 100644 --- a/Yi.Pure.Vue3/src/api/account.ts +++ b/Yi.Pure.Vue3/src/api/account.ts @@ -18,6 +18,14 @@ export type LoginResult = { }; }; +export type TenantResult = { + status: number; + data: Array<{ + id: string; + name: string; + }>; +}; + export type UserResult = { status: number; data: { @@ -69,6 +77,11 @@ export const getCodeImg = () => { return http.request("get", "/account/captcha-image"); }; +/** 获取租户信息 */ +export const getTenant = () => { + return http.request("get", "/tenant/select"); +} + /** 刷新`token` */ export const refreshTokenApi = (data?: object) => { return http.request("post", "/refresh-token", { data }); diff --git a/Yi.Pure.Vue3/src/store/modules/user.ts b/Yi.Pure.Vue3/src/store/modules/user.ts index e5f103797c51586759363f8b622b324d8f8128ed..1ba17c1e90dde2148455551194cf29c245f19a79 100644 --- a/Yi.Pure.Vue3/src/store/modules/user.ts +++ b/Yi.Pure.Vue3/src/store/modules/user.ts @@ -15,7 +15,7 @@ import { refreshTokenApi } from "@/api/account"; import { useMultiTagsStoreHook } from "./multiTags"; -import { type DataInfo, setToken, removeToken, userKey } from "@/utils/auth"; +import {type DataInfo, setToken, removeToken, userKey, setTenantId, getTenantId} from "@/utils/auth"; export const useUserStore = defineStore({ id: "pure-user", @@ -38,7 +38,9 @@ export const useUserStore = defineStore({ // 是否勾选了登录页的免登录 isRemembered: false, // 登录页的免登录存储几天,默认7天 - loginDay: 7 + loginDay: 7, + // 租户id + tenantId: getTenantId() }), actions: { /** 存储头像 */ @@ -78,8 +80,9 @@ export const useUserStore = defineStore({ this.loginDay = Number(value); }, /** 登入 */ - async loginByUsername(data) { + async loginByUsername(data:any,tenantId:string) { return new Promise((resolve, reject) => { + setTenantId(tenantId); getLogin(data) .then(data => { if (data.status == 200) { @@ -100,6 +103,7 @@ export const useUserStore = defineStore({ } }) .catch(error => { + this.tenantId=null; reject(error); }); }); diff --git a/Yi.Pure.Vue3/src/store/types.ts b/Yi.Pure.Vue3/src/store/types.ts index d6503d9c424bb1309e66e16e86ef458809627b0f..04b9892faae6ef4d79f0b991e88bb80fa5b8310b 100644 --- a/Yi.Pure.Vue3/src/store/types.ts +++ b/Yi.Pure.Vue3/src/store/types.ts @@ -47,4 +47,5 @@ export type userType = { currentPage?: number; isRemembered?: boolean; loginDay?: number; + tenantId?:string; }; diff --git a/Yi.Pure.Vue3/src/utils/auth.ts b/Yi.Pure.Vue3/src/utils/auth.ts index a8a64a3e03286d577b6e21b6640d0ffbcdff9c50..2d036936ade3944d17d93d0072e875cc2eb24bca 100644 --- a/Yi.Pure.Vue3/src/utils/auth.ts +++ b/Yi.Pure.Vue3/src/utils/auth.ts @@ -23,6 +23,7 @@ export interface DataInfo { export const userKey = "user-info"; export const TokenKey = "authorized-token"; +export const TenantIdKey = 'Tenant-Id' /** * 通过`multiple-tabs`是否在`cookie`中,判断用户是否已经登录系统, * 从而支持多标签页打开已经登录的系统后无需再登录。 @@ -138,3 +139,25 @@ export const hasPerms = (value: string | Array): boolean => { : isIncludeAllChildren(value, permissions); return isAuths ? true : false; }; + +/** + * 获取租户id + */ +export function getTenantId() { + return Cookies.get(TenantIdKey) +} + +/** + * 设置租户id + * @param tenantId 租户id + */ +export function setTenantId(tenantId: string) { + return Cookies.set(TenantIdKey, tenantId) +} + +/** + * 删除租户id + */ +export function removeTenantId() { + return Cookies.remove(TenantIdKey) +} diff --git a/Yi.Pure.Vue3/src/views/login/index.vue b/Yi.Pure.Vue3/src/views/login/index.vue index 7adcc70addb25281599a94f2c3408635673b7747..6b6cdb5281b13ed505387b0993afb7aec79a3370 100644 --- a/Yi.Pure.Vue3/src/views/login/index.vue +++ b/Yi.Pure.Vue3/src/views/login/index.vue @@ -1,29 +1,29 @@