Merge branch 'dev'
26
CHANGELOG.md
@ -1,20 +1,30 @@
|
|||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
## 4.0.2
|
## 4.0.2
|
||||||
|
|
||||||
* 功能新增:支持前端菜单可以配置
|
* 功能新增:支持前端菜单可以配置
|
||||||
* 功能优化:手机端支持免登录预览功能
|
* 功能优化:在登录和注册界面标题显示软件版本号
|
||||||
|
* 功能优化:MJ 绘画支持 --sref 和 --cref 图片一致性参数
|
||||||
|
* 功能优化:使用 leveldb 解决 SD 绘图进度图片预览问题
|
||||||
|
* Bug修复:解决因为图片上传使用相对路径而导致融图失败的问题。
|
||||||
* 功能新增:手机端支持 Stable-Diffusion 绘画
|
* 功能新增:手机端支持 Stable-Diffusion 绘画
|
||||||
* 功能新增:管理后台登录页面增加行为验证码,防止爆破
|
* 功能新增:管理后台登录页面增加行为验证码,防止爆破
|
||||||
|
|
||||||
## v4.0.1
|
## v4.0.1
|
||||||
* 功能重构:重构 Stable-Diffusion 绘画实现,使用 SDAPI 替换之前的 websocket 接口,SDAPI 兼容各种 stable-diffusion 发行版,稳定性更强一些
|
|
||||||
* 功能优化:使用 [midjouney-proxy](https://github.com/novicezk/midjourney-proxy) 项目替换内置的原生 MidJourney API,兼容 MJ-Plus 中转
|
* 功能重构:重构 Stable-Diffusion 绘画实现,使用 SDAPI 替换之前的 websocket 接口,SDAPI 兼容各种 stable-diffusion
|
||||||
|
发行版,稳定性更强一些
|
||||||
|
* 功能优化:使用 [midjouney-proxy](https://github.com/novicezk/midjourney-proxy) 项目替换内置的原生 MidJourney API,兼容
|
||||||
|
MJ-Plus 中转
|
||||||
* 功能新增:用户算力消费日志增加统计功能,统计一段时间内用户消费的算力
|
* 功能新增:用户算力消费日志增加统计功能,统计一段时间内用户消费的算力
|
||||||
* Bug修复:修复 iphone 手机无法通过图形验证码的Bug,使用滑动验证码替换
|
* Bug修复:修复 iphone 手机无法通过图形验证码的Bug,使用滑动验证码替换
|
||||||
* Bug修复:修复手机端 MidJourney 绘画页面滚动条无法滚动的Bug
|
* Bug修复:修复手机端 MidJourney 绘画页面滚动条无法滚动的Bug
|
||||||
|
|
||||||
## v4.0.0
|
## v4.0.0
|
||||||
|
|
||||||
非兼容版本,重大重构,引入算力概念,将系统中所有的能力(AI对话,MJ绘画,SD绘画,DALL绘画)全部使用算力来兑换。
|
非兼容版本,重大重构,引入算力概念,将系统中所有的能力(AI对话,MJ绘画,SD绘画,DALL绘画)全部使用算力来兑换。
|
||||||
只要你的算力值余额不为0,你就可以进行任何操作。比如一次 GPT3.5 对话消耗1个单位算力,一次 GPT4 对话消耗10个算力。一次 MJ 对话消耗15个算力...
|
只要你的算力值余额不为0,你就可以进行任何操作。比如一次 GPT3.5 对话消耗1个单位算力,一次 GPT4 对话消耗10个算力。一次 MJ
|
||||||
|
对话消耗15个算力...
|
||||||
|
|
||||||
* 功能重构:重构整体系统,全部采用算力来进行结算
|
* 功能重构:重构整体系统,全部采用算力来进行结算
|
||||||
* 功能优化:SD 绘画页面采用 websocket 替换 http 轮询机制,节省带宽
|
* 功能优化:SD 绘画页面采用 websocket 替换 http 轮询机制,节省带宽
|
||||||
@ -29,6 +39,7 @@
|
|||||||
* 功能新增:管理后台新增7日内新增用户和新增订单统计
|
* 功能新增:管理后台新增7日内新增用户和新增订单统计
|
||||||
|
|
||||||
## v3.2.7
|
## v3.2.7
|
||||||
|
|
||||||
* 功能重构:采用 Vant 重构移动页面,新增 MidJourney 功能
|
* 功能重构:采用 Vant 重构移动页面,新增 MidJourney 功能
|
||||||
* 功能优化:优化 PC 端 MidJourney 页面布局,新增融图和换脸功能
|
* 功能优化:优化 PC 端 MidJourney 页面布局,新增融图和换脸功能
|
||||||
* Bug修复:修复 issue [
|
* Bug修复:修复 issue [
|
||||||
@ -43,6 +54,7 @@
|
|||||||
* 功能新增:后台管理新怎对话查看和检索功能
|
* 功能新增:后台管理新怎对话查看和检索功能
|
||||||
|
|
||||||
## v3.2.6
|
## v3.2.6
|
||||||
|
|
||||||
* 功能优化:恢复关闭注册系统配置项,管理员可以在后台关闭用户注册,只允许内部添加账号
|
* 功能优化:恢复关闭注册系统配置项,管理员可以在后台关闭用户注册,只允许内部添加账号
|
||||||
* 功能优化:兼用旧版本微信收款消息解析
|
* 功能优化:兼用旧版本微信收款消息解析
|
||||||
* 功能优化:优化订单扫码支付状态轮询功能,当关闭二维码时取消轮询,节约网络资源
|
* 功能优化:优化订单扫码支付状态轮询功能,当关闭二维码时取消轮询,节约网络资源
|
||||||
@ -56,16 +68,18 @@
|
|||||||
* 功能优化:给所有的 websocket 连接加上心跳,解决 "close 1006 (abnormal closure): unexpected EOF" Bug
|
* 功能优化:给所有的 websocket 连接加上心跳,解决 "close 1006 (abnormal closure): unexpected EOF" Bug
|
||||||
* 功能新增:新增短信宝短信平台发送平台集成
|
* 功能新增:新增短信宝短信平台发送平台集成
|
||||||
|
|
||||||
|
|
||||||
## v3.2.5
|
## v3.2.5
|
||||||
|
|
||||||
* 功能新增:**重磅更新!!!** 新增 MidJourney-Plus API 支持,一秒配置,开箱即用,高效稳定。
|
* 功能新增:**重磅更新!!!** 新增 MidJourney-Plus API 支持,一秒配置,开箱即用,高效稳定。
|
||||||
* 功能新增:**重磅更新!!!** 新增 GPT4-ALL 和 GPTs 模型支持,你只需花几块钱,可以丝滑享受 ChatGPT-Plus 会员的所有功能,无需再订阅 Plus 账号了!!!
|
* 功能新增:**重磅更新!!!** 新增 GPT4-ALL 和 GPTs 模型支持,你只需花几块钱,可以丝滑享受 ChatGPT-Plus 会员的所有功能,无需再订阅
|
||||||
|
Plus 账号了!!!
|
||||||
* 功能优化:增强 markdown 图片和引用块解析。
|
* 功能优化:增强 markdown 图片和引用块解析。
|
||||||
* 功能新增:新增用户文件管理,目前一支持上传文件跟 GPT 进行多态对话。
|
* 功能新增:新增用户文件管理,目前一支持上传文件跟 GPT 进行多态对话。
|
||||||
* 功能优化:function call 兼用中转 API。
|
* 功能优化:function call 兼用中转 API。
|
||||||
* Bug修复:修复部分已知的 Bug。
|
* Bug修复:修复部分已知的 Bug。
|
||||||
|
|
||||||
## v3.2.4.1
|
## v3.2.4.1
|
||||||
|
|
||||||
* 功能新增:新增 PayJs 支付通道
|
* 功能新增:新增 PayJs 支付通道
|
||||||
* Bug修复:紧急修复后台添加用户失败问题
|
* Bug修复:紧急修复后台添加用户失败问题
|
||||||
* Bug修复:紧急修复使用中转 API-KEY 无法绘图的问题
|
* Bug修复:紧急修复使用中转 API-KEY 无法绘图的问题
|
||||||
|
@ -91,7 +91,7 @@ KEY。
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
另外,如果您目前还没有 OpenAI 的 API KEY的,推荐您去 https://gpt.bemore.lol 购买,**无需魔法,高速稳定,且价格还远低于 OpenAI
|
另外,如果您目前还没有 OpenAI 的 API KEY的,推荐您去 https://api.chat-plus.net 购买,**无需魔法,高速稳定,且价格还远低于 OpenAI
|
||||||
官方**。
|
官方**。
|
||||||
|
|
||||||
## 使用须知
|
## 使用须知
|
||||||
|
@ -25,6 +25,8 @@ type MjTask struct {
|
|||||||
Type TaskType `json:"type"`
|
Type TaskType `json:"type"`
|
||||||
UserId int `json:"user_id"`
|
UserId int `json:"user_id"`
|
||||||
Prompt string `json:"prompt,omitempty"`
|
Prompt string `json:"prompt,omitempty"`
|
||||||
|
NegPrompt string `json:"neg_prompt,omitempty"`
|
||||||
|
Params string `json:"full_prompt"`
|
||||||
Index int `json:"index,omitempty"`
|
Index int `json:"index,omitempty"`
|
||||||
MessageId string `json:"message_id,omitempty"`
|
MessageId string `json:"message_id,omitempty"`
|
||||||
MessageHash string `json:"message_hash,omitempty"`
|
MessageHash string `json:"message_hash,omitempty"`
|
||||||
@ -43,7 +45,7 @@ type SdTask struct {
|
|||||||
type SdTaskParams struct {
|
type SdTaskParams struct {
|
||||||
TaskId string `json:"task_id"`
|
TaskId string `json:"task_id"`
|
||||||
Prompt string `json:"prompt"` // 提示词
|
Prompt string `json:"prompt"` // 提示词
|
||||||
NegativePrompt string `json:"negative_prompt"` // 反向提示词
|
NegPrompt string `json:"neg_prompt"` // 反向提示词
|
||||||
Steps int `json:"steps"` // 迭代步数,默认20
|
Steps int `json:"steps"` // 迭代步数,默认20
|
||||||
Sampler string `json:"sampler"` // 采样器
|
Sampler string `json:"sampler"` // 采样器
|
||||||
FaceFix bool `json:"face_fix"` // 面部修复
|
FaceFix bool `json:"face_fix"` // 面部修复
|
||||||
|
@ -28,10 +28,12 @@ require github.com/xxl-job/xxl-job-executor-go v1.2.0
|
|||||||
require (
|
require (
|
||||||
github.com/mojocn/base64Captcha v1.3.1
|
github.com/mojocn/base64Captcha v1.3.1
|
||||||
github.com/shopspring/decimal v1.3.1
|
github.com/shopspring/decimal v1.3.1
|
||||||
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||||
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 // indirect
|
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
16
api/go.sum
@ -27,6 +27,7 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
|||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/eatmoreapple/openwechat v1.2.1 h1:ez4oqF/Y2NSEX/DbPV8lvj7JlfkYqvieeo4awx5lzfU=
|
github.com/eatmoreapple/openwechat v1.2.1 h1:ez4oqF/Y2NSEX/DbPV8lvj7JlfkYqvieeo4awx5lzfU=
|
||||||
github.com/eatmoreapple/openwechat v1.2.1/go.mod h1:61HOzTyvLobGdgWhL68jfGNwTJEv0mhQ1miCXQrvWU8=
|
github.com/eatmoreapple/openwechat v1.2.1/go.mod h1:61HOzTyvLobGdgWhL68jfGNwTJEv0mhQ1miCXQrvWU8=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
@ -67,8 +68,11 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF0
|
|||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||||
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
@ -83,6 +87,7 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
|
|||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/imroc/req/v3 v3.37.2 h1:vEemuA0cq9zJ6lhe+mSRhsZm951bT0CdiSH47+KTn6I=
|
github.com/imroc/req/v3 v3.37.2 h1:vEemuA0cq9zJ6lhe+mSRhsZm951bT0CdiSH47+KTn6I=
|
||||||
github.com/imroc/req/v3 v3.37.2/go.mod h1:DECzjVIrj6jcUr5n6e+z0ygmCO93rx4Jy0RjOEe1YCI=
|
github.com/imroc/req/v3 v3.37.2/go.mod h1:DECzjVIrj6jcUr5n6e+z0ygmCO93rx4Jy0RjOEe1YCI=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
@ -133,9 +138,12 @@ github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ
|
|||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo/v2 v2.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs=
|
github.com/onsi/ginkgo/v2 v2.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs=
|
||||||
github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE=
|
github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE=
|
||||||
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
||||||
@ -193,6 +201,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
|||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||||
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
||||||
@ -235,6 +245,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
@ -243,11 +254,13 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -294,12 +307,15 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
@ -478,7 +478,7 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
|
|||||||
request = request.WithContext(ctx)
|
request = request.WithContext(ctx)
|
||||||
request.Header.Set("Content-Type", "application/json")
|
request.Header.Set("Content-Type", "application/json")
|
||||||
var proxyURL string
|
var proxyURL string
|
||||||
if apiKey.ProxyURL != "" { // 使用代理
|
if len(apiKey.ProxyURL) > 5 { // 使用代理
|
||||||
proxy, _ := url.Parse(apiKey.ProxyURL)
|
proxy, _ := url.Parse(apiKey.ProxyURL)
|
||||||
client = &http.Client{
|
client = &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
|
@ -220,7 +220,7 @@ func (h *FunctionHandler) Dall3(c *gin.Context) {
|
|||||||
var res imgRes
|
var res imgRes
|
||||||
var errRes ErrRes
|
var errRes ErrRes
|
||||||
var request *req.Request
|
var request *req.Request
|
||||||
if apiKey.ProxyURL != "" {
|
if len(apiKey.ProxyURL) > 5 {
|
||||||
request = req.C().SetProxyURL(apiKey.ProxyURL).R()
|
request = req.C().SetProxyURL(apiKey.ProxyURL).R()
|
||||||
} else {
|
} else {
|
||||||
request = req.C().R()
|
request = req.C().R()
|
||||||
|
@ -98,7 +98,10 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
|||||||
ImgArr []string `json:"img_arr"`
|
ImgArr []string `json:"img_arr"`
|
||||||
Tile bool `json:"tile"`
|
Tile bool `json:"tile"`
|
||||||
Quality float32 `json:"quality"`
|
Quality float32 `json:"quality"`
|
||||||
Weight float32 `json:"weight"`
|
Iw float32 `json:"iw"`
|
||||||
|
CRef string `json:"cref"` //生成角色一致的图像
|
||||||
|
SRef string `json:"sref"` //生成风格一致的图像
|
||||||
|
Cw int `json:"cw"` // 参考程度
|
||||||
}
|
}
|
||||||
if err := c.ShouldBindJSON(&data); err != nil {
|
if err := c.ShouldBindJSON(&data); err != nil {
|
||||||
resp.ERROR(c, types.InvalidArgs)
|
resp.ERROR(c, types.InvalidArgs)
|
||||||
@ -108,41 +111,57 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var prompt = data.Prompt
|
var params = ""
|
||||||
if data.Rate != "" && !strings.Contains(prompt, "--ar") {
|
if data.Rate != "" && !strings.Contains(params, "--ar") {
|
||||||
prompt += " --ar " + data.Rate
|
params += " --ar " + data.Rate
|
||||||
}
|
}
|
||||||
if data.Seed > 0 && !strings.Contains(prompt, "--seed") {
|
if data.Seed > 0 && !strings.Contains(params, "--seed") {
|
||||||
prompt += fmt.Sprintf(" --seed %d", data.Seed)
|
params += fmt.Sprintf(" --seed %d", data.Seed)
|
||||||
}
|
}
|
||||||
if data.Stylize > 0 && !strings.Contains(prompt, "--s") && !strings.Contains(prompt, "--stylize") {
|
if data.Stylize > 0 && !strings.Contains(params, "--s") && !strings.Contains(params, "--stylize") {
|
||||||
prompt += fmt.Sprintf(" --s %d", data.Stylize)
|
params += fmt.Sprintf(" --s %d", data.Stylize)
|
||||||
}
|
}
|
||||||
if data.Chaos > 0 && !strings.Contains(prompt, "--c") && !strings.Contains(prompt, "--chaos") {
|
if data.Chaos > 0 && !strings.Contains(params, "--c") && !strings.Contains(params, "--chaos") {
|
||||||
prompt += fmt.Sprintf(" --c %d", data.Chaos)
|
params += fmt.Sprintf(" --c %d", data.Chaos)
|
||||||
}
|
}
|
||||||
if data.Weight > 0 {
|
if len(data.ImgArr) > 0 && data.Iw > 0 {
|
||||||
prompt += fmt.Sprintf(" --iw %f", data.Weight)
|
params += fmt.Sprintf(" --iw %f", data.Iw)
|
||||||
}
|
}
|
||||||
if data.Raw {
|
if data.Raw {
|
||||||
prompt += " --style raw"
|
params += " --style raw"
|
||||||
}
|
}
|
||||||
if data.Quality > 0 {
|
if data.Quality > 0 {
|
||||||
prompt += fmt.Sprintf(" --q %.2f", data.Quality)
|
params += fmt.Sprintf(" --q %.2f", data.Quality)
|
||||||
}
|
|
||||||
if data.NegPrompt != "" {
|
|
||||||
prompt += fmt.Sprintf(" --no %s", data.NegPrompt)
|
|
||||||
}
|
}
|
||||||
if data.Tile {
|
if data.Tile {
|
||||||
prompt += " --tile "
|
params += " --tile "
|
||||||
}
|
}
|
||||||
if data.Model != "" && !strings.Contains(prompt, "--v") && !strings.Contains(prompt, "--niji") {
|
if data.CRef != "" {
|
||||||
prompt += fmt.Sprintf(" %s", data.Model)
|
params += fmt.Sprintf(" --cref %s", data.CRef)
|
||||||
|
if data.Cw > 0 {
|
||||||
|
params += fmt.Sprintf(" --cw %d", data.Cw)
|
||||||
|
} else {
|
||||||
|
params += " --cw 100"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.SRef != "" {
|
||||||
|
params += fmt.Sprintf(" --sref %s", data.CRef)
|
||||||
|
}
|
||||||
|
if data.Model != "" && !strings.Contains(params, "--v") && !strings.Contains(params, "--niji") {
|
||||||
|
params += fmt.Sprintf(" %s", data.Model)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理融图和换脸的提示词
|
// 处理融图和换脸的提示词
|
||||||
if data.TaskType == types.TaskSwapFace.String() || data.TaskType == types.TaskBlend.String() {
|
if data.TaskType == types.TaskSwapFace.String() || data.TaskType == types.TaskBlend.String() {
|
||||||
prompt = fmt.Sprintf("%s:%s", data.TaskType, strings.Join(data.ImgArr, ","))
|
params = fmt.Sprintf("%s:%s", data.TaskType, strings.Join(data.ImgArr, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果本地图片上传的是相对地址,处理成绝对地址
|
||||||
|
for k, v := range data.ImgArr {
|
||||||
|
if !strings.HasPrefix(v, "http") {
|
||||||
|
data.ImgArr[k] = fmt.Sprintf("http://localhost:5678/%s", strings.TrimLeft(v, "/"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
idValue, _ := c.Get(types.LoginUserID)
|
idValue, _ := c.Get(types.LoginUserID)
|
||||||
@ -158,7 +177,7 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
|||||||
UserId: userId,
|
UserId: userId,
|
||||||
TaskId: taskId,
|
TaskId: taskId,
|
||||||
Progress: 0,
|
Progress: 0,
|
||||||
Prompt: prompt,
|
Prompt: fmt.Sprintf("%s %s", data.Prompt, params),
|
||||||
Power: h.App.SysConfig.MjPower,
|
Power: h.App.SysConfig.MjPower,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
@ -181,7 +200,9 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
|||||||
TaskId: taskId,
|
TaskId: taskId,
|
||||||
SessionId: data.SessionId,
|
SessionId: data.SessionId,
|
||||||
Type: types.TaskType(data.TaskType),
|
Type: types.TaskType(data.TaskType),
|
||||||
Prompt: prompt,
|
Prompt: data.Prompt,
|
||||||
|
NegPrompt: data.NegPrompt,
|
||||||
|
Params: params,
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
ImgArr: data.ImgArr,
|
ImgArr: data.ImgArr,
|
||||||
})
|
})
|
||||||
|
@ -424,29 +424,23 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error {
|
|||||||
|
|
||||||
var opt string
|
var opt string
|
||||||
var power int
|
var power int
|
||||||
if user.Vip { // 已经是 VIP 用户
|
if remark.Days > 0 { // VIP 充值
|
||||||
if remark.Days > 0 { // 只延期 VIP,不增加调用次数
|
if user.ExpiredTime >= time.Now().Unix() {
|
||||||
user.ExpiredTime = time.Unix(user.ExpiredTime, 0).AddDate(0, 0, remark.Days).Unix()
|
user.ExpiredTime = time.Unix(user.ExpiredTime, 0).AddDate(0, 0, remark.Days).Unix()
|
||||||
|
opt = "VIP充值,VIP 没到期,只延期不增加算力"
|
||||||
|
} else {
|
||||||
|
user.ExpiredTime = time.Now().AddDate(0, 0, remark.Days).Unix()
|
||||||
|
user.Power += h.App.SysConfig.VipMonthPower
|
||||||
|
power = h.App.SysConfig.VipMonthPower
|
||||||
|
opt = "VIP充值"
|
||||||
|
}
|
||||||
|
user.Vip = true
|
||||||
} else { // 充值点卡,直接增加次数即可
|
} else { // 充值点卡,直接增加次数即可
|
||||||
user.Power += remark.Power
|
user.Power += remark.Power
|
||||||
opt = "点卡充值"
|
opt = "点卡充值"
|
||||||
power = remark.Power
|
power = remark.Power
|
||||||
}
|
}
|
||||||
|
|
||||||
} else { // 非 VIP 用户
|
|
||||||
if remark.Days > 0 { // vip 套餐:days > 0, power == 0
|
|
||||||
user.ExpiredTime = time.Now().AddDate(0, 0, remark.Days).Unix()
|
|
||||||
user.Power += h.App.SysConfig.VipMonthPower
|
|
||||||
user.Vip = true
|
|
||||||
opt = "VIP充值"
|
|
||||||
power = h.App.SysConfig.VipMonthPower
|
|
||||||
} else { //点卡:days == 0, calls > 0
|
|
||||||
user.Power += remark.Power
|
|
||||||
opt = "点卡充值"
|
|
||||||
power = remark.Power
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新用户信息
|
// 更新用户信息
|
||||||
res = h.DB.Updates(&user)
|
res = h.DB.Updates(&user)
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
|
@ -6,11 +6,11 @@ import (
|
|||||||
"chatplus/service"
|
"chatplus/service"
|
||||||
"chatplus/service/oss"
|
"chatplus/service/oss"
|
||||||
"chatplus/service/sd"
|
"chatplus/service/sd"
|
||||||
|
"chatplus/store"
|
||||||
"chatplus/store/model"
|
"chatplus/store/model"
|
||||||
"chatplus/store/vo"
|
"chatplus/store/vo"
|
||||||
"chatplus/utils"
|
"chatplus/utils"
|
||||||
"chatplus/utils/resp"
|
"chatplus/utils/resp"
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
@ -28,13 +28,15 @@ type SdJobHandler struct {
|
|||||||
pool *sd.ServicePool
|
pool *sd.ServicePool
|
||||||
uploader *oss.UploaderManager
|
uploader *oss.UploaderManager
|
||||||
snowflake *service.Snowflake
|
snowflake *service.Snowflake
|
||||||
|
leveldb *store.LevelDB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSdJobHandler(app *core.AppServer, db *gorm.DB, pool *sd.ServicePool, manager *oss.UploaderManager, snowflake *service.Snowflake) *SdJobHandler {
|
func NewSdJobHandler(app *core.AppServer, db *gorm.DB, pool *sd.ServicePool, manager *oss.UploaderManager, snowflake *service.Snowflake, levelDB *store.LevelDB) *SdJobHandler {
|
||||||
return &SdJobHandler{
|
return &SdJobHandler{
|
||||||
pool: pool,
|
pool: pool,
|
||||||
uploader: manager,
|
uploader: manager,
|
||||||
snowflake: snowflake,
|
snowflake: snowflake,
|
||||||
|
leveldb: levelDB,
|
||||||
BaseHandler: BaseHandler{
|
BaseHandler: BaseHandler{
|
||||||
App: app,
|
App: app,
|
||||||
DB: db,
|
DB: db,
|
||||||
@ -127,7 +129,7 @@ func (h *SdJobHandler) Image(c *gin.Context) {
|
|||||||
params := types.SdTaskParams{
|
params := types.SdTaskParams{
|
||||||
TaskId: taskId,
|
TaskId: taskId,
|
||||||
Prompt: data.Prompt,
|
Prompt: data.Prompt,
|
||||||
NegativePrompt: data.NegativePrompt,
|
NegPrompt: data.NegPrompt,
|
||||||
Steps: data.Steps,
|
Steps: data.Steps,
|
||||||
Sampler: data.Sampler,
|
Sampler: data.Sampler,
|
||||||
FaceFix: data.FaceFix,
|
FaceFix: data.FaceFix,
|
||||||
@ -257,10 +259,10 @@ func (h *SdJobHandler) getData(finish bool, userId uint, page int, pageSize int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if item.Progress < 100 {
|
if item.Progress < 100 {
|
||||||
// 正在运行中任务使用代理访问图片
|
// 从 leveldb 中获取图片预览数据
|
||||||
image, err := utils.DownloadImage(item.ImgURL, "")
|
imageData, err := h.leveldb.Get(item.TaskId)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
job.ImgURL = "data:image/png;base64," + base64.StdEncoding.EncodeToString(image)
|
job.ImgURL = "data:image/png;base64," + string(imageData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
jobs = append(jobs, job)
|
||||||
|
10
api/main.go
@ -53,6 +53,10 @@ func (l *AppLifecycle) OnStop(context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewAppLifeCycle() *AppLifecycle {
|
||||||
|
return &AppLifecycle{}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
configFile := os.Getenv("CONFIG_FILE")
|
configFile := os.Getenv("CONFIG_FILE")
|
||||||
if configFile == "" {
|
if configFile == "" {
|
||||||
@ -92,6 +96,7 @@ func main() {
|
|||||||
fx.Provide(store.NewGormConfig),
|
fx.Provide(store.NewGormConfig),
|
||||||
fx.Provide(store.NewMysql),
|
fx.Provide(store.NewMysql),
|
||||||
fx.Provide(store.NewRedisClient),
|
fx.Provide(store.NewRedisClient),
|
||||||
|
fx.Provide(store.NewLevelDB),
|
||||||
|
|
||||||
fx.Provide(func() embed.FS {
|
fx.Provide(func() embed.FS {
|
||||||
return xdbFS
|
return xdbFS
|
||||||
@ -292,7 +297,7 @@ func main() {
|
|||||||
group.POST("save", h.Save)
|
group.POST("save", h.Save)
|
||||||
group.GET("list", h.List)
|
group.GET("list", h.List)
|
||||||
group.POST("set", h.Set)
|
group.POST("set", h.Set)
|
||||||
group.POST("remove", h.Remove)
|
group.GET("remove", h.Remove)
|
||||||
}),
|
}),
|
||||||
fx.Invoke(func(s *core.AppServer, h *admin.UserHandler) {
|
fx.Invoke(func(s *core.AppServer, h *admin.UserHandler) {
|
||||||
group := s.Engine.Group("/api/admin/user/")
|
group := s.Engine.Group("/api/admin/user/")
|
||||||
@ -432,11 +437,14 @@ func main() {
|
|||||||
group.GET("list", h.List)
|
group.GET("list", h.List)
|
||||||
}),
|
}),
|
||||||
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
|
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
|
||||||
|
go func() {
|
||||||
err := s.Run(db)
|
err := s.Run(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
}),
|
}),
|
||||||
|
fx.Provide(NewAppLifeCycle),
|
||||||
// 注册生命周期回调函数
|
// 注册生命周期回调函数
|
||||||
fx.Invoke(func(lifecycle fx.Lifecycle, lc *AppLifecycle) {
|
fx.Invoke(func(lifecycle fx.Lifecycle, lc *AppLifecycle) {
|
||||||
lifecycle.Append(fx.Hook{
|
lifecycle.Append(fx.Hook{
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/imroc/req/v3"
|
"github.com/imroc/req/v3"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -16,17 +17,26 @@ import (
|
|||||||
type PlusClient struct {
|
type PlusClient struct {
|
||||||
Config types.MjPlusConfig
|
Config types.MjPlusConfig
|
||||||
apiURL string
|
apiURL string
|
||||||
|
client *req.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlusClient(config types.MjPlusConfig) *PlusClient {
|
func NewPlusClient(config types.MjPlusConfig) *PlusClient {
|
||||||
return &PlusClient{Config: config, apiURL: config.ApiURL}
|
return &PlusClient{
|
||||||
|
Config: config,
|
||||||
|
apiURL: config.ApiURL,
|
||||||
|
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"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PlusClient) Imagine(task types.MjTask) (ImageRes, error) {
|
func (c *PlusClient) Imagine(task types.MjTask) (ImageRes, error) {
|
||||||
apiURL := fmt.Sprintf("%s/mj-%s/mj/submit/imagine", c.apiURL, c.Config.Mode)
|
apiURL := fmt.Sprintf("%s/mj-%s/mj/submit/imagine", c.apiURL, c.Config.Mode)
|
||||||
|
prompt := fmt.Sprintf("%s %s", task.Prompt, task.Params)
|
||||||
|
if task.NegPrompt != "" {
|
||||||
|
prompt += fmt.Sprintf(" --no %s", task.NegPrompt)
|
||||||
|
}
|
||||||
body := ImageReq{
|
body := ImageReq{
|
||||||
BotType: "MID_JOURNEY",
|
BotType: "MID_JOURNEY",
|
||||||
Prompt: task.Prompt,
|
Prompt: prompt,
|
||||||
Base64Array: make([]string, 0),
|
Base64Array: make([]string, 0),
|
||||||
}
|
}
|
||||||
// 生成图片 Base64 编码
|
// 生成图片 Base64 编码
|
||||||
@ -42,7 +52,7 @@ func (c *PlusClient) Imagine(task types.MjTask) (ImageRes, error) {
|
|||||||
logger.Info("API URL: ", apiURL)
|
logger.Info("API URL: ", apiURL)
|
||||||
var res ImageRes
|
var res ImageRes
|
||||||
var errRes ErrRes
|
var errRes ErrRes
|
||||||
r, err := req.C().R().
|
r, err := c.client.R().
|
||||||
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||||
SetBody(body).
|
SetBody(body).
|
||||||
SetSuccessResult(&res).
|
SetSuccessResult(&res).
|
||||||
@ -81,7 +91,7 @@ func (c *PlusClient) Blend(task types.MjTask) (ImageRes, error) {
|
|||||||
}
|
}
|
||||||
var res ImageRes
|
var res ImageRes
|
||||||
var errRes ErrRes
|
var errRes ErrRes
|
||||||
r, err := req.C().R().
|
r, err := c.client.R().
|
||||||
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||||
SetBody(body).
|
SetBody(body).
|
||||||
SetSuccessResult(&res).
|
SetSuccessResult(&res).
|
||||||
@ -130,7 +140,7 @@ func (c *PlusClient) SwapFace(task types.MjTask) (ImageRes, error) {
|
|||||||
}
|
}
|
||||||
var res ImageRes
|
var res ImageRes
|
||||||
var errRes ErrRes
|
var errRes ErrRes
|
||||||
r, err := req.C().R().
|
r, err := c.client.SetTimeout(time.Minute).R().
|
||||||
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||||
SetBody(body).
|
SetBody(body).
|
||||||
SetSuccessResult(&res).
|
SetSuccessResult(&res).
|
||||||
@ -156,7 +166,7 @@ func (c *PlusClient) Upscale(task types.MjTask) (ImageRes, error) {
|
|||||||
apiURL := fmt.Sprintf("%s/mj/submit/action", c.apiURL)
|
apiURL := fmt.Sprintf("%s/mj/submit/action", c.apiURL)
|
||||||
var res ImageRes
|
var res ImageRes
|
||||||
var errRes ErrRes
|
var errRes ErrRes
|
||||||
r, err := req.C().R().
|
r, err := c.client.R().
|
||||||
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||||
SetBody(body).
|
SetBody(body).
|
||||||
SetSuccessResult(&res).
|
SetSuccessResult(&res).
|
||||||
@ -202,7 +212,7 @@ func (c *PlusClient) Variation(task types.MjTask) (ImageRes, error) {
|
|||||||
func (c *PlusClient) QueryTask(taskId string) (QueryRes, error) {
|
func (c *PlusClient) QueryTask(taskId string) (QueryRes, error) {
|
||||||
apiURL := fmt.Sprintf("%s/mj/task/%s/fetch", c.apiURL, taskId)
|
apiURL := fmt.Sprintf("%s/mj/task/%s/fetch", c.apiURL, taskId)
|
||||||
var res QueryRes
|
var res QueryRes
|
||||||
r, err := req.C().R().SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
r, err := c.client.R().SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||||
SetSuccessResult(&res).
|
SetSuccessResult(&res).
|
||||||
Get(apiURL)
|
Get(apiURL)
|
||||||
|
|
||||||
|
@ -22,8 +22,12 @@ func NewProxyClient(config types.MjProxyConfig) *ProxyClient {
|
|||||||
|
|
||||||
func (c *ProxyClient) Imagine(task types.MjTask) (ImageRes, error) {
|
func (c *ProxyClient) Imagine(task types.MjTask) (ImageRes, error) {
|
||||||
apiURL := fmt.Sprintf("%s/mj/submit/imagine", c.apiURL)
|
apiURL := fmt.Sprintf("%s/mj/submit/imagine", c.apiURL)
|
||||||
|
prompt := fmt.Sprintf("%s %s", task.Prompt, task.Params)
|
||||||
|
if task.NegPrompt != "" {
|
||||||
|
prompt += fmt.Sprintf(" --no %s", task.NegPrompt)
|
||||||
|
}
|
||||||
body := ImageReq{
|
body := ImageReq{
|
||||||
Prompt: task.Prompt,
|
Prompt: prompt,
|
||||||
Base64Array: make([]string, 0),
|
Base64Array: make([]string, 0),
|
||||||
}
|
}
|
||||||
// 生成图片 Base64 编码
|
// 生成图片 Base64 编码
|
||||||
@ -46,8 +50,6 @@ func (c *ProxyClient) Imagine(task types.MjTask) (ImageRes, error) {
|
|||||||
SetErrorResult(&errRes).
|
SetErrorResult(&errRes).
|
||||||
Post(apiURL)
|
Post(apiURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
all, err := io.ReadAll(r.Body)
|
|
||||||
logger.Info(string(all))
|
|
||||||
return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
|
return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +67,8 @@ func (s *Service) Run() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是 mj-proxy 则自动翻译提示词
|
// translate prompt
|
||||||
if utils.HasChinese(task.Prompt) && strings.HasPrefix(s.Name, "mj-proxy-service") {
|
if utils.HasChinese(task.Prompt) {
|
||||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt))
|
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
task.Prompt = content
|
task.Prompt = content
|
||||||
@ -76,6 +76,15 @@ func (s *Service) Run() {
|
|||||||
logger.Warnf("error with translate prompt: %v", err)
|
logger.Warnf("error with translate prompt: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// translate negative prompt
|
||||||
|
if task.NegPrompt != "" && utils.HasChinese(task.NegPrompt) {
|
||||||
|
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.NegPrompt))
|
||||||
|
if err == nil {
|
||||||
|
task.NegPrompt = content
|
||||||
|
} else {
|
||||||
|
logger.Warnf("error with translate prompt: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var job model.MidJourneyJob
|
var job model.MidJourneyJob
|
||||||
tx := s.db.Where("id = ?", task.Id).First(&job)
|
tx := s.db.Where("id = ?", task.Id).First(&job)
|
||||||
|
@ -20,7 +20,7 @@ type ServicePool struct {
|
|||||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig) *ServicePool {
|
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig, levelDB *store.LevelDB) *ServicePool {
|
||||||
services := make([]*Service, 0)
|
services := make([]*Service, 0)
|
||||||
taskQueue := store.NewRedisQueue("StableDiffusion_Task_Queue", redisCli)
|
taskQueue := store.NewRedisQueue("StableDiffusion_Task_Queue", redisCli)
|
||||||
notifyQueue := store.NewRedisQueue("StableDiffusion_Queue", redisCli)
|
notifyQueue := store.NewRedisQueue("StableDiffusion_Queue", redisCli)
|
||||||
@ -32,7 +32,7 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
|||||||
|
|
||||||
// create sd service
|
// create sd service
|
||||||
name := fmt.Sprintf("StableDifffusion Service-%s", config.Model)
|
name := fmt.Sprintf("StableDifffusion Service-%s", config.Model)
|
||||||
service := NewService(name, config, taskQueue, notifyQueue, db, manager)
|
service := NewService(name, config, taskQueue, notifyQueue, db, manager, levelDB)
|
||||||
// run sd service
|
// run sd service
|
||||||
go func() {
|
go func() {
|
||||||
service.Run()
|
service.Run()
|
||||||
|
@ -24,9 +24,10 @@ type Service struct {
|
|||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
uploadManager *oss.UploaderManager
|
uploadManager *oss.UploaderManager
|
||||||
name string // service name
|
name string // service name
|
||||||
|
leveldb *store.LevelDB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(name string, config types.StableDiffusionConfig, taskQueue *store.RedisQueue, notifyQueue *store.RedisQueue, db *gorm.DB, manager *oss.UploaderManager) *Service {
|
func NewService(name string, config types.StableDiffusionConfig, taskQueue *store.RedisQueue, notifyQueue *store.RedisQueue, db *gorm.DB, manager *oss.UploaderManager, levelDB *store.LevelDB) *Service {
|
||||||
config.ApiURL = strings.TrimRight(config.ApiURL, "/")
|
config.ApiURL = strings.TrimRight(config.ApiURL, "/")
|
||||||
return &Service{
|
return &Service{
|
||||||
name: name,
|
name: name,
|
||||||
@ -35,6 +36,7 @@ func NewService(name string, config types.StableDiffusionConfig, taskQueue *stor
|
|||||||
taskQueue: taskQueue,
|
taskQueue: taskQueue,
|
||||||
notifyQueue: notifyQueue,
|
notifyQueue: notifyQueue,
|
||||||
db: db,
|
db: db,
|
||||||
|
leveldb: levelDB,
|
||||||
uploadManager: manager,
|
uploadManager: manager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,11 +49,24 @@ func (s *Service) Run() {
|
|||||||
logger.Errorf("taking task with error: %v", err)
|
logger.Errorf("taking task with error: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// 翻译提示词
|
|
||||||
|
// translate prompt
|
||||||
if utils.HasChinese(task.Params.Prompt) {
|
if utils.HasChinese(task.Params.Prompt) {
|
||||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, task.Params.Prompt))
|
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, task.Params.Prompt))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
task.Params.Prompt = content
|
task.Params.Prompt = content
|
||||||
|
} else {
|
||||||
|
logger.Warnf("error with translate prompt: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate negative prompt
|
||||||
|
if task.Params.NegPrompt != "" && utils.HasChinese(task.Params.NegPrompt) {
|
||||||
|
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Params.NegPrompt))
|
||||||
|
if err == nil {
|
||||||
|
task.Params.NegPrompt = content
|
||||||
|
} else {
|
||||||
|
logger.Warnf("error with translate prompt: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +123,7 @@ type TaskProgressResp struct {
|
|||||||
func (s *Service) Txt2Img(task types.SdTask) error {
|
func (s *Service) Txt2Img(task types.SdTask) error {
|
||||||
body := Txt2ImgReq{
|
body := Txt2ImgReq{
|
||||||
Prompt: task.Params.Prompt,
|
Prompt: task.Params.Prompt,
|
||||||
NegativePrompt: task.Params.NegativePrompt,
|
NegativePrompt: task.Params.NegPrompt,
|
||||||
Steps: task.Params.Steps,
|
Steps: task.Params.Steps,
|
||||||
CfgScale: task.Params.CfgScale,
|
CfgScale: task.Params.CfgScale,
|
||||||
Width: task.Params.Width,
|
Width: task.Params.Width,
|
||||||
@ -167,15 +182,20 @@ func (s *Service) Txt2Img(task types.SdTask) error {
|
|||||||
}
|
}
|
||||||
s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumn("progress", 100)
|
s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumn("progress", 100)
|
||||||
s.notifyQueue.RPush(task.UserId)
|
s.notifyQueue.RPush(task.UserId)
|
||||||
|
// 从 leveldb 中删除预览图片数据
|
||||||
|
_ = s.leveldb.Delete(task.Params.TaskId)
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
err, resp := s.checkTaskProgress()
|
err, resp := s.checkTaskProgress()
|
||||||
// 更新任务进度
|
// 更新任务进度
|
||||||
if err == nil && resp.Progress > 0 {
|
if err == nil && resp.Progress > 0 {
|
||||||
logger.Debugf("Check task progress: %+v", resp.Progress)
|
|
||||||
s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumn("progress", int(resp.Progress*100))
|
s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumn("progress", int(resp.Progress*100))
|
||||||
// 发送更新状态信号
|
// 发送更新状态信号
|
||||||
s.notifyQueue.RPush(task.UserId)
|
s.notifyQueue.RPush(task.UserId)
|
||||||
|
// 保存预览图片数据
|
||||||
|
if resp.CurrentImage != "" {
|
||||||
|
_ = s.leveldb.Put(task.Params.TaskId, resp.CurrentImage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
|
110
api/store/leveldb.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LevelDB struct {
|
||||||
|
driver *leveldb.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLevelDB() (*LevelDB, error) {
|
||||||
|
db, err := leveldb.OpenFile("data", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &LevelDB{
|
||||||
|
driver: db,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *LevelDB) Put(key string, value interface{}) error {
|
||||||
|
var byteData []byte
|
||||||
|
if v, ok := value.(string); ok {
|
||||||
|
byteData = []byte(v)
|
||||||
|
} else {
|
||||||
|
b, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
byteData = b
|
||||||
|
}
|
||||||
|
return db.driver.Put([]byte(key), byteData, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *LevelDB) Get(key string) ([]byte, error) {
|
||||||
|
bytes, err := db.driver.Get([]byte(key), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *LevelDB) Search(prefix string) []string {
|
||||||
|
var items = make([]string, 0)
|
||||||
|
iter := db.driver.NewIterator(util.BytesPrefix([]byte(prefix)), nil)
|
||||||
|
defer iter.Release()
|
||||||
|
|
||||||
|
for iter.Next() {
|
||||||
|
items = append(items, string(iter.Value()))
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageVo struct {
|
||||||
|
Items []string
|
||||||
|
Page int
|
||||||
|
PageSize int
|
||||||
|
Total int
|
||||||
|
TotalPage int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *LevelDB) SearchPage(prefix string, page int, pageSize int) *PageVo {
|
||||||
|
var items = make([]string, 0)
|
||||||
|
iter := db.driver.NewIterator(util.BytesPrefix([]byte(prefix)), nil)
|
||||||
|
defer iter.Release()
|
||||||
|
|
||||||
|
res := &PageVo{Page: page, PageSize: pageSize}
|
||||||
|
// 计算数据总数和总页数
|
||||||
|
total := 0
|
||||||
|
for iter.Next() {
|
||||||
|
total++
|
||||||
|
}
|
||||||
|
res.TotalPage = (total + pageSize - 1) / pageSize
|
||||||
|
res.Total = total
|
||||||
|
|
||||||
|
// 计算目标页码的起始和结束位置
|
||||||
|
start := (page - 1) * pageSize
|
||||||
|
if start > total {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
end := start + pageSize
|
||||||
|
if end > total {
|
||||||
|
end = total
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转到目标页码的起始位置
|
||||||
|
count := 0
|
||||||
|
for iter.Next() {
|
||||||
|
if count >= start {
|
||||||
|
items = append(items, string(iter.Value()))
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
iter.Release()
|
||||||
|
res.Items = items
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *LevelDB) Delete(key string) error {
|
||||||
|
return db.driver.Delete([]byte(key), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close release resources
|
||||||
|
func (db *LevelDB) Close() error {
|
||||||
|
return db.driver.Close()
|
||||||
|
}
|
@ -1,5 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
func main() {
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
text := 1
|
||||||
|
bytes := reflect.ValueOf(text).Bytes()
|
||||||
|
fmt.Println(bytes)
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ func OpenAIRequest(db *gorm.DB, prompt string) (string, error) {
|
|||||||
var response apiRes
|
var response apiRes
|
||||||
var errRes apiErrRes
|
var errRes apiErrRes
|
||||||
client := req.C()
|
client := req.C()
|
||||||
if apiKey.ProxyURL != "" {
|
if len(apiKey.ProxyURL) > 5 {
|
||||||
client.SetProxyURL(apiKey.ApiURL)
|
client.SetProxyURL(apiKey.ApiURL)
|
||||||
}
|
}
|
||||||
r, err := client.R().SetHeader("Content-Type", "application/json").
|
r, err := client.R().SetHeader("Content-Type", "application/json").
|
||||||
|
@ -15,8 +15,8 @@ server {
|
|||||||
# ssl_prefer_server_ciphers on;
|
# ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
# 日志地址
|
# 日志地址
|
||||||
access_log /var/log/access.log;
|
access_log /var/log/nginx/access.log;
|
||||||
error_log /var/log/error.log;
|
error_log /var/log/nginx/error.log;
|
||||||
|
|
||||||
index index.html;
|
index index.html;
|
||||||
root /var/www/app/dist; # 这里改成前端静态页面的地址
|
root /var/www/app/dist; # 这里改成前端静态页面的地址
|
||||||
|
@ -72,6 +72,7 @@ services:
|
|||||||
- ./conf/config.toml:/var/www/app/config.toml
|
- ./conf/config.toml:/var/www/app/config.toml
|
||||||
- ./logs/app:/var/www/app/logs
|
- ./logs/app:/var/www/app/logs
|
||||||
- ./static:/var/www/app/static
|
- ./static:/var/www/app/static
|
||||||
|
- ./data/leveldb:/var/www/app/data
|
||||||
|
|
||||||
# 前端应用
|
# 前端应用
|
||||||
chatgpt-plus-web:
|
chatgpt-plus-web:
|
||||||
|
@ -6,3 +6,4 @@ VUE_APP_ADMIN_USER=admin
|
|||||||
VUE_APP_ADMIN_PASS=admin123
|
VUE_APP_ADMIN_PASS=admin123
|
||||||
VUE_APP_KEY_PREFIX=ChatPLUS_DEV_
|
VUE_APP_KEY_PREFIX=ChatPLUS_DEV_
|
||||||
VUE_APP_TITLE="Geek-AI 创作系统"
|
VUE_APP_TITLE="Geek-AI 创作系统"
|
||||||
|
VUE_APP_VERSION=v4.0.2
|
||||||
|
@ -2,3 +2,4 @@ VUE_APP_API_HOST=
|
|||||||
VUE_APP_WS_HOST=
|
VUE_APP_WS_HOST=
|
||||||
VUE_APP_KEY_PREFIX=ChatPLUS_
|
VUE_APP_KEY_PREFIX=ChatPLUS_
|
||||||
VUE_APP_TITLE="Geek-AI 创作系统"
|
VUE_APP_TITLE="Geek-AI 创作系统"
|
||||||
|
VUE_APP_VERSION=v4.0.2
|
||||||
|
Before Width: | Height: | Size: 741 B After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 2.1 KiB |
@ -182,6 +182,11 @@
|
|||||||
.page-mj .inner .el-form .el-slider {
|
.page-mj .inner .el-form .el-slider {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
}
|
}
|
||||||
|
.page-mj .inner .el-form .uploader-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
position: relative;
|
||||||
|
top: 3px;
|
||||||
|
}
|
||||||
.page-mj .inner .task-list-box {
|
.page-mj .inner .task-list-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@ -275,6 +280,13 @@
|
|||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
.page-mj .inner .task-list-box .task-list-inner .el-row.text-info {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
.page-mj .inner .task-list-box .task-list-inner .el-row.text-info .el-tag {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
.page-mj .inner .task-list-box .task-list-inner .submit-btn {
|
.page-mj .inner .task-list-box .task-list-inner .submit-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
@ -282,12 +294,6 @@
|
|||||||
.page-mj .inner .task-list-box .task-list-inner .submit-btn .el-button {
|
.page-mj .inner .task-list-box .task-list-inner .submit-btn .el-button {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
.page-mj .inner .task-list-box .task-list-inner .submit-btn .text-info {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: right;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.page-mj .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item {
|
.page-mj .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
@ -225,6 +225,12 @@
|
|||||||
.el-input, .el-slider {
|
.el-input, .el-slider {
|
||||||
width 180px
|
width 180px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.uploader-icon {
|
||||||
|
font-size 24px
|
||||||
|
position relative
|
||||||
|
top 3px
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@import "task-list.styl"
|
@import "task-list.styl"
|
||||||
|
@ -53,6 +53,12 @@
|
|||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
.page-sd .inner .sd-box .sd-params .text-info {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.page-sd .inner .sd-box .sd-params .text-info .el-tag {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
.page-sd .inner .sd-box .submit-btn {
|
.page-sd .inner .sd-box .submit-btn {
|
||||||
padding: 10px 15px 0 15px;
|
padding: 10px 15px 0 15px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -159,6 +165,13 @@
|
|||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
.page-sd .inner .task-list-box .task-list-inner .el-row.text-info {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
.page-sd .inner .task-list-box .task-list-inner .el-row.text-info .el-tag {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
.page-sd .inner .task-list-box .task-list-inner .submit-btn {
|
.page-sd .inner .task-list-box .task-list-inner .submit-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
@ -166,12 +179,6 @@
|
|||||||
.page-sd .inner .task-list-box .task-list-inner .submit-btn .el-button {
|
.page-sd .inner .task-list-box .task-list-inner .submit-btn .el-button {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
.page-sd .inner .task-list-box .task-list-inner .submit-btn .text-info {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: right;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.page-sd .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item {
|
.page-sd .inner .task-list-box .task-list-inner .job-list-box .running-job-list .job-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
@ -65,6 +65,14 @@
|
|||||||
padding-top 5px
|
padding-top 5px
|
||||||
padding-bottom 5px
|
padding-bottom 5px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-info {
|
||||||
|
padding 10px
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
margin-right 10px
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
.mobile-mj .content .text-line {
|
.mobile-mj .text-line {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-row .van-col .rate {
|
.mobile-mj .text-line .van-row .van-col .rate {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
@ -11,17 +11,17 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-row .van-col .rate .icon {
|
.mobile-mj .text-line .van-row .van-col .rate .icon {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-row .van-col .rate .icon .van-image {
|
.mobile-mj .text-line .van-row .van-col .rate .icon .van-image {
|
||||||
max-width: 20px;
|
max-width: 20px;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-row .van-col .rate .text {
|
.mobile-mj .text-line .van-row .van-col .rate .text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #555;
|
color: #555;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-row .van-col .model {
|
.mobile-mj .text-line .van-row .van-col .model {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
@ -30,40 +30,45 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-row .van-col .model .icon {
|
.mobile-mj .text-line .van-row .van-col .model .icon {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-row .van-col .model .icon .van-image {
|
.mobile-mj .text-line .van-row .van-col .model .icon .van-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-row .van-col .model .text {
|
.mobile-mj .text-line .van-row .van-col .model .text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #555;
|
color: #555;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-row .van-col .active {
|
.mobile-mj .text-line .van-row .van-col .active {
|
||||||
background-color: #e5e5e5;
|
background-color: #e5e5e5;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-button {
|
.mobile-mj .text-line .van-button {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .van-button .van-tag {
|
.mobile-mj .text-line .van-button .van-tag {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .text-line .align-right {
|
.mobile-mj .text-line .align-right {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content {
|
.mobile-mj .tip-text {
|
||||||
|
padding: 10px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #c1c1c1;
|
||||||
|
}
|
||||||
|
.mobile-mj .running-job-list .van-grid .van-grid-item .van-grid-item__content {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .van-image,
|
.mobile-mj .running-job-list .van-grid .van-grid-item .van-grid-item__content .van-image,
|
||||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue {
|
.mobile-mj .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue {
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .progress {
|
.mobile-mj .running-job-list .van-grid .van-grid-item .van-grid-item__content .progress {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -74,36 +79,36 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .progress .van-circle__text {
|
.mobile-mj .running-job-list .van-grid .van-grid-item .van-grid-item__content .progress .van-circle__text {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue {
|
.mobile-mj .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #c1c1c1;
|
color: #c1c1c1;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .icon {
|
.mobile-mj .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .icon {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .icon .iconfont {
|
.mobile-mj .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .icon .iconfont {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .text {
|
.mobile-mj .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .text {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content {
|
.mobile-mj .finish-job-list .van-grid .van-grid-item .van-grid-item__content {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item {
|
.mobile-mj .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .opt .opt-btn {
|
.mobile-mj .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .opt .opt-btn {
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -115,20 +120,20 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .van-image {
|
.mobile-mj .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .van-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .upscale {
|
.mobile-mj .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .upscale {
|
||||||
height: 260px;
|
height: 260px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .remove {
|
.mobile-mj .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .remove {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
}
|
}
|
||||||
.mobile-mj .content .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .remove .el-button {
|
.mobile-mj .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .remove .el-button {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
.mobile-mj {
|
.mobile-mj {
|
||||||
.content {
|
|
||||||
.text-line {
|
.text-line {
|
||||||
padding 6px
|
padding 6px
|
||||||
font-size 14px
|
font-size 14px
|
||||||
@ -74,6 +73,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tip-text {
|
||||||
|
padding 10px
|
||||||
|
line-height 1.5
|
||||||
|
color #c1c1c1
|
||||||
|
}
|
||||||
|
|
||||||
.running-job-list {
|
.running-job-list {
|
||||||
.van-grid {
|
.van-grid {
|
||||||
.van-grid-item {
|
.van-grid-item {
|
||||||
@ -186,4 +192,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
133
web/src/assets/css/mobile/image-sd.css
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
.mobile-sd .text-line {
|
||||||
|
padding: 0 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .rate {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .rate .icon {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .rate .icon .van-image {
|
||||||
|
max-width: 20px;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .rate .text {
|
||||||
|
text-align: center;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .el-input__inner {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .model {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 6px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .model .icon {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .model .icon .van-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .model .text {
|
||||||
|
text-align: center;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-row .van-col .active {
|
||||||
|
background-color: #e5e5e5;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-button {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .van-button .van-tag {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
.mobile-sd .text-line .align-right {
|
||||||
|
display: flex;
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
.mobile-sd .pt-6 {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.mobile-sd .tip-text {
|
||||||
|
padding: 10px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #c1c1c1;
|
||||||
|
}
|
||||||
|
.mobile-sd .running-job-list .van-grid .van-grid-item .van-grid-item__content {
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.mobile-sd .running-job-list .van-grid .van-grid-item .van-grid-item__content .van-image,
|
||||||
|
.mobile-sd .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue {
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
.mobile-sd .running-job-list .van-grid .van-grid-item .van-grid-item__content .progress {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(50,50,50,0.5);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.mobile-sd .running-job-list .van-grid .van-grid-item .van-grid-item__content .progress .van-circle__text {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.mobile-sd .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
color: #c1c1c1;
|
||||||
|
}
|
||||||
|
.mobile-sd .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .icon {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.mobile-sd .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .icon .iconfont {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.mobile-sd .running-job-list .van-grid .van-grid-item .van-grid-item__content .task-in-queue .text {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.mobile-sd .finish-job-list .van-grid .van-grid-item .van-grid-item__content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.mobile-sd .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item {
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 6px;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.mobile-sd .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .van-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
.mobile-sd .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .remove {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
.mobile-sd .finish-job-list .van-grid .van-grid-item .van-grid-item__content .job-item .remove .el-button {
|
||||||
|
margin-left: 5px;
|
||||||
|
height: auto;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
184
web/src/assets/css/mobile/image-sd.styl
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
.mobile-sd {
|
||||||
|
.text-line {
|
||||||
|
padding 0 6px
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
|
.van-row {
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
.van-col {
|
||||||
|
.rate {
|
||||||
|
display: flex;
|
||||||
|
justify-content center
|
||||||
|
background-color #f5f5f5
|
||||||
|
padding 5px 10px
|
||||||
|
margin 5px 0
|
||||||
|
border-radius 5px
|
||||||
|
flex-flow column
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
text-align center
|
||||||
|
|
||||||
|
.van-image {
|
||||||
|
max-width 20px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
text-align center
|
||||||
|
color #555555
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
text-align center
|
||||||
|
}
|
||||||
|
|
||||||
|
.model {
|
||||||
|
display: flex;
|
||||||
|
justify-content center
|
||||||
|
background-color #f5f5f5
|
||||||
|
padding 6px
|
||||||
|
margin 5px 0
|
||||||
|
border-radius 5px
|
||||||
|
flex-flow column
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
text-align center
|
||||||
|
|
||||||
|
.van-image {
|
||||||
|
width 100%
|
||||||
|
height 50px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
text-align center
|
||||||
|
color #555555
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background-color #e5e5e5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.van-button {
|
||||||
|
position relative
|
||||||
|
|
||||||
|
.van-tag {
|
||||||
|
position absolute
|
||||||
|
right 20px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
display flex
|
||||||
|
justify-content right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.pt-6 {
|
||||||
|
padding 10px
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-text {
|
||||||
|
padding 10px
|
||||||
|
line-height 1.5
|
||||||
|
color #c1c1c1
|
||||||
|
}
|
||||||
|
|
||||||
|
.running-job-list {
|
||||||
|
.van-grid {
|
||||||
|
.van-grid-item {
|
||||||
|
.van-grid-item__content {
|
||||||
|
padding 0
|
||||||
|
position relative
|
||||||
|
|
||||||
|
.van-image, .task-in-queue {
|
||||||
|
min-height 100px
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
display flex
|
||||||
|
justify-content center
|
||||||
|
align-items center
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
background rgba(50, 50, 50, 0.5)
|
||||||
|
position absolute
|
||||||
|
left 0
|
||||||
|
top 0
|
||||||
|
|
||||||
|
.van-circle__text {
|
||||||
|
color #ffffff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// end progress
|
||||||
|
|
||||||
|
.task-in-queue {
|
||||||
|
display flex
|
||||||
|
flex-flow column
|
||||||
|
justify-content center
|
||||||
|
color #c1c1c1
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
text-align center
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
font-size 24px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size 14px
|
||||||
|
margin-top 5px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//end running jobs
|
||||||
|
|
||||||
|
.finish-job-list {
|
||||||
|
.van-grid {
|
||||||
|
.van-grid-item {
|
||||||
|
.van-grid-item__content {
|
||||||
|
padding 0
|
||||||
|
|
||||||
|
.job-item {
|
||||||
|
overflow hidden
|
||||||
|
border-radius 6px
|
||||||
|
position relative
|
||||||
|
height 100%
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
.van-image {
|
||||||
|
width 100%
|
||||||
|
height 200px
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove {
|
||||||
|
position absolute
|
||||||
|
right 5px
|
||||||
|
top 5px
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
margin-left 5px
|
||||||
|
height auto
|
||||||
|
padding 5px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -116,6 +116,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-row.text-info {
|
||||||
|
width 100%
|
||||||
|
padding 10px 0
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
margin-right 10px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 提交按钮
|
// 提交按钮
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
@ -125,13 +134,6 @@
|
|||||||
.el-button {
|
.el-button {
|
||||||
width 200px
|
width 200px
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-info {
|
|
||||||
width 100%
|
|
||||||
display flex
|
|
||||||
justify-content right
|
|
||||||
align-items center
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,6 +120,10 @@ const removeFile = (file) => {
|
|||||||
|
|
||||||
const insertURL = (url) => {
|
const insertURL = (url) => {
|
||||||
show.value = false
|
show.value = false
|
||||||
|
// 如果是相对路径,处理成绝对路径
|
||||||
|
if (url.indexOf("http") === -1) {
|
||||||
|
url = location.protocol + "//" + location.host + url
|
||||||
|
}
|
||||||
emits('selected', url)
|
emits('selected', url)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
Powered by {{ author }} @
|
Powered by {{ author }} @
|
||||||
<el-link type="primary" href="https://github.com/yangjian102621/chatgpt-plus" target="_blank">{{ title }}
|
<el-link type="primary" href="https://github.com/yangjian102621/chatgpt-plus" target="_blank">
|
||||||
|
{{ title }} -
|
||||||
|
{{ version }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -12,6 +14,7 @@
|
|||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
|
|
||||||
const title = ref(process.env.VUE_APP_TITLE)
|
const title = ref(process.env.VUE_APP_TITLE)
|
||||||
|
const version = ref(process.env.VUE_APP_VERSION)
|
||||||
const author = ref('极客学长')
|
const author = ref('极客学长')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="title">{{ title }}</h1>
|
<h1 class="title">{{ title }}-{{ version }}</h1>
|
||||||
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@ -60,6 +60,7 @@ import {httpGet} from "@/utils/http";
|
|||||||
import {ElMessage} from "element-plus";
|
import {ElMessage} from "element-plus";
|
||||||
|
|
||||||
const title = ref(process.env.VUE_APP_TITLE)
|
const title = ref(process.env.VUE_APP_TITLE)
|
||||||
|
const version = ref(process.env.VUE_APP_VERSION)
|
||||||
|
|
||||||
const samples = ref([
|
const samples = ref([
|
||||||
"用小学生都能听懂的术语解释什么是量子纠缠",
|
"用小学生都能听懂的术语解释什么是量子纠缠",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<el-image :src="logo"/>
|
<el-image :src="logo"/>
|
||||||
<span class="text" v-show="!sidebar.collapse">{{ title }}</span>
|
<span class="text" v-show="!sidebar.collapse">{{ title }} - {{ version }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-menu
|
<el-menu
|
||||||
@ -60,6 +60,7 @@ import {ElMessage} from "element-plus";
|
|||||||
|
|
||||||
const title = ref('Chat-Plus-Admin')
|
const title = ref('Chat-Plus-Admin')
|
||||||
const logo = ref('/images/logo.png')
|
const logo = ref('/images/logo.png')
|
||||||
|
const version = ref(process.env.VUE_APP_VERSION)
|
||||||
|
|
||||||
// 加载系统配置
|
// 加载系统配置
|
||||||
httpGet('/api/admin/config/get?key=system').then(res => {
|
httpGet('/api/admin/config/get?key=system').then(res => {
|
||||||
|
@ -181,7 +181,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
name: 'mobile',
|
name: 'mobile',
|
||||||
path: '/mobile',
|
path: '/mobile',
|
||||||
meta: {title: 'ChatPlus-智能助手V3'},
|
meta: {title: 'Geek-AI v4.0'},
|
||||||
component: () => import('@/views/mobile/Home.vue'),
|
component: () => import('@/views/mobile/Home.vue'),
|
||||||
redirect: '/mobile/chat',
|
redirect: '/mobile/chat',
|
||||||
children: [
|
children: [
|
||||||
@ -191,9 +191,9 @@ const routes = [
|
|||||||
component: () => import('@/views/mobile/ChatList.vue'),
|
component: () => import('@/views/mobile/ChatList.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/mobile/mj',
|
path: '/mobile/image',
|
||||||
name: 'mobile-mj',
|
name: 'mobile-image',
|
||||||
component: () => import('@/views/mobile/ImageMj.vue'),
|
component: () => import('@/views/mobile/Image.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/mobile/profile',
|
path: '/mobile/profile',
|
||||||
|
@ -103,7 +103,13 @@
|
|||||||
:key="item.id"
|
:key="item.id"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
/>
|
>
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
<el-tag style="margin-left: 5px; position: relative; top:-2px" type="info" size="small">{{
|
||||||
|
item.power
|
||||||
|
}}算力
|
||||||
|
</el-tag>
|
||||||
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-button type="primary" @click="newChat">
|
<el-button type="primary" @click="newChat">
|
||||||
<el-icon>
|
<el-icon>
|
||||||
@ -445,6 +451,12 @@ const newChat = () => {
|
|||||||
showLoginDialog.value = true
|
showLoginDialog.value = true
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const role = getRoleById(roleId.value)
|
||||||
|
if (role.key === 'gpt') {
|
||||||
|
showHello.value = true
|
||||||
|
} else {
|
||||||
|
showHello.value = false
|
||||||
|
}
|
||||||
// 已有新开的会话
|
// 已有新开的会话
|
||||||
if (newChatItem.value !== null && newChatItem.value['role_id'] === roles.value[0]['role_id']) {
|
if (newChatItem.value !== null && newChatItem.value['role_id'] === roles.value[0]['role_id']) {
|
||||||
return;
|
return;
|
||||||
@ -479,9 +491,15 @@ const changeChat = (chat) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loadChat = function (chat) {
|
const loadChat = function (chat) {
|
||||||
|
if (!isLogin.value) {
|
||||||
|
showLoginDialog.value = true
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (activeChat.value['chat_id'] === chat.chat_id) {
|
if (activeChat.value['chat_id'] === chat.chat_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
activeChat.value = chat
|
activeChat.value = chat
|
||||||
newChatItem.value = null;
|
newChatItem.value = null;
|
||||||
roleId.value = chat.role_id;
|
roleId.value = chat.role_id;
|
||||||
|
@ -6,12 +6,9 @@
|
|||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="nav-items">
|
<ul class="nav-items">
|
||||||
<li v-for="item in navs" :key="item.path">
|
<li v-for="item in navs" :key="item.url">
|
||||||
<!-- <el-tooltip effect="light" :content="item.title" placement="right">-->
|
<a @click="changeNav(item)" :class="item.url === curPath ? 'active' : ''">
|
||||||
<!-- -->
|
<el-image :src="item.icon" style="width: 30px;height: 30px"/>
|
||||||
<!-- </el-tooltip>-->
|
|
||||||
<a @click="changeNav(item)" :class="item.path === curPath ? 'active' : ''">
|
|
||||||
<el-image :src="item.icon" :width="20"/>
|
|
||||||
</a>
|
</a>
|
||||||
<div :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</div>
|
<div :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</div>
|
||||||
</li>
|
</li>
|
||||||
@ -69,7 +66,7 @@ onMounted(() => {
|
|||||||
.navigator {
|
.navigator {
|
||||||
display flex
|
display flex
|
||||||
flex-flow column
|
flex-flow column
|
||||||
width 70px
|
width 60px
|
||||||
padding 10px 6px
|
padding 10px 6px
|
||||||
border-right: 1px solid #3c3c3c
|
border-right: 1px solid #3c3c3c
|
||||||
background-color: #25272D
|
background-color: #25272D
|
||||||
@ -79,6 +76,10 @@ onMounted(() => {
|
|||||||
flex-flow column
|
flex-flow column
|
||||||
align-items center
|
align-items center
|
||||||
|
|
||||||
|
.el-image {
|
||||||
|
width 50px
|
||||||
|
height 50px
|
||||||
|
}
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
border-bottom 1px solid #4A4A4A
|
border-bottom 1px solid #4A4A4A
|
||||||
@ -89,15 +90,13 @@ onMounted(() => {
|
|||||||
|
|
||||||
.nav-items {
|
.nav-items {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
padding-left: 10px;
|
padding 0 5px
|
||||||
padding-right: 10px;
|
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin-bottom 15px
|
margin-bottom 15px
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color #DADBDC
|
color #DADBDC
|
||||||
background-color #40444A
|
|
||||||
border-radius 10px
|
border-radius 10px
|
||||||
width 48px
|
width 48px
|
||||||
height 48px
|
height 48px
|
||||||
@ -117,6 +116,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
a:hover, a.active {
|
a:hover, a.active {
|
||||||
color #47fff1
|
color #47fff1
|
||||||
|
background-color #0F7A71
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -168,7 +168,48 @@
|
|||||||
<div class="extra-params">
|
<div class="extra-params">
|
||||||
<el-form>
|
<el-form>
|
||||||
<el-tabs v-model="activeName" class="title-tabs" @tabChange="tabChange">
|
<el-tabs v-model="activeName" class="title-tabs" @tabChange="tabChange">
|
||||||
<el-tab-pane label="文生图(可选)" name="image">
|
<el-tab-pane label="文生图" name="txt2img">
|
||||||
|
<div class="prompt-box">
|
||||||
|
<div class="param-line pt">
|
||||||
|
<div class="flex-row justify-between items-center">
|
||||||
|
<div class="flex-row justify-start items-center">
|
||||||
|
<span>提示词:</span>
|
||||||
|
<el-tooltip effect="light" content="输入你想要的内容,用逗号分割" placement="right">
|
||||||
|
<el-icon>
|
||||||
|
<InfoFilled/>
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="param-line pt">
|
||||||
|
<el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
||||||
|
ref="promptRef"
|
||||||
|
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="param-line pt">
|
||||||
|
<div class="flex-row justify-between items-center">
|
||||||
|
<div class="flex-row justify-start items-center">
|
||||||
|
<span>不希望出现的内容:(可选)</span>
|
||||||
|
<el-tooltip effect="light" content="不想出现在图片上的元素(例如:树,建筑)" placement="right">
|
||||||
|
<el-icon>
|
||||||
|
<InfoFilled/>
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="param-line pt">
|
||||||
|
<el-input v-model="params.neg_prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
||||||
|
ref="promptRef"
|
||||||
|
placeholder="请在此输入你不希望出现在图片上的内容,系统会自动翻译中文提示词"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="图生图" name="img2img">
|
||||||
<div class="text">图生图:以某张图片为底稿参考来创作绘画,生成类似风格或类型图像,支持 PNG 和 JPG 格式图片;
|
<div class="text">图生图:以某张图片为底稿参考来创作绘画,生成类似风格或类型图像,支持 PNG 和 JPG 格式图片;
|
||||||
</div>
|
</div>
|
||||||
<div class="param-line">
|
<div class="param-line">
|
||||||
@ -190,15 +231,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="param-line" style="padding-top: 10px">
|
<div class="param-line" style="padding-top: 10px">
|
||||||
<el-form-item label="图像权重:">
|
<el-form-item label="参考权重:">
|
||||||
<template #default>
|
<template #default>
|
||||||
<div class="form-item-inner">
|
<div class="form-item-inner">
|
||||||
<el-slider v-model.number="params.weight" :max="1" :step="0.01"
|
<el-slider v-model.number="params.iw" :max="1" :step="0.01"
|
||||||
style="width: 180px;--el-slider-main-bg-color:#47fff1"/>
|
style="width: 180px;--el-slider-main-bg-color:#47fff1"/>
|
||||||
<el-tooltip effect="light"
|
<el-tooltip effect="light"
|
||||||
content="使用图像权重参数--iw来调整图像 URL 与文本的重要性 <br/>权重较高时意味着图像提示将对完成的作业产生更大的影响"
|
content="使用图像权重参数--iw来调整图像 URL 与文本的重要性 <br/>权重较高时意味着图像提示将对完成的作业产生更大的影响"
|
||||||
raw-content placement="right">
|
raw-content placement="right">
|
||||||
<el-icon style="margin-top: 9px">
|
<el-icon>
|
||||||
<InfoFilled/>
|
<InfoFilled/>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
@ -248,8 +289,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="融图(可选)" name="blend">
|
<el-tab-pane label="融图" name="blend">
|
||||||
<div class="text">请上传两张以上的图片,最多不超过五张,超过五张图片请使用文生图功能</div>
|
<div class="text">请上传两张以上的图片,最多不超过五张,超过五张图片请使用图生图功能</div>
|
||||||
<div class="img-inline">
|
<div class="img-inline">
|
||||||
<div class="img-list-box">
|
<div class="img-list-box">
|
||||||
<div class="img-item" v-for="imgURL in imgList">
|
<div class="img-item" v-for="imgURL in imgList">
|
||||||
@ -267,8 +308,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="换脸(可选)" name="swapFace">
|
<el-tab-pane label="换脸" name="swapFace">
|
||||||
<div class="text">请上传两张有脸部的图片,用右边图片的脸替换左边图片的脸</div>
|
<div class="text">请上传两张有脸部的图片,用左边图片的脸替换右边图片的脸</div>
|
||||||
<div class="img-inline">
|
<div class="img-inline">
|
||||||
<div class="img-list-box">
|
<div class="img-list-box">
|
||||||
<div class="img-item" v-for="imgURL in imgList">
|
<div class="img-item" v-for="imgURL in imgList">
|
||||||
@ -285,13 +326,124 @@
|
|||||||
</el-upload>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane name="cref">
|
||||||
|
<template #label>
|
||||||
|
<el-badge value="New">
|
||||||
|
<span>一致性</span>
|
||||||
|
</el-badge>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="text">注意:只有于 niji6 和 v6 模型支持一致性功能,如果选择其他模型此功能将会生成失败。</div>
|
||||||
|
<div class="param-line">
|
||||||
|
<el-form-item label="角色一致性:" prop="cref">
|
||||||
|
<el-input v-model="params.cref" placeholder="请输入图片URL或者上传图片"
|
||||||
|
style="--el-input-focus-border-color:#47fff1;--el-input-text-color:#ffffff; max-width: 500px; width: 100%"
|
||||||
|
size="small">
|
||||||
|
<template #append>
|
||||||
|
<el-upload
|
||||||
|
:auto-upload="true"
|
||||||
|
:show-file-list="false"
|
||||||
|
@click="beforeUpload('cref')"
|
||||||
|
:http-request="uploadImg"
|
||||||
|
>
|
||||||
|
<el-icon class="uploader-icon">
|
||||||
|
<UploadFilled/>
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="param-line">
|
||||||
|
<el-form-item label="风格一致性:" prop="sref">
|
||||||
|
<el-input v-model="params.sref" placeholder="请输入图片URL或者上传图片"
|
||||||
|
style="--el-input-focus-border-color:#47fff1; --el-input-text-color:#ffffff; max-width: 500px; width: 100%"
|
||||||
|
size="small">
|
||||||
|
<template #append>
|
||||||
|
<el-upload
|
||||||
|
:auto-upload="true"
|
||||||
|
:show-file-list="false"
|
||||||
|
@click="beforeUpload('sref')"
|
||||||
|
:http-request="uploadImg"
|
||||||
|
>
|
||||||
|
<el-icon class="uploader-icon">
|
||||||
|
<UploadFilled/>
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="param-line" style="padding-top: 10px">
|
||||||
|
<el-form-item label="参考权重:">
|
||||||
|
<template #default>
|
||||||
|
<div class="form-item-inner">
|
||||||
|
<el-slider v-model.number="params.cw" :max="100" :step="1"
|
||||||
|
style="width: 180px;--el-slider-main-bg-color:#47fff1"/>
|
||||||
|
<el-tooltip effect="light"
|
||||||
|
content="取值范围 0-100 <br/>默认值100参考原图的脸部、头发和衣服<br/>0则表示只换脸"
|
||||||
|
raw-content placement="right">
|
||||||
|
<el-icon>
|
||||||
|
<InfoFilled/>
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="prompt-box">
|
||||||
|
<div class="param-line pt">
|
||||||
|
<div class="flex-row justify-between items-center">
|
||||||
|
<div class="flex-row justify-start items-center">
|
||||||
|
<span>提示词:</span>
|
||||||
|
<el-tooltip effect="light" content="输入你想要的内容,用逗号分割" placement="right">
|
||||||
|
<el-icon>
|
||||||
|
<InfoFilled/>
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="param-line pt">
|
||||||
|
<el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
||||||
|
ref="promptRef"
|
||||||
|
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="param-line pt">
|
||||||
|
<div class="flex-row justify-between items-center">
|
||||||
|
<div class="flex-row justify-start items-center">
|
||||||
|
<span>不希望出现的内容:(可选)</span>
|
||||||
|
<el-tooltip effect="light" content="不想出现在图片上的元素(例如:树,建筑)" placement="right">
|
||||||
|
<el-icon>
|
||||||
|
<InfoFilled/>
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="param-line pt">
|
||||||
|
<el-input v-model="params.neg_prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
||||||
|
ref="promptRef"
|
||||||
|
placeholder="请在此输入你不希望出现在图片上的内容,系统会自动翻译中文提示词"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|
||||||
|
<el-row class="text-info">
|
||||||
|
<el-tag>每次绘图消耗{{ mjPower }}算力,U/V 操作消耗{{ mjActionPower }}算力</el-tag>
|
||||||
|
<el-tag type="success">当前可用算力:{{ power }}</el-tag>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<div class="submit-btn">
|
<div class="submit-btn">
|
||||||
<el-button color="#47fff1" :dark="false" @click="generate" round>立即生成</el-button>
|
<el-button color="#47fff1" :dark="false" @click="generate" round>立即生成</el-button>
|
||||||
<div class="text-info">
|
|
||||||
<el-tag type="success">当前可用算力:{{ power }}</el-tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -341,7 +493,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>创作记录</h2>
|
<h2>创作记录</h2>
|
||||||
<div class="finish-job-list" v-loading="loading" element-loading-background="rgba(255, 255, 255, 0.5)">
|
<div class="finish-job-list" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.5)">
|
||||||
<div v-if="finishedJobs.length > 0">
|
<div v-if="finishedJobs.length > 0">
|
||||||
<ItemList :items="finishedJobs" :width="240" :gap="16">
|
<ItemList :items="finishedJobs" :width="240" :gap="16">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@ -373,10 +525,42 @@
|
|||||||
<div class="opt" v-if="scope.item['can_opt']">
|
<div class="opt" v-if="scope.item['can_opt']">
|
||||||
<div class="opt-line">
|
<div class="opt-line">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a @click="upscale(1, scope.item)">U1</a></li>
|
<li>
|
||||||
<li><a @click="upscale(2, scope.item)">U2</a></li>
|
<el-tooltip
|
||||||
<li><a @click="upscale(3, scope.item)">U3</a></li>
|
class="box-item"
|
||||||
<li><a @click="upscale(4, scope.item)">U4</a></li>
|
effect="light"
|
||||||
|
content="放大第一张"
|
||||||
|
placement="top">
|
||||||
|
<a @click="upscale(1, scope.item)">U1</a>
|
||||||
|
</el-tooltip>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="放大第二张"
|
||||||
|
placement="top">
|
||||||
|
<a @click="upscale(2, scope.item)">U2</a>
|
||||||
|
</el-tooltip>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="放大第三张"
|
||||||
|
placement="top">
|
||||||
|
<a @click="upscale(3, scope.item)">U3</a>
|
||||||
|
</el-tooltip>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="放大第四张"
|
||||||
|
placement="top">
|
||||||
|
<a @click="upscale(4, scope.item)">U4</a>
|
||||||
|
</el-tooltip>
|
||||||
|
</li>
|
||||||
<li class="show-prompt">
|
<li class="show-prompt">
|
||||||
|
|
||||||
<el-popover placement="left" title="提示词" :width="240" trigger="hover">
|
<el-popover placement="left" title="提示词" :width="240" trigger="hover">
|
||||||
@ -402,10 +586,42 @@
|
|||||||
|
|
||||||
<div class="opt-line">
|
<div class="opt-line">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a @click="variation(1, scope.item)">V1</a></li>
|
<li>
|
||||||
<li><a @click="variation(2, scope.item)">V2</a></li>
|
<el-tooltip
|
||||||
<li><a @click="variation(3, scope.item)">V3</a></li>
|
class="box-item"
|
||||||
<li><a @click="variation(4, scope.item)">V4</a></li>
|
effect="light"
|
||||||
|
content="变化第一张"
|
||||||
|
placement="top">
|
||||||
|
<a @click="variation(1, scope.item)">V1</a>
|
||||||
|
</el-tooltip>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="变化第二张"
|
||||||
|
placement="top">
|
||||||
|
<a @click="variation(2, scope.item)">V2</a>
|
||||||
|
</el-tooltip>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="变化第三张"
|
||||||
|
placement="top">
|
||||||
|
<a @click="variation(3, scope.item)">V3</a>
|
||||||
|
</el-tooltip>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<el-tooltip
|
||||||
|
class="box-item"
|
||||||
|
effect="light"
|
||||||
|
content="变化第四张"
|
||||||
|
placement="top">
|
||||||
|
<a @click="variation(4, scope.item)">V4</a>
|
||||||
|
</el-tooltip>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -442,7 +658,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {nextTick, onMounted, onUnmounted, ref} from "vue"
|
import {nextTick, onMounted, onUnmounted, ref} from "vue"
|
||||||
import {ChromeFilled, Delete, DocumentCopy, InfoFilled, Picture, Plus} from "@element-plus/icons-vue";
|
import {ChromeFilled, Delete, DocumentCopy, InfoFilled, Picture, Plus, UploadFilled} from "@element-plus/icons-vue";
|
||||||
import Compressor from "compressorjs";
|
import Compressor from "compressorjs";
|
||||||
import {httpGet, httpPost} from "@/utils/http";
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
||||||
@ -451,7 +667,7 @@ import Clipboard from "clipboard";
|
|||||||
import {checkSession} from "@/action/session";
|
import {checkSession} from "@/action/session";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
import {getSessionId} from "@/store/session";
|
import {getSessionId} from "@/store/session";
|
||||||
import {removeArrayItem} from "@/utils/libs";
|
import {copyObj, removeArrayItem} from "@/utils/libs";
|
||||||
import LoginDialog from "@/components/LoginDialog.vue";
|
import LoginDialog from "@/components/LoginDialog.vue";
|
||||||
|
|
||||||
const listBoxHeight = ref(window.innerHeight - 40)
|
const listBoxHeight = ref(window.innerHeight - 40)
|
||||||
@ -479,11 +695,12 @@ const models = [
|
|||||||
{text: "优质模式MJ-5.1", value: " --v 5.1", img: "/images/mj/mj-v5.1.jpg"},
|
{text: "优质模式MJ-5.1", value: " --v 5.1", img: "/images/mj/mj-v5.1.jpg"},
|
||||||
{text: "虚幻模式MJ-5", value: " --v 5", img: "/images/mj/mj-v5.jpg"},
|
{text: "虚幻模式MJ-5", value: " --v 5", img: "/images/mj/mj-v5.jpg"},
|
||||||
{text: "真实模式MJ-4", value: " --v 4", img: "/images/mj/mj-v4.jpg"},
|
{text: "真实模式MJ-4", value: " --v 4", img: "/images/mj/mj-v4.jpg"},
|
||||||
{text: "动漫风niji5 原始", value: " --niji 5", img: "/images/mj/mj-niji.png"},
|
{text: "动漫风-niji4", value: " --niji 4", img: "/images/mj/nj4.jpg"},
|
||||||
{text: "动漫风niji5 可爱", value: " --niji 5 --style cute", img: "/images/mj/nj1.jpg"},
|
{text: "动漫风-niji5", value: " --niji 5", img: "/images/mj/mj-niji.png"},
|
||||||
{text: "动漫风niji5 风景", value: " --niji 5 --style scenic", img: "/images/mj/nj2.jpg"},
|
{text: "动漫风-niji5 可爱", value: " --niji 5 --style cute", img: "/images/mj/nj1.jpg"},
|
||||||
{text: "动漫风niji5 表现力", value: " --niji 5 --style expressive", img: "/images/mj/nj3.jpg"},
|
{text: "动漫风-niji5 风景", value: " --niji 5 --style scenic", img: "/images/mj/nj2.jpg"},
|
||||||
{text: "动漫风niji4", value: " --niji 4", img: "/images/mj/nj4.jpg"},
|
{text: "动漫风-niji6", value: " --niji 6", img: "/images/mj/nj3.jpg"},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
@ -515,17 +732,20 @@ const initParams = {
|
|||||||
seed: 0,
|
seed: 0,
|
||||||
img_arr: [],
|
img_arr: [],
|
||||||
raw: false,
|
raw: false,
|
||||||
weight: 0.25,
|
iw: 0,
|
||||||
prompt: router.currentRoute.value.params["prompt"] ?? "",
|
prompt: router.currentRoute.value.params["prompt"] ?? "",
|
||||||
neg_prompt: "",
|
neg_prompt: "",
|
||||||
tile: false,
|
tile: false,
|
||||||
quality: 0
|
quality: 0,
|
||||||
|
cref: "",
|
||||||
|
sref: "",
|
||||||
|
cw: 0,
|
||||||
}
|
}
|
||||||
const params = ref(initParams)
|
const params = ref(copyObj(initParams))
|
||||||
|
|
||||||
const imgList = ref([])
|
const imgList = ref([])
|
||||||
|
|
||||||
const activeName = ref('image')
|
const activeName = ref('txt2img')
|
||||||
|
|
||||||
const runningJobs = ref([])
|
const runningJobs = ref([])
|
||||||
const finishedJobs = ref([])
|
const finishedJobs = ref([])
|
||||||
@ -714,6 +934,11 @@ const changeModel = (item) => {
|
|||||||
params.value.model = item.value
|
params.value.model = item.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const imgKey = ref("")
|
||||||
|
const beforeUpload = (key) => {
|
||||||
|
imgKey.value = key
|
||||||
|
}
|
||||||
|
|
||||||
// 图片上传
|
// 图片上传
|
||||||
const uploadImg = (file) => {
|
const uploadImg = (file) => {
|
||||||
if (!isLogin.value) {
|
if (!isLogin.value) {
|
||||||
@ -729,7 +954,12 @@ const uploadImg = (file) => {
|
|||||||
formData.append('file', result, result.name);
|
formData.append('file', result, result.name);
|
||||||
// 执行上传操作
|
// 执行上传操作
|
||||||
httpPost('/api/upload', formData).then((res) => {
|
httpPost('/api/upload', formData).then((res) => {
|
||||||
|
if (imgKey.value === '') {
|
||||||
imgList.value.push(res.data.url)
|
imgList.value.push(res.data.url)
|
||||||
|
} else { // 单张图片上传
|
||||||
|
params.value[imgKey.value] = res.data.url
|
||||||
|
imgKey.value = ''
|
||||||
|
}
|
||||||
ElMessage.success('上传成功')
|
ElMessage.success('上传成功')
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
ElMessage.error('上传失败:' + e.message)
|
ElMessage.error('上传失败:' + e.message)
|
||||||
@ -764,7 +994,8 @@ const generate = () => {
|
|||||||
httpPost("/api/mj/image", params.value).then(() => {
|
httpPost("/api/mj/image", params.value).then(() => {
|
||||||
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
|
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
|
||||||
power.value -= mjPower.value
|
power.value -= mjPower.value
|
||||||
params.value = initParams
|
params.value = copyObj(initParams)
|
||||||
|
imgList.value = []
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("任务推送失败:" + e.message)
|
ElMessage.error("任务推送失败:" + e.message)
|
||||||
})
|
})
|
||||||
@ -831,8 +1062,12 @@ const publishImage = (item, action) => {
|
|||||||
|
|
||||||
// 切换菜单
|
// 切换菜单
|
||||||
const tabChange = (tab) => {
|
const tabChange = (tab) => {
|
||||||
|
if (tab === "txt2img" || tab === "img2img" || tab === "cref") {
|
||||||
|
params.value.task_type = "image"
|
||||||
|
} else {
|
||||||
params.value.task_type = tab
|
params.value.task_type = tab
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 删除已上传图片
|
// 删除已上传图片
|
||||||
const removeUploadImage = (url) => {
|
const removeUploadImage = (url) => {
|
||||||
|
@ -222,7 +222,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="param-line" v-loading="translating" element-loading-background="rgba(122, 122, 122, 0.8)">
|
<div class="param-line">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="params.prompt"
|
v-model="params.prompt"
|
||||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||||
@ -246,16 +246,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="param-line">
|
<div class="param-line">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="params.negative_prompt"
|
v-model="params.neg_prompt"
|
||||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
placeholder="反向提示词"
|
placeholder="反向提示词"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="param-line" style="padding: 10px">
|
<div class="text-info">
|
||||||
|
<el-tag>每次绘图消耗{{ sdPower }}算力</el-tag>
|
||||||
<el-tag type="success">当前可用算力:{{ power }}</el-tag>
|
<el-tag type="success">当前可用算力:{{ power }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
<div class="submit-btn">
|
<div class="submit-btn">
|
||||||
@ -307,7 +309,7 @@
|
|||||||
<el-empty :image-size="100" v-else/>
|
<el-empty :image-size="100" v-else/>
|
||||||
</div>
|
</div>
|
||||||
<h2>创作记录</h2>
|
<h2>创作记录</h2>
|
||||||
<div class="finish-job-list" v-loading="loading" element-loading-background="rgba(255, 255, 255, 0.5)">
|
<div class="finish-job-list" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.5)">
|
||||||
<div v-if="finishedJobs.length > 0">
|
<div v-if="finishedJobs.length > 0">
|
||||||
<ItemList :items="finishedJobs" :width="240" :gap="16">
|
<ItemList :items="finishedJobs" :width="240" :gap="16">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@ -387,8 +389,8 @@
|
|||||||
反向提示词
|
反向提示词
|
||||||
</el-divider>
|
</el-divider>
|
||||||
<div class="prompt">
|
<div class="prompt">
|
||||||
<span>{{ item.params.negative_prompt }}</span>
|
<span>{{ item.params.neg_prompt }}</span>
|
||||||
<el-icon class="copy-prompt-sd" :data-clipboard-text="item.params.negative_prompt">
|
<el-icon class="copy-prompt-sd" :data-clipboard-text="item.params.neg_prompt">
|
||||||
<DocumentCopy/>
|
<DocumentCopy/>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
@ -479,7 +481,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, onUnmounted, ref} from "vue"
|
import {onMounted, onUnmounted, ref} from "vue"
|
||||||
import {Delete, DocumentCopy, InfoFilled, Orange, Picture, Refresh} from "@element-plus/icons-vue";
|
import {Delete, DocumentCopy, InfoFilled, Orange, Picture} from "@element-plus/icons-vue";
|
||||||
import {httpGet, httpPost} from "@/utils/http";
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
||||||
import ItemList from "@/components/ItemList.vue";
|
import ItemList from "@/components/ItemList.vue";
|
||||||
@ -494,7 +496,6 @@ const mjBoxHeight = ref(window.innerHeight - 150)
|
|||||||
const fullImgHeight = ref(window.innerHeight - 60)
|
const fullImgHeight = ref(window.innerHeight - 60)
|
||||||
const showTaskDialog = ref(false)
|
const showTaskDialog = ref(false)
|
||||||
const item = ref({})
|
const item = ref({})
|
||||||
const translating = ref(false)
|
|
||||||
const showLoginDialog = ref(false)
|
const showLoginDialog = ref(false)
|
||||||
const isLogin = ref(false)
|
const isLogin = ref(false)
|
||||||
|
|
||||||
@ -517,7 +518,7 @@ const params = ref({
|
|||||||
hd_scale_alg: scaleAlg[0],
|
hd_scale_alg: scaleAlg[0],
|
||||||
hd_steps: 0,
|
hd_steps: 0,
|
||||||
prompt: "",
|
prompt: "",
|
||||||
negative_prompt: "nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet",
|
neg_prompt: "nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet",
|
||||||
})
|
})
|
||||||
|
|
||||||
const runningJobs = ref([])
|
const runningJobs = ref([])
|
||||||
@ -619,9 +620,9 @@ const initData = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchRunningJobs = (userId) => {
|
const fetchRunningJobs = () => {
|
||||||
// 获取运行中的任务
|
// 获取运行中的任务
|
||||||
httpGet(`/api/sd/jobs?status=0&user_id=${userId}`).then(res => {
|
httpGet(`/api/sd/jobs?status=0`).then(res => {
|
||||||
const jobs = res.data
|
const jobs = res.data
|
||||||
const _jobs = []
|
const _jobs = []
|
||||||
for (let i = 0; i < jobs.length; i++) {
|
for (let i = 0; i < jobs.length; i++) {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="contain">
|
<div class="contain">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<el-image src="images/logo.png" fit="cover"/>
|
<el-image :src="logo" fit="cover"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="header">{{ title }}</div>
|
<div class="header">{{ title }}</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {Lock, UserFilled} from "@element-plus/icons-vue";
|
import {Lock, UserFilled} from "@element-plus/icons-vue";
|
||||||
import {httpPost} from "@/utils/http";
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
import {ElMessage} from "element-plus";
|
import {ElMessage} from "element-plus";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
import FooterBar from "@/components/FooterBar.vue";
|
import FooterBar from "@/components/FooterBar.vue";
|
||||||
@ -69,6 +69,15 @@ const title = ref('ChatPlus 用户登录');
|
|||||||
const username = ref(process.env.VUE_APP_USER);
|
const username = ref(process.env.VUE_APP_USER);
|
||||||
const password = ref(process.env.VUE_APP_PASS);
|
const password = ref(process.env.VUE_APP_PASS);
|
||||||
const showResetPass = ref(false)
|
const showResetPass = ref(false)
|
||||||
|
const logo = ref("/images/logo.png")
|
||||||
|
|
||||||
|
// 获取系统配置
|
||||||
|
httpGet("/api/config/get?key=system").then(res => {
|
||||||
|
logo.value = res.data.logo
|
||||||
|
}).catch(e => {
|
||||||
|
ElMessage.error("获取系统配置失败:" + e.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
checkSession().then(() => {
|
checkSession().then(() => {
|
||||||
if (isMobile()) {
|
if (isMobile()) {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<div class="page-inner">
|
<div class="page-inner">
|
||||||
<div class="contain" v-if="enableRegister">
|
<div class="contain" v-if="enableRegister">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<el-image src="images/logo.png" fit="cover"/>
|
<el-image :src="logo" fit="cover"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header">{{ title }}</div>
|
<div class="header">{{ title }}</div>
|
||||||
@ -141,6 +141,7 @@ const enableRegister = ref(true)
|
|||||||
const wxImg = ref("/images/wx.png")
|
const wxImg = ref("/images/wx.png")
|
||||||
const ways = []
|
const ways = []
|
||||||
const placeholder = ref("用户名:")
|
const placeholder = ref("用户名:")
|
||||||
|
const logo = ref("/images/logo.png")
|
||||||
|
|
||||||
httpGet("/api/config/get?key=system").then(res => {
|
httpGet("/api/config/get?key=system").then(res => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
@ -160,6 +161,7 @@ httpGet("/api/config/get?key=system").then(res => {
|
|||||||
if (res.data['wechat_card_url'] !== '') {
|
if (res.data['wechat_card_url'] !== '') {
|
||||||
wxImg.value = res.data['wechat_card_url']
|
wxImg.value = res.data['wechat_card_url']
|
||||||
}
|
}
|
||||||
|
logo.value = res.data.logo
|
||||||
}
|
}
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("获取系统配置失败:" + e.message)
|
ElMessage.error("获取系统配置失败:" + e.message)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<van-tabbar route v-model="active" @change="onChange">
|
<van-tabbar route v-model="active" @change="onChange">
|
||||||
<van-tabbar-item to="/mobile/chat" name="home" icon="chat-o">对话</van-tabbar-item>
|
<van-tabbar-item to="/mobile/chat" name="home" icon="chat-o">对话</van-tabbar-item>
|
||||||
<van-tabbar-item to="/mobile/mj" name="imageMj" icon="photo-o">绘图</van-tabbar-item>
|
<van-tabbar-item to="/mobile/image" name="image" icon="photo-o">绘图</van-tabbar-item>
|
||||||
<van-tabbar-item to="/mobile/img-wall" name="apps" icon="apps-o">广场</van-tabbar-item>
|
<van-tabbar-item to="/mobile/img-wall" name="apps" icon="apps-o">广场</van-tabbar-item>
|
||||||
<van-tabbar-item to="/mobile/profile" name="profile" icon="user-o">我的</van-tabbar-item>
|
<van-tabbar-item to="/mobile/profile" name="profile" icon="user-o">我的</van-tabbar-item>
|
||||||
</van-tabbar>
|
</van-tabbar>
|
||||||
@ -64,6 +64,11 @@ const onChange = (index) => {
|
|||||||
color #f56c6c
|
color #f56c6c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.van-toast--success {
|
||||||
|
background #D6FBCC
|
||||||
|
color #07C160
|
||||||
|
}
|
||||||
|
|
||||||
.van-nav-bar {
|
.van-nav-bar {
|
||||||
position fixed
|
position fixed
|
||||||
width 100%
|
width 100%
|
||||||
|
33
web/src/views/mobile/Image.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mobile-image container">
|
||||||
|
<van-tabs v-model:active="activeName" class="my-tab" animated sticky>
|
||||||
|
<van-tab title="MidJourney" name="mj">
|
||||||
|
<image-mj/>
|
||||||
|
</van-tab>
|
||||||
|
<van-tab title="Stable-Diffusion" name="sd">
|
||||||
|
<image-sd/>
|
||||||
|
</van-tab>
|
||||||
|
<van-tab title="DALL-E" name="dall">
|
||||||
|
<van-empty description="功能正在开发中"/>
|
||||||
|
</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {ref} from "vue";
|
||||||
|
import ImageMj from "@/views/mobile/ImageMj.vue";
|
||||||
|
import ImageSd from "@/views/mobile/ImageSd.vue";
|
||||||
|
|
||||||
|
const activeName = ref("sd")
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
.mobile-image {
|
||||||
|
.my-tab {
|
||||||
|
.van-tab__panel {
|
||||||
|
padding 10px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,8 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mobile-mj container">
|
<div class="mobile-mj">
|
||||||
<van-nav-bar :title="title"/>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<van-form @submit="generate">
|
<van-form @submit="generate">
|
||||||
<div class="text-line">图片比例</div>
|
<div class="text-line">图片比例</div>
|
||||||
<div class="text-line">
|
<div class="text-line">
|
||||||
@ -61,26 +58,100 @@
|
|||||||
</van-field>
|
</van-field>
|
||||||
</div>
|
</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"
|
||||||
|
rows="3"
|
||||||
|
autosize
|
||||||
|
type="textarea"
|
||||||
|
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
|
||||||
|
</div>
|
||||||
|
</van-tab>
|
||||||
|
<van-tab title="图生图" name="img2img">
|
||||||
<div class="text-line">
|
<div class="text-line">
|
||||||
<van-field v-model="params.prompt"
|
<van-field v-model="params.prompt"
|
||||||
rows="3"
|
rows="3"
|
||||||
label="提示词"
|
|
||||||
autosize
|
autosize
|
||||||
type="textarea"
|
type="textarea"
|
||||||
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
|
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<van-collapse v-model="activeColspan">
|
<div class="text-line">
|
||||||
<van-collapse-item title="垫图" name="img">
|
|
||||||
<van-field>
|
|
||||||
<template #input>
|
|
||||||
<van-uploader v-model="imgList" :after-read="uploadImg"/>
|
<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>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
</van-collapse-item>
|
</div>
|
||||||
|
|
||||||
|
<div class="tip-text">提示:只有于 niji6 和 v6 模型支持一致性功能,如果选择其他模型此功能将会生成失败。</div>
|
||||||
|
<van-cell-group>
|
||||||
|
<van-field
|
||||||
|
v-model="params.cref"
|
||||||
|
center
|
||||||
|
clearable
|
||||||
|
label="角色一致性"
|
||||||
|
placeholder="请输入图片URL或者上传图片"
|
||||||
|
>
|
||||||
|
<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-collapse-item title="反向提示词" name="neg_prompt">
|
||||||
<van-field
|
<van-field
|
||||||
v-model="params.prompt"
|
v-model="params.neg_prompt"
|
||||||
rows="3"
|
rows="3"
|
||||||
autosize
|
autosize
|
||||||
type="textarea"
|
type="textarea"
|
||||||
@ -88,10 +159,14 @@
|
|||||||
/>
|
/>
|
||||||
</van-collapse-item>
|
</van-collapse-item>
|
||||||
</van-collapse>
|
</van-collapse>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-line">
|
||||||
|
<el-tag>绘图消耗{{ mjPower }}算力,U/V 操作消耗{{ mjActionPower }}算力,当前算力:{{ power }}</el-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="text-line">
|
<div class="text-line">
|
||||||
<van-button round block type="primary" native-type="submit">
|
<van-button round block type="primary" native-type="submit">
|
||||||
<van-tag type="success">可用算力额度:{{ power }}</van-tag>
|
|
||||||
立即生成
|
立即生成
|
||||||
</van-button>
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
@ -196,7 +271,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -212,13 +286,11 @@ import {
|
|||||||
} from "vant";
|
} from "vant";
|
||||||
import {httpGet, httpPost} from "@/utils/http";
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
import Compressor from "compressorjs";
|
import Compressor from "compressorjs";
|
||||||
import {ElMessage} from "element-plus";
|
|
||||||
import {getSessionId} from "@/store/session";
|
import {getSessionId} from "@/store/session";
|
||||||
import {checkSession} from "@/action/session";
|
import {checkSession} from "@/action/session";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
import {Delete} from "@element-plus/icons-vue";
|
import {Delete} from "@element-plus/icons-vue";
|
||||||
|
|
||||||
const title = ref('MidJourney 绘画')
|
|
||||||
const activeColspan = ref([""])
|
const activeColspan = ref([""])
|
||||||
|
|
||||||
const rates = [
|
const rates = [
|
||||||
@ -232,10 +304,10 @@ const rates = [
|
|||||||
const models = [
|
const models = [
|
||||||
{text: "MJ-6.0", value: " --v 6", img: "/images/mj/mj-v6.png"},
|
{text: "MJ-6.0", value: " --v 6", img: "/images/mj/mj-v6.png"},
|
||||||
{text: "MJ-5.2", value: " --v 5.2", img: "/images/mj/mj-v5.2.png"},
|
{text: "MJ-5.2", value: " --v 5.2", img: "/images/mj/mj-v5.2.png"},
|
||||||
{text: "Niji5 原始", value: " --niji 5", img: "/images/mj/mj-niji.png"},
|
{text: "Niji5", value: " --niji 5", img: "/images/mj/mj-niji.png"},
|
||||||
{text: "Niji5 可爱", value: " --niji 5 --style cute", img: "/images/mj/nj1.jpg"},
|
{text: "Niji5 可爱", value: " --niji 5 --style cute", img: "/images/mj/nj1.jpg"},
|
||||||
{text: "Niji5 风景", value: " --niji 5 --style scenic", img: "/images/mj/nj2.jpg"},
|
{text: "Niji5 风景", value: " --niji 5 --style scenic", img: "/images/mj/nj2.jpg"},
|
||||||
{text: "Niji5 表现力", value: " --niji 5 --style expressive", img: "/images/mj/nj3.jpg"},
|
{text: "Niji6", value: " --niji 6", img: "/images/mj/nj3.jpg"},
|
||||||
]
|
]
|
||||||
const imgList = ref([])
|
const imgList = ref([])
|
||||||
const params = ref({
|
const params = ref({
|
||||||
@ -247,11 +319,14 @@ const params = ref({
|
|||||||
seed: 0,
|
seed: 0,
|
||||||
img_arr: [],
|
img_arr: [],
|
||||||
raw: false,
|
raw: false,
|
||||||
weight: 0.25,
|
iw: 0,
|
||||||
prompt: "",
|
prompt: "",
|
||||||
neg_prompt: "",
|
neg_prompt: "",
|
||||||
tile: false,
|
tile: false,
|
||||||
quality: 0
|
quality: 0,
|
||||||
|
cref: "",
|
||||||
|
sref: "",
|
||||||
|
cw: 0,
|
||||||
})
|
})
|
||||||
const userId = ref(0)
|
const userId = ref(0)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -259,6 +334,7 @@ const runningJobs = ref([])
|
|||||||
const finishedJobs = ref([])
|
const finishedJobs = ref([])
|
||||||
const socket = ref(null)
|
const socket = ref(null)
|
||||||
const power = ref(0)
|
const power = ref(0)
|
||||||
|
const activeName = ref("txt2img")
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkSession().then(user => {
|
checkSession().then(user => {
|
||||||
@ -284,7 +360,7 @@ httpGet("/api/config/get?key=system").then(res => {
|
|||||||
mjPower.value = res.data["mj_power"]
|
mjPower.value = res.data["mj_power"]
|
||||||
mjActionPower.value = res.data["mj_action_power"]
|
mjActionPower.value = res.data["mj_action_power"]
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("获取系统配置失败:" + e.message)
|
showNotify({type: "danger", message: "获取系统配置失败:" + e.message})
|
||||||
})
|
})
|
||||||
|
|
||||||
const heartbeatHandle = ref(null)
|
const heartbeatHandle = ref(null)
|
||||||
@ -355,7 +431,7 @@ const fetchRunningJobs = (userId) => {
|
|||||||
}
|
}
|
||||||
runningJobs.value = _jobs
|
runningJobs.value = _jobs
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("获取任务失败:" + e.message)
|
showNotify({type: "danger", message: "获取任务失败:" + e.message})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,6 +446,19 @@ const fetchFinishJobs = (page) => {
|
|||||||
httpGet(`/api/mj/jobs?status=1&page=${page}&page_size=${pageSize.value}`).then(res => {
|
httpGet(`/api/mj/jobs?status=1&page=${page}&page_size=${pageSize.value}`).then(res => {
|
||||||
const jobs = res.data
|
const jobs = res.data
|
||||||
for (let i = 0; i < jobs.length; i++) {
|
for (let i = 0; i < jobs.length; i++) {
|
||||||
|
if (jobs[i].progress === -1) {
|
||||||
|
showNotify({
|
||||||
|
message: `任务ID:${jobs[i]['task_id']} 原因:${jobs[i]['err_msg']}`,
|
||||||
|
type: 'danger',
|
||||||
|
})
|
||||||
|
if (jobs[i].type === 'image') {
|
||||||
|
power.value += mjPower.value
|
||||||
|
} else {
|
||||||
|
power.value += mjActionPower.value
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if (jobs[i]['use_proxy']) {
|
if (jobs[i]['use_proxy']) {
|
||||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?x-oss-process=image/quality,q_60&format=webp'
|
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?x-oss-process=image/quality,q_60&format=webp'
|
||||||
} else {
|
} else {
|
||||||
@ -414,6 +503,10 @@ const changeModel = (item) => {
|
|||||||
params.value.model = item.value
|
params.value.model = item.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const imgKey = ref("")
|
||||||
|
const beforeUpload = (key) => {
|
||||||
|
imgKey.value = key
|
||||||
|
}
|
||||||
|
|
||||||
// 图片上传
|
// 图片上传
|
||||||
const uploadImg = (file) => {
|
const uploadImg = (file) => {
|
||||||
@ -427,6 +520,10 @@ const uploadImg = (file) => {
|
|||||||
// 执行上传操作
|
// 执行上传操作
|
||||||
httpPost('/api/upload', formData).then(res => {
|
httpPost('/api/upload', formData).then(res => {
|
||||||
file.url = res.data.url
|
file.url = res.data.url
|
||||||
|
if (imgKey.value !== "") { // 单张图片上传
|
||||||
|
params.value[imgKey.value] = res.data.url
|
||||||
|
imgKey.value = ''
|
||||||
|
}
|
||||||
file.status = "done"
|
file.status = "done"
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
file.status = 'failed'
|
file.status = 'failed'
|
||||||
@ -449,10 +546,10 @@ const send = (url, index, item) => {
|
|||||||
session_id: getSessionId(),
|
session_id: getSessionId(),
|
||||||
prompt: item.prompt,
|
prompt: item.prompt,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
ElMessage.success("任务推送成功,请耐心等待任务执行...")
|
showSuccessToast("任务推送成功,请耐心等待任务执行...")
|
||||||
power.value -= mjActionPower.value
|
power.value -= mjActionPower.value
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("任务推送失败:" + e.message)
|
showFailToast("任务推送失败:" + e.message)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,9 +587,9 @@ const removeImage = (item) => {
|
|||||||
'此操作将会删除任务和图片,继续操作码?',
|
'此操作将会删除任务和图片,继续操作码?',
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
httpPost("/api/mj/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
|
httpPost("/api/mj/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
|
||||||
ElMessage.success("任务删除成功")
|
showSuccessToast("任务删除成功")
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("任务删除失败:" + e.message)
|
showFailToast("任务删除失败:" + e.message)
|
||||||
})
|
})
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
showToast("您取消了操作")
|
showToast("您取消了操作")
|
||||||
@ -524,6 +621,15 @@ const showPrompt = (item) => {
|
|||||||
const imageView = (item) => {
|
const imageView = (item) => {
|
||||||
showImagePreview([item['img_url']]);
|
showImagePreview([item['img_url']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 切换菜单
|
||||||
|
const tabChange = (tab) => {
|
||||||
|
if (tab === "txt2img" || tab === "img2img") {
|
||||||
|
params.value.task_type = "image"
|
||||||
|
} else {
|
||||||
|
params.value.task_type = tab
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus">
|
<style lang="stylus">
|
||||||
|
523
web/src/views/mobile/ImageSd.vue
Normal file
@ -0,0 +1,523 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mobile-sd">
|
||||||
|
<van-form @submit="generate">
|
||||||
|
<van-cell-group inset>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<van-field
|
||||||
|
v-model="params.prompt"
|
||||||
|
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"
|
||||||
|
autosize
|
||||||
|
type="textarea"
|
||||||
|
placeholder="不想出现在图片上的元素(例如:树,建筑)"
|
||||||
|
/>
|
||||||
|
</van-collapse-item>
|
||||||
|
</van-collapse>
|
||||||
|
|
||||||
|
<div class="text-line pt-6">
|
||||||
|
<el-tag>绘图消耗{{ sdPower }}算力,当前算力:{{ power }}</el-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-line">
|
||||||
|
<van-button round block type="primary" native-type="submit">
|
||||||
|
立即生成
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</van-cell-group>
|
||||||
|
</van-form>
|
||||||
|
|
||||||
|
<h3>任务列表</h3>
|
||||||
|
<div class="running-job-list">
|
||||||
|
<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">
|
||||||
|
<div v-if="item.progress > 0">
|
||||||
|
<van-image :src="item['img_url']">
|
||||||
|
<template v-slot:error>加载失败</template>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>创作记录</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">
|
||||||
|
<div class="job-item">
|
||||||
|
<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="showTask(item)" circle>
|
||||||
|
<i class="iconfont icon-prompt"></i>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-grid-item>
|
||||||
|
</van-grid>
|
||||||
|
</van-list>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {onMounted, onUnmounted, ref} from "vue"
|
||||||
|
import {Delete} from "@element-plus/icons-vue";
|
||||||
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
|
import Clipboard from "clipboard";
|
||||||
|
import {checkSession} from "@/action/session";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {getSessionId} from "@/store/session";
|
||||||
|
import {
|
||||||
|
showConfirmDialog, showDialog,
|
||||||
|
showFailToast,
|
||||||
|
showImagePreview,
|
||||||
|
showNotify,
|
||||||
|
showSuccessToast,
|
||||||
|
showToast
|
||||||
|
} from "vant";
|
||||||
|
|
||||||
|
const listBoxHeight = ref(window.innerHeight - 40)
|
||||||
|
const mjBoxHeight = ref(window.innerHeight - 150)
|
||||||
|
const showTaskDialog = ref(false)
|
||||||
|
const item = ref({})
|
||||||
|
const showLoginDialog = ref(false)
|
||||||
|
const isLogin = ref(false)
|
||||||
|
const activeColspan = ref([""])
|
||||||
|
|
||||||
|
window.onresize = () => {
|
||||||
|
listBoxHeight.value = window.innerHeight - 40
|
||||||
|
mjBoxHeight.value = window.innerHeight - 150
|
||||||
|
}
|
||||||
|
const samplers = ref([
|
||||||
|
{text: "Euler a", value: "Euler a"},
|
||||||
|
{text: "DPM++ 2S a Karras", value: "DPM++ 2S a Karras"},
|
||||||
|
{text: "DPM++ 2M Karras", value: "DPM++ 2M Karras"},
|
||||||
|
{text: "DPM++ 2M SDE Karras", value: "DPM++ 2M SDE Karras"},
|
||||||
|
{text: "DPM++ 2M Karras", value: "DPM++ 2M Karras"},
|
||||||
|
{text: "DPM++ 3M SDE Karras", value: "DPM++ 3M SDE Karras"},
|
||||||
|
])
|
||||||
|
const showSamplerPicker = ref(false)
|
||||||
|
|
||||||
|
const upscaleAlgArr = ref([
|
||||||
|
{text: "Latent", value: "Latent"},
|
||||||
|
{text: "ESRGAN_4x", value: "ESRGAN_4x"},
|
||||||
|
{text: "ESRGAN 4x+", value: "ESRGAN 4x+"},
|
||||||
|
{text: "SwinIR_4x", value: "SwinIR_4x"},
|
||||||
|
{text: "LDSR", value: "LDSR"},
|
||||||
|
])
|
||||||
|
const showUpscalePicker = ref(false)
|
||||||
|
|
||||||
|
const params = ref({
|
||||||
|
width: 1024,
|
||||||
|
height: 1024,
|
||||||
|
sampler: samplers.value[0].value,
|
||||||
|
seed: -1,
|
||||||
|
steps: 20,
|
||||||
|
cfg_scale: 7,
|
||||||
|
hd_fix: false,
|
||||||
|
hd_redraw_rate: 0.7,
|
||||||
|
hd_scale: 2,
|
||||||
|
hd_scale_alg: upscaleAlgArr.value[0].value,
|
||||||
|
hd_steps: 0,
|
||||||
|
prompt: "",
|
||||||
|
neg_prompt: "nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet",
|
||||||
|
})
|
||||||
|
|
||||||
|
const runningJobs = ref([])
|
||||||
|
const finishedJobs = ref([])
|
||||||
|
const router = useRouter()
|
||||||
|
// 检查是否有画同款的参数
|
||||||
|
const _params = router.currentRoute.value.params["copyParams"]
|
||||||
|
if (_params) {
|
||||||
|
params.value = JSON.parse(_params)
|
||||||
|
}
|
||||||
|
const power = ref(0)
|
||||||
|
const sdPower = ref(0) // 画一张 SD 图片消耗算力
|
||||||
|
|
||||||
|
const socket = ref(null)
|
||||||
|
const userId = ref(0)
|
||||||
|
const heartbeatHandle = ref(null)
|
||||||
|
const connect = () => {
|
||||||
|
let host = process.env.VUE_APP_WS_HOST
|
||||||
|
if (host === '') {
|
||||||
|
if (location.protocol === 'https:') {
|
||||||
|
host = 'wss://' + location.host;
|
||||||
|
} else {
|
||||||
|
host = 'ws://' + location.host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 心跳函数
|
||||||
|
const sendHeartbeat = () => {
|
||||||
|
clearTimeout(heartbeatHandle.value)
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
if (socket.value !== null) {
|
||||||
|
socket.value.send(JSON.stringify({type: "heartbeat", content: "ping"}))
|
||||||
|
}
|
||||||
|
resolve("success")
|
||||||
|
}).then(() => {
|
||||||
|
heartbeatHandle.value = setTimeout(() => sendHeartbeat(), 5000)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const _socket = new WebSocket(host + `/api/sd/client?user_id=${userId.value}`);
|
||||||
|
_socket.addEventListener('open', () => {
|
||||||
|
socket.value = _socket;
|
||||||
|
|
||||||
|
// 发送心跳消息
|
||||||
|
sendHeartbeat()
|
||||||
|
});
|
||||||
|
|
||||||
|
_socket.addEventListener('message', event => {
|
||||||
|
if (event.data instanceof Blob) {
|
||||||
|
fetchRunningJobs()
|
||||||
|
finished.value = false
|
||||||
|
page.value = 1
|
||||||
|
fetchFinishJobs(page.value)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_socket.addEventListener('close', () => {
|
||||||
|
if (socket.value !== null) {
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const clipboard = ref(null)
|
||||||
|
onMounted(() => {
|
||||||
|
initData()
|
||||||
|
clipboard.value = new Clipboard('.copy-prompt-sd');
|
||||||
|
clipboard.value.on('success', () => {
|
||||||
|
showNotify({type: "success", message: "复制成功!"});
|
||||||
|
})
|
||||||
|
|
||||||
|
clipboard.value.on('error', () => {
|
||||||
|
showNotify({type: "danger", message: '复制失败!'});
|
||||||
|
})
|
||||||
|
|
||||||
|
httpGet("/api/config/get?key=system").then(res => {
|
||||||
|
sdPower.value = res.data["sd_power"]
|
||||||
|
}).catch(e => {
|
||||||
|
showNotify({type: "danger", message: "获取系统配置失败:" + e.message})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clipboard.value.destroy()
|
||||||
|
socket.value = null
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const initData = () => {
|
||||||
|
checkSession().then(user => {
|
||||||
|
power.value = user['power']
|
||||||
|
userId.value = user.id
|
||||||
|
isLogin.value = true
|
||||||
|
fetchRunningJobs()
|
||||||
|
fetchFinishJobs(1)
|
||||||
|
connect()
|
||||||
|
}).catch(() => {
|
||||||
|
loading.value = false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchRunningJobs = () => {
|
||||||
|
// 获取运行中的任务
|
||||||
|
httpGet(`/api/sd/jobs?status=0`).then(res => {
|
||||||
|
const jobs = res.data
|
||||||
|
const _jobs = []
|
||||||
|
for (let i = 0; i < jobs.length; i++) {
|
||||||
|
if (jobs[i].progress === -1) {
|
||||||
|
showNotify({
|
||||||
|
message: `任务ID:${jobs[i]['task_id']} 原因:${jobs[i]['err_msg']}`,
|
||||||
|
type: 'danger',
|
||||||
|
})
|
||||||
|
power.value += sdPower.value
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_jobs.push(jobs[i])
|
||||||
|
}
|
||||||
|
runningJobs.value = _jobs
|
||||||
|
}).catch(e => {
|
||||||
|
showNotify({type: "danger", message: "获取任务失败:" + e.message})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const finished = ref(false)
|
||||||
|
const error = ref(false)
|
||||||
|
const page = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
// 获取已完成的任务
|
||||||
|
const fetchFinishJobs = (page) => {
|
||||||
|
loading.value = true
|
||||||
|
httpGet(`/api/sd/jobs?status=1&page=${page}&page_size=${pageSize.value}`).then(res => {
|
||||||
|
if (res.data.length < pageSize.value) {
|
||||||
|
finished.value = true
|
||||||
|
}
|
||||||
|
if (page === 1) {
|
||||||
|
finishedJobs.value = res.data
|
||||||
|
} else {
|
||||||
|
finishedJobs.value = finishedJobs.value.concat(res.data)
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
|
}).catch(e => {
|
||||||
|
loading.value = false
|
||||||
|
showNotify({type: "danger", message: "获取任务失败:" + e.message})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onLoad = () => {
|
||||||
|
page.value += 1
|
||||||
|
fetchFinishJobs(page.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建绘图任务
|
||||||
|
const promptRef = ref(null)
|
||||||
|
const generate = () => {
|
||||||
|
if (params.value.prompt === '') {
|
||||||
|
promptRef.value.focus()
|
||||||
|
return showToast("请输入绘画提示词!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLogin.value) {
|
||||||
|
showLoginDialog.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.value.seed === '') {
|
||||||
|
params.value.seed = -1
|
||||||
|
}
|
||||||
|
params.value.session_id = getSessionId()
|
||||||
|
httpPost("/api/sd/image", params.value).then(() => {
|
||||||
|
showSuccessToast("绘画任务推送成功,请耐心等待任务执行...")
|
||||||
|
power.value -= sdPower.value
|
||||||
|
}).catch(e => {
|
||||||
|
showFailToast("任务推送失败:" + e.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const showTask = (row) => {
|
||||||
|
item.value = row
|
||||||
|
showTaskDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyParams = (row) => {
|
||||||
|
params.value = row.params
|
||||||
|
showTaskDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeImage = (event, item) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '标题',
|
||||||
|
message:
|
||||||
|
'此操作将会删除任务和图片,继续操作码?',
|
||||||
|
}).then(() => {
|
||||||
|
httpPost("/api/sd/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
|
||||||
|
showSuccessToast("任务删除成功")
|
||||||
|
}).catch(e => {
|
||||||
|
showFailToast("任务删除失败:" + e.message)
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
showToast("您取消了操作")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布图片到作品墙
|
||||||
|
const publishImage = (event, item, action) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
let text = "图片发布"
|
||||||
|
if (action === false) {
|
||||||
|
text = "取消发布"
|
||||||
|
}
|
||||||
|
httpPost("/api/sd/publish", {id: item.id, action: action}).then(() => {
|
||||||
|
showSuccessToast(text + "成功")
|
||||||
|
item.publish = action
|
||||||
|
}).catch(e => {
|
||||||
|
showFailToast(text + "失败:" + e.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageView = (item) => {
|
||||||
|
showImagePreview([item['img_url']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const samplerConfirm = (item) => {
|
||||||
|
params.value.sampler = item.selectedOptions[0].text;
|
||||||
|
showSamplerPicker.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const upscaleConfirm = (item) => {
|
||||||
|
params.value.hd_scale_alg = item.selectedOptions[0].text;
|
||||||
|
showUpscalePicker.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const showInfo = (message) => {
|
||||||
|
showDialog({
|
||||||
|
title: "参数说明",
|
||||||
|
message: message,
|
||||||
|
}).then(() => {
|
||||||
|
// on close
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import "@/assets/css/mobile/image-sd.styl"
|
||||||
|
</style>
|