Browse Source

完善通知组件;完善用户注册组件

pull/1/head
cKey 6 years ago
parent
commit
2a7acb7b6c
  1. 25
      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. 24
      aspnet-core/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs
  4. 16
      aspnet-core/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Utils/SnowflakeIdGenerator.cs
  5. 12
      aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs
  6. 14
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs
  7. 2
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN.Abp.Notifications.csproj
  8. 2
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationStore.cs
  9. 4
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/DefaultNotificationDispatcher.cs
  10. 4
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationInfo.cs
  11. 30
      aspnet-core/modules/common/LINGYUN.Abp.Notifications/Newtonsoft/Json/HexLongConverter.cs
  12. 13
      aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/AbpMessageServiceDomainModule.cs
  13. 2
      aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Notifications/IUserNotificationRepository.cs
  14. 17
      aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Notifications/NotificationStore.cs
  15. 5
      aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Notifications/EfCoreUserNotificationRepository.cs
  16. 9
      vueJs/src/api/abpconfiguration.ts
  17. 112
      vueJs/src/components/Notification/index.vue
  18. 3
      vueJs/src/lang/zh.ts
  19. 2
      vueJs/src/store/modules/user.ts
  20. 3
      vueJs/src/views/admin/tenants/index.vue
  21. 3
      vueJs/src/views/admin/users/index.vue
  22. 13
      vueJs/src/views/login/index.vue
  23. 78
      vueJs/src/views/register/index.vue

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

@ -10,11 +10,11 @@ namespace LINGYUN.Abp.MessageService.Controllers
[Route("api/app/notifications")] [Route("api/app/notifications")]
public class NotificationController : AbpController public class NotificationController : AbpController
{ {
private readonly INotificationPublisher _notificationPublisher; private readonly INotificationDispatcher _notificationDispatcher;
public NotificationController( public NotificationController(
INotificationPublisher notificationPublisher) INotificationDispatcher notificationDispatcher)
{ {
_notificationPublisher = notificationPublisher; _notificationDispatcher = notificationDispatcher;
} }
[HttpPost] [HttpPost]
@ -24,17 +24,19 @@ namespace LINGYUN.Abp.MessageService.Controllers
var notificationInfo = new NotificationInfo var notificationInfo = new NotificationInfo
{ {
TenantId = null, TenantId = null,
NotificationSeverity = NotificationSeverity.Success, NotificationSeverity = notification.Severity,
NotificationType = NotificationType.Application, NotificationType = NotificationType.Application,
Id = 164589598456654164, Id = new Random().Next(int.MinValue, int.MaxValue),
Name = "TestApplicationNotofication" Name = "TestApplicationNotofication",
CreationTime = Clock.Now
}; };
notificationInfo.Data.Properties["Title"] = notification.Title; notificationInfo.Data.Properties["id"] = notificationInfo.Id.ToString();
notificationInfo.Data.Properties["Message"] = notification.Message; notificationInfo.Data.Properties["title"] = notification.Title;
notificationInfo.Data.Properties["DateTime"] = notification.DateTime; notificationInfo.Data.Properties["message"] = notification.Message;
notificationInfo.Data.Properties["Severity"] = notification.Severity; notificationInfo.Data.Properties["datetime"] = Clock.Now;
notificationInfo.Data.Properties["severity"] = notification.Severity;
await _notificationPublisher.PublishAsync(notificationInfo, new List<Guid>() { notification.UserId }); await _notificationDispatcher.DispatcheAsync(notificationInfo);
} }
} }
@ -42,7 +44,6 @@ namespace LINGYUN.Abp.MessageService.Controllers
{ {
public Guid UserId { get; set; } public Guid UserId { get; set; }
public string Title { get; set; } public string Title { get; set; }
public DateTime DateTime { get; set; } = DateTime.Now;
public string Message { get; set; } public string Message { get; set; }
public NotificationSeverity Severity { get; set; } = NotificationSeverity.Success; public NotificationSeverity Severity { get; set; } = NotificationSeverity.Success;
} }

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

