Abp Vnext Pro?的 Vue3 實(shí)現(xiàn)版本 開箱即用的中后臺(tái)前端/設(shè)計(jì)解決方案
開始 系統(tǒng)功能 項(xiàng)目結(jié)構(gòu) 后端 .
├── Directory.Build.props nuget 版本控制
├── frameworks # 公共模塊
│ ├── CAP # dotnetcore.cap
│ └── Extensions # 自定義擴(kuò)展
├── gateways # 網(wǎng)關(guān)
├── modules # 模塊
│ ├── DataDictionaryManagement # 數(shù)據(jù)字典
│ └── NotificationManagement # 通知服務(wù)
├── services # 公共靜態(tài)資源目錄
│ ├── host # 啟動(dòng)模塊
│ ├── CompanyName.ProjectName.HttpApi.Host # admin ui host
│ └── CompanyName.ProjectName.IdentityServer # IdentityServer host
│ ├── src # 源碼
│ └── CompanyName.ProjectName.DbMigrator # 遷移控制臺(tái)程序
│ └── test # 單元測試
前端 .
├── _nginx # docker 打包
├── build # 打包腳本相關(guān)
│ ├── config # 配置文件
│ ├── generate # 生成器
│ ├── script # 腳本
│ └── vite # vite配置
├── mock # mock文件夾
├── public # 公共靜態(tài)資源目錄
├── src # 主目錄
│ ├── api # 接口文件
│ ├── assets # 資源文件
│ │ ├── icons # icon sprite 圖標(biāo)文件夾
│ │ ├── images # 項(xiàng)目存放圖片的文件夾
│ │ └── svg # 項(xiàng)目存放svg圖片的文件夾
│ ├── components # 公共組件
│ ├── design # 樣式文件
│ ├── directives # 指令
│ ├── enums # 枚舉/常量
│ ├── hooks # hook
│ │ ├── component # 組件相關(guān)hook
│ │ ├── core # 基礎(chǔ)hook
│ │ ├── event # 事件相關(guān)hook
│ │ ├── setting # 配置相關(guān)hook
│ │ └── web # web相關(guān)hook
│ ├── layouts # 布局文件
│ │ ├── default # 默認(rèn)布局
│ │ ├── iframe # iframe布局
│ │ └── page # 頁面布局
│ ├── locales # 多語言
│ ├── logics # 邏輯
│ ├── main.ts # 主入口
│ ├── router # 路由配置
│ ├── services # Nswag生成的代理
│ │ ├── ServiceProxies.ts # Nswag生成的代理
│ │ ├── ServiceProxyBase.ts # Nswag生成的代理攔截器
│ ├── settings # 項(xiàng)目配置
│ │ ├── componentSetting.ts # 組件配置
│ │ ├── designSetting.ts # 樣式配置
│ │ ├── encryptionSetting.ts # 加密配置
│ │ ├── localeSetting.ts # 多語言配置
│ │ ├── projectSetting.ts # 項(xiàng)目配置
│ │ └── siteSetting.ts # 站點(diǎn)配置
│ ├── store # 數(shù)據(jù)倉庫
│ ├── utils # 工具類
│ └── views # 頁面
├── test # 測試
│ └── server # 測試用到的服務(wù)
│ ├── api # 測試服務(wù)器
│ ├── upload # 測試上傳服務(wù)器
│ └── websocket # 測試ws服務(wù)器
├── types # 類型文件
├── vite.config.ts # vite配置文件
└── windi.config.ts # windcss配置文件
運(yùn)行項(xiàng)目前提 獲取項(xiàng)目 git clone https://github.com/WangJunZzz/abp-vnext-pro.git
OR
git clone https://github.com/WangJunZzz/abp-vnext-pro-gui.git
啟動(dòng) 修改 HttpApi.Host-> appsettings.development.json 的數(shù)據(jù)庫連接字符串,Redis, RabbitMq,Es 地址即可(如果沒有 es 也可以運(yùn)行,只是前端 es 日志頁面無法使用而已,不影響后端項(xiàng)目啟動(dòng))
修改 IdentityServer-> appsettings.development.json 數(shù)據(jù)庫連接字符串
修改 DbMigrator-> appsettings.json 數(shù)據(jù)庫連接字符串
運(yùn)行 DbMigrator 生成數(shù)據(jù)庫
啟動(dòng) HttpApi.Host 和 IdentityServer
前端 yarn 之后,執(zhí)行 npm run dev 啟動(dòng)
配置說明 {// Serilog 日志配置,生成環(huán)境修改日志級(jí)別"Serilog": {"MinimumLevel": {"Default": "Information","Override": {"Microsoft": "Information","Volo.Abp": "Information","Hangfire": "Information","DotNetCore.CAP": "Information","Serilog.AspNetCore": "Information"}}},// 跨域設(shè)置"App": {"CorsOrigins": "https://*.ProjectName.com,http://localhost:4200,http://localhost:3100"},// 數(shù)據(jù)庫連接字符串,修改為你本地的mysql地址"ConnectionStrings": {"Default": "Data Source=localhost;Database=CompanyNameProjectNameDB;uid=root;pwd=1q2w3E*;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"},// Redis緩存"Cache": {"Redis": {"ConnectionString": "localhost","Password": "mypassword","DatabaseId": 0}},// Jwt配置"Jwt": {"Audience": "CompanyNameProjectName",//客戶端標(biāo)識(shí)"SecurityKey": "dzehzRz9a8asdfasfdadfasdfasdfafsdadfasbasdf=","Issuer": "CompanyNameProjectName",//簽發(fā)者"ExpirationTime": 24//過期時(shí)間 hour},// 使用了Dotnetcore.cap的rabbitmq,false的情況基于內(nèi)存"Cap": {"Enabled": "false","RabbitMq": {"HostName": "localhost","UserName": "admin","Password": "admin"}},// es日志地址配置"LogToElasticSearch": {"Enabled": "true","ElasticSearch": {"Url": "http://es.cn","IndexFormat": "companyname.projectname.development","UserName": "elastic","Password": "aVVhjQ95RP7nbwNy","DashboardIndex": "companyname.projectname"}},// identityserver地址"HttpClient": {"Sts": {"Url": "http://localhost:44354"}},// Consul 服務(wù)發(fā)現(xiàn)和治理"Consul": {"Enabled": false,"Host": "http://localhost:8500","Service": "Project-Service"}
}
{"App": {"SelfUrl": "https://localhost:44354","ClientUrl": "http://localhost:4200","CorsOrigins": "https://*.ProjectName.com,http://localhost:4200,https://localhost:44307,https://localhost:44315","RedirectAllowedUrls": "http://localhost:4200,https://localhost:44307"},// mysql連接字符串"ConnectionStrings": {"Default": "Data Source=localhost;Database=CompanyNameProjectNameDB;uid=root;pwd=1q2w3E*;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"},// Redis"Redis": {"Configuration": "localhost,password=mypassword"}
}
// 遷移數(shù)據(jù)庫"ConnectionStrings": {"Default": "Data Source=localhost;Database=CompanyNameProjectNameDB;uid=root;pwd=1q2w3E*;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"}
前端 // 一定要打Tags,因?yàn)榍岸藭?huì)根據(jù)這個(gè)生成代理類
// 建議參數(shù)都封裝為一個(gè)Input
[SwaggerOperation(summary: "登錄", Tags = new[] {"Account"})]
public Task<LoginOutput> LoginAsync(LoginInput input)
{return _loginAppService.LoginAsync(input);
}
"documentGenerator": {"fromDocument": {"url": "http://localhost:44315/swagger/v1/swagger.json", // 代理地址,只有生成的時(shí)候用,不區(qū)分環(huán)境}}
npm run nswag
健康檢查 模塊 用戶管理 角色管理 權(quán)限定義(Application.Contracts 層)
Abp 會(huì)自動(dòng)掃描繼承 PermissionDefinitionProvider
文檔 Abp 官方
在 Http.Api 的 Controller 打上 Authorize
設(shè)置管理 消息通知 /// <summary>
/// 發(fā)送普通文本消息
/// </summary>
/// <returns></returns>
/// <exception cref="NotificationManagementDomainException"></exception>
public async Task<Notification> SendCommonTextAsync(string title, string content, List<Guid> receiveIds)
{if (receiveIds is {Count: 0}){throw new NotificationManagementDomainException("消息接收人不能為空");var senderId = Guid.Empty;if (_currentUser?.Id != null){senderId = _currentUser.Id.Value;var entity = new Notification(GuidGenerator.Create(), title, content, MessageType.Text, senderId);foreach (var item in receiveIds){entity.AddNotificationSubscription(GuidGenerator.Create(), item);var notificationEto = ObjectMapper.Map<Notification, NotificationEto>(entity);// 發(fā)送集成事件entity.AddCreatedNotificationDistributedEvent(new CreatedNotificationDistributedEvent(notificationEto));return entity = await _notificationRepository.InsertAsync(entity);
}
/// <summary>
/// 發(fā)送消息
/// </summary>
public async Task SendMessageAsync(string title, string content, MessageType messageType, List<string> users)
{switch (messageType){case MessageType.Text:await SendMessageToClientByUserIdAsync(new SendNotificationDto(title, content, messageType), users);break;case MessageType.BroadCast:await SendMessageToAllClientAsync(new SendNotificationDto(title, content, messageType));break;default:throw new UserFriendlyException("未知的消息類型");}
}
// src/hooks/web/useSignalR.js
import * as signalR from "@microsoft/signalr";
import { useMessage } from "/@/hooks/web/useMessage";
import { useUserStoreWithOut } from "/@/store/modules/user";
export function useSignalR() {/*** 開始連接SignalR*/function startConnect(): void {let connection = connectionsignalR();//接收普通文本消息connection.on("ReceiveTextMessageAsync", ReceiveTextMessageHandlerAsync);//接收廣播消息connection.on("ReceiveBroadCastMessageAsync", ReceiveBroadCastMessageHandlerAsync);//開始連接connection.start();}/*** 連接signalr*/function connectionsignalR(): signalR.HubConnection {const userStore = useUserStoreWithOut();const token = userStore.getToken;const url = (import.meta.env.VITE_WEBSOCKE_URL as string) + "/ws/signalr/notification";const connection = new signalR.HubConnectionBuilder().withUrl(url, {accessTokenFactory: () => token,skipNegotiation: true,transport: signalR.HttpTransportType.WebSockets,}).withAutomaticReconnect({nextRetryDelayInMilliseconds: (retryContext) => {//重連規(guī)則:重連次數(shù)<300:間隔1s;重試次數(shù)<3000:間隔3s;重試次數(shù)>3000:間隔30slet count = retryContext.previousRetryCount / 300;if (count < 1) {//重試次數(shù)<300,間隔1sreturn 1000;} else if (count < 10) {//重試次數(shù)>300:間隔5sreturn 1000 * 5;} //重試次數(shù)>3000:間隔30selse {return 1000 * 30;}},}).configureLogging(signalR.LogLevel.Debug).build();return connection;}/*** 接收文本消息* @param message 消息體*/function ReceiveTextMessageHandlerAsync(message: any) {console.log(message);const { notification } = useMessage();notification.open({message: message.title,description: message.content,});}/*** 接收廣播消息* @param message 消息體*/function ReceiveBroadCastMessageHandlerAsync(message: any) {const { notification } = useMessage();notification.open({message: message.title,description: message.content,});}return { startConnect };
}
審計(jì)日志 ES 日志 "LogToElasticSearch": {"Enabled": "false", // 如果為fasel,日志也會(huì)寫入到本地,安裝ELK,參考上面的docker-compose"ElasticSearch": {"Url": "http://es.cn","IndexFormat": "companyname.projectname.development","UserName": "elastic","Password": "aVVhjQ95RP7nbwNy","DashboardIndex": "companyname.projectname"}},
后臺(tái)任務(wù) public override void OnPostApplicationInitialization(ApplicationInitializationContext context)
{context.CreateRecurringJob();base.OnPostApplicationInitialization(context);
}
集成事件 "Cap": {"Enabled": "false", //如果為false 默認(rèn)使用內(nèi)存級(jí)別的隊(duì)列,否則請(qǐng)安裝rabbitmq"RabbitMq": {"HostName": "localhost","UserName": "admin","Password": "admin"}},private void ConfigurationCap(ServiceConfigurationContext context)
{var configuration = context.Services.GetConfiguration();var enabled = configuration.GetValue<bool>("Cap:Enabled", false);if (enabled){context.AddAbpCap(capOptions =>{capOptions.UseEntityFramework<ProjectNameDbContext>();capOptions.UseRabbitMQ(option =>{option.HostName = configuration.GetValue<string>("Cap:RabbitMq:HostName");option.UserName = configuration.GetValue<string>("Cap:RabbitMq:UserName");option.Password = configuration.GetValue<string>("Cap:RabbitMq:Password");});var hostingEnvironment = context.Services.GetHostingEnvironment();bool auth = !hostingEnvironment.IsDevelopment();capOptions.UseDashboard(options => { options.UseAuth = auth; });});}else{context.AddAbpCap(capOptions =>{capOptions.UseInMemoryStorage();capOptions.UseInMemoryMessageQueue();var hostingEnvironment = context.Services.GetHostingEnvironment();bool auth = !hostingEnvironment.IsDevelopment();capOptions.UseDashboard(options => { options.UseAuth = auth; });});}
}
// 發(fā)送集成事件
entity.AddCreatedNotificationDistributedEvent(new CreatedNotificationDistributedEvent(notificationEto));
/// <summary>
/// 創(chuàng)建消息事件處理
/// </summary>
public classCreatedNotificationDistributedEventHandler : IDistributedEventHandler<CreatedNotificationDistributedEvent>,ITransientDependency
{private readonly INotificationAppService _hubAppService;public CreatedNotificationDistributedEventHandler(INotificationAppService hubAppService){_hubAppService = hubAppService;}public Task HandleEventAsync(CreatedNotificationDistributedEvent eventData){return _hubAppService.SendMessageAsync(eventData.NotificationEto.Title,eventData.NotificationEto.Content,eventData.NotificationEto.MessageType,eventData.NotificationEto.NotificationSubscriptions.Select(e => e.ReceiveId.ToString()).ToList());}
}
身份認(rèn)證中心 IdentityServer4
可重寫登錄界面 UI
租戶管理 Ocelot 網(wǎng)關(guān)(可選) 部署 Docker 方式 HttpApi.Host FROM mcr.microsoft.com/dotnet/aspnet:5.0# 創(chuàng)建目錄
RUN mkdir /appCOPY publish /app# 設(shè)置工作目錄
WORKDIR /app# 暴露80端口
EXPOSE 80# 設(shè)置環(huán)境變量
ENV ASPNETCORE_ENVIRONMENT=ProductionENTRYPOINT ["dotnet", "CompanyName.ProjectName.HttpApi.Host.dll"]
docker build -t abp-vnext-pro-admin .
docker run -itd --name abp-vnext-pro-admin -p 8011:80 abp-vnext-pro-admin
IdentityServer.Host 前端 npm run build
FROM nginx:1.17.3-alpine as base
EXPOSE 80
COPY /_nginx/nginx.conf /etc/nginx/nginx.conf
COPY /_nginx/env.js /etc/nginx/env.js
COPY /_nginx/default.conf /etc/nginx/conf.d/default.conf
COPY /dist/ /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]
docker build -t abp-vnext-pro-ui .
docker run -itd --name abp-vnext-pro-ui -p 8012:80 abp-vnext-pro-ui
常見問題 VS 編譯項(xiàng)目字符串超過 256 個(gè)字符 Hangfire 和 Cap 界面加載不出來
總結(jié)
以上是生活随笔 為你收集整理的Abp Vnext Vue3 的版本实现 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。