Compare commits

..

465 Commits

Author SHA1 Message Date
橙子
1689b1b579 update README.md.
Signed-off-by: 橙子 <454313500@qq.com>
2023-04-16 06:35:09 +00:00
橙子
d1a4110c64 update README.md.
Signed-off-by: 橙子 <454313500@qq.com>
2023-04-16 06:34:18 +00:00
橙子
38ab7c9cb4 update README.md.
Signed-off-by: 橙子 <454313500@qq.com>
2023-04-16 06:33:43 +00:00
橙子
b342e407ed 删除文件 Yi.Framework.Net6/src/project/Template 2023-04-15 06:46:24 +00:00
橙子
be550a78f6 删除文件 Yi.Framework.Net6/src/project/BBS 2023-04-15 06:46:15 +00:00
橙子
ccd39474c7 update README.md.
Signed-off-by: 橙子 <454313500@qq.com>
2023-04-08 13:46:57 +00:00
陈淳
386ec5ade0 feat:完善图片模块 2023-04-06 11:47:36 +08:00
橙子
ee6b3b535c doc:更新文档 2023-04-05 22:42:23 +08:00
橙子
d5ee57f04d doc:添加readme 2023-04-05 22:13:30 +08:00
橙子
b6c70bad45 docs:完善文档 2023-04-05 16:08:06 +08:00
橙子
921d35367b feat:更新模块关系 2023-04-03 23:43:10 +08:00
橙子
253bd47c75 fix:修复换行问题 2023-04-03 22:55:49 +08:00
橙子
8fa10cd8c1 1 2023-04-02 22:11:30 +08:00
橙子
01e5b52500 chore:忽略wwwroot 2023-04-02 19:31:39 +08:00
橙子
9c921adc5b feat:完善模块化+缩略图模块 2023-04-02 19:29:44 +08:00
橙子
b90962943a fix:修复请求问题 2023-04-02 00:17:07 +08:00
陈淳
ad4fc6ea9b 1 2023-04-01 18:16:41 +08:00
陈淳
62310ab863 feat:使用缩略图 2023-04-01 10:05:16 +08:00
陈淳
ef7b8cd98f Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework 2023-03-31 16:56:34 +08:00
陈淳
5e128ebf04 fix:完善注册 2023-03-31 16:56:28 +08:00
橙子
b324d98125 feat:完成注册功能 2023-03-30 22:09:36 +08:00
陈淳
8329728b81 fix:修复线上bug 2023-03-30 18:06:57 +08:00
陈淳
ba220e9d55 fix:long类型修复 2023-03-30 13:42:57 +08:00
陈淳
6b2ef71296 feat:完善注册功能 2023-03-30 11:49:39 +08:00
橙子
bcf7802f94 feat:完成注册功能 2023-03-29 23:20:54 +08:00
橙子
769e2cb897 feat:区分配置文件 2023-03-29 22:39:20 +08:00
橙子
d55caf2278 Delete Yi.DataBaseTool.zip 2023-03-29 22:04:45 +08:00
橙子
69ca6677e9 feat:添加配置 2023-03-29 22:03:18 +08:00
陈淳
3d94626ff1 feat:添加短信 2023-03-29 19:30:48 +08:00
橙子
5458819ef5 feat: 添加注册功能 2023-03-28 22:23:00 +08:00
陈淳
2ff8aef1bf feat:修改使用mysql 2023-03-28 14:09:41 +08:00
橙子
16556ddb84 fix:修复bug 2023-03-27 23:15:04 +08:00
陈淳
aef6fe9229 feat:修复bug 2023-03-27 19:36:21 +08:00
橙子
e5460ae3cc feat:完成权限相关、全局配置、优化细节 2023-03-26 16:22:49 +08:00
橙子
b6f4cbfb4f feat:完成仅自己可见功能 2023-03-26 01:28:48 +08:00
橙子
c83fcb7f26 feat:完成底部 2023-03-24 22:39:35 +08:00
陈淳
ab0d126c49 style:优化样式 2023-03-24 18:49:39 +08:00
陈淳
fd3142bc19 feat:完善评论功能 2023-03-24 17:33:05 +08:00
陈淳
af80d8e89b Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework
# Conflicts:
#	Yi.Framework.Net6/src/project/bbs/Yi.BBS.Application.Contracts/Forum/Dtos/Comment/CommentGetListOutputDto.cs
2023-03-24 17:15:46 +08:00
陈淳
c98eefe3ec perf:优化批量dto 2023-03-24 17:13:25 +08:00
橙子
84ec0a7e1c feat: 完成评论功能 2023-03-23 23:12:26 +08:00
陈淳
4babe3e05d feat:完成评论功能搭建 2023-03-23 18:15:30 +08:00
橙子
8213f6f8d7 feat:完成评论功能 2023-03-23 00:08:55 +08:00
陈淳
30329ea4db feat;添加评论 2023-03-22 19:49:20 +08:00
橙子
4b856c4905 feat:完成登录页面+各种细节 2023-03-21 23:05:42 +08:00
橙子
418f4a4785 feat:添加点赞功能 2023-03-21 00:16:24 +08:00
橙子
2d31aeecd1 feat:完成点赞功能 2023-03-20 23:43:49 +08:00
陈淳
b3b3ca3fe4 feat: 完成banner展示模块 2023-03-20 19:46:47 +08:00
陈淳
5a4ac549f6 style: 修改目录 2023-03-20 18:59:34 +08:00
橙子
f4cdeb3dc5 refactor: 重构异常处理 2023-03-19 23:44:38 +08:00
橙子
7b01d4722f feat:完善结构 2023-03-19 23:18:17 +08:00
橙子
019c73ceca feat:完善discuss主题相关功能及界面 2023-03-19 23:12:27 +08:00
橙子
10fef4e2d9 feat:添加个人中心 2023-03-19 00:42:18 +08:00
橙子
c613b185da feat:添加登录页面,完善文章目录 2023-03-18 01:34:07 +08:00
橙子
1bb7ce6805 feat:完善 2023-03-17 00:30:26 +08:00
橙子
9b3d8b5a06 Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework 2023-03-16 21:35:07 +08:00
橙子
5f603e6652 feat:完成登录功能 2023-03-16 21:34:52 +08:00
陈淳
a20bd6933b style:添加登录样式 2023-03-16 20:26:29 +08:00
陈淳
13ea3ae9b3 feat:添加素材 2023-03-16 19:46:21 +08:00
陈淳
a73920f4c3 feat:搭建登录页面 2023-03-15 13:53:34 +08:00
陈淳
543d54f844 perf:优化文章内容处理 2023-03-15 13:38:05 +08:00
陈淳
4bd374e747 style:修改子文章样式 2023-03-15 09:55:08 +08:00
橙子
295cf5e066 feat: 添加子文章crud 2023-03-14 22:58:35 +08:00
陈淳
2824e1325d feat: 完成头像组件封装 2023-03-14 16:32:47 +08:00
陈淳
cd9e27bcf3 perf: 改用sqlsugar db上下文对象 2023-03-14 09:22:20 +08:00
橙子
9600e450af feat: 添加头像信息封装 2023-03-13 23:24:10 +08:00
陈淳
8e56667760 feat: 完成子文章的操作 2023-03-12 19:49:08 +08:00
陈淳
1eac218910 feat: 添加log配置 2023-03-12 14:13:39 +08:00
橙子
27962cd25f feat: 完善文章编辑功能 2023-03-12 01:50:11 +08:00
橙子
3de32945f2 feat: 添加文章页面 2023-03-11 17:00:36 +08:00
橙子
9593b68d33 feat: 修复雪花id问题 2023-03-11 15:02:50 +08:00
橙子
9166fc50aa feat: 完成字典模块改造,前端完成首页界面 2023-03-11 01:16:28 +08:00
橙子
fb5342594f Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework 2023-03-10 22:02:27 +08:00
橙子
733ff867e9 feat: 完善基础页面 2023-03-10 22:02:19 +08:00
陈淳
d3f9b43b12 refactor: 删除注入代码 2023-03-09 08:45:07 +08:00
陈淳
30ab479315 feat:添加编辑框 2023-03-07 18:44:39 +08:00
橙子
9d3559cddb feat: 添加文章页面 2023-03-07 00:02:48 +08:00
橙子
6d3edff5b6 feat:添加头像 2023-03-05 23:48:07 +08:00
橙子
619471369d Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework 2023-03-05 20:30:53 +08:00
橙子
2d328234a1 faet: 添加bbs vue3前端 2023-03-05 20:30:44 +08:00
陈淳
06acfe1ee3 feat:添加日志模块 2023-03-04 18:53:33 +08:00
橙子
a55cba4c1f feat: 完成首页界面搭建 2023-03-03 23:56:37 +08:00
橙子
61f8a07753 feat: 添加bbs前端搭建 2023-03-03 21:58:58 +08:00
橙子
e0da9e1a87 fix: 完善修复种子数据 2023-03-03 21:26:18 +08:00
陈淳
3ba3ea6317 fix: 修复种子数据问题,等待完善 2023-03-03 18:26:24 +08:00
陈淳
834e40d6f2 feat: 完善种子数据功能 2023-03-03 18:12:44 +08:00
陈淳
f93cccd849 feat: 添加sqlsugar上下文对象、及审计日志 2023-03-02 11:05:38 +08:00
陈淳
0127b43374 feat: 添加多租户模块 2023-03-02 10:19:18 +08:00
陈淳
5cea38e95c perf: 优化全部模块程序集 2023-03-02 09:40:46 +08:00
陈淳
964b8aa5f6 feat: 添加多租户搭建 2023-02-28 15:44:40 +08:00
陈淳
33616de6c8 fix: 修复currenuser为空问题 2023-02-27 08:52:45 +08:00
橙子
6bf490f7da Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework 2023-02-26 13:17:44 +08:00
橙子
3e31f7783f feat: 添加vue3bbs前端 2023-02-26 13:17:19 +08:00
陈淳
05f5122c0b fix: 修复头像更新覆盖问题 2023-02-23 14:38:31 +08:00
陈淳
93de208ac0 feat: 前端添加完善头像功能 2023-02-23 14:37:33 +08:00
陈淳
272466bbbf feat: 完成文件模块开发 2023-02-23 14:15:24 +08:00
陈淳
8639372513 feat: 完成合并 2023-02-22 19:21:21 +08:00
陈淳
49acf0c7c9 feat: 完成操作日志模块+添加文件模块 2023-02-22 17:30:20 +08:00
陈淳
232cbad5bd fix: 修复引入问题 2023-02-22 17:14:30 +08:00
陈淳
aa2f9d4f50 Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework 2023-02-22 17:03:50 +08:00
陈淳
99aa7d3361 refactor: 抽象操作日志模块 2023-02-22 16:49:24 +08:00
橙子
72decd970a fix: 修复操作日志前端显示问题 2023-02-21 21:15:05 +08:00
陈淳
fa4e0b3752 perf: 优化操作日志方式 2023-02-21 19:55:05 +08:00
陈淳
6615229003 feat: 添加操作日志搭建 2023-02-21 19:37:48 +08:00
陈淳
24300e6e50 feat: 优化权限使用方式 2023-02-21 18:56:51 +08:00
橙子
f8445ab2e4 feat: 完成登录日志记录功能 2023-02-20 21:10:06 +08:00
陈淳
9645decf59 fix: 修复角色选择菜单问题 2023-02-20 19:55:52 +08:00
陈淳
f34c33b0d8 feat: 添加登录role鉴权令牌 2023-02-20 11:36:45 +08:00
陈淳
5cd20b1e22 refactor: 重构当前用户模块 2023-02-20 11:31:40 +08:00
橙子
d65c565127 refactor: 重构markdown 2023-02-20 00:24:57 +08:00
橙子
2f654a1772 feat: 添加bbs前端 2023-02-19 23:50:42 +08:00
橙子
380f728de2 feat: 添加用户、角色状态修改 2023-02-19 22:18:04 +08:00
橙子
dffdaa8d68 feat: 添加事件总线 2023-02-19 20:57:31 +08:00
橙子
1f33204697 feat: 完成查询动态条件筛选,做一个快乐的crud boy 2023-02-19 17:18:52 +08:00
橙子
0566606bfb feat: 代码生成工具5分钟完成参数设置 2023-02-19 17:04:17 +08:00
橙子
e6f95d0cd8 feat: 完善各个模块功能 2023-02-19 16:49:11 +08:00
橙子
8eda2cd814 refactor: 基础设施 2023-02-19 11:34:15 +08:00
橙子
961634981a Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework 2023-02-16 23:07:49 +08:00
橙子
50aadb574d Update SqlsugarExtensions.cs 2023-02-16 23:07:33 +08:00
小希
a114457c6f 多查 对bool类型做处理 2023-02-15 15:33:30 +08:00
小希
9274d88c76 优化多查 2023-02-15 15:03:01 +08:00
小希
97bf39f031 优化(多查)查询功能 2023-02-14 19:01:38 +08:00
chenchun
158cab9f9b feat: 添加用户、角色功能 2023-02-12 18:43:11 +08:00
chenchun
bc42efe703 feat: rbac查询页面 2023-02-11 15:56:54 +08:00
橙子
e2e1d2ad78 feat: 添加岗位子查询 2023-02-10 21:07:08 +08:00
橙子
772f2695e7 Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework 2023-02-10 19:49:31 +08:00
橙子
5df1144cd0 feat: 部门查询 2023-02-10 19:49:28 +08:00
陈淳
943a7344f6 aspnetcore模块转移 2023-02-09 19:11:56 +08:00
陈淳
5867559502 日志统一采用微软 2023-02-07 10:50:37 +08:00
橙子
fdd1eda9ec 简化对象映射Mapper,简化自定义仓储 2023-02-06 23:08:12 +08:00
橙子
1d7c17e253 更新crud 2023-02-05 22:59:22 +08:00
橙子
95484877a3 完成字典、字典类型管理相关 2023-02-05 15:17:11 +08:00
橙子
b01d242cbc 完成验证码及登录功能 2023-02-04 18:06:42 +08:00
橙子
cfd25b0a8d Merge branch 'framework' of https://gitee.com/ccnetcore/Yi into framework 2023-02-04 10:04:52 +08:00
橙子
2f08c07c20 删除前端 2023-02-04 10:04:48 +08:00
陈淳
fd5e02c1f4 删除前端 2023-02-02 17:21:10 +08:00
陈淳
a549edb174 更改启动接口 2023-02-01 16:58:29 +08:00
橙子
1c8f20440c 完善更新功能 2023-01-31 20:09:54 +08:00
陈淳
5fb09c1c4a 完善登录 2023-01-31 18:08:27 +08:00
橙子
b7260ed982 添加登录 2023-01-30 20:07:09 +08:00
橙子
aec0150a26 添加用户领域 2023-01-30 19:08:50 +08:00
陈淳
5c5dfcac89 添加用户实体 2023-01-30 18:14:44 +08:00
陈淳
32b9611eb5 修改大小写问题 2023-01-30 14:44:24 +08:00
橙子
12881db9ef Update YiBBSApplicationModule.cs 2023-01-29 21:18:22 +08:00
橙子
bb8508abbd 添加rabc项目模块 2023-01-29 21:13:20 +08:00
橙子
d6565bd2d9 添加bbs模块 2023-01-29 20:56:11 +08:00
橙子
b4633efcba Update Program.cs 2023-01-28 20:43:18 +08:00
橙子
0a003359ea 更改逻辑删除问题 2023-01-27 16:21:35 +08:00
橙子
c7e74774de 添加聚合根 2023-01-26 22:38:37 +08:00
橙子
dfefd0452d 完善DDD领域驱动设计 2023-01-26 21:00:01 +08:00
橙子
38d69c9e6f 添加评论模块 2023-01-26 11:25:04 +08:00
橙子
ac26df6827 完善主题内容 2023-01-25 22:27:19 +08:00
橙子
99787950a8 开始业务模块 2023-01-25 17:48:48 +08:00
橙子
80723496d0 更改项目目录 2023-01-24 21:02:39 +08:00
橙子
ec440f13b1 完成模板模块动态支持命名空间 2023-01-24 20:46:00 +08:00
橙子
8a9c7e54e2 添加bbs模块 2023-01-24 20:28:33 +08:00
橙子
67ca25cec4 完成模板生成功能 2023-01-24 20:20:32 +08:00
橙子
4ca7b2e023 完成种子数据模块,完成服务替换模块 2023-01-24 13:46:22 +08:00
橙子
aea8b55e82 更新种子数据扩展,留个坑 2023-01-23 19:33:10 +08:00
橙子
55a4b0ad1b Update appsettings.json 2023-01-22 23:21:07 +08:00
橙子
f4124db320 好的,已经修复完成 2023-01-21 23:15:43 +08:00
橙子
e23e5a292d 完成模板模块,但配置类有问题 2023-01-21 22:40:10 +08:00
橙子
1f702c20ae 添加代码生成模块 2023-01-21 18:09:21 +08:00
橙子
9b1b915925 完成逻辑删除 2023-01-21 16:56:39 +08:00
橙子
11bfefcd04 完成逻辑删除功能 2023-01-21 16:10:46 +08:00
橙子
fceefac0ee 非常优雅的完成了数据模块 2023-01-20 20:03:25 +08:00
橙子
98375f8629 实现sqlsugar过滤器 2023-01-20 17:04:23 +08:00
橙子
d4e0cf7e18 升级sqlsguar版本 2023-01-20 15:29:55 +08:00
橙子
62af066234 添加抽象种子数据 2023-01-20 15:29:21 +08:00
橙子
00d368080b 添加过滤器模块及种子数据 2023-01-20 15:28:36 +08:00
橙子
e43b0d1522 添加全局using功能 2023-01-19 18:19:22 +08:00
橙子
400b14cd75 完善授权与认证模块 2023-01-19 17:58:46 +08:00
橙子
f88655e214 添加授权鉴权模块 2023-01-19 15:35:50 +08:00
橙子
fc74a000a6 添加jwt认证模块 2023-01-18 19:42:13 +08:00
橙子
eb1a86e5b2 完成工作单元 2023-01-18 00:34:14 +08:00
橙子
2fa3570f85 完成工作单元模块 2023-01-17 23:33:14 +08:00
橙子
da2cf2acc5 完成错误中间件,统一返回结果 2023-01-17 22:47:15 +08:00
橙子
f68ffefaa9 BFS+倒置遍历完成模块化依赖功能 2023-01-17 17:50:15 +08:00
橙子
617fbdf8f7 完成excel模块、wechat模块 2023-01-16 23:24:59 +08:00
橙子
034abb06ad 添加excel模块 2023-01-16 23:05:01 +08:00
橙子
46b176fc59 启动时间测试 2023-01-16 21:10:00 +08:00
橙子
506686b11e 完善autofac模块 2023-01-16 20:55:36 +08:00
橙子
1314cf1c19 全流程跑通啦~~框架完成基本功能增删改查 2023-01-15 17:32:27 +08:00
橙子
f1e314fa13 DDD 应用层完善 2023-01-15 14:32:43 +08:00
橙子
3bce7de015 同步命名空间 2023-01-15 09:47:56 +08:00
橙子
1bd035e1ca 更改目录结构,分层基本已经完善
This reverts commit c905489ea5.
2023-01-14 18:46:34 +08:00
橙子
3a9ad5adb8 Revert "更改abp.vnext"
This reverts commit c905489ea5.
2023-01-14 18:16:16 +08:00
橙子
022d5bbd7d Update .gitignore 2023-01-14 14:49:48 +08:00
陈淳
c905489ea5 更改abp.vnext 2023-01-13 18:03:08 +08:00
陈淳
65377d9236 更改自动api 2023-01-13 13:40:54 +08:00
陈淳
8ead6c59c0 完善ddd模块及错误异常中间件 2023-01-12 18:30:57 +08:00
陈淳
ea4e8856c2 添加SqlSugarCoreNoDrive 2023-01-12 16:53:39 +08:00
陈淳
318cfb5fe2 修改各层关系
领域层不能依赖sqlsugar层
sqlsugar层依赖领域层
2023-01-12 15:25:46 +08:00
陈淳
7706126479 添加模块规则 2023-01-12 14:58:16 +08:00
陈淳
ba84d0ead3 添加缓存模块 2023-01-11 18:16:58 +08:00
陈淳
5604c6ece5 删除测试用例 2023-01-11 16:41:34 +08:00
陈淳
140f2970c4 添加模块化功能+动态api 2023-01-11 15:05:31 +08:00
陈淳
b2ac98d25e 框架搭建 2023-01-11 11:10:59 +08:00
陈淳
1ed37897d5 还原部门页面 2023-01-06 11:42:47 +08:00
陈淳
29df3d658b 模板重新生成可空类型 2023-01-06 11:12:47 +08:00
陈淳
576397a042 修改可空类型 2023-01-06 10:15:37 +08:00
橙子
b7c9b84449 更改null操作 2023-01-05 20:32:58 +08:00
陈淳
efbf799218 采购订单添加物料功能 2023-01-05 19:21:48 +08:00
橙子
7d578ce363 创建采购订单接口 2023-01-04 20:50:10 +08:00
陈淳
e03e584684 采购订单搭建 2023-01-04 18:16:56 +08:00
陈淳
e8e6b928cf 完善单位定义界面 2023-01-04 16:47:24 +08:00
陈淳
5f6d29a3f8 自动生成定义管理通用代码 2023-01-04 15:46:29 +08:00
陈淳
a27d83a5e3 Update README.md 2023-01-04 14:09:03 +08:00
陈淳
ef1154d6f3 更新说明 2023-01-04 13:44:35 +08:00
陈淳
ae2400fd0b Merge branch 'sqlsugar-dev' into sqlsugar 2023-01-04 13:40:58 +08:00
陈淳
fbcd004b7e 添加各个木块代码生成 2023-01-04 13:40:30 +08:00
橙子
cb307f95ce update README.md.
Signed-off-by: 橙子 <454313500@qq.com>
2023-01-04 05:39:00 +00:00
橙子
7838cd1a6a Update Yi.Framework.Template.csproj 2023-01-03 21:03:29 +08:00
橙子
03dcb7d860 完善代码生成器自动生成目录 2023-01-03 21:03:07 +08:00
橙子
62c5470efe 添加代码生成器模块service实现 2023-01-03 20:06:16 +08:00
陈淳
53b4674da4 Create ProgramTemplateProvider.cs 2023-01-03 18:14:18 +08:00
陈淳
ff2cf68b08 添加模板生成代码 2023-01-03 18:05:57 +08:00
陈淳
8b55373794 更新文件helper 2023-01-03 16:03:11 +08:00
橙子
dba67d7127 添加供应商定义模块 2023-01-02 23:32:40 +08:00
橙子
762c455b53 删除多余文件 2023-01-02 21:18:38 +08:00
chenchun
714d1a36c4 添加供应商 2023-01-02 17:27:12 +08:00
chenchun
0e0ddbbd99 Update README.md 2023-01-02 15:09:59 +08:00
chenchun
497f8cfd1f 添加erp模块 2023-01-02 15:08:04 +08:00
chenchun
1ff709e6f8 优化分层结构,添加常量 2023-01-02 14:29:16 +08:00
chenchun
ea35871aba 使用sqlsugar内置工作单元 2023-01-02 13:59:05 +08:00
chenchun
0e78857645 添加自动映射 2023-01-02 00:09:30 +08:00
chenchun
5d7d115910 添加dto模式的demo测试 2023-01-01 23:51:05 +08:00
chenchun
b9384afd5d 爆肝,重构框架,你懂得 2023-01-01 23:06:11 +08:00
陈淳
dbe020dc94 添加工作单元测试 2022-12-29 09:53:45 +08:00
陈淳
9673aa7690 Merge branch 'sqlsugar' into sqlsugar-dev 2022-12-29 09:08:43 +08:00
黎明
ec06d30d59 添加工作单元 2022-12-26 14:19:12 +08:00
橙子
356d2e592b Update README.md 2022-12-20 15:32:59 +08:00
橙子
521c2e7ca6 Update README.md 2022-12-18 23:17:23 +08:00
陈淳
c4014c9333 更新单元测试 2022-12-16 16:00:36 +08:00
陈淳
2908a8d8a9 测试兼容中间件方式测验 2022-12-14 23:27:54 +08:00
陈淳
c964b98240 添加xunit单元测试 2022-12-14 00:14:22 +08:00
陈淳
f7c74b5c96 Quartz更改,使用内置注册 2022-12-13 23:12:10 +08:00
陈淳
d34c9818b4 更改mit协议 2022-12-13 23:01:23 +08:00
陈淳
6414c93116 Update README.md 2022-12-09 10:58:42 +08:00
陈淳
5a12c4a3ce Update README.md 2022-12-06 23:41:41 +08:00
陈淳
97a6ee39e5 Update README.md 2022-12-05 23:18:10 +08:00
陈淳
44db5ab150 添加评论功能 2022-12-01 16:06:53 +08:00
陈淳
e9bcd29e36 评论建表 2022-12-01 13:38:06 +08:00
橙子
a2ca897fca 社区板块添加点赞功能 2022-11-29 23:03:10 +08:00
陈淳
9a34e63d5f 点赞功能接口开发 2022-11-29 18:47:26 +08:00
陈淳
e501b894c3 job异常处理 2022-11-29 12:49:36 +08:00
陈淳
d97ef84b7e 删除hangfire(不支持毫秒级别)
This reverts commit 49a9eb5460.
2022-11-29 12:34:45 +08:00
陈淳
0f2dc4d3ba Merge branch 'sqlsugar-dev' of https://gitee.com/ccnetcore/Yi into sqlsugar-dev 2022-11-29 12:31:28 +08:00
橙子
49a9eb5460 hangfire测试 2022-11-27 15:05:27 +08:00
陈淳
7e7780a754 Update README.md 2022-11-21 11:20:24 +08:00
陈淳
d0770970f0 Merge branch 'sqlsugar-dev' of https://gitee.com/ccnetcore/Yi into sqlsugar-dev 2022-11-20 23:36:02 +08:00
陈淳
d5a10a5817 Update README.md 2022-11-19 18:18:03 +08:00
橙子
7b63d8b2ba Update README.md 2022-11-16 19:49:48 +08:00
陈淳
c544de8909 Update README.md 2022-11-14 17:04:39 +08:00
橙子
89da2ab50f 菜单权限管控 2022-11-13 22:52:03 +08:00
陈淳
a1a6b5967b Update README.md 2022-11-12 16:58:58 +08:00
陈淳
53ffb1b565 Update README.md 2022-11-11 08:54:11 +08:00
陈淳
6efecd123f Update README.md 2022-11-09 08:53:48 +08:00
陈淳
1ec5349a96 1108 2022-11-08 21:02:50 +08:00
陈淳
187abcd10c 编码转换 2022-11-07 21:45:29 +08:00
陈淳
d6ca4429d5 添加属性依赖注入、添加自定义日志扩展 2022-11-07 01:31:37 +08:00
陈淳
86e869ff16 注释 2022-11-06 21:38:55 +08:00
陈淳
dc58f9397f saas分库,仓储+公共库+业务库+动态配置连接串框架搭建 2022-11-06 20:22:32 +08:00
陈淳
97b4ab2f15 数据权限功能、用户信息缓存 2022-11-04 23:23:54 +08:00
陈淳
23a9d02aba 修复sqlserver codefirst的问题 2022-11-02 16:36:08 +08:00
陈淳
32ed6c3e97 准备接入Saas多租户 2022-11-02 11:06:41 +08:00
陈淳
102556dd2a 准备接入多租户 2022-11-02 08:48:01 +08:00
陈淳
2df3f7c56d 删除测试文件 2022-10-29 10:33:51 +08:00
陈淳
d6fd02ec19 修复路由切换问题 2022-10-29 10:33:08 +08:00
陈淳
c901b4bc06 时间单位精确到秒 2022-10-28 17:38:14 +08:00
陈淳
e19c89ccd9 修复jwt配置文件问题 2022-10-28 17:22:02 +08:00
陈淳
a667f1a65e 验证码缓存接入 2022-10-28 14:25:59 +08:00
陈淳
9f5829876c 登录验证码 2022-10-28 14:10:11 +08:00
陈淳
189d7c4719 缓存接入 2022-10-27 20:54:05 +08:00
陈淳
dfe877c438 缓存测试 2022-10-26 20:08:16 +08:00
陈淳
3a634d7888 配置文件更改 2022-10-26 20:01:18 +08:00
陈淳
fd9f3d04d9 商品首页完善 2022-10-25 10:59:51 +08:00
陈淳
ac70fc37f6 商品子页面完善 2022-10-25 09:47:30 +08:00
橙子
c29aeeee41 缓存搭建 2022-10-24 22:31:53 +08:00
橙子
51c13c7b52 角色 权限code完善 2022-10-24 21:22:03 +08:00
橙子
fec8c0fe53 Merge branch 'sqlsugar-dev' of https://gitee.com/ccnetcore/Yi into sqlsugar-dev 2022-10-23 22:29:44 +08:00
橙子
5da2143212 10/23更新 2022-10-23 22:29:35 +08:00
陈淳
30158ac145 spu、skus数据测试 2022-10-20 18:11:54 +08:00
陈淳
ff7eecee55 10/20更新 2022-10-20 16:33:29 +08:00
陈淳
0755a4026a 修复codefirst 2022-10-19 19:10:48 +08:00
陈淳
9aaf363584 10/19更新 2022-10-19 14:34:53 +08:00
陈淳
0a27cd7403 商城模块 2022-10-18 19:27:58 +08:00
陈淳
ab8cdd88b9 添加codeFirst功能 2022-10-18 18:01:16 +08:00
陈淳
dab4a092d9 sql日志打印输出配置 2022-10-18 09:01:16 +08:00
陈淳
9d365dbf1e 改造null 2022-10-17 18:08:16 +08:00
橙子
16d25fb60d 添加缩略图同步功能 2022-10-17 01:10:31 +08:00
橙子
50d43a2fc5 完善推荐页显示 2022-10-16 22:51:53 +08:00
橙子
6d99539730 修复列表展示问题 2022-10-16 16:09:45 +08:00
橙子
cf43dc8e70 修复上传问题 2022-10-16 15:54:49 +08:00
橙子
95c506c638 修复菜单排序问题 2022-10-16 13:43:23 +08:00
橙子
464b768c55 Revert "文章标题默认值"
This reverts commit 00ca4cc5bd.
2022-10-16 13:18:07 +08:00
橙子
00ca4cc5bd 文章标题默认值 2022-10-16 13:16:40 +08:00
橙子
bfb81ef60d 10/16更新 2022-10-16 13:15:43 +08:00
陈淳
420701bf23 文章管理、前端api、视图代码模板生成 2022-10-14 18:08:45 +08:00
橙子
88073aaa20 样式优化 2022-10-14 02:18:25 +08:00
橙子
8456320884 头像上传功能、性别选择 2022-10-13 23:55:21 +08:00
陈淳
3555b08fe8 修改数据 2022-10-13 14:47:45 +08:00
陈淳
55ad046e96 文件种子数据 2022-10-13 14:46:30 +08:00
陈淳
ea4ddb68f3 修复页面加载不出的问题 2022-10-13 14:04:48 +08:00
陈淳
edde5f8a88 前端登录逻辑对接 2022-10-13 11:05:43 +08:00
陈淳
de28fd4ca4 登录前后端联调对接 2022-10-12 19:00:26 +08:00
陈淳
dd7bbb138a 文件上传功能优化 2022-10-12 14:43:35 +08:00
陈淳
4a54eb56a7 完善登录页面 2022-10-12 14:20:03 +08:00
陈淳
24cda70cbc 登录页面搭建 2022-10-11 18:18:19 +08:00
陈淳
a0c869d0a1 前端联调接口 2022-10-11 16:48:25 +08:00
橙子
58f85992b0 设置及发表图文页面 2022-10-10 00:02:13 +08:00
陈淳
f11f5e1ca4 Update components.d.ts 2022-10-09 17:36:12 +08:00
陈淳
0672698ba7 添加特性自动依赖注入 2022-10-09 17:32:44 +08:00
陈淳
cf37f7c950 完善我的页面 2022-10-09 13:30:42 +08:00
陈淳
c7d64554ad 升级httphelper 2022-10-09 09:54:41 +08:00
橙子
039021532e 我的页面 2022-10-09 01:10:30 +08:00
橙子
e39ae170c3 tab与添加主页 2022-10-09 00:14:17 +08:00
橙子
8a44fe9d1c 推荐页面优化 2022-10-08 23:05:19 +08:00
橙子
70649653fb vant搭建 2022-10-05 23:54:53 +08:00
橙子
db1f241c33 Update TestController.cs 2022-10-04 15:22:34 +08:00
橙子
c9ed317e62 修复在线用户重连问题 2022-10-03 22:56:36 +08:00
橙子
afde26a7ae Update README.md 2022-10-03 18:51:51 +08:00
橙子
1a2d9ba2b2 强制退出功能 2022-10-03 18:27:37 +08:00
橙子
3943536485 实时在线用户功能 2022-10-03 17:04:59 +08:00
橙子
e963a4051f 添加登录日志功能 2022-10-02 14:02:21 +08:00
橙子
2b02194a18 修改sql日志打印 2022-10-02 11:49:19 +08:00
橙子
dd1aec3b60 操作日志功能完善 2022-10-01 23:53:43 +08:00
橙子
0b05d4d186 操作类型完善修改 2022-10-01 15:26:30 +08:00
橙子
8f0327604f 添加日志管理动态多级菜单 2022-10-01 15:04:35 +08:00
陈淳
40dddb3316 获取请求参数扩展 2022-09-29 18:01:19 +08:00
陈淳
be7ea64c5a 操作日志特性、操作日志全局过滤器搭建 2022-09-29 17:02:34 +08:00
陈淳
8eef0e410e 更新 2022-09-29 16:14:50 +08:00
陈淳
63f0b20fd6 通用数据导入功能 2022-09-29 10:03:21 +08:00
陈淳
87a685b823 通用数据控制器 2022-09-28 18:05:49 +08:00
陈淳
4796a494de 数据导入功能 2022-09-28 16:54:53 +08:00
陈淳
a329ff3796 注释 2022-09-27 19:31:34 +08:00
陈淳
00ab65e720 数据导出 2022-09-27 19:30:11 +08:00
陈淳
4997e25cdc 数据模板导入、数据导出 2022-09-27 19:27:17 +08:00
陈淳
6ce05984d5 权限code过滤器 2022-09-27 16:22:34 +08:00
陈淳
26e08774b0 更换背景图片 2022-09-26 16:15:51 +08:00
橙子
83ce9fb4c4 修复部署问题 2022-09-26 00:27:55 +08:00
橙子
ac946f4903 Update .env.staging 2022-09-25 20:54:33 +08:00
橙子
d6480db609 update README.md.
Signed-off-by: 橙子 <454313500@qq.com>
2022-09-25 12:51:52 +00:00
橙子
9f9c191240 readme 2022-09-25 20:50:51 +08:00
橙子
cb781aa4ad Update README.md 2022-09-25 20:47:07 +08:00
橙子
6a31e88855 swagger跳转 2022-09-25 20:41:55 +08:00
橙子
f862e5ea1b 用户去重判定 2022-09-25 20:04:04 +08:00
橙子
b8de3e9867 前端修改 2022-09-25 19:56:14 +08:00
橙子
1959160681 重置部门选择 2022-09-25 19:07:42 +08:00
橙子
b665ec5717 部门种子数据 2022-09-25 18:58:17 +08:00
橙子
8ef2c12be1 添加字典初始化 2022-09-25 18:06:07 +08:00
橙子
d85e7f0bcb Merge branch 'sqlsugar-dev' of https://gitee.com/ccnetcore/Yi into sqlsugar-dev 2022-09-24 20:28:57 +08:00
陈淳
544d65c7d0 Update README.md 2022-09-23 18:55:12 +08:00
陈淳
1bd5fc389a 字典样式类型及样式class设置 2022-09-23 18:53:59 +08:00
陈淳
9149d6de9a 配置管理增删改查 2022-09-23 18:46:37 +08:00
陈淳
261d9fcd79 Update README.md 2022-09-23 16:03:32 +08:00
橙子
5ecac4223d Update README.md 2022-09-22 19:42:17 +08:00
陈淳
215c21ad8a Update README.md 2022-09-21 19:01:30 +08:00
陈淳
5772de888c 岗位、部门种子数据 2022-09-20 20:57:01 +08:00
陈淳
55979e90dc 菜单角色种子 2022-09-20 15:11:55 +08:00
陈淳
5785f5beea 数据库种子初始化搭建 2022-09-19 17:25:43 +08:00
陈淳
e8bb256a8d 添加种子数据基础 2022-09-19 14:24:13 +08:00
chenchun
729a563545 添加模块 2022-09-18 17:50:38 +08:00
chenchun
1b38ed5c78 Update README.md 2022-09-18 17:49:17 +08:00
chenchun
483aea5c4f 数据权限功能 2022-09-18 17:22:47 +08:00
chenchun
a64d493a29 添加验证码 2022-09-18 16:46:31 +08:00
陈淳
2a8d436267 添加验证码 2022-09-17 18:09:18 +08:00
陈淳
7905f82d65 验证码 2022-09-16 20:01:01 +08:00
陈淳
dcf82d041a 验证码 2022-09-16 19:57:56 +08:00
陈淳
9618bd891f 添加用户性别字段 2022-09-16 19:18:24 +08:00
陈淳
c63f9de5c5 数据权限更新页面 2022-09-16 19:04:40 +08:00
陈淳
0e6113f0a6 数据权限 2022-09-16 18:57:32 +08:00
陈淳
71cf85f535 数据权限查询 2022-09-15 19:45:51 +08:00
陈淳
2a4f646181 数据权限 2022-09-15 19:05:57 +08:00
陈淳
0cc326836c 数据范围枚举 2022-09-15 18:59:01 +08:00
陈淳
52b8bc8909 上下文对象获取用户id扩展 2022-09-15 18:44:12 +08:00
陈淳
844a7b455c 鉴权 2022-09-15 18:40:24 +08:00
陈淳
489a0b6fb8 配置文件热重载功能 2022-09-15 17:09:30 +08:00
陈淳
e535133eca 个人中心信息更新、重置cc密码 2022-09-14 20:35:45 +08:00
陈淳
ea2be7609c 修复状态问题、用户中心页面查询及修改 2022-09-14 19:53:53 +08:00
陈淳
011d9d639b 用户部门查询 2022-09-13 19:39:16 +08:00
陈淳
e78e9d8e55 用户列表部门岗位角色信息 2022-09-13 19:04:41 +08:00
陈淳
89762cad78 路由构建充血模型 2022-09-13 18:15:01 +08:00
陈淳
4788562241 用户界面部门角色岗位选择 2022-09-13 17:59:35 +08:00
陈淳
5b1ad450d3 部门管理:增删改查、用户部门显示 2022-09-13 17:50:20 +08:00
陈淳
7b6d8671cf 岗位管理:增删改查功能 2022-09-13 17:05:53 +08:00
chenchun
01631860f4 菜单完善 2022-09-11 22:05:56 +08:00
chenchun
c65e76bbc3 树形构造器修改 2022-09-11 16:49:40 +08:00
chenchun
ef35e1cfd9 接口名称简化 2022-09-11 15:55:08 +08:00
chenchun
1d535b5d61 角色添加、角色编辑关联菜单功能 2022-09-11 15:39:55 +08:00
chenchun
004cb20132 添加角色关联菜单功能 2022-09-11 14:52:54 +08:00
chenchun
909bdf60e7 重置密码功能 2022-09-11 13:16:24 +08:00
chenchun
0cd3bea6bd 用户信息添加修改 2022-09-11 12:39:22 +08:00
chenchun
d001a0de15 用户编辑界面 2022-09-11 02:39:33 +08:00
chenchun
0dca7acee6 简单基类控制器封装 2022-09-10 20:49:46 +08:00
chenchun
b5ad7a1721 字典页面与字典信息页面完成增删改查 2022-09-10 20:25:39 +08:00
chenchun
9f23b911c1 字典信息表查询删除接口 2022-09-10 20:05:45 +08:00
chenchun
1db8bb4d13 字典类型查询 2022-09-10 14:02:41 +08:00
chenchun
eebafda9e5 db查询对象简化 2022-09-10 13:36:01 +08:00
陈淳
0fb57a0a2c 菜单类型接口 2022-09-09 19:34:20 +08:00
陈淳
d4c55620f1 菜单到前端路由转换 2022-09-09 19:22:14 +08:00
陈淳
ef26567850 添加目录、菜单、控件 2022-09-09 18:15:10 +08:00
陈淳
c15c43dba4 用户角色菜单数据对应 2022-09-09 16:52:32 +08:00
陈淳
5d738d99fe 分页参数时间范围处理 2022-09-09 15:53:11 +08:00
陈淳
43120b0017 测试 2022-09-08 18:24:51 +08:00
陈淳
23dc82f042 vue动态路由测试 2022-09-08 18:18:34 +08:00
陈淳
49330536c7 vue3.x-ruoyi前端 2022-09-07 18:30:43 +08:00
陈淳
781fa7ea1b ruoyi对接
登录接口、用户信息接口、用户管理接口等
2022-09-07 18:22:15 +08:00
橙子
bf17312a5f 9/06更新 2022-09-06 23:32:17 +08:00
陈淳
e4da8d4f15 对接ruoyi接口
用户、角色、菜单查询部分
2022-09-06 19:32:32 +08:00
陈淳
cc26bd09e6 Merge branch 'sqlsugar-dev' into sqlsugar 2022-09-04 18:47:51 +08:00
陈淳
9dc1c5c9e9 添加实时通信 2022-09-04 18:47:39 +08:00
陈淳
442ae94ad5 Merge branch 'sqlsugar-dev' into sqlsugar 2022-08-13 18:12:12 +08:00
陈淳
43820f71a3 Update README.md 2022-08-13 18:11:19 +08:00
陈淳
63920e034a 单例redis、rabbitmq更新、任务调度更新 2022-08-13 18:10:11 +08:00
陈淳
cddc47305f Update README.md 2022-07-13 10:34:29 +08:00
陈淳
583bebd10c Merge branch 'sqlsugar-dev' into sqlsugar 2022-07-13 10:33:08 +08:00
陈淳
7fe9a6c900 项目引用更新 2022-07-13 10:32:43 +08:00
chenchun
670e457dc6 Merge branch 'sqlsugar-dev' into sqlsugar 2022-06-22 14:07:21 +08:00
chenchun
300d8224ec 升级excel操作 2022-06-22 14:07:09 +08:00
橙子
924f5320bb Merge branch 'sqlsugar-dev' of https://gitee.com/ccnetcore/Yi into sqlsugar-dev 2022-06-04 14:41:08 +08:00
橙子
dd09fb2283 Update README.md 2022-06-04 14:40:59 +08:00
chenchun
3e18af626b Update README.md 2022-05-29 16:56:46 +08:00
chenchun
89dd5b79d6 Merge branch 'sqlsugar-dev' into sqlsugar 2022-05-29 16:56:11 +08:00
chenchun
356f71f13e 添加高级保存及查询排序方式 2022-05-29 16:56:02 +08:00
橙子
2f5d71a299 Merge branch 'sqlsugar-dev' into sqlsugar 2022-05-28 10:32:50 +08:00
橙子
b581c12edb 实体更改使用newjson 2022-05-28 10:32:12 +08:00
橙子
d4d6aeb0b4 Update README.md 2022-05-25 23:18:31 +08:00
橙子
77114e6cfc Merge branch 'sqlsugar-dev' into sqlsugar 2022-05-22 20:58:26 +08:00
橙子
e14a078440 修复了前端用户信息更新存储的bug 2022-05-22 20:58:17 +08:00
橙子
3b38a0d628 完善界面 2022-05-19 23:29:37 +08:00
橙子
927d8cb6c9 Update README.md 2022-05-14 17:51:09 +08:00
橙子
eb8ea2f549 Update README.md 2022-05-14 17:46:52 +08:00
橙子
208c93bc8f 添加微信支付模块 2022-05-14 17:45:17 +08:00
橙子
93180faa23 Merge branch 'sqlsugar-dev' into sqlsugar 2022-05-13 23:10:10 +08:00
橙子
816858f231 修改前端样式 2022-05-13 23:08:52 +08:00
橙子
fa1d68848f Update README.md 2022-05-12 23:40:27 +08:00
橙子
bce955a1e1 添加按钮权限 2022-05-12 23:39:42 +08:00
chenchun
82f70e0ac9 Update menuDic.js 2022-05-12 19:37:33 +08:00
chenchun
fc9b78cfef jwt授权不在需要添加bear 2022-05-11 16:24:36 +08:00
chenchun
72400a48da 添加sql参数 2022-05-11 12:21:59 +08:00
橙子
537b39b3c4 Update README.md 2022-05-08 14:46:53 +08:00
橙子
47e6e48729 Merge branch 'sqlsugar-dev' into sqlsugar 2022-05-08 14:46:35 +08:00
橙子
d252229777 完善动态菜单、设置角色等功能,修复登录问题 2022-05-08 14:46:22 +08:00
橙子
7f4c7f607d Update README.md 2022-05-06 22:57:36 +08:00
橙子
994ba5dd1a Merge branch 'sqlsugar-dev' into sqlsugar 2022-05-06 22:56:40 +08:00
橙子
e3a06b28dd 更新数据库 2022-05-06 22:56:26 +08:00
橙子
10d512470e 合并冲突 2022-05-06 22:51:45 +08:00
橙子
c1d8040fd5 合并冲突 2022-05-06 22:49:12 +08:00
橙子
e4b81da386 Merge branch 'sqlsugar-dev' of https://gitee.com/ccnetcore/Yi into sqlsugar-dev 2022-05-06 22:47:35 +08:00
橙子
fd7360e6f4 预添加前端权限控制 2022-05-06 22:47:26 +08:00
chenchun
62f15e218e Merge branch 'sqlsugar-dev' into sqlsugar 2022-05-05 17:05:12 +08:00
chenchun
5c1b91f348 完善权限 2022-05-05 17:04:49 +08:00
橙子
378cbd580f Update README.md 2022-05-04 15:55:19 +08:00
橙子
3994f14010 通用对象查询封装、权限封装 2022-05-04 15:54:40 +08:00
橙子
e7f4e743e3 Update README.md 2022-05-03 19:42:47 +08:00
橙子
b934ce2893 添加文件操作 2022-05-03 19:40:13 +08:00
橙子
5eec076ea2 添加前端权限 2022-05-03 17:34:38 +08:00
橙子
ada36ebff5 Update README.md 2022-05-01 18:33:04 +08:00
橙子
d9543ca23c 添加修改密码及用户信息 2022-05-01 18:31:06 +08:00
橙子
3871eb3c84 Update README.md 2022-04-30 22:14:49 +08:00
橙子
4ba696d289 添加用户简介 2022-04-30 22:12:25 +08:00
橙子
2f69e0b96c 添加前端用户信息展示 2022-04-30 22:04:47 +08:00
橙子
d6b0c56c35 添加登录用户全部信息查询 2022-04-30 21:48:18 +08:00
橙子
d8fe983b9d Update README.md 2022-04-29 12:58:35 +08:00
1229 changed files with 58302 additions and 26396 deletions

