Browse Source

前端增加即时通讯signalr集成

pull/1/head
cKey 6 years ago
parent
commit
e2851cbcff
  1. 61
      aspnet-core/LINGYUN.Abp.MessageService.HttpApi.Host/Controllers/NotificationController.cs
  2. 1
      aspnet-core/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj
  3. 35
      aspnet-core/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs
  4. 5
      aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Hubs/MessageHub.cs
  5. 4
      aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Messages/SignalRMessageSender.cs
  6. 3
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs
  7. 12
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/OnlineClientHubBase.cs
  8. 4
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublisher.cs
  9. 26
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/Microsoft/AspNetCore/Builder/SignalRJwtTokenMiddleware.cs
  10. 12
      aspnet-core/modules/message/LINGYUN.Abp.MessageService.HttpApi/LINGYUN.Abp.MessageService.HttpApi.csproj
  11. 20
      aspnet-core/modules/message/LINGYUN.Abp.MessageService.HttpApi/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiModule.cs
  12. 2
      vueJs/.env.development
  13. 131
      vueJs/package-lock.json
  14. 1
      vueJs/package.json
  15. 145
      vueJs/src/components/Notification/index.vue
  16. 5
      vueJs/src/layout/components/Navbar/index.vue

61
aspnet-core/LINGYUN.Abp.MessageService.HttpApi.Host/Controllers/NotificationController.cs

@ -0,0 +1,61 @@
using LINGYUN.Abp.Notifications;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
namespace LINGYUN.Abp.MessageService.Controllers
{
[Route("api/app/notifications")]
public class NotificationController : AbpController
{
private readonly INotificationPublisher _notificationPublisher;
public NotificationController(
INotificationPublisher notificationPublisher)
{
_notificationPublisher = notificationPublisher;
}
[HttpPost]
[Route("Send")]
public async Task SendNofitication([FromForm] SendNotification notification)
{
var notificationInfo = new NotificationInfo
{
TenantId = null,
NotificationSeverity = NotificationSeverity.Success,
NotificationType = NotificationType.Application,
Id = 164589598456654164,
Name = "TestApplicationNotofication"
};
notificationInfo.Data.Properties["Title"] = notification.Title;
notificationInfo.Data.Properties["Message"] = notification.Message;
notificationInfo.Data.Properties["DateTime"] = notification.DateTime;
notificationInfo.Data.Properties["Severity"] = notification.Severity;
await _notificationPublisher.PublishAsync(notificationInfo, new List<Guid>() { notification.UserId });
}
}
public class SendNotification
{
public Guid UserId { get; set; }
public string Title { get; set; }
public DateTime DateTime { get; set; } = DateTime.Now;
public string Message { get; set; }
public NotificationSeverity Severity { get; set; } = NotificationSeverity.Success;
}
public class TestApplicationNotificationData : NotificationData
{
public object Message
{
get { return this[nameof(Message)]; }
set
{
Properties[nameof(Message)] = value;
}
}
}
}

1
aspnet-core/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj

@ -33,6 +33,7 @@
<ProjectReference Include="..\modules\common\LINGYUN.Abp.IM.SignalR\LINGYUN.Abp.IM.SignalR.csproj" />
<ProjectReference Include="..\modules\common\LINGYUN.Abp.Notifications.SignalR\LINGYUN.Abp.Notifications.SignalR.csproj" />
<ProjectReference Include="..\modules\message\LINGYUN.Abp.MessageService.EntityFrameworkCore\LINGYUN.Abp.MessageService.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\modules\message\LINGYUN.Abp.MessageService.HttpApi\LINGYUN.Abp.MessageService.HttpApi.csproj" />
</ItemGroup>
</Project>

35
aspnet-core/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs

