From 30de46b84035f63a800d4c3a7c99047015756a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A9=99=E5=AD=90?= <454313500@qq.com> Date: Mon, 11 Dec 2023 22:14:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E9=87=8D=E6=9E=84=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E6=A8=A1=E5=9D=97-=E5=A4=A7=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../YiCrudAppService.cs | 7 +- .../Dtos/Article/ArticleGetListInputVo.cs | 3 +- .../Dtos/Article/ArticleGetListOutputDto.cs | 2 + .../Dtos/Article/ArticleGetOutputDto.cs | 2 + .../Dtos/Plate/PlateCreateInputVo.cs | 2 + .../Dtos/Plate/PlateGetListInputVo.cs | 6 +- .../Dtos/Plate/PlateGetListOutputDto.cs | 4 + .../Dtos/Plate/PlateGetOutputDto.cs | 3 + .../Dtos/Plate/PlateUpdateInputVo.cs | 2 + .../Services/ArticleService.cs | 17 ++ .../Services/PlateService.cs | 19 +- .../Entities/ArticleEntity.cs | 12 +- .../Entities/PlateEntity.cs | 15 +- Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs | 12 +- Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db | Bin 229376 -> 229376 bytes Yi.BBS.Vue3/.env.development | 4 +- Yi.BBS.Vue3/.env.production | 5 +- Yi.BBS.Vue3/src/apis/auth.js | 65 ++++ Yi.BBS.Vue3/src/apis/configApi.js | 14 +- Yi.BBS.Vue3/src/config/axios/ErrorCode.js | 13 + Yi.BBS.Vue3/src/config/axios/config.js | 34 +++ Yi.BBS.Vue3/src/config/axios/service.js | 89 ++++++ Yi.BBS.Vue3/src/hooks/useAuths.js | 138 +++++++++ Yi.BBS.Vue3/src/permission.js | 79 ++--- Yi.BBS.Vue3/src/router/index.js | 90 +++--- Yi.BBS.Vue3/src/stores/user.js | 177 +++++------ Yi.BBS.Vue3/src/utils/file.js | 140 +++++++++ Yi.BBS.Vue3/src/utils/storage.js | 53 ++++ Yi.BBS.Vue3/src/views/Login.vue | 176 ++++++----- Yi.RuoYi.Vue3/src/api/bbs/articleApi.js | 14 +- Yi.RuoYi.Vue3/src/api/bbs/plateApi.js | 47 +++ Yi.RuoYi.Vue3/src/api/template.txt | 3 +- Yi.RuoYi.Vue3/src/views/bbs/plate/index.vue | 282 ++++++++++++++++++ Yi.RuoYi.Vue3/src/views/template.txt | 4 +- 34 files changed, 1249 insertions(+), 284 deletions(-) create mode 100644 Yi.BBS.Vue3/src/apis/auth.js create mode 100644 Yi.BBS.Vue3/src/config/axios/ErrorCode.js create mode 100644 Yi.BBS.Vue3/src/config/axios/config.js create mode 100644 Yi.BBS.Vue3/src/config/axios/service.js create mode 100644 Yi.BBS.Vue3/src/hooks/useAuths.js create mode 100644 Yi.BBS.Vue3/src/utils/file.js create mode 100644 Yi.BBS.Vue3/src/utils/storage.js create mode 100644 Yi.RuoYi.Vue3/src/api/bbs/plateApi.js create mode 100644 Yi.RuoYi.Vue3/src/views/bbs/plate/index.vue diff --git a/Yi.Abp.Net8/framework/Yi.Framework.Ddd.Application/YiCrudAppService.cs b/Yi.Abp.Net8/framework/Yi.Framework.Ddd.Application/YiCrudAppService.cs index 31700d81..ba23f367 100644 --- a/Yi.Abp.Net8/framework/Yi.Framework.Ddd.Application/YiCrudAppService.cs +++ b/Yi.Abp.Net8/framework/Yi.Framework.Ddd.Application/YiCrudAppService.cs @@ -62,6 +62,11 @@ namespace Yi.Framework.Ddd.Application { } + public override Task> GetListAsync(TGetListInput input) + { + throw new NotImplementedException($"【{typeof(TEntity)}】实体的CrudAppService,查询为具体业务,通用查询几乎无实际场景,请重写实现!"); + } + /// /// 偷梁换柱 /// @@ -72,7 +77,7 @@ namespace Yi.Framework.Ddd.Application { await Repository.DeleteManyAsync(id); } - [RemoteService(isEnabled:false)] + [RemoteService(isEnabled: false)] public override Task DeleteAsync(TKey id) { return base.DeleteAsync(id); diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetListInputVo.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetListInputVo.cs index dec2f195..b10278a1 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetListInputVo.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetListInputVo.cs @@ -1,8 +1,9 @@ using Volo.Abp.Application.Dtos; +using Yi.Framework.Ddd.Application.Contracts; namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Article { - public class ArticleGetListInputVo : PagedAndSortedResultRequestDto + public class ArticleGetListInputVo : PagedAllResultRequestDto { public string? Content { get; set; } public string? Name { get; set; } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetListOutputDto.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetListOutputDto.cs index 735dfac5..b63cd27e 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetListOutputDto.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetListOutputDto.cs @@ -10,5 +10,7 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Article public Guid DiscussId { get; set; } public List? Children { get; set; } + + public DateTime CreationTime { get; set; } } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetOutputDto.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetOutputDto.cs index 85825322..0d974c80 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetOutputDto.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Article/ArticleGetOutputDto.cs @@ -8,5 +8,7 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Article public string Name { get; set; } public Guid DiscussId { get; set; } public Guid ParentId { get; set; } + + public DateTime CreationTime { get; set; } } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateCreateInputVo.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateCreateInputVo.cs index 0280902b..143bb793 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateCreateInputVo.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateCreateInputVo.cs @@ -8,5 +8,7 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Plate public string Name { get; set; } public string? Logo { get; set; } public string? Introduction { get; set; } + + public string Code { get; set; } } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetListInputVo.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetListInputVo.cs index b7a6ec3d..b424154c 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetListInputVo.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetListInputVo.cs @@ -1,11 +1,11 @@ using Volo.Abp.Application.Dtos; +using Yi.Framework.Ddd.Application.Contracts; namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Plate { - public class PlateGetListInputVo : PagedAndSortedResultRequestDto + public class PlateGetListInputVo : PagedAllResultRequestDto { public string? Name { get; set; } - public string? Logo { get; set; } - public string? Introduction { get; set; } + public string? Code { get; set; } } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetListOutputDto.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetListOutputDto.cs index 1f2970c7..f3621635 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetListOutputDto.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetListOutputDto.cs @@ -8,5 +8,9 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Plate public string Name { get; set; } public string? Logo { get; set; } public string? Introduction { get; set; } + + public string Code { get; set; } + + public DateTime CreationTime { get; set; } } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetOutputDto.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetOutputDto.cs index 24ba4208..ed0b9ad3 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetOutputDto.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateGetOutputDto.cs @@ -7,5 +7,8 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Plate public string Name { get; set; } public string? Logo { get; set; } public string? Introduction { get; set; } + public string Code { get; set; } + + public DateTime CreationTime { get; set; } } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateUpdateInputVo.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateUpdateInputVo.cs index 16a55e14..d5dfb272 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateUpdateInputVo.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application.Contracts/Dtos/Plate/PlateUpdateInputVo.cs @@ -5,5 +5,7 @@ namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Plate public string? Name { get; set; } public string? Logo { get; set; } public string? Introduction { get; set; } + + public string? Code { get; set; } } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/ArticleService.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/ArticleService.cs index 12fa9001..83400b0d 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/ArticleService.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/ArticleService.cs @@ -3,8 +3,12 @@ using Mapster; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; +using SqlSugar; using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Repositories; using Yi.Framework.Bbs.Application.Contracts.Dtos.Article; +using Yi.Framework.Bbs.Application.Contracts.Dtos.Plate; using Yi.Framework.Bbs.Application.Contracts.IServices; using Yi.Framework.Bbs.Domain.Entities; using Yi.Framework.Bbs.Domain.Repositories; @@ -37,6 +41,19 @@ namespace Yi.Framework.Bbs.Application.Services private IArticleRepository _articleRepository { get; set; } private ISqlSugarRepository _discussRepository { get; set; } private IDiscussService _discussService { get; set; } + + public override async Task> GetListAsync(ArticleGetListInputVo input) + { + RefAsync total = 0; + + var entities = await _articleRepository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name.Contains(input.Name!)) + //.WhereIF(!string.IsNullOrEmpty(input.Code), x => x.Name.Contains(input.Code!)) + .WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + return new PagedResultDto(total, await MapToGetListOutputDtosAsync(entities)); + } + + /// /// 获取文章全部平铺信息 /// diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/PlateService.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/PlateService.cs index b438a99a..6ef7b09b 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/PlateService.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Application/Services/PlateService.cs @@ -1,8 +1,12 @@ +using SqlSugar; +using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Repositories; using Yi.Framework.Bbs.Application.Contracts.Dtos.Plate; using Yi.Framework.Bbs.Application.Contracts.IServices; using Yi.Framework.Bbs.Domain.Entities; using Yi.Framework.Ddd.Application; +using Yi.Framework.Rbac.Application.Contracts.Dtos.Config; +using Yi.Framework.SqlSugarCore.Abstractions; namespace Yi.Framework.Bbs.Application.Services { @@ -12,8 +16,21 @@ namespace Yi.Framework.Bbs.Application.Services public class PlateService : YiCrudAppService, IPlateService { - public PlateService(IRepository repository) : base(repository) + private ISqlSugarRepository _repository; + public PlateService(ISqlSugarRepository repository) : base(repository) { + _repository= repository; + } + + public override async Task> GetListAsync(PlateGetListInputVo input) + { + RefAsync total = 0; + + var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name.Contains(input.Name!)) + .WhereIF(!string.IsNullOrEmpty(input.Code), x => x.Name.Contains(input.Code!)) + .WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + return new PagedResultDto(total, await MapToGetListOutputDtosAsync(entities)); } } } diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/ArticleEntity.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/ArticleEntity.cs index 58eef9c5..21652fc6 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/ArticleEntity.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/ArticleEntity.cs @@ -1,11 +1,12 @@ using SqlSugar; using Volo.Abp; +using Volo.Abp.Auditing; using Volo.Abp.Domain.Entities; namespace Yi.Framework.Bbs.Domain.Entities { [SugarTable("Article")] - public class ArticleEntity : Entity, ISoftDelete + public class ArticleEntity : Entity, ISoftDelete,IAuditedObject { [SugarColumn(ColumnName = "Id", IsPrimaryKey = true)] public override Guid Id { get; protected set; } @@ -23,6 +24,15 @@ namespace Yi.Framework.Bbs.Domain.Entities [SugarColumn(IsIgnore = true)] public List? Children { get; set; } + + + public DateTime CreationTime { get; set; } + + public Guid? CreatorId { get; set; } + + public Guid? LastModifierId { get; set; } + + public DateTime? LastModificationTime { get; set; } } public static class ArticleEntityExtensions diff --git a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/PlateEntity.cs b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/PlateEntity.cs index 693690ff..3d2f171e 100644 --- a/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/PlateEntity.cs +++ b/Yi.Abp.Net8/module/bbs/Yi.Framework.Bbs.Domain/Entities/PlateEntity.cs @@ -1,18 +1,31 @@ using SqlSugar; using Volo.Abp.Domain.Entities; using Volo.Abp; +using Volo.Abp.Auditing; namespace Yi.Framework.Bbs.Domain.Entities { [SugarTable("Plate")] - public class PlateEntity : Entity, ISoftDelete + public class PlateEntity : Entity, ISoftDelete,IAuditedObject { [SugarColumn(ColumnName = "Id", IsPrimaryKey = true)] public override Guid Id { get; protected set; } + + public string Code { get; set; } public string Name { get; set; } public string? Logo { get; set; } public string? Introduction { get; set; } public bool IsDeleted { get; set; } + + + + public DateTime CreationTime { get; set; } + + public Guid? CreatorId { get; set; } + + public Guid? LastModifierId { get; set; } + + public DateTime? LastModificationTime { get; set; } } } diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index fa8a7da3..faa20eee 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -55,7 +55,7 @@ namespace Yi.Abp.Web //动态Api Configure(options => { - options.ConventionalControllers.Create(typeof(YiAbpApplicationModule).Assembly,options=>options.RemoteServiceName="default"); + options.ConventionalControllers.Create(typeof(YiAbpApplicationModule).Assembly, options => options.RemoteServiceName = "default"); options.ConventionalControllers.Create(typeof(YiFrameworkRbacApplicationModule).Assembly, options => options.RemoteServiceName = "rbac"); options.ConventionalControllers.Create(typeof(YiFrameworkBbsApplicationModule).Assembly, options => options.RemoteServiceName = "bbs"); }); @@ -63,7 +63,6 @@ namespace Yi.Abp.Web //设置api格式 service.AddControllers().AddNewtonsoftJson(options => { - // options.SerializerSettings.Converters.Add(new DateTimeJsonConverter("yyyy-MM-dd HH:mm:ss")); options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; }); Configure(options => @@ -76,7 +75,10 @@ namespace Yi.Abp.Web }); //Swagger - context.Services.AddYiSwaggerGen(); + context.Services.AddYiSwaggerGen(options => + { + options.SwaggerDoc("default", new OpenApiInfo { Title = "Yi.Framework.Abp", Version = "v1",Description="集大成者" }); + }); //跨域 context.Services.AddCors(options => @@ -145,10 +147,10 @@ namespace Yi.Abp.Web app.UseRouting(); - + //跨域 app.UseCors(DefaultCorsPolicyName); - + //鉴权 app.UseAuthentication(); diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db b/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db index a244c81284affd37ea0c8386708077c5c15ff2ed..df0255e71257b64e2a8951edd9a9783de1b4dc33 100644 GIT binary patch delta 1654 zcmcJPO>7%Q6o6;fC0_43yO@wzZ78)>qc&~F*_oZ0U0V=|=0Z`CpveNNsL1YmoLUVr zu^kc-AaSb}aVSX@fu%+jKcxp$kpwJZ5CVcKaYO0>Bo0U+h>{yZ>LnKhYsVp>0^|#; zx1+b3H}8G(_PsYVqt48zbKQ|X0D$GcTTlGb5ku(vaOl0+J&IfeT3Hj-v+cjPzZ~8O zPbwQqQNAq~tMR*S`O#Mt+3t@V*cXlW0KySBsDq$kx(+l5LeQg_K*Q0q%r!XShJkf# zK!hPeF^tkUmBut_A%+m0e`tYKZ(okwwMS9=fI6q0QinC7T~Pb9CG{swQ9sqbuf}6B z6&?)OLMyS9y5amEmlM_5#qmgBP`o%FT(L#**O*K@KN zrkl|9hyy*7S&(H+uSriG*YU_;-7tkYGdzMJ*BOB>aVhi|+cG4sgRJa8-OwXuh7*#7 zX4XRxyIvL=u0fj~iDM$>oTwX~3*V@PZwMO?c7;MyKzl@8R6E*_hnK<;rAK};H07t- z2|ZPQda%=$`fUKD&;uc8@9mbP5aQ@Dni3Mzzr6YC`t`Th z7vEW*yWqOTv{NYMyb+!r$rVej+`~J~y)$lpvU$#T(c4sebE6qPS$=$A2YipcJ6eZ7 z44TQb{xh39=~0>(X_`tR-LfdAP5LWe-nvrRrhk6KDYee-j+aXL(Z-oNcSC7X^AkSx z!?xWoggPW(cXZaN0k|V0z5n=Y@1VwMf|3|vmfB2t<*R7{b~W0-zmblTIx;A>uyge2 z!2Hc;6G3-;xYy^YMZ)55SNDSj5CJ1VSx|1tC#4^yYTM`1OM#!;UKdvb?+e$&kJ<~B z#IiWCOS%qLYi})!VM&xC%FV+ynmV9k%BVRa{8H$aM=ElZ(F2hGlxonOX z{N3YDvGiCz)A;-slge5`xVLlLXV2LY7>q`v!g;fDZCU(wx*jB-@uR-r6ui?;;UK28 zXDjmmg0xNTIi#a!itVI~6 zkT45DL}&_9_E(QnE5i+)PmJ-giNe@Kb4A#g82fv5P%DjDk|D-S(`eN6$#R#V*qvaX zwATQ3KeGrS%pef!DGmv7ILw;BEU`_qn6*{^I-TLRjWKWQWSRqcuTaBAg-;(>r2Y6bCKJ@zLNR@Y?! delta 281 zcmV+!0p|XIfDV9w4v-rWFaQ7mH~;_u00000000007?CV90Wh&(q>mU13iALDfe!)> zf(`u*eGK>vcnkOoa|-jbArRLJv-6q}1sDnrX8;b64_yvt4>S*k53dg54+{>t57@I2 zFiQ>uC<9dilmD6|vyZNI5d%>J2$KiERg+%890Cl@vw*-20+TQhfrALcw+O=l{5%i_ z3=seiNDi(J6AerZuM82hAt3z=w-F2i@S`ILs{j!IYybA3 { + // 在发送请求之前做些什么 token + const token = getToken(); + if (token) { + config.headers["Authorization"] = `Bearer ${token}`; + } + if (Session.get("tenantId")) { + config.headers["TenantId"] = Session.get("tenantId"); + } + return config; + }, + (error) => { + // 对请求错误做些什么 + return Promise.reject(error); + } +); + +// 添加响应拦截器 +service.interceptors.response.use( + (response) => { + return Promise.resolve(response); + }, + (error) => { + // 对响应错误做点什么 + if (error.message.indexOf("timeout") != -1) { + ElMessage({ + type: "danger", + message: "网络超时", + }); + } else if (error.message == "Network Error") { + ElMessage({ + type: "danger", + message: "网络连接错误", + }); + } else { + const res = error.response || {}; + const status = Number(res.status) || 200; + const message = res.data.error.message; + if (status === 401) { + ElMessage({ + type: "danger", + message, + }); + return; + } + if (status !== 200) { + if (status >= 500) { + ElMessage({ + type: "danger", + message: "网络开小差了,请稍后再试", + }); + return Promise.reject(new Error(message)); + } + // 避开找不到后端接口的提醒 + if (status !== 404) { + ElMessage({ + type: "danger", + message, + }); + } + } + } + return Promise.reject(new Error(error)); + } +); + +// 导出 axios 实例 +export default service; diff --git a/Yi.BBS.Vue3/src/hooks/useAuths.js b/Yi.BBS.Vue3/src/hooks/useAuths.js new file mode 100644 index 00000000..38552666 --- /dev/null +++ b/Yi.BBS.Vue3/src/hooks/useAuths.js @@ -0,0 +1,138 @@ +import { ElMessage, ElMessageBox } from "element-plus"; +import useUserStore from "@/stores/user"; +import router from "@/router"; +import { Session, Local } from "@/utils/storage"; +import { userLogin, getUserDetailInfo, userLogout } from "@/apis/auth"; + +const TokenKey = "AccessToken"; +export const AUTH_MENUS = "AUTH_MENUS"; +export const AUTH_USER = "AUTH_USER"; + +export default function useAuths(opt) { + const defaultOpt = { + loginUrl: "/login", // 登录页跳转url 默认: /login + loginReUrl: "", // 登录页登陆成功后带重定向redirect=的跳转url 默认为空 + homeUrl: "/index", // 主页跳转url 默认: /index + otherQuery: {}, // 成功登录后携带的(除redirect外)其他参数 + }; + + let option = { + ...defaultOpt, + ...opt, + }; + + // 获取token + const getToken = () => { + return Session.get(TokenKey); + }; + + // 存储token到cookies + const setToken = (token) => { + if (token == null) { + return false; + } + Session.set(TokenKey, token); + return true; + }; + + // 删除token + const removeToken = () => { + Session.remove(TokenKey); + return true; + }; + + // 退出登录 + const logoutFun = async () => { + let flag = true; + try { + await userLogout().then((res) => { + ElMessage({ + message: "退出成功", + type: "info", + duration: 2000, + }); + }); + } catch (error) { + flag = await ElMessageBox.confirm( + `退出登录失败,是否强制退出?`, + "提示", + { + confirmButtonText: "确 定", + cancelButtonText: "取 消", + type: "warning", + } + ) + .then(() => { + return true; + }) + .catch(() => { + //取消 + return false; + }); + } + if (flag) { + clearStorage(); + } + }; + + // 清空本地存储的信息 + const clearStorage = () => { + Session.clear(); + Local.clear(); + removeToken(); + window.location.reload(); + Session.set("vuex", null); + }; + + // 用户名密码登录 + const loginFun = async (params) => { + const res = await userLogin(params); + ElMessage({ + message: `您好${params.userName},登录成功!`, + type: "success", + }); + loginSuccess(res); + return res; + }; + + // 获取用户基本信息、角色、菜单权限 + const getUserInfo = async () => { + try { + let { data } = await getUserDetailInfo(); + // useUserStore + // store.dispatch("updateUserInfo", result); + return data; + } catch (error) { + return {}; + } + }; + + // 登录成功之后的操作 + const loginSuccess = async (res) => { + const { token } = res.data; + + setToken(token); + try { + // 存储用户信息 + await getUserInfo(); // 用户信息 + // 登录成功后 路由跳转 + router.replace({ + path: option.loginReUrl ? option.loginReUrl : option.homeUrl, + query: option.otherQuery, + }); + } catch (error) { + removeToken(); + return false; + } + }; + + return { + getToken, + setToken, + removeToken, + loginFun, + getUserInfo, + logoutFun, + clearStorage, + }; +} diff --git a/Yi.BBS.Vue3/src/permission.js b/Yi.BBS.Vue3/src/permission.js index aa0af493..01e51f38 100644 --- a/Yi.BBS.Vue3/src/permission.js +++ b/Yi.BBS.Vue3/src/permission.js @@ -1,65 +1,52 @@ -import router from './router' -import { ElMessage } from 'element-plus' -import NProgress from 'nprogress' -import 'nprogress/nprogress.css' -import { getToken } from '@/utils/auth' -import { isRelogin } from '@/utils/request' -import useUserStore from '@/stores/user' - +import router from "./router"; +import useAuths from "@/hooks/useAuths"; +import { ElMessage } from "element-plus"; +import NProgress from "nprogress"; +import "nprogress/nprogress.css"; +import useUserStore from "@/stores/user"; NProgress.configure({ showSpinner: false }); - -const whiteList = ['/login', '/auth-redirect', '/bind', '/register']; +const { getToken, logoutFun } = useAuths(); +const whiteList = ["/login", "/auth-redirect", "/bind", "/register"]; router.beforeEach((to, from, next) => { - NProgress.start() - if (getToken()) { - // to.meta.title && useSettingsStore().setTitle(to.meta.title) - /* has token*/ - if (to.path === '/login') { - next({ path: '/' }) - NProgress.done() + NProgress.start(); + const hasToken = getToken(); + if (hasToken) { + if (to.path === "/login") { + // 已经登陆跳转到首页 + next({ path: "/" }); + NProgress.done(); } else { - - if (useUserStore().roles.length === 0) - { - isRelogin.show = true + if (useUserStore().roles.length === 0) { // 判断当前用户是否已拉取完user_info信息 - useUserStore().getInfo().then(() => { - isRelogin.show = false - //这里不需要动态路由 - // usePermissionStore().generateRoutes().then(accessRoutes => { - // // 根据roles权限生成可访问的路由表 - // accessRoutes.forEach(route => { - // if (!isHttp(route.path)) { - // router.addRoute(route) // 动态添加可访问路由表 - // } - // }) - // next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 - // }) - next({ ...to, replace: true }) - }).catch(err => { - useUserStore().logOut().then(() => { - ElMessage.error(err) - next({ path: '/' }) + useUserStore() + .getInfo() + .then(() => { + next({ ...to, replace: true }); }) - }) + .catch((err) => { + logoutFun.then(() => { + ElMessage.error(err); + next({ path: "/" }); + }); + }); } else { - next() + next(); } } } else { // 没有token if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 - next() + next(); } else { - next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 - NProgress.done() + next(`/login?redirect=${to.path}&unTourist=true`); // 否则全部重定向到登录页 + NProgress.done(); } } -}) +}); router.afterEach(() => { - NProgress.done() -}) + NProgress.done(); +}); diff --git a/Yi.BBS.Vue3/src/router/index.js b/Yi.BBS.Vue3/src/router/index.js index e5a40141..efd0a79d 100644 --- a/Yi.BBS.Vue3/src/router/index.js +++ b/Yi.BBS.Vue3/src/router/index.js @@ -1,76 +1,74 @@ -import { createRouter, createWebHistory } from 'vue-router' -import Layout from '../layout/Index.vue' -import NotFound from '../views/error/404.vue' -import LoginLayout from '../layout/LoginLayout.vue' +import { createRouter, createWebHistory } from "vue-router"; +import Layout from "../layout/Index.vue"; +import NotFound from "../views/error/404.vue"; +import LoginLayout from "../layout/LoginLayout.vue"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), scrollBehavior(to, from, savedPosition) { // 始终滚动到顶部 - return { top: 0 } + return { top: 0 }; }, routes: [ { - name:'test', - path: '/test', - component: () => import('../views/Test.vue') + name: "test", + path: "/test", + component: () => import("../views/Test.vue"), }, { - - path: '/loginLayout', - name: 'loginLayout', + path: "/loginLayout", + name: "loginLayout", component: LoginLayout, - redirect: '/login' , - children :[ + redirect: "/login", + children: [ { - name:'login', - path: '/login', - component: () => import('../views/Login.vue') + name: "login", + path: "/login", + component: () => import("../views/Login.vue"), }, { - name:'register', - path: '/register', - component: () => import('../views/Register.vue') + name: "register", + path: "/register", + component: () => import("../views/Register.vue"), }, - ] + ], }, { - path: '/', - name: 'layout', + path: "/", + name: "layout", component: Layout, - redirect: '/index' , - children :[ + redirect: "/index", + children: [ { - name:'index', - path: '/index', - component: () => import('../views/Index.vue') + name: "index", + path: "/index", + component: () => import("../views/Index.vue"), }, { - name:'article', - path: '/article/:discussId/:articleId?', - component: () => import('../views/Article.vue') + name: "article", + path: "/article/:discussId/:articleId?", + component: () => import("../views/Article.vue"), }, { - name:'discuss', - path: '/discuss/:plateId?', - component: () => import('../views/Discuss.vue') + name: "discuss", + path: "/discuss/:plateId?", + component: () => import("../views/Discuss.vue"), }, { //artType:discuss主题、article文章 //operType:create创建、update更新 - name:'editArt', - path:'/editArt', - component:()=>import('../views/EditArticle.vue') + name: "editArt", + path: "/editArt", + component: () => import("../views/EditArticle.vue"), }, { - name:'profile', - path:'/profile', - component:()=>import('../views/profile/Index.vue') - - } - ] + name: "profile", + path: "/profile", + component: () => import("../views/profile/Index.vue"), + }, + ], }, - { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound }, - ] -}) + { path: "/:pathMatch(.*)*", name: "NotFound", component: NotFound }, + ], +}); -export default router +export default router; diff --git a/Yi.BBS.Vue3/src/stores/user.js b/Yi.BBS.Vue3/src/stores/user.js index ea55f132..744a944b 100644 --- a/Yi.BBS.Vue3/src/stores/user.js +++ b/Yi.BBS.Vue3/src/stores/user.js @@ -1,98 +1,109 @@ -import { login, logout, getInfo,register } from '@/apis/accountApi' -import { getToken, setToken, removeToken } from '@/utils/auth' -import { defineStore } from 'pinia' -const useUserStore = defineStore('user', -{ - state: () => ({ - id:'', - token: getToken(), - name: '游客', - userName:'', - icon: null, - roles: [], - permissions: [] - }), - getters: { - }, - actions: { - // 登录 - login(userInfo) { - const userName = userInfo.userName.trim() - const password = userInfo.password - const code = userInfo.code - const uuid = userInfo.uuid - return new Promise((resolve, reject) => { - login(userName, password, code, uuid).then(response => { - const res=response.data; +import { login, logout, getInfo, register } from "@/apis/accountApi"; +import { getUserDetailInfo } from "@/apis/auth"; +import useAuths from "@/hooks/useAuths"; +import { defineStore } from "pinia"; + +const { getToken, setToken, removeToken } = useAuths(); + +const useUserStore = defineStore("user", { + state: () => ({ + id: "", + token: getToken(), + name: "游客", + userName: "", + icon: null, + roles: [], + permissions: [], + }), + getters: {}, + actions: { + // 登录 + login(userInfo) { + const userName = userInfo.userName.trim(); + const password = userInfo.password; + const code = userInfo.code; + const uuid = userInfo.uuid; + return new Promise((resolve, reject) => { + login(userName, password, code, uuid) + .then((response) => { + const res = response.data; setToken(res.token); this.token = res.token; resolve(response); - }).catch(error => { - reject(error) }) - }) - }, - // 获取用户信息 - getInfo() { - return new Promise((resolve, reject) => { - getInfo().then(response => { - const res=response.data; - const user = res.user - const avatar = (user.icon == "" || user.icon == null) ? "/src/assets/logo.ico" : import.meta.env.VITE_APP_BASEAPI + "/file/"+user.icon; - - if (res.roleCodes && res.roleCodes.length > 0) { // 验证返回的roles是否是一个非空数组 - this.roles = res.roleCodes - this.permissions = res.permissionCodes + .catch((error) => { + reject(error); + }); + }); + }, + // 获取用户信息 + getInfo() { + return new Promise((resolve, reject) => { + getUserDetailInfo() + .then((response) => { + const res = response.data; + const user = res.user; + const avatar = + user.icon == "" || user.icon == null + ? "/src/assets/logo.ico" + : import.meta.env.VITE_APP_BASEAPI + "/file/" + user.icon; + + if (res.roleCodes && res.roleCodes.length > 0) { + // 验证返回的roles是否是一个非空数组 + this.roles = res.roleCodes; + this.permissions = res.permissionCodes; // this.roles = ["admin"]; // this.permissions=["*:*:*"] - } else { - this.roles = ['ROLE_DEFAULT'] + this.roles = ["ROLE_DEFAULT"]; } // this.roles = ["admin"]; // this.permissions=["*:*:*"] - this.name = user.nick + this.name = user.nick; this.icon = avatar; - this.userName=user.userName; - this.id=user.id; - resolve(res) - }).catch(error => { - reject(error) + this.userName = user.userName; + this.id = user.id; + resolve(res); }) - }) - }, - // 退出系统 - logOut() { - return new Promise((resolve, reject) => { - logout().then(() => { - this.token = '' - this.roles = [] - this.permissions = [] - removeToken() - resolve() - }).catch(error => { - reject(error) + .catch((error) => { + reject(error); + }); + }); + }, + // 退出系统 + logOut() { + return new Promise((resolve, reject) => { + logout() + .then(() => { + this.token = ""; + this.roles = []; + this.permissions = []; + removeToken(); + resolve(); }) - }) - }, - // 注册 - register(userInfo) { - const userName = userInfo.userName.trim() - const password = userInfo.password.trim() - const phone = userInfo.phone; - const uuid = userInfo.uuid; - const code=userInfo.code; - return new Promise((resolve, reject) => { - register(userName,password,phone,code,uuid).then(response => { - resolve(response); - }).catch(error => { - reject(error) - }) - }) - }, - - }, -}) + .catch((error) => { + reject(error); + }); + }); + }, + // 注册 + register(userInfo) { + const userName = userInfo.userName.trim(); + const password = userInfo.password.trim(); + const phone = userInfo.phone; + const uuid = userInfo.uuid; + const code = userInfo.code; + return new Promise((resolve, reject) => { + register(userName, password, phone, code, uuid) + .then((response) => { + resolve(response); + }) + .catch((error) => { + reject(error); + }); + }); + }, + }, +}); export default useUserStore; - \ No newline at end of file diff --git a/Yi.BBS.Vue3/src/utils/file.js b/Yi.BBS.Vue3/src/utils/file.js new file mode 100644 index 00000000..5d65a68a --- /dev/null +++ b/Yi.BBS.Vue3/src/utils/file.js @@ -0,0 +1,140 @@ +/** + * 根据后缀判断文件类型 + * @param {string} fileName 文件后缀名 + * @returns + */ +export function matchType(fileName) { + // 后缀获取 + let suffix = ""; + // 获取类型结果 + let result = ""; + try { + let flieArr = fileName.split("."); + suffix = flieArr[flieArr.length - 1]; + } catch (err) { + suffix = ""; + } + // fileName无后缀返回 false + if (!suffix) { + result = false; + return result; + } + // 图片格式 + let imglist = ["png", "jpg", "jpeg", "bmp", "gif"]; + // 进行图片匹配 + result = imglist.some(function (item) { + return item == suffix; + }); + if (result) { + result = "image"; + return result; + } + // 匹配txt + let txtlist = ["txt"]; + result = txtlist.some(function (item) { + return item == suffix; + }); + if (result) { + result = "txt"; + return result; + } + // 匹配 excel + let excelist = ["xls", "xlsx"]; + result = excelist.some(function (item) { + return item == suffix; + }); + if (result) { + result = "excel"; + return result; + } + // 匹配 word + let wordlist = ["doc", "docx"]; + result = wordlist.some(function (item) { + return item == suffix; + }); + if (result) { + result = "word"; + return result; + } + // 匹配 pdf + let pdflist = ["pdf"]; + result = pdflist.some(function (item) { + return item == suffix; + }); + if (result) { + result = "pdf"; + return result; + } + // 匹配 ppt + let pptlist = ["ppt"]; + result = pptlist.some(function (item) { + return item == suffix; + }); + if (result) { + result = "ppt"; + return result; + } + // 匹配 视频 + let videolist = ["mp4", "m2v", "mkv"]; + result = videolist.some(function (item) { + return item == suffix; + }); + if (result) { + result = "video"; + return result; + } + // 匹配 音频 + let radiolist = ["mp3", "wav", "wmv"]; + result = radiolist.some(function (item) { + return item == suffix; + }); + if (result) { + result = "radio"; + return result; + } + // 其他 文件类型 + result = "other"; + return result; +} + +/** + * url处理 + * @param {string} path url路径 + * @returns + */ +export function convertToUrl(path) { + // 替换反斜杠为正斜杠 + const normalizedPathWithSlashes = path.replace(/\\/g, "/"); + // 去掉开始的点号和反斜杠 + const removedDotsAndSlashes = normalizedPathWithSlashes.replace(/^\.\//, ""); + // 添加斜杠作为根路径 + const url = `/${removedDotsAndSlashes}`; + return url; +} + +/** + * 下载文件 + * + * @param {*} path 下载地址/下载请求地址。 + * @param {string} name 下载文件的名字(考虑到兼容性问题,最好加上后缀名 + */ +export const downLoadFile = (path, name) => { + const link = document.createElement("a"); + link.href = path; + link.download = name; + + if (isMobileDevice()) { + link.target = "_blank"; + link.rel = "noopener noreferrer"; + } + + link.style.display = "none"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +}; + +// 判断是否移动设备 +export function isMobileDevice() { + return /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent); +} diff --git a/Yi.BBS.Vue3/src/utils/storage.js b/Yi.BBS.Vue3/src/utils/storage.js new file mode 100644 index 00000000..fe57badc --- /dev/null +++ b/Yi.BBS.Vue3/src/utils/storage.js @@ -0,0 +1,53 @@ +/** + * window.localStorage 浏览器永久缓存 + * @method set 设置永久缓存 + * @method get 获取永久缓存 + * @method remove 移除永久缓存 + * @method clear 移除全部永久缓存 + */ +export const Local = { + // 设置永久缓存 + set(key, val) { + window.localStorage.setItem(key, JSON.stringify(val)); + }, + // 获取永久缓存 + get(key) { + let json = window.localStorage.getItem(key); + return JSON.parse(json); + }, + // 移除永久缓存 + remove(key) { + window.localStorage.removeItem(key); + }, + // 移除全部永久缓存 + clear() { + window.localStorage.clear(); + }, +}; + +/** + * window.sessionStorage 浏览器会话临时缓存 + * @method set 设置临时缓存 + * @method get 获取临时缓存 + * @method remove 移除临时缓存 + * @method clear 移除全部临时缓存 + */ +export const Session = { + // 设置临时缓存 + set(key, val) { + window.sessionStorage.setItem(key, JSON.stringify(val)); + }, + // 获取临时缓存 + get(key) { + let json = window.sessionStorage.getItem(key); + return JSON.parse(json); + }, + // 移除临时缓存 + remove(key) { + window.sessionStorage.removeItem(key); + }, + // 移除全部临时缓存 + clear() { + window.sessionStorage.clear(); + }, +}; diff --git a/Yi.BBS.Vue3/src/views/Login.vue b/Yi.BBS.Vue3/src/views/Login.vue index a39f813b..70bfe533 100644 --- a/Yi.BBS.Vue3/src/views/Login.vue +++ b/Yi.BBS.Vue3/src/views/Login.vue @@ -1,47 +1,59 @@ diff --git a/Yi.RuoYi.Vue3/src/api/bbs/articleApi.js b/Yi.RuoYi.Vue3/src/api/bbs/articleApi.js index 403e94c0..dd128760 100644 --- a/Yi.RuoYi.Vue3/src/api/bbs/articleApi.js +++ b/Yi.RuoYi.Vue3/src/api/bbs/articleApi.js @@ -3,7 +3,7 @@ // 分页查询 export function listData(query) { return request({ - url: '/article/pageList', + url: '/article', method: 'get', params: query }) @@ -12,7 +12,7 @@ export function listData(query) { // id查询 export function getData(code) { return request({ - url: '/article/getById/' + code, + url: '/article/' + code, method: 'get' }) } @@ -20,7 +20,7 @@ export function getData(code) { // 新增 export function addData(data) { return request({ - url: '/article/add', + url: '/article', method: 'post', data: data }) @@ -29,17 +29,17 @@ export function addData(data) { // 修改 export function updateData(data) { return request({ - url: '/article/update', + url: `/article/${data.id}`, method: 'put', data: data }) } // 删除 -export function delData(code) { +export function delData(ids) { return request({ - url: '/article/delList', + url: '/article', method: 'delete', - data:"string"==typeof(code)?[code]:code + params:{id:ids} }) } diff --git a/Yi.RuoYi.Vue3/src/api/bbs/plateApi.js b/Yi.RuoYi.Vue3/src/api/bbs/plateApi.js new file mode 100644 index 00000000..71b14236 --- /dev/null +++ b/Yi.RuoYi.Vue3/src/api/bbs/plateApi.js @@ -0,0 +1,47 @@ +import request from '@/utils/request' +/* 以下为api的模板,通用的crud,将以下变量替换即可: +plate : 实体模型 +*/ +// 分页查询 +export function listData(query) { + return request({ + url: '/plate', + method: 'get', + params: query + }) +} + +// id查询 +export function getData(id) { + return request({ + url: `/plate/${id}`, + method: 'get' + }) +} + +// 新增 +export function addData(data) { + return request({ + url: '/plate', + method: 'post', + data: data + }) +} + +// 修改 +export function updateData(id,data) { + return request({ + url: `/plate/${id}`, + method: 'put', + data: data + }) +} + +// 删除 +export function delData(ids) { + return request({ + url: `/plate`, + method: 'delete', + params:{id:ids} + }) +} diff --git a/Yi.RuoYi.Vue3/src/api/template.txt b/Yi.RuoYi.Vue3/src/api/template.txt index 128ed557..06c97338 100644 --- a/Yi.RuoYi.Vue3/src/api/template.txt +++ b/Yi.RuoYi.Vue3/src/api/template.txt @@ -40,7 +40,8 @@ export function updateData(id,data) { // 删除 export function delData(ids) { return request({ - url: `/@model@/${ids}`, + url: `/@model@`, method: 'delete', + params:{id:ids} }) } diff --git a/Yi.RuoYi.Vue3/src/views/bbs/plate/index.vue b/Yi.RuoYi.Vue3/src/views/bbs/plate/index.vue new file mode 100644 index 00000000..1d81c57c --- /dev/null +++ b/Yi.RuoYi.Vue3/src/views/bbs/plate/index.vue @@ -0,0 +1,282 @@ + + + + \ No newline at end of file diff --git a/Yi.RuoYi.Vue3/src/views/template.txt b/Yi.RuoYi.Vue3/src/views/template.txt index cb6cabde..3b6f0e9f 100644 --- a/Yi.RuoYi.Vue3/src/views/template.txt +++ b/Yi.RuoYi.Vue3/src/views/template.txt @@ -92,11 +92,11 @@ -->