63
.gitattributes vendored
View File

@@ -1,63 +0,0 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

14
.gitignore vendored
View File

@@ -2,17 +2,11 @@
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
WebFirst/SoEasyPlatform.exe
WebFirst/excel
WebFirst/wwwroot
WebFirst/SoEasyPlatform.exe
WebFirst/appsettings.Development.json
WebFirst/appsettings.json
WebFirst/SoEasyPlatform.pdb
WebFirst/SoEasyPlatform.Views.pdb
WebFirst/web.config
WebFirst/WebFirst.exe
dist/
appsettings.Production.json
appsettings.Development.json
wwwroot
# User-specific files
*.rsuser
*.suo

214
LICENSE
View File

@@ -1,201 +1,21 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
MIT License
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Copyright (c) 2022 jacktang
1. Definitions.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

293
README.md
View File

@@ -1,7 +1,6 @@
<h1 align="center"><img align="left" height="100px" src="https://user-images.githubusercontent.com/68722157/138828506-f58b7c57-5e10-4178-8f7d-5d5e12050113.png"> Yi框架</h1>
<h4 align="center">一套与SqlSugar一样爽的.Net6低代码开源框架</h4>
<h1 align="center"><img align="left" height="150px" src="https://user-images.githubusercontent.com/68722157/138828506-f58b7c57-5e10-4178-8f7d-5d5e12050113.png"> Yi框架</h1>
<h4 align="center">一套以用户体验出发的.Net6 Web开源框架</h4>
<h5 align="center">支持原生版本、Furion版本、Abp版本前端后台接入Ruoyi Vue3.0</h5>
<h2 align="center">集大成者,终究轮子</h2>
[English](README-en.md) | 简体中文
@@ -10,29 +9,38 @@
****
### 简介:
**中文:意框架**(和他的名字一样“简易”)
**中文:意框架**(和他的名字一样“简易”同时接入Java的Ruoyi Vue3.0前端
模块分化较多,可根据业务自行引用或抛弃,集大成者,大而全乎,也许你能从中学习到一些独特见解
**英文YiFramework**
Yi框架-一套与SqlSugar一样爽的.Net6低代码开源框架。
Yi框架-一套与SqlSugar一样爽的.Net6开源框架。
与Sqlsugar理念一致以用户体验出发。
架构干净整洁、无业务代码、采用微软风格原生框架封装、WebFrist开发。
适合.Net6学习、Sqlsugar学习 、项目二次开发。
集大成者,终究轮子
Yi框架最新版本标签`v1.0.5`,具体版本可以查看标签迭代
Yi框架最新版本标签`v3.0.0`,具体版本可以查看标签迭代
项目与Sqlsugar同步更新但这作者老杰哥代码天天爆肝到凌晨两点我们也尽量会跟上他的脚步。更新频繁所以可watching持续关注。
————这不仅仅是一个程序,更是一个艺术品,面向艺术的开发!
**分支**
> 核心特点简单好用框架不以打包形式引用而是直接以项目附带源码给出自由度拉满遵循Mit协议允许随意修改请注明来源即可
本项目由EFCore版本历经3年不断迭代至Sqlsugar版本现EFcore版本已弃用目前sqlsugar不带任何业务之后会更新业务功能
**分支:**
**SqlSugar**:.Net6 DDD领域驱动设计 简单分层微服务架构
本项目由EFCore版本历经3年不断迭代至Sqlsugar版本现EFcore版本已弃用目前sqlsugar已带业务功能
**ec**:EFcore完整电商项目
- **Framework**: 框架分支,所有东西都在这里
- **Furion**: 基于Furion分支回归开发本质极度简单用起来贼爽
- ~~**SqlSugar**:.Net6 DDD领域驱动设计 简单分层微服务架构~~
- ~~**SqlSugar-Dev**为sqlsugar分支的实时开发版本~~
- ~~**ec**: EFcore完整电商项目~~
****
@@ -40,19 +48,13 @@ Yi框架最新版本标签`v1.0.5`,具体版本可以查看标签迭代
废话少说直接上地址,**请不要**更改里面的数据
API服务~~[yi.ccnetcore.com](http://yi.ccnetcore.com) 管理员账号admin 、 123~~
官网网址:[ccnetcore.com](https://ccnetcore.com) (已上线,欢迎加入)
网关地址:~~[gate.ccnetcore.com/swagger](http://gate.ccnetcore.com/swagger)~~
Bbs社区系统[ccnetcore.com](https://ccnetcore.com) (已上线,欢迎加入)
WebFirst开发所有代码生成器已经配置完成无需任何操作数据库及任何代码只需要网页表格上点点点即可
Rbac后台管理系统[yi.ccnetcore.com](http://yi.ccnetcore.com) (已上线)~~管理员账号cc 、 123456~~
[https://www.donet5.com/Doc/11](https://www.donet5.com/Doc/11)
谁能把持的住Sqlsugar作者自己都依赖成瘾的东西呢这是继DbFirst、CodeFirst下一代的划时代产品无脑爽
![image](https://s1.ax1x.com/2022/04/12/Lnm5Yq.png)
(首次添加实体后,生成代码记得修改对应的路径哦~~
网关地址:~~[gate.ccnetcore.com/swagger](http://gate.ccnetcore.com/swagger)~~(目前使用单体架构)
### 支持:
@@ -62,113 +64,158 @@ WebFirst开发所有代码生成器已经配置完成无需任何操作数
- [ ] 即将支持网格服务架构我们将在后续版本加入dapr
****
### 软件架构:
### 详细到爆炸的Yi框架教程导航
**架构**:后端.NET6(Asp.NetCore 6)、WebFirst代码生成器~~与.NET5(Asp.NetCore 5)、前端Vue2.0~~
**关系型数据库**mysql、sql server、sqlite、oracle(正在兼容中)
**操作系统**Windows、Linux
**身份验证**JWT、IdentityServer4
**组件**~~EFcore~~SqlSugar、Autofac、Castle、Swagger、Log4Net、Redis、RabbitMq、ES、Quartz.net、~~T4~~
**分布式**CAP、Lock
**微服务**Consul、Ocelot、IdentityService、Apollo、Docker、Jenkins、Nginx、K8s、ELK、Polly
**封装**Json处理模块滑动验证码模块base64图片处理模块异常捕捉模块、邮件处理模块、linq封装模块、随机数模块、统一接口模块、基于策略的jwt验证、过滤器、数据库连接、跨域、初始化种子数据、Base32、Console输出、日期处理、文件传输、html筛选、http请求、ip过滤、md5加密、Rsa加密、序列化、雪花算法、字符串处理、编码处理、地址处理、xml处理、心跳检查。。。
1. [框架快速开始](https://ccnetcore.com/article/1641733850189139969)(已完成)
2. [框架模块教程](https://ccnetcore.com/article/1641733991574933505)(已完成)
3. [应用模块教程](https://ccnetcore.com/article/1641734073091231745)
4. [Yi.RBAC后台系统](https://ccnetcore.com/article/1641734171128893441)
5. [Yi.BBS社区系统](https://ccnetcore.com/article/1641734308475572225)
****
### 支持模块:
### 它的理念:
优雅的进行快速开发通常简单程度与优雅程度不可兼得Yi框架并不一昧的追求极致的解耦会站在用户使用角度上在使用难易度进行考虑衡量
大致如图:
例如我们大部分功能紧密贴合Sqlsugar虽然缺少其他orm的替换性但在使用程度上降低的使用难度
![image](https://user-images.githubusercontent.com/68722157/142923071-2fa524eb-e833-4143-a926-51566e56e889.png)
![image](https://user-images.githubusercontent.com/68722157/142923150-ebe1b538-c3fc-42dd-bea8-83e10e0f819a.png)
![image](https://user-images.githubusercontent.com/68722157/142923529-e4fbb2f6-def1-4702-b9da-5adbd22f0a2f.png)
例如:我们在应用层中使用属性注入,虽然依赖关系会比较模糊,但是使用起来会减少一定代码量
(删除线代表已实现功能还未迁移过来)
- [x] 支持大致`DDD领域驱动设计`进行分层,支持微服务扩展
- [x] 支持采用`异步`开发awit/async
- [x] 支持数据库主从`读写分离`
- [x] 支持功能替换,无需改动代码,只需配置`json文件`进行装配即可
- [x] ~~-支持采用DbFirst开发方式使用`T4模板代码生成器`自动映射模型一键生成Service及IService所有代码~~
- [x] 支持WebFirst无需改动代码自动生成全套代码与数据库只需点点点
- [x] ~~-支持`用户-角色-菜单-接口`以及vue2.0前端全部逻辑代码,下载无需修改直接使用~~
- [x] 支持`Aop封装`FilterAop、IocAop、LogAop、SqlAop
- [x] 支持`Log4Net日志`记录自动生成至bin目录下的logs文件夹
- [x] 支持`DbSeed数据库种子数据`接入
- [x] 支持主流`数据库随意切换`Mysql/Sqlite/Sqlserver/Oracle
- [x] 支持上海杰哥官方`SqlSugar ORM`封装
- [x] 支持新版`SwaggerWebAPI`jwt身份认证接入
- [x] 支持`Cors`跨域
- [x] 支持`AutoFac`自动映射依赖注入
- [x] 支持`consul`服务器注册与发现
- [x] 支持`健康检查`
- [x] 支持`RabbitMQ`消息队列
- [x] 支持`Redis`多级缓存
- [x] 支持`Ocelot`网关路由、服务聚合、服务发现、认证、鉴权、限流、熔断、缓存、Header头传递
- [x] 支持`Apollo`全局配置中心;
- [x] 支持`docker`镜像制作
- [x] ~~-支持页面`静态化处理`,将动态页面生成静态页面~~
- [x] 支持`Quartz.net`任务调度,实现任意接口被调度
- [x] 支持`ELK`log4net+kafka+es+logstach+kibana
- [x] 支持`IdentityService4`授权中心
- [x] 支持`Es`分词查询
- [x] 支持多级`缓存`
- [x] 支持`CAP`分布式事务mysql+rabbitMq
- [x] 支持`Docker+k8s`部署
- [x] 支持`Jenkins+CI/CD`
- [x] 支持`AutoMapper`模块映射
- [ ] 支持`微信支付`(没账号)
- [x] 支持`单表多租户`常用功能
- [x] 支持`逻辑删除`常用功能
- [x] 支持`操作日志`常用功能
- [x] 支持`自动分表`
- [x] 支持 太多了忘了
> 一个面向用户的快速开发后端框架
在真正的使用这,你会明白这一点,极致的简单,也是优雅的一种体现。
****
### 特点:
- 面向用户的后端框架,使用简单,适合小型、企业级项目
- 项目内置源码,不打包
- 开箱即用
- 支持模块化
- 支持动态Api
- 支持属性注入
- 内置包含大量通用场景模块
- 等等
### 基础设施简介
- Jwt鉴权
- 接口级别授权
- 对象映射
- O/RM
- 数据过滤
- 多租户
- 逻辑删除
- 审计日志
- 种子数据
- 工作单元
- 模块化
- 动态Api
- 属性注入
- 自动依赖注入
- 当前用户
- 仓储
- Crud
### 内置模块简介
- 后台任务
- 本地缓存
- 分布式缓存
- 事件总线
- 字典管理
- 文件管理
- 图片操作
- Excel操作
- 操作日志管理
- Sms短信
- 微信支付
- 模板代码生成
### 业务项目
- RABC后台管理系统
- BBS社区系统
> 重复的东西,无需再写一遍,这也是优雅的体现之一
****
### 目录结构:
### 核心技术
#### 后端
C# Asp.NetCore 6.0
- [x] 动态ApiCike.AutoApi
- [x] 鉴权授权Jwt
- [x] 日志Nlog
- [x] 模块化StartupModules
- [x] 依赖注入Autofac
- [x] 对象映射Mapster
- [x] ORM:SqlsugarCore
- [x] 多租户Abp
- [x] 后台任务Quartz.Net
- [x] 本地缓存MemortCache
- [x] 分布式缓存CSRedisCore
- [x] 事件总线Cike.EventBus
- [x] 图像操作SixLabors.ImageSharp
- [x] Excle操作ExcelToObject.Npoi
![image](https://s1.ax1x.com/2022/04/09/LCTleH.png)
![image](https://s1.ax1x.com/2022/04/24/L4qlSs.png)
![image](https://s1.ax1x.com/2022/04/24/L4q1ln.png)
#### 前端
js Vue3.2
- [x] 异步请求axios
- [x] 图表echarts
- [x] uielement-plus
- [x] 存储pinia
- [x] 路由vue-router
- [x] 打包vite
我们大致依照DDD领域驱动设计分层
分层如此清晰什么还感觉太复杂了用户只需关注Api、Service其他都是轮子啊
~~- BackGround后台进程目前可以无视等待更新~~
- Client客户端测试、客户端
- Domain领域层Dto、服务接口层、模型层、仓储层、服务层
- Infrastructure基础实例层(通用工具层、核心层、定时任务Job、国际化、Web扩展层)
- MicroServiceInstance服务层微服务
#### 运维
- [x] 部署nginx
- [x] CICDgitlab+Jenkins
- [x] Dockerharbor
****
### 安装教程:
### 业务支持模块:
我们将在之后更新教程手册!
RABC权限管理系统正在更新
采用ruoyi前端
- 用户管理
- 角色管理
- 菜单管理
- 部门管理
- 岗位管理
- 字典管理
- 参数管理
- 用户在线
- 操作日志
- 登录日志
- 等等
1. 下载全部源码默认使用sqlite数据库已经生成
2. 直接点击sln文件运行即可没有其他依赖
**演示截图:**
![输入图片说明](readme/1.png)
![输入图片说明](readme/2.png)
![输入图片说明](readme/3.png)
![输入图片说明](readme/4.png)
![输入图片说明](readme/5.png)
![输入图片说明](readme/6.png)
![输入图片说明](readme/7.png)
![输入图片说明](readme/8.png)
![输入图片说明](readme/9.png)
![输入图片说明](readme/10.png)
****
### 使用说明:
BBS论坛系统持续迭代
- 文章管理
- 评论管理
- 主题管理
- 板块管理
- 点赞管理
- 等等
1. 导入使用仓库中的WebFirst数据库
2. 使用WebFirst添加实体、同步实体、修改模板生成路径并生成方案
ERP进销存系统正在更新
- 供货商管理
- 等等
没了,恭喜你已经成功完成了项目,并且已经具备大部分通用场景业务
是不是一个字?爽!
到此为止,你无需写任何一个代码!
SHOP电商系统持续迭代
- SPU管理
- SKU管理
- 商品规格
- 商品分类
- 等等
**爽点**
新人,看这里,项目下载之后直接可以启动,无任何依赖,之后你可以查看`Test控制器`,迫不及待的快来爽一爽!
我们将使用说明转移至我们的官方论坛中,正在制作中,尽情期待!
****
### 感谢:
@@ -196,29 +243,11 @@ QQ交流群官方一群已满、官方二群已满、官方三群
联系作者,这里人人都是顾问
官方网址:正在建设
官方网址留言区:[ccnetcore.com](https://ccnetcore.com)
****
### FQA:
问1为什么不采用EFcore
前往官网查看留言区
答1别问问就是Sqlsugar和本框架一样爽
问2以后会持续更新下去吗
答2一定会的我们的标题是 一个和Sqlsugar一样爽的.Net6开源框架 只要Sqlsugar在我们将一直更新下去。
问3这个框架的针对人群是哪些人适合所有人吗
答3并不是适合所有人应该算适合需要有一定基础的开发人员当然如果你是大神你完全可以将这个框架二次开发
问4花如此多的精力制作这个框架是为了什么是为了赚钱吗和目前主流的abp等框架比又有什么意义呢
答4我们与Sqlsugar作者理念一致我们是从用户角度出发框架是为用户服务与ABP复杂上手理念完全是相反的。
问5为何不出版一个详细的说明书呢
答5暂时不会了之后可能会代码都是基于Asp.NetCore框架适用于新手不用造轮子整个框架较为简单阅读源码后基本能自定义改造使用了过难也已经封装完毕别忘了其意义是为了开发更加简易建议添加作者好友这里人人都是顾问。
我大抵要厌倦了负重前行。
[留言区](https://ccnetcore.com/discuss/1641030787056930818)

Binary file not shown.

0
Yi.BBS.Vue3/.env Normal file
View File

View File

@@ -0,0 +1,2 @@
VITE_APP_BASEAPI="/api-dev"
VITE_APP_URL="http://localhost:19001/api"

View File

@@ -0,0 +1 @@
VITE_APP_BASEAPI="/prod-api"

View File

@@ -1,21 +1,26 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.vscode
*.suo
*.ntvs*
*.njsproj

3
Yi.BBS.Vue3/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

29
Yi.BBS.Vue3/README.md Normal file
View File

@@ -0,0 +1,29 @@
# yi-bbs
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Compile and Minify for Production
```sh
npm run build
```

14
Yi.BBS.Vue3/index.html Normal file
View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>意社区</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

6826
Yi.BBS.Vue3/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

33
Yi.BBS.Vue3/package.json Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "yi-bbs",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"axios": "^1.3.4",
"echarts": "^5.4.2",
"element-plus": "^2.2.32",
"highlight": "^0.2.4",
"marked": "^4.2.12",
"mavon-editor": "^3.0.0",
"nprogress": "^0.2.0",
"pinia": "^2.0.32",
"vue": "^3.2.47",
"vue-cropper": "1.0.3",
"vue-router": "^4.1.6",
"yarm": "^0.4.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"sass": "1.52.1",
"unplugin-auto-import": "^0.15.0",
"unplugin-vue-components": "^0.24.0",
"vite": "^4.1.3"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

28
Yi.BBS.Vue3/src/App.vue Normal file
View File

@@ -0,0 +1,28 @@
<script setup>
</script>
<template>
<el-config-provider :locale="locale">
<RouterView />
</el-config-provider>
</template>
<script setup>
import useConfigStore from "@/stores/config";
import { ElConfigProvider } from 'element-plus'
import {onMounted } from "vue";
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
const locale= zhCn;
const configStore = useConfigStore();
//加载全局信息
onMounted(async()=>{
await configStore.getConfig();
})
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,79 @@
import request from '@/utils/request'
// 登录方法
export function login(username, password, code, uuid) {
const data = {
username,
password,
code,
uuid
}
return request({
url: '/account/login',
headers: {
isToken: false
},
method: 'post',
data: data
})
}
// 注册方法
export function register(userName,password,phone,code,uuid) {
const data = {
userName,
password,
phone,
code,
uuid
}
return request({
url: '/account/register',
headers: {
isToken: false
},
method: 'post',
data: data
})
}
// 获取用户详细信息
export function getInfo() {
return request({
url: '/account',
method: 'get'
})
}
// 退出方法
export function logout() {
return request({
url: '/account/logout',
method: 'post'
})
}
// 获取验证码
export function getCodeImg() {
return request({
url: '/account/captcha-image',
headers: {
isToken: false
},
method: 'get',
timeout: 20000
})
}
// 获取短信验证码
export function getCodePhone(phone) {
return request({
url: '/account/captcha-phone',
headers: {
isToken: false
},
method: 'post',
timeout: 20000,
data:{phone}
})
}

View File

@@ -0,0 +1,11 @@
import myaxios from '@/utils/request'
export function operate(discussId){
if(discussId==undefined)
{
return;
}
return myaxios({
url: `/agree/operate/${discussId}`,
method: 'post'
})
};

View File

@@ -0,0 +1,41 @@
import myaxios from '@/utils/request'
export function getList(data){
return myaxios({
url: '/article',
method: 'get',
params:data
})
};
export function get(id){
return myaxios({
url: `/article/${id}`,
method: 'get'
})
};
export function add(data){
return myaxios({
url: `/article`,
method: 'post',
data:data
})
};
export function update(id,data){
return myaxios({
url: `/article/${id}`,
method: 'put',
data:data
})
};
export function del(ids){
return myaxios({
url: `/article/${ids}`,
method: 'delete'
})
};
export function all(discussId)
{
return myaxios({
url: `/article/all/discuss-id/${discussId}`,
method: 'get'
})
}

View File

@@ -0,0 +1,8 @@
import myaxios from '@/utils/request'
export function getList(data){
return myaxios({
url: '/banner',
method: 'get',
params:data
})
};

View File

@@ -0,0 +1,22 @@
import myaxios from '@/utils/request'
export function getListByDiscussId(discussId,data){
return myaxios({
url: `/comment/discuss-id/${discussId}`,
method: 'get',
params:data
})
};
export function add(data){
return myaxios({
url: `/comment`,
method: 'post',
data:data
})
};
export function del(ids){
return myaxios({
url: `/comment/${ids}`,
method: 'delete'
})
};

View File

@@ -0,0 +1,9 @@
import myaxios from '@/utils/request'
//获取配置
export function getAll(){
return myaxios({
url: '/config',
method: 'get'
})
};

View File

@@ -0,0 +1,51 @@
import myaxios from '@/utils/request'
export function getList(data){
return myaxios({
url: '/discuss',
method: 'get',
params:data
})
};
export function getTopList(data){
if(data==undefined)
{
data={isTop:true}
}
else
{
data["isTop"]=true;
}
return myaxios({
url: '/discuss',
method: 'get',
params:data
})
};
export function get(id){
return myaxios({
url: `/discuss/${id}`,
method: 'get'
})
};
export function add(data){
return myaxios({
url: `/discuss`,
method: 'post',
data:data
})
};
export function update(id,data){
return myaxios({
url: `/discuss/${id}`,
method: 'put',
data:data
})
};
export function del(ids){
return myaxios({
url: `/discuss/${ids}`,
method: 'delete'
})
};

View File

@@ -0,0 +1,9 @@
import myaxios from '@/utils/request'
export function upload(data){
return myaxios({
url: '/file',
method: 'post',
data:data,
headers: { 'Content-Type': 'multipart/form-data' }
})
};

View File

@@ -0,0 +1,8 @@
import myaxios from '@/utils/request'
export function getList(data){
return myaxios({
url: '/plate',
method: 'get',
params:data
})
};

View File

@@ -0,0 +1,139 @@
import myaxios from '@/utils/request'
// 查询用户列表
export function listUser(query) {
return myaxios({
url: '/user',
method: 'get',
params: query
})
}
// 查询用户详细
export function getUser(userId) {
return myaxios({
url: '/user/' + parseStrEmpty(userId),
method: 'get'
})
}
// 新增用户
export function addUser(data) {
return myaxios({
url: '/user',
method: 'post',
data: data
})
}
// 修改用户
export function updateUser(id, data) {
return myaxios({
url: `/user/${id}`,
method: 'put',
data: data
})
}
// 删除用户
export function delUser(userId) {
return myaxios({
url: `/user/${userId}`,
method: 'delete',
})
}
// 用户密码重置
export function resetUserPwd(id, password) {
const data = {
password
}
return myaxios({
url: `/account/rest-password/${id}`,
method: 'put',
data: data
})
}
// 用户状态修改
export function changeUserStatus(userId, isDel) {
return myaxios({
url: `/user/${userId}/${isDel}`,
method: 'put'
})
}
// 查询用户个人信息
export function getUserProfile() {
return myaxios({
url: '/account',
method: 'get'
})
}
// 修改用户个人信息
export function updateUserProfile(data) {
return myaxios({
url: `/user/profile`,
method: 'put',
data: data
})
}
// 只修改用户头像
export function updateUserIcon(data) {
return myaxios({
url: `/account/icon`,
method: 'put',
data:{icon:data}
})
}
// 用户密码重置
export function updateUserPwd(oldPassword, newPassword) {
const data = {
oldPassword,
newPassword
}
return myaxios({
url: '/account/password',
method: 'put',
data: data
})
}
// 用户头像上传
export function uploadAvatar(data) {
return request({
url: '/system/user/profile/avatar',
method: 'post',
data: data
})
}
// 查询授权角色
export function getAuthRole(userId) {
return request({
url: '/system/user/authRole/' + userId,
method: 'get'
})
}
// 保存授权角色
export function updateAuthRole(data) {
return request({
url: '/system/user/authRole',
method: 'put',
params: data
})
}
// // 查询部门下拉树结构
// export function deptTreeSelect() {
// return request({
// url: '/system/user/deptTree',
// method: 'get'
// })
// }

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

View File

@@ -0,0 +1,710 @@
@font-face {
font-family: octicons-link;
src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff');
}
.markdown-body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
line-height: 1.5;
color: #24292e;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
}
.markdown-body .pl-c {
color: #6a737d;
}
.markdown-body .pl-c1,
.markdown-body .pl-s .pl-v {
color: #005cc5;
}
.markdown-body .pl-e,
.markdown-body .pl-en {
color: #6f42c1;
}
.markdown-body .pl-smi,
.markdown-body .pl-s .pl-s1 {
color: #24292e;
}
.markdown-body .pl-ent {
color: #22863a;
}
.markdown-body .pl-k {
color: #d73a49;
}
.markdown-body .pl-s,
.markdown-body .pl-pds,
.markdown-body .pl-s .pl-pse .pl-s1,
.markdown-body .pl-sr,
.markdown-body .pl-sr .pl-cce,
.markdown-body .pl-sr .pl-sre,
.markdown-body .pl-sr .pl-sra {
color: #032f62;
}
.markdown-body .pl-v,
.markdown-body .pl-smw {
color: #e36209;
}
.markdown-body .pl-bu {
color: #b31d28;
}
.markdown-body .pl-ii {
color: #fafbfc;
background-color: #b31d28;
}
.markdown-body .pl-c2 {
color: #fafbfc;
background-color: #d73a49;
}
.markdown-body .pl-c2::before {
content: "^M";
}
.markdown-body .pl-sr .pl-cce {
font-weight: bold;
color: #22863a;
}
.markdown-body .pl-ml {
color: #735c0f;
}
.markdown-body .pl-mh,
.markdown-body .pl-mh .pl-en,
.markdown-body .pl-ms {
font-weight: bold;
color: #005cc5;
}
.markdown-body .pl-mi {
font-style: italic;
color: #24292e;
}
.markdown-body .pl-mb {
font-weight: bold;
color: #24292e;
}
.markdown-body .pl-md {
color: #b31d28;
background-color: #ffeef0;
}
.markdown-body .pl-mi1 {
color: #22863a;
background-color: #f0fff4;
}
.markdown-body .pl-mc {
color: #e36209;
background-color: #ffebda;
}
.markdown-body .pl-mi2 {
color: #f6f8fa;
background-color: #005cc5;
}
.markdown-body .pl-mdr {
font-weight: bold;
color: #6f42c1;
}
.markdown-body .pl-ba {
color: #586069;
}
.markdown-body .pl-sg {
color: #959da5;
}
.markdown-body .pl-corl {
text-decoration: underline;
color: #032f62;
}
.markdown-body .octicon {
display: inline-block;
vertical-align: text-top;
fill: currentColor;
}
.markdown-body a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
.markdown-body a:active,
.markdown-body a:hover {
outline-width: 0;
}
.markdown-body strong {
font-weight: inherit;
}
.markdown-body strong {
font-weight: bolder;
}
.markdown-body h1 {
font-size: 2em;
margin: 0.67em 0;
}
.markdown-body img {
border-style: none;
}
.markdown-body svg:not(:root) {
overflow: hidden;
}
.markdown-body code,
.markdown-body kbd,
.markdown-body pre {
font-family: monospace, monospace;
font-size: 1em;
}
.markdown-body hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
.markdown-body input {
font: inherit;
margin: 0;
}
.markdown-body input {
overflow: visible;
}
.markdown-body [type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
.markdown-body * {
box-sizing: border-box;
}
.markdown-body input {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
.markdown-body a {
color: #0366d6;
text-decoration: none;
}
.markdown-body a:hover {
text-decoration: underline;
}
.markdown-body strong {
font-weight: 600;
}
.markdown-body hr {
height: 0;
margin: 15px 0;
overflow: hidden;
background: transparent;
border: 0;
border-bottom: 1px solid #dfe2e5;
}
.markdown-body hr::before {
display: table;
content: "";
}
.markdown-body hr::after {
display: table;
clear: both;
content: "";
}
.markdown-body table {
border-spacing: 0;
border-collapse: collapse;
}
.markdown-body td,
.markdown-body th {
padding: 0;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
margin-top: 0;
margin-bottom: 0;
}
.markdown-body h1 {
font-size: 32px;
font-weight: 600;
}
.markdown-body h2 {
font-size: 24px;
font-weight: 600;
}
.markdown-body h3 {
font-size: 20px;
font-weight: 600;
}
.markdown-body h4 {
font-size: 16px;
font-weight: 600;
}
.markdown-body h5 {
font-size: 14px;
font-weight: 600;
}
.markdown-body h6 {
font-size: 12px;
font-weight: 600;
}
.markdown-body p {
margin-top: 0;
margin-bottom: 10px;
}
.markdown-body blockquote {
margin: 0;
}
.markdown-body ul,
.markdown-body ol {
padding-left: 0;
margin-top: 0;
margin-bottom: 0;
}
.markdown-body ol ol,
.markdown-body ul ol {
list-style-type: lower-roman;
}
.markdown-body ul ul ol,
.markdown-body ul ol ol,
.markdown-body ol ul ol,
.markdown-body ol ol ol {
list-style-type: lower-alpha;
}
.markdown-body dd {
margin-left: 0;
}
.markdown-body code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}
.markdown-body pre {
margin-top: 0;
margin-bottom: 0;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}
.markdown-body .octicon {
vertical-align: text-bottom;
}
.markdown-body .pl-0 {
padding-left: 0 !important;
}
.markdown-body .pl-1 {
padding-left: 4px !important;
}
.markdown-body .pl-2 {
padding-left: 8px !important;
}
.markdown-body .pl-3 {
padding-left: 16px !important;
}
.markdown-body .pl-4 {
padding-left: 24px !important;
}
.markdown-body .pl-5 {
padding-left: 32px !important;
}
.markdown-body .pl-6 {
padding-left: 40px !important;
}
.markdown-body::before {
display: table;
content: "";
}
.markdown-body::after {
display: table;
clear: both;
content: "";
}
.markdown-body>*:first-child {
margin-top: 0 !important;
}
.markdown-body>*:last-child {
margin-bottom: 0 !important;
}
.markdown-body a:not([href]) {
color: inherit;
text-decoration: none;
}
.markdown-body .anchor {
float: left;
padding-right: 4px;
margin-left: -20px;
line-height: 1;
}
.markdown-body .anchor:focus {
outline: none;
}
.markdown-body p,
.markdown-body blockquote,
.markdown-body ul,
.markdown-body ol,
.markdown-body dl,
.markdown-body table,
.markdown-body pre {
margin-top: 0;
margin-bottom: 16px;
}
.markdown-body hr {
height: 0.25em;
padding: 0;
margin: 24px 0;
background-color: #e1e4e8;
border: 0;
}
.markdown-body blockquote {
padding: 0 1em;
color: #6a737d;
border-left: 0.25em solid #dfe2e5;
}
.markdown-body blockquote>:first-child {
margin-top: 0;
}
.markdown-body blockquote>:last-child {
margin-bottom: 0;
}
.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
color: #444d56;
vertical-align: middle;
background-color: #fafbfc;
border: solid 1px #c6cbd1;
border-bottom-color: #959da5;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #959da5;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: #1b1f23;
vertical-align: middle;
visibility: hidden;
}
.markdown-body h1:hover .anchor,
.markdown-body h2:hover .anchor,
.markdown-body h3:hover .anchor,
.markdown-body h4:hover .anchor,
.markdown-body h5:hover .anchor,
.markdown-body h6:hover .anchor {
text-decoration: none;
}
.markdown-body h1:hover .anchor .octicon-link,
.markdown-body h2:hover .anchor .octicon-link,
.markdown-body h3:hover .anchor .octicon-link,
.markdown-body h4:hover .anchor .octicon-link,
.markdown-body h5:hover .anchor .octicon-link,
.markdown-body h6:hover .anchor .octicon-link {
visibility: visible;
}
.markdown-body h1 {
padding-bottom: 0.3em;
font-size: 2em;
border-bottom: 1px solid #eaecef;
}
.markdown-body h2 {
padding-bottom: 0.3em;
font-size: 1.5em;
border-bottom: 1px solid #eaecef;
}
.markdown-body h3 {
font-size: 1.25em;
}
.markdown-body h4 {
font-size: 1em;
}
.markdown-body h5 {
font-size: 0.875em;
}
.markdown-body h6 {
font-size: 0.85em;
color: #6a737d;
}
.markdown-body ul,
.markdown-body ol {
padding-left: 2em;
}
.markdown-body ul ul,
.markdown-body ul ol,
.markdown-body ol ol,
.markdown-body ol ul {
margin-top: 0;
margin-bottom: 0;
}
.markdown-body li>p {
margin-top: 16px;
}
.markdown-body li+li {
margin-top: 0.25em;
}
.markdown-body dl {
padding: 0;
}
.markdown-body dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: 600;
}
.markdown-body dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
.markdown-body table {
display: block;
width: 100%;
overflow: auto;
}
.markdown-body table th {
font-weight: 600;
}
.markdown-body table th,
.markdown-body table td {
padding: 6px 13px;
border: 1px solid #dfe2e5;
}
.markdown-body table tr {
background-color: #fff;
border-top: 1px solid #c6cbd1;
}
.markdown-body table tr:nth-child(2n) {
background-color: #f6f8fa;
}
.markdown-body img {
max-width: 100%;
box-sizing: content-box;
background-color: #fff;
}
.markdown-body img[align=right] {
padding-left: 20px;
}
.markdown-body img[align=left] {
padding-right: 20px;
}
.markdown-body code {
padding: 0;
padding-top: 0.2em;
padding-bottom: 0.2em;
margin: 0;
font-size: 85%;
background-color: rgba(27,31,35,0.05);
border-radius: 3px;
}
.markdown-body code::before,
.markdown-body code::after {
letter-spacing: -0.2em;
content: "\00a0";
}
.markdown-body pre {
word-wrap: normal;
}
.markdown-body pre>code {
padding: 0;
margin: 0;
font-size: 100%;
word-break: normal;
white-space: pre;
background: transparent;
border: 0;
}
.markdown-body .highlight {
margin-bottom: 16px;
}
.markdown-body .highlight pre {
margin-bottom: 0;
word-break: normal;
}
.markdown-body .highlight pre,
.markdown-body pre {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
/* background-color: #f6f8fa; */
border-radius: 3px;
}
.markdown-body pre code {
display: inline;
max-width: auto;
padding: 0;
margin: 0;
overflow: visible;
line-height: inherit;
word-wrap: normal;
background-color: transparent;
border: 0;
}
.markdown-body pre code::before,
.markdown-body pre code::after {
content: normal;
}
.markdown-body .full-commit .btn-outline:not(:disabled):hover {
color: #005cc5;
border-color: #005cc5;
}
.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
line-height: 10px;
color: #444d56;
vertical-align: middle;
background-color: #fafbfc;
border: solid 1px #d1d5da;
border-bottom-color: #c6cbd1;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #c6cbd1;
}
.markdown-body :checked+.radio-label {
position: relative;
z-index: 1;
border-color: #0366d6;
}
.markdown-body .task-list-item {
list-style-type: none;
}
.markdown-body .task-list-item+.task-list-item {
margin-top: 3px;
}
.markdown-body .task-list-item input {
margin: 0 0.2em 0.25em -1.6em;
vertical-align: middle;
}
.markdown-body hr {
border-bottom-color: #eee;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,48 @@
/* @import './base.css'; */
/* #app {
margin: 0 auto;
font-weight: normal;
} */
body
{
margin: 0 auto;
font-weight: normal;
}
a{
text-decoration: none;
}
/*
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
} */

View File

@@ -0,0 +1,190 @@
// @import './variables.module.scss';
// @import './mixin.scss';
// @import './transition.scss';
// @import './element-ui.scss';
// @import './sidebar.scss';
// @import './btn.scss';
// @import './ruoyi.scss';
// body {
// height: 100%;
// margin: 0;
// -moz-osx-font-smoothing: grayscale;
// -webkit-font-smoothing: antialiased;
// text-rendering: optimizeLegibility;
// font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
// }
// label {
// font-weight: 700;
// }
// html {
// height: 100%;
// box-sizing: border-box;
// }
// #app {
// height: 100%;
// }
// *,
// *:before,
// *:after {
// box-sizing: inherit;
// }
// .no-padding {
// padding: 0px !important;
// }
// .padding-content {
// padding: 4px 0;
// }
// a:focus,
// a:active {
// outline: none;
// }
// a,
// a:focus,
// a:hover {
// cursor: pointer;
// color: inherit;
// text-decoration: none;
// }
// div:focus {
// outline: none;
// }
// .fr {
// float: right;
// }
// .fl {
// float: left;
// }
// .pr-5 {
// padding-right: 5px;
// }
// .pl-5 {
// padding-left: 5px;
// }
// .block {
// display: block;
// }
// .pointer {
// cursor: pointer;
// }
// .inlineBlock {
// display: block;
// }
// .clearfix {
// &:after {
// visibility: hidden;
// display: block;
// font-size: 0;
// content: " ";
// clear: both;
// height: 0;
// }
// }
// aside {
// background: #eef1f6;
// padding: 8px 24px;
// margin-bottom: 20px;
// border-radius: 2px;
// display: block;
// line-height: 32px;
// font-size: 16px;
// font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
// color: #2c3e50;
// -webkit-font-smoothing: antialiased;
// -moz-osx-font-smoothing: grayscale;
// a {
// color: #337ab7;
// cursor: pointer;
// &:hover {
// color: rgb(32, 160, 255);
// }
// }
// }
// .components-container {
// margin: 30px 50px;
// position: relative;
// }
// .pagination-container {
// margin-top: 30px;
// }
.text-center {
text-align: center
}
// .sub-navbar {
// height: 50px;
// line-height: 50px;
// position: relative;
// width: 100%;
// text-align: right;
// padding-right: 20px;
// transition: 600ms ease position;
// background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
// .subtitle {
// font-size: 20px;
// color: #fff;
// }
// &.draft {
// background: #d0d0d0;
// }
// &.deleted {
// background: #d0d0d0;
// }
// }
// .link-type,
// .link-type:focus {
// color: #337ab7;
// cursor: pointer;
// &:hover {
// color: rgb(32, 160, 255);
// }
// }
// .filter-container {
// padding-bottom: 10px;
// .filter-item {
// display: inline-block;
// vertical-align: middle;
// margin-bottom: 10px;
// }
// }
// //refine vue-multiselect plugin
// .multiselect {
// line-height: 16px;
// }
// .multiselect--active {
// z-index: 1000 !important;
// }

View File

@@ -0,0 +1,206 @@
* {
margin: 0;
padding: 0;
}
/*公共CSS*/
.box {
width: 100vw;
height: 100vh;
background-color: rgb(29, 67, 89);
.content {
box-shadow: 0px 1px 6px #3B4859;
.login-wrapper {
h1 {
text-align: center;
}
.login-form {
.form-item {
margin: 20px 0;
span {
display: block;
margin: 5px 20px;
font-weight: 100;
}
.input-item {
width: 100%;
border-radius: 40px;
padding: 20px;
box-sizing: border-box;
font-size: 20px;
font-weight: 200;
&:focus {
outline: none;
}
}
}
.login-btn {
width: 100%;
border-radius: 40px;
color: #fff;
border: 0;
font-weight: 100;
margin-top: 10px;
cursor: pointer;
}
}
.divider {
width: 100%;
margin: 20px 0;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
span:nth-child(1) {
flex: 1;
}
span:nth-child(3) {
flex: 1;
}
.line {
display: inline-block;
max-width: 30%;
width: 30%;
}
.divider-text {
vertical-align: middle;
margin: 0px 20px;
line-height: 0px;
display: inline-block;
width: 100px;
}
}
.other-login-wrapper {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.other-login-item {
border: 1px solid rgb(214, 222, 228);
padding: 10px;
margin: 10px;
cursor: pointer;
}
}
}
}
/*一般大于手机的尺寸CSS*/
@media (min-width: 767px) {
.box {
background-color: #F0F2F5;
.content {
width: 85vw;
height: 90vh;
background: url("@/assets/login_images/login_two.jpg") no-repeat;
background-size: 90% 100%;
position: absolute;
right: 15%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 20px;
background-color: #fff;
.login-wrapper {
width: 25vw;
position: absolute;
right: 15%;
top: 50%;
transform: translateY(-50%);
h1 {
text-align: center;
font-size: 45px;
color: rgb(81, 100, 115);
margin-bottom: 40px;
}
.login-form {
margin: 10px 0;
.form-item {
span {
color: rgb(81, 100, 115);
}
.input-item {
height: 60px;
border: 1px solid rgb(214, 222, 228);
}
}
.login-btn {
height: 50px;
background-color: rgb(59, 72, 89);
font-size: 20px;
}
}
.divider {
.line {
border-bottom: 1px solid rgb(214, 222, 228);
}
}
.other-login-item {
border-radius: 20px;
img {
width: 40px;
height: 40px;
}
}
}
}
}
}
/*手机端CSS*/
@media (max-width: 768px) {
.box {
.content {
width: 100vw;
height: 100vh;
background: url("@/assets/login_images/login_bg_phone.png") no-repeat;
background-size: 100% 100%;
display: flex;
align-items: flex-start;
justify-content: center;
.login-wrapper {
width: 70%;
height: 60%;
padding-top: 15%;
h1 {
font-size: 30px;
color: #fff;
}
.login-form {
.form-item {
margin: 10px 0;
span {
color: rgb(113, 129, 141);
}
.input-item {
height: 30px;
border: 1px solid rgb(113, 129, 141);
background-color: transparent;
color: #fff;
}
}
.login-btn {
height: 40px;
background-color: rgb(235, 95, 93);
font-size: 16px;
}
}
.divider {
.line {
border-bottom: 1px solid #fff;
}
.divider-text {
color: #fff;
}
}
.other-login-item {
border-radius: 15px;
img {
width: 35px;
height: 35px;
}
}
}
}
}
}

View File

@@ -0,0 +1,271 @@
/**
* 通用css样式布局处理
* Copyright (c) 2019 ruoyi
*/
/** 基础通用 **/
.pt5 {
padding-top: 5px;
}
.pr5 {
padding-right: 5px;
}
.pb5 {
padding-bottom: 5px;
}
.mt5 {
margin-top: 5px;
}
.mr5 {
margin-right: 5px;
}
.mb5 {
margin-bottom: 5px;
}
.mb8 {
margin-bottom: 8px;
}
.ml5 {
margin-left: 5px;
}
.mt10 {
margin-top: 10px;
}
.mr10 {
margin-right: 10px;
}
.mb10 {
margin-bottom: 10px;
}
.ml10 {
margin-left: 10px;
}
.mt20 {
margin-top: 20px;
}
.mr20 {
margin-right: 20px;
}
.mb20 {
margin-bottom: 20px;
}
.ml20 {
margin-left: 20px;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.1;
color: inherit;
}
.el-dialog:not(.is-fullscreen) {
margin-top: 6vh !important;
}
.el-dialog.scrollbar .el-dialog__body {
overflow: auto;
overflow-x: hidden;
max-height: 70vh;
padding: 10px 20px 0;
}
.el-table {
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
th {
word-break: break-word;
background-color: #f8f8f9 !important;
color: #515a6e;
height: 40px !important;
font-size: 13px;
}
}
.el-table__body-wrapper {
.el-button [class*="el-icon-"] + span {
margin-left: 1px;
}
}
}
/** 表单布局 **/
.form-header {
font-size:15px;
color:#6379bb;
border-bottom:1px solid #ddd;
margin:8px 10px 25px 10px;
padding-bottom:5px
}
/** 表格布局 **/
.pagination-container {
// position: relative;
height: 25px;
margin-bottom: 10px;
margin-top: 15px;
padding: 10px 20px !important;
}
/* tree border */
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7;
background: #FFFFFF none;
border-radius:4px;
width: 100%;
}
.pagination-container .el-pagination {
right: 0;
position: absolute;
}
@media ( max-width : 768px) {
.pagination-container .el-pagination > .el-pagination__jump {
display: none !important;
}
.pagination-container .el-pagination > .el-pagination__sizes {
display: none !important;
}
}
.el-table .fixed-width .el-button--small {
padding-left: 0;
padding-right: 0;
width: inherit;
}
/** 表格更多操作下拉样式 */
.el-table .el-dropdown-link {
cursor: pointer;
color: #409EFF;
margin-left: 10px;
}
.el-table .el-dropdown, .el-icon-arrow-down {
font-size: 12px;
}
.el-tree-node__content > .el-checkbox {
margin-right: 8px;
}
.list-group-striped > .list-group-item {
border-left: 0;
border-right: 0;
border-radius: 0;
padding-left: 0;
padding-right: 0;
}
.list-group {
padding-left: 0px;
list-style: none;
}
.list-group-item {
border-bottom: 1px solid #e7eaec;
border-top: 1px solid #e7eaec;
margin-bottom: -1px;
padding: 11px 0px;
font-size: 13px;
}
// .el-card__header {
// padding: 14px 15px 7px !important;
// min-height: 40px;
// }
// .el-card__body {
// padding: 15px 20px 20px 20px !important;
// }
.card-box {
padding-right: 15px;
padding-left: 15px;
margin-bottom: 10px;
}
/* button color */
.el-button--cyan.is-active,
.el-button--cyan:active {
background: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
.el-button--cyan:focus,
.el-button--cyan:hover {
background: #48D1CC;
border-color: #48D1CC;
color: #FFFFFF;
}
.el-button--cyan {
background-color: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
/* text color */
.text-navy {
color: #1ab394;
}
.text-primary {
color: inherit;
}
.text-success {
color: #1c84c6;
}
.text-info {
color: #23c6c8;
}
.text-warning {
color: #f8ac59;
}
.text-danger {
color: #ed5565;
}
.text-muted {
color: #888888;
}
/* image */
.img-circle {
border-radius: 50%;
}
.img-lg {
width: 120px;
height: 120px;
}
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 200px;
height: 200px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
}
/* 拖拽列样式 */
.sortable-ghost{
opacity: .8;
color: #fff!important;
background: #42b983!important;
}
/* 表格右侧工具栏样式 */
.top-right-btn {
margin-left: auto;
}

View File

@@ -0,0 +1,55 @@
<template>
<el-button text @click="agree">
<el-icon v-if="data.isAgree" color="#409EFF">
<CircleCheckFilled />
</el-icon>
<el-icon v-else color="#1E1E1E">
<Pointer />
</el-icon> 点赞:{{ data.agreeNum ?? 0 }}</el-button>
</template>
<script setup>
import {onMounted,reactive,watch} from 'vue'
import { operate } from '@/apis/agreeApi'
//'isAgree','agreeNum','id'
const props = defineProps([ 'data'])
watch(()=>props,(n)=>{
data.id=n.data.id;
data.isAgree=n.data.isAgree;
data.agreeNum=n.data.agreeNum;
},{deep:true})
const data=reactive({
id:'',
isAgree:false,
agreeNum:0
})
// onMounted(()=>{
// })
//点赞操作
const agree = async () => {
const response = await operate(data.id)
const res = response.data;
//提示框,颜色区分
if (res.isAgree) {
data.isAgree = true;
data.agreeNum += 1;
ElMessage({
message: res.message,
type: 'success',
})
}
else {
data.isAgree = false;
data.agreeNum-= 1;
ElMessage({
message: res.message,
type: 'warning',
})
}
}
</script>

View File

@@ -0,0 +1,43 @@
<template>
<div>
<div class="markdown-body" v-html="outputHtml"></div>
</div>
</template>
<script setup>
import { marked } from 'marked';
import hljs from "highlight.js";
//可以设置加载样式切换主题
import 'highlight.js/styles/atom-one-dark.css'
import '@/assets/github-markdown.css'
import { ref,watch } from 'vue';
const outputHtml=ref("")
const props = defineProps(['code'])
watch(props,(n,o)=>{
marked.setOptions({
renderer: new marked.Renderer(),
highlight: function(code) {
return hljs.highlightAuto(code).value;
},
pedantic: false,
gfm: true,//允许 Git Hub标准的markdown
tables: true,//支持表格
breaks: true,
sanitize: false,
smartLists: true,
smartypants: false,
xhtml: false,
smartLists: true,
}
);
//需要注意代码块样式
outputHtml.value = marked(n.code).replace(/<pre>/g, "<pre class='hljs'>")
})
</script>

View File

@@ -0,0 +1,131 @@
<template >
<div class="avatar">
<div class="avatar-left">
<el-avatar :size="props.size" :src="iconUrl" />
<div v-if="props.isSelf">
<div class="nick"> {{ userInfo.nick }}</div>
</div>
<div v-if="!props.isSelf">
<div class="nick" :class="{ mt_1: props.time != 'undefined' }"> {{ userInfo.nick }}</div>
<div class="remarks" v-if="props.time"> {{ props.time }}</div>
<div class="remarks">
<slot name="bottom" />
</div>
</div>
<div class="info" v-if="!props.isSelf">
<el-tag class="ml-2" type="warning">V8</el-tag>
<el-tag class="ml-2" type="danger">会员</el-tag>
</div>
</div>
<el-button v-if="props.showWatching" type="primary" size="default" icon="Plus">关注</el-button>
</div>
</template>
<script setup>
import useUserStore from '@/stores/user'
import { reactive, watch, onMounted, computed, ref } from 'vue';
//userInfo
//{icon,name,role,id},根据判断userInfo是否等于未定义来觉得是当前登录用户信息还是其他人信息
const props = defineProps(['size', 'showWatching', 'time', 'userInfo', 'isSelf'])
const userStore = useUserStore();
const userInfo = reactive({
icon: "",
nick: "",
role: [],
id: ""
});
const iconUrl=ref('/src/assets/logo.ico');
const iconUrlHandler = () => {
if (userInfo.icon == null || userInfo.icon == undefined || userInfo.icon == '') {
return '/src/assets/logo.ico';
}
return `${import.meta.env.VITE_APP_BASEAPI}/file/${userInfo.icon}`;
}
watch(userStore, (n) => {
if (props.userInfo == undefined) {
userInfo.nick = n.name;
}
})
watch(() => props, (n) => {
Init();
}, { deep: true })
onMounted(() => {
Init();
})
const Init = () => {
//使用传入值
if (props.userInfo != undefined) {
userInfo.icon = props.userInfo.icon;
userInfo.nick = props.userInfo.nick;
userInfo.role = props.userInfo.role;
userInfo.id = props.userInfo.id;
iconUrl.value=iconUrlHandler(userInfo.icon)
}
//使用当前登录用户
else {
userInfo.icon = userStore.icon;
userInfo.nick = userStore.name;
userInfo.role = userStore.role;
userInfo.id = userStore.id;
iconUrl.value=userInfo.icon;
}
}
</script>
<style scoped>
.mt_1 {
margin-top: 0.5rem;
}
.nick {
font-weight: bold;
}
.info {
margin-top: 0.6rem;
margin-left: 1rem;
}
.info .el-tag {
margin-right: 1rem;
}
.el-icon {
color: white;
}
.avatar {
display: flex;
justify-content: space-between;
}
.avatar-left {
display: flex;
justify-content: flex-start;
align-items: center;
}
.el-avatar {
margin-right: 1.2rem;
}
.remarks {
padding-top: 0.5rem;
color: #8C8C8C;
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<div class="botton-div">
<a><el-icon><UserFilled /></el-icon>站长{{configStore.author}}</a>
<a><el-icon><Search /></el-icon>{{configStore.bottom}}</a>
<a><el-icon><View /></el-icon>关于本站</a>
<a><el-icon><Message /></el-icon>建议反馈</a>
<p></p>
<a ><el-icon><Position /></el-icon>{{configStore.icp}}</a>
</div>
</template>
<script setup>
import useConfigStore from "@/stores/config";
const configStore= useConfigStore();
</script>
<style scoped>
.el-icon
{margin: 0 0.2rem;}
a{
margin-right: 2rem;
line-height: 1.8rem;
}
a:hover {
color: #40a9ff;
cursor:pointer;
}
.botton-div
{
background: transparent;
color: rgba(0,0,0,.45);
font-size: 14px;
display: flex;
flex-wrap: wrap;
height: auto;
width: auto;
justify-content: center;
margin: 0.5rem auto;
}
</style>

View File

@@ -0,0 +1,204 @@
<template>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="评论" name="comment"></el-tab-pane>
<el-tab-pane label="相关内容" name="interrelated"></el-tab-pane>
</el-tabs>
<div class="total">
<div style="align-self: center;"> {{total}}个评论</div>
<div> <el-radio-group v-model="selectRadio">
<el-radio-button label="new" name="new">最新</el-radio-button>
<el-radio-button label="host" name="host">最热</el-radio-button>
</el-radio-group></div>
</div>
<el-divider />
<div v-hasPer="['bbs:comment:add']">
<el-input v-model="topContent" placeholder="发表一个友善的评论吧~" :rows="5" type="textarea"></el-input>
<el-button @click="addTopComment" type="primary" class="btn-top-comment" >发表评论</el-button>
<el-button class="btn-top-comment">其他</el-button>
<el-divider />
</div>
<!-- 开始评论主体 -->
<div v-for="item in commentList" :key="item.id" class="comment1">
<AvatarInfo :userInfo="item.createUser" />
<div class="content">
{{ item.content }}
</div>
<span class="time"> {{ item.creationTime }} </span>
<span class="pointer"><el-icon>
<Pointer />
</el-icon> 0</span>
<el-button type="primary" @click="replay(item.createUser.nick, item.id, item.id)" size="large" text v-hasPer="['bbs:comment:add']">回复</el-button>
<el-button type="danger" @click="delComment(item.id)" size="large" text v-hasPer="['bbs:comment:remove']">删除</el-button>
<div v-show="replayId == item.id" class="input-reply">
<el-input v-model="form.content" :placeholder="placeholder" :rows="3" type="textarea"></el-input>
<div class="btn-reply">
<el-button @click="addComment" type="primary" v-hasPer="['bbs:comment:add']">回复</el-button>
</div>
</div>
<!-- 开始子评论主体 -->
<div v-for="children in item.children" :key="children.id" class="comment2">
<div style="display: flex ;">
<AvatarInfo :userInfo="children.createUser" />
<span style="align-self: center;color:#606266;"> 回复@{{ children.commentedUser.nick }}</span>
</div>
<div class="content">
{{ children.content }}
</div>
<span class="time">{{ children.creationTime }} </span>
<span class="pointer"> <el-icon>
<Pointer />
</el-icon>0</span>
<el-button type="primary" @click="replay(children.createUser.nick, children.id, item.id)" size="large" text v-hasPer="['bbs:comment:add']">回复</el-button>
<el-button type="danger" @click="delComment(children.id)" size="large" text v-hasPer="['bbs:comment:remove']">删除</el-button>
<div v-show="replayId == children.id" class="input-reply">
<el-input v-model="form.content" :placeholder="placeholder" :rows="3" type="textarea"></el-input>
<div class="btn-reply">
<el-button @click="addComment" type="primary" v-hasPer="['bbs:comment:add']">回复</el-button>
</div>
</div>
</div>
<el-divider />
</div>
<el-empty v-show="commentList.length<=0" description="评论空空如也,快来抢占沙发~" />
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { getListByDiscussId, add ,del} from "@/apis/commentApi.js";
import AvatarInfo from './AvatarInfo.vue';
//数据定义
const route = useRoute();
const router = useRouter();
const commentList = ref([]);
const query = reactive({});
const topContent=ref('');
//当前回复id
const replayId = ref('');
//回复文本框
const placeholder = ref('')
//选择类型:评论
const activeName = ref('comment');
//选择 最新
const selectRadio = ref('new');
//评论总数
const total=ref(0);
const form = reactive({
content: "",
discussId: route.params.discussId,
query,
parentId: 0,
rootId: 0,
});
onMounted(async () => {
await loadComment();
});
const loadComment = async () => {
topContent.value='';
form.content = '';
const response = await getListByDiscussId(route.params.discussId, query);
commentList.value = response.data.items;
total.value=response.data.total
};
const addTopComment = async () => {
form.parentId = 0;
form.rootId = 0;
form.content=topContent.value;
await addComment();
}
const addComment = async () => {
if(form.content.length<=0)
{
ElMessage.error('输入评论不能为空!')
return
}
await add(form);
await loadComment();
ElMessage({
message: '评论发表成功!',
type: 'success',
})
};
const delComment=async(ids)=>{
ElMessageBox.confirm(`确定是否删除编号[${ids}]的评论吗?`, "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
}).then(async () => {
await del(ids);
await loadComment();
ElMessage({
message: '评论已删除!',
type: 'success',
})
});
}
const replay = async (parentUserName, parentId, rootId) => {
replayId.value = parentId;
form.parentId = parentId;
form.rootId = rootId;
placeholder.value = `回复@${parentUserName}`;
}
//切换 评论、相关内容
const handleClick = () => { }
</script>
<style scoped>
.input-reply {
margin-top: 1rem;
}
.btn-reply {
margin: 1rem 0;
display: flex;
justify-content: end;
}
.comment1 .pointer {
margin: 0 0 0 1rem;
}
.time {
color: #8C8C8C;
}
.content {
margin: 1rem 0;
}
.total {
display: flex;
justify-content: space-between;
}
.comment2 {
margin-left: 3rem;
}
.el-divider {
margin: 1rem 0;
}
.btn-top-comment {
margin-top: 0.5rem;
margin-right: 1rem;
}
</style>

View File

@@ -0,0 +1,197 @@
<template>
<el-badge :value="props.badge ?? ''" class="box-card">
<el-card shadow="never" :style="{ 'border-color': discuss.color }">
<el-row>
<!-- 头部 -->
<el-col :span=24 class="card-header">
<AvatarInfo :userInfo="discuss.user" :time="discuss.creationTime" />
</el-col>
<!-- 身体 -->
<el-col :span=18 >
<el-row>
<el-col v-if="discuss.isBan" :span=24 class=" item item-title "> <el-link size="100" :underline="false"
style="color:#F56C6C;" >{{ discuss.title }}</el-link></el-col>
<el-col v-else :span=24 class=" item item-title "> <el-link size="100" :underline="false"
@click="enterDiscuss(discuss.id)">{{ discuss.title }}</el-link></el-col>
<el-col :span=24 class=" item item-description">{{ discuss.introduction }}</el-col>
<el-col :span=24 class=" item item-tag"><el-tag v-for="i in 4" :key="i">教程</el-tag></el-col>
</el-row>
</el-col>
<el-col :span=6 style=" display: flex;justify-content: center;">
<el-image :preview-src-list="[getUrl(discuss.cover)]" v-if="discuss.cover" :src="getUrl(discuss.cover)" style="width: 100px;height: 100px;" />
</el-col>
<!-- 底部 -->
<el-col :span=24 class=" item item-bottom " style=" margin-bottom: 0;">
<el-space :size="10" :spacer="spacer">
<div class="item-description">
{{ discuss.creationTime }}
</div>
<AgreeInfo :data="discuss"/>
<!--
<el-button text @click="agree">
<el-icon v-if="discuss.isAgree" color="#409EFF">
<CircleCheckFilled />
</el-icon>
<el-icon v-else color="#1E1E1E">
<Pointer />
</el-icon> 点赞:{{ discuss.agreeNum ?? 0 }}</el-button>
<el-button icon="Star" text>
收藏</el-button> -->
<el-button icon="View" text>
浏览数:{{ discuss.seeNum ?? 0 }}</el-button>
</el-space>
</el-col>
</el-row>
</el-card>
</el-badge>
</template>
<script setup>
import { h, ref, toRef, onMounted ,reactive} from 'vue'
import { useRouter } from 'vue-router'
import AvatarInfo from './AvatarInfo.vue';
import AgreeInfo from './AgreeInfo.vue'
import { operate } from '@/apis/agreeApi'
const props = defineProps(['discuss','badge'])
const discuss=reactive({
id:'',
title:"",
introduction:"",
creationTime:"",
user:{},
color:"",
seeNum:0,
agreeNum:0,
isAgree:false,
cover:"",
isBan:false,
isAgree:false,
agreeNum:0
})
const router = useRouter()
const spacer = h(ElDivider, { direction: 'vertical' })
const enterDiscuss = (id) => {
router.push(`/article/${id}`)
}
const getUrl= (str)=>{
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
}
//点赞操作
const agree = async () => {
const response = await operate(discuss.id)
const res = response.data;
//提示框,颜色区分
if (res.isAgree) {
discuss.isAgree = true;
discuss.agreeNum += 1;
ElMessage({
message: res.message,
type: 'success',
})
}
else {
discuss.isAgree = false;
discuss.agreeNum-= 1;
ElMessage({
message: res.message,
type: 'warning',
})
}
}
onMounted(() => {
// id:'',
// title:"",
// introduction:"",
// creationTime:"",
// user:{},
// color:"",
// seeNum:0,
// agreeNum:0,
// isAgree:""
discuss.id=props.discuss.id;
discuss.title=props.discuss.title;
discuss.introduction=props.discuss.introduction;
discuss.creationTime=props.discuss.creationTime;
discuss.user=props.discuss.user;
discuss.color=props.discuss.color;
discuss.seeNum=props.discuss.seeNum;
discuss.isAgree=props.discuss.isAgree;
discuss.agreeNum=props.discuss.agreeNum;
discuss.isBan=props.discuss.isBan;
discuss.cover=props.discuss.cover;
discuss.value = props.isAgree;
discuss.value = props.agreeNum;
})
</script>
<style scoped>
.el-card {
border: 2px solid white
}
.item-bottom .el-icon {
margin-right: 0.4rem;
}
.card-header {
display: flex;
margin-bottom: 1.5rem;
align-items: center;
}
.item {
font-size: 14px;
margin-bottom: 18px;
}
.box-card {
width: 100%;
min-height: 15rem;
/* right: calc(1px + var(--el-badge-size)/ 2) !important; */
/* top: 0 !important; */
}
.item-title {
/* font-size: var(--el-font-size-large); */
}
.item-description {
font-size: var(--el-font-size-small);
color: #8C8C8C;
}
.item .el-tag {
margin-right: 1rem;
}
.ml-2 {
margin-left: 1.2rem;
}
.el-link {
font-size: initial;
font-weight: 700;
}
</style>

View File

@@ -0,0 +1,55 @@
<script setup>
import { ref } from 'vue'
const props = defineProps(['items','header','text','hideDivider'])
</script>
<template>
<el-card class="box-card" shadow="never">
<template #header>
<div class="card-header">
<span>{{props.header}}</span>
<el-link :underline="false" type="primary">{{props.text}}</el-link>
</div>
</template>
<slot name="content" />
<div v-for="(item,i) in props.items " >
<div class="text item">
<slot name="item" v-bind="item"/>
</div>
<el-divider v-if="i!=props.items.length-1&&hideDivider==undefined" />
</div>
</el-card>
</template>
<style scoped>
.el-divider
{
margin: 0.2rem 0;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.text {
font-size: 14px;
}
.item {
margin: 0.5rem 0;
}
.box-card {
width: 100%;
}
</style>

View File

@@ -0,0 +1,151 @@
<template>
<mavon-editor ref='md' v-model="text" :subfield="true" :codeStyle="props.codeStyle" :ishljs="true"
:style="{ minHeight: props.height, maxHeight: '100%' }" class="edit" @imgAdd="imgAdd">
<!-- 引用视频链接的自定义按钮 -->
<template v-slot:left-toolbar-after>
<!--点击按钮触发的事件是打开表单对话框-->
<el-button @click="fileDialogVisible=true" aria-hidden="true" class="op-icon fa" title="上传文件">
<el-icon ><FolderChecked /></el-icon>
</el-button>
<el-dropdown :hide-on-click='false'>
<el-button aria-hidden="true" class="op-icon fa" title="表情包">
😊
</el-button>
<template #dropdown>
<el-dropdown-menu >
<el-dropdown-item>
<table border="1">
<tr>
<td @click="text+='😊'">😊</td>
<td>😊</td>
<td>😊</td>
</tr>
<tr>
<td>😊</td>
<td>😊</td>
<td>😊</td>
</tr>
<tr>
<td>😊</td>
<td>😊</td>
<td>😊</td>
</tr>
</table>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dialog
:modal=false
:draggable=true
v-model="fileDialogVisible"
title="文件上传"
width="30%"
:before-close="fileHandleClose"
>
<span>选择你的文件</span>
<el-upload
class="upload-demo"
drag
:action="fileUploadUrl"
multiple
:on-success="onSuccess"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
可将文件拖拽到这里 <em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
文件需小于100MB以内
</div>
</template>
</el-upload>
<p v-for="(item,i) in fileUrlList" :key="i">{{` ${i+1}: ${getUrl(item)}` }} <el-button></el-button></p>
<template #footer>
<span class="dialog-footer">
<el-button @click="fileDialogVisible = false">取消</el-button>
<el-button type="primary" @click="dialogVisible = false">
确认
</el-button>
</span>
</template>
</el-dialog>
</template>
</mavon-editor>
</template>
<script setup>
import { mavonEditor } from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
import { ref, computed, watch, onMounted } from 'vue';
import { upload } from '@/apis/fileApi'
const md = ref(null);
const props = defineProps(['height', 'modelValue', "codeStyle"])
const emit = defineEmits(['update:modelValue'])
const fileDialogVisible=ref(false)
//已经上传好的文件列表url
const fileUrlList=ref([])
const fileUploadUrl=`${import.meta.env.VITE_APP_BASEAPI}/file`
// //v-model传值出去
const text = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
const getUrl= (str)=>{
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
}
//关闭文件上传弹窗
const fileHandleClose=()=>{
fileDialogVisible.value=false;
}
//文件上传成功后
const onSuccess=(response)=>{
fileUrlList.value.push(response[0].id)
}
//图片上传
const imgAdd = async (pos, $file) => {
// 第一步.将图片上传到服务器.
var formdata = new FormData();
formdata.append('file', $file);
const response = await upload(formdata)
const url = `${import.meta.env.VITE_APP_BASEAPI}/file/${response.data[0].id}/true`;
console.log(url)
md.value.$img2Url(pos, url);
}
</script>
<style scoped>
.edit {
width: 100%;
}
</style>

View File

@@ -0,0 +1,49 @@
<template>
<el-row>
<el-col
>
<el-card :body-style="{ padding: '0px' }" shadow="never">
<img
src=""
class="image"
/>
<div style="padding: 14px">
<span>{{props.name}}</span>
<div class="bottom">
<time class="remarks">{{ props.introduction }}</time>
<RouterLink :to="`/discuss/${props.id}`"> <el-button text class="button" type="primary">进入<el-icon><CaretRight /></el-icon></el-button> </RouterLink>
</div>
</div>
</el-card>
</el-col>
</el-row>
</template>
<script setup>
import { onMounted } from 'vue';
const props = defineProps(['name','introduction','id'])
</script>
<style>
.remarks {
font-size: 12px;
color: #999;
}
.bottom {
margin-top: 13px;
line-height: 12px;
display: flex;
justify-content: space-between;
align-items: center;
}
.image {
width: 100%;
display: block;
}
</style>

View File

@@ -0,0 +1,28 @@
<template>
<el-scrollbar>
<div class="scrollbar-flex-content">
<p v-for="item in 50" :key="item" class="scrollbar-item">
推荐
</p>
</div>
</el-scrollbar>
</template>
<style scoped>
.scrollbar-flex-content {
display: flex;
}
.scrollbar-item {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 4rem;
height: 2.6rem;
margin: 0 0.2rem ;
text-align: center;
border-radius: 4px;
background-color:#FAFAFA;
font-size: 14px;
}
</style>

View File

@@ -0,0 +1,67 @@
<template>
<el-tree
:data="props.data==''?[]:props.data"
:props="defaultProps"
@node-click="handleNodeClick"
:expand-on-click-node="false"
node-key="id"
:default-expand-all='true'
:highlight-current="true"
:current-node-key="currentNodeKey"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<span>{{data.name}}</span>
<span>
<a style="color: #409EFF; margin-left: 8px" @click="$emit('create',node, data)" v-hasPer="['bbs:article:add']"
> + </a>
<a style="color: #409EFF; margin-left: 8px" @click="$emit('update',node, data)" v-hasPer="['bbs:article:edit']"
> 编辑 </a>
<a style="color: #f56c6c; margin-left: 8px" @click="$emit('remove',node, data)" v-hasPer="['bbs:article:remove']"
> 删除 </a>
</span>
</span>
</template>
</el-tree>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps(['data',"currentNodeKey"])
const emits= defineEmits(["handleNodeClick"])
const currentNodeKey=props.currentNodeKey;
//数据定义
//子文章数据
// const articleData =ref([]);
//树形子文章选项
const defaultProps = {
children: "children",
label: "name",
};
// //子文章初始化
// const loadArticleData=async()=>
// {
// articleData.value= await articleall(route.params.discussId);
// }
//点击事件
const handleNodeClick = (data) => {
emits('handleNodeClick',data)
};
</script>
<style scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>

View File

@@ -0,0 +1,129 @@
<template>
<el-select
style="width: 600px;"
v-model="value"
multiple
filterable
remote
reserve-keyword
placeholder="请输入用户账号(可多选)"
remote-show-suffix
:remote-method="remoteMethod"
:loading="loading"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
<script setup>
import { onMounted, ref,computed } from 'vue'
import {listUser} from '@/apis/userApi'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
//这个为可选择的列表,{value,label},value为用户idlabel为账号名称不可重复
const options = ref([])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
const loading = ref(false)
onMounted( async()=>{
const response= await listUser({ids:value.value.join()});
const res=response.data.items;
//下拉列表
options.value = res
.map((item) => {
return { value: `${item.id}`, label: `用户:${item.userName}` }
})
})
const loadUser=async(query)=>{
const response= await listUser({userName:query});
const res=response.data.items;
//下拉列表
options.value = res
.map((item) => {
return { value: `${item.id}`, label: `用户:${item.userName}` }
})
}
const remoteMethod =async (query) => {
if (query) {
loading.value = true
await loadUser(query);
loading.value = false
} else {
options.value = []
}
}
const states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Carolina',
'North Dakota',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming',
]
</script>

View File

@@ -0,0 +1,51 @@
<template>
<div ref="VisitsLineChart"></div>
</template>
<script setup>
import * as echarts from 'echarts/core';
import { GridComponent } from 'echarts/components';
import { LineChart } from 'echarts/charts';
import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import { ref ,onMounted} from 'vue';
echarts.use([GridComponent, LineChart, CanvasRenderer, UniversalTransition]);
const VisitsLineChart=ref(null);
onMounted(()=>{
var myChart = echarts.init(VisitsLineChart.value, null, {
width: 320,
height: 230
});
var option;
option = {
xAxis: {
type: 'category',
boundaryGap: false,
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [82, 93, 90, 93, 129, 133, 132],
type: 'line',
areaStyle: {}
}
]
};
option && myChart.setOption(option);
window.addEventListener('resize', function() {
myChart.resize();
});
})
</script>

View File

@@ -0,0 +1,9 @@
import hasRole from './permission/hasRole'
import hasPermi from './permission/hasPermi'
// import copyText from './common/copyText'
export default function directive(app){
app.directive('hasRole', hasRole)
app.directive('hasPer', hasPermi)
// app.directive('copyText', copyText)
}

View File

@@ -0,0 +1,29 @@
/**
* v-hasDiscussPermi 操作权限处理
*/
import useUserStore from '@/stores/user'
//传一个值一个主题id的创建者id判断当前主题是否为自己创建拥有*:*:*,直接跳过
export default {
mounted(el, binding, vnode) {
const { value } = binding
const all_permission = "*:*:*";
const permissions = useUserStore().permissions
const userId = useUserStore().id
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag==userId
})
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置操作主题用户签值`)
}
}
}

View File

@@ -0,0 +1,27 @@
/**
* v-hasPermi 操作权限处理
* Copyright (c) 2019 ruoyi
*/
import useUserStore from '@/stores/user'
export default {
mounted(el, binding, vnode) {
const { value } = binding
const all_permission = "*:*:*";
const permissions = useUserStore().permissions
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag.includes(permission)
})
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置操作权限标签值`)
}
}
}

View File

@@ -0,0 +1,28 @@
/**
* v-hasRole 角色权限处理
* Copyright (c) 2019 ruoyi
*/
import useUserStore from '@/stores/user'
export default {
mounted(el, binding, vnode) {
const { value } = binding
const super_admin = "admin";
const roles = useUserStore().roles
if (value && value instanceof Array && value.length > 0) {
const roleFlag = value
const hasRole = roles.some(role => {
return super_admin === role || roleFlag.includes(role)
})
if (!hasRole) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置角色权限标签值"`)
}
}
}

View File

@@ -0,0 +1,24 @@
<template class="back-color">
<div class="content-main">
<RouterView />
</div>
</template>
<style scoped>
.body-main{
min-height: 10rem;
min-width: 10rem;
background-color:#F0F2F5;
}
.content-main{
margin: 0 auto;
display: flex;
justify-content: center;
min-height: 1150px;
}
</style>

View File

@@ -0,0 +1,112 @@
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
:ellipsis="false"
@select="handleSelect"
>
<el-menu-item class="logo" index="" @click="enterIndex" >
<img class="img-icon" style="width: 35px; height: 35px" src="@/assets/logo.ico" />{{configStore.name}}</el-menu-item>
<el-menu-item index="1" @click="enterIndex">主页</el-menu-item>
<el-sub-menu index="2">
<template #title>学习</template>
<el-menu-item index="2-1">学习 one</el-menu-item>
<el-menu-item index="2-2">学习 two</el-menu-item>
<el-menu-item index="2-3">学习 three</el-menu-item>
</el-sub-menu>
<el-sub-menu index="3">
<template #title>资源</template>
<el-menu-item index="3-1">资源 one</el-menu-item>
<el-menu-item index="3-2">资源 two</el-menu-item>
<el-menu-item index="3-3">资源 three</el-menu-item>
</el-sub-menu>
<el-sub-menu index="4">
<template #title>问答</template>
<el-menu-item index="4-1">问答 one</el-menu-item>
<el-menu-item index="4-2">问答 two</el-menu-item>
<el-menu-item index="4-3">问答 three</el-menu-item>
</el-sub-menu>
<div class="flex-grow" />
<el-menu-item index="5">
<div style="width: 350px;">
<el-input style="width: 300px;" v-model="searchText" placeholder="全站搜索" clearable prefix-icon="Search" />
<el-button type="primary" plain @click="search">搜索</el-button>
</div>
</el-menu-item>
<el-menu-item index="6" @click="enterProfile" >
<AvatarInfo :size='30' :isSelf="true" />
</el-menu-item>
<el-sub-menu index="6">
<template #title>个人中心</template>
<el-menu-item index="6-1" @click="enterProfile">进入个人中心</el-menu-item>
<el-menu-item index="6-2" @click="enterProfile">其他</el-menu-item>
<el-menu-item index="6-3" @click="logout">登出</el-menu-item>
</el-sub-menu>
</el-menu>
</template>
<script setup>
import AvatarInfo from '@/components/AvatarInfo.vue'
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import useUserStore from '@/stores/user.js'
import useConfigStore from "@/stores/config";
const configStore= useConfigStore();
const router = useRouter()
const userStore =useUserStore();
const activeIndex = ref('1')
const searchText=ref('')
const handleSelect = (key, keyPath) => {
console.log(key, keyPath)
}
const logout= async()=>{
ElMessageBox.confirm(`确定登出系统吗?`, "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
}).then( async() => {
//异步
await userStore.logOut();
//删除成功后,跳转到主页
router.push("/login");
ElMessage({
type: "success",
message: "登出成功",
});
});
}
const enterIndex=()=>{
router.push("/index");
};
const enterProfile=()=>{
router.push("/profile");}
const search=()=>{
var routerPer = { path: `/discuss`,query:{q:searchText.value} };
searchText.value='';
router.push(routerPer)
}
</script>
<style scoped>
.logo{
min-width: 14rem;
font-size: large;
font-weight: 600;
}
.flex-grow {
flex-grow: 1;
}
.img-icon
{
margin-right: 0.5rem;
}
</style>

View File

@@ -0,0 +1,27 @@
<template>
<div class="common-layout">
<el-container>
<el-header>
<AppHeader />
</el-header>
<el-main>
<AppBody />
</el-main>
</el-container>
</div>
</template>
<script setup>
import AppHeader from "./AppHeader.vue"
import AppBody from "./AppBody.vue"
</script>
<style scoped>
.el-main{
margin: 0;
padding: 0;
min-height: 10rem;
background-color: #F0F2F5;
}
</style>

View File

@@ -0,0 +1,8 @@
<template>
<div class="box">
<div class="content">
<RouterView/>
</div>
</div>
</template>
<style src="@/assets/styles/login.scss" scoped></style>

23
Yi.BBS.Vue3/src/main.js Normal file
View File

@@ -0,0 +1,23 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import './assets/main.css'
import '@/assets/styles/index.scss' // global css
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import directive from './directive' // directive
import "./permission"
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(createPinia())
directive(app);
app.use(router)
app.mount('#app')

View File

@@ -0,0 +1,65 @@
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'
NProgress.configure({ showSpinner: false });
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()
} else {
if (useUserStore().roles.length === 0)
{
isRelogin.show = true
// 判断当前用户是否已拉取完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: '/' })
})
})
} else {
next()
}
}
} else {
// 没有token
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
NProgress.done()
}
}
})
router.afterEach(() => {
NProgress.done()
})

View File

@@ -0,0 +1,76 @@
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 }
},
routes: [
{
name:'test',
path: '/test',
component: () => import('../views/Test.vue')
},
{
path: '/loginLayout',
name: 'loginLayout',
component: LoginLayout,
redirect: '/login' ,
children :[
{
name:'login',
path: '/login',
component: () => import('../views/Login.vue')
},
{
name:'register',
path: '/register',
component: () => import('../views/Register.vue')
},
]
},
{
path: '/',
name: 'layout',
component: Layout,
redirect: '/index' ,
children :[
{
name:'index',
path: '/index',
component: () => import('../views/Index.vue')
},
{
name:'article',
path: '/article/:discussId/:articleId?',
component: () => import('../views/Article.vue')
},
{
name:'discuss',
path: '/discuss/:plateId?',
component: () => import('../views/Discuss.vue')
},
{
//artTypediscuss主题、article文章
//operTypecreate创建、update更新
name:'editArt',
path:'/editArt',
component:()=>import('../views/EditArticle.vue')
},
{
name:'profile',
path:'/profile',
component:()=>import('../views/profile/Index.vue')
}
]
},
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
]
})
export default router

View File

@@ -0,0 +1,22 @@
import {getAll} from '@/apis/configApi'
import { defineStore } from 'pinia'
const useConfigStore = defineStore('config',
{
state: () => ({
data: []
}),
getters: {
name:(state)=>state.data.filter(s=> s.configKey=='bbs.site.name').map(x=>x.configValue)[0],
author:(state)=>state.data.filter(s=> s.configKey=='bbs.site.author').map(x=>x.configValue)[0],
icp:(state)=>state.data.filter(s=> s.configKey=='bbs.site.icp').map(x=>x.configValue)[0],
bottom:(state)=>state.data.filter(s=>s.configKey=='bbs.site.bottom').map(x=>x.configValue)[0]
},
actions: {
// 登录
async getConfig() {
const response = await getAll();
this.data = response.data.items;
},
},
})
export default useConfigStore;

View File

@@ -0,0 +1,98 @@
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;
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
// this.roles = ["admin"];
// this.permissions=["*:*:*"]
} else {
this.roles = ['ROLE_DEFAULT']
}
// this.roles = ["admin"];
// this.permissions=["*:*:*"]
this.name = user.nick
this.icon = avatar;
this.userName=user.userName;
this.id=user.id;
resolve(res)
}).catch(error => {
reject(error)
})
})
},
// 退出系统
logOut() {
return new Promise((resolve, reject) => {
logout().then(() => {
this.token = ''
this.roles = []
this.permissions = []
removeToken()
resolve()
}).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;

View File

@@ -0,0 +1,13 @@
const TokenKey = 'Admin-Token'
export function getToken() {
return localStorage.getItem(TokenKey)
}
export function setToken(token) {
return localStorage.setItem(TokenKey, token)
}
export function removeToken() {
return localStorage.removeItem (TokenKey)
}

View File

@@ -0,0 +1,81 @@
export default {
'agate': 1,
'androidstudio': 1,
'arduino-light': 1,
'arta': 1,
'ascetic': 1,
'atelier-cave-dark': 1,
'atelier-cave-light': 1,
'atelier-dune-dark': 1,
'atelier-dune-light': 1,
'atelier-estuary-dark': 1,
'atelier-estuary-light': 1,
'atelier-forest-dark': 1,
'atelier-forest-light': 1,
'atelier-heath-dark': 1,
'atelier-heath-light': 1,
'atelier-lakeside-dark': 1,
'atelier-lakeside-light': 1,
'atelier-plateau-dark': 1,
'atelier-plateau-light': 1,
'atelier-savanna-dark': 1,
'atelier-savanna-light': 1,
'atelier-seaside-dark': 1,
'atelier-seaside-light': 1,
'atelier-sulphurpool-dark': 1,
'atelier-sulphurpool-light': 1,
'atom-one-dark': 1,
'atom-one-light': 1,
'brown-paper': 1,
'codepen-embed': 1,
'color-brewer': 1,
'darcula': 1,
'dark': 1,
'darkula': 1,
'default': 1,
'docco': 1,
'dracula': 1,
'far': 1,
'foundation': 1,
'github-gist': 1,
'github': 1,
'googlecode': 1,
'grayscale': 1,
'gruvbox-dark': 1,
'gruvbox-light': 1,
'hopscotch': 1,
'hybrid': 1,
'idea': 1,
'ir-black': 1,
'kimbie.dark': 1,
'kimbie.light': 1,
'magula': 1,
'mono-blue': 1,
'monokai-sublime': 1,
'monokai': 1,
'obsidian': 1,
'ocean': 1,
'paraiso-dark': 1,
'paraiso-light': 1,
'pojoaque': 1,
'purebasic': 1,
'qtcreator_dark': 1,
'qtcreator_light': 1,
'railscasts': 1,
'rainbow': 1,
'routeros': 1,
'school-book': 1,
'solarized-dark': 1,
'solarized-light': 1,
'sunburst': 1,
'tomorrow-night-blue': 1,
'tomorrow-night-bright': 1,
'tomorrow-night-eighties': 1,
'tomorrow-night': 1,
'tomorrow': 1,
'vs': 1,
'vs2015': 1,
'xcode': 1,
'xt256': 1,
'zenburn': 1
};

View File

@@ -0,0 +1,59 @@
import axios from 'axios';
import { getToken } from '@/utils/auth'
export let isRelogin = { show: false };
// import JsonBig from 'json-bigint'
const myaxios = axios.create({
baseURL:import.meta.env.VITE_APP_BASEAPI,
timeout: 50000,
// transformResponse: [data => {
// try {
// const json = JsonBig({
// storeAsString: true
// })
// return json.parse(data)
// } catch (err) {
// // 如果转换失败,则包装为统一数据格式并返回
// return {
// data
// }
// }
// }],
})
// 请求拦截器
myaxios.interceptors.request.use(function (config) {
if (getToken()) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
return config;
}, function (error) {
return Promise.reject(error);
});
// 响应拦截器
myaxios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const response=error.response.data;
//业务异常+应用异常,统一处理
switch(response.code)
{
case 401:
ElMessage.error('登录已过期')
break;
case 403:
ElMessage.error(response.message)
break;
case 500:
ElMessage.error(response.message)
break;
}
return Promise.reject(error);
});
export default myaxios

View File

@@ -0,0 +1,386 @@
<template>
<div style="width: 90%; min-width: 1200px">
<!-- <div style="width: 1200px;"> -->
<el-row :gutter="20" class="top-div">
<el-col :span="5">
<el-row class="art-info-left">
<el-col :span="24">
<InfoCard header="文章信息" text="展开" hideDivider="true">
<template #content>
<el-button style="width: 100%; margin-bottom: 0.8rem" @click="loadDiscuss(true)">首页</el-button>
<el-button v-hasPer="['bbs:article:add']" @click="addArticle(0)" type="primary"
style="width: 100%; margin-bottom: 0.8rem; margin-left: 0">添加子文章</el-button>
<!--目录在这里 -->
<TreeArticleInfo :data="articleData" @remove="delArticle" @update="updateArticle" @create="addNextArticle"
@handleNodeClick="handleNodeClick" :currentNodeKey="currentNodeKey"/>
</template>
</InfoCard>
</el-col>
<el-col :span="24">
<InfoCard :items="items" header="作者分享" text="更多">
<template #item="temp">
<AvatarInfo />
</template>
</InfoCard>
</el-col>
<el-col :span="24">
<InfoCard :items="items" header="内容推荐" text="更多">
<template #item="temp">
<AvatarInfo />
</template>
</InfoCard>
</el-col>
</el-row>
</el-col>
<el-col :span="14">
<el-row class="left-div">
<el-col :span="24">
<!-- {{ discuss.user }} -->
<AvatarInfo :size="50" :showWatching="true" :time="discuss.creationTime" :userInfo="discuss.user"></AvatarInfo>
<!-- :userInfo="{nick:'qwe'} -->
<el-divider />
<h2>{{ discuss.title }}</h2>
<el-image :preview-src-list="[getUrl(discuss.cover)]" v-if="discuss.cover" :src="getUrl(discuss.cover)" style="width: 150px;height: 150px;" />
<ArticleContentInfo :code="discuss.content??''"></ArticleContentInfo>
<el-divider class="tab-divider" />
<el-space :size="10" :spacer="spacer">
<AgreeInfo :data="discuss"/>
<el-button icon="Star" text> 0</el-button>
<el-button icon="Share" text> 分享</el-button>
<el-button icon="Operation" text> 操作</el-button>
</el-space>
</el-col>
<el-col :span="24" class="comment">
<CommentInfo/>
</el-col>
</el-row>
<BottomInfo/>
</el-col>
<el-col :span="5">
<el-row class="right-div">
<el-col :span="24">
<InfoCard class="art-info-right" header="主题信息" text="更多" hideDivider="true">
<template #content>
<div>
<ul class="art-info-ul">
<li>
<el-button type="primary" size="default"
v-hasPer="['bbs:discuss:edit']"
@click="updateHander(route.params.discussId)">编辑</el-button>
<el-button style="margin-left: 1rem" type="danger"
v-hasPer="['bbs:discuss:remove']"
@click="delHander(route.params.discussId)">删除</el-button>
</li>
<li>分类: <span>主题</span></li>
标签:
<el-tag type="success">主题</el-tag>
<el-tag type="info">资源</el-tag>
</ul>
</div>
</template>
</InfoCard>
</el-col>
<el-col :span="24">
<InfoCard class="art-info-right" header="目录" hideDivider="true">
<template #content>
<div>
<el-empty :image-size="100" style="padding: 20px 0;" v-if="catalogueData.length==0" description="无目录" />
<ul v-else class="art-info-ul">
<li v-for="(item, i) in catalogueData" :key="i">
<el-button style="width: 100%; justify-content: left" type="primary" text>{{ `${i + 1} ${item}`
}}</el-button>
</li>
</ul>
</div>
</template>
</InfoCard>
</el-col>
<el-col :span="24">
<InfoCard :items="items" header="其他" text="更多">
<template #item="temp">
<AvatarInfo />
</template>
</InfoCard>
</el-col>
<el-col :span="24">
<InfoCard :items="items" header="其他" text="更多">
<template #item="temp">
<AvatarInfo />
</template>
</InfoCard>
</el-col>
</el-row>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { h, ref, onMounted } from "vue";
import AvatarInfo from "@/components/AvatarInfo.vue";
import InfoCard from "@/components/InfoCard.vue";
import ArticleContentInfo from "@/components/ArticleContentInfo.vue";
import CommentInfo from "@/components/CommentInfo.vue";
import BottomInfo from '@/components/BottomInfo.vue'
import TreeArticleInfo from "@/components/TreeArticleInfo.vue";
import { useRoute, useRouter } from "vue-router";
import AgreeInfo from '@/components/AgreeInfo.vue'
import { get as discussGet, del as discussDel } from "@/apis/discussApi.js";
import { all as articleall, del as articleDel, get as articleGet } from "@/apis/articleApi.js";
//数据定义
const route = useRoute();
const router = useRouter();
const spacer = h(ElDivider, { direction: "vertical" });
const items = [{ user: "用户1" }, { user: "用户2" }, { user: "用户3" }];
//子文章数据
const articleData = ref([]);
//主题内容
const discuss = ref({});
//封面url
const getUrl= (str)=>{
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
}
//当前默认选择的子文章
const currentNodeKey=route.params.articleId;
//目录数据
const catalogueData = ref([]);
//子文章初始化
const loadArticleData = async () => {
const response= await articleall(route.params.discussId)
articleData.value = response.data;
}
//主题初始化
const loadDiscuss = async (isRewrite) => {
if (isRewrite) {
//跳转路由
router.push(`/article/${route.params.discussId}`);
}
discuss.value = (await discussGet(route.params.discussId)).data;
if (route.params.articleId != "") {
const response = await articleGet(route.params.articleId);
discuss.value.content = response.data.content;
}
ContentHander();
};
//加载文章及目录
const ContentHander = () => {
//加载目录
var reg = /(#{1,6})\s(.*)/g;
if(discuss.value.content)
{
var myArray = discuss.value.content.match(reg);
if (myArray != null) {
catalogueData.value = myArray.map((x) => {
return x.replace(/#/g, "").replace(/\s/g, "");
});
}
}
}
//添加树型子文章
const addArticle = (parentArticleId) => {
//跳转路由
var routerPer = {
path: "/editArt",
query: {
operType: "create",
artType: "article",
discussId: route.params.discussId,
parentArticleId: parentArticleId,
},
};
router.push(routerPer);
};
//删除主题
const delHander = async (ids) => {
ElMessageBox.confirm(`确定是否删除编号[${ids}]的主题吗?`, "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
}).then(async () => {
const response = await discussDel(ids);
//删除成功后,跳转到主页
router.push("/index");
ElMessage({
type: "success",
message: "删除成功",
});
});
};
//更新操作
const updateHander = (discussId) => {
//跳转路由
var routerPer = {
path: "/editArt",
query: {
operType: "update",
artType: "discuss",
discussId: discussId,
},
};
router.push(routerPer);
};
//跳转添加子菜单
const addNextArticle = (node, data) => {
//跳转路由
var routerPer = {
path: "/editArt",
query: {
operType: "create",
artType: "article",
discussId: data.discussId,
parentArticleId: data.id,
},
};
router.push(routerPer);
}
//跳转更新子菜单
const updateArticle = (node, data) => {
//跳转路由
var routerPer = {
path: "/editArt",
query: {
operType: "update",
artType: "article",
discussId: data.discussId,
parentArticleId: data.parentId,
articleId: data.id
},
};
router.push(routerPer);
}
//单机节点
const handleNodeClick = async(data) => {
//跳转路由
router.push(`/article/${route.params.discussId}/${data.id}`);
const response=await articleGet(data.id);
discuss.value.content = response.data.content;
ContentHander();
}
//删除子文章
const delArticle = (node, data) => {
ElMessageBox.confirm(`确定是否删除编号[${data.id}]的子文章吗?`, "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
}).then(async () => {
await articleDel(data.id);
await loadArticleData();
ElMessage({
type: "success",
message: "删除成功",
});
});
}
onMounted(async () => {
await loadDiscuss();
await loadArticleData();
});
</script>
<style scoped >
.comment {
min-height: 40rem;
}
.art-info-left .el-col {
margin-bottom: 1rem;
}
.art-info-ul span {
margin-left: 1rem;
}
.art-info-ul .el-tag {
margin-left: 1rem;
}
.art-info-ul {
padding: 0;
margin: 0;
}
li {
list-style: none;
margin-bottom: 0.5rem;
}
.art-info-right {
height: 100%;
}
.left-div .el-col {
background-color: #ffffff;
min-height: 12rem;
margin-bottom: 1rem;
}
.right-div .el-col {
background-color: #ffffff;
min-height: 10rem;
margin-bottom: 1rem;
}
.left-top-div .el-col {
min-height: 2rem;
background-color: #fafafa;
margin-bottom: 1rem;
margin-left: 10px;
}
.top-div {
padding-top: 1rem;
}
.left-top-div {
font-size: small;
text-align: center;
line-height: 2rem;
}
h2 {
margin-bottom: 0.5em;
color: rgba(0, 0, 0, 0.85);
font-weight: 600;
font-size: 30px;
line-height: 1.35;
}
.left-div .el-col {
padding: 1.4rem 1.4rem 0.5rem 1.4rem;
}
.el-space {
display: flex;
vertical-align: top;
justify-content: space-evenly;
}
.tab-divider {
margin-bottom: 0.5rem;
}
</style>

View File

@@ -0,0 +1,221 @@
<template>
<div style="width: 1200px;" class="body-div">
<div class="header">
<el-form :inline="true" >
<el-form-item label="标题:" >
<el-input v-model="query.title" placeholder="请输入标题"
></el-input>
</el-form-item>
<el-form-item label="标签:">
<el-input
placeholder="搜索当下分类下的标签"
/>
</el-form-item>
<div class="form-right">
<el-button>重置</el-button>
<el-button type="primary" @click="async()=>{ await loadDiscussList();}">查询</el-button>
<el-button @click="enterEditArticle" type="primary" v-hasPer="['bbs:discuss:add']">分享</el-button>
<el-dropdown>
<span class="el-dropdown-link">
展开
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>Action 1</el-dropdown-item>
<el-dropdown-item>Action 2</el-dropdown-item>
<el-dropdown-item>Action 3</el-dropdown-item>
<el-dropdown-item disabled>Action 4</el-dropdown-item>
<el-dropdown-item divided>Action 5</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-form>
</div>
<el-tabs v-model="activeName" @tab-change="handleClick">
<el-tab-pane label="最新" name="new"> </el-tab-pane>
<el-tab-pane label="推荐" name="suggest"> </el-tab-pane>
<el-tab-pane label="最热" name="host"> </el-tab-pane>
</el-tabs>
<el-collapse class="collapse-list" style="background-color: #F0F2F5;" >
<el-collapse-item >
<template #title>
<div class="collapse-top">
已置顶主题<el-icon class="header-icon">
<info-filled />
</el-icon>
</div>
</template>
<div class="div-item" v-for="i in topDiscussList" >
<DisscussCard :discuss="i" badge="置顶"/>
</div>
</el-collapse-item>
</el-collapse>
<el-divider v-show="topDiscussList.length>0" />
<div class="div-item" v-for="i in discussList" >
<DisscussCard :discuss="i"/>
</div>
<div>
<el-pagination
v-model:current-page="query.pageNum"
v-model:page-size="query.pageSize"
:page-sizes="[10, 20, 30, 50]"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="async(val)=>{ await loadDiscussList();}"
@current-change="async(val)=>{ await loadDiscussList();}"
/>
</div>
<el-empty v-if="discussList.length==0" description="空空如也" />
<BottomInfo/>
</div>
</template>
<script setup>
import DisscussCard from '@/components/DisscussCard.vue'
import {getList,getTopList} from '@/apis/discussApi.js'
import { onMounted, ref,reactive } from 'vue'
import { useRoute,useRouter } from 'vue-router'
import BottomInfo from '@/components/BottomInfo.vue'
//数据定义
const route=useRoute()
const router=useRouter()
const activeName = ref('new')
//主题内容
const discussList=ref([]);
//置顶主题内容
const topDiscussList = ref([]);
const total=ref(100)
const query=reactive({
pageNum:1,
pageSize:10,
title:'',
plateId:route.params.plateId,
type:activeName.value
})
const handleClick =async (tab, event) => {
query.type=activeName.value ;
await loadDiscussList();
}
onMounted(async()=>{
if(route.query.q !=undefined)
{
query.title=route.query.q??''
router.push("/discuss")
}
await loadDiscussList();
})
//加载discuss
const loadDiscussList=async()=>{
const response= await getList(query);
discussList.value=response.data.items;
total.value=Number( response.data.total);
//全查,无需参数
const topResponse=await getTopList();
topDiscussList.value=topResponse.data.items;
}
//进入添加主题页面
const enterEditArticle=()=>{
//跳转路由
var routerPer= { path: '/editArt', query: {
operType: 'create',
artType:'discuss',
plateId:route.params.plateId,
}}
router.push(routerPer);
}
</script>
<style scoped>
.el-pagination
{margin: 2rem 0rem 2rem 0rem;justify-content: right;}
.body-div{
min-height: 1000px;
}
.el-dropdown-link {
cursor: pointer;
color: var(--el-color-primary);
display: flex;
align-items: center;
}
.header{
background-color: #FFFFFF;
padding: 1rem;
margin: 1rem 0rem ;
}
.collapse-top
{
padding-left: 2rem;
}
.header .el-input
{
}
.el-tabs
{
background-color: #FFFFFF;
padding-left: 2rem;
}
.el-tabs >>> .el-tabs__header
{
margin-bottom: 0;
}
.div-item
{
margin-bottom: 1rem;
}
.el-form {
--el-form-label-font-size: var(--el-font-size-base);
display: flex;
align-items: center;
}
.el-form-item
{padding-top: 0.8rem;}
.form-right
{
align-items: center;
display: flex;
margin-left: auto;
}
.form-right .el-button
{
margin-right: 0.6rem;
}
.header .el-input
{
width:20rem;
}
.collapse-list >>> .el-collapse-item__header {
border-bottom-color: #F0F2F5 !important;
}
.el-divider
{
margin: 0.5rem 0;
}
</style>

View File

@@ -0,0 +1,294 @@
<template>
<div style="width: 100%">
<div class="body-div">
<el-form label-width="120px" :model="editForm" label-position="left" :rules="rules" ref="ruleFormRef">
<el-form-item label="类型:">
<el-radio-group v-model="radio">
<el-radio-button label="discuss">主题</el-radio-button>
<el-radio-button label="article">文章</el-radio-button>
<el-radio-button label="plate">板块</el-radio-button>
<el-radio-button label="orther">其他</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="权限:" v-if="route.query.artType == 'discuss'">
<el-radio-group v-model="perRadio">
<el-radio-button label="Public">公开</el-radio-button>
<el-radio-button label="Oneself">仅自己可见</el-radio-button>
<el-radio-button label="User">部分用户可见</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="可见用户:" v-if="route.query.artType == 'discuss' && perRadio=='User'">
<UserSelectInfo v-model="editForm.permissionUserIds"/>
</el-form-item>
<el-form-item v-if="route.query.artType == 'article'" label="子文章名称:" prop="name">
<el-input placeholder="请输入" v-model="editForm.name" />
</el-form-item>
<el-form-item v-else label="标题:" prop="title">
<el-input placeholder="请输入" v-model="editForm.title" />
</el-form-item>
<el-form-item label="描述:" prop="introduction">
<el-input placeholder="请输入" v-model="editForm.introduction" />
</el-form-item>
<el-form-item label="内容:" prop="content">
<MavonEdit height="30rem" v-model="editForm.content" :codeStyle="codeStyle" />
</el-form-item>
<el-form-item label="封面:" v-if="route.query.artType == 'discuss'">
<!-- 主题封面选择 -->
<el-upload
class="avatar-uploader"
:action="fileUploadUrl"
:show-file-list="false"
:on-success="onSuccess"
>
<el-image v-if="dialogImageUrl" :src="getUrl(dialogImageUrl)" style="width: 178px;height: 178px;" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
<el-form-item label="标签:" prop="types">
<el-input placeholder="请输入" v-model="editForm.types" />
</el-form-item>
<el-form-item>
<el-button @click="submit(ruleFormRef)" class="submit-btn" type="primary">提交</el-button></el-form-item>
</el-form>
</div>
</div>
</template>
<script setup>
import MavonEdit from "@/components/MavonEdit.vue";
import UserSelectInfo from '@/components/UserSelectInfo.vue'
import { ref, reactive, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
add as discussAdd,
update as discussUpdate,
get as discussGet,
} from "@/apis/discussApi.js";
import {
add as articleAdd,
update as articleUpdate,
get as articleGet,
} from "@/apis/articleApi.js";
//数据定义
const route = useRoute();
const router = useRouter();
const perRadio=ref("Public");
const radio = ref(route.query.artType);
const codeStyle = "atom-one-dark";
//封面完整显示的url
const fileUploadUrl=`${import.meta.env.VITE_APP_BASEAPI}/file`
//封面的url
const dialogImageUrl = ref('')
//文件上传成功后
const onSuccess=(response)=>{
dialogImageUrl.value=response[0].id
}
//封面url
const getUrl= (str)=>{
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
}
//整个页面上的表单
const editForm = reactive({
title: "",
types: "",
introduction: "",
content: "",
name: "",
permissionUserIds:[]
});
//组装主题内容: 需要更新主题信息
const discuss = {
};
//组装文章内容:需要添加的文章信息
const article = {
};
//定义效验规则
const ruleFormRef = ref(null);
const rules = reactive({
title: [
{ required: true, message: "请输入标题", trigger: "blur" },
{ min: 3, max: 40, message: "长度 3 到 20", trigger: "blur" },
],
name: [
{ required: true, message: "请输入子文章名称", trigger: "blur" },
],
content: [
{ required: true, message: "请输入内容", trigger: "blur" },
{ min: 10, message: "长度 大于 10", trigger: "blur" },
],
});
//提交按钮,需要区分操作类型
const submit = async (formEl) => {
if (!formEl) return;
await formEl.validate(async (valid, fields) => {
if (valid) {
//dicuss主题处理
if (route.query.artType == "discuss") {
discuss.title = editForm.title;
discuss.types = editForm.types;
discuss.introduction = editForm.introduction;
discuss.content = editForm.content;
discuss.plateId = discuss.plateId ?? route.query.plateId
discuss.cover=dialogImageUrl.value;
discuss.permissionType=perRadio.value;
discuss.permissionUserIds=editForm.permissionUserIds;
//主题创建
if (route.query.operType == "create") {
const response = await discussAdd(discuss);
ElMessage({
message: `[${discuss.title}]主题创建成功!`,
type: 'success',
})
var routerPer = { path: `/article/${response.data.id}` };
router.push(routerPer);
}
//主题更新
else if (route.query.operType == "update") {
await discussUpdate(route.query.discussId, discuss);
ElMessage({
message: `[${discuss.title}]主题更新成功!`,
type: 'success',
})
var routerPer = { path: `/article/${route.query.discussId}` };
router.push(routerPer);
}
}
//artcle文章处理
else if (route.query.artType == "article") {
//组装文章内容:需要添加的文章信息
article.content = editForm.content;
article.name = editForm.name;
article.discussId = route.query.discussId;
article.parentId = route.query.parentArticleId
//文章创建
if (route.query.operType == "create") {
const response = await articleAdd(article);
ElMessage({
message: `[${article.name}]文章创建成功!`,
type: 'success',
})
var routerPer = { path: `/article/${route.query.discussId}/${response.data.id}` };
router.push(routerPer);
}
//文章更新
else if (route.query.operType == "update") {
await articleUpdate(route.query.articleId, article);
ElMessage({
message: `[${article.name}]文章更新成功!`,
type: 'success',
})
var routerPer = { path: `/article/${route.query.discussId}/${route.query.articleId}` };
router.push(routerPer);
}
}
//添加成功后跳转到该页面
// var routerPer = { path: `/discuss/${discuss.plateId}` };
// router.push(routerPer);
// ruleFormRef.value.resetFields();
// discuss.plateId = route.query.plateId;
}
});
};
onMounted(async () => {
//如果是更新操作,需要先查询
if (route.query.operType == "update") {
//更新主题
if (route.query.artType == "discuss") {
await loadDiscuss();
//更新文章
} else if (route.query.artType == "article") {
await loadArticle();
}
}
});
//加载主题
const loadDiscuss = async () => {
const response = await discussGet(route.query.discussId);
const res = response.data
editForm.content = res.content;
editForm.title = res.title;
editForm.types = res.types;
editForm.introduction = res.introduction;
discuss.plateId = res.plateId;
dialogImageUrl.value= res.cover;
perRadio.value=res.permissionType;
editForm.permissionUserIds=res.permissionUserIds;
};
//加载文章
const loadArticle = async () => {
const response = await articleGet(route.query.articleId);
const res = response.data
editForm.content = res.content;
editForm.name = res.name;
editForm.discussId = res.discussId;
};
</script>
<style scoped>
.submit-btn {
width: 40%;
}
.body-div {
min-height: 1000px;
background-color: #fff;
margin: 1.5rem;
padding: 1.5rem;
}
.avatar-uploader >>>.el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader >>>.el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
.el-upload
{
}
</style>

View File

@@ -0,0 +1,175 @@
<template >
<div style="width: 1200px;">
<el-row :gutter="20" class="top-div">
<el-col :span="17">
<div class="scrollbar">
<ScrollbarInfo />
</div>
<el-row class="left-div">
<el-col :span="8" v-for="i in plateList" class="plate"
:style="{ 'padding-left': i % 3 == 1 ? 0 : 0.2 + 'rem', 'padding-right': i % 3 == 0 ? 0 : 0.2 + 'rem' }">
<PlateCard :name="i.name" :introduction="i.introduction" :id="i.id" />
</el-col>
<el-col :span="24" v-for="i in discussList">
<DisscussCard :discuss="i" />
</el-col>
<el-col :span="24">
<el-empty v-show="discussList.length <= 0" description="推荐位置,空空如也" />
</el-col>
</el-row>
</el-col>
<el-col :span="7">
<el-row class="right-div">
<el-col :span="24">
<el-carousel trigger="click" height="150px">
<el-carousel-item v-for="item in bannerList">
<div class="carousel-font" :style="{ color: item.color }">{{ item.name }}</div>
<el-image style="width: 100%; height: 100%" :src="item.logo" fit="cover" />
</el-carousel-item>
</el-carousel>
</el-col>
<el-col :span="24" >
<InfoCard header="访问统计" class="VisitsLineChart" text="详情">
<template #content>
<VisitsLineChart />
</template>
</InfoCard>
</el-col>
<el-col :span="24">
<InfoCard header="简介" text="详情">
<template #content>
<div class="introduce">
没有什么能够阻挡人类对代码<span style="color: #1890ff;">优雅</span>的追求
</div>
</template>
</InfoCard>
</el-col>
<el-col :span="24">
<InfoCard :items=items header="本月排行" text="更多">
<template #item="temp">
<AvatarInfo>
<template #bottom>
本月积分680
</template>
</AvatarInfo>
</template>
</InfoCard>
</el-col>
<el-col :span="24">
<InfoCard :items=items header="推荐好友" text="更多">
<template #item="temp">
<AvatarInfo />
</template>
</InfoCard>
</el-col>
<el-col :span="24" style=" background: transparent;">
<BottomInfo />
</el-col>
</el-row>
</el-col>
</el-row>
</div>
</template>
<script setup>
import DisscussCard from '@/components/DisscussCard.vue'
import InfoCard from '@/components/InfoCard.vue'
import PlateCard from '@/components/PlateCard.vue'
import ScrollbarInfo from '@/components/ScrollbarInfo.vue'
import AvatarInfo from '@/components/AvatarInfo.vue'
import BottomInfo from '@/components/BottomInfo.vue'
import VisitsLineChart from '@/components/echars/VisitsLineChart.vue'
import { getList } from '@/apis/plateApi.js'
import { getList as bannerGetList } from '@/apis/bannerApi.js'
import { getList as discussGetList } from '@/apis/discussApi.js'
import { onMounted, ref, reactive } from 'vue'
var plateList = ref([]);
var discussList = ref([]);
var bannerList = ref([]);
const items = [{ user: "用户1" }, { user: "用户2" }, { user: "用户3" }]
//主题查询参数
const query = reactive({
pageNum: 1,
pageSize: 10,
isTop: true
});
//初始化
onMounted(async () => {
plateList.value = (await getList()).data.items;
discussList.value = (await discussGetList(query)).data.items;
bannerList.value = (await bannerGetList()).data.items
});
</script>
<style scoped >
.introduce {
color: rgba(0, 0, 0, .45);
font-size: small;
}
.plate {
background: transparent !important;
}
.left-div .el-col {
background-color: #FFFFFF;
margin-bottom: 1rem;
}
.right-div .el-col {
background-color: #FFFFFF;
margin-bottom: 1rem;
}
.carousel-font {
position: absolute;
z-index: 1;
top: 10%;
left: 10%;
}
.top-div {
padding-top: 0.5rem;
}
.scrollbar {
display: block;
margin-bottom: 0.5rem;
}
.VisitsLineChart >>> .el-card__body
{
padding: 0.5rem;
}
</style>

View File

@@ -0,0 +1,101 @@
<template>
<div class="login-wrapper">
<h1>{{configStore.name}}-登录</h1>
<div class="login-form">
<el-form ref="loginFormRef" v-model="loginForm" :rules="rules" >
<div class="username form-item">
<el-form-item prop="userName">
<span>使用账号</span>
<input type="text" class="input-item" v-model="loginForm.userName">
</el-form-item>
</div>
<div class="password form-item">
<el-form-item prop="password">
<span>密码</span>
<input type="password" class="input-item" v-model="loginForm.password">
</el-form-item>
</div>
</el-form>
<RouterLink to="/register" > 没有账号前往注册</RouterLink>
<button class="login-btn" @click="login"> </button>
<button class="login-btn" @click="guestlogin">游客临时登录</button>
</div>
<div class="divider">
<span class="line"></span>
<span class="divider-text">其他方式登录</span>
<span class="line"></span>
</div>
<div class="other-login-wrapper">
<div class="other-login-item">
<img src="@/assets/login_images/QQ.png" alt="">
</div>
<div class="other-login-item">
<img src="@/assets/login_images/WeChat.png" alt="">
</div>
</div>
</div>
<!-- <h2> 登录-欢迎</h2>
<el-input v-model="loginForm.userName" placeholder="用户名" />
<el-input v-model="loginForm.password" placeholder="密码" show-password />
<el-button class="login-btn" type="primary" @click="login">登录</el-button>
<br>
<el-button class="login-btn" type="primary" @click="guestlogin">游客临时登录</el-button> -->
</template>
<script setup>
import { reactive } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import useUserStore from '@/stores/user.js'
import useConfigStore from "@/stores/config";
const configStore= useConfigStore();
const userStore = useUserStore();
const router = useRouter();
const route = useRoute();
const rules = reactive({
userName: [
{ required: true, message: '请输入账号名', trigger: 'blur' },
{ min: 3, message: '至少大于等于3位', trigger: 'blur' },
],
password:[
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 3, message: '至少大于等于6位', trigger: 'blur' },
]})
const loginForm = reactive({
userName: "",
password: "",
uuid: "",
code: ""
})
const guestlogin = async () => {
loginForm.userName = "guest";
loginForm.password = "123456"
await userStore.login(loginForm);
const redirect = route.query?.redirect ?? '/index'
router.push(redirect)
}
const login = async () => {
const response = await userStore.login(loginForm).catch((e) => {
loginForm.password = "";
});
if (response!=undefined) {
ElMessage({
message: `您好${loginForm.userName},登录成功!`,
type: 'success',
})
const redirect = route.query?.redirect ?? '/index'
router.push(redirect)
}
}
</script>
<style src="@/assets/styles/login.scss" scoped></style>

