Compare commits

..

1148 Commits

Author SHA1 Message Date
RockYang b18b8ccb02 release: v4.2.8
整合开源版 v4.2.8 功能:Sora2 视频、路由重构、手机站开关、DALL-E 参考图,以及启动时自动同步数据表字段等修复与优化。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 11:12:37 +08:00
RockYang f8a01cb9a2 feat: 启动时自动同步 model 与数据表字段
用 AutoMigrate 补齐缺列,并删除 model 中已不存在的多余列(保护 id/时间戳),修复 ChatModel.desc 等字段缺失导致的 1054 错误。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 10:37:38 +08:00
RockYang 64832a2a04 update readme 2026-05-24 10:14:28 +08:00
RockYang b1dd781a3d update readme 2026-05-24 10:12:51 +08:00
RockYang 71dc082e40 Merge tag 'v4.2.7' 2026-05-09 20:56:07 +08:00
RockYang 10836bbb4a merge v4.2.6
整合 v4.2.6 的后端中间件与服务层重构、前端样式体系迁移和管理端/移动端功能更新,统一清理历史冲突并完成版本升级。

Made-with: Cursor
2026-04-08 15:08:34 +08:00
RockYang d8237ae301 merge v4.2.5 2026-04-05 21:35:30 +08:00
RockYang 3974f1be87 merge v4.2.4 2026-02-14 17:20:53 +08:00
RockYang 126c9921a8 merge v4.2.3 2026-01-29 20:11:50 +08:00
RockYang c84754fc0c merge v4.2.2 2025-12-02 11:18:30 +08:00
RockYang 172807d326 add EasyDevelop deploy docs 2025-12-02 11:15:54 +08:00
RockYang 72608af77e merge v4.2.1 2025-11-26 20:09:48 +08:00
RockYang 7879459c3f update docker images version to docker-compose.yaml 2025-11-11 11:07:22 +08:00
RockYang ad482dad7f merge v4.2.0 2025-11-11 10:17:03 +08:00
RockYang 58e99976b3 更新数据库文件 2025-10-27 16:25:58 +08:00
RockYang 567803ebeb 添加备案信息配置项,给登录页面 Logo 增加圆角 2025-10-27 16:25:58 +08:00
RockYang 8e3d67cc7f 优化编译指令,减少程序体积 2025-10-27 16:25:58 +08:00
RockYang b463608ab7 支持微模型绑定 Dalle 绘图的 API KEY 2025-10-27 16:25:58 +08:00
RockYang eefd36562d 管理后台用户算力日志页面增加过滤查询功能 2025-10-27 16:25:58 +08:00
RockYang ebf4497b34 允许配置登录注册页面的自定义 Logo 2025-10-27 16:25:58 +08:00
RockYang 194b0343bf 修复微信登录功能 2025-10-27 16:25:58 +08:00
RockYang adb1dfbded 首页增加码云连接地址 2025-10-27 16:25:58 +08:00
RockYang 9e689f6022 支持在 Chat 页面显示,隐藏对话列表 2025-10-27 16:25:55 +08:00
RockYang 499f5c915f merge and resolve conflicts 2025-10-27 16:21:46 +08:00
RockYang a9f11d79a9 optimize login page styles 2025-10-27 16:19:34 +08:00
RockYang db5e9ea2d0 更改文档地址 2025-10-27 16:19:34 +08:00
GeekMaser 31fbf7e48a Merge pull request #247 from zayn-code/themechange
主题切换按钮:月亮图标重复了
2025-06-03 09:20:57 +08:00
GeekMaser e2e24078c5 Merge pull request #248 from zayn-code/chatlist
移动端会话列表:切换tab的时候有时会出现列表一直在加载中
2025-06-03 09:20:19 +08:00
zhangyao 39ef92e1ce 移动端会话列表:切换tab的时候有时会出现列表一直在加载中 2025-05-30 15:55:18 +08:00
zhangyao f63ba187dd 移动端会话列表:切换tab的时候有时会出现列表一直在加载中 2025-05-30 15:48:50 +08:00
zhangyao 23d1ac1021 主题切换按钮:月亮图标重复了 2025-05-30 15:46:33 +08:00
GeekMaser f08329eef4 Merge pull request #241 from coderwei99/chore/migrate-to-pnpm
Chore/migrate to pnpm
2025-05-18 12:24:19 +08:00
coderwei e993a582cd chore: 修正环境变量配置文件 2025-05-16 14:42:58 +08:00
coderwei 1524e189dc chore: migrate from npm to pnpm 2025-05-16 14:32:55 +08:00
GeekMaster 2b5165324c update docker image version 2025-04-17 10:22:41 +08:00
GeekMaster 8f67826072 update docker image version 2025-04-17 10:08:38 +08:00
GeekMaster ac9a31f049 merge code for v4.1.8 2025-04-17 10:07:04 +08:00
RockYang ed2cdbbc31 更新 README 2025-04-07 17:52:06 +08:00
RockYang 8bd2feaf7e Merge branch 'main' of gitee.com:blackfox/geekai 2025-03-25 10:48:46 +08:00
RockYang 4eccfe6d2c remove sensitive message for openai API KEY 2025-03-25 10:48:18 +08:00
RockYang 5ba2e000a1 update database file 2025-03-24 20:29:03 +08:00
RockYang fe9adadb3a update database file 2025-03-24 20:29:03 +08:00
RockYang 805c2a045e merge release v4.1.7 2025-03-24 17:13:16 +08:00
RockYang fd7b196134 merge release v4.1.7 2025-03-24 17:13:16 +08:00
RockYang 1b1c327c35 Merge tag 'v4.1.7' of gitee.com:blackfox/geekai-plus 2025-03-24 11:27:11 +08:00
RockYang a228f603d2 Merge tag 'v4.1.7' of gitee.com:blackfox/geekai-plus 2025-03-24 11:27:11 +08:00
RockYang 7d0a05ee11 update v4.1.5 sql file 2025-03-06 14:03:44 +08:00
RockYang 5f0a62b63e update v4.1.5 sql file 2025-03-06 14:03:44 +08:00
RockYang 825a1b1027 文档对话不显示文档列表的 bug 2025-03-06 14:00:13 +08:00
RockYang 38dde1a373 文档对话不显示文档列表的 bug 2025-03-06 14:00:13 +08:00
RockYang 950e7d1b00 Merge branch 'dev' 2025-03-05 18:42:44 +08:00
RockYang 89ded74345 Merge branch 'dev' 2025-03-05 18:42:44 +08:00
RockYang d8f9f48278 merge v4.1.6 2025-03-05 18:42:30 +08:00
RockYang 818bb38617 merge v4.1.6 2025-03-05 18:42:30 +08:00
GeekMaser bdd76addf3 Merge pull request #238 from gotoworld/main
v4.1.5中,存在Merge冲突脚本 #237
2025-02-25 09:27:15 +08:00
GeekMaser 0202d75ff4 Merge pull request #238 from gotoworld/main
v4.1.5中,存在Merge冲突脚本 #237
2025-02-25 09:27:15 +08:00
Welcome Aboard 2936f21f12 Merge pull request #1 from gotoworld/gotoworld-patch-1
Bugs: fixed merge conflict in geekai_plus-v4.1.5.sql
2025-02-24 19:59:00 +08:00
Welcome Aboard a0831ec6cd Merge pull request #1 from gotoworld/gotoworld-patch-1
Bugs: fixed merge conflict in geekai_plus-v4.1.5.sql
2025-02-24 19:59:00 +08:00
Welcome Aboard 705ca4d20a Bugs: fixed merge conflict in geekai_plus-v4.1.5.sql 2025-02-24 19:57:40 +08:00
Welcome Aboard 2926aab7a4 Bugs: fixed merge conflict in geekai_plus-v4.1.5.sql 2025-02-24 19:57:40 +08:00
RockYang 6a066f1b8e 默认允许跨域请求API 2025-02-11 14:34:31 +08:00
RockYang 775fb1f853 默认允许跨域请求API 2025-02-11 14:34:31 +08:00
RockYang b7d137247a 优化 docker 容器依赖关系 2025-02-11 10:10:43 +08:00
RockYang 22278c40bf 优化 docker 容器依赖关系 2025-02-11 10:10:43 +08:00
RockYang 4373642ebd 优化打包脚本,新增 arm64 架构打包脚本 2025-02-11 10:01:05 +08:00
RockYang e29b32bd2d 优化打包脚本,新增 arm64 架构打包脚本 2025-02-11 10:01:05 +08:00
RockYang 5bf90920f5 resolve conflicts 2025-02-11 09:53:41 +08:00
RockYang fa6bb5e46a resolve conflicts 2025-02-11 09:53:41 +08:00
RockYang edcbb3e226 merge v4.1.5 2025-02-11 09:45:26 +08:00
RockYang cebf8497a4 merge v4.1.5 2025-02-11 09:45:26 +08:00
RockYang 5213bdf08b fixed bug for calculate chat message tokens 2025-01-19 13:08:38 +08:00
RockYang cded4bdcc7 fixed bug for calculate chat message tokens 2025-01-19 13:08:38 +08:00
RockYang cf817fd8ea merge code for v4.1.4 2025-01-18 23:20:41 +08:00
RockYang 596b700d63 merge code for v4.1.4 2025-01-18 23:20:41 +08:00
RockYang a2481ff1cf update readme 2025-01-07 11:58:16 +08:00
RockYang 59dfa95bf8 update readme 2025-01-07 11:58:16 +08:00
RockYang bc7d06d3e5 修复登录失效的 Bug 2024-12-21 21:44:52 +08:00
RockYang f6b5a94b29 修复登录失效的 Bug 2024-12-21 21:44:52 +08:00
RockYang 8e81dfa12a update database name 2024-12-16 11:26:54 +08:00
RockYang 188fb23f08 update database name 2024-12-16 11:26:54 +08:00
RockYang 0ff76f0f21 update database file 2024-12-16 11:11:42 +08:00
RockYang 5347a12035 update database file 2024-12-16 11:11:42 +08:00
RockYang 787caa84c8 merge config.toml file 2024-12-16 10:09:23 +08:00
RockYang 691d453f41 merge config.toml file 2024-12-16 10:09:23 +08:00
RockYang c2503e663a merge v4.1.3 2024-12-16 10:07:52 +08:00
RockYang 5b7f2603ae merge v4.1.3 2024-12-16 10:07:52 +08:00
RockYang 405a88862b Merge branch 'dev' 2024-11-27 15:04:26 +08:00
RockYang 3fd7f810f0 Merge branch 'dev' 2024-11-27 15:04:26 +08:00
RockYang 296eabe09a merge v4.1.2 2024-11-27 15:00:02 +08:00
RockYang e04a48623d merge v4.1.2 2024-11-27 15:00:02 +08:00
RockYang 2e1bad387c redeem export function is ready 2024-11-27 11:52:18 +08:00
RockYang 54b45ec2ff update docker-compose.yaml 2024-11-14 16:37:43 +08:00
RockYang 5c18a50330 update docker-compose.yaml 2024-11-14 16:37:43 +08:00
RockYang c434f85045 update database 2024-11-14 16:01:27 +08:00
RockYang d78d3ffe02 update database 2024-11-14 16:01:27 +08:00
RockYang 4d10279870 merge v4.1.1 and fixed conflicts 2024-11-13 18:40:04 +08:00
RockYang 44240f65d5 merge v4.1.1 and fixed conflicts 2024-11-13 18:40:04 +08:00
RockYang 97e81a7dcc fixed bug for chat context not work for chating with image 2024-11-12 18:23:27 +08:00
RockYang 9e8f1ed6bf meta prompt function is ready 2024-11-12 17:13:38 +08:00
RockYang 4dbfdab50d auto restore user's power for failure tasks 2024-11-11 18:12:35 +08:00
RockYang c39814ce2b save task origin info for AI generating jobs 2024-11-11 17:22:08 +08:00
RockYang 95a071014c enable to set the translate model 2024-11-08 18:06:39 +08:00
RockYang 6e03f4b363 fixed bug for audio and video downloading 2024-11-05 11:38:32 +08:00
RockYang 9de9489673 remove sensitive words 2024-11-04 10:05:57 +08:00
RockYang bb8644dea0 remove sensitive words 2024-11-04 10:05:57 +08:00
RockYang ff9142ddd4 micro fix 2024-10-30 18:11:36 +08:00
RockYang 9814fec930 update the default api url to https://api.geekai.pro 2024-10-29 14:09:55 +08:00
RockYang 35f469fb82 update the default api url to https://api.geekai.pro 2024-10-29 14:09:55 +08:00
RockYang 53ba731159 update database sql file 2024-10-29 14:07:49 +08:00
RockYang 705ad58a8f update database sql file 2024-10-29 14:07:49 +08:00
RockYang c96f86fbbf 优化实时语音对话组件,处理异常 2024-10-23 18:04:09 +08:00
RockYang 4b3b64e9e2 modify text link color for register page 2024-10-21 18:26:19 +08:00
RockYang 2f2e146951 Merge branch 'main' of gitee.com:blackfox/geekai-plus 2024-10-21 18:21:21 +08:00
RockYang a09c529414 更换登录页面背景图片 2024-10-21 18:21:04 +08:00
RockYang a8d1d58e95 给 realtime 语音对话增加音效 2024-10-18 06:26:05 +08:00
RockYang 6695be815e 优化充值产品定价逻辑,确保手机端和PC端显示的价格一致 2024-10-17 18:15:25 +08:00
RockYang fbd3478772 the relay server for openai websocket is ready 2024-10-17 16:46:41 +08:00
RockYang 2102e1afbb add websocket relayer for openai realtime api 2024-10-16 18:16:09 +08:00
RockYang 155c56f502 integrated openai realtime console 2024-10-15 19:25:18 +08:00
RockYang 37a9b0e485 add PCM16 audio stream to wave is reday 2024-10-14 18:39:50 +08:00
RockYang 52a3f13f1e add voice chat test case 2024-10-12 19:07:29 +08:00
RockYang a678a11c33 suno and luma task management funtion in admin console is ready 2024-10-10 17:07:40 +08:00
RockYang d34b785238 image task list page for admin console is ready 2024-10-09 18:17:44 +08:00
RockYang 1086e1d631 fixed bug in FileSelect component for deleting files 2024-10-08 18:00:46 +08:00
RockYang b2f57aa483 merge v4.1.0 and fixed conflicts 2024-10-08 17:54:08 +08:00
RockYang 3094b9c8fd merge v4.1.0 and fixed conflicts 2024-10-08 17:54:08 +08:00
RockYang 4c2dba1004 merge v4.1.0 and fixed conflicts 2024-10-08 17:51:14 +08:00
RockYang 0f0a4c0a7e merge v4.1.0 and fixed conflicts 2024-10-08 17:51:14 +08:00
RockYang c374126f69 fixed bug for websocket message handler rebind 2024-10-08 16:41:19 +08:00
RockYang 8498cd71dc fixed webscoket event re-bind bug 2024-10-05 21:18:59 +08:00
RockYang bf30517393 fixed alipay mobile payment 2024-10-05 11:45:44 +08:00
RockYang 495f86e7fc fixed alipay mobile payment 2024-10-05 10:23:00 +08:00
RockYang 471017657f auto jump to mobile page when use mobile device access the page 2024-10-04 11:25:01 +08:00
RockYang 4861dd75be fixed bug for: websocket is not auto connected when user not login 2024-10-02 07:26:34 +08:00
RockYang 342ceea371 update database 2024-09-30 17:12:23 +08:00
RockYang 880c34dfee add message handler ONLY when websocket connect successfully 2024-09-30 16:33:26 +08:00
RockYang a1e487100d support wechat and alipay payment for mobile page 2024-09-30 16:20:40 +08:00
RockYang 77948b1e16 optimize the vue component communication, replace event listening with share data 2024-09-30 14:20:59 +08:00
RockYang e28a12a1ee websocket api refactor is ready 2024-09-29 19:28:47 +08:00
RockYang 00a8bc6784 mj websocket refactor is ready 2024-09-29 07:51:08 +08:00
RockYang 8fffa60569 sd websocket refactor is finished 2024-09-27 18:28:54 +08:00
RockYang 2debe7e927 refactor websocket message protocol, keep the only connection for all clients 2024-09-27 17:50:54 +08:00
RockYang 478bc32ddd add ws handler 2024-09-25 18:43:12 +08:00
RockYang dfd2be1265 update database sql file for v4.1.4 2024-09-23 15:54:22 +08:00
RockYang 33ff2e29e5 logout the user when it has been disabled 2024-09-20 16:49:03 +08:00
RockYang 4df1c7a136 add release v4.1.4 2024-09-20 15:50:04 +08:00
RockYang c522248d39 add email white list check in register handler 2024-09-20 14:10:40 +08:00
RockYang 246d96ee63 add email white list 2024-09-20 10:40:37 +08:00
RockYang 909ae4aa00 payment for mobile page is ready 2024-09-19 19:03:03 +08:00
RockYang 26e3ababcf wechat payment for mobile page is ready 2024-09-19 17:59:27 +08:00
RockYang 4d9f89f630 wechat payment for pc is ready 2024-09-19 14:42:25 +08:00
RockYang b989199edb fixed bug geek-plus#6, register page first tab not auto active 2024-09-19 09:03:14 +08:00
RockYang 0d81fe6c8e geek payment notify api is ready 2024-09-18 22:24:05 +08:00
RockYang be45f41e34 urgent bug fix: remove suno and luma task will recharge user power 2024-09-18 20:33:29 +08:00
RockYang e9ac58b1ef Geek Pay notify is ready 2024-09-18 18:07:49 +08:00
RockYang 59d9ae96ac add geek payment 2024-09-18 07:03:46 +08:00
RockYang 3cff6f7189 recommend user to use Google Chrome 2024-09-15 10:25:50 +08:00
RockYang 14aee28289 merge app type function branch 2024-09-14 18:17:55 +08:00
RockYang 8c1b4d4516 refactor AI chat message struct, allow users to set whether the AI responds in stream, compatible with the GPT-o1 model 2024-09-14 17:06:13 +08:00
胡双明 068df8fa15 feat: 应用分类功能 2024-09-14 11:05:49 +08:00
RockYang e371310d02 refactor chat message body struct 2024-09-14 07:11:45 +08:00
RockYang 96b8121210 return at least one chat role for getUserRoles API 2024-09-14 05:55:56 +08:00
RockYang 1960a85ead add tid field for chat app role 2024-09-13 18:32:13 +08:00
RockYang 69cb479e01 fixed bug, filelist page support pagination, do not load captcha component for user login first time 2024-09-13 17:03:05 +08:00
胡双明 db90131bd6 Merge branch 'main' into husm_2024-09-02 2024-09-13 10:25:15 +08:00
RockYang c18d272413 fixed bug for reset password 2024-09-12 17:25:19 +08:00
RockYang cd0952e170 fixed bug for register page code verification 2024-09-12 15:42:09 +08:00
胡双明 e6a6d0485d Merge branch 'main' into husm_2024-09-02 2024-09-11 09:57:01 +08:00
RockYang a642ae0332 optimize download function for suno 2024-09-11 08:39:28 +08:00
胡双明 67f508c7da feat: 210 AI对话页面文件列表增加分页功能 2024-09-10 18:14:34 +08:00
RockYang 2225f09797 file list api support pagination 2024-09-10 15:24:36 +08:00
胡双明 fe7fa46a0c Merge branch 'main' into husm_2024-09-02 2024-09-10 14:29:08 +08:00
RockYang 6d8901fa21 update v4.1.3 database sql file 2024-09-10 11:15:26 +08:00
RockYang 7750cb0021 update v4.1.3 database sql file 2024-09-10 11:11:17 +08:00
RockYang 327d68a347 support multiple delete users, update database sql file 2024-09-10 10:56:04 +08:00
RockYang 7c272b0efd update config file 2024-09-09 18:58:03 +08:00
RockYang 5eb4ed157e merge luma page code for v4.1.3 2024-09-09 18:07:10 +08:00
RockYang d4e00b960c Merge remote-tracking branch 'inet/husm_2024-09-02' into dev-4.1.3 2024-09-09 10:54:43 +08:00
RockYang 79adc871ef export the newest database sql file 2024-09-05 15:29:00 +08:00
RockYang af0eff88b8 export the newest database sql file 2024-09-05 15:29:00 +08:00
RockYang c755befa28 优化聊天页面代码,刷新页面之后自动加载当前对话的 role_id 和 model_id 2024-09-05 14:50:37 +08:00
胡双明 3f6890da27 feat: 生成视频页面2 2024-09-05 11:56:02 +08:00
RockYang 8144fada25 merged v4.0.9 and fixed conflicts 2024-09-05 11:02:32 +08:00
RockYang 08319391ea merged v4.0.9 and fixed conflicts 2024-09-05 11:02:32 +08:00
RockYang ae7f1c03e2 add auto execute task to downloading video files 2024-09-05 10:54:58 +08:00
胡双明 151017ed47 feat: 生成视频页面 2024-09-05 08:50:49 +08:00
RockYang 941d15b635 remove unused css file 2024-09-04 22:42:56 +08:00
RockYang d8058400dd adjust chat page styles 2024-09-04 18:07:39 +08:00
RockYang 5a1ed953de user can select function tools by themself 2024-09-04 14:53:21 +08:00
RockYang 58d127b602 add page and total field for pagination vo 2024-09-03 18:32:15 +08:00
RockYang 13bf73e6cf add sync lock for sub or add user's power 2024-09-03 12:09:36 +08:00
RockYang aa46fdb113 luma create and list api is ready 2024-09-02 18:08:50 +08:00
胡双明 4a09da3a25 feat(Luma): 加上传前后帧调换功能 2024-09-02 17:33:03 +08:00
RockYang d70fcbb9f3 optimize ChatPlus page, fixed bug for websocket reconnection 2024-09-02 16:35:15 +08:00
RockYang a16ef6476d add luma api service 2024-08-30 18:12:14 +08:00
RockYang dae75343d0 update change log file 2024-08-30 16:47:52 +08:00
RockYang d7564127d2 suno add new function for merging full songs and upload custom music 2024-08-30 16:46:48 +08:00
RockYang c6bf54fd9d add logo for favirate icon 2024-08-29 13:36:35 +08:00
RockYang 8d47072e1c luma page, upload image and remove image function is ready 2024-08-26 17:59:05 +08:00
RockYang d601226187 add download for video 2024-08-26 07:24:04 +08:00
RockYang 4801832d9d luma page video list component is ready 2024-08-23 18:25:58 +08:00
RockYang 6b0f42d0b8 luma page is ready 2024-08-20 14:31:40 +08:00
RockYang 08379c21ac refactor reset password functions 2024-08-19 12:05:00 +08:00
RockYang 2499bd9ad3 fixed styles 2024-08-19 06:42:53 +08:00
RockYang ad6da0d320 add bind mobile, bind email, bind wechat function is ready 2024-08-14 15:56:50 +08:00
RockYang a2583c0591 add mobile and email filed for user 2024-08-13 18:40:50 +08:00
RockYang 0ea4a29152 add verification code for login and register page 2024-08-13 14:55:47 +08:00
RockYang 7fe50788a5 add Captcha components 2024-08-13 10:01:07 +08:00
RockYang c4a68076d6 fixed conflicts 2024-08-13 06:48:22 +08:00
RockYang 9e365895af add wechat login for login dialog 2024-08-12 18:00:34 +08:00
RockYang 9ece25e17d add drag icon for dragable rows 2024-08-12 14:00:50 +08:00
RockYang 2140eff51b release v4.1.2 2024-08-09 18:50:02 +08:00
RockYang dfba3c30a5 redeem code function is ready 2024-08-09 18:19:51 +08:00
RockYang 4b717109d2 add redeem code function 2024-08-08 18:28:50 +08:00
RockYang e54e908fbc add cache for getting user info and system configs 2024-08-08 14:37:33 +08:00
RockYang 7a11a9ef15 add clear unpaid order functions 2024-08-07 18:00:28 +08:00
RockYang c9d0700fd9 refactor stable diffusion service, use api key instead of configs 2024-08-07 17:30:59 +08:00
RockYang f9b809801d refactor midjourney service, use api key in database 2024-08-06 18:30:57 +08:00
RockYang cc551ba266 show sql error message 2024-08-05 16:14:44 +08:00
RockYang 754ba02263 fixed build script 2024-08-01 18:45:53 +08:00
RockYang 23787ff462 fixed build script 2024-08-01 18:45:53 +08:00
RockYang 7ddf57ae06 merge v4.0.8 2024-08-01 18:09:00 +08:00
RockYang 9f603c5e77 merge v4.0.8 2024-08-01 18:09:00 +08:00
RockYang 3d720bdd81 update change log 2024-08-01 08:54:04 +08:00
RockYang cc5180a6f7 update readme 2024-08-01 08:52:46 +08:00
RockYang a8eccbe43b update readme 2024-08-01 08:52:46 +08:00
RockYang 2ac77ac39f restore use power when removed not finish jobs 2024-07-31 16:08:46 +08:00
RockYang 4eaa518cf3 remove platform field for api key and chat model 2024-07-30 17:24:21 +08:00
RockYang 5622f94fc8 update datebasesl 2024-07-30 14:55:49 +08:00
RockYang 95457b7dcd add back-to-top component for all list page 2024-07-29 11:00:53 +08:00
RockYang 6a9de72c78 remove chat debug log 2024-07-28 18:55:17 +08:00
RockYang 088a614160 add function to generate lyrics 2024-07-28 10:04:53 +08:00
RockYang 013ee98f53 song detail page is ready 2024-07-26 19:12:44 +08:00
RockYang dc46ed0e05 adjust chat records layout styles 2024-07-25 11:01:27 +08:00
RockYang 2a0c657ca3 add put url file for oss interface 2024-07-23 18:36:26 +08:00
RockYang a0aee80c63 remove other platform supports, ONLY use chatGPT API 2024-07-22 18:36:58 +08:00
RockYang 4910be3403 optimize foot copyright snaps 2024-07-22 17:54:09 +08:00
RockYang 1541d74c84 add close button for music player 2024-07-22 07:12:21 +08:00
RockYang 34c9151dc1 enable use random pure color background for index page 2024-07-19 18:43:01 +08:00
RockYang 6c69770ed6 the music player is ready 2024-07-18 18:34:11 +08:00
RockYang 5b7c38c67f add suno page 2024-07-17 18:58:09 +08:00
RockYang b7bec8ecb7 support upload file from clipboard 2024-07-17 10:23:02 +08:00
RockYang 82fe9c3596 allow user to use chat role directly, no need to add to workspace 2024-07-16 18:28:08 +08:00
RockYang 9f44c34d34 update docs url 2024-07-16 18:15:34 +08:00
RockYang afd84516b0 update docs url 2024-07-16 18:15:34 +08:00
RockYang 024f00b73c show error message for Midjourney task list page 2024-07-16 17:16:58 +08:00
RockYang 24a21bf2ee fixed bug for function call for openai 2024-07-16 06:25:40 +08:00
RockYang f22c6bf658 优化版权显示逻辑,允许激活用户更改自定义版权 2024-07-15 18:44:14 +08:00
RockYang 0df700ec18 update docker-compose file 2024-07-12 18:13:43 +08:00
RockYang 46141f87b8 update database file, add tika host config 2024-07-12 18:10:32 +08:00
RockYang eecce10018 tidy apis 2024-07-12 14:39:14 +08:00
RockYang b793b81768 update geekai image version 2024-07-05 11:09:13 +08:00
RockYang c8506e3d3b update geekai image version 2024-07-05 11:09:13 +08:00
RockYang bddd611cc1 wechat login is ready 2024-07-04 15:34:32 +08:00
RockYang b399fc557a feat: support wechat login function 2024-07-02 18:27:06 +08:00
RockYang 233f6e00f0 fixed conflicts 2024-06-30 06:09:12 +08:00
RockYang b20ea734fd fixed conflicts 2024-06-30 06:09:12 +08:00
RockYang b7dba68549 optimize ngin configuration for chat-plus.conf 2024-06-30 05:44:39 +08:00
RockYang 7ab0acd6a4 optimize ngin configuration for chat-plus.conf 2024-06-30 05:44:39 +08:00
RockYang 1d60ccc516 docs: update change log file 2024-06-28 16:21:43 +08:00
RockYang 2a8d2213b1 feat: optimize chat page data list style, support list style and chat style 2024-06-28 15:53:49 +08:00
RockYang a27ce36a32 feat: chat with file function is ready 2024-06-27 18:01:49 +08:00
RockYang 3fdcc895ed enable set custom index background image 2024-06-27 10:49:31 +08:00
RockYang 88e510d07a add test code for reading pdf files 2024-06-26 18:50:48 +08:00
RockYang 2526feb0d9 feat: new UI for chat file manager is ready 2024-06-25 18:59:27 +08:00
RockYang 4171b2bdfb fixed bug: mobile chat list could not update chat title 2024-06-25 09:53:08 +08:00
RockYang 0611f8de6c update version 2024-06-23 17:54:42 +08:00
RockYang df9e495dc5 add wechat payment configs sample 2024-06-22 16:04:04 +08:00
RockYang 8a48476105 update docker-compose file 2024-06-22 12:37:41 +08:00
RockYang 9d4a1705a5 add database files 2024-06-22 12:17:35 +08:00
RockYang 871e5733c7 finish mobile wechat payment 2024-06-22 12:10:43 +08:00
RockYang bdea12c51a update docker image mirror url 2024-06-19 08:35:05 +08:00
RockYang e8a663e3c7 update docker image mirror url 2024-06-19 08:35:05 +08:00
RockYang a27d9ea259 Merge branch 'main' of gitee.com:blackfox/geekai 2024-06-19 08:22:00 +08:00
RockYang 422d439627 Merge branch 'main' of gitee.com:blackfox/geekai 2024-06-19 08:22:00 +08:00
RockYang 7cd824c284 update docker image version to 4.0.6 2024-06-15 15:35:32 +08:00
RockYang edeff3c169 update docker image version to 4.0.6 2024-06-15 15:35:32 +08:00
RockYang e27d95e2b5 update docker image version to 4.0.6 2024-06-15 15:33:38 +08:00
RockYang 13076421a1 update docker image version to 4.0.6 2024-06-15 15:33:38 +08:00
RockYang 3943e7f05f update change log files 2024-06-14 18:23:54 +08:00
GeekMaster 6839827db0 Merge branch 'main' of https://github.com/yangjian102621/geekai into main 2024-06-12 15:44:26 +08:00
GeekMaster cd0d9dad98 Merge branch 'main' of https://github.com/yangjian102621/geekai into main 2024-06-12 15:44:26 +08:00
RockYang 857da34b9c wechat payment is ready for PC 2024-06-12 14:20:37 +08:00
RockYang ccad7e7bb5 change payment component, upgrade golang to 1.22.4 2024-06-11 11:48:41 +08:00
RockYang b8de15d66e fix bug: free model not record the chat history 2024-06-06 15:01:32 +08:00
RockYang c02661ea29 fixe page styles 2024-06-05 18:08:23 +08:00
RockYang 3c70c8ae59 fixed bug markmap generation 2024-06-04 16:21:08 +08:00
RockYang d6a04f96fe Merge pull request #208 from mari1995/main
图片墙,选择框单选问题
2024-06-04 08:33:51 +08:00
RockYang b985b62068 Merge pull request #208 from mari1995/main
图片墙,选择框单选问题
2024-06-04 08:33:51 +08:00
RockYang 297b760293 dalle3 and gptt-4o api compatible with azure 2024-06-03 18:34:37 +08:00
RockYang 5f820b9dc1 merge v4.0.6 2024-06-03 14:22:08 +08:00
RockYang 088abfe7ab merge v4.0.6 2024-06-03 14:22:08 +08:00
RockYang 17340ae0ac fixed bug for function call error None is not of type 'array' 2024-05-30 09:59:44 +08:00
RockYang fe687af8ca fixed bug for mobile chat share 2024-05-30 08:37:14 +08:00
RockYang 7e26029268 add v4.0.8 database sql file 2024-05-29 17:41:37 +08:00
RockYang 03f7f2a53e feat: add dalle3 page for h5 2024-05-29 17:25:01 +08:00
RockYang 296260bf6a feat: add system config for enable rand background image for index page 2024-05-29 16:24:56 +08:00
RockYang dd07acd21a feat: add system config for enable rand background image for index page 2024-05-29 16:23:42 +08:00
RockYang 89f6402fbf fix markdown formula parse plugin 2024-05-29 13:49:45 +08:00
SSMario 6788edbe9d Update Image.vue 2024-05-27 21:23:11 +08:00
SSMario 853059c814 Update Image.vue 2024-05-27 21:23:11 +08:00
SSMario 3895305882 Update Image.vue 2024-05-27 21:00:59 +08:00
SSMario 5a0876f8dc Update Image.vue 2024-05-27 21:00:59 +08:00
RockYang 2e32238652 micro fixs 2024-05-27 17:39:17 +08:00
SSMario c2acbaaa94 Update ImagesWall.vue 2024-05-27 17:04:15 +08:00
SSMario ad1a99aa44 Update ImagesWall.vue 2024-05-27 17:04:15 +08:00
RockYang 24e4be019a fixed bug for dalle prompt translate 2024-05-27 11:42:14 +08:00
RockYang 13c917ad7e put model and app selector on the top of chat page 2024-05-24 12:33:22 +08:00
RockYang 0a8cf6870f fixed bug for payment api authorization 2024-05-24 11:31:38 +08:00
RockYang 061291cebb chore: use config value for order pay timeout 2024-05-22 18:15:06 +08:00
RockYang b8e0d7760b feat: add sign check for PC QR code payment 2024-05-22 17:47:53 +08:00
RockYang 962de0183c extract code for saving chat history 2024-05-22 15:32:44 +08:00
RockYang 627396dbf7 check if the api url in whitelist for mj plus client 2024-05-22 11:47:04 +08:00
RockYang 6c300bb018 fixed bug for mobile chat page change chat model not work 2024-05-21 17:54:03 +08:00
RockYang 9273df4af2 auto resize the input element rows, when use inputed more than one line 2024-05-21 17:36:47 +08:00
RockYang 4a99be2f15 remove license code 2024-05-21 16:20:29 +08:00
RockYang 669d784f54 remove license code 2024-05-21 16:20:29 +08:00
RockYang 5253d657b6 add logs for updating database failed 2024-05-21 11:55:38 +08:00
RockYang 27c816cf3b merge conflicts for v4.0.5 2024-05-21 11:30:40 +08:00
RockYang c2548c5007 merge conflicts for v4.0.5 2024-05-21 11:30:40 +08:00
RockYang 0d81776212 update docker image name 2024-05-21 11:21:27 +08:00
RockYang bdec823148 update docker image name 2024-05-21 11:21:27 +08:00
RockYang a6de958daa update docker image url 2024-05-21 11:03:11 +08:00
RockYang cccab31c0f rename project name to geekai 2024-05-20 15:14:02 +08:00
RockYang 50c25d4574 rename project name to geekai 2024-05-20 15:14:02 +08:00
RockYang 68100f7f24 rename project name to geekai 2024-05-20 15:11:14 +08:00
RockYang 38777ea285 update database file 2024-05-19 19:37:16 +08:00
RockYang 00c8f08179 remove code for set left component fixed height 2024-05-18 08:07:09 +08:00
RockYang 45d6579fb6 refactor login dialog for front page 2024-05-18 00:27:32 +08:00
RockYang dad9254128 finished refactor chat page UI 2024-05-17 19:25:38 +08:00
RockYang 4ddf3bf2bf fixed bugs for send message captcha component 2024-05-17 08:41:09 +08:00
RockYang 9934143f00 fixed bugs for send message captcha component 2024-05-17 08:41:09 +08:00
RockYang f617bde81b fixed bugs for send message captcha component 2024-05-16 22:33:00 +08:00
RockYang b2771b7b3f feat: add top navbar for front page 2024-05-16 20:10:00 +08:00
RockYang 44b3237dd6 feat: support add external link menu 2024-05-16 10:53:00 +08:00
RockYang b987a2d365 handler chat error in the chat entry func 2024-05-15 15:30:34 +08:00
RockYang 1cf299f090 add charge link for insufficient of power 2024-05-15 07:10:31 +08:00
RockYang c87d78c666 fix bug for white-list api key check 2024-05-14 22:30:42 +08:00
RockYang 3d37a3d367 update readme 2024-05-14 18:23:12 +08:00
RockYang 79c8d90049 update readme 2024-05-14 18:23:12 +08:00
RockYang 73d8236697 update change log 2024-05-14 18:20:52 +08:00
RockYang 1afc3e5e1c update change log 2024-05-14 18:20:52 +08:00
RockYang 87e8bbfa41 update version 2024-05-14 18:03:58 +08:00
RockYang 3e3d3e162e refactor: use waterflow component in mj, sd and dall image drawing page 2024-05-13 19:04:00 +08:00
RockYang cc80b87fee fixed sd page waterfall component 2024-05-11 18:27:35 +08:00
RockYang b614ecccc2 enable to update AI Drawing configuarations in admin console page 2024-05-11 17:27:14 +08:00
RockYang 8251c6589b fix: markmap do not cost power in front page 2024-05-11 07:08:14 +08:00
RockYang 4edf2ea02a optimize chat handler error handle code 2024-05-10 18:26:19 +08:00
RockYang 12dd4659cd fix bug: remove chat role failed 2024-05-10 17:38:55 +08:00
RockYang 5d4f40c004 fixed bug for dalle3 task not decrease power 2024-05-10 11:18:37 +08:00
RockYang 3c34e8e0e7 fixed bug for dalle3 task not decrease power 2024-05-10 11:17:52 +08:00
RockYang 7441a3a717 fixed bug for dalle3 task not decrease power 2024-05-10 11:17:52 +08:00
RockYang 3eef9a836b add toolbar for markmap component 2024-05-10 06:38:34 +08:00
RockYang 922202734a fixed bug for markmap 2024-05-09 21:55:40 +08:00
RockYang fffd7e375f fixed bug for markmap 2024-05-09 21:55:40 +08:00
RockYang d87f09c957 use proxy for downloading discord images 2024-05-09 18:48:53 +08:00
RockYang b29884f8df add stable diffusion default negtive prompt system config 2024-05-07 16:49:54 +08:00
RockYang b270960a04 remove license code 2024-05-07 16:41:35 +08:00
RockYang c0ee2c2d8e remove license code 2024-05-07 16:41:35 +08:00
RockYang 5c4899df6e upgrade to v4.0.4 2024-05-07 16:32:05 +08:00
RockYang be1580e949 upgrade to v4.0.4 2024-05-07 16:32:05 +08:00
RockYang 6424eb871c add stable diffusion default negtive prompt system config 2024-05-07 16:21:31 +08:00
RockYang 8a8c43c7a5 handle the exception for web front page 2024-05-06 17:39:58 +08:00
RockYang 9a503ddc72 fixed conflicts 2024-05-06 14:44:09 +08:00
RockYang b130466c8f chore: change module name to geekai, add copyright in source code 2024-05-06 14:41:27 +08:00
RockYang 2f0215ac87 Update README.md 2024-05-06 10:45:50 +08:00
RockYang 3ab5459778 Update README.md 2024-05-06 10:45:50 +08:00
RockYang dd5cc206e5 update LICENSE.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-05-05 03:05:23 +00:00
RockYang 4eb1bf2de2 update LICENSE.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-05-05 03:05:23 +00:00
RockYang 6c47551b03 fix bug for waterflow component 2024-05-05 10:52:29 +08:00
RockYang 657ecccee3 update changelog 2024-05-04 21:32:43 +08:00
RockYang b4ba70c0f3 update changelog 2024-05-04 21:32:43 +08:00
RockYang c2bf5e845a fix bug for license synchronize 2024-05-04 21:30:29 +08:00
RockYang 8900e72e45 opt: optimize mobile images page styles 2024-05-03 08:14:33 +08:00
RockYang 0cb192135d fix: fix bug for dalle power refund 2024-05-01 08:48:32 +08:00
RockYang c1ca687630 chore: update docker image name 2024-05-01 07:59:20 +08:00
RockYang 29cc24508b opt: styles and view micro optimization 2024-05-01 07:40:56 +08:00
RockYang 3db55cbd48 opt: close unused websocket connections 2024-04-30 22:54:39 +08:00
RockYang c0a7f41747 chore: replace docker image url with AliYun 2024-04-30 19:08:33 +08:00
RockYang 1ba08bbfa6 feat: change theme for mobile site is ready 2024-04-30 18:57:15 +08:00
RockYang 51a8f42d89 feat: mobile page refactor is finished 2024-04-29 19:22:00 +08:00
RockYang 3b081ff0f4 opt: add chat config for mobile chat session 2024-04-29 09:39:23 +08:00
RockYang 7f31a301e3 feat: add index page for mobile 2024-04-28 19:09:26 +08:00
RockYang 039d949523 theme change is ready 2024-04-27 13:13:28 +08:00
RockYang 5b16dc6ee7 feat: admin console dark theme 2024-04-26 18:10:17 +08:00
RockYang 34648c704f opt: optimize index and login page UI 2024-04-26 16:07:02 +08:00
RockYang 1966e69460 synchronize license every 10 secs 2024-04-26 14:35:01 +08:00
RockYang 74b1b07b31 change index page background 2024-04-25 18:54:33 +08:00
RockYang 2e023cb8dc update readme 2024-04-25 06:27:30 +08:00
RockYang 118c0e1ff1 update readme 2024-04-25 06:27:30 +08:00
RockYang e933f32d9c remove unused files 2024-04-24 21:22:56 +08:00
RockYang 81d075c028 remove unused files 2024-04-24 21:22:56 +08:00
RockYang 514dd6c76a show license info in admin active page, optimize markdown generate prompt 2024-04-24 19:00:28 +08:00
RockYang 0b2501c1d8 output the error to chat page directly, replace the common error message 'AI开小差' 2024-04-24 10:10:03 +08:00
RockYang ddc323a8c3 output the error to chat page directly, replace the common error message 'AI开小差' 2024-04-24 10:10:03 +08:00
RockYang 2933c057a2 fixed bug for markmap 2024-04-23 20:47:06 +08:00
RockYang c1d892069e release v4.0.4 2024-04-23 18:47:23 +08:00
RockYang 20ed6d1d3d release v4.0.4 2024-04-23 18:47:23 +08:00
RockYang 90f5275201 optimize styles and release v4.0.4 2024-04-23 18:46:32 +08:00
RockYang fe64b203da feat: hide more navigator items 2024-04-22 16:27:53 +08:00
RockYang 9eb1353455 feat: support markmap svg export and download as png image 2024-04-22 14:20:51 +08:00
RockYang 8a1c55c731 allow users to select a chatApp to chat in chat app list page 2024-04-22 11:18:55 +08:00
RockYang 9059bebd07 optimize index page UI 2024-04-22 10:43:33 +08:00
RockYang d18f79fe74 optimize code for remove timeout and failed image drawing job 2024-04-21 21:44:28 +08:00
RockYang 3a8f0be28a image wall page add dalle 2024-04-21 20:42:42 +08:00
RockYang 60cf380f96 dalle image page is ready 2024-04-21 20:23:47 +08:00
RockYang ab8240613e DO NOT refresh finished jobs when job is running 2024-04-20 21:30:55 +08:00
RockYang d6e9dc6839 fixed markdown generating styles 2024-04-19 18:22:45 +08:00
RockYang 6891bdde53 support send email use TLS 2024-04-19 12:04:59 +08:00
RockYang bda335212d Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2024-04-19 10:56:05 +08:00
RockYang ecadfeab19 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2024-04-19 10:56:05 +08:00
RockYang 06f4cdc649 fixed bug for QWen response blank quotes 2024-04-19 10:55:29 +08:00
RockYang a38d547200 fixed bug for QWen response blank quotes 2024-04-19 10:55:29 +08:00
RockYang a15f431a7f fixed bug for QWen response blank quotes 2024-04-19 10:30:02 +08:00
RockYang 3bb5b66e8b fixed bug for chat handler doRequest method 2024-04-16 23:49:56 +08:00
RockYang 9bf7fa4081 compatible freeGPT35 API 2024-04-15 21:03:19 +08:00
RockYang d9ff3242c6 compatible freeGPT35 API 2024-04-15 21:03:19 +08:00
RockYang cd562ab8ed fixed bug for websocket close tip message 2024-04-15 18:15:15 +08:00
RockYang 3a8a69ac2e feat: markmap function is ready 2024-04-15 17:23:59 +08:00
RockYang 4ca9dfd9c0 fixed upscale and variation action url 2024-04-15 15:14:49 +08:00
RockYang 0d5b4936bd fixed upscale and variation action url 2024-04-15 15:14:49 +08:00
RockYang b5f6eaf159 opt: close the old connection for mj and sd clients 2024-04-15 09:34:20 +08:00
RockYang adfee8bf58 update version 2024-04-15 09:05:54 +08:00
RockYang 25c80921e0 update version 2024-04-15 09:05:54 +08:00
RockYang fbfa2a71a9 release v4.0.3 2024-04-15 08:26:07 +08:00
RockYang 5c51ed48c3 release v4.0.3 2024-04-15 08:26:07 +08:00
RockYang 22febfc42a markmap enable to select ai model 2024-04-15 06:16:53 +08:00
RockYang de482dd9f4 fixed chat page styles 2024-04-12 21:57:41 +08:00
RockYang 74af67a41f feat: markmap page view is ready 2024-04-12 18:49:24 +08:00
RockYang 170b41441b feat: allow chat model bind a fixed api key 2024-04-12 17:09:22 +08:00
RockYang 4991e50e16 chore: update build.sh 2024-04-11 21:26:33 +08:00
RockYang 1f236ef929 feat: use custom mode for mj upscale and variation operarions 2024-04-11 17:32:34 +08:00
RockYang 7f86c5984d feat: add index page 2024-04-10 18:23:55 +08:00
RockYang 6fcee49b19 feat: support for freeGPT35 API 2024-04-10 14:49:07 +08:00
RockYang ac527851a0 feat: support gpt-4-turbo-2014-04-09 vision function 2024-04-10 11:47:10 +08:00
RockYang 64bbeaedcb fixed bug for gpt-4-turbo-2024-0409 model function calls 2024-04-10 10:23:45 +08:00
RockYang 3e984a1bcb fix bug: remove timeout task ONLY for unfinished(progress < 100) 2024-04-10 06:25:54 +08:00
RockYang a7237fe62f Merge branch 'main' of gitee.com:blackfox/chatgpt-plus 2024-04-07 18:33:55 +08:00
RockYang 79165214d7 Merge branch 'main' of gitee.com:blackfox/chatgpt-plus 2024-04-07 18:33:55 +08:00
RockYang c3c454b7d7 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2024-04-07 18:32:20 +08:00
RockYang 3f7855dd6e Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2024-04-07 18:32:20 +08:00
RockYang d4d708d44b update database files 2024-04-07 18:26:45 +08:00
RockYang f0610281e5 update database files 2024-04-07 18:26:45 +08:00
RockYang 7f0b6a3a46 set the enable status for adding new api key with default value true 2024-04-07 10:17:10 +08:00
RockYang e87e7e2164 set the enable status for adding new api key with default value true 2024-04-07 10:17:10 +08:00
RockYang c2a7c089d2 update change log 2024-04-07 08:40:43 +08:00
RockYang 1a3ad390fd update change log 2024-04-07 08:40:43 +08:00
RockYang df5bd4df60 Merge branch 'dev' 2024-04-07 08:03:14 +08:00
RockYang c383367e9f Merge branch 'dev' 2024-04-07 08:03:14 +08:00
RockYang 79b6010104 optimize ngin configuration for chat-plus.conf 2024-04-07 08:02:47 +08:00
RockYang 7a9f45c96b optimize ngin configuration for chat-plus.conf 2024-04-07 08:02:47 +08:00
RockYang 97b0a98793 fix 404 error with remove api keys 2024-04-07 08:01:25 +08:00
RockYang 7277cb289f fix 404 error with remove api keys 2024-04-07 08:01:25 +08:00
RockYang 86b2d5eff2 fix bug for remove api 404 errorc 2024-04-06 20:36:52 +08:00
RockYang 7873847616 feat: show bind model on chat role list page 2024-04-05 21:21:28 +08:00
RockYang 5e46f149d9 feat: support uploading role icon 2024-04-05 17:41:23 +08:00
RockYang 574fc52332 feat: allow bind a chat model for chat role 2024-04-05 12:51:18 +08:00
RockYang 8c924ca98f update change log 2024-04-05 06:58:13 +08:00
RockYang a9adc0c1a2 feat: stable diffusion image drawing on mobile is ready 2024-04-03 18:13:48 +08:00
RockYang ed286c8894 feat: midjourney role and style consistency is ready 2024-04-02 19:01:28 +08:00
RockYang d900a3d08e update README.md.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-04-02 10:00:45 +00:00
RockYang a55da15202 update README.md.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-04-02 10:00:45 +00:00
RockYang cdf5b66729 Update README.md 2024-04-02 17:59:53 +08:00
RockYang 9728118f94 Update README.md 2024-04-02 17:59:53 +08:00
RockYang 9cc2ea412b feat: image preview for stable-diffusion task is ready 2024-04-02 17:24:38 +08:00
RockYang 3f1ad4b7dc feat: support midjourney --cref and --sref for role consistency 2024-04-02 14:59:53 +08:00
RockYang 56c225bf20 feat: update menu icons, add version in site titles 2024-04-01 18:20:00 +08:00
RockYang 8b68bfb28c show power for chat and imaging page 2024-03-31 20:49:12 +08:00
RockYang a5299b52d4 opt: change the relative path with absolute path for midjourney image uploading 2024-03-31 17:45:22 +08:00
RockYang 9e9bc52737 Merge branch 'main' into dev 2024-03-30 11:57:31 +08:00
RockYang 76106e4504 remove dead code 2024-03-30 11:57:23 +08:00
RockYang a6c00c42fa fixed conflicts 2024-03-29 18:10:59 +08:00
RockYang 4e23005b5b fixed conflicts 2024-03-29 18:10:59 +08:00
RockYang f27e2de220 update readme file 2024-03-29 18:06:55 +08:00
RockYang eefcabcff3 update databases 2024-03-29 17:43:38 +08:00
RockYang 63e4669e3f feat: support custom menu 2024-03-29 15:41:58 +08:00
RockYang 7d5428f775 add tip for mj image buttons 2024-03-28 21:41:49 +08:00
RockYang ab3c4562fa fix: fix overflow hidden for admin page 2024-03-28 18:51:09 +08:00
RockYang 13f89bf335 fix: fix overflow hidden for mobile page 2024-03-28 18:13:33 +08:00
RockYang e6a18445c3 fix: use slide captcha for iphone 2024-03-28 15:00:53 +08:00
RockYang c8ab209426 feat: add mj_action_power system config item 2024-03-28 09:53:41 +08:00
RockYang 360fea4085 feat: change midjourney origin implements, replace midjourney bot with midjourney-proxy 2024-03-27 18:57:15 +08:00
RockYang 9794d67eaa feat: auto translate and rewrite prompt for midjourney and stable-diffusion 2024-03-27 13:45:52 +08:00
RockYang b60a639312 feat: stable-diffusion refactored, replace websocket api with sdapi 2024-03-26 18:23:08 +08:00
RockYang 870706c4ff fix: can not change user's power in admin console 2024-03-25 11:40:03 +08:00
RockYang 491471fa10 fix: fix bug for update user's power in admin page did not work 2024-03-23 16:21:37 +08:00
RockYang 6dbf61d4e4 No need to login with Stable-Diffusion page and Invite page 2024-03-23 15:45:37 +08:00
RockYang 81545d192b fix: fix code highlight error when add formule detecting 2024-03-22 19:09:04 +08:00
RockYang 37cdc26160 chore: correct prompt messages 2024-03-22 18:27:57 +08:00
RockYang fb6a35ebe4 feat: add chart for admin dashbord 2024-03-22 16:57:30 +08:00
RockYang f7a565bb80 feat: integrate xxl-job-admin to implements automatic task scheduling 2024-03-22 13:47:16 +08:00
RockYang bf5e72b7e0 feat: save prompt in power log for dalle-3 2024-03-21 15:55:39 +08:00
RockYang ec6186596d feat: add manager list page in console page 2024-03-21 15:24:28 +08:00
RockYang dece19cec6 feat: add powerlog page for admin console 2024-03-21 13:46:39 +08:00
RockYang 6aa1d27711 opt: optimize the formula show styles 2024-03-21 11:04:12 +08:00
RockYang 264e77f383 always parse authorization token for all request 2024-03-20 21:11:52 +08:00
RockYang 0cbc284e42 fixed conflicts 2024-03-20 20:40:22 +08:00
RockYang 9e42a334fa update README.md.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-03-20 20:39:44 +08:00
RockYang 49438d2ee1 feat: Async loading midjourney job for mobile MidJourney page 2024-03-20 18:39:14 +08:00
RockYang 735aa24020 feat: h5 payment for payjs is ready 2024-03-20 17:46:39 +08:00
RockYang 14f63a203d feat: payment for mobile pages is ready 2024-03-20 16:14:02 +08:00
RockYang e11c0a3633 feat: the power log page is ready 2024-03-20 14:14:30 +08:00
RockYang ca83f139f7 feat: no need login refactor with member and chatApps page 2024-03-19 18:59:02 +08:00
RockYang 58e44b7d12 feat: load preview page do not require user to login 2024-03-19 18:25:01 +08:00
RockYang 8f47474edd feat: optimize login dialog 2024-03-19 10:47:13 +08:00
RockYang c5200aada8 feat: refactoring adjustments for reward page is ready 2024-03-18 18:28:34 +08:00
RockYang 4e39b93673 feat: refactoring adjustments for member pages 2024-03-18 16:59:07 +08:00
RockYang f215643f2e feat: The 'chat_models' field of user table, holds the model IDS in place of the model values 2024-03-18 15:37:46 +08:00
RockYang aa72be6ff3 remove new-ui files 2024-03-18 12:01:34 +08:00
RockYang b567e56b60 chore: adjust page styles 2024-03-18 06:46:08 +08:00
RockYang 17431c1707 重构主体工作完成 2024-03-15 18:35:10 +08:00
RockYang 7c9ebef5e9 restore new ui files 2024-03-15 11:13:02 +08:00
廖彦棋 72b0e11543 fix(ui): 交互修复调整 2024-03-15 11:07:41 +08:00
RockYang 9e2af9dbca feat: refactor user list page for new UI 2024-03-15 09:29:19 +08:00
廖彦棋 b7cc987132 fix(ui): 用户管理有效期传参调整,权限标识补充 2024-03-15 09:25:06 +08:00
廖彦棋 8476c73b0c Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-14 17:49:18 +08:00
廖彦棋 e76b49afd2 refactor(ui): 无权限页面调整 2024-03-14 17:49:15 +08:00
吴汉强 c712b95e0d feat(ui): 后台首页去掉权限判断 2024-03-14 17:24:40 +08:00
廖彦棋 1ee1a8d6d9 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-14 17:19:41 +08:00
廖彦棋 f01c764589 feat(ui): 无权限判断 2024-03-14 17:19:39 +08:00
吴汉强 6df66d150b Merge remote-tracking branch 'origin/ui' into ui 2024-03-14 17:12:53 +08:00
吴汉强 6132f94eff feat(ui): 新增 sql 2024-03-14 17:12:48 +08:00
廖彦棋 ac5e67ea73 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-14 17:06:15 +08:00
吴汉强 143d2b44d0 feat(ui): 403,没权限 2024-03-14 16:41:38 +08:00
吴汉强 ef130fe377 feat(ui): 网站配置需授权,去掉 2024-03-14 16:28:49 +08:00
廖彦棋 d7d1e2100c fix(ui): 调整 2024-03-14 16:14:49 +08:00
吴汉强 af5afd7700 feat(ui): 后端加权限验证 2024-03-14 15:39:12 +08:00
廖彦棋 a64df5cf0c feat(ui): 角色管理 2024-03-14 15:25:17 +08:00
廖彦棋 3866319373 feat(ui): 新增系统分类菜单 2024-03-14 15:17:53 +08:00
廖彦棋 ec5ad809f2 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-14 10:56:56 +08:00
廖彦棋 268acb174d fix(ui): 修复 2024-03-14 10:56:54 +08:00
吴汉强 3733877be1 Merge remote-tracking branch 'origin/ui' into ui 2024-03-14 10:28:39 +08:00
吴汉强 6d5579a8d6 feat(ui): 登录接口返回权限 2024-03-14 10:28:32 +08:00
廖彦棋 306cd2f945 feat(ui): 管理后台新增权限及部分组合式函数优化 2024-03-14 10:27:09 +08:00
廖彦棋 5d4dd1e66f Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-13 17:30:26 +08:00
廖彦棋 5f371bdc4a feat(ui): web移动端初始化 2024-03-13 17:30:24 +08:00
吴汉强 aa58ba392e Merge remote-tracking branch 'origin/ui' into ui 2024-03-13 17:24:38 +08:00
吴汉强 0485610818 feat(ui): 增加角色管理,管理员方法新增角色关联 2024-03-13 17:24:30 +08:00
chenzifan 7cb8becc09 Merge remote-tracking branch 'origin/ui' into ui 2024-03-13 14:40:45 +08:00
chenzifan 8fa535a01b refactor: remove api change to post request 2024-03-13 14:40:38 +08:00
吴汉强 5f202e03a2 Merge remote-tracking branch 'origin/ui' into ui 2024-03-13 11:41:07 +08:00
吴汉强 ef2dfe330b feat(ui): 增加系统权限管理 2024-03-13 11:41:01 +08:00
廖彦棋 fcd86fbebd fix(ui): ui调整 2024-03-13 09:54:20 +08:00
RockYang 960d294aa2 docs: update sql file 2024-03-13 08:49:40 +08:00
RockYang 27f8e63f3d Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-13 08:48:10 +08:00
chenzifan e8d7ad58be fix: 删除系统管理员失效的问题 2024-03-13 08:47:17 +08:00
chenzifan f4d537e0f5 Merge remote-tracking branch 'origin/ui' into ui
# Conflicts:
#	api/handler/admin/admin_user_handler.go
2024-03-13 08:46:16 +08:00
chenzifan d6c93dd537 fix: 删除系统管理员失效的问题 2024-03-13 08:45:09 +08:00
廖彦棋 aa5fff47de Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-13 08:44:50 +08:00
廖彦棋 c920d8423d feat(ui): 调整 2024-03-13 08:44:48 +08:00
chenzf@pvc123.com b8fcfe2e7c feat: 超级管理员不支持修改和删除 2024-03-12 21:16:05 +08:00
RockYang 072c96e1ec Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-12 18:07:24 +08:00
RockYang 38c8ece642 fix conflicts 2024-03-12 18:07:19 +08:00
chenzifan 9bf1576fb4 feat: 增加系统管理员 2024-03-12 18:06:49 +08:00
RockYang d648e177c4 fix conflicts 2024-03-12 18:03:24 +08:00
RockYang e8f62af7f6 refactor: use power replace calls for front pages 2024-03-12 17:47:06 +08:00
RockYang e746aafa2f refactor: 重构项目,为所有的 AI 工具都引入算力,采用算力统一结算各个工具的调用次数和权限 2024-03-12 15:40:44 +08:00
廖彦棋 71f7a1b166 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-12 08:37:29 +08:00
廖彦棋 678f570a90 refactor(ui): 调整 2024-03-12 08:37:27 +08:00
huangqj 4bc1f195db fix(ui):环境变量 2024-03-11 16:10:49 +08:00
廖彦棋 b2ff49ee94 feat(ui): 新增系统管理员 2024-03-11 15:59:15 +08:00
廖彦棋 d9558f13ad fix(ui): 删除冗余 2024-03-11 14:23:11 +08:00
廖彦棋 8dc8f2567e fix(ui): 删除冗余 2024-03-11 14:22:39 +08:00
RockYang 316636f83c feat: replace Tools param with Function param for OpenAI chat API 2024-03-11 14:09:19 +08:00
廖彦棋 e3c2fbf82a Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-11 13:52:24 +08:00
廖彦棋 1b6175e598 fix(ui): type 2024-03-11 13:52:22 +08:00
chenzifan 26dc479596 feat: 优化后台UI 2024-03-11 13:51:26 +08:00
chenzifan dca25dbb78 Merge remote-tracking branch 'origin/ui' into ui 2024-03-11 13:46:46 +08:00
廖彦棋 c55275872e refactor(ui): 调整优化 2024-03-11 13:46:08 +08:00
廖彦棋 d4d1fb26f6 feat(ui): 细节优化 2024-03-11 12:02:20 +08:00
廖彦棋 a48cb7bc34 feat(ui): 上传功能补充 2024-03-11 11:41:50 +08:00
廖彦棋 8af6e9290f feat(ui): 登录新增验证码及记住密码功能 2024-03-11 10:49:13 +08:00
廖彦棋 14b277d813 feat(ul): 顶部信息 2024-03-11 09:00:00 +08:00
huangqj 20915038a3 feat(ui):看板图表 样式调整 2024-03-11 08:35:21 +08:00
chenzifan 15723457cb Merge remote-tracking branch 'origin/ui' into ui 2024-03-11 08:01:54 +08:00
RockYang be8a0ec184 opt: remove global keyup event bind 2024-03-10 09:45:59 +08:00
RockYang ec79fc933c opt: remove global keyup event bind 2024-03-10 09:45:59 +08:00
RockYang b02e3aad95 docs: update config.toml 2024-03-10 09:45:59 +08:00
RockYang 3ed5a420a7 docs: update config.toml 2024-03-10 09:45:59 +08:00
RockYang 08eca511ad docs: add database sql file for v3.2.7 2024-03-10 09:45:59 +08:00
RockYang 711e701933 docs: add database sql file for v3.2.7 2024-03-10 09:45:59 +08:00
RockYang c34e911596 feat: allow to view chat message in manager console 2024-03-10 09:45:59 +08:00
RockYang e6bbd83c6b feat: allow to view chat message in manager console 2024-03-10 09:45:59 +08:00
RockYang 8a452c3072 feat: download image which ai generated in dialog and replace the image url 2024-03-10 09:45:59 +08:00
RockYang b99b310306 feat: download image which ai generated in dialog and replace the image url 2024-03-10 09:45:59 +08:00
RockYang 13bfb14107 feat: image-wall page for mobile is ready 2024-03-10 09:45:59 +08:00
RockYang e3c46016e0 feat: image-wall page for mobile is ready 2024-03-10 09:45:59 +08:00
RockYang 4188b0969e feat: allow user config third-party platform openai and mj api key 2024-03-10 09:45:59 +08:00
RockYang eb739fcccb feat: allow user config third-party platform openai and mj api key 2024-03-10 09:45:59 +08:00
RockYang 0c27795a10 feat: added delete file function 2024-03-10 09:45:59 +08:00
RockYang 9a94505f43 feat: added delete file function 2024-03-10 09:45:59 +08:00
RockYang d05693c5c1 fix: verifycation component touch event coordinates misplace in iphone browser 2024-03-10 09:45:59 +08:00
RockYang 7c5c3d8a3c fix: verifycation component touch event coordinates misplace in iphone browser 2024-03-10 09:45:59 +08:00
RockYang c0b2063b38 fix: fix bug for regenerate button did not work 2024-03-10 09:45:59 +08:00
RockYang 7027f9a55b fix: fix bug for regenerate button did not work 2024-03-10 09:45:59 +08:00
RockYang 4d183747b1 fix: Upscale and Variation task overrite each other 2024-03-10 09:45:59 +08:00
RockYang cf3d8af260 fix: Upscale and Variation task overrite each other 2024-03-10 09:45:59 +08:00
RockYang 08fe1b2f75 feat: midjourney mobile page all function is ready 2024-03-10 09:45:59 +08:00
RockYang 4ea7352674 feat: midjourney mobile page all function is ready 2024-03-10 09:45:59 +08:00
RockYang db3e8a267e feat: mobile mj list page is ready 2024-03-10 09:45:59 +08:00
RockYang 6e6885849b feat: mobile mj list page is ready 2024-03-10 09:45:59 +08:00
RockYang 8fc62682c4 feat: add mj image list component for mobile page. fixed bug for html tag escape 2024-03-10 09:45:59 +08:00
RockYang 827f2b9739 feat: add mj image list component for mobile page. fixed bug for html tag escape 2024-03-10 09:45:59 +08:00
RockYang 75031914a3 feat: add functions mj page for mobile 2024-03-10 09:45:59 +08:00
RockYang 54d70623ab feat: add functions mj page for mobile 2024-03-10 09:45:59 +08:00
RockYang a4c9fdd95a opt: add default extension for mj image 2024-03-10 09:45:59 +08:00
RockYang ed819e8c79 opt: add default extension for mj image 2024-03-10 09:45:59 +08:00
RockYang 6a9bfeb5aa feat: mj for mobile page payout is ready 2024-03-10 09:45:59 +08:00
RockYang 94680c4411 feat: mj for mobile page payout is ready 2024-03-10 09:45:59 +08:00
RockYang e654766f60 add docs and github link 2024-03-10 09:45:59 +08:00
RockYang 8bcbb68438 add docs and github link 2024-03-10 09:45:59 +08:00
RockYang 0ef6955f96 opt: enable use cdn url for mj-plus 2024-03-10 09:45:59 +08:00
RockYang b9f31d42f4 opt: enable use cdn url for mj-plus 2024-03-10 09:45:59 +08:00
RockYang b4501557c9 feat: LaTeX parse is ready 2024-03-10 09:45:59 +08:00
RockYang dc2b768c6b feat: LaTeX parse is ready 2024-03-10 09:45:59 +08:00
RockYang a2ed99e6cb feat: add model field for chat_item and and chat_history data table 2024-03-10 09:45:59 +08:00
RockYang 60df732db7 feat: add model field for chat_item and and chat_history data table 2024-03-10 09:45:59 +08:00
RockYang 6bd6bb3885 feat: add err_msg field for mj and sd jobs 2024-03-10 09:45:59 +08:00
RockYang 6d85d128cc feat: add err_msg field for mj and sd jobs 2024-03-10 09:45:59 +08:00
RockYang 399cf65fc9 feat: blend and swap face function for midjourney-plus is ready 2024-03-10 09:45:59 +08:00
RockYang 96785ca037 feat: blend and swap face function for midjourney-plus is ready 2024-03-10 09:45:59 +08:00
RockYang 24906a6df1 feat: add blend and swapface task implements for midjourney 2024-03-10 09:45:59 +08:00
RockYang 7cfc747653 feat: add blend and swapface task implements for midjourney 2024-03-10 09:45:59 +08:00
RockYang d772bbebe6 opt: refactor chat session page for mobile device 2024-03-10 09:45:59 +08:00
RockYang 05f911628a opt: refactor chat session page for mobile device 2024-03-10 09:45:59 +08:00
RockYang 14988853a3 opt: optimize chat list page for mobile 2024-03-10 09:45:59 +08:00
RockYang 18b718a328 opt: optimize chat list page for mobile 2024-03-10 09:45:59 +08:00
RockYang 7b3f16ac9f feat: use vant replace element-plus as mobile UI framework 2024-03-10 09:45:59 +08:00
RockYang 6d9b9bd3f7 feat: use vant replace element-plus as mobile UI framework 2024-03-10 09:45:59 +08:00
RockYang 82b2755c18 feat: add websocket heartbeat message for mj page 2024-03-10 09:45:59 +08:00
RockYang 04e990aeb3 feat: add websocket heartbeat message for mj page 2024-03-10 09:45:59 +08:00
廖彦棋 cbbcd4c571 fix(ui): 细节调整 2024-03-08 17:46:48 +08:00
huangqj 5ad9adeb4a feat(ui):细节调整 2024-03-08 10:24:38 +08:00
廖彦棋 13c9594be0 feat(ui): prettier 2024-03-08 09:59:40 +08:00
廖彦棋 e4f5f78bfa refactor(ui): 登录页重构 2024-03-08 09:45:09 +08:00
廖彦棋 bfa0208692 refactor(ui): 优化 2024-03-08 09:12:39 +08:00
huangqj 64994cdeda feat(ui):路由 2024-03-08 08:35:41 +08:00
廖彦棋 9796a841d9 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-07 18:03:08 +08:00
廖彦棋 404b493d23 feat(ui): 过期跳转登录 2024-03-07 18:03:06 +08:00
huangqj c04f9eb993 feat(ui):apiKey 语言模型 角色管理 产品 2024-03-07 17:58:25 +08:00
廖彦棋 825da42208 feat(ui): 新增系统设置 2024-03-07 17:24:50 +08:00
廖彦棋 721a8f1ecb feat(ui): 对话管理 2024-03-07 15:32:32 +08:00
huangqj 586a174d6c feat(ui):用户 2024-03-07 15:05:01 +08:00
huangqj bec6966542 feat(ui):simpleTable 2024-03-07 14:59:45 +08:00
huangqj b1f4e1d4d8 feat(ui):searchtable 2024-03-07 14:59:16 +08:00
huangqj d7d64e8259 feat(ui):用户 2024-03-07 14:03:55 +08:00
廖彦棋 08b498fccd feat(ui): 函数管理 2024-03-07 11:40:57 +08:00
廖彦棋 ecc5a52232 feat(ui): 新增弹窗及时间格式化 2024-03-07 09:23:45 +08:00
chenzifan 7327b90255 Merge branch 'main' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-07 08:37:54 +08:00
chenzifan 6ce7a9c16f feat: 增加系统用户管理 2024-03-07 08:37:48 +08:00
廖彦棋 eb2658a91c fix(ui): ts类型 2024-03-06 18:20:07 +08:00
RockYang 9f59a18756 feat: update changelogs 2024-03-06 17:58:17 +08:00
廖彦棋 ef06f0da98 feat(ui): 新增登录 2024-03-06 17:54:38 +08:00
RockYang 4dec0e2c69 feat: Mj and sd jobs data loading in pages 2024-03-06 17:31:54 +08:00
RockYang 5f36f1af11 add prompt translating function for mobile midjourney page 2024-03-06 16:22:03 +08:00
huangqj 5a1a596098 feat(ui):simpleTable 2024-03-06 15:33:37 +08:00
廖彦棋 7a25c6e062 feat(ui): 新增请求方法及表格 2024-03-06 13:55:38 +08:00
廖彦棋 a73ed71b9d feat(ui): 管理后台基础配置 2024-03-06 10:23:55 +08:00
廖彦棋 2abbb95ea2 chore(ui): 目录结构调整 2024-03-06 09:32:47 +08:00
廖彦棋 1f80ba9463 feat(ui): 初始化 2024-03-06 09:27:11 +08:00
chenzifan 4327fdac12 refactor: 初始化UI重构 2024-03-06 08:57:46 +08:00
RockYang a6f05a5874 Merge branch 'dev' of gitee.com:blackfox/chatgpt-plus-pro into dev 2024-03-04 08:34:12 +08:00
RockYang 3fb74a1598 feat: add removing order button in admin order list page 2024-03-03 19:27:22 +08:00
RockYang 2ac44cdeb6 fix: fix major bugs for unauthorized access to data 2024-03-03 10:40:32 +08:00
RockYang 4e4dc4cb73 Update README.md 2024-03-01 23:08:05 +08:00
RockYang 855a953010 Update README.md 2024-03-01 23:08:05 +08:00
RockYang 3264fda06e fix: fixed bug image preview im mobile chat session page 2024-02-29 15:41:45 +08:00
RockYang 2c7d472069 feat: add draw same image for midjourney page 2024-02-29 11:44:09 +08:00
RockYang 8182e6797f opt: add logs for mj-plus api error 2024-02-28 15:50:42 +08:00
RockYang 99cb48ce88 opt: replace proxy url for discord image url 2024-02-27 17:45:57 +08:00
RockYang e391bfca80 feat: add change password in with mobile page 2024-02-27 15:36:20 +08:00
RockYang 0181ad3715 feat: add image preview for mobile chat page 2024-02-26 18:11:37 +08:00
RockYang b95dff0751 feat: replace http polling with webscoket notify in sd image page 2024-02-26 15:45:54 +08:00
RockYang 668d4c9c64 chore: replace 'token' with power 2024-02-23 18:11:57 +08:00
RockYang 66c0d1b2f7 docs: update mj-plus api domain 2024-02-23 15:41:02 +08:00
RockYang bef0996375 opt: remove global keyup event bind 2024-02-23 11:43:27 +08:00
RockYang 391a1e4cf0 docs: update config.toml 2024-02-22 17:53:39 +08:00
RockYang 38facf517a docs: add database sql file for v3.2.7 2024-02-22 17:28:22 +08:00
RockYang 5c9b1e8764 feat: allow to view chat message in manager console 2024-02-22 17:16:44 +08:00
RockYang dbcf14b566 feat: download image which ai generated in dialog and replace the image url 2024-02-20 18:38:03 +08:00
RockYang cc59f0c761 feat: image-wall page for mobile is ready 2024-02-20 17:38:18 +08:00
RockYang f263fc067a feat: allow user config third-party platform openai and mj api key 2024-02-20 11:23:55 +08:00
RockYang 6635b88baa feat: added delete file function 2024-02-19 16:43:03 +08:00
RockYang 87ea2a611f fix: verifycation component touch event coordinates misplace in iphone browser 2024-02-19 14:04:50 +08:00
RockYang ea8a2135cd fix: fix bug for regenerate button did not work 2024-02-19 11:22:42 +08:00
RockYang 76d86395ef fix: Upscale and Variation task overrite each other 2024-02-16 18:08:29 +08:00
RockYang e268870636 feat: midjourney mobile page all function is ready 2024-02-16 15:55:04 +08:00
RockYang 49e9f41ef2 feat: mobile mj list page is ready 2024-02-15 18:11:22 +08:00
RockYang 68cda968a1 feat: add mj image list component for mobile page. fixed bug for html tag escape 2024-02-15 11:39:04 +08:00
RockYang f6826fcefc feat: add functions mj page for mobile 2024-01-31 07:24:35 +08:00
RockYang 56a91db7e7 opt: add default extension for mj image 2024-01-30 21:46:17 +08:00
RockYang 8f4d94f046 feat: mj for mobile page payout is ready 2024-01-30 18:34:01 +08:00
RockYang 9446bf5f98 add docs and github link 2024-01-30 16:18:27 +08:00
RockYang 23e353b18b opt: enable use cdn url for mj-plus 2024-01-28 21:56:25 +08:00
RockYang 46daaa4ba1 feat: LaTeX parse is ready 2024-01-26 18:04:53 +08:00
RockYang f66d59531d feat: add model field for chat_item and and chat_history data table 2024-01-26 16:54:00 +08:00
RockYang b76cd1d5ae feat: add err_msg field for mj and sd jobs 2024-01-26 14:50:36 +08:00
RockYang 55ba0d08a5 feat: blend and swap face function for midjourney-plus is ready 2024-01-26 11:57:08 +08:00
RockYang cc78780a8e feat: add blend and swapface task implements for midjourney 2024-01-25 18:50:24 +08:00
RockYang 4a00809061 opt: refactor chat session page for mobile device 2024-01-25 14:07:10 +08:00
RockYang cd760bbd84 opt: optimize chat list page for mobile 2024-01-24 18:23:24 +08:00
RockYang 84bdc6be7f feat: use vant replace element-plus as mobile UI framework 2024-01-24 17:34:30 +08:00
RockYang 8dd6bf8933 feat: add websocket heartbeat message for mj page 2024-01-24 09:33:04 +08:00
RockYang 08f170d217 add v3.2.6 database sql file 2024-01-23 18:00:49 +08:00
RockYang 20da6a1c20 doc: update config sample file 2024-01-23 17:56:22 +08:00
RockYang 8586931630 fix: auto fill apiURL when platform changed for ApiKey add page 2024-01-23 17:30:54 +08:00
RockYang 667fc79a6f feat: merge sms branch,add DuanXinBao sms service implemetation 2024-01-23 16:16:47 +08:00
RockYang ac7430dded opt: add heartbeat message for websocket connects 2024-01-22 18:42:51 +08:00
whale_fall 9c73e16560 feat: 添加支持多个短信服务商支持 添加短信宝服务商支持,同时添加配置示例 2024-01-22 16:38:44 +08:00
RockYang c605e222a7 fix: fix bug for wechat transfer message parse failed 2024-01-22 16:10:08 +08:00
RockYang 578f26a738 feat: HuPiPay order check function is ready 2024-01-22 15:17:26 +08:00
RockYang 24218e7570 opt: verify the order in notify callback 2024-01-22 13:58:25 +08:00
RockYang 16f946550e chore: print error detail when call http api failed with mj 2024-01-21 22:30:24 +08:00
RockYang 9960a31485 opt: add image upload support for md-editor-3 2024-01-19 18:43:13 +08:00
RockYang 437632f859 fixed conflicts 2024-01-19 18:21:49 +08:00
RockYang 98f0f442eb feat: system notice function is ready 2024-01-19 18:19:51 +08:00
RockYang 3b999dc5e2 feat: system notice function is ready 2024-01-19 18:18:10 +08:00
RockYang 20943ad3d3 fix: fixed bug for img_call increased when upscale task run failed 2024-01-19 17:10:52 +08:00
RockYang 3b1544b5e4 feat: add system config item for wechat qrcode 2024-01-19 16:58:13 +08:00
RockYang b317d597ac chore: optimize variable name 2024-01-19 11:26:22 +08:00
RockYang 3bb19811e7 Merge branch 'main' into dev 2024-01-19 10:09:18 +08:00
RockYang 065a01de48 Merge branch 'main' of gitee.com:blackfox/chatgpt-plus 2024-01-19 10:09:01 +08:00
RockYang 09fb2f6557 !4 添加支持阿里旗下的大模型 通义千问对话
Merge pull request !4 from 鲸落/qwen
2024-01-19 02:06:17 +00:00
whale_fall 9ca97bf4bc feat: 添加支持阿里的通义千问对话 2024-01-19 09:52:16 +08:00
RockYang 0c59503edb feat: add image publish function, ONLY published image show in image wall page 2024-01-19 06:52:23 +08:00
RockYang 6fa08861f8 update change log 2024-01-18 18:11:25 +08:00
RockYang 72b95151a1 feat: add system config disable user registeration 2024-01-18 17:24:02 +08:00
RockYang 378e6ec9af opt: compatible wechat old message format for parsing wechat transfer message 2024-01-18 16:58:20 +08:00
RockYang 27b68c1174 opt: optimize order query alg, reduce polling times 2024-01-18 09:39:36 +08:00
RockYang 1ae8eabe99 update config 2024-01-16 15:24:06 +08:00
RockYang 72726c1cf9 docs: update change log 2024-01-16 15:05:54 +08:00
RockYang 6203d9a150 docs: update config files 2024-01-16 14:38:18 +08:00
RockYang 861f11af66 opt: optimize markdown image parser, identify image and blockquote tags 2024-01-16 10:13:00 +08:00
RockYang 39fdfae541 feat: attachments manage function is ready 2024-01-15 18:48:01 +08:00
RockYang 9f57fb1421 feat: gpt-4-gizmo-g-* model is supported 2024-01-15 15:03:05 +08:00
RockYang 059f57db2d feat: gpt-4-all model is ready 2024-01-15 14:07:24 +08:00
RockYang 13c8be8d06 opt: optimize vip recharge logic 2024-01-15 11:01:57 +08:00
RockYang e0dbfb0488 Merge branch 'dev' 2024-01-15 10:29:01 +08:00
RockYang 722ed128a1 opt: optimize vip recharge logic 2024-01-15 10:28:46 +08:00
RockYang cc628821e6 feat: add asynchronously pull midjourney task progress in case the synchronization callback is fails 2024-01-12 18:24:28 +08:00
RockYang 857af61af1 feat: midjourney plus service is ready 2024-01-11 18:16:48 +08:00
RockYang f6fc484cb3 feat: update video tutorial 2024-01-10 08:50:13 +08:00
RockYang ba07946f1e feat: update video tutorial 2024-01-10 08:48:05 +08:00
RockYang 600f668c63 remove api key for hupipay 2024-01-09 17:16:27 +08:00
RockYang 8a8990b12b fix: fixed bug for gorm insert record failed and Error is not nil 2024-01-08 18:10:32 +08:00
RockYang fe85c1bbb9 feat: change mobile field to username 2024-01-08 17:34:09 +08:00
RockYang a89ff72308 add changelog 2024-01-08 12:01:58 +08:00
RockYang a99e58184b chore: do not close pop window when click model 2024-01-08 11:01:19 +08:00
RockYang d981bc8fd0 fix: function call 兼容中转 API 2024-01-07 22:32:59 +08:00
RockYang ff22480d89 opt: add support to disable code verify 2024-01-07 17:31:26 +08:00
RockYang 8ec85e2829 feat: payjs payment channel is ready 2024-01-07 14:36:02 +08:00
RockYang 706aacd8e6 fix: add user failed in admin user list page 2024-01-07 10:49:36 +08:00
RockYang 803bc2d9fc release v3.2.4 2024-01-06 21:09:19 +08:00
RockYang 7cc67cf8f0 chore: remove useless system config items 2024-01-06 17:38:55 +08:00
RockYang 9909c3a0ed chore: error recover is enable ONLY in debug mode 2024-01-06 17:16:02 +08:00
RockYang 60a3751839 feat: payjs service is ready 2024-01-06 15:53:30 +08:00
RockYang ce87e0c40b chore: rename bind username api 2024-01-05 18:21:47 +08:00
RockYang 71cd548c00 feat: email registration function is ready 2024-01-05 18:17:11 +08:00
RockYang 1ddd05302b feat: update api key last_use_time after dalle3 call 2024-01-04 18:15:00 +08:00
RockYang ea3301c75c feat: support dall-e3 api mirrors, add name field for ApiKey 2024-01-04 16:29:57 +08:00
RockYang 4b1c4f7ccc feat: refactor LLM api request code, get API URL from ApiKey object 2024-01-04 14:51:33 +08:00
RockYang 21f2622a4b feat: api key manage page funciton is ready 2024-01-04 10:48:04 +08:00
RockYang 79f6a43019 fix: fixed bug for concurrency risk for getting token for chat histroy with issue #92 2024-01-04 09:03:19 +08:00
RockYang 3d75093b2c fix: add unique key for MidJourney task_id 2024-01-03 18:06:10 +08:00
RockYang 023dc89c3e feat: show notice in chat page 2024-01-03 15:19:24 +08:00
RockYang b5641be30d feat: fixed bug for wechat bot to parse transactions. enable user to exchange reward with img_calls 2024-01-03 11:15:54 +08:00
RockYang ff4515bbf1 fix: fixed chat export page styles 2024-01-02 11:32:36 +08:00
RockYang 5dc0fe05af fix: fixed for img_call repeated reductions 2024-01-01 18:54:48 +08:00
RockYang 25bba912f6 feat: add switch for enable|disable chat role 2023-12-29 17:51:56 +08:00
RockYang cb6e323596 feat: add nickname field for user 2023-12-29 17:39:37 +08:00
RockYang cf4b04e047 feat: add authorization for local function call 2023-12-29 17:21:29 +08:00
RockYang 243b5be31c update changelog 2023-12-29 11:53:37 +08:00
RockYang c3d753cf38 feat: add router for function manager 2023-12-29 11:22:26 +08:00
RockYang 70c46d098b fix: restore user img_calls quota when image task run failed 2023-12-29 10:41:29 +08:00
RockYang 816e4a096e merge pull request #72 2023-12-29 10:09:37 +08:00
RockYang 1d1e819817 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-12-29 09:39:52 +08:00
RockYang 342daf1be8 Merge pull request #72 from Unclesimonlau/main
重新设计了移动端web页面,新增了移动端CSS,增加移动端SD绘图页面
2023-12-29 09:39:33 +08:00
RockYang f6bb9b582a merge pull request #71 2023-12-29 09:31:25 +08:00
RockYang aad62bc976 Merge pull request #71 from JingHong0202/main
fix: Azure Api request failure after changing the API-version parameter
2023-12-29 09:27:23 +08:00
RockYang 38a49a7f37 chore: remove dead code 2023-12-29 09:02:55 +08:00
RockYang f603bf6be7 feat: function manager refactor is ready 2023-12-28 18:14:38 +08:00
JingHong 6cc05e9e27 Merge branch 'yangjian102621:main' into main 2023-12-27 23:33:46 +08:00
UncleSimonlau c00e67542f Merge branch 'main' of https://github.com/Unclesimonlau/chatgpt-plus 2023-12-26 14:19:25 +08:00
RockYang 22660362a3 fix: fixed bug #70, XunFei 1.5 url version map error 2023-12-26 14:19:00 +08:00
RockYang b1ab9975b7 fix: fixed bug #70, XunFei 1.5 url version map error 2023-12-25 08:54:17 +08:00
RockYang 7ca89d8b54 feat: function CRUD operation is ready 2023-12-24 22:12:12 +08:00
jinghong0202 998b07ab8c fix: Azure Api 更换api-version参数后请求失败的问题 2023-12-24 08:36:34 +01:00
RockYang 4936896ff7 feat: function add for admin page is ready 2023-12-23 22:30:27 +08:00
RockYang 18b7484c5b feat: support CDN reverse proxy for MidJourney and OpenAI API 2023-12-22 17:25:31 +08:00
RockYang 6754c8e85e feat: add function list page in admin console 2023-12-21 18:06:09 +08:00
RockYang 57243ff010 opt: optimize image compress alg, add cache control for image 2023-12-21 15:00:46 +08:00
RockYang 7c4dfe96ee feat: add funcitons manger page 2023-12-21 08:58:24 +08:00
RockYang bf19120c27 feat: auto translate image creating prompt 2023-12-19 18:54:19 +08:00
RockYang 3d37087916 fix: fixed bug for HuPiPay qrcode generation. set field 'openid' of result struct to Any data type 2023-12-19 11:31:57 +08:00
RockYang 6949f1328c chore: update copyright information 2023-12-18 18:19:41 +08:00
RockYang b63090c20f feat: add remove action to remove task and images for MJ and SD task list page 2023-12-18 17:44:52 +08:00
RockYang b15ebda948 opt: merge RAG branch 2023-12-18 16:41:40 +08:00
RockYang 252eef2e5e opt: make sure the Upscale and Variation task is assign to the same mj service with Image task 2023-12-18 16:34:33 +08:00
RockYang 3bea8f9706 fix: fixed bug for mj service pool config pointer 2023-12-15 22:52:57 +08:00
RockYang dfe808bee7 feat: add img_calls field for recharge products 2023-12-15 16:56:56 +08:00
RockYang 58f3dd5336 chore: update default config.toml 2023-12-15 11:23:13 +08:00
RockYang b00f16648f opt: limit the image display size in reply component 2023-12-15 10:48:13 +08:00
RockYang fc66c31e89 docs: update changelog 2023-12-15 09:04:02 +08:00
RockYang a26c849532 docs: add sql file 2023-12-14 17:05:51 +08:00
RockYang d2991e60b6 refactor: refactor stable diffusion service, add service pool support 2023-12-14 16:48:54 +08:00
RockYang 10ba1430f9 chore: add sub dir support for OSS 2023-12-13 17:02:49 +08:00
RockYang 6d71f24f75 refactor: add midjourney pool implementation, add translate prompt for mj drawing 2023-12-13 16:38:27 +08:00
RockYang 8f4d20e411 fix: fixed bug for aliyun OSS img url 2023-12-13 09:49:55 +08:00
RockYang cf758d773e refactor mj service, add mj service pool support 2023-12-12 18:33:24 +08:00
RockYang c012f0c4c5 opt: remove default value for stable-diffusion page 2023-12-12 09:59:20 +08:00
RockYang 9e85900057 docs: update readme file 2023-12-12 09:52:28 +08:00
RockYang 68e3b787f9 docs: update build config.toml 2023-12-12 07:25:36 +08:00
RockYang 6706704ad6 docs: update changelog file 2023-12-11 17:17:17 +08:00
RockYang 1067fa015e add translate api for midjourney 2023-12-11 17:01:02 +08:00
RockYang 411335ebcb feat: add prompt translate handler 2023-12-11 06:56:00 +08:00
RockYang f19c15491e feat: add system config item for dall e3 generate image num 2023-12-10 17:13:25 +08:00
RockYang 0da81a4d10 chore: change default params for stable diffusion 2023-12-10 14:45:22 +08:00
RockYang 049839a5c7 feat: add HuPiPay payment support 2023-12-08 19:43:13 +08:00
RockYang a901927844 feat: add image generation API URL in chat configurations 2023-12-07 16:31:32 +08:00
RockYang 07ab0bc5a1 docs: add arm64 build script 2023-12-07 15:44:20 +08:00
RockYang a6025e6fab fix: fixed bug for prompt code format, prevent xss attacks 2023-12-07 14:02:13 +08:00
RockYang e841a61bf0 opt: save chat ID when the chat websocket disconnect 2023-12-07 11:07:08 +08:00
RockYang 2f1c2110c6 feat: adjust task list component styles 2023-12-06 19:05:51 +08:00
RockYang 8e573eea4c Merge branch 'pr_3' into dev 2023-12-06 18:54:45 +08:00
RockYang 149765764e feat: refactor midjourney image creating page 2023-12-06 18:54:30 +08:00
RockYang 45d6444c7e docs: update readme file 2023-12-06 14:44:06 +08:00
liyuwanglan aa65f49190 修改 2023-12-05 11:08:03 +08:00
RockYang 1bda41ff67 docs: update comments 2023-11-30 17:35:56 +08:00
RockYang 5a799139cd fix: fixed bug for upload image failed 2023-11-29 17:46:46 +08:00
RockYang eb36d0742a opt: create new chat session when change role or model, fix bug for mobile no validate 2023-11-29 17:36:27 +08:00
RockYang d8a9123852 docs: update change logs 2023-11-28 15:33:30 +08:00
RockYang 19031c2197 docs: 添加一键部署脚本 2023-11-28 15:25:51 +08:00
RockYang 7a2ffdf39c opt: 缩略图生成算法 2023-11-28 14:50:19 +08:00
RockYang ce8fa79206 feat: 为大图片生成缩略图,加快前端图片加载速度 2023-11-28 12:04:02 +08:00
RockYang b05c63549d fix: fix bug for oss image domain 2023-11-28 07:27:18 +08:00
RockYang 9f98660423 docs: update build script 2023-11-27 18:24:52 +08:00
RockYang 7b0fa862d2 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-11-27 12:03:23 +08:00
RockYang bfe2d4d573 feat: merge mysql and redis docker service to docker-compose.yaml file 2023-11-27 10:56:18 +08:00
RockYang 26950c5673 Merge pull request #55 from openjst/main
修改code显示颜色样式
2023-11-27 10:27:13 +08:00
RockYang 86788362a5 docs: make a full docker-compose.yaml 2023-11-27 07:21:37 +08:00
openjst deedc5fb2e 修改code显示颜色样式 2023-11-26 23:26:08 +08:00
RockYang f4fbe67db9 feat: implements image function replace Mj with DALL-E-3 2023-11-26 20:37:48 +08:00
RockYang 599ce0eade feat: add type field for api key 2023-11-24 18:05:59 +08:00
RockYang 11f3ab8dc7 feat: add support for registing use force use invite code 2023-11-24 12:02:28 +08:00
RockYang 75b4a6dd46 opt: optimize image preview for MidJourney image list page, only preview current image not for all images 2023-11-23 17:55:12 +08:00
RockYang 86bbea337d opt: 增加中间件自动对HTTP请求的参数去掉首尾空格 2023-11-23 17:50:55 +08:00
RockYang e63a30064b opt: optimize styles for invitation page 2023-11-23 17:40:15 +08:00
RockYang 222b1ddbd9 feat: add invitation and promotion functions 2023-11-23 16:30:15 +08:00
RockYang a88dd88a07 docs: add database sql file for v3.1.9 2023-11-23 09:58:01 +08:00
RockYang a47c01fd41 docs: update change log 2023-11-23 09:46:49 +08:00
RockYang 35e385f7a7 docs: update change log and readme file 2023-11-23 09:14:42 +08:00
RockYang 191e3b7d2c feat: 支持讯飞大模型 v3.0 2023-11-23 07:11:13 +08:00
RockYang 13fbbc190a feat: reset password function is ready 2023-11-22 18:00:45 +08:00
RockYang 61c6a6e7f3 feat: add copy code btn in chat page, fixed bug for code wrap in model of ChatGLM and XunFei 2023-11-22 17:00:43 +08:00
RockYang 911f218f0b feat: add feekback orcode in chat page 2023-11-22 12:05:58 +08:00
RockYang af41350eb9 fix: fixed bug for enable user vip in admin console not work 2023-11-22 11:30:58 +08:00
RockYang 8f63e348b3 option: replace leveldb with redis in storing message code 2023-11-22 10:57:24 +08:00
RockYang 8901cf8e81 Merge pull request #52 from KunMingStar/fix_migrate
fix: remove dead code
2023-11-22 08:23:01 +08:00
tongkunming e11736e1d2 fix: remove dead code 2023-11-21 22:18:53 +08:00
RockYang c062fba69c chore: remove dead code 2023-11-21 19:04:53 +08:00
RockYang 4628095fe7 feat: add wechat id card for closing register 2023-11-20 16:38:04 +08:00
RockYang 787ed9bc0f docs: update docs installation docs url 2023-11-17 18:42:13 +08:00
RockYang daaff8974e docs: update readme and config sample files 2023-11-16 17:20:38 +08:00
RockYang 4fcbd511c0 fix: fix bug with missing chat context 2023-11-15 18:20:34 +08:00
RockYang 64d6bff6d9 fix: unbind event for login page when the component is unmount 2023-11-14 18:19:40 +08:00
RockYang 998cbace92 feat: add link on logo 2023-11-14 13:50:44 +08:00
RockYang 2204038951 fix: fix member page flex styles 2023-11-14 11:21:16 +08:00
RockYang 998f67ac5d fix: deducating the user's img call quota after stable diffusion callback 2023-11-14 08:59:39 +08:00
RockYang be981a2c9b feat: 更改 MJ 和 SD 菜单图标 2023-11-13 11:27:42 +08:00
RockYang da3cf0d4a3 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-11-13 10:11:45 +08:00
RockYang 7124b9a36c feat: 更改 MJ 和 SD 菜单图标 2023-11-13 10:11:21 +08:00
RockYang 712a6b6c39 chore: change xxl-job name 2023-11-12 15:39:12 +08:00
RockYang ed0a1c57d6 opt: 优化 ItemList 组件样式,调整支付页面布局 2023-11-11 22:11:04 +08:00
RockYang 7858228505 docs: update change log file 2023-11-11 12:58:57 +08:00
RockYang 2525a22d78 fix: fix bug for issue 49, stable diffusion service not decrease img_calls 2023-11-11 11:00:26 +08:00
RockYang 7b4ef8fc31 opt: 优化 ItemList 组件样式 2023-11-11 10:53:33 +08:00
RockYang ce14cd02e4 feat: add order list compponent 2023-11-10 18:06:32 +08:00
RockYang 05f501af52 Merge branch 'alipay' 2023-11-10 16:51:00 +08:00
RockYang e94fa4844f docs: export database, update readme doc, remove useless configs for alipay 2023-11-10 16:49:07 +08:00
RockYang e0965aae5e feat: add switch to disable xxl-job service, update readme 2023-11-10 15:22:35 +08:00
RockYang 414c1de963 feat: 增加订单倒计时组件,自动清理过期未支付订单 2023-11-10 14:39:27 +08:00
RockYang b589102be8 feat: add system configration switch option for order pay service, support sandbox env for alipay 2023-11-09 18:28:56 +08:00
RockYang df367e0d47 feat: check if the user's chat quota is gt than current chat model required before starting a conversation 2023-11-09 16:56:44 +08:00
RockYang 680219ebcb Merge pull request #47 from KunMingStar/fix/sendMessage
fix: 添加messages为空校验
2023-11-09 08:21:24 +08:00
RockYang ef87487f60 fix: fix bug for token expired with QiNiu oss upload file 2023-11-08 21:14:09 +08:00
RockYang 8bafe72434 feat: adjust layout for user profile page 2023-11-08 18:33:26 +08:00
RockYang 76dcc69e44 feat: order payment function is ready 2023-11-08 17:48:07 +08:00
tongkunming 682ef22194 fix: 添加messages为空校验 2023-11-08 16:51:36 +08:00
RockYang ee8dd41605 feat: update stable diffusion api version 2023-11-08 14:54:10 +08:00
RockYang d0b8d666e4 feat: finish payment page layout 2023-11-07 18:10:28 +08:00
RockYang 4a81826d19 feat: adjust table styles for markdown 2023-11-07 12:02:16 +08:00
RockYang 848a10c3a8 feat: integrated Alipay payment module 2023-11-06 17:55:46 +08:00
RockYang a4baa29678 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-11-06 15:04:11 +08:00
RockYang a9b5280691 opt:update mj api version, wrapper chat api error message as a constant 2023-11-06 15:03:56 +08:00
RockYang 264706210b chore: remove invalid filepath for windows 2023-11-02 19:16:02 +08:00
RockYang 7677ae254f feat: different AI model consuming different amounts of use_calls 2023-10-26 14:38:06 +08:00
RockYang 7ccb4c5f06 docs: add database full backup 2023-10-26 14:00:06 +08:00
RockYang 01205af018 feat: 支持文心4.0模型,不同的用户可以订阅不同的AI模型 2023-10-26 13:41:49 +08:00
RockYang 5b5150e6d4 feat: add system configration item to close rewarding function 2023-10-19 11:19:29 +08:00
RockYang 9d8e3f5049 docs: add alipay reward qrcode 2023-10-17 17:58:03 +08:00
RockYang 99ede83bdc chore: set app list page as the index page 2023-10-16 14:20:44 +08:00
RockYang 61488e840d update changelog file 2023-10-16 13:40:44 +08:00
RockYang a80d01209c opt: adjust styles for ItemList component, cut string for chat role's hello message 2023-10-16 10:46:10 +08:00
RockYang d2a8d655c8 feat: finish adding chat role to user function 2023-10-16 06:56:42 +08:00
RockYang 80e2b34abc opt: adjust ItemList component styles 2023-10-15 15:47:42 +08:00
RockYang fb3d43d2d5 opt: optimize imgae loading for image wall page, loading thumb image replace source image 2023-10-13 23:07:41 +08:00
RockYang ebdec4fa44 feat: create chat app list page, build the layout of page 2023-10-13 18:05:40 +08:00
RockYang ead8dbbaa5 feat: auto login when register successfully 2023-10-13 16:27:40 +08:00
RockYang 95615a5501 docs: update images for readme 2023-10-13 15:55:30 +08:00
RockYang 75487b1f58 Merge branch 'main' into image-wall 2023-10-13 15:39:34 +08:00
RockYang c7daaa00cd feat: image wall stable diffusion image list component is ready 2023-10-13 15:16:40 +08:00
RockYang 606f24e981 feat: optimize the midjourney image list styles 2023-10-13 11:14:39 +08:00
RockYang c83d9287a7 docs: update readme, database files 2023-10-13 09:59:05 +08:00
RockYang fa8092b2cd docs: update readme file 2023-10-13 06:49:21 +08:00
RockYang b2b07a2be6 docs: update change log 2023-10-13 06:39:44 +08:00
RockYang a21628e350 style: fix style for update chat title input element 2023-10-13 06:29:25 +08:00
RockYang 9fcd686fda feat: image wall page is ready 2023-10-12 18:09:50 +08:00
RockYang 1759fd4cf9 opt: close websocket connection when finish a chat call for XunFei API 2023-10-12 10:02:12 +08:00
RockYang 12e7837602 feat: XunFei ai mode api implements is ready 2023-10-11 18:17:03 +08:00
RockYang ff349f2edf feat: adjust package struct, put chat code the seperate 'chatimpl' package, fix bug: baidu api chat context number must be even number 2023-10-11 15:46:40 +08:00
RockYang 088d8ca207 feat: add system configration for enable/disable funciton in chat session 2023-10-11 14:35:47 +08:00
RockYang c37902d3a4 finish baidu ai model api implementation 2023-10-11 14:21:16 +08:00
RockYang da38684cdd add baidu ai model api configrations 2023-10-10 18:19:56 +08:00
RockYang cb2cb72333 docs: update readme 2023-10-09 12:13:21 +08:00
RockYang d53c8f9830 docs: add 3.1.4 release version change logs 2023-10-09 12:05:51 +08:00
RockYang c8136d4231 doc: update database sql file 2023-10-08 20:44:55 +08:00
RockYang e4b8380530 docs: udpate readme and config.toml template 2023-10-08 18:09:46 +08:00
RockYang fbd599194c feat: add system config item for reward image, add app config item to use custom text2img param json file 2023-10-08 17:48:50 +08:00
RockYang 0270ec26fb feat: add configuration handler for AliYun SMS signature and template ID 2023-10-08 12:01:09 +08:00
RockYang 79e342be57 optimze the style for stable diffusion image dialog 2023-10-08 11:39:55 +08:00
RockYang c89f23b018 feat: stable diffusion page function is ready 2023-10-06 22:25:37 +08:00
RockYang f971ec5d34 fix: fixed bug for generating the upload file path 2023-10-05 18:09:42 +08:00
RockYang c5776ce41f feat: stable diffusion page is ready 2023-09-28 18:09:45 +08:00
RockYang 75c5ebbffa feat: migrate the chatgpt-plus-ext project code to this project 2023-09-27 18:14:07 +08:00
RockYang d51a724ade feat: add implements for stable diffusion service 2023-09-26 18:16:51 +08:00
RockYang c1143d7a6d fix: fixed bug for register error with parse args 2023-09-26 09:30:18 +08:00
RockYang f3697431a4 fix: fixed bug for add user in admin console page that calls and img_calls parameter not work 2023-09-22 18:15:32 +08:00
RockYang e3d6e5f420 feat: the upload handler for AliYun OSS is ready 2023-09-22 09:56:45 +08:00
RockYang af6f7a7146 docs: adjust ui styles for mj image page 2023-09-20 14:33:15 +08:00
RockYang c18d95e89a docs: update docker-compose.yaml, change image version to v3.1.3 2023-09-20 11:42:23 +08:00
RockYang fa42d03a65 some page ui optimization, add release v3.1.3 2023-09-20 11:39:11 +08:00
RockYang 22f7de2c09 fix socket connect for mj task notify 2023-09-20 06:59:30 +08:00
RockYang b4b9df81cb opt: add sessionId for mj task 2023-09-19 18:15:08 +08:00
RockYang 2a71c2b0e7 feat: mj advance drawing page function is ready, use better task scheduling argorithm 2023-09-17 18:03:45 +08:00
RockYang c37b3d3df5 feat: add task type params for add new mj task 2023-09-16 14:01:53 +08:00
RockYang ae135b89d6 feat: adjust UI for task list page 2023-09-15 18:16:59 +08:00
RockYang 4112426848 feat: midjourney page task and image list component is ready 2023-09-15 17:40:39 +08:00
RockYang 638b399b31 feat: midjourney page parameter ui is ready 2023-09-14 18:28:24 +08:00
RockYang 8a5713ef7c feat: finish mj model layout 2023-09-14 06:55:03 +08:00
RockYang 382d7bfaa1 feat: add ImageMj page view 2023-09-13 18:04:31 +08:00
RockYang 3daf2f7738 feat: optimize mj notidy api, use job queue to send ai drawing request 2023-09-13 15:50:00 +08:00
RockYang 944d35971d feat: chat chrawing function is refactored 2023-09-13 06:57:25 +08:00
RockYang 9ce8af0b82 feat: refactor MidJourney service for conpatible drawing in chat and draw in app 2023-09-12 18:01:24 +08:00
RockYang d825b9ec26 fix: fixed bug for jwt token expire caculation 2023-09-12 10:49:55 +08:00
RockYang 9afe4aea4b feat: new WebUI for the main page, add MJ and SD drawing function pages 2023-09-11 16:22:11 +08:00
RockYang 3cc8c3284a opt: optimize the styles of chat page; caculate all tokens of context as chat history's token 2023-09-11 13:34:20 +08:00
RockYang e2c18c4e1e chore: update gpt avatar 2023-09-10 12:02:37 +08:00
RockYang 81fe768f6a opt: refactor the web page's router and layout 2023-09-08 22:14:58 +08:00
RockYang 8923779ab0 opt: return error when download mj image failed 2023-09-08 21:02:21 +08:00
RockYang 583218a045 opt: add lock for mj task callback 2023-09-08 18:12:18 +08:00
RockYang 153b2bfa53 opt: add lock for mj task callback 2023-09-08 17:23:32 +08:00
RockYang 0a9b325360 opt: Only create actived upload service 2023-09-08 06:46:24 +08:00
RockYang af9e6f2c46 update desktop package deps 2023-09-07 18:05:13 +08:00
RockYang 0e31726fd3 fix: remove username and nickname field for updating user info 2023-09-07 15:41:01 +08:00
RockYang 590d9c2e61 Update README.md 2023-09-07 15:23:38 +08:00
RockYang c12fe9f25e adjust home page styles 2023-09-07 15:12:43 +08:00
RockYang 9bd83f3acf use absolute path for all routes 2023-09-07 10:27:18 +08:00
RockYang aada15fc41 feat: add linux package configs for desktop 2023-09-07 09:45:11 +08:00
RockYang 6a2d383be6 feat: extract css to a single file, add chat_id path variable support 2023-09-07 09:29:16 +08:00
RockYang 545c8fa482 feat: use eletron to build desktop version for chatgpt-plus 2023-09-06 18:19:27 +08:00
RockYang e3d18643b8 opt: clear space in the reward transfer number 2023-09-06 17:09:55 +08:00
RockYang 2f318bbe9f feat: add QiNiu OSS storage implements 2023-09-06 14:37:13 +08:00
RockYang fac7eb61b3 remove username search for console user list page 2023-09-05 17:52:46 +08:00
RockYang 4450d03a54 update sql file, remove truncate table sql 2023-09-05 17:05:33 +08:00
RockYang e4e7868b30 fix: 采用弹窗的方式显示验证码,解决验证码在低分辨率下被掩盖的Bug 2023-09-05 16:47:40 +08:00
RockYang 3d7fa680cb docs: update change log and create a new release version 2023-09-05 14:28:30 +08:00
RockYang c38035e25e fix: replace session handler with jwt authorization 2023-09-05 11:47:03 +08:00
RockYang 8d4fdaf902 refactor: refactor mobile pages for the chat model updating 2023-09-04 18:15:56 +08:00
RockYang 2820adad53 feat: allow user to set custom api keys for different platforms 2023-09-04 17:34:29 +08:00
RockYang f7a427d2c0 refactor: refactor chat model, replace mode value with mode id. refactored system config module, add seperate configration for every chat model 2023-09-04 16:32:20 +08:00
RockYang 59ed8c9660 refactor: refactor chat_handler, add support for Azure and ChatGLM 2023-09-04 06:43:15 +08:00
RockYang 71562ab0e5 docs: add log dir to docker 2023-08-24 08:55:11 +08:00
RockYang 278ddf037f docs: update docker-compose file 2023-08-23 06:56:04 +08:00
RockYang ac3151af92 chore: update bin file name 2023-08-22 22:18:07 +08:00
RockYang 1189ad7862 feat: change order by for login log and reward records 2023-08-22 22:04:46 +08:00
RockYang 88c13e7b9a opt: optimize element ui dialog styles, make it to the center of screen 2023-08-22 08:57:14 +08:00
RockYang 596baac62e opt: upgrade element-plus to 2.3.0, optimize the styles for for element dialog 2023-08-22 07:14:13 +08:00
RockYang 5a8503ec14 chore: remove debug codes 2023-08-21 10:55:49 +08:00
RockYang e44f95724d doc: update readme docs 2023-08-21 06:57:04 +08:00
RockYang dec0d0dea7 feat: delete the old avatar image file when update the user profile 2023-08-21 06:27:30 +08:00
RockYang 46f96e94ec feat: add oss service factory implements, add support for setting custom upload handler, localstorage and minio oss 2023-08-20 22:29:08 +08:00
RockYang 89b30bcf58 opt: optimize code for remove chat item 2023-08-20 19:06:18 +08:00
RockYang 473d8f5f85 feat: add minio service implementation, download midjourney image to local storage 2023-08-20 16:17:42 +08:00
RockYang 08d8d65599 opt:Optimize the algorithm to check whether Midjourney images can continue to variation 2023-08-17 14:20:16 +08:00
RockYang 2964c1d82e docs: update demo url 2023-08-17 07:18:44 +08:00
RockYang 75ab8552d9 chore: show img_calls num for user profile page 2023-08-17 07:09:53 +08:00
RockYang b78d5b3d03 docs: update docs 2023-08-17 06:55:11 +08:00
RockYang 064081c771 feat: add authorization for MidJourney function calls 2023-08-16 23:16:44 +08:00
RockYang 3525d35d15 chore: update database sql file 2023-08-15 22:53:32 +08:00
RockYang 9d9ee7c585 opt: unset timeout for websocket connection 2023-08-15 18:29:53 +08:00
RockYang e65c32c4c7 feat: midjourney image variation function is ready 2023-08-15 17:31:02 +08:00
RockYang 113769791f feat: midjourney image upscale function is ready 2023-08-15 15:28:40 +08:00
RockYang 5d2a1d21d5 feat: midjourney drawing image function is ready 2023-08-14 17:59:21 +08:00
RockYang 6e40f92aaf feat: add midjourney message receive handler 2023-08-14 07:09:52 +08:00
RockYang 302cb8a5be feat: add midjouney api implements, optimize function calls 2023-08-11 18:46:56 +08:00
RockYang 0b27890484 feat: remove wechat bot, replace with api callback for receive wechat payment transactions 2023-08-10 17:24:23 +08:00
RockYang 5db247e632 feat: add samples and function introduce in welcome page 2023-08-08 14:19:09 +08:00
RockYang c1155a4338 docs: add dashboard image 2023-08-07 20:07:05 +08:00
RockYang da806b9492 feat: enabled user to set default GPT model for chat 2023-08-04 14:25:54 +08:00
RockYang 3d6d46b30d feat: chat export function is ready 2023-08-04 12:08:07 +08:00
RockYang 57c69738ba feat: add chat export button 2023-08-03 18:24:30 +08:00
RockYang 0c8157dbc0 feat: add rewards statistic in dashboard page 2023-08-03 10:19:37 +08:00
RockYang d7b278f2f7 opt: add enabled_msg_service config var to system config database 2023-08-03 10:04:45 +08:00
RockYang 18d74f1057 opt: optimize build script, auto push to aliyun docker hub 2023-08-02 18:09:19 +08:00
RockYang e04856f794 opt: optimize the layout for regiseter page. add function to disable registration 2023-08-02 17:44:13 +08:00
RockYang d7bbfb0fc3 feat: the dashboard page is ready for admin console 2023-08-02 16:37:47 +08:00
RockYang a0857817de refactor: refactor admin console page layout 2023-08-02 15:00:18 +08:00
RockYang 8d39234fd0 fix: fixed bug for chat request's token reach the max token limit for OpenAI 2023-08-01 17:58:03 +08:00
RockYang 0fce1c0fd8 feat: new function for add user in manger console user list page 2023-08-01 16:02:49 +08:00
RockYang c23c285f82 doc: add video tutorial for deploying system 2023-08-01 10:15:43 +08:00
RockYang 81b560d791 docs: remove docs for deploy manually 2023-07-31 20:04:22 +08:00
RockYang 55459a4f42 opt: optimize the build script and docker-compose yaml file 2023-07-31 17:31:02 +08:00
RockYang 9eafd3e6ca feat: add env var to set log level 2023-07-31 08:34:11 +08:00
RockYang e7ac26ff5a feat: update system config cache for AppServer when updating system config in admin console 2023-07-31 08:13:20 +08:00
RockYang fad78641d6 add system config cache for AppServer object 2023-07-31 06:56:28 +08:00
RockYang a32cb0142b feat: allow user to login with username and mobile no 2023-07-27 15:37:50 +08:00
RockYang 4a3c133152 fix: deauthorize some apis 2023-07-27 10:53:14 +08:00
RockYang ae79c34508 fix: fix bug for mobile verify code function not available in register page 2023-07-27 10:29:59 +08:00
RockYang 7eb69c1ad7 style: adjust reward image styles 2023-07-26 17:57:19 +08:00
RockYang 8aaff0af89 chore: optimize build script, compress reward img 2023-07-26 17:48:16 +08:00
RockYang 4b77280c65 feat: add reset password function to user list page 2023-07-26 15:22:11 +08:00
RockYang a0df84e4f2 docs: add change log file 2023-07-25 17:37:20 +08:00
RockYang 8876e6a098 docs: 更新文档和数据库文件 2023-07-25 17:25:23 +08:00
RockYang 9bee2a90f9 feat: 完成人机交互验证 API 接入,增加短信防刷验证 2023-07-25 17:00:24 +08:00
RockYang cd6a811bbd feat: 头条,微博热搜等函数 API 实现 2023-07-25 15:02:43 +08:00
RockYang 84f9a83f55 opt: 取消强制手机号验证,更新配置 2023-07-24 18:18:09 +08:00
RockYang 2efc669ab2 feat: 完成众筹后台管理功能 2023-07-24 15:59:29 +08:00
RockYang 5444ed77ad chore: add error log for reading chat response buffer 2023-07-24 12:05:38 +08:00
RockYang dd71fe80a5 opt: record user_id for reward verify 2023-07-22 08:42:30 +08:00
RockYang b73092eb64 feat: reward verify is ready 2023-07-21 22:29:14 +08:00
RockYang aa28f87b0c feat: 增加打赏核销功能 2023-07-21 18:26:51 +08:00
RockYang 1e1bcd4a30 feat: 集成微信收款服务 2023-07-20 17:46:32 +08:00
RockYang d24b3c46bf put logs in log file 2023-07-20 16:36:12 +08:00
RockYang 37222f07d9 fix: 修复阿里云短信发送失败的 bug 2023-07-16 16:05:11 +08:00
RockYang 49646a79e5 fix: 用户使用自己绑定的 API 调用,则不计算 calls 和 token 消耗 2023-07-16 11:55:45 +08:00
RockYang 9940e1210e Merge branch 'main' into prod 2023-07-16 09:57:27 +08:00
RockYang 10892812c3 chore: 更新项目打包脚本 2023-07-16 09:55:00 +08:00
RockYang d8ff5987dd feat: plugin function is ready 2023-07-15 21:52:30 +08:00
RockYang d014d418e9 feat: 支持上下文深度配置,计算每轮对话消耗的总 token 数量 2023-07-15 18:37:25 +08:00
RockYang cc1b56501d refactor: 更改 OpenAI 请求 Body 数据结构,兼容函数调用请求 2023-07-15 18:00:40 +08:00
RockYang a5ad9648bf chore: small fixs 2023-07-14 18:00:01 +08:00
RockYang 3630099234 feat: 完成每日早报函数开发 2023-07-10 18:59:53 +08:00
RockYang 941a24c75b fix: fixed conflicts 2023-07-10 10:11:17 +08:00
RockYang 50bdc01484 docs: update readme file 2023-07-10 09:44:24 +08:00
RockYang d9e340ddc4 refactor: 调整项目目录结构,移除其他语言 API 目录 2023-07-10 09:42:11 +08:00
RockYang beee1e91d6 test: add plugin test code 2023-07-10 07:05:56 +08:00
RockYang 35935d2bac fixed conflicts 2023-07-06 10:49:38 +08:00
RockYang 5d0b0ad33e opt: add tip message when no available key 2023-07-06 10:47:36 +08:00
RockYang d1a6ac531f opt: add tip message when no available key 2023-07-06 10:34:01 +08:00
RockYang ae255a3bd9 fix: 取消 ElMessage 的 appendTo 属性,防止被 Dialog 组件覆盖 2023-07-04 17:59:55 +08:00
RockYang fa733afa51 opt: 优化验证码发送逻辑,加入防刷验证 2023-07-04 17:15:02 +08:00
RockYang b60c723457 Merge branch 'main' into feat-sms 2023-07-04 10:55:20 +08:00
RockYang 2bb7ff3c13 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-07-04 10:54:35 +08:00
RockYang 68b4cf00e2 docs: update README, 增加免责申明 2023-07-04 10:52:52 +08:00
RockYang 440169ff60 feat: 短信验证码功能已完成,手机端同步实现。 2023-07-03 15:18:15 +08:00
RockYang 1fc361242e opt: 将短信发送按钮封装成组件 2023-07-03 06:55:15 +08:00
RockYang 72cc6f3d75 feat: 注册短信验证码验证功能已经开启 2023-07-02 20:51:13 +08:00
RockYang a2b1924e00 Merge branch 'main' into prod 2023-07-02 00:04:59 +08:00
RockYang 7e926167f0 fix: 修复 nodejs apple M1 跨平台打包,运行报错 exec format error 2023-07-02 00:04:12 +08:00
RockYang 32484bd32e feat: 增加用户 token 消耗统计功能 2023-07-01 23:29:24 +08:00
RockYang 04ade1008b Merge branch 'main' into prod 2023-07-01 10:24:08 +08:00
RockYang 4603de0e70 Merge pull request #18 from ly307787186/main
Update Setting.vue
2023-07-01 10:22:01 +08:00
RockYang 12c1ff65e9 opt: remove chat role info from user login api's response 2023-06-30 18:52:43 +08:00
ly307787186 7b8cbf7f86 Update Setting.vue
关掉aip-key 必需的验证
2023-06-30 13:20:30 +08:00
RockYang c198e83f70 fix: fixed bug for Emoji can not insert to MySQL 2023-06-30 09:15:57 +08:00
RockYang e60863a290 chore: change ubuntu docker image with aliyun 2023-06-29 15:56:11 +08:00
RockYang 946563d3b2 chore: add test code for fix role icon url of db 2023-06-29 08:58:19 +08:00
RockYang 3f2ef1d54e fix: 修正前端 user_init_call 字段错误和用户注册初始化头像路径问题 2023-06-28 20:01:44 +08:00
RockYang 0d2b60b905 fix: 修复 PC 端聊天界面滚动条问题 2023-06-28 18:16:28 +08:00
RockYang 161b11e428 opt: 优化启动参数接收处理 2023-06-28 05:51:55 +08:00
RockYang f7748d51df opt: 通过环境变量来传参,修正 docker compose 配置参数 2023-06-27 18:29:46 +08:00
RockYang 5d13b4b705 docs: 更新文档,新增移动端预览图 2023-06-27 14:28:00 +08:00
RockYang 92e52a2284 fix: 修复 markdown 换行符不解析的 Bug,修复新发布的模型 token 统计失败错误 2023-06-27 14:18:20 +08:00
RockYang 3efd5fb77a feat: vue-mobile => 完成用户信息修改功能,前后端都添加文件上传功能。 2023-06-27 12:11:55 +08:00
RockYang c3d62bb8d8 feat: vue-mobile => 完成移动端聊天配置功能 2023-06-26 18:18:45 +08:00
RockYang c099e843d5 feat: vue-mobile => 完成会话聊天页面功能,增加主题切换功能 2023-06-26 16:39:00 +08:00
RockYang 5f3a5871f1 feat: vue-mobile => 优化聊天记录拍版样式 2023-06-25 18:21:38 +08:00
RockYang f01fdd0070 feat: vue-mobile => 完善移动端聊天列表页功能 2023-06-25 17:01:04 +08:00
RockYang d20cc367b8 fixed: go-api => 增加全局错误处理 handler,修复业务处理异常导致服务退出的 Bug 2023-06-25 11:34:55 +08:00
RockYang eadb9a733f docs: 增加容器部署文档 2023-06-25 11:06:18 +08:00
RockYang 32ac454b5b opt: 优化前端登录判断逻辑 2023-06-25 09:46:23 +08:00
RockYang 2cadd6af44 feat: chat list page for mobile is ready 2023-06-25 06:53:22 +08:00
RockYang 3c8b5cb313 feat: 完成移动端前段框架搭建 2023-06-24 11:45:26 +08:00
RockYang fcf2387794 style: 调整聊天侧边栏样式 2023-06-23 18:31:50 +08:00
RockYang f9783b4806 opt: 优化 docker-compse 构建脚本,修复后端路由 Bug 2023-06-23 18:04:16 +08:00
RockYang cbdc532f83 chore: 添加 docker 镜像构建脚本 2023-06-23 07:08:16 +08:00
RockYang 124554bae5 opt: 抽离 session 验证函数,修正前端路由覆盖 bug 2023-06-23 06:31:25 +08:00
RockYang 2725536d7d chore: 使用阿里云镜像仓库 2023-06-22 22:23:48 +08:00
RockYang beeef7a4f7 docs: 增加 docker-compose 部署支持 2023-06-22 22:14:18 +08:00
RockYang 7f2ebb6aeb docs: 完善新版本文档 2023-06-22 15:47:51 +08:00
RockYang c8e30383ba refactor: embed xdb file for ip2region 2023-06-22 11:08:44 +08:00
RockYang a7d5a6ccb9 style: 调整后台管理框架样式 2023-06-21 18:44:18 +08:00
RockYang be3380aaf3 refactor: 更新 iconfont 图标,增加打赏二维码弹窗 2023-06-21 15:34:38 +08:00
RockYang 40a4ab5410 refactor: refactor the frame layout of admin module 2023-06-21 14:22:28 +08:00
RockYang 54a2181960 refactor: user login log list for admin is ready 2023-06-21 06:53:41 +08:00
RockYang e490e15bc4 feat: API Key manage is ready 2023-06-20 18:05:33 +08:00
RockYang 469844d97b opt: optimize role sorting 2023-06-20 16:07:26 +08:00
RockYang 892ea29ba8 refactor: chat role manage for admin is ready 2023-06-20 11:46:13 +08:00
RockYang acde1c6742 refactor: user remove is ready for console 2023-06-20 07:08:37 +08:00
RockYang 07da11d852 refactor: 管理后台用户编辑功能 is ready 2023-06-19 21:53:07 +08:00
RockYang 502b8c2270 refactor: 管理后台用户列表页面重构 2023-06-19 18:23:09 +08:00
RockYang af3f7ac810 refactor: 完成管理后台的系统设置页面重构 2023-06-19 15:58:52 +08:00
RockYang 935c6caf96 feat: admin login page is ready 2023-06-19 11:09:23 +08:00
RockYang 90bce1d437 refactor: refactor controller handler module and admin module 2023-06-19 07:06:59 +08:00
RockYang 831dd3e2e0 style: optimize code styles 2023-06-18 15:17:59 +08:00
RockYang b8e208eb32 style: optimize styles for change password page 2023-06-17 00:02:49 +08:00
RockYang 542c908e30 fix: fixed bu for 'slice bounds out of range' 2023-06-16 23:30:04 +08:00
RockYang 08529242bf style: add logo at login and register page 2023-06-16 18:14:43 +08:00
RockYang 1ae9449d92 chore: tracke the logo file 2023-06-16 17:44:38 +08:00
RockYang 7fd0b1fa08 feat: import iconfont styles, change password function is ready 2023-06-16 17:28:21 +08:00
RockYang 111572e3f2 fix: add lock map data structure, fixed bug for 'concurrent map writes' 2023-06-16 15:32:11 +08:00
RockYang c9875d24b4 feat: add footbar for login and register page, add send button for chat page 2023-06-16 13:57:05 +08:00
RockYang 94a75603c3 chore: compress bg images 2023-06-16 11:14:45 +08:00
RockYang d55925bccd chore: delete bin file 2023-06-16 10:01:34 +08:00
RockYang 0b5adcbfab fix: fixed bug for ssl websocket url 2023-06-16 10:00:05 +08:00
RockYang 8d84562f32 chore: update config sample file 2023-06-15 16:03:42 +08:00
RockYang 136d159833 opt: add email and mobile validation for register 2023-06-15 15:09:00 +08:00
RockYang e3026062d8 opt: automatic get the host for api and websocket from the 'location' var 2023-06-15 14:39:05 +08:00
RockYang 8a69c69b7f opt: update the main chat compnent after updating the user profile 2023-06-15 11:29:16 +08:00
RockYang 197714a57a refactor: add system config key 'user_init_calls' to init the new register user's api calls 2023-06-15 10:06:21 +08:00
RockYang 567cdc6f8d refactor: V3 版本重构已基本完成 2023-06-15 09:41:30 +08:00
RockYang be6e8c7713 Merge pull request #6 from EyreFree/master
feat: docker supported
2023-06-05 08:32:34 +08:00
EyreFree f667b35403 docker 2023-06-03 20:48:32 +08:00
RockYang 942e482ca4 chore: 打招呼的时候输出本项目地址 2023-05-09 16:11:50 +08:00
RockYang 10b381965e Update 1.bug.yml 2023-05-09 15:43:48 +08:00
RockYang 79c3a99a38 Update 1.bug.yml 2023-05-09 15:39:47 +08:00
RockYang 463d3c8e97 Update 1.bug.yml 2023-05-09 15:35:07 +08:00
RockYang da007729c6 Update 1.bug.yml 2023-05-09 15:32:57 +08:00
RockYang b7b42b5fd4 chore: 重命名 issue 模版 2023-05-09 15:29:57 +08:00
RockYang 735f9bd053 chore: 添加 issue 和 PR 模板 2023-05-09 15:26:38 +08:00
RockYang 6096b65374 chore: 替换文字描述 用户 => 2023-05-09 14:30:09 +08:00
RockYang 727d342eb3 docs: 优化文档 2023-05-06 18:33:27 +08:00
RockYang 58815ba8bc docs: 优化文档 2023-05-06 18:13:44 +08:00
RockYang 49d0502775 docs: 优化文档排版 2023-05-06 16:36:04 +08:00
RockYang 0b6c6e6d2c docs: 完善文档 2023-05-06 16:27:44 +08:00
RockYang 45ede6047a opt: 优化配置文档加载 2023-05-06 13:58:56 +08:00
RockYang 8f469d4ebb Merge branch 'master' of gitee.com:blackfox/wechat-gpt 2023-05-06 12:07:17 +08:00
RockYang f3264d056d add LICENSE.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2023-05-06 04:03:33 +00:00
85 changed files with 2354 additions and 2071 deletions
+1
View File
@@ -14,3 +14,4 @@ logs
*.njsproj
*.sln
*.sw?
miniprogram
+14
View File
@@ -1,5 +1,19 @@
# 更新日志
## v4.2.8
- Bug 修复:修复管理后台邮件配置报“参数错误”问题
- 功能优化:手机端也不显示后端禁用的菜单功能页面
- 功能优化:**聊天页面支持 Sora2 文生视频/图生视频功能** 🔥🔥🔥
- 功能优化:**重构路由代码,优化路由加载逻辑,支持关闭手机站功能** 🔥🔥🔥
- Bug 修复:修复用户算力扣减日志记录逻辑错误问题
- 功能优化:优化 404 页面样式,兼容 PC 端和移动端
- Bug 修复:ipv6 地址登录报错:Data too long for column 'last_login_ip'
- Bug 修复:移动端登录页面输入密码的时候会覆盖在确认按钮上层
- 功能优化:**移动端的 DALL-E 绘图页面支持上传参考图** 🔥🔥🔥
- 功能优化:优化 WebFooter 组件,如果配置了备案号,则显示备案号,否则不显示
- 功能优化:启动时自动同步 model 与数据表字段,缺列自动新建、多余列自动删除
## v4.2.7
- Bug 修复:修复超级管理员无法修改密码的 Bug
+1
View File
@@ -16,6 +16,7 @@ tmp
bin
data
config.toml
config.toml.*.bak
static/upload
static/audio
storage.json
+1 -1
View File
@@ -1,6 +1,6 @@
Listen = "0.0.0.0:5678"
ProxyURL = "" # 如 http://127.0.0.1:7777
MysqlDns = "root:12345678@tcp(172.22.11.200:3307)/chatgpt_plus?charset=utf8mb4&collation=utf8mb4_unicode_ci&parseTime=True&loc=Local"
MysqlDns = "root:12345678@tcp(localhost:3306)/geekai?charset=utf8mb4&collation=utf8mb4_unicode_ci&parseTime=True&loc=Local"
StaticDir = "./static" # 静态资源的目录
StaticUrl = "/static" # 静态资源访问 URL
TikaHost = "http://tika:9998"
+1 -1
View File
@@ -44,11 +44,11 @@ func NewServer(appConfig *types.AppConfig, redis *redis.Client, sysConfig *types
}
func (s *AppServer) Init(client *redis.Client) {
s.Engine.Use(middleware.ThumbMiddleware())
s.Engine.Use(middleware.ParameterHandlerMiddleware())
s.Engine.Use(errorHandler)
// 添加静态资源访问
s.Engine.Static("/static", s.Config.StaticDir)
s.Engine.Use(middleware.StaticMiddleware())
}
func (s *AppServer) Run(db *gorm.DB) error {
-10
View File
@@ -85,15 +85,6 @@ func LoadSystemConfig(db *gorm.DB) *types.SystemConfig {
logger.Error("load system config error: ", err)
}
// 加载许可证配置
var license types.License
sysConfig.Id = 0
db.Where("name", types.ConfigKeyLicense).First(&sysConfig)
err = utils.JsonDecode(sysConfig.Value, &license)
if err != nil {
logger.Error("load license config error: ", err)
}
// 加载验证码配置
var captchaConfig types.CaptchaConfig
sysConfig.Id = 0
@@ -168,7 +159,6 @@ func LoadSystemConfig(db *gorm.DB) *types.SystemConfig {
return &types.SystemConfig{
Base: baseConfig,
License: license,
SMS: smsConfig,
OSS: ossConfig,
SMTP: smtpConfig,
+3
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"geekai/core/types"
logger2 "geekai/logger"
"geekai/utils"
"geekai/utils/resp"
"time"
@@ -13,6 +14,8 @@ import (
"github.com/golang-jwt/jwt"
)
var logger = logger2.GetLogger()
// 前端用户授权验证
func UserAuthMiddleware(secretKey string, redis *redis.Client) gin.HandlerFunc {
return func(c *gin.Context) {
@@ -14,8 +14,8 @@ import (
"golang.org/x/image/webp"
)
// 静态资源中间件
func StaticMiddleware() gin.HandlerFunc {
// 缩略图中间件
func ThumbMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
url := c.Request.URL.String()
+2 -18
View File
@@ -37,22 +37,6 @@ type RedisConfig struct {
DB int
}
// LicenseKey 存储许可证书的 KEY
const LicenseKey = "Geek-AI-License"
type License struct {
Key string `json:"key"` // 许可证书密钥
MachineId string `json:"machine_id"` // 机器码
ExpiredAt int64 `json:"expired_at"` // 过期时间
IsActive bool `json:"is_active"` // 是否激活
Configs LicenseConfig `json:"configs"`
}
type LicenseConfig struct {
UserNum int `json:"user_num"` // 用户数量
DeCopy bool `json:"de_copy"` // 去版权
}
func (c RedisConfig) Url() string {
return fmt.Sprintf("%s:%d", c.Host, c.Port)
}
@@ -97,6 +81,8 @@ type BaseConfig struct {
EmailWhiteList []string `json:"email_white_list"` // 邮箱白名单列表
AssistantModelId int `json:"assistant_model_id"` // 用来做提示词,翻译的AI模型 id
MaxFileSize int `json:"max_file_size"` // 最大文件大小,单位:MB
EnableMobileSite bool `json:"enable_mobile_site"` // 是否开启手机站点
}
type SystemConfig struct {
@@ -108,7 +94,6 @@ type SystemConfig struct {
Captcha CaptchaConfig
WxLogin WxLoginConfig
Jimeng JimengConfig
License License
Moderation ModerationConfig
}
@@ -121,7 +106,6 @@ const (
ConfigKeyMarkMap = "mark_map"
ConfigKeyCaptcha = "captcha"
ConfigKeyWxLogin = "wx_login"
ConfigKeyLicense = "license"
ConfigKeySms = "sms"
ConfigKeySmtp = "smtp"
ConfigKeyOss = "oss"
+2 -1
View File
@@ -15,7 +15,8 @@ type JimengConfig struct {
type JMTaskStatus string
const (
JMTaskStatusInQueue = JMTaskStatus("in_queue") // 任务已提交
JMTaskStatusSubmited = JMTaskStatus("submited") // 任务已提交
JMTaskStatusInQueue = JMTaskStatus("in_queue") // 任务排队中
JMTaskStatusGenerating = JMTaskStatus("generating") // 任务处理中
JMTaskStatusDone = JMTaskStatus("done") // 处理完成
JMTaskStatusNotFound = JMTaskStatus("not_found") // 任务未找到
+2 -3
View File
@@ -72,14 +72,13 @@ type SdTaskParams struct {
type DallTask struct {
ModelId uint `json:"model_id"`
ModelName string `json:"model_name"`
ModelValue string `json:"model_value"`
Image []string `json:"image,omitempty"`
Id uint `json:"id"`
UserId uint `json:"user_id"`
Prompt string `json:"prompt"`
N int `json:"n"`
Quality string `json:"quality"`
AspectRatio string `json:"aspect_ratio"`
Size string `json:"size"`
Style string `json:"style"`
Power int `json:"power"`
TranslateModelId int `json:"translate_model_id"` // 提示词翻译模型ID
}
-51
View File
@@ -26,7 +26,6 @@ import (
type ConfigHandler struct {
handler.BaseHandler
licenseService *service.LicenseService
sysConfig *types.SystemConfig
alipayService *payment.AlipayService
wxpayService *payment.WxPayService
@@ -41,7 +40,6 @@ type ConfigHandler struct {
func NewConfigHandler(
app *core.AppServer,
db *gorm.DB,
licenseService *service.LicenseService,
sysConfig *types.SystemConfig,
alipayService *payment.AlipayService,
wxpayService *payment.WxPayService,
@@ -54,7 +52,6 @@ func NewConfigHandler(
) *ConfigHandler {
return &ConfigHandler{
BaseHandler: handler.BaseHandler{App: app, DB: db},
licenseService: licenseService,
sysConfig: sysConfig,
alipayService: alipayService,
wxpayService: wxpayService,
@@ -87,8 +84,6 @@ func (h *ConfigHandler) RegisterRoutes() {
rg.POST("update/oss", h.UpdateOss)
rg.POST("update/smtp", h.UpdateStmp)
rg.GET("get", h.Get)
rg.POST("license/active", h.Active)
rg.GET("license/get", h.GetLicense)
}
}
@@ -101,19 +96,6 @@ func (h *ConfigHandler) UpdateBase(c *gin.Context) {
return
}
// 未授权的话不允许修改版权
license := h.licenseService.GetLicense()
if !license.IsActive && data.Copyright != h.sysConfig.Base.Copyright {
resp.ERROR(c, "未授权系统不允许修改版权信息")
return
}
// 未授权的话不允许修改 Logo
if !license.IsActive && data.Logo != h.sysConfig.Base.Logo {
resp.ERROR(c, "未授权系统不允许修改 Logo")
return
}
err := h.Update(types.ConfigKeySystem, data)
if err != nil {
resp.ERROR(c, err.Error())
@@ -407,36 +389,3 @@ func (h *ConfigHandler) Get(c *gin.Context) {
resp.SUCCESS(c, value)
}
// Active 激活系统
func (h *ConfigHandler) Active(c *gin.Context) {
var data struct {
License string `json:"license"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
err := h.licenseService.ActiveLicense(data.License)
license := h.licenseService.GetLicense()
if err != nil {
resp.ERROR(c, err.Error())
return
}
if err := h.Update(types.ConfigKeyLicense, license); err != nil {
resp.ERROR(c, err.Error())
return
}
// 更新系统配置
h.sysConfig.License = *license
resp.SUCCESS(c, license.MachineId)
}
// GetLicense 获取 License 信息
func (h *ConfigHandler) GetLicense(c *gin.Context) {
license := h.licenseService.GetLicense()
resp.SUCCESS(c, license)
}
+4 -17
View File
@@ -13,7 +13,6 @@ import (
"geekai/core/middleware"
"geekai/core/types"
"geekai/handler"
"geekai/service"
"geekai/store/model"
"geekai/store/vo"
"geekai/utils"
@@ -28,12 +27,11 @@ import (
type UserHandler struct {
handler.BaseHandler
licenseService *service.LicenseService
redis *redis.Client
redis *redis.Client
}
func NewUserHandler(app *core.AppServer, db *gorm.DB, licenseService *service.LicenseService, redisCli *redis.Client) *UserHandler {
return &UserHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, licenseService: licenseService, redis: redisCli}
func NewUserHandler(app *core.AppServer, db *gorm.DB, redisCli *redis.Client) *UserHandler {
return &UserHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, redis: redisCli}
}
// RegisterRoutes 注册路由
@@ -114,13 +112,6 @@ func (h *UserHandler) Save(c *gin.Context) {
resp.ERROR(c, types.InvalidArgs)
return
}
// 检测最大注册人数
var totalUser int64
h.DB.Model(&model.User{}).Count(&totalUser)
if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum {
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
return
}
var user = model.User{}
var res *gorm.DB
var userVo vo.User
@@ -198,11 +189,7 @@ func (h *UserHandler) Save(c *gin.Context) {
ChatModels: utils.JsonEncode(data.ChatModels),
ExpiredTime: utils.Str2stamp(data.ExpiredTime),
}
if h.licenseService.GetLicense().Configs.DeCopy {
u.Nickname = fmt.Sprintf("用户@%d", utils.RandomNumber(6))
} else {
u.Nickname = fmt.Sprintf("极客学长@%d", utils.RandomNumber(6))
}
u.Nickname = fmt.Sprintf("用户@%d", utils.RandomNumber(6))
res = h.DB.Create(&u)
_ = utils.CopyObject(u, &userVo)
userVo.Id = u.Id
+54 -24
View File
@@ -19,6 +19,7 @@ import (
"geekai/service"
"geekai/service/moderation"
"geekai/service/oss"
"geekai/service/sora"
"geekai/store/model"
"geekai/store/vo"
"geekai/utils"
@@ -65,36 +66,39 @@ type ChatHandler struct {
BaseHandler
redis *redis.Client
uploadManager *oss.UploaderManager
licenseService *service.LicenseService
ReqCancelFunc *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function
userService *service.UserService
moderationManager *moderation.ServiceManager
userLocks *types.UserLockManager
soraService *sora.SoraService
}
func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manager *oss.UploaderManager, licenseService *service.LicenseService, userService *service.UserService, moderationManager *moderation.ServiceManager) *ChatHandler {
func NewChatHandler(app *core.AppServer,
db *gorm.DB,
redis *redis.Client,
manager *oss.UploaderManager,
userService *service.UserService,
soraService *sora.SoraService,
moderationManager *moderation.ServiceManager) *ChatHandler {
return &ChatHandler{
BaseHandler: BaseHandler{App: app, DB: db},
redis: redis,
uploadManager: manager,
licenseService: licenseService,
ReqCancelFunc: types.NewLMap[string, context.CancelFunc](),
userService: userService,
moderationManager: moderationManager,
userLocks: types.NewUserLockManager(),
soraService: soraService,
}
}
// RegisterRoutes 注册路由
func (h *ChatHandler) RegisterRoutes() {
group := h.App.Engine.Group("/api/chat/")
// 聊天接口不需要授权(已在authConfig中配置)
group.Any("message", h.Chat)
// 其他接口需要用户授权
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
{
group.Any("message", h.Chat)
group.GET("list", h.List)
group.GET("detail", h.Detail)
group.POST("update", h.Update)
@@ -340,14 +344,14 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C
for _, file := range input.Files {
logger.Debugf("detected file: %+v", file.URL)
// 处理图片
if isImageURL(file.URL) {
if isImageURL(file.URL) || isVideoURL(file.URL) {
imgList = append(imgList, gin.H{
"type": "image_url",
"image_url": gin.H{
"url": file.URL,
},
})
} else {
} else if isTextURL(file.URL) {
// 处理文件,提取文件内容
content, err := utils.ReadFileContent(file.URL, h.App.Config.TikaHost)
if err != nil {
@@ -408,23 +412,43 @@ func isImageURL(url string) bool {
".svg": true,
".ico": true,
}
return validImageExts[ext]
}
if !validImageExts[ext] {
// 判断是个链接是否是文本链接
func isTextURL(url string) bool {
// 检查是否是有效的URL
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
return false
}
// 发送HEAD请求检查Content-Type
client := &http.Client{
Timeout: 5 * time.Second,
// 检查文件扩展名
ext := strings.ToLower(path.Ext(url))
validTextExts := map[string]bool{
".txt": true,
".doc": true,
".docx": true,
".pdf": true,
}
resp, err := client.Head(url)
if err != nil {
return validTextExts[ext]
}
// 判断是个链接是否是视频
func isVideoURL(url string) bool {
// 检查是否是有效的URL
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
return false
}
defer resp.Body.Close()
contentType := resp.Header.Get("Content-Type")
return strings.HasPrefix(contentType, "image/")
// 检查文件扩展名
ext := strings.ToLower(path.Ext(url))
validVideoExts := map[string]bool{
".mp4": true,
".avi": true,
".mov": true,
".wmv": true,
}
return validVideoExts[ext]
}
// Tokens 统计 token 数量
@@ -505,11 +529,6 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, input
return nil, errors.New("no available key, please import key")
}
// ONLY allow apiURL in blank list
err := h.licenseService.IsValidApiURL(apiKey.ApiURL)
if err != nil {
return nil, err
}
logger.Debugf("对话请求消息体:%+v", req)
var apiURL string
p, _ := url.Parse(apiKey.ApiURL)
@@ -644,6 +663,16 @@ func (h *ChatHandler) saveChatHistory(
replyTokens, _ = utils.CalcTokens(message.Content, req.Model)
totalTokens = replyTokens + getTotalTokens(req)
}
// 如果是 Sora 相关模型,则下载视频
files := make([]vo.File, 0)
if strings.HasPrefix(req.Model, "sora") {
video, err := h.soraService.DownloadVideoURL(message.Content)
if err == nil {
files = append(files, *video)
}
}
historyReplyMsg := model.ChatMessage{
UserId: userVo.Id,
ChatId: input.ChatId,
@@ -652,7 +681,7 @@ func (h *ChatHandler) saveChatHistory(
Icon: input.ChatRole.Icon,
Content: utils.JsonEncode(vo.MsgContent{
Text: message.Content,
Files: input.Files,
Files: files,
}),
Tokens: replyTokens,
TotalTokens: totalTokens,
@@ -676,6 +705,7 @@ func (h *ChatHandler) saveChatHistory(
if err != nil {
content.Text = historyReplyMsg.Content
}
content.Files = files
messageVo.Content = content
messageVo.CreatedAt = historyReplyMsg.CreatedAt.Unix()
messageVo.UpdatedAt = historyReplyMsg.UpdatedAt.Unix()
+8
View File
@@ -212,6 +212,14 @@ func (h *ChatHandler) sendOpenAiMessage(
} else {
errMsg = utils.InterfaceToString(apiRes.Data)
contents = append(contents, errMsg)
// 扣减算力
if function.Power > 0 {
h.userService.DecreasePower(userVo.Id, function.Power, model.PowerLog{
Type: types.PowerConsume,
Model: function.Name,
Remark: fmt.Sprintf("函数调用,%s(%s)", function.Label, function.Name),
})
}
}
pushMessage(c, "text", errMsg)
}
+2 -11
View File
@@ -9,7 +9,6 @@ package handler
import (
"geekai/core"
"geekai/service"
"geekai/store/model"
"geekai/utils"
"geekai/utils/resp"
@@ -20,11 +19,10 @@ import (
type ConfigHandler struct {
BaseHandler
licenseService *service.LicenseService
}
func NewConfigHandler(app *core.AppServer, db *gorm.DB, licenseService *service.LicenseService) *ConfigHandler {
return &ConfigHandler{BaseHandler: BaseHandler{App: app, DB: db}, licenseService: licenseService}
func NewConfigHandler(app *core.AppServer, db *gorm.DB) *ConfigHandler {
return &ConfigHandler{BaseHandler: BaseHandler{App: app, DB: db}}
}
// RegisterRoutes 注册路由
@@ -33,7 +31,6 @@ func (h *ConfigHandler) RegisterRoutes() {
// 无需授权的接口
group.GET("get", h.Get)
group.GET("license", h.License)
}
// Get 获取指定的系统配置
@@ -55,9 +52,3 @@ func (h *ConfigHandler) Get(c *gin.Context) {
resp.SUCCESS(c, value)
}
// License 获取 License 配置
func (h *ConfigHandler) License(c *gin.Context) {
license := h.licenseService.GetLicense()
resp.SUCCESS(c, license.Configs)
}
+4 -4
View File
@@ -117,12 +117,12 @@ func (h *DallJobHandler) Image(c *gin.Context) {
task := types.DallTask{
UserId: uint(userId),
ModelId: chatModel.Id,
ModelName: chatModel.Value,
ModelName: chatModel.Name,
ModelValue: chatModel.Value,
Image: data.Image,
Prompt: data.Prompt,
Quality: data.Quality,
AspectRatio: data.AspectRatio,
Size: data.Size,
Style: data.Style,
TranslateModelId: h.App.SysConfig.Base.AssistantModelId,
Power: chatModel.Power,
}
@@ -144,7 +144,7 @@ func (h *DallJobHandler) Image(c *gin.Context) {
// 扣减算力
err = h.userService.DecreasePower(user.Id, chatModel.Power, model.PowerLog{
Type: types.PowerConsume,
Model: chatModel.Value,
Model: chatModel.Name,
Remark: fmt.Sprintf("绘画提示词:%s", utils.CutWords(task.Prompt, 10)),
})
if err != nil {
+2 -5
View File
@@ -215,12 +215,9 @@ func (h *FunctionHandler) Dall3(c *gin.Context) {
UserId: user.Id,
Prompt: prompt,
ModelId: chatModel.Id,
ModelName: chatModel.Value,
ModelValue: chatModel.Value,
ModelName: chatModel.Name,
TranslateModelId: h.App.SysConfig.Base.AssistantModelId,
N: 1,
Quality: "standard",
Size: "1024x1024",
Style: "vivid",
Power: chatModel.Power,
}
job := model.DallJob{
+1 -19
View File
@@ -247,30 +247,12 @@ func (h *JimengHandler) Remove(c *gin.Context) {
return
}
tx := h.DB.Begin()
if err := tx.Where("id = ? AND user_id = ?", jobId, user.Id).Delete(&model.JimengJob{}).Error; err != nil {
if err := h.DB.Where("id = ? AND user_id = ?", jobId, user.Id).Delete(&model.JimengJob{}).Error; err != nil {
logger.Errorf("delete jimeng job failed: %v", err)
resp.ERROR(c, "删除任务失败")
return
}
// 失败任务删除后退回算力
if job.Status == types.JMTaskStatusFailed {
logger.Infof("delete jimeng job failed, refund power: %d", job.Power)
err = h.userService.IncreasePower(user.Id, job.Power, model.PowerLog{
Type: types.PowerRefund,
Model: job.ReqKey,
Remark: fmt.Sprintf("删除任务,退回%d算力", job.Power),
})
if err != nil {
resp.ERROR(c, "退回算力失败")
tx.Rollback()
return
}
}
tx.Commit()
resp.SUCCESS(c, gin.H{})
}
+18
View File
@@ -30,6 +30,7 @@ func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler {
func (h *MenuHandler) RegisterRoutes() {
group := h.App.Engine.Group("/api/menu/")
group.GET("list", h.List)
group.GET("list/all", h.ListAll)
}
// List 数据列表
@@ -54,3 +55,20 @@ func (h *MenuHandler) List(c *gin.Context) {
}
resp.SUCCESS(c, list)
}
// ListAll 获取所有的菜单列表
func (h *MenuHandler) ListAll(c *gin.Context) {
var items []model.Menu
var list = make([]vo.Menu, 0)
res := h.DB.Order("sort_num ASC").Find(&items)
if res.Error == nil {
for _, item := range items {
var product vo.Menu
err := utils.CopyObject(item, &product)
if err == nil {
list = append(list, product)
}
}
}
resp.SUCCESS(c, list)
}
+1
View File
@@ -138,6 +138,7 @@ func (h *PaymentHandler) SyncOrders() error {
}
case payment.PayChannelAL:
res, err = h.alipayService.Query(order.OrderNo)
logger.Debugf("支付宝支付订单状态:%+v", res)
if err != nil {
logger.Errorf("error with query order info: %v", err)
continue
+9 -17
View File
@@ -35,7 +35,6 @@ type UserHandler struct {
searcher *xdb.Searcher
redis *redis.Client
levelDB *store.LevelDB
licenseService *service.LicenseService
captchaService *service.CaptchaService
userService *service.UserService
wxLoginService *service.WxLoginService
@@ -51,15 +50,13 @@ func NewUserHandler(
captcha *service.CaptchaService,
userService *service.UserService,
wxLoginService *service.WxLoginService,
ipSearcher *xdb.Searcher,
licenseService *service.LicenseService) *UserHandler {
ipSearcher *xdb.Searcher) *UserHandler {
return &UserHandler{
BaseHandler: BaseHandler{DB: db, App: app},
searcher: searcher,
redis: client,
levelDB: levelDB,
captchaService: captcha,
licenseService: licenseService,
userService: userService,
wxLoginService: wxLoginService,
ipSearcher: ipSearcher,
@@ -132,14 +129,6 @@ func (h *UserHandler) Register(c *gin.Context) {
return
}
// 检测最大注册人数
var totalUser int64
h.DB.Model(&model.User{}).Count(&totalUser)
if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum {
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
return
}
// 检查验证码
var key string
if data.RegWay == "email" {
@@ -327,7 +316,7 @@ func (h *UserHandler) GetWxLoginState(c *gin.Context) {
}
// createNewUser 创建新用户
func (h *UserHandler) createNewUser(user model.User, inviteCode string) (model.User, error) {
func (h *UserHandler) createNewUser(user model.User, code string) (model.User, error) {
if user.OpenId != "" {
user.Platform = "wechat"
user.Nickname = fmt.Sprintf("微信用户@%d", utils.RandomNumber(6))
@@ -353,19 +342,22 @@ func (h *UserHandler) createNewUser(user model.User, inviteCode string) (model.U
// 创建用户
tx := h.DB.Begin()
if err := tx.Create(&user).Error; err != nil {
tx.Rollback()
return user, err
}
// 记录邀请关系
if inviteCode != "" {
inviteCode := model.InviteCode{}
err := h.DB.Where("code = ?", inviteCode).First(&inviteCode).Error
if code != "" {
var inviteCode model.InviteCode
err := h.DB.Where("code = ?", code).First(&inviteCode).Error
if err != nil {
logger.Error("无效的邀请码:", err.Error())
tx.Rollback()
return user, fmt.Errorf("无效的邀请码")
}
// 增加邀请数量
h.DB.Model(&model.InviteCode{}).Where("code = ?", inviteCode).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1))
h.DB.Model(&model.InviteCode{}).Where("code = ?", code).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1))
if h.App.SysConfig.Base.InvitePower > 0 {
err := h.userService.IncreasePower(inviteCode.UserId, h.App.SysConfig.Base.InvitePower, model.PowerLog{
Type: types.PowerInvite,
+6 -12
View File
@@ -24,6 +24,7 @@ import (
"geekai/service/payment"
"geekai/service/sd"
"geekai/service/sms"
"geekai/service/sora"
"geekai/service/suno"
"geekai/service/video"
"geekai/store"
@@ -166,12 +167,6 @@ func main() {
// 邮件服务
fx.Provide(service.NewSmtpService),
// License 服务
fx.Provide(service.NewLicenseService),
fx.Invoke(func(licenseService *service.LicenseService) {
licenseService.SyncLicense()
}),
// Dalle 服务
fx.Provide(dalle.NewService),
fx.Invoke(func(s *dalle.Service) {
@@ -189,6 +184,9 @@ func main() {
s.DownloadImages()
}),
// Sora service
fx.Provide(sora.NewSoraService),
// Stable Diffusion 机器人
fx.Provide(sd.NewService),
fx.Invoke(func(s *sd.Service, config *types.AppConfig) {
@@ -222,12 +220,8 @@ func main() {
fx.Provide(sms.NewAliYunSmsService),
fx.Provide(sms.NewBaoSmsService),
fx.Provide(sms.NewSmsManager),
fx.Provide(func(config *types.SystemConfig) *service.CaptchaService {
return service.NewCaptchaService(config.Captcha)
}),
fx.Provide(func(config *types.SystemConfig, client *redis.Client) *service.WxLoginService {
return service.NewWxLoginService(config.WxLogin, client)
}),
fx.Provide(service.NewCaptchaService),
fx.Provide(service.NewWxLoginService),
// 支付服务
fx.Provide(payment.NewAlipayService),
+3 -3
View File
@@ -20,9 +20,9 @@ type CaptchaService struct {
client *req.Client
}
func NewCaptchaService(captchaConfig types.CaptchaConfig) *CaptchaService {
func NewCaptchaService(sysConfig *types.SystemConfig) *CaptchaService {
return &CaptchaService{
config: captchaConfig,
config: sysConfig.Captcha,
client: req.C().SetTimeout(10 * time.Second),
}
}
@@ -35,7 +35,7 @@ func (s *CaptchaService) GetConfig() types.CaptchaConfig {
return s.config
}
func (s *CaptchaService) Get() (interface{}, error) {
func (s *CaptchaService) Get() (any, error) {
url := fmt.Sprintf("%s/api/captcha/get", types.GeekAPIURL)
var res types.BizVo
r, err := s.client.R().
@@ -16,7 +16,6 @@ import (
"geekai/store"
"geekai/store/model"
"geekai/utils"
"strings"
"time"
"github.com/go-redis/redis/v8"
@@ -98,10 +97,8 @@ type imgReq struct {
Model string `json:"model"`
Image []string `json:"image,omitempty"`
Prompt string `json:"prompt"`
N int `json:"n,omitempty"`
AspectRatio string `json:"aspect_ratio,omitempty"`
Size string `json:"size,omitempty"`
Quality string `json:"quality,omitempty"`
Style string `json:"style,omitempty"`
ResponseFormat string `json:"response_format,omitempty"`
}
@@ -116,10 +113,10 @@ type imgRes struct {
type ErrRes struct {
Error struct {
Code interface{} `json:"code"`
Message string `json:"message"`
Param interface{} `json:"param"`
Type string `json:"type"`
Code any `json:"code"`
Message string `json:"message"`
Param any `json:"param"`
Type string `json:"type"`
} `json:"error"`
}
@@ -130,7 +127,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
if task.ModelId > 0 {
s.db.Where("id", task.ModelId).First(&chatModel)
} else {
s.db.Where("value", task.ModelName).First(&chatModel)
s.db.Where("value", task.ModelValue).First(&chatModel)
}
// get image generation API KEY
@@ -153,16 +150,16 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
}
apiURL := fmt.Sprintf("%s/v1/images/generations", apiKey.ApiURL)
reqBody := imgReq{
Model: chatModel.Value,
Prompt: task.Prompt,
N: 1,
Size: task.Size,
Style: task.Style,
Quality: task.Quality,
Model: chatModel.Value,
Prompt: task.Prompt,
AspectRatio: task.AspectRatio,
Size: task.Size,
ResponseFormat: "url",
}
// 图片编辑
if len(task.Image) > 0 {
reqBody.Prompt = fmt.Sprintf("%s, %s", strings.Join(task.Image, " "), task.Prompt)
reqBody.Image = task.Image
}
logger.Infof("Channel:%s, API KEY:%s, BODY: %+v", apiURL, apiKey.Value, reqBody)
@@ -185,7 +182,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
// update the api key last use time
s.db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix())
var imgURL string
var data = map[string]interface{}{
var data = map[string]any{
"progress": 100,
"prompt": task.Prompt,
}
+41 -24
View File
@@ -11,6 +11,7 @@ import (
"geekai/core/types"
logger2 "geekai/logger"
"geekai/service"
"geekai/service/oss"
"geekai/store"
"geekai/store/model"
@@ -23,29 +24,31 @@ var logger = logger2.GetLogger()
// Service 即梦服务(合并了消费者功能)
type Service struct {
db *gorm.DB
redis *redis.Client
taskQueue *store.RedisQueue
client *Client
ctx context.Context
cancel context.CancelFunc
running bool
uploader *oss.UploaderManager
db *gorm.DB
redis *redis.Client
taskQueue *store.RedisQueue
client *Client
ctx context.Context
cancel context.CancelFunc
running bool
uploader *oss.UploaderManager
userService *service.UserService
}
// NewService 创建即梦服务
func NewService(db *gorm.DB, redisCli *redis.Client, uploader *oss.UploaderManager, client *Client) *Service {
func NewService(db *gorm.DB, redisCli *redis.Client, uploader *oss.UploaderManager, client *Client, userService *service.UserService) *Service {
taskQueue := store.NewRedisQueue("JimengTaskQueue", redisCli)
ctx, cancel := context.WithCancel(context.Background())
return &Service{
db: db,
redis: redisCli,
taskQueue: taskQueue,
client: client,
ctx: ctx,
cancel: cancel,
running: false,
uploader: uploader,
db: db,
redis: redisCli,
taskQueue: taskQueue,
client: client,
ctx: ctx,
cancel: cancel,
running: false,
uploader: uploader,
userService: userService,
}
}
@@ -115,7 +118,7 @@ func (s *Service) CreateTask(userId uint, req *types.JimengTaskRequest) (*model.
ReqKey: req.ReqKey,
Prompt: req.Prompt,
Params: utils.JsonEncode(req),
Status: types.JMTaskStatusInQueue,
Status: types.JMTaskStatusSubmited,
Power: req.Power,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
@@ -216,6 +219,7 @@ func (s *Service) ProcessTask(jobId uint) error {
if err := s.db.Model(&model.JimengJob{}).Where("id = ?", job.Id).Updates(map[string]any{
"task_id": resp.Data.TaskId,
"raw_data": string(rawData),
"status": types.JMTaskStatusInQueue, // 把任务状态改成排队中,以便开启轮询
"updated_at": time.Now(),
}).Error; err != nil {
logger.Errorf("update jimeng job task_id failed: %v", err)
@@ -277,12 +281,10 @@ func (s *Service) pollTaskStatus() {
for {
var jobs []model.JimengJob
s.db.Where("status IN (?)", []types.JMTaskStatus{types.JMTaskStatusGenerating, types.JMTaskStatusInQueue}).Find(&jobs)
if len(jobs) == 0 {
logger.Debugf("no jimeng task to poll, sleep 10s")
time.Sleep(10 * time.Second)
continue
}
// 找出排队中和处理中的任务进行轮询
s.db.Where("status IN (?)", []types.JMTaskStatus{
types.JMTaskStatusGenerating,
types.JMTaskStatusInQueue}).Find(&jobs)
for _, job := range jobs {
// 任务超时处理
@@ -366,6 +368,21 @@ func (s *Service) pollTaskStatus() {
}
// 找出失败的任务,并恢复其扣减算力
s.db.Where("status = ?", types.JMTaskStatusFailed).Where("power > ?", 0).Find(&jobs)
for _, job := range jobs {
err := s.userService.IncreasePower(job.UserId, job.Power, model.PowerLog{
Type: types.PowerRefund,
Model: job.ReqKey,
Remark: fmt.Sprintf("任务失败,退回算力。任务ID%d", job.Id),
})
if err != nil {
continue
}
// 更新任务状态
s.db.Model(&job).UpdateColumn("power", 0)
}
time.Sleep(5 * time.Second)
}
-224
View File
@@ -1,224 +0,0 @@
package service
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
// * Use of this source code is governed by a Apache-2.0 license
// * that can be found in the LICENSE file.
// * @Author yangjian102621@163.com
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import (
"errors"
"fmt"
"geekai/core/types"
"geekai/store/model"
"geekai/utils"
"strings"
"time"
"github.com/imroc/req/v3"
"github.com/shirou/gopsutil/host"
"gorm.io/gorm"
)
type LicenseService struct {
license *types.License
urlWhiteList []string
machineId string
db *gorm.DB
}
func NewLicenseService(sysConfig *types.SystemConfig, db *gorm.DB) *LicenseService {
var machineId string
info, err := host.Info()
if err == nil {
machineId = info.HostID
}
logger.Infof("License: %+v", sysConfig.License)
return &LicenseService{
license: &sysConfig.License,
machineId: machineId,
db: db,
}
}
type License struct {
Name string `json:"name"`
License string `json:"license"`
MachineId string `json:"mid"`
ActiveAt int64 `json:"active_at"`
ExpiredAt int64 `json:"expired_at"`
UserNum int `json:"user_num"`
Configs types.LicenseConfig `json:"configs"`
}
// ActiveLicense 激活 License
func (s *LicenseService) ActiveLicense(license string) error {
var res struct {
Code types.BizCode `json:"code"`
Message string `json:"message"`
Data License `json:"data"`
}
apiURL := fmt.Sprintf("%s/%s", types.GeekAPIURL, "api/license/active")
response, err := req.C().R().
SetBody(map[string]string{"license": license, "machine_id": s.machineId}).
SetSuccessResult(&res).Post(apiURL)
if err != nil {
return fmt.Errorf("发送激活请求失败: %v", err)
}
if response.IsErrorState() {
return fmt.Errorf("发送激活请求失败:%v", response.Status)
}
if res.Code != types.Success {
return fmt.Errorf("激活失败:%v", res.Message)
}
if res.Data.ExpiredAt > 0 && res.Data.ExpiredAt < time.Now().Unix() {
return fmt.Errorf("License 已过期")
}
s.license = &types.License{
Key: license,
MachineId: s.machineId,
Configs: res.Data.Configs,
ExpiredAt: res.Data.ExpiredAt,
IsActive: true,
}
// 保存 License 到数据库
err = s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).UpdateColumn("value", utils.JsonEncode(s.license)).Error
if err != nil {
return fmt.Errorf("保存 License 到数据库失败: %v", err)
}
return nil
}
// SyncLicense 定期同步 License
func (s *LicenseService) SyncLicense() {
go func() {
retryCounter := 0
for {
license, err := s.fetchLicense()
if err != nil {
retryCounter++
if retryCounter < 5 {
logger.Debug(err)
}
s.license.IsActive = false
} else {
s.license = license
// 保存 License 到数据库
err = s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).UpdateColumn("value", utils.JsonEncode(s.license)).Error
if err != nil {
logger.Errorf("保存 License 到数据库失败: %v", err)
}
}
urls, err := s.fetchUrlWhiteList()
if err == nil {
s.urlWhiteList = urls
}
time.Sleep(time.Second * 10)
}
}()
}
func (s *LicenseService) fetchLicense() (*types.License, error) {
var res struct {
Code types.BizCode `json:"code"`
Message string `json:"message"`
Data License `json:"data"`
}
apiURL := fmt.Sprintf("%s/%s", types.GeekAPIURL, "api/license/check")
response, err := req.C().R().
SetBody(map[string]string{"license": s.license.Key, "machine_id": s.machineId}).
SetSuccessResult(&res).Post(apiURL)
if err != nil {
return nil, fmt.Errorf("License 同步失败: %v", err)
}
if response.IsErrorState() {
return nil, fmt.Errorf("License 同步失败:%v", response.Status)
}
if res.Code != types.Success {
return nil, fmt.Errorf("License 同步失败:%v", res.Message)
}
return &types.License{
Key: res.Data.License,
MachineId: res.Data.MachineId,
Configs: res.Data.Configs,
ExpiredAt: res.Data.ExpiredAt,
IsActive: true,
}, nil
}
func (s *LicenseService) fetchUrlWhiteList() ([]string, error) {
var res struct {
Code types.BizCode `json:"code"`
Message string `json:"message"`
Data []string `json:"data"`
}
apiURL := fmt.Sprintf("%s/%s", types.GeekAPIURL, "api/license/urls")
response, err := req.C().R().SetSuccessResult(&res).Get(apiURL)
if err != nil {
return nil, fmt.Errorf("发送请求失败: %v", err)
}
if response.IsErrorState() {
return nil, fmt.Errorf("发送请求失败:%v", response.Status)
}
if res.Code != types.Success {
return nil, fmt.Errorf("获取白名单失败:%v", res.Message)
}
return res.Data, nil
}
// GetLicense 获取许可信息
func (s *LicenseService) GetLicense() *types.License {
if s.license == nil {
var config model.Config
s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).First(&config)
if config.Value != "" {
utils.JsonDecode(config.Value, &s.license)
}
}
return s.license
}
func (s *LicenseService) SetLicense(licenseKey string) {
s.license.Key = licenseKey
}
// IsValidApiURL 判断是否合法的中转 URL
func (s *LicenseService) IsValidApiURL(uri string) error {
// 获得许可授权的直接放行
if s.license.IsActive {
if s.license.MachineId != s.machineId {
return errors.New("系统使用了盗版的许可证书")
}
if time.Now().Unix() > s.license.ExpiredAt {
return errors.New("系统许可证书已经过期")
}
return nil
}
if len(s.urlWhiteList) == 0 {
urls, err := s.fetchUrlWhiteList()
if err == nil {
s.urlWhiteList = urls
}
}
for _, v := range s.urlWhiteList {
if strings.HasPrefix(uri, v) {
return nil
}
}
return fmt.Errorf("当前 API 地址 %s 不在白名单列表当中。", uri)
}
+167 -109
View File
@@ -8,16 +8,17 @@ package service
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import (
"context"
"encoding/json"
"fmt"
"geekai/core/types"
"geekai/store"
"geekai/store/model"
"strings"
"sync"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
const (
@@ -29,61 +30,29 @@ const (
// MigrationService 配置迁移服务
type MigrationService struct {
db *gorm.DB
redisClient *redis.Client
appConfig *types.AppConfig
levelDB *store.LevelDB
licenseService *LicenseService
db *gorm.DB
redisClient *redis.Client
appConfig *types.AppConfig
levelDB *store.LevelDB
}
func NewMigrationService(db *gorm.DB, redisClient *redis.Client, appConfig *types.AppConfig, levelDB *store.LevelDB, licenseService *LicenseService) *MigrationService {
func NewMigrationService(db *gorm.DB, redisClient *redis.Client, appConfig *types.AppConfig, levelDB *store.LevelDB) *MigrationService {
return &MigrationService{
db: db,
redisClient: redisClient,
appConfig: appConfig,
levelDB: levelDB,
licenseService: licenseService,
db: db,
redisClient: redisClient,
appConfig: appConfig,
levelDB: levelDB,
}
}
func (s *MigrationService) StartMigrate() {
// 表结构同步必须在对外服务前完成,避免缺列导致业务报错
s.TableMigration()
go func() {
s.MigrateConfig(s.appConfig)
s.TableMigration()
s.MigrateLicense()
_ = s.MigrateConfig(s.appConfig)
}()
}
// 迁移 License
func (s *MigrationService) MigrateLicense() {
key := "migrate:license"
if s.redisClient.Get(context.Background(), key).Val() == "1" {
logger.Info("License 已迁移,跳过迁移")
return
}
logger.Info("开始迁移 License...")
var license types.License
err := s.levelDB.Get(types.LicenseKey, &license)
if err != nil {
license = types.License{
Key: "",
MachineId: "",
Configs: types.LicenseConfig{UserNum: 0, DeCopy: false},
ExpiredAt: 0,
IsActive: false,
}
}
logger.Infof("迁移 License: %+v", license)
if err := s.saveConfig(types.ConfigKeyLicense, license); err != nil {
logger.Errorf("迁移 License 失败: %v", err)
return
}
s.licenseService.SetLicense(license.Key)
logger.Info("迁移 License 完成")
s.redisClient.Set(context.Background(), key, "1", 0)
}
// 迁移配置内容
func (s *MigrationService) MigrateConfigContent() error {
// 用户协议
@@ -157,74 +126,163 @@ func (s *MigrationService) MigrateConfigContent() error {
return nil
}
// 数据表迁移
func (s *MigrationService) TableMigration() {
// 永不删除的保护列(大小写不敏感)
var protectedColumns = map[string]struct{}{
"id": {},
"created_at": {},
"updated_at": {},
}
// v4.2.7 数据表迁移
if s.db.Migrator().HasColumn(&model.JimengJob{}, "task_params") {
s.db.Migrator().RenameColumn(&model.JimengJob{}, "task_params", "params")
}
// 新数据表
if !s.db.Migrator().HasTable(&model.Moderation{}) {
s.db.AutoMigrate(&model.Moderation{})
}
// 订单字段整理
if s.db.Migrator().HasColumn(&model.Order{}, "pay_type") {
s.db.Migrator().RenameColumn(&model.Order{}, "pay_type", "channel")
}
if !s.db.Migrator().HasColumn(&model.Order{}, "checked") {
s.db.Migrator().AddColumn(&model.Order{}, "checked")
}
// 重命名 config 表字段
if s.db.Migrator().HasColumn(&model.Config{}, "config_json") {
s.db.Migrator().RenameColumn(&model.Config{}, "config_json", "value")
}
if s.db.Migrator().HasColumn(&model.Config{}, "marker") {
s.db.Migrator().RenameColumn(&model.Config{}, "marker", "name")
}
if s.db.Migrator().HasIndex(&model.Config{}, "idx_chatgpt_configs_key") {
s.db.Migrator().DropIndex(&model.Config{}, "idx_chatgpt_configs_key")
}
if s.db.Migrator().HasIndex(&model.Config{}, "marker") {
s.db.Migrator().DropIndex(&model.Config{}, "marker")
}
// 手动删除字段
if s.db.Migrator().HasColumn(&model.Order{}, "deleted_at") {
s.db.Migrator().DropColumn(&model.Order{}, "deleted_at")
}
if s.db.Migrator().HasColumn(&model.ChatItem{}, "deleted_at") {
s.db.Migrator().DropColumn(&model.ChatItem{}, "deleted_at")
}
if s.db.Migrator().HasColumn(&model.ChatMessage{}, "deleted_at") {
s.db.Migrator().DropColumn(&model.ChatMessage{}, "deleted_at")
}
if s.db.Migrator().HasColumn(&model.User{}, "chat_config") {
s.db.Migrator().DropColumn(&model.User{}, "chat_config")
}
if s.db.Migrator().HasColumn(&model.ChatModel{}, "category") {
s.db.Migrator().DropColumn(&model.ChatModel{}, "category")
}
if s.db.Migrator().HasColumn(&model.ChatModel{}, "description") {
s.db.Migrator().DropColumn(&model.ChatModel{}, "description")
}
if s.db.Migrator().HasColumn(&model.Product{}, "discount") {
s.db.Migrator().DropColumn(&model.Product{}, "discount")
}
if s.db.Migrator().HasColumn(&model.Product{}, "days") {
s.db.Migrator().DropColumn(&model.Product{}, "days")
}
if s.db.Migrator().HasColumn(&model.Product{}, "app_url") {
s.db.Migrator().DropColumn(&model.Product{}, "app_url")
}
if s.db.Migrator().HasColumn(&model.Product{}, "url") {
s.db.Migrator().DropColumn(&model.Product{}, "url")
// allModels 全部需要同步的数据表 model
func allModels() []any {
return []any{
&model.AdminUser{},
&model.ApiKey{},
&model.AppType{},
&model.ChatApp{},
&model.ChatItem{},
&model.ChatMessage{},
&model.ChatModel{},
&model.Config{},
&model.DallJob{},
&model.File{},
&model.Function{},
&model.InviteCode{},
&model.InviteLog{},
&model.JimengJob{},
&model.Menu{},
&model.MidJourneyJob{},
&model.Moderation{},
&model.Order{},
&model.PowerLog{},
&model.Product{},
&model.Redeem{},
&model.SdJob{},
&model.SunoJob{},
&model.User{},
&model.UserLoginLog{},
&model.VideoJob{},
}
}
// 数据表迁移:先处理字段重命名(保数据),再全量同步 schema
func (s *MigrationService) TableMigration() {
logger.Info("开始数据表迁移...")
s.renameColumns()
if err := s.SyncAllModels(); err != nil {
logger.Errorf("同步数据表字段失败: %v", err)
return
}
logger.Info("数据表迁移完成")
}
// renameColumns 只处理「改名」场景:删旧加新会丢数据,必须先 Rename
func (s *MigrationService) renameColumns() {
m := s.db.Migrator()
if m.HasColumn(&model.JimengJob{}, "task_params") {
_ = m.RenameColumn(&model.JimengJob{}, "task_params", "params")
}
if m.HasColumn(&model.Order{}, "pay_type") {
_ = m.RenameColumn(&model.Order{}, "pay_type", "channel")
}
if m.HasColumn(&model.Config{}, "config_json") {
_ = m.RenameColumn(&model.Config{}, "config_json", "value")
}
if m.HasColumn(&model.Config{}, "marker") {
_ = m.RenameColumn(&model.Config{}, "marker", "name")
}
if m.HasIndex(&model.Config{}, "idx_chatgpt_configs_key") {
_ = m.DropIndex(&model.Config{}, "idx_chatgpt_configs_key")
}
if m.HasIndex(&model.Config{}, "marker") {
_ = m.DropIndex(&model.Config{}, "marker")
}
}
// SyncAllModels 按 model 定义同步所有数据表:缺列新建,多余列删除
func (s *MigrationService) SyncAllModels() error {
var firstErr error
for _, m := range allModels() {
if err := s.syncModel(m); err != nil {
logger.Errorf("同步 model %T 失败: %v", m, err)
if firstErr == nil {
firstErr = err
}
}
}
return firstErr
}
func (s *MigrationService) syncModel(dst any) error {
tableName := s.tableName(dst)
if err := s.db.AutoMigrate(dst); err != nil {
return fmt.Errorf("AutoMigrate %s: %w", tableName, err)
}
if err := s.dropUnusedColumns(dst); err != nil {
return fmt.Errorf("drop unused columns %s: %w", tableName, err)
}
logger.Infof("已同步数据表: %s", tableName)
return nil
}
func (s *MigrationService) dropUnusedColumns(dst any) error {
dbCols, err := s.db.Migrator().ColumnTypes(dst)
if err != nil {
return err
}
modelCols, err := s.modelColumnNames(dst)
if err != nil {
return err
}
for _, col := range dbCols {
name := col.Name()
if s.isProtectedColumn(name) {
continue
}
if _, ok := modelCols[strings.ToLower(name)]; ok {
continue
}
logger.Infof("删除多余字段: %s.%s", s.tableName(dst), name)
if err := s.db.Migrator().DropColumn(dst, name); err != nil {
return fmt.Errorf("DropColumn %s: %w", name, err)
}
}
return nil
}
func (s *MigrationService) modelColumnNames(dst any) (map[string]struct{}, error) {
parsed, err := schema.Parse(dst, &schemaCache, s.db.Config.NamingStrategy)
if err != nil {
return nil, err
}
cols := make(map[string]struct{}, len(parsed.Fields))
for _, field := range parsed.Fields {
if field.DBName == "" || field.IgnoreMigration {
continue
}
cols[strings.ToLower(field.DBName)] = struct{}{}
}
return cols, nil
}
func (s *MigrationService) isProtectedColumn(name string) bool {
_, ok := protectedColumns[strings.ToLower(name)]
return ok
}
func (s *MigrationService) tableName(dst any) string {
stmt := &gorm.Statement{DB: s.db}
if err := stmt.Parse(dst); err != nil {
return fmt.Sprintf("%T", dst)
}
return stmt.Schema.Table
}
// schema.Parse 进程内复用的 schema cache
var schemaCache sync.Map
// 迁移配置数据
func (s *MigrationService) MigrateConfig(config *types.AppConfig) error {
+5 -12
View File
@@ -13,7 +13,6 @@ import (
"fmt"
"geekai/core/types"
logger2 "geekai/logger"
"geekai/service"
"geekai/store/model"
"geekai/utils"
"github.com/imroc/req/v3"
@@ -26,9 +25,8 @@ import (
// Client MidJourney client
type Client struct {
client *req.Client
licenseService *service.LicenseService
db *gorm.DB
client *req.Client
db *gorm.DB
}
type ImageReq struct {
@@ -77,11 +75,10 @@ type QueryRes struct {
var logger = logger2.GetLogger()
func NewClient(licenseService *service.LicenseService, db *gorm.DB) *Client {
func NewClient(db *gorm.DB) *Client {
return &Client{
client: req.C().SetTimeout(time.Minute).SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"),
licenseService: licenseService,
db: db,
client: req.C().SetTimeout(time.Minute).SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"),
db: db,
}
}
@@ -198,10 +195,6 @@ func (c *Client) doRequest(body interface{}, apiPath string, channel string) (Im
return ImageRes{}, fmt.Errorf("no available MidJourney api key: %v", err)
}
if err = c.licenseService.IsValidApiURL(apiKey.ApiURL); err != nil {
return ImageRes{}, err
}
apiURL := fmt.Sprintf("%s/%s", apiKey.ApiURL, apiPath)
logger.Info("API URL: ", apiURL)
r, err := req.C().R().
+90
View File
@@ -0,0 +1,90 @@
package sora
import (
"encoding/json"
"errors"
"geekai/service/oss"
"geekai/store/vo"
"geekai/utils"
"io"
"net/http"
"path/filepath"
"regexp"
"time"
logger2 "geekai/logger"
)
var logger = logger2.GetLogger()
type SoraService struct {
uploadManager *oss.UploaderManager
}
func NewSoraService(uploadManager *oss.UploaderManager) *SoraService {
return &SoraService{
uploadManager: uploadManager,
}
}
// 下载视频地址
func (s *SoraService) DownloadVideoURL(text string) (*vo.File, error) {
videoDataURL, err := s.ExtractVideoURL(text)
if err != nil {
return nil, err
}
// 获取 JSON 数据
resp, err := http.Get(videoDataURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// 解析视频下载地址
var videoData map[string]any
err = json.Unmarshal(body, &videoData)
if err != nil {
return nil, err
}
if v, ok := videoData["url"].(string); ok && v != "" {
logger.Infof("try to download video: %s", v)
videoURL, err := s.uploadManager.GetUploadHandler().PutUrlFile(v, ".mp4", true)
if err != nil {
return nil, err
}
// 获取文件大小
size, _ := utils.GetFileSize(videoURL)
name := filepath.Base(videoURL)
return &vo.File{
Name: name,
ObjKey: name,
URL: videoURL,
Ext: ".mp4",
Size: size,
CreatedAt: time.Now().Unix(),
}, nil
}
return nil, errors.New("no video URL found: " + string(body))
}
// 从文本中提取视频URL
func (s *SoraService) ExtractVideoURL(text string) (string, error) {
// 提取原始 JSON 数据地址
//[原始数据](https://asyncdata.net/source/task_01k8pye324ef7t6heq6jyaxbfe) 类似这样的链接
re := regexp.MustCompile(`\[原始数据\]\((https?://.*?)\)`)
matches := re.FindAllStringSubmatch(text, 1)
if len(matches) == 0 {
return "", errors.New("no video URL found")
}
return matches[0][1], nil
}
+1
View File
@@ -70,6 +70,7 @@ func (s *UserService) DecreasePower(userId uint, power int, log model.PowerLog)
return fmt.Errorf("扣减算力失败:%v", err)
}
tx.Where("id", userId).First(&user)
err = tx.Create(&model.PowerLog{
UserId: user.Id,
Username: user.Username,
+2 -2
View File
@@ -39,9 +39,9 @@ const (
LoginStatusExpired = "expired" // 登录失效,需要重新登录
)
func NewWxLoginService(config types.WxLoginConfig, redisClient *redis.Client) *WxLoginService {
func NewWxLoginService(sysConfig *types.SystemConfig, redisClient *redis.Client) *WxLoginService {
return &WxLoginService{
config: config,
config: sysConfig.WxLogin,
client: req.C().SetTimeout(10 * time.Second),
redisClient: redisClient,
}
+1 -1
View File
@@ -11,7 +11,7 @@ type AdminUser struct {
Salt string `gorm:"column:salt;type:char(12);not null;comment:密码盐" json:"salt"`
Status bool `gorm:"column:status;type:tinyint(1);not null;comment:当前状态" json:"status"`
LastLoginAt int64 `gorm:"column:last_login_at;type:int;not null;comment:最后登录时间" json:"last_login_at"`
LastLoginIp string `gorm:"column:last_login_ip;type:char(16);not null;comment:最后登录 IP" json:"last_login_ip"`
LastLoginIp string `gorm:"column:last_login_ip;type:char(32);not null;comment:最后登录 IP" json:"last_login_ip"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;comment:创建时间" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null;comment:更新时间" json:"updated_at"`
}
+1
View File
@@ -9,6 +9,7 @@ type Function struct {
Token string `gorm:"column:token;type:varchar(255);comment:API授权token" json:"token"`
Action string `gorm:"column:action;type:varchar(255);comment:函数处理 API" json:"action"`
Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启用" json:"enabled"`
Power int `gorm:"column:power;type:int(11);not null;default:0;comment:消费算力点数" json:"power"`
}
func (m *Function) TableName() string {
+1 -1
View File
@@ -21,7 +21,7 @@ type User struct {
ChatModels string `gorm:"column:chat_models_json;type:text;default:null;comment:AI模型 json" json:"chat_models"`
LastLoginAt int64 `gorm:"column:last_login_at;type:int;not null;comment:最后登录时间" json:"last_login_at"`
Vip bool `gorm:"column:vip;type:tinyint(1);not null;default:0;comment:是否会员" json:"vip"`
LastLoginIp string `gorm:"column:last_login_ip;type:char(16);not null;comment:最后登录 IP" json:"last_login_ip"`
LastLoginIp string `gorm:"column:last_login_ip;type:char(32);not null;comment:最后登录 IP" json:"last_login_ip"`
OpenId string `gorm:"column:openid;type:varchar(100);comment:第三方登录账号ID" json:"openid"`
Platform string `gorm:"column:platform;type:varchar(30);comment:登录平台" json:"platform"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`
+1 -1
View File
@@ -8,7 +8,7 @@ type UserLoginLog struct {
Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户ID" json:"user_id"`
Username string `gorm:"column:username;type:varchar(30);not null;comment:用户名" json:"username"`
LoginIp string `gorm:"column:login_ip;type:char(16);not null;comment:登录IP" json:"login_ip"`
LoginIp string `gorm:"column:login_ip;type:char(32);not null;comment:登录IP" json:"login_ip"`
LoginAddress string `gorm:"column:login_address;type:varchar(30);not null;comment:登录地址" json:"login_address"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"`
+1
View File
@@ -20,4 +20,5 @@ type Function struct {
Action string `json:"action"`
Token string `json:"token"`
Enabled bool `json:"enabled"`
Power int `json:"power"`
}
-10
View File
@@ -1,10 +0,0 @@
package test
import (
"fmt"
"testing"
)
func Test(t *testing.T) {
fmt.Println("test")
}
+13
View File
@@ -101,3 +101,16 @@ func GetImgExt(filename string) string {
}
return ext
}
func GetFileSize(url string) (int64, error) {
resp, err := http.Get(url)
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return 0, err
}
return int64(len(body)), nil
}
+6 -6
View File
@@ -14,15 +14,15 @@ npm run build
cd ../build
# remove docker image if exists
docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-api:$version-$arch
docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:$version-$arch
# build docker image for Geek-AI API
docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-api:$version-$arch -f dockerfile-api-go ../
docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:$version-$arch -f dockerfile-api-go ../
# build docker image for Geek-AI-web
docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-web:$version-$arch
docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-web:$version-$arch -f dockerfile-web-$arch ../
docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:$version-$arch
docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:$version-$arch -f dockerfile-web-$arch ../
if [ "$3" = "push" ];then
docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-api:$version-$arch
docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-web:$version-$arch
docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:$version-$arch
docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:$version-$arch
fi
+3 -3
View File
@@ -18,10 +18,10 @@ SET time_zone = "+00:00";
/*!40101 SET NAMES utf8mb4 */;
--
-- 数据库: `geekai_plus`
-- 数据库: `geekai`
--
CREATE DATABASE IF NOT EXISTS `geekai_plus` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
USE `geekai_plus`;
CREATE DATABASE IF NOT EXISTS `geekai` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
USE `geekai`;
-- --------------------------------------------------------
+46
View File
@@ -0,0 +1,46 @@
map $http_upgrade $connection_upgrade {
default upgrade;
'websocket' upgrade;
}
server {
# listen 443 ssl;
listen 8080;
# server_name www.chatgpt.com; #替换成你自己的域名
# ssl_certificate xxx.pem; # 替换成自己的 SSL 证书
# ssl_certificate_key xxx.key;
# ssl_session_timeout 5m;
# ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_prefer_server_ciphers on;
# 日志地址
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
index index.html;
root /var/www/app/dist; # 这里改成前端静态页面的地址
location / {
try_files $uri $uri/ /index.html;
# 后端 API 的转发
location /api/ {
proxy_http_version 1.1;
proxy_connect_timeout 300s;
proxy_read_timeout 300s;
proxy_send_timeout 12s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://geekai-api:5678;
}
# 静态资源转发
location /static/ {
proxy_pass http://geekai-api:5678;
}
}
}
+83
View File
@@ -0,0 +1,83 @@
version: '3'
services:
# mysql
geekai-mysql:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/mysql:8.0.33
container_name: geekai-mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
- MYSQL_ROOT_PASSWORD=mhSCk0NheGhmtsha
ports:
- '3307:3306'
volumes:
- ./conf/mysql/my.cnf:/etc/mysql/my.cnf
- ./data/mysql/data:/var/lib/mysql
- ./logs/mysql:/var/log/mysql
- ./data/mysql/init.d:/docker-entrypoint-initdb.d/
healthcheck:
test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost']
interval: 5s
timeout: 10s
retries: 10
# redis
geekai-redis:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/redis:6.0.6
restart: always
container_name: geekai-redis
command: redis-server --requirepass mhSCk0NheGhmtsha
volumes:
- ./data/redis:/data
ports:
- '6380:6379'
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 3s
timeout: 10s
retries: 5
geekai-tika:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/tika:latest
container_name: geekai-tika
restart: always
ports:
- '9999:9998'
# 后端 API 程序
geekai-api:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:v4.2.8-amd64
container_name: geekai-api
restart: always
depends_on:
geekai-mysql:
condition: service_healthy
geekai-redis:
condition: service_healthy
environment:
- DEBUG=false
- LOG_LEVEL=info
- CONFIG_FILE=config.toml
ports:
- '5678:5678'
volumes:
- /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime
- ./conf/config.toml:/var/www/app/config.toml
- ./logs/app:/var/www/app/logs
- ./static:/var/www/app/static
- ./data/leveldb:/var/www/app/data
# 前端应用
geekai-web:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:v4.2.8-amd64
container_name: geekai-web
restart: always
depends_on:
- geekai-api
ports:
- '8080:8080'
volumes:
- ./logs/nginx:/var/log/nginx
- ./conf/nginx/conf.d:/etc/nginx/conf.d
- ./conf/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./conf/nginx/ssl:/etc/nginx/ssl
+1 -1
View File
@@ -6,7 +6,7 @@ VITE_ADMIN_USER=admin
VITE_ADMIN_PASS=admin123
VITE_KEY_PREFIX=GeekAI_DEV_
VITE_TITLE="Geek-AI 创作系统"
VITE_VERSION=v4.2.7
VITE_VERSION=v4.2.8
VITE_DOCS_URL=https://docs.geekai.me
VITE_GITHUB_URL=https://github.com/yangjian102621/geekai
VITE_GITEE_URL=https://gitee.com/blackfox/geekai
+1 -1
View File
@@ -1,7 +1,7 @@
VITE_API_HOST=
VITE_WS_HOST=
VITE_KEY_PREFIX=GeekAI_
VITE_VERSION=v4.2.7
VITE_VERSION=v4.2.8
VITE_TITLE="Geek-AI 创作系统"
VITE_DOCS_URL=https://docs.geekai.me
VITE_GITHUB_URL=https://github.com/yangjian102621/geekai
+4 -3
View File
@@ -83,7 +83,7 @@
width: 100%;
}
.el-input__wrapper {
background: var(--card-bg);
//background: var(--card-bg);
}
.el-dialog__title {
font-weight: bold;
@@ -138,7 +138,8 @@
border: none;
}
.el-tag, .el-tag.el-tag--primary {
.el-tag,
.el-tag.el-tag--primary {
--el-tag-bg-color: #f0ebff;
}
.box-card {
@@ -148,4 +149,4 @@
}
.el-table th.el-table__cell {
background-color: var(--chat-bg);
}
}
+3 -2
View File
@@ -189,10 +189,11 @@
}
blockquote {
border-left: 4px solid #42b983;
border-left: 5px solid #c1c1c1;
padding: 10px 15px;
color: #777;
background-color: rgba(66, 185, 131, 0.1);
background-color: rgba(193, 193, 193, 0.2);
border-radius: 10px;
}
table {
+139
View File
@@ -0,0 +1,139 @@
<template>
<div v-if="list.length" class="space-y-3 mb-2">
<div
v-for="(f, idx) in list"
:key="f.url || idx"
class="flex !items-start flex-col justify-center gap-3 p-3 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg"
>
<!-- Image -->
<div
v-if="isImageFile(f)"
class="flex-shrink-0 bg-gray-100 max-w-[500px] max-h-[500px] dark:bg-gray-700 rounded-lg overflow-hidden"
>
<el-image
:src="f.url"
fit="cover"
:preview-src-list="imageUrls"
:initial-index="imageIndexMap.get(f.url) || 0"
hide-on-click-modal
:z-index="3000"
class="w-full h-full"
/>
</div>
<!-- Video -->
<div
v-else-if="isVideoFile(f)"
class="flex-shrink-0 bg-gray-100 max-w-[500px] dark:bg-gray-700 rounded-lg overflow-hidden"
>
<video :src="f.url" controls preload="metadata" class="w-full h-full object-cover"></video>
</div>
<!-- Audio -->
<div
v-else-if="isAudioFile(f)"
class="flex-shrink-0 bg-gray-100 dark:bg-gray-700 rounded-lg flex items-center justify-center"
>
<audio :src="f.url" controls preload="metadata"></audio>
</div>
<!-- Other Files -->
<div
v-else
class="flex-shrink-0 w-20 h-20 bg-gray-100 dark:bg-gray-700 rounded-lg flex items-center justify-center"
>
<img :src="GetFileIcon(extOf(f))" class="w-20 h-20" alt="file" />
</div>
<div class="flex w-full flex-row min-w-0 justify-between items-center">
<!-- File Info -->
<div class="text-xs text-gray-500 dark:text-gray-400 mt-1">
{{ extOf(f).replace('.', '').toUpperCase() || 'FILE' }} ·
{{ FormatFileSize(f.size || 0) }}
</div>
<!-- Download Button -->
<div class="flex-shrink-0">
<el-tooltip class="box-item" effect="dark" content="下载" placement="top">
<i
class="iconfont icon-download !text-sm cursor-pointer"
v-if="!f.downloading"
@click="downloadFile(f)"
></i>
<el-image src="/images/loading.gif" class="w-4 h-4" fit="cover" v-else />
</el-tooltip>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { FormatFileSize, GetFileIcon } from '@/store/system'
import { computed } from 'vue'
import { httpDownload } from '@/utils/http'
import { replaceImg } from '@/utils/libs'
const props = defineProps({
files: {
type: Array,
default: () => [],
},
})
const list = computed(() => props.files || [])
const normalizeExt = (ext) => (ext || '').toLowerCase()
const urlExt = (url) => {
if (!url) return ''
try {
const path = url.split('?')[0]
const dot = path.lastIndexOf('.')
return dot >= 0 ? path.substring(dot) : ''
} catch (_) {
return ''
}
}
const extOf = (f) => normalizeExt(f.ext || urlExt(f.url))
const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg'])
const VIDEO_EXTS = new Set(['.mp4', '.webm', '.ogg', '.mov', '.m4v'])
const AUDIO_EXTS = new Set(['.mp3', '.wav', '.ogg', '.aac', '.m4a'])
const isImageFile = (f) => IMAGE_EXTS.has(extOf(f))
const isVideoFile = (f) => VIDEO_EXTS.has(extOf(f))
const isAudioFile = (f) => AUDIO_EXTS.has(extOf(f))
// image preview urls and index mapping for el-image gallery
const imageUrls = computed(() => list.value.filter((f) => isImageFile(f)).map((f) => f.url))
const imageIndexMap = computed(() => {
const map = new Map()
imageUrls.value.forEach((u, i) => map.set(u, i))
return map
})
const downloadFile = async (item) => {
const url = replaceImg(item.url)
const downloadURL = `/api/download?url=${url}`
const urlObj = new URL(url)
const fileName = urlObj.pathname.split('/').pop()
item.downloading = true
try {
const response = await httpDownload(downloadURL)
const blob = new Blob([response.data])
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = fileName
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(link.href)
item.downloading = false
} catch (error) {
showMessageError('下载失败')
item.downloading = false
}
}
</script>
+1 -1
View File
@@ -9,7 +9,7 @@
<div v-if="files && files.length > 0" class="file-list-box">
<div v-for="file in files" :key="file.url">
<div class="image" v-if="isImage(file.ext)">
<el-image :src="file.url" fit="cover" />
<el-image :src="file.url" fit="cover" :preview-src-list="[file.url]" />
</div>
<div class="item" v-else>
<div class="icon">
+25 -4
View File
@@ -6,7 +6,7 @@
<img :src="data.icon" alt="ChatGPT" />
</div>
<div class="chat-item">
<div class="content-wrapper">
<div class="content-wrapper flex-col">
<div
class="content"
v-html="md.render(processContent(data.content.text))"
@@ -15,6 +15,12 @@
<div class="content flex justify-start items-center" v-else>
<span class="mr-2">AI 思考中</span> <Thinking :duration="1.5" />
</div>
<div class="mt-3">
<AttachmentList
v-if="data.content && data.content.files && data.content.files.length"
:files="data.content.files"
/>
</div>
</div>
<div
class="flex text-gray-500 text-sm py-2 items-center space-x-2"
@@ -79,6 +85,7 @@ import emoji from 'markdown-it-emoji'
import mathjaxPlugin from 'markdown-it-mathjax3'
import { nextTick, onMounted, reactive, ref, watchEffect } from 'vue'
import Thinking from './Thinking.vue'
import AttachmentList from './AttachmentList.vue'
// eslint-disable-next-line no-undef,no-unused-vars
const props = defineProps({
data: {
@@ -118,7 +125,7 @@ const md = new MarkdownIt({
// /
const copyBtn = `<div class="flex">
<span class="text-[12px] mr-2 text-[#00e0e0] cursor-pointer expand-btn" data-code-id="${codeIndex}" onclick="window.toggleCodeBlock('${codeIndex}')">收起</span>
<span class="copy-code-btn" data-clipboard-action="copy" data-clipboard-target="#copy-target-${codeIndex}">复制</span>
<span class="copy-code-btn text-blue-500 text-sm cursor-pointer" data-clipboard-action="copy" data-clipboard-target="#copy-target-${codeIndex}">复制</span>
</div><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy-target-${codeIndex}">${str.replace(
/<\/textarea>/g,
'&lt;/textarea>'
@@ -127,7 +134,7 @@ const md = new MarkdownIt({
let preCode = ''
//
if (lang && hl.getLanguage(lang)) {
langHtml = `<span class="lang-name">${lang}</span>`
langHtml = `<span class="lang-name text-white">${lang}</span>`
preCode = hl.highlight(str, { language: lang }).value
} else {
preCode = md.utils.escapeHtml(str)
@@ -137,7 +144,7 @@ const md = new MarkdownIt({
return `<pre class="code-container flex flex-col code-expanded" data-code-id="${codeIndex}">
<div class="flex justify-between bg-[#50505a] w-full rounded-tl-[10px] rounded-tr-[10px] px-3 py-1">${langHtml}${copyBtn}</div>
<code class="language-${lang} hljs">${preCode}</code>
<span class="copy-code-btn absolute right-3 bottom-3" data-clipboard-action="copy" data-clipboard-target="#copy-target-${codeIndex}">复制</span></pre>`
<span class="copy-code-btn absolute right-3 bottom-3 text-white text-sm cursor-pointer" data-clipboard-action="copy" data-clipboard-target="#copy-target-${codeIndex}">复制</span></pre>`
},
})
md.use(mathjaxPlugin)
@@ -313,6 +320,16 @@ const setupCodeBlockEvents = () => {
border-radius: 0 10px 10px 10px;
width: 100%;
.code-expanded {
position: relative;
}
pre code.hljs {
min-width: 100%;
max-width: 100%;
word-break: break-word;
}
p:first-child {
margin-top: 0;
}
@@ -320,6 +337,10 @@ const setupCodeBlockEvents = () => {
p:last-child {
margin-bottom: 0;
}
think {
border: 1px solid var(--el-border-color);
}
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
<el-container class="chat-file-list">
<div v-for="file in fileList" :key="file.url">
<div class="image" v-if="isImage(file.ext)">
<el-image :src="file.url" fit="cover" />
<el-image :src="file.url" fit="cover" :preview-src-list="[file.url]" />
<div class="action">
<el-icon @click="removeFile(file)"><CircleCloseFilled /></el-icon>
</div>
+22 -20
View File
@@ -11,22 +11,26 @@
<span class="mr-2">{{ copyRight }}</span>
</div>
<div class="flex justify-center text-sm">
<a href="https://beian.miit.gov.cn" target="_blank">ICP备案{{ icp }}</a>
<span>|</span>
<img :src="gaBeianImg" class="w-4 h-4 mx-1" alt="beian" />
<a
:href="`http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=${getCodeNum(
gaBeian
)}`"
target="_blank"
>{{ gaBeian }}</a
>
<template v-if="icp">
<a href="https://beian.miit.gov.cn" target="_blank">ICP备案{{ icp }}</a>
</template>
<template v-if="gaBeian">
<span>|</span>
<img :src="gaBeianImg" class="w-4 h-4 mx-1" alt="beian" />
<a
:href="`http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=${getCodeNum(
gaBeian
)}`"
target="_blank"
>{{ gaBeian }}</a
>
</template>
</div>
</div>
</div>
</template>
<script setup>
import { getLicenseInfo, getSystemInfo } from '@/store/cache'
import { getSystemInfo } from '@/store/cache'
import { showMessageError } from '@/utils/dialog'
import { ref } from 'vue'
@@ -36,7 +40,6 @@ const gitURL = ref(import.meta.env.VITE_GITHUB_URL)
const copyRight = ref('')
const icp = ref('')
const gaBeian = ref('')
const license = ref({})
const props = defineProps({
textColor: {
type: String,
@@ -61,14 +64,6 @@ getSystemInfo()
showMessageError('获取系统配置失败:' + e.message)
})
getLicenseInfo()
.then((res) => {
license.value = res.data
})
.catch((e) => {
showMessageError('获取 License 失败:' + e.message)
})
//
const getCodeNum = (code) => {
//
@@ -86,6 +81,7 @@ const getCodeNum = (code) => {
<style scoped lang="scss">
.foot-container {
// PC fixed
position: fixed;
left: 0;
bottom: 0;
@@ -95,6 +91,12 @@ const getCodeNum = (code) => {
// background: var(--theme-bg);
margin-top: -4px;
@media (max-width: 768px) {
margin-top: 2rem !important;
position: static;
margin-top: 0;
}
.footer {
// max-width: 400px;
text-align: center;
+41 -19
View File
@@ -21,15 +21,19 @@
</div>
<div v-else class="upload-item single-image-item">
<el-image :src="imageList[0]" fit="cover" class="upload-image" />
<div class="upload-overlay">
<el-button
type="danger"
:icon="Delete"
size="small"
circle
@click="removeImage(0)"
class="remove-btn"
/>
<div class="upload-overlay flex items-center justify-center space-x-2">
<el-tooltip content="删除" placement="top">
<i
class="iconfont icon-remove text-base text-red-500 cursor-pointer"
@click="removeImage(index)"
></i>
</el-tooltip>
<el-tooltip content="预览" placement="top">
<i
class="iconfont icon-eye-open text-lg text-white cursor-pointer"
@click="previewImage(index)"
></i>
</el-tooltip>
</div>
</div>
</div>
@@ -40,15 +44,19 @@
<div class="upload-list" v-if="imageList.length > 0">
<div v-for="(image, index) in imageList" :key="index" class="upload-item">
<el-image :src="image" fit="cover" class="upload-image" />
<div class="upload-overlay">
<el-button
type="danger"
:icon="Delete"
size="small"
circle
@click="removeImage(index)"
class="remove-btn"
/>
<div class="upload-overlay flex items-center justify-center space-x-2">
<el-tooltip content="删除" placement="top">
<i
class="iconfont icon-remove text-base text-red-500 cursor-pointer"
@click="removeImage(index)"
></i>
</el-tooltip>
<el-tooltip content="预览" placement="top">
<i
class="iconfont icon-eye-open text-lg text-white cursor-pointer"
@click="previewImage(index)"
></i>
</el-tooltip>
</div>
</div>
<!-- 上传按钮 -->
@@ -100,13 +108,20 @@
:stroke-width="4"
class="upload-progress"
/>
<!-- 图片预览弹窗 -->
<el-image-viewer
v-if="previewVisible"
:url-list="[previewImageSrc]"
@close="previewVisible = false"
/>
</div>
</template>
<script setup>
import { httpPost } from '@/utils/http'
import { replaceImg } from '@/utils/libs'
import { Delete, UploadFilled } from '@element-plus/icons-vue'
import { UploadFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { computed, ref } from 'vue'
@@ -138,6 +153,8 @@ const emit = defineEmits(['update:modelValue', 'upload-success'])
//
const uploading = ref(false)
const uploadProgress = ref(0)
const previewVisible = ref(false)
const previewImageSrc = ref('')
//
const imageList = computed({
@@ -225,6 +242,11 @@ const removeImage = (index) => {
newList.splice(index, 1)
imageList.value = newList
}
const previewImage = (index) => {
previewImageSrc.value = imageList.value[index]
previewVisible.value = true
}
</script>
<style lang="scss">
@@ -224,11 +224,6 @@ const items = [
index: '/admin/config/menu',
title: '菜单配置',
},
{
icon: 'license',
index: '/admin/config/license',
title: '授权激活',
},
{
icon: 'recharge',
index: '/admin/config/payment',
+8 -1
View File
@@ -16,6 +16,10 @@
</div>
</div>
<div class="mt-3">
<AttachmentList v-if="files && files.length" :files="files" />
</div>
<!-- 操作按钮区域 -->
<div class="action-buttons" v-if="showActions && orgContent">
<van-button
@@ -50,7 +54,8 @@ import emoji from 'markdown-it-emoji'
import mathjaxPlugin from 'markdown-it-mathjax3'
import { showImagePreview, showNotify } from 'vant'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import Thinking from '../Thinking.vue'
import AttachmentList from '@/components/AttachmentList.vue'
import Thinking from '@/components/Thinking.vue'
const props = defineProps({
content: {
@@ -90,6 +95,8 @@ const props = defineProps({
},
})
const files = computed(() => props.content.files || [])
const emits = defineEmits(['regenerate'])
const md = new MarkdownIt({
breaks: true,
+11 -2
View File
@@ -8,8 +8,17 @@
isImage(f.ext) ? 'p-0' : 'p-2',
]"
>
<div v-if="isImage(f.ext)" class="relative w-[56px] h-[56px] overflow-hidden rounded-lg">
<img :src="f.url" alt="img" class="w-full h-full object-cover" />
<div
v-if="isImage(f.ext)"
class="relative max-w-[200px] max-h-[200px] overflow-hidden rounded-lg"
>
<el-image
:src="f.url"
alt="img"
class="w-full h-full"
:preview-src-list="[f.url]"
fit="cover"
/>
</div>
<div v-else :class="['flex items-center', direction === 'col' ? 'w-full' : 'max-w-[240px]']">
<img :src="GetFileIcon(f.ext)" class="w-10 h-10 mr-2" />
+52
View File
@@ -0,0 +1,52 @@
<template>
<div class="flex flex-col justify-center items-center py-10 px-4 text-center">
<div class="mb-4 flex items-center justify-center">
<div class="w-14 h-14 rounded-full bg-gray-100 flex items-center justify-center shadow">
<svg class="w-7 h-7 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 11c1.657 0 3-1.567 3-3.5S13.657 4 12 4 9 5.567 9 7.5 10.343 11 12 11z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 20v-1a5 5 0 015-5h2a5 5 0 015 5v1"
/>
</svg>
</div>
</div>
<h3 class="text-lg font-semibold text-gray-800 mb-1">{{ title }}</h3>
<p class="text-gray-500 text-sm">{{ statusText }}</p>
<p v-if="description" class="text-xs text-gray-400 mt-2 max-w-sm leading-relaxed">
{{ description }}
</p>
</div>
<!-- 允许使用插槽扩展比如放置返回按钮或客服入口 -->
<slot />
</template>
<script setup>
const props = defineProps({
title: {
type: String,
default: '功能暂未开放',
},
statusText: {
type: String,
default: '该功能暂不可用,请稍后重试或联系管理员',
},
description: {
type: String,
default: '',
},
})
</script>
<style lang="scss">
/* 保留最小样式,去除无关动画与进度条 */
</style>
+222 -210
View File
@@ -6,6 +6,112 @@
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import { createRouter, createWebHistory } from 'vue-router'
import { getSystemInfo, getMenus } from '@/store/cache'
// PC 端主页面路由
const homeRoutes = {
name: 'home',
path: '/home',
redirect: '/chat',
component: () => import('@/views/Home.vue'),
children: [
{
name: 'chat',
path: '/chat',
meta: { title: '创作中心' },
component: () => import('@/views/ChatPlus.vue'),
},
{
name: 'chat-id',
path: '/chat/:id',
meta: { title: '创作中心' },
component: () => import('@/views/ChatPlus.vue'),
},
{
name: 'image-mj',
path: '/mj',
meta: { title: 'MidJourney 绘画中心' },
component: () => import('@/views/ImageMj.vue'),
},
{
name: 'image-sd',
path: '/sd',
meta: { title: 'stable diffusion 绘画中心' },
component: () => import('@/views/ImageSd.vue'),
},
{
name: 'member',
path: '/member',
meta: { title: '会员充值中心' },
component: () => import('@/views/Member.vue'),
},
{
name: 'chat-app',
path: '/apps',
meta: { title: '应用中心' },
component: () => import('@/views/ChatApps.vue'),
},
{
name: 'images',
path: '/images-wall',
meta: { title: '作品展示' },
component: () => import('@/views/ImagesWall.vue'),
},
{
name: 'user-invitation',
path: '/invite',
meta: { title: '推广计划' },
component: () => import('@/views/Invitation.vue'),
},
{
name: 'powerLog',
path: '/powerLog',
meta: { title: '消费日志' },
component: () => import('@/views/PowerLog.vue'),
},
{
name: 'xmind',
path: '/xmind',
meta: { title: '思维导图' },
component: () => import('@/views/MarkMap.vue'),
},
{
name: 'dalle',
path: '/dalle',
meta: { title: 'DALLE-3' },
component: () => import('@/views/Dalle.vue'),
},
{
name: 'suno',
path: '/suno',
meta: { title: 'Suno音乐创作' },
component: () => import('@/views/Suno.vue'),
},
{
name: 'ExternalLink',
path: '/external',
component: () => import('@/views/ExternalPage.vue'),
},
{
name: 'song',
path: '/song/:id',
meta: { title: 'Suno音乐播放' },
component: () => import('@/views/Song.vue'),
},
{
name: 'video',
path: '/video',
meta: { title: '视频创作中心' },
component: () => import('@/views/Video.vue'),
},
{
name: 'jimeng',
path: '/jimeng',
meta: { title: '即梦AI' },
component: () => import('@/views/Jimeng.vue'),
},
],
}
const routes = [
{
@@ -14,109 +120,6 @@ const routes = [
meta: { title: '首页' },
component: () => import('@/views/Index.vue'),
},
{
name: 'home',
path: '/home',
redirect: '/chat',
component: () => import('@/views/Home.vue'),
children: [
{
name: 'chat',
path: '/chat',
meta: { title: '创作中心' },
component: () => import('@/views/ChatPlus.vue'),
},
{
name: 'chat-id',
path: '/chat/:id',
meta: { title: '创作中心' },
component: () => import('@/views/ChatPlus.vue'),
},
{
name: 'image-mj',
path: '/mj',
meta: { title: 'MidJourney 绘画中心' },
component: () => import('@/views/ImageMj.vue'),
},
{
name: 'image-sd',
path: '/sd',
meta: { title: 'stable diffusion 绘画中心' },
component: () => import('@/views/ImageSd.vue'),
},
{
name: 'member',
path: '/member',
meta: { title: '会员充值中心' },
component: () => import('@/views/Member.vue'),
},
{
name: 'chat-app',
path: '/apps',
meta: { title: '应用中心' },
component: () => import('@/views/ChatApps.vue'),
},
{
name: 'images',
path: '/images-wall',
meta: { title: '作品展示' },
component: () => import('@/views/ImagesWall.vue'),
},
{
name: 'user-invitation',
path: '/invite',
meta: { title: '推广计划' },
component: () => import('@/views/Invitation.vue'),
},
{
name: 'powerLog',
path: '/powerLog',
meta: { title: '消费日志' },
component: () => import('@/views/PowerLog.vue'),
},
{
name: 'xmind',
path: '/xmind',
meta: { title: '思维导图' },
component: () => import('@/views/MarkMap.vue'),
},
{
name: 'dalle',
path: '/dalle',
meta: { title: 'DALLE-3' },
component: () => import('@/views/Dalle.vue'),
},
{
name: 'suno',
path: '/suno',
meta: { title: 'Suno音乐创作' },
component: () => import('@/views/Suno.vue'),
},
{
name: 'ExternalLink',
path: '/external',
component: () => import('@/views/ExternalPage.vue'),
},
{
name: 'song',
path: '/song/:id',
meta: { title: 'Suno音乐播放' },
component: () => import('@/views/Song.vue'),
},
{
name: 'video',
path: '/video',
meta: { title: '视频创作中心' },
component: () => import('@/views/Video.vue'),
},
{
name: 'jimeng',
path: '/jimeng',
meta: { title: '即梦AI' },
component: () => import('@/views/Jimeng.vue'),
},
],
},
{
name: 'chat-export',
path: '/chat/export',
@@ -240,12 +243,6 @@ const routes = [
meta: { title: '菜单配置' },
component: () => import('@/views/admin/settings/MenuConfig.vue'),
},
{
path: '/admin/config/license',
name: 'admin-config-license',
meta: { title: '授权激活' },
component: () => import('@/views/admin/settings/LicenseConfig.vue'),
},
{
path: '/admin/user',
name: 'admin-user',
@@ -351,106 +348,6 @@ const routes = [
],
},
{
name: 'mobile',
path: '/mobile',
meta: { title: '首页' },
component: () => import('@/views/mobile/Home.vue'),
redirect: '/mobile/index',
children: [
{
path: '/mobile/index',
name: 'mobile-index',
component: () => import('@/views/mobile/Index.vue'),
},
{
meta: { title: 'AI对话' },
path: '/mobile/chat',
name: 'mobile-chat',
component: () => import('@/views/mobile/ChatList.vue'),
},
{
meta: { title: '创作中心' },
path: '/mobile/create',
name: 'mobile-create',
component: () => import('@/views/mobile/Create.vue'),
},
{
meta: { title: '发现' },
path: '/mobile/discover',
name: 'mobile-discover',
component: () => import('@/views/mobile/Discover.vue'),
},
{
meta: { title: '个人中心' },
path: '/mobile/profile',
name: 'mobile-profile',
component: () => import('@/views/mobile/Profile.vue'),
},
{
meta: { title: '会员充值' },
path: '/mobile/member',
name: 'mobile-member',
component: () => import('@/views/mobile/Member.vue'),
},
{
meta: { title: '作品展示' },
path: '/mobile/imgWall',
name: 'mobile-img-wall',
component: () => import('@/views/mobile/pages/ImgWall.vue'),
},
{
path: '/mobile/chat/session',
name: 'mobile-chat-session',
component: () => import('@/views/mobile/ChatSession.vue'),
},
{
meta: { title: '应用中心' },
path: '/mobile/apps',
name: 'mobile-apps',
component: () => import('@/views/mobile/Apps.vue'),
},
// 新增的功能页面路由
{
meta: { title: '消费日志' },
path: '/mobile/power-log',
name: 'mobile-power-log',
component: () => import('@/views/mobile/PowerLog.vue'),
},
{
meta: { title: '推广计划' },
path: '/mobile/invite',
name: 'mobile-invite',
component: () => import('@/views/mobile/Invite.vue'),
},
{
meta: { title: '设置' },
path: '/mobile/settings',
name: 'mobile-settings',
component: () => import('@/views/mobile/Settings.vue'),
},
{
meta: { title: 'Suno音乐创作' },
path: '/mobile/suno',
name: 'mobile-suno',
component: () => import('@/views/mobile/SunoCreate.vue'),
},
{
meta: { title: '视频生成' },
path: '/mobile/video',
name: 'mobile-video',
component: () => import('@/views/mobile/VideoCreate.vue'),
},
{
meta: { title: '即梦AI' },
path: '/mobile/jimeng',
name: 'mobile-jimeng',
component: () => import('@/views/mobile/JimengCreate.vue'),
},
],
},
{
name: 'test',
path: '/test',
@@ -466,6 +363,106 @@ const routes = [
},
]
const mobileRoutes = {
name: 'mobile',
path: '/mobile',
meta: { title: '首页' },
component: () => import('@/views/mobile/Home.vue'),
redirect: '/mobile/index',
children: [
{
path: '/mobile/index',
name: 'mobile-index',
component: () => import('@/views/mobile/Index.vue'),
},
{
meta: { title: 'AI对话' },
path: '/mobile/chat',
name: 'mobile-chat',
component: () => import('@/views/mobile/ChatList.vue'),
},
{
meta: { title: '创作中心' },
path: '/mobile/create',
name: 'mobile-create',
component: () => import('@/views/mobile/Create.vue'),
},
{
meta: { title: '发现' },
path: '/mobile/discover',
name: 'mobile-discover',
component: () => import('@/views/mobile/Discover.vue'),
},
{
meta: { title: '个人中心' },
path: '/mobile/profile',
name: 'mobile-profile',
component: () => import('@/views/mobile/Profile.vue'),
},
{
meta: { title: '会员充值' },
path: '/mobile/member',
name: 'mobile-member',
component: () => import('@/views/mobile/Member.vue'),
},
{
meta: { title: '作品展示' },
path: '/mobile/imgWall',
name: 'mobile-img-wall',
component: () => import('@/views/mobile/pages/ImgWall.vue'),
},
{
path: '/mobile/chat/session',
name: 'mobile-chat-session',
component: () => import('@/views/mobile/ChatSession.vue'),
},
{
meta: { title: '应用中心' },
path: '/mobile/apps',
name: 'mobile-apps',
component: () => import('@/views/mobile/Apps.vue'),
},
// 新增的功能页面路由
{
meta: { title: '消费日志' },
path: '/mobile/power-log',
name: 'mobile-power-log',
component: () => import('@/views/mobile/PowerLog.vue'),
},
{
meta: { title: '推广计划' },
path: '/mobile/invite',
name: 'mobile-invite',
component: () => import('@/views/mobile/Invite.vue'),
},
{
meta: { title: '设置' },
path: '/mobile/settings',
name: 'mobile-settings',
component: () => import('@/views/mobile/Settings.vue'),
},
{
meta: { title: 'Suno音乐创作' },
path: '/mobile/suno',
name: 'mobile-suno',
component: () => import('@/views/mobile/SunoCreate.vue'),
},
{
meta: { title: '视频生成' },
path: '/mobile/video',
name: 'mobile-video',
component: () => import('@/views/mobile/VideoCreate.vue'),
},
{
meta: { title: '即梦AI' },
path: '/mobile/jimeng',
name: 'mobile-jimeng',
component: () => import('@/views/mobile/JimengCreate.vue'),
},
],
}
// console.log(MY_VARIABLE)
const router = createRouter({
history: createWebHistory(),
@@ -480,4 +477,19 @@ router.beforeEach((to, from, next) => {
next()
})
export { prevRoute, router }
// 检测是否启用了移动端(同步:顶层 await)
const res = await getSystemInfo()
const data = res.data
if (data && data.enable_mobile_site) {
router.addRoute(mobileRoutes)
}
// 获取所有的菜单列表,然后把禁用的菜单从router中移除
const menus = await getMenus()
homeRoutes.children = homeRoutes.children.filter((route) => {
return !menus[route.path] || menus[route.path]?.enabled
})
// 添加主页面路由
router.addRoute(homeRoutes)
export { prevRoute, router, mobileRoutes }
+69 -65
View File
@@ -1,99 +1,103 @@
import { httpGet } from "@/utils/http";
import Storage from "good-storage";
import { randString } from "@/utils/libs";
import { httpGet } from '@/utils/http'
import Storage from 'good-storage'
import { randString } from '@/utils/libs'
const userDataKey = "USER_INFO_CACHE_KEY";
const adminDataKey = "ADMIN_INFO_CACHE_KEY";
const systemInfoKey = "SYSTEM_INFO_CACHE_KEY";
const licenseInfoKey = "LICENSE_INFO_CACHE_KEY";
export function checkSession() {
const item = Storage.get(userDataKey) ?? { expire: 0, data: null };
const userDataKey = 'USER_INFO_CACHE_KEY'
const adminDataKey = 'ADMIN_INFO_CACHE_KEY'
const systemInfoKey = 'SYSTEM_INFO_CACHE_KEY'
const menusKey = 'MENUS_CACHE_KEY'
export function getMenus() {
const item = Storage.get(menusKey) ?? { expire: 0, data: null }
if (item.expire > Date.now()) {
return Promise.resolve(item.data);
return Promise.resolve(item.data)
}
return new Promise((resolve, reject) => {
httpGet("/api/user/session")
httpGet('/api/menu/list/all')
.then((res) => {
item.data = res.data;
item.expire = Date.now() + 1000 * 3;
Storage.set(userDataKey, item);
resolve(item.data);
const menus = {}
for (const menu of res.data) {
menus[menu.url] = menu
}
item.data = menus
item.expire = Date.now() + 1000 * 3
Storage.set(menusKey, item)
resolve(item.data)
})
.catch((err) => {
reject(err)
})
})
}
export function checkSession() {
const item = Storage.get(userDataKey) ?? { expire: 0, data: null }
if (item.expire > Date.now()) {
return Promise.resolve(item.data)
}
return new Promise((resolve, reject) => {
httpGet('/api/user/session')
.then((res) => {
item.data = res.data
item.expire = Date.now() + 1000 * 3
Storage.set(userDataKey, item)
resolve(item.data)
})
.catch((e) => {
Storage.remove(userDataKey);
reject(e);
});
});
Storage.remove(userDataKey)
reject(e)
})
})
}
export function checkAdminSession() {
const item = Storage.get(adminDataKey) ?? { expire: 0, data: null };
const item = Storage.get(adminDataKey) ?? { expire: 0, data: null }
if (item.expire > Date.now()) {
return Promise.resolve(item.data);
return Promise.resolve(item.data)
}
return new Promise((resolve, reject) => {
httpGet("/api/admin/session")
httpGet('/api/admin/session')
.then((res) => {
item.data = res.data;
item.expire = Date.now() + 1000 * 30;
Storage.set(adminDataKey, item);
resolve(item.data);
item.data = res.data
item.expire = Date.now() + 1000 * 3
Storage.set(adminDataKey, item)
resolve(item.data)
})
.catch((e) => {
Storage.remove(adminDataKey);
reject(e);
});
});
Storage.remove(adminDataKey)
reject(e)
})
})
}
export function removeAdminInfo() {
Storage.remove(adminDataKey);
Storage.remove(adminDataKey)
}
export function getSystemInfo() {
const item = Storage.get(systemInfoKey) ?? { expire: 0, data: null };
const item = Storage.get(systemInfoKey) ?? { expire: 0, data: null }
if (item.expire > Date.now()) {
return Promise.resolve(item.data);
return Promise.resolve(item.data)
}
return new Promise((resolve, reject) => {
httpGet("/api/config/get?key=system")
httpGet('/api/config/get?key=system')
.then((res) => {
item.data = res;
item.expire = Date.now() + 1000 * 30;
Storage.set(systemInfoKey, item);
resolve(item.data);
item.data = res
item.expire = Date.now() + 1000 * 3
Storage.set(systemInfoKey, item)
resolve(item.data)
})
.catch((err) => {
reject(err);
});
});
}
export function getLicenseInfo() {
const item = Storage.get(licenseInfoKey) ?? { expire: 0, data: null };
if (item.expire > Date.now()) {
return Promise.resolve(item.data);
}
return new Promise((resolve, reject) => {
httpGet("/api/config/license")
.then((res) => {
item.data = res;
item.expire = Date.now() + 1000 * 30;
Storage.set(licenseInfoKey, item);
resolve(item.data);
reject(err)
})
.catch((err) => {
resolve(err);
});
});
})
}
export function getClientId() {
let clientId = Storage.get("client_id");
let clientId = Storage.get('client_id')
if (clientId) {
return clientId;
return clientId
}
clientId = randString(42);
Storage.set("client_id", clientId);
return clientId;
clientId = randString(42)
Storage.set('client_id', clientId)
return clientId
}
+1 -1
View File
@@ -64,7 +64,7 @@ export const JimengParams = {
type: 'image',
required: false,
placeholder: '请上传图片',
maxSize: 10,
maxSize: 15,
multiple: true,
maxCount: 10,
accept: '.png,.jpg,.jpeg',
+1
View File
@@ -71,6 +71,7 @@ export const useJimengStore = defineStore('jimeng', () => {
// 获取任务状态文本
const getTaskStatusText = (status) => {
const statusMap = {
submited: '任务已提交',
in_queue: '任务排队中',
generating: '任务执行中',
success: '任务成功',
+1 -1
View File
@@ -203,7 +203,7 @@ export function processContent(content) {
if (content.includes('<think>')) {
content = content.replace(/<think>(.*?)<\/think>/gs, (match, content) => {
if (content.length > 10) {
return `<blockquote>${content}</blockquote>`
return `<blockquote>\n\n${content}</blockquote>`
}
return ''
})
+74 -18
View File
@@ -1,39 +1,95 @@
<template>
<div class="page-404" :style="{ height: winHeight + 'px' }">
<div class="page-404">
<div class="inner">
<h1>404</h1>
<h2>饶了地球一圈还是没有找到您要的页面</h2>
<div class="code">404</div>
<p class="title">抱歉页面走丢了</p>
<p class="desc">您访问的页面不存在或已被移动</p>
<div class="actions">
<button class="btn" @click="goHome">返回首页</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const winHeight = ref(window.innerHeight)
const router = useRouter()
function goHome() {
router.replace({ path: '/' })
}
</script>
<style lang="scss" scoped>
.page-404 {
position: relative;
display: flex;
align-items: center;
justify-content: center;
background-color: #282c34;
min-height: 100vh;
padding: calc(env(safe-area-inset-top, 0px) + 24px) 16px
calc(env(safe-area-inset-bottom, 0px) + 32px);
background: radial-gradient(1200px 600px at 50% -10%, #2f3542 0%, #1e222a 60%, #12161c 100%);
color: #ffffff;
text-align: center;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
.inner {
text-align: center;
width: 100%;
max-width: 720px;
margin: 0 auto;
}
h1 {
color: #202020;
font-size: 120px;
font-weight: bold;
letter-spacing: 0.1em;
text-shadow: -1px -1px 1px #111111, 2px 2px 1px #363636;
}
.code {
font-size: clamp(96px, 18vw, 160px);
font-weight: 800;
line-height: 1;
letter-spacing: 0.04em;
background: linear-gradient(180deg, #ffffff 0%, #cbd5e1 60%, #94a3b8 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 12px 30px rgba(0, 0, 0, 0.35);
}
h2 {
color: #ffffff;
font-weight: bold;
}
.title {
margin-top: 16px;
font-size: clamp(18px, 4.6vw, 28px);
font-weight: 700;
}
.desc {
margin-top: 8px;
font-size: clamp(14px, 3.6vw, 16px);
color: #cbd5e1;
}
.actions {
margin-top: 24px;
}
.btn {
appearance: none;
border: 0;
border-radius: 9999px;
padding: 12px 20px;
font-weight: 600;
font-size: 14px;
color: #ffffff;
background: #4f46e5;
box-shadow: 0 10px 20px rgba(79, 70, 229, 0.35), inset 0 0 0 1px rgba(255, 255, 255, 0.15);
cursor: pointer;
transition: transform 0.12s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.btn:hover {
background: #4338ca;
box-shadow: 0 12px 24px rgba(79, 70, 229, 0.45), inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}
.btn:active {
transform: translateY(1px);
}
}
</style>
+7 -16
View File
@@ -140,14 +140,9 @@
plain
>
<div class="selected-model-display">
<span class="model-name-text">{{ getSelectedModelName() }}</span>
<el-tag
v-if="getSelectedModel()"
size="small"
type="info"
style="margin-left: 8px; flex-shrink: 0"
>
{{ getSelectedModel() && getSelectedModel().power }}算力
<span class="model-name-text">{{ selectedModel.name }}</span>
<el-tag size="small" type="info" style="margin-left: 8px; flex-shrink: 0">
{{ selectedModel.power }}算力
</el-tag>
</div>
</el-button>
@@ -546,10 +541,10 @@ watch(
)
//
const getSelectedModelName = () => {
const selectedModel = computed(() => {
const model = getSelectedModel()
return model ? model.name : '选择模型'
}
return model ? model : { name: '选择模型', power: 0 }
})
//
const getSelectedModel = () => {
@@ -585,10 +580,6 @@ watch(
}
)
if (isMobile()) {
router.push('/mobile/chat')
}
// ID
if (router.currentRoute.value.query.role_id) {
roleId.value = parseInt(router.currentRoute.value.query.role_id)
@@ -666,7 +657,7 @@ const initData = async () => {
//
const modelRes = await httpGet('/api/model/list')
models.value = modelRes.data
if (models.value.length > 0) {
if (models.value.length > 0 && !modelID.value) {
modelID.value = models.value[0].id
}
+71 -69
View File
@@ -3,7 +3,7 @@
<div class="page-dall">
<div class="inner custom-scroll">
<div class="sd-box">
<h2>DALL-E 创作中心</h2>
<h2 class="!text-[#252f76] py-3">AI图像生成</h2>
<div class="sd-params">
<el-form :model="params" label-width="80px" label-position="left">
@@ -25,55 +25,25 @@
</div>
<div class="param-line">
<el-form-item label="图片质量">
<el-form-item label="图片比例">
<template #default>
<div class="form-item-inner">
<el-select v-model="params.quality" style="width: 150px">
<el-option
v-for="v in qualities"
:label="v.name"
:value="v.value"
:key="v.value"
/>
</el-select>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="图片尺寸">
<template #default>
<div class="form-item-inner">
<el-select v-model="params.size" style="width: 150px">
<el-option v-for="v in sizes" :label="v" :value="v" :key="v" />
</el-select>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="图片样式">
<template #default>
<div class="form-item-inner">
<el-select v-model="params.style" style="width: 150px">
<el-option
v-for="v in styles"
:label="v.name"
:value="v.value"
:key="v.value"
/>
</el-select>
<el-tooltip
content="生动使模型倾向于生成超真实和戏剧性的图像"
raw-content
placement="right"
<el-select
v-model="params.aspect_ratio"
style="width: 150px"
value-key="value"
@change="changeAspectRatio"
>
<el-icon class="info-icon">
<InfoFilled />
</el-icon>
</el-tooltip>
<el-option
v-for="v in radioAspects"
:label="v.label"
:value="v.value"
:key="v.value"
>
<i class="iconfont mr-1" :class="v.icon"></i>
{{ v.label }}
</el-option>
</el-select>
</div>
</template>
</el-form-item>
@@ -105,7 +75,12 @@
<div class="mt-2 mb-2">
<label class="text-gray-700 font-semibold">参考图(可选)</label>
<div class="py-2">
<ImageUpload v-model="params.image" :max-count="5" :multiple="true" />
<ImageUpload
v-model="params.image"
:max-count="5"
:max-size="20"
:multiple="true"
/>
</div>
</div>
</el-form>
@@ -291,18 +266,18 @@
import nodata from '@/assets/img/no-data.png'
import BackTop from '@/components/BackTop.vue'
import ImageUpload from '@/components/ImageUpload.vue'
import TaskList from '@/components/TaskList.vue'
import { checkSession, getSystemInfo } from '@/store/cache'
import { checkSession } from '@/store/cache'
import { useSharedStore } from '@/store/sharedata'
import { showMessageError, showMessageOK } from '@/utils/dialog'
import { httpGet, httpPost } from '@/utils/http'
import { Delete, InfoFilled } from '@element-plus/icons-vue'
import { Delete } from '@element-plus/icons-vue'
import Clipboard from 'clipboard'
import { ElMessage, ElMessageBox } from 'element-plus'
import { onMounted, onUnmounted, ref } from 'vue'
import { LazyImg, Waterfall } from 'vue-waterfall-plugin-next'
import 'vue-waterfall-plugin-next/dist/style.css'
import ImageUpload from '@/components/ImageUpload.vue'
const listBoxHeight = ref(0)
// const paramBoxHeight = ref(0)
@@ -321,21 +296,48 @@ resizeElement()
window.onresize = () => {
resizeElement()
}
const qualities = [
{ name: '标准', value: 'standard' },
{ name: '高清', value: 'hd' },
]
const dalleSizes = ['1024x1024', '1792x1024', '1024x1792']
const fluxSizes = ['1024x1024', '1152x896', '896x1152', '1280x960', '1024x576']
const sizes = ref(dalleSizes)
const styles = [
{ name: '生动', value: 'vivid' },
{ name: '自然', value: 'natural' },
]
// size aspect_ratio label aspect_ratio size
const radioAspects = ref({
'1:1': { value: '1:1', size: '1024x1024', label: '1:1(正方形,头像)', icon: 'icon-aspect_1_1' },
'2:3': {
value: '2:3',
size: '512x768',
label: '2:3(社交媒体,自拍)',
icon: 'icon-aspect_2_3',
},
'4:3': {
value: '4:3',
size: '1024x768',
label: '4:3(文章配图,插画)',
icon: 'icon-aspect_4_3',
},
'9:16': {
value: '9:16',
size: '768x1366',
label: '9:16(手机壁纸,人像)',
icon: 'icon-aspect_9_16',
},
'16:9': {
value: '16:9',
size: '1366x768',
label: '16:9(桌面壁纸,风景)',
icon: 'icon-aspect_16_9',
},
'3:2': { value: '3:2', size: '768x512', label: '3:21248x832', icon: 'icon-aspect_3_2' },
'3:4': { value: '3:4', size: '960x1280', label: '3:4864x1152', icon: 'icon-aspect_3_4' },
'4:5': { value: '4:5', size: '960x1280', label: '4:5896x1120', icon: 'icon-aspect_4_5' },
'5:4': {
value: '5:4',
size: '1280x960',
label: '5:41120x896',
icon: 'icon-aspect_5_4',
},
'21:9': { value: '21:9', size: '1344x576', label: '21:91536x658', icon: 'icon-aspect_16_9' },
})
const params = ref({
quality: 'standard',
aspect_ratio: '1:1',
size: '1024x1024',
style: 'vivid',
prompt: '',
})
@@ -385,6 +387,11 @@ onUnmounted(() => {
}
})
//
const changeAspectRatio = (value) => {
params.value.size = radioAspects.value[value].size
}
const initData = () => {
checkSession()
.then((user) => {
@@ -580,11 +587,6 @@ const generatePrompt = () => {
const changeModel = (model) => {
dallPower.value = model.power
if (model.name.startsWith('dall')) {
sizes.value = dalleSizes
} else {
sizes.value = fluxSizes
}
params.value.model_id = selectedModel.value.id
}
</script>
+2 -12
View File
@@ -82,7 +82,7 @@
<span class="username title">账户信息</span>
</div>
</li>
<li v-if="!license.de_copy">
<li>
<a :href="githubURL" target="_blank" class="flex">
<i class="iconfont icon-github"></i>
<span class="title">项目源码</span>
@@ -150,10 +150,9 @@
import LoginDialog from '@/components/LoginDialog.vue'
import ThemeChange from '@/components/ThemeChange.vue'
import ConfigDialog from '@/components/UserInfoDialog.vue'
import { checkSession, getLicenseInfo, getSystemInfo } from '@/store/cache'
import { checkSession, getSystemInfo } from '@/store/cache'
import { removeUserToken } from '@/store/session'
import { useSharedStore } from '@/store/sharedata'
import { showMessageError } from '@/utils/dialog'
import { httpGet } from '@/utils/http'
import { UserFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
@@ -171,7 +170,6 @@ const store = useSharedStore()
const loginUser = ref({})
const routerViewKey = ref(0)
const showConfigDialog = ref(false)
const license = ref({ de_copy: true })
const showLoginDialog = ref(false)
const githubURL = ref(import.meta.env.VITE_GITHUB_URL)
@@ -261,14 +259,6 @@ onMounted(() => {
ElMessage.error('获取系统菜单失败:' + e.message)
})
getLicenseInfo()
.then((res) => {
license.value = res.data
})
.catch((e) => {
license.value = { de_copy: false }
showMessageError('获取 License 配置:' + e.message)
})
curPath.value = '/' + getFirstPathSegment(window.location.href)
init()
})
+6 -18
View File
@@ -8,7 +8,7 @@
<img :src="logo" class="logo" alt="Geek-AI" />
</div>
<div class="menu-item">
<span v-if="!license || !license.de_copy">
<span>
<el-tooltip class="box-item" content="部署文档" placement="bottom">
<a :href="docsURL" class="link-button mr-3" target="_blank">
<i class="iconfont icon-book"></i>
@@ -88,7 +88,7 @@
<script setup>
import FooterBar from '@/components/FooterBar.vue'
import ThemeChange from '@/components/ThemeChange.vue'
import { checkSession, getLicenseInfo, getSystemInfo } from '@/store/cache'
import { checkSession, getSystemInfo } from '@/store/cache'
import { removeUserToken } from '@/store/session'
import { httpGet } from '@/utils/http'
import { isMobile } from '@/utils/libs'
@@ -102,7 +102,6 @@ const router = useRouter()
const title = ref('')
const logo = ref('')
const license = ref({ de_copy: true })
const isLogin = ref(false)
const docsURL = ref(import.meta.env.VITE_DOCS_URL)
@@ -123,17 +122,15 @@ const md = new MarkdownIt({
typographer: true,
}).use(emoji)
if (isMobile()) {
router.push('/mobile/index')
}
getSystemInfo()
.then((res) => {
const data = res.data
title.value = data.title
logo.value = data.logo
console.log(data)
if (data.index_page) {
if (isMobile() && data.enable_mobile_site) {
router.push('/mobile/index')
} else if (data.index_page) {
router.push(data.index_page)
}
})
@@ -142,15 +139,6 @@ getSystemInfo()
})
onMounted(() => {
getLicenseInfo()
.then((res) => {
license.value = res.data
})
.catch((e) => {
license.value = { de_copy: false }
ElMessage.error('获取 License 配置失败:' + e.message)
})
httpGet('/api/menu/list?index=1')
.then((res) => {
navs.value = res.data
-1
View File
@@ -229,7 +229,6 @@
>
<template #default="{ item }">
<div class="task-item">
<!-- 保持原有内容 -->
<div class="task-left">
<div class="task-preview">
<el-image
+5 -3
View File
@@ -41,7 +41,8 @@
<el-table-column prop="type" label="模型类型">
<template #default="scope">
<el-tag type="primary" v-if="scope.row.type === 'img'">绘图</el-tag>
<el-tag type="success" v-else>聊天</el-tag>
<el-tag type="warning" v-if="scope.row.type === 'tts'">语音</el-tag>
<el-tag type="success" v-if="scope.row.type === 'chat'">聊天</el-tag>
</template>
</el-table-column>
@@ -230,7 +231,7 @@
<script setup>
import { httpGet, httpPost } from '@/utils/http'
import { dateFormat, removeArrayItem, substr } from '@/utils/libs'
import { DocumentCopy, InfoFilled, Plus, Search, Delete } from '@element-plus/icons-vue'
import { Delete, DocumentCopy, InfoFilled, Plus, Search } from '@element-plus/icons-vue'
import ClipboardJS from 'clipboard'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Sortable } from 'sortablejs'
@@ -239,7 +240,7 @@ import { onMounted, onUnmounted, reactive, ref } from 'vue'
//
const items = ref([])
const query = ref({ name: '' })
const item = ref({})
const item = ref({ options: {} })
const showDialog = ref(false)
const title = ref('')
const rules = reactive({
@@ -353,6 +354,7 @@ const add = function () {
max_tokens: 1024,
max_context: 8192,
temperature: 0.9,
options: {},
}
}
+4
View File
@@ -17,6 +17,7 @@
<el-switch v-model="scope.row.enabled" @change="functionSet('enabled', scope.row)" />
</template>
</el-table-column>
<el-table-column label="消耗算力" prop="power" />
<el-table-column label="操作" width="150" align="right">
<template #default="scope">
@@ -128,6 +129,9 @@
<el-form-item label="启用状态">
<el-switch v-model="item.enabled" />
</el-form-item>
<el-form-item label="消耗算力" prop="power">
<el-input-number v-model.number="item.power" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
+2 -4
View File
@@ -138,12 +138,12 @@
/>
</el-form-item>
<el-form-item label="聊天角色" prop="chat_roles">
<el-form-item label="应用权限" prop="chat_roles">
<el-select
v-model="user.chat_roles"
multiple
:filterable="true"
placeholder="选择聊天角色,多选"
placeholder="选择 AI 应用"
>
<el-option v-for="item in roles" :key="item.key" :label="item.name" :value="item.key" />
</el-select>
@@ -243,8 +243,6 @@ const rules = reactive({
{ required: true, message: '请输入提问次数' },
{ type: 'number', message: '请输入有效数字' },
],
chat_roles: [{ required: true, message: '请选择聊天角色', trigger: 'change' }],
chat_models: [{ required: true, message: '请选择AI模型', trigger: 'change' }],
})
const loading = ref(true)
+20 -2
View File
@@ -115,7 +115,7 @@
</el-form-item>
<el-form-item label="版权信息" prop="copyright">
<el-input v-model="system['copyright']" placeholder="更改此选项需要获取 License 授权" />
<el-input v-model="system['copyright']" placeholder="请输入版权信息" />
</el-form-item>
<el-form-item label="ICP 备案号" prop="icp">
@@ -251,7 +251,19 @@
</el-form-item>
</div>
<div style="padding: 10px">
<el-form-item>
<template #label>
<div class="label-title">
启用手机站
<span class="text-xs text-gray-500"
>启用手机站点之后用户可以在手机上访问网站</span
>
</div>
</template>
<el-switch v-model="system['enable_mobile_site']" />
</el-form-item>
<div class="py-2">
<el-form-item>
<el-button type="primary" @click="save">保存</el-button>
</el-form-item>
@@ -323,6 +335,12 @@ const save = function () {
if (valid) {
httpPost('/api/admin/config/update/base', system.value)
.then(() => {
//
if (system.value['enable_mobile_site']) {
// addMobileRoutes()
} else {
// removeMobileRoutes()
}
ElMessage.success('操作成功!')
})
.catch((e) => {
@@ -1,159 +0,0 @@
<template>
<div class="license-config form p-5" v-loading="loading">
<div class="container">
<el-descriptions
v-if="license.is_active"
class="margin-top"
title="已授权信息"
:column="1"
border
>
<el-descriptions-item>
<template #label>
<div class="cell-item">License Key</div>
</template>
{{ license.key }}
<el-tooltip content="复制" placement="top">
<i class="iconfont icon-copy ml-2 cursor-pointer" @click="copy(license.key)"></i>
</el-tooltip>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">机器码</div>
</template>
{{ license.machine_id }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">到期时间</div>
</template>
{{ dateFormat(license.expired_at) }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">用户人数</div>
</template>
{{ license.configs?.user_num }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">去版权</div>
</template>
<el-icon class="selected" v-if="license.configs?.de_copy"><Select /></el-icon>
<el-icon class="closed" v-else><CloseBold /></el-icon>
<span class="text">去版权之后前端页面将不会显示版权信息和源码地址</span>
</el-descriptions-item>
</el-descriptions>
<div class="mt-5 p-5 border border-gray-200 rounded-md bg-gray-50">
<h3>激活后可获得以下权限</h3>
<div class="py-3 text-gray-500 leading-relaxed">
<p>1使用任意第三方中转 API KEY而不用局限于 GeekAI 推荐的白名单列表</p>
<p>2可以在相关页面去除 GeekAI 的版权信息或者修改为自己的版权信息</p>
</div>
<el-form label-position="top">
<el-form-item label="许可授权码" prop="license">
<el-input v-model="licenseKey" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="active">立即激活</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script setup>
import { httpGet, httpPost } from '@/utils/http'
import { dateFormat } from '@/utils/libs'
import { CloseBold, Select } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'
const loading = ref(true)
const license = ref({ is_active: false })
const licenseKey = ref('')
onMounted(() => {
fetchLicense()
})
const fetchLicense = () => {
httpGet('/api/admin/config/license/get')
.then((res) => {
license.value = res.data
})
.catch((e) => {
ElMessage.error('获取 License 失败:' + e.message)
})
.finally(() => {
loading.value = false
})
}
//
const active = () => {
if (licenseKey.value === '') {
return ElMessage.error('请输入授权码')
}
httpPost('/api/admin/config/license/active', { license: licenseKey.value })
.then((res) => {
ElMessage.success('授权成功,机器编码为:' + res.data)
fetchLicense()
})
.catch((e) => {
ElMessage.error(e.message)
})
}
const copy = (text) => {
navigator.clipboard.writeText(text)
ElMessage.success('复制成功')
}
</script>
<style scoped>
.license-config {
display: flex;
justify-content: center;
}
.container {
width: 100%;
background-color: var(--el-bg-color);
padding: 10px 20px 40px 20px;
}
.margin-top {
margin-top: 20px;
}
.cell-item {
font-weight: bold;
}
.selected {
color: #67c23a;
}
.closed {
color: #f56c6c;
}
.text {
margin-left: 10px;
}
.active-info {
margin: 20px 0;
padding-left: 20px;
}
.active-info li {
margin: 10px 0;
line-height: 1.6;
}
</style>
+1 -1
View File
@@ -37,7 +37,7 @@
<i class="iconfont icon-info"></i>
</el-tooltip>
</label>
<el-input v-model="smtpConfig.port" type="number" placeholder="请输入端口号" />
<el-input v-model.number="smtpConfig.port" type="number" placeholder="请输入端口号" />
</el-form-item>
<el-form-item label="是否使用TLS"
+14 -5
View File
@@ -38,14 +38,14 @@
</el-tab-pane>
<el-tab-pane label="MinIO" name="minio">
<div class="rounded-md bg-blue-100 p-3 text-gray-500 border-blue-500 border-2 text-base">
<Alert type="info">
如果你不知道怎么获取这些配置信息请参考文档
<a
href="https://docs.geekai.me/plus/config/oss.html#%E6%90%AD%E5%BB%BA-minio-%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1"
target="_blank"
>Minio 配置</a
>
</div>
</Alert>
<el-form :model="minio" class="mt-4" label-position="top">
<el-form-item label="Endpoint"><el-input v-model="minio.endpoint" /></el-form-item>
<el-form-item label="AccessKey"><el-input v-model="minio.access_key" /></el-form-item>
@@ -59,14 +59,14 @@
</el-tab-pane>
<el-tab-pane label="七牛云" name="qiniu">
<div class="rounded-md bg-blue-100 p-3 text-gray-500 border-blue-500 border-2 text-base">
<Alert type="info">
如果你不知道怎么获取这些配置信息请参考文档
<a
href="https://docs.geekai.me/plus/config/oss.html#%E4%B8%83%E7%89%9B%E4%BA%91-oss-%E9%85%8D%E7%BD%AE"
target="_blank"
>七牛云配置</a
>
</div>
</Alert>
<el-form :model="qiniu" class="mt-4" label-position="top">
<el-form-item label="Zone">
<template #label>
@@ -96,6 +96,14 @@
</el-tab-pane>
<el-tab-pane label="阿里云OSS" name="aliyun">
<Alert type="info">
如果你不知道怎么获取这些配置信息请参考文档
<a
href="https://docs.geekai.me/plus/config/oss.html#%E9%98%BF%E9%87%8C%E4%BA%91-oss-%E9%85%8D%E7%BD%AE"
target="_blank"
>阿里云OSS配置</a
>
</Alert>
<el-form :model="aliyun" class="mt-4" label-position="top">
<el-form-item label="Endpoint"><el-input v-model="aliyun.endpoint" /></el-form-item>
<el-form-item label="AccessKey"><el-input v-model="aliyun.access_key" /></el-form-item>
@@ -128,11 +136,12 @@
import { httpGet, httpPost } from '@/utils/http'
import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'
import Alert from '@/components/ui/Alert.vue'
const loading = ref(true)
const activeTab = ref('local')
const active = ref('local')
const local = ref({ base_path: '', base_url: '' })
const local = ref({ base_path: './static/upload', base_url: '/static/upload' })
const minio = ref({
endpoint: '',
access_key: '',
+59 -44
View File
@@ -1,5 +1,5 @@
<template>
<div class="app-background">
<div class="app-background" v-if="menus['/chat']?.enabled">
<div class="container mobile-chat-list">
<van-nav-bar
:title="title"
@@ -72,15 +72,19 @@
<van-field v-model="tmpChatTitle" label="" placeholder="请输入对话标题" class="field" />
</van-dialog>
</div>
<div v-else>
<FunDisabled />
</div>
</template>
<script setup>
import { checkSession } from '@/store/cache'
import { checkSession, getMenus } from '@/store/cache'
import { httpGet, httpPost } from '@/utils/http'
import { removeArrayItem, showLoginDialog } from '@/utils/libs'
import { showConfirmDialog, showFailToast, showSuccessToast } from 'vant'
import { ref } from 'vue'
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import FunDisabled from '@/components/ui/FunDisabled.vue'
const title = ref('会话列表')
const router = useRouter()
@@ -99,50 +103,61 @@ const columns = ref([roles.value, models.value])
const showEditChat = ref(false)
const item = ref({})
const tmpChatTitle = ref('')
const menus = ref({})
checkSession()
.then((user) => {
loginUser.value = user
isLogin.value = true
})
.finally(() => {
loading.value = false
finished.value = true
//
httpGet(`/api/app/list`)
.then((res) => {
if (res.data) {
const items = res.data
for (let i = 0; i < items.length; i++) {
// console.log(items[i])
roles.value.push({
text: items[i].name,
value: items[i].id,
icon: items[i].icon,
helloMsg: items[i].hello_msg,
model_id: items[i].model_id,
})
}
}
})
.catch(() => {
showFailToast('加载聊天角色失败')
})
onMounted(() => {
getMenus()
.then((data) => {
menus.value = data
})
.catch((e) => {
showNotify({ type: 'danger', message: '获取菜单失败:' + e.message })
})
//
httpGet('/api/model/list?enable=1')
.then((res) => {
if (res.data) {
const items = res.data
for (let i = 0; i < items.length; i++) {
models.value.push({ text: items[i].name, value: items[i].id })
checkSession()
.then((user) => {
loginUser.value = user
isLogin.value = true
})
.finally(() => {
loading.value = false
finished.value = true
//
httpGet(`/api/app/list`)
.then((res) => {
if (res.data) {
const items = res.data
for (let i = 0; i < items.length; i++) {
// console.log(items[i])
roles.value.push({
text: items[i].name,
value: items[i].id,
icon: items[i].icon,
helloMsg: items[i].hello_msg,
model_id: items[i].model_id,
})
}
}
}
})
.catch((e) => {
showFailToast('加载模型失败: ' + e.message)
})
})
})
.catch(() => {
showFailToast('加载聊天角色失败')
})
//
httpGet('/api/model/list?enable=1')
.then((res) => {
if (res.data) {
const items = res.data
for (let i = 0; i < items.length; i++) {
models.value.push({ text: items[i].name, value: items[i].id })
}
}
})
.catch((e) => {
showFailToast('加载模型失败: ' + e.message)
})
})
})
const onLoad = () => {
checkSession()
-6
View File
@@ -50,12 +50,6 @@ import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const activeTab = ref(route.query.tab || 'mj')
const menus = ref([])
const activeMenu = ref({
mj: false,
sd: false,
dall: false,
})
// Tab
const onTabChange = (name) => {
+16
View File
@@ -42,8 +42,10 @@
</template>
<script setup>
import { getMenus } from '@/store/cache'
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { onMounted } from 'vue'
const router = useRouter()
@@ -60,6 +62,7 @@ const aiTools = ref([
status: 'active',
statusText: '可用',
url: '/mobile/create?tab=mj',
path: '/mj',
},
{
key: 'sd',
@@ -71,6 +74,7 @@ const aiTools = ref([
status: 'active',
statusText: '可用',
url: '/mobile/create?tab=sd',
path: '/sd',
},
{
key: 'dalle',
@@ -82,6 +86,7 @@ const aiTools = ref([
status: 'active',
statusText: '可用',
url: '/mobile/create?tab=dalle',
path: '/dalle',
},
{
key: 'suno',
@@ -94,6 +99,7 @@ const aiTools = ref([
status: 'active',
statusText: '可用',
url: '/mobile/suno',
path: '/suno',
},
{
key: 'video',
@@ -105,6 +111,7 @@ const aiTools = ref([
status: 'beta',
statusText: '测试版',
url: '/mobile/video',
path: '/video',
},
{
key: 'jimeng',
@@ -116,6 +123,7 @@ const aiTools = ref([
status: 'active',
statusText: '可用',
url: '/mobile/jimeng',
path: '/jimeng',
},
{
key: 'imgWall',
@@ -127,6 +135,7 @@ const aiTools = ref([
status: 'active',
statusText: '可用',
url: '/mobile/imgWall',
path: '/images-wall',
},
{
key: 'apps',
@@ -138,6 +147,7 @@ const aiTools = ref([
status: 'active',
statusText: '可用',
url: '/mobile/apps',
path: '/apps',
},
])
@@ -149,6 +159,12 @@ const navigateTo = (url) => {
router.push(url)
}
}
onMounted(() => {
getMenus().then((menus) => {
aiTools.value = aiTools.value.filter((tool) => menus[tool.path]?.enabled)
})
})
</script>
<style lang="scss" scoped>
+1 -1
View File
@@ -84,7 +84,7 @@ onMounted(() => {
</script>
<style lang="scss">
@use '../../assets/iconfont/iconfont.css' as *;
@use '@/assets/iconfont/iconfont.css' as *;
.mobile-home {
.page-content {
+15 -1
View File
@@ -119,7 +119,7 @@
</template>
<script setup>
import { checkSession, getSystemInfo } from '@/store/cache'
import { checkSession, getSystemInfo, getMenus } from '@/store/cache'
import { httpGet, httpPost } from '@/utils/http'
import { arrayContains, removeArrayItem, showLoginDialog, substr } from '@/utils/libs'
import { ElMessage } from 'element-plus'
@@ -145,6 +145,7 @@ const features = ref([
icon: 'icon-dalle',
color: '#F59E0B',
url: '/mobile/create?tab=dalle',
path: '/dalle',
},
{
key: 'suno',
@@ -152,6 +153,7 @@ const features = ref([
icon: 'icon-mp3',
color: '#EF4444',
url: '/mobile/suno',
path: '/suno',
},
{
key: 'video',
@@ -159,6 +161,7 @@ const features = ref([
icon: 'icon-video',
color: '#10B981',
url: '/mobile/video',
path: '/video',
},
{
key: 'jimeng',
@@ -166,6 +169,7 @@ const features = ref([
icon: 'icon-jimeng',
color: '#F97316',
url: '/mobile/jimeng',
path: '/jimeng',
},
{
key: '3d',
@@ -173,6 +177,7 @@ const features = ref([
icon: 'icon-3d',
color: '#8B5CF6',
url: '/mobile/3d',
path: '/3d',
},
{ key: 'agent', name: '智能体', icon: 'icon-app', color: '#3B82F6', url: '/mobile/apps' },
{
@@ -181,6 +186,7 @@ const features = ref([
icon: 'icon-image-list',
color: '#EC4899',
url: '/mobile/imgWall',
path: '/images-wall',
},
])
@@ -225,6 +231,14 @@ onMounted(() => {
})
.catch(() => {})
getMenus()
.then((menus) => {
features.value = features.value.filter((feature) => menus[feature.path]?.enabled)
})
.catch((e) => {
ElMessage.error('获取菜单失败:' + e.message)
})
fetchApps()
})
+15 -2
View File
@@ -1,5 +1,5 @@
<template>
<div class="member-page">
<div class="member-page" v-if="menus['/member']?.enabled">
<div class="member-content" v-loading="loading" :element-loading-text="loadingText">
<!-- 产品套餐 -->
<div class="products-section">
@@ -103,14 +103,18 @@
</div>
</van-dialog>
</div>
<div v-else>
<FunDisabled />
</div>
</template>
<script setup>
import RedeemVerify from '@/components/RedeemVerify.vue'
import UserOrder from '@/components/UserOrder.vue'
import { checkSession, getSystemInfo } from '@/store/cache'
import { checkSession, getSystemInfo, getMenus } from '@/store/cache'
import { useSharedStore } from '@/store/sharedata'
import { httpGet, httpPost } from '@/utils/http'
import FunDisabled from '@/components/ui/FunDisabled.vue'
import QRCode from 'qrcode'
import { showFailToast, showLoadingToast, showSuccessToast } from 'vant'
import { onMounted, onUnmounted, ref } from 'vue'
@@ -123,6 +127,7 @@ const loading = ref(true)
const loadingText = ref('加载中...')
const vipInfoText = ref('')
const userOrderKey = ref(0)
const menus = ref({})
//
const showRedeemVerifyDialog = ref(false)
@@ -171,6 +176,14 @@ onMounted(() => {
.catch((e) => {
console.error('获取系统配置失败:', e.message)
})
getMenus()
.then((data) => {
menus.value = data
})
.catch((e) => {
showNotify({ type: 'danger', message: '获取菜单失败:' + e.message })
})
})
//
+225 -230
View File
@@ -1,203 +1,193 @@
<template>
<div class="mobile-sd">
<van-form>
<van-cell-group class="px-3 pt-3 pb-4">
<div>
<van-field
v-model="selectedModel"
is-link
label="生图模型"
placeholder="选择生图模型"
@click="showModelPicker = true"
/>
<van-popup v-model:show="showModelPicker" position="bottom" teleport="#app">
<van-picker
:columns="models"
@cancel="showModelPicker = false"
@confirm="modelConfirm"
<div v-if="menus['/dalle']?.enabled">
<van-form>
<van-cell-group class="px-3 pt-3 pb-4">
<div>
<van-field
v-model="selectedModel"
is-link
label="生图模型"
placeholder="选择生图模型"
@click="showModelPicker = true"
/>
</van-popup>
</div>
<div>
<van-field
v-model="quality"
is-link
label="图片质量"
placeholder="选择图片质量"
@click="showQualityPicker = true"
/>
<van-popup v-model:show="showQualityPicker" position="bottom" teleport="#app">
<van-picker
:columns="qualities"
@cancel="showQualityPicker = false"
@confirm="qualityConfirm"
/>
</van-popup>
</div>
<div>
<van-field
v-model="size"
is-link
label="图片尺寸"
placeholder="选择图片尺寸"
@click="showSizePicker = true"
/>
<van-popup v-model:show="showSizePicker" position="bottom" teleport="#app">
<van-picker :columns="sizes" @cancel="showSizePicker = false" @confirm="sizeConfirm" />
</van-popup>
</div>
<div>
<van-field
v-model="style"
is-link
label="图片样式"
placeholder="选择图片样式"
@click="showStylePicker = true"
/>
<van-popup v-model:show="showStylePicker" position="bottom" teleport="#app">
<van-picker
:columns="styles"
@cancel="showStylePicker = false"
@confirm="styleConfirm"
/>
</van-popup>
</div>
<van-field
v-model="params.prompt"
rows="3"
autosize
maxlength="2000"
type="textarea"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
/>
<div class="sticky bottom-4 bg-[var(--van-cell-group-background)] rounded-xl p-4 shadow-sm">
<button
@click="generate"
:disabled="loading"
type="button"
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
>
<i v-if="loading" class="iconfont icon-loading animate-spin"></i>
<i v-else class="iconfont icon-chuangzuo"></i>
<span>{{ loading ? '创作中...' : '立即生成' }}({{ dallPower }}算力)</span>
</button>
</div>
</van-cell-group>
</van-form>
<h3 class="m-3">任务列表</h3>
<div class="running-job-list pt-3 pb-3">
<van-empty
v-if="runningJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-grid :gutter="10" :column-num="3" v-else>
<van-grid-item v-for="item in runningJobs" :key="item.id">
<div v-if="item.progress > 0">
<van-image src="/images/img-holder.png"></van-image>
<div class="progress">
<van-circle
v-model:current-rate="item.progress"
:rate="item.progress"
:speed="100"
:text="item.progress + '%'"
:stroke-width="60"
size="90px"
<van-popup v-model:show="showModelPicker" position="bottom" teleport="#app">
<van-picker
:columns="models"
@cancel="showModelPicker = false"
@confirm="modelConfirm"
/>
</van-popup>
</div>
<div>
<van-field
v-model="aspectRatio"
is-link
label="图片比例"
placeholder="选择图片比例"
@click="showSizePicker = true"
/>
<van-popup v-model:show="showSizePicker" position="bottom" teleport="#app">
<van-picker
:columns="radioAspects"
@cancel="showSizePicker = false"
@confirm="sizeConfirm"
/>
</van-popup>
</div>
<van-field
v-model="params.prompt"
rows="3"
autosize
maxlength="2000"
type="textarea"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
/>
<div class="mt-3 mb-3 px-3">
<label class="text-gray-700 font-semibold">参考图(可选)</label>
<div class="py-2">
<ImageUpload v-model="params.image" :max-count="5" :multiple="true" />
</div>
</div>
<div v-else class="task-in-queue">
<span class="icon"><i class="iconfont icon-quick-start"></i></span>
<span class="text">排队中</span>
<div
class="sticky bottom-4 bg-[var(--van-cell-group-background)] rounded-xl p-4 shadow-sm"
>
<button
@click="generate"
:disabled="loading"
type="button"
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
>
<i v-if="loading" class="iconfont icon-loading animate-spin"></i>
<i v-else class="iconfont icon-chuangzuo"></i>
<span>{{ loading ? '创作中...' : '立即生成' }}({{ dallPower }}算力)</span>
</button>
</div>
</van-grid-item>
</van-grid>
</div>
</van-cell-group>
</van-form>
<h3 class="m-3">创作记录</h3>
<div class="finish-job-list">
<van-empty
v-if="finishedJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-list
v-else
v-model:error="error"
v-model:loading="loading"
:finished="finished"
error-text="请求失败,点击重新加载"
finished-text="没有更多了"
@load="onLoad"
>
<van-grid :gutter="10" :column-num="2">
<van-grid-item v-for="item in finishedJobs" :key="item.id">
<div class="failed" v-if="item.progress === 101">
<div class="title">任务失败</div>
<div class="opt">
<van-button size="small" @click="showErrMsg(item)">详情</van-button>
<van-button type="danger" @click="removeImage($event, item)" size="small"
>删除</van-button
>
<h3 class="m-3">任务列表</h3>
<div class="running-job-list pt-3 pb-3">
<van-empty
v-if="runningJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-grid :gutter="10" :column-num="3" v-else>
<van-grid-item v-for="item in runningJobs" :key="item.id">
<div v-if="item.progress > 0">
<van-image src="/images/img-holder.png"></van-image>
<div class="progress">
<van-circle
v-model:current-rate="item.progress"
:rate="item.progress"
:speed="100"
:text="item.progress + '%'"
:stroke-width="60"
size="90px"
/>
</div>
</div>
<div class="job-item" v-else>
<van-image
:src="item['img_url']"
:class="item['can_opt'] ? '' : 'upscale'"
lazy-load
@click="imageView(item)"
fit="cover"
>
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
</van-image>
<div class="remove">
<el-button type="danger" :icon="Delete" @click="removeImage($event, item)" circle />
<el-button
type="warning"
v-if="item.publish"
@click="publishImage($event, item, false)"
circle
>
<i class="iconfont icon-cancel-share"></i>
</el-button>
<el-button type="success" v-else @click="publishImage($event, item, true)" circle>
<i class="iconfont icon-share-bold"></i>
</el-button>
<el-button type="primary" @click="showPrompt(item)" circle>
<i class="iconfont icon-prompt"></i>
</el-button>
</div>
<div v-else class="task-in-queue">
<span class="icon"><i class="iconfont icon-quick-start"></i></span>
<span class="text">排队中</span>
</div>
</van-grid-item>
</van-grid>
</van-list>
</div>
<h3 class="m-3">创作记录</h3>
<div class="finish-job-list">
<van-empty
v-if="finishedJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-list
v-else
v-model:error="error"
v-model:loading="loading"
:finished="finished"
error-text="请求失败,点击重新加载"
finished-text="没有更多了"
@load="onLoad"
>
<van-grid :gutter="10" :column-num="2">
<van-grid-item v-for="item in finishedJobs" :key="item.id">
<div class="failed" v-if="item.progress === 101">
<div class="title">任务失败</div>
<div class="opt">
<van-button size="small" @click="showErrMsg(item)">详情</van-button>
<van-button type="danger" @click="removeImage($event, item)" size="small"
>删除</van-button
>
</div>
</div>
<div class="job-item" v-else>
<van-image
:src="item['img_url']"
:class="item['can_opt'] ? '' : 'upscale'"
lazy-load
@click="imageView(item)"
fit="cover"
>
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
</van-image>
<div class="remove">
<el-button
type="danger"
:icon="Delete"
@click="removeImage($event, item)"
circle
/>
<el-button
type="warning"
v-if="item.publish"
@click="publishImage($event, item, false)"
circle
>
<i class="iconfont icon-cancel-share"></i>
</el-button>
<el-button type="success" v-else @click="publishImage($event, item, true)" circle>
<i class="iconfont icon-share-bold"></i>
</el-button>
<el-button type="primary" @click="showPrompt(item)" circle>
<i class="iconfont icon-prompt"></i>
</el-button>
</div>
</div>
</van-grid-item>
</van-grid>
</van-list>
</div>
<button
style="display: none"
class="copy-prompt-dall"
:data-clipboard-text="prompt"
id="copy-btn-dall"
>
复制
</button>
</div>
<div v-else>
<FunDisabled />
</div>
<button
style="display: none"
class="copy-prompt-dall"
:data-clipboard-text="prompt"
id="copy-btn-dall"
>
复制
</button>
</div>
</template>
<script setup>
import { checkSession, getSystemInfo } from '@/store/cache'
import { checkSession, getSystemInfo, getMenus } from '@/store/cache'
import { getSessionId } from '@/store/session'
import { useSharedStore } from '@/store/sharedata'
import { httpGet, httpPost } from '@/utils/http'
@@ -215,52 +205,64 @@ import {
} from 'vant'
import { onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import FunDisabled from '@/components/ui/FunDisabled.vue'
import ImageUpload from '@/components/ImageUpload.vue'
const listBoxHeight = ref(window.innerHeight - 40)
const mjBoxHeight = ref(window.innerHeight - 150)
const isLogin = ref(false)
const menus = ref({})
window.onresize = () => {
listBoxHeight.value = window.innerHeight - 40
mjBoxHeight.value = window.innerHeight - 150
}
const qualities = [
{ text: '标准', value: 'standard' },
{ text: '高清', value: 'hd' },
]
const fluxSizes = [
{ text: '1024x1024', value: '1024x1024' },
{ text: '1024x768', value: '1024x768' },
{ text: '768x1024', value: '768x1024' },
{ text: '1280x960', value: '1280x960' },
{ text: '960x1280', value: '960x1280' },
{ text: '1366x768', value: '1366x768' },
{ text: '768x1366', value: '768x1366' },
]
const dalleSizes = [
{ text: '1024x1024', value: '1024x1024' },
{ text: '1792x1024', value: '1792x1024' },
{ text: '1024x1792', value: '1024x1792' },
]
let sizes = dalleSizes
const styles = [
{ text: '生动', value: 'vivid' },
{ text: '自然', value: 'natural' },
]
// size aspect_ratio label aspect_ratio size
const radioAspects = ref([
{ value: '1:1', size: '1024x1024', text: '1:1(正方形,头像)', icon: 'icon-aspect_1_1' },
{
value: '2:3',
size: '512x768',
text: '2:3(社交媒体,自拍)',
icon: 'icon-aspect_2_3',
},
{
value: '4:3',
size: '1024x768',
text: '4:3(文章配图,插画)',
icon: 'icon-aspect_4_3',
},
{
value: '9:16',
size: '768x1366',
text: '9:16(手机壁纸,人像)',
icon: 'icon-aspect_9_16',
},
{
value: '16:9',
size: '1366x768',
text: '16:9(桌面壁纸,风景)',
icon: 'icon-aspect_16_9',
},
{ value: '3:2', size: '768x512', text: '3:21248x832', icon: 'icon-aspect_3_2' },
{ value: '3:4', size: '960x1280', text: '3:4864x1152', icon: 'icon-aspect_3_4' },
{ value: '4:5', size: '960x1280', text: '4:5896x1120', icon: 'icon-aspect_4_5' },
{
value: '5:4',
size: '1280x960',
label: '5:41120x896',
icon: 'icon-aspect_5_4',
},
{ value: '21:9', size: '1344x576', text: '21:91536x658', icon: 'icon-aspect_16_9' },
])
const params = ref({
quality: qualities[0].value,
size: sizes[0].value,
style: styles[0].value,
aspect_ratio: '1:1',
size: '1024x1024',
prompt: '',
})
const quality = ref(qualities[0].text)
const size = ref(sizes[0].text)
const style = ref(styles[0].text)
const aspectRatio = ref(radioAspects.value[0].text)
const showQualityPicker = ref(false)
const showStylePicker = ref(false)
const showSizePicker = ref(false)
const showModelPicker = ref(false)
@@ -313,6 +315,14 @@ onMounted(() => {
.catch((e) => {
showMessageError('获取模型列表失败:' + e.message)
})
getMenus()
.then((data) => {
menus.value = data
})
.catch((e) => {
showNotify({ type: 'danger', message: '获取菜单失败:' + e.message })
})
})
onUnmounted(() => {
@@ -408,10 +418,6 @@ const generate = () => {
promptRef.value.focus()
return showToast('请输入绘画提示词!')
}
if (!params.value.seed) {
params.value.seed = -1
}
params.value.session_id = getSessionId()
httpPost('/api/dall/image', params.value)
.then(() => {
@@ -492,21 +498,10 @@ const imageView = (item) => {
showImagePreview([item['img_url']])
}
const qualityConfirm = (item) => {
params.value.quality = item.selectedOptions[0].value
quality.value = item.selectedOptions[0].text
showQualityPicker.value = false
}
const styleConfirm = (item) => {
params.value.style = item.selectedOptions[0].value
style.value = item.selectedOptions[0].text
showStylePicker.value = false
}
const sizeConfirm = (item) => {
params.value.size = item.selectedOptions[0].value
size.value = item.selectedOptions[0].text
params.value.aspect_ratio = item.selectedOptions[0].value
params.value.size = item.selectedOptions[0].size
aspectRatio.value = item.selectedOptions[0].text
showSizePicker.value = false
}
+339 -314
View File
@@ -1,338 +1,353 @@
<template>
<div class="mobile-mj">
<van-form>
<div class="text-line">图片比例</div>
<div class="text-line">
<van-row :gutter="10">
<van-col :span="4" v-for="item in rates" :key="item.value">
<div
:class="item.value === params.rate ? 'rate active' : 'rate'"
@click="changeRate(item)"
>
<div class="icon">
<van-image :src="item.img" fit="cover"></van-image>
</div>
<div class="text">{{ item.text }}</div>
</div>
</van-col>
</van-row>
</div>
<div class="text-line">模型选择</div>
<div class="text-line">
<van-row :gutter="10">
<van-col :span="8" v-for="item in models" :key="item.value">
<div
:class="item.value === params.model ? 'model active' : 'model'"
@click="changeModel(item)"
>
<div class="icon">
<van-image :src="item.img" fit="cover"></van-image>
</div>
<div class="text">
<van-text-ellipsis :content="item.text" />
</div>
</div>
</van-col>
</van-row>
</div>
<div class="text-line">
<van-field label="创意度">
<template #input>
<van-slider
v-model.number="params.chaos"
:max="100"
:step="1"
@update:model-value="showToast('当前值:' + params.chaos)"
/>
</template>
</van-field>
</div>
<div class="text-line">
<van-field label="风格化">
<template #input>
<van-slider
v-model.number="params.stylize"
:max="1000"
:step="1"
@update:model-value="showToast('当前值:' + params.stylize)"
/>
</template>
</van-field>
</div>
<div class="text-line">
<van-field label="原始模式">
<template #input>
<van-switch v-model="params.raw" />
</template>
</van-field>
</div>
<div class="text-line">
<van-tabs v-model:active="activeName" @change="tabChange" animated>
<van-tab title="文生图" name="txt2img">
<div class="text-line">
<van-field
v-model="params.prompt"
maxlength="2000"
rows="3"
autosize
type="textarea"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
/>
</div>
</van-tab>
<van-tab title="图生图" name="img2img">
<div class="text-line">
<van-field
v-model="params.prompt"
rows="3"
autosize
maxlength="2000"
type="textarea"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
/>
</div>
<div class="text-line">
<van-uploader v-model="imgList" :after-read="uploadImg" />
</div>
<div class="text-line">
<van-field label="垫图权重">
<template #input>
<van-slider
v-model.number="params.iw"
:max="1"
:step="0.01"
@update:model-value="showToast('当前值:' + params.iw)"
/>
</template>
</van-field>
</div>
<div class="tip-text">
提示只有于 niji6 v6 模型支持一致性功能如果选择其他模型此功能将会生成失败
</div>
<van-cell-group>
<van-field
v-model="params.cref"
center
clearable
label="角色一致性"
placeholder="请输入图片URL或者上传图片"
<div v-if="menus['/mj']?.enabled">
<van-form>
<div class="text-line">图片比例</div>
<div class="text-line">
<van-row :gutter="10">
<van-col :span="4" v-for="item in rates" :key="item.value">
<div
:class="item.value === params.rate ? 'rate active' : 'rate'"
@click="changeRate(item)"
>
<template #button>
<van-uploader @click="beforeUpload('cref')" :after-read="uploadImg">
<van-button size="mini" type="primary" icon="plus" />
</van-uploader>
</template>
</van-field>
</van-cell-group>
<van-cell-group>
<van-field
v-model="params.sref"
center
clearable
label="风格一致性"
placeholder="请输入图片URL或者上传图片"
<div class="icon">
<van-image :src="item.img" fit="cover"></van-image>
</div>
<div class="text">{{ item.text }}</div>
</div>
</van-col>
</van-row>
</div>
<div class="text-line">模型选择</div>
<div class="text-line">
<van-row :gutter="10">
<van-col :span="8" v-for="item in models" :key="item.value">
<div
:class="item.value === params.model ? 'model active' : 'model'"
@click="changeModel(item)"
>
<template #button>
<van-uploader @click="beforeUpload('sref')" :after-read="uploadImg">
<van-button size="mini" type="primary" icon="plus" />
</van-uploader>
</template>
</van-field>
</van-cell-group>
<div class="text-line">
<van-field label="一致性权重">
<template #input>
<van-slider
v-model.number="params.cw"
:max="100"
:step="1"
@update:model-value="showToast('当前值:' + params.cw)"
/>
</template>
</van-field>
</div>
</van-tab>
<van-tab title="融图" name="blend">
<div class="tip-text">
请上传两张以上的图片最多不超过五张超过五张图片请使用图生图功能
</div>
<div class="text-line">
<van-uploader v-model="imgList" :after-read="uploadImg" />
</div>
</van-tab>
<van-tab title="换脸" name="swapFace">
<div class="tip-text">请上传两张有脸部的图片用左边图片的脸替换右边图片的脸</div>
<div class="text-line">
<van-uploader v-model="imgList" :after-read="uploadImg" />
</div>
</van-tab>
</van-tabs>
</div>
<div class="text-line">
<van-collapse v-model="activeColspan">
<van-collapse-item title="反向提示词" name="neg_prompt">
<van-field
v-model="params.neg_prompt"
rows="3"
maxlength="2000"
autosize
type="textarea"
placeholder="不想出现在图片上的元素(例如:树,建筑)"
/>
</van-collapse-item>
</van-collapse>
</div>
<div class="sticky bottom-4 bg-[var(--van-cell-group-background)] rounded-xl p-4 shadow-sm">
<button
@click="generate"
:disabled="loading"
type="button"
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
>
<i v-if="loading" class="iconfont icon-loading animate-spin"></i>
<i v-else class="iconfont icon-chuangzuo"></i>
<span>{{ loading ? '创作中...' : '立即生成' }}({{ mjPower }}算力)</span>
</button>
</div>
</van-form>
<h3 class="m-3">任务列表</h3>
<div class="running-job-list pt-3 pb-3">
<van-empty
v-if="runningJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-grid :gutter="10" :column-num="3" v-else>
<van-grid-item v-for="item in runningJobs" :key="item.id">
<div v-if="item.progress > 0">
<van-image src="/images/img-holder.png"></van-image>
<div class="progress">
<van-circle
v-model:current-rate="item.progress"
:rate="item.progress"
:speed="100"
:text="item.progress + '%'"
:stroke-width="60"
size="90px"
<div class="icon">
<van-image :src="item.img" fit="cover"></van-image>
</div>
<div class="text">
<van-text-ellipsis :content="item.text" />
</div>
</div>
</van-col>
</van-row>
</div>
<div class="text-line">
<van-field label="创意度">
<template #input>
<van-slider
v-model.number="params.chaos"
:max="100"
:step="1"
@update:model-value="showToast('当前值:' + params.chaos)"
/>
</div>
</div>
</template>
</van-field>
</div>
<div v-else class="task-in-queue">
<span class="icon"><i class="iconfont icon-quick-start"></i></span>
<span class="text">排队中</span>
</div>
</van-grid-item>
</van-grid>
</div>
<div class="text-line">
<van-field label="风格化">
<template #input>
<van-slider
v-model.number="params.stylize"
:max="1000"
:step="1"
@update:model-value="showToast('当前值:' + params.stylize)"
/>
</template>
</van-field>
</div>
<h3 class="m-3">创作记录</h3>
<div class="finish-job-list">
<van-empty
v-if="finishedJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<div class="text-line">
<van-field label="原始模式">
<template #input>
<van-switch v-model="params.raw" />
</template>
</van-field>
</div>
<van-list
v-else
v-model:error="error"
v-model:loading="loading"
:finished="finished"
error-text="请求失败,点击重新加载"
finished-text="没有更多了"
@load="onLoad"
>
<van-grid :gutter="10" :column-num="2">
<van-grid-item v-for="item in finishedJobs" :key="item.id" class="min-h-[270px]">
<div class="failed" v-if="item.progress === 101">
<div class="title">任务失败</div>
<div class="opt">
<van-button size="small" @click="showErrMsg(item)">详情</van-button>
<van-button type="danger" @click="removeImage(item)" size="small">删除</van-button>
<div class="text-line">
<van-tabs v-model:active="activeName" @change="tabChange" animated>
<van-tab title="文生图" name="txt2img">
<div class="text-line">
<van-field
v-model="params.prompt"
maxlength="2000"
rows="3"
autosize
type="textarea"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
/>
</div>
</div>
<div class="job-item" v-else>
<van-image
:src="item['thumb_url']"
:class="item['can_opt'] ? '' : 'upscale'"
lazy-load
@click="imageView(item)"
fit="cover"
>
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
<template v-slot:error>
<span style="margin-bottom: 20px">正在下载图片</span>
<van-loading type="circular" color="#1989fa" size="40" />
</template>
</van-image>
<div class="opt" v-if="item['can_opt']">
<van-grid :gutter="3" :column-num="4">
<van-grid-item><a @click="upscale(1, item)" class="opt-btn">U1</a></van-grid-item>
<van-grid-item><a @click="upscale(2, item)" class="opt-btn">U2</a></van-grid-item>
<van-grid-item><a @click="upscale(3, item)" class="opt-btn">U3</a></van-grid-item>
<van-grid-item><a @click="upscale(4, item)" class="opt-btn">U4</a></van-grid-item>
<van-grid-item
><a @click="variation(1, item)" class="opt-btn">V1</a></van-grid-item
>
<van-grid-item
><a @click="variation(2, item)" class="opt-btn">V2</a></van-grid-item
>
<van-grid-item
><a @click="variation(3, item)" class="opt-btn">V3</a></van-grid-item
>
<van-grid-item
><a @click="variation(4, item)" class="opt-btn">V4</a></van-grid-item
>
</van-grid>
</van-tab>
<van-tab title="图生图" name="img2img">
<div class="text-line">
<van-field
v-model="params.prompt"
rows="3"
autosize
maxlength="2000"
type="textarea"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
/>
</div>
<div class="remove">
<el-button type="danger" :icon="Delete" @click="removeImage(item)" circle />
<el-button
type="warning"
v-if="item.publish"
@click="publishImage(item, false)"
circle
<div class="text-line">
<van-uploader v-model="imgList" :after-read="uploadImg" />
</div>
<div class="text-line">
<van-field label="垫图权重">
<template #input>
<van-slider
v-model.number="params.iw"
:max="1"
:step="0.01"
@update:model-value="showToast('当前值:' + params.iw)"
/>
</template>
</van-field>
</div>
<div class="tip-text">
提示只有于 niji6 v6 模型支持一致性功能如果选择其他模型此功能将会生成失败
</div>
<van-cell-group>
<van-field
v-model="params.cref"
center
clearable
label="角色一致性"
placeholder="请输入图片URL或者上传图片"
>
<i class="iconfont icon-cancel-share"></i>
</el-button>
<el-button type="success" v-else @click="publishImage(item, true)" circle>
<i class="iconfont icon-share-bold"></i>
</el-button>
<el-button type="primary" @click="showPrompt(item)" circle>
<i class="iconfont icon-prompt"></i>
</el-button>
<template #button>
<van-uploader @click="beforeUpload('cref')" :after-read="uploadImg">
<van-button size="mini" type="primary" icon="plus" />
</van-uploader>
</template>
</van-field>
</van-cell-group>
<van-cell-group>
<van-field
v-model="params.sref"
center
clearable
label="风格一致性"
placeholder="请输入图片URL或者上传图片"
>
<template #button>
<van-uploader @click="beforeUpload('sref')" :after-read="uploadImg">
<van-button size="mini" type="primary" icon="plus" />
</van-uploader>
</template>
</van-field>
</van-cell-group>
<div class="text-line">
<van-field label="一致性权重">
<template #input>
<van-slider
v-model.number="params.cw"
:max="100"
:step="1"
@update:model-value="showToast('当前值:' + params.cw)"
/>
</template>
</van-field>
</div>
</van-tab>
<van-tab title="融图" name="blend">
<div class="tip-text">
请上传两张以上的图片最多不超过五张超过五张图片请使用图生图功能
</div>
<div class="text-line">
<van-uploader v-model="imgList" :after-read="uploadImg" />
</div>
</van-tab>
<van-tab title="换脸" name="swapFace">
<div class="tip-text">请上传两张有脸部的图片用左边图片的脸替换右边图片的脸</div>
<div class="text-line">
<van-uploader v-model="imgList" :after-read="uploadImg" />
</div>
</van-tab>
</van-tabs>
</div>
<div class="text-line">
<van-collapse v-model="activeColspan">
<van-collapse-item title="反向提示词" name="neg_prompt">
<van-field
v-model="params.neg_prompt"
rows="3"
maxlength="2000"
autosize
type="textarea"
placeholder="不想出现在图片上的元素(例如:树,建筑)"
/>
</van-collapse-item>
</van-collapse>
</div>
<div class="sticky bottom-4 bg-[var(--van-cell-group-background)] rounded-xl p-4 shadow-sm">
<button
@click="generate"
:disabled="loading"
type="button"
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
>
<i v-if="loading" class="iconfont icon-loading animate-spin"></i>
<i v-else class="iconfont icon-chuangzuo"></i>
<span>{{ loading ? '创作中...' : '立即生成' }}({{ mjPower }}算力)</span>
</button>
</div>
</van-form>
<h3 class="m-3">任务列表</h3>
<div class="running-job-list pt-3 pb-3">
<van-empty
v-if="runningJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-grid :gutter="10" :column-num="3" v-else>
<van-grid-item v-for="item in runningJobs" :key="item.id">
<div v-if="item.progress > 0">
<van-image src="/images/img-holder.png"></van-image>
<div class="progress">
<van-circle
v-model:current-rate="item.progress"
:rate="item.progress"
:speed="100"
:text="item.progress + '%'"
:stroke-width="60"
size="90px"
/>
</div>
</div>
<div v-else class="task-in-queue">
<span class="icon"><i class="iconfont icon-quick-start"></i></span>
<span class="text">排队中</span>
</div>
</van-grid-item>
</van-grid>
</van-list>
</div>
</div>
<button style="display: none" class="copy-prompt" :data-clipboard-text="prompt" id="copy-btn">
复制
</button>
<h3 class="m-3">创作记录</h3>
<div class="finish-job-list">
<van-empty
v-if="finishedJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-list
v-else
v-model:error="error"
v-model:loading="loading"
:finished="finished"
error-text="请求失败,点击重新加载"
finished-text="没有更多了"
@load="onLoad"
>
<van-grid :gutter="10" :column-num="2">
<van-grid-item v-for="item in finishedJobs" :key="item.id" class="min-h-[270px]">
<div class="failed" v-if="item.progress === 101">
<div class="title">任务失败</div>
<div class="opt">
<van-button size="small" @click="showErrMsg(item)">详情</van-button>
<van-button type="danger" @click="removeImage(item)" size="small"
>删除</van-button
>
</div>
</div>
<div class="job-item" v-else>
<van-image
:src="item['thumb_url']"
:class="item['can_opt'] ? '' : 'upscale'"
lazy-load
@click="imageView(item)"
fit="cover"
>
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
<template v-slot:error>
<span style="margin-bottom: 20px">正在下载图片</span>
<van-loading type="circular" color="#1989fa" size="40" />
</template>
</van-image>
<div class="opt" v-if="item['can_opt']">
<van-grid :gutter="3" :column-num="4">
<van-grid-item
><a @click="upscale(1, item)" class="opt-btn">U1</a></van-grid-item
>
<van-grid-item
><a @click="upscale(2, item)" class="opt-btn">U2</a></van-grid-item
>
<van-grid-item
><a @click="upscale(3, item)" class="opt-btn">U3</a></van-grid-item
>
<van-grid-item
><a @click="upscale(4, item)" class="opt-btn">U4</a></van-grid-item
>
<van-grid-item
><a @click="variation(1, item)" class="opt-btn">V1</a></van-grid-item
>
<van-grid-item
><a @click="variation(2, item)" class="opt-btn">V2</a></van-grid-item
>
<van-grid-item
><a @click="variation(3, item)" class="opt-btn">V3</a></van-grid-item
>
<van-grid-item
><a @click="variation(4, item)" class="opt-btn">V4</a></van-grid-item
>
</van-grid>
</div>
<div class="remove">
<el-button type="danger" :icon="Delete" @click="removeImage(item)" circle />
<el-button
type="warning"
v-if="item.publish"
@click="publishImage(item, false)"
circle
>
<i class="iconfont icon-cancel-share"></i>
</el-button>
<el-button type="success" v-else @click="publishImage(item, true)" circle>
<i class="iconfont icon-share-bold"></i>
</el-button>
<el-button type="primary" @click="showPrompt(item)" circle>
<i class="iconfont icon-prompt"></i>
</el-button>
</div>
</div>
</van-grid-item>
</van-grid>
</van-list>
</div>
<button style="display: none" class="copy-prompt" :data-clipboard-text="prompt" id="copy-btn">
复制
</button>
</div>
<div v-else>
<FunDisabled />
</div>
</div>
</template>
<script setup>
import { checkSession, getSystemInfo } from '@/store/cache'
import { checkSession, getSystemInfo, getMenus } from '@/store/cache'
import { getSessionId } from '@/store/session'
import { useSharedStore } from '@/store/sharedata'
import { httpGet, httpPost } from '@/utils/http'
@@ -340,6 +355,7 @@ import { showLoginDialog } from '@/utils/libs'
import { Delete } from '@element-plus/icons-vue'
import Clipboard from 'clipboard'
import Compressor from 'compressorjs'
import FunDisabled from '@/components/ui/FunDisabled.vue'
import {
showConfirmDialog,
showDialog,
@@ -351,6 +367,7 @@ import {
} from 'vant'
import { nextTick, onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
const menus = ref({})
const activeColspan = ref([''])
@@ -437,6 +454,14 @@ onMounted(() => {
.catch(() => {
// router.push('/login')
})
getMenus()
.then((data) => {
menus.value = data
})
.catch((e) => {
showNotify({ type: 'danger', message: '获取菜单失败:' + e.message })
})
})
onUnmounted(() => {
+241 -219
View File
@@ -1,265 +1,278 @@
<template>
<div class="mobile-sd">
<van-form>
<van-cell-group class="px-3 pt-3 pb-4">
<div>
<van-field
v-model="params.sampler"
is-link
readonly
label="采样方法"
placeholder="选择采样方法"
@click="showSamplerPicker = true"
/>
<van-popup v-model:show="showSamplerPicker" position="bottom" teleport="#app">
<van-picker
:columns="samplers"
@cancel="showSamplerPicker = false"
@confirm="samplerConfirm"
/>
</van-popup>
</div>
<van-field label="图片尺寸">
<template #input>
<van-row gutter="20">
<van-col span="12">
<el-input v-model="params.width" size="small" placeholder="宽" />
</van-col>
<van-col span="12">
<el-input v-model="params.height" size="small" placeholder="高" />
</van-col>
</van-row>
</template>
</van-field>
<van-field v-model.number="params.steps" label="迭代步数" placeholder="">
<template #right-icon>
<van-icon
name="info-o"
@click="showInfo('值越大则代表细节越多,同时也意味着出图速度越慢,一般推荐20-30')"
/>
</template>
</van-field>
<van-field v-model.number="params.cfg_scale" label="引导系数" placeholder="">
<template #right-icon>
<van-icon
name="info-o"
@click="
showInfo('提示词引导系数,图像在多大程度上服从提示词,较低值会产生更有创意的结果')
"
/>
</template>
</van-field>
<van-field v-model.number="params.seed" label="随机因子" placeholder="">
<template #right-icon>
<van-icon
name="info-o"
@click="
showInfo('随机数种子,相同的种子会得到相同的结果,设置为 -1 则每次随机生成种子')
"
/>
</template>
</van-field>
<van-field label="高清修复">
<template #input>
<van-switch v-model="params.hd_fix" />
</template>
</van-field>
<div v-if="params.hd_fix">
<div v-if="menus['/sd']?.enabled">
<van-form>
<van-cell-group class="px-3 pt-3 pb-4">
<div>
<van-field
v-model="params.hd_scale_alg"
v-model="params.sampler"
is-link
readonly
label="放大算法"
placeholder="选择放大算法"
@click="showUpscalePicker = true"
label="采样方法"
placeholder="选择采样方法"
@click="showSamplerPicker = true"
/>
<van-popup v-model:show="showUpscalePicker" position="bottom" teleport="#app">
<van-popup v-model:show="showSamplerPicker" position="bottom" teleport="#app">
<van-picker
:columns="upscaleAlgArr"
@cancel="showUpscalePicker = false"
@confirm="upscaleConfirm"
:columns="samplers"
@cancel="showSamplerPicker = false"
@confirm="samplerConfirm"
/>
</van-popup>
</div>
<van-field v-model.number="params.hd_scale" label="放大倍数" />
<van-field v-model.number="params.hd_steps" label="迭代步数" />
<van-field label="重绘幅度">
<van-field label="图片尺寸">
<template #input>
<van-slider
v-model.number="params.hd_redraw_rate"
:max="1"
:step="0.1"
@update:model-value="showToast('当前值:' + params.hd_redraw_rate)"
/>
<van-row gutter="20">
<van-col span="12">
<el-input v-model="params.width" size="small" placeholder="宽" />
</van-col>
<van-col span="12">
<el-input v-model="params.height" size="small" placeholder="高" />
</van-col>
</van-row>
</template>
</van-field>
<van-field v-model.number="params.steps" label="迭代步数" placeholder="">
<template #right-icon>
<van-icon
name="info-o"
@click="showInfo('决定算法对图像内容的影响程度,较大的值将得到越有创意的图像')"
@click="showInfo('值越大则代表细节越多,同时也意味着出图速度越慢,一般推荐20-30')"
/>
</template>
</van-field>
</div>
<van-field
v-model="params.prompt"
maxlength="2000"
rows="3"
autosize
type="textarea"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
/>
<van-collapse v-model="activeColspan">
<van-collapse-item title="反向提示词" name="neg_prompt">
<van-field
v-model="params.neg_prompt"
rows="3"
maxlength="2000"
autosize
type="textarea"
placeholder="不想出现在图片上的元素(例如:树,建筑)"
/>
</van-collapse-item>
</van-collapse>
<div class="sticky bottom-4 bg-[var(--van-cell-group-background)] rounded-xl p-4 shadow-sm">
<button
@click="generate"
:disabled="loading"
type="button"
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
>
<i v-if="loading" class="iconfont icon-loading animate-spin"></i>
<i v-else class="iconfont icon-chuangzuo"></i>
<span>{{ loading ? '创作中...' : '立即生成' }}({{ sdPower }}算力)</span>
</button>
</div>
</van-cell-group>
</van-form>
<h3 class="m-3">任务列表</h3>
<div class="running-job-list pt-3 pb-3">
<van-empty
v-if="runningJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-grid :gutter="10" :column-num="3" v-else>
<van-grid-item v-for="item in runningJobs" :key="item.id">
<div v-if="item.progress > 0">
<van-image src="/images/img-holder.png"></van-image>
<div class="progress">
<van-circle
v-model:current-rate="item.progress"
:rate="item.progress"
:speed="100"
:text="item.progress + '%'"
:stroke-width="60"
size="90px"
<van-field v-model.number="params.cfg_scale" label="引导系数" placeholder="">
<template #right-icon>
<van-icon
name="info-o"
@click="
showInfo('提示词引导系数,图像在多大程度上服从提示词,较低值会产生更有创意的结果')
"
/>
</template>
</van-field>
<van-field v-model.number="params.seed" label="随机因子" placeholder="">
<template #right-icon>
<van-icon
name="info-o"
@click="
showInfo('随机数种子,相同的种子会得到相同的结果,设置为 -1 则每次随机生成种子')
"
/>
</template>
</van-field>
<van-field label="高清修复">
<template #input>
<van-switch v-model="params.hd_fix" />
</template>
</van-field>
<div v-if="params.hd_fix">
<div>
<van-field
v-model="params.hd_scale_alg"
is-link
readonly
label="放大算法"
placeholder="选择放大算法"
@click="showUpscalePicker = true"
/>
<van-popup v-model:show="showUpscalePicker" position="bottom" teleport="#app">
<van-picker
:columns="upscaleAlgArr"
@cancel="showUpscalePicker = false"
@confirm="upscaleConfirm"
/>
</van-popup>
</div>
<van-field v-model.number="params.hd_scale" label="放大倍数" />
<van-field v-model.number="params.hd_steps" label="迭代步数" />
<van-field label="重绘幅度">
<template #input>
<van-slider
v-model.number="params.hd_redraw_rate"
:max="1"
:step="0.1"
@update:model-value="showToast('当前值:' + params.hd_redraw_rate)"
/>
</template>
<template #right-icon>
<van-icon
name="info-o"
@click="showInfo('决定算法对图像内容的影响程度,较大的值将得到越有创意的图像')"
/>
</template>
</van-field>
</div>
<div v-else class="task-in-queue">
<span class="icon"><i class="iconfont icon-quick-start"></i></span>
<span class="text">排队中</span>
<van-field
v-model="params.prompt"
maxlength="2000"
rows="3"
autosize
type="textarea"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
/>
<van-collapse v-model="activeColspan">
<van-collapse-item title="反向提示词" name="neg_prompt">
<van-field
v-model="params.neg_prompt"
rows="3"
maxlength="2000"
autosize
type="textarea"
placeholder="不想出现在图片上的元素(例如:树,建筑)"
/>
</van-collapse-item>
</van-collapse>
<div
class="sticky bottom-4 bg-[var(--van-cell-group-background)] rounded-xl p-4 shadow-sm"
>
<button
@click="generate"
:disabled="loading"
type="button"
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
>
<i v-if="loading" class="iconfont icon-loading animate-spin"></i>
<i v-else class="iconfont icon-chuangzuo"></i>
<span>{{ loading ? '创作中...' : '立即生成' }}({{ sdPower }}算力)</span>
</button>
</div>
</van-grid-item>
</van-grid>
</div>
</van-cell-group>
</van-form>
<h3 class="m-3">创作记录</h3>
<div class="finish-job-list">
<van-empty
v-if="finishedJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-list
v-else
v-model:error="error"
v-model:loading="loading"
:finished="finished"
error-text="请求失败,点击重新加载"
finished-text="没有更多了"
@load="onLoad"
>
<van-grid :gutter="10" :column-num="2">
<van-grid-item v-for="item in finishedJobs" :key="item.id">
<div class="failed" v-if="item.progress === 101">
<div class="title">任务失败</div>
<div class="opt">
<van-button size="small" @click="showErrMsg(item)">详情</van-button>
<van-button type="danger" @click="removeImage($event, item)" size="small"
>删除</van-button
>
<h3 class="m-3">任务列表</h3>
<div class="running-job-list pt-3 pb-3">
<van-empty
v-if="runningJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-grid :gutter="10" :column-num="3" v-else>
<van-grid-item v-for="item in runningJobs" :key="item.id">
<div v-if="item.progress > 0">
<van-image src="/images/img-holder.png"></van-image>
<div class="progress">
<van-circle
v-model:current-rate="item.progress"
:rate="item.progress"
:speed="100"
:text="item.progress + '%'"
:stroke-width="60"
size="90px"
/>
</div>
</div>
<div class="job-item" v-else>
<van-image
:src="item['img_url']"
:class="item['can_opt'] ? '' : 'upscale'"
lazy-load
@click="imageView(item)"
fit="cover"
>
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
</van-image>
<div class="remove">
<el-button type="danger" :icon="Delete" @click="removeImage($event, item)" circle />
<el-button
type="warning"
v-if="item.publish"
@click="publishImage($event, item, false)"
circle
>
<i class="iconfont icon-cancel-share"></i>
</el-button>
<el-button type="success" v-else @click="publishImage($event, item, true)" circle>
<i class="iconfont icon-share-bold"></i>
</el-button>
<el-button type="primary" @click="showPrompt(item)" circle>
<i class="iconfont icon-prompt"></i>
</el-button>
</div>
<div v-else class="task-in-queue">
<span class="icon"><i class="iconfont icon-quick-start"></i></span>
<span class="text">排队中</span>
</div>
</van-grid-item>
</van-grid>
</van-list>
</div>
<h3 class="m-3">创作记录</h3>
<div class="finish-job-list">
<van-empty
v-if="finishedJobs.length === 0"
image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
image-size="80"
description="暂无记录"
/>
<van-list
v-else
v-model:error="error"
v-model:loading="loading"
:finished="finished"
error-text="请求失败,点击重新加载"
finished-text="没有更多了"
@load="onLoad"
>
<van-grid :gutter="10" :column-num="2">
<van-grid-item v-for="item in finishedJobs" :key="item.id">
<div class="failed" v-if="item.progress === 101">
<div class="title">任务失败</div>
<div class="opt">
<van-button size="small" @click="showErrMsg(item)">详情</van-button>
<van-button type="danger" @click="removeImage($event, item)" size="small"
>删除</van-button
>
</div>
</div>
<div class="job-item" v-else>
<van-image
:src="item['img_url']"
:class="item['can_opt'] ? '' : 'upscale'"
lazy-load
@click="imageView(item)"
fit="cover"
>
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
</van-image>
<div class="remove">
<el-button
type="danger"
:icon="Delete"
@click="removeImage($event, item)"
circle
/>
<el-button
type="warning"
v-if="item.publish"
@click="publishImage($event, item, false)"
circle
>
<i class="iconfont icon-cancel-share"></i>
</el-button>
<el-button type="success" v-else @click="publishImage($event, item, true)" circle>
<i class="iconfont icon-share-bold"></i>
</el-button>
<el-button type="primary" @click="showPrompt(item)" circle>
<i class="iconfont icon-prompt"></i>
</el-button>
</div>
</div>
</van-grid-item>
</van-grid>
</van-list>
</div>
<button
style="display: none"
class="copy-prompt-sd"
:data-clipboard-text="prompt"
id="copy-btn-sd"
>
复制
</button>
</div>
<div v-else>
<FunDisabled />
</div>
<button
style="display: none"
class="copy-prompt-sd"
:data-clipboard-text="prompt"
id="copy-btn-sd"
>
复制
</button>
</div>
</template>
<script setup>
import { checkSession, getSystemInfo } from '@/store/cache'
import { checkSession, getSystemInfo, getMenus } from '@/store/cache'
import { getSessionId } from '@/store/session'
import { useSharedStore } from '@/store/sharedata'
import { httpGet, httpPost } from '@/utils/http'
import { showLoginDialog } from '@/utils/libs'
import { Delete } from '@element-plus/icons-vue'
import Clipboard from 'clipboard'
import FunDisabled from '@/components/ui/FunDisabled.vue'
import {
showConfirmDialog,
showDialog,
@@ -276,6 +289,7 @@ const listBoxHeight = ref(window.innerHeight - 40)
const mjBoxHeight = ref(window.innerHeight - 150)
const isLogin = ref(false)
const activeColspan = ref([''])
const menus = ref({})
window.onresize = () => {
listBoxHeight.value = window.innerHeight - 40
@@ -352,6 +366,14 @@ onMounted(() => {
.catch((e) => {
showNotify({ type: 'danger', message: '获取系统配置失败:' + e.message })
})
getMenus()
.then((data) => {
menus.value = data
})
.catch((e) => {
showNotify({ type: 'danger', message: '获取菜单失败:' + e.message })
})
})
onUnmounted(() => {
+17
View File
@@ -25,6 +25,23 @@ export default defineConfig(({ mode }) => {
base: env.VITE_BASE_URL,
build: {
outDir: 'dist', // 构建输出目录
target: 'esnext', // 允许使用顶层 await 等最新特性
},
// 开发环境与依赖预构建同样对齐到 esnext,避免 esbuild 报错
esbuild: {
target: 'esnext',
supported: {
'top-level-await': true,
},
},
optimizeDeps: {
esbuildOptions: {
target: 'esnext',
supported: {
'top-level-await': true,
},
},
},
resolve: {