@ -30,6 +30,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\modules\common\LINGYUN.Abp.EventBus.CAP\LINGYUN.Abp.EventBus.CAP.csproj" />
<ProjectReference Include="..\modules\common\LINGYUN.Abp.IM.SignalR\LINGYUN.Abp.IM.SignalR.csproj" /> <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\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.EntityFrameworkCore\LINGYUN.Abp.MessageService.EntityFrameworkCore.csproj" />

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

@ -87,30 +87,6 @@ namespace LINGYUN.Abp.MessageService
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); 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") context.Services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options => .AddIdentityServerAuthentication(options =>
{ {

16
aspnet-core/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Utils/SnowflakeIdGenerator.cs

@ -0,0 +1,16 @@
using DotNetCore.CAP.Internal;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.MessageService.Utils
{
[Dependency(ServiceLifetime.Singleton, TryRegister = true)]
[ExposeServices(typeof(ISnowflakeIdGenerator))]
public class SnowflakeIdGenerator : ISnowflakeIdGenerator
{
public long Create()
{
return SnowflakeId.Default().NextId();
}
}
}

12
aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs

@ -59,13 +59,15 @@ namespace LINGYUN.Abp.Account
var userEmail = input.EmailAddress ?? input.PhoneNumber + "@abp.io"; var userEmail = input.EmailAddress ?? input.PhoneNumber + "@abp.io";
var userName = input.UserName ?? input.PhoneNumber; var userName = input.UserName ?? input.PhoneNumber;
var user = new IdentityUser(GuidGenerator.Create(), userName, userEmail, CurrentTenant.Id); var user = new IdentityUser(GuidGenerator.Create(), userName, userEmail, CurrentTenant.Id)
user.Name = input.Name ?? input.PhoneNumber; {
Name = input.Name ?? input.PhoneNumber
};
(await UserManager.CreateAsync(user, input.Password)).CheckErrors(); (await UserManager.CreateAsync(user, input.Password)).CheckErrors();
await UserManager.ChangePhoneNumberAsync(user, input.PhoneNumber, input.VerifyCode); (await UserManager.SetPhoneNumberAsync(user, input.PhoneNumber)).CheckErrors();
await UserManager.SetEmailAsync(user, userEmail); (await UserManager.SetEmailAsync(user, userEmail)).CheckErrors();
await UserManager.AddDefaultRolesAsync(user); (await UserManager.AddDefaultRolesAsync(user)).CheckErrors();
await Cache.RemoveAsync(phoneVerifyCacheKey); await Cache.RemoveAsync(phoneVerifyCacheKey);

14
aspnet-core/modules/common/LINGYUN.Abp.Notifications.SignalR/LINGYUN/Abp/Notifications/SignalR/Hubs/NotificationsHub.cs

@ -2,7 +2,6 @@
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.SignalR;
using Volo.Abp.Users; using Volo.Abp.Users;
namespace LINGYUN.Abp.Notifications.SignalR.Hubs namespace LINGYUN.Abp.Notifications.SignalR.Hubs
@ -14,11 +13,20 @@ namespace LINGYUN.Abp.Notifications.SignalR.Hubs
protected INotificationStore NotificationStore => LazyGetRequiredService(ref _notificationStore); protected INotificationStore NotificationStore => LazyGetRequiredService(ref _notificationStore);
[HubMethodName("GetNotification")] [HubMethodName("GetNotification")]
public virtual async Task<ListResultDto<NotificationInfo>> GetNotificationAsync(NotificationReadState readState = NotificationReadState.UnRead) public virtual async Task<ListResultDto<NotificationInfo>> GetNotificationAsync(
NotificationReadState readState = NotificationReadState.UnRead, int maxResultCount = 10)
{ {
var userNotifications = await NotificationStore.GetUserNotificationsAsync(CurrentTenant.Id, CurrentUser.GetId(), readState); var userNotifications = await NotificationStore.GetUserNotificationsAsync(CurrentTenant.Id, CurrentUser.GetId(), readState, maxResultCount);
return new ListResultDto<NotificationInfo>(userNotifications); return new ListResultDto<NotificationInfo>(userNotifications);
} }
[HubMethodName("ChangeState")]
public virtual async Task ChangeStateAsync(long id, NotificationReadState readState = NotificationReadState.Read)
{
await NotificationStore.ChangeUserNotificationReadStateAsync(CurrentTenant.Id, CurrentUser.GetId(), id, readState);
}
} }
} }