View File

@@ -0,0 +1,4 @@
<template>
404
</template>

View File

@@ -0,0 +1,108 @@
<template>
<div class="login-wrapper">
<h1>{{configStore.name}}-注册</h1>
<div class="login-form">
<div class="username form-item">
<span>登录账号</span>
<input type="text" class="input-item" v-model="registerForm.userName">
</div>
<div class="username form-item">
<span>手机号</span>
<input style="width: 70%;" type="text" class="input-item" v-model="registerForm.phone">
<button v-if="!isSendCaptcha" style="width: 30%;background-color: #C14949;" class="login-btn" @click="captcha" >验证码</button>
<button v-else style="width: 30%;background-color:#F0F2F5;" class="login-btn" >已发送</button>
</div>
<div class="username form-item" v-show="isSendCaptcha">
<span>手机短信验证码</span>
<input type="text" class="input-item" v-model="registerForm.code">
</div>
<div class="password form-item">
<span>密码</span>
<input type="password" class="input-item" v-model="registerForm.password">
</div>
<div class="password form-item">
<span>确认密码</span>
<input type="password" class="input-item" v-model="passwordConfirm">
</div>
<RouterLink to="/login" > 已有账号前往登录</RouterLink>
<button class="login-btn" @click="register">注册</button>
</div>
<div class="divider">
<span class="line"></span>
<span class="divider-text">其他方式注册</span>
<span class="line"></span>
</div>
<div class="other-login-wrapper">
<div class="other-login-item">
<img src="@/assets/login_images/QQ.png" alt="">
</div>
<div class="other-login-item">
<img src="@/assets/login_images/WeChat.png" alt="">
</div>
</div>
</div>
</template>
<script setup>
import { reactive ,ref} from 'vue';
import { useRouter, useRoute } from 'vue-router';
import {getCodePhone} from '@/apis/accountApi'
import useUserStore from '@/stores/user.js'
import useConfigStore from "@/stores/config";
const configStore= useConfigStore();
const userStore = useUserStore();
const router = useRouter();
const route = useRoute();
const passwordConfirm=ref('');
const registerForm = reactive({
userName: "",
password: "",
uuid: "",
code: "",
phone:""
})
const isSendCaptcha=ref(false)
//验证码
const captcha=async()=>{
isSendCaptcha.value=true;
const response= await getCodePhone(registerForm.phone);
ElMessage({
message: `已向${registerForm.phone}发送验证码,请注意查收`,
type: 'success',
})
}
const register = async () => {
if(registerForm.password!=passwordConfirm.value)
{
ElMessage.error('两次密码输入不一致')
return;
}
const response = await userStore.register(registerForm).catch((e) => {
registerForm.password="";
passwordConfirm.value="";
});
//成功
if (response!=undefined) {
ElMessage({
message: `恭喜!${registerForm.userName},注册成功!请登录!`,
type: 'success',
})
const redirect = route.query?.redirect ?? '/login'
router.push(redirect)
}
}
</script>
<style src="@/assets/styles/login.scss" scoped></style>

