1.
sync Sqlite structure started sync Sqlite structure succeed sync data started table: ai_template sync data succeed table: ad_dictionary_type sync data succeed table: ad_dictionary sync data succeed table: ad_user sync data succeed table: ad_user_staff sync data succeed table: ad_org sync data succeed table: ad_role sync data succeed table: ad_api sync data succeed table: ad_view sync data succeed table: ad_permission sync data succeed table: ad_user_role sync data succeed table: ad_user_org sync data succeed table: ad_role_permission sync data succeed table: ad_tenant sync data succeed table: ad_tenant_permission import data [] table: ad_permission_api sync data succeed sync data succeed SELECT a."Id", a."Topic", a."Body", a."Round", a."Interval", a."IntervalArgument", a."CreateTime", a."LastRunTime", a."CurrentRound", a."ErrorTimes", a."Status" FROM "app_task" a WHERE (a."Status" = 0 AND (a."Round" < 0 OR a."CurrentRound" < a."Round")) info: Microsoft.Hosting.Lifetime[14] Now listening on: http://[::]:8000 info: ZhonTai.Admin.Core.Auth.ResponseAuthenticationHandler[12] AuthenticationScheme: ResponseAuthenticationHandler was challenged. AuthenticationScheme: ResponseAuthenticationHandler was challenged. info: ZhonTai.Admin.Core.Auth.ResponseAuthenticationHandler[12] AuthenticationScheme: ResponseAuthenticationHandler was challenged. AuthenticationScheme: ResponseAuthenticationHandler was challenged. fail: ZhonTai.Admin.Core.Filters.ValidateInputFilter[0] Password:密码不能为空!|UserName:用户名不能为空! Password:密码不能为空!|UserName:用户名不能为空! SELECT a."Id", a."CreatedUserId", a."CreatedUserName", a."CreatedTime", a."ModifiedUserId", a."ModifiedUserName", a."ModifiedTime", a."IsDeleted", a."UserName", a."Password", a."Name", a."Mobile", a."Email", a."OrgId", a."ManagerUserId", a."NickName", a."Avatar", a."Status", a."Type" FROM "ad_user" a WHERE (a."UserName" = 'admin' AND a."Password" = '96E79218965EB72C92A549DD5A330112') limit 0,1 INSERT INTO "ad_login_log"("Id", "CreatedUserId", "CreatedUserName", "CreatedTime", "Name", "IP", "Browser", "Os", "Device", "BrowserInfo", "ElapsedMilliseconds", "Status", "Msg", "Result") VALUES(479375531114565, 161223411986501, 'admin', datetime(current_timestamp,'localtime'), 'admin', '127.0.0.1', 'Edge', 'Windows', '', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0', 157, 1, NULL, NULL)
2.
D:\terrywork\aibpm.ui.plus-main>npm install npm ERR! code EINVALIDTAGNAME npm ERR! Invalid tag name "^6.1@.9" of package "@codemirror/lang-javascript@^6.1@.9": Tags may not have any characters that encodeURIComponent encodes. npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\user\AppData\Local\npm-cache\_logs\2023-11-05T16_08_05_000Z-debug-0.log
添加菜单,在【权限管理】-【权限管理】里. 可以添加【分组】,【菜单】,【权限点】
后端菜单权限在【权限管理】-》【角色管理】-》【菜单权限】
有些图标显示不了,应该是css font字体被block了,
src\utils\setIconfont.ts 里面的
// 字体图标 url const cssCdnUrlList: Array<string> = [ '//at.alicdn.com/t/c/font_2298093_rnp72ifj3ba.css', '//cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.css', ]
src\utils\getStyleSheets.ts 里的,改一下网址 cdn.staticfile.org
for (let i = 0; i < styles.length; i++) { if (styles[i].href && styles[i].href.indexOf('netdna.bootstrapcdn.com') > -1) { sheetsList.push(styles[i]) } }
菜单权限是后端控制的
更改左边顶部的logo