2
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN.Abp.Notifications.csproj

@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Volo.Abp.Core" Version="2.8.0" /> <PackageReference Include="Volo.Abp.Json" Version="2.8.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationStore.cs

@ -24,6 +24,8 @@ namespace LINGYUN.Abp.Notifications
Task InsertUserNotificationAsync(NotificationInfo notification, Guid userId); Task InsertUserNotificationAsync(NotificationInfo notification, Guid userId);
Task InsertUserNotificationsAsync(NotificationInfo notification, IEnumerable<Guid> userIds);
Task DeleteUserNotificationAsync(Guid? tenantId, Guid userId, long notificationId); Task DeleteUserNotificationAsync(Guid? tenantId, Guid userId, long notificationId);
Task<NotificationInfo> GetNotificationOrNullAsync(Guid? tenantId, long notificationId); Task<NotificationInfo> GetNotificationOrNullAsync(Guid? tenantId, long notificationId);

4
aspnet-core/modules/common/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/Internal/DefaultNotificationDispatcher.cs

@ -23,9 +23,9 @@ namespace LINGYUN.Abp.Notifications.Internal
// 获取用户订阅列表 // 获取用户订阅列表
var userSubscriptions = await _notificationStore.GetSubscriptionsAsync(notification.TenantId, notification.Name); var userSubscriptions = await _notificationStore.GetSubscriptionsAsync(notification.TenantId, notification.Name);
// 持久化用户订阅通知 // 持久化用户通知
var subscriptionUserIds = userSubscriptions.Select(us => us.UserId); var subscriptionUserIds = userSubscriptions.Select(us => us.UserId);
await _notificationStore.InsertUserSubscriptionAsync(notification.TenantId, subscriptionUserIds, notification.Name); await _notificationStore.InsertUserNotificationsAsync(notification, subscriptionUserIds);
// 发布用户通知 // 发布用户通知
await _notificationPublisher.PublishAsync(notification, subscriptionUserIds); await _notificationPublisher.PublishAsync(notification, subscriptionUserIds);

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

@ -1,4 +1,5 @@
using System; using Newtonsoft.Json;
using System;
namespace LINGYUN.Abp.Notifications namespace LINGYUN.Abp.Notifications
{ {
@ -6,6 +7,7 @@ namespace LINGYUN.Abp.Notifications
{ {
public Guid? TenantId { get; set; } public Guid? TenantId { get; set; }
public string Name { get; set; } public string Name { get; set; }
[JsonConverter(typeof(HexLongConverter))]
public long Id { get; set; } public long Id { get; set; }
public NotificationData Data { get; set; } public NotificationData Data { get; set; }
public DateTime CreationTime { get; set; } public DateTime CreationTime { get; set; }

30
aspnet-core/modules/common/LINGYUN.Abp.Notifications/Newtonsoft/Json/HexLongConverter.cs

@ -0,0 +1,30 @@
using System;
namespace Newtonsoft.Json
{
public class HexLongConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
long v = value is ulong ? (long)(ulong)value : (long)value;
writer.WriteValue(v.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string value = reader.Value as string;
long lValue = long.Parse(value);
return typeof(ulong) == objectType ? (object)(ulong)lValue : lValue;
}
public override bool CanConvert(Type objectType)
{
switch (objectType.FullName)
{
case "System.Int64":
case "System.UInt64":
return true;
default:
return false;
}
}
}
}

13
aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/AbpMessageServiceDomainModule.cs

@ -1,9 +1,18 @@
using Volo.Abp.Modularity; using LINGYUN.Abp.MessageService.Mapper;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.MessageService namespace LINGYUN.Abp.MessageService
{ {
[DependsOn(typeof(AbpAutoMapperModule))]
public class AbpMessageServiceDomainModule : AbpModule public class AbpMessageServiceDomainModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<MessageServiceDomainAutoMapperProfile>(validate: true);
});
}
} }
} }

2
aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Notifications/IUserNotificationRepository.cs