View File

@@ -0,0 +1 @@
<template></template>

View File

@@ -0,0 +1,85 @@
<template>
<div class="errPage-container">
<el-button icon="arrow-left" class="pan-back-btn" @click="back">
返回
</el-button>
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">
401错误!
</h1>
<h2>您没有访问权限</h2>
<h6>对不起您没有访问权限请不要进行非法操作您可以返回主页面</h6>
<ul class="list-unstyled">
<li class="link-type">
<router-link to="/">
回首页
</router-link>
</li>
</ul>
</el-col>
<el-col :span="12">
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
</div>
</template>
<script setup>
import errImage from "@/assets/401_images/401.gif";
import { useRouter } from "vue-router";
let { proxy } = getCurrentInstance();
const router=useRouter();
const errGif = ref(errImage + "?" + +new Date());
function back() {
if (proxy.$route.query.noGoBack) {
router.push({ path: "/" });
} else {
router.go(-1);
}
}
</script>
<style lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
border: none !important;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>

View File

@@ -0,0 +1,228 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">
404错误!
</div>
<div class="bullshit__headline">
{{ message }}
</div>
<div class="bullshit__info">
对不起您正在寻找的页面不存在尝试检查URL的错误然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容
</div>
<router-link to="/index" class="bullshit__return-home">
返回首页
</router-link>
</div>
</div>
</div>
</template>
<script setup>
import {computed} from 'vue'
let message = computed(() => {
return '找不到网页!'
})
</script>
<style lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