@ -3,14 +3,18 @@ using LINGYUN.Abp.IM.SignalR;
using LINGYUN.Abp.MessageService.EntityFrameworkCore;
using LINGYUN.Abp.MessageService.MultiTenancy;
using LINGYUN.Abp.Notifications.SignalR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using StackExchange.Redis;
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
using Volo.Abp.AspNetCore.MultiTenancy;
@ -28,6 +32,7 @@ using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.MessageService
{
[DependsOn(
typeof(AbpMessageServiceHttpApiModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpMessageServiceEntityFrameworkCoreModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule),
@ -82,6 +87,30 @@ namespace LINGYUN.Abp.MessageService
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
});
Configure<JwtBearerOptions>(options =>
{
// 处理signalr某些请求由querystring发送请求令牌
// https://docs.microsoft.com/zh-cn/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1
options.Authority = configuration["AuthServer:Authority"];
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/signalr-hubs/notifications")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
context.Services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
@ -98,7 +127,7 @@ namespace LINGYUN.Abp.MessageService
{
options.Configuration = configuration["RedisCache:ConnectString"];
var instanceName = configuration["RedisCache:RedisPrefix"];
options.InstanceName = instanceName.IsNullOrEmpty() ? "Platform_Cache" : instanceName;
options.InstanceName = instanceName.IsNullOrEmpty() ? "MessageService_Cache" : instanceName;
});
if (!hostingEnvironment.IsDevelopment())
@ -121,10 +150,14 @@ namespace LINGYUN.Abp.MessageService
app.UseAbpRequestLocalization();
//路由
app.UseRouting();
// 加入自定义中间件
app.UseMiddleware<SignalRJwtTokenMiddleware>();
// 认证
app.UseAuthentication();
// jwt
app.UseJwtTokenMiddleware();
// 授权
app.UseAuthorization();
// 多租户
app.UseMultiTenancy();
// Swagger

5
aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Hubs/AbpMessageHub.cs → aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Hubs/MessageHub.cs

@ -10,12 +10,11 @@ using Volo.Abp.AspNetCore.SignalR;
namespace LINGYUN.Abp.IM.SignalR.Hubs
{
[Authorize]
[HubRoute("messages")]
public class AbpMessageHub : OnlineClientHubBase
public class MessageHub : OnlineClientHubBase
{
private readonly IMessageStore _messageStore;
public AbpMessageHub(
public MessageHub(
IMessageStore messageStore)
{
_messageStore = messageStore;

4
aspnet-core/modules/common/LINGYUN.Abp.IM.SignalR/LINGYUN/Abp/IM/SignalR/Messages/SignalRMessageSender.cs

@ -16,13 +16,13 @@ namespace LINGYUN.Abp.IM.SignalR.Messages
private readonly IOnlineClientManager _onlineClientManager;
private readonly IHubContext<AbpMessageHub> _hubContext;
private readonly IHubContext<MessageHub> _hubContext;
private readonly IMessageStore _messageStore;
public SignalRMessageSender(
IOnlineClientManager onlineClientManager,
IHubContext<AbpMessageHub> hubContext,
IHubContext<MessageHub> hubContext,
IMessageStore messageStore)
{
_hubContext = hubContext;

3
aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/AbpNotificationsHub.cs → aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs

@ -8,8 +8,7 @@ using Volo.Abp.Users;
namespace LINGYUN.Abp.Notifications.SignalR.Hubs
{
[Authorize]
[HubRoute("notifications")]
public class AbpNotificationsHub : OnlineClientHubBase
public class NotificationsHub : OnlineClientHubBase
{
private INotificationStore _notificationStore;
protected INotificationStore NotificationStore => LazyGetRequiredService(ref _notificationStore);

12
aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/OnlineClientHubBase.cs

@ -1,14 +1,19 @@
using LINGYUN.Abp.RealTime.Client;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.SignalR;
using Volo.Abp.Security.Claims;
namespace LINGYUN.Abp.Notifications.SignalR
{
public abstract class OnlineClientHubBase : AbpHub
{
private ICurrentPrincipalAccessor _currentPrincipalAccessor;
protected ICurrentPrincipalAccessor CurrentPrincipalAccessor => LazyGetRequiredService(ref _currentPrincipalAccessor);
private IOnlineClientManager _onlineClientManager;
protected IOnlineClientManager OnlineClientManager => LazyGetRequiredService(ref _onlineClientManager);
@ -44,13 +49,18 @@ namespace LINGYUN.Abp.Notifications.SignalR
}
protected virtual IOnlineClient CreateClientForCurrentConnection()
{
// abp框架没有处理,需要切换一下用户身份令牌.否则无法获取用户信息
using (CurrentPrincipalAccessor.Change(Context.User))
{
return new OnlineClient(Context.ConnectionId, GetClientIpAddress(),
CurrentTenant.Id, CurrentUser.Id)
{
ConnectTime = Clock.Now
ConnectTime = Clock.Now,
UserName = CurrentUser.UserName
};
}
}
protected virtual string GetClientIpAddress()
{

4
aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/SignalRNotificationPublisher.cs

@ -16,11 +16,11 @@ namespace LINGYUN.Abp.Notifications.SignalR
private readonly IOnlineClientManager _onlineClientManager;
private readonly IHubContext<AbpNotificationsHub> _hubContext;
private readonly IHubContext<NotificationsHub> _hubContext;
public SignalRNotificationPublisher(
IOnlineClientManager onlineClientManager,
IHubContext<AbpNotificationsHub> hubContext)
IHubContext<NotificationsHub> hubContext)
{
_hubContext = hubContext;
_onlineClientManager = onlineClientManager;

26
aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/Microsoft/AspNetCore/Builder/SignalRJwtTokenMiddleware.cs

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Microsoft.AspNetCore.Builder
{
public class SignalRJwtTokenMiddleware : IMiddleware, ITransientDependency
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// 仅针对自定义的SignalR hub
if (context.Request.Path.StartsWithSegments("/signalr-hubs/notifications"))
{
if (context.User.Identity?.IsAuthenticated != true)
{
if (context.Request.Query.TryGetValue("access_token", out var accessToken))
{
context.Request.Headers.Add("Authorization", $"Bearer {accessToken}");
}
}
}
await next(context);
}
}
}

12
aspnet-core/modules/message/LINGYUN.Abp.MessageService.HttpApi/LINGYUN.Abp.MessageService.HttpApi.csproj

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="2.8.0" />
</ItemGroup>
</Project>

20
aspnet-core/modules/message/LINGYUN.Abp.MessageService.HttpApi/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiModule.cs

@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.MessageService
{
[DependsOn(
typeof(AbpAspNetCoreMvcModule)
)]
public class AbpMessageServiceHttpApiModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpMessageServiceHttpApiModule).Assembly);
});
}
}
}

2
vueJs/.env.development

@ -3,6 +3,8 @@
VUE_APP_BASE_MOCK_API = '/dev-api'
VUE_APP_BASE_API = '/api'
#Signalr
VUE_APP_SIGNALR_SERVER = '/signalr-hubs'
VUE_APP_BASE_IDENTITY_SERVICE = '/api/identity'

131
vueJs/package-lock.json

@ -1989,6 +1989,26 @@
}
}
},
"@microsoft/signalr": {
"version": "3.1.4",
"resolved": "https://registry.npm.taobao.org/@microsoft/signalr/download/@microsoft/signalr-3.1.4.tgz",
"integrity": "sha1-EILSBjvi0E1LRpnGovsXIyEn/0M=",
"requires": {
"eventsource": "^1.0.7",
"request": "^2.88.0",
"ws": "^6.0.0"
},
"dependencies": {
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npm.taobao.org/ws/download/ws-6.2.1.tgz?cache=0&sync_timestamp=1589091396925&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fws%2Fdownload%2Fws-6.2.1.tgz",
"integrity": "sha1-RC/fCkftZPWbal2P8TD0dI7VJPs=",
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
"@mrmlnc/readdir-enhanced": {
"version": "2.2.1",
"resolved": "https://registry.npm.taobao.org/@mrmlnc/readdir-enhanced/download/@mrmlnc/readdir-enhanced-2.2.1.tgz",
@ -3830,7 +3850,6 @@
"version": "6.12.2",
"resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.2.tgz?cache=0&sync_timestamp=1587338460514&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.2.tgz",
"integrity": "sha1-xinF7O0XuvMUQ3kY0tqIyZ1ZWM0=",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -4030,7 +4049,6 @@
"version": "0.2.4",
"resolved": "https://registry.npm.taobao.org/asn1/download/asn1-0.2.4.tgz",
"integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=",
"dev": true,
"requires": {
"safer-buffer": "~2.1.0"
}
@ -4076,8 +4094,7 @@
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"assign-symbols": {
"version": "1.0.0",
@ -4105,8 +4122,7 @@
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/async-limiter/download/async-limiter-1.0.1.tgz?cache=0&sync_timestamp=1574271725892&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fasync-limiter%2Fdownload%2Fasync-limiter-1.0.1.tgz",
"integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=",
"dev": true
"integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0="
},
"async-validator": {
"version": "1.8.5",
@ -4119,8 +4135,7 @@
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"atob": {
"version": "2.1.2",
@ -4168,14 +4183,12 @@
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
"dev": true
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"aws4": {
"version": "1.9.1",
"resolved": "https://registry.npm.taobao.org/aws4/download/aws4-1.9.1.tgz",
"integrity": "sha1-fjPY99RJs/ZzzXLeuavcVS2+Uo4=",
"dev": true
"integrity": "sha1-fjPY99RJs/ZzzXLeuavcVS2+Uo4="
},
"axios": {
"version": "0.19.2",
@ -4631,7 +4644,6 @@
"version": "1.0.2",
"resolved": "https://registry.npm.taobao.org/bcrypt-pbkdf/download/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"dev": true,
"requires": {
"tweetnacl": "^0.14.3"
}
@ -5218,8 +5230,7 @@
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"cfb": {
"version": "1.1.4",
@ -5845,7 +5856,6 @@
"version": "1.0.8",
"resolved": "https://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.8.tgz",
"integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
@ -6848,7 +6858,6 @@
"version": "1.14.1",
"resolved": "https://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
@ -7175,8 +7184,7 @@
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"delegate": {
"version": "3.2.0",
@ -7438,7 +7446,6 @@
"version": "0.1.2",
"resolved": "https://registry.npm.taobao.org/ecc-jsbn/download/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"dev": true,
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
@ -8354,7 +8361,6 @@
"version": "1.0.7",
"resolved": "https://registry.npm.taobao.org/eventsource/download/eventsource-1.0.7.tgz",
"integrity": "sha1-j7xyyT/NNAiAkLwKTmT0tc7m2NA=",
"dev": true,
"requires": {
"original": "^1.0.0"
}
@ -8588,8 +8594,7 @@
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz",
"integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=",
"dev": true
"integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo="
},
"extend-shallow": {
"version": "3.0.2",
@ -8758,8 +8763,7 @@
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npm.taobao.org/extsprintf/download/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
"dev": true
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"faker": {
"version": "4.1.0",
@ -8769,8 +8773,7 @@
"fast-deep-equal": {
"version": "3.1.1",
"resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.1.tgz",
"integrity": "sha1-VFFFB3xQFJHjOxXsQIwpQ3bpSuQ=",
"dev": true
"integrity": "sha1-VFFFB3xQFJHjOxXsQIwpQ3bpSuQ="
},
"fast-glob": {
"version": "2.2.7",
@ -8789,8 +8792,7 @@
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=",
"dev": true
"integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM="
},
"fast-levenshtein": {
"version": "2.0.6",
@ -9041,8 +9043,7 @@
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
"dev": true
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"fork-ts-checker-webpack-plugin": {
"version": "3.1.1",
@ -9185,7 +9186,6 @@
"version": "2.3.3",
"resolved": "https://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz",
"integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
@ -9970,7 +9970,6 @@
"version": "0.1.7",
"resolved": "https://registry.npm.taobao.org/getpass/download/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
@ -10085,14 +10084,12 @@
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/har-schema/download/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"dev": true
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
"version": "5.1.3",
"resolved": "https://registry.npm.taobao.org/har-validator/download/har-validator-5.1.3.tgz",
"integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=",
"dev": true,
"requires": {
"ajv": "^6.5.5",
"har-schema": "^2.0.0"
@ -10458,7 +10455,6 @@
"version": "1.2.0",
"resolved": "https://registry.npm.taobao.org/http-signature/download/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
@ -11147,8 +11143,7 @@
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"is-utf8": {
"version": "0.2.1",
@ -11193,8 +11188,7 @@
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"istanbul-lib-coverage": {
"version": "2.0.5",
@ -14375,8 +14369,7 @@
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npm.taobao.org/jsbn/download/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"dev": true
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"jsdom": {
"version": "11.12.0",
@ -14435,14 +14428,12 @@
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz?cache=0&sync_timestamp=1567740732347&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema%2Fdownload%2Fjson-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
"dev": true
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz",
"integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=",
"dev": true
"integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA="
},
"json-stable-stringify": {
"version": "1.0.1",
@ -14462,8 +14453,7 @@
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
"dev": true
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"json3": {
"version": "3.3.3",
@ -14508,7 +14498,6 @@
"version": "1.4.1",
"resolved": "https://registry.npm.taobao.org/jsprim/download/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"dev": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
@ -16207,14 +16196,12 @@
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.44.0.tgz",
"integrity": "sha1-+hHF6wrKEzS0Izy01S8QxaYnL5I=",
"dev": true
"integrity": "sha1-+hHF6wrKEzS0Izy01S8QxaYnL5I="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.27.tgz?cache=0&sync_timestamp=1587700357177&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-types%2Fdownload%2Fmime-types-2.1.27.tgz",
"integrity": "sha1-R5SfmOJ56lMRn1ci4PNOUpvsAJ8=",
"dev": true,
"requires": {
"mime-db": "1.44.0"
}
@ -16720,8 +16707,7 @@
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.9.0.tgz",
"integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=",
"dev": true
"integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU="
},
"object-assign": {
"version": "4.1.1",
@ -16962,7 +16948,6 @@
"version": "1.0.2",
"resolved": "https://registry.npm.taobao.org/original/download/original-1.0.2.tgz",
"integrity": "sha1-5EKmHP/hxf0gpl8yYcJmY7MD8l8=",
"dev": true,
"requires": {
"url-parse": "^1.4.3"
}
@ -17258,8 +17243,7 @@
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"picomatch": {
"version": "2.2.2",
@ -18223,8 +18207,7 @@
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npm.taobao.org/psl/download/psl-1.8.0.tgz",
"integrity": "sha1-kyb4vPsBOtzABf3/BWrM4CDlHCQ=",
"dev": true
"integrity": "sha1-kyb4vPsBOtzABf3/BWrM4CDlHCQ="
},
"public-encrypt": {
"version": "4.0.3",
@ -18275,8 +18258,7 @@
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz",
"integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=",
"dev": true
"integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew="
},
"q": {
"version": "1.5.1",
@ -18286,8 +18268,7 @@
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz?cache=0&sync_timestamp=1585168614364&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fqs%2Fdownload%2Fqs-6.5.2.tgz",
"integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=",
"dev": true
"integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY="
},
"query-string": {
"version": "4.3.4",
@ -18314,8 +18295,7 @@
"querystringify": {
"version": "2.1.1",
"resolved": "https://registry.npm.taobao.org/querystringify/download/querystringify-2.1.1.tgz",
"integrity": "sha1-YOWl/WSn+L+k0qsu1v30yFutFU4=",
"dev": true
"integrity": "sha1-YOWl/WSn+L+k0qsu1v30yFutFU4="
},
"ramda": {
"version": "0.24.1",
@ -18661,7 +18641,6 @@
"version": "2.88.2",
"resolved": "https://registry.npm.taobao.org/request/download/request-2.88.2.tgz?cache=0&sync_timestamp=1581439006948&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frequest%2Fdownload%2Frequest-2.88.2.tgz",
"integrity": "sha1-1zyRhzHLWofaBH4gcjQUb2ZNErM=",
"dev": true,
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
@ -18727,8 +18706,7 @@
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/requires-port/download/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"resize-observer-polyfill": {
"version": "1.5.1",
@ -18863,8 +18841,7 @@
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsafer-buffer%2Fdownload%2Fsafer-buffer-2.1.2.tgz",
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=",
"dev": true
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo="
},
"sane": {
"version": "4.1.0",
@ -19644,7 +19621,6 @@
"version": "1.16.1",
"resolved": "https://registry.npm.taobao.org/sshpk/download/sshpk-1.16.1.tgz",
"integrity": "sha1-+2YcC+8ps520B2nuOfpwCT1vaHc=",
"dev": true,
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
@ -20422,7 +20398,6 @@
"version": "2.5.0",
"resolved": "https://registry.npm.taobao.org/tough-cookie/download/tough-cookie-2.5.0.tgz?cache=0&sync_timestamp=1584645708631&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftough-cookie%2Fdownload%2Ftough-cookie-2.5.0.tgz",
"integrity": "sha1-zZ+yoKodWhK0c72fuW+j3P9lreI=",
"dev": true,
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
@ -20820,7 +20795,6 @@
"version": "0.6.0",
"resolved": "https://registry.npm.taobao.org/tunnel-agent/download/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
@ -20828,8 +20802,7 @@
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npm.taobao.org/tweetnacl/download/tweetnacl-0.14.5.tgz?cache=0&sync_timestamp=1581364304221&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftweetnacl%2Fdownload%2Ftweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
},
"type-check": {
"version": "0.3.2",
@ -21075,7 +21048,6 @@
"version": "4.2.2",
"resolved": "https://registry.npm.taobao.org/uri-js/download/uri-js-4.2.2.tgz",
"integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
@ -21119,7 +21091,6 @@
"version": "1.4.7",
"resolved": "https://registry.npm.taobao.org/url-parse/download/url-parse-1.4.7.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Furl-parse%2Fdownload%2Furl-parse-1.4.7.tgz",
"integrity": "sha1-qKg1NejACjFuQDpdtKwbm4U64ng=",
"dev": true,
"requires": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
@ -21179,8 +21150,7 @@
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npm.taobao.org/uuid/download/uuid-3.4.0.tgz?cache=0&sync_timestamp=1588192972951&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuuid%2Fdownload%2Fuuid-3.4.0.tgz",
"integrity": "sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=",
"dev": true
"integrity": "sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4="
},
"v-click-outside-x": {
"version": "3.7.1",
@ -21237,7 +21207,6 @@
"version": "1.10.0",
"resolved": "https://registry.npm.taobao.org/verror/download/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",

1
vueJs/package.json

@ -15,6 +15,7 @@
"test:unit": "jest --clearCache && vue-cli-service test:unit"
},
"dependencies": {
"@microsoft/signalr": "^3.1.4",
"@tinymce/tinymce-vue": "^3.2.0",
"axios": "^0.19.2",
"clipboard": "^2.0.6",

145
vueJs/src/components/Notification/index.vue

@ -0,0 +1,145 @@
<template>
<el-dropdown>
<div>
<svg-icon
name="message"
class="message-icon"
/>
</div>
<el-dropdown-menu slot="dropdown">
<div
class="app-container"
style="width: 400px;max-height: 300px;"
>
<el-tabs
stretch
>
<el-tab-pane
label="通知"
>
<div
style="overflow-x: hidden;overflow-y: auto;"
>
<List
size="small"
>
<ListItem
v-for="(notify, index) in notifications"
:key="index"
>
<ListItemMeta
:title="notify.Message"
:description="formatDateTime(notify.DateTime)"
avatar="ios-person"
/>
</ListItem>
</List>
</div>
</el-tab-pane>
<el-tab-pane label="消息">
消息系统
</el-tab-pane>
</el-tabs>
</div>
</el-dropdown-menu>
</el-dropdown>
</template>
<script lang="ts">
import { HubConnection, HubConnectionBuilder, HubConnectionState } from '@microsoft/signalr'
import { Component, Vue } from 'vue-property-decorator'
import { UserModule } from '@/store/modules/user'
import { dateFormat } from '@/utils/index'
enum Severity {
success = 0,
info = 10,
warn = 20,
error = 30,
fatal = 40
}
class Notification {
Title!: string
Message!: string
DateTime!: Date
Severity!: Severity
}
@Component({
name: 'Notification'
})
export default class extends Vue {
private connection!: HubConnection
private notifications = new Array<Notification>()
mounted() {
this.handleStartConnection()
}
destroyed() {
this.handleStopConnection()
}
private renderIconType(item: any) {
if (item.Severity !== Severity.success) {
return ' el-icon-circle-close'
}
return ' el-icon-circle-check'
}
private renderIconStyle(item: any) {
if (item.Severity !== Severity.success) {
return 'backgroundColor: #f56a00'
}
return 'backgroundColor: #87d068'
}
private formatDateTime(datetime: string) {
const date = new Date(datetime)
return dateFormat(date, 'YYYY-mm-dd HH:MM:SS')
}
private handleStartConnection() {
console.log('start signalr connection...')
if (!this.connection) {
const builder = new HubConnectionBuilder()
console.log(builder)
const userToken = UserModule.token.replace('Bearer ', '')
console.log(userToken)
this.connection = builder
.withUrl('/signalr-hubs/signalr-hubs/notifications', { accessTokenFactory: () => userToken })
.withAutomaticReconnect({ nextRetryDelayInMilliseconds: () => 60000 })
.build()
this.connection.on('getNotification', data => this.onNotificationReceived(data))
this.connection.onclose(error => {
console.log('signalr connection has closed, error:')
console.log(error)
})
}
if (this.connection.state !== HubConnectionState.Connected) {
this.connection.start()
}
}
private handleStopConnection() {
console.log('stop signalr connection...')
if (this.connection && this.connection.state === HubConnectionState.Connected) {
this.connection.stop()
}
}
private onNotificationReceived(data: any) {
console.log('received signalr message...')
console.log(data)
this.notifications.push(data.data.properties)
this.$notify.success(data.data.properties.message)
}
}
</script>
<style scoped>
.el-scrollbar__wrap{
overflow-x: hidden;
}
</style>

5
vueJs/src/layout/components/Navbar/index.vue

@ -23,6 +23,7 @@
<size-select class="right-menu-item hover-effect" />
</el-tooltip>
<lang-select class="right-menu-item hover-effect" />
<notification class="right-menu-item hover-effect" />
</template>
<el-dropdown
class="avatar-container right-menu-item hover-effect"
@ -69,6 +70,7 @@ import HeaderSearch from '@/components/HeaderSearch/index.vue'
import LangSelect from '@/components/LangSelect/index.vue'
import Screenfull from '@/components/Screenfull/index.vue'
import SizeSelect from '@/components/SizeSelect/index.vue'
import Notification from '@/components/Notification/index.vue'
@Component({
name: 'Navbar',
@ -79,7 +81,8 @@ import SizeSelect from '@/components/SizeSelect/index.vue'
HeaderSearch,
LangSelect,
Screenfull,
SizeSelect
SizeSelect,
Notification
}
})
export default class extends Vue {

Loading…
Cancel
Save