@ -8,6 +8,8 @@ namespace LINGYUN.Abp.MessageService.Notifications
{ {
public interface IUserNotificationRepository : IBasicRepository<UserNotification, long> public interface IUserNotificationRepository : IBasicRepository<UserNotification, long>
{ {
Task InsertUserNotificationsAsync(IEnumerable<UserNotification> userNotifications);
Task<UserNotification> GetByIdAsync(Guid userId, long notificationId); Task<UserNotification> GetByIdAsync(Guid userId, long notificationId);
Task<List<Notification>> GetNotificationsAsync(Guid userId, NotificationReadState readState = NotificationReadState.UnRead, int maxResultCount = 10); Task<List<Notification>> GetNotificationsAsync(Guid userId, NotificationReadState readState = NotificationReadState.UnRead, int maxResultCount = 10);

17
aspnet-core/modules/message/LINGYUN.Abp.MessageService.Domain/LINGYUN/Abp/MessageService/Notifications/NotificationStore.cs

@ -155,7 +155,11 @@ namespace LINGYUN.Abp.MessageService.Notifications
{ {
using (CurrentTenant.Change(notification.TenantId)) using (CurrentTenant.Change(notification.TenantId))
{ {
var notify = new Notification(SnowflakeIdGenerator.Create(), notification.Name, var notifyId = SnowflakeIdGenerator.Create();
// 保存主键,防止前端js long类型溢出
notification.Data["id"] = notifyId.ToString();
var notify = new Notification(notifyId, notification.Name,
notification.Data.GetType().AssemblyQualifiedName, notification.Data.GetType().AssemblyQualifiedName,
JsonSerializer.Serialize(notification.Data), notification.NotificationSeverity) JsonSerializer.Serialize(notification.Data), notification.NotificationSeverity)
{ {
@ -235,5 +239,16 @@ namespace LINGYUN.Abp.MessageService.Notifications
using (CurrentTenant.Change(tenantId)) using (CurrentTenant.Change(tenantId))
return await UserSubscribeRepository.UserSubscribeExistsAysnc(notificationName, userId); return await UserSubscribeRepository.UserSubscribeExistsAysnc(notificationName, userId);
} }
public async Task InsertUserNotificationsAsync(NotificationInfo notification, IEnumerable<Guid> userIds)
{
var userNofitications = new List<UserNotification>();
foreach(var userId in userIds)
{
var userNofitication = new UserNotification(notification.Id, userId);
userNofitications.Add(userNofitication);
}
await UserNotificationRepository.InsertUserNotificationsAsync(userNofitications);
}
} }
} }

5
aspnet-core/modules/message/LINGYUN.Abp.MessageService.EntityFrameworkCore/LINGYUN/Abp/MessageService/Notifications/EfCoreUserNotificationRepository.cs

@ -20,6 +20,11 @@ namespace LINGYUN.Abp.MessageService.Notifications
{ {
} }
public async Task InsertUserNotificationsAsync(IEnumerable<UserNotification> userNotifications)
{
await DbSet.AddRangeAsync(userNotifications);
}
public async Task ChangeUserNotificationReadStateAsync(Guid userId, long notificationId, NotificationReadState readState) public async Task ChangeUserNotificationReadStateAsync(Guid userId, long notificationId, NotificationReadState readState)
{ {
var userNofitication = await GetByIdAsync(userId, notificationId); var userNofitication = await GetByIdAsync(userId, notificationId);

9
vueJs/src/api/abpconfiguration.ts

@ -90,6 +90,8 @@ export interface IAbpConfiguration {
multiTenancy: MultiTenancy multiTenancy: MultiTenancy
objectExtensions: any objectExtensions: any
setting: Setting setting: Setting
getSetting(key: string): string | undefined
} }
export class AbpConfiguration implements IAbpConfiguration { export class AbpConfiguration implements IAbpConfiguration {
@ -111,4 +113,11 @@ export class AbpConfiguration implements IAbpConfiguration {
this.multiTenancy = new MultiTenancy() this.multiTenancy = new MultiTenancy()
this.currentTenant = new CurrentTenant() this.currentTenant = new CurrentTenant()
} }
public getSetting(key: string) {
if (this.setting.values && this.setting.values[key]) {
return this.setting.values[key]
}
return undefined
}
} }

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

@ -1,11 +1,15 @@
<template> <template>
<el-dropdown> <el-dropdown trigger="click">
<div> <el-badge
:value="notifications.length"
class="item"
:hidden="notifications.length<=0"
>
<svg-icon <svg-icon
name="message" name="message"
class="message-icon" class="message-icon"
/> />
</div> </el-badge>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<div <div
class="app-container" class="app-container"
@ -16,25 +20,28 @@
> >
<el-tab-pane <el-tab-pane
label="通知" label="通知"
> class="notification"
<div
style="overflow-x: hidden;overflow-y: auto;"
> >
<List <List
size="small" size="small"
> >
<ListItem <ListItem
v-for="(notify, index) in notifications" v-for="(notify) in notifications"
:key="index" :key="notify.id"
> >
<ListItemMeta <ListItemMeta
:title="notify.Message" :title="notify.message"
:description="formatDateTime(notify.DateTime)" :description="formatDateTime(notify.datetime)"
avatar="ios-person" @click="handleClickNotification(notify.id)"
>
<template slot="avatar">
<Avatar
icon="ios-person"
/> />
</template>
</ListItemMeta>
</ListItem> </ListItem>
</List> </List>
</div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="消息"> <el-tab-pane label="消息">
消息系统 消息系统
@ -50,6 +57,7 @@ import { HubConnection, HubConnectionBuilder, HubConnectionState } from '@micros
import { Component, Vue } from 'vue-property-decorator' import { Component, Vue } from 'vue-property-decorator'
import { UserModule } from '@/store/modules/user' import { UserModule } from '@/store/modules/user'
import { dateFormat } from '@/utils/index' import { dateFormat } from '@/utils/index'
import { MessageType } from 'element-ui/types/message'
enum Severity { enum Severity {
success = 0, success = 0,
@ -59,11 +67,17 @@ enum Severity {
fatal = 40 fatal = 40
} }
enum ReadState {
Read = 0,
UnRead = 1
}
class Notification { class Notification {
Title!: string id!: string
Message!: string title!: string
DateTime!: Date message!: string
Severity!: Severity datetime!: Date
severity!: Severity
} }
@Component({ @Component({
@ -82,14 +96,14 @@ export default class extends Vue {
} }
private renderIconType(item: any) { private renderIconType(item: any) {
if (item.Severity !== Severity.success) { if (item.severity !== Severity.success) {
return ' el-icon-circle-close' return ' el-icon-circle-close'
} }
return ' el-icon-circle-check' return ' el-icon-circle-check'
} }
private renderIconStyle(item: any) { private renderIconStyle(item: any) {
if (item.Severity !== Severity.success) { if (item.severity !== Severity.success) {
return 'backgroundColor: #f56a00' return 'backgroundColor: #f56a00'
} }
return 'backgroundColor: #87d068' return 'backgroundColor: #87d068'
@ -104,9 +118,7 @@ export default class extends Vue {
console.log('start signalr connection...') console.log('start signalr connection...')
if (!this.connection) { if (!this.connection) {
const builder = new HubConnectionBuilder() const builder = new HubConnectionBuilder()
console.log(builder)
const userToken = UserModule.token.replace('Bearer ', '') const userToken = UserModule.token.replace('Bearer ', '')
console.log(userToken)
this.connection = builder this.connection = builder
.withUrl('/signalr-hubs/signalr-hubs/notifications', { accessTokenFactory: () => userToken }) .withUrl('/signalr-hubs/signalr-hubs/notifications', { accessTokenFactory: () => userToken })
.withAutomaticReconnect({ nextRetryDelayInMilliseconds: () => 60000 }) .withAutomaticReconnect({ nextRetryDelayInMilliseconds: () => 60000 })
@ -118,7 +130,16 @@ export default class extends Vue {
}) })
} }
if (this.connection.state !== HubConnectionState.Connected) { if (this.connection.state !== HubConnectionState.Connected) {
this.connection.start() this.connection.start().then(() => {
this.connection.invoke('GetNotification', ReadState.UnRead, 10).then(result => {
console.log(result)
result.items.forEach((notify: any) => {
const notification = notify.data.properties
notification.id = notify.id
this.notifications.push(notification)
})
})
})
} }
} }
@ -132,14 +153,53 @@ export default class extends Vue {
private onNotificationReceived(data: any) { private onNotificationReceived(data: any) {
console.log('received signalr message...') console.log('received signalr message...')
console.log(data) console.log(data)
this.notifications.push(data.data.properties) const notification = data.data.properties
this.$notify.success(data.data.properties.message) this.pushUserNotification(notification)
this.$notify({
title: notification.title,
message: notification.message,
type: this.getNofiyType(data.notificationSeverity)
})
}
private handleClickNotification(notificationId: string) {
console.log('handleClickNotification')
this.connection.invoke('ChangeState', notificationId, ReadState.Read).then(() => {
const removeNotifyIndex = this.notifications.findIndex(n => n.id === notificationId)
this.notifications.splice(removeNotifyIndex)
})
}
private pushUserNotification(notification: any) {
if (this.notifications.length === 20) {
this.notifications.shift()
}
this.notifications.push(notification)
}
private getNofiyType(severity: Severity) {
const mapNotifyType: {[key: number]: MessageType } = {
0: 'success',
10: 'info',
20: 'warning',
30: 'error',
40: 'error'
}
return mapNotifyType[severity]
} }
} }
</script> </script>
<style scoped> <style lang="scss">
.el-scrollbar__wrap{ .item {
overflow-x: hidden; margin-top: 0px;
margin-right: 10px;
}
.item.el-dropdown-selfdefine > .el-badge__content.el-badge__content--undefined.is-fixed {
top: 10px;
}
.notification > .ivu-list.ivu-list-small.ivu-list-horizontal.ivu-list-split{
max-height: 200px;
overflow: auto;
} }
</style> </style>

3
vueJs/src/lang/zh.ts

@ -90,6 +90,9 @@ export default {
login: { login: {
title: '系统登录', title: '系统登录',
logIn: '登录', logIn: '登录',
register: '注册',
notAccount: '没有账户?',
existsAccount: '已有账户?',
userLogin: '用户密码登录', userLogin: '用户密码登录',
phoneLogin: '手机免密登录', phoneLogin: '手机免密登录',
tenantName: '租户', tenantName: '租户',

2
vueJs/src/store/modules/user.ts

@ -137,7 +137,7 @@ class User extends VuexModule implements IUserState {
@Action @Action
public RefreshSession() { public RefreshSession() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const token = getItem(tokenKey) const token = getItem(refreshTokenKey)
if (token) { if (token) {
UserApiService.refreshToken(token).then(result => { UserApiService.refreshToken(token).then(result => {
const token = result.token_type + ' ' + result.access_token const token = result.token_type + ' ' + result.access_token

3
vueJs/src/views/admin/tenants/index.vue

@ -78,7 +78,7 @@
<el-table-column <el-table-column
:label="$t('global.lastModificationTime')" :label="$t('global.lastModificationTime')"
prop="lastModificationTime" prop="lastModificationTime"
width="170px" width="230px"
align="center" align="center"
> >
<template slot-scope="{row}"> <template slot-scope="{row}">
@ -93,6 +93,7 @@
:label="$t('global.operaActions')" :label="$t('global.operaActions')"
align="center" align="center"
width="250px" width="250px"
min-width="250px"
fixed="right" fixed="right"
> >
<template slot-scope="{row}"> <template slot-scope="{row}">

3
vueJs/src/views/admin/users/index.vue

@ -327,9 +327,6 @@ export default class extends Vue {
vertical-align: top; vertical-align: top;
margin-left: 20px; margin-left: 20px;
} }
.el-dropdown + .el-dropdown {
margin-left: 15px;
}
.el-icon-arrow-down { .el-icon-arrow-down {
font-size: 12px; font-size: 12px;
} }

13
vueJs/src/views/login/index.vue

@ -105,14 +105,15 @@
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<el-form-item <el-form-item
v-show="selfRegistration"
label-width="100px" label-width="100px"
label="没有用户?" :label="$t('login.notAccount')"
> >
<el-link <el-link
type="success" type="success"
@click="handleRedirectRegister" @click="handleRedirectRegister"
> >
注册 {{ $t('login.register') }}
</el-link> </el-link>
</el-form-item> </el-form-item>
@ -169,6 +170,14 @@ export default class extends Vue {
return AbpConfigurationModule.configuration.multiTenancy.isEnabled return AbpConfigurationModule.configuration.multiTenancy.isEnabled
} }
get selfRegistration() {
const selfRegister = AbpConfigurationModule.configuration.setting.values['Abp.Account.IsSelfRegistrationEnabled']
if (selfRegister) {
return selfRegister === 'true'
}
return false
}
private validatePhoneNumberValue = (rule: any, value: string, callback: any) => { private validatePhoneNumberValue = (rule: any, value: string, callback: any) => {
const phoneReg = /^1[34578]\d{9}$/ const phoneReg = /^1[34578]\d{9}$/
if (!value || !phoneReg.test(value)) { if (!value || !phoneReg.test(value)) {

78
vueJs/src/views/register/index.vue

@ -2,8 +2,8 @@
<div class="login-container"> <div class="login-container">
<el-form <el-form
ref="formLogin" ref="formLogin"
:model="loginForm" :model="registerForm"
:rules="loginFormRules" :rules="registerFormRules"
label-position="left" label-position="left"
label-width="0px" label-width="0px"
class="demo-ruleForm login-page" class="demo-ruleForm login-page"
@ -17,14 +17,14 @@
<el-form-item label-width="0px"> <el-form-item label-width="0px">
<tenant-box <tenant-box
v-if="isMultiEnabled" v-if="isMultiEnabled"
v-model="loginForm.tenantName" v-model="registerForm.tenantName"
/> />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
prop="username" prop="username"
> >
<el-input <el-input
v-model="loginForm.username" v-model="registerForm.username"
prefix-icon="el-icon-user" prefix-icon="el-icon-user"
type="text" type="text"
auto-complete="off" auto-complete="off"
@ -38,13 +38,13 @@
<el-input <el-input
:key="passwordType" :key="passwordType"
ref="password" ref="password"
v-model="loginForm.password" v-model="registerForm.password"
prefix-icon="el-icon-lock" prefix-icon="el-icon-lock"
:type="passwordType" :type="passwordType"
:placeholder="$t('global.pleaseInputBy', {key: $t('login.password')})" :placeholder="$t('global.pleaseInputBy', {key: $t('login.password')})"
name="password" name="password"
tabindex="2" tabindex="2"
@keyup.enter.native="handleUserLogin" @keyup.enter.native="handleUserRegister"
/> />
<span <span
class="show-pwd" class="show-pwd"
@ -58,7 +58,7 @@
> >
<el-input <el-input
ref="loginItemPhone" ref="loginItemPhone"
v-model="loginForm.phoneNumber" v-model="registerForm.phoneNumber"
prefix-icon="el-icon-mobile-phone" prefix-icon="el-icon-mobile-phone"
type="text" type="text"
maxlength="11" maxlength="11"
@ -72,7 +72,7 @@
<el-row> <el-row>
<el-col :span="16"> <el-col :span="16">
<el-input <el-input
v-model="loginForm.verifyCode" v-model="registerForm.verifyCode"
auto-complete="off" auto-complete="off"
:placeholder="$t('global.pleaseInputBy', {key: $t('login.phoneVerifyCode')})" :placeholder="$t('global.pleaseInputBy', {key: $t('login.phoneVerifyCode')})"
prefix-icon="el-icon-key" prefix-icon="el-icon-key"
@ -93,13 +93,13 @@
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label-width="100px" label-width="100px"
label="已有用户?" :label="$t('login.existsAccount')"
> >
<el-link <el-link
type="success" type="success"
@click="handleRedirectLogin" @click="handleRedirectLogin"
> >
登录 {{ $t('login.logIn') }}
</el-link> </el-link>
</el-form-item> </el-form-item>
@ -107,10 +107,10 @@
<el-button <el-button
type="primary" type="primary"
style="width:100%;" style="width:100%;"
:loading="logining" :loading="registing"
@click="handleUserLogin" @click="handleUserRegister"
> >
{{ $t('login.logIn') }} {{ $t('login.register') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -120,7 +120,6 @@
<script lang="ts"> <script lang="ts">
import { Input } from 'element-ui' import { Input } from 'element-ui'
import { Route } from 'vue-router' import { Route } from 'vue-router'
import { UserModule } from '@/store/modules/user'
import { Dictionary } from 'vue-router/types/router' import { Dictionary } from 'vue-router/types/router'
import TenantBox from '@/components/TenantBox/index.vue' import TenantBox from '@/components/TenantBox/index.vue'
import LangSelect from '@/components/LangSelect/index.vue' import LangSelect from '@/components/LangSelect/index.vue'
@ -136,15 +135,14 @@ import { AbpConfigurationModule } from '@/store/modules/abp'
} }
}) })
export default class extends Vue { export default class extends Vue {
private loginType = 'password'
private passwordType = 'password' private passwordType = 'password'
private redirect?: string private redirect?: string
private sendTimer: any private sendTimer: any
private sending = false private sending = false
private sendButtonName = this.l('login.sendVerifyCode') private sendButtonName = this.l('login.sendVerifyCode')
private logining = false private registing = false
private loginForm = { private registerForm = {
tenantName: '', tenantName: '',
username: '', username: '',
password: '', password: '',
@ -165,7 +163,7 @@ export default class extends Vue {
} }
} }
private loginFormRules = { private registerFormRules = {
username: [ username: [
{ {
required: true, message: this.l('global.pleaseInputBy', { key: this.l('login.username') }), trigger: 'blur' required: true, message: this.l('global.pleaseInputBy', { key: this.l('login.username') }), trigger: 'blur'
@ -219,33 +217,23 @@ export default class extends Vue {
this.$router.replace('login') this.$router.replace('login')
} }
private handleUserLogin() { private handleUserRegister() {
const frmLogin = this.$refs.formLogin as any const frmLogin = this.$refs.formLogin as any
frmLogin.validate(async(valid: boolean) => { frmLogin.validate(async(valid: boolean) => {
if (valid) { if (valid) {
this.logining = true this.registing = true
try { try {
if (this.loginType === 'password') { const userRegister = new UserRegisterData()
const userLogin = { userRegister.phoneNumber = this.registerForm.phoneNumber
tenantName: this.loginForm.tenantName, userRegister.verifyCode = this.registerForm.verifyCode
username: this.loginForm.username, userRegister.name = this.registerForm.username
password: this.loginForm.password userRegister.userName = this.registerForm.username
} userRegister.password = this.registerForm.password
await UserModule.Login(userLogin) UserService.userRegister(userRegister).then(() => {
this.$router.push({ this.handleRedirectLogin()
path: this.redirect || '/' }).finally(() => {
}) this.resetLoginButton()
} else {
const phoneLogin = {
tenantName: this.loginForm.tenantName,
phoneNumber: this.loginForm.phoneNumber,
verifyCode: this.loginForm.verifyCode
}
await UserModule.PhoneLogin(phoneLogin)
this.$router.push({
path: this.redirect || '/'
}) })
}
} catch { } catch {
this.resetLoginButton() this.resetLoginButton()
} }
@ -259,8 +247,8 @@ export default class extends Vue {
if (!errorMsg) { if (!errorMsg) {
this.sending = true this.sending = true
const phoneVerify = new PhoneVerify() const phoneVerify = new PhoneVerify()
phoneVerify.phoneNumber = this.loginForm.phoneNumber phoneVerify.phoneNumber = this.registerForm.phoneNumber
phoneVerify.verifyType = VerifyType.signin phoneVerify.verifyType = VerifyType.register
UserService.sendPhoneVerifyCode(phoneVerify).then(() => { UserService.sendPhoneVerifyCode(phoneVerify).then(() => {
let interValTime = 60 let interValTime = 60
const sendingName = this.l('login.afterSendVerifyCode') const sendingName = this.l('login.afterSendVerifyCode')
@ -281,17 +269,13 @@ export default class extends Vue {
}) })
} }
private handleLoginTabChanged(tab: any) {
this.loginType = tab.paneName === '1' ? 'phone' : 'password'
}
private l(name: string, values?: any[] | { [key: string]: any }) { private l(name: string, values?: any[] | { [key: string]: any }) {
return this.$t(name, values).toString() return this.$t(name, values).toString()
} }
private resetLoginButton() { private resetLoginButton() {
setTimeout(() => { setTimeout(() => {
this.logining = false this.registing = false
}, 0.5 * 1000) }, 0.5 * 1000)
} }
} }

Loading…
Cancel
Save