View File

@@ -0,0 +1,124 @@
<template>
<div class="app-container" style="width: 1400px;">
<el-row :gutter="20">
<el-col :span="6" :xs="24">
<el-card class="box-card">
<template v-slot:header>
<div class="clearfix">
<span>个人信息</span>
</div>
</template>
<div>
<div class="text-center">
<userAvatar :user="state.user" />
</div>
<ul class="list-group list-group-striped">
<li class="list-group-item">
<el-icon><User /></el-icon> 用户名称
<div class="pull-right">{{ state.user.userName }}</div>
</li>
<li class="list-group-item">
<el-icon><Phone /></el-icon> 手机号码
<div class="pull-right">{{ state.user.phone }}</div>
</li>
<li class="list-group-item">
<el-icon><Message /></el-icon> 用户邮箱
<div class="pull-right">{{ state.user.email }}</div>
</li>
<li class="list-group-item">
<el-icon><HelpFilled /></el-icon> 所属部门
<div class="pull-right" v-if="state.dept">{{ state.dept.deptName }}</div>
</li>
<li class="list-group-item">
<el-icon><Avatar /></el-icon> 所属角色
<div class="pull-right"><span v-for="role in state.roles" :key="role.id">{{ role.roleName }} /</span></div>
</li>
<li class="list-group-item">
<el-icon><Stopwatch /></el-icon> 创建日期
<div class="pull-right">{{ state.user.creationTime }}</div>
</li>
</ul>
</div>
</el-card>
</el-col>
<el-col :span="18" :xs="24">
<el-card>
<template v-slot:header>
<div class="clearfix">
<span>基本资料</span>
</div>
</template>
<el-tabs v-model="activeTab">
<el-tab-pane label="基本资料" name="userinfo">
<userInfo :user="state.user" />
</el-tab-pane>
<el-tab-pane label="修改密码" name="resetPwd">
<resetPwd />
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup name="Profile">
import userAvatar from "./UserAvatar.vue";
import userInfo from "./UserInfo.vue";
import resetPwd from "./ResetPwd.vue";
import { getUserProfile } from "@/apis/userApi.js";
import { onMounted ,ref,reactive} from "vue";
const activeTab = ref("userinfo");
const state = reactive({
user: {},
dept: {},
roles: [],
roleGroup: {},
postGroup: {}
});
function getUser() {
getUserProfile().then(response => {
const res=response.data;
state.user = res.user;
state.dept=res.dept;
state.roles=res.roles;
state.roleGroup = res.roleGroup;
state.postGroup = res.postGroup;
});
};
onMounted(()=>{
getUser();
})
</script>
<style scoped>
.pull-right {
float: right !important;
}
.list-group-striped > .list-group-item {
border-left: 0;
border-right: 0;
border-radius: 0;
padding-left: 0;
padding-right: 0;
}
.list-group {
padding-left: 0px;
list-style: none;
}
.list-group-item {
border-bottom: 1px solid #e7eaec;
border-top: 1px solid #e7eaec;
margin-bottom: -1px;
padding: 11px 0px;
font-size: 13px;
}
.app-container {
padding: 20px;
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
<el-form-item label="旧密码" prop="oldPassword">
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password />
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password />
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">保存</el-button>
<el-button type="danger" @click="close">关闭</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { updateUserPwd } from "@/apis/userApi";
import { ref,reactive } from "vue";
const pwdRef=ref(null);
const user = reactive({
oldPassword: undefined,
newPassword: undefined,
confirmPassword: undefined
});
const equalToPassword = (rule, value, callback) => {
if (user.newPassword !== value) {
callback(new Error("两次输入的密码不一致"));
} else {
callback();
}
};
const rules = ref({
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }],
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }]
});
/** 提交按钮 */
function submit() {
pwdRef.value.validate(async valid => {
if (valid) {
const response=await updateUserPwd(user.oldPassword, user.newPassword)
if(response.status==200)
{
ElMessage({
type: "success",
message: "更新密码成功",
});
}
}
});
};
/** 关闭按钮 */
function close() {
// proxy.$tab.closePage();
};
</script>

