Compare commits

..

301 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
1386 changed files with 38718 additions and 38965 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. ## files generated by popular Visual Studio add-ons.
## ##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
WebFirst/SoEasyPlatform.exe dist/
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
appsettings.Production.json
appsettings.Development.json
wwwroot
# User-specific files # User-specific files
*.rsuser *.rsuser
*.suo *.suo

214
LICENSE
View File

@@ -1,201 +1,21 @@
Apache License MIT License
Version 2.0, January 2004
http://www.apache.org/licenses/
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, The above copyright notice and this permission notice shall be included in all
and distribution as defined by Sections 1 through 9 of this document. copies or substantial portions of the Software.
"Licensor" shall mean the copyright owner or entity authorized by THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
the copyright owner that is granting the License. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
"Legal Entity" shall mean the union of the acting entity and all AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
other entities that control, are controlled by, or are under common LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
control with that entity. For the purposes of this definition, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
"control" means (i) the power, direct or indirect, to cause the SOFTWARE.
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.

303
README.md
View File

@@ -1,7 +1,6 @@
<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>
<h1 align="center"><img align="left" height="100px" src="https://user-images.githubusercontent.com/68722157/138828506-f58b7c57-5e10-4178-8f7d-5d5e12050113.png"> Yi框架</h1> <h5 align="center">支持原生版本、Furion版本、Abp版本前端后台接入Ruoyi Vue3.0</h5>
<h4 align="center">一套与SqlSugar一样爽的.Net6开源框架</h4>
<h2 align="center">集大成者,终究轮子</h2> <h2 align="center">集大成者,终究轮子</h2>
[English](README-en.md) | 简体中文 [English](README-en.md) | 简体中文
@@ -14,37 +13,34 @@
模块分化较多,可根据业务自行引用或抛弃,集大成者,大而全乎,也许你能从中学习到一些独特见解 模块分化较多,可根据业务自行引用或抛弃,集大成者,大而全乎,也许你能从中学习到一些独特见解
正在持续更进业务模块已接入ruoyi
**英文YiFramework** **英文YiFramework**
Yi框架-一套与SqlSugar一样爽的.Net6低代码开源框架。 Yi框架-一套与SqlSugar一样爽的.Net6开源框架。
与Sqlsugar理念一致以用户体验出发。 与Sqlsugar理念一致以用户体验出发。
架构干净整洁、无业务代码、采用微软风格原生框架封装、WebFrist开发。
适合.Net6学习、Sqlsugar学习 、项目二次开发。 适合.Net6学习、Sqlsugar学习 、项目二次开发。
集大成者,终究轮子 集大成者,终究轮子
Yi框架最新版本标签`v1.2.0`,具体版本可以查看标签迭代 Yi框架最新版本标签`v3.0.0`,具体版本可以查看标签迭代
项目与Sqlsugar同步更新但这作者老杰哥代码天天爆肝到凌晨两点我们也尽量会跟上他的脚步。更新频繁所以可watching持续关注。 项目与Sqlsugar同步更新但这作者老杰哥代码天天爆肝到凌晨两点我们也尽量会跟上他的脚步。更新频繁所以可watching持续关注。
————这不仅仅是一个程序,更是一个艺术品,面向艺术的开发! ————这不仅仅是一个程序,更是一个艺术品,面向艺术的开发!
**分支** > 核心特点简单好用框架不以打包形式引用而是直接以项目附带源码给出自由度拉满遵循Mit协议允许随意修改请注明来源即可
**分支:**
本项目由EFCore版本历经3年不断迭代至Sqlsugar版本现EFcore版本已弃用目前sqlsugar已带业务功能 本项目由EFCore版本历经3年不断迭代至Sqlsugar版本现EFcore版本已弃用目前sqlsugar已带业务功能
**SqlSugar**:.Net6 DDD领域驱动设计 简单分层微服务架构 - **Framework**: 框架分支,所有东西都在这里
- Yi.Framework.Net6.NetCore 6 意框架 - **Furion**: 基于Furion分支回归开发本质极度简单用起来贼爽
- Yi.Vue3.X.RuoYiVue3 RuoYi前端框架 - ~~**SqlSugar**:.Net6 DDD领域驱动设计 简单分层微服务架构~~
你没有听错已经接入java流行指数最高最火爆的框架之一与其他框架不同Yi框架后端为完全重制版并非为ruoyi java模仿版 - ~~**SqlSugar-Dev**为sqlsugar分支的实时开发版本~~
**SqlSugar-Dev**为sqlsugar分支的实时开发版本 - ~~**ec**: EFcore完整电商项目~~
~~**ec**: EFcore完整电商项目~~
**** ****
@@ -52,19 +48,13 @@ Yi框架最新版本标签`v1.2.0`,具体版本可以查看标签迭代
废话少说直接上地址,**请不要**更改里面的数据 废话少说直接上地址,**请不要**更改里面的数据
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) 网关地址:~~[gate.ccnetcore.com/swagger](http://gate.ccnetcore.com/swagger)~~(目前使用单体架构)
谁能把持的住Sqlsugar作者自己都依赖成瘾的东西呢这是继DbFirst、CodeFirst下一代的划时代产品无脑爽
![image](https://s1.ax1x.com/2022/04/12/Lnm5Yq.png)
(首次添加实体后,生成代码记得修改对应的路径哦~~
### 支持: ### 支持:
@@ -74,141 +64,158 @@ WebFirst开发所有代码生成器已经配置完成无需任何操作数
- [ ] 即将支持网格服务架构我们将在后续版本加入dapr - [ ] 即将支持网格服务架构我们将在后续版本加入dapr
**** ****
### 软件架构: ### 详细到爆炸的Yi框架教程导航
**架构**:后端.NET6(Asp.NetCore 6)、WebFirst代码生成器~~与.NET5(Asp.NetCore 5)、前端Vue2.0~~ 1. [框架快速开始](https://ccnetcore.com/article/1641733850189139969)(已完成)
2. [框架模块教程](https://ccnetcore.com/article/1641733991574933505)(已完成)
**关系型数据库**mysql、sql server、sqlite、oracle(正在兼容中) 3. [应用模块教程](https://ccnetcore.com/article/1641734073091231745)
4. [Yi.RBAC后台系统](https://ccnetcore.com/article/1641734171128893441)
**操作系统**Windows、Linux 5. [Yi.BBS社区系统](https://ccnetcore.com/article/1641734308475572225)
**身份验证**JWT、IdentityServer4
**组件**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处理、心跳检查。。。
**** ****
<h3>业务支持模块</h3> ### 它的理念:
优雅的进行快速开发通常简单程度与优雅程度不可兼得Yi框架并不一昧的追求极致的解耦会站在用户使用角度上在使用难易度进行考虑衡量
大部分ruoyi功能采用ruoyi前端 例如我们大部分功能紧密贴合Sqlsugar虽然缺少其他orm的替换性但在使用程度上降低的使用难度
- 用户管理 例如:我们在应用层中使用属性注入,虽然依赖关系会比较模糊,但是使用起来会减少一定代码量
- 角色管理 > 一个面向用户的快速开发后端框架
- 菜单管理 在真正的使用这,你会明白这一点,极致的简单,也是优雅的一种体现。
****
- 部门管理
- 岗位管理
- 字典管理
- 参数管理
- 用户在线
- 操作日志
- 登录日志
### 特点:
- 面向用户的后端框架,使用简单,适合小型、企业级项目
- 项目内置源码,不打包
- 开箱即用
- 支持模块化
- 支持动态Api
- 支持属性注入
- 内置包含大量通用场景模块
- 等等 - 等等
![输入图片说明](Readme/%E7%94%A8%E6%88%B7%E7%AE%A1%E7%90%86.png) ### 基础设施简介
- Jwt鉴权
- 接口级别授权
- 对象映射
- O/RM
- 数据过滤
- 多租户
- 逻辑删除
- 审计日志
- 种子数据
- 工作单元
- 模块化
- 动态Api
- 属性注入
- 自动依赖注入
- 当前用户
- 仓储
- Crud
![输入图片说明](Readme/%E8%8F%9C%E5%8D%95%E7%AE%A1%E7%90%86.png)
### 框架支持模块: ### 内置模块简介
- 后台任务
- 本地缓存
- 分布式缓存
- 事件总线
- 字典管理
- 文件管理
- 图片操作
- Excel操作
- 操作日志管理
- Sms短信
- 微信支付
- 模板代码生成
大致如图: ### 业务项目
- RABC后台管理系统
- BBS社区系统
![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] 支持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] 支持`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] 支持`自动分表`
- [x] 支持 太多了忘了
**** ****
### 目录结构: ### 核心技术
#### 后端
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) js Vue3.2
![image](https://s1.ax1x.com/2022/04/24/L4q1ln.png) - [x] 异步请求axios
- [x] 图表echarts
- [x] uielement-plus
- [x] 存储pinia
- [x] 路由vue-router
- [x] 打包vite
我们大致依照DDD领域驱动设计分层 #### 运维
- [x] 部署nginx
分层如此清晰什么还感觉太复杂了用户只需关注Api、Service其他都是轮子啊 - [x] CICDgitlab+Jenkins
- [x] Dockerharbor
~~- BackGround后台进程目前可以无视等待更新~~
- Client客户端测试、客户端
- Domain领域层Dto、服务接口层、模型层、仓储层、服务层
- Infrastructure基础实例层(通用工具层、核心层、定时任务Job、国际化、Web扩展层)
- MicroServiceInstance服务层微服务
**** ****
### 安装教程: ### 业务支持模块:
我们将在之后更新教程手册! 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数据库 ERP进销存系统正在更新
2. 使用WebFirst添加实体、同步实体、修改模板生成路径并生成方案 - 供货商管理
- 等等
没了,恭喜你已经成功完成了项目,并且已经具备大部分通用场景业务 SHOP电商系统持续迭代
是不是一个字?爽! - SPU管理
到此为止,你无需写任何一个代码! - SKU管理
- 商品规格
- 商品分类
- 等等
**爽点**
新人,看这里,项目下载之后直接可以启动,无任何依赖,之后你可以查看`Test控制器`,迫不及待的快来爽一爽!
我们将使用说明转移至我们的官方论坛中,正在制作中,尽情期待!
**** ****
### 感谢: ### 感谢:
@@ -236,29 +243,11 @@ QQ交流群官方一群已满、官方二群已满、官方三群
联系作者,这里人人都是顾问 联系作者,这里人人都是顾问
官方网址:正在建设 官方网址留言区:[ccnetcore.com](https://ccnetcore.com)
**** ****
### FQA: ### FQA:
问1为什么不采用EFcore 前往官网查看留言区
答1别问问就是Sqlsugar和本框架一样爽 [留言区](https://ccnetcore.com/discuss/1641030787056930818)
问2以后会持续更新下去吗
答2一定会的我们的标题是 一个和Sqlsugar一样爽的.Net6开源框架 只要Sqlsugar在我们将一直更新下去。
问3这个框架的针对人群是哪些人适合所有人吗
答3并不是适合所有人应该算适合需要有一定基础的开发人员当然如果你是大神你完全可以将这个框架二次开发
问4花如此多的精力制作这个框架是为了什么是为了赚钱吗和目前主流的abp等框架比又有什么意义呢
答4我们与Sqlsugar作者理念一致我们是从用户角度出发框架是为用户服务与ABP复杂上手理念完全是相反的。
问5为何不出版一个详细的说明书呢
答5暂时不会了之后可能会代码都是基于Asp.NetCore框架适用于新手不用造轮子整个框架较为简单阅读源码后基本能自定义改造使用了过难也已经封装完毕别忘了其意义是为了开发更加简易建议添加作者好友这里人人都是顾问。
我大抵要厌倦了负重前行。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

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

@@ -8,15 +8,19 @@ pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
node_modules node_modules
.DS_Store
dist dist
dist-ssr dist-ssr
coverage
*.local *.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
.idea .idea
.DS_Store
*.suo *.suo
*.ntvs* *.ntvs*
*.njsproj *.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'
// })
// }

View File

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

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

@@ -4,10 +4,10 @@ export function getToken() {
return localStorage.getItem(TokenKey) return localStorage.getItem(TokenKey)
} }
export function setToken(token:string) { export function setToken(token) {
return localStorage.setItem(TokenKey, token) return localStorage.setItem(TokenKey, token)
} }
export function removeToken() { export function removeToken() {
return localStorage.removeItem(TokenKey) 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,622 +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="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.RestCC">
<summary>
重置管理员CC的密码
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.Login(Yi.Framework.DTOModel.LoginDto)">
<summary>
没啥说,登录
</summary>
<param name="loginDto"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.Register(Yi.Framework.DTOModel.RegisterDto)">
<summary>
没啥说,注册
</summary>
<param name="registerDto"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.Logout">
<summary>
没啥说,登出
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.GetUserAllInfo">
<summary>
通过已登录的用户获取用户信息
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.GetRouterInfo">
<summary>
获取当前登录用户的前端路由
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.UpdateUserByHttp(Yi.Framework.Model.Models.UserEntity)">
<summary>
更新已登录用户的用户信息
</summary>
<param name="user"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.UpdatePassword(Yi.Framework.DTOModel.UpdatePasswordDto)">
<summary>
自己更新密码
</summary>
<param name="dto"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.CaptchaImage">
<summary>
验证码
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.ArticleController.PageList(Yi.Framework.Model.Models.ArticleEntity,Yi.Framework.Common.Models.PageParModel)">
<summary>
动态条件分页查询
</summary>
<param name="entity"></param>
<param name="page"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.ArticleController.Add(Yi.Framework.Model.Models.ArticleEntity)">
<summary>
添加
</summary>
<param name="entity"></param>
<returns></returns>
</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.BaseExcelController`1.Template">
<summary>
下载模板
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseExcelController`1.Export">
<summary>
导出数据
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseExcelController`1.Import(Microsoft.AspNetCore.Http.IFormFile)">
<summary>
导入数据
</summary>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleCrudController`1">
<summary>
Json To Sql 类比模式,通用模型
</summary>
<typeparam name="T"></typeparam>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleCrudController`1.GetById(System.Int64)">
<summary>
主键查询
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleCrudController`1.GetList">
<summary>
全部列表查询
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleCrudController`1.Add(`0)">
<summary>
添加
</summary>
<param name="entity"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleCrudController`1.Update(`0)">
<summary>
修改
</summary>
<param name="entity"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleCrudController`1.DelList(System.Collections.Generic.List{System.Int64})">
<summary>
列表删除
</summary>
<param name="ids"></param>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleRdController`1">
<summary>
Json To Sql 类比模式,通用模型
</summary>
<typeparam name="T"></typeparam>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleRdController`1.GetById(System.Int64)">
<summary>
主键查询
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleRdController`1.GetList">
<summary>
全部列表查询
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseSimpleRdController`1.DelList(System.Collections.Generic.List{System.Int64})">
<summary>
列表删除
</summary>
<param name="ids"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.ConfigController.PageList(Yi.Framework.Model.Models.ConfigEntity,Yi.Framework.Common.Models.PageParModel)">
<summary>
动态条件分页查询
</summary>
<param name="dic"></param>
<param name="page"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.DeptController.SelctGetList(Yi.Framework.Model.Models.DeptEntity)">
<summary>
动态条件查询
</summary>
<param name="dept"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.DeptController.Add(Yi.Framework.Model.Models.DeptEntity)">
<summary>
添加
</summary>
<param name="entity"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.DeptController.Update(Yi.Framework.Model.Models.DeptEntity)">
<summary>
更新
</summary>
<param name="entity"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.DeptController.GetListByRoleId(System.Int64)">
<summary>
根据角色id获取该角色下全部部门
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.DictionaryController.PageList(Yi.Framework.Model.Models.DictionaryEntity,Yi.Framework.Common.Models.PageParModel)">
<summary>
动态条件分页查询
</summary>
<param name="dic"></param>
<param name="page"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.DictionaryInfoController.PageList(Yi.Framework.Model.Models.DictionaryInfoEntity,Yi.Framework.Common.Models.PageParModel)">
<summary>
动态条件分页查询
</summary>
<param name="dicInfo"></param>
<param name="page"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.DictionaryInfoController.GetListByType(System.String)">
<summary>
根据字典类别获取字典信息
</summary>
<param name="type"></param>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.FileController">
<summary>
文件
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.FileController.#ctor(Yi.Framework.Interface.IFileService,Microsoft.Extensions.Hosting.IHostEnvironment)">
<summary>
文件上传下载
</summary>
<param name="iFileService"></param>
<param name="env"></param>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.FileController.Get(System.Int64)">
<summary>
文件下载,只需用文件code即可
</summary>
<param name="code"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.FileController.Upload(System.String,Microsoft.AspNetCore.Http.IFormFileCollection,System.String)">
<summary>
多文件上传,type可空默认上传至File文件夹下swagger返回雪花id精度是有问题的
</summary>
<param name="type">文件类型,可空</param>
<param name="file">多文件表单</param>
<param name="remark">描述</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="M:Yi.Framework.ApiMicroservice.Controllers.LoginLogController.PageList(Yi.Framework.Model.Models.LoginLogEntity,Yi.Framework.Common.Models.PageParModel)">
<summary>
动态条件分页查询
</summary>
<param name="loginLog"></param>
<param name="page"></param>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.MenuController">
<summary>
菜单管理
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.MenuController.SelctGetList(Yi.Framework.Model.Models.MenuEntity)">
<summary>
动态条件查询全部
</summary>
<param name="menu"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.MenuController.Add(Yi.Framework.Model.Models.MenuEntity)">
<summary>
插入
</summary>
<param name="menu"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.MenuController.Update(Yi.Framework.Model.Models.MenuEntity)">
<summary>
更新
</summary>
<param name="menu"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.MenuController.GetMenuTree">
<summary>
得到树形菜单
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.MenuController.GetListByRoleId(System.Int64)">
<summary>
根据角色id获取该角色下全部菜单
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.OnlineController">
<summary>
在线管理
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.OnlineController.PageList(Yi.Framework.WebCore.SignalRHub.OnlineUser,Yi.Framework.Common.Models.PageParModel)">
<summary>
动态条件获取当前在线用户
</summary>
<param name="online"></param>
<param name="page"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.OnlineController.ForceOut(System.String)">
<summary>
强制退出用户
</summary>
<param name="connnectionId"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.OperationLogController.PageList(Yi.Framework.Model.Models.OperationLogEntity,Yi.Framework.Common.Models.PageParModel)">
<summary>
动态条件分页查询
</summary>
<param name="operationLog"></param>
<param name="page"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.PostController.PageList(Yi.Framework.Model.Models.PostEntity,Yi.Framework.Common.Models.PageParModel)">
<summary>
动态条件分页查询
</summary>
<param name="post"></param>
<param name="page"></param>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.RoleController">
<summary>
角色管理
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.RoleController.PageList(Yi.Framework.Model.Models.RoleEntity,Yi.Framework.Common.Models.PageParModel)">
<summary>
动态条件分页查询
</summary>
<returns></returns>
</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.Add(Yi.Framework.DTOModel.RoleInfoDto)">
<summary>
添加角色包含菜单
</summary>
<param name="roleDto"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.RoleController.Update(Yi.Framework.DTOModel.RoleInfoDto)">
<summary>
更新角色信息
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.RoleController.UpdateStatus(System.Int64,System.Boolean)">
<summary>
更改角色状态
</summary>
<param name="roleId"></param>
<param name="isDel"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.RoleController.UpdateDataScpoce(Yi.Framework.DTOModel.RoleInfoDto)">
<summary>
更改角色数据权限
</summary>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.TestController">
<summary>
测试控制器
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.#ctor(Microsoft.AspNetCore.SignalR.IHubContext{Yi.Framework.WebCore.SignalRHub.MainHub},Microsoft.Extensions.Logging.ILogger{Yi.Framework.Model.Models.UserEntity},Yi.Framework.Interface.IRoleService,Yi.Framework.Interface.IUserService,Microsoft.Extensions.Localization.IStringLocalizer{Yi.Framework.Language.LocalLanguage},Yi.Framework.Core.QuartzInvoker)">
<summary>
依赖注入
</summary>
<param name="hub"></param>
<param name="logger"></param>
<param name="iRoleService"></param>
<param name="iUserService"></param>
<param name="local"></param>
<param name="quartzInvoker"></param>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.Swagger">
<summary>
swagger跳转
</summary>
<returns></returns>
</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="M:Yi.Framework.ApiMicroservice.Controllers.TestController.TreeTest">
<summary>
树形结构构建测试
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.AuthorizeTest">
<summary>
授权测试
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.ClearDb">
<summary>
清空数据库
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.SeedDb">
<summary>
种子数据
</summary>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.LogTest(System.Collections.Generic.List{System.String})">
<summary>
操作日志测试
</summary>
<param name="par"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.SignalrTest(System.Int32)">
<summary>
Signalr实时推送测试
</summary>
<param name="msg"></param>
<returns></returns>
</member>
<member name="T:Yi.Framework.ApiMicroservice.Controllers.UserController">
<summary>
用户管理
</summary>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.PageList(Yi.Framework.Model.Models.UserEntity,Yi.Framework.Common.Models.PageParModel,System.Nullable{System.Int64})">
<summary>
动态条件分页查询
</summary>
<param name="user"></param>
<param name="page"></param>
<param name="deptId"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.UpdateStatus(System.Int64,System.Boolean)">
<summary>
更改用户状态
</summary>
<param name="userId"></param>
<param name="isDel"></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.GetById(System.Int64)">
<summary>
通过用户id得到用户信息关联部门、岗位、角色
</summary>
<param name="id"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.Update(Yi.Framework.DTOModel.UserInfoDto)">
<summary>
更新用户信息
</summary>
<param name="userDto"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.UpdateProfile(Yi.Framework.DTOModel.UserInfoDto)">
<summary>
更新个人中心信息
</summary>
<param name="userDto"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.Add(Yi.Framework.DTOModel.UserInfoDto)">
<summary>
添加用户
</summary>
<param name="userDto"></param>
<returns></returns>
</member>
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.RestPassword(Yi.Framework.Model.Models.UserEntity)">
<summary>
重置密码
</summary>
<param name="user"></param>
<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,211 +0,0 @@
using Hei.Captcha;
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.Const;
using Yi.Framework.Common.Enum;
using Yi.Framework.Common.Helper;
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]
[Authorize]
[Route("api/[controller]/[action]")]
public class AccountController : ControllerBase
{
private IUserService _iUserService;
private JwtInvoker _jwtInvoker;
private ILogger _logger;
private SecurityCodeHelper _securityCode;
private IRepository<UserEntity> _repository;
public AccountController(ILogger<UserEntity> logger, IUserService iUserService, JwtInvoker jwtInvoker, SecurityCodeHelper securityCode)
{
_iUserService = iUserService;
_jwtInvoker = jwtInvoker;
_logger = logger;
_securityCode = securityCode;
_repository = iUserService._repository;
}
/// <summary>
/// 重置管理员CC的密码
/// </summary>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public async Task<Result> RestCC()
{
var user = await _iUserService._repository.GetFirstAsync(u => u.UserName == "cc");
user.Password = "123456";
user.BuildPassword();
await _iUserService._repository.UpdateIgnoreNullAsync(user);
return Result.Success();
}
/// <summary>
/// 没啥说,登录
/// </summary>
/// <param name="loginDto"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
public async Task<Result> Login(LoginDto loginDto)
{
//跳过需要redis缓存获取uuid与code的关系进行比较即可
//先效验验证码和UUID
//登录还需要进行登录日志的落库
var loginInfo = HttpContext.GetLoginLogInfo();
loginInfo.LoginUser = loginDto.UserName;
loginInfo.LogMsg = "登录成功!";
var loginLogRepository = _repository.ChangeRepository<Repository<LoginLogEntity>>();
UserEntity user = new();
if (await _iUserService.Login(loginDto.UserName, loginDto.Password, o => user = o))
{
var userRoleMenu = await _iUserService.GetUserAllInfo(user.Id);
await loginLogRepository.InsertReturnSnowflakeIdAsync(loginInfo);
return Result.Success(loginInfo.LogMsg).SetData(new { token = _jwtInvoker.GetAccessToken(userRoleMenu.User, userRoleMenu.Menus) });
}
loginInfo.LogMsg = "登录失败!用户名或者密码错误!";
await loginLogRepository.InsertReturnSnowflakeIdAsync(loginInfo);
return Result.Error(loginInfo.LogMsg);
}
/// <summary>
/// 没啥说,注册
/// </summary>
/// <param name="registerDto"></param>
/// <returns></returns>
[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("注册失败!用户名已存在!");
}
/// <summary>
/// 没啥说,登出
/// </summary>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public Result Logout()
{
return Result.Success("安全登出成功!");
}
/// <summary>
/// 通过已登录的用户获取用户信息
/// </summary>
/// <returns></returns>
[HttpGet]
//[Authorize]
public async Task<Result> GetUserAllInfo()
{
//通过鉴权jwt获取到用户的id
var userId = HttpContext.GetUserIdInfo();
var data = await _iUserService.GetUserAllInfo(userId);
//系统用户数据被重置,老前端访问重新授权
if (data is null)
{
return Result.UnAuthorize();
}
data.Menus.Clear();
return Result.Success().SetData(data);
}
/// <summary>
/// 获取当前登录用户的前端路由
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<Result> GetRouterInfo()
{
var userId = HttpContext.GetUserIdInfo();
var data = await _iUserService.GetUserAllInfo(userId);
var menus = data.Menus.ToList();
//为超级管理员直接给全部路由
if (SystemConst.Admin.Equals(data.User.UserName))
{
menus = await _iUserService._repository.ChangeRepository<Repository<MenuEntity>>().GetListAsync();
}
//将后端菜单转换成前端路由,组件级别需要过滤
List<VueRouterModel> routers = MenuEntity.RouterBuild(menus);
return Result.Success().SetData(routers);
}
/// <summary>
/// 更新已登录用户的用户信息
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPut]
public async Task<Result> UpdateUserByHttp(UserEntity user)
{
//当然,密码是不能给他修改的
user.Password = null;
user.Salt = null;
//修改需要赋值上主键哦
user.Id = HttpContext.GetUserIdInfo();
return Result.Success().SetStatus(await _iUserService._repository.UpdateIgnoreNullAsync(user));
}
/// <summary>
/// 自己更新密码
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPut]
public async Task<Result> UpdatePassword(UpdatePasswordDto dto)
{
long userId = HttpContext.GetUserIdInfo();
if (await _iUserService.UpdatePassword(dto, userId))
{
return Result.Success();
}
return Result.Error("更新失败!");
}
/// <summary>
/// 验证码
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpGet]
public Result CaptchaImage()
{
var uuid = Guid.NewGuid();
var code = _securityCode.GetRandomEnDigitalText(4);
//将uuid与codeRedis缓存中心化保存起来登录根据uuid比对即可
var imgbyte = _securityCode.GetEnDigitalCodeByte(code);
return Result.Success().SetData(new { uuid = uuid, img = imgbyte });
}
}
}

View File

@@ -1,56 +0,0 @@
using AutoMapper;
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
{
[ApiController]
[Route("api/[controller]/[action]")]
public class ArticleController : BaseSimpleCrudController<ArticleEntity>
{
private IArticleService _iArticleService;
private IMapper _mapper;
public ArticleController(ILogger<ArticleEntity> logger, IArticleService iArticleService, IMapper mapper) : base(logger, iArticleService)
{
_iArticleService = iArticleService;
_mapper = mapper;
}
/// <summary>
/// 动态条件分页查询
/// </summary>
/// <param name="entity"></param>
/// <param name="page"></param>
/// <returns></returns>
[HttpGet]
public async Task<Result> PageList([FromQuery] ArticleEntity entity, [FromQuery] PageParModel page)
{
var pageData= await _iArticleService.SelctPageList(entity, page);
return Result.Success().SetData(new PageModel() { Data = _mapper.Map<List<ArticleVo>>(pageData.Data), Total = pageData.Total }) ;
}
/// <summary>
/// 添加
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public override Task<Result> Add(ArticleEntity entity)
{
entity.UserId = HttpContext.GetUserIdInfo();
return base.Add(entity);
}
}
}

View File

@@ -1,94 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Yi.Framework.Common.Helper;
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> : BaseExcelController<T> where T : class,new()
{
protected readonly ILogger<T> _logger;
protected IBaseService<T> _baseService;
public BaseCrudController(ILogger<T> logger, IBaseService<T> iBaseService):base(iBaseService._repository)
{
_logger = logger;
_baseService = iBaseService;
}
/// <summary>
/// 主键查询
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet]
public virtual async Task<Result> GetById(long id)
{
return Result.Success().SetData(await _repository.GetByIdAsync(id));
}
/// <summary>
/// 列表查询
/// </summary>
/// <returns></returns>
[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>
[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>
[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>
[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>
[HttpDelete]
public virtual async Task<Result> DeleteList(List<long> ids)
{
return Result.Success().SetStatus(await _repository.DeleteByIdAsync(ids.ToDynamicArray()));
}
}
}

View File

@@ -1,76 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Yi.Framework.Common.Const;
using Yi.Framework.Common.Enum;
using Yi.Framework.Common.Helper;
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
{
[ApiController]
public class BaseExcelController<T> : ControllerBase where T : class, new()
{
protected IRepository<T> _repository;
public BaseExcelController(IRepository<T> repository)
{
_repository = repository;
}
/// <summary>
/// 下载模板
/// </summary>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public IActionResult Template()
{
List<T> users = new();
var fileName = typeof(T).Name + PathConst.DataTemplate;
var path = ExcelHelper.DownloadImportTemplate(users, fileName, Path.Combine(PathConst.wwwroot, PathEnum.Excel.ToString()));
var file = System.IO.File.OpenRead(path);
return File(file, "text/plain", $"{DateTime.Now.ToString("yyyyMMddHHmmssffff") + fileName }.xlsx");
}
/// <summary>
/// 导出数据
/// </summary>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Export()
{
var users = await _repository.GetListAsync();
var fileName = DateTime.Now.ToString("yyyyMMddHHmmssffff") + nameof(T) + PathConst.DataExport;
var path = ExcelHelper.ExportExcel(users, fileName, Path.Combine(PathConst.wwwroot, PathEnum.Temp.ToString()));
var file = System.IO.File.OpenRead(path);
return File(file, "text/plain", $"{ fileName }.xlsx");
}
/// <summary>
/// 导入数据
/// </summary>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public async Task<Result> Import([FromForm(Name = "file")]IFormFile formFile)
{
List<T> datas = ExcelHelper.ImportData<T>(formFile.OpenReadStream());
//全量删除在重新插入
var res = await _repository.UseTranAsync(async () =>
{
await _repository.DeleteAsync(u => true);
await _repository.InsertRangeAsync(datas);
});
return Result.Success().SetStatus(res);
}
}
}

View File

@@ -1,86 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Yi.Framework.Common.Helper;
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 BaseSimpleCrudController<T> : BaseExcelController<T> where T : class, new()
{
protected readonly ILogger<T> _logger;
protected IBaseService<T> _baseService;
public BaseSimpleCrudController(ILogger<T> logger, IBaseService<T> iBaseService):base(iBaseService._repository)
{
_logger = logger;
_baseService = iBaseService;
}
/// <summary>
/// 主键查询
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[Permission($"{nameof(T)}:get")]
[Route("{id}")]
[HttpGet]
public virtual async Task<Result> GetById([FromRoute]long id)
{
return Result.Success().SetData(await _repository.GetByIdAsync(id));
}
/// <summary>
/// 全部列表查询
/// </summary>
/// <returns></returns>
[HttpGet]
public virtual async Task<Result> GetList()
{
return Result.Success().SetData(await _repository.GetListAsync());
}
/// <summary>
/// 添加
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[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>
[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>
[HttpDelete]
public virtual async Task<Result> DelList(List<long> ids)
{
return Result.Success().SetStatus(await _repository.DeleteByIdsAsync(ids.ToDynamicArray()));
}
}
}

View File

@@ -1,62 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Yi.Framework.Common.Helper;
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 BaseSimpleRdController<T> : BaseExcelController<T> where T : class, new()
{
protected readonly ILogger<T> _logger;
protected IBaseService<T> _baseService;
public BaseSimpleRdController(ILogger<T> logger, IBaseService<T> iBaseService):base(iBaseService._repository)
{
_logger = logger;
_baseService = iBaseService;
}
/// <summary>
/// 主键查询
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[Route("{id}")]
[HttpGet]
public virtual async Task<Result> GetById([FromRoute]long id)
{
return Result.Success().SetData(await _repository.GetByIdAsync(id));
}
/// <summary>
/// 全部列表查询
/// </summary>
/// <returns></returns>
[HttpGet]
public virtual async Task<Result> GetList()
{
return Result.Success().SetData(await _repository.GetListAsync());
}
/// <summary>
/// 列表删除
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[HttpDelete]
public virtual async Task<Result> DelList(List<long> ids)
{
return Result.Success().SetStatus(await _repository.DeleteByIdsAsync(ids.ToDynamicArray()));
}
}
}

View File

@@ -1,40 +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 ConfigController : BaseSimpleCrudController<ConfigEntity>
{
private IConfigService _iConfigService;
public ConfigController(ILogger<ConfigEntity> logger, IConfigService iConfigService) : base(logger, iConfigService)
{
_iConfigService = iConfigService;
}
/// <summary>
/// 动态条件分页查询
/// </summary>
/// <param name="dic"></param>
/// <param name="page"></param>
/// <returns></returns>
[HttpGet]
public async Task<Result> PageList([FromQuery] ConfigEntity dic, [FromQuery] PageParModel page)
{
return Result.Success().SetData(await _iConfigService.SelctPageList(dic, page));
}
}
}

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