using FreeSql.DataAnnotations; using ZhonTai.Admin.Core.Entities; namespace Fox.ERP.Model { /// <summary> /// 料品 /// </summary> [Table(Name = "erp_product")] [Index("idx_{tablename}_01", nameof(TenantId), true)] public class ProductEntity : EntityTenant { /// <summary> /// 料品名称 /// </summary> [Column(StringLength = 100)] public string Name { get; set; } /// <summary> /// 料品编码 /// </summary> [Column(StringLength = 100)] public string Sku { get; set; } /// <summary> /// 料品描述 /// </summary> public string ShortDescription { get; set; } /// <summary> /// 是否虚拟点卡 /// </summary> public bool IsGiftCard { get; set; } /// <summary> /// 是否可下载 /// </summary> public bool IsDownload { get; set; } } }

using Fox.ERP.Model; using ZhonTai.Admin.Core.Repositories; namespace Fox.ERP.Repository { /// <summary> /// /// </summary> public interface IProductRepository : IRepositoryBase<ProductEntity> { } }

using Fox.ERP.Model; using ZhonTai.Admin.Core.Consts; using ZhonTai.Admin.Core.Db.Transaction; using ZhonTai.Admin.Core.Repositories; namespace Fox.ERP.Repository { /// <summary> /// /// </summary> public class ProductRepository : RepositoryBase<ProductEntity>, IProductRepository { /// <summary> /// /// </summary> /// <param name="muowm"></param> public ProductRepository(UnitOfWorkManagerCloud muowm) : base(DbKeys.AppDb, muowm) { } } }

using Fox.ERP.Services.ERP.Product.Dto; namespace Fox.ERP.Services.ERP.Product { /// <summary> /// 料品服务 /// </summary> public interface IProductService { Task<ProductGetOutput> GetAsync(long id); Task<long> AddAsync(ProductAddInput input); Task UpdateAsync(ProductUpdateInput input); Task DeleteAsync(long id); } }

using Fox.ERP.Model; using Fox.ERP.Repository; using Fox.ERP.Services.ERP.Product.Dto; using ZhonTai.Admin.Core.Attributes; using ZhonTai.Admin.Core.Configs; using ZhonTai.Admin.Services; using ZhonTai.DynamicApi; using ZhonTai.DynamicApi.Attributes; namespace Fox.ERP.Services.ERP.Product { /// <summary> /// 料品服务 /// </summary> [DynamicApi(Area = ERPConstants.AreaName)] public class ProductService : BaseService,IProductService, IDynamicApi { //??究竟应该是构造函数注入,还是LazyGetRequiredService注入呢? private AppConfig _appConfig => LazyGetRequiredService<AppConfig>(); private IProductRepository _productRepository => LazyGetRequiredService<IProductRepository>(); /// <summary> /// 构造函数 /// </summary> public ProductService() { } /// <summary> /// 查询 /// </summary> /// <param name="id"></param> /// <returns></returns> public async Task<ProductGetOutput> GetAsync(long id) { var Entity = await _productRepository.Select .WhereDynamic(id) .ToOneAsync(); var output = Mapper.Map<ProductGetOutput>(Entity); return output; } /// <summary> /// 新增 /// </summary> /// <param name="input"></param> /// <returns></returns> [AdminTransaction] public virtual async Task<long> AddAsync(ProductAddInput input) { var entity = Mapper.Map<ProductEntity>(input); var prod = await _productRepository.InsertAsync(entity); var prodId = prod.Id; return prodId; } /// <summary> /// 修改 /// </summary> /// <param name="input"></param> /// <returns></returns> public async Task UpdateAsync(ProductUpdateInput input) { var entity = Mapper.Map<ProductEntity>(input); await _productRepository.UpdateAsync(entity); } /// <summary> /// 彻底删除 /// </summary> /// <param name="id"></param> /// <returns></returns> [AdminTransaction] public virtual async Task DeleteAsync(long id) { await _productRepository.DeleteAsync(a => a.Id == id); } } }
2. appconfig.json 里 增加projects,调试后端接口
接口文档的调试出现401 未登录,是要先用authorization
3. 后端接口调试完成后,在【接口管理】点【同步】,自动更新接口
前端 gen
根据./templates生成api相关文件,接口更新后执行npm run gen:bpm-api
会生成接口的定义和接口模型

"name": "aibpm.ui", "version": "0.9.3.3", "description": "aibpm vue3 ", "author": "Leo", "license": "MIT", "scripts": { "dev": "vite --force", "build": "vite build", "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/", "format": "npx prettier --write .", "install:pkg": "npm install --registry https://registry.npmmirror.com", "gen:admin-api": "node ./gen/gen-admin-api", "gen:bpm-api": "node ./gen/gen-bpm-api" },
4.【权限管理】里新增【权限点】(查询,新增,修改,删除,批量删除... 等等),要选择API接口,
权限点的编码(api:erp:product:get, api:erp:product:add,api:erp:product:update,api:erp:product:delete 在前端要用到)
5.【视图管理】,新建一个url路由和前端视图地址的对应关系
前端权限判断
<el-button v-auth="'api:erp:product:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button> <el-button v-auth="'api:erp:product:update'" icon="ele-EditPen" size="small" text type="primary" @click="onEdit(row)">编辑</el-button> <my-dropdown-more v-auths="['api:admin:permission:assign', 'api:erp:product:delete']"> <template #dropdown> <el-dropdown-menu> <el-dropdown-item v-if="auth('api:erp:product:delete')" @click="onDelete(row)">删除</el-dropdown-item> </el-dropdown-menu> </template> </my-dropdown-more>
01.前后端分离中台框架后端 Admin.Core 学习-介绍与配置说明 - 易墨 - 博客园 (cnblogs.com)
/// <summary> /// 员工服务 /// </summary> [DynamicApi(Area = BPMConstants.AreaName)] public class MyUserService : UserService, IMyUserService /// <summary> /// 用户服务 /// </summary> [Order(10)] [DynamicApi(Area = AdminConsts.AreaName)] public partial class UserService : BaseService, IUserService, IDynamicApi
var freeSqlBuilder = new FreeSqlBuilder() .UseConnectionString(dbConfig.Type, dbConfig.ConnectionString, providerType) .UseAutoSyncStructure(false) .UseLazyLoading(false) .UseNoneCommandParameter(true);
表实体 | Admin - 后台权限管理 (zhontai.net)
比如客户编码要保持唯一,
在实体的特性加上 [Index("idx_{tablename}_02", nameof(Code), true)]
但这样保持实体,会抛出异常,假如没有捕抓的话,前端就显示内部服务器错误。
需要捕捉异常
var entity = Mapper.Map<SupplierEntity>(input); try { await _SupplierRepository.UpdateAsync(entity); } catch (Exception ex) { ResultOutput.NotOk(ex.Message); throw ResultOutput.Exception("编码不能相同!"); }
动态API的,在【接口管理】点【同步】,生成的接口是把驼峰改成连接线的,比如【SaleOrder】=> sale-order
在mac运行,uploadconfig.json 要改路径,原来的
//上传路径 D:/upload/admin/avatar "uploadPath": "/upload/admin/avatar", //请求路径 "requestPath": "/upload/admin/avatar", "document": { //上传路径 D:/upload/admin/document "uploadPath": "/upload/admin/document",
要改成
1 2 3 4 5 6 | //上传路径 D:/upload/admin/avatar "uploadPath" : "/Users/terryzhang/upload/admin/avatar" , //请求路径 "requestPath" : "/Users/terryzhang/upload/admin/avatar" , //上传路径 D:/upload/admin/document "uploadPath" : "/Users/terryzhang/upload/admin/document" , |
dbconfig.json 改成 //
"connectionString": "Data Source=|DataDirectory|//admindb.db; Pooling=true;Min Pool Size=1",
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
2021-11-06 ABP vNext V5 + VS2022+ .Net 6.0 学习笔记(1)