View File

@@ -0,0 +1,187 @@
<template>
<div class="user-info-head" @click="editCropper()"><img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
</div>
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
<el-row>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<vue-cropper ref="cropper" :img="options.img" :info="true" :autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth" :autoCropHeight="options.autoCropHeight" :fixedBox="options.fixedBox"
@realTime="realTime" v-if="visible" />
</el-col>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<div class="avatar-upload-preview">
<img :src="options.previews.url" :style="options.previews.img" />
</div>
</el-col>
</el-row>
<br />
<el-row>
<el-col :lg="2" :md="2">
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
<el-button>
选择
<el-icon class="el-icon--right">
<Upload />
</el-icon>
</el-button>
</el-upload>
</el-col>
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
<el-button icon="Plus" @click="changeScale(1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
</el-col>
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
<el-button type="primary" @click="uploadImg()">上传</el-button>
</el-col>
</el-row>
</el-dialog>
</template>
<script setup>
import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper";
import { upload } from "@/apis/fileApi";
import { updateUserIcon } from "@/apis/userApi";
import useUserStore from '@/stores/user'
import { ref, reactive } from "vue";
const userStore = useUserStore()
const cropper = ref(null);
const open = ref(false);
const visible = ref(false);
const title = ref("修改头像");
//图片裁剪数据
const options = reactive({
img: userStore.icon, // 裁剪图片的地址
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 200, // 默认生成截图框宽度
autoCropHeight: 200, // 默认生成截图框高度
fixedBox: true, // 固定截图框大小 不允许改变
previews: {} //预览数据
});
/** 编辑头像 */
function editCropper() {
open.value = true;
};
/** 打开弹出层结束时的回调 */
function modalOpened() {
visible.value = true;
};
/** 覆盖默认上传行为 */
function requestUpload() {
};
/** 向左旋转 */
function rotateLeft() {
cropper.value.rotateLeft();
};
/** 向右旋转 */
function rotateRight() {
cropper.value.rotateRight();
};
/** 图片缩放 */
function changeScale(num) {
num = num || 1;
cropper.value.changeScale(num);
};
/** 上传预处理 */
function beforeUpload(file) {
if (file.type.indexOf("image/") == -1) {
ElMessage.error("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。")
} else {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
options.img = reader.result;
};
}
};
/** 上传图片 */
async function uploadImg() {
await cropper.value.getCropBlob(async data => {
let formData = new FormData();
formData.append("file", data);
const response = await upload(formData)
open.value = false;
options.img = import.meta.env.VITE_APP_BASEAPI + "/file/" + response.data[0].id;
userStore.icon = options.img;
const iconResponse= await updateUserIcon(response.data[0].id);
if(iconResponse.status==200)
{
ElMessage({
type: "success",
message: "头像更新成功",
});
}
});
};
/** 实时预览 */
function realTime(data) {
options.previews = data;
};
/** 关闭窗口 */
function closeDialog() {
options.img = userStore.icon;
options.visible = false;
};
</script>
<style lang='scss' scoped>
.user-info-head {
position: relative;
display: inline-block;
height: 120px;
}
.user-info-head:hover:after {
content: "+";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
color: #eee;
background: rgba(0, 0, 0, 0.5);
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
cursor: pointer;
line-height: 110px;
border-radius: 50%;
}
.img-circle {
border-radius: 50%;
}
.img-lg {
width: 120px;
height: 120px;
}
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 200px;
height: 200px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<el-form ref="userRef" :model="user" :rules="rules" label-width="80px">
<el-form-item label="账号" prop="userName">
<el-input v-model="user.userName" disabled />
</el-form-item>
<el-form-item label="用户昵称" prop="nick">
<el-input v-model="user.nick" maxlength="30" />
</el-form-item>
<el-form-item label="手机号码" prop="phone">
<el-input v-model="user.phone" maxlength="11" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="user.email" maxlength="50" />
</el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="user.sex">
<el-radio :label="'Male'"></el-radio>
<el-radio :label="'Woman'"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">保存</el-button>
<el-button type="danger" @click="close">关闭</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { updateUserProfile } from "@/apis/userApi";
import useUserStore from "@/stores/user"
import { ref } from "vue";
const userStore = useUserStore();
const props = defineProps({
user: {
type: Object
}
});
const userRef = ref(null);
const rules = ref({
nick: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
phone: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
});
/** 提交按钮 */
function submit() {
userRef.value.validate(async valid => {
if (valid) {
const response = await updateUserProfile(props.user);
if (response.status == 200) {
ElMessage({
type: "success",
message: "用户信息修改成功",
});
}
}
});
};
/** 关闭按钮 */
function close() {
// proxy.$tab.closePage();
};
</script>

View File

@@ -0,0 +1,45 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
var CopyWebpackPlugin = require('copy-webpack-plugin');
/** @type {import('vite').UserConfig} */
export default defineConfig(({ command, mode })=>{
const env = loadEnv(mode, process.cwd(), '')
return {
// envDir: 'env',
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server:{
port:18001,
open:true,
proxy:{
[env.VITE_APP_BASEAPI]: {
target: env.VITE_APP_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api-dev/, ''),
},
}
}
}
}
)

View File

@@ -1,25 +0,0 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

View File

@@ -1,4 +0,0 @@
[*.cs]
# SYSLIB0014: 类型或成员已过时
dotnet_diagnostic.SYSLIB0014.severity = none

View File

@@ -0,0 +1,3 @@
<Project>
</Project>

View File

@@ -0,0 +1,90 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyCompany("YiFramework")]
[assembly: AssemblyFileVersion(VersionInfo.FullVersion)]
[assembly: AssemblyInformationalVersion(VersionInfo.FullVersion)]
[assembly: AssemblyVersion(VersionInfo.FullVersion)]
[assembly: ComVisible(false)]
#pragma warning disable IDE1006 // 命名样式
internal static class VersionInfo
{
/// <summary>
/// 主版本: 项目进行了重大修改导致不再向下兼容主版本号加1其它版本号清0
/// </summary>
public const string Major = "1";
/// <summary>
/// 次版本项目进行了重构增加了功能但向下兼容子版本号加1主版本号不变 修正版本号清0
/// </summary>
public const string Minor = "1";
/// <summary>
/// 生成号
/// </summary>
public const string Build = "0";
/// <summary>
/// 修订号项目进行了Bug修复向下兼容修正版本号加1主版本号、子版本号不 变;
/// </summary>
public const string Revision = "0";
/// <summary>
/// 版本名称
/// </summary>
public const string VersionName = null;
/// <summary>
/// 版本
/// </summary>
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
#pragma warning disable CS1570 // XML 注释出现 XML 格式错误
/// <summary>
/// 比较 a,b两个版本
/// 当 a > b 时,返回 1
/// 当 a = b 时,返回 0
/// 当 a < b 时,返回 -1
/// </summary>
#pragma warning restore CS1570 // XML 注释出现 XML 格式错误
public static int Compare(this Version a, Version b)
{
// 比较主版本
if (a.Major != b.Major)
{
return a.Major > b.Major ? 1 : -1;
}
// 比较次版本
if (a.Minor != b.Minor)
{
return a.Minor > b.Minor ? 1 : -1;
}
// 比较生成号
if (a.Build < 0 || b.Build < 0)
{
return 0;
}
if (a.Build != b.Build)
{
return a.Build > b.Build ? 1 : -1;
}
// 比较修订号
if (a.Revision < 0 || b.Revision < 0)
{
return 0;
}
if (a.Revision != b.Revision)
{
return a.Revision > b.Revision ? 1 : -1;
}
return 0;
}
#pragma warning restore IDE1006 // 命名样式
}

View File

@@ -1,65 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<log4net>
<!-- 将日志以回滚文件的形式写到文件中 -->
<!-- 按日期切分日志文件,并将日期作为日志文件的名字 -->
<!--Error-->
<appender name="ErrorLog" type="log4net.Appender.RollingFileAppender">
<!--不加utf-8编码格式中文字符将显示成乱码-->
<param name="Encoding" value="utf-8" />
<file value="log/"/>
<appendToFile value="true" />
<rollingStyle value="Date" />
<!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置-->
<datePattern value="&quot;GlobalExceptionLogs_&quot;yyyyMMdd&quot;.log&quot;" />
<!--日志文件名是否为静态-->
<StaticLogFileName value="false"/>
<!--多线程时采用最小锁定-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<!--布局(向用户显示最后经过格式化的输出信息)-->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date| %-5level %newline%message%newline--------------------------------%newline" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="ERROR" />
<levelMax value="FATAL" />
</filter>
</appender>
<!--Error-->
<!--Info-->
<appender name="InfoLog" type="log4net.Appender.RollingFileAppender">
<!--不加utf-8编码格式中文字符将显示成乱码-->
<param name="Encoding" value="utf-8" />
<!--定义文件存放位置-->
<file value="log/"/>
<appendToFile value="true" />
<rollingStyle value="Date" />
<!--日志文件名是否为静态-->
<StaticLogFileName value="false"/>
<!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置-->
<datePattern value="&quot;GlobalInfoLogs_&quot;yyyyMMdd&quot;.log&quot;" />
<!--多线程时采用最小锁定-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<!--布局(向用户显示最后经过格式化的输出信息)-->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date| %-5level%c %newline%message%newline--------------------------------%newline" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG" />
<levelMax value="WARN" />
</filter>
</appender>
<!--Info-->
<root>
<!-- 控制级别由低到高ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF -->
<!-- 比如定义级别为INFO则INFO级别向下的级别比如DEBUG日志将不会被记录 -->
<!-- 如果没有定义LEVEL的值则缺省为DEBUG -->
<level value="ALL" />
<!-- 按日期切分日志文件,并将日期作为日志文件的名字 -->
<appender-ref ref="ErrorLog" />
<appender-ref ref="InfoLog" />
</root>
</log4net>

View File

@@ -1,185 +0,0 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Yi.Framework.ApiMicroservice</name>
</assembly>
<members>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.AccountController">
<summary>
账户管理
</summary>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1">
<summary>
Json To Sql 类比模式,通用模型
</summary>
<typeparam name="T"></typeparam>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.GetById(System.Int64)">
<summary>
主键查询
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.GetList(Yi.Framework.Model.Query.QueryCondition)">
<summary>
列表查询
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.PageList(Yi.Framework.Model.Query.QueryPageCondition)">
<summary>
条件分页查询
</summary>
<param name="queryCondition"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.Add(`0)">
<summary>
添加
</summary>
<param name="entity"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.Update(`0)">
<summary>
修改
</summary>
<param name="entity"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.DeleteList(System.Collections.Generic.List{System.Int64})">
<summary>
列表删除
</summary>
<param name="ids"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.LogController.Add">
<summary>
自动分表,日志添加
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.LogController.GetList">
<summary>
查询近20年与21年的日志表
</summary>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.MenuController">
<summary>
菜单管理
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.MenuController.GetMenuTree">
<summary>
得到树形菜单
</summary>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.RoleController">
<summary>
角色管理
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.RoleController.GiveRoleSetMenu(Yi.Framework.DTOModel.GiveRoleSetMenuDto)">
<summary>
给多用户设置多角色
</summary>
<param name="giveRoleSetMenuDto"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.RoleController.GetInMenuByRoleId(System.Int64)">
<summary>
通过角色id来获取菜单列表
</summary>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.TestController">
<summary>
测试控制器
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.DbTest">
<summary>
仓储上下文对象测试
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.SqlTest">
<summary>
执行Sql返回
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.LocalTest">
<summary>
国际化测试
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.PermissionTest">
<summary>
权限测试
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.AuthTest">
<summary>
策略授权测试
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.TranTest">
<summary>
异步事务测试
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.IncludeTest">
<summary>
极爽导航属性
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.JobTest">
<summary>
启动一个定时任务
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.stopJob">
<summary>
停止任务
</summary>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.UserController">
<summary>
用户管理
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.Add(Yi.Framework.Model.Models.UserEntity)">
<summary>
添加用户,去重,密码加密
</summary>
<param name="entity"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.GiveUserSetRole(Yi.Framework.DTOModel.GiveUserSetRoleDto)">
<summary>
给多用户设置多角色
</summary>
<param name="giveUserSetRoleDto"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.GetRoleListByUserId(System.Int64)">
<summary>
通过用户id得到角色列表
</summary>
<returns></returns>
</member>
</members>
</doc>

View File

@@ -1,8 +0,0 @@
{
"apollo": {
"AppId": "ApiMicroservice",
"Env": "DEV",
"MetaServer": "http://[xxxx]:18080",
"ConfigServer": [ "http://[xxxx]:18080" ]
}
}

View File

@@ -1,59 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yi.Framework.Common.Models;
using Yi.Framework.Core;
using Yi.Framework.DTOModel;
using Yi.Framework.Interface;
using Yi.Framework.Model.Models;
using Yi.Framework.Repository;
using Yi.Framework.WebCore;
using Yi.Framework.WebCore.AttributeExtend;
using Yi.Framework.WebCore.AuthorizationPolicy;
namespace Yi.Framework.ApiMicroservice.Controllers
{
/// <summary>
/// 账户管理
/// </summary>
[ApiController]
[Route("api/[controller]/[action]")]
public class AccountController :ControllerBase
{
private IUserService _iUserService;
private JwtInvoker _jwtInvoker;
public AccountController(ILogger<UserEntity> logger, IUserService iUserService, JwtInvoker jwtInvoker)
{
_iUserService = iUserService;
_jwtInvoker = jwtInvoker;
}
[AllowAnonymous]
[HttpPost]
public async Task<Result> Login(LoginDto loginDto)
{
UserEntity user=new();
if (await _iUserService.Login(loginDto.UserName, loginDto.Password,o=> user=o))
{
return Result.Success("登录成功!").SetData(new { user, token = _jwtInvoker.GetAccessToken(user)});
}
return Result.SuccessError("登录失败!用户名或者密码错误!");
}
[AllowAnonymous]
[HttpPost]
public async Task<Result> Register(RegisterDto registerDto)
{
UserEntity user = new();
if (await _iUserService.Register(WebCore.Mapper.MapperHelper.Map<UserEntity, RegisterDto>(registerDto), o => user = o))
{
return Result.Success("注册成功!").SetData(user);
}
return Result.SuccessError("注册失败!用户名已存在!");
}
}
}

View File

@@ -1,101 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Yi.Framework.Common.Models;
using Yi.Framework.Interface;
using Yi.Framework.Language;
using Yi.Framework.Model.Models;
using Yi.Framework.Model.Query;
using Yi.Framework.Repository;
using Yi.Framework.WebCore.AttributeExtend;
namespace Yi.Framework.ApiMicroservice.Controllers
{
/// <summary>
/// Json To Sql 类比模式,通用模型
/// </summary>
/// <typeparam name="T"></typeparam>
[ApiController]
public class BaseCrudController<T> : ControllerBase where T : class, IBaseModelEntity,new()
{
private readonly ILogger<T> _logger;
private IBaseService<T> _baseService;
private IRepository<T> _repository;
public BaseCrudController(ILogger<T> logger, IBaseService<T> iBaseService)
{
_logger = logger;
_baseService = iBaseService;
_repository = iBaseService._repository;
}
/// <summary>
/// 主键查询
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[Permission($"{nameof(T)}:get:one")]
[HttpGet]
public virtual async Task<Result> GetById(long id)
{
return Result.Success().SetData(await _repository.GetByIdAsync(id));
}
/// <summary>
/// 列表查询
/// </summary>
/// <returns></returns>
[Permission($"{nameof(T)}:get:list")]
[HttpPost]
public virtual async Task<Result> GetList(QueryCondition queryCondition)
{
return Result.Success().SetData(await _repository.GetListAsync(queryCondition));
}
/// <summary>
/// 条件分页查询
/// </summary>
/// <param name="queryCondition"></param>
/// <returns></returns>
[Permission($"{nameof(T)}:get:page")]
[HttpPost]
public virtual async Task<Result> PageList(QueryPageCondition queryCondition)
{
return Result.Success().SetData(await _repository.CommonPageAsync(queryCondition));
}
/// <summary>
/// 添加
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[Permission($"{nameof(T)}:add")]
[HttpPost]
public virtual async Task<Result> Add(T entity)
{
return Result.Success().SetData(await _repository.InsertReturnSnowflakeIdAsync(entity));
}
/// <summary>
/// 修改
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[Permission($"{nameof(T)}:update")]
[HttpPut]
public virtual async Task<Result> Update(T entity)
{
return Result.Success().SetStatus(await _repository.UpdateIgnoreNullAsync(entity));
}
/// <summary>
/// 列表删除
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[Permission($"{nameof(T)}:delete:list")]
[HttpDelete]
public virtual async Task<Result> DeleteList(List<long> ids)
{
return Result.Success().SetStatus(await _repository.DeleteByLogicAsync(ids));
}
}
}

View File

@@ -1,55 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yi.Framework.Common.Models;
using Yi.Framework.Interface;
using Yi.Framework.Model.Models;
using Yi.Framework.Repository;
using Yi.Framework.WebCore;
using Yi.Framework.WebCore.AttributeExtend;
using Yi.Framework.WebCore.AuthorizationPolicy;
namespace Yi.Framework.ApiMicroservice.Controllers
{
[ApiController]
[Route("api/[controller]/[action]")]
public class LogController : ControllerBase
{
private ILogService _iLogService;
//大量日志将采用自动分表形式默认1年分一次表
public LogController(ILogger<LogEntity> logger, ILogService iLogService)
{
_iLogService = iLogService;
}
/// <summary>
/// 自动分表,日志添加
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<Result> Add()
{
Random random = new Random();
var logList = new List<LogEntity>() {
new LogEntity() { LogCreateTime = Convert.ToDateTime("2019-12-1"), Message = "jack"+random.Next() } ,
new LogEntity() { LogCreateTime = Convert.ToDateTime("2022-02-1"), Message = "jack"+random.Next() },
new LogEntity() { LogCreateTime = Convert.ToDateTime("2020-02-1"), Message = "jack"+random.Next() },
new LogEntity() { LogCreateTime = Convert.ToDateTime("2021-12-1"), Message = "jack"+random.Next() } };
return Result.Success().SetData(await _iLogService.AddListTest(logList));
}
/// <summary>
/// 查询近20年与21年的日志表
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<Result> GetList()
{
return Result.Success().SetData(await _iLogService.GetListTest());
}
}
}

View File

@@ -1,44 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yi.Framework.Common.Models;
using Yi.Framework.Interface;
using Yi.Framework.Model.Models;
using Yi.Framework.Repository;
using Yi.Framework.WebCore;
using Yi.Framework.WebCore.AttributeExtend;
using Yi.Framework.WebCore.AuthorizationPolicy;
namespace Yi.Framework.ApiMicroservice.Controllers
{
/// <summary>
/// 菜单管理
/// </summary>
[ApiController]
[Route("api/[controller]/[action]")]
public class MenuController : BaseCrudController<MenuEntity>
{
private IMenuService _iMenuService;
public MenuController(ILogger<MenuEntity> logger, IMenuService iMenuService) : base(logger, iMenuService)
{
_iMenuService = iMenuService;
}
/// <summary>
/// 得到树形菜单
/// </summary>
/// <returns></returns>
[HttpGet]
//暂未制作逻辑删除与多租户的过滤
public async Task<Result> GetMenuTree()
{
return Result.Success().SetData(await _iMenuService. GetMenuTreeAsync());
}
}
}

View File

@@ -1,53 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yi.Framework.Common.Models;
using Yi.Framework.DTOModel;
using Yi.Framework.Interface;
using Yi.Framework.Model.Models;
using Yi.Framework.Repository;
using Yi.Framework.WebCore;
using Yi.Framework.WebCore.AttributeExtend;
using Yi.Framework.WebCore.AuthorizationPolicy;
namespace Yi.Framework.ApiMicroservice.Controllers
{
/// <summary>
/// 角色管理
/// </summary>
[ApiController]
[Route("api/[controller]/[action]")]
public class RoleController : BaseCrudController<RoleEntity>
{
private IRoleService _iRoleService;
public RoleController(ILogger<RoleEntity> logger, IRoleService iRoleService) : base(logger, iRoleService)
{
_iRoleService = iRoleService;
}
/// <summary>
/// 给多用户设置多角色
/// </summary>
/// <param name="giveRoleSetMenuDto"></param>
/// <returns></returns>
[HttpPut]
public async Task<Result> GiveRoleSetMenu(GiveRoleSetMenuDto giveRoleSetMenuDto)
{
return Result.Success().SetStatus(await _iRoleService.GiveRoleSetMenu(giveRoleSetMenuDto.RoleIds, giveRoleSetMenuDto.MenuIds));
}
/// <summary>
/// 通过角色id来获取菜单列表
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<Result> GetInMenuByRoleId(long RoleId)
{
return Result.Success().SetData(await _iRoleService.GetInMenuByRoleId(RoleId));
}
}
}

View File

@@ -1,183 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yi.Framework.Common.Const;
using Yi.Framework.Common.Models;
using Yi.Framework.Core;
using Yi.Framework.Interface;
using Yi.Framework.Language;
using Yi.Framework.Model.Models;
using Yi.Framework.Repository;
using Yi.Framework.WebCore;
using Yi.Framework.WebCore.AttributeExtend;
using Yi.Framework.WebCore.AuthorizationPolicy;
namespace Yi.Framework.ApiMicroservice.Controllers
{
/// <summary>
/// 测试控制器
/// </summary>
[ApiController]
[Route("api/[controller]/[action]")]
public class TestController : ControllerBase
{
private IStringLocalizer<LocalLanguage> _local;
private IUserService _iUserService;
private IRoleService _iRoleService;
private QuartzInvoker _quartzInvoker;
//你可以依赖注入服务层各各接口,也可以注入其他仓储层,怎么爽怎么来!
public TestController(ILogger<UserEntity> logger, IRoleService iRoleService, IUserService iUserService, IStringLocalizer<LocalLanguage> local, QuartzInvoker quartzInvoker)
{
_local = local;
_iUserService = iUserService;
_iRoleService = iRoleService;
_quartzInvoker = quartzInvoker;
}
/// <summary>
/// 仓储上下文对象测试
/// </summary>
/// <returns></returns>
[HttpGet]
// 特点:化繁为简!意框架仓储代理上下文对象,用起来就是爽,但最好按规范来爽!
// 规范控制器不建议使用切换仓储方法、控制器严禁使用DB上下文对象其它怎么爽怎么来
public async Task<Result> DbTest()
{
//非常好使用UserService的特有方法
await _iUserService.DbTest();
//非常好依赖注入使用其他Service的特有方法
await _iRoleService.DbTest();
//很核理,使用仓储的通用方法
await _iUserService._repository.GetListAsync();
//挺不错,依赖注入其他仓储
await _iRoleService._repository.GetListAsync();
//不建议操作,直接切换其他仓储
await _iUserService._repository.ChangeRepository<Repository<RoleEntity>>().GetListAsync();
//最好不要直接操作Db对象
await _iUserService._repository._Db.Queryable<UserEntity>().ToListAsync();
return Result.Success().SetData(await _iUserService.DbTest());
}
/// <summary>
/// 执行Sql返回
/// </summary>
/// <returns></returns>
[HttpGet]
//简单语句不推荐!
public async Task<Result> SqlTest()
{
return Result.Success().SetData(await _iUserService._repository.UseSqlAsync<UserEntity>("select * from User"));
}
/// <summary>
/// 国际化测试
/// </summary>
/// <returns></returns>
[HttpGet]
//根据浏览器语言设置来切换输出
public Result LocalTest()
{
return Result.Success().SetData(_local["succeed"]);
}
/// <summary>
/// 权限测试
/// </summary>
/// <returns></returns>
[HttpGet]
[Permission("user:get:test")]
public Result PermissionTest()
{
return Result.Success();
}
/// <summary>
/// 策略授权测试
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(PolicyName.Sid)]
public Result AuthTest()
{
return Result.Success();
}
/// <summary>
/// 异步事务测试
/// </summary>
/// <returns></returns>
[HttpGet]
//注册一个用户获取它的信息之后再更新它,但是这个年龄可能会报错
//如果一个事务中有任何一个错误,将会把所有执行过的操作进行回滚,确保数据的原子性
public async Task<Result> TranTest()
{
UserEntity user = new() { UserName = $"杰哥{DateTime.Now}", Password = "5201314", Age = 99 };
var res = await _iUserService._repository.UseTranAsync(async () =>
{
await _iUserService.Register(user, (o) => user = o);
user.Age = 18 / (new Random().Next(0, 2));
await _iUserService._repository.UpdateAsync(user);
});
if (res)
{
return Result.Success("执行成功!");
}
else
{
return Result.Error("发生错误,插入已回滚!");
}
}
/// <summary>
/// 极爽导航属性
/// </summary>
/// <returns></returns>
[HttpGet]
//Sqlsugar精髓之一必学最新版本
public async Task<Result> IncludeTest()
{
return Result.Success().SetData(await _iUserService.GetListInRole());
}
/// <summary>
/// 启动一个定时任务
/// </summary>
/// <returns></returns>
[HttpGet]
//每5秒访问一次百度可查看控制台
public async Task<Result> JobTest()
{
Dictionary<string, object> data = new Dictionary<string, object>()
{
{JobConst.method,"get" },
{JobConst.url,"https://www.baidu.com" }
};
await _quartzInvoker.start("*/5 * * * * ?", new Quartz.JobKey("test", "my"), "Yi.Framework.Job", "HttpJob", data: data);
return Result.Success();
}
/// <summary>
/// 停止任务
/// </summary>
/// <returns></returns>
[HttpPut]
public async Task<Result> stopJob()
{
await _quartzInvoker.Stop(new Quartz.JobKey("test", "my"));
return Result.Success();
}
}
}

View File

@@ -1,71 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yi.Framework.Common.Models;
using Yi.Framework.DTOModel;
using Yi.Framework.Interface;
using Yi.Framework.Model.Models;
using Yi.Framework.Repository;
using Yi.Framework.WebCore;
using Yi.Framework.WebCore.AttributeExtend;
using Yi.Framework.WebCore.AuthorizationPolicy;
namespace Yi.Framework.ApiMicroservice.Controllers
{
/// <summary>
/// 用户管理
/// </summary>
[ApiController]
[Route("api/[controller]/[action]")]
public class UserController : BaseCrudController<UserEntity>
{
private IUserService _iUserService;
public UserController(ILogger<UserEntity> logger, IUserService iUserService) : base(logger, iUserService)
{
_iUserService = iUserService;
}
/// <summary>
/// 添加用户,去重,密码加密
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[Permission($"{nameof(UserEntity)}:add")]
[HttpPost]
public override async Task<Result> Add(UserEntity entity)
{
if (!await _iUserService.Exist(entity.UserName))
{
entity.BuildPassword();
return Result.Success().SetData(await _iUserService._repository.InsertReturnSnowflakeIdAsync(entity));
}
return Result.SuccessError("用户已存在");
}
/// <summary>
/// 给多用户设置多角色
/// </summary>
/// <param name="giveUserSetRoleDto"></param>
/// <returns></returns>
[HttpPut]
public async Task<Result> GiveUserSetRole(GiveUserSetRoleDto giveUserSetRoleDto)
{
return Result.Success().SetStatus(await _iUserService.GiveUserSetRole(giveUserSetRoleDto.UserIds, giveUserSetRoleDto.RoleIds));
}
/// <summary>
/// 通过用户id得到角色列表
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<Result> GetRoleListByUserId(long userId)
{
return Result.Success().SetData(await _iUserService.GetRoleListByUserId(userId));
}
}
}

View File

@@ -1,181 +0,0 @@
using Autofac.Extensions.DependencyInjection;
using Yi.Framework.WebCore.BuilderExtend;
using Yi.Framework.Core;
using Yi.Framework.WebCore.MiddlewareExtend;
using Yi.Framework.WebCore.Utility;
using Autofac;
using Yi.Framework.Common.Models;
using Yi.Framework.Language;
using Microsoft.Extensions.Localization;
using Yi.Framework.WebCore.AttributeExtend;
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddCommandLine(args);
builder.WebHost.UseUrls(builder.Configuration.GetValue<string>("StartUrl"));
builder.Host.ConfigureAppConfiguration((hostBuilderContext, configurationBuilder) =>
{
configurationBuilder.AddCommandLine(args);
configurationBuilder.AddJsonFileService();
#region
//Apollo配置
#endregion
configurationBuilder.AddApolloService("Yi");
});
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
#region
//交由Module依赖注入
#endregion
containerBuilder.RegisterModule<CustomAutofacModule>();
});
builder.Host.ConfigureLogging(loggingBuilder =>
{
loggingBuilder.AddFilter("System", Microsoft.Extensions.Logging.LogLevel.Warning);
loggingBuilder.AddFilter("Microsoft", Microsoft.Extensions.Logging.LogLevel.Warning);
loggingBuilder.AddLog4Net("./Config/Log4net.config");
});
#region
//配置类配置
//builder.Host.ConfigureWebHostDefaults(webBuilder =>
// {
// //webBuilder.UseStartup<Startup>();
// });
#endregion
//-----------------------------------------------------------------------------------------------------------
#region
//Ioc配置
#endregion
builder.Services.AddIocService(builder.Configuration);
#region
//Sqlsugar上下文注入
#endregion
builder.Services.AddSqlsugarServer();
#region
//Quartz任务调度配置
#endregion
builder.Services.AddQuartzService();
#region
//AutoMapper注入
#endregion
builder.Services.AddAutoMapperService();
#region
//控制器+过滤器配置
#endregion
builder.Services.AddControllers(optios => {
//optios.Filters.Add<PermissionAttribute>();
}).AddJsonFileService();
#region
//权限过滤器
#endregion
builder.Services.AddSingleton<PermissionAttribute>();
#region
//Swagger服务配置
#endregion
builder.Services.AddSwaggerService<Program>();
#region
//跨域服务配置
#endregion
builder.Services.AddCorsService();
#region
//Jwt鉴权配置
#endregion
builder.Services.AddJwtService();
#region
//授权配置
#endregion
builder.Services.AddAuthorizationService();
#region
//Redis服务配置
#endregion
builder.Services.AddRedisService();
#region
//RabbitMQ服务配置
#endregion
builder.Services.AddRabbitMQService();
#region
//ElasticSeach服务配置
#endregion
builder.Services.AddElasticSeachService();
#region
//短信服务配置
#endregion
builder.Services.AddSMSService();
#region
//CAP服务配置
#endregion
builder.Services.AddCAPService();
#region
//国际化配置
#endregion
builder.Services.AddLocalizerService();
//-----------------------------------------------------------------------------------------------------------
var app = builder.Build();
#region
//服务容器
#endregion
ServiceLocator.Instance = app.Services;
//if (app.Environment.IsDevelopment())
{
#region
//测试页面注入
#endregion
app.UseDeveloperExceptionPage();
#region
//Swagger服务注入
#endregion
app.UseSwaggerService();
}
#region
//错误抓取反馈注入
#endregion
app.UseErrorHandlingService();
#region
//静态文件注入
#endregion
app.UseStaticFiles();
#region
//多语言国际化注入
#endregion
app.UseLocalizerService();
#region
//HttpsRedirection注入
#endregion
app.UseHttpsRedirection();
#region
//路由注入
#endregion
app.UseRouting();
#region
//跨域服务注入
#endregion
app.UseCorsService();
#region
//健康检查注入
#endregion
app.UseHealthCheckService();
#region
//鉴权注入
#endregion
app.UseAuthentication();
#region
//授权注入
#endregion
app.UseAuthorization();
#region
//Consul服务注入
#endregion
app.UseConsulService();
#region
//redis种子注入
#endregion
app.UseRedisSeedInitService();
#region
//Endpoints注入
#endregion
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();

View File

@@ -1,36 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>f5ce4739-9524-4330-9aea-cfcdb41501de</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>./Config/SwaggerDoc.xml</DocumentationFile>
<NoWarn>1701;1702;CS1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Remove="wwwrooot\**" />
<Content Remove="wwwrooot\**" />
<EmbeddedResource Remove="wwwrooot\**" />
<None Remove="wwwrooot\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yi.Framework.DTOModel\Yi.Framework.DTOModel.csproj" />
<ProjectReference Include="..\Yi.Framework.Interface\Yi.Framework.Interface.csproj" />
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
<ProjectReference Include="..\Yi.Framework.Service\Yi.Framework.Service.csproj" />
<ProjectReference Include="..\Yi.Framework.WebCore\Yi.Framework.WebCore.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\file\" />
<Folder Include="wwwroot\image\" />
</ItemGroup>
</Project>

Some files were not shown because too many files have changed in this diff Show More